aboutsummaryrefslogtreecommitdiff
path: root/contrib
diff options
context:
space:
mode:
Diffstat (limited to 'contrib')
-rw-r--r--contrib/bc/LICENSE.md4
-rw-r--r--contrib/bc/MAINTENANCE-TERMS.md80
-rw-r--r--contrib/bc/Makefile.in285
-rw-r--r--contrib/bc/NEWS.md23
-rw-r--r--contrib/bc/NOTICE.md2
-rw-r--r--contrib/bc/README.md21
-rw-r--r--contrib/bc/VERSION.txt1
-rw-r--r--contrib/bc/build.gaml402
-rw-r--r--contrib/bc/build.pkg.rig2345
-rw-r--r--contrib/bc/build.rig575
-rw-r--r--contrib/bc/compile_flags.txt3
-rwxr-xr-xcontrib/bc/configure.sh477
-rw-r--r--contrib/bc/gen/bc_help.txt4
-rw-r--r--contrib/bc/gen/dc_help.txt4
-rw-r--r--contrib/bc/gen/lib.bc2
-rw-r--r--contrib/bc/gen/lib2.bc115
-rw-r--r--contrib/bc/gen/strgen.c4
-rwxr-xr-xcontrib/bc/gen/strgen.sh4
-rw-r--r--contrib/bc/include/args.h2
-rw-r--r--contrib/bc/include/bc.h8
-rw-r--r--contrib/bc/include/bcl.h2
-rw-r--r--contrib/bc/include/dc.h2
-rw-r--r--contrib/bc/include/file.h2
-rw-r--r--contrib/bc/include/history.h26
-rw-r--r--contrib/bc/include/lang.h6
-rw-r--r--contrib/bc/include/lex.h2
-rw-r--r--contrib/bc/include/library.h2
-rw-r--r--contrib/bc/include/num.h2
-rw-r--r--contrib/bc/include/opt.h2
-rw-r--r--contrib/bc/include/ossfuzz.h2
-rw-r--r--contrib/bc/include/parse.h2
-rw-r--r--contrib/bc/include/program.h16
-rw-r--r--contrib/bc/include/rand.h2
-rw-r--r--contrib/bc/include/read.h2
-rw-r--r--contrib/bc/include/status.h7
-rw-r--r--contrib/bc/include/vector.h2
-rw-r--r--contrib/bc/include/version.h42
-rw-r--r--contrib/bc/include/vm.h3
-rw-r--r--contrib/bc/locales/de_DE.ISO8859-1.msg2
-rw-r--r--contrib/bc/locales/de_DE.UTF-8.msg2
-rw-r--r--contrib/bc/locales/en_US.msg2
-rw-r--r--contrib/bc/locales/es_ES.ISO8859-1.msg2
-rw-r--r--contrib/bc/locales/es_ES.UTF-8.msg2
-rw-r--r--contrib/bc/locales/fr_FR.ISO8859-1.msg2
-rw-r--r--contrib/bc/locales/fr_FR.UTF-8.msg2
-rw-r--r--contrib/bc/locales/ja_JP.UTF-8.msg2
-rw-r--r--contrib/bc/locales/ja_JP.eucJP.msg2
-rw-r--r--contrib/bc/locales/nl_NL.ISO8859-1.msg2
-rw-r--r--contrib/bc/locales/nl_NL.UTF-8.msg2
-rw-r--r--contrib/bc/locales/pl_PL.ISO8859-2.msg2
-rw-r--r--contrib/bc/locales/pl_PL.UTF-8.msg2
-rw-r--r--contrib/bc/locales/pt_PT.ISO8859-1.msg2
-rw-r--r--contrib/bc/locales/pt_PT.UTF-8.msg2
-rw-r--r--contrib/bc/locales/ru_RU.CP1251.msg2
-rw-r--r--contrib/bc/locales/ru_RU.CP866.msg2
-rw-r--r--contrib/bc/locales/ru_RU.ISO8859-5.msg2
-rw-r--r--contrib/bc/locales/ru_RU.KOI8-R.msg2
-rw-r--r--contrib/bc/locales/ru_RU.UTF-8.msg2
-rw-r--r--contrib/bc/locales/zh_CN.GB18030.msg2
-rw-r--r--contrib/bc/locales/zh_CN.GB2312.msg2
-rw-r--r--contrib/bc/locales/zh_CN.GBK.msg2
-rw-r--r--contrib/bc/locales/zh_CN.UTF-8.msg2
-rw-r--r--contrib/bc/locales/zh_CN.eucCN.msg2
-rw-r--r--contrib/bc/manuals/bc/A.115
-rw-r--r--contrib/bc/manuals/bc/A.1.md14
-rw-r--r--contrib/bc/manuals/bc/E.14
-rw-r--r--contrib/bc/manuals/bc/E.1.md4
-rw-r--r--contrib/bc/manuals/bc/EH.14
-rw-r--r--contrib/bc/manuals/bc/EH.1.md4
-rw-r--r--contrib/bc/manuals/bc/EHN.14
-rw-r--r--contrib/bc/manuals/bc/EHN.1.md4
-rw-r--r--contrib/bc/manuals/bc/EN.14
-rw-r--r--contrib/bc/manuals/bc/EN.1.md4
-rw-r--r--contrib/bc/manuals/bc/H.115
-rw-r--r--contrib/bc/manuals/bc/H.1.md14
-rw-r--r--contrib/bc/manuals/bc/HN.115
-rw-r--r--contrib/bc/manuals/bc/HN.1.md14
-rw-r--r--contrib/bc/manuals/bc/N.115
-rw-r--r--contrib/bc/manuals/bc/N.1.md14
-rw-r--r--contrib/bc/manuals/bcl.34
-rw-r--r--contrib/bc/manuals/bcl.3.md4
-rw-r--r--contrib/bc/manuals/build.md6
-rw-r--r--contrib/bc/manuals/dc/A.14
-rw-r--r--contrib/bc/manuals/dc/A.1.md4
-rw-r--r--contrib/bc/manuals/dc/E.14
-rw-r--r--contrib/bc/manuals/dc/E.1.md4
-rw-r--r--contrib/bc/manuals/dc/EH.14
-rw-r--r--contrib/bc/manuals/dc/EH.1.md4
-rw-r--r--contrib/bc/manuals/dc/EHN.14
-rw-r--r--contrib/bc/manuals/dc/EHN.1.md4
-rw-r--r--contrib/bc/manuals/dc/EN.14
-rw-r--r--contrib/bc/manuals/dc/EN.1.md4
-rw-r--r--contrib/bc/manuals/dc/H.14
-rw-r--r--contrib/bc/manuals/dc/H.1.md4
-rw-r--r--contrib/bc/manuals/dc/HN.14
-rw-r--r--contrib/bc/manuals/dc/HN.1.md4
-rw-r--r--contrib/bc/manuals/dc/N.14
-rw-r--r--contrib/bc/manuals/dc/N.1.md4
-rw-r--r--contrib/bc/project/README.md32
-rw-r--r--contrib/bc/project/gitea.dbbin0 -> 86016 bytes
-rw-r--r--contrib/bc/project/github_issues.json3667
-rw-r--r--contrib/bc/project/github_prs.json7729
-rw-r--r--contrib/bc/project/issue10.md104
-rwxr-xr-xcontrib/bc/scripts/exec-install.sh2
-rwxr-xr-xcontrib/bc/scripts/format.sh2
-rwxr-xr-xcontrib/bc/scripts/functions.sh2
-rwxr-xr-xcontrib/bc/scripts/karatsuba.py2
-rwxr-xr-xcontrib/bc/scripts/link.sh2
-rwxr-xr-xcontrib/bc/scripts/lint.sh2
-rwxr-xr-xcontrib/bc/scripts/locale_install.sh2
-rwxr-xr-xcontrib/bc/scripts/locale_uninstall.sh2
-rw-r--r--contrib/bc/scripts/os.c2
-rw-r--r--contrib/bc/scripts/release.pkg.yao1410
-rw-r--r--contrib/bc/scripts/sqrt_frac_guess.bc2
-rw-r--r--contrib/bc/scripts/sqrt_int_guess.bc2
-rw-r--r--contrib/bc/scripts/sqrt_random.bc2
-rwxr-xr-xcontrib/bc/scripts/sqrt_random.sh2
-rw-r--r--contrib/bc/src/args.c2
-rw-r--r--contrib/bc/src/bc.c2
-rw-r--r--contrib/bc/src/bc_fuzzer.c3
-rw-r--r--contrib/bc/src/bc_lex.c2
-rw-r--r--contrib/bc/src/bc_parse.c2
-rw-r--r--contrib/bc/src/data.c142
-rw-r--r--contrib/bc/src/dc.c2
-rw-r--r--contrib/bc/src/dc_fuzzer.c3
-rw-r--r--contrib/bc/src/dc_lex.c2
-rw-r--r--contrib/bc/src/dc_parse.c2
-rw-r--r--contrib/bc/src/file.c5
-rw-r--r--contrib/bc/src/history.c16
-rw-r--r--contrib/bc/src/lang.c7
-rw-r--r--contrib/bc/src/lex.c2
-rw-r--r--contrib/bc/src/library.c2
-rw-r--r--contrib/bc/src/main.c3
-rw-r--r--contrib/bc/src/num.c3
-rw-r--r--contrib/bc/src/opt.c2
-rw-r--r--contrib/bc/src/parse.c2
-rw-r--r--contrib/bc/src/program.c67
-rw-r--r--contrib/bc/src/rand.c2
-rw-r--r--contrib/bc/src/read.c23
-rw-r--r--contrib/bc/src/vector.c2
-rw-r--r--contrib/bc/src/vm.c35
-rwxr-xr-xcontrib/bc/tests/all.sh42
-rw-r--r--contrib/bc/tests/bc/all.txt131
-rw-r--r--contrib/bc/tests/bc/errors/39.txt1
-rw-r--r--contrib/bc/tests/bc/lib2.txt477
-rw-r--r--contrib/bc/tests/bc/lib2_a2.txt18
-rw-r--r--contrib/bc/tests/bc/lib2_a2_results.txt18
-rw-r--r--contrib/bc/tests/bc/lib2_bytes.txt46
-rw-r--r--contrib/bc/tests/bc/lib2_bytes_results.txt46
-rw-r--r--contrib/bc/tests/bc/lib2_ceil.txt24
-rw-r--r--contrib/bc/tests/bc/lib2_ceil_results.txt24
-rw-r--r--contrib/bc/tests/bc/lib2_d2r.txt16
-rw-r--r--contrib/bc/tests/bc/lib2_d2r_results.txt16
-rw-r--r--contrib/bc/tests/bc/lib2_fac.txt6
-rw-r--r--contrib/bc/tests/bc/lib2_fac_results.txt6
-rw-r--r--contrib/bc/tests/bc/lib2_gcd.txt7
-rw-r--r--contrib/bc/tests/bc/lib2_gcd_results.txt7
-rw-r--r--contrib/bc/tests/bc/lib2_log.txt32
-rw-r--r--contrib/bc/tests/bc/lib2_log_results.txt32
-rw-r--r--contrib/bc/tests/bc/lib2_p.txt4
-rw-r--r--contrib/bc/tests/bc/lib2_p_results.txt5
-rw-r--r--contrib/bc/tests/bc/lib2_perm.txt9
-rw-r--r--contrib/bc/tests/bc/lib2_perm_results.txt8
-rw-r--r--contrib/bc/tests/bc/lib2_pi.txt5
-rw-r--r--contrib/bc/tests/bc/lib2_pi_results.txt6
-rw-r--r--contrib/bc/tests/bc/lib2_r.txt23
-rw-r--r--contrib/bc/tests/bc/lib2_r2d.txt17
-rw-r--r--contrib/bc/tests/bc/lib2_r2d_results.txt16
-rw-r--r--contrib/bc/tests/bc/lib2_r_results.txt23
-rw-r--r--contrib/bc/tests/bc/lib2_rand.txt11
-rw-r--r--contrib/bc/tests/bc/lib2_rand_results.txt7
-rw-r--r--contrib/bc/tests/bc/lib2_root.txt13
-rw-r--r--contrib/bc/tests/bc/lib2_root_results.txt13
-rw-r--r--contrib/bc/tests/bc/lib2_tan.txt30
-rw-r--r--contrib/bc/tests/bc/lib2_tan_results.txt29
-rw-r--r--contrib/bc/tests/bc/lib2_uint.txt218
-rw-r--r--contrib/bc/tests/bc/lib2_uint_results.txt (renamed from contrib/bc/tests/bc/lib2_results.txt)256
-rw-r--r--contrib/bc/tests/bc/scripts/add_00100.bc (renamed from contrib/bc/tests/bc/scripts/add.bc)2
-rw-r--r--contrib/bc/tests/bc/scripts/add_00200.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_00300.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_00400.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_00500.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_00600.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_00700.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_00800.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_00900.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_01000.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_01100.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_01200.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_01300.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_01400.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_01500.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_01600.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_01700.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_01800.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_01900.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_02000.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_02100.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_02200.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_02300.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_02400.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_02500.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_02600.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_02700.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_02800.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_02900.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_03000.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_03100.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_03200.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_03300.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_03400.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_03500.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_03600.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_03700.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_03800.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_03900.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_04000.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_04100.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_04200.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_04300.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_04400.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_04500.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_04600.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_04700.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_04800.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_04900.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_05000.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_05100.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_05200.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_05300.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_05400.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_05500.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_05600.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_05700.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_05800.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_05900.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_06000.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_06100.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_06200.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_06300.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_06400.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_06500.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_06600.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_06700.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_06800.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_06900.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_07000.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_07100.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_07200.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_07300.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_07400.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_07500.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_07600.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_07700.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_07800.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_07900.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_08000.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_08100.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_08200.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_08300.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_08400.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_08500.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_08600.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_08700.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_08800.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_08900.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_09000.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_09100.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_09200.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_09300.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_09400.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_09500.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_09600.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_09700.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_09800.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_09900.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/add_10000.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/all.txt406
-rw-r--r--contrib/bc/tests/bc/scripts/divide_00100.bc (renamed from contrib/bc/tests/bc/scripts/divide.bc)2
-rw-r--r--contrib/bc/tests/bc/scripts/divide_00200.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_00300.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_00400.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_00500.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_00600.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_00700.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_00800.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_00900.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_01000.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_01100.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_01200.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_01300.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_01400.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_01500.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_01600.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_01700.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_01800.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_01900.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_02000.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_02100.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_02200.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_02300.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_02400.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_02500.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_02600.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_02700.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_02800.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_02900.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_03000.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_03100.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_03200.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_03300.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_03400.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_03500.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_03600.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_03700.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_03800.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_03900.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_04000.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_04100.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_04200.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_04300.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_04400.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_04500.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_04600.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_04700.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_04800.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_04900.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_05000.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_05100.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_05200.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_05300.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_05400.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_05500.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_05600.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_05700.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_05800.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_05900.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_06000.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_06100.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_06200.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_06300.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_06400.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_06500.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_06600.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_06700.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_06800.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_06900.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_07000.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_07100.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_07200.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_07300.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_07400.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_07500.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_07600.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_07700.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_07800.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_07900.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_08000.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_08100.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_08200.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_08300.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_08400.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_08500.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_08600.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_08700.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_08800.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_08900.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_09000.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_09100.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_09200.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_09300.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_09400.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_09500.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_09600.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_09700.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_09800.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_09900.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/divide_10000.bc23
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_00100.bc (renamed from contrib/bc/tests/bc/scripts/multiply.bc)2
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_00200.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_00300.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_00400.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_00500.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_00600.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_00700.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_00800.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_00900.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_01000.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_01100.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_01200.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_01300.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_01400.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_01500.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_01600.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_01700.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_01800.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_01900.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_02000.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_02100.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_02200.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_02300.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_02400.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_02500.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_02600.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_02700.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_02800.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_02900.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_03000.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_03100.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_03200.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_03300.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_03400.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_03500.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_03600.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_03700.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_03800.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_03900.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_04000.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_04100.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_04200.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_04300.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_04400.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_04500.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_04600.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_04700.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_04800.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_04900.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_05000.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_05100.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_05200.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_05300.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_05400.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_05500.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_05600.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_05700.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_05800.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_05900.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_06000.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_06100.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_06200.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_06300.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_06400.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_06500.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_06600.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_06700.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_06800.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_06900.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_07000.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_07100.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_07200.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_07300.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_07400.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_07500.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_07600.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_07700.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_07800.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_07900.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_08000.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_08100.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_08200.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_08300.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_08400.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_08500.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_08600.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_08700.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_08800.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_08900.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_09000.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_09100.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_09200.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_09300.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_09400.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_09500.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_09600.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_09700.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_09800.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_09900.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/multiply_10000.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/parse.bc20
-rw-r--r--contrib/bc/tests/bc/scripts/parse_02.bc19
-rw-r--r--contrib/bc/tests/bc/scripts/parse_03.bc19
-rw-r--r--contrib/bc/tests/bc/scripts/parse_04.bc19
-rw-r--r--contrib/bc/tests/bc/scripts/parse_05.bc19
-rw-r--r--contrib/bc/tests/bc/scripts/parse_06.bc19
-rw-r--r--contrib/bc/tests/bc/scripts/parse_07.bc19
-rw-r--r--contrib/bc/tests/bc/scripts/parse_08.bc19
-rw-r--r--contrib/bc/tests/bc/scripts/parse_09.bc19
-rw-r--r--contrib/bc/tests/bc/scripts/parse_11.bc19
-rw-r--r--contrib/bc/tests/bc/scripts/parse_12.bc19
-rw-r--r--contrib/bc/tests/bc/scripts/parse_13.bc19
-rw-r--r--contrib/bc/tests/bc/scripts/parse_14.bc19
-rw-r--r--contrib/bc/tests/bc/scripts/parse_15.bc19
-rw-r--r--contrib/bc/tests/bc/scripts/parse_16.bc19
-rw-r--r--contrib/bc/tests/bc/scripts/print.bc25
-rw-r--r--contrib/bc/tests/bc/scripts/print_002.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_003.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_004.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_005.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_006.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_007.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_008.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_009.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_011.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_012.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_013.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_014.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_015.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_016.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_017.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_018.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_019.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_020.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_021.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_022.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_023.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_024.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_025.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_026.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_027.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_028.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_029.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_030.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_031.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_032.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_033.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_034.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_035.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_036.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_037.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_038.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_039.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_040.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_041.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_042.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_043.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_044.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_045.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_046.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_047.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_048.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_049.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_050.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_051.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_052.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_053.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_054.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_055.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_056.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_057.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_058.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_059.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_060.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_061.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_062.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_063.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_064.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_065.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_066.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_067.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_068.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_069.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_070.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_071.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_072.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_073.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_074.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_075.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_076.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_077.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_078.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_079.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_080.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_081.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_082.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_083.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_084.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_085.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_086.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_087.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_088.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_089.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_090.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_091.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_092.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_093.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_094.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_095.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_096.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_097.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_098.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_099.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/print_100.bc22
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_00100.bc (renamed from contrib/bc/tests/bc/scripts/subtract.bc)2
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_00200.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_00300.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_00400.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_00500.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_00600.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_00700.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_00800.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_00900.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_01000.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_01100.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_01200.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_01300.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_01400.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_01500.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_01600.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_01700.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_01800.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_01900.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_02000.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_02100.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_02200.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_02300.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_02400.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_02500.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_02600.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_02700.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_02800.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_02900.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_03000.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_03100.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_03200.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_03300.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_03400.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_03500.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_03600.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_03700.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_03800.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_03900.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_04000.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_04100.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_04200.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_04300.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_04400.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_04500.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_04600.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_04700.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_04800.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_04900.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_05000.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_05100.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_05200.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_05300.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_05400.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_05500.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_05600.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_05700.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_05800.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_05900.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_06000.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_06100.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_06200.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_06300.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_06400.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_06500.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_06600.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_06700.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_06800.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_06900.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_07000.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_07100.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_07200.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_07300.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_07400.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_07500.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_07600.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_07700.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_07800.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_07900.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_08000.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_08100.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_08200.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_08300.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_08400.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_08500.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_08600.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_08700.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_08800.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_08900.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_09000.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_09100.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_09200.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_09300.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_09400.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_09500.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_09600.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_09700.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_09800.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_09900.bc17
-rw-r--r--contrib/bc/tests/bc/scripts/subtract_10000.bc17
-rwxr-xr-xcontrib/bc/tests/bc/timeconst.sh5
-rw-r--r--contrib/bc/tests/bcl.c2
-rw-r--r--contrib/bc/tests/dc/scripts/easter.dc49
-rwxr-xr-xcontrib/bc/tests/dc/scripts/easter.sh93
-rw-r--r--contrib/bc/tests/dc/scripts/easter.txt5
-rw-r--r--contrib/bc/tests/dc/scripts/prime.dc2
-rwxr-xr-xcontrib/bc/tests/error.sh4
-rwxr-xr-xcontrib/bc/tests/errors.sh4
-rw-r--r--contrib/bc/tests/extra_required.txt17
-rwxr-xr-xcontrib/bc/tests/history.py4
-rwxr-xr-xcontrib/bc/tests/history.sh2
-rwxr-xr-xcontrib/bc/tests/other.sh593
-rwxr-xr-xcontrib/bc/tests/read.sh166
-rwxr-xr-xcontrib/bc/tests/script.sh25
-rwxr-xr-xcontrib/bc/tests/scripts.sh17
-rwxr-xr-xcontrib/bc/tests/stdin.sh2
-rwxr-xr-xcontrib/bc/tests/test.sh24
-rw-r--r--contrib/bc/vs/bc.vcxproj1
-rw-r--r--contrib/bc/vs/bc.vcxproj.filters3
-rw-r--r--contrib/bc/vs/bcl.vcxproj1
-rw-r--r--contrib/bc/vs/bcl.vcxproj.filters3
-rw-r--r--contrib/expat/Changes135
-rw-r--r--contrib/expat/Makefile.am7
-rw-r--r--contrib/expat/Makefile.in128
-rw-r--r--contrib/expat/README.md8
-rw-r--r--contrib/expat/configure.ac24
-rw-r--r--contrib/expat/doc/Makefile.in29
-rw-r--r--contrib/expat/doc/reference.html146
-rw-r--r--contrib/expat/doc/xmlwf.130
-rw-r--r--contrib/expat/doc/xmlwf.xml28
-rw-r--r--contrib/expat/examples/Makefile.in35
-rw-r--r--contrib/expat/expat_config.h.in12
-rw-r--r--contrib/expat/fuzz/xml_lpm_fuzzer.cpp6
-rw-r--r--contrib/expat/fuzz/xml_parse_fuzzer.c20
-rw-r--r--contrib/expat/fuzz/xml_parsebuffer_fuzzer.c24
-rw-r--r--contrib/expat/lib/Makefile.in49
-rw-r--r--contrib/expat/lib/expat.h64
-rw-r--r--contrib/expat/lib/expat_external.h123
-rw-r--r--contrib/expat/lib/internal.h14
-rw-r--r--contrib/expat/lib/xmlparse.c673
-rw-r--r--contrib/expat/lib/xmlrole.h36
-rw-r--r--contrib/expat/lib/xmltok.c4
-rw-r--r--contrib/expat/lib/xmltok.h226
-rw-r--r--contrib/expat/tests/Makefile.in99
-rw-r--r--contrib/expat/tests/alloc_tests.c242
-rw-r--r--contrib/expat/tests/basic_tests.c8
-rw-r--r--contrib/expat/tests/benchmark/Makefile.in35
-rw-r--r--contrib/expat/tests/common.c11
-rw-r--r--contrib/expat/tests/handlers.c29
-rw-r--r--contrib/expat/tests/minicheck.h3
-rw-r--r--contrib/expat/tests/misc_tests.c101
-rw-r--r--contrib/expat/tests/nsalloc_tests.c11
-rw-r--r--contrib/expat/xmlwf/Makefile.in37
-rw-r--r--contrib/expat/xmlwf/unixfilemap.c6
-rw-r--r--contrib/expat/xmlwf/xmlfile.c25
-rw-r--r--contrib/expat/xmlwf/xmlwf.c21
-rwxr-xr-xcontrib/expat/xmlwf/xmlwf_helpgen.py18
-rw-r--r--contrib/jemalloc/ChangeLog100
-rwxr-xr-xcontrib/jemalloc/FREEBSD-upgrade254
-rw-r--r--contrib/jemalloc/INSTALL.md424
-rw-r--r--contrib/jemalloc/Makefile.in762
-rw-r--r--contrib/jemalloc/README20
-rw-r--r--contrib/jemalloc/TUNING.md129
-rw-r--r--contrib/jemalloc/VERSION2
-rwxr-xr-xcontrib/jemalloc/autogen.sh17
-rw-r--r--contrib/jemalloc/bin/jemalloc-config.in83
-rw-r--r--contrib/jemalloc/bin/jemalloc.sh.in9
-rw-r--r--contrib/jemalloc/bin/jeprof.in5723
-rwxr-xr-xcontrib/jemalloc/build-aux/config.guess1701
-rwxr-xr-xcontrib/jemalloc/build-aux/config.sub1855
-rwxr-xr-xcontrib/jemalloc/build-aux/install-sh250
-rw-r--r--contrib/jemalloc/config.stamp.in (renamed from contrib/libucl/python/tests/__init__.py)0
-rw-r--r--contrib/jemalloc/configure.ac2669
-rw-r--r--contrib/jemalloc/doc/html.xsl.in5
-rw-r--r--contrib/jemalloc/doc/jemalloc.xml.in3788
-rw-r--r--contrib/jemalloc/doc/manpages.xsl.in4
-rw-r--r--contrib/jemalloc/doc/stylesheet.xsl10
-rw-r--r--contrib/jemalloc/doc_internal/PROFILING_INTERNALS.md127
-rw-r--r--contrib/jemalloc/doc_internal/jemalloc.svg1
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/activity_callback.h23
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/arena_externs.h77
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/arena_inlines_a.h35
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/arena_inlines_b.h489
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/arena_stats.h227
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/arena_structs.h101
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/arena_structs_a.h11
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/arena_structs_b.h232
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/arena_types.h23
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/atomic.h33
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/atomic_msvc.h158
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/background_thread_externs.h7
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/background_thread_inlines.h14
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/background_thread_structs.h12
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/base.h110
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/base_externs.h22
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/base_inlines.h13
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/base_structs.h59
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/base_types.h33
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/bin.h85
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/bin_info.h50
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/bin_stats.h5
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/bin_types.h2
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/bit_util.h457
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/bitmap.h21
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/buf_writer.h32
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/cache_bin.h625
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/counter.h34
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/ctl.h31
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/decay.h186
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/ecache.h55
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/edata.h698
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/edata_cache.h49
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/ehooks.h412
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/emap.h357
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/emitter.h74
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/eset.h77
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/exp_grow.h50
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/extent.h137
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/extent_externs.h83
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/extent_inlines.h501
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/extent_structs.h256
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/extent_types.h23
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/fb.h373
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/fxp.h126
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/hash.h63
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/hpa.h182
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/hpa_hooks.h17
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/hpa_opts.h74
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/hpdata.h413
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/inspect.h40
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/jemalloc_internal_decls.h18
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/jemalloc_internal_defs.h.in427
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/jemalloc_internal_externs.h28
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/jemalloc_internal_includes.h16
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/jemalloc_internal_inlines_a.h82
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/jemalloc_internal_inlines_b.h52
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/jemalloc_internal_inlines_c.h124
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/jemalloc_internal_macros.h13
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/jemalloc_internal_types.h28
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/jemalloc_preamble.h.in262
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/large_externs.h26
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/lockedint.h204
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/malloc_io.h11
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/mpsc_queue.h134
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/mutex.h63
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/mutex_pool.h94
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/mutex_prof.h13
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/nstime.h43
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/pa.h243
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/pac.h179
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/pages.h31
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/pai.h95
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/peak.h37
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/peak_event.h24
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/ph.h817
-rwxr-xr-xcontrib/jemalloc/include/jemalloc/internal/private_namespace.sh5
-rwxr-xr-xcontrib/jemalloc/include/jemalloc/internal/private_symbols.sh51
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/prng.h93
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/prof_data.h37
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/prof_externs.h116
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/prof_hook.h21
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/prof_inlines.h261
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/prof_inlines_a.h85
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/prof_inlines_b.h250
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/prof_log.h22
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/prof_recent.h23
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/prof_stats.h17
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/prof_structs.h47
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/prof_sys.h30
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/prof_types.h37
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/psset.h131
-rwxr-xr-xcontrib/jemalloc/include/jemalloc/internal/public_namespace.sh6
-rwxr-xr-xcontrib/jemalloc/include/jemalloc/internal/public_unnamespace.sh6
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/ql.h129
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/qr.h130
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/quantum.h12
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/rb.h920
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/rtree.h520
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/rtree_tsd.h24
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/safety_check.h7
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/san.h191
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/san_bump.h52
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/sc.h78
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/sec.h120
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/sec_opts.h59
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/slab_data.h12
-rwxr-xr-xcontrib/jemalloc/include/jemalloc/internal/smoothstep.sh101
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/stats.h29
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/sz.h97
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/tcache_externs.h60
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/tcache_inlines.h156
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/tcache_structs.h62
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/tcache_types.h40
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/test_hooks.h16
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/thread_event.h301
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/ticker.h92
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/tsd.h243
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/tsd_generic.h23
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/tsd_malloc_thread_cleanup.h2
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/tsd_types.h2
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/tsd_win.h139
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/typed_list.h55
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/util.h56
-rw-r--r--contrib/jemalloc/include/jemalloc/internal/witness.h183
-rwxr-xr-xcontrib/jemalloc/include/jemalloc/jemalloc.sh27
-rw-r--r--contrib/jemalloc/include/jemalloc/jemalloc_defs.h.in54
-rw-r--r--contrib/jemalloc/include/jemalloc/jemalloc_macros.h.in149
-rwxr-xr-xcontrib/jemalloc/include/jemalloc/jemalloc_mangle.sh45
-rw-r--r--contrib/jemalloc/include/jemalloc/jemalloc_protos.h.in71
-rwxr-xr-xcontrib/jemalloc/include/jemalloc/jemalloc_rename.sh22
-rw-r--r--contrib/jemalloc/include/jemalloc/jemalloc_typedefs.h.in77
-rw-r--r--contrib/jemalloc/include/msvc_compat/C99/stdbool.h20
-rw-r--r--contrib/jemalloc/include/msvc_compat/C99/stdint.h247
-rw-r--r--contrib/jemalloc/include/msvc_compat/strings.h58
-rw-r--r--contrib/jemalloc/include/msvc_compat/windows_extra.h6
-rw-r--r--contrib/jemalloc/jemalloc.pc.in12
-rw-r--r--contrib/jemalloc/m4/ax_cxx_compile_stdcxx.m4951
-rwxr-xr-xcontrib/jemalloc/run_tests.sh1
-rwxr-xr-xcontrib/jemalloc/scripts/check-formatting.sh28
-rw-r--r--contrib/jemalloc/scripts/freebsd/before_install.sh3
-rw-r--r--contrib/jemalloc/scripts/freebsd/before_script.sh10
-rw-r--r--contrib/jemalloc/scripts/freebsd/script.sh3
-rwxr-xr-xcontrib/jemalloc/scripts/gen_run_tests.py130
-rwxr-xr-xcontrib/jemalloc/scripts/gen_travis.py327
-rw-r--r--contrib/jemalloc/scripts/linux/before_install.sh13
-rw-r--r--contrib/jemalloc/scripts/windows/before_install.sh83
-rw-r--r--contrib/jemalloc/scripts/windows/before_script.sh20
-rw-r--r--contrib/jemalloc/scripts/windows/script.sh10
-rw-r--r--contrib/jemalloc/src/arena.c2063
-rw-r--r--contrib/jemalloc/src/background_thread.c335
-rw-r--r--contrib/jemalloc/src/base.c193
-rw-r--r--contrib/jemalloc/src/bin.c30
-rw-r--r--contrib/jemalloc/src/bin_info.c30
-rw-r--r--contrib/jemalloc/src/bitmap.c1
-rw-r--r--contrib/jemalloc/src/buf_writer.c144
-rw-r--r--contrib/jemalloc/src/cache_bin.c99
-rw-r--r--contrib/jemalloc/src/ckh.c7
-rw-r--r--contrib/jemalloc/src/counter.c30
-rw-r--r--contrib/jemalloc/src/ctl.c1687
-rw-r--r--contrib/jemalloc/src/decay.c295
-rw-r--r--contrib/jemalloc/src/ecache.c35
-rw-r--r--contrib/jemalloc/src/edata.c6
-rw-r--r--contrib/jemalloc/src/edata_cache.c154
-rw-r--r--contrib/jemalloc/src/ehooks.c275
-rw-r--r--contrib/jemalloc/src/emap.c386
-rw-r--r--contrib/jemalloc/src/eset.c282
-rw-r--r--contrib/jemalloc/src/exp_grow.c8
-rw-r--r--contrib/jemalloc/src/extent.c2484
-rw-r--r--contrib/jemalloc/src/extent_dss.c42
-rw-r--r--contrib/jemalloc/src/extent_mmap.c1
-rw-r--r--contrib/jemalloc/src/fxp.c124
-rw-r--r--contrib/jemalloc/src/hash.c3
-rw-r--r--contrib/jemalloc/src/hook.c6
-rw-r--r--contrib/jemalloc/src/hpa.c1044
-rw-r--r--contrib/jemalloc/src/hpa_hooks.c63
-rw-r--r--contrib/jemalloc/src/hpdata.c325
-rw-r--r--contrib/jemalloc/src/inspect.c77
-rw-r--r--contrib/jemalloc/src/jemalloc.c2155
-rw-r--r--contrib/jemalloc/src/jemalloc_cpp.cpp254
-rw-r--r--contrib/jemalloc/src/large.c299
-rw-r--r--contrib/jemalloc/src/malloc_io.c46
-rw-r--r--contrib/jemalloc/src/mutex.c21
-rw-r--r--contrib/jemalloc/src/mutex_pool.c18
-rw-r--r--contrib/jemalloc/src/nstime.c127
-rw-r--r--contrib/jemalloc/src/pa.c277
-rw-r--r--contrib/jemalloc/src/pa_extra.c191
-rw-r--r--contrib/jemalloc/src/pac.c587
-rw-r--r--contrib/jemalloc/src/pages.c209
-rw-r--r--contrib/jemalloc/src/pai.c31
-rw-r--r--contrib/jemalloc/src/peak_event.c82
-rw-r--r--contrib/jemalloc/src/prng.c3
-rw-r--r--contrib/jemalloc/src/prof.c2923
-rw-r--r--contrib/jemalloc/src/prof_data.c1447
-rw-r--r--contrib/jemalloc/src/prof_log.c717
-rw-r--r--contrib/jemalloc/src/prof_recent.c600
-rw-r--r--contrib/jemalloc/src/prof_stats.c57
-rw-r--r--contrib/jemalloc/src/prof_sys.c669
-rw-r--r--contrib/jemalloc/src/psset.c385
-rw-r--r--contrib/jemalloc/src/rtree.c75
-rw-r--r--contrib/jemalloc/src/safety_check.c16
-rw-r--r--contrib/jemalloc/src/san.c208
-rw-r--r--contrib/jemalloc/src/san_bump.c104
-rw-r--r--contrib/jemalloc/src/sc.c17
-rw-r--r--contrib/jemalloc/src/sec.c422
-rw-r--r--contrib/jemalloc/src/stats.c794
-rw-r--r--contrib/jemalloc/src/sz.c52
-rw-r--r--contrib/jemalloc/src/tcache.c1137
-rw-r--r--contrib/jemalloc/src/thread_event.c343
-rw-r--r--contrib/jemalloc/src/ticker.c31
-rwxr-xr-xcontrib/jemalloc/src/ticker.py15
-rw-r--r--contrib/jemalloc/src/tsd.c75
-rw-r--r--contrib/jemalloc/src/witness.c44
-rw-r--r--contrib/jemalloc/src/zone.c469
-rw-r--r--contrib/kyua/doc/kyuafile.5.in12
-rw-r--r--contrib/kyua/drivers/report_junit_test.cpp3
-rw-r--r--contrib/kyua/engine/atf_list.cpp2
-rw-r--r--contrib/kyua/engine/requirements.cpp57
-rw-r--r--contrib/kyua/engine/requirements.hpp26
-rw-r--r--contrib/kyua/integration/cmd_report_junit_test.sh4
-rw-r--r--contrib/kyua/integration/cmd_report_test.sh1
-rw-r--r--contrib/kyua/model/metadata.cpp16
-rw-r--r--contrib/kyua/model/metadata.hpp4
-rw-r--r--contrib/kyua/model/metadata_test.cpp6
-rw-r--r--contrib/kyua/model/test_case_test.cpp2
-rw-r--r--contrib/kyua/model/test_program_test.cpp8
-rw-r--r--contrib/kyua/os/freebsd/main.cpp11
-rw-r--r--contrib/kyua/os/freebsd/reqs_checker_kmods.cpp50
-rw-r--r--contrib/kyua/os/freebsd/reqs_checker_kmods.hpp54
-rw-r--r--contrib/libucl/CMakeLists.txt314
-rw-r--r--contrib/libucl/ChangeLog.md103
-rw-r--r--contrib/libucl/FREEBSD-Xlist40
-rw-r--r--contrib/libucl/FREEBSD-upgrade39
-rw-r--r--contrib/libucl/Makefile.am81
-rw-r--r--contrib/libucl/Makefile.unix89
-rw-r--r--contrib/libucl/Makefile.w3292
-rw-r--r--contrib/libucl/README.md418
-rwxr-xr-xcontrib/libucl/autogen.sh2
-rw-r--r--contrib/libucl/configure.ac188
-rw-r--r--contrib/libucl/doc/Makefile.am9
-rw-r--r--contrib/libucl/doc/api.md506
-rw-r--r--contrib/libucl/doc/lua_api.md196
-rw-r--r--contrib/libucl/doc/pandoc.template12
-rw-r--r--contrib/libucl/examples/ucl_cpp.cc26
-rw-r--r--contrib/libucl/haskell/hucl.hs123
-rw-r--r--contrib/libucl/include/ucl.h4
-rw-r--r--contrib/libucl/libucl.pc11
-rw-r--r--contrib/libucl/libucl.pc.in11
-rw-r--r--contrib/libucl/lua/Makefile.am26
-rw-r--r--contrib/libucl/lua/libucl.rockspec.in26
-rw-r--r--contrib/libucl/lua/lua_ucl.c87
-rw-r--r--contrib/libucl/m4/ax_lua.m4664
-rw-r--r--contrib/libucl/m4/gcov.m489
-rw-r--r--contrib/libucl/python/MANIFEST.in5
-rw-r--r--contrib/libucl/python/setup.py75
-rw-r--r--contrib/libucl/python/src/uclmodule.c335
-rw-r--r--contrib/libucl/python/tests/compat.py8
-rw-r--r--contrib/libucl/python/tests/test_dump.py66
-rw-r--r--contrib/libucl/python/tests/test_example.py59
-rw-r--r--contrib/libucl/python/tests/test_load.py122
-rw-r--r--contrib/libucl/python/tests/test_validation.py50
-rw-r--r--contrib/libucl/python/ucl.pyi15
-rw-r--r--contrib/libucl/src/Makefile.am30
-rw-r--r--contrib/libucl/src/mum.h2
-rw-r--r--contrib/libucl/src/ucl_emitter.c80
-rw-r--r--contrib/libucl/src/ucl_emitter_streamline.c9
-rw-r--r--contrib/libucl/src/ucl_hash.c146
-rw-r--r--contrib/libucl/src/ucl_msgpack.c4
-rw-r--r--contrib/libucl/src/ucl_parser.c289
-rw-r--r--contrib/libucl/src/ucl_schema.c1
-rw-r--r--contrib/libucl/src/ucl_util.c87
-rw-r--r--contrib/libucl/stamp-h.in1
-rw-r--r--contrib/libucl/tests/.gitignore10
-rw-r--r--contrib/libucl/tests/Makefile.am45
-rw-r--r--contrib/libucl/tests/schema/definitions.json32
-rw-r--r--contrib/libucl/tests/schema/ref.json16
-rw-r--r--contrib/libucl/tests/schema/refRemote.json76
-rw-r--r--contrib/libucl/tests/test_speed.c2
-rw-r--r--contrib/libucl/tests/test_streamline.c43
-rw-r--r--contrib/libucl/uthash/utlist.h749
-rw-r--r--contrib/libucl/utils/CMakeLists.txt12
-rw-r--r--contrib/libucl/utils/Makefile.am23
-rw-r--r--contrib/libucl/utils/chargen.c128
-rw-r--r--contrib/libucl/utils/objdump.c185
-rw-r--r--contrib/libucl/utils/ucl-tool.c170
-rw-r--r--contrib/libxo/libxo/xo.h8
-rw-r--r--contrib/libxo/libxo/xo_encoder.h8
-rw-r--r--contrib/llvm-project/libcxx/include/__functional/binary_function.h5
-rw-r--r--contrib/llvm-project/libcxx/include/__functional/unary_function.h5
-rw-r--r--contrib/llvm-project/libcxx/include/__functional/weak_result_type.h2
-rw-r--r--contrib/llvm-project/libcxx/include/__memory/allocator_traits.h2
-rw-r--r--contrib/llvm-project/libcxx/include/__memory/uninitialized_algorithms.h3
-rw-r--r--contrib/llvm-project/libcxx/include/__type_traits/is_trivially_relocatable.h8
-rw-r--r--contrib/llvm-project/libcxx/include/tuple4
-rw-r--r--contrib/llvm-project/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp25
-rw-r--r--contrib/llvm-project/llvm/lib/Target/PowerPC/PPCMergeStringPool.cpp5
-rw-r--r--contrib/lua/Makefile2
-rw-r--r--contrib/lua/README2
-rw-r--r--contrib/lua/doc/contents.html14
-rw-r--r--contrib/lua/doc/lua.16
-rw-r--r--contrib/lua/doc/lua.css1
-rw-r--r--contrib/lua/doc/manual.html50
-rw-r--r--contrib/lua/doc/readme.html48
-rw-r--r--contrib/lua/src/lapi.c6
-rw-r--r--contrib/lua/src/lauxlib.c28
-rw-r--r--contrib/lua/src/lcode.c38
-rw-r--r--contrib/lua/src/lcode.h3
-rw-r--r--contrib/lua/src/ldebug.c231
-rw-r--r--contrib/lua/src/ldebug.h1
-rw-r--r--contrib/lua/src/ldo.c27
-rw-r--r--contrib/lua/src/ldo.h2
-rw-r--r--contrib/lua/src/lgc.c20
-rw-r--r--contrib/lua/src/liolib.c27
-rw-r--r--contrib/lua/src/lmathlib.c31
-rw-r--r--contrib/lua/src/loadlib.c9
-rw-r--r--contrib/lua/src/lobject.c2
-rw-r--r--contrib/lua/src/lobject.h18
-rw-r--r--contrib/lua/src/lopcodes.h8
-rw-r--r--contrib/lua/src/loslib.c2
-rw-r--r--contrib/lua/src/lparser.c19
-rw-r--r--contrib/lua/src/lstate.c11
-rw-r--r--contrib/lua/src/lstate.h3
-rw-r--r--contrib/lua/src/lstring.c13
-rw-r--r--contrib/lua/src/ltable.c39
-rw-r--r--contrib/lua/src/ltable.h2
-rw-r--r--contrib/lua/src/ltm.h5
-rw-r--r--contrib/lua/src/lua.c28
-rw-r--r--contrib/lua/src/lua.h8
-rw-r--r--contrib/lua/src/luaconf.h9
-rw-r--r--contrib/lua/src/lundump.c4
-rw-r--r--contrib/lua/src/lundump.h3
-rw-r--r--contrib/lua/src/lvm.c81
-rw-r--r--contrib/lyaml/ext/yaml/yaml.c4
-rw-r--r--contrib/mandoc/Makefile2
-rw-r--r--contrib/mandoc/TODO11
-rw-r--r--contrib/mandoc/catman.8238
-rw-r--r--contrib/mandoc/catman.c231
-rw-r--r--contrib/mandoc/dba.c7
-rw-r--r--contrib/mandoc/gmdiff4
-rw-r--r--contrib/mandoc/main.c52
-rw-r--r--contrib/mandoc/man.7151
-rw-r--r--contrib/mandoc/man.c3
-rw-r--r--contrib/mandoc/man.options.140
-rw-r--r--contrib/mandoc/man_html.c19
-rw-r--r--contrib/mandoc/man_term.c133
-rw-r--r--contrib/mandoc/man_validate.c18
-rw-r--r--contrib/mandoc/mandoc.143
-rw-r--r--contrib/mandoc/mandoc.css4
-rw-r--r--contrib/mandoc/mandocd.826
-rw-r--r--contrib/mandoc/mandocd.c34
-rw-r--r--contrib/mandoc/manpath.c11
-rw-r--r--contrib/mandoc/mdoc.735
-rw-r--r--contrib/mandoc/mdoc_html.c15
-rw-r--r--contrib/mandoc/mdoc_man.c5
-rw-r--r--contrib/mandoc/mdoc_markdown.c10
-rw-r--r--contrib/mandoc/mdoc_term.c122
-rw-r--r--contrib/mandoc/mdoc_validate.c10
-rw-r--r--contrib/mandoc/out.c63
-rw-r--r--contrib/mandoc/out.h24
-rw-r--r--contrib/mandoc/roff.7195
-rw-r--r--contrib/mandoc/roff_term.c32
-rw-r--r--contrib/mandoc/tbl.h16
-rw-r--r--contrib/mandoc/tbl_html.c34
-rw-r--r--contrib/mandoc/tbl_layout.c10
-rw-r--r--contrib/mandoc/tbl_term.c287
-rw-r--r--contrib/mandoc/term.c201
-rw-r--r--contrib/mandoc/term.h38
-rw-r--r--contrib/mandoc/term_ascii.c91
-rw-r--r--contrib/mandoc/term_ps.c18
-rw-r--r--contrib/mandoc/term_tab.c27
-rw-r--r--contrib/ofed/libcxgb4/dev.c9
-rw-r--r--contrib/ofed/libcxgb4/libcxgb4.h5
-rw-r--r--contrib/ofed/libcxgb4/t4_chip_type.h10
-rw-r--r--contrib/ofed/libcxgb4/t4_pci_id_tbl.h27
-rw-r--r--contrib/one-true-awk/FIXES8
-rw-r--r--contrib/one-true-awk/main.c2
-rw-r--r--contrib/one-true-awk/run.c4
-rw-r--r--contrib/tcpdump/print-pfsync.c26
-rw-r--r--contrib/tnftp/src/ftp.15
-rw-r--r--contrib/tzcode/Makefile32
-rw-r--r--contrib/tzcode/NEWS109
-rw-r--r--contrib/tzcode/asctime.c124
-rw-r--r--contrib/tzcode/date.1122
-rw-r--r--contrib/tzcode/difftime.c2
-rw-r--r--contrib/tzcode/localtime.c873
-rw-r--r--contrib/tzcode/newctime.3152
-rw-r--r--contrib/tzcode/newstrftime.386
-rw-r--r--contrib/tzcode/newtzset.324
-rw-r--r--contrib/tzcode/private.h121
-rw-r--r--contrib/tzcode/strftime.c83
-rw-r--r--contrib/tzcode/theory.html40
-rw-r--r--contrib/tzcode/tz-link.html45
-rw-r--r--contrib/tzcode/tzfile.570
-rw-r--r--contrib/tzcode/tzfile.h2
-rw-r--r--contrib/tzcode/tzselect.826
-rw-r--r--contrib/tzcode/version2
-rw-r--r--contrib/tzcode/zdump.814
-rw-r--r--contrib/tzcode/zdump.c62
-rw-r--r--contrib/tzcode/zic.827
-rw-r--r--contrib/tzcode/zic.c142
-rwxr-xr-xcontrib/unbound/config.guess17
-rw-r--r--contrib/unbound/config.h.in360
-rwxr-xr-xcontrib/unbound/config.sub28
-rwxr-xr-xcontrib/unbound/configure4435
-rw-r--r--contrib/unbound/configure.ac5
-rw-r--r--contrib/unbound/doc/README2
-rw-r--r--contrib/unbound/doc/example.conf300
-rw-r--r--contrib/unbound/doc/example.conf.in2
-rw-r--r--contrib/unbound/doc/libunbound.34
-rw-r--r--contrib/unbound/doc/libunbound.3.in4
-rw-r--r--contrib/unbound/doc/unbound-anchor.814
-rw-r--r--contrib/unbound/doc/unbound-anchor.8.in2
-rw-r--r--contrib/unbound/doc/unbound-checkconf.88
-rw-r--r--contrib/unbound/doc/unbound-checkconf.8.in2
-rw-r--r--contrib/unbound/doc/unbound-control.8343
-rw-r--r--contrib/unbound/doc/unbound-control.8.in2
-rw-r--r--contrib/unbound/doc/unbound-host.14
-rw-r--r--contrib/unbound/doc/unbound-host.1.in2
-rw-r--r--contrib/unbound/doc/unbound.88
-rw-r--r--contrib/unbound/doc/unbound.8.in4
-rw-r--r--contrib/unbound/doc/unbound.conf.5741
-rw-r--r--contrib/unbound/doc/unbound.conf.5.in2
-rw-r--r--contrib/unbound/edns-subnet/subnetmod.c152
-rw-r--r--contrib/unbound/edns-subnet/subnetmod.h4
-rw-r--r--contrib/unbound/ltmain.sh1555
-rw-r--r--[-rwxr-xr-x]contrib/unbound/smallapp/unbound-control-setup.sh227
1197 files changed, 88798 insertions, 25716 deletions
diff --git a/contrib/bc/LICENSE.md b/contrib/bc/LICENSE.md
index c8f6758e6d4b..e5d44dee6dab 100644
--- a/contrib/bc/LICENSE.md
+++ b/contrib/bc/LICENSE.md
@@ -1,6 +1,6 @@
# License
-Copyright (c) 2018-2024 Gavin D. Howard <gavin@gavinhoward.com>
+Copyright (c) 2018-2025 Gavin D. Howard <gavin@gavinhoward.com>
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
@@ -60,7 +60,7 @@ The files `src/rand.c` and `include/rand.h` are under the following copyrights
and license:
Copyright (c) 2014-2017 Melissa O'Neill and PCG Project contributors<br>
-Copyright (c) 2018-2024 Gavin D. Howard <gavin@gavinhoward.com>
+Copyright (c) 2018-2025 Gavin D. Howard <gavin@gavinhoward.com>
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
diff --git a/contrib/bc/MAINTENANCE-TERMS.md b/contrib/bc/MAINTENANCE-TERMS.md
new file mode 100644
index 000000000000..ef24202cc6c2
--- /dev/null
+++ b/contrib/bc/MAINTENANCE-TERMS.md
@@ -0,0 +1,80 @@
+# Maintenance Terms
+
+> Last Updated: 27 June 2025
+
+The code, text, and other materials in this repository are provided as-is under
+the terms of the repository's [`LICENSE.md`][0] file, as a gift to the commons
+and the common good. In providing this software as-is, its author(s) admit no
+further obligations from anyone using the software for any reason, particularly
+with respect to:
+
+* Releases,
+* Response time,
+* Change review and integration,
+* Disclosure schedules,
+* Discretionary, proprietary or otherwise secretive communications, and
+* Any other non-contractual obligations or conventions, regardless of their
+ presumed urgency or severity.
+
+Should anyone wish to make a contract with me (Gavin Howard) to ensure that work
+he or she deems critical gets done, the terms are as follows:
+
+* Compute time will be charged at \$25/hr.
+* My time will be charged at \$100/hr.
+* All issues deemed critical by either me or the requester, that also change the
+ source code (anything in `gen`, `include`, or `src`) will require at least two
+ weeks of fuzzing without error.
+ * If errors are found, those hours will still be charged on top of the final
+ two weeks.
+* All changes will require running the [release script][2] on Linux (GCC), Linux
+ (Clang), FreeBSD, OpenBSD, macOS, Windows.
+* Any hours spent on bugs or code that have been, or are suspected to have been,
+ generated by "AI" will be charged double rates.
+
+Compute time includes, but is not limited to:
+
+* Fuzzing.
+* Running my [release script][2].
+* Running tests and my [release script][2] on macOS.
+
+My time includes, but is not limited to:
+
+* Code review.
+* Reading bug reports.
+* Design.
+* Coding.
+* Any compute time that interferes with my ability to do any other work:
+ * Fuzzing makes my computer unusable, so fuzzing for the 8-12 hours of the
+ day that I could be working will be charged at \$100/hr.
+ * Same with running my [release script][2] because I run two instances on my
+ machine and two in VMs at the same time.
+ * Running my [release script][2] or any other compute time on Windows
+ because Windows blocks me from doing my main work on Linux.
+ * Any other instances of blocking compute time.
+
+All amounts will be billed by, and paid to, [Yzena, LLC][2]. Invoices will be
+provided, including line items for what each hour was spent on.
+
+It is suggested that the following amounts be budgeted:
+
+* At least \$3000 for a non-critical issue or change.
+
+ The release script takes about 10 hours, and I would need to run it once on
+ Linux (and others at the same time) and once on Windows, which is 20 hours.
+ Most of that won't be at the \$100/hr rate, but some probably will be. Then
+ an extra \$1000 for other work.
+
+* At least \$15,000 for a critical issue or change.
+
+ The \$3000 above is the start, which leaves \$12,000. Fuzzing is expected to
+ cost \$11,400 (6 days a week, 8 hours a day, at \$100, the rest at \$25), and
+ rounded up to \$12,000 for good measure.
+
+---
+
+This document is inspired by [Mike Hoye's Maintenance Terms][1].
+
+[0]: LICENSE.md
+[1]: https://github.com/mhoye/maintenance-terms
+[2]: scripts/release.sh
+[3]: https://yzena.com/
diff --git a/contrib/bc/Makefile.in b/contrib/bc/Makefile.in
index c63dc242e79a..f8b120c1328e 100644
--- a/contrib/bc/Makefile.in
+++ b/contrib/bc/Makefile.in
@@ -1,7 +1,7 @@
#
# SPDX-License-Identifier: BSD-2-Clause
#
-# Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+# Copyright (c) 2018-2025 Gavin D. Howard and contributors.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
@@ -38,6 +38,8 @@ GENDIR = $(ROOTDIR)/gen
BUILDDIR = %%BUILDDIR%%
+VERSION = %%VERSION%%
+
SRC = %%SRC%%
OBJ = %%OBJ%%
GCDA = %%GCDA%%
@@ -99,17 +101,16 @@ BC_FUZZER_C = $(BIN)/$(BC)_fuzzer_C
DC_FUZZER = $(BIN)/$(DC)_fuzzer_c
DC_FUZZER_C = $(BIN)/$(DC)_fuzzer_C
-BC_TEST_OUTPUTS = tests/bc_outputs
BC_FUZZ_OUTPUTS = tests/fuzzing/bc_outputs1 tests/fuzzing/bc_outputs2 tests/fuzzing/bc_outputs3
-DC_TEST_OUTPUTS = tests/dc_outputs
DC_FUZZ_OUTPUTS = tests/fuzzing/dc_outputs
LIB = libbcl
LIB_NAME = $(LIB).a
LIBBC = $(BIN)/$(LIB_NAME)
BCL = bcl
-BCL_TEST = $(BIN)/$(BCL)
-BCL_TEST_C = $(TESTSDIR)/$(BCL).c
+
+GENERATE_TESTS = %%GENERATE_TESTS%%
+PROBLEMATIC_TESTS = %%PROBLEMATIC_TESTS%%
MANUALS = manuals
BC_MANPAGE_NAME = $(EXEC_PREFIX)$(BC)$(EXEC_SUFFIX).1
@@ -152,11 +153,11 @@ BC_ENABLE_EXTRA_MATH = %%EXTRA_MATH%%
BC_ENABLE_NLS = %%NLS%%
BC_EXCLUDE_EXTRA_MATH = %%EXCLUDE_EXTRA_MATH%%
-BC_ENABLE_AFL = %%FUZZ%%
-BC_ENABLE_OSSFUZZ = %%OSSFUZZ%%
-BC_ENABLE_MEMCHECK = %%MEMCHECK%%
+BC_ENABLE_AFL = 0
+BC_ENABLE_OSSFUZZ = 0
+BC_ENABLE_MEMCHECK = 0
-LIB_FUZZING_ENGINE = %%LIB_FUZZING_ENGINE%%
+LIB_FUZZING_ENGINE = 0
BC_DEFAULT_BANNER = %%BC_DEFAULT_BANNER%%
BC_DEFAULT_SIGINT_RESET = %%BC_DEFAULT_SIGINT_RESET%%
@@ -189,10 +190,6 @@ KARATSUBA = $(SCRIPTSDIR)/karatsuba.py
LOCALE_INSTALL = $(SCRIPTSDIR)/locale_install.sh
LOCALE_UNINSTALL = $(SCRIPTSDIR)/locale_uninstall.sh
-VALGRIND_ARGS = --error-exitcode=100 --leak-check=full --show-leak-kinds=all --errors-for-leak-kinds=all
-
-TEST_STARS = ***********************************************************************
-
BC_NUM_KARATSUBA_LEN = %%KARATSUBA_LEN%%
BC_DEFS0 = -DBC_DEFAULT_BANNER=$(BC_DEFAULT_BANNER)
@@ -218,7 +215,7 @@ CPPFLAGS6 = $(CPPFLAGS5) -DBC_ENABLE_NLS=$(BC_ENABLE_NLS)
CPPFLAGS7 = $(CPPFLAGS6) -D$(BC_ENABLE_EXTRA_MATH_NAME)=$(BC_ENABLE_EXTRA_MATH)
CPPFLAGS8 = $(CPPFLAGS7) -DBC_ENABLE_HISTORY=$(BC_ENABLE_HISTORY) -DBC_ENABLE_LIBRARY=$(BC_ENABLE_LIBRARY)
CPPFLAGS9 = $(CPPFLAGS8) -DBC_ENABLE_MEMCHECK=$(BC_ENABLE_MEMCHECK) -DBC_ENABLE_AFL=$(BC_ENABLE_AFL)
-CPPFLAGS = $(CPPFLAGS9) -DBC_ENABLE_OSSFUZZ=$(BC_ENABLE_OSSFUZZ)
+CPPFLAGS = -DVERSION=$(VERSION) $(CPPFLAGS9) -DBC_ENABLE_OSSFUZZ=$(BC_ENABLE_OSSFUZZ)
CFLAGS = $(CPPFLAGS) $(BC_DEFS) $(DC_DEFS) %%CPPFLAGS%% %%CFLAGS%%
LDFLAGS = %%LDFLAGS%%
@@ -293,236 +290,10 @@ help:
@printf ' check alias for `make test`\n'
@printf ' clean removes all build files\n'
@printf ' clean_config removes all build files as well as the generated Makefile\n'
- @printf ' clean_tests removes all build files, the generated Makefile,\n'
- @printf ' and generated tests\n'
@printf ' install installs binaries to "%s%s"\n' "$(DESTDIR)" "$(BINDIR)"
@printf ' and (if enabled) manpages to "%s%s"\n' "$(DESTDIR)" "$(MAN1DIR)"
- @printf ' karatsuba runs the karatsuba script (requires Python 3)\n'
- @printf ' karatsuba_test runs the karatsuba script while running tests\n'
- @printf ' (requires Python 3)\n'
@printf ' uninstall uninstalls binaries from "%s%s"\n' "$(DESTDIR)" "$(BINDIR)"
@printf ' and (if enabled) manpages from "%s%s"\n' "$(DESTDIR)" "$(MAN1DIR)"
- @printf ' test runs the test suite\n'
- @printf ' test_bc runs the bc test suite, if bc has been built\n'
- @printf ' test_dc runs the dc test suite, if dc has been built\n'
- @printf ' time_test runs the test suite, displaying times for some things\n'
- @printf ' time_test_bc runs the bc test suite, displaying times for some things\n'
- @printf ' time_test_dc runs the dc test suite, displaying times for some things\n'
- @printf ' timeconst runs the test on the Linux timeconst.bc script,\n'
- @printf ' if it exists and bc has been built\n'
-
-run_all_tests: bc_all_tests timeconst_all_tests dc_all_tests
-
-run_all_tests_np: bc_all_tests_np timeconst_all_tests dc_all_tests_np
-
-bc_all_tests:
- %%BC_ALL_TESTS%%
-
-bc_all_tests_np:
- %%BC_ALL_TESTS_NP%%
-
-timeconst_all_tests:
- %%TIMECONST_ALL_TESTS%%
-
-dc_all_tests:
- %%DC_ALL_TESTS%%
-
-dc_all_tests_np:
- %%DC_ALL_TESTS_NP%%
-
-history_all_tests:
- %%HISTORY_TESTS%%
-
-check: test
-
-test: %%TESTS%%
-
-test_bc: test_bc_header test_bc_tests test_bc_scripts test_bc_errors test_bc_stdin test_bc_read test_bc_other
- @printf '\nAll bc tests passed.\n\n$(TEST_STARS)\n'
-
-test_bc_tests:%%BC_TESTS%%
-
-test_bc_scripts:%%BC_SCRIPT_TESTS%%
-
-test_bc_stdin:
- @export BC_TEST_OUTPUT_DIR="$(BUILDDIR)/tests"; sh $(TESTSDIR)/stdin.sh bc %%BC_TEST_EXEC%%
-
-test_bc_read:
- @export BC_TEST_OUTPUT_DIR="$(BUILDDIR)/tests"; sh $(TESTSDIR)/read.sh bc %%BC_TEST_EXEC%%
-
-test_bc_errors: test_bc_error_lines%%BC_ERROR_TESTS%%
-
-test_bc_error_lines:
- @export BC_TEST_OUTPUT_DIR="$(BUILDDIR)/tests"; sh $(TESTSDIR)/errors.sh bc %%BC_TEST_EXEC%%
-
-test_bc_other:
- @export BC_TEST_OUTPUT_DIR="$(BUILDDIR)/tests"; sh $(TESTSDIR)/other.sh bc $(BC_ENABLE_EXTRA_MATH) %%BC_TEST_EXEC%%
-
-test_bc_header:
- @printf '$(TEST_STARS)\n\nRunning bc tests...\n\n'
-
-test_dc: test_dc_header test_dc_tests test_dc_scripts test_dc_errors test_dc_stdin test_dc_read test_dc_other
- @printf '\nAll dc tests passed.\n\n$(TEST_STARS)\n'
-
-test_dc_tests:%%DC_TESTS%%
-
-test_dc_scripts:%%DC_SCRIPT_TESTS%%
-
-test_dc_stdin:
- @export BC_TEST_OUTPUT_DIR="$(BUILDDIR)/tests"; sh $(TESTSDIR)/stdin.sh dc %%DC_TEST_EXEC%%
-
-test_dc_read:
- @export BC_TEST_OUTPUT_DIR="$(BUILDDIR)/tests"; sh $(TESTSDIR)/read.sh dc %%DC_TEST_EXEC%%
-
-test_dc_errors: test_dc_error_lines%%DC_ERROR_TESTS%%
-
-test_dc_error_lines:
- @export BC_TEST_OUTPUT_DIR="$(BUILDDIR)/tests"; sh $(TESTSDIR)/errors.sh dc %%DC_TEST_EXEC%%
-
-test_dc_other:
- @export BC_TEST_OUTPUT_DIR="$(BUILDDIR)/tests"; sh $(TESTSDIR)/other.sh dc $(BC_ENABLE_EXTRA_MATH) %%DC_TEST_EXEC%%
-
-test_dc_header:
- @printf '$(TEST_STARS)\n\nRunning dc tests...\n\n'
-
-timeconst:
- %%TIMECONST%%
-
-test_history: test_history_header test_bc_history test_dc_history
- @printf '\nAll history tests passed.\n\n$(TEST_STARS)\n'
-
-test_bc_history:%%BC_HISTORY_TEST_PREREQS%%
-
-test_bc_history_all: test_bc_history0 test_bc_history1 test_bc_history2 test_bc_history3 test_bc_history4 test_bc_history5 test_bc_history6 test_bc_history7 test_bc_history8 test_bc_history9 test_bc_history10 test_bc_history11 test_bc_history12 test_bc_history13 test_bc_history14 test_bc_history15 test_bc_history16 test_bc_history17 test_bc_history18 test_bc_history19 test_bc_history20 test_bc_history21
-
-test_bc_history_skip:
- @printf 'No bc history tests to run\n'
-
-test_bc_history0:
- @sh $(TESTSDIR)/history.sh bc 0 %%BC_TEST_EXEC%%
-
-test_bc_history1:
- @sh $(TESTSDIR)/history.sh bc 1 %%BC_TEST_EXEC%%
-
-test_bc_history2:
- @sh $(TESTSDIR)/history.sh bc 2 %%BC_TEST_EXEC%%
-
-test_bc_history3:
- @sh $(TESTSDIR)/history.sh bc 3 %%BC_TEST_EXEC%%
-
-test_bc_history4:
- @sh $(TESTSDIR)/history.sh bc 4 %%BC_TEST_EXEC%%
-
-test_bc_history5:
- @sh $(TESTSDIR)/history.sh bc 5 %%BC_TEST_EXEC%%
-
-test_bc_history6:
- @sh $(TESTSDIR)/history.sh bc 6 %%BC_TEST_EXEC%%
-
-test_bc_history7:
- @sh $(TESTSDIR)/history.sh bc 7 %%BC_TEST_EXEC%%
-
-test_bc_history8:
- @sh $(TESTSDIR)/history.sh bc 8 %%BC_TEST_EXEC%%
-
-test_bc_history9:
- @sh $(TESTSDIR)/history.sh bc 9 %%BC_TEST_EXEC%%
-
-test_bc_history10:
- @sh $(TESTSDIR)/history.sh bc 10 %%BC_TEST_EXEC%%
-
-test_bc_history11:
- @sh $(TESTSDIR)/history.sh bc 11 %%BC_TEST_EXEC%%
-
-test_bc_history12:
- @sh $(TESTSDIR)/history.sh bc 12 %%BC_TEST_EXEC%%
-
-test_bc_history13:
- @sh $(TESTSDIR)/history.sh bc 13 %%BC_TEST_EXEC%%
-
-test_bc_history14:
- @sh $(TESTSDIR)/history.sh bc 14 %%BC_TEST_EXEC%%
-
-test_bc_history15:
- @sh $(TESTSDIR)/history.sh bc 15 %%BC_TEST_EXEC%%
-
-test_bc_history16:
- @sh $(TESTSDIR)/history.sh bc 16 %%BC_TEST_EXEC%%
-
-test_bc_history17:
- @sh $(TESTSDIR)/history.sh bc 17 %%BC_TEST_EXEC%%
-
-test_bc_history18:
- @sh $(TESTSDIR)/history.sh bc 18 %%BC_TEST_EXEC%%
-
-test_bc_history19:
- @sh $(TESTSDIR)/history.sh bc 19 %%BC_TEST_EXEC%%
-
-test_bc_history20:
- @sh $(TESTSDIR)/history.sh bc 20 %%BC_TEST_EXEC%%
-
-test_bc_history21:
- @sh $(TESTSDIR)/history.sh bc 21 %%BC_TEST_EXEC%%
-
-test_dc_history:%%DC_HISTORY_TEST_PREREQS%%
-
-test_dc_history_all: test_dc_history0 test_dc_history1 test_dc_history2 test_dc_history3 test_dc_history4 test_dc_history5 test_dc_history6 test_dc_history7 test_dc_history8 test_dc_history9 test_dc_history10
-
-test_dc_history_skip:
- @printf 'No dc history tests to run\n'
-
-test_dc_history0:
- @sh $(TESTSDIR)/history.sh dc 0 %%DC_TEST_EXEC%%
-
-test_dc_history1:
- @sh $(TESTSDIR)/history.sh dc 1 %%DC_TEST_EXEC%%
-
-test_dc_history2:
- @sh $(TESTSDIR)/history.sh dc 2 %%DC_TEST_EXEC%%
-
-test_dc_history3:
- @sh $(TESTSDIR)/history.sh dc 3 %%DC_TEST_EXEC%%
-
-test_dc_history4:
- @sh $(TESTSDIR)/history.sh dc 4 %%DC_TEST_EXEC%%
-
-test_dc_history5:
- @sh $(TESTSDIR)/history.sh dc 5 %%DC_TEST_EXEC%%
-
-test_dc_history6:
- @sh $(TESTSDIR)/history.sh dc 6 %%DC_TEST_EXEC%%
-
-test_dc_history7:
- @sh $(TESTSDIR)/history.sh dc 7 %%DC_TEST_EXEC%%
-
-test_dc_history8:
- @sh $(TESTSDIR)/history.sh dc 8 %%DC_TEST_EXEC%%
-
-test_dc_history9:
- @sh $(TESTSDIR)/history.sh dc 9 %%DC_TEST_EXEC%%
-
-test_dc_history10:
- @sh $(TESTSDIR)/history.sh dc 10 %%DC_TEST_EXEC%%
-
-test_history_header:
- @printf '$(TEST_STARS)\n\nRunning history tests...\n\n'
-
-library_test: $(LIBBC)
- $(CC) $(CFLAGS) -lpthread $(BCL_TEST_C) $(LIBBC) -o $(BCL_TEST)
-
-test_library: library_test
- %%BCL_TEST_EXEC%%
-
-karatsuba:
- %%KARATSUBA%%
-
-karatsuba_test:
- %%KARATSUBA_TEST%%
-
-coverage_output:
- %%COVERAGE_OUTPUT%%
-
-coverage:%%COVERAGE_PREREQS%%
manpages:
$(MANPAGE) bc
@@ -559,37 +330,25 @@ clean_config: clean clean_benchmarks
@$(RM) -f compile_commands.json
@$(RM) -f $(BCL_PC)
-clean_coverage:
- @printf 'Cleaning coverage files...\n'
- @$(RM) -f *.gcov
- @$(RM) -f *.html *.css
- @$(RM) -f *.gcda *.gcno
- @$(RM) -f *.profraw
- @$(RM) -f $(GCDA) $(GCNO)
- @$(RM) -f $(BC_GCDA) $(BC_GCNO)
- @$(RM) -f $(DC_GCDA) $(DC_GCNO)
- @$(RM) -f $(HISTORY_GCDA) $(HISTORY_GCNO)
- @$(RM) -f $(RAND_GCDA) $(RAND_GCNO)
- @$(RM) -f $(BC_LIB_GCDA) $(BC_LIB_GCNO)
- @$(RM) -f $(BC_LIB2_GCDA) $(BC_LIB2_GCNO)
- @$(RM) -f $(BC_HELP_GCDA) $(BC_HELP_GCNO)
- @$(RM) -f $(DC_HELP_GCDA) $(DC_HELP_GCNO)
-
-clean_tests: clean clean_config clean_coverage
+test:
+ @if [ $(BC_ENABLED) -ne 0 ]; then $(TESTSDIR)/all.sh -n bc $(BC_ENABLE_EXTRA_MATH) 1 $(GENERATE_TESTS) $(PROBLEMATIC_TESTS) $(BC_EXEC); fi
+ @if [ $(DC_ENABLED) -ne 0 ]; then $(TESTSDIR)/all.sh -n dc $(BC_ENABLE_EXTRA_MATH) 1 $(GENERATE_TESTS) $(PROBLEMATIC_TESTS) $(DC_EXEC); fi
+
+clean_tests: clean clean_config
@printf 'Cleaning test files...\n'
@$(RM) -fr $(BC_TEST_OUTPUTS) $(DC_TEST_OUTPUTS)
@$(RM) -fr $(BC_FUZZ_OUTPUTS) $(DC_FUZZ_OUTPUTS)
- @$(RM) -f $(TESTSDIR)/bc/parse.txt $(TESTSDIR)/bc/parse_results.txt
- @$(RM) -f $(TESTSDIR)/bc/print.txt $(TESTSDIR)/bc/print_results.txt
+ @$(RM) -f $(TESTSDIR)/bc/parse_*.txt $(TESTSDIR)/bc/parse_*_results.txt
+ @$(RM) -f $(TESTSDIR)/bc/print_*.txt $(TESTSDIR)/bc/print_*_results.txt
@$(RM) -f $(TESTSDIR)/bc/bessel.txt $(TESTSDIR)/bc/bessel_results.txt
@$(RM) -f $(TESTSDIR)/bc/strings2.txt $(TESTSDIR)/bc/strings2_results.txt
@$(RM) -f $(TESTSDIR)/bc/scripts/bessel.txt
@$(RM) -f $(TESTSDIR)/bc/scripts/parse.txt
@$(RM) -f $(TESTSDIR)/bc/scripts/print.txt
- @$(RM) -f $(TESTSDIR)/bc/scripts/add.txt
- @$(RM) -f $(TESTSDIR)/bc/scripts/divide.txt
- @$(RM) -f $(TESTSDIR)/bc/scripts/multiply.txt
- @$(RM) -f $(TESTSDIR)/bc/scripts/subtract.txt
+ @$(RM) -f $(TESTSDIR)/bc/scripts/add_*.txt
+ @$(RM) -f $(TESTSDIR)/bc/scripts/divide_*.txt
+ @$(RM) -f $(TESTSDIR)/bc/scripts/multiply_*.txt
+ @$(RM) -f $(TESTSDIR)/bc/scripts/subtract_*.txt
@$(RM) -f $(TESTSDIR)/bc/scripts/strings2.txt
@$(RM) -f $(TESTSDIR)/dc/scripts/prime.txt
@$(RM) -f .log_*.txt
diff --git a/contrib/bc/NEWS.md b/contrib/bc/NEWS.md
index e3b1f9ecb7bc..72a276b8c015 100644
--- a/contrib/bc/NEWS.md
+++ b/contrib/bc/NEWS.md
@@ -1,5 +1,27 @@
# News
+## 7.1.0
+
+This is an ***UNTESTED*** release. If you would like testing, see the
+[maintenance terms][23].
+
+This fixes a few bugs:
+
+* Improper response to double `SIGINT` with editline.
+* Not letting `libedit` handle terminal size changes.
+* A `dc` crash from improperly handling an error.
+* A duplicate check for reference arrays.
+* Build failures with GCC 15.
+
+It also has a performance increase in the `band()` function and others in the
+math library.
+
+## 7.0.3
+
+This is a production release that fixes build warnings on the musl libc.
+
+Other users do ***NOT*** need to upgrade.
+
## 7.0.2
This is a production release that fixes `Ctrl+d` on FreeBSD and Linux when using
@@ -1588,3 +1610,4 @@ not thoroughly tested.
[20]: https://github.com/apjanke/ronn-ng
[21]: https://pandoc.org/
[22]: ./scripts/locale_uninstall.sh
+[23]: ./MAINTENANCE-TERMS.md
diff --git a/contrib/bc/NOTICE.md b/contrib/bc/NOTICE.md
index 35536b2c27d7..c3c211a92309 100644
--- a/contrib/bc/NOTICE.md
+++ b/contrib/bc/NOTICE.md
@@ -1,6 +1,6 @@
# Notice
-Copyright 2018-2024 Gavin D. Howard and contributors.
+Copyright 2018-2025 Gavin D. Howard and contributors.
## Contributors
diff --git a/contrib/bc/README.md b/contrib/bc/README.md
index 696e6186b8bd..a203386f3b65 100644
--- a/contrib/bc/README.md
+++ b/contrib/bc/README.md
@@ -1,9 +1,7 @@
# `bc`
-***WARNING: New user registration for <https://git.gavinhoward.com/> is disabled
-because of spam. If you need to report a bug with `bc`, email gavin at this site
-minus the `git.` part for an account, and I will create one for you. Or you can
-report an issue at [GitHub][29].***
+***WARNING: This project has moved back to GitHub temporarily; self-hosted Git
+forges are not working for me, so I am trying to replace them.***
***WARNING: This project has moved to [https://git.gavinhoward.com/][20] for
[these reasons][21], though GitHub will remain a mirror.***
@@ -282,6 +280,12 @@ The easiest way to run this script is with `make karatsuba`.
If desired, maintainers can also skip running this script because there is a
sane default for the Karatsuba number.
+##### `timeconst.bc`
+
+The test suite will print a warning in normal usage. The warning is about a
+missing `timeconst.bc`. This file [comes from][37] the [Linux kernel][38], which
+has an incompatible license. The warning can be ignored.
+
## Status
This `bc` is robust.
@@ -432,6 +436,8 @@ Other projects based on this bc are:
* [macOS `bc`][35]. Any bugs in that `bc` should be reported to me, but do
expect bugs because the version is old.
* [Android Open Source `bc`][32]. Any bugs in that `bc` can be reported here.
+* [A Fedora package][36]. If this package does not have any patches, you can
+ report bugs to me.
This is a non-comprehensive list of Linux distros that use this `bc` as the
system `bc`:
@@ -469,12 +475,16 @@ Files:
.gitignore The git ignore file (maintainer use only).
.gitattributes The git attributes file (maintainer use only).
bcl.pc.in A template pkg-config file for bcl.
+ build.gaml The GAML file with options for building under Rig.
+ build.pkg.rig The Rig build package file.
+ build.rig The Rig build script.
configure A symlink to configure.sh to make packaging easier.
configure.sh The configure script.
LICENSE.md A Markdown form of the BSD 2-clause License.
Makefile.in The Makefile template.
NEWS.md The changelog.
NOTICE.md List of contributors and copyright owners.
+ VERSION.txt A file containing the version.
Folders:
@@ -516,3 +526,6 @@ Folders:
[33]: https://github.com/gentoo/gentoo/blob/master/app-alternatives/bc/bc-0.ebuild#L8
[34]: https://www.linuxfromscratch.org/lfs/view/stable/chapter08/bc.html
[35]: https://github.com/apple-oss-distributions/bc/tree/main/bc
+[36]: https://copr.fedorainfracloud.org/coprs/tkbcopr/bc-gh/
+[37]: https://github.com/torvalds/linux/blob/master/kernel/time/timeconst.bc
+[38]: https://github.com/torvalds/linux
diff --git a/contrib/bc/VERSION.txt b/contrib/bc/VERSION.txt
new file mode 100644
index 000000000000..a3fcc7121bba
--- /dev/null
+++ b/contrib/bc/VERSION.txt
@@ -0,0 +1 @@
+7.1.0
diff --git a/contrib/bc/build.gaml b/contrib/bc/build.gaml
new file mode 100644
index 000000000000..b8ce873835b5
--- /dev/null
+++ b/contrib/bc/build.gaml
@@ -0,0 +1,402 @@
+/*
+ * *****************************************************************************
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * *****************************************************************************
+ *
+ * The build options file.
+ *
+ */
+
+project: @com.gavinhoward.bc
+
+language: @C11
+
+version: {
+ min: @24.04.05
+}
+
+mode: {
+ language: @iterative
+ stampers: @metadata
+ dependencies: @dynamic
+}
+
+default_target: @all
+
+presets: {
+ debug: {
+ debug: true
+ optimization: "0"
+ memcheck: true
+ devtools: true
+ strip: false
+ }
+ release: {
+ optimization: "3"
+ lto: true
+ }
+ // This is the predefined build for BSDs.
+ bsd: {
+ optimization: "3"
+ history: @editline
+ generated_tests: false
+ install_manpages: false
+ install_locales: @system
+ strip: true
+ bc_default_banner: false
+ bc_default_sigint_reset: true
+ dc_default_sigint_reset: true
+ bc_default_tty_mode: true
+ dc_default_tty_mode: false
+ bc_default_prompt: @off
+ dc_default_prompt: @off
+ bc_default_expr_exit: true
+ dc_default_expr_exit: true
+ bc_default_digit_clamp: false
+ dc_default_digit_clamp: false
+ }
+ // This is the predefined build to match the GNU bc/dc.
+ gnu: {
+ optimization: "3"
+ generated_tests: false
+ install_manpages: true
+ install_locales: @system
+ strip: true
+ bc_default_banner: true
+ bc_default_sigint_reset: true
+ dc_default_sigint_reset: false
+ bc_default_tty_mode: true
+ dc_default_tty_mode: false
+ bc_default_prompt: @tty_mode
+ dc_default_prompt: @tty_mode
+ bc_default_expr_exit: true
+ dc_default_expr_exit: true
+ bc_default_digit_clamp: false
+ dc_default_digit_clamp: false
+ }
+ // This is the preferred release build of the author, Gavin D. Howard.
+ gdh: {
+ optimization: "3"
+ install_manpages: true
+ install_locales: @none
+ bc/default_banner: true
+ bc/default_sigint_reset: true
+ dc/default_sigint_reset: true
+ bc/default_tty_mode: true
+ dc/default_tty_mode: true
+ bc/default_prompt: @tty_mode
+ dc/default_prompt: @tty_mode
+ bc/default_expr_exit: false
+ dc/default_expr_exit: false
+ bc/default_digit_clamp: true
+ dc/default_digit_clamp: true
+ }
+ // This is the preferred debug build of the author, Gavin D. Howard.
+ dbg: {
+ optimization: "0"
+ debug: true
+ strip: false
+ install_manpages: true
+ install_locales: @system
+ bc/default_banner: true
+ bc/default_sigint_reset: true
+ dc/default_sigint_reset: true
+ bc/default_tty_mode: true
+ dc/default_tty_mode: true
+ bc/default_prompt: @tty_mode
+ dc/default_prompt: @tty_mode
+ bc/default_expr_exit: false
+ dc/default_expr_exit: false
+ bc/default_digit_clamp: true
+ dc/default_digit_clamp: true
+ }
+}
+
+default_development: @debug
+default_release: @release
+
+options: {
+ build_mode: {
+ type: @option
+ options: [
+ @both
+ @bc
+ @dc
+ @library
+ ]
+ default: @both
+ desc: "Which of the executables or library to build."
+ }
+ extra_math: {
+ type: @bool
+ default: true
+ desc: "Enable the extra math extensions."
+ }
+ history: {
+ type: @option
+ options: [
+ @none
+ @builtin
+ @editline
+ @readline
+ ]
+ default: @builtin
+ desc: "Which history implementation should be used, if any."
+ }
+ locales: {
+ type: @option
+ options: [
+ @none
+ @system
+ @all
+ ]
+ default: @system
+ desc: "Whether to disable locales, use just the system ones, or use all (for building a package)."
+ }
+ bc/default_banner: {
+ type: @bool
+ default: false
+ desc: "Whether to display the bc version banner by default when in interactive mode."
+ }
+ bc/default_sigint_reset: {
+ type: @bool
+ default: true
+ desc: "Whether SIGINT will reset bc by default, instead of exiting, when in interactive mode."
+ }
+ dc/default_sigint_reset: {
+ type: @bool
+ default: true
+ desc: "Whether SIGINT will reset dc by default, instead of exiting, when in interactive mode."
+ }
+ bc/default_tty_mode: {
+ type: @bool
+ default: true
+ desc: "Whether TTY mode for bc should be on by default when available."
+ }
+ dc/default_tty_mode: {
+ type: @bool
+ default: false
+ desc: "Whether TTY mode for dc should be on by default when available."
+ }
+ bc/default_prompt: {
+ type: @option
+ options: [
+ @off
+ @tty_mode
+ @on
+ ]
+ default: @tty_mode
+ desc: "Whether the prompt for bc should be on by default in TTY mode. This defaults to match TTY mode."
+ }
+ dc/default_prompt: {
+ type: @option
+ options: [
+ @off
+ @tty_mode
+ @on
+ ]
+ default: @tty_mode
+ desc: "Whether the prompt for dc should be on by default in TTY mode. This defaults to match TTY mode."
+ }
+ bc/default_expr_exit: {
+ type: @bool
+ default: true
+ desc: "Whether to exit bc by default if an expression or expression file is given with the -e or -f options."
+ }
+ dc/default_expr_exit: {
+ type: @bool
+ default: true
+ desc: "Whether to exit dc by default if an expression or expression file is given with the -e or -f options."
+ }
+ bc/default_digit_clamp: {
+ type: @bool
+ default: false
+ desc: "Whether to have bc, by default, clamp digits that are greater than or equal to the current ibase when parsing numbers."
+ }
+ dc/default_digit_clamp: {
+ type: @bool
+ default: false
+ desc: "Whether to have dc, by default, clamp digits that are greater than or equal to the current ibase when parsing numbers."
+ }
+ karatsuba_len: {
+ type: @num
+ default: 32
+ desc: "Set the Karatsuba length (default is 32). Must be a number and greater than or equal to 16."
+ }
+ execprefix: {
+ type: @string
+ default: ""
+ desc: "The prefix to prepend to the executable names, to prevent collisions."
+ }
+ execsuffix: {
+ type: @string
+ default: ""
+ desc: "The suffix to append to the executable names, to prevent collisions."
+ }
+ debug: {
+ type: @bool
+ default: false
+ desc: "Enable debug info."
+ }
+ optimization: {
+ type: @string
+ default: "0"
+ desc: "The optimization level for the C compiler."
+ }
+ lto: {
+ type: @bool
+ default: false
+ desc: "Build with link-time optimization, if available."
+ }
+ strip: {
+ type: @bool
+ default: true
+ desc: "Strip any binaries."
+ }
+ strict: {
+ type: @bool
+ default: true
+ desc: "Build with strict compiler options."
+ }
+ force: {
+ type: @bool
+ default: false
+ desc: "Force options that don't work. THIS IS FOR DEV ONLY!"
+ }
+ memcheck: {
+ type: @bool
+ default: false
+ desc: "Enable memcheck mode, to check for memory leaks."
+ }
+ valgrind: {
+ type: @bool
+ default: false
+ desc: "Enable Valgrind mode, to check for memory bugs."
+ }
+ afl: {
+ type: @bool
+ default: false
+ desc: "Enable AFL++ mode."
+ }
+ ossfuzz: {
+ type: @bool
+ default: false
+ desc: "Enable OSSFUZZ mode."
+ }
+ generated_tests: {
+ type: @bool
+ default: true
+ desc: "Enable tests generated from a GNU bc-compatible program."
+ }
+ problematic_tests: {
+ type: @bool
+ default: true
+ desc: "Enable tests that may be problematic."
+ }
+ coverage: {
+ type: @bool
+ default: false
+ desc: "Enable code coverage (only works on GCC)."
+ }
+ install_manpages: {
+ type: @bool
+ default: true
+ desc: "Whether to install manpages or not."
+ }
+ cflags: {
+ type: @list
+ default: []
+ desc: "The command-line flags for the C compiler."
+ }
+ ldflags: {
+ type: @list
+ default: []
+ desc: "The command-line flags for the C linker."
+ }
+ destdir: {
+ type: @path
+ default: ""
+ desc: "The equivalent of $DESTDIR in other build systems."
+ }
+ prefix: {
+ type: @path
+ default: "/usr/local"
+ desc: "The default prefix to install everything into."
+ }
+ bindir: {
+ type: @path
+ default: ""
+ desc: "The directory to install executables into. Defaults to \"$prefix/bin\"."
+ }
+ libdir: {
+ type: @path
+ default: ""
+ desc: "The directory to install libraries into. Defaults to \"$prefix/lib\"."
+ }
+ includedir: {
+ type: @path
+ default: ""
+ desc: "The location to install headers in. Defaults to \"$prefix/include\"."
+ }
+ nlspath: {
+ type: @path
+ default: "/usr/share/locale/%L/%N"
+ desc: "The location to install locales."
+ }
+ pc_path: {
+ type: @path
+ default: ""
+ desc: "The location to pkg-config files to. Defaults to the output of `pkg-config --variable=pc_path pkg-config`."
+ }
+ datarootdir: {
+ type: @path
+ default: ""
+ desc: "The root directory for data files. Defaults to `$prefix/share`."
+ }
+ datadir: {
+ type: @path
+ default: ""
+ desc: "The directory for data files. Defaults to `$datarootdir`."
+ }
+ mandir: {
+ type: @path
+ default: ""
+ desc: "The root directory for manpages. Defaults to `$datadir/man`."
+ }
+ man1dir: {
+ type: @path
+ default: ""
+ desc: "The directory for manpages in section 1. Defaults to `$mandir/man1`."
+ }
+ man3dir: {
+ type: @path
+ default: ""
+ desc: "The directory for manpages in section 3. Defaults to `$mandir/man3`."
+ }
+}
diff --git a/contrib/bc/build.pkg.rig b/contrib/bc/build.pkg.rig
new file mode 100644
index 000000000000..d607e8885737
--- /dev/null
+++ b/contrib/bc/build.pkg.rig
@@ -0,0 +1,2345 @@
+/*
+ * *****************************************************************************
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * *****************************************************************************
+ *
+ * The build package file.
+ *
+ */
+
+/// The path to the safe install script.
+SAFE_INSTALL: str = path.join(src_dir, "scripts/safe-install.sh");
+
+/// The file mode for executables, as an argument to the safe install script.
+EXEC_INSTALL_MODE: str = "-Dm755";
+
+/// The file mode for man pages and other files, as an argument to the safe
+/// install script.
+MANPAGE_INSTALL_MODE: str = "-Dm644";
+
+// Save this.
+OS: str = platform.os;
+
+DESTDIR: str = str(config["destdir"]);
+
+EXECPREFIX: str = str(config["execprefix"]);
+EXECSUFFIX: str = str(config["execsuffix"]);
+
+/**
+ * Generates the true executable name for the given base name.
+ * @param name The base name of the executable.
+ * @return The true name of the executable, including prefix, suffix, and
+ extension.
+ */
+fn exe_name(name: str) -> str
+{
+ temp: str = EXECPREFIX +~ name +~ EXECSUFFIX;
+ return if OS == "Windows" { temp +~ ".exe"; } else { temp; };
+}
+
+/**
+ * Generates the default executable name for the given base name.
+ * @param name The base name of the executable.
+ * @return The true name of the executable, including prefix, suffix, and
+ extension.
+ */
+fn default_exe_name(name: str) -> str
+{
+ return if OS == "Windows" { name +~ ".exe"; } else { name; };
+}
+
+/**
+ * Generates the true library name for the given base name.
+ * @param name The base name of the library.
+ * @return The true name of the library, including prefix and extension.
+ */
+fn lib_name(name: str) -> str
+{
+ ext: str = if OS == "Windows" { ".lib"; } else { ".a"; };
+ return "lib" +~ name +~ ext;
+}
+
+BC_BIN: str = exe_name("bc");
+DC_BIN: str = exe_name("dc");
+LIBRARY: str = lib_name("libbcl");
+
+BC_MANPAGE: str = EXECPREFIX +~ "bc" +~ EXECSUFFIX +~ ".1";
+DC_MANPAGE: str = EXECPREFIX +~ "dc" +~ EXECSUFFIX +~ ".1";
+BCL_MANPAGE: str = "bcl.3";
+
+BCL_HEADER: str = "bcl.h";
+BCL_HEADER_PATH: str = path.join(src_dir, path.join("include", BCL_HEADER));
+PC_FILE: str = "bcl.pc";
+
+/**
+ * Returns the string value of the define for a prompt default define for an
+ * executable.
+ * @param name The base name of the executable.
+ * @return The string value of the compiler define for the prompt default.
+ */
+fn prompt(name: str) -> str
+{
+ opt: sym = sym(config[name +~ "/default_prompt"]);
+
+ ret: str =
+ if opt == @off
+ {
+ "0";
+ }
+ else if opt == @tty_mode
+ {
+ str(uint(bool(config[name +~ "/default_tty_mode"])));
+ }
+ else
+ {
+ "1";
+ };
+
+ return ret;
+}
+
+HEADERS: []str = find_src_ext("include", "h");
+
+FORCE: bool = bool(config["force"]);
+
+BUILD_MODE: sym = sym(config["build_mode"]);
+
+BC_ENABLED: str = str(uint(BUILD_MODE == @both || BUILD_MODE == @bc));
+DC_ENABLED: str = str(uint(BUILD_MODE == @both || BUILD_MODE == @dc));
+LIBRARY_ENABLED: str = str(uint(BUILD_MODE == @library));
+
+EXTRA_MATH_ENABLED: str = str(uint(bool(config["extra_math"])));
+
+HISTORY: sym = sym(config["history"]);
+HISTORY_ENABLED: str = str(uint(HISTORY != @none));
+EDITLINE_ENABLED: str = str(uint(HISTORY == @editline));
+READLINE_ENABLED: str = str(uint(HISTORY == @readline));
+
+NLS_ENABLED: str =
+if OS == "Windows" || BUILD_MODE == @library
+{
+ "0";
+}
+else
+{
+ str(uint(sym(config["locales"]) != @none));
+};
+
+BUILD_TYPE: str =
+if EXTRA_MATH_ENABLED != "0" && HISTORY_ENABLED != "0" && NLS_ENABLED != "0"
+{
+ "A";
+}
+else
+{
+ t: str = if EXTRA_MATH_ENABLED != "0" { ""; } else { "E"; } +~
+ if HISTORY_ENABLED != "0" { ""; } else { "H"; } +~
+ if NLS_ENABLED != "0" { ""; } else { "N"; };
+
+ t;
+};
+
+OPTIMIZE: str = str(config["optimization"]);
+
+VALGRIND_ARGS: []str = @[
+ "valgrind",
+ "--error-exitcode=100",
+ "--leak-check=full",
+ "--show-leak-kinds=all",
+ "--errors-for-leak-kinds=all",
+ "--track-fds=yes",
+ "--track-origins=yes",
+];
+
+// Get the compiler. The user might have set one at the command line.
+CC: str = language.compiler;
+
+// Set optimization to "0" if it is empty.
+CFLAGS_OPT: str = if OPTIMIZE == "" { "0"; } else { OPTIMIZE; };
+
+// Get the command-line option for defining a preprocessor variable.
+DEFOPT: str = compiler_db["opt.define"];
+
+// Get the command-line string for the optimization option for the compiler.
+OPTOPT: str = compiler_db["opt.optimization"] +~ CFLAGS_OPT;
+
+// Get the compiler option for the object file to output to.
+OBJOUTOPT: str = compiler_db["opt.objout"];
+EXEOUTOPT: str = compiler_db["opt.exeout"];
+
+// Get the compiler option for outputting an object file rather than an
+// executable.
+OBJOPT: str = compiler_db["opt.obj"];
+
+// Get the compiler option for setting an include directory.
+INCOPT: str = compiler_db["opt.include"] +~ path.join(src_dir, "include");
+
+COVERAGE_CFLAGS: []str =
+if bool(config["coverage"])
+{
+ @[ "-fprofile-arcs", "-ftest-coverage", "-g", "-O0", DEFOPT +~ "NDEBUG" ];
+};
+
+MAINEXEC: str =
+if BUILD_MODE == @both || BUILD_MODE == @bc || BUILD_MODE == @library
+{
+ BC_BIN;
+}
+else
+{
+ DC_BIN;
+};
+
+MAINEXEC_FLAGS: []str = @[ DEFOPT +~ "MAINEXEC=" +~ MAINEXEC ];
+
+// XXX: Library needs these defines to be true.
+BC_DEF: str = if LIBRARY_ENABLED == "0" { BC_ENABLED; } else { "1"; };
+DC_DEF: str = if LIBRARY_ENABLED == "0" { DC_ENABLED; } else { "1"; };
+
+CFLAGS1: []str = config_list["cflags"] +~ @[ OPTOPT, INCOPT ] +~
+ COVERAGE_CFLAGS +~ MAINEXEC_FLAGS;
+CFLAGS2: []str = @[
+ DEFOPT +~ "BC_ENABLED=" +~ BC_DEF,
+ DEFOPT +~ "DC_ENABLED=" +~ DC_DEF,
+ DEFOPT +~ "BUILD_TYPE=" +~ BUILD_TYPE,
+ DEFOPT +~ "EXECPREFIX=" +~ str(config["execprefix"]),
+ DEFOPT +~ "BC_NUM_KARATSUBA_LEN=" +~ str(num(config["karatsuba_len"])),
+ DEFOPT +~ "BC_ENABLE_LIBRARY=" +~ LIBRARY_ENABLED,
+ DEFOPT +~ "BC_ENABLE_NLS=" +~ NLS_ENABLED,
+ DEFOPT +~ "BC_ENABLE_EXTRA_MATH=" +~ EXTRA_MATH_ENABLED,
+ DEFOPT +~ "BC_ENABLE_HISTORY=" +~ HISTORY_ENABLED,
+ DEFOPT +~ "BC_ENABLE_EDITLINE=" +~ EDITLINE_ENABLED,
+ DEFOPT +~ "BC_ENABLE_READLINE=" +~ READLINE_ENABLED,
+ DEFOPT +~ "BC_ENABLE_MEMCHECK=" +~ str(uint(bool(config["memcheck"]))),
+ DEFOPT +~ "BC_ENABLE_AFL=" +~ str(uint(bool(config["afl"]))),
+ DEFOPT +~ "BC_ENABLE_OSSFUZZ=" +~ str(uint(bool(config["ossfuzz"]))),
+ DEFOPT +~ "BC_DEFAULT_BANNER=" +~
+ str(uint(bool(config["bc/default_banner"]))),
+ DEFOPT +~ "BC_DEFAULT_SIGINT_RESET=" +~
+ str(uint(bool(config["bc/default_sigint_reset"]))),
+ DEFOPT +~ "BC_DEFAULT_TTY_MODE=" +~
+ str(uint(bool(config["bc/default_tty_mode"]))),
+ DEFOPT +~ "BC_DEFAULT_PROMPT=" +~ prompt("bc"),
+ DEFOPT +~ "BC_DEFAULT_EXPR_EXIT=" +~
+ str(uint(bool(config["bc/default_expr_exit"]))),
+ DEFOPT +~ "BC_DEFAULT_DIGIT_CLAMP=" +~
+ str(uint(bool(config["bc/default_digit_clamp"]))),
+ DEFOPT +~ "DC_DEFAULT_SIGINT_RESET=" +~
+ str(uint(bool(config["dc/default_sigint_reset"]))),
+ DEFOPT +~ "DC_DEFAULT_TTY_MODE=" +~
+ str(uint(bool(config["dc/default_tty_mode"]))),
+ DEFOPT +~ "DC_DEFAULT_PROMPT=" +~ prompt("dc"),
+ DEFOPT +~ "DC_DEFAULT_EXPR_EXIT=" +~
+ str(uint(bool(config["dc/default_expr_exit"]))),
+ DEFOPT +~ "DC_DEFAULT_DIGIT_CLAMP=" +~
+ str(uint(bool(config["dc/default_digit_clamp"]))),
+];
+CFLAGS: []str = CFLAGS1 +~ CFLAGS2;
+
+LDFLAGS: []str = config_list["ldflags"];
+
+COMMON_C_FILES: []str = @[
+ "src/data.c",
+ "src/num.c",
+ "src/rand.c",
+ "src/vector.c",
+ "src/vm.c",
+];
+
+EXEC_C_FILES: []str = @[
+ "src/args.c",
+ "src/file.c",
+ "src/lang.c",
+ "src/lex.c",
+ "src/main.c",
+ "src/opt.c",
+ "src/parse.c",
+ "src/program.c",
+ "src/read.c",
+];
+
+BC_C_FILES: []str = @[
+ "src/bc.c",
+ "src/bc_lex.c",
+ "src/bc_parse.c",
+];
+
+DC_C_FILES: []str = @[
+ "src/dc.c",
+ "src/dc_lex.c",
+ "src/dc_parse.c",
+];
+
+HISTORY_C_FILES: []str = @[
+ "src/history.c",
+];
+
+LIBRARY_C_FILES: []str = @[
+ "src/library.c",
+];
+
+GEN_HEADER1: str =
+ "// Copyright (c) 2018-2025 Gavin D. Howard and contributors.\n" +~
+ "// Licensed under the 2-clause BSD license.\n" +~
+ "// *** AUTOMATICALLY GENERATED FROM ";
+GEN_HEADER2: str = ". DO NOT MODIFY. ***\n\n";
+
+GEN_LABEL1: str = "const char *";
+GEN_LABEL2: str = " = \"";
+GEN_LABEL3: str = "\";\n\n";
+GEN_NAME1: str = "const char ";
+GEN_NAME2: str = "[] = {\n";
+
+GEN_LABEL_EXTERN1: str = "extern const char *";
+GEN_LABEL_EXTERN2: str = ";\n\n";
+GEN_NAME_EXTERN1: str = "extern const char ";
+GEN_NAME_EXTERN2: str = "[];\n\n";
+
+GEN_IFDEF1: str = "#if ";
+GEN_IFDEF2: str = "\n";
+GEN_ENDIF1: str = "#endif // ";
+GEN_ENDIF2: str = "\n";
+
+GEN_EX_START: str = "{{ A H N HN }}";
+GEN_EX_END: str = "{{ end }}";
+
+/// This is the max width to print characters to strgen files. This is to ensure
+/// that lines don't go much over 80 characters.
+MAX_WIDTH: usize = usize(72);
+
+/**
+ * A function to generate a C file that contains a C character array with the
+ * contents of a text file. For more detail, see the `gen/strgen.c` program;
+ * this function is exactly equivalent to that or should be.
+ * @param input The input file name.
+ * @param output The output file name.
+ * @param exclude True if extra math stuff should be excluded, false if
+ * they should be included.
+ * @param name The name of the array.
+ * @param label If not equal to "", this is the label for the array,
+ * which is essentially the "file name" in `bc` and `dc`.
+ * @param define If not equal to "", this is the preprocessor define
+ * expression that should be used to guard the array with a
+ * `#if`/`#endif` combo.
+ * @param remove_tabs True if tabs should be ignored, false if they should be
+ * included.
+ */
+fn strgen(
+ input: str,
+ output: str,
+ exclude: bool,
+ name: str,
+ label: str,
+ def: str,
+ remove_tabs: bool,
+) -> void
+{
+ in: str = io.read_file(input);
+
+ io.open(output, "w"): f
+ {
+ f.print(GEN_HEADER1 +~ input +~ GEN_HEADER2);
+
+ if label != ""
+ {
+ f.print(GEN_LABEL_EXTERN1 +~ label +~ GEN_LABEL_EXTERN2);
+ }
+
+ f.print(GEN_NAME_EXTERN1 +~ name +~ GEN_NAME_EXTERN2);
+
+ if def != ""
+ {
+ f.print(GEN_IFDEF1 +~ def +~ GEN_IFDEF2);
+ }
+
+ if label != ""
+ {
+ f.print(GEN_LABEL1 +~ label +~ GEN_LABEL2 +~ name +~ GEN_LABEL3);
+ }
+
+ f.print(GEN_NAME1 +~ name +~ GEN_NAME2);
+
+ i: !usize = usize(0);
+ count: !usize = usize(0);
+ slashes: !usize = usize(0);
+
+ // This is where the end of the license comment is found.
+ while slashes < 2 && in[i] > 0
+ {
+ if slashes == 1 && in[i] == '*' && in[i + 1] == '/' &&
+ (in[i + 2] == '\n' || in[i + 2] == '\r')
+ {
+ slashes! = slashes + usize(1);
+ i! = i + usize(2);
+ }
+ else if slashes == 0 && in[i] == '/' && in[i + 1] == '*'
+ {
+ slashes! = slashes + usize(1);
+ i! = i + usize(1);
+ }
+
+ i! = i + usize(1);
+ }
+
+ // The file is invalid if the end of the license comment could not be
+ // found.
+ if i == in.len
+ {
+ error("Could not find end of license comment");
+ }
+
+ i! = i + usize(1);
+
+ // Do not put extra newlines at the beginning of the char array.
+ while in[i] == '\n' || in[i] == '\r'
+ {
+ i! = i + usize(1);
+ }
+
+ // This loop is what generates the actual char array. It counts how many
+ // chars it has printed per line in order to insert newlines at
+ // appropriate places. It also skips tabs if they should be removed.
+ while i < in.len
+ {
+ if in[i] == '\r'
+ {
+ i! = i + usize(1);
+ continue;
+ }
+
+ // If we should output the character, i.e., it is not a tab or we
+ // can remove tabs...
+ if !remove_tabs || in[i] != '\t'
+ {
+ // Check for excluding something for extra math.
+ if in[i] == '{'
+ {
+ if i + GEN_EX_START.len <= in.len &&
+ in.slice(i, i + GEN_EX_START.len) == GEN_EX_START
+ {
+ if exclude
+ {
+ // Get past the braces.
+ i! = i + usize(2);
+
+ // Find the end of the end.
+ while in[i] != '{' &&
+ in.slice(i, i + GEN_EX_END.len) != GEN_EX_END
+ {
+ i! = i + usize(1);
+ }
+
+ i! = i + GEN_EX_END.len;
+
+ // Skip the last newline.
+ if in[i] == '\r'
+ {
+ i! = i + usize(1);
+ }
+
+ i! = i + usize(1);
+
+ continue;
+ }
+ }
+ else if !exclude &&
+ in.slice(i, i + GEN_EX_END.len) == GEN_EX_END
+ {
+ i! = i + GEN_EX_END.len;
+
+ // Skip the last newline.
+ if in[i] == '\r'
+ {
+ i! = i + usize(1);
+ }
+
+ i! = i + usize(1);
+
+ continue;
+ }
+ }
+
+ // Print a tab if we are at the beginning of a line.
+ if count == 0
+ {
+ f.print("\t");
+ }
+
+ val: str = str(in[i]) +~ ",";
+
+ // Print the character.
+ f.print(val);
+
+ // Adjust the count.
+ count! = count + val.len;
+
+ if count > MAX_WIDTH
+ {
+ count! = usize(0);
+ f.print("\n");
+ }
+ }
+
+ i! = i + usize(1);
+ }
+
+ // Make sure the end looks nice.
+ if count == 0
+ {
+ f.print(" ");
+ }
+
+ // Insert the NUL byte at the end.
+ f.print("0\n};\n");
+
+ if def != ""
+ {
+ f.print(GEN_ENDIF1 +~ def +~ GEN_ENDIF2);
+ }
+ }
+}
+
+/**
+ * Creates a target to generate an object file from the given C file and returns
+ * the target name of the new target.
+ * @param c_file The name of the C file target.
+ * @return The name of the object file target.
+ */
+fn c2o(c_file: str) -> str
+{
+ o_file: str = c_file +~ (if OS == "Windows" { ".obj"; } else { ".o"; });
+
+ target o_file: c_file, HEADERS
+ {
+ $ $CC %(config_list["other_cflags"]) %(CFLAGS) $OBJOPT $OBJOUTOPT @(tgt)
+ @(file_dep);
+ }
+
+ return o_file;
+}
+
+/**
+ * Generates a target to turn a text file into a C file with the text file's
+ * contents as a char array, then generates a target to generate an object file
+ * from that C file, then returns the name of the object file target.
+ * @param txt_file The name of the text file.
+ * @param name The name of the char array in the C file.
+ * @param label The label for the array, if any. (See the @a strgen()
+ * function for more information.)
+ * @param def The preprocessor define(s) to guard the array, if any.
+ * (See the @a strgen() function for more information.)
+ * @param remove_tabs True if tabs should be ignored, false otherwise. (See the
+ * @a strgen() function for more information.)
+ * @return The name of the object file target.
+ */
+fn txt2o(
+ txt_file: str,
+ name: str,
+ label: str,
+ def: str,
+ remove_tabs: bool,
+) -> str
+{
+ c_file: str = txt_file +~ ".c";
+
+ c_config: Gaml = @(gaml){
+ strgen_name: $name
+ strgen_label: $label
+ strgen_define: $def
+ strgen_remove_tabs: $remove_tabs
+ };
+
+ push c_config: config_stack
+ {
+ target c_file: txt_file
+ {
+ strgen(file_dep, tgt, EXTRA_MATH_ENABLED == "0",
+ str(config["strgen_name"]), str(config["strgen_label"]),
+ str(config["strgen_define"]),
+ bool(config["strgen_remove_tabs"]));
+ }
+ }
+
+ return c2o(c_file);
+}
+
+/**
+ * Generates a target for an executable and returns its name.
+ * @param name The name of the executable.
+ * @param o_files The object files for the executable.
+ * @return The name of the generated target.
+ */
+fn exe(name: str, o_files: []str) -> void
+{
+ target name: o_files
+ {
+ $ $CC %(config_list["other_cflags"]) %(config_list["strip_flag"])
+ %(CFLAGS) %(LDFLAGS) $EXEOUTOPT @(tgt) %(file_deps);
+ }
+}
+
+/**
+ * Generates a target for a link.
+ * @param name The name of the link.
+ * @param exec The name of the executable target.
+ */
+fn ln(name: str, exec: str) -> void
+{
+ if OS == "Windows"
+ {
+ target name: exec
+ {
+ $ copy /v /y /b @(file_dep) @(tgt);
+ }
+ }
+ else
+ {
+ target name: exec
+ {
+ $ ln -fs @("./" +~ path.basename(file_dep)) @(tgt);
+ }
+ }
+}
+
+/**
+ * Generates a target for a library.
+ * @param name The name of the library.
+ * @param exec The name of the executable target.
+ */
+fn lib(name: str, o_files: []str) -> void
+{
+ if OS == "WINDOWS"
+ {
+ exe(name, o_files);
+ }
+ else
+ {
+ target name: o_files
+ {
+ $ ar -r -cu @(tgt) %(file_deps);
+ }
+ }
+}
+
+fn check_err_test(
+ name: str,
+ res: CmdResult,
+) -> void
+{
+ if res.exitcode > 127
+ {
+ error("Test \"" +~ name +~ "\" crashed");
+ }
+
+ if res.exitcode == 0
+ {
+ error("Test \"" +~ name +~ "\" returned no error");
+ }
+
+ if res.exitcode == 100
+ {
+ error("Test \"" +~ name +~ "\" had memory errors on non-fatal error\n");
+ }
+
+ if res.stderr.len <= 1
+ {
+ error("Test \"" +~ name +~ "\" produced no error message");
+ }
+}
+
+fn check_test_retcode(
+ name: str,
+ exitcode: uint,
+) -> void
+{
+ if exitcode != 0
+ {
+ error("Test \"" +~ name +~ "\" failed with exitcode: " +~
+ str(exitcode) +~ "\n");
+ }
+}
+
+fn check_test(
+ name: str,
+ res: CmdResult,
+ exp_path: str,
+) -> void
+{
+ check_test_retcode(name, res.exitcode);
+
+ exp := io.read_file_bytes(exp_path);
+
+ if exp != res.stdout_full
+ {
+ error("Test \"" +~ name +~ "\" failed\n" +~ str(res.stderr));
+ }
+}
+
+fn register_standard_tests(
+ bin: str,
+ testdir: str,
+ src_testdir: str,
+ extra: bool,
+) -> void
+{
+ all_file: str = path.join(src_testdir, "all.txt");
+ tests: []str = io.read_file(all_file).split("\n");
+
+ extra_path := path.join(src_dir, "tests/extra_required.txt");
+ extra_required: []str = io.read_file(extra_path).split("\n");
+
+ for t: tests
+ {
+ if t == ""
+ {
+ continue;
+ }
+
+ // Skip extra math tests if it is not enabled.
+ if !extra && extra_required contains t
+ {
+ continue;
+ }
+
+ test sym(path.join(testdir, t)): bin
+ {
+ halt: str = str(config["halt"]);
+
+ name: str = path.basename(tgt_name);
+ testdir: str = path.dirname(tgt_name);
+ calc: str = path.basename(testdir);
+
+ test_file: str = tgt_name +~ ".txt";
+ test_result_file: str = tgt_name +~ "_results.txt";
+
+ src_test_file: str = path.join(src_dir, test_file);
+ src_test_result_file: str = path.join(src_dir, test_result_file);
+
+ actual_test_file: str =
+ if !path.isfile(src_test_file)
+ {
+ // If we shouldn't generate tests, skip.
+ if !bool(config["generated_tests"])
+ {
+ io.eprint("Skipping test " +~ tgt_name +~ "\n");
+ return;
+ }
+
+ script_name: str = name +~ "." +~ calc;
+
+ scriptdir: str = path.join(testdir, "scripts");
+ src_scriptdir: str = path.join(src_dir, scriptdir);
+ src_script_name: str = path.join(src_scriptdir, script_name);
+
+ $ @(default_exe_name(calc)) $src_script_name > $test_file;
+
+ test_file;
+ }
+ else
+ {
+ src_test_file;
+ };
+
+ exp_result_file: str =
+ if !path.isfile(src_test_result_file)
+ {
+ tmpfile: str = path.tmp(calc +~ "_test_result");
+
+ $ @(default_exe_name(calc)) %(config_list["gen_options"])
+ $actual_test_file << $halt > $tmpfile;
+
+ tmpfile;
+ }
+ else
+ {
+ src_test_result_file;
+ };
+
+ res := $ %(config_list["args"]) %(config_list["options"])
+ $actual_test_file << $halt;
+
+ check_test(tgt_name, res, exp_result_file);
+ }
+ }
+}
+
+fn register_script_tests(
+ bin: str,
+ testdir: str,
+ src_testdir: str,
+ extra: bool,
+) -> void
+{
+ scriptdir: str = path.join(testdir, "scripts");
+ src_scriptdir: str = path.join(src_testdir, "scripts");
+ all_file: str = path.join(src_scriptdir, "all.txt");
+ tests: []str = io.read_file(all_file).split("\n");
+
+ for t: tests
+ {
+ if t == ""
+ {
+ continue;
+ }
+
+ // Skip extra math tests if it is not enabled.
+ if !extra && (t == "rand.bc" || t == "root.bc" || t == "i2rand.bc")
+ {
+ continue;
+ }
+
+ test sym(path.join(scriptdir, t)): bin
+ {
+ halt: str = str(config["halt"]);
+
+ name: str = path.basename(tgt_name);
+ testdir: str = path.dirname(tgt_name);
+ testdir2: str = path.dirname(testdir);
+ calc: str = path.basename(testdir2);
+
+ test_file: str = tgt_name;
+ test_file_dir: str = path.dirname(tgt_name);
+ test_file_name: str = path.basename(tgt_name, "." +~ calc);
+ test_result_file: str = path.join(test_file_dir,
+ test_file_name +~ ".txt");
+
+ src_test_file: str = path.join(src_dir, test_file);
+ src_test_result_file: str = path.join(src_dir, test_result_file);
+
+ exp_result_file: str =
+ if !path.isfile(src_test_result_file)
+ {
+ tmpfile: str = path.tmp(calc +~ "_script_test_result");
+
+ // This particular test needs to be generated straight. Also, on
+ // Windows, we don't have `sed`, and the `bc`/`dc` there is
+ // probably this one anyway.
+ if name == "stream.dc" || host.os == "Windows"
+ {
+ $ @(default_exe_name(calc)) $src_test_file << $halt
+ > $tmpfile;
+ }
+ else
+ {
+ root_testdir: str = path.join(src_dir, "tests");
+
+ // This sed and the script are to remove an incompatibility
+ // with GNU bc, where GNU bc is wrong. See the development
+ // manual (manuals/development.md#script-tests) for more
+ // information.
+ $ @(default_exe_name(calc)) $src_test_file << $halt |
+ sed -n -f @(path.join(root_testdir, "script.sed"))
+ > $tmpfile;
+ }
+
+ tmpfile;
+ }
+ else
+ {
+ src_test_result_file;
+ };
+
+ if calc == "bc"
+ {
+ res1 := $ %(config_list["args"]) -g
+ %(config_list["script_options"]) $src_test_file
+ << $halt;
+
+ check_test(tgt_name, res1, exp_result_file);
+ }
+
+ // These tests do not need to run without global stacks.
+ if name == "globals.bc" || name == "references.bc" ||
+ name == "rand.bc"
+ {
+ return;
+ }
+
+ res2 := $ %(config_list["args"]) %(config_list["script_options"])
+ $src_test_file << $halt;
+
+ check_test(tgt_name, res2, exp_result_file);
+ }
+ }
+}
+
+fn register_stdin_test(
+ bin: str,
+ testdir: str,
+ name: str
+) -> void
+{
+ test sym(path.join(testdir, name)): bin
+ {
+ name: str = path.basename(tgt_name);
+ testdir: str = path.dirname(tgt_name);
+ calc: str = path.basename(testdir);
+
+ halt: str = if name == "bc" { "halt"; } else { "q"; };
+
+ test_file: str = tgt_name +~ ".txt";
+ test_result_file: str = tgt_name +~ "_results.txt";
+
+ src_test_file: str = path.join(src_dir, test_file);
+ src_test_result_file: str = path.join(src_dir, test_result_file);
+
+ res := $ %(config_list["args"]) %(config_list["options"])
+ < $src_test_file;
+
+ check_test(tgt_name, res, src_test_result_file);
+ }
+}
+
+fn register_stdin_tests(
+ bin: str,
+ testdir: str,
+ src_testdir: str,
+) -> void
+{
+ calc: str = path.basename(testdir);
+
+ if calc == "bc"
+ {
+ for t: @[ "stdin", "stdin1", "stdin2" ]
+ {
+ register_stdin_test(bin, testdir, t);
+ }
+ }
+ else
+ {
+ // dc only needs one.
+ register_stdin_test(bin, testdir, "stdin");
+ }
+}
+
+fn register_read_tests(
+ bin: str,
+ testdir: str,
+ src_testdir: str,
+) -> void
+{
+ calc: str = path.basename(testdir);
+
+ read_call: str = if calc == "bc" { "read()"; } else { "?"; };
+ read_expr: str =
+ if calc == "bc"
+ {
+ read_call +~ "\n5+5;";
+ }
+ else
+ {
+ read_call;
+ };
+ read_multiple: str =
+ if calc == "bc"
+ {
+ "3\n2\n1\n";
+ }
+ else
+ {
+ "3pR\n2pR\n1pR\n";
+ };
+
+ read_test_config: Gaml = @(gaml){
+ read_call: $read_call
+ read_expr: $read_expr
+ read_multiple: $read_multiple
+ };
+
+ push read_test_config: config_stack
+ {
+ // First test is the regular read test.
+ test sym(path.join(testdir, "read")): bin
+ {
+ testdir: str = path.dirname(tgt_name);
+ src_testdir: str = path.join(src_dir, testdir);
+
+ test_file: str = tgt_name +~ ".txt";
+ src_test_file: str = path.join(src_dir, test_file);
+
+ read_call: str = str(config["read_call"]);
+
+ lines: []str = io.read_file(src_test_file).split("\n");
+
+ for l: lines
+ {
+ if l == ""
+ {
+ continue;
+ }
+
+ res := $ %(config_list["args"]) %(config_list["options"])
+ << @(read_call +~ "\n" +~ l +~ "\n");
+
+ check_test(tgt_name, res,
+ path.join(src_testdir, "read_results.txt"));
+ }
+ }
+
+ // Next test is reading multiple times.
+ test sym(path.join(testdir, "read_multiple")): bin
+ {
+ testdir: str = path.dirname(tgt_name);
+
+ test_file: str = tgt_name +~ ".txt";
+
+ path.mkdirp(path.dirname(test_file));
+
+ read_call: str = str(config["read_call"]);
+
+ exp_path: str = path.tmp("read_multiple_results");
+
+ io.open(exp_path, "w"): f
+ {
+ f.print("3\n2\n1\n");
+ }
+
+ res := $ %(config_list["args"]) %(config_list["options"])
+ -e $read_call -e $read_call -e $read_call
+ << @(str(config["read_multiple"]));
+
+ check_test(tgt_name, res, exp_path);
+ }
+
+ // Next test is the read errors test.
+ test sym(path.join(testdir, "read_errors")): bin
+ {
+ testdir: str = path.dirname(tgt_name);
+ src_testdir: str = path.join(src_dir, testdir);
+
+ test_file: str = tgt_name +~ ".txt";
+ src_test_file: str = path.join(src_dir, test_file);
+
+ path.mkdirp(path.dirname(test_file));
+
+ read_call: str = str(config["read_call"]);
+
+ lines: []str = io.read_file(src_test_file).split("\n");
+
+ for l: lines
+ {
+ if l == ""
+ {
+ continue;
+ }
+
+ res := $ %(config_list["args"]) %(config_list["options"])
+ << @(read_call +~ "\n" +~ l +~ "\n");
+
+ check_err_test(tgt_name, res);
+ }
+ }
+
+ // Next test is the empty read test.
+ test sym(path.join(testdir, "read_empty")): bin
+ {
+ read_call: str = str(config["read_call"]);
+
+ res := $ %(config_list["args"]) %(config_list["options"])
+ << @(read_call +~ "\n");
+
+ check_err_test(tgt_name, res);
+ }
+
+ // Next test is the read EOF test.
+ test sym(path.join(testdir, "read_EOF")): bin
+ {
+ read_call: str = str(config["read_call"]);
+
+ res := $ %(config_list["args"]) %(config_list["options"])
+ << $read_call;
+
+ check_err_test(tgt_name, res);
+ }
+ }
+}
+
+fn run_error_lines_test(name: str) -> void
+{
+ file: str = path.join(src_dir, name);
+
+ lines: []str = io.read_file(file).split("\n");
+
+ for l: lines
+ {
+ if l == ""
+ {
+ continue;
+ }
+
+ res := $ %(config_list["args"]) %(config_list["options"])
+ %(config_list["error_options"]) << @(l +~ "\n");
+
+ check_err_test(name, res);
+ }
+}
+
+fn register_error_tests(
+ bin: str,
+ testdir: str,
+ src_testdir: str,
+) -> void
+{
+ calc: str = path.basename(testdir);
+
+ // First test is command-line expression error.
+ test sym(path.join(testdir, "command-line_expr_error")): bin
+ {
+ halt: str = str(config["halt"]);
+
+ res := $ %(config_list["args"]) %(config_list["options"]) -e "1+1" -f-
+ -e "2+2" << $halt;
+
+ check_err_test(tgt_name, res);
+ }
+
+ // First test is command-line file expression error.
+ test sym(path.join(testdir, "command-line_file_expr_error")): bin
+ {
+ testdir: str = path.dirname(tgt_name);
+ halt: str = str(config["halt"]);
+
+ res := $ %(config_list["args"]) %(config_list["options"]) -e "1+1" -f-
+ -f @(path.join(testdir, "decimal.txt")) << $halt;
+
+ check_err_test(tgt_name, res);
+ }
+
+ if calc == "bc"
+ {
+ test sym(path.join(testdir, "posix_warning")): bin
+ {
+ res := $ %(config_list["args"]) %(config_list["options"]) -w
+ << @("line");
+
+ if res.exitcode != 0
+ {
+ error("Test \"" +~ tgt_name +~ "\" returned an error (" +~
+ str(res.exitcode) +~ ")");
+ }
+
+ output: str = str(res.stderr);
+
+ if output == "" || output == "\n"
+ {
+ error("Test \"" +~ tgt_name +~ "\" did not print a warning");
+ }
+ }
+
+ test sym(path.join(testdir, "posix_errors.txt")): bin
+ {
+ run_error_lines_test(tgt_name);
+ }
+ }
+
+ test sym(path.join(testdir, "errors.txt")): bin
+ {
+ run_error_lines_test(tgt_name);
+ }
+
+ errors_dir: str = path.join(testdir, "errors");
+
+ for f: find_src_ext(errors_dir, "txt")
+ {
+ // Skip the problematic test, if requested.
+ if calc == "bc" && f contains "33.txt" &&
+ !bool(config["problematic_tests"])
+ {
+ continue;
+ }
+
+ test sym(f): bin
+ {
+ errors_dir: str = path.dirname(tgt_name);
+ testdir: str = path.dirname(errors_dir);
+ calc: str = path.basename(testdir);
+
+ halt: str = str(config["halt"]);
+
+ res1 := $ %(config_list["args"]) %(config_list["error_options"]) -c
+ @(tgt_name) << $halt;
+
+ check_err_test(tgt_name, res1);
+
+ res2 := $ %(config_list["args"]) %(config_list["error_options"]) -C
+ @(tgt_name) << $halt;
+
+ check_err_test(tgt_name, res2);
+
+ res3 := $ %(config_list["args"]) %(config_list["error_options"]) -c
+ < @(path.join(src_dir, tgt_name));
+
+ check_err_test(tgt_name, res3);
+
+ res4 := $ %(config_list["args"]) %(config_list["error_options"]) -C
+ < @(path.join(src_dir, tgt_name));
+
+ check_err_test(tgt_name, res4);
+ }
+ }
+}
+
+fn check_kwredef_test(
+ name: str,
+ res: CmdResult,
+) -> void
+{
+ testdir: str = path.dirname(name);
+ redefine_exp: str = path.join(testdir, "redefine_exp.txt");
+
+ check_test(tgt_name, res, redefine_exp);
+}
+
+OTHER_LINE_LEN_RESULTS_NAME: str = "line_length_test_results.txt";
+OTHER_LINE_LEN70_RESULTS_NAME: str = "line_length70_test_results.txt";
+OTHER_MATHLIB_SCALE_RESULTS_NAME: str = "mathlib_scale_results.txt";
+
+fn register_other_tests(
+ bin: str,
+ testdir: str,
+ src_testdir: str,
+ extra: bool,
+) -> void
+{
+ calc: str = path.basename(testdir);
+
+ path.mkdirp(testdir);
+
+ // Halt test.
+ test sym(path.join(testdir, "halt")): bin
+ {
+ halt: str = str(config["halt"]) +~ "\n";
+
+ res := $ %(config_list["args"]) << $halt;
+
+ check_test_retcode(tgt_name, res.exitcode);
+ }
+
+ if calc == "bc"
+ {
+ // bc has two halt or quit commands, so test the second as well.
+ test sym(path.join(testdir, "quit")): bin
+ {
+ res := $ %(config_list["args"]) << @("quit\n");
+
+ check_test_retcode(tgt_name, res.exitcode);
+ }
+
+ // Also, make sure quit only quits after an expression.
+ test sym(path.join(testdir, "quit_after_expr")): bin
+ {
+ res := $ %(config_list["args"]) -e "1+1" << @("quit\n");
+
+ check_test_retcode(tgt_name, res.exitcode);
+
+ if str(res.stdout) != "2"
+ {
+ error("Test \"" +~ tgt_name +~
+ "\" did not have the right output");
+ }
+ }
+
+ test sym(path.join(testdir, "env_args1")): bin
+ {
+ env.set env.str("BC_ENV_ARGS", " '-l' '' -q")
+ {
+ res := $ %(config_list["args"]) << @("s(.02893)\n");
+
+ check_test_retcode(tgt_name, res.exitcode);
+ }
+ }
+
+ test sym(path.join(testdir, "env_args2")): bin
+ {
+ env.set env.str("BC_ENV_ARGS", " '-l' '' -q")
+ {
+ res := $ %(config_list["args"]) -e 4 << @("halt\n");
+
+ check_test_retcode(tgt_name, res.exitcode);
+ }
+ }
+
+ redefine_exp: str = path.join(testdir, "redefine_exp.txt");
+
+ io.open(redefine_exp, "w"): f
+ {
+ f.print("5\n0\n");
+ }
+
+ test sym(path.join(testdir, "keyword_redefinition1")): bin
+ {
+ res := $ %(config_list["args"]) --redefine=print -e
+ "define print(x) { x }" -e "print(5)" << @("halt\n");
+
+ check_kwredef_test(tgt_name, res);
+ }
+
+ test sym(path.join(testdir, "keyword_redefinition2")): bin
+ {
+ res := $ %(config_list["args"]) -r abs -r else -e
+ "abs = 5; else = 0" -e "abs;else" << @("halt\n");
+
+ check_kwredef_test(tgt_name, res);
+ }
+
+ if extra
+ {
+ test sym(path.join(testdir, "keyword_redefinition_lib2")): bin
+ {
+ res := $ %(config_list["args"]) -lr abs -e "perm(5, 1)" -e 0
+ << @("halt\n");
+
+ check_kwredef_test(tgt_name, res);
+ }
+
+ test sym(path.join(testdir, "leading_zero_script")): bin
+ {
+ testdir: str = path.dirname(tgt_name);
+ src_testdir: str = path.join(src_dir, testdir);
+
+ res := $ %(config_list["args"]) -lz
+ @(path.join(src_testdir, "leadingzero.txt"))
+ << @(str(config["halt"]));
+
+ check_test(tgt_name, res,
+ path.join(src_testdir, "leadingzero_results.txt"));
+ }
+ }
+
+ test sym(path.join(testdir, "keyword_redefinition3")): bin
+ {
+ res := $ %(config_list["args"]) -r abs -r else -e
+ "abs = 5; else = 0" -e "abs;else" << @("halt\n");
+
+ check_kwredef_test(tgt_name, res);
+ }
+
+ test sym(path.join(testdir, "keyword_redefinition_error")): bin
+ {
+ res := $ %(config_list["args"]) -r break -e "define break(x) { x }";
+
+ check_err_test(tgt_name, res);
+ }
+
+ test sym(path.join(testdir,
+ "keyword_redefinition_without_redefine")): bin
+ {
+ res := $ %(config_list["args"]) -e "define read(x) { x }";
+
+ check_err_test(tgt_name, res);
+ }
+
+ test sym(path.join(testdir, "multiline_comment_in_expr_file")): bin
+ {
+ testdir: str = path.dirname(tgt_name);
+ src_testdir: str = path.join(src_dir, testdir);
+
+ // tests/bc/misc1.txt happens to have a multiline comment in it.
+ src_test_file: str = path.join(src_testdir, "misc1.txt");
+ src_test_results_file: str = path.join(src_testdir,
+ "misc1_results.txt");
+
+ res := $ %(config_list["args"]) -f $src_test_file << @("halt\n");
+
+ check_test(tgt_name, res, src_test_results_file);
+ }
+
+ test sym(path.join(testdir,
+ "multiline_comment_error_in_expr_file")): bin
+ {
+ testdir: str = path.dirname(tgt_name);
+ src_testdir: str = path.join(src_dir, testdir);
+
+ src_test_file: str = path.join(src_testdir, "errors/05.txt");
+
+ res := $ %(config_list["args"]) -f $src_test_file << @("halt\n");
+
+ check_err_test(tgt_name, res);
+ }
+
+ test sym(path.join(testdir, "multiline_string_in_expr_file")): bin
+ {
+ testdir: str = path.dirname(tgt_name);
+ src_testdir: str = path.join(src_dir, testdir);
+
+ // tests/bc/strings.txt happens to have a multiline string in it.
+ src_test_file: str = path.join(src_testdir, "strings.txt");
+ src_test_results_file: str = path.join(src_testdir,
+ "strings_results.txt");
+
+ res := $ %(config_list["args"]) -f $src_test_file << @("halt\n");
+
+ check_test(tgt_name, res, src_test_results_file);
+ }
+
+ tst := path.join(testdir,
+ "multiline_string_with_backslash_error_in_expr_file");
+
+ test sym(tst): bin
+ {
+ testdir: str = path.dirname(tgt_name);
+ src_testdir: str = path.join(src_dir, testdir);
+
+ src_test_file: str = path.join(src_testdir, "errors/16.txt");
+
+ res := $ %(config_list["args"]) -f $src_test_file << @("halt\n");
+
+ check_err_test(tgt_name, res);
+ }
+
+ tst2 := path.join(testdir, "multiline_string_error_in_expr_file");
+
+ test sym(tst2): bin
+ {
+ testdir: str = path.dirname(tgt_name);
+ src_testdir: str = path.join(src_dir, testdir);
+
+ src_test_file: str = path.join(src_testdir, "errors/04.txt");
+
+ res := $ %(config_list["args"]) -f $src_test_file << @("halt\n");
+
+ check_err_test(tgt_name, res);
+ }
+
+ test sym(path.join(testdir, "interactive_halt")): bin
+ {
+ res := $ %(config_list["args"]) -i << @("halt\n");
+
+ check_test_retcode(tgt_name, res.exitcode);
+ }
+ }
+ else
+ {
+ test sym(path.join(testdir, "env_args1")): bin
+ {
+ env.set env.str("DC_ENV_ARGS", "'-x'"), env.str("DC_EXPR_EXIT", "1")
+ {
+ res := $ %(config_list["args"]) << @("4s stuff\n");
+
+ check_test_retcode(tgt_name, res.exitcode);
+ }
+ }
+
+ test sym(path.join(testdir, "env_args2")): bin
+ {
+ env.set env.str("DC_ENV_ARGS", "'-x'"), env.str("DC_EXPR_EXIT", "1")
+ {
+ res := $ %(config_list["args"]) -e 4pR;
+
+ check_test_retcode(tgt_name, res.exitcode);
+ }
+ }
+
+ test sym(path.join(testdir, "extended_register_command1")): bin
+ {
+ testdir: str = path.dirname(tgt_name);
+ results: str = tgt_name +~ ".txt";
+
+ path.mkdirp(testdir);
+
+ io.open(results, "w"): f
+ {
+ f.print("0\n");
+ }
+
+ res := $ %(config_list["args"]) -e gxpR << @("q\n");
+
+ check_test(tgt_name, res, results);
+ }
+
+ test sym(path.join(testdir, "extended_register_command2")): bin
+ {
+ testdir: str = path.dirname(tgt_name);
+ results: str = tgt_name +~ ".txt";
+
+ path.mkdirp(testdir);
+
+ io.open(results, "w"): f
+ {
+ f.print("1\n");
+ }
+
+ res := $ %(config_list["args"]) -x -e gxpR << @("q\n");
+
+ check_test(tgt_name, res, results);
+ }
+ }
+
+ path.mkdirp(testdir);
+
+ other_tests_results: []str = config_list["other_tests_results"];
+
+ io.open(path.join(testdir, OTHER_LINE_LEN_RESULTS_NAME), "w"): f
+ {
+ f.print(other_tests_results[0] +~ "\n");
+ }
+
+ io.open(path.join(testdir, OTHER_LINE_LEN70_RESULTS_NAME), "w"): f
+ {
+ f.print(other_tests_results[1] +~ "\n");
+ }
+
+ test sym(path.join(testdir, "line_length1")): bin
+ {
+ env.set env.str(str(config["var"]), "80")
+ {
+ testdir: str = path.dirname(tgt_name);
+
+ other_tests: []str = config_list["other_tests"];
+
+ res := $ %(config_list["args"]) << @(other_tests[3]);
+
+ check_test(tgt_name, res,
+ path.join(testdir, OTHER_LINE_LEN_RESULTS_NAME));
+ }
+ }
+
+ test sym(path.join(testdir, "line_length2")): bin
+ {
+ env.set env.str(str(config["var"]), "2147483647")
+ {
+ testdir: str = path.dirname(tgt_name);
+
+ other_tests: []str = config_list["other_tests"];
+
+ res := $ %(config_list["args"]) << @(other_tests[3]);
+
+ check_test(tgt_name, res,
+ path.join(testdir, OTHER_LINE_LEN70_RESULTS_NAME));
+ }
+ }
+
+ test sym(path.join(testdir, "expr_and_file_args_test")): bin
+ {
+ testdir: str = path.dirname(tgt_name);
+ src_testdir: str = path.join(src_dir, testdir);
+
+ input_file: str = path.join(src_testdir, "add.txt");
+ input: str = io.read_file(input_file);
+ results_file: str = path.join(src_testdir, "add_results.txt");
+ results: str = io.read_file(results_file);
+
+ output_file: str = path.join(testdir, "expr_file_args.txt");
+
+ io.open(output_file, "w"): f
+ {
+ f.print(results +~ results +~ results +~ results);
+ }
+
+ res := $ %(config_list["args"]) -e $input -f $input_file
+ --expression $input --file $input_file
+ -e @(str(config["halt"]));
+
+ check_test(tgt_name, res, output_file);
+ }
+
+ test sym(path.join(testdir, "files_test")): bin
+ {
+ env.set env.str(str(config["var"]), "2147483647")
+ {
+ testdir: str = path.dirname(tgt_name);
+ src_testdir: str = path.join(src_dir, testdir);
+
+ input_file: str = path.join(src_testdir, "add.txt");
+ input: str = io.read_file(input_file);
+ results_file: str = path.join(src_testdir, "add_results.txt");
+ results: str = io.read_file(results_file);
+
+ output_file: str = path.join(testdir, "files.txt");
+
+ io.open(output_file, "w"): f
+ {
+ f.print(results +~ results +~ results +~ results);
+ }
+
+ res := $ %(config_list["args"]) -- $input_file $input_file
+ $input_file $input_file << @(str(config["halt"]));
+
+ check_test(tgt_name, res, output_file);
+ }
+ }
+
+ test sym(path.join(testdir, "line_length3")): bin
+ {
+ env.set env.str(str(config["var"]), "62")
+ {
+ testdir: str = path.dirname(tgt_name);
+
+ other_tests: []str = config_list["other_tests"];
+
+ res := $ %(config_list["args"]) -L << @(other_tests[3]);
+
+ check_test(tgt_name, res,
+ path.join(testdir, OTHER_LINE_LEN_RESULTS_NAME));
+ }
+ }
+
+ test sym(path.join(testdir, "line_length_func")): bin
+ {
+ env.set env.str(str(config["var"]), "62")
+ {
+ testdir: str = path.dirname(tgt_name);
+ results: str = tgt_name +~ ".txt";
+
+ path.mkdirp(testdir);
+
+ io.open(results, "w"): f
+ {
+ f.print("0\n");
+ }
+
+ other_tests: []str = config_list["other_tests"];
+
+ res := $ %(config_list["args"]) -L << @(other_tests[2]);
+
+ check_test(tgt_name, res, results);
+ }
+ }
+
+ test sym(path.join(testdir, "arg")): bin
+ {
+ halt: str = str(config["halt"]);
+
+ res1 := $ %(config_list["args"]) -h << $halt;
+ check_test_retcode(tgt_name, res1.exitcode);
+
+ res2 := $ %(config_list["args"]) -P << $halt;
+ check_test_retcode(tgt_name, res2.exitcode);
+
+ res3 := $ %(config_list["args"]) -R << $halt;
+ check_test_retcode(tgt_name, res3.exitcode);
+
+ res4 := $ %(config_list["args"]) -v << $halt;
+ check_test_retcode(tgt_name, res4.exitcode);
+
+ res5 := $ %(config_list["args"]) -V << $halt;
+ check_test_retcode(tgt_name, res5.exitcode);
+ }
+
+ test sym(path.join(testdir, "leading_zero_arg")): bin
+ {
+ testdir: str = path.dirname(tgt_name);
+ calc: str = path.basename(testdir);
+
+ expected_file: str = tgt_name +~ ".txt";
+
+ expected: str = "0.1\n-0.1\n1.1\n-1.1\n0.1\n-0.1\n";
+
+ io.open(expected_file, "w"): f
+ {
+ f.print(expected);
+ }
+
+ data: str =
+ if calc == "bc"
+ {
+ "0.1\n-0.1\n1.1\n-1.1\n.1\n-.1\n";
+ }
+ else
+ {
+ "0.1pR\n_0.1pR\n1.1pR\n_1.1pR\n.1pR\n_.1pR\n";
+ };
+
+ res := $ %(config_list["args"]) -z << $data;
+
+ check_test(tgt_name, res, expected_file);
+ }
+
+ test sym(path.join(testdir, "invalid_file_arg")): bin
+ {
+ res := $ %(config_list["args"]) -f
+ "astoheusanotehynstahonsetihaotsnuhynstahoaoetusha.txt";
+
+ check_err_test(tgt_name, res);
+ }
+
+ test sym(path.join(testdir, "invalid_option_arg")): bin
+ {
+ other_tests: []str = config_list["other_tests"];
+
+ res := $ %(config_list["args"]) @("-" +~ other_tests[0])
+ -e @(str(config["halt"]));
+
+ check_err_test(tgt_name, res);
+ }
+
+ test sym(path.join(testdir, "invalid_long_option_arg")): bin
+ {
+ other_tests: []str = config_list["other_tests"];
+
+ res := $ %(config_list["args"]) @("--" +~ other_tests[1])
+ -e @(str(config["halt"]));
+
+ check_err_test(tgt_name, res);
+ }
+
+ test sym(path.join(testdir, "unrecognized_option_arg")): bin
+ {
+ res := $ %(config_list["args"]) -u -e @(str(config["halt"]));
+
+ check_err_test(tgt_name, res);
+ }
+
+ test sym(path.join(testdir, "unrecognized_long_option_arg")): bin
+ {
+ res := $ %(config_list["args"]) --uniform -e @(str(config["halt"]));
+
+ check_err_test(tgt_name, res);
+ }
+
+ test sym(path.join(testdir, "no_required_arg_for_option")): bin
+ {
+ res := $ %(config_list["args"]) -f;
+
+ check_err_test(tgt_name, res);
+ }
+
+ test sym(path.join(testdir, "no_required_arg_for_long_option")): bin
+ {
+ res := $ %(config_list["args"]) --file;
+
+ check_err_test(tgt_name, res);
+ }
+
+ test sym(path.join(testdir, "given_arg_for_long_option_with_no_arg")): bin
+ {
+ res := $ %(config_list["args"]) --version=5;
+
+ check_err_test(tgt_name, res);
+ }
+
+ test sym(path.join(testdir, "colon_option")): bin
+ {
+ res := $ %(config_list["args"]) -:;
+
+ check_err_test(tgt_name, res);
+ }
+
+ test sym(path.join(testdir, "colon_long_option")): bin
+ {
+ res := $ %(config_list["args"]) --:;
+
+ check_err_test(tgt_name, res);
+ }
+
+ test sym(path.join(testdir, "builtin_variable_arg_test")): bin
+ {
+ testdir: str = path.dirname(tgt_name);
+ calc: str = path.basename(testdir);
+
+ extra: bool = bool(config["extra_math"]);
+
+ output: str =
+ if extra
+ {
+ "14\n15\n16\n17.25\n";
+ }
+ else
+ {
+ "14\n15\n16\n";
+ };
+
+ output_file: str = tgt_name +~ ".txt";
+
+ io.open(output_file, "w"): f
+ {
+ f.print(output);
+ }
+
+ data: str =
+ if extra
+ {
+ if calc == "bc"
+ {
+ "s=scale;i=ibase;o=obase;t=seed@2;ibase=A;obase=A;s;i;o;t;";
+ }
+ else
+ {
+ "J2@OIKAiAopRpRpRpR";
+ }
+ }
+ else
+ {
+ if calc == "bc"
+ {
+ "s=scale;i=ibase;o=obase;ibase=A;obase=A;s;i;o;";
+ }
+ else
+ {
+ "OIKAiAopRpRpR";
+ }
+ };
+
+ args: []str =
+ if extra
+ {
+ @[ "-S14", "-I15", "-O16", "-E17.25" ];
+ }
+ else
+ {
+ @[ "-S14", "-I15", "-O16" ];
+ };
+
+ res1 := $ %(config_list["args"]) %(args) << $data;
+ check_test(tgt_name, res1, output_file);
+
+ long_args: []str =
+ if extra
+ {
+ @[ "--scale=14", "--ibase=15", "--obase=16", "--seed=17.25" ];
+ }
+ else
+ {
+ @[ "--scale=14", "--ibase=15", "--obase=16" ];
+ };
+
+ res2 := $ %(config_list["args"]) %(long_args) << $data;
+ check_test(tgt_name, res2, output_file);
+ }
+
+ if calc == "bc"
+ {
+ io.open(path.join(testdir, OTHER_MATHLIB_SCALE_RESULTS_NAME), "w"): f
+ {
+ f.print("100\n");
+ }
+
+ test sym(path.join(testdir, "builtin_var_arg_with_lib")): bin
+ {
+ testdir: str = path.dirname(tgt_name);
+ results_file: str = path.join(testdir,
+ OTHER_MATHLIB_SCALE_RESULTS_NAME);
+
+ res := $ %(config_list["args"]) -S100 -l << @("scale\n");
+
+ check_test(tgt_name, res, results_file);
+ }
+
+ test sym(path.join(testdir, "builtin_variable_long_arg_with_lib")): bin
+ {
+ testdir: str = path.dirname(tgt_name);
+ results_file: str = path.join(testdir,
+ OTHER_MATHLIB_SCALE_RESULTS_NAME);
+
+ res := $ %(config_list["args"]) --scale=100 --mathlib <<
+ @("scale\n");
+
+ check_test(tgt_name, res, results_file);
+ }
+
+ test sym(path.join(testdir, "builtin_var_arg_with_lib_env_arg")): bin
+ {
+ env.set env.str("BC_ENV_ARGS", "-l")
+ {
+ testdir: str = path.dirname(tgt_name);
+ results_file: str = path.join(testdir,
+ OTHER_MATHLIB_SCALE_RESULTS_NAME);
+
+ res := $ %(config_list["args"]) -S100 << @("scale\n");
+
+ check_test(tgt_name, res, results_file);
+ }
+ }
+
+ test sym(path.join(testdir,
+ "builtin_var_long_arg_with_lib_env_arg")): bin
+ {
+ env.set env.str("BC_ENV_ARGS", "-l")
+ {
+ testdir: str = path.dirname(tgt_name);
+ results_file: str = path.join(testdir,
+ OTHER_MATHLIB_SCALE_RESULTS_NAME);
+
+ res := $ %(config_list["args"]) --scale=100 << @("scale\n");
+
+ check_test(tgt_name, res, results_file);
+ }
+ }
+
+ test sym(path.join(testdir, "builtin_var_env_arg_with_lib_arg")): bin
+ {
+ env.set env.str("BC_ENV_ARGS", "-S100")
+ {
+ testdir: str = path.dirname(tgt_name);
+ results_file: str = path.join(testdir,
+ OTHER_MATHLIB_SCALE_RESULTS_NAME);
+
+ res := $ %(config_list["args"]) -l << @("scale\n");
+
+ check_test(tgt_name, res, results_file);
+ }
+ }
+
+ test sym(path.join(testdir,
+ "builtin_var_long_env_arg_with_lib_arg")): bin
+ {
+ env.set env.str("BC_ENV_ARGS", "--scale=100")
+ {
+ testdir: str = path.dirname(tgt_name);
+ results_file: str = path.join(testdir,
+ OTHER_MATHLIB_SCALE_RESULTS_NAME);
+
+ res := $ %(config_list["args"]) -l << @("scale\n");
+
+ check_test(tgt_name, res, results_file);
+ }
+ }
+
+ test sym(path.join(testdir, "limits")): bin
+ {
+ res := $ %(config_list["args"]) << @("limits\n");
+
+ check_test_retcode(tgt_name, res.exitcode);
+
+ if str(res.stdout) == "" || str(res.stdout) == "\n"
+ {
+ error("Test \"" +~ tgt_name +~ "\" did not produce output");
+ }
+ }
+ }
+
+ test sym(path.join(testdir, "bad_arg_for_builtin_var_option")): bin
+ {
+ testdir: str = path.dirname(tgt_name);
+ calc: str = path.basename(testdir);
+
+ scale: str = if calc == "bc" { "scale\n"; } else { "K\n"; };
+
+ res1 := $ %(config_list["args"]) --scale=18923c.rlg << $scale;
+
+ check_err_test(tgt_name, res1);
+
+ if bool(config["extra_math"])
+ {
+ seed: str = if calc == "bc" { "seed\n"; } else { "J\n"; };
+
+ res2 := $ %(config_list["args"]) --seed=18923c.rlg << $seed;
+
+ check_err_test(tgt_name, res2);
+ }
+ }
+
+ test sym(path.join(testdir, "directory_as_file")): bin
+ {
+ testdir: str = path.dirname(tgt_name);
+
+ res := $ %(config_list["args"]) $testdir;
+
+ check_err_test(tgt_name, res);
+ }
+
+ test sym(path.join(testdir, "binary_file")): bin
+ {
+ res := $ %(config_list["args"]) @(file_dep);
+
+ check_err_test(tgt_name, res);
+ }
+
+ test sym(path.join(testdir, "binary_stdin")): bin
+ {
+ res := $ %(config_list["args"]) < @(file_dep);
+
+ check_err_test(tgt_name, res);
+ }
+}
+
+fn register_timeconst_tests(
+ bin: str,
+ testdir: str,
+ src_testdir: str,
+) -> void
+{
+ timeconst: str = path.join(testdir, "scripts/timeconst.bc");
+
+ if !path.isfile(path.join(src_dir, timeconst))
+ {
+ io.eprint("Warning: " +~ timeconst +~ " does not exist\n");
+ io.eprint(timeconst +~ " is not part of this bc because of " +~
+ "license incompatibility\n");
+ io.eprint("To test it, get it from the Linux kernel at " +~
+ "`kernel/time/timeconst.bc`\n");
+ io.eprint("Skipping...\n");
+
+ return;
+ }
+
+ for i: range(1001)
+ {
+ test sym(path.join(timeconst, str(i)))
+ {
+ idx: str = path.basename(tgt_name) +~ "\n";
+ file: str = path.join(src_dir, path.dirname(tgt_name));
+
+ // Generate.
+ res1 := $ bc -q $file << $idx;
+
+ if res1.exitcode != 0
+ {
+ io.eprint("Other bc is not GNU compatible. Skipping...\n");
+ return;
+ }
+
+ // Run.
+ res2 := $ %(config_list["args"]) -q $file << $idx;
+
+ if res2.exitcode != 0 || res2.stdout != res1.stdout
+ {
+ error("\nFailed on input: " +~ idx +~ "\n");
+ }
+ }
+ }
+}
+
+fn register_history_tests(
+ bin: str,
+ testdir: str,
+ src_testdir: str,
+) -> void
+{
+ calc: str = path.basename(testdir);
+
+ src_test_scriptdir: str = path.dirname(src_testdir);
+
+ len_res := $ @(path.join(src_test_scriptdir, "history.py")) $calc -a;
+
+ if len_res.exitcode != 0
+ {
+ io.eprint("Python 3 with pexpect doesn't work. Skipping history tests");
+ return;
+ }
+
+ len: usize = usize(str(len_res.stdout));
+
+ for i: range(len)
+ {
+ test sym(calc +~ "/history/" +~ str(i)): bin
+ {
+ name: str = tgt_name;
+ parts: []str = name.split("/");
+
+ calc: str = parts[0];
+ idx: str= parts[2];
+
+ src_testdir: str = path.join(src_dir, "tests");
+
+ $ @(path.join(src_testdir, "history.py")) -t $calc $idx @(file_dep);
+ }
+ }
+}
+
+/**
+ * Generates all of the test targets for an executable.
+ * @param name The base name of the executable.
+ * @param targets The targets that tests should depend on.
+ */
+fn exe_tests(name: str) -> void
+{
+ bin: str = exe_name(name);
+
+ testdir: str = path.join("tests", name);
+ src_testdir: str = path.join(src_dir, testdir);
+
+ halt: str = if name == "bc" { "halt"; } else { "q"; };
+ gen_options: []str = if name == "bc" { @[ "-lq" ]; };
+ options: []str = if name == "bc" { @[ "-lqc" ]; } else { @[ "-xc" ]; };
+
+ other_num: str = "10000000000000000000000000000000000000000000000000" +~
+ "0000000000000000000000000000";
+ other_num70: str = "10000000000000000000000000000000000000000000000" +~
+ "000000000000000000000\\\n0000000000";
+
+ other_tests: []str =
+ if name == "bc"
+ {
+ @[ "x", "extended-register", "line_length()", other_num ];
+ }
+ else
+ {
+ @[ "l", "mathlib", "glpR", other_num +~ "pR" ];
+ };
+
+ other_tests_results: []str = @[ other_num, other_num70 ];
+
+ var: str = name.toupper() +~ "_LINE_LENGTH";
+
+ script_options: []str =
+ if name == "bc"
+ {
+ @[ "-lqC" ];
+ }
+ else
+ {
+ @[ "-xC" ];
+ };
+
+ error_options: []str = if name == "bc" { @[ "-ls" ]; } else { @[ "-x" ]; };
+
+ args: []str =
+ if bool(config["valgrind"])
+ {
+ VALGRIND_ARGS +~ @[ "./" +~ bin ];
+ }
+ else
+ {
+ @[ "./" +~ bin ];
+ };
+
+ test_config: Gaml = @(gaml){
+ args: $args
+ halt: $halt
+ gen_options: $gen_options
+ options: $options
+ script_options: $script_options
+ error_options: $error_options
+ other_tests: $other_tests
+ other_tests_results: $other_tests_results
+ var: $var
+ };
+
+ push test_config: config_stack
+ {
+ extra: bool = bool(config["extra_math"]);
+
+ register_standard_tests(bin, testdir, src_testdir, extra);
+ register_script_tests(bin, testdir, src_testdir, extra);
+ register_stdin_tests(bin, testdir, src_testdir);
+ register_read_tests(bin, testdir, src_testdir);
+ register_error_tests(bin, testdir, src_testdir);
+ register_other_tests(bin, testdir, src_testdir, extra);
+
+ if name == "bc" && bool(config["generated_tests"]) &&
+ path.isfile(path.join(src_testdir, "scripts/timeconst.bc"))
+ {
+ register_timeconst_tests(bin, testdir, src_testdir);
+ }
+
+ if host.os != "Windows" && sym(config["history"]) == @builtin
+ {
+ register_history_tests(bin, testdir, src_testdir);
+ }
+ }
+}
+
+/**
+ * Gets the `$BINDIR`, including the `$DESTDIR`. This generates the default
+ * value if it wasn't set.
+ * @return The `$BINDIR`, with the `$DESTDIR`.
+ */
+fn get_bindir() -> str
+{
+ temp: str = str(config["bindir"]);
+
+ bindir: str =
+ if temp == ""
+ {
+ path.join(str(config["prefix"]), "bin");
+ }
+ else
+ {
+ temp;
+ };
+
+ return path.join(DESTDIR, bindir);
+}
+
+/**
+ * Gets the `$LIBDIR`, including the `$DESTDIR`. This generates the default
+ * value if it wasn't set.
+ * @return The `$LIBDIR`, with the `$DESTDIR`.
+ */
+fn get_libdir() -> str
+{
+ temp: str = str(config["libdir"]);
+
+ libdir: str =
+ if temp == ""
+ {
+ path.join(str(config["prefix"]), "lib");
+ }
+ else
+ {
+ temp;
+ };
+
+ return path.join(DESTDIR, libdir);
+}
+
+/**
+ * Gets the `$INCLUDEDIR`, including the `$DESTDIR`. This generates the default
+ * value if it wasn't set.
+ * @return The `$INCLUDEDIR`, with the `$DESTDIR`.
+ */
+fn get_includedir() -> str
+{
+ temp: str = str(config["includedir"]);
+
+ includedir: str =
+ if temp == ""
+ {
+ path.join(str(config["prefix"]), "include");
+ }
+ else
+ {
+ temp;
+ };
+
+ return path.join(DESTDIR, includedir);
+}
+
+/**
+ * Gets the `$PC_PATH`, including the `$DESTDIR`. This generates the default
+ * value if it wasn't set.
+ * @return The `$PC_PATH`, with the `$DESTDIR`.
+ */
+fn get_pc_path() -> str
+{
+ pc_path: str =
+ if str(config["pc_path"]) == ""
+ {
+ res := $ pkg-config --variable=pc_path pkg-config;
+
+ str(res.stdout);
+ }
+ else
+ {
+ str(config["pc_path"]);
+ };
+
+ return path.join(DESTDIR, pc_path);
+}
+
+/**
+ * Gets the `$DATAROOTDIR`, including the `$DESTDIR`. This generates the default
+ * value if it wasn't set.
+ * @return The `$DATAROOTDIR`, with the `$DESTDIR`.
+ */
+fn get_datarootdir() -> str
+{
+ temp: str = str(config["datarootdir"]);
+
+ datarootdir: str =
+ if temp == ""
+ {
+ path.join(str(config["prefix"]), "share");
+ }
+ else
+ {
+ temp;
+ };
+
+ return path.join(DESTDIR, datarootdir);
+}
+
+/**
+ * Gets the `$DATADIR`, including the `$DESTDIR`. This generates the default
+ * value if it wasn't set.
+ * @return The `$DATADIR`, with the `$DESTDIR`.
+ */
+fn get_datadir() -> str
+{
+ temp: str = str(config["datadir"]);
+
+ datadir: str =
+ if temp == ""
+ {
+ get_datarootdir();
+ }
+ else
+ {
+ temp;
+ };
+
+ return path.join(DESTDIR, datadir);
+}
+
+/**
+ * Gets the `$MANDIR`, including the `$DESTDIR`. This generates the default
+ * value if it wasn't set.
+ * @return The `$MANDIR`, with the `$DESTDIR`.
+ */
+fn get_mandir() -> str
+{
+ temp: str = str(config["mandir"]);
+
+ mandir: str =
+ if temp == ""
+ {
+ path.join(get_datadir(), "man");
+ }
+ else
+ {
+ temp;
+ };
+
+ return path.join(DESTDIR, mandir);
+}
+
+/**
+ * Gets the `$MAN1DIR`, including the `$DESTDIR`. This generates the default
+ * value if it wasn't set.
+ * @return The `$MAN1DIR`, with the `$DESTDIR`.
+ */
+fn get_man1dir() -> str
+{
+ temp: str = str(config["man1dir"]);
+
+ man1dir: str =
+ if temp == ""
+ {
+ path.join(get_mandir(), "man1");
+ }
+ else
+ {
+ temp;
+ };
+
+ return path.join(DESTDIR, man1dir);
+}
+
+/**
+ * Gets the `$MAN3DIR`, including the `$DESTDIR`. This generates the default
+ * value if it wasn't set.
+ * @return The `$MAN3DIR`, with the `$DESTDIR`.
+ */
+fn get_man3dir() -> str
+{
+ temp: str = str(config["man3dir"]);
+
+ man3dir: str =
+ if temp == ""
+ {
+ path.join(get_mandir(), "man3");
+ }
+ else
+ {
+ temp;
+ };
+
+ return path.join(DESTDIR, man3dir);
+}
diff --git a/contrib/bc/build.rig b/contrib/bc/build.rig
new file mode 100644
index 000000000000..55f8c85daddd
--- /dev/null
+++ b/contrib/bc/build.rig
@@ -0,0 +1,575 @@
+/*
+ * *****************************************************************************
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * *****************************************************************************
+ *
+ * The build script file.
+ *
+ */
+
+if OS == "Windows" && bool(config["lto"])
+{
+ error("Link-time optimization is not supported on Windows");
+}
+
+if LIBRARY_ENABLED == "0"
+{
+ if OS != "Windows" && NLS_ENABLED != "0"
+ {
+ io.eprint("Testing NLS...\n");
+
+ clang_flags: []str =
+ if CC contains "clang"
+ {
+ @[ "-Wno_unreachable-code" ];
+ };
+
+ flags: []str = clang_flags +~ @[
+ DEFOPT +~ "BC_ENABLE_NLS=1",
+ DEFOPT +~ "BC_ENABLED=" +~ BC_ENABLED,
+ DEFOPT +~ "DC_ENABLED=" +~ DC_ENABLED,
+ DEFOPT +~ "BC_ENABLE_HISTORY=0",
+ DEFOPT +~ "BC_ENABLE_LIBRARY=0",
+ DEFOPT +~ "BC_ENABLE_AFL=0",
+ DEFOPT +~ "BC_ENABLE_EXTRA_MATH=" +~ EXTRA_MATH_ENABLED,
+ DEFOPT +~ "BC_ENABLE_OSSFUZZ=0",
+ DEFOPT +~ "_POSIX_C_SOURCE=200809L",
+ DEFOPT +~ "_XOPEN_SOURCE=700",
+ INCOPT,
+ ];
+
+ res := $ $CC %(flags) -c @(path.join(src_dir, "src/vm.c")) -E;
+
+ if res.exitcode != 0
+ {
+ if FORCE
+ {
+ io.eprint("Forcing NLS...\n");
+ }
+ else
+ {
+ error("NLS does not work\n");
+ }
+ }
+ else
+ {
+ if path.isfile("vm.o")
+ {
+ path.rm("vm.o");
+ }
+
+ io.eprint("NLS works.\n\n");
+ io.eprint("Testing gencat...\n");
+
+ res2 := $ gencat ./en_US.cat
+ @(path.join(src_dir, "locales/en_US.msg"));
+
+ if res2.exitcode != 0
+ {
+ if FORCE
+ {
+ io.eprint("Forcing NLS...\n");
+ }
+ else
+ {
+ error("gencat does not work\n");
+ }
+ }
+ else
+ {
+ io.eprint("gencat works.\n\n");
+
+ if platform != host
+ {
+ error("Cross compiles will not work!\n\n");
+ }
+ }
+ }
+ }
+
+ if OS != "Windows" && sym(config["history"]) != @none
+ {
+ io.eprint("Testing history...\n");
+
+ flags: []str = @[
+ DEFOPT +~ "BC_ENABLE_HISTORY=1",
+ DEFOPT +~ "BC_ENABLED=" +~ BC_ENABLED,
+ DEFOPT +~ "DC_ENABLED=" +~ DC_ENABLED,
+ DEFOPT +~ "BC_ENABLE_NLS=" +~ NLS_ENABLED,
+ DEFOPT +~ "BC_ENABLE_LIBRARY=0",
+ DEFOPT +~ "BC_ENABLE_AFL=0",
+ DEFOPT +~ "BC_ENABLE_EDITLINE=" +~ EDITLINE_ENABLED,
+ DEFOPT +~ "BC_ENABLE_READLINE=" +~ READLINE_ENABLED,
+ DEFOPT +~ "BC_ENABLE_EXTRA_MATH=" +~ EXTRA_MATH_ENABLED,
+ DEFOPT +~ "BC_ENABLE_OSSFUZZ=0",
+ DEFOPT +~ "_POSIX_C_SOURCE=200809L",
+ DEFOPT +~ "_XOPEN_SOURCE=700",
+ INCOPT,
+ ];
+
+ res := $ $CC %(flags) -c @(path.join(src_dir, "src/history.c")) -E;
+
+ if res.exitcode != 0
+ {
+ if FORCE
+ {
+ io.eprint("Forcing history...\n");
+ }
+ else
+ {
+ error("History does not work\n");
+ }
+ }
+ else
+ {
+ if path.isfile("history.o")
+ {
+ path.rm("history.o");
+ }
+
+ io.eprint("History works.\n\n");
+ }
+ }
+}
+
+freebsd_flags: []str =
+if OS != "FreeBSD"
+{
+ @[ DEFOPT +~ "_POSIX_C_SOURCE=200809L", DEFOPT +~ "_XOPEN_SOURCE=700" ];
+};
+
+macos: bool = (OS == "Darwin");
+
+macos_flags: []str =
+if macos
+{
+ @[ DEFOPT +~ "_DARWIN_C_SOURCE" ];
+};
+
+openbsd_flags: []str =
+if OS == "OpenBSD"
+{
+ if READLINE_ENABLED != "0"
+ {
+ error("Cannot use readline on OpenBSD");
+ }
+
+ @[ DEFOPT +~ "_BSD_SOURCE" ];
+};
+
+strip_flag: []str =
+if OS != "Windows" && !bool(config["debug"]) && !macos && bool(config["strip"])
+{
+ @[ "-s" ];
+};
+
+lto_flag: []str =
+if bool(config["lto"])
+{
+ @[ "-flto" ];
+};
+
+strict_flags: []str =
+if bool(config["strict"])
+{
+ // Strict build only works for GCC and Clang, so we do want to set that
+ // here.
+ if CC contains "gcc" || CC contains "clang"
+ {
+ // These are the standard strict build flags for both compilers.
+ std_strict: []str = @[ "-Wall", "-Wextra", "-Werror", "-pedantic" ];
+
+ // Clang has -Weverything, which I ensure Yc builds under.
+ //
+ // I also want unlimited errors because Clang is my development
+ // compiler; it caps at 20 by default.
+ compiler_strict: []str =
+ if CC contains "clang"
+ {
+ // Oh, and add the standard.
+ @[ "-Weverything", "-ferror-limit=100000", "-Wno-padded",
+ "-Wno-unknown-warning-option", "-Wno-unsafe-buffer-usage",
+ "-Wno-documentation-unknown-command", "-Wno-pre-c11-compat",
+ "-Wno-enum-enum-conversion", "-Wno-switch-default" ];
+ };
+
+ // Return the combination of the sets.
+ std_strict +~ compiler_strict;
+ }
+ else if OS == "Windows"
+ {
+ // Return the combo of the strict options, the standard, and the
+ // sanitizer defines.
+ @[ "/W4", "/WX", "/wd\"4996\"", "/permissive-" ];
+ }
+};
+
+version_contents: str = io.read_file(path.join(src_dir, "VERSION.txt"));
+version_lines: []str = version_contents.split("\n");
+version: str = version_lines[0];
+
+version_flag: []str = @[ DEFOPT +~ "VERSION=" +~ version ];
+
+other_flags: []str = freebsd_flags +~ macos_flags +~ openbsd_flags +~
+ lto_flag +~ strict_flags +~ version_flag +~
+if bool(config["debug"])
+{
+ @[ compiler_db["opt.debug"] ];
+};
+
+history_files: []str =
+if HISTORY != @none
+{
+ HISTORY_C_FILES;
+};
+
+c_files: []str =
+if BUILD_MODE == @both
+{
+ COMMON_C_FILES +~ EXEC_C_FILES +~ BC_C_FILES +~ DC_C_FILES +~ history_files;
+}
+else if BUILD_MODE == @bc
+{
+ COMMON_C_FILES +~ EXEC_C_FILES +~ BC_C_FILES +~ history_files;
+}
+else if BUILD_MODE == @dc
+{
+ COMMON_C_FILES +~ EXEC_C_FILES +~ DC_C_FILES +~ history_files;
+}
+else
+{
+ COMMON_C_FILES +~ LIBRARY_C_FILES;
+};
+
+build_config: Gaml = @(gaml){
+ other_cflags: $other_flags
+ strip_flag: $strip_flag
+};
+
+targets: []str =
+push build_config: config_stack
+{
+ gen_o_files: []str =
+ if BUILD_MODE != @library
+ {
+ @[
+ txt2o("gen/lib.bc", "bc_lib", "bc_lib_name", "BC_ENABLED", true),
+ txt2o("gen/lib2.bc", "bc_lib2", "bc_lib2_name",
+ "BC_ENABLED && BC_ENABLE_EXTRA_MATH", true),
+ txt2o("gen/bc_help.txt", "bc_help", "", "BC_ENABLED", false),
+ txt2o("gen/dc_help.txt", "dc_help", "", "DC_ENABLED", false),
+ ];
+ };
+
+ obj_files: []str = gen_o_files +~
+ for f: c_files
+ {
+ c2o(f);
+ };
+
+ if BUILD_MODE == @both || BUILD_MODE == @bc
+ {
+ if OS != "Windows" && bool(config["install_manpages"])
+ {
+ src: str = path.join("manuals/bc", BUILD_TYPE +~ ".1");
+
+ target BC_MANPAGE: src
+ {
+ $ cp -f @(file_dep) @(tgt);
+ }
+ }
+
+ exe(BC_BIN, obj_files);
+ }
+
+ if BUILD_MODE == @both || BUILD_MODE == @dc
+ {
+ if OS != "Windows" && bool(config["install_manpages"])
+ {
+ src: str = path.join("manuals/dc", BUILD_TYPE +~ ".1");
+
+ target DC_MANPAGE: src
+ {
+ $ cp -f @(file_dep) @(tgt);
+ }
+ }
+
+ if BUILD_MODE == @both
+ {
+ ln(DC_BIN, BC_BIN);
+ }
+ else
+ {
+ exe(DC_BIN, obj_files);
+ }
+ }
+
+ if BUILD_MODE == @library
+ {
+ lib(LIBRARY, obj_files);
+ }
+
+ if BUILD_MODE == @both
+ {
+ @[ BC_BIN, DC_BIN ];
+ }
+ else if BUILD_MODE == @bc
+ {
+ @[ DC_BIN ];
+ }
+ else if BUILD_MODE == @dc
+ {
+ @[ DC_BIN ];
+ }
+ else
+ {
+ includedir: str = get_includedir();
+ libdir: str = get_libdir();
+
+ pc_config: Gaml = @(gaml){
+ INCLUDEDIR: $includedir
+ LIBDIR: $libdir
+ VERSION: $version
+ };
+
+ push pc_config: config_stack
+ {
+ target PC_FILE: PC_FILE +~ ".in"
+ {
+ configure_file(file_dep, tgt, "%%");
+ }
+ }
+
+ @[ LIBRARY, PC_FILE ];
+ }
+};
+
+if OS != "Windows"
+{
+ if LIBRARY_ENABLED == "0"
+ {
+ target @install: targets
+ {
+ bindir: str = get_bindir();
+
+ if BC_ENABLED != "0"
+ {
+ $ $SAFE_INSTALL $EXEC_INSTALL_MODE $BC_BIN
+ @(path.join(bindir, BC_BIN));
+ }
+
+ if DC_ENABLED != "0"
+ {
+ if BC_ENABLED != "0"
+ {
+ $ ln -sf @("./" +~ BC_BIN) @(path.join(bindir, DC_BIN));
+ }
+ else
+ {
+ $ $SAFE_INSTALL $EXEC_INSTALL_MODE $BC_BIN
+ @(path.join(bindir, BC_BIN));
+ }
+ }
+
+ if NLS_ENABLED != "0"
+ {
+ locale_install_args: []str =
+ if sym(config["locales"]) == @all
+ {
+ @[ "-l" ];
+ };
+
+ if DESTDIR != ""
+ {
+ $ @(path.join(src_dir, "scripts/locale_install.sh"))
+ %(locale_install_args) @(str(config["nlspath"]))
+ $MAINEXEC $DESTDIR;
+ }
+ else
+ {
+ $ @(path.join(src_dir, "scripts/locale_install.sh"))
+ %(locale_install_args) @(str(config["nlspath"]))
+ $MAINEXEC;
+ }
+ }
+
+ if bool(config["install_manpages"])
+ {
+ man1dir: str = get_man1dir();
+
+ if BC_ENABLED != "0"
+ {
+ $ rm -rf @(path.join(man1dir, BC_MANPAGE));
+ }
+
+ if DC_ENABLED != "0"
+ {
+ $ rm -rf @(path.join(man1dir, DC_MANPAGE));
+ }
+ }
+ }
+
+ target @uninstall
+ {
+ bindir: str = get_bindir();
+
+ if BC_ENABLED != "0"
+ {
+ $ rm -rf @(path.join(bindir, BC_BIN));
+ }
+
+ if DC_ENABLED != "0"
+ {
+ $ rm -rf @(path.join(bindir, DC_BIN));
+ }
+
+ if NLS_ENABLED != "0"
+ {
+ if DESTDIR != ""
+ {
+ $ @(path.join(src_dir, "scripts/locale_uninstall.sh"))
+ @(str(config["nlspath"])) $MAINEXEC $DESTDIR;
+ }
+ else
+ {
+ $ @(path.join(src_dir, "scripts/locale_uninstall.sh"))
+ @(str(config["nlspath"])) $MAINEXEC;
+ }
+ }
+
+ if bool(config["install_manpages"])
+ {
+ man1dir: str = get_man1dir();
+ $ rm -rf @(path.join(man1dir, BC_MANPAGE))
+ @(path.join(man1dir, DC_MANPAGE));
+ }
+ }
+ }
+ else
+ {
+ target @install: targets, BCL_HEADER_PATH
+ {
+ full_libdir: str = get_libdir();
+
+ $ $SAFE_INSTALL $EXEC_INSTALL_MODE @(file_dep)
+ @(path.join(full_libdir, file_dep));
+
+ full_pc_path: str = get_pc_path();
+ bcl_pc: str = file_deps[1];
+
+ $ $SAFE_INSTALL $MANPAGE_INSTALL_MODE $bcl_pc
+ @(path.join(full_pc_path, bcl_pc));
+
+ full_includedir: str = get_includedir();
+
+ $ $SAFE_INSTALL $MANPAGE_INSTALL_MODE @(file_deps[2])
+ @(path.join(full_includedir, BCL_HEADER));
+
+ if bool(config["install_manpages"])
+ {
+ $ $SAFE_INSTALL $MANPAGE_INSTALL_MODE
+ @(path.join(src_dir, path.join("manuals", BCL_MANPAGE)))
+ @(path.join(get_man3dir(), BCL_MANPAGE));
+ }
+ }
+
+ target @uninstall
+ {
+ $ rm -rf @(path.join(get_libdir(), LIBRARY))
+ @(path.join(get_pc_path(), PC_FILE))
+ @(path.join(get_includedir(), BCL_HEADER));
+
+ if bool(config["install_manpages"])
+ {
+ $ rm -rf @(path.join(get_man3dir(), BCL_MANPAGE));
+ }
+ }
+ }
+}
+
+// If the platform matches the host, we can run the test suite.
+if platform == host
+{
+ // If we have the library, build and run that test.
+ if BUILD_MODE == @library
+ {
+ libtesto: str = c2o("tests/bcl.c");
+
+ libtest: str = "bcl";
+
+ exe(libtest, @[ libtesto, targets[0] ]);
+
+ test @bcl: libtest
+ {
+ $ @(str(tgt_name));
+ }
+ }
+ else
+ {
+ if BUILD_MODE != @dc
+ {
+ exe_tests("bc");
+ }
+
+ if BUILD_MODE != @bc
+ {
+ exe_tests("dc");
+ }
+
+ target @clean_tests
+ {
+ for f: path.find_ext(build_dir, "txt")
+ {
+ path.rm(f);
+ }
+ }
+ }
+}
+
+target "bitfuncgen"
+{
+ error("TODO: Make this");
+}
+
+target @bitfuncgen: "bitfuncgen"
+{
+ error("TODO: Make this");
+}
+
+target "ministat"
+{
+ error("TODO: Make this");
+}
+
+target @ministat: "ministat"
+{
+ error("TODO: Make this");
+}
+
+target @all: targets;
diff --git a/contrib/bc/compile_flags.txt b/contrib/bc/compile_flags.txt
index 3324798013c6..64af9a35e75e 100644
--- a/contrib/bc/compile_flags.txt
+++ b/contrib/bc/compile_flags.txt
@@ -1,6 +1,9 @@
-Weverything
-pedantic
-Wno-unsafe-buffer-usage
+-Wno-pre-c11-compat
+-Wno-unknown-warning-option
+-Wno-switch-default
-D_POSIX_C_SOURCE=200809L
-D_XOPEN_SOURCE=700
-D_BSD_SOURCE
diff --git a/contrib/bc/configure.sh b/contrib/bc/configure.sh
index 442165d15693..92ff45cca84a 100755
--- a/contrib/bc/configure.sh
+++ b/contrib/bc/configure.sh
@@ -2,7 +2,7 @@
#
# SPDX-License-Identifier: BSD-2-Clause
#
-# Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+# Copyright (c) 2018-2025 Gavin D. Howard and contributors.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
@@ -55,7 +55,7 @@ usage() {
printf ' %s [-a|-bD|-dB|-c] [-CeEfgGHilmMNPrtTvz] [-O OPT_LEVEL] [-k KARATSUBA_LEN]\\\n' "$script"
printf ' [-s SETTING] [-S SETTING] [-p TYPE]\n'
printf ' %s \\\n' "$script"
- printf ' [--library|--bc-only --disable-dc|--dc-only --disable-bc|--coverage] \\\n'
+ printf ' [--library|--bc-only --disable-dc|--dc-only --disable-bc] \\\n'
printf ' [--force --debug --disable-extra-math --disable-generated-tests] \\\n'
printf ' [--disable-history --disable-man-pages --disable-nls --disable-strip] \\\n'
printf ' [--enable-editline] [--enable-readline] [--enable-internal-history] \\\n'
@@ -74,7 +74,7 @@ usage() {
printf '\n'
printf ' -a, --library\n'
printf ' Build the libbcl instead of the programs. This is meant to be used with\n'
- printf ' Other software like programming languages that want to make use of the\n'
+ printf ' other software like programming languages that want to make use of the\n'
printf ' parsing and math capabilities. This option will install headers using\n'
printf ' `make install`.\n'
printf ' -b, --bc-only\n'
@@ -83,10 +83,6 @@ usage() {
printf ' -B, --disable-bc\n'
printf ' Disable bc. It is an error if "-b", "--bc-only", "-D", or "--disable-dc"\n'
printf ' are specified too.\n'
- printf ' -c, --coverage\n'
- printf ' Generate test coverage code. Requires gcov and gcovr.\n'
- printf ' It is an error if either "-b" ("-D") or "-d" ("-B") is specified.\n'
- printf ' Requires a compiler that use gcc-compatible coverage options\n'
printf ' -C, --disable-clean\n'
printf ' Disable the clean that configure.sh does before configure.\n'
printf ' -d, --dc-only\n'
@@ -133,8 +129,6 @@ usage() {
printf ' Installs all locales, regardless of how many are on the system. This\n'
printf ' option is useful for package maintainers who want to make sure that\n'
printf ' a package contains all of the locales that end users might need.\n'
- printf ' -m, --enable-memcheck\n'
- printf ' Enable memcheck mode, to ensure no memory leaks. For development only.\n'
printf ' -M, --disable-man-pages\n'
printf ' Disable installing manpages.\n'
printf ' -N, --disable-nls\n'
@@ -172,17 +166,9 @@ usage() {
printf ' Set the default named by SETTING to off. See below for possible values\n'
printf ' for SETTING. For multiple instances of the -s or -S for the the same\n'
printf ' setting, the last one is used.\n'
- printf ' -t, --enable-test-timing\n'
- printf ' Enable the timing of tests. This is for development only.\n'
printf ' -T, --disable-strip\n'
printf ' Disable stripping symbols from the compiled binary or binaries.\n'
printf ' Stripping symbols only happens when debug mode is off.\n'
- printf ' -v, --enable-valgrind\n'
- printf ' Enable a build appropriate for valgrind. For development only.\n'
- printf ' -z, --enable-fuzz-mode\n'
- printf ' Enable fuzzing mode. THIS IS FOR DEVELOPMENT ONLY.\n'
- printf ' -Z, --enable-ossfuzz-mode\n'
- printf ' Enable fuzzing mode for OSS-Fuzz. THIS IS FOR DEVELOPMENT ONLY.\n'
printf ' --prefix PREFIX\n'
printf ' The prefix to install to. Overrides "$PREFIX" if it exists.\n'
printf ' If PREFIX is "/usr", install path will be "/usr/bin".\n'
@@ -254,12 +240,12 @@ usage() {
printf ' path (or contain one). This is treated the same as the POSIX\n'
printf ' definition of $NLSPATH (see POSIX environment variables for\n'
printf ' more information). Default is "/usr/share/locale/%%L/%%N".\n'
- printf ' PC_PATH The location to install pkg-config files to. Must be an\n'
+ printf ' PC_PATH The location to install pkg-config files to. Must be a\n'
printf ' path or contain one. Default is the first path given by the\n'
printf ' output of `pkg-config --variable=pc_path pkg-config`.\n'
printf ' EXECSUFFIX The suffix to append to the executable names, used to not\n'
printf ' interfere with other installed bc executables. Default is "".\n'
- printf ' EXECPREFIX The prefix to append to the executable names, used to not\n'
+ printf ' EXECPREFIX The prefix to prepend to the executable names, used to not\n'
printf ' interfere with other installed bc executables. Default is "".\n'
printf ' DESTDIR For package creation. Default is "". If it is empty when\n'
printf ' `%s` is run, it can also be passed to `make install`\n' "$script"
@@ -482,7 +468,7 @@ find_src_files() {
}
# This function generates a list of files to go into the Makefile. It generates
-# the list of object files, as well as the list of test coverage files.
+# the list of object files.
#
# @param contents The contents of the Makefile template to put the list of
# files into.
@@ -503,8 +489,6 @@ gen_file_list() {
_gen_file_list_needle_src="SRC"
_gen_file_list_needle_obj="OBJ"
- _gen_file_list_needle_gcda="GCDA"
- _gen_file_list_needle_gcno="GCNO"
_gen_file_list_replacement=$(find_src_files $_gen_file_list_unneeded | tr '\n' ' ')
_gen_file_list_contents=$(replace "$_gen_file_list_contents" \
@@ -521,151 +505,9 @@ gen_file_list() {
_gen_file_list_contents=$(replace "$_gen_file_list_contents" \
"$_gen_file_list_needle_obj" "$_gen_file_list_replacement")
- _gen_file_list_replacement=$(replace_exts "$_gen_file_list_replacement" "o" "gcda")
- _gen_file_list_contents=$(replace "$_gen_file_list_contents" \
- "$_gen_file_list_needle_gcda" "$_gen_file_list_replacement")
-
- _gen_file_list_replacement=$(replace_exts "$_gen_file_list_replacement" "gcda" "gcno")
- _gen_file_list_contents=$(replace "$_gen_file_list_contents" \
- "$_gen_file_list_needle_gcno" "$_gen_file_list_replacement")
-
printf '%s\n' "$_gen_file_list_contents"
}
-# Generates the proper test targets for each test to have its own target. This
-# allows `make test` to run in parallel.
-#
-# @param name Which calculator to generate tests for.
-# @param extra_math An integer that, if non-zero, activates extra math tests.
-# @param time_tests An integer that, if non-zero, tells the test suite to time
-# the execution of each test.
-gen_std_tests() {
-
- _gen_std_tests_name="$1"
- shift
-
- _gen_std_tests_extra_math="$1"
- shift
-
- _gen_std_tests_time_tests="$1"
- shift
-
- _gen_std_tests_extra_required=$(cat "$scriptdir/tests/extra_required.txt")
-
- for _gen_std_tests_t in $(cat "$scriptdir/tests/$_gen_std_tests_name/all.txt"); do
-
- if [ "$_gen_std_tests_extra_math" -eq 0 ]; then
-
- if [ -z "${_gen_std_tests_extra_required##*$_gen_std_tests_t*}" ]; then
- printf 'test_%s_%s:\n\t@printf "Skipping %s %s\\n"\n\n' \
- "$_gen_std_tests_name" "$_gen_std_tests_t" "$_gen_std_tests_name" \
- "$_gen_std_tests_t" >> "Makefile"
- continue
- fi
-
- fi
-
- printf 'test_%s_%s:\n\t@export BC_TEST_OUTPUT_DIR="%s/tests"; sh $(TESTSDIR)/test.sh %s %s %s %s %s\n\n' \
- "$_gen_std_tests_name" "$_gen_std_tests_t" "$builddir" "$_gen_std_tests_name" \
- "$_gen_std_tests_t" "$generate_tests" "$time_tests" \
- "$*" >> "Makefile"
-
- done
-}
-
-# Generates a list of test targets that will be used as prerequisites for other
-# targets.
-#
-# @param name The name of the calculator to generate test targets for.
-gen_std_test_targets() {
-
- _gen_std_test_targets_name="$1"
- shift
-
- _gen_std_test_targets_tests=$(cat "$scriptdir/tests/${_gen_std_test_targets_name}/all.txt")
-
- for _gen_std_test_targets_t in $_gen_std_test_targets_tests; do
- printf ' test_%s_%s' "$_gen_std_test_targets_name" "$_gen_std_test_targets_t"
- done
-
- printf '\n'
-}
-
-# Generates the proper test targets for each error test to have its own target.
-# This allows `make test_bc_errors` and `make test_dc_errors` to run in
-# parallel.
-#
-# @param name Which calculator to generate tests for.
-gen_err_tests() {
-
- _gen_err_tests_name="$1"
- shift
-
- _gen_err_tests_fs=$(ls "$scriptdir/tests/$_gen_err_tests_name/errors/")
-
- for _gen_err_tests_t in $_gen_err_tests_fs; do
-
- printf 'test_%s_error_%s:\n\t@export BC_TEST_OUTPUT_DIR="%s/tests"; sh $(TESTSDIR)/error.sh %s %s %s %s\n\n' \
- "$_gen_err_tests_name" "$_gen_err_tests_t" "$builddir" "$_gen_err_tests_name" \
- "$_gen_err_tests_t" "$problematic_tests" "$*" >> "Makefile"
-
- done
-
-}
-
-# Generates a list of error test targets that will be used as prerequisites for
-# other targets.
-#
-# @param name The name of the calculator to generate test targets for.
-gen_err_test_targets() {
-
- _gen_err_test_targets_name="$1"
- shift
-
- _gen_err_test_targets_tests=$(ls "$scriptdir/tests/$_gen_err_test_targets_name/errors/")
-
- for _gen_err_test_targets_t in $_gen_err_test_targets_tests; do
- printf ' test_%s_error_%s' "$_gen_err_test_targets_name" "$_gen_err_test_targets_t"
- done
-
- printf '\n'
-}
-
-# Generates the proper script test targets for each script test to have its own
-# target. This allows `make test` to run in parallel.
-#
-# @param name Which calculator to generate tests for.
-# @param extra_math An integer that, if non-zero, activates extra math tests.
-# @param generate An integer that, if non-zero, activates generated tests.
-# @param time_tests An integer that, if non-zero, tells the test suite to time
-# the execution of each test.
-gen_script_tests() {
-
- _gen_script_tests_name="$1"
- shift
-
- _gen_script_tests_extra_math="$1"
- shift
-
- _gen_script_tests_generate="$1"
- shift
-
- _gen_script_tests_time="$1"
- shift
-
- _gen_script_tests_tests=$(cat "$scriptdir/tests/$_gen_script_tests_name/scripts/all.txt")
-
- for _gen_script_tests_f in $_gen_script_tests_tests; do
-
- _gen_script_tests_b=$(basename "$_gen_script_tests_f" ".${_gen_script_tests_name}")
-
- printf 'test_%s_script_%s:\n\t@export BC_TEST_OUTPUT_DIR="%s/tests"; sh $(TESTSDIR)/script.sh %s %s %s 1 %s %s %s\n\n' \
- "$_gen_script_tests_name" "$_gen_script_tests_b" "$builddir" "$_gen_script_tests_name" \
- "$_gen_script_tests_f" "$_gen_script_tests_extra_math" "$_gen_script_tests_generate" \
- "$_gen_script_tests_time" "$*" >> "Makefile"
- done
-}
-
set_default() {
_set_default_on="$1"
@@ -710,7 +552,6 @@ predefined_build() {
BSD)
bc_only=0
dc_only=0
- coverage=0
debug=0
optimization="3"
hist=1
@@ -723,11 +564,6 @@ predefined_build() {
strip_bin=1
all_locales=0
library=0
- fuzz=0
- ossfuzz=0
- time_tests=0
- vg=0
- memcheck=0
clean=1
bc_default_banner=0
bc_default_sigint_reset=1
@@ -744,7 +580,6 @@ predefined_build() {
GNU)
bc_only=0
dc_only=0
- coverage=0
debug=0
optimization="3"
hist=1
@@ -757,11 +592,6 @@ predefined_build() {
strip_bin=1
all_locales=0
library=0
- fuzz=0
- ossfuzz=0
- time_tests=0
- vg=0
- memcheck=0
clean=1
bc_default_banner=1
bc_default_sigint_reset=1
@@ -777,10 +607,10 @@ predefined_build() {
GDH)
CFLAGS="-Weverything -Wno-padded -Wno-unsafe-buffer-usage -Wno-poison-system-directories"
- CFLAGS="$CFLAGS -Wno-switch-default -Werror -pedantic -std=c11"
+ CFLAGS="$CFLAGS -Wno-unknown-warning-option -Wno-switch-default -Wno-pre-c11-compat"
+ CFLAGS="$CFLAGS -Werror -pedantic -std=c11"
bc_only=0
dc_only=0
- coverage=0
debug=0
optimization="3"
hist=1
@@ -793,11 +623,6 @@ predefined_build() {
strip_bin=1
all_locales=0
library=0
- fuzz=0
- ossfuzz=0
- time_tests=0
- vg=0
- memcheck=0
clean=1
bc_default_banner=1
bc_default_sigint_reset=1
@@ -813,10 +638,10 @@ predefined_build() {
DBG)
CFLAGS="-Weverything -Wno-padded -Wno-unsafe-buffer-usage -Wno-poison-system-directories"
- CFLAGS="$CFLAGS -Wno-switch-default -Werror -pedantic -std=c11"
+ CFLAGS="$CFLAGS -Wno-unknown-warning-option -Wno-switch-default -Wno-pre-c11-compat"
+ CFLAGS="$CFLAGS -Werror -pedantic -std=c11"
bc_only=0
dc_only=0
- coverage=0
debug=1
optimization="0"
hist=1
@@ -829,11 +654,6 @@ predefined_build() {
strip_bin=1
all_locales=0
library=0
- fuzz=0
- ossfuzz=0
- time_tests=0
- vg=0
- memcheck=1
clean=1
bc_default_banner=1
bc_default_sigint_reset=1
@@ -852,36 +672,12 @@ predefined_build() {
esac
}
-# Generates a list of script test targets that will be used as prerequisites for
-# other targets.
-#
-# @param name The name of the calculator to generate script test targets for.
-gen_script_test_targets() {
-
- _gen_script_test_targets_name="$1"
- shift
-
- _gen_script_test_targets_tests=$(cat "$scriptdir/tests/$_gen_script_test_targets_name/scripts/all.txt")
-
- for _gen_script_test_targets_f in $_gen_script_test_targets_tests; do
- _gen_script_test_targets_b=$(basename "$_gen_script_test_targets_f" \
- ".$_gen_script_test_targets_name")
- printf ' test_%s_script_%s' "$_gen_script_test_targets_name" \
- "$_gen_script_test_targets_b"
- done
-
- printf '\n'
-}
-
# This is a list of defaults, but it is also the list of possible options for
# users to change.
#
-# The development options are: force (force options even if they fail), valgrind
-# (build in a way suitable for valgrind testing), memcheck (same as valgrind),
-# and fuzzing (build in a way suitable for fuzzing).
+# The development options are: force (force options even if they fail).
bc_only=0
dc_only=0
-coverage=0
karatsuba_len=32
debug=0
hist=1
@@ -895,11 +691,6 @@ force=0
strip_bin=1
all_locales=0
library=0
-fuzz=0
-ossfuzz=0
-time_tests=0
-vg=0
-memcheck=0
clean=1
problematic_tests=1
@@ -920,13 +711,12 @@ dc_default_digit_clamp=0
# getopts is a POSIX utility, but it cannot handle long options. Thus, the
# handling of long options is done by hand, and that's the reason that short and
# long options cannot be mixed.
-while getopts "abBcdDeEfgGhHik:lMmNO:p:PrS:s:tTvzZ-" opt; do
+while getopts "abBcdDeEfgGhHik:lMNO:p:PrS:s:T-" opt; do
case "$opt" in
a) library=1 ;;
b) bc_only=1 ;;
B) dc_only=1 ;;
- c) coverage=1 ;;
C) clean=0 ;;
d) dc_only=1 ;;
D) bc_only=1 ;;
@@ -940,7 +730,6 @@ while getopts "abBcdDeEfgGhHik:lMmNO:p:PrS:s:tTvzZ-" opt; do
i) hist_impl="internal" ;;
k) karatsuba_len="$OPTARG" ;;
l) all_locales=1 ;;
- m) memcheck=1 ;;
M) install_manpages=0 ;;
N) nls=0 ;;
O) optimization="$OPTARG" ;;
@@ -949,11 +738,7 @@ while getopts "abBcdDeEfgGhHik:lMmNO:p:PrS:s:tTvzZ-" opt; do
r) hist_impl="readline" ;;
S) set_default 0 "$OPTARG" ;;
s) set_default 1 "$OPTARG" ;;
- t) time_tests=1 ;;
T) strip_bin=0 ;;
- v) vg=1 ;;
- z) fuzz=1 ;;
- Z) ossfuzz=1 ;;
-)
arg="$1"
arg="${arg#--}"
@@ -963,7 +748,6 @@ while getopts "abBcdDeEfgGhHik:lMmNO:p:PrS:s:tTvzZ-" opt; do
library) library=1 ;;
bc-only) bc_only=1 ;;
dc-only) dc_only=1 ;;
- coverage) coverage=1 ;;
debug) debug=1 ;;
force) force=1 ;;
prefix=?*) PREFIX="$LONG_OPTARG" ;;
@@ -1077,27 +861,20 @@ while getopts "abBcdDeEfgGhHik:lMmNO:p:PrS:s:tTvzZ-" opt; do
enable-editline) hist_impl="editline" ;;
enable-readline) hist_impl="readline" ;;
enable-internal-history) hist_impl="internal" ;;
- enable-test-timing) time_tests=1 ;;
- enable-valgrind) vg=1 ;;
- enable-fuzz-mode) fuzz=1 ;;
- enable-ossfuzz-mode) ossfuzz=1 ;;
- enable-memcheck) memcheck=1 ;;
install-all-locales) all_locales=1 ;;
- help* | bc-only* | dc-only* | coverage* | debug*)
+ help* | bc-only* | dc-only* | debug*)
usage "No arg allowed for --$arg option" ;;
disable-bc* | disable-dc* | disable-clean*)
usage "No arg allowed for --$arg option" ;;
disable-extra-math*)
usage "No arg allowed for --$arg option" ;;
- disable-generated-tests* | disable-history*)
+ disable-history*)
usage "No arg allowed for --$arg option" ;;
disable-man-pages* | disable-nls* | disable-strip*)
usage "No arg allowed for --$arg option" ;;
disable-problematic-tests*)
usage "No arg allowed for --$arg option" ;;
- enable-fuzz-mode* | enable-test-timing* | enable-valgrind*)
- usage "No arg allowed for --$arg option" ;;
- enable-memcheck* | install-all-locales*)
+ install-all-locales*)
usage "No arg allowed for --$arg option" ;;
enable-editline* | enable-readline*)
usage "No arg allowed for --$arg option" ;;
@@ -1221,33 +998,7 @@ link="@printf 'No link necessary\\\\n'"
main_exec="BC"
executable="BC_EXEC"
-tests="test_bc timeconst test_dc"
-
-bc_test="@export BC_TEST_OUTPUT_DIR=\"$builddir/tests\"; \$(TESTSDIR)/all.sh bc $extra_math 1 $generate_tests $problematic_tests $time_tests \$(BC_EXEC)"
-bc_test_np="@export BC_TEST_OUTPUT_DIR=\"$builddir/tests\"; \$(TESTSDIR)/all.sh -n bc $extra_math 1 $generate_tests $problematic_tests $time_tests \$(BC_EXEC)"
-dc_test="@export BC_TEST_OUTPUT_DIR=\"$builddir/tests\"; \$(TESTSDIR)/all.sh dc $extra_math 1 $generate_tests $problematic_tests $time_tests \$(DC_EXEC)"
-dc_test_np="@export BC_TEST_OUTPUT_DIR=\"$builddir/tests\"; \$(TESTSDIR)/all.sh -n dc $extra_math 1 $generate_tests $problematic_tests $time_tests \$(DC_EXEC)"
-
-timeconst="@export BC_TEST_OUTPUT_DIR=\"$builddir/tests\"; \$(TESTSDIR)/bc/timeconst.sh \$(TESTSDIR)/bc/scripts/timeconst.bc \$(BC_EXEC)"
-
-# In order to have cleanup at exit, we need to be in
-# debug mode, so don't run valgrind without that.
-if [ "$vg" -ne 0 ]; then
- debug=1
- bc_test_exec='valgrind $(VALGRIND_ARGS) $(BC_EXEC)'
- dc_test_exec='valgrind $(VALGRIND_ARGS) $(DC_EXEC)'
- bcl_test_exec='valgrind $(VALGRIND_ARGS) $(BCL_TEST)'
-else
- bc_test_exec='$(BC_EXEC)'
- dc_test_exec='$(DC_EXEC)'
- bcl_test_exec='$(BCL_TEST)'
-fi
-
-test_bc_history_prereqs="test_bc_history_all"
-test_dc_history_prereqs="test_dc_history_all"
-
karatsuba="@printf 'karatsuba cannot be run because one of bc or dc is not built\\\\n'"
-karatsuba_test="@printf 'karatsuba cannot be run because one of bc or dc is not built\\\\n'"
bc_lib="\$(GEN_DIR)/lib.o"
bc_help="\$(GEN_DIR)/bc_help.o"
@@ -1274,9 +1025,6 @@ if [ "$library" -ne 0 ]; then
default_target_prereqs="\$(BIN) \$(OBJ)"
default_target_cmd="ar -r -cu \$(LIBBC) \$(OBJ)"
default_target="\$(LIBBC)"
- tests="test_library"
- test_bc_history_prereqs=" test_bc_history_skip"
- test_dc_history_prereqs=" test_dc_history_skip"
install_prereqs=" install_library"
uninstall_prereqs=" uninstall_library"
@@ -1292,10 +1040,6 @@ elif [ "$bc_only" -eq 1 ]; then
executables="bc"
- dc_test="@printf 'No dc tests to run\\\\n'"
- dc_test_np="@printf 'No dc tests to run\\\\n'"
- test_dc_history_prereqs=" test_dc_history_skip"
-
install_prereqs=" install_execs"
install_man_prereqs=" install_bc_manpage"
uninstall_prereqs=" uninstall_bc"
@@ -1303,7 +1047,6 @@ elif [ "$bc_only" -eq 1 ]; then
default_target="\$(BC_EXEC)"
second_target="\$(DC_EXEC)"
- tests="test_bc timeconst"
elif [ "$dc_only" -eq 1 ]; then
@@ -1318,58 +1061,11 @@ elif [ "$dc_only" -eq 1 ]; then
main_exec="DC"
executable="DC_EXEC"
- bc_test="@printf 'No bc tests to run\\\\n'"
- bc_test_np="@printf 'No bc tests to run\\\\n'"
- test_bc_history_prereqs=" test_bc_history_skip"
-
- timeconst="@printf 'timeconst cannot be run because bc is not built\\\\n'"
-
install_prereqs=" install_execs"
install_man_prereqs=" install_dc_manpage"
uninstall_prereqs=" uninstall_dc"
uninstall_man_prereqs=" uninstall_dc_manpage"
- tests="test_dc"
-
-elif [ "$ossfuzz" -eq 1 ]; then
-
- if [ "$bc_only" -ne 0 ] || [ "$dc_only" -ne 0 ]; then
- usage "An OSS-Fuzz build must build both fuzzers."
- fi
-
- bc=1
- dc=1
-
- # Expressions *cannot* exit in an OSS-Fuzz build.
- bc_default_expr_exit=0
- dc_default_expr_exit=0
-
- executables="bc_fuzzer and dc_fuzzer"
-
- karatsuba="@\$(KARATSUBA) 30 0 \$(BC_EXEC)"
- karatsuba_test="@\$(KARATSUBA) 1 100 \$(BC_EXEC)"
-
- if [ "$library" -eq 0 ]; then
- install_prereqs=" install_execs"
- install_man_prereqs=" install_bc_manpage install_dc_manpage"
- uninstall_prereqs=" uninstall_bc uninstall_dc"
- uninstall_man_prereqs=" uninstall_bc_manpage uninstall_dc_manpage"
- else
- install_prereqs=" install_library install_bcl_header"
- install_man_prereqs=" install_bcl_manpage"
- uninstall_prereqs=" uninstall_library uninstall_bcl_header"
- uninstall_man_prereqs=" uninstall_bcl_manpage"
- tests="test_library"
- fi
-
- second_target_prereqs="src/bc_fuzzer.o $default_target_prereqs"
- default_target_prereqs="\$(BC_FUZZER) src/dc_fuzzer.o $default_target_prereqs"
- default_target_cmd="\$(CXX) \$(CFLAGS) src/dc_fuzzer.o \$(LIB_FUZZING_ENGINE) \$(OBJS) \$(LDFLAGS) -o \$(DC_FUZZER) \&\& ln -sf ./dc_fuzzer_c \$(DC_FUZZER_C)"
- second_target_cmd="\$(CXX) \$(CFLAGS) src/bc_fuzzer.o \$(LIB_FUZZING_ENGINE) \$(OBJS) \$(LDFLAGS) -o \$(BC_FUZZER) \&\& ln -sf ./bc_fuzzer_c \$(BC_FUZZER_C)"
-
- default_target="\$(DC_FUZZER) \$(DC_FUZZER_C)"
- second_target="\$(BC_FUZZER) \$(BC_FUZZER_C)"
-
else
bc=1
@@ -1378,7 +1074,6 @@ else
executables="bc and dc"
karatsuba="@\$(KARATSUBA) 30 0 \$(BC_EXEC)"
- karatsuba_test="@\$(KARATSUBA) 1 100 \$(BC_EXEC)"
if [ "$library" -eq 0 ]; then
install_prereqs=" install_execs"
@@ -1390,7 +1085,6 @@ else
install_man_prereqs=" install_bcl_manpage"
uninstall_prereqs=" uninstall_library uninstall_bcl_header"
uninstall_man_prereqs=" uninstall_bcl_manpage"
- tests="test_library"
fi
second_target_prereqs="$default_target_prereqs"
@@ -1399,18 +1093,6 @@ else
fi
-if [ "$fuzz" -ne 0 ] && [ "$ossfuzz" -ne 0 ]; then
- usage "Fuzzing mode and OSS-Fuzz mode are mutually exclusive"
-fi
-
-# We need specific stuff for fuzzing.
-if [ "$fuzz" -ne 0 ] || [ "$ossfuzz" -ne 0 ]; then
- debug=1
- hist=0
- nls=0
- optimization="3"
-fi
-
# This sets some necessary things for debug mode.
if [ "$debug" -eq 1 ]; then
@@ -1429,26 +1111,6 @@ if [ -n "$optimization" ]; then
CFLAGS="-O$optimization $CFLAGS"
fi
-# Set test coverage defaults.
-if [ "$coverage" -eq 1 ]; then
-
- if [ "$bc_only" -eq 1 ] || [ "$dc_only" -eq 1 ]; then
- usage "Can only specify -c without -b or -d"
- fi
-
- CFLAGS="-fprofile-arcs -ftest-coverage -g -O0 $CFLAGS"
- CPPFLAGS="-DNDEBUG $CPPFLAGS"
-
- COVERAGE_OUTPUT="@gcov -pabcdf \$(GCDA) \$(BC_GCDA) \$(DC_GCDA) \$(HISTORY_GCDA) \$(RAND_GCDA)"
- COVERAGE_OUTPUT="$COVERAGE_OUTPUT;\$(RM) -f \$(GEN)*.gc*"
- COVERAGE_OUTPUT="$COVERAGE_OUTPUT;gcovr --exclude-unreachable-branches --exclude-throw-branches --html-details --output index.html"
- COVERAGE_PREREQS=" test coverage_output"
-
-else
- COVERAGE_OUTPUT="@printf 'Coverage not generated\\\\n'"
- COVERAGE_PREREQS=""
-fi
-
# Set some defaults.
if [ -z "${DESTDIR+set}" ]; then
destdir=""
@@ -1567,7 +1229,7 @@ if [ "$nls" -ne 0 ]; then
printf 'NLS works.\n\n'
printf 'Testing gencat...\n'
- gencat "./en_US.cat" "$scriptdir/locales/en_US.msg" > /dev/null
+ gencat "./en_US.cat" "$scriptdir/locales/en_US.msg" 2>&1
err="$?"
@@ -1672,26 +1334,10 @@ else
fi
-# We have to disable the history tests if it is disabled or valgrind is on. Or
-# if we are using editline or readline.
-if [ "$hist" -eq 0 ] || [ "$vg" -ne 0 ]; then
- test_bc_history_prereqs=" test_bc_history_skip"
- test_dc_history_prereqs=" test_dc_history_skip"
- history_tests="@printf 'Skipping history tests...\\\\n'"
+if [ "$hist" -eq 0 ]; then
CFLAGS="$CFLAGS -DBC_ENABLE_EDITLINE=0 -DBC_ENABLE_READLINE=0"
else
- if [ "$editline" -eq 0 ] && [ "$readline" -eq 0 ]; then
- history_tests="@printf '\$(TEST_STARS)\\\\n\\\\nRunning history tests...\\\\n\\\\n'"
- history_tests="$history_tests \&\& \$(TESTSDIR)/history.sh bc -a \&\&"
- history_tests="$history_tests \$(TESTSDIR)/history.sh dc -a \&\& printf"
- history_tests="$history_tests '\\\\nAll history tests passed.\\\\n\\\\n\$(TEST_STARS)\\\\n'"
- else
- test_bc_history_prereqs=" test_bc_history_skip"
- test_dc_history_prereqs=" test_dc_history_skip"
- history_tests="@printf 'Skipping history tests...\\\\n'"
- fi
-
# We are also setting the CFLAGS and LDFLAGS here.
if [ "$editline" -ne 0 ]; then
LDFLAGS="$LDFLAGS -ledit"
@@ -1795,7 +1441,7 @@ GEN_DIR="$scriptdir/gen"
# `gen/strgen.sh` is used.
GEN="strgen"
GEN_EXEC_TARGET="\$(HOSTCC) -DBC_ENABLE_AFL=0 -DBC_ENABLE_OSSFUZZ=0 -I$scriptdir/include/ \$(HOSTCFLAGS) -o \$(GEN_EXEC) \$(GEN_C)"
-CLEAN_PREREQS=" clean_gen clean_coverage"
+CLEAN_PREREQS=" clean_gen"
if [ -z "${GEN_HOST+set}" ]; then
GEN_HOST=1
@@ -1803,7 +1449,6 @@ else
if [ "$GEN_HOST" -eq 0 ]; then
GEN="strgen.sh"
GEN_EXEC_TARGET="@printf 'Do not need to build gen/strgen.c\\\\n'"
- CLEAN_PREREQS=" clean_coverage"
fi
fi
@@ -1848,9 +1493,7 @@ else
headers="$headers \$(DC_HEADERS)"
fi
-# This convoluted mess does pull the version out. If you change the format of
-# include/version.h, you may have to change this line.
-version=$(cat "$scriptdir/include/version.h" | grep "VERSION " - | awk '{ print $3 }' -)
+version=$(cat "$scriptdir/VERSION.txt" | head -n1)
if [ "$library" -ne 0 ]; then
@@ -1880,14 +1523,6 @@ if [ "$library" -ne 0 ]; then
fi
-elif [ "$ossfuzz" -ne 0 ]; then
-
- unneeded="$unneeded library.c main.c"
-
- PC_PATH=""
- pkg_config_install=""
- pkg_config_uninstall=""
-
else
unneeded="$unneeded library.c"
@@ -1909,10 +1544,6 @@ if [ "$manpage_args" = "" ]; then
manpage_args="A"
fi
-if [ "$vg" -ne 0 ] || [ "$ossfuzz" -ne 0 ]; then
- memcheck=1
-fi
-
if [ "$bc_default_prompt" = "" ]; then
bc_default_prompt="$bc_default_tty_mode"
fi
@@ -1921,14 +1552,6 @@ if [ "$dc_default_prompt" = "" ]; then
dc_default_prompt="$dc_default_tty_mode"
fi
-# Generate the test targets and prerequisites.
-bc_tests=$(gen_std_test_targets bc)
-bc_script_tests=$(gen_script_test_targets bc)
-bc_err_tests=$(gen_err_test_targets bc)
-dc_tests=$(gen_std_test_targets dc)
-dc_script_tests=$(gen_script_test_targets dc)
-dc_err_tests=$(gen_err_test_targets dc)
-
printf 'unneeded: %s\n' "$unneeded"
# Print out the values; this is for debugging.
@@ -1949,7 +1572,6 @@ printf 'BC_ENABLE_LIBRARY=%s\n\n' "$library"
printf 'BC_ENABLE_HISTORY=%s\n' "$hist"
printf 'BC_ENABLE_EXTRA_MATH=%s\n' "$extra_math"
printf 'BC_ENABLE_NLS=%s\n\n' "$nls"
-printf 'BC_ENABLE_AFL=%s\n' "$fuzz"
printf '\n'
printf 'BC_NUM_KARATSUBA_LEN=%s\n' "$karatsuba_len"
printf '\n'
@@ -2046,26 +1668,11 @@ contents=$(replace "$contents" "BUILDDIR" "$builddir")
contents=$(replace "$contents" "HEADERS" "$headers")
+contents=$(replace "$contents" "VERSION" "$version")
+
contents=$(replace "$contents" "BC_ENABLED" "$bc")
contents=$(replace "$contents" "DC_ENABLED" "$dc")
-contents=$(replace "$contents" "BC_ALL_TESTS" "$bc_test")
-contents=$(replace "$contents" "BC_ALL_TESTS_NP" "$bc_test_np")
-contents=$(replace "$contents" "BC_TESTS" "$bc_tests")
-contents=$(replace "$contents" "BC_SCRIPT_TESTS" "$bc_script_tests")
-contents=$(replace "$contents" "BC_ERROR_TESTS" "$bc_err_tests")
-contents=$(replace "$contents" "BC_TEST_EXEC" "$bc_test_exec")
-contents=$(replace "$contents" "TIMECONST_ALL_TESTS" "$timeconst")
-
-contents=$(replace "$contents" "DC_ALL_TESTS" "$dc_test")
-contents=$(replace "$contents" "DC_ALL_TESTS_NP" "$dc_test_np")
-contents=$(replace "$contents" "DC_TESTS" "$dc_tests")
-contents=$(replace "$contents" "DC_SCRIPT_TESTS" "$dc_script_tests")
-contents=$(replace "$contents" "DC_ERROR_TESTS" "$dc_err_tests")
-contents=$(replace "$contents" "DC_TEST_EXEC" "$dc_test_exec")
-
-contents=$(replace "$contents" "BCL_TEST_EXEC" "$bcl_test_exec")
-
contents=$(replace "$contents" "BUILD_TYPE" "$manpage_args")
contents=$(replace "$contents" "EXCLUDE_EXTRA_MATH" "$exclude_extra_math")
@@ -2073,10 +1680,6 @@ contents=$(replace "$contents" "LIBRARY" "$library")
contents=$(replace "$contents" "HISTORY" "$hist")
contents=$(replace "$contents" "EXTRA_MATH" "$extra_math")
contents=$(replace "$contents" "NLS" "$nls")
-contents=$(replace "$contents" "FUZZ" "$fuzz")
-contents=$(replace "$contents" "OSSFUZZ" "$ossfuzz")
-contents=$(replace "$contents" "MEMCHECK" "$memcheck")
-contents=$(replace "$contents" "LIB_FUZZING_ENGINE" "$LIB_FUZZING_ENGINE")
contents=$(replace "$contents" "BC_LIB_O" "$bc_lib")
contents=$(replace "$contents" "BC_HELP_O" "$bc_help")
@@ -2099,8 +1702,6 @@ contents=$(replace "$contents" "CPPFLAGS" "$CPPFLAGS")
contents=$(replace "$contents" "LDFLAGS" "$LDFLAGS")
contents=$(replace "$contents" "CC" "$CC")
contents=$(replace "$contents" "HOSTCC" "$HOSTCC")
-contents=$(replace "$contents" "COVERAGE_OUTPUT" "$COVERAGE_OUTPUT")
-contents=$(replace "$contents" "COVERAGE_PREREQS" "$COVERAGE_PREREQS")
contents=$(replace "$contents" "INSTALL_PREREQS" "$install_prereqs")
contents=$(replace "$contents" "INSTALL_MAN_PREREQS" "$install_man_prereqs")
contents=$(replace "$contents" "INSTALL_LOCALES" "$install_locales")
@@ -2126,22 +1727,14 @@ contents=$(replace "$contents" "BC_EXEC_CMD" "$bc_exec_cmd")
contents=$(replace "$contents" "DC_EXEC_PREREQ" "$dc_exec_prereq")
contents=$(replace "$contents" "DC_EXEC_CMD" "$dc_exec_cmd")
+contents=$(replace "$contents" "GENERATE_TESTS" "$generate_tests")
+contents=$(replace "$contents" "PROBLEMATIC_TESTS" "$problematic_tests")
+
contents=$(replace "$contents" "EXECUTABLES" "$executables")
contents=$(replace "$contents" "MAIN_EXEC" "$main_exec")
contents=$(replace "$contents" "EXEC" "$executable")
-contents=$(replace "$contents" "TESTS" "$tests")
-
-contents=$(replace "$contents" "BC_HISTORY_TEST_PREREQS" "$test_bc_history_prereqs")
-contents=$(replace "$contents" "DC_HISTORY_TEST_PREREQS" "$test_dc_history_prereqs")
-contents=$(replace "$contents" "HISTORY_TESTS" "$history_tests")
-
-contents=$(replace "$contents" "VG_BC_TEST" "$vg_bc_test")
-contents=$(replace "$contents" "VG_DC_TEST" "$vg_dc_test")
-
-contents=$(replace "$contents" "TIMECONST" "$timeconst")
contents=$(replace "$contents" "KARATSUBA" "$karatsuba")
-contents=$(replace "$contents" "KARATSUBA_TEST" "$karatsuba_test")
contents=$(replace "$contents" "LONG_BIT_DEFINE" "$LONG_BIT_DEFINE")
@@ -2169,28 +1762,6 @@ contents=$(replace "$contents" "DC_DEFAULT_DIGIT_CLAMP" "$dc_default_digit_clamp
# Do the first print to the Makefile.
printf '%s\n%s\n\n' "$contents" "$SRC_TARGETS" > "Makefile"
-# Generate the individual test targets.
-if [ "$bc" -ne 0 ]; then
- gen_std_tests bc "$extra_math" "$time_tests" $bc_test_exec
- gen_script_tests bc "$extra_math" "$generate_tests" "$time_tests" $bc_test_exec
- gen_err_tests bc $bc_test_exec
-fi
-
-if [ "$dc" -ne 0 ]; then
- gen_std_tests dc "$extra_math" "$time_tests" $dc_test_exec
- gen_script_tests dc "$extra_math" "$generate_tests" "$time_tests" $dc_test_exec
- gen_err_tests dc $dc_test_exec
-fi
-
-if [ "$ossfuzz" -ne 0 ]; then
-
- printf 'bc_fuzzer_c: $(BC_FUZZER)\n\tln -sf $(BC_FUZZER) bc_fuzzer_c\n' >> Makefile
- printf 'bc_fuzzer_C: $(BC_FUZZER)\n\tln -sf $(BC_FUZZER) bc_fuzzer_C\n' >> Makefile
- printf 'dc_fuzzer_c: $(DC_FUZZER)\n\tln -sf $(DC_FUZZER) dc_fuzzer_c\n' >> Makefile
- printf 'dc_fuzzer_C: $(DC_FUZZER)\n\tln -sf $(DC_FUZZER) dc_fuzzer_C\n' >> Makefile
-
-fi
-
# Copy the correct manuals to the expected places.
mkdir -p manuals
cp -f "$scriptdir/manuals/bc/$manpage_args.1.md" manuals/bc.1.md
diff --git a/contrib/bc/gen/bc_help.txt b/contrib/bc/gen/bc_help.txt
index 489b54a185f1..ce82d2677737 100644
--- a/contrib/bc/gen/bc_help.txt
+++ b/contrib/bc/gen/bc_help.txt
@@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+ * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -37,7 +37,7 @@ usage: %s [options] [file...]
bc is a command-line, arbitrary-precision calculator with a Turing-complete
language. For details, use `man %s` or see the online documentation at
-https://git.gavinhoward.com/gavin/bc/src/tag/%s/manuals/bc/%s.1.md.
+https://github.com/gavinhoward/bc/tree/%s/manuals/bc/%s.1.md .
This bc is compatible with both the GNU bc and the POSIX bc spec. See the GNU bc
manual (https://www.gnu.org/software/bc/manual/bc.html) and bc spec
diff --git a/contrib/bc/gen/dc_help.txt b/contrib/bc/gen/dc_help.txt
index df4ede1583a2..897ab31a8820 100644
--- a/contrib/bc/gen/dc_help.txt
+++ b/contrib/bc/gen/dc_help.txt
@@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+ * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -37,7 +37,7 @@ usage: %s [options] [file...]
dc is a reverse-polish notation command-line calculator which supports unlimited
precision arithmetic. For details, use `man %s` or see the online documentation
-at https://git.gavinhoward.com/gavin/bc/src/tag/%s/manuals/bc/%s.1.md.
+at https://github.com/gavinhoward/bc/tree/%s/manuals/dc/%s.1.md .
This dc is (mostly) compatible with the OpenBSD dc and the GNU dc. See the
OpenBSD man page (http://man.openbsd.org/OpenBSD-current/man1/dc.1) and the GNU
diff --git a/contrib/bc/gen/lib.bc b/contrib/bc/gen/lib.bc
index 0c9389b8510d..95ba2765ddfa 100644
--- a/contrib/bc/gen/lib.bc
+++ b/contrib/bc/gen/lib.bc
@@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+ * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/gen/lib2.bc b/contrib/bc/gen/lib2.bc
index d6d9f70fe063..cc959db73ceb 100644
--- a/contrib/bc/gen/lib2.bc
+++ b/contrib/bc/gen/lib2.bc
@@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+ * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -444,108 +444,77 @@ define void uint64(x){uintn(x,8)}
define void int64(x){intn(x,8)}
define void uint(x){uintn(x,ubytes(x))}
define void int(x){intn(x,sbytes(x))}
-define bunrev(t){
- auto a,s,m[]
- s=scale
- scale=0
- t=abs(t)$
- while(t!=1){
- t=divmod(t,2,m[])
- a*=2
- a+=m[0]
- }
- scale=s
- return a
-}
define band(a,b){
- auto s,t,m[],n[]
+ auto r,p,s,m[]
a=abs(a)$
b=abs(b)$
- if(b>a){
- t=b
- b=a
- a=t
- }
+ r=0
+ p=1
s=scale
scale=0
- t=1
- while(b){
+ while(a&&b) {
a=divmod(a,2,m[])
- b=divmod(b,2,n[])
- t*=2
- t+=(m[0]&&n[0])
+ if(m[0]){
+ b=divmod(b,2,m[])
+ if(m[0])r+=p
+ }else b/=2
+ p*=2
}
scale=s
- return bunrev(t)
+ return r
}
define bor(a,b){
- auto s,t,m[],n[]
+ auto r,p,s,m[]
a=abs(a)$
b=abs(b)$
- if(b>a){
- t=b
- b=a
- a=t
- }
+ r=0
+ p=1
s=scale
scale=0
- t=1
- while(b){
- a=divmod(a,2,m[])
- b=divmod(b,2,n[])
- t*=2
- t+=(m[0]||n[0])
- }
- while(a){
+ while(a||b){
a=divmod(a,2,m[])
- t*=2
- t+=m[0]
+ if(!m[0])b=divmod(b,2,m[])
+ else b/=2
+ if(m[0])r+=p
+ p*=2
}
scale=s
- return bunrev(t)
+ return r
}
define bxor(a,b){
- auto s,t,m[],n[]
+ auto r,p,s,m[],n[]
a=abs(a)$
b=abs(b)$
- if(b>a){
- t=b
- b=a
- a=t
- }
+ r=0
+ p=1
s=scale
scale=0
- t=1
- while(b){
+ while(a||b){
a=divmod(a,2,m[])
b=divmod(b,2,n[])
- t*=2
- t+=(m[0]+n[0]==1)
- }
- while(a){
- a=divmod(a,2,m[])
- t*=2
- t+=m[0]
+ if(m[0]+n[0]==1)r+=p
+ p*=2
}
scale=s
- return bunrev(t)
+ return r
}
define bshl(a,b){return abs(a)$*2^abs(b)$}
define bshr(a,b){return(abs(a)$/2^abs(b)$)$}
define bnotn(x,n){
- auto s,t,m[]
+ auto r,p,s,t,m[]
s=scale
scale=0
- t=2^(abs(n)$*8)
- x=abs(x)$%t+t
- t=1
+ r=2^(abs(n)$*8)
+ x=abs(x)$%r+r
+ r=0
+ p=1
while(x!=1){
x=divmod(x,2,m[])
- t*=2
- t+=!m[0]
+ if(!m[0])r+=p
+ p*=2
}
scale=s
- return bunrev(t)
+ return r
}
define bnot8(x){return bnotn(x,1)}
define bnot16(x){return bnotn(x,2)}
@@ -553,13 +522,19 @@ define bnot32(x){return bnotn(x,4)}
define bnot64(x){return bnotn(x,8)}
define bnot(x){return bnotn(x,ubytes(x))}
define brevn(x,n){
- auto s,t,m[]
+ auto a,s,m[]
s=scale
scale=0
- t=2^(abs(n)$*8)
- x=abs(x)$%t+t
+ a=2^(abs(n)$*8)
+ x=abs(x)$%a+a
+ a=0
+ while(x!=1){
+ x=divmod(x,2,m[])
+ a*=2
+ a+=m[0]
+ }
scale=s
- return bunrev(x)
+ return a
}
define brev8(x){return brevn(x,1)}
define brev16(x){return brevn(x,2)}
diff --git a/contrib/bc/gen/strgen.c b/contrib/bc/gen/strgen.c
index 1394a05c4a76..996ea9b3c83a 100644
--- a/contrib/bc/gen/strgen.c
+++ b/contrib/bc/gen/strgen.c
@@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+ * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -68,7 +68,7 @@ static const char* const bc_gen_ex_end = "{{ end }}";
// This is exactly what it looks like. It just slaps a simple license header on
// the generated C source file.
static const char* const bc_gen_header =
- "// Copyright (c) 2018-2024 Gavin D. Howard and contributors.\n"
+ "// Copyright (c) 2018-2025 Gavin D. Howard and contributors.\n"
"// Licensed under the 2-clause BSD license.\n"
"// *** AUTOMATICALLY GENERATED FROM %s. DO NOT MODIFY. ***\n\n";
// clang-format on
diff --git a/contrib/bc/gen/strgen.sh b/contrib/bc/gen/strgen.sh
index 8542bd40ee83..683307d4fa80 100755
--- a/contrib/bc/gen/strgen.sh
+++ b/contrib/bc/gen/strgen.sh
@@ -2,7 +2,7 @@
#
# SPDX-License-Identifier: BSD-2-Clause
#
-# Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+# Copyright (c) 2018-2025 Gavin D. Howard and contributors.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
@@ -96,7 +96,7 @@ if [ -n "$remove_tabs" ]; then
fi
cat<<EOF
-// Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+// Copyright (c) 2018-2025 Gavin D. Howard and contributors.
// Licensed under the 2-clause BSD license.
// *** AUTOMATICALLY GENERATED FROM ${input}. DO NOT MODIFY. ***
diff --git a/contrib/bc/include/args.h b/contrib/bc/include/args.h
index 8f8f00be4630..fd9ca60c29c9 100644
--- a/contrib/bc/include/args.h
+++ b/contrib/bc/include/args.h
@@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+ * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/include/bc.h b/contrib/bc/include/bc.h
index 2213278be1da..19159c45e018 100644
--- a/contrib/bc/include/bc.h
+++ b/contrib/bc/include/bc.h
@@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+ * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -265,9 +265,9 @@ bc_lex_token(BcLex* l);
* @return An expression entry for bc_parse_exprs[].
*/
#define BC_PARSE_EXPR_ENTRY(e1, e2, e3, e4, e5, e6, e7, e8) \
- ((UINTMAX_C(e1) << 7) | (UINTMAX_C(e2) << 6) | (UINTMAX_C(e3) << 5) | \
- (UINTMAX_C(e4) << 4) | (UINTMAX_C(e5) << 3) | (UINTMAX_C(e6) << 2) | \
- (UINTMAX_C(e7) << 1) | (UINTMAX_C(e8) << 0))
+ ((UINT8_C(e1) << 7) | (UINT8_C(e2) << 6) | (UINT8_C(e3) << 5) | \
+ (UINT8_C(e4) << 4) | (UINT8_C(e5) << 3) | (UINT8_C(e6) << 2) | \
+ (UINT8_C(e7) << 1) | (UINT8_C(e8) << 0))
/**
* Returns true if token @a i is a token that belongs in an expression.
diff --git a/contrib/bc/include/bcl.h b/contrib/bc/include/bcl.h
index 8e762b694f4d..a46610de47f5 100644
--- a/contrib/bc/include/bcl.h
+++ b/contrib/bc/include/bcl.h
@@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+ * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/include/dc.h b/contrib/bc/include/dc.h
index 63f5ccbd10e3..edab5605c7e7 100644
--- a/contrib/bc/include/dc.h
+++ b/contrib/bc/include/dc.h
@@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+ * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/include/file.h b/contrib/bc/include/file.h
index 86f368db11c6..d152645b1209 100644
--- a/contrib/bc/include/file.h
+++ b/contrib/bc/include/file.h
@@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+ * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/include/history.h b/contrib/bc/include/history.h
index 13f6dc6e985c..03b729707556 100644
--- a/contrib/bc/include/history.h
+++ b/contrib/bc/include/history.h
@@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+ * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -120,30 +120,6 @@ typedef struct BcHistory
extern const char bc_history_editrc[];
extern const size_t bc_history_editrc_len;
-#ifdef __APPLE__
-
-/**
- * Returns true if the line is a valid line, false otherwise.
- * @param line The line.
- * @param len The length of the line.
- * @return True if the line is valid, false otherwise.
- */
-#define BC_HISTORY_INVALID_LINE(line, len) \
- ((line) == NULL && ((len) == -1 || errno == EINTR))
-
-#else // __APPLE__
-
-/**
- * Returns true if the line is a valid line, false otherwise.
- * @param line The line.
- * @param len The length of the line.
- * @return True if the line is valid, false otherwise.
- */
-#define BC_HISTORY_INVALID_LINE(line, len) \
- ((line) == NULL && (len) == -1 && errno == EINTR)
-
-#endif // __APPLE__
-
#else // BC_ENABLE_EDITLINE
#if BC_ENABLE_READLINE
diff --git a/contrib/bc/include/lang.h b/contrib/bc/include/lang.h
index 6c8245139719..d0582a7d2199 100644
--- a/contrib/bc/include/lang.h
+++ b/contrib/bc/include/lang.h
@@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+ * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -533,6 +533,10 @@ typedef enum BcType
} BcType;
#if BC_ENABLED
+
+/// Check if type array or array reference
+#define BC_IS_ARRAY(e) (e == BC_TYPE_ARRAY || e == BC_TYPE_REF)
+
/// An auto variable in bc.
typedef struct BcAuto
{
diff --git a/contrib/bc/include/lex.h b/contrib/bc/include/lex.h
index d2be3c7526ef..5f97a47341df 100644
--- a/contrib/bc/include/lex.h
+++ b/contrib/bc/include/lex.h
@@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+ * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/include/library.h b/contrib/bc/include/library.h
index 9942705a5f36..db7cb1302fe8 100644
--- a/contrib/bc/include/library.h
+++ b/contrib/bc/include/library.h
@@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+ * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/include/num.h b/contrib/bc/include/num.h
index 6cead6eb3823..970873c75589 100644
--- a/contrib/bc/include/num.h
+++ b/contrib/bc/include/num.h
@@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+ * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/include/opt.h b/contrib/bc/include/opt.h
index 41058cb4e29c..a5194b34cbb8 100644
--- a/contrib/bc/include/opt.h
+++ b/contrib/bc/include/opt.h
@@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+ * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/include/ossfuzz.h b/contrib/bc/include/ossfuzz.h
index 5c12a3c9c9fb..4e233df9ab95 100644
--- a/contrib/bc/include/ossfuzz.h
+++ b/contrib/bc/include/ossfuzz.h
@@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+ * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/include/parse.h b/contrib/bc/include/parse.h
index 7f0f8768b0db..e39ea09a8aad 100644
--- a/contrib/bc/include/parse.h
+++ b/contrib/bc/include/parse.h
@@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+ * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/include/program.h b/contrib/bc/include/program.h
index e16e5c079d7d..17454057cdba 100644
--- a/contrib/bc/include/program.h
+++ b/contrib/bc/include/program.h
@@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+ * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -145,6 +145,9 @@ typedef struct BcProgram
#endif // BC_ENABLED
+ /// The number of results that have not been retired.
+ size_t nresults;
+
// The BcDig array for strmb. This uses BC_NUM_LONG_LOG10 because it is used
// in bc_num_ulong2num(), which attempts to realloc, unless it is big
// enough. This is big enough.
@@ -207,11 +210,16 @@ typedef struct BcProgram
* operands while preserving the result (which we assumed was pushed before the
* actual operation).
* @param p The program.
- * @param nres The number of results returned by the instruction.
* @param nops The number of operands used by the instruction.
*/
-#define bc_program_retire(p, nres, nops) \
- (bc_vec_npopAt(&(p)->results, (nops), (p)->results.len - (nres + nops)))
+#define bc_program_retire(p, nops) \
+ do \
+ { \
+ bc_vec_npopAt(&(p)->results, (nops), \
+ (p)->results.len - ((p)->nresults + nops)); \
+ p->nresults = 0; \
+ } \
+ while (0)
#if DC_ENABLED
diff --git a/contrib/bc/include/rand.h b/contrib/bc/include/rand.h
index aee63b866cf6..f88de071b25d 100644
--- a/contrib/bc/include/rand.h
+++ b/contrib/bc/include/rand.h
@@ -13,7 +13,7 @@
* This code is under the following license:
*
* Copyright (c) 2014-2017 Melissa O'Neill and PCG Project contributors
- * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+ * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
diff --git a/contrib/bc/include/read.h b/contrib/bc/include/read.h
index 62e6897635a2..c43a9980327b 100644
--- a/contrib/bc/include/read.h
+++ b/contrib/bc/include/read.h
@@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+ * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/include/status.h b/contrib/bc/include/status.h
index 203f09af628b..8e3ab9ed218b 100644
--- a/contrib/bc/include/status.h
+++ b/contrib/bc/include/status.h
@@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+ * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -683,12 +683,7 @@ typedef enum BcMode
#define BC_NO_SIG_EXC(vm) \
BC_LIKELY((vm)->status == (sig_atomic_t) BC_STATUS_SUCCESS && !(vm)->sig)
-#ifndef _WIN32
-#define BC_SIG_INTERRUPT(vm) \
- BC_UNLIKELY((vm)->sig != 0 && (vm)->sig != SIGWINCH)
-#else // _WIN32
#define BC_SIG_INTERRUPT(vm) BC_UNLIKELY((vm)->sig != 0)
-#endif // _WIN32
#if BC_DEBUG
diff --git a/contrib/bc/include/vector.h b/contrib/bc/include/vector.h
index cad5fc2aa7c3..01b029961465 100644
--- a/contrib/bc/include/vector.h
+++ b/contrib/bc/include/vector.h
@@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+ * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/include/version.h b/contrib/bc/include/version.h
deleted file mode 100644
index a4fb8def5024..000000000000
--- a/contrib/bc/include/version.h
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * *****************************************************************************
- *
- * SPDX-License-Identifier: BSD-2-Clause
- *
- * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- *
- * * Redistributions of source code must retain the above copyright notice, this
- * list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *
- * *****************************************************************************
- *
- * The version of bc.
- *
- */
-
-#ifndef BC_VERSION_H
-#define BC_VERSION_H
-
-/// The current version.
-#define VERSION 7.0.2
-
-#endif // BC_VERSION_H
diff --git a/contrib/bc/include/vm.h b/contrib/bc/include/vm.h
index e81206b63871..86e0fe06edc1 100644
--- a/contrib/bc/include/vm.h
+++ b/contrib/bc/include/vm.h
@@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+ * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -52,7 +52,6 @@
#endif // BC_ENABLE_NLS
-#include <version.h>
#include <status.h>
#include <num.h>
#include <lex.h>
diff --git a/contrib/bc/locales/de_DE.ISO8859-1.msg b/contrib/bc/locales/de_DE.ISO8859-1.msg
index 9700ab070b2c..3269b7632cc7 100644
--- a/contrib/bc/locales/de_DE.ISO8859-1.msg
+++ b/contrib/bc/locales/de_DE.ISO8859-1.msg
@@ -1,7 +1,7 @@
$ $
$ SPDX-License-Identifier: BSD-2-Clause
$ $
-$ Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+$ Copyright (c) 2018-2025 Gavin D. Howard and contributors.
$ $
$ Redistribution and use in source and binary forms, with or without
$ modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/locales/de_DE.UTF-8.msg b/contrib/bc/locales/de_DE.UTF-8.msg
index 7b918fc6d1cd..7d5859584d88 100644
--- a/contrib/bc/locales/de_DE.UTF-8.msg
+++ b/contrib/bc/locales/de_DE.UTF-8.msg
@@ -1,7 +1,7 @@
$ $
$ SPDX-License-Identifier: BSD-2-Clause
$ $
-$ Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+$ Copyright (c) 2018-2025 Gavin D. Howard and contributors.
$ $
$ Redistribution and use in source and binary forms, with or without
$ modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/locales/en_US.msg b/contrib/bc/locales/en_US.msg
index 4afcbcd1f813..7ae50d189b57 100644
--- a/contrib/bc/locales/en_US.msg
+++ b/contrib/bc/locales/en_US.msg
@@ -1,7 +1,7 @@
$ $
$ SPDX-License-Identifier: BSD-2-Clause
$ $
-$ Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+$ Copyright (c) 2018-2025 Gavin D. Howard and contributors.
$ $
$ Redistribution and use in source and binary forms, with or without
$ modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/locales/es_ES.ISO8859-1.msg b/contrib/bc/locales/es_ES.ISO8859-1.msg
index 4d022d9bf664..a9edbfa26734 100644
--- a/contrib/bc/locales/es_ES.ISO8859-1.msg
+++ b/contrib/bc/locales/es_ES.ISO8859-1.msg
@@ -1,7 +1,7 @@
$ $
$ SPDX-License-Identifier: BSD-2-Clause
$ $
-$ Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+$ Copyright (c) 2018-2025 Gavin D. Howard and contributors.
$ $
$ Redistribution and use in source and binary forms, with or without
$ modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/locales/es_ES.UTF-8.msg b/contrib/bc/locales/es_ES.UTF-8.msg
index 364cff6ee57f..25512c0862da 100644
--- a/contrib/bc/locales/es_ES.UTF-8.msg
+++ b/contrib/bc/locales/es_ES.UTF-8.msg
@@ -1,7 +1,7 @@
$ $
$ SPDX-License-Identifier: BSD-2-Clause
$ $
-$ Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+$ Copyright (c) 2018-2025 Gavin D. Howard and contributors.
$ $
$ Redistribution and use in source and binary forms, with or without
$ modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/locales/fr_FR.ISO8859-1.msg b/contrib/bc/locales/fr_FR.ISO8859-1.msg
index b4b39866c96e..a3935535521c 100644
--- a/contrib/bc/locales/fr_FR.ISO8859-1.msg
+++ b/contrib/bc/locales/fr_FR.ISO8859-1.msg
@@ -1,7 +1,7 @@
$ $
$ SPDX-License-Identifier: BSD-2-Clause
$ $
-$ Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+$ Copyright (c) 2018-2025 Gavin D. Howard and contributors.
$ $
$ Redistribution and use in source and binary forms, with or without
$ modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/locales/fr_FR.UTF-8.msg b/contrib/bc/locales/fr_FR.UTF-8.msg
index c3387e31ae9f..dee83c110230 100644
--- a/contrib/bc/locales/fr_FR.UTF-8.msg
+++ b/contrib/bc/locales/fr_FR.UTF-8.msg
@@ -1,7 +1,7 @@
$ $
$ SPDX-License-Identifier: BSD-2-Clause
$ $
-$ Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+$ Copyright (c) 2018-2025 Gavin D. Howard and contributors.
$ $
$ Redistribution and use in source and binary forms, with or without
$ modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/locales/ja_JP.UTF-8.msg b/contrib/bc/locales/ja_JP.UTF-8.msg
index 21640eb9f1cb..5a6aac17120a 100644
--- a/contrib/bc/locales/ja_JP.UTF-8.msg
+++ b/contrib/bc/locales/ja_JP.UTF-8.msg
@@ -1,7 +1,7 @@
$ $
$ SPDX-License-Identifier: BSD-2-Clause
$ $
-$ Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+$ Copyright (c) 2018-2025 Gavin D. Howard and contributors.
$ $
$ Redistribution and use in source and binary forms, with or without
$ modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/locales/ja_JP.eucJP.msg b/contrib/bc/locales/ja_JP.eucJP.msg
index 3e3b73d20f4e..3bd47e1e7d30 100644
--- a/contrib/bc/locales/ja_JP.eucJP.msg
+++ b/contrib/bc/locales/ja_JP.eucJP.msg
@@ -1,7 +1,7 @@
$ $
$ SPDX-License-Identifier: BSD-2-Clause
$ $
-$ Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+$ Copyright (c) 2018-2025 Gavin D. Howard and contributors.
$ $
$ Redistribution and use in source and binary forms, with or without
$ modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/locales/nl_NL.ISO8859-1.msg b/contrib/bc/locales/nl_NL.ISO8859-1.msg
index aaf41c65b04d..4531269752a2 100644
--- a/contrib/bc/locales/nl_NL.ISO8859-1.msg
+++ b/contrib/bc/locales/nl_NL.ISO8859-1.msg
@@ -1,7 +1,7 @@
$ $
$ SPDX-License-Identifier: BSD-2-Clause
$ $
-$ Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+$ Copyright (c) 2018-2025 Gavin D. Howard and contributors.
$ $
$ Redistribution and use in source and binary forms, with or without
$ modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/locales/nl_NL.UTF-8.msg b/contrib/bc/locales/nl_NL.UTF-8.msg
index 0ab0b9c3dc61..29eb3f344fba 100644
--- a/contrib/bc/locales/nl_NL.UTF-8.msg
+++ b/contrib/bc/locales/nl_NL.UTF-8.msg
@@ -1,7 +1,7 @@
$ $
$ SPDX-License-Identifier: BSD-2-Clause
$ $
-$ Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+$ Copyright (c) 2018-2025 Gavin D. Howard and contributors.
$ $
$ Redistribution and use in source and binary forms, with or without
$ modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/locales/pl_PL.ISO8859-2.msg b/contrib/bc/locales/pl_PL.ISO8859-2.msg
index 5b427c808fdd..c1e2b8b5355e 100644
--- a/contrib/bc/locales/pl_PL.ISO8859-2.msg
+++ b/contrib/bc/locales/pl_PL.ISO8859-2.msg
@@ -1,7 +1,7 @@
$ $
$ SPDX-License-Identifier: BSD-2-Clause
$ $
-$ Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+$ Copyright (c) 2018-2025 Gavin D. Howard and contributors.
$ $
$ Redistribution and use in source and binary forms, with or without
$ modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/locales/pl_PL.UTF-8.msg b/contrib/bc/locales/pl_PL.UTF-8.msg
index fd0f85b5f767..27cbabfc33d4 100644
--- a/contrib/bc/locales/pl_PL.UTF-8.msg
+++ b/contrib/bc/locales/pl_PL.UTF-8.msg
@@ -1,7 +1,7 @@
$ $
$ SPDX-License-Identifier: BSD-2-Clause
$ $
-$ Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+$ Copyright (c) 2018-2025 Gavin D. Howard and contributors.
$ $
$ Redistribution and use in source and binary forms, with or without
$ modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/locales/pt_PT.ISO8859-1.msg b/contrib/bc/locales/pt_PT.ISO8859-1.msg
index 9b365b4a7bd1..0bec4b284f9e 100644
--- a/contrib/bc/locales/pt_PT.ISO8859-1.msg
+++ b/contrib/bc/locales/pt_PT.ISO8859-1.msg
@@ -1,7 +1,7 @@
$ $
$ SPDX-License-Identifier: BSD-2-Clause
$ $
-$ Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+$ Copyright (c) 2018-2025 Gavin D. Howard and contributors.
$ $
$ Redistribution and use in source and binary forms, with or without
$ modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/locales/pt_PT.UTF-8.msg b/contrib/bc/locales/pt_PT.UTF-8.msg
index f5054a178cf4..5204e8beef40 100644
--- a/contrib/bc/locales/pt_PT.UTF-8.msg
+++ b/contrib/bc/locales/pt_PT.UTF-8.msg
@@ -1,7 +1,7 @@
$ $
$ SPDX-License-Identifier: BSD-2-Clause
$ $
-$ Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+$ Copyright (c) 2018-2025 Gavin D. Howard and contributors.
$ $
$ Redistribution and use in source and binary forms, with or without
$ modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/locales/ru_RU.CP1251.msg b/contrib/bc/locales/ru_RU.CP1251.msg
index ac8957cc6aa8..3d25413b058c 100644
--- a/contrib/bc/locales/ru_RU.CP1251.msg
+++ b/contrib/bc/locales/ru_RU.CP1251.msg
@@ -1,7 +1,7 @@
$ $
$ SPDX-License-Identifier: BSD-2-Clause
$ $
-$ Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+$ Copyright (c) 2018-2025 Gavin D. Howard and contributors.
$ $
$ Redistribution and use in source and binary forms, with or without
$ modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/locales/ru_RU.CP866.msg b/contrib/bc/locales/ru_RU.CP866.msg
index 763fd55a3653..6700dc6c401f 100644
--- a/contrib/bc/locales/ru_RU.CP866.msg
+++ b/contrib/bc/locales/ru_RU.CP866.msg
@@ -1,7 +1,7 @@
$ $
$ SPDX-License-Identifier: BSD-2-Clause
$ $
-$ Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+$ Copyright (c) 2018-2025 Gavin D. Howard and contributors.
$ $
$ Redistribution and use in source and binary forms, with or without
$ modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/locales/ru_RU.ISO8859-5.msg b/contrib/bc/locales/ru_RU.ISO8859-5.msg
index bbb1f418c3a9..66766fbf1936 100644
--- a/contrib/bc/locales/ru_RU.ISO8859-5.msg
+++ b/contrib/bc/locales/ru_RU.ISO8859-5.msg
@@ -1,7 +1,7 @@
$ $
$ SPDX-License-Identifier: BSD-2-Clause
$ $
-$ Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+$ Copyright (c) 2018-2025 Gavin D. Howard and contributors.
$ $
$ Redistribution and use in source and binary forms, with or without
$ modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/locales/ru_RU.KOI8-R.msg b/contrib/bc/locales/ru_RU.KOI8-R.msg
index d1e2bdc014d2..ecc7f03a1a93 100644
--- a/contrib/bc/locales/ru_RU.KOI8-R.msg
+++ b/contrib/bc/locales/ru_RU.KOI8-R.msg
@@ -1,7 +1,7 @@
$ $
$ SPDX-License-Identifier: BSD-2-Clause
$ $
-$ Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+$ Copyright (c) 2018-2025 Gavin D. Howard and contributors.
$ $
$ Redistribution and use in source and binary forms, with or without
$ modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/locales/ru_RU.UTF-8.msg b/contrib/bc/locales/ru_RU.UTF-8.msg
index b45b3634a76b..fb796155f497 100644
--- a/contrib/bc/locales/ru_RU.UTF-8.msg
+++ b/contrib/bc/locales/ru_RU.UTF-8.msg
@@ -1,7 +1,7 @@
$ $
$ SPDX-License-Identifier: BSD-2-Clause
$ $
-$ Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+$ Copyright (c) 2018-2025 Gavin D. Howard and contributors.
$ $
$ Redistribution and use in source and binary forms, with or without
$ modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/locales/zh_CN.GB18030.msg b/contrib/bc/locales/zh_CN.GB18030.msg
index 3625c5b40fdf..c01a2239f624 100644
--- a/contrib/bc/locales/zh_CN.GB18030.msg
+++ b/contrib/bc/locales/zh_CN.GB18030.msg
@@ -1,7 +1,7 @@
$ $
$ SPDX-License-Identifier: BSD-2-Clause
$ $
-$ Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+$ Copyright (c) 2018-2025 Gavin D. Howard and contributors.
$ $
$ Redistribution and use in source and binary forms, with or without
$ modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/locales/zh_CN.GB2312.msg b/contrib/bc/locales/zh_CN.GB2312.msg
index 3625c5b40fdf..c01a2239f624 100644
--- a/contrib/bc/locales/zh_CN.GB2312.msg
+++ b/contrib/bc/locales/zh_CN.GB2312.msg
@@ -1,7 +1,7 @@
$ $
$ SPDX-License-Identifier: BSD-2-Clause
$ $
-$ Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+$ Copyright (c) 2018-2025 Gavin D. Howard and contributors.
$ $
$ Redistribution and use in source and binary forms, with or without
$ modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/locales/zh_CN.GBK.msg b/contrib/bc/locales/zh_CN.GBK.msg
index 3625c5b40fdf..c01a2239f624 100644
--- a/contrib/bc/locales/zh_CN.GBK.msg
+++ b/contrib/bc/locales/zh_CN.GBK.msg
@@ -1,7 +1,7 @@
$ $
$ SPDX-License-Identifier: BSD-2-Clause
$ $
-$ Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+$ Copyright (c) 2018-2025 Gavin D. Howard and contributors.
$ $
$ Redistribution and use in source and binary forms, with or without
$ modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/locales/zh_CN.UTF-8.msg b/contrib/bc/locales/zh_CN.UTF-8.msg
index 95813f411698..3767e45a3950 100644
--- a/contrib/bc/locales/zh_CN.UTF-8.msg
+++ b/contrib/bc/locales/zh_CN.UTF-8.msg
@@ -1,7 +1,7 @@
$ $
$ SPDX-License-Identifier: BSD-2-Clause
$ $
-$ Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+$ Copyright (c) 2018-2025 Gavin D. Howard and contributors.
$ $
$ Redistribution and use in source and binary forms, with or without
$ modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/locales/zh_CN.eucCN.msg b/contrib/bc/locales/zh_CN.eucCN.msg
index 3625c5b40fdf..c01a2239f624 100644
--- a/contrib/bc/locales/zh_CN.eucCN.msg
+++ b/contrib/bc/locales/zh_CN.eucCN.msg
@@ -1,7 +1,7 @@
$ $
$ SPDX-License-Identifier: BSD-2-Clause
$ $
-$ Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+$ Copyright (c) 2018-2025 Gavin D. Howard and contributors.
$ $
$ Redistribution and use in source and binary forms, with or without
$ modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/manuals/bc/A.1 b/contrib/bc/manuals/bc/A.1
index adeb62f82e6a..9b38de7dc1bc 100644
--- a/contrib/bc/manuals/bc/A.1
+++ b/contrib/bc/manuals/bc/A.1
@@ -1,7 +1,7 @@
.\"
.\" SPDX-License-Identifier: BSD-2-Clause
.\"
-.\" Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+.\" Copyright (c) 2018-2025 Gavin D. Howard and contributors.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions are met:
@@ -2109,17 +2109,6 @@ If you want to a use signed two\[cq]s complement argument, use
\f[B]s2u(x)\f[R] to convert.
.RE
.TP
-\f[B]bunrev(t)\f[R]
-Assumes \f[B]t\f[R] is a bitwise\-reversed number with an extra set bit
-one place more significant than the real most significant bit (which was
-the least significant bit in the original number).
-This number is reversed and returned without the extra set bit.
-.RS
-.PP
-This function is used to implement other bitwise functions; it is not
-meant to be used by users, but it can be.
-.RE
-.TP
\f[B]plz(x)\f[R]
If \f[B]x\f[R] is not equal to \f[B]0\f[R] and greater that
\f[B]\-1\f[R] and less than \f[B]1\f[R], it is printed with a leading
@@ -2943,7 +2932,7 @@ Before version \f[B]6.1.0\f[R], this bc(1) had incorrect behavior for
the \f[B]quit\f[R] statement.
.PP
No other bugs are known.
-Report bugs at https://git.gavinhoward.com/gavin/bc .
+Report bugs at https://github.com/gavinhoward/bc .
.SH AUTHORS
Gavin D. Howard \c
.MT gavin@gavinhoward.com
diff --git a/contrib/bc/manuals/bc/A.1.md b/contrib/bc/manuals/bc/A.1.md
index e89305b1af44..73da0caa0a64 100644
--- a/contrib/bc/manuals/bc/A.1.md
+++ b/contrib/bc/manuals/bc/A.1.md
@@ -2,7 +2,7 @@
SPDX-License-Identifier: BSD-2-Clause
-Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+Copyright (c) 2018-2025 Gavin D. Howard and contributors.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
@@ -1766,16 +1766,6 @@ The extended library is a **non-portable extension**.
If you want to a use signed two's complement argument, use **s2u(x)** to
convert.
-**bunrev(t)**
-
-: Assumes **t** is a bitwise-reversed number with an extra set bit one place
- more significant than the real most significant bit (which was the least
- significant bit in the original number). This number is reversed and
- returned without the extra set bit.
-
- This function is used to implement other bitwise functions; it is not meant
- to be used by users, but it can be.
-
**plz(x)**
: If **x** is not equal to **0** and greater that **-1** and less than **1**,
@@ -2521,7 +2511,7 @@ This bc(1) supports error messages for different locales, and thus, it supports
Before version **6.1.0**, this bc(1) had incorrect behavior for the **quit**
statement.
-No other bugs are known. Report bugs at https://git.gavinhoward.com/gavin/bc .
+No other bugs are known. Report bugs at https://github.com/gavinhoward/bc .
# AUTHORS
diff --git a/contrib/bc/manuals/bc/E.1 b/contrib/bc/manuals/bc/E.1
index e2f1b034e69a..36f402d99936 100644
--- a/contrib/bc/manuals/bc/E.1
+++ b/contrib/bc/manuals/bc/E.1
@@ -1,7 +1,7 @@
.\"
.\" SPDX-License-Identifier: BSD-2-Clause
.\"
-.\" Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+.\" Copyright (c) 2018-2025 Gavin D. Howard and contributors.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions are met:
@@ -1758,7 +1758,7 @@ Before version \f[B]6.1.0\f[R], this bc(1) had incorrect behavior for
the \f[B]quit\f[R] statement.
.PP
No other bugs are known.
-Report bugs at https://git.gavinhoward.com/gavin/bc .
+Report bugs at https://github.com/gavinhoward/bc .
.SH AUTHORS
Gavin D. Howard \c
.MT gavin@gavinhoward.com
diff --git a/contrib/bc/manuals/bc/E.1.md b/contrib/bc/manuals/bc/E.1.md
index 0082caea8408..4536381f72b3 100644
--- a/contrib/bc/manuals/bc/E.1.md
+++ b/contrib/bc/manuals/bc/E.1.md
@@ -2,7 +2,7 @@
SPDX-License-Identifier: BSD-2-Clause
-Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+Copyright (c) 2018-2025 Gavin D. Howard and contributors.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
@@ -1509,7 +1509,7 @@ This bc(1) supports error messages for different locales, and thus, it supports
Before version **6.1.0**, this bc(1) had incorrect behavior for the **quit**
statement.
-No other bugs are known. Report bugs at https://git.gavinhoward.com/gavin/bc .
+No other bugs are known. Report bugs at https://github.com/gavinhoward/bc .
# AUTHORS
diff --git a/contrib/bc/manuals/bc/EH.1 b/contrib/bc/manuals/bc/EH.1
index c132a0b76a49..bf29cca9e820 100644
--- a/contrib/bc/manuals/bc/EH.1
+++ b/contrib/bc/manuals/bc/EH.1
@@ -1,7 +1,7 @@
.\"
.\" SPDX-License-Identifier: BSD-2-Clause
.\"
-.\" Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+.\" Copyright (c) 2018-2025 Gavin D. Howard and contributors.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions are met:
@@ -1731,7 +1731,7 @@ Before version \f[B]6.1.0\f[R], this bc(1) had incorrect behavior for
the \f[B]quit\f[R] statement.
.PP
No other bugs are known.
-Report bugs at https://git.gavinhoward.com/gavin/bc .
+Report bugs at https://github.com/gavinhoward/bc .
.SH AUTHORS
Gavin D. Howard \c
.MT gavin@gavinhoward.com
diff --git a/contrib/bc/manuals/bc/EH.1.md b/contrib/bc/manuals/bc/EH.1.md
index 7e682058234c..27c3f46f9edc 100644
--- a/contrib/bc/manuals/bc/EH.1.md
+++ b/contrib/bc/manuals/bc/EH.1.md
@@ -2,7 +2,7 @@
SPDX-License-Identifier: BSD-2-Clause
-Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+Copyright (c) 2018-2025 Gavin D. Howard and contributors.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
@@ -1483,7 +1483,7 @@ This bc(1) supports error messages for different locales, and thus, it supports
Before version **6.1.0**, this bc(1) had incorrect behavior for the **quit**
statement.
-No other bugs are known. Report bugs at https://git.gavinhoward.com/gavin/bc .
+No other bugs are known. Report bugs at https://github.com/gavinhoward/bc .
# AUTHORS
diff --git a/contrib/bc/manuals/bc/EHN.1 b/contrib/bc/manuals/bc/EHN.1
index e3395b1cc20d..2c0a2ddc9cd9 100644
--- a/contrib/bc/manuals/bc/EHN.1
+++ b/contrib/bc/manuals/bc/EHN.1
@@ -1,7 +1,7 @@
.\"
.\" SPDX-License-Identifier: BSD-2-Clause
.\"
-.\" Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+.\" Copyright (c) 2018-2025 Gavin D. Howard and contributors.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions are met:
@@ -1725,7 +1725,7 @@ Before version \f[B]6.1.0\f[R], this bc(1) had incorrect behavior for
the \f[B]quit\f[R] statement.
.PP
No other bugs are known.
-Report bugs at https://git.gavinhoward.com/gavin/bc .
+Report bugs at https://github.com/gavinhoward/bc .
.SH AUTHORS
Gavin D. Howard \c
.MT gavin@gavinhoward.com
diff --git a/contrib/bc/manuals/bc/EHN.1.md b/contrib/bc/manuals/bc/EHN.1.md
index 9578d2ab7720..eee3c9c8cb99 100644
--- a/contrib/bc/manuals/bc/EHN.1.md
+++ b/contrib/bc/manuals/bc/EHN.1.md
@@ -2,7 +2,7 @@
SPDX-License-Identifier: BSD-2-Clause
-Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+Copyright (c) 2018-2025 Gavin D. Howard and contributors.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
@@ -1475,7 +1475,7 @@ use a period (**.**) as a radix point, regardless of the value of
Before version **6.1.0**, this bc(1) had incorrect behavior for the **quit**
statement.
-No other bugs are known. Report bugs at https://git.gavinhoward.com/gavin/bc .
+No other bugs are known. Report bugs at https://github.com/gavinhoward/bc .
# AUTHORS
diff --git a/contrib/bc/manuals/bc/EN.1 b/contrib/bc/manuals/bc/EN.1
index c1ccbec567ec..ce88b2af8e14 100644
--- a/contrib/bc/manuals/bc/EN.1
+++ b/contrib/bc/manuals/bc/EN.1
@@ -1,7 +1,7 @@
.\"
.\" SPDX-License-Identifier: BSD-2-Clause
.\"
-.\" Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+.\" Copyright (c) 2018-2025 Gavin D. Howard and contributors.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions are met:
@@ -1752,7 +1752,7 @@ Before version \f[B]6.1.0\f[R], this bc(1) had incorrect behavior for
the \f[B]quit\f[R] statement.
.PP
No other bugs are known.
-Report bugs at https://git.gavinhoward.com/gavin/bc .
+Report bugs at https://github.com/gavinhoward/bc .
.SH AUTHORS
Gavin D. Howard \c
.MT gavin@gavinhoward.com
diff --git a/contrib/bc/manuals/bc/EN.1.md b/contrib/bc/manuals/bc/EN.1.md
index f6ad00930902..8752c4eda7e1 100644
--- a/contrib/bc/manuals/bc/EN.1.md
+++ b/contrib/bc/manuals/bc/EN.1.md
@@ -2,7 +2,7 @@
SPDX-License-Identifier: BSD-2-Clause
-Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+Copyright (c) 2018-2025 Gavin D. Howard and contributors.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
@@ -1501,7 +1501,7 @@ use a period (**.**) as a radix point, regardless of the value of
Before version **6.1.0**, this bc(1) had incorrect behavior for the **quit**
statement.
-No other bugs are known. Report bugs at https://git.gavinhoward.com/gavin/bc .
+No other bugs are known. Report bugs at https://github.com/gavinhoward/bc .
# AUTHORS
diff --git a/contrib/bc/manuals/bc/H.1 b/contrib/bc/manuals/bc/H.1
index 9dc46ee50dee..ee0312f3e8db 100644
--- a/contrib/bc/manuals/bc/H.1
+++ b/contrib/bc/manuals/bc/H.1
@@ -1,7 +1,7 @@
.\"
.\" SPDX-License-Identifier: BSD-2-Clause
.\"
-.\" Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+.\" Copyright (c) 2018-2025 Gavin D. Howard and contributors.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions are met:
@@ -2109,17 +2109,6 @@ If you want to a use signed two\[cq]s complement argument, use
\f[B]s2u(x)\f[R] to convert.
.RE
.TP
-\f[B]bunrev(t)\f[R]
-Assumes \f[B]t\f[R] is a bitwise\-reversed number with an extra set bit
-one place more significant than the real most significant bit (which was
-the least significant bit in the original number).
-This number is reversed and returned without the extra set bit.
-.RS
-.PP
-This function is used to implement other bitwise functions; it is not
-meant to be used by users, but it can be.
-.RE
-.TP
\f[B]plz(x)\f[R]
If \f[B]x\f[R] is not equal to \f[B]0\f[R] and greater that
\f[B]\-1\f[R] and less than \f[B]1\f[R], it is printed with a leading
@@ -2916,7 +2905,7 @@ Before version \f[B]6.1.0\f[R], this bc(1) had incorrect behavior for
the \f[B]quit\f[R] statement.
.PP
No other bugs are known.
-Report bugs at https://git.gavinhoward.com/gavin/bc .
+Report bugs at https://github.com/gavinhoward/bc .
.SH AUTHORS
Gavin D. Howard \c
.MT gavin@gavinhoward.com
diff --git a/contrib/bc/manuals/bc/H.1.md b/contrib/bc/manuals/bc/H.1.md
index fbc0658d8171..76763ed648e5 100644
--- a/contrib/bc/manuals/bc/H.1.md
+++ b/contrib/bc/manuals/bc/H.1.md
@@ -2,7 +2,7 @@
SPDX-License-Identifier: BSD-2-Clause
-Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+Copyright (c) 2018-2025 Gavin D. Howard and contributors.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
@@ -1766,16 +1766,6 @@ The extended library is a **non-portable extension**.
If you want to a use signed two's complement argument, use **s2u(x)** to
convert.
-**bunrev(t)**
-
-: Assumes **t** is a bitwise-reversed number with an extra set bit one place
- more significant than the real most significant bit (which was the least
- significant bit in the original number). This number is reversed and
- returned without the extra set bit.
-
- This function is used to implement other bitwise functions; it is not meant
- to be used by users, but it can be.
-
**plz(x)**
: If **x** is not equal to **0** and greater that **-1** and less than **1**,
@@ -2495,7 +2485,7 @@ This bc(1) supports error messages for different locales, and thus, it supports
Before version **6.1.0**, this bc(1) had incorrect behavior for the **quit**
statement.
-No other bugs are known. Report bugs at https://git.gavinhoward.com/gavin/bc .
+No other bugs are known. Report bugs at https://github.com/gavinhoward/bc .
# AUTHORS
diff --git a/contrib/bc/manuals/bc/HN.1 b/contrib/bc/manuals/bc/HN.1
index 7b4577f2dbd3..fb593b6d3a64 100644
--- a/contrib/bc/manuals/bc/HN.1
+++ b/contrib/bc/manuals/bc/HN.1
@@ -1,7 +1,7 @@
.\"
.\" SPDX-License-Identifier: BSD-2-Clause
.\"
-.\" Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+.\" Copyright (c) 2018-2025 Gavin D. Howard and contributors.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions are met:
@@ -2109,17 +2109,6 @@ If you want to a use signed two\[cq]s complement argument, use
\f[B]s2u(x)\f[R] to convert.
.RE
.TP
-\f[B]bunrev(t)\f[R]
-Assumes \f[B]t\f[R] is a bitwise\-reversed number with an extra set bit
-one place more significant than the real most significant bit (which was
-the least significant bit in the original number).
-This number is reversed and returned without the extra set bit.
-.RS
-.PP
-This function is used to implement other bitwise functions; it is not
-meant to be used by users, but it can be.
-.RE
-.TP
\f[B]plz(x)\f[R]
If \f[B]x\f[R] is not equal to \f[B]0\f[R] and greater that
\f[B]\-1\f[R] and less than \f[B]1\f[R], it is printed with a leading
@@ -2910,7 +2899,7 @@ Before version \f[B]6.1.0\f[R], this bc(1) had incorrect behavior for
the \f[B]quit\f[R] statement.
.PP
No other bugs are known.
-Report bugs at https://git.gavinhoward.com/gavin/bc .
+Report bugs at https://github.com/gavinhoward/bc .
.SH AUTHORS
Gavin D. Howard \c
.MT gavin@gavinhoward.com
diff --git a/contrib/bc/manuals/bc/HN.1.md b/contrib/bc/manuals/bc/HN.1.md
index 015035c14daf..fbbf3b09715d 100644
--- a/contrib/bc/manuals/bc/HN.1.md
+++ b/contrib/bc/manuals/bc/HN.1.md
@@ -2,7 +2,7 @@
SPDX-License-Identifier: BSD-2-Clause
-Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+Copyright (c) 2018-2025 Gavin D. Howard and contributors.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
@@ -1766,16 +1766,6 @@ The extended library is a **non-portable extension**.
If you want to a use signed two's complement argument, use **s2u(x)** to
convert.
-**bunrev(t)**
-
-: Assumes **t** is a bitwise-reversed number with an extra set bit one place
- more significant than the real most significant bit (which was the least
- significant bit in the original number). This number is reversed and
- returned without the extra set bit.
-
- This function is used to implement other bitwise functions; it is not meant
- to be used by users, but it can be.
-
**plz(x)**
: If **x** is not equal to **0** and greater that **-1** and less than **1**,
@@ -2487,7 +2477,7 @@ use a period (**.**) as a radix point, regardless of the value of
Before version **6.1.0**, this bc(1) had incorrect behavior for the **quit**
statement.
-No other bugs are known. Report bugs at https://git.gavinhoward.com/gavin/bc .
+No other bugs are known. Report bugs at https://github.com/gavinhoward/bc .
# AUTHORS
diff --git a/contrib/bc/manuals/bc/N.1 b/contrib/bc/manuals/bc/N.1
index 193e0d15f6fb..8fc3249f8b73 100644
--- a/contrib/bc/manuals/bc/N.1
+++ b/contrib/bc/manuals/bc/N.1
@@ -1,7 +1,7 @@
.\"
.\" SPDX-License-Identifier: BSD-2-Clause
.\"
-.\" Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+.\" Copyright (c) 2018-2025 Gavin D. Howard and contributors.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions are met:
@@ -2109,17 +2109,6 @@ If you want to a use signed two\[cq]s complement argument, use
\f[B]s2u(x)\f[R] to convert.
.RE
.TP
-\f[B]bunrev(t)\f[R]
-Assumes \f[B]t\f[R] is a bitwise\-reversed number with an extra set bit
-one place more significant than the real most significant bit (which was
-the least significant bit in the original number).
-This number is reversed and returned without the extra set bit.
-.RS
-.PP
-This function is used to implement other bitwise functions; it is not
-meant to be used by users, but it can be.
-.RE
-.TP
\f[B]plz(x)\f[R]
If \f[B]x\f[R] is not equal to \f[B]0\f[R] and greater that
\f[B]\-1\f[R] and less than \f[B]1\f[R], it is printed with a leading
@@ -2937,7 +2926,7 @@ Before version \f[B]6.1.0\f[R], this bc(1) had incorrect behavior for
the \f[B]quit\f[R] statement.
.PP
No other bugs are known.
-Report bugs at https://git.gavinhoward.com/gavin/bc .
+Report bugs at https://github.com/gavinhoward/bc .
.SH AUTHORS
Gavin D. Howard \c
.MT gavin@gavinhoward.com
diff --git a/contrib/bc/manuals/bc/N.1.md b/contrib/bc/manuals/bc/N.1.md
index 859c32e3e774..6c9b6fce5d04 100644
--- a/contrib/bc/manuals/bc/N.1.md
+++ b/contrib/bc/manuals/bc/N.1.md
@@ -2,7 +2,7 @@
SPDX-License-Identifier: BSD-2-Clause
-Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+Copyright (c) 2018-2025 Gavin D. Howard and contributors.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
@@ -1766,16 +1766,6 @@ The extended library is a **non-portable extension**.
If you want to a use signed two's complement argument, use **s2u(x)** to
convert.
-**bunrev(t)**
-
-: Assumes **t** is a bitwise-reversed number with an extra set bit one place
- more significant than the real most significant bit (which was the least
- significant bit in the original number). This number is reversed and
- returned without the extra set bit.
-
- This function is used to implement other bitwise functions; it is not meant
- to be used by users, but it can be.
-
**plz(x)**
: If **x** is not equal to **0** and greater that **-1** and less than **1**,
@@ -2513,7 +2503,7 @@ use a period (**.**) as a radix point, regardless of the value of
Before version **6.1.0**, this bc(1) had incorrect behavior for the **quit**
statement.
-No other bugs are known. Report bugs at https://git.gavinhoward.com/gavin/bc .
+No other bugs are known. Report bugs at https://github.com/gavinhoward/bc .
# AUTHORS
diff --git a/contrib/bc/manuals/bcl.3 b/contrib/bc/manuals/bcl.3
index f2791624b2ca..adfa61681bd8 100644
--- a/contrib/bc/manuals/bcl.3
+++ b/contrib/bc/manuals/bcl.3
@@ -1,7 +1,7 @@
.\"
.\" SPDX-License-Identifier: BSD-2-Clause
.\"
-.\" Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+.\" Copyright (c) 2018-2025 Gavin D. Howard and contributors.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions are met:
@@ -1833,7 +1833,7 @@ the value of \f[B]LC_NUMERIC\f[R].
This is also true of bcl(3).
.SH BUGS
None are known.
-Report bugs at https://git.gavinhoward.com/gavin/bc.
+Report bugs at https://github.com/gavinhoward/bc .
.SH AUTHORS
Gavin D. Howard \c
.MT gavin@gavinhoward.com
diff --git a/contrib/bc/manuals/bcl.3.md b/contrib/bc/manuals/bcl.3.md
index 41c1c120b623..48eb6174bf48 100644
--- a/contrib/bc/manuals/bcl.3.md
+++ b/contrib/bc/manuals/bcl.3.md
@@ -2,7 +2,7 @@
SPDX-License-Identifier: BSD-2-Clause
-Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+Copyright (c) 2018-2025 Gavin D. Howard and contributors.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
@@ -1532,7 +1532,7 @@ use a period (**.**) as a radix point, regardless of the value of
# BUGS
-None are known. Report bugs at https://git.gavinhoward.com/gavin/bc.
+None are known. Report bugs at https://github.com/gavinhoward/bc .
# AUTHORS
diff --git a/contrib/bc/manuals/build.md b/contrib/bc/manuals/build.md
index d9c46ae22602..c0e7e35934b6 100644
--- a/contrib/bc/manuals/build.md
+++ b/contrib/bc/manuals/build.md
@@ -6,6 +6,9 @@ POSIX-compliant system.
To accomplish that, a POSIX-compatible, custom `configure.sh` script is used to
select build options, compiler, and compiler flags and generate a `Makefile`.
+The rest of this document talks about that, *not* the build system using
+[Rig][12], which is much simpler to understand, change, and use.
+
The general form of configuring, building, and installing this `bc` is as
follows:
@@ -40,7 +43,7 @@ accepted build options.
## Windows
For releases, Windows builds of `bc`, `dc`, and `bcl` are available for download
-from <https://git.gavinhoward.com/gavin/bc> and GitHub.
+from GitHub.
However, if you wish to build it yourself, this `bc` can be built using Visual
Studio or MSBuild.
@@ -993,3 +996,4 @@ Both commands are equivalent.
[9]: #nls-locale-support
[10]: #extra-math
[11]: #settings
+[12]: https://rigbuild.dev/
diff --git a/contrib/bc/manuals/dc/A.1 b/contrib/bc/manuals/dc/A.1
index d59e0fa68a58..50c14dd4098f 100644
--- a/contrib/bc/manuals/dc/A.1
+++ b/contrib/bc/manuals/dc/A.1
@@ -1,7 +1,7 @@
.\"
.\" SPDX-License-Identifier: BSD-2-Clause
.\"
-.\" Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+.\" Copyright (c) 2018-2025 Gavin D. Howard and contributors.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions are met:
@@ -1687,7 +1687,7 @@ specification at
https://pubs.opengroup.org/onlinepubs/9699919799/utilities/bc.html .
.SH BUGS
None are known.
-Report bugs at https://git.gavinhoward.com/gavin/bc .
+Report bugs at https://github.com/gavinhoward/bc .
.SH AUTHOR
Gavin D. Howard \c
.MT gavin@gavinhoward.com
diff --git a/contrib/bc/manuals/dc/A.1.md b/contrib/bc/manuals/dc/A.1.md
index ad0c59934fd1..bfe50b8230c6 100644
--- a/contrib/bc/manuals/dc/A.1.md
+++ b/contrib/bc/manuals/dc/A.1.md
@@ -2,7 +2,7 @@
SPDX-License-Identifier: BSD-2-Clause
-Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+Copyright (c) 2018-2025 Gavin D. Howard and contributors.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
@@ -1522,7 +1522,7 @@ https://pubs.opengroup.org/onlinepubs/9699919799/utilities/bc.html .
# BUGS
-None are known. Report bugs at https://git.gavinhoward.com/gavin/bc .
+None are known. Report bugs at https://github.com/gavinhoward/bc .
# AUTHOR
diff --git a/contrib/bc/manuals/dc/E.1 b/contrib/bc/manuals/dc/E.1
index a5febe44705f..b079e40ed62d 100644
--- a/contrib/bc/manuals/dc/E.1
+++ b/contrib/bc/manuals/dc/E.1
@@ -1,7 +1,7 @@
.\"
.\" SPDX-License-Identifier: BSD-2-Clause
.\"
-.\" Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+.\" Copyright (c) 2018-2025 Gavin D. Howard and contributors.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions are met:
@@ -1466,7 +1466,7 @@ specification at
https://pubs.opengroup.org/onlinepubs/9699919799/utilities/bc.html .
.SH BUGS
None are known.
-Report bugs at https://git.gavinhoward.com/gavin/bc .
+Report bugs at https://github.com/gavinhoward/bc .
.SH AUTHOR
Gavin D. Howard \c
.MT gavin@gavinhoward.com
diff --git a/contrib/bc/manuals/dc/E.1.md b/contrib/bc/manuals/dc/E.1.md
index 54b877999d0d..7bde12de49a2 100644
--- a/contrib/bc/manuals/dc/E.1.md
+++ b/contrib/bc/manuals/dc/E.1.md
@@ -2,7 +2,7 @@
SPDX-License-Identifier: BSD-2-Clause
-Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+Copyright (c) 2018-2025 Gavin D. Howard and contributors.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
@@ -1346,7 +1346,7 @@ https://pubs.opengroup.org/onlinepubs/9699919799/utilities/bc.html .
# BUGS
-None are known. Report bugs at https://git.gavinhoward.com/gavin/bc .
+None are known. Report bugs at https://github.com/gavinhoward/bc .
# AUTHOR
diff --git a/contrib/bc/manuals/dc/EH.1 b/contrib/bc/manuals/dc/EH.1
index 61fbaa4efe92..199f50fb0fe5 100644
--- a/contrib/bc/manuals/dc/EH.1
+++ b/contrib/bc/manuals/dc/EH.1
@@ -1,7 +1,7 @@
.\"
.\" SPDX-License-Identifier: BSD-2-Clause
.\"
-.\" Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+.\" Copyright (c) 2018-2025 Gavin D. Howard and contributors.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions are met:
@@ -1442,7 +1442,7 @@ specification at
https://pubs.opengroup.org/onlinepubs/9699919799/utilities/bc.html .
.SH BUGS
None are known.
-Report bugs at https://git.gavinhoward.com/gavin/bc .
+Report bugs at https://github.com/gavinhoward/bc .
.SH AUTHOR
Gavin D. Howard \c
.MT gavin@gavinhoward.com
diff --git a/contrib/bc/manuals/dc/EH.1.md b/contrib/bc/manuals/dc/EH.1.md
index 6398477a84dd..bbddd4ae9479 100644
--- a/contrib/bc/manuals/dc/EH.1.md
+++ b/contrib/bc/manuals/dc/EH.1.md
@@ -2,7 +2,7 @@
SPDX-License-Identifier: BSD-2-Clause
-Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+Copyright (c) 2018-2025 Gavin D. Howard and contributors.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
@@ -1323,7 +1323,7 @@ https://pubs.opengroup.org/onlinepubs/9699919799/utilities/bc.html .
# BUGS
-None are known. Report bugs at https://git.gavinhoward.com/gavin/bc .
+None are known. Report bugs at https://github.com/gavinhoward/bc .
# AUTHOR
diff --git a/contrib/bc/manuals/dc/EHN.1 b/contrib/bc/manuals/dc/EHN.1
index 974cb3c86791..3b4afa90e30f 100644
--- a/contrib/bc/manuals/dc/EHN.1
+++ b/contrib/bc/manuals/dc/EHN.1
@@ -1,7 +1,7 @@
.\"
.\" SPDX-License-Identifier: BSD-2-Clause
.\"
-.\" Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+.\" Copyright (c) 2018-2025 Gavin D. Howard and contributors.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions are met:
@@ -1439,7 +1439,7 @@ specification at
https://pubs.opengroup.org/onlinepubs/9699919799/utilities/bc.html .
.SH BUGS
None are known.
-Report bugs at https://git.gavinhoward.com/gavin/bc .
+Report bugs at https://github.com/gavinhoward/bc .
.SH AUTHOR
Gavin D. Howard \c
.MT gavin@gavinhoward.com
diff --git a/contrib/bc/manuals/dc/EHN.1.md b/contrib/bc/manuals/dc/EHN.1.md
index 51e30849996e..fc2c01d52a56 100644
--- a/contrib/bc/manuals/dc/EHN.1.md
+++ b/contrib/bc/manuals/dc/EHN.1.md
@@ -2,7 +2,7 @@
SPDX-License-Identifier: BSD-2-Clause
-Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+Copyright (c) 2018-2025 Gavin D. Howard and contributors.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
@@ -1318,7 +1318,7 @@ https://pubs.opengroup.org/onlinepubs/9699919799/utilities/bc.html .
# BUGS
-None are known. Report bugs at https://git.gavinhoward.com/gavin/bc .
+None are known. Report bugs at https://github.com/gavinhoward/bc .
# AUTHOR
diff --git a/contrib/bc/manuals/dc/EN.1 b/contrib/bc/manuals/dc/EN.1
index 5ce8defc91c7..2d1c038754e9 100644
--- a/contrib/bc/manuals/dc/EN.1
+++ b/contrib/bc/manuals/dc/EN.1
@@ -1,7 +1,7 @@
.\"
.\" SPDX-License-Identifier: BSD-2-Clause
.\"
-.\" Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+.\" Copyright (c) 2018-2025 Gavin D. Howard and contributors.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions are met:
@@ -1463,7 +1463,7 @@ specification at
https://pubs.opengroup.org/onlinepubs/9699919799/utilities/bc.html .
.SH BUGS
None are known.
-Report bugs at https://git.gavinhoward.com/gavin/bc .
+Report bugs at https://github.com/gavinhoward/bc .
.SH AUTHOR
Gavin D. Howard \c
.MT gavin@gavinhoward.com
diff --git a/contrib/bc/manuals/dc/EN.1.md b/contrib/bc/manuals/dc/EN.1.md
index ab9647a196be..ca8bb1ebdd01 100644
--- a/contrib/bc/manuals/dc/EN.1.md
+++ b/contrib/bc/manuals/dc/EN.1.md
@@ -2,7 +2,7 @@
SPDX-License-Identifier: BSD-2-Clause
-Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+Copyright (c) 2018-2025 Gavin D. Howard and contributors.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
@@ -1341,7 +1341,7 @@ https://pubs.opengroup.org/onlinepubs/9699919799/utilities/bc.html .
# BUGS
-None are known. Report bugs at https://git.gavinhoward.com/gavin/bc .
+None are known. Report bugs at https://github.com/gavinhoward/bc .
# AUTHOR
diff --git a/contrib/bc/manuals/dc/H.1 b/contrib/bc/manuals/dc/H.1
index 82c1bbd5c2b9..990eadec23f5 100644
--- a/contrib/bc/manuals/dc/H.1
+++ b/contrib/bc/manuals/dc/H.1
@@ -1,7 +1,7 @@
.\"
.\" SPDX-License-Identifier: BSD-2-Clause
.\"
-.\" Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+.\" Copyright (c) 2018-2025 Gavin D. Howard and contributors.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions are met:
@@ -1663,7 +1663,7 @@ specification at
https://pubs.opengroup.org/onlinepubs/9699919799/utilities/bc.html .
.SH BUGS
None are known.
-Report bugs at https://git.gavinhoward.com/gavin/bc .
+Report bugs at https://github.com/gavinhoward/bc .
.SH AUTHOR
Gavin D. Howard \c
.MT gavin@gavinhoward.com
diff --git a/contrib/bc/manuals/dc/H.1.md b/contrib/bc/manuals/dc/H.1.md
index 64c7142bc4a7..844dd686f215 100644
--- a/contrib/bc/manuals/dc/H.1.md
+++ b/contrib/bc/manuals/dc/H.1.md
@@ -2,7 +2,7 @@
SPDX-License-Identifier: BSD-2-Clause
-Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+Copyright (c) 2018-2025 Gavin D. Howard and contributors.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
@@ -1499,7 +1499,7 @@ https://pubs.opengroup.org/onlinepubs/9699919799/utilities/bc.html .
# BUGS
-None are known. Report bugs at https://git.gavinhoward.com/gavin/bc .
+None are known. Report bugs at https://github.com/gavinhoward/bc .
# AUTHOR
diff --git a/contrib/bc/manuals/dc/HN.1 b/contrib/bc/manuals/dc/HN.1
index c3f8c8ab1ff5..ebc4292bb138 100644
--- a/contrib/bc/manuals/dc/HN.1
+++ b/contrib/bc/manuals/dc/HN.1
@@ -1,7 +1,7 @@
.\"
.\" SPDX-License-Identifier: BSD-2-Clause
.\"
-.\" Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+.\" Copyright (c) 2018-2025 Gavin D. Howard and contributors.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions are met:
@@ -1660,7 +1660,7 @@ specification at
https://pubs.opengroup.org/onlinepubs/9699919799/utilities/bc.html .
.SH BUGS
None are known.
-Report bugs at https://git.gavinhoward.com/gavin/bc .
+Report bugs at https://github.com/gavinhoward/bc .
.SH AUTHOR
Gavin D. Howard \c
.MT gavin@gavinhoward.com
diff --git a/contrib/bc/manuals/dc/HN.1.md b/contrib/bc/manuals/dc/HN.1.md
index 28b9dadd4b4f..58c870f8efc0 100644
--- a/contrib/bc/manuals/dc/HN.1.md
+++ b/contrib/bc/manuals/dc/HN.1.md
@@ -2,7 +2,7 @@
SPDX-License-Identifier: BSD-2-Clause
-Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+Copyright (c) 2018-2025 Gavin D. Howard and contributors.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
@@ -1494,7 +1494,7 @@ https://pubs.opengroup.org/onlinepubs/9699919799/utilities/bc.html .
# BUGS
-None are known. Report bugs at https://git.gavinhoward.com/gavin/bc .
+None are known. Report bugs at https://github.com/gavinhoward/bc .
# AUTHOR
diff --git a/contrib/bc/manuals/dc/N.1 b/contrib/bc/manuals/dc/N.1
index 6e2baa587b1c..74ed018b97ab 100644
--- a/contrib/bc/manuals/dc/N.1
+++ b/contrib/bc/manuals/dc/N.1
@@ -1,7 +1,7 @@
.\"
.\" SPDX-License-Identifier: BSD-2-Clause
.\"
-.\" Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+.\" Copyright (c) 2018-2025 Gavin D. Howard and contributors.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions are met:
@@ -1684,7 +1684,7 @@ specification at
https://pubs.opengroup.org/onlinepubs/9699919799/utilities/bc.html .
.SH BUGS
None are known.
-Report bugs at https://git.gavinhoward.com/gavin/bc .
+Report bugs at https://github.com/gavinhoward/bc .
.SH AUTHOR
Gavin D. Howard \c
.MT gavin@gavinhoward.com
diff --git a/contrib/bc/manuals/dc/N.1.md b/contrib/bc/manuals/dc/N.1.md
index 22ea9c96bc80..10e8befb1d76 100644
--- a/contrib/bc/manuals/dc/N.1.md
+++ b/contrib/bc/manuals/dc/N.1.md
@@ -2,7 +2,7 @@
SPDX-License-Identifier: BSD-2-Clause
-Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+Copyright (c) 2018-2025 Gavin D. Howard and contributors.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
@@ -1517,7 +1517,7 @@ https://pubs.opengroup.org/onlinepubs/9699919799/utilities/bc.html .
# BUGS
-None are known. Report bugs at https://git.gavinhoward.com/gavin/bc .
+None are known. Report bugs at https://github.com/gavinhoward/bc .
# AUTHOR
diff --git a/contrib/bc/project/README.md b/contrib/bc/project/README.md
new file mode 100644
index 000000000000..9086bb2e9270
--- /dev/null
+++ b/contrib/bc/project/README.md
@@ -0,0 +1,32 @@
+# `bc` Project Management History
+
+This directory has the project management history of `bc`. This `README` exists
+to explain what the files are.
+
+* `gitea.db`
+
+ Because I (Gavin Howard, the main author) do not trust big companies, I moved
+ `bc` to a self-hosted Gitea instance. This is what's left of the database from
+ that instance. Obviously, I wiped of personal identifying information as much
+ as possible, but it still has the comments on issues and pull requests, as
+ well as the issues and pull requests themselves (whatever matters anyway).
+
+* `github_issues.json`
+
+ This is information about issues reported on GitHub. I used the GitHub CLI to
+ export *all* of the available information, since it's public.
+
+* `github_prs.json`
+
+ This is information about pull requests opened on GitHub. I used the GitHub
+ CLI to export *all* of the available information, since it's public.
+
+* `issue10.md`
+
+ When I first started self-hosting Gitea, I was not a good sysadmin. On top of
+ that, I was learning how to use OpenZFS, and Gitea was stored in a ZFS
+ dataset.
+
+ This is the best I could do to reproduce an actual issue that was reported and
+ got erased when I rolled back to a ZFS snapshot. It was originally `#10` on
+ Gitea, hence the file name.
diff --git a/contrib/bc/project/gitea.db b/contrib/bc/project/gitea.db
new file mode 100644
index 000000000000..c63c79ff928f
--- /dev/null
+++ b/contrib/bc/project/gitea.db
Binary files differ
diff --git a/contrib/bc/project/github_issues.json b/contrib/bc/project/github_issues.json
new file mode 100644
index 000000000000..3ee05206a3e9
--- /dev/null
+++ b/contrib/bc/project/github_issues.json
@@ -0,0 +1,3667 @@
+[
+ {
+ "assignees": [],
+ "author": {
+ "id": "U_kgDOCpvj3Q",
+ "is_bot": false,
+ "login": "Palafitas",
+ "name": "Palafitas"
+ },
+ "body": "I'm using 'bc' to perform a calculation in a shell script and I'm getting the incorrect result from a simple calculation...\n\n80 - (30 * 0) / 50 - (80 / 100) * 38\n\nbc returns value 80\n\nReal value is 49.6\n\n\nOBS.: Can use the calculation direct in bc command to see output\nOBS.: bc version is 1.07.1",
+ "closed": true,
+ "closedAt": "2025-02-25T19:10:08Z",
+ "comments": [
+ {
+ "id": "IC_kwDOCL0xJc6f65pc",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Two things.\n\nFirst, `bc` is not like other calculators that figure out what precision they need. Instead, `bc` requires you to set the precision before doing any calculation.\n\nThe way to do this is to set the special variable `scale` to an integer value, and then calculations will be done to at least that precision, in digits after the decimal point.\n\nOne quirk of `bc` is that `scale` is set to 0 by default, which means integer-only math.\n\nApplying integer-only math to the expression you gave, we get:\n\n```\n80 - (30 * 0) / 50 - (80 / 100) * 38\n80 - 0 / 50 - (80 / 100) * 38\n80 - 0 - (80 / 100) * 38\n80 - (80 / 100) * 38\n80 - 0 * 38\n80\n```\n\nNotice that `80 / 100` simplifies to 0 with integer-only arithmetic. That is expected.\n\nNow, watch what happens when we set `scale` to 1 first:\n\n```\nscale = 1\n80 - (30 * 0) / 50 - (80 / 100) * 38\n80 - 0 / 50 - (80 / 100) * 38\n80 - 0 - (80 / 100) * 38\n80 - (80 / 100) * 38\n80 - 0.8 * 38\n80 - 30.4\n49.6\n```\n\nAnd that is the value you expected.\n\nSo there is no calculation error; just be sure to set the precision with `scale` before running a calculation.\n\nSecond, this `bc` has never had a `1.07.1` version. That is, in fact, the latest version of the [GNU `bc`][1], so you are not even running this `bc`. So I am closing this issue as invalid.\n\n[1]: https://www.gnu.org/software/bc/",
+ "createdAt": "2025-02-25T19:10:08Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [
+ {
+ "content": "THUMBS_UP",
+ "users": {
+ "totalCount": 1
+ }
+ }
+ ],
+ "url": "https://github.com/gavinhoward/bc/issues/87#issuecomment-2683017820",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc6f9KsZ",
+ "author": {
+ "login": "Palafitas"
+ },
+ "authorAssociation": "NONE",
+ "body": "Thanks, i solved the problem with '-l' to 'bc'\n\ne.g:\necho '1 + 1' | bc -l",
+ "createdAt": "2025-02-26T00:44:54Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/87#issuecomment-2683611929",
+ "viewerDidAuthor": false
+ }
+ ],
+ "createdAt": "2025-02-25T17:57:06Z",
+ "id": "I_kwDOCL0xJc6rnKtq",
+ "isPinned": false,
+ "labels": [],
+ "milestone": null,
+ "number": 87,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "state": "CLOSED",
+ "stateReason": "COMPLETED",
+ "title": "Calculation error",
+ "updatedAt": "2025-02-26T00:44:54Z",
+ "url": "https://github.com/gavinhoward/bc/issues/87"
+ },
+ {
+ "assignees": [],
+ "author": {
+ "id": "MDQ6VXNlcjI2Nzg4ODg=",
+ "is_bot": false,
+ "login": "shpati",
+ "name": ""
+ },
+ "body": "Hi! \r\nI am trying to load multiple .bc files stored in the same directory as the program (from your modified function files from Carl's collection) but bc will not load them. \r\n\r\nThe command I give is: \r\n`bc -l *.bc`\r\n\r\nThe error I get is: \r\n```\r\nFatal error: cannot open file: *.bc\r\n 0: (main)\r\n```\r\nLoading files separately works, e.g. bc -l file1.bc file2.bc\r\nLoading files using wildcards works in the gnu bc, and it is practical. \r\n\r\nI am using the windows 10 terminal. ",
+ "closed": true,
+ "closedAt": "2024-11-10T14:27:05Z",
+ "comments": [
+ {
+ "id": "IC_kwDOCL0xJc6TB7mr",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Hello.\r\n\r\nI am actually really surprised that GNU `bc` will load the files in the Windows 10 terminal, and that is because, as far as I know, wildcard expansion is done by the shell, not the program. So GNU `bc` doesn't actually do it, or so I thought.\r\n\r\nIf you were to run my `bc` with that command under bash on Linux, it would work fine because bash would expand the wildcard into a list of file names and pass that to my `bc`. It seems the Windows 10 terminal does not do that, but passes the wildcard to my `bc`, which does not know how to expand it.\r\n\r\nUnfortunately, wildcard expansion is a heavy feature, not one I can implement in an afternoon. I think it is out of scope for my `bc`, and I don't plan on implementing it.\r\n\r\nThere might be some way to get the Windows 10 terminal to do wildcard expansion, but if that doesn't work, you can run my `bc` under bash using the Windows Subsystem for Linux.",
+ "createdAt": "2024-11-10T14:27:05Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/85#issuecomment-2466757035",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc6TC19F",
+ "author": {
+ "login": "shpati"
+ },
+ "authorAssociation": "NONE",
+ "body": "Thanks a lot for the detailed answer Gavin! It seems like the solution is simple and does not need any change in code. You only need to change the linker used during compiling, like this: \r\n\r\n`cl example.c /link setargv.obj`\r\n\r\nCan you try it please? 🙂\r\n\r\nSource: https://learn.microsoft.com/en-us/cpp/c-language/expanding-wildcard-arguments?view=msvc-170",
+ "createdAt": "2024-11-11T00:00:13Z",
+ "includesCreatedEdit": true,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/85#issuecomment-2466996037",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc6UKlNz",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "I tried adding `setargv.obj` to the linker command (next to `bcrypt.lib`), and that didn't work.\r\n\r\nIf you can get it working, feel free to send me a PR, and I will probably accept it.",
+ "createdAt": "2024-11-19T14:00:38Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/85#issuecomment-2485801843",
+ "viewerDidAuthor": true
+ }
+ ],
+ "createdAt": "2024-11-10T09:58:36Z",
+ "id": "I_kwDOCL0xJc6dxwXg",
+ "isPinned": false,
+ "labels": [],
+ "milestone": null,
+ "number": 85,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "state": "CLOSED",
+ "stateReason": "NOT_PLANNED",
+ "title": "Does not load multiple .bc files using * wildcard",
+ "updatedAt": "2024-11-19T14:00:40Z",
+ "url": "https://github.com/gavinhoward/bc/issues/85"
+ },
+ {
+ "assignees": [],
+ "author": {
+ "id": "MDQ6VXNlcjgyNzIwNQ==",
+ "is_bot": false,
+ "login": "DanielRuf",
+ "name": "Daniel Ruf"
+ },
+ "body": "I'm new to C and I try to create a PHP extension of bc with SWIG, but as direct replacement for `eval()` in PHP.\r\n\r\nWhat is the **correct function from the bc code to parse a string** and return the calculation result or some error / null when parsing fails?\r\n\r\nI tried looking at the code, but didn't find the right one.",
+ "closed": true,
+ "closedAt": "2025-02-16T13:40:55Z",
+ "comments": [
+ {
+ "id": "IC_kwDOCL0xJc6LSQRq",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "I'm not sure what you're asking. `bc` is a program, not a library, and even though I *do* have a library, it does not include expression parsing.\r\n\r\nAre you wanting a library that can parse `bc` expressions? Unfortunately, this `bc` cannot do that, and to do so would require refactoring that I don't have time for.",
+ "createdAt": "2024-09-08T20:42:08Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/83#issuecomment-2336818282",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc6LSRDF",
+ "author": {
+ "login": "DanielRuf"
+ },
+ "authorAssociation": "NONE",
+ "body": "Understood, so I can not do some `bc_parse(some-string-from-stdin)` with that and I have to resort to some other solution then.\r\n\r\nWhere can I find the current logic of the CLI? Maybe I can do something with that.",
+ "createdAt": "2024-09-08T20:56:06Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/83#issuecomment-2336821445",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc6LSRiv",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "You are correct.\r\n\r\nHowever, what is easiest depends on what you are trying to do. Do you need the full `bc` language? Or do you just need simple expressions? Or something else?\r\n\r\nIf you need the full language, the best solution would be to run `bc` as a child process and just feed it data.\r\n\r\nIf you need simple expression parsing, then you could implement the parsing with Lex and Yacc, and then use `bcl`, the `bc` library, to do the math.\r\n\r\nIf you need something else, we can see what might work best.",
+ "createdAt": "2024-09-08T21:03:51Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/83#issuecomment-2336823471",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc6LSSix",
+ "author": {
+ "login": "DanielRuf"
+ },
+ "authorAssociation": "NONE",
+ "body": "> Or do you just need simple expressions? Or something else?\r\n\r\nJust basic calculations like `eval(1+2/3*4-5)`.\r\n\r\n> If you need the full language, the best solution would be to run bc as a child process and just feed it data.\r\n\r\nUnfortunately this brings much overhead, I already tested that with `exec(\"bc ...\")` in PHP.\r\n\r\n> If you need simple expression parsing, then you could implement the parsing with Lex and Yacc, and then use bcl, the bc library, to do the math.\r\n\r\nThis sounds more like what I am looking for, even though I wanted to avoid Yacc.",
+ "createdAt": "2024-09-08T21:19:11Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/83#issuecomment-2336827569",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc6LSSw-",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "While refactoring to bring the full `bc` parser into the library may be too much, I may be able to whip a small expression parser in a few hours.\r\n\r\nDo you just need the four basic arithmetic operators? Do you need parentheses? Do you need square root?\r\n\r\nLet me know what you need. I may need a week to get it to you.",
+ "createdAt": "2024-09-08T21:22:48Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/83#issuecomment-2336828478",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc6LSTkN",
+ "author": {
+ "login": "DanielRuf"
+ },
+ "authorAssociation": "NONE",
+ "body": "I will check that and let you know in the next days. At least parentheses are also needed.\r\n\r\nIf there is at least one function (like sqrt or max) implemened, then I can check the implementation and contribute some of my time to implement more or at least I can help with that.",
+ "createdAt": "2024-09-08T21:35:19Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/83#issuecomment-2336831757",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc6LSUHv",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "`sqrt()` is already builtin; I'll add parsing for it. I can also add parsing for `max()` and `min()`.\r\n\r\nYou can see everything that's builtin [here](https://github.com/gavinhoward/bc/blob/master/manuals/bcl.3.md).",
+ "createdAt": "2024-09-08T21:44:30Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/83#issuecomment-2336834031",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc6LTBjd",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Ack! I'm sorry! I just had something come up in my personal life that will take a lot of time to resolve!\r\n\r\nSo despite hoping to help with the parsing, I can't even help with a simple thing anymore.\r\n\r\nHowever, I can point you in the right direction. Use the [Shunting-Yard Algorithm](https://en.wikipedia.org/wiki/Shunting-yard_algorithm) like [this](https://softwareengineering.stackexchange.com/questions/254074/how-exactly-is-an-abstract-syntax-tree-created/254075#254075) to make an abstract syntax tree, and then do a post-order traversal.\r\n\r\nIf you need examples of how to use `bcl`, then [its test code](https://github.com/gavinhoward/bc/blob/master/tests/bcl.c) is a good start.\r\n\r\nAgain, I'm really sorry about this, but I figured it would be better to tell you sooner rather than later.",
+ "createdAt": "2024-09-09T03:03:41Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [
+ {
+ "content": "THUMBS_UP",
+ "users": {
+ "totalCount": 1
+ }
+ }
+ ],
+ "url": "https://github.com/gavinhoward/bc/issues/83#issuecomment-2337020125",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc6LTUUy",
+ "author": {
+ "login": "DanielRuf"
+ },
+ "authorAssociation": "NONE",
+ "body": "No problem and thanks for letting me know. I will try to understand and solve it.",
+ "createdAt": "2024-09-09T04:37:26Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/83#issuecomment-2337097010",
+ "viewerDidAuthor": false
+ }
+ ],
+ "createdAt": "2024-09-08T20:22:38Z",
+ "id": "I_kwDOCL0xJc6Vwp4y",
+ "isPinned": false,
+ "labels": [],
+ "milestone": null,
+ "number": 83,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "state": "CLOSED",
+ "stateReason": "COMPLETED",
+ "title": "what function to call for parsing string expressions",
+ "updatedAt": "2025-02-16T13:40:55Z",
+ "url": "https://github.com/gavinhoward/bc/issues/83"
+ },
+ {
+ "assignees": [],
+ "author": {
+ "id": "U_kgDOBqIXaQ",
+ "is_bot": false,
+ "login": "GregTonoski",
+ "name": "Greg Tonoski"
+ },
+ "body": "Would you like to add elliptic curve point multiplication (https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication) to math library, perhaps?",
+ "closed": true,
+ "closedAt": "2024-08-31T18:09:54Z",
+ "comments": [
+ {
+ "id": "IC_kwDOCL0xJc6Kdiqk",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "I have wanted to, but I haven't *despite* wanting to. And there is a good reason: someone is going to use my `bc` to implement cryptography with vulnerabilities.\r\n\r\nIf my `bc` ships the elliptic curve arithmetic to do that, I will feel responsible. If they write the arithmetic themselves, well, they are responsible.\r\n\r\nOf course, there are other reasons to want it, but because of cryptography, I hesitate to add it.\r\n\r\nSorry!",
+ "createdAt": "2024-08-31T18:09:54Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/82#issuecomment-2322999972",
+ "viewerDidAuthor": true
+ }
+ ],
+ "createdAt": "2024-08-31T07:57:25Z",
+ "id": "I_kwDOCL0xJc6U72YL",
+ "isPinned": false,
+ "labels": [],
+ "milestone": null,
+ "number": 82,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "state": "CLOSED",
+ "stateReason": "COMPLETED",
+ "title": "Feature request: elliptic curve point multiplication",
+ "updatedAt": "2024-08-31T18:09:55Z",
+ "url": "https://github.com/gavinhoward/bc/issues/82"
+ },
+ {
+ "assignees": [],
+ "author": {
+ "id": "U_kgDOBqIXaQ",
+ "is_bot": false,
+ "login": "GregTonoski",
+ "name": "Greg Tonoski"
+ },
+ "body": "",
+ "closed": true,
+ "closedAt": "2024-08-30T14:14:44Z",
+ "comments": [
+ {
+ "id": "IC_kwDOCL0xJc6KXcRi",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Thank you so much for the compliment! I hardly get them nowadays. You made my day!\r\n\r\nI presume \"EOM\" means \"End of Message\" which means there is no bug? So I'm going to close this now, but if you do have a bug, feel free to reopen.",
+ "createdAt": "2024-08-30T14:14:44Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/80#issuecomment-2321400930",
+ "viewerDidAuthor": true
+ }
+ ],
+ "createdAt": "2024-08-30T07:30:14Z",
+ "id": "I_kwDOCL0xJc6UzPqQ",
+ "isPinned": false,
+ "labels": [],
+ "milestone": null,
+ "number": 80,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "state": "CLOSED",
+ "stateReason": "COMPLETED",
+ "title": "Excellent. Works fine. Thank you. [EOM]",
+ "updatedAt": "2024-08-30T14:14:44Z",
+ "url": "https://github.com/gavinhoward/bc/issues/80"
+ },
+ {
+ "assignees": [],
+ "author": {
+ "id": "MDQ6VXNlcjU3MDY1NjU3",
+ "is_bot": false,
+ "login": "exikyut",
+ "name": ""
+ },
+ "body": "Was just playing around and happened to get my stack out of sync:\r\n\r\n```\r\n1\r\n+\r\n\r\nRuntime error: stack has too few elements\r\n 0: (main)\r\n```\r\n\r\nBut I noticed this cleared the stack:\r\n\r\n<pre>\r\nf\r\n<i>(no output)</i>\r\n</pre>\r\n\r\nTrying to improve my edge-case-fu spidey sense :) I thought I'd check how other dc implementations behave.\r\n\r\nGNU `dc` immediately showed me something might be up:\r\n\r\n```\r\n1\r\n+\r\ndc: stack empty\r\nf\r\n1\r\nq\r\n$ _\r\n```\r\n\r\nHuh.\r\n\r\nOpenBSD `dc` produced byte-identical output to the above.\r\n\r\nOK... what should arbitrate as reference here? I guess some form of going back to the beginning.\r\n\r\nHow far can we go back?\r\n\r\nApparently the first version of `dc` was written in B while UNIX was being ported. Man that would be fun to play with. Perhaps there's a printout of it starting on page 9,576 of an OCR-averse PDF on bitsavers just waiting to be found :smile: \r\n\r\nhttp://takahirox.github.io/pdp11-js/unixv6.html and http://pdp11.aiju.de/ both emulate UNIX v6 in-browser, which is nice and accessible. The `dc` in both of these has the same size and timestamp; copying from the second emulator for fun, which emulates an uppercase teletype, we get:\r\n\r\n```\r\n# DC\r\n1\r\n+\r\n( +) ?\r\nF\r\n1\r\nQ\r\n# # \r\n```\r\n\r\nHrm. That's V6 UNIX. Can we go further back?\r\n\r\nYes and no, as far as I can manage.\r\n\r\nIt looks like a V1+V2 amalgamation was put together using surviving source code and tape dumps: https://www.in-ulm.de/~mascheck/various/ancient/\r\n\r\nThe project appears to have been quietly migrated to https://github.com/DoctorWkt/unix-jun72, with terrifying instructions to \"now run `make`\" underneath a wall of \"last modified: 16 years ago\" :melting_face:, but I discovered some pre-built SIMH images at https://code.google.com/archive/p/unix-jun72/downloads, which is where the project previously lived (and is what the page in the previous paragraph links to), which still work perfectly - you just run `pdp11 simh.cfg`.\r\n\r\nUnfortunately, while the system is already saying `:login:` before you can even blink and figure out whether it worked, and the distribution includes `dc`, its `f` command appears to be broken... even though I see references to support for an `f` command [on line 949 in dc1.s](https://github.com/DoctorWkt/unix-jun72/blob/1d438bd5874ec628157fbeab370c8e3f3a3ecb8b/src/cmd/dc1.s#L949) (the source is distributed across [dc2.s](https://github.com/DoctorWkt/unix-jun72/blob/1d438bd5874ec628157fbeab370c8e3f3a3ecb8b/src/cmd/dc2.s), [dc3.s](https://github.com/DoctorWkt/unix-jun72/blob/1d438bd5874ec628157fbeab370c8e3f3a3ecb8b/src/cmd/dc3.s), [dc4.s](https://github.com/DoctorWkt/unix-jun72/blob/1d438bd5874ec628157fbeab370c8e3f3a3ecb8b/src/cmd/dc4.s) and [dc5.s](https://github.com/DoctorWkt/unix-jun72/blob/1d438bd5874ec628157fbeab370c8e3f3a3ecb8b/src/cmd/dc5.s), presumably due to memory constraints). I'm not sure if I'm falling through a `default: exit(0);`, a segfault, or a case of mismatching binary/source.\r\n\r\nThe path forward there would be figuring out how to recompile the `dc` implementation; I think this might be possible but it's beyond the scope of the cursory level of interest I approached this with.\r\n\r\nIf anyone wants a rainy day project, I would be really interested to learn more about the result of others' digging around to get the earliest surviving copies of `dc` running.\r\n\r\nZooming back out to the topic, it would **seem** that current consensus is that errors *of this type* don't consume the stack; they leave it alone. But it would be nice to formally qualify this situation better. How should that be framed? Parser consumption expectations? Stack effects by errors in general?\r\n\r\nThis bugreport is somewhat of a thought experiment. I don't have any authoritative ideas or suggestions myself.",
+ "closed": true,
+ "closedAt": "2024-08-23T03:03:02Z",
+ "comments": [
+ {
+ "id": "IC_kwDOCL0xJc6Bc1ec",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Thank you for digging up the history!\r\n\r\nYou are correct that my `dc` clears the stack. When I was implementing `bc` and `dc`, I wanted to minimize the possibility that an error could screw up code later.\r\n\r\nHence, my `bc` and `dc` both [\"reset\"][1] on error, which means they clear everything and try to start completely fresh.\r\n\r\nThat said, the documentation could be clearer on that; I could make it say that errors clear the stack too. (And they do clear the stack because they have to clear the stack in `bc` since the stack is implicit.)\r\n\r\nHowever, this could go both ways:\r\n\r\n* I could declare this as a mere *documentation bug*, since `dc` is *not* standardized. In that case, I would just update the manual.\r\n* Or I could declare this a *conformance bug*, because while there is no standard, all other `dc` implementations do the opposite. In this case, I would make the change and fix the bugs that pop up.\r\n\r\nQuite frankly, I'm not looking forward to doing a full release cycle, which includes 2 weeks of fuzzing (during which my machine is unusable) and 24 hours of tests, possibly repeated. This means I want to fix the docs and call it a day, but going off of what I *want* to do is not rational.\r\n\r\nI don't know if anyone watches this repo, but this is one case where I'd like to hear comments from users.\r\n\r\n[1]: https://github.com/gavinhoward/bc/blob/master/manuals/bc/A.1.md#reset",
+ "createdAt": "2024-06-16T19:15:40Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/79#issuecomment-2171819932",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc6BfXEg",
+ "author": {
+ "login": "exikyut"
+ },
+ "authorAssociation": "NONE",
+ "body": "> You are correct that my `dc` clears the stack. When I was implementing `bc` and `dc`, I wanted to minimize the possibility that an error could screw up code later.\r\n> \r\n> Hence, my `bc` and `dc` both [\"reset\"](https://github.com/gavinhoward/bc/blob/master/manuals/bc/A.1.md#reset) on error, which means they clear everything and try to start completely fresh.\r\n\r\nHuh. That makes pedagogical sense, upon consideration.\r\n\r\n> That said, the documentation could be clearer on that; I could make it say that errors clear the stack too.\r\n\r\nThat would probably be a good idea, both because `gh-dc` deviates from the norm, and because there *is* no norm; this would be the first point at which this particular graph edge / state machine transition is properly documented. Until now it's lived as an edge case in UB land.\r\n\r\n> (And they do clear the stack because they have to clear the stack in `bc` since the stack is implicit.)\r\n\r\nHuh. I wonder how other `bc`->`dc` architectural approaches have handled this situation?\r\n\r\n> However, this could go both ways:\r\n> \r\n> * I could declare this as a mere _documentation bug_, since `dc` is _not_ standardized. In that case, I would just update the manual.\r\n> * Or I could declare this a _conformance bug_, because while there is no standard, all other `dc` implementations do the opposite. In this case, I would make the change and fix the bugs that pop up.\r\n> \r\n> Quite frankly, I'm not looking forward to doing a full release cycle, which includes 2 weeks of fuzzing (during which my machine is unusable) and 24 hours of tests, possibly repeated. This means I want to fix the docs and call it a day, but going off of what I _want_ to do is not rational.\r\n\r\nGood net question. Hmm.\r\n\r\n> I don't know if anyone watches this repo, but this is one case where I'd like to hear comments from users.\r\n\r\nI am too.\r\n\r\nBut I'll add my 2&cent;:\r\n\r\n- Nobody has screamed until now, but `gh-dc` adoption is still rising.\r\n\r\n- Taking on the complexity of making this a `./configure`able option doesn't sound reasonable in practice.\r\n\r\n- I can only imagine a `dc` script generating different results after being switched to this implementation after some sort of upgrade process that presumably has capable humans involved in it. It would follow that said humans would presumably classify the deviation in output as the result of logic bugs and then fix them, so keeping this architectural approach is theoretically a net positive.\r\n\r\n- Applying to make `dc` a POSIX standard would make for an interesting 180-season TV series that would be excellent mid-afternoon watching while having a nap (and dreaming of how to make `dc` count to infinity twice). I'd definitely seed all 4,961 episodes.\r\n\r\n- Given that `gh-dc` is now in FreeBSD, it may well meet the theoretical/implicational interpretation (and possibly beyond) of \"significant user base\" required to [apply for OSS-Fuzz](https://google.github.io/oss-fuzz/getting-started/accepting-new-projects/) which would subject it to continual analysis and, if accepted, probably provide a slow trickle of genuinely interesting feedback. It also sounds like it would unblock a significant burnout barrier to commoditizing the process of cutting new releases - opting to letting new versions sit in OSS-Fuzz for a fortnight before releasing them (a cool strategy, and if only everything was architecturally simple enough that this could be applied everywhere) would no longer have a local performance impact.\r\n",
+ "createdAt": "2024-06-17T07:21:08Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/79#issuecomment-2172481824",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc6BmRt8",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "> Nobody has screamed until now, but `gh-dc` adoption is still rising.\r\n\r\nYes, I agree. In fact, I initially wanted to dismiss your report as \"only one user,\" but I did not for this reason.\r\n\r\n> Taking on the complexity of making this a `./configure`able option doesn't sound reasonable in practice.\r\n\r\nI do not want that to be a build-time option. I have too many as it is.\r\n\r\n> I can only imagine a `dc` script generating different results after being switched to this implementation after some sort of upgrade process that presumably has capable humans involved in it. It would follow that said humans would presumably classify the deviation in output as the result of logic bugs and then fix them, so keeping this architectural approach is theoretically a net positive.\r\n\r\nTrue, although people would be better served to switch to `bc` since it's standard and portable.\r\n\r\n> Applying to make `dc` a POSIX standard would make for an interesting 180-season TV series that would be excellent mid-afternoon watching while having a nap (and dreaming of how to make dc count to infinity twice). I'd definitely seed all 4,961 episodes.\r\n\r\nThe [rationale in the `bc` standard][1] says these two things:\r\n\r\n> `dc` was not selected to be part of this volume of POSIX.1-2017 because `bc` was thought to have a more intuitive programmatic interface.\r\n\r\n> The consensus of the standard developers was that `dc` is a fundamentally less usable language and that that would be far too severe a penalty for avoiding the issue of being similar to but incompatible with C.\r\n\r\nYes, that is from the 2017 standard, but that language goes back to the beginning, I believe. This means that standardizing `dc` will not happen, barring some rich fellow deciding that it's his life mission to make it happen.\r\n\r\n> Given that `gh-dc` is now in FreeBSD, it may well meet the theoretical/implicational interpretation (and possibly beyond) of \"significant user base\" required to [apply for OSS-Fuzz](https://google.github.io/oss-fuzz/getting-started/accepting-new-projects/) which would subject it to continual analysis and, if accepted, probably provide a slow trickle of genuinely interesting feedback. It also sounds like it would unblock a significant burnout barrier to commoditizing the process of cutting new releases - opting to letting new versions sit in OSS-Fuzz for a fortnight before releasing them (a cool strategy, and if only everything was architecturally simple enough that this could be applied everywhere) would no longer have a local performance impact.\r\n\r\nI agree with this, so [I have applied][2]. We'll see where that goes.\r\n\r\n[1]: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/bc.html#tag_20_09_18\r\n[2]: https://github.com/google/oss-fuzz/pull/12078",
+ "createdAt": "2024-06-17T19:45:51Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/79#issuecomment-2174294908",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc6BpTwG",
+ "author": {
+ "login": "exikyut"
+ },
+ "authorAssociation": "NONE",
+ "body": "> > Nobody has screamed until now, but `gh-dc` adoption is still rising.\r\n> \r\n> Yes, I agree. In fact, I initially wanted to dismiss your report as \"only one user,\" but I did not for this reason.\r\n\r\n(Sentiment of appreciation)\r\n\r\n> > Taking on the complexity of making this a `./configure`able option doesn't sound reasonable in practice.\r\n> \r\n> I do not want that to be a build-time option. I have too many as it is.\r\n\r\nI was genuinely surprised at the number of configurable options :) completely agree there haha\r\n\r\n> > I can only imagine a `dc` script generating different results after being switched to this implementation after some sort of upgrade process that presumably has capable humans involved in it. It would follow that said humans would presumably classify the deviation in output as the result of logic bugs and then fix them, so keeping this architectural approach is theoretically a net positive.\r\n> \r\n> True, although people would be better served to switch to `bc` since it's standard and portable.\r\n> \r\n> > Applying to make `dc` a POSIX standard would make for an interesting 180-season TV series that would be excellent mid-afternoon watching while having a nap (and dreaming of how to make dc count to infinity twice). I'd definitely seed all 4,961 episodes.\r\n> \r\n> The [rationale in the `bc` standard](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/bc.html#tag_20_09_18) says these two things:\r\n> \r\n> > `dc` was not selected to be part of this volume of POSIX.1-2017 because `bc` was thought to have a more intuitive programmatic interface.\r\n\r\nThanks for the TIL! I should have expected there to be extant discussion on the matter...\r\n\r\n> > The consensus of the standard developers was that `dc` is a fundamentally less usable language and that that would be far too severe a penalty for avoiding the issue of being similar to but incompatible with C.\r\n> \r\n> Yes, that is from the 2017 standard, but that language goes back to the beginning, I believe. This means that standardizing `dc` will not happen, barring some rich fellow deciding that it's his life mission to make it happen.\r\n\r\nI see. Thanks very much for the insight.\r\n\r\n> \r\n> > Given that `gh-dc` is now in FreeBSD, it may well meet the theoretical/implicational interpretation (and possibly beyond) of \"significant user base\" required to [apply for OSS-Fuzz](https://google.github.io/oss-fuzz/getting-started/accepting-new-projects/) which would subject it to continual analysis and, if accepted, probably provide a slow trickle of genuinely interesting feedback. It also sounds like it would unblock a significant burnout barrier to commoditizing the process of cutting new releases - opting to letting new versions sit in OSS-Fuzz for a fortnight before releasing them (a cool strategy, and if only everything was architecturally simple enough that this could be applied everywhere) would no longer have a local performance impact.\r\n> \r\n> I agree with this, so [I have applied](https://github.com/google/oss-fuzz/pull/12078). We'll see where that goes.\r\n\r\n**YOU GOT ACCEPTED :D :face_holding_back_tears:**\r\n\r\nI forgot that it ships in macOS, and TIL it ships in Android as well. OSS-Fuzz's scope tries to extend beyond Google-immediate interests, so this broader status quo (incl. FreeBSD) constitutes a meaningful precedent of relevance &ndash; potentially represented by the speed with which the project was accepted without further question (basically 45 minutes).",
+ "createdAt": "2024-06-18T06:03:35Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/79#issuecomment-2175089670",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc6B3Zis",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "> YOU GOT ACCEPTED :D 🥹\r\n\r\nYes, unfortunately, I [ran into problems integrating fuzzers][1], and I don't know what to do next, so I have to put OSS-Fuzz on the back plate. Thus, we're back to where we started.\r\n\r\nThank you for the suggestion, though.\r\n\r\n[1]: https://github.com/google/oss-fuzz/pull/12098",
+ "createdAt": "2024-06-19T13:54:56Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/79#issuecomment-2178783404",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc6C-bO7",
+ "author": {
+ "login": "spatula75"
+ },
+ "authorAssociation": "NONE",
+ "body": "I also noticed this behavioral change, because on occasion I need to add a long list of numbers in `dc`, and rather than count how many numbers I have and issuing the right number of `+` operators, I have a habit of just entering way more `+` operators than I need and then grabbing the stack top value afterward. e.g.:\r\n```\r\n1 7 8 6 4 9 1 2 3 6 4 + + + + + + + + + + + + + + + + + + p\r\n```\r\nHistorically, and with other implementations of `dc`, this would result in a lot of `dc: stack empty` messages followed by the sum at the end. Now it results in a clear stack with no result.\r\n\r\nI can certainly see the merit of returning to a clean slate on an error condition when running in an \"automated\" fashion - blowing the stack in this case would generally mean you did something wrong and the results might not be trustworthy.\r\n\r\nI guess what frustrates me is that in interactive mode, this is a significant change from historical behavior that caught me off-guard and also blows up my use case, requiring me to now count how many numbers I have on the stack, and then to supply N-1 operators to get to the result I want. It's also making my stack more \"fragile\" in the sense that I now have to be very careful about my interactions with `dc`.\r\n\r\nI agree that changing behavior based on configuration-time options sounds like overkill and a lot of added complexity, especially when some users of a system might find the new behavior desirable.\r\n\r\nI see a few other possibilities:\r\n- A command line switch to either enable or disable stack clearing when running in `dc` mode, so a user can be explicit about whether they want the new behavior or the historical behavior. Whether the switch enables the old behavior, or enables the new behavior doesn't matter much to me; I'd just add a shell alias if I needed to always supply a switch.\r\n- A rule that when running in interactive mode (ie, from a keyboard not from a pipe or a file), you get the historical (non-clearing) behavior, but in non-interactive mode you always get the new (stack-clearing) behavior.\r\n- Some hybrid of the two: maybe in interactive mode you get the historical behavior _unless_ you supply a command-line switch\r\n\r\nOf course I realize that all of these options add complexity too, but hopefully not altogether too much. Any one of them would make me a happy camper.",
+ "createdAt": "2024-06-28T17:58:14Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [
+ {
+ "content": "THUMBS_UP",
+ "users": {
+ "totalCount": 1
+ }
+ }
+ ],
+ "url": "https://github.com/gavinhoward/bc/issues/79#issuecomment-2197402555",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc6DCMZY",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "I am not a fan of having different behavior in interative mode vs non-interactive mode.\r\n\r\nHowever, I think `dc` implementations tend to exit on any error in non-interactive mode anyway. At least GNU `dc` does, IIRC. This one definitely does, and this behavior is documented.\r\n\r\nSo this change would only affect interactive uses anyway.\r\n\r\nI will go ahead and make the change. I will start fuzzing as well. If few or no problems appear, I will make a release.",
+ "createdAt": "2024-06-30T00:55:05Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [
+ {
+ "content": "THUMBS_UP",
+ "users": {
+ "totalCount": 2
+ }
+ }
+ ],
+ "url": "https://github.com/gavinhoward/bc/issues/79#issuecomment-2198390360",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc6JdDT8",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Okay, it took me a long time to have the time for fuzzing and testing, but this change made it into 7.0.0.\r\n\r\nI think I can close this report now, but if there are still problems, feel free to reopen.",
+ "createdAt": "2024-08-23T03:03:02Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/79#issuecomment-2306094332",
+ "viewerDidAuthor": true
+ }
+ ],
+ "createdAt": "2024-06-16T10:02:06Z",
+ "id": "I_kwDOCL0xJc6MaBsK",
+ "isPinned": false,
+ "labels": [],
+ "milestone": null,
+ "number": 79,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "state": "CLOSED",
+ "stateReason": "COMPLETED",
+ "title": "Stack-consumption-on-error headscratch in comparison to other `dc`s",
+ "updatedAt": "2024-08-23T03:03:02Z",
+ "url": "https://github.com/gavinhoward/bc/issues/79"
+ },
+ {
+ "assignees": [],
+ "author": {
+ "id": "MDQ6VXNlcjI2NjI1MTQx",
+ "is_bot": false,
+ "login": "freebrowser1",
+ "name": ""
+ },
+ "body": "Excellent software !\r\n\r\nThis `bc` is MUCH faster than the bc supplied to Linux distros. I downloaded the source package, compiled under Ubuntu ARM and it worked. I did the same on Termux on an Android cellphone (which also has the slow 1.7 version) and it was blazingly fast as well !\r\nIt can be used for other bases up till sixteen. I changed it (locally, not on this Github site) to max base 36.\r\nMaybe that can be an option to publish it here.",
+ "closed": true,
+ "closedAt": "2024-05-15T15:52:29Z",
+ "comments": [
+ {
+ "id": "IC_kwDOCL0xJc59oi_N",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Thank you for your compliments!\r\n\r\nAs it turns out, it's the accompanying `dc` that only goes up to base 16. `bc` goes up to base 36.\r\n\r\nThere is a reason for base 36: according to the `bc` standard, it must be possible to set all input bases with a one character number. This is so any input base could be set no matter the *current* input base.\r\n\r\nFor example, say you write a function that should work in multiple bases. What if it temporarily has to set a new input base. How does it do that when the input base could be anything from binary to hexadecimal?\r\n\r\nThe answer is [here][1] (scroll down to where it says, \"When either ibase or obase is assigned a single digit value...\").\r\n\r\nNow my `bc` does take that up to base 35, which is the base you can reach by using `Z` as the single digit value.\r\n\r\nTo test this, try this code:\r\n\r\n```\r\n$ bc\r\n>>> obase=Z\r\n>>> 35\r\n01 00\r\n>>> quit\r\n```\r\n\r\nI hope this helps.\r\n\r\n[1]: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/bc.html#tag_20_09_13_03",
+ "createdAt": "2024-05-13T14:35:35Z",
+ "includesCreatedEdit": true,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/78#issuecomment-2107781069",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc59sMoG",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "I realize I forgot to include an example with `ibase`.\r\n\r\n```\r\n$ bc\r\n>>> ibase=Z\r\n>>> 10\r\n35\r\n>>> ibase=6*6\r\n>>> 10\r\n36\r\n>>> quit\r\n```\r\n\r\nSo yes, my `bc` already goes up to base 36 for `ibase` too, although you do need some special way, like `6*6`, to get it.",
+ "createdAt": "2024-05-13T20:24:59Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/78#issuecomment-2108738054",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5978rz",
+ "author": {
+ "login": "freebrowser1"
+ },
+ "authorAssociation": "NONE",
+ "body": "I have changed a few source files (find in the files in the accompanied zip lines with //!!).\r\nThis allows using max base 36 with all digits and all UPPERCASE letters. When I and O are not wanted, use `export NO_I_O_DIGITS=1` (or any value) before executing bc and 34 = @, 35 = #.\r\n\r\n[src.zip](https://github.com/gavinhoward/bc/files/15323750/src.zip)\r\n",
+ "createdAt": "2024-05-15T15:31:15Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/78#issuecomment-2112867059",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc598Hyk",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "You did not read my comments. My `bc` already supports up to base 36.\r\n\r\nYour changes are not needed.\r\n\r\nClosing.",
+ "createdAt": "2024-05-15T15:52:29Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/78#issuecomment-2112912548",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5-Emcm",
+ "author": {
+ "login": "freebrowser1"
+ },
+ "authorAssociation": "NONE",
+ "body": "I checked again, downloaded the source kit, compiled it (Ubuntu arm64) and ran this:\r\n' bin/bc -l <<< \"ibase=obase=24; 2^G;\"'\r\nThis results in `04 17 18 16`, i.e. decimal numbers as 'digits' which should only appear with base above 36.\r\nAfter applying my changes, it issues the correct value '4HIG'.\r\n",
+ "createdAt": "2024-05-16T12:35:20Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/78#issuecomment-2115135270",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc5-E7EK",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "This cannot be changed.\r\n\r\nThere is a [standard for `bc`][1], and in that standard, it says,\r\n\r\n> For bases greater than 16, each digit shall be written as a separate multi-digit decimal number. Each digit except the most significant fractional digit shall be preceded by a single <space>. For bases from 17 to 100, bc shall write two-digit decimal numbers; for bases from 101 to 1000, three-digit decimal strings, and so on.\r\n\r\nSo no, as much as I would like to change it, it cannot be changed.\r\n\r\n[1]: https://pubs.opengroup.org/onlinepubs/009696799/utilities/bc.html",
+ "createdAt": "2024-05-16T13:15:39Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/78#issuecomment-2115219722",
+ "viewerDidAuthor": true
+ }
+ ],
+ "createdAt": "2024-05-13T07:31:19Z",
+ "id": "I_kwDOCL0xJc6Infdz",
+ "isPinned": false,
+ "labels": [],
+ "milestone": null,
+ "number": 78,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "state": "CLOSED",
+ "stateReason": "NOT_PLANNED",
+ "title": "Not an issue, but a change proposal",
+ "updatedAt": "2024-05-16T13:15:40Z",
+ "url": "https://github.com/gavinhoward/bc/issues/78"
+ },
+ {
+ "assignees": [],
+ "author": {
+ "id": "U_kgDOB00gDQ",
+ "is_bot": false,
+ "login": "oliverkwebb",
+ "name": "Oliver Webb"
+ },
+ "body": "First I'd like to thank you for the work and care in making bc.\r\n\r\nI replaced GNU bc with your bc on my system, and ran into a error trying to run bc with some external bc libraries that define functions such as `abs()`\r\n\r\nThis is because your bc already has a built in `abs()`, and unlike the functions in the -l math library, will not let you redefine it because it appear to not just be a function, but also a lexer tokens/keyword.\r\n\r\nIs this a problem that you've ran into or considered before? ",
+ "closed": true,
+ "closedAt": "2024-05-08T17:39:46Z",
+ "comments": [
+ {
+ "id": "IC_kwDOCL0xJc59O96s",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Yup! And my `bc` already has a solution:\r\n\r\n```\r\n$ bc -r abs <other_args...>\r\n```\r\n\r\nSee [here][1], scroll to the `-r`/`--redefine` option.\r\n\r\nIn short, it allows you to \"redefine\" keywords as functions, variables, or arrays.\r\n\r\nOf course, if that doesn't fix your problem, then it is a bug.\r\n\r\n[1]: https://github.com/gavinhoward/bc/blob/master/manuals/bc/A.1.md#options",
+ "createdAt": "2024-05-08T17:34:29Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/77#issuecomment-2101075628",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc59O_7j",
+ "author": {
+ "login": "oliverkwebb"
+ },
+ "authorAssociation": "NONE",
+ "body": "Putting `-r abs` in my function arguments will run the library.\r\n\r\nThank you for your help",
+ "createdAt": "2024-05-08T17:39:46Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [
+ {
+ "content": "THUMBS_UP",
+ "users": {
+ "totalCount": 1
+ }
+ }
+ ],
+ "url": "https://github.com/gavinhoward/bc/issues/77#issuecomment-2101083875",
+ "viewerDidAuthor": false
+ }
+ ],
+ "createdAt": "2024-05-08T17:29:50Z",
+ "id": "I_kwDOCL0xJc6IQwkm",
+ "isPinned": false,
+ "labels": [],
+ "milestone": null,
+ "number": 77,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "state": "CLOSED",
+ "stateReason": "COMPLETED",
+ "title": "Allowing redefinition of builtin function such as abs() for compatibility with bc scripts",
+ "updatedAt": "2024-05-08T17:39:46Z",
+ "url": "https://github.com/gavinhoward/bc/issues/77"
+ },
+ {
+ "assignees": [],
+ "author": {
+ "id": "MDQ6VXNlcjc0MzM1NDcx",
+ "is_bot": false,
+ "login": "mogando668",
+ "name": ""
+ },
+ "body": "Dear Mr. Howard,\r\n\r\nFor gigantic `obase`s, like those below the line, even though they're clearly beyond the hard-coded `obase `max of `2,147,483,647`,`bc` appears to calculate the expression to full precision before erroring out, thus undermining any early exit criteria that ensure built-in named variables are within designated limits. I haven't tested against `ibase` but I suspect something similar would plague it, i.e. the right hand side expression is being calculated to full precision before any attempts to check them against caps.\r\n\r\nThe full `zsh`-based testing code and output are attached below.\r\n\r\nYours Sincerely\r\nJason K\r\n\r\nps : I've noticed the same issue plagues both `gnu-bc` I've installed via Homebrew as well as the macOS built-in `bc`. \r\n\r\n```\r\n 2 ^ 8 ^ 1 := 2 ^ 8\r\n 2 ^ 8 ^ 2 := 2 ^ 64\r\n 2 ^ 8 ^ 3 := 2 ^ 512\r\n 2 ^ 8 ^ 4 := 2 ^ 4096\r\n 2 ^ 8 ^ 5 := 2 ^ 32768\r\n 2 ^ 8 ^ 6 := 2 ^ 262144\r\n 2 ^ 8 ^ 7 := 2 ^ 2097152\r\n----------------------------\r\n 2 ^ 8 ^ 8 := 2 ^ 16777216\r\n 2 ^ 8 ^ 9 := 2 ^ 134217728\r\n 2 ^ 8 ^ 10 := 2 ^ 1073741824\r\n```\r\n\r\n\r\n```\r\nbc 1.07.1\r\nCopyright 1991-1994, 1997, 1998, 2000, 2004, 2006, 2008, 2012-2017 Free Software Foundation, Inc.\r\n\r\nDarwin m1mx4CT 22.3.0 Darwin Kernel Version 22.3.0: Mon Jan 30 20:38:37 PST 2023; root:xnu-8792.81.3~2/RELEASE_ARM64_T6000 arm64\r\n\r\ngbc is /usr/local/bin/gbc\r\n\r\nBC_BASE_MAX = 2147483647\r\nBC_DIM_MAX = 16777215\r\nBC_SCALE_MAX = 2147483647\r\nBC_STRING_MAX = 2147483647\r\nMAX Exponent = 9223372036854775807\r\nNumber of vars = 32767\r\n\r\n```\r\n\r\n`for __ in $( jot 10 ); do ( time ( printf 'obase=2^8^%d\\0' \"$__\" | xargs -0 -n 1 -P 24 timeout -v --preserve-status --foreground 8 dash -c 'for __; do echo \"$__\" | gbc; done' _ ) ); echo \"\\f ----------\\n\\t finished 2^8^$__ with exit status $? ... \\n --------\"; done`\r\n\r\n```\r\n( printf 'obase=2^8^%d\\0' \"$__\" | xargs -0 -n 1 -P 24 timeout -v --foregroun) 0.00s user 0.01s system 86% cpu 0.008 total\r\n\r\n ----------\r\n\t finished 2^8^1 with exit status 0 ... \r\n --------\r\nRuntime warning (func=(main), adr=13): obase too large, set to 2147483647\r\n( printf 'obase=2^8^%d\\0' \"$__\" | xargs -0 -n 1 -P 24 timeout -v --foregroun) 0.00s user 0.01s system 58% cpu 0.012 total\r\n\r\n ----------\r\n\t finished 2^8^2 with exit status 0 ... \r\n --------\r\nRuntime warning (func=(main), adr=13): obase too large, set to 2147483647\r\n( printf 'obase=2^8^%d\\0' \"$__\" | xargs -0 -n 1 -P 24 timeout -v --foregroun) 0.00s user 0.00s system 82% cpu 0.007 total\r\n\r\n ----------\r\n\t finished 2^8^3 with exit status 0 ... \r\n --------\r\nRuntime warning (func=(main), adr=13): obase too large, set to 2147483647\r\n( printf 'obase=2^8^%d\\0' \"$__\" | xargs -0 -n 1 -P 24 timeout -v --foregroun) 0.00s user 0.00s system 84% cpu 0.008 total\r\n\r\n ----------\r\n\t finished 2^8^4 with exit status 0 ... \r\n --------\r\nRuntime warning (func=(main), adr=13): obase too large, set to 2147483647\r\n( printf 'obase=2^8^%d\\0' \"$__\" | xargs -0 -n 1 -P 24 timeout -v --foregroun) 0.01s user 0.00s system 90% cpu 0.012 total\r\n\r\n ----------\r\n\t finished 2^8^5 with exit status 0 ... \r\n --------\r\nRuntime warning (func=(main), adr=13): obase too large, set to 2147483647\r\n( printf 'obase=2^8^%d\\0' \"$__\" | xargs -0 -n 1 -P 24 timeout -v --foregroun) 0.14s user 0.01s system 98% cpu 0.143 total\r\n\r\n ----------\r\n\t finished 2^8^6 with exit status 0 ... \r\n --------\r\nRuntime warning (func=(main), adr=13): obase too large, set to 2147483647\r\n( printf 'obase=2^8^%d\\0' \"$__\" | xargs -0 -n 1 -P 24 timeout -v --foregroun) 3.64s user 0.01s system 99% cpu 3.654 total\r\n\r\n ----------\r\n\t finished 2^8^7 with exit status 0 ... \r\n --------\r\ntimeout: sending signal TERM to command ‘dash’\r\n( printf 'obase=2^8^%d\\0' \"$__\" | xargs -0 -n 1 -P 24 timeout -v --foregroun) 0.00s user 0.00s system 0% cpu 8.009 total\r\n\r\n ----------\r\n\t finished 2^8^8 with exit status 0 ... \r\n --------\r\ntimeout: sending signal TERM to command ‘dash’\r\n( printf 'obase=2^8^%d\\0' \"$__\" | xargs -0 -n 1 -P 24 timeout -v --foregroun) 0.00s user 0.00s system 0% cpu 8.008 total\r\n\r\n ----------\r\n\t finished 2^8^9 with exit status 0 ... \r\n --------\r\nRuntime warning (func=(main), adr=13): obase too large, set to 2147483647\r\ntimeout: sending signal TERM to command ‘dash’\r\n( printf 'obase=2^8^%d\\0' \"$__\" | xargs -0 -n 1 -P 24 timeout -v --foregroun) 0.00s user 0.01s system 0% cpu 8.011 total\r\n\r\n ----------\r\n\t finished 2^8^10 with exit status 0 ... \r\n --------\r\n```",
+ "closed": true,
+ "closedAt": "2024-01-27T20:24:51Z",
+ "comments": [
+ {
+ "id": "IC_kwDOCL0xJc5yCwr9",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "In general, this problem is unsolvable. It's a consequence of the limits of computing. (The technical term is \"Turing-completeness.\")\r\n\r\nFor example, to us it's obvious that `2^8^10` is out of range. But computers do not understand any \"concept\" of math, so they can't tell.\r\n\r\nAll they can do is execute the code that they are given.\r\n\r\nYou'd see an different execution time if you did this:\r\n\r\n```\r\nobase = 104438888141315250669175271071662438257996424904738378038423348328395390\\\r\n797155745684882681193499755834089010671443926283798757343818579360726323\\\r\n608785136527794595697654370999834036159013438371831442807001185594622637\\\r\n631883939771274567233468434458661749680790870580370407128404874011860911\\\r\n446797778359802900668693897688178778594690563019026094059957945343282346\\\r\n930302669644305902501597239986771421554169383555988529148631823791443449\\\r\n673408781187263949647510018904134900841706167509366833385055103297208826\\\r\n955076998361636941193301521379682583718809183365675122131849284636812555\\\r\n022599830041234478486259567449219461702380650591324561082573183538008760\\\r\n862210283427019769820231316901767800667519548507992163641937028537512478\\\r\n401490715913545998279051339961155179427110683113409058427288427979155484\\\r\n978295432353451706522326906139490598769300212296339568778287894844061600\\\r\n741294567491982305057164237715481632138063104590291613692670834285644073\\\r\n044789997190178146576347322385026725305989979599609079946920177462481771\\\r\n844986745565925017832907047311943316555080756822184657174637329688491281\\\r\n952031745700244092661691087414838507841192980452298185733897764810312608\\\r\n590300130241346718972667321649151113160292078173803343609024380470834040\\\r\n3154190336\r\n```\r\n\r\ninstead of this:\r\n\r\n```\r\nobase = 2^8^4\r\n```\r\n\r\nAgain, the computer has no idea that `2^8^4` cannot work until it calculates it. We can see it by eye (\"`8^4` is definitely greater than 64, and `2^64` is the max\"), but the computer doesn't think that like. It sees:\r\n\r\n```\r\nconstant 2\r\nconstant 8\r\nconstant 4\r\nx = exponentiation 8 4\r\nexponentiation 2 x\r\n```\r\n\r\nI hope this makes sense.",
+ "createdAt": "2024-01-27T20:24:51Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/75#issuecomment-1913326333",
+ "viewerDidAuthor": true
+ }
+ ],
+ "createdAt": "2024-01-27T19:16:44Z",
+ "id": "I_kwDOCL0xJc59ZJcH",
+ "isPinned": false,
+ "labels": [],
+ "milestone": null,
+ "number": 75,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "state": "CLOSED",
+ "stateReason": "COMPLETED",
+ "title": "bc attempts to first calculate gigantic obase before erroring out",
+ "updatedAt": "2024-01-27T20:24:51Z",
+ "url": "https://github.com/gavinhoward/bc/issues/75"
+ },
+ {
+ "assignees": [],
+ "author": {
+ "id": "U_kgDOCGPbwA",
+ "is_bot": false,
+ "login": "shuang886",
+ "name": "Steven Huang"
+ },
+ "body": "Hi, just want to make sure I'm not missing something glaringly obvious...\r\n\r\nI'm trying to build just the library on macOS, so:\r\n\r\n```\r\n% ./configure -a\r\nTesting for FreeBSD...\r\nNot on FreeBSD. Using _POSIX_C_SOURCE and _XOPEN_SOURCE.\r\n\r\nTesting for Mac OSX...\r\nOn Mac OSX. Using _DARWIN_C_SOURCE.\r\n\r\nTesting for OpenBSD...\r\nNot on OpenBSD.\r\n\r\nVersion: 6.7.2\r\nBuilding bc\r\nBuilding dc\r\n\r\nBC_ENABLE_LIBRARY=1\r\n\r\nBC_ENABLE_HISTORY=0\r\nBC_ENABLE_EXTRA_MATH=1\r\nBC_ENABLE_NLS=0\r\n\r\nBC_ENABLE_AFL=0\r\n\r\nBC_NUM_KARATSUBA_LEN=32\r\n\r\nCC=c99\r\nCFLAGS= -DBC_ENABLE_EDITLINE=0 -DBC_ENABLE_READLINE=0\r\nHOSTCC=c99\r\nHOSTCFLAGS=\r\nCPPFLAGS=-DNDEBUG -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700\r\nLDFLAGS=-s \r\nPREFIX=/usr/local\r\nBINDIR=/usr/local/bin\r\nINCLUDEDIR=/usr/local/include\r\nLIBDIR=/usr/local/lib\r\nDATAROOTDIR=/usr/local/share\r\nDATADIR=/usr/local/share\r\nMANDIR=/usr/local/share/man\r\nMAN1DIR=/usr/local/share/man/man1\r\nMAN3DIR=/usr/local/share/man/man3\r\nNLSPATH=\r\nPC_PATH=/opt/homebrew/lib/pkgconfig\r\nEXECSUFFIX=\r\nEXECPREFIX=\r\nDESTDIR=\r\nLONG_BIT=\r\nGEN_HOST=1\r\nGEN_EMU=\r\n\r\nSetting Defaults\r\n================\r\nbc.banner=0\r\nbc.sigint_reset=1\r\ndc.sigint_reset=1\r\nbc.tty_mode=1\r\ndc.tty_mode=0\r\nbc.prompt=1\r\ndc.prompt=0\r\nbc.expr_exit=1\r\ndc.expr_exit=1\r\nbc.digit_clamp=0\r\ndc.digit_clamp=0\r\n```\r\n\r\nseems to be happy, but it doesn't actually build:\r\n\r\n```\r\n% make\r\nmkdir -p bin\r\nc99 -DBC_ENABLED=1 -DDC_ENABLED=1 -I./include/ -DBUILD_TYPE=HN -DEXECPREFIX= -DMAINEXEC=bc -D_DARWIN_C_SOURCE -DBC_NUM_KARATSUBA_LEN=32 -DBC_ENABLE_NLS=0 -DBC_ENABLE_EXTRA_MATH=1 -DBC_ENABLE_HISTORY=0 -DBC_ENABLE_LIBRARY=1 -DBC_ENABLE_MEMCHECK=0 -DBC_ENABLE_AFL=0 -DBC_DEFAULT_BANNER=0 -DBC_DEFAULT_SIGINT_RESET=1 -DBC_DEFAULT_TTY_MODE=1 -DBC_DEFAULT_PROMPT=1 -DBC_DEFAULT_EXPR_EXIT=1 -DBC_DEFAULT_DIGIT_CLAMP=0 -DDC_DEFAULT_SIGINT_RESET=1 -DDC_DEFAULT_TTY_MODE=0 -DDC_DEFAULT_PROMPT=0 -DDC_DEFAULT_EXPR_EXIT=1 -DDC_DEFAULT_DIGIT_CLAMP=0 -DNDEBUG -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -DBC_ENABLE_EDITLINE=0 -DBC_ENABLE_READLINE=0 -o src/args.o -c ./src//args.c\r\n./src//args.c:62:6: error: use of undeclared identifier 'vm'\r\n if (vm->exprs.v == NULL)\r\n ^\r\n./src//args.c:64:16: error: use of undeclared identifier 'vm'\r\n bc_vec_init(&vm->exprs, sizeof(uchar), BC_DTOR_NONE);\r\n ^\r\n...lots more...\r\n```\r\n\r\n`vm` appears to be defined in `src/vm.c` but inside a `#if !BC_ENABLE_LIBRARY`, while a lot of code in `src/args.c` are not correspondingly enclosed. I tried to sprinkle in some `#if !BC_ENABLE_LIBRARY` directives but that just exposed more code that needed to be `#ifdef`ed out.\r\n\r\nBuilding the executables (i.e., just plain `./configure`) compiles and runs fine.\r\n\r\nWould you mind clarifying if there's more needed than `./configure -a`? I couldn't find anything that helps in the build manual, and I get the sense that `args.c` should not have been in the `Makefile` at all...\r\n\r\nThanks!",
+ "closed": true,
+ "closedAt": "2023-11-27T21:30:42Z",
+ "comments": [
+ {
+ "id": "IC_kwDOCL0xJc5s_tei",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "You're not missing something glaringly obvious. That is a bug because it should build with no errors.\r\n\r\nI think I fixed it in 7807eead159b80b51b8c81680608f8187284971e; can you pull and test for me?\r\n\r\nIf that *is* the fix, then the problem was an extra slash in a path; apparently Mac OSX does not clean paths when doing a string comparison. Oops.",
+ "createdAt": "2023-11-27T21:24:37Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/71#issuecomment-1828640674",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5s_vWb",
+ "author": {
+ "login": "shuang886"
+ },
+ "authorAssociation": "NONE",
+ "body": "Works now, thanks!",
+ "createdAt": "2023-11-27T21:30:42Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/71#issuecomment-1828648347",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc5s_vvD",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "You're welcome. I'll put out a new release with the fix.",
+ "createdAt": "2023-11-27T21:31:57Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/71#issuecomment-1828649923",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5s_y9T",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Ah! I made a mistake! Could you pull 7eaa40ab8cac29893155471076c621c2f9929d33 and see if that works?",
+ "createdAt": "2023-11-27T21:41:25Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/71#issuecomment-1828663123",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5s_zsO",
+ "author": {
+ "login": "shuang886"
+ },
+ "authorAssociation": "NONE",
+ "body": "Yup, still works.",
+ "createdAt": "2023-11-27T21:43:29Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/71#issuecomment-1828666126",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc5s_0Ig",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Thank you, and I'm sorry.",
+ "createdAt": "2023-11-27T21:44:49Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [
+ {
+ "content": "HEART",
+ "users": {
+ "totalCount": 1
+ }
+ }
+ ],
+ "url": "https://github.com/gavinhoward/bc/issues/71#issuecomment-1828667936",
+ "viewerDidAuthor": true
+ }
+ ],
+ "createdAt": "2023-11-27T17:48:32Z",
+ "id": "I_kwDOCL0xJc53-Mb1",
+ "isPinned": false,
+ "labels": [],
+ "milestone": null,
+ "number": 71,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "state": "CLOSED",
+ "stateReason": "COMPLETED",
+ "title": "Build errors after ./configure -a",
+ "updatedAt": "2023-11-27T21:44:50Z",
+ "url": "https://github.com/gavinhoward/bc/issues/71"
+ },
+ {
+ "assignees": [],
+ "author": {
+ "id": "U_kgDOBZsPsA",
+ "is_bot": false,
+ "login": "STSMHQ",
+ "name": "STSM"
+ },
+ "body": "Hi, @gavinhoward,\r\n\r\nI read the [documentation file](https://git.gavinhoward.com/gavin/bc/src/branch/master/manuals/bc/A.1.md) and it seems that arrays can only old a single value per each index (a number or a string). Is there any workaround to be able to store more than one single value in an array?\r\n\r\n```shell\r\n# This works\r\nbc_example[0] = 1;\r\n\r\n# This doesn't\r\nbc_example[1] = [1, 2, 3];\r\nbc_example[2] = [1, \"Random\", 3, \"Another\"];\r\n\r\nprint bc_example[2][1] # Random\r\n```\r\n\r\nAs I already stated before in another issue, I'm using your amazing tool to keep track of my expenses and it would be amazing to be able to create a \"matrix\" or an \"array of arrays\" as people often call it. Although I know that my use case isn't the general one and that the type of feature that I'm requesting doesn't make much sense considering the main goal/purpose of this tool, I'm opening this issue to know if what I want is somehow possible using any syntax workaround not documented in the above file.",
+ "closed": true,
+ "closedAt": "2023-11-13T16:02:39Z",
+ "comments": [
+ {
+ "id": "IC_kwDOCL0xJc5ruIip",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Unfortunately, there is not any workaround, and that is on purpose. The `bc` language is just too restricted to go any further without *serious* work.\r\n\r\nIt is *possible*, yes. However, I believe that would be a mistake.\r\n\r\nI once said this:\r\n\r\n> Good software design includes putting whatever complexity must exist where it *best fits*.\r\n>\r\n> -- [\"Justifying a Backwards Design Decision for Yao][1]\r\n\r\nSo one of the skills of a good programmer is knowing *where* complexity should go.\r\n\r\nIf you need multiple values in one array slot, it sounds like you need structs, objects, or some other compound data thing. That sort of complexity should live in a \"proper\" programming language.\r\n\r\nSo I suggest that you use a \"proper\" programming language with arbitrary-precision numbers, like Python.\r\n\r\nThat said, I don't want to just hang you out to dry with no solution!\r\n\r\nYou're using my `bc` because it was the best for the job until now, right? Can you tell me why that is?\r\n\r\nYou've mentioned that you use it for finances; unlike most other math, finances basically require base 10 math, so is that why my `bc` was best?\r\n\r\nIf so, I might have a solution for you.\r\n\r\nDo you know Python?\r\n\r\nIf so, I could learn how to create a C extension for Python, and then I could use my [`bc`'s library form][2] to create a Python extension to use my `bc`'s library. That way, you could use Python, but still have the decimal-based math you need for finances, and I wouldn't have to do a ton of work to add multi-values to `bc` while also adding my `bc` to Python.\r\n\r\nAs an extra bonus, if you use Python, you could then have your files be output to JSON or another well-known format. And hey, I may even be able to help you convert your scripts to Python if you send them to me. (Do it through email, not this bug report!)\r\n\r\nWill that work for you?\r\n\r\n[1]: https://gavinhoward.com/2023/02/justifying-a-backwards-design-decision-for-yao/#designing-for-lsp\r\n[2]: https://github.com/gavinhoward/bc/blob/master/manuals/bcl.3.md",
+ "createdAt": "2023-11-12T22:06:36Z",
+ "includesCreatedEdit": true,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/70#issuecomment-1807255721",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5rytlH",
+ "author": {
+ "login": "STSMHQ"
+ },
+ "authorAssociation": "NONE",
+ "body": "Hi, @gavinhoward,\r\n\r\nI'm using your `bc` tool for over a year to keep track of my expenses and everything related to my personal finances. The main reason behind this decision is what you just said, generally speaking, finances use base 10 math and `bc-gh` allows me to have arbitrary-precision numbers on it. The second big reason is **you**. The passion you put into your projects is truly amazing. I know that as long as you're alive and well, you'll keep this project running and that's a important thing for me. Also, your support as shown here in this issue is very, very good. To end, I'm a follower of your [blog](https://gavinhoward.com/) since the beginning of 2022.\r\n\r\nI don't know Python. I could learn it but I don't like much its' syntax and the biggest part of its' design choices. I'll probably take this chance to improve my NuShell knowledge and try to integrate `bc-gh` into it. With NuShell, I know that I can work with well-known formats like JSON/YAML/TOML in order to be able to implement my desired workflow. Thank you for everything and keep up the excellent work.\r\n\r\n",
+ "createdAt": "2023-11-13T16:02:39Z",
+ "includesCreatedEdit": true,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/70#issuecomment-1808456007",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc5ry87l",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "You flatter me! :) But more seriously, thank you for the compliments; I struggle to see worth in my code, and that helps a lot.\r\n\r\nI'm sorry I couldn't help more. But feel free to ask any questions to help with your port to NuShell. I'm happy to help with that since I was not much help here!",
+ "createdAt": "2023-11-13T16:35:41Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/70#issuecomment-1808518885",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5r93lR",
+ "author": {
+ "login": "STSMHQ"
+ },
+ "authorAssociation": "NONE",
+ "body": "> You flatter me! :) But more seriously, thank you for the compliments; I struggle to see worth in my code, and that helps a lot.\r\n\r\nYour code and your blog posts truly make a difference to my life 💙\r\n\r\n> I'm sorry I couldn't help more. But feel free to ask any questions to help with your port to NuShell. I'm happy to help with that since I was not much help here!\r\n\r\nYou don't have to be sorry. I read the documentation of NuShell and I think I'm able to implement my current workflow with `bc-gh` on it pretty easily. Thank you for all your spent time and help offered. Have a nice rest of week!",
+ "createdAt": "2023-11-14T21:51:18Z",
+ "includesCreatedEdit": true,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/70#issuecomment-1811380561",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc5sEd4h",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "You're welcome.\r\n\r\nHey, when you finish your NuShell implementation, I would love it if you could email it to me; I'm almost done with my new language, and I'd love to use it as a test application.",
+ "createdAt": "2023-11-15T19:12:11Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/70#issuecomment-1813110305",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5sIRpw",
+ "author": {
+ "login": "STSMHQ"
+ },
+ "authorAssociation": "NONE",
+ "body": "I'm still studying it. As NuShell didn't reach yet the first ever stable level, I don't know if I should implement all of my workflow directly using it. Currently, I'm just exploring the data processing pipeline features that they have against a workflow using `sed`/`awk` and your `bc-gh`, for instance. All I'm doing is pretty much pick a JSON file, iterate over it, apply some editions/additions using your `bc-gh` and then save it again to a JSON file. Hope you like NuShell as much as I do and one day in the future, it can have the same popularity as `zsh`.\r\n\r\nIf in the future I implement something that I'm proud of, I'll happily share it with you, Gavin.",
+ "createdAt": "2023-11-16T09:48:33Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [
+ {
+ "content": "THUMBS_UP",
+ "users": {
+ "totalCount": 1
+ }
+ }
+ ],
+ "url": "https://github.com/gavinhoward/bc/issues/70#issuecomment-1814108784",
+ "viewerDidAuthor": false
+ }
+ ],
+ "createdAt": "2023-11-12T18:51:58Z",
+ "id": "I_kwDOCL0xJc52lbxv",
+ "isPinned": false,
+ "labels": [],
+ "milestone": null,
+ "number": 70,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "state": "CLOSED",
+ "stateReason": "COMPLETED",
+ "title": "Array - Multiple values ",
+ "updatedAt": "2023-11-16T09:48:33Z",
+ "url": "https://github.com/gavinhoward/bc/issues/70"
+ },
+ {
+ "assignees": [],
+ "author": {
+ "id": "MDQ6VXNlcjQ4MjY5Mzk5",
+ "is_bot": false,
+ "login": "TediusTimmy",
+ "name": "Thomas"
+ },
+ "body": "Greetings,\r\n\r\nI am going to start with some pleasantries and BS and work from there.\r\n\r\nHello. I also like bc. I have some bc-related repos: https://github.com/TediusTimmy/GNU_bc_with_GMP and https://github.com/TediusTimmy/OpenBSD_bc_with_GMP . The first one is an example of how fast bc can be, if we just use GMP. I also fixed as many of the crashes as I was aware of (admittedly, by fixing the back-end to be more robust rather than fixing the garbage generated by the front-end; if you find a crash, please let me know). In the second repo, I have a comparison of the speed of several implementations of bc (based on my own benchmark), including yours. I found it to be roughly comparable to, but still slightly slower than, OpenBSD's bc (except on one specific task, where it even beat GMP). I also wrote a spreadsheet program to use bc-like numbers: https://github.com/TediusTimmy/BC-DeciCalc (which I _really_ need to work on the documentation for).\r\n\r\nAs for those bug-fixes in GNU bc: I really haven't been able to get ahold of Phil, or Ken. Thankfully, there were some really helpful people at Debian, and maybe one day, either https://salsa.debian.org/debian/bc/-/merge_requests/4 will be merged in, or they will just use a better bc implementation, like yours.\r\n\r\nFinally, are you aware of this: https://www.php.net/manual/en/book.bc.php ? They have forked the GNU bc code for number.c and include it in their builds. Maybe they could use the library version of your code? (My only concern would be https://bugs.php.net/bug.php?id=66364 )\r\n\r\n\r\nOn to the issue: I'm not a big fan of your `p(x,y)` function. Let me explain by way of a contrived example: `p(1024,32.1)`. Now `1024` is `2^10`, so this SHOULD give us `2^10^32.1` which is `2^(10*32.1)` or `2^321`. And that's an integer, so it should be exact. But, when we do this we find that only the first 18 digits are correct with the default scale of 20. As you manipulate the scale variable, you find a correlation between the scale variable and the number of correct digits. So, in order to compute this result to 20 digits of scale, you would need to compute the log and exponential to around 117 digits of scale. I don't think that I'm out of line to expect that when you increase the scale of a computation that maybe the last three digits change and you then get extra new good digits. With this implementation of `p(x,y)`, a good chunk of the number changes.\r\n\r\nAs I was thinking about this, four cases came up:\r\n1) If we are raising a number greater than one to a positive power, then we want to bump the scale by the length of the integer part.\r\n2) If we are raising a number greater than one to a negative power, then we can probably use `e(y*l(x))` as that result goes to zero.\r\n3) Conversely, if we are raising a number less than one to a positive power, then we can probably use `e(y*l(x))` as the result goes to zero.\r\n4) Finally, if if we are raising a number less than one to a negative power, then we need to be extra careful. We need to use the reciprocal to find the integer part, but want the reciprocal to the increased scale to have an accurate result (in my testing, it removed a problem in the unit in the last place).\r\n\r\nSo, my final function to improve `p(x,y)`, then commentary:\r\n```\r\ndefine pow(x,y){\r\n\tauto a,i,s,z\r\n\tif(0==y)return 1@scale\r\n\tif(0==x){\r\n\t\tif(y>0)return 0\r\n\t\treturn 1/0\r\n\t}\r\n\ta=y$\r\n\tif(y==a)return(x^a)@scale\r\n\tz=0\r\n\tif(x<1){\r\n\t\ty=-y\r\n\t\ta=-a\r\n\t\tz=x\r\n\t\tx=1/x\r\n\t}\r\n\tif(y<0){\r\n\t\treturn e(y*l(x))\r\n\t}\r\n\ti=x^a\r\n\ts=scale\r\n\tscale+=length(i)\r\n\tif(z){\r\n\t\tx=1/z\r\n\t\ti=x^a\r\n\t}\r\n\ti*=e((y-a)*l(x))\r\n\tscale=s\r\n\treturn i@scale\r\n}\r\n```\r\nI started with your `p(x,y)` function and then added code. We begin with some special case handling for zeros, because people will complain (I didn't realize that bc already defines `0^0==1`). Next is the detection for the easy case of an integer exponent that you already had. We then handle if x is between zero and one: we save x and proceed with the reciprocal, while negating the exponent. Remember that `l(x)==-l(1/x)`. I specifically ignore if x is negative, because we will eventually call `l(x)`, which will return an erroneous value if x is negative. If the exponent is now negative, we fall back on `e(y*l(x))`: that number is going to zero anyway. Next, we compute the integral portion of the exponent and use it to get the working scale for the fractional part of the exponent. After that, if we took the reciprocal of x, recompute the reciprocal and the integral part of the exponent at this higher scale to ensure that both are accurate. Penultimately, compute the fractional part of the exponent and multiply it by the integral part to get the complete exponent. Finally, return a result at the desired scale.\r\n",
+ "closed": true,
+ "closedAt": "2023-08-19T06:38:17Z",
+ "comments": [
+ {
+ "id": "IC_kwDOCL0xJc5kV35W",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Hello.\r\n\r\n> As for those bug-fixes in GNU bc: I really haven't been able to get ahold of Phil, or Ken. Thankfully, there were some really helpful people at Debian, and maybe one day, either https://salsa.debian.org/debian/bc/-/merge_requests/4 will be merged in, or they will just use a better bc implementation, like yours.\r\n\r\nMaybe I'm selfish here, but I think they should just use mine. I tested my `bc` against all of the items at <https://lists.debian.org/debian-devel/2022/08/msg00035.html> and <https://bugs.launchpad.net/ubuntu/+source/bc/+bug/1775776>, and not only does my `bc` not crash, ASan, UBSan, and Valgrind report no errors, not even memory leaks. (Yay for fuzzing!) If you know how I might submit a request for them to do so, please let me know. I'll even build the Debian package myself!\r\n\r\n> Finally, are you aware of this: https://www.php.net/manual/en/book.bc.php ? They have forked the GNU bc code for number.c and include it in their builds. Maybe they could use the library version of your code? \r\n\r\nI *am* aware, actually. This was in the back of my mind when I accepted a request from my FreeBSD contact to implement the `bcl` library. But I don't know how to contact them and convince them to use my library.\r\n\r\n> (My only concern would be https://bugs.php.net/bug.php?id=66364 )\r\n\r\nUnfortunately, the PHP authors are wrong; that behavior is well-documented in the [POSIX standard for `bc`][1], which says about multiplication:\r\n\r\n> The result shall be the product of the two expressions. If a and b are the scales of the two expressions, then the scale of the result shall be:\r\n>\r\n> `min(a+b,max(scale,a,b))`\r\n\r\nSo their fix for that bug report is wrong, in my opinion. Thus, I don't think they'll adopt my library because I won't patch my library for their \"bug.\"\r\n\r\n> On to the issue: I'm not a big fan of your `p(x,y)` function...\r\n\r\nYour criticisms are absolutely fair. Fair enough that I took your code (with style fixes), documented it, and pushed it as the `better_pow` branch in this repo.\r\n\r\nCan you take a look to check if the code is correct, that it works for you, and that the documentation I added in `manuals/algorithms.md` is correct? I documented it in my own words, to make sure I understood the code.\r\n\r\nFinally, last item: do you think that you'll open more issues on `bc` in the future? If so, I'll add an account for you on my website; I just need an email address.\r\n\r\n[1]: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/bc.html#tag_20_09_13_03",
+ "createdAt": "2023-08-18T07:09:12Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/69#issuecomment-1683455574",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5kV5_7",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Forgot to say this:\r\n\r\n> In the second repo, I have a comparison of the speed of several implementations of bc (based on my own benchmark), including yours. I found it to be roughly comparable to, but still slightly slower than, OpenBSD's bc (except on one specific task, where it even beat GMP).\r\n\r\nThose benchmarks make sense to me, except plain OpenBSD `bc` being faster than mine; it uses plain `char` for digits, so mine should be faster. My guess is that it is parsing without many error checks.\r\n\r\nAlso, I store constants as strings, like GNU. This is because someone could change the `ibase` at any time, and if they do, that constant needs to be interpreted according to the new `ibase`. But I know for a fact that the OpenBSD `bc` has a different behavior: it will interpret the number according to the digits, no matter if the digits are invalid for the `ibase`. This allows it to precalculate, where GNU and I cannot.",
+ "createdAt": "2023-08-18T07:17:28Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/69#issuecomment-1683464187",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5kbO1o",
+ "author": {
+ "login": "TediusTimmy"
+ },
+ "authorAssociation": "NONE",
+ "body": "Many apologies. I posted that improvement and went straight to bed last night, and then life got in the way of me responding. And I am going to go straight to bed tonight, also.\r\n\r\n> If you know how I might submit a request for them to do so, please let me know.\r\n\r\nHonestly, when it comes to Debian, Philip Hands was a godsend (but, he isn't the maintainer). I tried emailing Phil Nelson and Ken Pizzini and Ryan Kavanagh, but those probably went into spam filters. Eventually, I emailed the debian-devel mailing list and someone who helps first-time contributors (Hands) picked it up and ran with it. Like any large open-source project, it has an inscrutable political organization to outsiders like us. I was lucky that they even have a merge request for the changes (which has been open for over a year).\r\n\r\nThe real problem is that GNU bc is \"good enough\". It has vulnerabilities that no one is known to have exploited (if they even are exploitable). People complain, but not loud enough to get things done. It works for its purpose, and doesn't seem to have a large user base.\r\n\r\n> But I don't know how to contact them and convince them to use my library.\r\n\r\nAgain: inscrutable political organization. I don't actually program with PHP, I also am just aware of this extension. Looking at this bug report ( https://github.com/php/php-src/issues/10967 ), maybe the internal mailing list or the RFC process is what you want. \r\n\r\n> the PHP authors are wrong\r\n\r\nYeah. Thought, looking at it again, they could have fixed the issue here ( https://github.com/php/php-src/blob/master/ext/bcmath/bcmath.c#L258 ) instead of where they did. You could convince them that this is the better place for a bodge between their code and the bc library (as the behavior is standards-driven). Though, even Debian has a bug for this: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=610988\r\n\r\n> with style fixes\r\n\r\nWhat did you change? Oh, that. I get that from a former workplace.\r\n\r\n> it uses plain char for digits\r\n\r\nIt uses the OpenSSL BIGNUM library, which is binary words ( https://github.com/openbsd/src/blob/master/usr.bin/dc/bcode.h#L24 and https://github.com/openbsd/src/blob/master/lib/libcrypto/bn/bn_local.h#L121 ). For every 64 bits of a number, they are operating on all 64 bits, and you are operating on a little over 63 bits of it and have a normalization step. The trade-off rears its ugly head when numbers get converted to/from decimal.\r\n\r\n> do you think that you'll open more issues on bc in the future\r\n\r\nI don't have any plans. This was a spur-of-the-moment thing. I had \"finished\" a pow function for the spreadsheet, and decided to rewrite it in bc. (And then, I spent a bunch of time rewriting it again, as I realized that I had missed some corner cases). I don't plan to make a habit of this.\r\n\r\nAs for reviewing things:\r\n\r\n> being non-zero is a flag for later and a value to be used\r\n\r\nI did that, didn't I? Bad me.\r\n\r\nI don't know if the algorithm needs this much detail. It is probably good to give the impression that someone put thought and care into making sure the results were accurate. Though, really it was just some rando who kept doing really large exponents and wanted some numerical stability as he cranked up the precision while looking at numbers like https://www.youtube.com/watch?v=BdHFLfv-ThQ.",
+ "createdAt": "2023-08-19T06:38:17Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/69#issuecomment-1684860264",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc5kbPwt",
+ "author": {
+ "login": "TediusTimmy"
+ },
+ "authorAssociation": "NONE",
+ "body": "And I forgot to say something: I understand that the frustrating thing about the bcmath extension to PHP is that you have something that is _just better_. And you have no idea how to make things better.",
+ "createdAt": "2023-08-19T06:46:55Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/69#issuecomment-1684864045",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc5kcbqC",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "> Eventually, I emailed the debian-devel mailing list and someone who helps first-time contributors (Hands) picked it up and ran with it. Like any large open-source project, it has an inscrutable political organization to outsiders like us.\r\n\r\nYeah, that sounds exhausting. Not worth it to me at the moment.\r\n\r\n> The real problem is that GNU bc is \"good enough\". It has vulnerabilities that no one is known to have exploited (if they even are exploitable). People complain, but not loud enough to get things done. It works for its purpose, and doesn't seem to have a large user base.\r\n\r\nThis is true.\r\n\r\n> Yeah. Thought, looking at it again, they could have fixed the issue here ( https://github.com/php/php-src/blob/master/ext/bcmath/bcmath.c#L258 ) instead of where they did. You could convince them that this is the better place for a bodge between their code and the bc library (as the behavior is standards-driven).\r\n\r\nEh, probably not worth it.\r\n\r\n> Though, even Debian has a bug for this: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=610988\r\n\r\nYikes. People don't read, do they? Okay, I *really* don't care about getting into Debian.\r\n\r\n> It uses the OpenSSL BIGNUM library, which is binary words\r\n\r\nOh, that was not what I understood. Oops. Those benchmarks make sense then.\r\n\r\n> I don't have any plans. This was a spur-of-the-moment thing.\r\n\r\nOkay.\r\n\r\n> As for reviewing things...I did that, didn't I? Bad me.\r\n\r\nWell, that `bc` code goes straight into the binary as a string. Using `z` as a flag and a value is useful to reduce the number of bytes used. I kept it for a reason.\r\n\r\n> I don't know if the algorithm needs this much detail. It is probably good to give the impression that someone put thought and care into making sure the results were accurate.\r\n\r\n*I* need that much detail. I *have* to understand every bit of code in this repo.\r\n\r\nTake, for example, the comment [here][1]. That was me explaining, *to myself*, an algorithm that someone else coded up. If there was a bug in that algorithm when I got it (and there was), there was no way for me to debug it unless I knew what it was supposed to be doing.\r\n\r\nIf there is a bug in your code, I need to understand your code to debug it.\r\n\r\nThat's why there is a wealth of information for developers; it's for *me*.\r\n\r\nSo I'm glad to know that the explanation is correct, but I'll keep all of it. :)\r\n\r\nI may wait a while to release a new version (I have a new PR about adding CMake support), but I will release a new version with your code within a reasonable time.\r\n\r\nThank you.\r\n\r\n[1]: https://github.com/gavinhoward/bc/blob/75cf2e3358b5a76780db0d448c02cf6f61065921/src/num.c#L3077-L3113",
+ "createdAt": "2023-08-20T04:43:10Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/69#issuecomment-1685174914",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5nZ8wq",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "There is now a version (`6.6.1`) with your `p()` implementation. Thank you very much!",
+ "createdAt": "2023-09-26T05:30:27Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/69#issuecomment-1734855722",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5nnXnb",
+ "author": {
+ "login": "TediusTimmy"
+ },
+ "authorAssociation": "NONE",
+ "body": "You are welcome. I hope it is as useful for your other users as it is to me and not a maintenance burden on you.",
+ "createdAt": "2023-09-28T03:11:48Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/69#issuecomment-1738373595",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc5nnXzO",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "None at all now that I understand it, thanks to you!",
+ "createdAt": "2023-09-28T03:13:15Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/69#issuecomment-1738374350",
+ "viewerDidAuthor": true
+ }
+ ],
+ "createdAt": "2023-08-18T05:29:27Z",
+ "id": "I_kwDOCL0xJc5uoYUw",
+ "isPinned": false,
+ "labels": [],
+ "milestone": null,
+ "number": 69,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "state": "CLOSED",
+ "stateReason": "COMPLETED",
+ "title": "Incoherent rambling followed by a suggestion for an improvement.",
+ "updatedAt": "2023-09-28T03:13:15Z",
+ "url": "https://github.com/gavinhoward/bc/issues/69"
+ },
+ {
+ "assignees": [],
+ "author": {
+ "id": "MDQ6VXNlcjI5MDIyNQ==",
+ "is_bot": false,
+ "login": "mhorowitz",
+ "name": ""
+ },
+ "body": "platform: MacOS Ventura 13.2.1 (m1 max)\r\ncompiler: c99 from Xcode 14.2.0\r\nversion: https://git.gavinhoward.com/gavin/bc.git 55a6c05b280fbfb6873e42a5825403c17417029a\r\n\r\nBuild fails:\r\n```\r\n$ ./configure -O3\r\n<success>\r\n$ make\r\nc99 -DBC_ENABLED=1 -DDC_ENABLED=1 -I./include/ -DBUILD_TYPE=A -DEXECPREFIX= -DMAINEXEC=bc -DBC_NUM_KARATSUBA_LEN=32 -DBC_ENABLE_NLS=1 -DBC_ENABLE_EXTRA_MATH=1 -DBC_ENABLE_HISTORY=1 -DBC_ENABLE_LIBRARY=0 -DBC_ENABLE_MEMCHECK=0 -DBC_ENABLE_AFL=0 -DBC_DEFAULT_BANNER=0 -DBC_DEFAULT_SIGINT_RESET=1 -DBC_DEFAULT_TTY_MODE=1 -DBC_DEFAULT_PROMPT=1 -DBC_DEFAULT_EXPR_EXIT=1 -DBC_DEFAULT_DIGIT_CLAMP=0 -DDC_DEFAULT_SIGINT_RESET=1 -DDC_DEFAULT_TTY_MODE=0 -DDC_DEFAULT_PROMPT=0 -DDC_DEFAULT_EXPR_EXIT=1 -DDC_DEFAULT_DIGIT_CLAMP=0 -DNDEBUG -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -O3 -DBC_ENABLE_EDITLINE=0 -DBC_ENABLE_READLINE=0 -o src/program.o -c ./src//program.c\r\n./src//program.c:2994:6: error: use of undeclared identifier 'SIGWINCH'\r\n if (BC_SIG_INTERRUPT(vm))\r\n ^\r\n./include/status.h:698:45: note: expanded from macro 'BC_SIG_INTERRUPT'\r\n BC_UNLIKELY((vm)->sig != 0 && (vm)->sig != SIGWINCH)\r\n ^\r\n./src//program.c:3724:6: error: use of undeclared identifier 'SIGWINCH'\r\n if (BC_SIG_INTERRUPT(vm))\r\n ^\r\n./include/status.h:698:45: note: expanded from macro 'BC_SIG_INTERRUPT'\r\n BC_UNLIKELY((vm)->sig != 0 && (vm)->sig != SIGWINCH)\r\n ^\r\n2 errors generated.\r\nmake: *** [src/program.o] Error 1\r\n```\r\nI think ideally, configure.sh would detect this, and apply an appropriate set of flags, or the source would be changed appropriately.\r\n\r\nAs a workaround, this allows a successful build:\r\n```\r\nCFLAGS=-D_DARWIN_C_SOURCE ./configure -O3\r\n```",
+ "closed": true,
+ "closedAt": "2023-03-31T17:44:43Z",
+ "comments": [
+ {
+ "id": "IC_kwDOCL0xJc5Y4XHV",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Stupid GitHub, closing the issue just because I mention the number in a commit...\r\n\r\nAnyway, yes, this is a bug, and yes, `configure.sh` should detect this.\r\n\r\nI've added 29b4b6a4f27b07c4a176258aaf831a5208d4e116 in an attempt to fix this. Unfortunately, I don't have a Mac, so you'll need to test this for me. Would you please do that? Thank you.",
+ "createdAt": "2023-03-31T01:36:32Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/67#issuecomment-1491169749",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5Y8585",
+ "author": {
+ "login": "mhorowitz"
+ },
+ "authorAssociation": "NONE",
+ "body": "> I've added https://github.com/gavinhoward/bc/commit/29b4b6a4f27b07c4a176258aaf831a5208d4e116 in an attempt to fix this. Unfortunately, I don't have a Mac, so you'll need to test this for me. Would you please do that? Thank you.\r\n\r\nI tested it. ./configure.sh output includes\r\n\r\n On Mac OSX. Using _DARWIN_C_SOURCE.\r\n\r\nAnd make completes without error. Thanks for the quick fix!",
+ "createdAt": "2023-03-31T17:44:43Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/67#issuecomment-1492361017",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc5Y867a",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Thank you for testing! And thank you for the report. I rely on users like you to help me with Mac OSX!",
+ "createdAt": "2023-03-31T17:46:45Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [
+ {
+ "content": "ROCKET",
+ "users": {
+ "totalCount": 1
+ }
+ }
+ ],
+ "url": "https://github.com/gavinhoward/bc/issues/67#issuecomment-1492365018",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5c_2t2",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Release `6.6.0` is out with the fix. Thank you!",
+ "createdAt": "2023-05-23T23:11:04Z",
+ "includesCreatedEdit": true,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/67#issuecomment-1560243062",
+ "viewerDidAuthor": true
+ }
+ ],
+ "createdAt": "2023-03-30T22:25:15Z",
+ "id": "I_kwDOCL0xJc5iQKue",
+ "isPinned": false,
+ "labels": [],
+ "milestone": null,
+ "number": 67,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "state": "CLOSED",
+ "stateReason": "COMPLETED",
+ "title": "Build fails on macOS",
+ "updatedAt": "2023-05-23T23:11:20Z",
+ "url": "https://github.com/gavinhoward/bc/issues/67"
+ },
+ {
+ "assignees": [],
+ "author": {
+ "id": "U_kgDOBZsPsA",
+ "is_bot": false,
+ "login": "STSMHQ",
+ "name": "STSM"
+ },
+ "body": "Hi, there,\r\n\r\nI love your command line tool and your blog (I read all your posts). You're an amazing developer. I hope one day I can be like you.\r\nI'm opening this issue to ask you if there is any way to bypass the `Math error: overflow: number cannot fit` error on your calculator. I would like to test it for benchmark purposes against Julia REPL (for example), but your `bc` gives me this error when the number has too many digits.\r\n\r\nExample Calculation: `((169287^137)^920)^13256118217109`.\r\n\r\nRegards,\r\n**STSM**",
+ "closed": true,
+ "closedAt": "2023-03-21T17:06:49Z",
+ "comments": [
+ {
+ "id": "IC_kwDOCL0xJc5XyVJh",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Hello,\r\n\r\nThank you for the compliments!\r\n\r\n> I'm opening this issue to ask you if there is any way to bypass the `Math error: overflow: number cannot fit` error on your calculator.\r\n\r\nIt depends.\r\n\r\nOn the particular example you gave, my machine did not give the error. (I am running an `x86_64` Linux machine.)\r\n\r\nThe error happens in this case because `bc` limits exponents to the maximum that can fit in the `unsigned long` type. There are good reasons for this, not least of which calculating a power with an exponent larger than that takes a long time.\r\n\r\nFor example, I started running the example calculation you gave seven hours ago, and it's still going.\r\n\r\nThe other reason is that the exponent needs to be converted into an `unsigned long` to do the actual calculation, which involves running a loop.\r\n\r\nBy the way, this is also the reason that exponents need to be integers.\r\n\r\nAnyway, one way to get around the limit would be to get a computer with a 64-bit `unsigned long` type. Machines that may *not* have that could include Windows (even if 64-bit) or 32-bit machines with any OS.\r\n\r\nAs an example, here are the limits for my machine (found by the `limits` keyword):\r\n\r\n```\r\n$ bc\r\n>>> limits\r\nBC_LONG_BIT = 64\r\nBC_BASE_DIGS = 9\r\nBC_BASE_POW = 1000000000\r\nBC_OVERFLOW_MAX = 18446744073709551615\r\n\r\nBC_BASE_MAX = 1000000000\r\nBC_DIM_MAX = 18446744073709551614\r\nBC_SCALE_MAX = 18446744073709551614\r\nBC_STRING_MAX = 18446744073709551614\r\nBC_NAME_MAX = 18446744073709551614\r\nBC_NUM_MAX = 18446744073709551614\r\nBC_RAND_MAX = 18446744073709551615\r\nMAX Exponent = 18446744073709551615\r\nNumber of vars = 18446744073709551614\r\n>>> quit\r\n```\r\n\r\nThere are two to notice: `BC_LONG_BIT` and `MAX Exponent`.\r\n\r\n`BC_LONG_BIT` is 64, which means `bc` was built assuming a 64-bit `unsigned long` type. `MAX Exponent` is the actual limit to the exponent, which equals `(2^64)-1`, which is `ULONG_MAX` on a machine with a 64-bit `unsigned long`.\r\n\r\nHowever, if I tell my `bc` to assume a 32-bit `unsigned long` when I build it, using:\r\n\r\n```\r\n$ LONG_BIT=32 ./configure <args>\r\n$ make\r\n```\r\n\r\nThen the limits look different:\r\n\r\n```\r\n$ bin/bc\r\n>>> limits\r\nBC_LONG_BIT = 32\r\nBC_BASE_DIGS = 4\r\nBC_BASE_POW = 10000\r\nBC_OVERFLOW_MAX = 4294967295\r\n\r\nBC_BASE_MAX = 10000\r\nBC_DIM_MAX = 4294967294\r\nBC_SCALE_MAX = 4294967294\r\nBC_STRING_MAX = 4294967294\r\nBC_NAME_MAX = 4294967294\r\nBC_NUM_MAX = 4294967294\r\nBC_RAND_MAX = 4294967295\r\nMAX Exponent = 4294967295\r\nNumber of vars = 18446744073709551614\r\n>>> quit\r\n```\r\n\r\n`BC_LONG_BIT` is now 32, and `MAX Exponent` is now 4294967295, which is `(2^32)-1`. Notice that 4294967295 is less than the last exponent (13256118217109) in your example calculation. This means that if 4294967295 is your `MAX Exponent`, you will get that error. And I *do* get that error when trying your example computation with that build.\r\n\r\nNow, there *is* still a way around it, but it requires some math knowledge.\r\n\r\nYou must know one fact: `(x^y)*(x^z) == x^(y+z)`.\r\n\r\nIn essence, if you multiply two powers of one number, the effect is the same as adding the exponents of those powers and then calculating the power from the sum of those exponents.\r\n\r\nUsually, you would want to just add the exponents and then calculate the power because multiplications would be much more expensive. However, in this case, we can do the opposite: split the exponent (13256118217109) into several numbers and then multiply all of those numbers together.\r\n\r\nLet's decide on one exponent to use, which I'll call `E`. I can divide 13256118217109 by `E`, and the result is the number of times that I need to multiply the power by itself. However, that will not be quite right because `E` almost certainly does not divide 13256118217109 evenly, so `13256118217109%E` won't be 0. In that case, we'll want to multiply the end result by the power where the exponent is equal to `13256118217109%E`.\r\n\r\nIt will all look like this (in `bc` code):\r\n\r\n```\r\nscale = 0\r\ns = ((169287^137)^920)\r\nn = s^E\r\nr = n\r\nt = 13256118217109 / E\r\nm = 13256118217109 % E\r\n\r\nfor (i = 1; i < t; ++i)\r\n{\r\n r *= n\r\n}\r\n\r\nr *= s^m\r\n```\r\n\r\nBut even then, we can make it faster. Since we are just multiplying the value by itself many times, we can use power again. This time, the exponents (3086 and 6172) are back within limits, so we're safe.\r\n\r\nIf we do that, we get:\r\n\r\n```\r\nscale = 0\r\ns = ((169287^137)^920)\r\nt = 13256118217109 / E\r\nm = 13256118217109 % E\r\nn = s^E\r\nr = n^t\r\nr *= s^m\r\n```\r\n\r\nAnd that should give us our answer.\r\n\r\nWe should set those exponents as best as we can to make it as efficient as possible.\r\n\r\nI know that my `bc` uses the binary representation of the exponent to calculate power. Every bit that's a 1 has an extra multiply.\r\n\r\nSo if I set one of the powers to `(2^32)-1`, I know every single bit will be 1, meaning that there will be extra multiplications. However, it will also mean I need to use an exponent of 3086 (`13256118217109/((2^32)-1)`), whereas if I set one of the powers to be `2^31`, there will only be a single 1 bit, but I will need to use an exponent of 6172.\r\n\r\nAn exponent of 6172 has the same amount of 1 bits as 3086 (it's just 3086 times 2), so it will require only one more multiply. However, `2^31` will require 30 less multiplies than `(2^32)-1`, so we'll go with that.\r\n\r\nThat gives us this code:\r\n\r\n```\r\nscale = 0\r\ne = 2^31\r\ns = ((169287^137)^920)\r\nt = 13256118217109 / e\r\nm = 13256118217109 % e\r\nn = s^e\r\nr = n^t\r\nr *= s^m\r\n```\r\n\r\nI haven't tested the above code because it will take forever. If there are problems, please let me know.\r\n\r\n> I would like to test it for benchmark purposes against Julia REPL (for example)\r\n\r\nMy `bc` will be slower than Julia. I am confident of that.\r\n\r\nThis is because my `bc` does not use the full range of hardware-native integers in order to use decimal-based math. I bet Julia does use the full range and uses binary-based math.\r\n\r\nAlso, since it's taken seven hours on my `bc` on a 64-bit machine, I am *wildly* confident that Julia will be better.\r\n\r\nYou may want a benchmark with a smaller exponent.",
+ "createdAt": "2023-03-16T22:04:11Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/66#issuecomment-1472811617",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5X2u-Q",
+ "author": {
+ "login": "STSMHQ"
+ },
+ "authorAssociation": "NONE",
+ "body": "First of all, I just want to let you know that you're really, really amazing. The passion that you've for your project(s) and for the people that interact with them is something that melts my heart. I hope one day I can be a programmer as good as you and also, more important, have the same amount of passion that you have.\r\n\r\nEnglish is not my primary language, so this may not make any sense.\r\n\r\n> Anyway, one way to get around the limit would be to get a computer with a 64-bit unsigned long type. Machines that may not have that could include Windows (even if 64-bit) or 32-bit machines with any OS.\r\n\r\nI'm using a x64 Windows Machine, that's the reason, so. I'll start compiling your `bc` on Linux using WSL.\r\n\r\n> You must know one fact: `(x^y)*(x^z) == x^(y+z)`.\r\n> \r\n> In essence, if you multiply two powers of one number, the effect is the same as adding the exponents of those powers and then calculating the power from the sum of those exponents.\r\n\r\nI knew that, but somehow I didn't think about it. Thanks for the recap.\r\n\r\n> My `bc` will be slower than Julia. I am confident of that.\r\n>\r\n> This is because my `bc` does not use the full range of hardware-native integers in order to use decimal-based math. I bet Julia does use the full range and uses binary-based math.\r\n\r\nWell, that's kinda \"chinese\" to me. I'm not as good as you to understand that low-level programming stuff\r\n\r\n> You may want a benchmark with a smaller exponent.\r\n\r\nI'll.\r\n\r\nOn another topic, I'm using your tool to keep track of my expenses. Before closing this issue, I would like to ask you if there is any way that I can use \"import\" statements in your `bc` ecosystem.\r\n\r\nI know that I can work with more than one file at the same time using the `-f`/`-c` flags, but I'm wondering if is possible to access multiple files by only importing one - and use data from other files inside it.\r\n\r\nMy goal (if that can help you answering it) is to have a single file with functions/operations and then a bunch of other files only with data (expenses and gains) - like a local mini database - that I can call in the \"main\" `bc` file and work with.\r\n",
+ "createdAt": "2023-03-17T14:54:05Z",
+ "includesCreatedEdit": true,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/66#issuecomment-1473965968",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc5X5p-w",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Thank you for your compliment!\r\n\r\nJust so you know, I have [not always had this passion][1]. But I firmly believe that software is meant to serve people. That's why I focus so much on helping others.\r\n\r\nIt makes me really happy that you noticed!\r\n\r\n> I'm using a x64 Windows Machine, that's the reason, so. I'll start compiling your `bc` on Linux using WSL.\r\n\r\nYeah, sorry about that. Windows is dumb in that they decided that the `unsigned long` type should remain 32 bits. It was a stupid decision.\r\n\r\n> Well, that's kinda \"chinese\" to me. I'm not as good as you to understand that low-level programming stuff\r\n\r\nI apologize. I'll try again.\r\n\r\nUnder the hood, `bc` uses 32-bit unsigned integers (on 64-bit) as \"digits\". It turns out that the highest power of 10 that 32 bits can contain is `10^9`, or 1,000,000,000. This is one more than the maximum value that `bc` allows in each 32-bit integer. If it goes higher than this, `bc` will detect that it went higher and add the extra to the next \"digit\". This keeps each digit below 1,000,000,000.\r\n\r\nHowever, that is not optimal. I did that because `bc` needs to have decimal-based math. (In other words, the math needs to be in base 10.) If you can have your math in binary, base 2, it's much cheaper because then you can make your limit `2^32`, which is much higher than `10^9` (4,294,967,296 vs. 1,000,000,000).\r\n\r\nJulia uses base 2. This means that each \"digit\" can store more, which means the numbers are smaller (in number of digits). And smaller numbers mean faster execution.\r\n\r\nI hope that made sense.\r\n\r\n> On another topic, I'm using your tool to keep track of my expenses.\r\n\r\nI admit that's *terrifying* to me. I mean, technically, it should be safe, as long as you don't do any division or modulus in your scripts. But I don't want to screw up your finances with `bc`.\r\n\r\nI hope you remember there's **no warranty**. If there is a bug, I'll try to help, but I can't guarantee that your financial records will be correct in the presence of bugs.\r\n\r\nI *will* guarantee, though, that I know of no bugs. I have fixed all of the ones I am aware of.\r\n\r\n> Before closing this issue, I would like to ask you if there is any way that I can use \"import\" statements in your bc ecosystem.\r\n\r\nSort of.\r\n\r\n> I know that I can work with more than one file at the same time using the `-f`/`-c` flags, but I'm wondering if is possible to access multiple files by only importing one - and use data from other files inside it.\r\n\r\nWell, that's the only way.\r\n\r\nActually, it's even simpler than that. `bc` runs scripts in the order given on the command-line. For example, you could run this:\r\n\r\n```\r\n$ bc funcs.bc database.bc main.bc\r\n```\r\n\r\nAnd `bc` will run `funcs.bc`, then `database.bc`, then `main.bc`.\r\n\r\nThis means that anything in `funcs.bc` can affect the execution of `database.bc`, and anything in `funcs.bc` and `database.bc` can affect the execution of `main.bc`.\r\n\r\nThis means that you can chain together scripts in a way that acts like import. Sort of. If you would only import at the top of a file.\r\n\r\nBasically, this would mean that if `bc` had an `import` statement, the above would be equivalent to putting:\r\n\r\n```\r\nimport funcs.bc\r\nimport database.bc\r\n```\r\n\r\nat the *top* of `main.bc`.\r\n\r\nI do this myself; I have a math research project, and when I get new ideas, I start a new script. But I have all of my common functions in a `lib.bc` script, so I run it like this:\r\n\r\n```\r\n$ bc lib.bc <current_script>\r\n```\r\n\r\nThis even works for global variables; if you set a variable `food_expenses` to say `101.00` at the top level of a script, that variable will equal `101.00` in later scripts.\r\n\r\nSo if I were you, I would have a `funcs.bc` script that defines all of the functions. Then I would have a `database.bc` script that has the current values of all of your expenses. It should look something like this:\r\n\r\n```\r\nfood_expenses = 101.00\r\nentertainment_expenses = 45.50\r\n```\r\n\r\nand so on.\r\n\r\nThen, in the last script, I would have the code to actually update the database, but instead of directly updating, I would have it print the new \"version\" of `database.bc` to `stdout`.\r\n\r\nThen you would run it like this:\r\n\r\n```\r\n$ bc funcs.bc database.bc main.bc > temp.bc\r\n```\r\n\r\nThen check that `temp.bc` is correct. If it is, you can remove `database.bc` and rename `temp.bc` to `database.bc`:\r\n\r\n```\r\n$ rm database.bc\r\n$ mv temp.bc database.bc\r\n```\r\n\r\nTo make the example more concrete, let's assume that you have a `funcs.bc` that looks like this:\r\n\r\n```\r\ndefine void add_to_food_expenses(expense) {\r\n # If you don't put `food_expenses` in an `auto` statement,\r\n # then the global variable will be affected.\r\n food_expenses += expense\r\n # The global `total_expenses` is also affected with an `auto`.\r\n total_expenses += expense\r\n}\r\n\r\ndefine void print_new_database() {\r\n print \"food_expenses = \", food_expenses, \"\\n\"\r\n print \"total_expenses = \", total_expenses, \"\\n\"\r\n}\r\n```\r\n\r\nThen perhaps you have a `database.bc` that looks like this:\r\n\r\n```\r\nfood_expenses = 101.00\r\ntotal_expenses = 404.40\r\n```\r\n\r\nThen you have a `main.bc` that looks like this:\r\n\r\n```\r\n# read() reads from stdin.\r\nexpense = read()\r\n\r\nadd_to_food_expenses(expense)\r\n\r\nprint_new_database()\r\n```\r\n\r\nThen running the following:\r\n\r\n```\r\n$ echo \"35.75\" | bc funcs.bc database.bc main.bc\r\n```\r\n\r\nwill print:\r\n\r\n```\r\nfood_expenses = 136.75\r\ntotal_expenses = 440.15\r\n```\r\n\r\nwhich if you redirect to `temp.bc`:\r\n\r\n```\r\n$ echo \"35.75\" | bc funcs.bc database.bc main.bc > temp.bc\r\n```\r\n\r\nwill make `temp.bc` a valid replacement for `database.bc`.\r\n\r\n(While it may be safe to redirect straight to `database.bc`, I would not suggest doing so. If `funcs.bc` has any print statements that run, `database.bc` will be overwritten before it is ever read.)\r\n\r\n> My goal (if that can help you answering it) is to have a single file with functions/operations and then a bunch of other files only with data (expenses and gains) - like a local mini database - that I can call in the \"main\" `bc` file and work with.\r\n\r\nI *think* I answered according to your goal. I hope that helps, but please come back with any questions you have.\r\n\r\n[1]: https://gavinhoward.com/2021/12/is-it-even-worth-working-on-foss-anymore/",
+ "createdAt": "2023-03-18T05:48:18Z",
+ "includesCreatedEdit": true,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/66#issuecomment-1474731952",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5YHLzP",
+ "author": {
+ "login": "STSMHQ"
+ },
+ "authorAssociation": "NONE",
+ "body": "Hi. Sorry for the delay replying.\r\n\r\n> It makes me really happy that you noticed!\r\n\r\nOf course. I'm lacking motivation for programming - even in my university project (I'm currently in my last year of my master degree) -, specially now with all the improvements made in the IA field, but reading your blog posts and messages like this one, somehow, gives me motivation to continue.\r\n\r\nI would like to suggest, if possible, a way to interact with you in your blog (like a comments section, for example). \r\n\r\n> I hope that made sense.\r\n\r\nIt did. I studied it in my first year at university, but I didn't know that specifically, so thank you.\r\n\r\n> I hope you remember there's **no warranty**. If there is a bug, I'll try to help, but I can't guarantee that your financial records will be correct in the presence of bugs.\r\n> \r\n> I *will* guarantee, though, that I know of no bugs. I have fixed all of the ones I am aware of.\r\n\r\nAnd that's all that matters for me. There is nothing that compares to the support that you offer.\r\n\r\n> I think I answered according to your goal. I hope that helps, but please come back with any questions you have.\r\n\r\nYou did. Thanks again for the time spent on this. I use a different set of files, but the information that you gave me is going to help me improve my system. So, thanks (one more time xD).",
+ "createdAt": "2023-03-21T17:06:49Z",
+ "includesCreatedEdit": true,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/66#issuecomment-1478278351",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc5YIXfA",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "> Sorry for the delay replying.\r\n\r\nNo worries!\r\n\r\n> Of course. I'm lacking motivation for programming - even in my university project (I'm currently in my last year of my master degree)\r\n\r\nOh, cool, what is your project? (I dropped out of a Master's; I'm kind of jealous.)\r\n\r\n> I would like to suggest, if possible, a way to interact with you in your blog (like a comments section, for example).\r\n\r\nI have thought about this many times and have always rejected the idea due to the amount of spam-busting and moderation required, at least as told by other bloggers. Oh, and I don't want people to see a buggy site if they have JavaScript turned off as comments nearly always require JavaScript.\r\n\r\nHowever, your suggestion has made me look at it again, and I had an idea.\r\n\r\nOne of the best pieces of software, at least for forums, is the software underpinning <https://lobste.rs/>. While I have been banned from that site, I appreciate the software for its moderation capabilities. This includes invite-only registration, which would help a lot to only allow people who want to interact with me and others in a good way.\r\n\r\nI've also always wanted to run a forum site where Free Speech was honored as much as possible because [I think Free Speech is necessary to find the truth][1].\r\n\r\nSo in response to your suggestion, if the lobste.rs software will work for my purposes, I'll spin up an instance at <https://forum.gavinhoward.com/> (or something similar), and I'll send you an invite, along with anyone else whose opinions I trust. And every time I post something new, I'll put it on the instance and link to it from the post. That should allow me to have a \"comments section\" while not interfering with my current site.\r\n\r\nI think I'll also allow others to post stuff too. Maybe I'll make its theme to be anything that is intellectually stimulating. I don't know; I'm just spitballing here.\r\n\r\n> I studied it in my first year at university, but I didn't know that specifically, so thank you.\r\n\r\nYou're welcome!\r\n\r\n> And that's all that matters for me.\r\n\r\nThank goodness!\r\n\r\n> There is nothing that compares to the support that you offer.\r\n\r\nYou're welcome! And thank you. I am overjoyed to hear this, actually.\r\n\r\nI'm starting a business right now, and my product is support for Open Source software that I have written. I was wondering if that would be a good product, but if my support is good, it might work. So I'm *thrilled* that you think so!\r\n\r\n(Don't worry; I'll always support `bc` for free; it's my gift to the world. The software that I'll be supporting for pay is at <https://git.yzena.com/Yzena/Yc>.)\r\n\r\nFull disclosure: this is also why I took your suggestion for a comments section more seriously; it would be a good way for me to network with potential clients. I apologize for having less than perfect motives.\r\n\r\nAlso, to take it one step further, can I point to this interaction we had as an example of the support I will give? I think it would be great material to advertise with. No offense if you don't want that though; I know that's a selfish motive.\r\n\r\n> You did. Thanks again for the time spent on this. I use a different set of files, but the information that you gave me is going to help me improve my system. So, thanks (one more time xD).\r\n\r\nYou're welcome! Glad I could help!\r\n\r\n[1]: https://gavinhoward.com/2019/11/recommendations-and-radicalization/",
+ "createdAt": "2023-03-21T21:13:49Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/66#issuecomment-1478588352",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5YLZeg",
+ "author": {
+ "login": "STSMHQ"
+ },
+ "authorAssociation": "NONE",
+ "body": "> Oh, cool, what is your project? (I dropped out of a Master's; I'm kind of jealous.)\r\n\r\nYou don't need to be. You seem to have the required knowledge to be a teacher on my university. I'm from Portugal, btw.\r\n\r\nMy project is about telemetry. I'm building a telemetry solution using OpenTelemetry and multiple analysis backends for a health company. The problem is that such project is inside DevOps field and that's not my expertice or even the field that I want to work on. I want to be a programmer, a system programmer to be more precise.\r\n\r\n> This includes invite-only registration, which would help a lot to only allow people who want to interact with me and others in a good way.\r\n\r\nThat seems a good way to implement it. I didn't know about that specific website, but after a brief usage of it, I can say that I liked the way that it works.\r\n\r\n> So in response to your suggestion, if the lobste.rs software will work for my purposes, I'll spin up an instance at https://forum.gavinhoward.com/ (or something similar), and I'll send you an invite, along with anyone else whose opinions I trust. And every time I post something new, I'll put it on the instance and link to it from the post. That should allow me to have a \"comments section\" while not interfering with my current site.\r\n\r\nThat makes a lot of sense. I appreciate the consideration for my opinion xD\r\n\r\nWhen that instance is online, let me know, please.\r\n\r\n> I'm starting a business right now, and my product is support for Open Source software that I have written. I was wondering if that would be a good product, but if my support is good, it might work. So I'm thrilled that you think so!\r\n\r\nI see.. something like the commercial support that Daniel Stenberg offers for cURL?\r\n\r\n> (Don't worry; I'll always support bc for free; it's my gift to the world. The software that I'll be supporting for pay is at https://git.yzena.com/Yzena/Yc.)\r\n\r\nThat's nice to read. I'm planning to use `bc` for a very long time :d\r\n\r\n> Full disclosure: this is also why I took your suggestion for a comments section more seriously; it would be a good way for me to network with potential clients. I apologize for having less than perfect motives.\r\n\r\nIt wasn't necessary to make that disclosure. You're too kind just for considering having to do it. \r\n\r\n> Also, to take it one step further, can I point to this interaction we had as an example of the support I will give? I think it would be great material to advertise with. No offense if you don't want that though; I know that's a selfish motive.\r\n\r\nOf course you can. I'm flattered to be part of that.\r\n\r\n> You're welcome! Glad I could help!\r\n\r\nYou always do. Thanks (one more time)!",
+ "createdAt": "2023-03-22T11:20:57Z",
+ "includesCreatedEdit": true,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/66#issuecomment-1479382944",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc5YagqG",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "> The problem is that such project is inside DevOps field and that's not my expertice or even the field that I want to work on. I want to be a programmer, a system programmer to be more precise.\r\n\r\nOuch. I feel for you, and I feel lucky.\r\n\r\nI know this may not help, but I have personally found that my system programming skills (dev) got better after I learned system admininstration (ops).\r\n\r\nSystem administration is important to me for another reason: without it, I can't host my code or the forum that I mentioned.\r\n\r\nOnce you get your Master's, maybe you can try to do what I have done: build excellent system software and support it. That may give you the room you need to make it a career. If it is going to work for me, it will surely work for you too!\r\n\r\n> When that instance is online, let me know, please.\r\n\r\nIt's up at <https://forum.gavinhoward.com/>, but email is not working, so I need to wait. Please send me an email using the email listed at <https://gavinhoward.com/contact/>, and I'll send you an invite once email is working.\r\n\r\n> I see.. something like the commercial support that Daniel Stenberg offers for cURL?\r\n\r\nYes, basically.\r\n\r\n> Of course you can. I'm flattered to be part of that.\r\n\r\nThank you!",
+ "createdAt": "2023-03-24T20:02:20Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/66#issuecomment-1483344518",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5YtkJ8",
+ "author": {
+ "login": "STSMHQ"
+ },
+ "authorAssociation": "NONE",
+ "body": "Sorry for the delay replying (again).\r\n\r\n> I know this may not help, but I have personally found that my system programming skills (dev) got better after I learned system admininstration (ops).\r\n\r\nI hope you're right.\r\n\r\n> Once you get your Master's, maybe you can try to do what I have done: build excellent system software and support it. That may give you the room you need to make it a career. If it is going to work for me, it will surely work for you too!\r\n\r\nI'm trying to learn Rust on my free time, but it's been hard - I know your opinion about Rust xD -, let's see. \r\n\r\n> It's up at https://forum.gavinhoward.com/, but email is not working, so I need to wait. Please send me an email using the email listed at https://gavinhoward.com/contact/, and I'll send you an invite once email is working.\r\n\r\nThe [forum](https://forum.gavinhoward.com/) returns a 500 error code to me and the [contact page](https://gavinhoward.com/contact/) shows me that your e-mail is `<my_first_name>@<this_website>`. I'll send an email to `<redacted>` and hope it works.\r\n\r\n> Yes, basically.\r\n\r\nI see. Well, good luck for your new project. I'm pretty sure that people will love your support as much as I do.",
+ "createdAt": "2023-03-29T10:27:50Z",
+ "includesCreatedEdit": true,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/66#issuecomment-1488339580",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc5YwWgF",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "> I'm trying to learn Rust on my free time, but it's been hard - I know your opinion about Rust xD -, let's see.\r\n\r\nRemember that my Rust opinion is personal; Rust is pretty good in general.\r\n\r\n> The [forum](https://forum.gavinhoward.com/) returns a 500 error code to me and the [contact page](https://gavinhoward.com/contact/) shows me that your e-mail is `<my_first_name>@<this_website>`.\r\n\r\nI'm having trouble with the forum. In fact, it turns out that sending email just doesn't work. My email provider is blaming the lobste.rs software, but the software has the right settings, so...\r\n\r\nI'm probably just going to have to write my own forum software. Sorry for the delay. It may be a year or so.\r\n\r\n> I'll send an email to `<redacted>` and hope it works.\r\n\r\nThat is the correct address. I've redacted it because I don't want it machine-readable (to avoid spam), but it is correct.\r\n\r\n> I see. Well, good luck for your new project. I'm pretty sure that people will love your support as much as I do.\r\n\r\nThank you!",
+ "createdAt": "2023-03-29T18:07:27Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/66#issuecomment-1489070085",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5Y0Yev",
+ "author": {
+ "login": "STSMHQ"
+ },
+ "authorAssociation": "NONE",
+ "body": "> Remember that my Rust opinion is personal; Rust is pretty good in general.\r\n\r\nI hope it is here to stay for the long run.\r\n\r\n> I'm probably just going to have to write my own forum software. Sorry for the delay. It may be a year or so.\r\n\r\nNo problem. I'll put a reminder on my calendar, so. See you in a year (or so). Have a nice week!",
+ "createdAt": "2023-03-30T11:18:53Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/66#issuecomment-1490126767",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc5kIVVh",
+ "author": {
+ "login": "TediusTimmy"
+ },
+ "authorAssociation": "NONE",
+ "body": "Hey, I realize that you asked this five months ago, but I just saw this and I wanted to chime in, because I love REALLLY BIG NUMBERS.\r\n\r\nNow, Gavin noted that `(x^y)*(x^z) == x^(y+z)`. It is also true that `(x^y)^z == x^(y*z)`. So, we can simplify your question to `169287^1670801140084418360`. Hoo, boy is that a big number! How big is it? `2.4917161343640611*10^8735990286585912792` The number that describes how many digits are in the number has 19 digits in it.\r\n\r\nHow did I do this (and what is Julia probably doing under the covers)? The same way a slide rule works: logarithms. It is definitionally true that `a^u=e^(ln(a^u))`. So `a^u=e(u*ln(a))`. But this works in any base, so I'll choose base 10 (I hope you remember your laws of logarithms).\r\n\r\nTo follow along:\r\n```\r\nscale=40\r\n137*920*13256118217109*l(169287)/l(10)\r\n```\r\nThat should give you that exponent. Remember: the number we want is ten raised to this power. To get the leading digits, we extract the mantissa (the fractional portion of a logarithm), and then raise 10 to this power. This works because of what Gavin noted: we can separate `3.2` into `3+.2`, and we are raising ten to this power, so it becomes `(10^3)*(10^.2)`:\r\n```\r\ne(.3964985643509537948664437764527360339575*l(10))\r\n```\r\n\r\nI want to point out two things: firstly, `bc` is going to compute all of the digits of this number; secondly, it is a big number. If we pack 19 decimal digits into every 8 bytes, it will still take over 3,000 petabytes of memory to store this number. To give you a sense of scale: at the end of 2021, Internet Archive had 212 petabytes of data archived.",
+ "createdAt": "2023-08-16T03:29:40Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [
+ {
+ "content": "THUMBS_UP",
+ "users": {
+ "totalCount": 1
+ }
+ }
+ ],
+ "url": "https://github.com/gavinhoward/bc/issues/66#issuecomment-1679906145",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc5kl8FL",
+ "author": {
+ "login": "STSMHQ"
+ },
+ "authorAssociation": "NONE",
+ "body": "Hi, @TediusTimmy. Thank you for your explanation. It helped a lot. I didn't know some of that mathematic equivalences. Have a nice week.",
+ "createdAt": "2023-08-22T07:56:38Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/66#issuecomment-1687667019",
+ "viewerDidAuthor": false
+ }
+ ],
+ "createdAt": "2023-03-16T12:20:42Z",
+ "id": "I_kwDOCL0xJc5g_6ME",
+ "isPinned": false,
+ "labels": [],
+ "milestone": null,
+ "number": 66,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "state": "CLOSED",
+ "stateReason": "COMPLETED",
+ "title": "[QUESTION] Bypass Math Overflow for Large Computations",
+ "updatedAt": "2023-08-22T07:56:38Z",
+ "url": "https://github.com/gavinhoward/bc/issues/66"
+ },
+ {
+ "assignees": [],
+ "author": {
+ "id": "U_kgDOB5Gq1Q",
+ "is_bot": false,
+ "login": "ikolesnikes",
+ "name": ""
+ },
+ "body": "bin/bc enters an infinite loop while evaluating the cube-root of 0.01\r\n\r\nbin/bc -l -e 'cbrt(0.1)'\r\nreturns .46415888336127788924 (which is expected)\r\n\r\nbin/bc -l -e 'cbrt(0.01)'\r\nfreezes\r\n\r\nLooking at the definition of root(x,n) in lib2.bc the following loop never exits\r\n\r\n\twhile(r!=q){\r\n\t\tr=q\r\n\t\tq=(p*r+x/r^p)/n\r\n\t}\r\n\r\nA simple hack makes the cbrt function working.\r\n\r\n\twhile(abs(r-q)>0.000001){\r\n\t\tr=q\r\n\t\tq=(p*r+x/r^p)/n\r\n\t}",
+ "closed": true,
+ "closedAt": "2023-03-15T21:56:14Z",
+ "comments": [
+ {
+ "id": "IC_kwDOCL0xJc5XXKFy",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Yes, this is a bug. I have reproduced it.\r\n\r\nYour fix pointed right to the problem, which I suspected: it ran into a pathological case where the two values, `r` and `q`, never equaled each other because they would keep switching, i.e., `r` would equal `x`, and `q` would equal `y`, then on the next iteration, `r` would equal `y`, and `q` would equal `x`.\r\n\r\nThis is the reason I hate algorithms that are calculated by series. They can easily have problems like this.\r\n\r\nI tried to fix it with 01230fcd9cfb547c5666247e1a36c624d3f2d01c. It increases the precision, but then does the comparison at a smaller precision.\r\n\r\nCan you pull and test it for me? I'll start my release process in the meantime.",
+ "createdAt": "2023-03-13T08:13:31Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/64#issuecomment-1465688434",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5XYwKM",
+ "author": {
+ "login": "ikolesnikes"
+ },
+ "authorAssociation": "NONE",
+ "body": "I tested your solution and it worked!\r\nMaybe it's worth to add a test for this case.\r\n\r\nNever mind, you already did it.",
+ "createdAt": "2023-03-13T13:03:47Z",
+ "includesCreatedEdit": true,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/64#issuecomment-1466106508",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc5XZaRe",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Yes, but you are right that if I did not, I should have!\r\n\r\nI will put out a release with the fix as soon as I can. I'll close this issue at that time.",
+ "createdAt": "2023-03-13T14:45:27Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/64#issuecomment-1466279006",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5XrBvX",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Release `6.5.0` is out! Thank you for the report!",
+ "createdAt": "2023-03-15T21:56:14Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/64#issuecomment-1470897111",
+ "viewerDidAuthor": true
+ }
+ ],
+ "createdAt": "2023-03-13T06:01:54Z",
+ "id": "I_kwDOCL0xJc5gm5mO",
+ "isPinned": false,
+ "labels": [],
+ "milestone": null,
+ "number": 64,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "state": "CLOSED",
+ "stateReason": "COMPLETED",
+ "title": "bc freezes evaluating cbrt(0.01)",
+ "updatedAt": "2023-03-15T21:56:14Z",
+ "url": "https://github.com/gavinhoward/bc/issues/64"
+ },
+ {
+ "assignees": [],
+ "author": {
+ "id": "MDQ6VXNlcjE0ODEyNjg=",
+ "is_bot": false,
+ "login": "pg83",
+ "name": "Anton Samokhvalov"
+ },
+ "body": "https://github.com/gavinhoward/bc/blob/master/scripts/safe-install.sh#L28 creates hard dependency loop on previously available bc for install process, which can be not available:\r\n\r\n* in bootstrap environments\r\n* in cross-compile environments\r\n",
+ "closed": true,
+ "closedAt": "2023-02-25T15:11:27Z",
+ "comments": [
+ {
+ "id": "IC_kwDOCL0xJc5WEExO",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Whoops. I did not think of that. My bad.\r\n\r\nThis should be fixed in 313738df7f6d65875dd91ef63565227d0f782472 and 349cd801d5a7f4ab2fad76096d271397aa738063. I put out `6.3.1` because I know for a fact that Linux from Scratch will need these changes too, and they usually just grab the latest release.\r\n\r\nI hope this new release works for you. Please close this issue if it does.",
+ "createdAt": "2023-02-24T16:05:13Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [
+ {
+ "content": "THUMBS_UP",
+ "users": {
+ "totalCount": 1
+ }
+ }
+ ],
+ "url": "https://github.com/gavinhoward/bc/issues/63#issuecomment-1443908686",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5WIxOt",
+ "author": {
+ "login": "pg83"
+ },
+ "authorAssociation": "NONE",
+ "body": "thanx!",
+ "createdAt": "2023-02-25T15:11:27Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/63#issuecomment-1445139373",
+ "viewerDidAuthor": false
+ }
+ ],
+ "createdAt": "2023-02-24T11:34:48Z",
+ "id": "I_kwDOCL0xJc5fRqQO",
+ "isPinned": false,
+ "labels": [],
+ "milestone": null,
+ "number": 63,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "state": "CLOSED",
+ "stateReason": "COMPLETED",
+ "title": "bc -> bc dependency loop",
+ "updatedAt": "2023-02-25T15:11:28Z",
+ "url": "https://github.com/gavinhoward/bc/issues/63"
+ },
+ {
+ "assignees": [],
+ "author": {
+ "id": "MDQ6VXNlcjUzMTI5ODE2",
+ "is_bot": false,
+ "login": "enh-google",
+ "name": ""
+ },
+ "body": "trying to update AOSP to 6.2.4 (https://android-review.googlesource.com/c/platform/external/bc/+/2433072) i hit:\r\n```\r\ntests/: 413 files pushed, 0 skipped. 6.5 MB/s (1197549 bytes in 0.175s)\r\nscripts/functions.sh: 1 file pushed, 0 skipped. 60.6 MB/s (14190 bytes in 0.000s)\r\n***********************************************************************\r\n\r\nRunning bc tests...\r\n\r\nRunning bc decimal...pass\r\nSkipping bc print test\r\nSkipping bc parse test\r\nSkipping bc lib2\r\nRunning bc print2...pass\r\nRunning bc length...pass\r\nRunning bc scale...pass\r\nSkipping bc shift\r\nRunning bc add...pass\r\nRunning bc subtract...pass\r\nRunning bc multiply...pass\r\nRunning bc divide...pass\r\nRunning bc modulus...pass\r\nRunning bc power...pass\r\nRunning bc sqrt...pass\r\nSkipping bc trunc\r\nSkipping bc places\r\nRunning bc vars...pass\r\nRunning bc boolean...pass\r\nRunning bc comp...pass\r\nRunning bc abs...pass\r\nRunning bc assignments...pass\r\nRunning bc functions...pass\r\nSkipping bc scientific\r\nSkipping bc engineering\r\nRunning bc globals...pass\r\nRunning bc strings...pass\r\nSkipping bc strings2 test\r\nRunning bc letters...pass\r\nRunning bc exponent...pass\r\nRunning bc log...pass\r\nRunning bc pi...pass\r\nRunning bc arctangent...pass\r\nRunning bc sine...pass\r\nRunning bc cosine...pass\r\nSkipping bc bessel test\r\nRunning bc arrays...pass\r\nRunning bc misc...pass\r\nRunning bc misc1...pass\r\nRunning bc misc2...pass\r\nRunning bc misc3...pass\r\nRunning bc misc4...pass\r\nRunning bc misc5...pass\r\nRunning bc misc6...pass\r\nRunning bc misc7...pass\r\nRunning bc misc8...pass\r\nRunning bc void...pass\r\nSkipping bc rand\r\nRunning bc recursive_arrays...pass\r\nRunning bc divmod...pass\r\nRunning bc modexp...pass\r\nSkipping bc bitfuncs\r\nSkipping bc leadingzero\r\nRunning bc is_number...pass\r\nRunning bc is_string...pass\r\nRunning bc asciify_array...pass\r\nRunning bc line_by_line1...pass\r\nRunning bc line_by_line2...pass\r\nRunning bc line_loop_quit1...pass\r\nRunning bc line_loop_quit2...pass\r\nRunning bc stdin tests...pass\r\n/data/local/tmp/bc-tests/tests/scripts.sh[66]: check_d_arg: inaccessible or not found\r\n\r\nExit Code: 127\r\n```\r\nlooking at scripts.sh, it doesn't source functions.sh where the preceding scripts do. i'm not sure why that works with bash? i didn't think bash exported functions by default, but maybe it's doing so?\r\n\r\nanyway, i tested the obvious fix (https://android-review.googlesource.com/c/platform/external/bc/+/2433072/2/tests/scripts.sh).",
+ "closed": true,
+ "closedAt": "2023-02-15T16:15:07Z",
+ "comments": [
+ {
+ "id": "IC_kwDOCL0xJc5VLKwA",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Whoops. That's a bug.\r\n\r\nI tried clicking your links, but they complained that I wasn't signed in our didn't have permission.\r\n\r\nI presume your obvious fix was to source `functions.sh`? I'll do that in my repo when I get home. Do you want a tagged release?",
+ "createdAt": "2023-02-14T01:55:30Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/62#issuecomment-1428990976",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5VLMMt",
+ "author": {
+ "login": "enh-google"
+ },
+ "authorAssociation": "NONE",
+ "body": "> I tried clicking your links, but they complained that I wasn't signed in our didn't have permission.\r\n\r\ngah, don't get me started ... the powers that be inflicted a stupid url rewriter on us recently that mean we can't give working urls to anyone. i'd tried to manually fix these, but didn't test them. fixed now (retconned above).\r\n\r\n> I presume your obvious fix was to source `functions.sh`?\r\n\r\nexactly.\r\n\r\n> I'll do that in my repo when I get home. Do you want a tagged release?\r\n\r\nyes please... our tooling isn't yet clever enough to get us back on to the next tagged release if we switch to a sha, so until we implement that, it's quite a bit more convenient if there's another tag. (especially if i happen to go under a bus :-) )",
+ "createdAt": "2023-02-14T02:02:08Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/62#issuecomment-1428996909",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc5VLqu1",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Fixed in 714782c613ec2a4570f63c1285d38785961edb89, which should also preemptively fix any other occurrences of this same bug.\r\n\r\nI'm running a basic release process to ensure that the release still builds and tests, but I should have a tag out tomorrow morning (US time).",
+ "createdAt": "2023-02-14T05:04:50Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/62#issuecomment-1429121973",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5VP1W1",
+ "author": {
+ "login": "enh-google"
+ },
+ "authorAssociation": "NONE",
+ "body": "> I'm running a basic release process to ensure that the release still builds and tests, but I should have a tag out tomorrow morning (US time).\r\n\r\nthanks! no hurry...",
+ "createdAt": "2023-02-14T18:46:00Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/62#issuecomment-1430214069",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc5VQreo",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "It's out! Because I haven't been able to get an Android environment going yet, I'm still not sure this will work for you. If it does, though, please close this issue. Otherwise, I'll try something else.",
+ "createdAt": "2023-02-14T22:04:15Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/62#issuecomment-1430435752",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5VRBCe",
+ "author": {
+ "login": "enh-google"
+ },
+ "authorAssociation": "NONE",
+ "body": "thanks... testing now: https://android-review.googlesource.com/c/platform/external/bc/+/2436773",
+ "createdAt": "2023-02-14T23:24:21Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/62#issuecomment-1430524062",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc5VVNiI",
+ "author": {
+ "login": "enh-google"
+ },
+ "authorAssociation": "NONE",
+ "body": "merged! thanks for your help :-)",
+ "createdAt": "2023-02-15T16:15:07Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/62#issuecomment-1431623816",
+ "viewerDidAuthor": false
+ }
+ ],
+ "createdAt": "2023-02-14T00:32:53Z",
+ "id": "I_kwDOCL0xJc5eXxKU",
+ "isPinned": false,
+ "labels": [],
+ "milestone": null,
+ "number": 62,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "state": "CLOSED",
+ "stateReason": "COMPLETED",
+ "title": "another mksh/bash difference?",
+ "updatedAt": "2023-02-15T16:15:07Z",
+ "url": "https://github.com/gavinhoward/bc/issues/62"
+ },
+ {
+ "assignees": [],
+ "author": {
+ "id": "MDQ6VXNlcjUzMTI5ODE2",
+ "is_bot": false,
+ "login": "enh-google",
+ "name": ""
+ },
+ "body": "i haven't been able to update for some time (5.0.1 was our last update!) because the tests fail in a weird way without much useful information. i've included the current failures for 6.2.2 below, for example.\r\n\r\nmy guess is that this is something to do with Android using mksh as its /bin/sh. the interleaved output makes it appear like there might be some disagreement about job handling between bash and mksh?\r\n\r\nare the current bc tests known to work on shells other than bash (and in particular mksh)? any ideas where i should look (or some kind of parallelism i can just disable)?\r\n```\r\n~/aosp-master-with-phones/external/bc$ ./run-bc-tests-on-android.sh \r\ntests/: 413 files pushed, 0 skipped. 0.8 MB/s (1191155 bytes in 1.372s)\r\nscripts/functions.sh: 1 file pushed, 0 skipped. 75.4 MB/s (12065 bytes in 0.000s)\r\n***********************************************************************\r\n\r\nRunning bc tests...\r\n\r\nSkipping bc lib2\r\nSkipping bc shift\r\nSkipping bc trunc\r\nSkipping bc places\r\nSkipping bc print test\r\nSkRunning bc decimal...Running bc print2...ipping bc parse test\r\nRunning bc scale...Skipping bc scientific\r\nRunning bc length...Running bc sqrt...Running bc modulus...RuRunning bc add...nning bc divide...Running bc subtract...Skipping bc engineering\r\nRunning bc multiply...Running bc power...Running bc abs...Running bc functions...pass\r\nRunning bc boolean...Running bc comp...Running bc assignments...Running bc vars...pass\r\nSkipping bc rand\r\npass\r\nSRunning bc globals...kipping bc bitfuncs\r\npass\r\nRunning bc strings...pass\r\nRunning bc cosine...pass\r\nSkipping bc strings2 test\r\nSkipping bc bessel test\r\npass\r\nRunning bc sine...Running bc misc2...RRunning bc exponent...pRunning bc log...Running bc misc8...Running bc misc...Running bc misc4...Running bc pi...upass\r\nSkipping bc leadingzero\r\nnning bc misc1...ppasRunning bc arrays...asss\r\n\r\nass\r\nRunning bc arctangent...Running bc misc6...Running bc modexp...Running bc void...Running bc misc3...Running bc letters...pass\r\npass\r\npRunning bc misc7...ass\r\npass\r\npass\r\nRunning bc divmod...Running bc recursive_arrays...Running bc misc5...pass\r\npass\r\npass\r\npass\r\npass\r\npass\r\npass\r\nppass\r\nass\r\nRunning bc is_number...pass\r\npass\r\npass\r\npass\r\nRunning bc asciify_array...Running bc line_loop_quit1...pass\r\nppass\r\npass\r\npass\r\npass\r\npass\r\nass\r\nRunning bc is_string...Running bc line_by_line1...Running bc line_by_line2...Running bc line_loop_quit2...Running bc command-line error tests...Running bc stdin tests...pass\r\nRunning bc error file 01.txt with clamping...pass\r\npass\r\npass\r\nRunning bc read...Running bc error file 02.txt with clamping...Running bc error file 03.txt with clamping...pass\r\npass\r\npass\r\npass\r\npass\r\npass\r\npass\r\npass\r\nRunning bc error file 01.txt without clamping...Running bc error file 04.txt with clamping...ppass\r\nSkipping bc script multiply.bc\r\npass\r\nass\r\nRunning bc error file 02.txt without clamping...pass\r\nRunning bc error file 05.txt with clamping...Skipping bc script divide.bc\r\nRunning bc error file 03.txt without clamping...Skipping bc script subtract.bc\r\npass\r\nRunning bc errors...Running bc read errors...pass\r\nSkipping bc script add.bc\r\nRunning bc error file 06.txt with clamping...Running bc error file 04.txt without clamping...pRunning bc error file 07.txt with clamping...ass\r\nRunning bc error file 01.txt through cat with clamping...pass\r\nRunning bc error file 08.txt with clamping...pass\r\nSkipping bc script print.bc\r\nRunning bc error file 02.txt through cat with clamping...Running bc error file 03.txt through cat with clamping...pass\r\npass\r\npass\r\nRunning bc error file 05.txt without clamping...pass\r\npass\r\nRunning bc error file 09.txt with clamping...pass\r\nRunning bc error file 07.txt without clamping...pass\r\nSkipping bc script parse.bc\r\nRunRunning bc error file 04.txt through cat wnith clamping...ing bc error file 06.txt without clamping...pass\r\nRunning bc error file 10.txt with clamping...RRunning bc empty read...Running bc error file 01.txt through cat without clamping...unning bc script array.bc...RRunning bc error file 03.txt through cat without clamping...pass\r\npaRunning bc error file 08.txt without clamping...Running bc error file 11.txt with clamping...pss\r\nunning bc script array2.bc...ass\r\npass\r\npass\r\nRunning bc error file 02.txt through cat without clamping...pass\r\npass\r\nRunning bc script atan.bc...Running bc error file 09.txt without clamping...Running bc error file 05.txt through cat with clamping...pass\r\npass\r\nRunning bc error file 10.txt without clamping...Running bc error file 06.txt through cat with clamping...Running bc error file 12.txt with clamping...RunRunning bc error file 04.txt through cat without clamping...pass\r\nSkipping bc script bessel.bc\r\npass\r\nning bc error file 07.txt through cat with clamping...Running bc script functions.bc...pass\r\nRunning bc error file 11.txt without clamping...Running bc error file 13.txt with clamping...pass\r\npass\r\nRunning bc error file 08.txt through cat with clamping...pass\r\npass\r\nRunning bc error file 14.txt with clamping...pass\r\nRunning bc read EOF...pass\r\nRunning bc error file 09.txt through cat with clamping...pass\r\npass\r\npass\r\nRunning bc error file 05.txt through cat without clamping...pass\r\npRunning bc script globals.bc...pass\r\nass\r\nRunning bc error file 15.txt with clamping...Skipping bc script: rand.bc\r\nRRunning Rbc error ufninlien g10.txt through cbca ts cwriitpht len.bc...clamping...pass\r\nunning bc error file 07.txt through cat without clamping...pass\r\nRRunning bc error file 06.txt through cat without clamping...pass\r\nRunning bc error file 11.txt through cat with clamping...unning bc error file 12.txt without clamping...pass\r\nRunning bc error file 08.txt through cat without clamping...pass\r\nRunning bc error file 16.txt with clamping...pass\r\nRunning bc error file 14.txt without clamping...pass\r\nRunning bc error file 13.txt without clamping...pass\r\nRunning bc script references.bc...Running bc error file 17.txt with clamping...Running bc error file 09.txt through cat without clamping...pass\r\npass\r\npass\r\npass\r\npass\r\npass\r\nRuRunning bc error file 15.txt without clamping...nning bc error file 18.txt with clamping...Running bc script screen.bc...pass\r\nRpass\r\npapass\r\nunning bc error file 12.txt through cat with clamping...ss\r\nRunning bc error file 11.txt through cat without clamping...Rpass\r\nunning bc error file 10.txt through cat without clamping...Running bc error file 19.txt with clamping...Skipping bc script strings2.bc\r\npass\r\nRunning bc error file 13.txt through cat with clamping...pass\r\nRunning bc error file 16.txt without clamping...pass\r\nRunning bc error file 14.txt through cat with clamping...Running bc error file 20.txt with clamping...pass\r\nRunning bc error file 17.txt without clamping...pass\r\npass\r\nRunning bc error file 21.txt with clamping...paspass\r\nRunning bc script ifs.bc...s\r\nRunning bc error file 18.txt without clamping...pass\r\nRunning bc error file 15.txt through cat with clamping...pass\r\nRunning bc script ifs2.bc...Rpass\r\npass\r\nRunning bc error file 12.txt through cat without clamping...pass\r\nunning bc error file 19.txt without clamping...Running bc erpass\r\nror file 22.txt with clamping...Running bc error file 13.txt through cat without clamping...Rpass\r\npRunning bc error file 20.txt without clamping...pass\r\nass\r\nRuRRuunning bc error file 14.txt through cat without clamping...nnning bc script afl1.bc...unning bc error file 23.txt with clning bcamp einrrg..or. file 16.txt through cat with clamping...pass\r\npass\r\nRunning bc error file 17.txt through cat with clamping...Running bc error filepass\r\npass\r\npass\r\nRunniRunning bc error file 15.txt through cat without clamping... 21.txtng bc error file 24.txt with clamping... without clamping...Running bc error file 18.txt through cat with clamping...pass\r\npass\r\npass\r\npass\r\npass\r\nRunning bc error file 25.txt with clamping...pass\r\nRunning bc error file 22.txt without clamping...Running bc error file 19.txt through cat with clamping...pass\r\nRunning bc error file 16.txt through cat without clamping...Running bc error file 23.txt without clamping...pass\r\npass\r\npass\r\nRunning bc error file 20.txt through cat with clamping...pass\r\nRunning bc error file 26.txt with clamping...Running bc error file 17.txt through cat without clamping...Running bc error file 24.txt without clamping...Running bc error file 27.txt with clamping...pass\r\nRunning bc error file 18.txt through cat without clamping...pass\r\npass\r\npass\r\nRunning bc error file 21.txt through cat with clamping...pass\r\nRunning bc error file 19.txt through cat without clamping...pass\r\nRunning bc error file 22.txt through cat with clamping...pass\r\nRunning bc error file 25.txt without clamping...Running bc error file 28.txt with clamping...pass\r\npass\r\npass\r\npass\r\npass\r\nRunning bc error file 23.txt through cat with clamping...Running bc error file 20.txt through cat without clamping...RuRunning bc error file 29.txt with clamping...Running bc error file 24.txt through cat with clamping...pass\r\nnning bc error file 26.txt without clamping...pass\r\nRunning bc error file 27.txt without clamping...pass\r\npass\r\npass\r\npass\r\nRunning bc error file 22.txt through cat without clamping...Running bc error file 30.txt with clamping...Running bc error file 21.txt through cat without clamping...Running bc error file 31.txt with clamping...pass\r\npass\r\nRunning bc error file 25.txt through cat with clamping...Running bc error file 28.txt without clamping...pass\r\nRunning bc error file 23.txt through cat without clamping...pass\r\nRunning bc error file 27.txt through cat with clamping...pass\r\nRunning bc error file 32.txt with clamping...Running bc error file 24.txt through cat without clamping...Skipping problematic bc error file 33.txt...\r\nRunning bc error file 26.txt through cat with clamping...pass\r\npass\r\npass\r\npass\r\npaRunning bc error file 29.txt withss\r\npout clamping...ass\r\npass\r\nRuRupass\r\nA test failed!\r\nRunning bc error file 25.txt through cat without clamping...Running bc error file 30.txt without clamping...passnning bc error file 28.txt through nning bc error file 31.txt without clamping...\r\ncat with clamping...pass\r\nRunning bc error file 27.txt through cat without clamping...pass\r\npass\r\nA test failed!\r\nRunning bc error file 34.txt with clamping...Running bc error file 32.txt without clamping...pass\r\npass\r\nA test failed!\r\nRunning bc error file 26.txt through cat without clampiRunning bc error file 29.txt through cat with clamping...pass\r\nng...pRunning bc error file 35.txt with clamping...pass\r\nRunning bc error file 31.txt through cat with clamping...A Running bc error file 36.txt with clamping...pass\r\nRunning bc error file 30.txt through cat with clamping...\r\nRunning bc quit test...test failed!\r\nass\r\npass\r\nA test failed!\r\npass\r\nRunning bc error file 32.txt through cat with clamping...Running bc error file 28.txt through cat without clamping...Running bc error file 34.txt without clamping...pass\r\npass\r\npass\r\npass\r\nRA pass\r\ntest failed!\r\nunning bc error file 29.txt through cat without clamping...Running bcpass\r\n error file 35.txt without clamping...Running bc error file 30.txt through cat without clamping...pass\r\nRunning bc error file 31.txt through cat without clamping...Running bc error file 36.txt without clamping...A pass\r\ntest failed!\r\npass\r\nRunning bc error file 34.txt through cat with clamping...pA Running bc error file 32.txt through cat without clamping...ass\r\ntest failed!\r\npass\r\npass\r\npass\r\npass\r\nA test failed!\r\nRuRunning bc error file 35.txt through cat with clamping...nning bc error file 36.txt through cat with clamping...Rupass\r\nnning bc environment var tests...pass\r\nA test failed!\r\nRunning bc error file 34.txt through cat without clamping...A pass\r\ntest failed!\r\npass\r\nRunning bc error file 36.txt through cat without clamping...A test failed!pass\r\nRunning bc error file 35.txt through cat witpass\r\n\r\nhout clamping...Running keyword redefinition test...paA test failed!\r\nss\r\npass\r\nA test failed!\r\nA test failed!\r\nA test failed!\r\nA test failed!\r\nA test failed!\r\npass\r\nA test failed!\r\nRunning multiline comment expression file test...A test failed!\r\nA test failed!\r\nA test failed!\r\nA test failed!\r\npass\r\nA test failed!\r\nA test failed!\r\nA Rtest ufnaniilnegd !m\r\nultiline comment expression file error test...A test failed!\r\nA test failed!\r\nA test failed!\r\npass\r\nA test failed!\r\nA test failed!\r\nRunning multiline string expression file test...A test failed!\r\nA test failed!\r\nA test failed!\r\nA test failed!\r\npass\r\nRunning multiline string expression file error test...A test failed!\r\nA test failed!\r\npass\r\nA test failed!\r\nRunning bc line length tests...A test failed!\r\nA test failed!\r\nA test failed!\r\nA test failed!\r\nA test failed!\r\nA test failed!\r\nA test failed!\r\nA test failed!\r\nA test failed!\r\nA test failed!\r\nA test failed!\r\nA test failed!\r\npass\r\nA test failed!\r\nA test failed!\r\nRunning bc arg tests...pass\r\nRunning bc builtin variable arg tests...pass\r\nRunning bc directory test...pass\r\nRunning bc binary file test...pass\r\nRunning bc binary stdin test...pass\r\nRunning bc limits tests...pass\r\npass\r\nRunning bc posix_errors...pass\r\n~/aosp-master-with-phones/external/bc$ \r\n```",
+ "closed": true,
+ "closedAt": "2023-01-12T17:51:06Z",
+ "comments": [
+ {
+ "id": "IC_kwDOCL0xJc5SPfLa",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "> i haven't been able to update for some time (5.0.1 was our last update!)\r\n\r\nOof...I'm sorry. Have I been missing communication with you?\r\n\r\n> because the tests fail in a weird way without much useful information. i've included the current failures for 6.2.2 below, for example.\r\n\r\nI agree; that is useless.\r\n\r\n> my guess is that this is something to do with Android using mksh as its /bin/sh. the interleaved output makes it appear like there might be some disagreement about job handling between bash and mksh?\r\n\r\nI think that's a great guess, and it's my guess too because the `tests/all.sh` script does use job control. I tried to write it according to the POSIX standard, but I suspect that job control is finicky in general.\r\n\r\n> are the current bc tests known to work on shells other than bash (and in particular mksh)? any ideas where i should look (or some kind of parallelism i can just disable)?\r\n\r\nThey are known to work on `tcsh` (FreeBSD), but other than that, `bash`, and `dash`, I don't know.\r\n\r\n> any ideas where i should look (or some kind of parallelism i can just disable)?\r\n\r\nTo disable parallelism, change the last line of `run-bc-tests-on-android.sh` to:\r\n\r\n```\r\nexec adb shell $dash_t /data/local/tmp/bc-tests/tests/all.sh -n bc 0 1 0 0 0 bc\r\n```\r\n\r\nI made two changes:\r\n\r\n* Most importantly, there is a `-n` option, which is how to disable parallelism in the `tests/all.sh` script.\r\n* I also added another 0 before the last `bc` because there are now five integer positional arguments.\r\n\r\nMy hope (and it is only a hope) is that that missing integer argument is what is causing the weird failures because the script would otherwise treat the last `bc` argument as the last integer argument and cause some sort of failure. `bash` fails because it has a non-integer argument, but I don't know how `mksh` would fail in that case.\r\n\r\nThe other reason there might be failures is because I added a test that is \"problematic.\" It's my test for `malloc()` failure, and unfortunately, it only works on Linux systems with swap disabled and some other changes. I don't think it would work on Android. I did add an option to disable that test in `tests/all.sh`; that is the missing integer option, in fact. But I don't think that's the reason because I added it in the fourth position (the last is whether to time the tests), so it is a 0 in your current script.\r\n\r\nRegardless, with the parallelism disabled, you should see exactly what test is failing, and you should be able to give me that output, even if either of those are not the reason for the failure.\r\n\r\nIn the meantime, I'll work on my end. Android is a first-class user of my `bc`, and I need to start treating it that way, including setting up a building and testing environment. I'll do my best to do so myself, but if possible, can you give me a helpful link?",
+ "createdAt": "2023-01-12T04:16:14Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/61#issuecomment-1379791578",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5STRaK",
+ "author": {
+ "login": "enh-google"
+ },
+ "authorAssociation": "NONE",
+ "body": "> > i haven't been able to update for some time (5.0.1 was our last update!)\r\n> \r\n> Oof...I'm sorry. Have I been missing communication with you?\r\n\r\nno, this is just the first time i've had time to actually look at the problem and file a bug!\r\n\r\n> > are the current bc tests known to work on shells other than bash (and in particular mksh)? any ideas where i should look (or some kind of parallelism i can just disable)?\r\n> \r\n> They are known to work on `tcsh` (FreeBSD), but other than that, `bash`, and `dash`, I don't know.\r\n\r\nyeah, there's been a lot about job control on the POSIX mailing list recently too.\r\n\r\n> > any ideas where i should look (or some kind of parallelism i can just disable)?\r\n> \r\n> To disable parallelism, change the last line of `run-bc-tests-on-android.sh` to:\r\n> \r\n> ```\r\n> exec adb shell $dash_t /data/local/tmp/bc-tests/tests/all.sh -n bc 0 1 0 0 0 bc\r\n> ```\r\n\r\nyes, that works. thanks!\r\n\r\n> In the meantime, I'll work on my end. Android is a first-class user of my `bc`, and I need to start treating it that way, including setting up a building and testing environment. I'll do my best to do so myself, but if possible, can you give me a helpful link?\r\n\r\nthere's https://source.android.com/docs/setup/build/building (and other pages about running), but it's a pretty large undertaking.\r\n\r\ntbh, i've only had two problems with bc updates:\r\n\r\n1. build changes such as the move from a shell script to a .c file for strgen. those are almost certainly easier for me to do anyway.\r\n2. test changes like this one. these have been harder for me to adapt to (although this is the first one i've failed with). there i'd say \"yes, please make sure you add options like `-n` when you make scary changes\" but one thing you haven't done but could that i think would help would be to fail hard --- making extra options optional (ho ho) makes my life harder. an error message would be more helpful, especially because changes to a row of 0s and 1s aren't necessarily immediately obvious!\r\n\r\nanyway, https://android-review.googlesource.com/c/platform/external/bc/+/2385321 works for me locally, so it should make it through CI. (and if it doesn't, that's probably a question for me anyway :-) )\r\n\r\nthanks!",
+ "createdAt": "2023-01-12T17:51:06Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/61#issuecomment-1380783754",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc5SUcYn",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "> there's https://source.android.com/docs/setup/build/building (and other pages about running), but it's a pretty large undertaking.\r\n\r\nI'll do my best anyway. Thank you!\r\n\r\n> tbh, i've only had two problems with bc updates\r\n>\r\n> 1. build changes such as the move from a shell script to a .c file for strgen. those are almost certainly easier for me to do anyway.\r\n> 2. test changes like this one. these have been harder for me to adapt to (although this is the first one i've failed with). there i'd say \"yes, please make sure you add options like -n when you make scary changes\" but one thing you haven't done but could that i think would help would be to fail hard --- making extra options optional (ho ho) makes my life harder. an error message would be more helpful, especially because changes to a row of 0s and 1s aren't necessarily immediately obvious!\r\n\r\nI think both of these are oversights on my part. I should add sections to my `NEWS.md` entry for each version about building, packaging, and testing changes. I've never thought they were necessary to list, but here is hard evidence that yes, they are.\r\n\r\nI apologize. I will do that going forward. And I also apologize for accidentally using you as my test subject while I learn how to run a project.\r\n\r\nNow, about making things fail hard: I agree that I messed up by making arguments optional.\r\n\r\nHowever, because I have downstream users that could depend on the options *staying* optional, I'm not sure I can make them required at this point.\r\n\r\nNevertheless, I think I can do the next best thing: do error checking on each argument. This would (hopefully) allow me to add the \"fail hard\" you want (and it's a good idea!) while not causing problems for other downstream users. (Unless they have bugs in their scripts, in which case, they should be fixed anyway.)\r\n\r\nI have implemented the ideas in d5e6dbdf328407bddf53efb655abe4b9c2fcb90f, so they'll be in the next release. I hope that helps.",
+ "createdAt": "2023-01-12T23:06:57Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/61#issuecomment-1381090855",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5SUv2M",
+ "author": {
+ "login": "enh-google"
+ },
+ "authorAssociation": "NONE",
+ "body": "> I apologize. I will do that going forward. And I also apologize for accidentally using you as my test subject while I learn how to run a project.\r\n\r\nno worries --- we're a bit of an outlier anyway. it's only when you get included in a \"ridiculously large\" meta-project (most commonly an operating system [or moral equivalent] such as android or chromium or whatever) that you tend to find people _not_ using your build system because the pain of having to duplicate your build system is still slightly less than the pain of trying to incorporate your build system into theirs!\r\n\r\n> Now, about making things fail hard: I agree that I messed up by making arguments optional.\r\n> \r\n> However, because I have downstream users that could depend on the options _staying_ optional, I'm not sure I can make them required at this point.\r\n> \r\n> Nevertheless, I think I can do the next best thing: do error checking on each argument. This would (hopefully) allow me to add the \"fail hard\" you want (and it's a good idea!) while not causing problems for other downstream users. (Unless they have bugs in their scripts, in which case, they should be fixed anyway.)\r\n\r\nyeah, maybe if you ever need more arguments, make them named? (like `--foo=bar` or `foo=bar`?)\r\n\r\n> I have implemented the ideas in [d5e6dbd](https://github.com/gavinhoward/bc/commit/d5e6dbdf328407bddf53efb655abe4b9c2fcb90f), so they'll be in the next release. I hope that helps.\r\n\r\nthanks. since you're obviously interested in the feedback, i'll try to let you know sooner next time i have an update where manual intervention is required, if i have any trouble :-)",
+ "createdAt": "2023-01-13T00:55:27Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [
+ {
+ "content": "THUMBS_UP",
+ "users": {
+ "totalCount": 1
+ }
+ }
+ ],
+ "url": "https://github.com/gavinhoward/bc/issues/61#issuecomment-1381170572",
+ "viewerDidAuthor": false
+ }
+ ],
+ "createdAt": "2023-01-12T01:26:05Z",
+ "id": "I_kwDOCL0xJc5bMSgX",
+ "isPinned": false,
+ "labels": [],
+ "milestone": null,
+ "number": 61,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "state": "CLOSED",
+ "stateReason": "COMPLETED",
+ "title": "test failures on Android",
+ "updatedAt": "2023-01-13T00:55:28Z",
+ "url": "https://github.com/gavinhoward/bc/issues/61"
+ },
+ {
+ "assignees": [],
+ "author": {
+ "id": "MDQ6VXNlcjUwNzg5NDY5",
+ "is_bot": false,
+ "login": "mcoret",
+ "name": ""
+ },
+ "body": "Hi,\r\nI'm trying to compile the project using Visual Studio 19, and I get the following error: `error C2065: 'SIGWINCH': undeclared identifier`. I cannot find such definition in MSVC. MSYS64 version compiles without problems.\r\nThank you!",
+ "closed": true,
+ "closedAt": "2022-12-06T15:40:35Z",
+ "comments": [
+ {
+ "id": "IC_kwDOCL0xJc5PvhRI",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "What version are you trying to compile?\r\n\r\nIf it's the latest `master `, I have some changes there to fix a [FreeBSD bug][1] that I suspected would break the Windows build. I guess I was right.\r\n\r\nUsually, I wait until release time to fix Windows build bugs, but I'll boot Windows and push a fix later today if that will fix your problem. I'll also have a release coming soon with new stuff.\r\n\r\nI hope this helps.\r\n\r\n[1]: https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=268076",
+ "createdAt": "2022-12-05T17:55:25Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [
+ {
+ "content": "THUMBS_UP",
+ "users": {
+ "totalCount": 1
+ }
+ }
+ ],
+ "url": "https://github.com/gavinhoward/bc/issues/60#issuecomment-1337857096",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5PvnLD",
+ "author": {
+ "login": "mcoret"
+ },
+ "authorAssociation": "NONE",
+ "body": "Ah, OK, thank you again!\r\nYes, it's the `master` branch.",
+ "createdAt": "2022-12-05T18:11:45Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/60#issuecomment-1337881283",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc5PxCgn",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "I've pushed e1205eaccfa03a8dbbfd625af664f6b074b69a65 which should fix the problem for now. Can you confirm for me?",
+ "createdAt": "2022-12-05T22:18:12Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/60#issuecomment-1338255399",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5P2DiK",
+ "author": {
+ "login": "mcoret"
+ },
+ "authorAssociation": "NONE",
+ "body": "Hi, I can confirm, everything is OK!",
+ "createdAt": "2022-12-06T15:40:34Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [
+ {
+ "content": "THUMBS_UP",
+ "users": {
+ "totalCount": 1
+ }
+ }
+ ],
+ "url": "https://github.com/gavinhoward/bc/issues/60#issuecomment-1339570314",
+ "viewerDidAuthor": false
+ }
+ ],
+ "createdAt": "2022-12-05T17:00:01Z",
+ "id": "I_kwDOCL0xJc5YCR8i",
+ "isPinned": false,
+ "labels": [],
+ "milestone": null,
+ "number": 60,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "state": "CLOSED",
+ "stateReason": "COMPLETED",
+ "title": "Windows MSVC compilation problem",
+ "updatedAt": "2022-12-06T15:40:35Z",
+ "url": "https://github.com/gavinhoward/bc/issues/60"
+ },
+ {
+ "assignees": [],
+ "author": {
+ "id": "MDQ6VXNlcjQ2MTYwNzI3",
+ "is_bot": false,
+ "login": "firasuke",
+ "name": "Firas Khalil Khana"
+ },
+ "body": "As in the title, what's the difference between the two? and why is `--localedir` only mentioned in `build.md` and not when running `./configure --help`?",
+ "closed": true,
+ "closedAt": "2022-11-26T16:37:39Z",
+ "comments": [
+ {
+ "id": "IC_kwDOCL0xJc5PKKvC",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "So I originally added `--localedir` because it was one of the standard GNU configure options. This was before I understood that GNU does *not* use POSIX locales.\r\n\r\n`--localedir` does not make sense when a program is using POSIX locales, so I've [removed references to `LOCALEDIR` and friends from everything][1].\r\n\r\nDoes that answer your question?\r\n\r\n[1]: https://github.com/gavinhoward/bc/commit/f4816582b1264b64566fc162ef6512601077cc63",
+ "createdAt": "2022-11-26T15:24:49Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/58#issuecomment-1328065474",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5PKLiA",
+ "author": {
+ "login": "firasuke"
+ },
+ "authorAssociation": "CONTRIBUTOR",
+ "body": "So `--localedir` shouldn't be used?",
+ "createdAt": "2022-11-26T15:45:11Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/58#issuecomment-1328068736",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc5PKNBl",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "You are correct; it should not be used.\r\n\r\nIt wasn't used to change anything in the Makefile, so it merely just didn't do anything. With the change I made, the configure script should more give an error if it is given.",
+ "createdAt": "2022-11-26T16:24:25Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/58#issuecomment-1328074853",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5PKNey",
+ "author": {
+ "login": "firasuke"
+ },
+ "authorAssociation": "CONTRIBUTOR",
+ "body": "Ok cool, thanks for clarifying. Closing..",
+ "createdAt": "2022-11-26T16:37:19Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/58#issuecomment-1328076722",
+ "viewerDidAuthor": false
+ }
+ ],
+ "createdAt": "2022-11-26T10:36:13Z",
+ "id": "I_kwDOCL0xJc5XU90M",
+ "isPinned": false,
+ "labels": [],
+ "milestone": null,
+ "number": 58,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "state": "CLOSED",
+ "stateReason": "COMPLETED",
+ "title": "Difference between --localedir and NLSPATH",
+ "updatedAt": "2022-11-26T16:37:39Z",
+ "url": "https://github.com/gavinhoward/bc/issues/58"
+ },
+ {
+ "assignees": [],
+ "author": {
+ "id": "MDQ6VXNlcjQ2MTYwNzI3",
+ "is_bot": false,
+ "login": "firasuke",
+ "name": "Firas Khalil Khana"
+ },
+ "body": "Hey there,\r\n\r\nI've encountered an error recently where attempting to cross-compile `bc` while using `CC` as the cross compiler and `HOSTCC` as the compiler and when passing `--prefix=/usr` as a configuration flag.\r\n\r\nIt appears that `bc` is searching for header files in `$PREFIX/include`, although the help message mentions that these options control the installation directories of bc files and not the directories header/library files are searched for.\r\n\r\nThe error disappears when I remove the `--prefix=/usr` flag and the installation defaults to `/usr/local` as there's no way to change it because `--prefix` causes an error.\r\n\r\nThe error is:\r\n\r\n```C\r\n /usr/include/bits/types/time_t.h:8:9: error: unknown type name '__time64_t'\r\n 8 | typedef __time64_t time_t;\r\n | ^~~~~~~~~~\r\n```\r\n\r\nI don't recall changing the configuration flags I use for `bc` since version 4.0, did the behavior of these flags change?\r\n\r\nThanks in advance!",
+ "closed": true,
+ "closedAt": "2022-09-14T07:40:47Z",
+ "comments": [
+ {
+ "id": "IC_kwDOCL0xJc5KF-Ax",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "This looks like a bug in `configure.sh`. However, I need to know now to track it down.\r\n\r\nCan you tell me what the host system is, what the target system is, what the host compiler is, and what the target compiler is?",
+ "createdAt": "2022-09-12T00:19:32Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/56#issuecomment-1243078705",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5KGmRk",
+ "author": {
+ "login": "firasuke"
+ },
+ "authorAssociation": "CONTRIBUTOR",
+ "body": "The host system architecture tuple is `x86_64-unknown-linux-gnu` and the target architecture tuple is `x86_64-linux-musl`.\n\nThe host compiler `HOSTCC=gcc` and the target compiler is `CC=x86_64-linux-musl-gcc`.",
+ "createdAt": "2022-09-12T05:35:13Z",
+ "includesCreatedEdit": true,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/56#issuecomment-1243243620",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc5KJkBF",
+ "author": {
+ "login": "firasuke"
+ },
+ "authorAssociation": "CONTRIBUTOR",
+ "body": "I thought this will might help narrow down the cause.\r\n\r\nThe last known working version that I tried using the same configuration flags and recipe file is version `5.2.5`, so my guess would be that the change happened after that release, possibly with the latest 6.0 release.",
+ "createdAt": "2022-09-12T16:52:51Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/56#issuecomment-1244020805",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc5KKJKJ",
+ "author": {
+ "login": "firasuke"
+ },
+ "authorAssociation": "CONTRIBUTOR",
+ "body": "I think I found the [faulty commit](https://github.com/gavinhoward/bc/commit/c36b91b024e743edb45bf607cb3c2a9f28f0cc48).",
+ "createdAt": "2022-09-12T19:05:19Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/56#issuecomment-1244172937",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc5KM5Kx",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "I think you did too.\r\n\r\nThat commit appears to have been made as exploration; I should have added more to the commit message. Nevertheless, it appears to not be necessary anymore because I tested the FreeBSD port without those changes, and it worked fine.\r\n\r\nSo I committed 2b65eb21cfc575fdb04a090c687bd102a80cc43c to erase the rest of that commit. Can you pull and test it?",
+ "createdAt": "2022-09-13T04:49:35Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/56#issuecomment-1244893873",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5KP1Kn",
+ "author": {
+ "login": "firasuke"
+ },
+ "authorAssociation": "CONTRIBUTOR",
+ "body": "I can confirm that this indeed 2b65eb21cfc575fdb04a090c687bd102a80cc43c fixed the issue.\r\n\r\nCan you release this fix in a new bugfix version?",
+ "createdAt": "2022-09-13T16:34:53Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/56#issuecomment-1245663911",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc5KQ_MW",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Wonderful!\r\n\r\nI'll release a bug fix version as soon as I have confirmation that this fix does not break FreeBSD. I hope that will be soon.",
+ "createdAt": "2022-09-13T21:22:20Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/56#issuecomment-1245967126",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5KShlk",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Release `6.0.3` is out. I hope it fixes your issue.\r\n\r\nIf it doesn't, please feel free to reopen.",
+ "createdAt": "2022-09-14T07:40:47Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/56#issuecomment-1246370148",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5KV1-D",
+ "author": {
+ "login": "firasuke"
+ },
+ "authorAssociation": "CONTRIBUTOR",
+ "body": "Yup, the new release solved the issue.\r\n\r\nThanks for your time and effort!",
+ "createdAt": "2022-09-14T19:59:10Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [
+ {
+ "content": "THUMBS_UP",
+ "users": {
+ "totalCount": 2
+ }
+ }
+ ],
+ "url": "https://github.com/gavinhoward/bc/issues/56#issuecomment-1247240067",
+ "viewerDidAuthor": false
+ }
+ ],
+ "createdAt": "2022-09-11T13:48:03Z",
+ "id": "I_kwDOCL0xJc5RmIzx",
+ "isPinned": false,
+ "labels": [],
+ "milestone": null,
+ "number": 56,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "state": "CLOSED",
+ "stateReason": "COMPLETED",
+ "title": "Specifying `--prefix` when cross-compiling",
+ "updatedAt": "2022-09-14T19:59:10Z",
+ "url": "https://github.com/gavinhoward/bc/issues/56"
+ },
+ {
+ "assignees": [],
+ "author": {
+ "id": "MDQ6VXNlcjEzNTQxNjk5",
+ "is_bot": false,
+ "login": "depler",
+ "name": ""
+ },
+ "body": "Hi! Here is my command line: `bc.exe --mathlib --leading-zeroes --no-line-length --scale=100`. \r\n\r\nLast argument is ignored by some reason. As I see it `--mathlib` is overriding `scale` value",
+ "closed": true,
+ "closedAt": "2022-08-30T15:32:46Z",
+ "comments": [
+ {
+ "id": "IC_kwDOCL0xJc5IMQzl",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Nice to have you back! Sorry that I rejected your original feature request for `--scale`; I was wrong to do so.\r\n\r\nI have reproduced the issue. I think you are right with what is happening.\r\n\r\nI'll get to work on fixing it.",
+ "createdAt": "2022-08-10T19:29:45Z",
+ "includesCreatedEdit": true,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [
+ {
+ "content": "THUMBS_UP",
+ "users": {
+ "totalCount": 1
+ }
+ }
+ ],
+ "url": "https://github.com/gavinhoward/bc/issues/55#issuecomment-1211174117",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5IMfdA",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Stupid GitHub automatically closing issues.\r\n\r\nCan you please pull the latest `master` and check if it does what you want? In the meantime, I'll get started on the release process.",
+ "createdAt": "2022-08-10T20:29:31Z",
+ "includesCreatedEdit": true,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/55#issuecomment-1211234112",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5IMmDI",
+ "author": {
+ "login": "depler"
+ },
+ "authorAssociation": "CONTRIBUTOR",
+ "body": "Yep, it works now. Thanks!",
+ "createdAt": "2022-08-10T20:55:12Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/55#issuecomment-1211261128",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc5IMmVH",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Yay! Version `6.0.2` should come out in the next day or two. Thank you for your report!",
+ "createdAt": "2022-08-10T20:56:21Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/55#issuecomment-1211262279",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5Iclau",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "It's taking a little longer to get the release out. There are problems with FreeBSD. I need to fix those problems before I release. I'm sorry.\r\n\r\nI'll let you know when `6.0.2` is out.",
+ "createdAt": "2022-08-15T17:31:11Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [
+ {
+ "content": "THUMBS_UP",
+ "users": {
+ "totalCount": 1
+ }
+ }
+ ],
+ "url": "https://github.com/gavinhoward/bc/issues/55#issuecomment-1215452846",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5JbEBZ",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "`6.0.2` is out.\r\n\r\nThat should fix the issue for you. Feel free to reopen if it does not.",
+ "createdAt": "2022-08-30T15:32:46Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [
+ {
+ "content": "ROCKET",
+ "users": {
+ "totalCount": 1
+ }
+ }
+ ],
+ "url": "https://github.com/gavinhoward/bc/issues/55#issuecomment-1231831129",
+ "viewerDidAuthor": true
+ }
+ ],
+ "createdAt": "2022-08-10T19:09:02Z",
+ "id": "I_kwDOCL0xJc5Pk4LF",
+ "isPinned": false,
+ "labels": [],
+ "milestone": null,
+ "number": 55,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "state": "CLOSED",
+ "stateReason": "COMPLETED",
+ "title": "Scale argument is ignored",
+ "updatedAt": "2022-08-30T15:32:46Z",
+ "url": "https://github.com/gavinhoward/bc/issues/55"
+ },
+ {
+ "assignees": [],
+ "author": {
+ "id": "MDQ6VXNlcjk3Njg2MjI=",
+ "is_bot": false,
+ "login": "drawkula",
+ "name": "yeti"
+ },
+ "body": "Testing on Debian11...\r\n\r\nAfter configuring with `--prefix=/tmp/bctmpdir` and a successful build, `make install` fails:\r\n```\r\n/tmp/bc $ make install\r\n./scripts/locale_install.sh /usr/share/locale/%L/%N bc \r\nmkdir: cannot create directory ‘//usr/share/locale/de_DE.utf8’: Permission denied\r\nmkdir: cannot create directory ‘//usr/share/locale/de_DE.UTF-8’: Permission denied\r\nln: failed to create symbolic link '//usr/share/locale/de_DE.utf8/bc': No such file or directory\r\nmkdir: cannot create directory ‘//usr/share/locale/en_GB.utf8’: Permission denied\r\nln: failed to create symbolic link '//usr/share/locale/en_GB.utf8/bc': No such file or directory\r\nmkdir: cannot create directory ‘//usr/share/locale/en_US.utf8’: Permission denied\r\nln: failed to create symbolic link '//usr/share/locale/en_US.utf8/bc': No such file or directory\r\n./scripts/safe-install.sh -Dm644 manuals/bc.1 /tmp/bctmpdir/share/man/man1/bc.1\r\n./scripts/safe-install.sh -Dm644 manuals/dc.1 /tmp/bctmpdir/share/man/man1/dc.1\r\n./scripts/exec-install.sh /tmp/bctmpdir/bin \"\" \"/tmp/bc/bin\"\r\n```\r\nSome paths aren't adapted to fit the `--prefix=...` setting, some others fit the desired prefix setting.",
+ "closed": true,
+ "closedAt": "2022-07-23T16:23:02Z",
+ "comments": [
+ {
+ "id": "IC_kwDOCL0xJc5HDhWA",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Thank you for this report. I will look into it. Thankfully, you caught me before I made a release, which is coming soon. This will be fixed in that release, even if I have to delay it.",
+ "createdAt": "2022-07-22T02:07:54Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/53#issuecomment-1192105344",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5HDm8x",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "I noticed that you are installing `bc` in a temp directory. Is this a holding directory for a package, which will then install `bc` in its true place? Or is it something eke?\r\n\r\nI ask because locales are *special* (meaning, they are bad). They *must* be installed in one, and only one place. That place is `$NLSPATH`, which is actually a format string (the funny looking path after `./scripts/locale_install.sh` in your pasted output), but it usually points to `/usr/share/locale` with separate directories for every locale and then separate files in those directories for each program.\r\n\r\nIf locales are not installed properly in `$NLSPATH`, they will not work. This means that locales *have to* ignore `--prefix`. It's stupid, but that's POSIX for you. (This behavior is mandated by the POSIX standard.)\r\n\r\nSo if you are trying to install `bc` in `/tmp/bctmpdir`, unfortunately, you still have to install the locale files in `$NLSPATH`, anyway.\r\n\r\nIf you're installing in `/tmp/bctmpdir` to later be installed in its proper place by a package manager, then you can use the `$DESTDIR` environment variable when running `configure.sh`. If you do this, then locales will be installed at `$DESTDIR/$NLSPATH`, while other files will be installed at `$DESTDIR/$PREFIX/<whatever>`, where `$PREFIX` was passed in with `--prefix=$PREFIX`. Once that is done, and the package is made, then when users install the package, the locales will be put into the correct `$NLSPATH`.\r\n\r\nIf, however, you absolutely *must* override the behavior of installing the locales in `$NLSPATH`, you can set your own `$NLSPATH` when running `configure.sh`. The `%L` in the format specifier will be replaced by the locale name, and the `%N` will be replaced by the name of the program. You probably want both of those format specifiers in there.\r\n\r\nBut I do ***NOT*** recommend this because those installed locales will not work unless your `$NLSPATH` is set to that same format during in normal usage, which it appears it is not based on the format string in your output above.\r\n\r\nI hope that explains why things are the way they are. If that is the reason you are having problems, I am willing to help you figure out what would be best for you to do.\r\n\r\nHowever, if you still believe there is a bug in the install behavior, I'll do my best to find it.\r\n\r\nPlease let me know; I would like to help in any way I can.",
+ "createdAt": "2022-07-22T02:57:50Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/53#issuecomment-1192128305",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5HD5j0",
+ "author": {
+ "login": "drawkula"
+ },
+ "authorAssociation": "NONE",
+ "body": "I just repeated the build in `/tmp` for catching the log.\r\n\r\nOriginally I wanted to have `bc`/`dc` in an own \"bonsai subtree\" in `/opt/gavinhoward-bc` for not interfering with the default `dc`/`bc` of Debian and I typically manage `/opt` via an own account not being `root` (and not my standard user account). Had I tried this as `root` I easily could have overlooked the files falling out of that subtree. I think that may surprise other users in some other contexts too or will just drop the NLS files somewhere they do not even notice.\r\n\r\nMaybe dropping them into the desired prefix too and issuing a warning is an idea?\r\n\r\nWell, so far nobody complained (or noticed it?). Maybe I'm just the one with strange ideas...\r\n\r\nIn my case, if the NLS files are dropped into that subtree with the consequence that only english works, it would be good enough. And alternatively I can just deactivate NLS.\r\n\r\nThis issue just should be about the surprise that some paths don't respect `--prefix=...` and maybe about minimising surprises.\r\n\r\nAnd it definitely is not urgent.",
+ "createdAt": "2022-07-22T05:42:58Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/53#issuecomment-1192204532",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc5HFHU6",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "I agree that the surprise is bad.\r\n\r\nThe problem with putting them into the desired prefix is that they just won't work, and then I'll get bug reports about it because I know several distros that depend on the current behavior.\r\n\r\nI think there are two things I can do. First, I can ensure that locales are *not* installed if NLS is disabled. I did that. (It already did it right, thankfully.) Second, I can add warnings. I added 7 in https://github.com/gavinhoward/bc/commit/b78e8e4cfb03b1135f03426b2a4aaf848b6c4d5d.\r\n\r\nActually, there's a third thing I can do: I can have `configure.sh` output a warning to the user when the prefix does *not* match with `$NLSPATH`. That has been done in https://github.com/gavinhoward/bc/commit/6dccfebe21c62d1c043387590d23a91b8499f68c.\r\n\r\nWhat else would you like me to do or think that I should do?",
+ "createdAt": "2022-07-22T12:29:32Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/53#issuecomment-1192523066",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5HFar1",
+ "author": {
+ "login": "drawkula"
+ },
+ "authorAssociation": "NONE",
+ "body": "Thanks!\r\nThe warnings at configure-time really should help everyone.\r\n\r\nMay I add a tiny \"last\" (for in this issue) wish?\r\nCan the warning be reformatted to fit an 80 columns terminal?\r\nReformatted to be slightly below 80 CpL it will not even take more lines.\r\n",
+ "createdAt": "2022-07-22T13:55:53Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/53#issuecomment-1192602357",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc5HF6Eg",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "I meant to have 80 columns or less from the beginning. Whoops.\r\n\r\nIs https://github.com/gavinhoward/bc/commit/7cdddb8cc53fc1c08fe52fcf7fedbf7c84800c7c at 80 columns or less?",
+ "createdAt": "2022-07-22T16:12:18Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/53#issuecomment-1192730912",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5HGEY4",
+ "author": {
+ "login": "drawkula"
+ },
+ "authorAssociation": "NONE",
+ "body": "Without changing the indentation 77 chars are the maximal sub-80 width that fits.\r\n```\r\n*****************************************************************************\r\n\r\nWARNING: Locales will *NOT* be installed in $PREFIX (/opt/gavinhoward-bc).\r\n\r\n This is because they *MUST* be installed at a fixed location to even\r\n work, and that fixed location is $NLSPATH ().\r\n\r\n This location is *outside* of $PREFIX. If you do not wish to install\r\n locales outside of $PREFIX, you must disable NLS with the -N or the\r\n --disable-nls options.\r\n\r\n The author apologizes for the inconvenience, but the need to install\r\n the locales at a fixed location is mandated by POSIX. It is not\r\n possible for the author to change.\r\n\r\n*****************************************************************************\r\n```\r\n",
+ "createdAt": "2022-07-22T17:05:23Z",
+ "includesCreatedEdit": true,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/53#issuecomment-1192773176",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc5HHHpw",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Whoops. How does https://github.com/gavinhoward/bc/commit/6035d39a68e6078f578823fe407892930fe0d955 look?",
+ "createdAt": "2022-07-23T03:21:18Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/53#issuecomment-1193048688",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5HHS2n",
+ "author": {
+ "login": "drawkula"
+ },
+ "authorAssociation": "NONE",
+ "body": "Looks good:\r\n\r\n![20220723-092628](https://user-images.githubusercontent.com/9768622/180599430-95db0a76-f00d-4dc2-bb0a-e3d679acb9f2.png)\r\n\r\nWould it make sense not to show it when `configure` is run with `--disable-nls`?",
+ "createdAt": "2022-07-23T09:27:41Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/53#issuecomment-1193094567",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc5HHW-X",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Whoops.\r\n\r\nYou are absolutely right, of course. Does https://github.com/gavinhoward/bc/commit/8d8935e44bba15e96e8db83536ba54cd1deab398 fix the issue for you?",
+ "createdAt": "2022-07-23T11:39:44Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/53#issuecomment-1193111447",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5HHXEq",
+ "author": {
+ "login": "drawkula"
+ },
+ "authorAssociation": "NONE",
+ "body": "Perfect!\r\nThanks!",
+ "createdAt": "2022-07-23T11:43:19Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/53#issuecomment-1193111850",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc5HHgig",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "You're welcome.\r\n\r\nSince it appears this issue has been resolved, I'm going to close it, but if you disagree, feel free to reopen it.",
+ "createdAt": "2022-07-23T16:23:02Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [
+ {
+ "content": "THUMBS_UP",
+ "users": {
+ "totalCount": 1
+ }
+ }
+ ],
+ "url": "https://github.com/gavinhoward/bc/issues/53#issuecomment-1193150624",
+ "viewerDidAuthor": true
+ }
+ ],
+ "createdAt": "2022-07-21T21:12:21Z",
+ "id": "I_kwDOCL0xJc5OTyTx",
+ "isPinned": false,
+ "labels": [],
+ "milestone": null,
+ "number": 53,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "state": "CLOSED",
+ "stateReason": "COMPLETED",
+ "title": "--prefix=... is partially ignored?",
+ "updatedAt": "2022-07-23T16:23:02Z",
+ "url": "https://github.com/gavinhoward/bc/issues/53"
+ },
+ {
+ "assignees": [],
+ "author": {
+ "id": "MDQ6VXNlcjQ2MTYwNzI3",
+ "is_bot": false,
+ "login": "firasuke",
+ "name": "Firas Khalil Khana"
+ },
+ "body": "Is it possible to provide release tarballs in `gzip` format as well for limited systems?\r\n\r\nThanks!",
+ "closed": true,
+ "closedAt": "2022-04-30T22:23:08Z",
+ "comments": [
+ {
+ "id": "IC_kwDOCL0xJc5CZjfO",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Yes, absolutely.\r\n\r\nJust to be sure, you mean `.tar.gz` files, correct?",
+ "createdAt": "2022-04-30T14:08:36Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/52#issuecomment-1113995214",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5CZoVz",
+ "author": {
+ "login": "firasuke"
+ },
+ "authorAssociation": "CONTRIBUTOR",
+ "body": "Yup, if possible.",
+ "createdAt": "2022-04-30T16:25:20Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/52#issuecomment-1114015091",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc5CZov4",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Not a problem. I have a new release coming out soon, and I will be sure to have `.tar.gz` files for it, as well as every future release.",
+ "createdAt": "2022-04-30T16:38:05Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/52#issuecomment-1114016760",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5CZ0M3",
+ "author": {
+ "login": "firasuke"
+ },
+ "authorAssociation": "CONTRIBUTOR",
+ "body": "Thanks!\r\n\r\nClosing this now.",
+ "createdAt": "2022-04-30T22:23:08Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/52#issuecomment-1114063671",
+ "viewerDidAuthor": false
+ }
+ ],
+ "createdAt": "2022-04-30T10:20:12Z",
+ "id": "I_kwDOCL0xJc5I0yHJ",
+ "isPinned": false,
+ "labels": [],
+ "milestone": null,
+ "number": 52,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "state": "CLOSED",
+ "stateReason": "COMPLETED",
+ "title": "Release tarballs in `gzip` format",
+ "updatedAt": "2022-04-30T22:23:08Z",
+ "url": "https://github.com/gavinhoward/bc/issues/52"
+ },
+ {
+ "assignees": [],
+ "author": {
+ "id": "MDQ6VXNlcjg4MjY0OTc5",
+ "is_bot": false,
+ "login": "DelilahHoare",
+ "name": "Delilah Hoare"
+ },
+ "body": "Comments delimited by `/* */` and spanning multiple lines in files result in `Parse error: comment end cannot be found`. Such comments are accepted into stdin.",
+ "closed": true,
+ "closedAt": "2022-03-05T04:53:19Z",
+ "comments": [
+ {
+ "id": "IC_kwDOCL0xJc4_JAlq",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "That does sound like a bug.\r\n\r\nCould you send me the output of `bc --version`? Also, could you send me one or more files that cause it to happen?",
+ "createdAt": "2022-03-04T16:41:15Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/50#issuecomment-1059326314",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc4_KFB6",
+ "author": {
+ "login": "DelilahHoare"
+ },
+ "authorAssociation": "NONE",
+ "body": "I should clarify that this only happens when using the `-f` flag; passing the file as an argument without that flag works.\r\n\r\n```\r\nbc 5.2.2\r\nCopyright (c) 2018-2021 Gavin D. Howard and contributors\r\nReport bugs at: https://git.yzena.com/gavin/bc\r\n\r\nThis is free software with ABSOLUTELY NO WARRANTY.\r\n```\r\n\r\n[testoneline.bc](https://github.com/gavinhoward/bc/files/8189282/testoneline.bc.txt) works,\r\n[testmultiline.bc](https://github.com/gavinhoward/bc/files/8189281/testmultiline.bc.txt) doesn't. \r\n[timeconst.bc](https://github.com/gavinhoward/bc/files/8189283/timeconst.bc.txt) comes from the linux source tree and also doesn't work.\r\n\r\n",
+ "createdAt": "2022-03-04T23:39:16Z",
+ "includesCreatedEdit": true,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/50#issuecomment-1059606650",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc4_KNpz",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Thank you. I have confirmed the bug, and I'm working on debugging and a fix.",
+ "createdAt": "2022-03-05T01:30:06Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/50#issuecomment-1059641971",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc4_KWij",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Okay, I have found the problem and have committed a fix in d778d0b9177c75f207dca16b57974edbb5f9e15c and dbc4dc4c4e94712fa1d4800c81e84d6da18f5188.\r\n\r\nCould you pull and test the updated commits for me, to make sure they work for you? In the meantime, I'll prepare a release with the fix.",
+ "createdAt": "2022-03-05T04:03:17Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/50#issuecomment-1059678371",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc4_KYdX",
+ "author": {
+ "login": "DelilahHoare"
+ },
+ "authorAssociation": "NONE",
+ "body": "It's working, thanks!",
+ "createdAt": "2022-03-05T04:53:19Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/50#issuecomment-1059686231",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc4_KYmm",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Great! I'll have version `5.2.3` out in a few days.",
+ "createdAt": "2022-03-05T04:56:48Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/50#issuecomment-1059686822",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc4_QD5x",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "`5.2.3` is out!",
+ "createdAt": "2022-03-07T21:53:42Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/50#issuecomment-1061174897",
+ "viewerDidAuthor": true
+ }
+ ],
+ "createdAt": "2022-03-04T00:24:45Z",
+ "id": "I_kwDOCL0xJc5FFokp",
+ "isPinned": false,
+ "labels": [],
+ "milestone": null,
+ "number": 50,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "state": "CLOSED",
+ "stateReason": "COMPLETED",
+ "title": "Multi-line comments in files result in parse error",
+ "updatedAt": "2022-03-07T21:53:42Z",
+ "url": "https://github.com/gavinhoward/bc/issues/50"
+ },
+ {
+ "assignees": [],
+ "author": {
+ "id": "MDQ6VXNlcjI3OTUyNTcx",
+ "is_bot": false,
+ "login": "oguz-ismail",
+ "name": ""
+ },
+ "body": "Observe:\r\n```\r\n$ bc -s <<x\r\ndefine a(){\r\n}define b(){\r\n}\r\nx\r\n\r\nParse error: bad token\r\n <stdin>:2\r\n\r\n$ bc -s <<x\r\ndefine a(){\r\n};define b(){\r\n}\r\nx\r\n$\r\n```\r\nAs per POSIX, the first one should work fine, and the second should fail as a newline is required after a `semicolon_list` production. No other bc implementation exhibits this behavior except busybox bc, which is a fork of this one if I recall correctly.",
+ "closed": true,
+ "closedAt": "2021-11-23T05:42:14Z",
+ "comments": [
+ {
+ "id": "IC_kwDOCL0xJc46Kepe",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "This has (hopefully) been fixed in commits 5b2fe303c8c7d85d299b6576f62c8191e492fdb0, 81f838f657a2b942a76c1240d1107a4d358fd2a2, and 9ffdd5ec6915bc01fa0ceb7f5b5e9e4327615044.\r\n\r\nCould you please pull down those commits and test? If they work, I'll release an update soon.",
+ "createdAt": "2021-11-22T18:57:57Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/48#issuecomment-975825502",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc46KhAF",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Oh, I meant to say that I looked at the standard, and I agree with you on the interpretation of it.",
+ "createdAt": "2021-11-22T19:09:25Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/48#issuecomment-975835141",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc46Kkv8",
+ "author": {
+ "login": "oguz-ismail"
+ },
+ "authorAssociation": "NONE",
+ "body": "@gavinhoward Yeah, it works fine now. Thanks for the fix",
+ "createdAt": "2021-11-22T19:29:26Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/48#issuecomment-975850492",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc46KynT",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Thank you!\r\n\r\nI'll run my release process and put out `5.2.1` as soon as it passes.",
+ "createdAt": "2021-11-22T20:50:45Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/48#issuecomment-975907283",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc46L1uZ",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "`5.2.1` is out. I believe that solves this issue, so I am going to close. Feel free to reopen if you need to.\r\n\r\nThank you for your report!",
+ "createdAt": "2021-11-23T05:42:14Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [
+ {
+ "content": "THUMBS_UP",
+ "users": {
+ "totalCount": 1
+ }
+ }
+ ],
+ "url": "https://github.com/gavinhoward/bc/issues/48#issuecomment-976182169",
+ "viewerDidAuthor": true
+ }
+ ],
+ "createdAt": "2021-11-22T11:06:32Z",
+ "id": "I_kwDOCL0xJc4_Ln9A",
+ "isPinned": false,
+ "labels": [],
+ "milestone": null,
+ "number": 48,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "state": "CLOSED",
+ "stateReason": "COMPLETED",
+ "title": "Divergence from grammar defined by POSIX",
+ "updatedAt": "2021-11-23T05:42:15Z",
+ "url": "https://github.com/gavinhoward/bc/issues/48"
+ },
+ {
+ "assignees": [],
+ "author": {
+ "id": "MDQ6VXNlcjEzNTQxNjk5",
+ "is_bot": false,
+ "login": "depler",
+ "name": ""
+ },
+ "body": "Is there any command line switch to set scale at start, like `bc.exe --scale 100`? If not, could you please implement?",
+ "closed": true,
+ "closedAt": "2022-06-10T18:05:26Z",
+ "comments": [
+ {
+ "id": "IC_kwDOCL0xJc4387LJ",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "There is no direct way to do it, but you can do it like this:\r\n\r\n```\r\nbc.exe -e \"scale = 100\" -f-\r\n```\r\n\r\n`-e` is the command-line option to take expressions, and since `bc` exits by default when giving it an expression, you add `-f-` to tell it to also accept input from `stdin`.\r\n\r\nI think next release, I will add the ability to *not* exit by default on expressions for Windows. (FreeBSD wants my `bc` to exit by default.) Once I do that, you will be able to do:\r\n\r\n```\r\nbc.exe -e \"scale = 100\"\r\n```",
+ "createdAt": "2021-10-08T15:09:39Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/47#issuecomment-938717897",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc439Fcs",
+ "author": {
+ "login": "depler"
+ },
+ "authorAssociation": "CONTRIBUTOR",
+ "body": "Well, this is not really an option for me - I want bc to exit after `-e` switch. The point is to have ability to manipulate of scale value from outside - the only option for now to do it is within a script or command. Anyway, not really a problem. Fell free to close this, if you are not going implement scale switch.",
+ "createdAt": "2021-10-08T16:09:16Z",
+ "includesCreatedEdit": true,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/47#issuecomment-938759980",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc439QqT",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Yeah, I'm sorry. It's a little too specialized to do in my opinion.\r\n\r\nThank you for understanding.",
+ "createdAt": "2021-10-08T16:34:17Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/47#issuecomment-938805907",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5Dmtyj",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "@depler would you still want this? I'm going to do a release soon, and I'm reconsidering it.",
+ "createdAt": "2022-05-23T06:10:23Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/47#issuecomment-1134222499",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5EjtT2",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "@depler I'm going to implement this.",
+ "createdAt": "2022-06-08T17:44:03Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/47#issuecomment-1150211318",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5EksDZ",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "It is now implemented in 488d48c87c5b9e02e5c3911918c2d6d7687246e8. The command-line options are `-S`/`--scale`, `-I`/`--ibase`, `-O`/`--obase`, and `-E`/`--seed`.\r\n\r\nIf you could test before I release `5.3.0`, I would appreciate it.\r\n\r\nAlso, history is working on Windows now!",
+ "createdAt": "2022-06-08T22:15:08Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/47#issuecomment-1150468313",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5Es3Dl",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "It's been released in `5.3.0`. Hope this helps!",
+ "createdAt": "2022-06-10T18:05:26Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/47#issuecomment-1152610533",
+ "viewerDidAuthor": true
+ }
+ ],
+ "createdAt": "2021-10-08T11:02:30Z",
+ "id": "I_kwDOCL0xJc482sjv",
+ "isPinned": false,
+ "labels": [],
+ "milestone": null,
+ "number": 47,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "state": "CLOSED",
+ "stateReason": "COMPLETED",
+ "title": "Command switch for scale?",
+ "updatedAt": "2022-06-10T18:05:27Z",
+ "url": "https://github.com/gavinhoward/bc/issues/47"
+ },
+ {
+ "assignees": [],
+ "author": {
+ "id": "MDQ6VXNlcjEzNTQxNjk5",
+ "is_bot": false,
+ "login": "depler",
+ "name": ""
+ },
+ "body": "Hi! When are you going to release new version with recent changes?",
+ "closed": true,
+ "closedAt": "2021-10-06T20:42:31Z",
+ "comments": [
+ {
+ "id": "IC_kwDOCL0xJc43pTbJ",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Yes. It will be `5.1.0`.\r\n\r\nI have not done so yet because of two things. First, I was not really sure if you would find more bugs, and second, my release process takes a while. (It includes, among other things, building `bc` in every supported configuration and running the test suite for every build, on multiple platforms.)\r\n\r\nI expect that the release will go out sometime today (US time).",
+ "createdAt": "2021-10-04T15:03:22Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/46#issuecomment-933574345",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc43p7GT",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Release is out! Thank you for your help.",
+ "createdAt": "2021-10-04T18:19:40Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [
+ {
+ "content": "THUMBS_UP",
+ "users": {
+ "totalCount": 1
+ }
+ }
+ ],
+ "url": "https://github.com/gavinhoward/bc/issues/46#issuecomment-933736851",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc43qhxQ",
+ "author": {
+ "login": "depler"
+ },
+ "authorAssociation": "CONTRIBUTOR",
+ "body": "Thanks! Why did you put project files, debug executables and test files into release archive?",
+ "createdAt": "2021-10-04T22:11:43Z",
+ "includesCreatedEdit": true,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/46#issuecomment-933895248",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc43qjSr",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "I should not have put the test files (sorry; I'll take them out next release), but I put the others there in case users *want* a debug executable. Basically, I put whatever I thought users might want even if they don't want the full repo. (I know at least Linux distro that builds a debug version of my `bc`.)",
+ "createdAt": "2021-10-04T22:23:01Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/46#issuecomment-933901483",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc432vHa",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "The next release is out (there was a bug), and I have implemented your suggestions, except for debug executables. Thank you.\r\n\r\nI will go ahead and close this bug now, but you can reopen if you feel the need.",
+ "createdAt": "2021-10-06T20:42:31Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/46#issuecomment-937095642",
+ "viewerDidAuthor": true
+ }
+ ],
+ "createdAt": "2021-10-04T13:53:49Z",
+ "id": "I_kwDOCL0xJc48guzb",
+ "isPinned": false,
+ "labels": [],
+ "milestone": null,
+ "number": 46,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "state": "CLOSED",
+ "stateReason": "COMPLETED",
+ "title": "New release",
+ "updatedAt": "2021-10-06T20:42:31Z",
+ "url": "https://github.com/gavinhoward/bc/issues/46"
+ },
+ {
+ "assignees": [],
+ "author": {
+ "id": "MDQ6VXNlcjMxNzI2ODc=",
+ "is_bot": false,
+ "login": "gavinhoward",
+ "name": "Gavin D. Howard"
+ },
+ "body": "@depler do you know how to get packages into Winget and Chocolatey? I had [someone ask me to do that][1].\r\n\r\n[1]: https://news.ycombinator.com/item?id=28653815",
+ "closed": true,
+ "closedAt": "2021-10-01T17:38:41Z",
+ "comments": [
+ {
+ "id": "IC_kwDOCL0xJc43ktg6",
+ "author": {
+ "login": "depler"
+ },
+ "authorAssociation": "CONTRIBUTOR",
+ "body": "Sorry, never did this. I prefer portable windows utilities without package manager.",
+ "createdAt": "2021-10-01T16:20:38Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/45#issuecomment-932370490",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc43k7w9",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": ":+1: ",
+ "createdAt": "2021-10-01T17:38:41Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/45#issuecomment-932428861",
+ "viewerDidAuthor": true
+ }
+ ],
+ "createdAt": "2021-10-01T04:59:55Z",
+ "id": "I_kwDOCL0xJc48X3lM",
+ "isPinned": false,
+ "labels": [],
+ "milestone": null,
+ "number": 45,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "state": "CLOSED",
+ "stateReason": "COMPLETED",
+ "title": "Get `bc` into Winget and Chocolatey",
+ "updatedAt": "2021-10-01T17:38:41Z",
+ "url": "https://github.com/gavinhoward/bc/issues/45"
+ },
+ {
+ "assignees": [],
+ "author": {
+ "id": "MDQ6VXNlcjEzNTQxNjk5",
+ "is_bot": false,
+ "login": "depler",
+ "name": ""
+ },
+ "body": "Can you please implement some new command line arguments?\r\n\r\n1. Ability to print equation result without line breaks, i.e. just a solid text.\r\n2. Ability to force prepending zero in equation result, i.e. `0.123` instead of current `.123`",
+ "closed": true,
+ "closedAt": "2021-09-29T23:02:01Z",
+ "comments": [
+ {
+ "id": "IC_kwDOCL0xJc43UXox",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Those are good ideas.\r\n\r\nFor the first, there is already an environment variable, `BC_LINE_LENGTH`, that can be used to do that. You just set it to a large number. Would that work, or would that still not be convenient (and I use that word for a reason; I do want it to be easy for you) on Windows? Also, if you want a separate option to remove line limits entirely, why?\r\n\r\nFor the second, I think that one might be a *really* good idea. EDIT: However, could this be implemented by something like a function in the math library such that it's not a global option affecting all numbers? Would that be better? Should we have both?\r\n\r\nIn both of these cases, I don't want to just willy-nilly implement something; I want them to be designed correctly. So I'll get started on that, but please feel free to give me your opinions about the design of them.",
+ "createdAt": "2021-09-27T17:16:40Z",
+ "includesCreatedEdit": true,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/43#issuecomment-928086577",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc43UkoZ",
+ "author": {
+ "login": "depler"
+ },
+ "authorAssociation": "CONTRIBUTOR",
+ "body": "> Also, if you want a separate option to remove line limits entirely, why?\r\n\r\nThis is exactly what I want - remove line limits entirely. Environment variables are not really useful on Windows unless you heavily use scripts. Usually you just run executable with desired variables to archive functionality (or using some config file as alternative).\r\n\r\n> However, could this be implemented by something like a function in the math library such that it's not a global option affecting all numbers? Would that be better? Should we have both?\r\n\r\nFor exactly my needs global option is preferable - I just want to see all numbers in range `-1 < 0 < 1` with zero.\r\n\r\nSo if you you think that environment variables are needed - feel free to implement both. Personally I don't like to use environment switches, command arguments are more convenient for me.\r\n",
+ "createdAt": "2021-09-27T18:06:20Z",
+ "includesCreatedEdit": true,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/43#issuecomment-928139801",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc43VIy5",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Good to know. Let me get on that and see if I can come up with something that works for you.",
+ "createdAt": "2021-09-27T21:15:44Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/43#issuecomment-928287929",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc43Vnrz",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "I have added the two command line arguments (`z` and `C`). Could you please pull and test that they do what you want?\r\n\r\nI'm not done yet, but the rest of the work is not really going to impact what you want. It's a way to query the status of those things so that I can write library functions to print them in different ways.",
+ "createdAt": "2021-09-27T23:07:11Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [
+ {
+ "content": "THUMBS_UP",
+ "users": {
+ "totalCount": 1
+ }
+ }
+ ],
+ "url": "https://github.com/gavinhoward/bc/issues/43#issuecomment-928414451",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc43XcED",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "I've changed the options to `-z`/`--leading-zeroes` and `-L`/`--no-line-length`.",
+ "createdAt": "2021-09-28T06:21:27Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [
+ {
+ "content": "THUMBS_UP",
+ "users": {
+ "totalCount": 1
+ }
+ }
+ ],
+ "url": "https://github.com/gavinhoward/bc/issues/43#issuecomment-928891139",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc43YQUv",
+ "author": {
+ "login": "depler"
+ },
+ "authorAssociation": "CONTRIBUTOR",
+ "body": "Working fine, thanks! Can you please add new tests to windows scripts? It is just a new lines with filename of appropriate test in `tests_bc.bat` and `tests_dc.bat`.",
+ "createdAt": "2021-09-28T11:34:55Z",
+ "includesCreatedEdit": true,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/43#issuecomment-929105199",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc43Y4pl",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "I'm sorry, I feel stupid, but I'm not sure what tests you want me to add. For these features? Or more tests in general?",
+ "createdAt": "2021-09-28T14:12:35Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/43#issuecomment-929270373",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc43ZFR5",
+ "author": {
+ "login": "depler"
+ },
+ "authorAssociation": "CONTRIBUTOR",
+ "body": "You've added this one (and may be something else): https://github.com/gavinhoward/bc/blob/master/tests/bc/leadingzero.txt\r\nWindows script doesn't know about it yet.",
+ "createdAt": "2021-09-28T15:09:39Z",
+ "includesCreatedEdit": true,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/43#issuecomment-929322105",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc43Zugt",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "I see. I've added that test to the Windows bat file, but once again, can you pull and test for me? The reason is that it needs to run the test twice, and I want to be sure that works for you.",
+ "createdAt": "2021-09-28T17:54:31Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/43#issuecomment-929490989",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc43btxR",
+ "author": {
+ "login": "depler"
+ },
+ "authorAssociation": "CONTRIBUTOR",
+ "body": "A few changes are needed to differ `leadingzero` test from `leadingzero_z` : https://github.com/gavinhoward/bc/pull/44\r\nOtherwise it works fine.",
+ "createdAt": "2021-09-29T09:35:21Z",
+ "includesCreatedEdit": true,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/43#issuecomment-930012241",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc43crqE",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "#44 is merged. Is there anything else that needs to change for you?",
+ "createdAt": "2021-09-29T15:06:22Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/43#issuecomment-930265732",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc43d-9e",
+ "author": {
+ "login": "depler"
+ },
+ "authorAssociation": "CONTRIBUTOR",
+ "body": "Nope. Thanks 👍",
+ "createdAt": "2021-09-29T23:02:01Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/43#issuecomment-930606942",
+ "viewerDidAuthor": false
+ }
+ ],
+ "createdAt": "2021-09-27T15:12:39Z",
+ "id": "I_kwDOCL0xJc48GQ_o",
+ "isPinned": false,
+ "labels": [],
+ "milestone": null,
+ "number": 43,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "state": "CLOSED",
+ "stateReason": "COMPLETED",
+ "title": "Additional arguments",
+ "updatedAt": "2021-09-29T23:02:02Z",
+ "url": "https://github.com/gavinhoward/bc/issues/43"
+ },
+ {
+ "assignees": [],
+ "author": {
+ "id": "MDQ6VXNlcjQ2Njg2NTY1",
+ "is_bot": false,
+ "login": "rubyFeedback",
+ "name": ""
+ },
+ "body": "Hey there.\n\nI am using this configure line:\n\n CC=gcc ./configure --prefix=/home/Programs/Bc/7.0.3/ -G -O3 -r\n\nThis is what LFS (Linux from Scratch) recommends, but they use the conventional\n/usr/ prefix.\n\nNow interestingly, when I use the above, I get this result - a warning:\n\n WARNING: Locales will *NOT* be installed in $PREFIX (/home/Programs/Bc/7.0.3/).\n\n This is because they *MUST* be installed at a fixed location to even\n work, and that fixed location is $NLSPATH (/usr/share/locale/%L/%N).\n\n This location is *outside* of $PREFIX. If you do not wish to install\n locales outside of $PREFIX, you must disable NLS with the -N or the\n --disable-nls options.\n\n The author apologizes for the inconvenience, but the need to install\n the locales at a fixed location is mandated by POSIX, and it is not\n possible for the author to change that requirement.\n\nThe warning makes sense on traditional systems that use /usr/ as prefix.\nGoboLinux uses versioned appdirs instead. (I am not using GoboLinux\nright now, as they use /Programs/ dir; I use a slightly modified self-compiled\nvariant instead. At the least currently.)\n\nI do not believe that POSIX mandates that /usr/share/local/ must be in\nthe way described. For instance, I am about 100% certain that symlinks \nare allowed; at the least not forbidden, so I would reason that POSIX\ncan not be cited here. GoboLinux also uses symlinks by the way; they\neven still have /usr/ etc... despite versioned appdirs.\n\nMy question is: is this warning really needed? I can install all glibc-related\nlocales into /home/Programs/Glibc/2.41/share/local/ etc... and I do not\nget any warning about this. I believe the warning has had good intentions,\nbut the explanation is a bit shaky to me, and does not seem to account\nfor e. g. alternative ways to handle a linux system. Does POSIX really\nsay that symlinks are forbidden?\n\n(Also, is it true that locales must be at a fixed location per se? Because in\nprinciple, whatever is searching for the file, could also have another \nlocation. I use the example of symlinks, but even without symlinks we \nhave things such as --libdir and various other flags that allow fine-tuning\nof various things here. Even meson-build systems allow for quite some\nflexibility here.)\n",
+ "closed": true,
+ "closedAt": "2025-04-10T16:47:30Z",
+ "comments": [
+ {
+ "id": "IC_kwDOCL0xJc6mkMkS",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Yes, it is necessary. I have had more people ask why locales don't work than people like you asking why there is a warning. In fact, you're the first.\n\nTo remove the warning, set `NLSPATH` on the call to `configure`, like so:\n\n```\nCC=gcc NLSPATH=/home/Programs/Bc/7.0.3/%L/%N \\\n ./configure --prefix=/home/Programs/Bc/7.0.3/ -G -O3 -r\n```\n\nThen yes, you can set up symlinks; POSIX does allow that.",
+ "createdAt": "2025-04-10T16:47:30Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/90#issuecomment-2794506514",
+ "viewerDidAuthor": true
+ }
+ ],
+ "createdAt": "2025-04-10T11:40:10Z",
+ "id": "I_kwDOCL0xJc6x8Vgk",
+ "isPinned": false,
+ "labels": [],
+ "milestone": null,
+ "number": 90,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "state": "CLOSED",
+ "stateReason": "COMPLETED",
+ "title": "locales - is the warning for non-standard prefixes still necessary?",
+ "updatedAt": "2025-04-10T16:47:30Z",
+ "url": "https://github.com/gavinhoward/bc/issues/90"
+ },
+ {
+ "assignees": [],
+ "author": {
+ "id": "U_kgDOBxD9gQ",
+ "is_bot": false,
+ "login": "tungstengmd",
+ "name": "harlow foxworthy"
+ },
+ "body": "this might have been excluded for a reason but can you add the \"!\" command back ? only reason being that i wanna clear the screen on certain events",
+ "closed": true,
+ "closedAt": "2025-04-18T20:43:24Z",
+ "comments": [
+ {
+ "id": "IC_kwDOCL0xJc6n20Uz",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "It was excluded for a reason, and no, I will not add it. Have your shell clear the screen.",
+ "createdAt": "2025-04-18T20:43:23Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/91#issuecomment-2816165171",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc6n25O1",
+ "author": {
+ "login": "tungstengmd"
+ },
+ "authorAssociation": "NONE",
+ "body": "had a feeling, and damn you're fast",
+ "createdAt": "2025-04-18T20:58:15Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/91#issuecomment-2816185269",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc6n3ljG",
+ "author": {
+ "login": "drawkula"
+ },
+ "authorAssociation": "NONE",
+ "body": "You can hardcode the sequence to clear the screen.\nTo be a bit independent of the terminal, catching what `clear` will send may be the way.\n```\n$ clear | xxd -C -p -u -\n1B5B481B5B324A\n```\nSo this ...\n```\n$ clear | xxd -C -p -u - | dc -e '16i ? P'\n```\n... demonstrates it. Capture the value, in base10 if you prefer.\n\nTBH, I miss `!` too.\n",
+ "createdAt": "2025-04-18T23:55:14Z",
+ "includesCreatedEdit": true,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/91#issuecomment-2816366790",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc6n4p5E",
+ "author": {
+ "login": "tungstengmd"
+ },
+ "authorAssociation": "NONE",
+ "body": "oh wait you're onto something",
+ "createdAt": "2025-04-19T10:18:42Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/91#issuecomment-2816646724",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc6n5dwl",
+ "author": {
+ "login": "tungstengmd"
+ },
+ "authorAssociation": "NONE",
+ "body": "ok so basically you can just catch command output [as hex form] in xxd and input it to dc with the input radix as 16\ntherefore bypassing the need for `!`\nthanks @drawkula",
+ "createdAt": "2025-04-19T20:40:25Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/91#issuecomment-2816859173",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc6n-PJv",
+ "author": {
+ "login": "drawkula"
+ },
+ "authorAssociation": "NONE",
+ "body": "Sure you can read ™somewhere™ which control sequence your `$TERM` will use to clear the screen and then manually turn that into this string alike integer, but you'd have to do that anew everywhere you run your code attached to a differently behaving terminal emulation. So catching the sequence from `clear`, similar `tput` commands or directly looking it up in Terminfo or Termcap terminal definitions makes it portable and I assumed catching the output of `clear` would be the easiest of these alternatives.\n\nUsing `xxd` may limit portability a bit. That's probably the weak spot of this idea. There are alternatives, but if available, `xxd` seems to fit this job best.",
+ "createdAt": "2025-04-21T10:18:17Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/91#issuecomment-2818110063",
+ "viewerDidAuthor": false
+ }
+ ],
+ "createdAt": "2025-04-18T20:41:22Z",
+ "id": "I_kwDOCL0xJc6zJ4VJ",
+ "isPinned": false,
+ "labels": [],
+ "milestone": null,
+ "number": 91,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [
+ {
+ "content": "THUMBS_UP",
+ "users": {
+ "totalCount": 1
+ }
+ }
+ ],
+ "state": "CLOSED",
+ "stateReason": "COMPLETED",
+ "title": "add ! command",
+ "updatedAt": "2025-04-21T10:18:18Z",
+ "url": "https://github.com/gavinhoward/bc/issues/91"
+ },
+ {
+ "assignees": [],
+ "author": {
+ "id": "U_kgDOB8YAUQ",
+ "is_bot": false,
+ "login": "tkb-github",
+ "name": ""
+ },
+ "body": "With the release of RHEL 10 and Fedora 42, we’ve tried to build 7.0.3 on both. No issues with Clang.\n\nWith GCC (v14 for RHEL 10, v15 for Fedora 42), though, the build succeeded under the [epel-10-aarch64](https://download.copr.fedorainfracloud.org/results/tkbcopr/bc-gh/epel-10-aarch64/09140402-bc-gh/) chroot but failed with [epel-10-x86_64](https://download.copr.fedorainfracloud.org/results/tkbcopr/bc-gh/epel-10-x86_64/09140402-bc-gh/), [fedora-42-x86_64](https://download.copr.fedorainfracloud.org/results/tkbcopr/bc-gh/fedora-42-x86_64/09140402-bc-gh/) and [fedora-42-aarch64](https://download.copr.fedorainfracloud.org/results/tkbcopr/bc-gh/fedora-42-aarch64/09140402-bc-gh/). Here’s excerpts from the failed builds.\n\nepel-10-x86_64:\n\n```sh\n\ngcc -DBC_ENABLED=1 -DDC_ENABLED=1 -I./include/ -DBUILD_TYPE=A -DEXECPREFIX= -DMAINEXEC=bc -DBC_NUM_KARATSUBA_LEN=34 -DBC_ENABLE_NLS=1 -DBC_ENABLE_EXTRA_MATH=1 -DBC_ENABLE_HISTORY=1 -DBC_ENABLE_LIBRARY=0 -DBC_ENABLE_MEMCHECK=0 -DBC_ENABLE_AFL=0 -DBC_ENABLE_OSSFUZZ=0 -DBC_DEFAULT_BANNER=0 -DBC_DEFAULT_SIGINT_RESET=1 -DBC_DEFAULT_TTY_MODE=1 -DBC_DEFAULT_PROMPT=1 -DBC_DEFAULT_EXPR_EXIT=1 -DBC_DEFAULT_DIGIT_CLAMP=0 -DDC_DEFAULT_SIGINT_RESET=1 -DDC_DEFAULT_TTY_MODE=0 -DDC_DEFAULT_PROMPT=0 -DDC_DEFAULT_EXPR_EXIT=1 -DDC_DEFAULT_DIGIT_CLAMP=0 -DBC_ENABLE_EDITLINE=0 -DBC_ENABLE_READLINE=0 -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -O3 -g -flto ./gen/dc_help.o ./gen/bc_help.o ./gen/lib.o ./gen/lib2.o src/args.o src/bc.o src/bc_lex.o src/bc_parse.o src/data.o src/dc.o src/dc_lex.o src/dc_parse.o src/file.o src/history.o src/lang.o src/lex.o src/main.o src/num.o src/opt.o src/parse.o src/program.o src/rand.o src/read.o src/vector.o src/vm.o -Wl,-z,relro -Wl,--as-needed -Wl,-z,pack-relative-relocs -Wl,-z,now -specs=/usr/lib/rpm/redhat/redhat-hardened-ld -specs=/usr/lib/rpm/redhat/redhat-hardened-ld-errors -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -Wl,--build-id=sha1 -specs=/usr/lib/rpm/redhat/redhat-package-notes -o bin/bc\n/usr/bin/ld: /tmp/ccMAotuu.ltrans0.ltrans.o: relocation R_X86_64_32 against `.rodata' can not be used when making a PIE object; recompile with -fPIE\n/usr/bin/ld: failed to set dynamic section sizes: bad value\ncollect2: error: ld returned 1 exit status\nmake: *** [Makefile:241: bin/bc] Error 1\nerror: Bad exit status from /var/tmp/rpm-tmp.G7xvMo (%build)\n\nRPM build errors:\n Bad exit status from /var/tmp/rpm-tmp.G7xvMo (%build)\n\n```\n\nfedora-42-x86_64\n\n```sh\n\ngcc -DBC_ENABLED=1 -DDC_ENABLED=1 -I./include/ -DBUILD_TYPE=A -DEXECPREFIX= -DMAINEXEC=bc -DBC_NUM_KARATSUBA_LEN=34 -DBC_ENABLE_NLS=1 -DBC_ENABLE_EXTRA_MATH=1 -DBC_ENABLE_HISTORY=1 -DBC_ENABLE_LIBRARY=0 -DBC_ENABLE_MEMCHECK=0 -DBC_ENABLE_AFL=0 -DBC_ENABLE_OSSFUZZ=0 -DBC_DEFAULT_BANNER=0 -DBC_DEFAULT_SIGINT_RESET=1 -DBC_DEFAULT_TTY_MODE=1 -DBC_DEFAULT_PROMPT=1 -DBC_DEFAULT_EXPR_EXIT=1 -DBC_DEFAULT_DIGIT_CLAMP=0 -DDC_DEFAULT_SIGINT_RESET=1 -DDC_DEFAULT_TTY_MODE=0 -DDC_DEFAULT_PROMPT=0 -DDC_DEFAULT_EXPR_EXIT=1 -DDC_DEFAULT_DIGIT_CLAMP=0 -DBC_ENABLE_EDITLINE=0 -DBC_ENABLE_READLINE=0 -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -O3 -g -flto -o src/data.o -c ./src/data.c\nIn file included from /usr/lib/gcc/x86_64-redhat-linux/15/include/stdint.h:11,\n from ./include/status.h:46,\n from ./include/args.h:39,\n from ./src/data.c:39:\n./src/data.c:920:29: error: â^@^XfalseULâ^@^Y undeclared here (not in a function)\n 920 | BC_PARSE_EXPR_ENTRY(false, false, true, true, true, true, true, true),\n | ^~~~~\n./src/data.c:920:9: note: in expansion of macro â^@^XBC_PARSE_EXPR_ENTRYâ^@^Y\n 920 | BC_PARSE_EXPR_ENTRY(false, false, true, true, true, true, true, true),\n | ^~~~~~~~~~~~~~~~~~~\n./src/data.c:920:43: error: â^@^XtrueULâ^@^Y undeclared here (not in a function)\n 920 | BC_PARSE_EXPR_ENTRY(false, false, true, true, true, true, true, true),\n | ^~~~\n./src/data.c:920:9: note: in expansion of macro â^@^XBC_PARSE_EXPR_ENTRYâ^@^Y\n 920 | BC_PARSE_EXPR_ENTRY(false, false, true, true, true, true, true, true),\n | ^~~~~~~~~~~~~~~~~~~\nmake: *** [Makefile:668: src/data.o] Error 1\ngcc -DBC_ENABLED=1 -DDC_ENABLED=1 -I./include/ -DBUILD_TYPE=A -DEXECPREFIX= -DMAINEXEC=bc -DBC_NUM_KARATSUBA_LEN=34 -DBC_ENABLE_NLS=1 -DBC_ENABLE_EXTRA_MATH=1 -DBC_ENABLE_HISTORY=1 -DBC_ENABLE_LIBRARY=0 -DBC_ENABLE_MEMCHECK=0 -DBC_ENABLE_AFL=0 -DBC_ENABLE_OSSFUZZ=0 -DBC_DEFAULT_BANNER=0 -DBC_DEFAULT_SIGINT_RESET=1 -DBC_DEFAULT_TTY_MODE=1 -DBC_DEFAULT_PROMPT=1 -DBC_DEFAULT_EXPR_EXIT=1 -DBC_DEFAULT_DIGIT_CLAMP=0 -DDC_DEFAULT_SIGINT_RESET=1 -DDC_DEFAULT_TTY_MODE=0 -DDC_DEFAULT_PROMPT=0 -DDC_DEFAULT_EXPR_EXIT=1 -DDC_DEFAULT_DIGIT_CLAMP=0 -DBC_ENABLE_EDITLINE=0 -DBC_ENABLE_READLINE=0 -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -O3 -g -flto -o src/dc.o -c ./src/dc.c\nmake: *** Waiting for unfinished jobs....\ngcc -DBC_ENABLE_AFL=0 -DBC_ENABLE_OSSFUZZ=0 -I./include/ -flto -o ./gen/strgen ./gen/strgen.c\ngcc -DBC_ENABLED=1 -DDC_ENABLED=1 -I./include/ -DBUILD_TYPE=A -DEXECPREFIX= -DMAINEXEC=bc -DBC_NUM_KARATSUBA_LEN=34 -DBC_ENABLE_NLS=1 -DBC_ENABLE_EXTRA_MATH=1 -DBC_ENABLE_HISTORY=1 -DBC_ENABLE_LIBRARY=0 -DBC_ENABLE_MEMCHECK=0 -DBC_ENABLE_AFL=0 -DBC_ENABLE_OSSFUZZ=0 -DBC_DEFAULT_BANNER=0 -DBC_DEFAULT_SIGINT_RESET=1 -DBC_DEFAULT_TTY_MODE=1 -DBC_DEFAULT_PROMPT=1 -DBC_DEFAULT_EXPR_EXIT=1 -DBC_DEFAULT_DIGIT_CLAMP=0 -DDC_DEFAULT_SIGINT_RESET=1 -DDC_DEFAULT_TTY_MODE=0 -DDC_DEFAULT_PROMPT=0 -DDC_DEFAULT_EXPR_EXIT=1 -DDC_DEFAULT_DIGIT_CLAMP=0 -DBC_ENABLE_EDITLINE=0 -DBC_ENABLE_READLINE=0 -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -O3 -g -flto -o src/bc_parse.o -c ./src/bc_parse.c\nerror: Bad exit status from /var/tmp/rpm-tmp.hRViel (%build)\n\nRPM build errors:\n Bad exit status from /var/tmp/rpm-tmp.hRViel (%build)\n\n```\n\nfedora-42-aarch64\n\n```sh\n\ngcc -DBC_ENABLED=1 -DDC_ENABLED=1 -I./include/ -DBUILD_TYPE=A -DEXECPREFIX= -DMAINEXEC=bc -DBC_NUM_KARATSUBA_LEN=34 -DBC_ENABLE_NLS=1 -DBC_ENABLE_EXTRA_MATH=1 -DBC_ENABLE_HISTORY=1 -DBC_ENABLE_LIBRARY=0 -DBC_ENABLE_MEMCHECK=0 -DBC_ENABLE_AFL=0 -DBC_ENABLE_OSSFUZZ=0 -DBC_DEFAULT_BANNER=0 -DBC_DEFAULT_SIGINT_RESET=1 -DBC_DEFAULT_TTY_MODE=1 -DBC_DEFAULT_PROMPT=1 -DBC_DEFAULT_EXPR_EXIT=1 -DBC_DEFAULT_DIGIT_CLAMP=0 -DDC_DEFAULT_SIGINT_RESET=1 -DDC_DEFAULT_TTY_MODE=0 -DDC_DEFAULT_PROMPT=0 -DDC_DEFAULT_EXPR_EXIT=1 -DDC_DEFAULT_DIGIT_CLAMP=0 -DBC_ENABLE_EDITLINE=0 -DBC_ENABLE_READLINE=0 -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -O3 -g -flto -o src/data.o -c ./src/data.c\nIn file included from /usr/lib/gcc/aarch64-redhat-linux/15/include/stdint.h:11,\n from ./include/status.h:46,\n from ./include/args.h:39,\n from ./src/data.c:39:\n./src/data.c:920:29: error: â^@^XfalseULâ^@^Y undeclared here (not in a function)\n 920 | BC_PARSE_EXPR_ENTRY(false, false, true, true, true, true, true, true),\n | ^~~~~\n./src/data.c:920:9: note: in expansion of macro â^@^XBC_PARSE_EXPR_ENTRYâ^@^Y\n 920 | BC_PARSE_EXPR_ENTRY(false, false, true, true, true, true, true, true),\n | ^~~~~~~~~~~~~~~~~~~\n./src/data.c:920:43: error: â^@^XtrueULâ^@^Y undeclared here (not in a function)\n 920 | BC_PARSE_EXPR_ENTRY(false, false, true, true, true, true, true, true),\n | ^~~~\n./src/data.c:920:9: note: in expansion of macro â^@^XBC_PARSE_EXPR_ENTRYâ^@^Y\n 920 | BC_PARSE_EXPR_ENTRY(false, false, true, true, true, true, true, true),\n | ^~~~~~~~~~~~~~~~~~~\nmake: *** [Makefile:668: src/data.o] Error 1\nmake: *** Waiting for unfinished jobs....\ngcc -DBC_ENABLED=1 -DDC_ENABLED=1 -I./include/ -DBUILD_TYPE=A -DEXECPREFIX= -DMAINEXEC=bc -DBC_NUM_KARATSUBA_LEN=34 -DBC_ENABLE_NLS=1 -DBC_ENABLE_EXTRA_MATH=1 -DBC_ENABLE_HISTORY=1 -DBC_ENABLE_LIBRARY=0 -DBC_ENABLE_MEMCHECK=0 -DBC_ENABLE_AFL=0 -DBC_ENABLE_OSSFUZZ=0 -DBC_DEFAULT_BANNER=0 -DBC_DEFAULT_SIGINT_RESET=1 -DBC_DEFAULT_TTY_MODE=1 -DBC_DEFAULT_PROMPT=1 -DBC_DEFAULT_EXPR_EXIT=1 -DBC_DEFAULT_DIGIT_CLAMP=0 -DDC_DEFAULT_SIGINT_RESET=1 -DDC_DEFAULT_TTY_MODE=0 -DDC_DEFAULT_PROMPT=0 -DDC_DEFAULT_EXPR_EXIT=1 -DDC_DEFAULT_DIGIT_CLAMP=0 -DBC_ENABLE_EDITLINE=0 -DBC_ENABLE_READLINE=0 -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -O3 -g -flto -o src/dc.o -c ./src/dc.c\ngcc -DBC_ENABLED=1 -DDC_ENABLED=1 -I./include/ -DBUILD_TYPE=A -DEXECPREFIX= -DMAINEXEC=bc -DBC_NUM_KARATSUBA_LEN=34 -DBC_ENABLE_NLS=1 -DBC_ENABLE_EXTRA_MATH=1 -DBC_ENABLE_HISTORY=1 -DBC_ENABLE_LIBRARY=0 -DBC_ENABLE_MEMCHECK=0 -DBC_ENABLE_AFL=0 -DBC_ENABLE_OSSFUZZ=0 -DBC_DEFAULT_BANNER=0 -DBC_DEFAULT_SIGINT_RESET=1 -DBC_DEFAULT_TTY_MODE=1 -DBC_DEFAULT_PROMPT=1 -DBC_DEFAULT_EXPR_EXIT=1 -DBC_DEFAULT_DIGIT_CLAMP=0 -DDC_DEFAULT_SIGINT_RESET=1 -DDC_DEFAULT_TTY_MODE=0 -DDC_DEFAULT_PROMPT=0 -DDC_DEFAULT_EXPR_EXIT=1 -DDC_DEFAULT_DIGIT_CLAMP=0 -DBC_ENABLE_EDITLINE=0 -DBC_ENABLE_READLINE=0 -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -O3 -g -flto -o src/bc_parse.o -c ./src/bc_parse.c\ngcc -DBC_ENABLE_AFL=0 -DBC_ENABLE_OSSFUZZ=0 -I./include/ -flto -o ./gen/strgen ./gen/strgen.c\nerror: Bad exit status from /var/tmp/rpm-tmp.5cLuhE (%build)\n\nRPM build errors:\n Bad exit status from /var/tmp/rpm-tmp.5cLuhE (%build)\n\n```",
+ "closed": true,
+ "closedAt": "2025-06-08T22:18:49Z",
+ "comments": [
+ {
+ "id": "IC_kwDOCL0xJc6v8kNF",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "For epel-10-x86_64, just add `-fPIE` to the `CFLAGS`, like so:\n\n```\n$ CFLAGS=-fpie ./configure.sh\n$ make\n```\n\nFor the other two, it appears `stdbool.h` does not properly define `false` and `true`.",
+ "createdAt": "2025-06-07T05:46:33Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/92#issuecomment-2951889733",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc6wDPLy",
+ "author": {
+ "login": "tkb-github"
+ },
+ "authorAssociation": "NONE",
+ "body": "Thanks!",
+ "createdAt": "2025-06-08T06:52:46Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/issues/92#issuecomment-2953638642",
+ "viewerDidAuthor": false
+ }
+ ],
+ "createdAt": "2025-06-07T03:51:28Z",
+ "id": "I_kwDOCL0xJc66WQa1",
+ "isPinned": false,
+ "labels": [],
+ "milestone": null,
+ "number": 92,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "state": "CLOSED",
+ "stateReason": "COMPLETED",
+ "title": "Build failures under RHEL 10 & Fedora 42 with GCC",
+ "updatedAt": "2025-06-08T22:18:49Z",
+ "url": "https://github.com/gavinhoward/bc/issues/92"
+ }
+]
diff --git a/contrib/bc/project/github_prs.json b/contrib/bc/project/github_prs.json
new file mode 100644
index 000000000000..c2ca634c3554
--- /dev/null
+++ b/contrib/bc/project/github_prs.json
@@ -0,0 +1,7729 @@
+[
+ {
+ "additions": 8,
+ "assignees": [],
+ "author": {
+ "id": "MDQ6VXNlcjM5MjUwOQ==",
+ "is_bot": false,
+ "login": "bolknote",
+ "name": "Evgeny Stepanischev"
+ },
+ "autoMergeRequest": null,
+ "baseRefName": "master",
+ "baseRefOid": "9f34d2427d3f13c8e95b8485f5a6041789ffe400",
+ "body": "Code:\r\n`define a(*t[], t[]) {}`\r\n\r\nExpected result:\r\nParse error: function parameter or auto \"t[]\" already exists\r\n\r\nActual result:\r\nParses successfully (incorrect)\r\n\r\nThe interpreter now correctly rejects functions with duplicate parameter names, even when one is a reference array (*t[]).",
+ "changedFiles": 3,
+ "closed": true,
+ "closedAt": "2025-03-25T04:55:24Z",
+ "comments": [
+ {
+ "id": "IC_kwDOCL0xJc6j52QL",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Oh, and another thing you could get credit for: if you create `tests/bc/errors/39.txt` and put your reproducer into it, I would accept that too.",
+ "createdAt": "2025-03-25T02:01:13Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/89#issuecomment-2749850635",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc6j6h5M",
+ "author": {
+ "login": "bolknote"
+ },
+ "authorAssociation": "CONTRIBUTOR",
+ "body": "Yes, no problem! I hope I’ve managed to cover everything needed. If something isn’t quite right, please let me know!",
+ "createdAt": "2025-03-25T04:16:09Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/89#issuecomment-2750029388",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc6j6uej",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Looks good!",
+ "createdAt": "2025-03-25T04:55:31Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/89#issuecomment-2750080931",
+ "viewerDidAuthor": true
+ }
+ ],
+ "commits": [
+ {
+ "authoredDate": "2025-03-24T22:31:38Z",
+ "authors": [
+ {
+ "email": "imbolk@gmail.com",
+ "id": "MDQ6VXNlcjM5MjUwOQ==",
+ "login": "bolknote",
+ "name": "Evgeny Stepanischev"
+ }
+ ],
+ "committedDate": "2025-03-24T22:31:38Z",
+ "messageBody": "",
+ "messageHeadline": "Fix duplicate param check for ref arrays",
+ "oid": "97756f3dc17b74c2a9c31488e8680a03f2f016eb"
+ },
+ {
+ "authoredDate": "2025-03-25T04:07:25Z",
+ "authors": [
+ {
+ "email": "imbolk@gmail.com",
+ "id": "MDQ6VXNlcjM5MjUwOQ==",
+ "login": "bolknote",
+ "name": "Evgeny Stepanischev"
+ }
+ ],
+ "committedDate": "2025-03-25T04:07:25Z",
+ "messageBody": "",
+ "messageHeadline": "Fix coding style",
+ "oid": "facbd16ceecdd72015abe21f6f4fe4d686b8e66b"
+ },
+ {
+ "authoredDate": "2025-03-25T04:10:52Z",
+ "authors": [
+ {
+ "email": "imbolk@gmail.com",
+ "id": "MDQ6VXNlcjM5MjUwOQ==",
+ "login": "bolknote",
+ "name": "Evgeny Stepanischev"
+ }
+ ],
+ "committedDate": "2025-03-25T04:10:52Z",
+ "messageBody": "This test ensures the interpreter correctly rejects functions with\nduplicate parameter names, even when one is a reference array (*t[]).",
+ "messageHeadline": "Add test for duplicate ref array parameters",
+ "oid": "a10b2fd6a2b77325dc981a8ce296d1c22c47ba0a"
+ }
+ ],
+ "createdAt": "2025-03-24T22:35:24Z",
+ "deletions": 2,
+ "files": [
+ {
+ "path": "include/lang.h",
+ "additions": 4,
+ "deletions": 0
+ },
+ {
+ "path": "src/lang.c",
+ "additions": 3,
+ "deletions": 2
+ },
+ {
+ "path": "tests/bc/errors/39.txt",
+ "additions": 1,
+ "deletions": 0
+ }
+ ],
+ "fullDatabaseId": "2414968976",
+ "headRefName": "master",
+ "headRefOid": "a10b2fd6a2b77325dc981a8ce296d1c22c47ba0a",
+ "headRepository": {
+ "id": "R_kgDOOARsdw",
+ "name": "bc"
+ },
+ "headRepositoryOwner": {
+ "id": "MDQ6VXNlcjM5MjUwOQ==",
+ "name": "Evgeny Stepanischev",
+ "login": "bolknote"
+ },
+ "id": "PR_kwDOCL0xJc6P8YCQ",
+ "isCrossRepository": true,
+ "isDraft": false,
+ "labels": [],
+ "latestReviews": [
+ {
+ "id": "",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "I'll be upfront: I have accepted this because it passes the tests, and yes, it is a bug.\r\n\r\nHowever, if you run `./scripts/format.sh` on the repo before I do that, I won't have to adjust the style, which would hide your contribution for the `if` statement in `src/lang.c`.\r\n\r\nIf you could do that, I'd appreciate it, and you would get to keep your credit. If not, no problem; just tell me.",
+ "submittedAt": "2025-03-25T01:55:21Z",
+ "includesCreatedEdit": false,
+ "reactionGroups": [],
+ "state": "CHANGES_REQUESTED",
+ "commit": {
+ "oid": ""
+ }
+ }
+ ],
+ "maintainerCanModify": false,
+ "mergeCommit": {
+ "oid": "0013dbee105fad63633ffe11240b18deb3d4bf9e"
+ },
+ "mergeStateStatus": "UNKNOWN",
+ "mergeable": "UNKNOWN",
+ "mergedAt": "2025-03-25T04:55:24Z",
+ "mergedBy": {
+ "id": "MDQ6VXNlcjMxNzI2ODc=",
+ "is_bot": false,
+ "login": "gavinhoward",
+ "name": "Gavin D. Howard"
+ },
+ "milestone": null,
+ "number": 89,
+ "potentialMergeCommit": null,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "reviewDecision": "CHANGES_REQUESTED",
+ "reviewRequests": [],
+ "reviews": [
+ {
+ "id": "PRR_kwDOCL0xJc6hpxI0",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "I'll be upfront: I have accepted this because it passes the tests, and yes, it is a bug.\r\n\r\nHowever, if you run `./scripts/format.sh` on the repo before I do that, I won't have to adjust the style, which would hide your contribution for the `if` statement in `src/lang.c`.\r\n\r\nIf you could do that, I'd appreciate it, and you would get to keep your credit. If not, no problem; just tell me.",
+ "submittedAt": "2025-03-25T01:55:21Z",
+ "includesCreatedEdit": false,
+ "reactionGroups": [],
+ "state": "CHANGES_REQUESTED",
+ "commit": {
+ "oid": "97756f3dc17b74c2a9c31488e8680a03f2f016eb"
+ }
+ }
+ ],
+ "state": "MERGED",
+ "statusCheckRollup": [],
+ "title": "Fix duplicate param check for ref arrays",
+ "updatedAt": "2025-03-25T04:55:32Z",
+ "url": "https://github.com/gavinhoward/bc/pull/89"
+ },
+ {
+ "additions": 21,
+ "assignees": [],
+ "author": {
+ "id": "MDQ6VXNlcjM5MjUwOQ==",
+ "is_bot": false,
+ "login": "bolknote",
+ "name": "Evgeny Stepanischev"
+ },
+ "autoMergeRequest": null,
+ "baseRefName": "master",
+ "baseRefOid": "79d5bffaa1c74888d62c47342e5b99a96797a8b5",
+ "body": "```\r\nsrand = 10\r\n\r\nfor (i = 0; i < 1000000; i++) {\r\n\ta = irand(100000000)\r\n\tb = irand(100000000)\r\n\r\n\t. = new_band(a, b)\r\n}\r\n```\r\n\r\ntime bc -l band.bc\r\n\r\nreal\t0m12.768s\r\nuser\t0m12.735s\r\nsys\t0m0.030s\r\n\r\n```\r\nsrand = 10\r\n\r\nfor (i = 0; i < 1000000; i++) {\r\n\ta = irand(100000000)\r\n\tb = irand(100000000)\r\n\r\n\t. = band(a, b)\r\n}\r\n```\r\ntime bc -l band.bc\r\n\r\nreal\t0m25.416s\r\nuser\t0m25.270s\r\nsys\t0m0.119s",
+ "changedFiles": 1,
+ "closed": true,
+ "closedAt": "2025-03-01T05:22:26Z",
+ "comments": [
+ {
+ "id": "IC_kwDOCL0xJc6gdBpI",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "After convincing myself that your changes are correct, I accept them.\r\n\r\nHowever, I *am* going to adjust the style; the lib2 file gets put into the executable as a string, and I want to eliminate useless characters.",
+ "createdAt": "2025-03-01T05:22:29Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/88#issuecomment-2691963464",
+ "viewerDidAuthor": true
+ }
+ ],
+ "commits": [
+ {
+ "authoredDate": "2025-02-27T06:55:52Z",
+ "authors": [
+ {
+ "email": "imbolk@gmail.com",
+ "id": "MDQ6VXNlcjM5MjUwOQ==",
+ "login": "bolknote",
+ "name": "Evgeny Stepanischev"
+ }
+ ],
+ "committedDate": "2025-02-27T06:55:52Z",
+ "messageBody": "",
+ "messageHeadline": "The band() function has been accelerated almost 2x",
+ "oid": "4112814ae6456dd8d0455a1a6b1d06f7ab78d59a"
+ }
+ ],
+ "createdAt": "2025-02-27T07:03:09Z",
+ "deletions": 19,
+ "files": [
+ {
+ "path": "gen/lib2.bc",
+ "additions": 21,
+ "deletions": 19
+ }
+ ],
+ "fullDatabaseId": "2361404896",
+ "headRefName": "master",
+ "headRefOid": "4112814ae6456dd8d0455a1a6b1d06f7ab78d59a",
+ "headRepository": {
+ "id": "R_kgDOOARsdw",
+ "name": "bc"
+ },
+ "headRepositoryOwner": {
+ "id": "MDQ6VXNlcjM5MjUwOQ==",
+ "name": "Evgeny Stepanischev",
+ "login": "bolknote"
+ },
+ "id": "PR_kwDOCL0xJc6MwC3g",
+ "isCrossRepository": true,
+ "isDraft": false,
+ "labels": [],
+ "latestReviews": [],
+ "maintainerCanModify": false,
+ "mergeCommit": {
+ "oid": "6c12610da98b69e873702479e49218e2944437da"
+ },
+ "mergeStateStatus": "UNKNOWN",
+ "mergeable": "UNKNOWN",
+ "mergedAt": "2025-03-01T05:22:26Z",
+ "mergedBy": {
+ "id": "MDQ6VXNlcjMxNzI2ODc=",
+ "is_bot": false,
+ "login": "gavinhoward",
+ "name": "Gavin D. Howard"
+ },
+ "milestone": null,
+ "number": 88,
+ "potentialMergeCommit": null,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "reviewDecision": "",
+ "reviewRequests": [],
+ "reviews": [],
+ "state": "MERGED",
+ "statusCheckRollup": [],
+ "title": "The band() function has been accelerated almost 2x",
+ "updatedAt": "2025-03-01T05:22:30Z",
+ "url": "https://github.com/gavinhoward/bc/pull/88"
+ },
+ {
+ "additions": 8,
+ "assignees": [],
+ "author": {
+ "id": "MDQ6VXNlcjQ5NzIxNTU=",
+ "is_bot": false,
+ "login": "dag-erling",
+ "name": "Dag-Erling Smørgrav"
+ },
+ "autoMergeRequest": null,
+ "baseRefName": "master",
+ "baseRefOid": "ca389548763c40852040e5c028b0869b47a3710f",
+ "body": "Previously, we would catch `SIGWINCH` and call `el_resize()` from the signal handler. This is unsafe and led to strange behavior such as terminating on second resize. The simplest solution is to let libedit handle `SIGWINCH` itself.\r\n\r\nThis reverts 56bb18255a24 and 89d6c3451a60 and removes all traces of `SIGWINCH` from bc itself, and instead sets the `EL_SIGNAL` flag on the editline context, which causes libedit to detect and handle terminal size changes internally.",
+ "changedFiles": 5,
+ "closed": true,
+ "closedAt": "2025-01-09T03:21:56Z",
+ "comments": [
+ {
+ "id": "IC_kwDOCL0xJc6UKjDG",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Thank you for your contribution.\r\n\r\nHowever, I cannot accept it. The man page for libedit says that it installs its own signal handler for a set of signals, one of which is `SIGINT`, which `bc` needs to treat specially. So I cannot use `EL_SIGNAL`. If libedit only handled `SIGWINCH`, this would work.",
+ "createdAt": "2024-11-19T13:57:00Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/86#issuecomment-2485792966",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc6UKzkD",
+ "author": {
+ "login": "dag-erling"
+ },
+ "authorAssociation": "CONTRIBUTOR",
+ "body": "Did you even test my patch? It works just fine, because (except for `SIGWINCH` and `SIGCONT`) libedit restores the previous signal handler and re-posts the signal after cleaning up.",
+ "createdAt": "2024-11-19T14:24:15Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/86#issuecomment-2485860611",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc6ULIip",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "I tested it on macOS. It did not work with `SIGINT`. I do not have access to a Linux machine, but I will later in the day.\r\n\r\nBut I do not like depending on that behavior. Signals are finicky, and that could expose platform differences.",
+ "createdAt": "2024-11-19T14:56:46Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/86#issuecomment-2485946537",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc6ULaEs",
+ "author": {
+ "login": "dag-erling"
+ },
+ "authorAssociation": "CONTRIBUTOR",
+ "body": "I developed the patch on macOS, it works perfectly fine there and on FreeBSD. If you have an issue with it, perhaps you can describe the symptoms so I can help you figure it out instead of dismissing it out of hand?",
+ "createdAt": "2024-11-19T15:22:57Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/86#issuecomment-2486018348",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc6UMfC7",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Please give me some leniency. Testing it is not dismissing it out of hand. I am wary of making changes that may break things. And I have little time.\r\n\r\nHowever, in this case, I decided to test my code as well and found that it had the same problem: that multiple interrupts would not be handled. I found the fix, and it works for your code.\r\n\r\nIt make still take me time to test on Linux and FreeBSD, but this patch may still be viable.",
+ "createdAt": "2024-11-19T17:16:46Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/86#issuecomment-2486300859",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc6Zugh4",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Tested on Linux, FreeBSD, and macOS. With my fix, it works. There should be a release out soon-ish.\r\n\r\nThank you for your patience.",
+ "createdAt": "2025-01-09T03:22:42Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/86#issuecomment-2579105912",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc6aDHFg",
+ "author": {
+ "login": "dag-erling"
+ },
+ "authorAssociation": "CONTRIBUTOR",
+ "body": "Thank you!",
+ "createdAt": "2025-01-10T22:18:03Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/86#issuecomment-2584506720",
+ "viewerDidAuthor": false
+ }
+ ],
+ "commits": [
+ {
+ "authoredDate": "2024-11-12T11:50:39Z",
+ "authors": [
+ {
+ "email": "des@des.dev",
+ "id": "MDQ6VXNlcjQ5NzIxNTU=",
+ "login": "dag-erling",
+ "name": "Dag-Erling Smørgrav"
+ }
+ ],
+ "committedDate": "2024-11-12T11:59:50Z",
+ "messageBody": "Previously, we would catch SIGWINCH and call el_resize() from the signal handler. This is unsafe and led to strange behavior such as terminating on second resize. The simplest solution is to let libedit handle SIGWINCH itself.\n\nThis reverts 56bb18255a24 and 89d6c3451a60 and removes all traces of SIGWINCH from bc itself, and instead sets the EL_SIGNAL flag on the editline context, which causes libedit to detect and handle terminal size changes internally.",
+ "messageHeadline": "Let libedit handle terminal size changes.",
+ "oid": "e671399e9dc92181219da87e3aa02d44a0b4a4c3"
+ }
+ ],
+ "createdAt": "2024-11-12T12:02:29Z",
+ "deletions": 79,
+ "files": [
+ {
+ "path": "include/history.h",
+ "additions": 0,
+ "deletions": 24
+ },
+ {
+ "path": "include/status.h",
+ "additions": 0,
+ "deletions": 5
+ },
+ {
+ "path": "src/history.c",
+ "additions": 2,
+ "deletions": 12
+ },
+ {
+ "path": "src/read.c",
+ "additions": 6,
+ "deletions": 12
+ },
+ {
+ "path": "src/vm.c",
+ "additions": 0,
+ "deletions": 26
+ }
+ ],
+ "fullDatabaseId": "2174734014",
+ "headRefName": "des/sigwinch",
+ "headRefOid": "e671399e9dc92181219da87e3aa02d44a0b4a4c3",
+ "headRepository": {
+ "id": "R_kgDONOIgRA",
+ "name": "bc"
+ },
+ "headRepositoryOwner": {
+ "id": "MDQ6VXNlcjQ5NzIxNTU=",
+ "name": "Dag-Erling Smørgrav",
+ "login": "dag-erling"
+ },
+ "id": "PR_kwDOCL0xJc6Bn86-",
+ "isCrossRepository": true,
+ "isDraft": false,
+ "labels": [],
+ "latestReviews": [],
+ "maintainerCanModify": false,
+ "mergeCommit": {
+ "oid": "79d5bffaa1c74888d62c47342e5b99a96797a8b5"
+ },
+ "mergeStateStatus": "UNKNOWN",
+ "mergeable": "UNKNOWN",
+ "mergedAt": "2025-01-09T03:21:55Z",
+ "mergedBy": {
+ "id": "MDQ6VXNlcjMxNzI2ODc=",
+ "is_bot": false,
+ "login": "gavinhoward",
+ "name": "Gavin D. Howard"
+ },
+ "milestone": null,
+ "number": 86,
+ "potentialMergeCommit": null,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "reviewDecision": "",
+ "reviewRequests": [],
+ "reviews": [],
+ "state": "MERGED",
+ "statusCheckRollup": [],
+ "title": "Let libedit handle terminal size changes.",
+ "updatedAt": "2025-01-10T22:18:04Z",
+ "url": "https://github.com/gavinhoward/bc/pull/86"
+ },
+ {
+ "additions": 12,
+ "assignees": [],
+ "author": {
+ "id": "U_kgDOCgjy1g",
+ "is_bot": false,
+ "login": "henke9600",
+ "name": ""
+ },
+ "autoMergeRequest": null,
+ "baseRefName": "master",
+ "baseRefOid": "c5b7724ee07daa729f0123c6024223b834de42e3",
+ "body": "It's possible that the warning being ignored wasn't enabled in the first place, so always enabling it again is wrong.\r\n\r\nIn practice, this resulted in unexpected -Wdisabled-macro-expansion warnings when compiling against musl libc.",
+ "changedFiles": 3,
+ "closed": true,
+ "closedAt": "2024-09-21T19:14:25Z",
+ "comments": [
+ {
+ "id": "IC_kwDOCL0xJc6M-4FI",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "I have no comments. You followed style, and I agree that this was a problem. In fact, I had been intending to fix it, but I kept forgetting.\r\n\r\nThe question now is whether I should release. Do you know of any musl-based distros that use this `bc`? If not, I may not worry about releasing.",
+ "createdAt": "2024-09-21T19:15:47Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/84#issuecomment-2365292872",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc6M-6p8",
+ "author": {
+ "login": "henke9600"
+ },
+ "authorAssociation": "CONTRIBUTOR",
+ "body": "Not sure. I use it in my hobby distro, but i can live with the warning :slightly_smiling_face: ",
+ "createdAt": "2024-09-21T19:57:46Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/84#issuecomment-2365303420",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc6M_b08",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": ":)\r\n\r\nI think I'll do a release soonish for Gentoo's sake; technically, Gentoo users can use musl.",
+ "createdAt": "2024-09-22T03:16:02Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/84#issuecomment-2365439292",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc6NRQWz",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "`7.0.3` is out. I hope it works for you.",
+ "createdAt": "2024-09-24T04:12:15Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/84#issuecomment-2370110899",
+ "viewerDidAuthor": true
+ }
+ ],
+ "commits": [
+ {
+ "authoredDate": "2024-09-21T15:30:32Z",
+ "authors": [
+ {
+ "email": "henrik@lxm.se",
+ "id": "U_kgDOCgjy1g",
+ "login": "henke9600",
+ "name": "Henrik Lindström"
+ }
+ ],
+ "committedDate": "2024-09-21T15:47:44Z",
+ "messageBody": "It's possible that the warning being ignored wasn't enabled in the first\nplace, so always enabling it again is wrong.\n\nIn practice, this resulted in unexpected -Wdisabled-macro-expansion warnings\nwhen compiling against musl libc.\n\nSigned-off-by: Henrik Lindström <henrik@lxm.se>",
+ "messageHeadline": "Don't unconditionally enable warnings after ignoring them",
+ "oid": "16c4f913ed0935a878ce2644aab2ccc0da05c8d0"
+ }
+ ],
+ "createdAt": "2024-09-21T15:49:45Z",
+ "deletions": 6,
+ "files": [
+ {
+ "path": "src/file.c",
+ "additions": 2,
+ "deletions": 1
+ },
+ {
+ "path": "src/program.c",
+ "additions": 8,
+ "deletions": 4
+ },
+ {
+ "path": "src/vm.c",
+ "additions": 2,
+ "deletions": 1
+ }
+ ],
+ "fullDatabaseId": "2084576864",
+ "headRefName": "master",
+ "headRefOid": "16c4f913ed0935a878ce2644aab2ccc0da05c8d0",
+ "headRepository": {
+ "id": "R_kgDOL0az5w",
+ "name": "bc"
+ },
+ "headRepositoryOwner": {
+ "id": "U_kgDOCgjy1g",
+ "login": "henke9600"
+ },
+ "id": "PR_kwDOCL0xJc58QB5g",
+ "isCrossRepository": true,
+ "isDraft": false,
+ "labels": [],
+ "latestReviews": [],
+ "maintainerCanModify": false,
+ "mergeCommit": {
+ "oid": "e5d675785383ce6142116243aa63d21e5afb54f7"
+ },
+ "mergeStateStatus": "UNKNOWN",
+ "mergeable": "UNKNOWN",
+ "mergedAt": "2024-09-21T19:14:25Z",
+ "mergedBy": {
+ "id": "MDQ6VXNlcjMxNzI2ODc=",
+ "is_bot": false,
+ "login": "gavinhoward",
+ "name": "Gavin D. Howard"
+ },
+ "milestone": null,
+ "number": 84,
+ "potentialMergeCommit": null,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "reviewDecision": "",
+ "reviewRequests": [],
+ "reviews": [],
+ "state": "MERGED",
+ "statusCheckRollup": [],
+ "title": "Don't unconditionally enable warnings after ignoring them",
+ "updatedAt": "2024-09-24T04:12:15Z",
+ "url": "https://github.com/gavinhoward/bc/pull/84"
+ },
+ {
+ "additions": 1,
+ "assignees": [],
+ "author": {
+ "id": "U_kgDOBqIXaQ",
+ "is_bot": false,
+ "login": "GregTonoski",
+ "name": "Greg Tonoski"
+ },
+ "autoMergeRequest": null,
+ "baseRefName": "master",
+ "baseRefOid": "324c30985c0d3d6f918fe81f2d8750c0bd1c78b1",
+ "body": "",
+ "changedFiles": 1,
+ "closed": true,
+ "closedAt": "2024-08-31T18:15:37Z",
+ "comments": [
+ {
+ "id": "IC_kwDOCL0xJc6KdjgL",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Thank you for catching that!\r\n\r\nUnfortunately, `A.1.md` is actually generated from `bc.1.md.in`. I edited `bc.1.md.in` instead and regenerated the manpages.\r\n\r\nI wasn't able to set you as the author of the commit because I don't know your email address, but I will if you want.",
+ "createdAt": "2024-08-31T18:16:53Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [
+ {
+ "content": "THUMBS_UP",
+ "users": {
+ "totalCount": 1
+ }
+ }
+ ],
+ "url": "https://github.com/gavinhoward/bc/pull/81#issuecomment-2323003403",
+ "viewerDidAuthor": true
+ }
+ ],
+ "commits": [
+ {
+ "authoredDate": "2024-08-31T07:52:27Z",
+ "authors": [
+ {
+ "email": "111286121+GregTonoski@users.noreply.github.com",
+ "id": "U_kgDOBqIXaQ",
+ "login": "GregTonoski",
+ "name": "Greg Tonoski"
+ }
+ ],
+ "committedDate": "2024-08-31T07:52:27Z",
+ "messageBody": "",
+ "messageHeadline": "Update A.1.md minor correction - number instead of integer",
+ "oid": "8269063d303979de8188f4810a1c6b070494e025"
+ }
+ ],
+ "createdAt": "2024-08-31T07:52:34Z",
+ "deletions": 1,
+ "files": [
+ {
+ "path": "manuals/bc/A.1.md",
+ "additions": 1,
+ "deletions": 1
+ }
+ ],
+ "fullDatabaseId": "2047601609",
+ "headRefName": "patch-1",
+ "headRefOid": "8269063d303979de8188f4810a1c6b070494e025",
+ "headRepository": {
+ "id": "R_kgDOMq13Zg",
+ "name": "bc"
+ },
+ "headRepositoryOwner": {
+ "id": "U_kgDOBqIXaQ",
+ "name": "Greg Tonoski",
+ "login": "GregTonoski"
+ },
+ "id": "PR_kwDOCL0xJc56C-vJ",
+ "isCrossRepository": true,
+ "isDraft": false,
+ "labels": [],
+ "latestReviews": [],
+ "maintainerCanModify": false,
+ "mergeCommit": null,
+ "mergeStateStatus": "UNKNOWN",
+ "mergeable": "UNKNOWN",
+ "mergedAt": null,
+ "mergedBy": null,
+ "milestone": null,
+ "number": 81,
+ "potentialMergeCommit": null,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "reviewDecision": "",
+ "reviewRequests": [],
+ "reviews": [],
+ "state": "CLOSED",
+ "statusCheckRollup": [],
+ "title": "Update A.1.md minor correction - number instead of integer",
+ "updatedAt": "2024-08-31T18:16:54Z",
+ "url": "https://github.com/gavinhoward/bc/pull/81"
+ },
+ {
+ "additions": 1,
+ "assignees": [],
+ "author": {
+ "id": "U_kgDOCgjy1g",
+ "is_bot": false,
+ "login": "henke9600",
+ "name": ""
+ },
+ "autoMergeRequest": null,
+ "baseRefName": "master",
+ "baseRefOid": "3278daef0079c20ccaed3a8666457c531dbe576b",
+ "body": "This avoids the `./configure.sh: 1693: [: unexpected operator` error when using dash.",
+ "changedFiles": 1,
+ "closed": true,
+ "closedAt": "2024-04-30T01:57:03Z",
+ "comments": [
+ {
+ "id": "IC_kwDOCL0xJc58OZbM",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Sorry for the wait.\r\n\r\nYes, you are correct that that is a bug. Yes, your fix is correct. Accepted without comment!\r\n\r\nThank you for your contribution!",
+ "createdAt": "2024-04-30T01:57:40Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/76#issuecomment-2084148940",
+ "viewerDidAuthor": true
+ }
+ ],
+ "commits": [
+ {
+ "authoredDate": "2024-04-28T16:12:24Z",
+ "authors": [
+ {
+ "email": "henrik@lxm.se",
+ "id": "U_kgDOCgjy1g",
+ "login": "henke9600",
+ "name": "Henrik Lindström"
+ }
+ ],
+ "committedDate": "2024-04-28T16:12:24Z",
+ "messageBody": "",
+ "messageHeadline": "Make configure.sh posix compliant",
+ "oid": "938fc2cbcace3fb1fb353a5befff1b4a81180350"
+ }
+ ],
+ "createdAt": "2024-04-28T16:25:19Z",
+ "deletions": 1,
+ "files": [
+ {
+ "path": "configure.sh",
+ "additions": 1,
+ "deletions": 1
+ }
+ ],
+ "fullDatabaseId": "1844494404",
+ "headRefName": "master",
+ "headRefOid": "938fc2cbcace3fb1fb353a5befff1b4a81180350",
+ "headRepository": {
+ "id": "R_kgDOL0az5w",
+ "name": "bc"
+ },
+ "headRepositoryOwner": {
+ "id": "U_kgDOCgjy1g",
+ "login": "henke9600"
+ },
+ "id": "PR_kwDOCL0xJc5t8MBE",
+ "isCrossRepository": true,
+ "isDraft": false,
+ "labels": [],
+ "latestReviews": [],
+ "maintainerCanModify": false,
+ "mergeCommit": {
+ "oid": "59cf3b86eb4cafb6d7aa164d988a8c14c287e7f8"
+ },
+ "mergeStateStatus": "UNKNOWN",
+ "mergeable": "UNKNOWN",
+ "mergedAt": "2024-04-30T01:57:03Z",
+ "mergedBy": {
+ "id": "MDQ6VXNlcjMxNzI2ODc=",
+ "is_bot": false,
+ "login": "gavinhoward",
+ "name": "Gavin D. Howard"
+ },
+ "milestone": null,
+ "number": 76,
+ "potentialMergeCommit": null,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "reviewDecision": "",
+ "reviewRequests": [],
+ "reviews": [],
+ "state": "MERGED",
+ "statusCheckRollup": [],
+ "title": "Make configure.sh posix compliant",
+ "updatedAt": "2024-04-30T01:57:41Z",
+ "url": "https://github.com/gavinhoward/bc/pull/76"
+ },
+ {
+ "additions": 1,
+ "assignees": [],
+ "author": {
+ "id": "U_kgDOB2enHg",
+ "is_bot": false,
+ "login": "naggamura",
+ "name": ""
+ },
+ "autoMergeRequest": null,
+ "baseRefName": "master",
+ "baseRefOid": "1a381d61b079fc3548d3e4dee3a8ccd9200a3f87",
+ "body": "Found a printing error.\r\nTry obase=2; 2^99; 2^100; 2^105; you'll get it right away.",
+ "changedFiles": 1,
+ "closed": true,
+ "closedAt": "2023-12-22T15:38:08Z",
+ "comments": [
+ {
+ "id": "IC_kwDOCL0xJc5vVPQZ",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "You are correct; that is a bug.\r\n\r\nYour fix is correct; I got the condition backwards. Accepted without comment.\r\n\r\nSorry for taking so long, I struggle to read my own code; this is why I put in so many \"useless\" comments!\r\n\r\nAlso, I can think of another place that this *could have been* a problem and quickly tested it.\r\n\r\nThe problem is in printing in bases above base 16, where multiple characters may be printed for one digit.\r\n\r\nGuess what? I tested your fix after accepting it (I wanted your name in the Contributors list regardless), and your fix works for that too!\r\n\r\nTry this:\r\n\r\n```\r\n$ BC_LINE_LENGTH=77 bc\r\n>>> obase=128\r\n>>> 2^126\r\n 001 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000\r\n>>> quit\r\n```\r\n\r\nThat's what it is with the fix. Without, the bug shows:\r\n\r\n```\r\n$ BC_LINE_LENGTH=77 bc\r\n>>> obase=128\r\n>>> 2^126\r\n 001 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 00\\\r\n0\r\n>>> quit\r\n```\r\n\r\nI checked your fix with `BC_LINE_LENGTH=78` and `BC_LINE_LENGTH=76`, and your fix properly handles both.\r\n\r\nI am confident in your fix.\r\n\r\nAnyway, I'll start my release process and put out a release with your fix ASAP.",
+ "createdAt": "2023-12-22T15:53:14Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/74#issuecomment-1867838489",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5vWpvU",
+ "author": {
+ "login": "naggamura"
+ },
+ "authorAssociation": "CONTRIBUTOR",
+ "body": "Oh I helped... ^^; I'm happy.\r\nI'm Jonathan Kim",
+ "createdAt": "2023-12-23T05:14:48Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/74#issuecomment-1868209108",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc5vWqt8",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Yes, you did! And your fix has now passed the release process untouched.\r\n\r\nPlease don't think I'm a jerk for your previous PR; I try to not be.\r\n\r\nI just have ridiculously high standards and personal weaknesses in programming. And an eccentric style.\r\n\r\nIn fact, I [don't accept contributions at all][1] in my [next project][2]. Nothing against people, but I really struggle with patches. It's so bad that I actually can't get a programming job because of that. If you work in the industry, you're doing better than me.\r\n\r\nAnd nice to meet you. :)\r\n\r\n[1]: https://git.yzena.com/Yzena/Yc#user-content-open-source-not-open-contribution\r\n[2]: https://git.yzena.com/Yzena/Yc",
+ "createdAt": "2023-12-23T05:37:53Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/74#issuecomment-1868213116",
+ "viewerDidAuthor": true
+ }
+ ],
+ "commits": [
+ {
+ "authoredDate": "2023-12-22T13:22:27Z",
+ "authors": [
+ {
+ "email": "jonathan@acryl.ai",
+ "id": "U_kgDOB2enHg",
+ "login": "naggamura",
+ "name": "jonathan"
+ }
+ ],
+ "committedDate": "2023-12-22T13:22:27Z",
+ "messageBody": "Try obase=2; 2^105;",
+ "messageHeadline": "Fixed printing error.",
+ "oid": "930cb2fd56d7851ecc1af3de16b4b7357b0b5ba8"
+ }
+ ],
+ "createdAt": "2023-12-22T13:25:36Z",
+ "deletions": 1,
+ "files": [
+ {
+ "path": "src/num.c",
+ "additions": 1,
+ "deletions": 1
+ }
+ ],
+ "fullDatabaseId": "1655098845",
+ "headRefName": "master",
+ "headRefOid": "930cb2fd56d7851ecc1af3de16b4b7357b0b5ba8",
+ "headRepository": null,
+ "headRepositoryOwner": {
+ "id": "U_kgDOB2enHg",
+ "login": "naggamura"
+ },
+ "id": "PR_kwDOCL0xJc5ips3d",
+ "isCrossRepository": true,
+ "isDraft": false,
+ "labels": [],
+ "latestReviews": [],
+ "maintainerCanModify": false,
+ "mergeCommit": {
+ "oid": "39bd2c5622167a3c092ca9e2c38886ff288ba508"
+ },
+ "mergeStateStatus": "UNKNOWN",
+ "mergeable": "UNKNOWN",
+ "mergedAt": "2023-12-22T15:38:08Z",
+ "mergedBy": {
+ "id": "MDQ6VXNlcjMxNzI2ODc=",
+ "is_bot": false,
+ "login": "gavinhoward",
+ "name": "Gavin D. Howard"
+ },
+ "milestone": null,
+ "number": 74,
+ "potentialMergeCommit": null,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "reviewDecision": "",
+ "reviewRequests": [],
+ "reviews": [],
+ "state": "MERGED",
+ "statusCheckRollup": [],
+ "title": "Fixed printing error.",
+ "updatedAt": "2023-12-23T05:37:55Z",
+ "url": "https://github.com/gavinhoward/bc/pull/74"
+ },
+ {
+ "additions": 657,
+ "assignees": [],
+ "author": {
+ "id": "U_kgDOB2enHg",
+ "is_bot": false,
+ "login": "naggamura",
+ "name": ""
+ },
+ "autoMergeRequest": null,
+ "baseRefName": "master",
+ "baseRefOid": "fcbe9d68ccc964fda12457ebbf687a27f57ff31f",
+ "body": "I'm sorry I missed many things on last PR.\r\nI followed your instructions step by step.\r\n\r\nImproved SQRT needs no long division in iteration.\r\nOnly one long division is needed for compose an initial estimation.\r\n\r\nNo meaningful difference for small input.\r\n\r\nFor large input, about 2.4 times faster, no meaningful difference in memory usage.\r\n\r\nHere is the result for large input.\r\n\r\n( 'CC=clang CFLAGS=-flto ./configure -O3' is used for configuring. )\r\n[stat_c1.txt](https://github.com/gavinhoward/bc/files/13696239/stat_c1.txt)\r\n[stat_c4.txt](https://github.com/gavinhoward/bc/files/13696240/stat_c4.txt)\r\n\r\nsqrt1_large.txt : by your code.\r\nsqrt2_large.txt : by my code.\r\n\r\n---------------------------------\r\n\r\nElapsed time for large input.\r\n\r\nx ../../sqrt1_large.txt\r\n+ ../../sqrt2_large.txt\r\n+--------------------------------------------------------------------------------+\r\n| + |\r\n| + |\r\n| + |\r\n| + |\r\n| + |\r\n| + |\r\n| + |\r\n| + |\r\n| + |\r\n| + |\r\n| + |\r\n| + x |\r\n| + x |\r\n| + x |\r\n| + x |\r\n| + x |\r\n| + x |\r\n| + x |\r\n| + x |\r\n| + x |\r\n| + x |\r\n| + x |\r\n| + x x|\r\n| ++ x xx|\r\n|+++ x xx|\r\n|+++ x xx|\r\n|+++ xxxx|\r\n|+++ xxxx|\r\n|+++ x xxxx|\r\n|+++ x xxxx|\r\n|+++ x xxxx|\r\n|++++ x xxxx|\r\n| A| |_A| |\r\n+--------------------------------------------------------------------------------+\r\n N Min Max Median Avg Stddev\r\nx 50 87.84 91.81 90.215 90.3572 1.0769137\r\n+ 50 36.96 38.71 37.805 37.8088 0.44755408\r\nDifference at 99.5% confidence\r\n -52.5484 +/- 0.523644\r\n -58.1563% +/- 0.315605%\r\n (Student's t, pooled s = 0.824636)\r\n\r\n\r\n----------------------\r\n\r\n\r\nMemory for large input.\r\n\r\nx ../../sqrt1_large.txt\r\n+ ../../sqrt2_large.txt\r\n+--------------------------------------------------------------------------------+\r\n| + |\r\n| + x |\r\n| + x |\r\n| + + x x x |\r\n| + + + x x x |\r\n| + + ++ ++ xx x x x x x |\r\n|+ ++++ +++++ + +++ x xx x x x xx xxx xx|\r\n|+ + +++++++++++++++++ * + * x xxxxxxxxx xxxx xxxx xx|\r\n| |_______AM_______| |______M__A_________| |\r\n+--------------------------------------------------------------------------------+\r\n N Min Max Median Avg Stddev\r\nx 50 5376 5596 5494 5508.96 51.80535\r\n+ 50 5184 5412 5302 5301.12 44.115174\r\nDifference at 99.5% confidence\r\n -207.84 +/- 30.5525\r\n -3.77276% +/- 0.542566%\r\n (Student's t, pooled s = 48.1141)\r\n\r\n\r\n- END -",
+ "changedFiles": 3,
+ "closed": true,
+ "closedAt": "2023-12-20T01:50:17Z",
+ "comments": [
+ {
+ "id": "IC_kwDOCL0xJc5u0b9I",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Unfortunately, you still did not check everything. That's probably my fault because I didn't tell you everything.\r\n\r\nNevertheless, this is what I found this time:\r\n\r\n* `CC=clang ./configure -pDBG; make`, my standard debug build, failed to build.\r\n* `CC=clang ./configure -gO0; make; make test`, a less strict debug build, failed the test suite.\r\n* `CC=clang ./configure -pGDH; make`, my standard release build for myself, failed to build.\r\n* `CC=clang ./configure -pGNU; make; make test`, the standard release build for Linux, failed the test suite.\r\n* `CC=gcc ./configure -pGNU; make; make test`, the same standard Linux build but with `gcc`, also failed the test suite.\r\n* `CC=clang ./configure -pBSD; make; make test`, the standard release build for FreeBSD and friends, failed the test suite.\r\n* `CC=gcc ./configure -pBSD; make; make test`, the same standard FreeBSD build but with `gcc`, also failed the test suite.\r\n\r\nIn addition, what failed on the test suite was off by a lot; the result I got for all cases was:\r\n\r\n```\r\nRunning bc sqrt...FAIL!!!\r\n12c12\r\n< .0000000000000035071355833500363\r\n---\r\n> .8915580480000035071355833500363\r\nbc failed test sqrt\r\n```\r\n\r\nAs you can see, the numbers are the same, except at the beginning, which means that answer was about as far off as it could be.\r\n\r\nIn addition, when I ran this:\r\n\r\n```\r\n$ ./configure -gO0 -v\r\n$ make\r\n$ make test\r\n```\r\n\r\nwhich is a Valgrind-enabled build, I got this:\r\n\r\n```\r\nRunning bc sqrt...==47836== Memcheck, a memory error detector\r\n==47836== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al.\r\n==47836== Using Valgrind-3.22.0 and LibVEX; rerun with -h for copyright info\r\n==47836== Command: bin/bc -lqc ./tests/bc/sqrt.txt\r\n==47836==\r\n==47836== Invalid write of size 8\r\n==47836== at 0x484C96E: memset (vg_replace_strmem.c:1386)\r\n==47836== by 0x12A28F: bc_int_elementary_mul (num.c:4274)\r\n==47836== by 0x1253AA: bc_int_k_mul (num.c:4327)\r\n==47836== by 0x124972: bc_num_sqrt (num.c:4636)\r\n==47836== by 0x13156F: bc_program_builtin (program.c:1990)\r\n==47836== by 0x12DB0C: bc_program_exec (program.c:3332)\r\n==47836== by 0x13D623: bc_vm_process (vm.c:1046)\r\n==47836== by 0x13CCA5: bc_vm_file (vm.c:1103)\r\n==47836== by 0x13C117: bc_vm_exec (vm.c:1498)\r\n==47836== by 0x13B4E1: bc_vm_boot (vm.c:1718)\r\n==47836== by 0x10C0CC: bc_main (bc.c:62)\r\n==47836== by 0x119995: main (main.c:108)\r\n==47836== Address 0x4aa2270 is 32 bytes inside a block of size 36 alloc'd\r\n==47836== at 0x4840784: malloc (vg_replace_malloc.c:442)\r\n==47836== by 0x13A4C3: bc_vm_malloc (vm.c:810)\r\n==47836== by 0x11BA76: bc_num_init (num.c:3368)\r\n==47836== by 0x1244B1: bc_num_sqrt (num.c:4567)\r\n==47836== by 0x13156F: bc_program_builtin (program.c:1990)\r\n==47836== by 0x12DB0C: bc_program_exec (program.c:3332)\r\n==47836== by 0x13D623: bc_vm_process (vm.c:1046)\r\n==47836== by 0x13CCA5: bc_vm_file (vm.c:1103)\r\n==47836== by 0x13C117: bc_vm_exec (vm.c:1498)\r\n==47836== by 0x13B4E1: bc_vm_boot (vm.c:1718)\r\n==47836== by 0x10C0CC: bc_main (bc.c:62)\r\n==47836== by 0x119995: main (main.c:108)\r\n==47836==\r\n==47836==\r\n==47836== HEAP SUMMARY:\r\n==47836== in use at exit: 0 bytes in 0 blocks\r\n==47836== total heap usage: 1,633 allocs, 1,633 frees, 331,419 bytes allocated\r\n==47836==\r\n==47836== All heap blocks were freed -- no leaks are possible\r\n==47836==\r\n==47836== For lists of detected and suppressed errors, rerun with: -s\r\n==47836== ERROR SUMMARY: 8 errors from 1 contexts (suppressed: 0 from 0)\r\nFAIL!!!\r\nbc failed test 'sqrt' with error code 100\r\n```\r\n\r\nThat needs to be fixed, both the out-of-bounds write and the unaligned write.\r\n\r\nIf you're wondering how much testing I do, do this:\r\n\r\n* Create two clones of your branch in separate directories, which I will call `bc1/` and `bc2/`.\r\n* In `bc1/`, run `scripts/release.sh 1 1 1 1 0 1 0 0 1 0 1 0 1 0 0 1 1`.\r\n* In `bc2/`, run `scripts/release.sh 1 1 1 0 1 0 1 0 1 0 1 0 0 1 1 1 1`.\r\n* Run them both in separate terminals and at the same time.\r\n* Leave it overnight.\r\n\r\nYep, you heard that last bit right: my full test run is an overnight thing. And I run both because one does `clang`, and one does `gcc`.\r\n\r\nMoving on to benchmarks, I ran\r\n\r\n```\r\n$ CC=clang CFLAGS=-flto ./configure -O3\r\n$ make\r\n```\r\n\r\nin both branches. Then I ran\r\n\r\n```\r\n$ ./scripts/benchmark.sh -p1 -n50 bc newton_raphson_sqrt_small > ../sqrt2_small.txt\r\n```\r\n\r\nin your branch, copied `benchmarks/bc/newton_raphson_sqrt_small.txt` to my branch and ran\r\n\r\n```\r\n$ ./scripts/benchmark.sh -p1 -n50 bc newton_raphson_sqrt_small > ../sqrt1_small.txt\r\n```\r\n\r\nThese are my results:\r\n\r\n```\r\n$ ./scripts/ministat -w 80 -c99.5 -C1 ../sqrt1_small.txt ../sqrt2_small.txt\r\nx ../sqrt1_small.txt\r\n+ ../sqrt2_small.txt\r\n+--------------------------------------------------------------------------------+\r\n| + x |\r\n| + x x |\r\n| + x x |\r\n| + + + x x |\r\n| + + + x x x |\r\n| + + + + x x x |\r\n| + + + + x x x |\r\n| + + + + x x x |\r\n| + + + + x x x |\r\n| + + + + + x x x x x x |\r\n| + + + + + + x x x x x x x |\r\n| + + + + + + x x x x x x x |\r\n|+ + + + + + + * x x x x x x x|\r\n| |________A_M_____| |________MA________| |\r\n+--------------------------------------------------------------------------------+\r\n N Min Max Median Avg Stddev\r\nx 50 1.45 1.52 1.48 1.4806 0.016463937\r\n+ 50 1.38 1.45 1.42 1.4164 0.015220824\r\nDifference at 99.5% confidence\r\n -0.0642 +/- 0.0100677\r\n -4.33608% +/- 0.664237%\r\n (Student's t, pooled s = 0.0158546)\r\n$ ./scripts/ministat -w 80 -c99.5 -C4 ../sqrt1_small.txt ../sqrt2_small.txt\r\nx ../sqrt1_small.txt\r\n+ ../sqrt2_small.txt\r\n+--------------------------------------------------------------------------------+\r\n| + |\r\n| + |\r\n| x + |\r\n| x x + |\r\n| x x + + |\r\n| x x +x + + + |\r\n| x + xx +x + x + + |\r\n| x *+ xx *x++ xx + + + |\r\n| xx +*+xxxx x**++ xx + + + |\r\n|x x xx +*+xxxx x***++xx*x+++++ + ++|\r\n| |__________A_|_______M|A_________| |\r\n+--------------------------------------------------------------------------------+\r\n N Min Max Median Avg Stddev\r\nx 50 18956 19244 19168 19168.16 57.880722\r\n+ 50 19144 19376 19224 19232.88 54.73373\r\nDifference at 99.5% confidence\r\n 64.72 +/- 35.769\r\n 0.337643% +/- 0.186939%\r\n (Student's t, pooled s = 56.3292)\r\n```\r\n\r\nYours was slightly faster and used slightly more memory.\r\n\r\nI ran the same benchmark for large, but only for one sample each, and my numbers say that yours are accurate for that benchmark.\r\n\r\nHowever, I cannot accept code that fails tests or has memory bugs. In addition, because of the test failures, you can expect that I will run a lot more tests on your code before I accept.\r\n\r\nSo I won't close this PR, but you need to fix the problems above (by running that `release.sh` script both ways and fixing every problem) before I'll look at it again.\r\n\r\nAlso, I can't understand `sqrt_2.png`. I can follow `sqrt_1.png`, but I need you to redo `sqrt_2.png` because I also won't accept the code if I don't understand it.",
+ "createdAt": "2023-12-17T18:09:30Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/73#issuecomment-1859239752",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5u10BR",
+ "author": {
+ "login": "naggamura"
+ },
+ "authorAssociation": "CONTRIBUTOR",
+ "body": "BcNum.len is managed as tight as possible. So clearing upper digits is needed for like sqrt(0.000...000xyz...) \r\nPassed 'make test'\r\n---",
+ "createdAt": "2023-12-18T05:52:00Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/73#issuecomment-1859600465",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc5u12rZ",
+ "author": {
+ "login": "naggamura"
+ },
+ "authorAssociation": "CONTRIBUTOR",
+ "body": "Last push was not the latest version. Pushed again.\r\n\r\nAnyway, test again and passed all tests.\r\n\r\n...\r\n==5965== \r\n==5965== HEAP SUMMARY:\r\n==5965== in use at exit: 0 bytes in 0 blocks\r\n==5965== total heap usage: 1,520 allocs, 1,520 frees, 205,685 bytes allocated\r\n==5965== \r\n==5965== All heap blocks were freed -- no leaks are possible\r\n==5965== \r\n==5965== For counts of detected and suppressed errors, rerun with: -v\r\n==5965== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)\r\npass\r\nRunning bc directory test...pass\r\nRunning bc binary file test...pass\r\nRunning bc binary stdin test...pass\r\nRunning bc limits tests...pass\r\n\r\nAll bc tests passed.\r\n\r\n...\r\n...\r\n\r\n==6927== \r\n==6927== HEAP SUMMARY:\r\n==6927== in use at exit: 0 bytes in 0 blocks\r\n==6927== total heap usage: 73 allocs, 73 frees, 37,886 bytes allocated\r\n==6927== \r\n==6927== All heap blocks were freed -- no leaks are possible\r\n==6927== \r\n==6927== For counts of detected and suppressed errors, rerun with: -v\r\n==6927== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)\r\npass\r\nRunning dc directory test...pass\r\nRunning dc binary file test...pass\r\nRunning dc binary stdin test...pass\r\n\r\nAll dc tests passed.\r\n\r\n***********************************************************************\r\n\r\n----",
+ "createdAt": "2023-12-18T06:05:29Z",
+ "includesCreatedEdit": true,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/73#issuecomment-1859611353",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc5u3LdX",
+ "author": {
+ "login": "naggamura"
+ },
+ "authorAssociation": "CONTRIBUTOR",
+ "body": "Doing scripts/release.sh test...",
+ "createdAt": "2023-12-18T09:47:53Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/73#issuecomment-1859958615",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc5u_Gjj",
+ "author": {
+ "login": "naggamura"
+ },
+ "authorAssociation": "CONTRIBUTOR",
+ "body": "Passed the tests.\r\n\r\nBenchmarks\r\n\r\n[stat_small_c1.txt](https://github.com/gavinhoward/bc/files/13712108/stat_small_c1.txt)\r\n[stat_small_c4.txt](https://github.com/gavinhoward/bc/files/13712109/stat_small_c4.txt)\r\n[stat_large_c1.txt](https://github.com/gavinhoward/bc/files/13712110/stat_large_c1.txt)\r\n[stat_large_c4.txt](https://github.com/gavinhoward/bc/files/13712111/stat_large_c4.txt)\r\n",
+ "createdAt": "2023-12-19T03:09:54Z",
+ "includesCreatedEdit": true,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/73#issuecomment-1862035683",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc5vCDYq",
+ "author": {
+ "login": "naggamura"
+ },
+ "authorAssociation": "CONTRIBUTOR",
+ "body": "sqrt_2.png updated.",
+ "createdAt": "2023-12-19T13:58:49Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/73#issuecomment-1862809130",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc5vFhZX",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "I have spent two full days on this since the last time I sent a message. I cannot spend any more time on this; I have a project to finish and a hard deadline.\r\n\r\nSo I have to make a final decision, which is to reject this. Thank you for your contribution, but I guess I don't want it.\r\n\r\nThere are several reasons why:\r\n\r\n* Your code still contains debug code.\r\n* You created your own functions to do things when functions to do those things already exist, which shows lack of research again. And duplication.\r\n* Your proof looks wrong to me, or I still can't understand it.\r\n* Your code passes the test suite, but it did not pass another set of tests I threw at it.\r\n* This is purely a performance PR, which is not really important to me right now.\r\n\r\nBut most of all, and this is nothing against you, I just can't read other people's code. I am unable to build a theory of mind, and this extends to reading code written by other people.\r\n\r\nThere is a reason I tend to do things by myself; I can't work on the code otherwise.\r\n\r\nSo I'm afraid I have run out of time and desire. Sorry.",
+ "createdAt": "2023-12-20T01:50:17Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/73#issuecomment-1863718487",
+ "viewerDidAuthor": true
+ }
+ ],
+ "commits": [
+ {
+ "authoredDate": "2023-12-17T12:30:19Z",
+ "authors": [
+ {
+ "email": "jonathan@acryl.ai",
+ "id": "U_kgDOB2enHg",
+ "login": "naggamura",
+ "name": "jonathan"
+ }
+ ],
+ "committedDate": "2023-12-17T12:30:19Z",
+ "messageBody": "",
+ "messageHeadline": "New bc_num_sqrt function needs only one long division.",
+ "oid": "a8821844d9f05df15fde8bed92332226e147b151"
+ },
+ {
+ "authoredDate": "2023-12-18T02:46:18Z",
+ "authors": [
+ {
+ "email": "jonathan@acryl.ai",
+ "id": "U_kgDOB2enHg",
+ "login": "naggamura",
+ "name": "jonathan"
+ }
+ ],
+ "committedDate": "2023-12-18T02:46:18Z",
+ "messageBody": "Passed 'sqrt test'",
+ "messageHeadline": "SQRT fix: post process for numbers like 0.000000...000000xyz...",
+ "oid": "a4e27bb46a0fc9dc59e51ba5de153b6f2fe55f5d"
+ },
+ {
+ "authoredDate": "2023-12-18T05:46:38Z",
+ "authors": [
+ {
+ "email": "jonathan@acryl.ai",
+ "id": "U_kgDOB2enHg",
+ "login": "naggamura",
+ "name": "jonathan"
+ }
+ ],
+ "committedDate": "2023-12-18T05:46:38Z",
+ "messageBody": "",
+ "messageHeadline": "SQRT: fixed memory bug. passed 'make test'",
+ "oid": "f4f8cf72d513f2986bb477d10bcd0fcb520a0e7e"
+ },
+ {
+ "authoredDate": "2023-12-18T06:01:30Z",
+ "authors": [
+ {
+ "email": "jonathan@acryl.ai",
+ "id": "U_kgDOB2enHg",
+ "login": "naggamura",
+ "name": "jonathan"
+ }
+ ],
+ "committedDate": "2023-12-18T06:01:30Z",
+ "messageBody": "",
+ "messageHeadline": "Commit missed!!! fix again.",
+ "oid": "1faa1296390f5e689d78c28fa9b434516f47c15d"
+ },
+ {
+ "authoredDate": "2023-12-18T09:22:18Z",
+ "authors": [
+ {
+ "email": "jonathan@acryl.ai",
+ "id": "U_kgDOB2enHg",
+ "login": "naggamura",
+ "name": "jonathan"
+ }
+ ],
+ "committedDate": "2023-12-18T09:22:18Z",
+ "messageBody": "…r running release.sh",
+ "messageHeadline": "Removed unused variables, explicit type conversion in assignments, fo…",
+ "oid": "fb9d7b88c60ecdb7ce8bdfe8a11e23d09b2342fb"
+ },
+ {
+ "authoredDate": "2023-12-18T13:17:09Z",
+ "authors": [
+ {
+ "email": "jonathan@acryl.ai",
+ "id": "U_kgDOB2enHg",
+ "login": "naggamura",
+ "name": "jonathan"
+ }
+ ],
+ "committedDate": "2023-12-18T13:17:09Z",
+ "messageBody": "…unning release.sh.\n\nRunning release.sh for this commit now...",
+ "messageHeadline": "Fixed memory warning(passing NULL with zero length to memcpy) while r…",
+ "oid": "9f171378fe38619d032107d388eef06519dde4a7"
+ },
+ {
+ "authoredDate": "2023-12-19T03:08:34Z",
+ "authors": [
+ {
+ "email": "jonathan@acryl.ai",
+ "id": "U_kgDOB2enHg",
+ "login": "naggamura",
+ "name": "jonathan"
+ }
+ ],
+ "committedDate": "2023-12-19T03:08:34Z",
+ "messageBody": "…old one!)\n\n2. Passed Valgrind memory test.\n./configure -gO0 -v\nmake\nmake test\n\n3. Passed\nscripts/release.sh 1 1 1 0 1 0 1 0 1 0 1 0 0 1 1 1 1\non Ubuntu 18.04.2 LTS",
+ "messageHeadline": "1. Simple multiplication modified based on Gavin's (much better than …",
+ "oid": "2b3616586454f315a5014360f7f7d5d36f10bd9a"
+ },
+ {
+ "authoredDate": "2023-12-19T13:57:21Z",
+ "authors": [
+ {
+ "email": "jonathan@acryl.ai",
+ "id": "U_kgDOB2enHg",
+ "login": "naggamura",
+ "name": "jonathan"
+ }
+ ],
+ "committedDate": "2023-12-19T13:57:21Z",
+ "messageBody": "",
+ "messageHeadline": "Proof related to sqrt approximation updated.",
+ "oid": "2d7b8fe4efe528076a568b0f174c35a53e47ae93"
+ }
+ ],
+ "createdAt": "2023-12-17T13:16:03Z",
+ "deletions": 72,
+ "files": [
+ {
+ "path": "sqrt_1.png",
+ "additions": 0,
+ "deletions": 0
+ },
+ {
+ "path": "sqrt_2.png",
+ "additions": 0,
+ "deletions": 0
+ },
+ {
+ "path": "src/num.c",
+ "additions": 657,
+ "deletions": 72
+ }
+ ],
+ "fullDatabaseId": "1647316989",
+ "headRefName": "master",
+ "headRefOid": "2d7b8fe4efe528076a568b0f174c35a53e47ae93",
+ "headRepository": null,
+ "headRepositoryOwner": {
+ "id": "U_kgDOB2enHg",
+ "login": "naggamura"
+ },
+ "id": "PR_kwDOCL0xJc5iMA_9",
+ "isCrossRepository": true,
+ "isDraft": false,
+ "labels": [],
+ "latestReviews": [],
+ "maintainerCanModify": false,
+ "mergeCommit": null,
+ "mergeStateStatus": "UNKNOWN",
+ "mergeable": "UNKNOWN",
+ "mergedAt": null,
+ "mergedBy": null,
+ "milestone": null,
+ "number": 73,
+ "potentialMergeCommit": null,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "reviewDecision": "",
+ "reviewRequests": [],
+ "reviews": [],
+ "state": "CLOSED",
+ "statusCheckRollup": [],
+ "title": "New bc_num_sqrt function needs only one long division.",
+ "updatedAt": "2023-12-20T01:50:17Z",
+ "url": "https://github.com/gavinhoward/bc/pull/73"
+ },
+ {
+ "additions": 831,
+ "assignees": [],
+ "author": {
+ "id": "U_kgDOB2enHg",
+ "is_bot": false,
+ "login": "naggamura",
+ "name": ""
+ },
+ "autoMergeRequest": null,
+ "baseRefName": "master",
+ "baseRefOid": "5ee6a05918bd8341a7ed97d6594862ad5d713557",
+ "body": "Newton-Raphson approximation is applied to division and sqrt.\r\nTry 5^1000000 / 2^1000000 and sort(5^999999)\r\n",
+ "changedFiles": 3,
+ "closed": true,
+ "closedAt": "2023-12-14T04:47:11Z",
+ "comments": [
+ {
+ "id": "IC_kwDOCL0xJc5uku2Q",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Thank you for your PR. I have some comments.\r\n\r\n* If you run `CC=clang ./configure.sh -pDBG; make`, my regular debug build, the build fails.\r\n* If you run `CC=clang ./configure.sh -pGDH; make`, my regular release build, the build fails.\r\n* If you run `CC=gcc CFLAGS=\"-pedantic -Wextra -Werror\" ./configure -O3; make`, the build fails.\r\n* If you run `CC=clang ./configure -gO3; make; make test`, a release build with debug information, the build succeeds, but the test suite fails on an assert.\r\n* You have functions that have reserved names (names that begin with an underscore); using those names is undefined behavior.\r\n* You did not follow my style even though there is a script (`scripts/format.sh`) that will style the code for you.\r\n\r\nIn addition, I can unfortunately tell that you did no research into my `bc`. If you had, you would know that [I already use Newton-Raphson for `sqrt()`][1].\r\n\r\nHowever, all of that could be safely discarded if the code shows promise. So I wrote [benchmarks to test your new code][2].\r\n\r\nI ran these commands with my code:\r\n\r\n```\r\n$ CC=clang CFLAGS=-flto ./configure -O3\r\n$ make\r\n```\r\n\r\nThen I ran the benchmarks as follows:\r\n\r\n```\r\n$ ./scripts/benchmark.sh -p1 -n50 bc newton_raphson_div_small > ../div1_small.txt\r\n$ ./scripts/benchmark.sh -p1 -n50 bc newton_raphson_div_large > ../div1_large.txt\r\n$ ./scripts/benchmark.sh -p1 -n50 bc newton_raphson_sqrt_small > ../sqrt1_small.txt\r\n$ ./scripts/benchmark.sh -p1 -n50 bc newton_raphson_sqrt_large > ../sqrt1_large.txt\r\n```\r\n\r\nThen I repeated the steps with your code:\r\n\r\n```\r\n$ CC=clang CFLAGS=-flto ./configure -O3\r\n$ make\r\n$ ./scripts/benchmark.sh -p1 -n50 bc newton_raphson_div_small > ../div2_small.txt\r\n$ ./scripts/benchmark.sh -p1 -n50 bc newton_raphson_div_large > ../div2_large.txt\r\n$ ./scripts/benchmark.sh -p1 -n50 bc newton_raphson_sqrt_small > ../sqrt2_small.txt\r\n$ ./scripts/benchmark.sh -p1 -n50 bc newton_raphson_sqrt_large > ../sqrt2_large.txt\r\n```\r\n\r\nThe `small` benchmarks test the operation on \"small\" numbers (below `2^64`), and the `large` benchmarks test the operation on large numbers (a small number to a power up to 1,000,000).\r\n\r\nTo test how your code did, I used `ministat` in the repo, so I ran this:\r\n\r\n```\r\n$ make ministat\r\n```\r\n\r\nThen to test the time difference on `div_small`, I ran the following:\r\n\r\n```\r\n$ ./scripts/ministat -w80 -c95 -C1 ../div1_small.txt ../div2_small.txt\r\nx ../div1_small.txt\r\n+ ../div2_small.txt\r\n+--------------------------------------------------------------------------------+\r\n| + x |\r\n| + + x * |\r\n| + + * * x |\r\n| + * * * x |\r\n| + * * * x |\r\n| + * * * x |\r\n| * * * * x + |\r\n| * * * * * x + x |\r\n|* * * * * * x * + x |\r\n|* * * * * * * * * x x + +|\r\n||____|____M_AA______|___| |\r\n+--------------------------------------------------------------------------------+\r\n N Min Max Median Avg Stddev\r\nx 50 3.41 3.52 3.45 3.4504 0.024071323\r\n+ 50 3.41 3.67 3.44 3.4484 0.039967334\r\nNo difference proven at 95.0% confidence\r\n```\r\n\r\nAs you can tell, there's no difference at the lowest confidence I can accept.\r\n\r\nTo test the memory difference (max RSS) for `div_small`, I ran the following:\r\n\r\n```\r\n$ ./scripts/ministat -w80 -c95 -C4 ../div1_small.txt ../div2_small.txt\r\nx ../div1_small.txt\r\n+ ../div2_small.txt\r\n+--------------------------------------------------------------------------------+\r\n| x x x+ + + x +xx xx ++ |\r\n| x x + x * x+ +++ ++ x x *+ +xx xx ++ + x + |\r\n|xx x +++ + + x x*x x* *++ ++ x x +x ** +** ** x++ +* +x + +xx ++|\r\n| |_____|_____________A____A_M___________|___| |\r\n+--------------------------------------------------------------------------------+\r\n N Min Max Median Avg Stddev\r\nx 50 34792 35000 34912 34896.88 55.262136\r\n+ 50 34808 35016 34916 34910 52.956663\r\nNo difference proven at 95.0% confidence\r\n```\r\n\r\nNo memory difference. Cool.\r\n\r\nFor the elapsed time for `div_large`:\r\n\r\n```\r\n$ ./scripts/ministat -w80 -c99.5 -C1 ../div1_large.txt ../div2_large.txt\r\nx ../div1_large.txt\r\n+ ../div2_large.txt\r\n+--------------------------------------------------------------------------------+\r\n| x + |\r\n| x ++ |\r\n| x ++ |\r\n| x ++ |\r\n| x ++ |\r\n| x ++ |\r\n| x ++ |\r\n| x ++ |\r\n| x ++ |\r\n| x ++ |\r\n| xx ++ |\r\n| xx ++ |\r\n| xx ++ |\r\n| xx ++ |\r\n| xx ++ |\r\n| xx +++ |\r\n| xx ++++ |\r\n| xx ++++ |\r\n| xxx ++++ |\r\n| xxx x ++++++|\r\n||____MA_____| |A| |\r\n+--------------------------------------------------------------------------------+\r\n N Min Max Median Avg Stddev\r\nx 50 13.72 21.71 13.845 14.013 1.1141964\r\n+ 50 26.7 27.64 27.005 27.0158 0.17722452\r\nDifference at 99.5% confidence\r\n 13.0028 +/- 0.506578\r\n 92.791% +/- 6.90636%\r\n (Student's t, pooled s = 0.79776)\r\n```\r\n\r\nAs you can see, there *is* a difference, with *99.5% confidence*, but in favor of the *old code*. In fact, if not for the one outlier, your code would be twice as slow.\r\n\r\nAnd if we look at the max RSS difference for `div_large`:\r\n\r\n```\r\n$ ./scripts/ministat -w80 -c99.5 -C4 ../div1_large.txt ../div2_large.txt\r\nx ../div1_large.txt\r\n+ ../div2_large.txt\r\n+--------------------------------------------------------------------------------+\r\n| + |\r\n| + |\r\n| + |\r\n| x + |\r\n| x + |\r\n| x + |\r\n| x + |\r\n| x + |\r\n| x + |\r\n| x + |\r\n| x + |\r\n| x + |\r\n| x + |\r\n| x + |\r\n| x + |\r\n| x + |\r\n| x + |\r\n| x + |\r\n| x + |\r\n| x + |\r\n| xx + |\r\n| xx ++|\r\n| xx +++|\r\n|xxx +++|\r\n|xxx +++|\r\n|xxx +++|\r\n|xxx +++|\r\n|xxx +++|\r\n|xxx +++|\r\n|xxx x +++|\r\n|xxxxx +++|\r\n||A| |A||\r\n+--------------------------------------------------------------------------------+\r\n N Min Max Median Avg Stddev\r\nx 50 8716 8968 8796 8802.72 57.549638\r\n+ 50 13812 13960 13888 13890.08 41.837314\r\nDifference at 99.5% confidence\r\n 5087.36 +/- 31.9473\r\n 57.793% +/- 0.51%\r\n (Student's t, pooled s = 50.3106)\r\n```\r\n\r\nYour code uses 50% more memory, with 99.5% confidence.\r\n\r\nMoving onto `sqrt_small`, for elapsed time, I got this:\r\n\r\n```\r\n$ ./scripts/ministat -w80 -c99.5 -C1 ../sqrt1_small.txt ../sqrt2_small.txt\r\nx ../sqrt1_small.txt\r\n+ ../sqrt2_small.txt\r\n+--------------------------------------------------------------------------------+\r\n| x + |\r\n| xx + |\r\n| xx + |\r\n| xx + |\r\n| xx + + |\r\n| xx ++++ |\r\n| xx ++++ |\r\n|xxx ++++ |\r\n|xxx ++++ |\r\n|xxxx +++++ |\r\n|xxxx +++++ |\r\n|xxxx +++++ |\r\n|xxxxx +++++ |\r\n|xxxxx x +++++++ +|\r\n| MA| |_A_| |\r\n+--------------------------------------------------------------------------------+\r\n N Min Max Median Avg Stddev\r\nx 50 1.47 1.6 1.5 1.5072 0.023822473\r\n+ 50 3.07 3.26 3.14 3.1452 0.036153866\r\nDifference at 99.5% confidence\r\n 1.638 +/- 0.0194408\r\n 108.678% +/- 1.83123%\r\n (Student's t, pooled s = 0.0306155)\r\n```\r\n\r\nYour code is twice as slow with 99.5% confidence.\r\n\r\nFor memory on `sqrt_small`, I got this:\r\n\r\n```\r\n$ ./scripts/ministat -w80 -c99.5 -C4 ../sqrt1_small.txt ../sqrt2_small.txt\r\nx ../sqrt1_small.txt\r\n+ ../sqrt2_small.txt\r\n+--------------------------------------------------------------------------------+\r\n| xx + x |\r\n| x xx + x x ++ |\r\n| x x x xx + + x x ++ |\r\n| x xxxx x xxx++ + x x +x x +++ ++ + |\r\n|xx + x x + + xxxxx + + +* *xx***+++ x+xx++***++++ ++++ ++++|\r\n| |_____________|__M__________|A_M____________| |\r\n+--------------------------------------------------------------------------------+\r\n N Min Max Median Avg Stddev\r\nx 50 18964 19252 19176 19160.4 68.353224\r\n+ 50 19004 19344 19242 19231.68 71.990203\r\nDifference at 99.5% confidence\r\n 71.28 +/- 44.574\r\n 0.372017% +/- 0.233047%\r\n (Student's t, pooled s = 70.1953)\r\n```\r\n\r\nSo there's a small increase of memory in your code, even at 99.5% confidence, but that's small enough that I can ignore it.\r\n\r\nFor time on `sqrt_large`, I got this:\r\n\r\n```\r\n$ ./scripts/ministat -w80 -c99.5 -C1 ../sqrt1_large.txt ../sqrt2_large.txt\r\nx ../sqrt1_large.txt\r\n+ ../sqrt2_large.txt\r\n+--------------------------------------------------------------------------------+\r\n| xx + |\r\n| xx ++ |\r\n| xx +++ |\r\n| xx +++ |\r\n| xx +++ |\r\n| xx +++ |\r\n| xx ++++ |\r\n| xx ++++ |\r\n| xxx +++++ |\r\n|xxxx +++++ |\r\n|xxxx +++++ |\r\n|xxxx +++++ |\r\n|xxxxx +++++++|\r\n| |A| |A_| |\r\n+--------------------------------------------------------------------------------+\r\n N Min Max Median Avg Stddev\r\nx 50 176.9 184.63 180.565 180.403 1.6383681\r\n+ 50 327.42 339.26 331.24 331.3832 2.8256482\r\nDifference at 99.5% confidence\r\n 150.98 +/- 1.4666\r\n 83.6905% +/- 1.02747%\r\n (Student's t, pooled s = 2.3096)\r\n```\r\n\r\nYour code is *almost* twice as slow at 99.5% confidence.\r\n\r\nFor memory on `sqrt_large`, I got this:\r\n\r\n```\r\n$ ./scripts/ministat -w80 -c99.5 -C4 ../sqrt1_large.txt ../sqrt2_large.txt\r\nx ../sqrt1_large.txt\r\n+ ../sqrt2_large.txt\r\n+--------------------------------------------------------------------------------+\r\n| x + |\r\n| x + |\r\n| x + |\r\n| x + |\r\n| x + |\r\n| x + |\r\n| x + |\r\n| x + |\r\n| x + |\r\n| x + |\r\n| x + |\r\n| x + |\r\n| x + |\r\n| x + |\r\n| x + |\r\n| x + |\r\n| x + |\r\n| x + |\r\n| x + |\r\n| x + |\r\n| x + |\r\n| x + |\r\n| x ++|\r\n| x ++|\r\n| x ++|\r\n| x ++|\r\n| x ++|\r\n| x ++|\r\n| x ++|\r\n|xx ++|\r\n|xx ++|\r\n|xx ++|\r\n|xx ++|\r\n|xx ++|\r\n|xxx ++|\r\n|xxx ++|\r\n||A A||\r\n+--------------------------------------------------------------------------------+\r\n N Min Max Median Avg Stddev\r\nx 50 6952 7156 7044 7046.48 42.561955\r\n+ 50 15412 15568 15496 15485.84 51.175313\r\nDifference at 99.5% confidence\r\n 8439.36 +/- 29.887\r\n 119.767% +/- 0.679408%\r\n (Student's t, pooled s = 47.0661)\r\n```\r\n\r\nYour code uses twice as much memory at 99.5% confidence.\r\n\r\nSo on average, your code is twice as slow and uses more memory.\r\n\r\nUnfortunately, based on the poor performance of your code compared to what already exists, I can't say that it shows promise.\r\n\r\nCombined with the problems I mentioned at the beginning, I'm afraid I must reject this PR completely. Sorry.\r\n\r\n[1]: https://github.com/gavinhoward/bc/blob/master/manuals/algorithms.md#square-root\r\n[2]: https://github.com/gavinhoward/bc/commit/fcbe9d68ccc964fda12457ebbf687a27f57ff31f",
+ "createdAt": "2023-12-14T04:47:11Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [
+ {
+ "content": "THUMBS_UP",
+ "users": {
+ "totalCount": 1
+ }
+ },
+ {
+ "content": "EYES",
+ "users": {
+ "totalCount": 1
+ }
+ }
+ ],
+ "url": "https://github.com/gavinhoward/bc/pull/72#issuecomment-1855122832",
+ "viewerDidAuthor": true
+ }
+ ],
+ "commits": [
+ {
+ "authoredDate": "2023-12-11T20:27:06Z",
+ "authors": [
+ {
+ "email": "jonathan@acryl.ai",
+ "id": "U_kgDOB2enHg",
+ "login": "naggamura",
+ "name": "jonathan"
+ }
+ ],
+ "committedDate": "2023-12-11T20:27:06Z",
+ "messageBody": "",
+ "messageHeadline": "div, sqrt improved using Newton-Raphson algorithm.",
+ "oid": "d6e0849220eea59ff7b380dfc779a43a33a234d1"
+ },
+ {
+ "authoredDate": "2023-12-12T11:49:47Z",
+ "authors": [
+ {
+ "email": "jonathan@acryl.ai",
+ "id": "U_kgDOB2enHg",
+ "login": "naggamura",
+ "name": "jonathan"
+ }
+ ],
+ "committedDate": "2023-12-12T11:49:47Z",
+ "messageBody": "",
+ "messageHeadline": "Make secure more",
+ "oid": "7a1cd5f7667308300e7f9db46b1827fbc1dc2703"
+ },
+ {
+ "authoredDate": "2023-12-13T13:53:11Z",
+ "authors": [
+ {
+ "email": "jonathan@acryl.ai",
+ "id": "U_kgDOB2enHg",
+ "login": "naggamura",
+ "name": "jonathan"
+ }
+ ],
+ "committedDate": "2023-12-13T13:53:11Z",
+ "messageBody": "",
+ "messageHeadline": "markdown test",
+ "oid": "1f0cce37b56205fbc24db847d184ce452bfdb8fa"
+ },
+ {
+ "authoredDate": "2023-12-13T16:30:08Z",
+ "authors": [
+ {
+ "email": "jonathan@acryl.ai",
+ "id": "U_kgDOB2enHg",
+ "login": "naggamura",
+ "name": "jonathan"
+ }
+ ],
+ "committedDate": "2023-12-13T16:30:08Z",
+ "messageBody": "",
+ "messageHeadline": "Newton-Raphson div, sqrt",
+ "oid": "248e3b9b34cf4893f6440c169584592fc78f6090"
+ }
+ ],
+ "createdAt": "2023-12-13T16:38:28Z",
+ "deletions": 50,
+ "files": [
+ {
+ "path": "newton-raphson-div.png",
+ "additions": 0,
+ "deletions": 0
+ },
+ {
+ "path": "newton-raphson-sqrt.png",
+ "additions": 0,
+ "deletions": 0
+ },
+ {
+ "path": "src/num.c",
+ "additions": 831,
+ "deletions": 50
+ }
+ ],
+ "fullDatabaseId": "1642781993",
+ "headRefName": "newton-raphson",
+ "headRefOid": "248e3b9b34cf4893f6440c169584592fc78f6090",
+ "headRepository": null,
+ "headRepositoryOwner": {
+ "id": "U_kgDOB2enHg",
+ "login": "naggamura"
+ },
+ "id": "PR_kwDOCL0xJc5h6t0p",
+ "isCrossRepository": true,
+ "isDraft": false,
+ "labels": [],
+ "latestReviews": [],
+ "maintainerCanModify": false,
+ "mergeCommit": null,
+ "mergeStateStatus": "UNKNOWN",
+ "mergeable": "UNKNOWN",
+ "mergedAt": null,
+ "mergedBy": null,
+ "milestone": null,
+ "number": 72,
+ "potentialMergeCommit": null,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "reviewDecision": "",
+ "reviewRequests": [],
+ "reviews": [],
+ "state": "CLOSED",
+ "statusCheckRollup": [],
+ "title": "Newton-Raphson divison, sqrt",
+ "updatedAt": "2023-12-14T04:47:11Z",
+ "url": "https://github.com/gavinhoward/bc/pull/72"
+ },
+ {
+ "additions": 337,
+ "assignees": [],
+ "author": {
+ "id": "MDQ6VXNlcjgwNzU4MA==",
+ "is_bot": false,
+ "login": "SamuelMarks",
+ "name": "Samuel Marks"
+ },
+ "autoMergeRequest": null,
+ "baseRefName": "master",
+ "baseRefOid": "3f4c8cc5f86092fa4a8d59bc99d8f10ad2894cf0",
+ "body": "WiP\r\n\r\nCan finish if you like. Can also send you a PR on your own website, just make me an account",
+ "changedFiles": 19,
+ "closed": true,
+ "closedAt": "2025-03-25T05:20:50Z",
+ "comments": [
+ {
+ "id": "IC_kwDOCL0xJc5kTkGI",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Thank you for the PR!\r\n\r\nAllow me to I apologize upfront. Usually, I do not question user requests; after all, there is a user, and they have a request, so obviously, there is a need or a want.\r\n\r\n*However*, personally, [I ***hate*** CMake][1]. I hate it so much that I have spent *three years* designing and implementing a build system to remove it from my [other project][2], a build system that would allow building on Windows, Mac OSX, Linux, the BSD's, etc. And I was going to add support for that build system to `bc`, which I hoped would remove any need for people to use CMake to build `bc` cross-platform.\r\n\r\nSo, I apologize because I can tell you put a lot of work into this, but for once, I am asking you to provide justification. I won't ask for much; I just need to know why CMake is necessary and why you believe that keeping a fork would not serve. (Since a separate build system won't touch the code, it seems like an easy fork to keep.)\r\n\r\nAgain, I'm sorry.\r\n\r\nAlso, if I do decide to merge, I'm going to need you to explain the more complicated bits; I've tried to avoid complicated CMake as much as possible. But I need to be able to maintain the CMake code without your help.\r\n\r\n[1]: https://news.ycombinator.com/item?id=36469860\r\n[2]: https://git.yzena.com/Yzena/Yc",
+ "createdAt": "2023-08-17T19:34:43Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/68#issuecomment-1682850184",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5kVWMk",
+ "author": {
+ "login": "SamuelMarks"
+ },
+ "authorAssociation": "NONE",
+ "body": "@gavinhoward I'm obsessed with interoperability. CMake is one way to achieve this. Open to your build system also, where I plan on taking `bc` to:\r\n- Windows (different MSVC versions);\r\n- MinGW (x86, x64, …)\r\n- DOS (via OpenWatcom and maybe via early MSVC versions)\r\n- Cygwin\r\n- Linux\r\n- macOS\r\n- iOS\r\n- Android\r\n- SunOS (Solaris→OpenSolaris→illumos→OpenIndiana)\r\n- *BSD (FreeBSD, OpenBSD, …)\r\n\r\n…and yes I know you support some of these targets already.\r\n\r\nThere are some other nice advantages like CPack and CTest, but honestly there are a number of other nice build generators out there and I tend to choose CMake just because it's the most popular and has a good number of generators.",
+ "createdAt": "2023-08-18T04:01:35Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/68#issuecomment-1683317540",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc5kVpyv",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Okay, interoperability is something I can understand. But oh boy, I would love it if my build system could do all that.\r\n\r\nThat said, my build system (named Rig, by the way) won't be able to cross-compile in the first release, so some of those targets may not be possible in the first release of Rig. (OpenWatcom is the one that makes me most nervous.)\r\n\r\nSo I'll make you a deal. I'll do these things:\r\n\r\n1. Merge your PR,\r\n2. Set up Rig in my `bc` once it's ready,\r\n3. Write you a tutorial on how to use Rig for `bc`, and\r\n4. Help you understand the new build scripts,\r\n\r\nas long as you'll do the following:\r\n\r\n1. Give me your contact info because this is a pretty big commitment, and I want to be able to ask you questions, if at all possible (you can use the info at <https://gavinhoward.com/contact/> to send me that info privately),\r\n2. Point me to material that will help me set up environments for all of those platforms (so I can test `bc` and CMake on them myself),\r\n3. Explain to me any CMake code I don't understand (I'll make them reviews in the PR),\r\n4. Learn how to use Rig for `bc` once it's ready (I don't care if you use it for anything else), and\r\n5. At that point, give me your honest opinion if Rig can fully replace the CMake build.\r\n\r\nIf it can, I'd ***really*** like to remove the CMake then. If it can't, well, it can't, and CMake will stay in perpetuity.\r\n\r\nIn other words, I'll add CMake now if you'll give Rig a fair shake later.\r\n\r\nOh, and point number 2 is crucial; if I can't test `bc` on those platforms with CMake, I won't merge the PR because I would basically be committing to supporting those platforms by merging the PR, and I can't do that without the ability to test.\r\n\r\nBy the way, I only need to know how to set up environments for:\r\n\r\n* Windows (specifically different MSVC versions because I can already compile on the latest Windows 10);\r\n* MinGW\r\n* DOS (via OpenWatcom and maybe via early MSVC versions)\r\n* Cygwin\r\n* iOS (do I need a Mac computer?)\r\n* Android\r\n* SunOS (Solaris, OpenSolaris, illumos, OpenIndiana)\r\n\r\nI have the capability for the rest.",
+ "createdAt": "2023-08-18T06:04:29Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/68#issuecomment-1683397807",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5kX99Z",
+ "author": {
+ "login": "SamuelMarks"
+ },
+ "authorAssociation": "NONE",
+ "body": "Great yeah sure I'll give it a shot. No guaranteed time commitments (because of everything going on in my life); but I'll see what I can do.\r\n\r\nMy email is samuel` `at symbol offscale.io; or you can use myfirstname mylastname` `at symbol gmail.com.\r\n\r\nI wrote some Windows Batch scripts for building on these different targets, and lots of things can run in a good CI/CD environment (e.g., GitHub Actions, Cirrus CI, &etc.): https://github.com/offscale/win-cmake-multi-build\r\n\r\nObviously still a ways to go to support all the different targets I'm interested in. But yeah something clean like your `bc` might be just complicated enough to test out my interoperability",
+ "createdAt": "2023-08-18T14:29:25Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/68#issuecomment-1684004697",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc5kcbRH",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Okay. Thank you.\r\n\r\nPlease let me know when this PR is not WIP.",
+ "createdAt": "2023-08-20T04:29:28Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/68#issuecomment-1685173319",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5kc7km",
+ "author": {
+ "login": "SamuelMarks"
+ },
+ "authorAssociation": "NONE",
+ "body": "Sure thing, I'll see if I can get to it later in the week. Last remaining errors:\r\n```\r\nbc.c.obj : error LNK2001: unresolved external symbol _bc_help\r\nvm.c.obj : error LNK2001: unresolved external symbol _bc_lib\r\nvm.c.obj : error LNK2001: unresolved external symbol _bc_lib_name\r\nvm.c.obj : error LNK2001: unresolved external symbol _bc_lib2\r\nvm.c.obj : error LNK2001: unresolved external symbol _bc_lib2_name\r\n```\r\n\r\nWhich should be defined in an object file that you define in your existent configure script:\r\n```sh\r\ncontents=$(replace \"$contents\" \"BC_HELP_O\" \"$bc_help\")\r\n```\r\n\r\nSo I just need to reverse-engineer it into CMake and we should be good to go™.",
+ "createdAt": "2023-08-20T14:58:39Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/68#issuecomment-1685305638",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc6j64Rc",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "It's been a year and a half, unfortunately, with no word since then.\r\n\r\nHowever, I have not stood still. I have [added][1] [Rig][2] [to `bc`][3], and I have [implemented the entire test suite in Rig][4].\r\n\r\nIn addition, Rig has the ability to cross compile now, and it will deactivate the tests under those conditions.\r\n\r\nSo because no progress has been made with CMake, and Rig is now working (except on Windows, but the only thing left is getting itself to bootstrap itself), I am rejecting this PR in favor of Rig.\r\n\r\n[1]: https://github.com/gavinhoward/bc/blob/master/build.gaml\r\n[2]: https://github.com/gavinhoward/bc/blob/master/build.rig\r\n[3]: https://github.com/gavinhoward/bc/blob/master/build.pkg.rig\r\n[4]: https://github.com/gavinhoward/bc/commit/77fcd66898f754cb1dbce262c6c0efd0879f855c",
+ "createdAt": "2025-03-25T05:20:50Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/68#issuecomment-2750121052",
+ "viewerDidAuthor": true
+ }
+ ],
+ "commits": [
+ {
+ "authoredDate": "2023-08-17T18:47:20Z",
+ "authors": [
+ {
+ "email": "807580+SamuelMarks@users.noreply.github.com",
+ "id": "MDQ6VXNlcjgwNzU4MA==",
+ "login": "SamuelMarks",
+ "name": "Samuel Marks"
+ }
+ ],
+ "committedDate": "2023-08-17T18:47:20Z",
+ "messageBody": "",
+ "messageHeadline": "CMake support (initial)",
+ "oid": "4256fe30a6eee88983bbe39b606a793a47b96ec0"
+ },
+ {
+ "authoredDate": "2023-08-17T20:06:53Z",
+ "authors": [
+ {
+ "email": "807580+SamuelMarks@users.noreply.github.com",
+ "id": "MDQ6VXNlcjgwNzU4MA==",
+ "login": "SamuelMarks",
+ "name": "Samuel Marks"
+ }
+ ],
+ "committedDate": "2023-08-17T20:06:53Z",
+ "messageBody": "",
+ "messageHeadline": "Improve CMake suppoprt and remove extraneous code & files",
+ "oid": "63e687a593ca72430a23216ab35eb897a4677b36"
+ },
+ {
+ "authoredDate": "2023-08-18T21:02:52Z",
+ "authors": [
+ {
+ "email": "807580+SamuelMarks@users.noreply.github.com",
+ "id": "MDQ6VXNlcjgwNzU4MA==",
+ "login": "SamuelMarks",
+ "name": "Samuel Marks"
+ }
+ ],
+ "committedDate": "2023-08-18T21:02:52Z",
+ "messageBody": "",
+ "messageHeadline": "Improve CMake support",
+ "oid": "a2e95a6e697fa9d6557a16c17ad6bf8d048b5bd3"
+ },
+ {
+ "authoredDate": "2023-08-19T00:43:54Z",
+ "authors": [
+ {
+ "email": "807580+SamuelMarks@users.noreply.github.com",
+ "id": "MDQ6VXNlcjgwNzU4MA==",
+ "login": "SamuelMarks",
+ "name": "Samuel Marks"
+ }
+ ],
+ "committedDate": "2023-08-19T00:43:54Z",
+ "messageBody": "",
+ "messageHeadline": "Improve CMake support",
+ "oid": "b945314d5edcb660bc286f63e60a5a63697b9ee4"
+ }
+ ],
+ "createdAt": "2023-08-17T18:49:48Z",
+ "deletions": 17,
+ "files": [
+ {
+ "path": ".gitignore",
+ "additions": 3,
+ "deletions": 0
+ },
+ {
+ "path": "CMakeLists.txt",
+ "additions": 123,
+ "deletions": 0
+ },
+ {
+ "path": "bcl.pc.in",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "cmake/BundleIcon.icns",
+ "additions": 0,
+ "deletions": 0
+ },
+ {
+ "path": "cmake/CTestConfig.cmake",
+ "additions": 7,
+ "deletions": 0
+ },
+ {
+ "path": "cmake/Config.cmake.in",
+ "additions": 4,
+ "deletions": 0
+ },
+ {
+ "path": "cmake/CustomVolumeIcon.icns",
+ "additions": 0,
+ "deletions": 0
+ },
+ {
+ "path": "cmake/Info.plist",
+ "additions": 14,
+ "deletions": 0
+ },
+ {
+ "path": "cmake/MultiCPackConfig.cmake",
+ "additions": 6,
+ "deletions": 0
+ },
+ {
+ "path": "cmake/README.txt",
+ "additions": 5,
+ "deletions": 0
+ },
+ {
+ "path": "cmake/Welcome.txt",
+ "additions": 1,
+ "deletions": 0
+ },
+ {
+ "path": "cmake/config.h.in",
+ "additions": 11,
+ "deletions": 0
+ },
+ {
+ "path": "include/bc.h",
+ "additions": 8,
+ "deletions": 7
+ },
+ {
+ "path": "include/dc.h",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "include/history.h",
+ "additions": 6,
+ "deletions": 4
+ },
+ {
+ "path": "include/read.h",
+ "additions": 2,
+ "deletions": 1
+ },
+ {
+ "path": "include/vm.h",
+ "additions": 10,
+ "deletions": 2
+ },
+ {
+ "path": "src/CMakeLists.txt",
+ "additions": 134,
+ "deletions": 0
+ },
+ {
+ "path": "src/vm.c",
+ "additions": 1,
+ "deletions": 1
+ }
+ ],
+ "fullDatabaseId": "1479523420",
+ "headRefName": "cmake",
+ "headRefOid": "b945314d5edcb660bc286f63e60a5a63697b9ee4",
+ "headRepository": {
+ "id": "R_kgDOKIWfvQ",
+ "name": "bc"
+ },
+ "headRepositoryOwner": {
+ "id": "MDQ6VXNlcjgwNzU4MA==",
+ "name": "Samuel Marks",
+ "login": "SamuelMarks"
+ },
+ "id": "PR_kwDOCL0xJc5YL7xc",
+ "isCrossRepository": true,
+ "isDraft": false,
+ "labels": [],
+ "latestReviews": [],
+ "maintainerCanModify": false,
+ "mergeCommit": null,
+ "mergeStateStatus": "DIRTY",
+ "mergeable": "CONFLICTING",
+ "mergedAt": null,
+ "mergedBy": null,
+ "milestone": null,
+ "number": 68,
+ "potentialMergeCommit": null,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "reviewDecision": "",
+ "reviewRequests": [],
+ "reviews": [],
+ "state": "CLOSED",
+ "statusCheckRollup": [],
+ "title": "CMake support",
+ "updatedAt": "2025-03-25T05:20:51Z",
+ "url": "https://github.com/gavinhoward/bc/pull/68"
+ },
+ {
+ "additions": 5,
+ "assignees": [],
+ "author": {
+ "id": "MDQ6VXNlcjc0MTgyNg==",
+ "is_bot": false,
+ "login": "dorjechang",
+ "name": "alexander naumochkin"
+ },
+ "autoMergeRequest": null,
+ "baseRefName": "master",
+ "baseRefOid": "71fa3cd4029d416a5272b262736c4d6ab709b6ce",
+ "body": "Casting int -1 to size_t produces SIZE_T_MAX so\r\nBC_NUM_PRINT_WIDTH is used instead of 0",
+ "changedFiles": 1,
+ "closed": true,
+ "closedAt": "2023-03-15T16:06:35Z",
+ "comments": [],
+ "commits": [
+ {
+ "authoredDate": "2023-03-15T12:46:29Z",
+ "authors": [
+ {
+ "email": "alexander.naumochkin@gmail.com",
+ "id": "MDQ6VXNlcjc0MTgyNg==",
+ "login": "dorjechang",
+ "name": "ash"
+ }
+ ],
+ "committedDate": "2023-03-15T12:46:29Z",
+ "messageBody": "Casting int -1 to size_t produces SIZE_T_MAX so\nBC_NUM_PRINT_WIDTH is used instead of 0",
+ "messageHeadline": "Fix incorrect processing of env:BC_LINE_LENGTH=0",
+ "oid": "31042aed8426850187b43d7e639f244354da0d6c"
+ }
+ ],
+ "createdAt": "2023-03-15T13:08:07Z",
+ "deletions": 2,
+ "files": [
+ {
+ "path": "src/vm.c",
+ "additions": 5,
+ "deletions": 2
+ }
+ ],
+ "fullDatabaseId": "1276831340",
+ "headRefName": "fix_BC_LINE_LENGTH=0",
+ "headRefOid": "31042aed8426850187b43d7e639f244354da0d6c",
+ "headRepository": null,
+ "headRepositoryOwner": {
+ "id": "MDQ6VXNlcjc0MTgyNg==",
+ "name": "alexander naumochkin",
+ "login": "dorjechang"
+ },
+ "id": "PR_kwDOCL0xJc5MGuZs",
+ "isCrossRepository": true,
+ "isDraft": false,
+ "labels": [],
+ "latestReviews": [],
+ "maintainerCanModify": false,
+ "mergeCommit": {
+ "oid": "0c42c52dfc0735026cb6d2534dc24436ff19778f"
+ },
+ "mergeStateStatus": "UNKNOWN",
+ "mergeable": "UNKNOWN",
+ "mergedAt": "2023-03-15T16:06:35Z",
+ "mergedBy": {
+ "id": "MDQ6VXNlcjMxNzI2ODc=",
+ "is_bot": false,
+ "login": "gavinhoward",
+ "name": "Gavin D. Howard"
+ },
+ "milestone": null,
+ "number": 65,
+ "potentialMergeCommit": null,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "reviewDecision": "",
+ "reviewRequests": [],
+ "reviews": [],
+ "state": "MERGED",
+ "statusCheckRollup": [],
+ "title": "Fix incorrect processing of env:BC_LINE_LENGTH=0",
+ "updatedAt": "2023-03-15T16:06:35Z",
+ "url": "https://github.com/gavinhoward/bc/pull/65"
+ },
+ {
+ "additions": 16,
+ "assignees": [],
+ "author": {
+ "id": "MDQ6VXNlcjQ2MTYwNzI3",
+ "is_bot": false,
+ "login": "firasuke",
+ "name": "Firas Khalil Khana"
+ },
+ "autoMergeRequest": null,
+ "baseRefName": "master",
+ "baseRefOid": "2ed62ba26831b34cae22b9e7b2f1dd2f3a74b541",
+ "body": "",
+ "changedFiles": 1,
+ "closed": true,
+ "closedAt": "2022-11-26T15:16:46Z",
+ "comments": [],
+ "commits": [
+ {
+ "authoredDate": "2022-11-26T10:30:09Z",
+ "authors": [
+ {
+ "email": "firasuke@gmail.com",
+ "id": "MDQ6VXNlcjQ2MTYwNzI3",
+ "login": "firasuke",
+ "name": "Firas Khalil Khana"
+ }
+ ],
+ "committedDate": "2022-11-26T10:30:09Z",
+ "messageBody": "",
+ "messageHeadline": "Mention MAN3DIR in build.md",
+ "oid": "f62ecdabc44ca2ed32f7f0895a78099919038da9"
+ }
+ ],
+ "createdAt": "2022-11-26T10:30:59Z",
+ "deletions": 7,
+ "files": [
+ {
+ "path": "manuals/build.md",
+ "additions": 16,
+ "deletions": 7
+ }
+ ],
+ "fullDatabaseId": "1136437158",
+ "headRefName": "master",
+ "headRefOid": "f62ecdabc44ca2ed32f7f0895a78099919038da9",
+ "headRepository": null,
+ "headRepositoryOwner": {
+ "id": "MDQ6VXNlcjQ2MTYwNzI3",
+ "name": "Firas Khalil Khana",
+ "login": "firasuke"
+ },
+ "id": "PR_kwDOCL0xJc5DvKem",
+ "isCrossRepository": true,
+ "isDraft": false,
+ "labels": [],
+ "latestReviews": [],
+ "maintainerCanModify": false,
+ "mergeCommit": {
+ "oid": "09247332bf9dafef6840864598c93df92ebc0820"
+ },
+ "mergeStateStatus": "UNKNOWN",
+ "mergeable": "UNKNOWN",
+ "mergedAt": "2022-11-26T15:16:46Z",
+ "mergedBy": {
+ "id": "MDQ6VXNlcjMxNzI2ODc=",
+ "is_bot": false,
+ "login": "gavinhoward",
+ "name": "Gavin D. Howard"
+ },
+ "milestone": null,
+ "number": 57,
+ "potentialMergeCommit": null,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "reviewDecision": "",
+ "reviewRequests": [],
+ "reviews": [],
+ "state": "MERGED",
+ "statusCheckRollup": [],
+ "title": "Mention MAN3DIR in build.md",
+ "updatedAt": "2022-11-26T15:16:46Z",
+ "url": "https://github.com/gavinhoward/bc/pull/57"
+ },
+ {
+ "additions": 1,
+ "assignees": [],
+ "author": {
+ "id": "MDQ6VXNlcjU3MDUxMDY4",
+ "is_bot": false,
+ "login": "pacordova",
+ "name": "Paul Cordova"
+ },
+ "autoMergeRequest": null,
+ "baseRefName": "master",
+ "baseRefOid": "4355599e9a59834098ed18e670b90cec2b84120d",
+ "body": "On POSIX systems, `find` may have indeterministic output (I believe based on the inode on the filesystem).\r\n`sort` is needed to ensure the output is deterministic.\r\n\r\nIn my builds, this resulted in the build order in the generated `Makefile` changing around.\r\nThus building on a fresh partition made with `mkfs.ext4` would result in the binary being different each time.\r\nThe compiled binary code in the`bc` binary shuffled around depending on which files were built first.\r\nPossibly the build order in the Makefile was affecting the order in which files were linked?\r\n\r\nI added `LC_ALL=C` recommended by reproducible-builds.org. \r\nSee below for reference:\r\n- [https://reproducible-builds.org/docs/archives/](https://reproducible-builds.org/docs/archives/)\r\n- [https://reproducible-builds.org/docs/stable-inputs/](https://reproducible-builds.org/docs/stable-inputs/)",
+ "changedFiles": 1,
+ "closed": true,
+ "closedAt": "2022-08-03T02:35:23Z",
+ "comments": [
+ {
+ "id": "IC_kwDOCL0xJc5Huq4A",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "The only hangup I would have had is if the way you did it was not portable to POSIX `sh`. I consulted the standard, and your solution is portable!\r\n\r\nI agree that reproducible builds are *very* important. So thank you for your contribution! I have accepted it, and it will be in the release out in a day or two. I just have to make sure everything builds correctly.",
+ "createdAt": "2022-08-03T02:37:10Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/54#issuecomment-1203416576",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5HuvNl",
+ "author": {
+ "login": "pacordova"
+ },
+ "authorAssociation": "CONTRIBUTOR",
+ "body": "No problem! Thank you for accepting my pull request!\r\nOne additional comment:\r\nWith this change I do get reproducible builds, although I do not use `-flto`\r\nYour recommended optimization of `-O3` I think should be ok, but I do know that Link Time Optimization (LTO) and Profile Guided Optimization (PGO) can both potentially break build determinism.\r\nEither way the end user should be able get reproducible builds if they want it with this change.\r\nThe CFLAGS I was testing with were `CFLAGS='-march=x86-64 -pipe -Os -fstack-protector-strong -fstack-clash-protection'` with GCC 11.2.0.",
+ "createdAt": "2022-08-03T03:13:48Z",
+ "includesCreatedEdit": true,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/54#issuecomment-1203434341",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc5Hwkv2",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Thank you!",
+ "createdAt": "2022-08-03T12:57:26Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/54#issuecomment-1203915766",
+ "viewerDidAuthor": true
+ }
+ ],
+ "commits": [
+ {
+ "authoredDate": "2022-08-02T16:49:50Z",
+ "authors": [
+ {
+ "email": "pac3nc@virginia.edu",
+ "id": "MDQ6VXNlcjU3MDUxMDY4",
+ "login": "pacordova",
+ "name": "pac"
+ }
+ ],
+ "committedDate": "2022-08-02T16:49:50Z",
+ "messageBody": "find has indeterministic output based on the inode on the filesystem,\nso sort is needed to ensure the output is deterministic",
+ "messageHeadline": "improve reproducibility",
+ "oid": "8c7d5fa125dae5049427cb9b0ddc9ee7bbf099b4"
+ },
+ {
+ "authoredDate": "2022-08-02T23:00:34Z",
+ "authors": [
+ {
+ "email": "pac3nc@virginia.edu",
+ "id": "MDQ6VXNlcjU3MDUxMDY4",
+ "login": "pacordova",
+ "name": "pac"
+ }
+ ],
+ "committedDate": "2022-08-02T23:00:34Z",
+ "messageBody": "",
+ "messageHeadline": "remove -z",
+ "oid": "6ea4d12e2f11e0ce0bed779d991a17230db3a889"
+ }
+ ],
+ "createdAt": "2022-08-02T17:17:46Z",
+ "deletions": 1,
+ "files": [
+ {
+ "path": "configure.sh",
+ "additions": 1,
+ "deletions": 1
+ }
+ ],
+ "fullDatabaseId": "1015375109",
+ "headRefName": "reproducible_builds",
+ "headRefOid": "6ea4d12e2f11e0ce0bed779d991a17230db3a889",
+ "headRepository": null,
+ "headRepositoryOwner": {
+ "id": "MDQ6VXNlcjU3MDUxMDY4",
+ "name": "Paul Cordova",
+ "login": "pacordova"
+ },
+ "id": "PR_kwDOCL0xJc48hWUF",
+ "isCrossRepository": true,
+ "isDraft": false,
+ "labels": [],
+ "latestReviews": [],
+ "maintainerCanModify": false,
+ "mergeCommit": {
+ "oid": "71215b0eb1c20aa622bd77e664ad7f099b349e91"
+ },
+ "mergeStateStatus": "UNKNOWN",
+ "mergeable": "UNKNOWN",
+ "mergedAt": "2022-08-03T02:35:23Z",
+ "mergedBy": {
+ "id": "MDQ6VXNlcjMxNzI2ODc=",
+ "is_bot": false,
+ "login": "gavinhoward",
+ "name": "Gavin D. Howard"
+ },
+ "milestone": null,
+ "number": 54,
+ "potentialMergeCommit": null,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "reviewDecision": "",
+ "reviewRequests": [],
+ "reviews": [],
+ "state": "MERGED",
+ "statusCheckRollup": [],
+ "title": "Improve Reproducibility (Reproducible Builds)",
+ "updatedAt": "2022-08-03T12:57:26Z",
+ "url": "https://github.com/gavinhoward/bc/pull/54"
+ },
+ {
+ "additions": 11,
+ "assignees": [],
+ "author": {
+ "id": "MDQ6VXNlcjQxMzkwMDY=",
+ "is_bot": false,
+ "login": "bsdimp",
+ "name": "Warner Losh"
+ },
+ "autoMergeRequest": null,
+ "baseRefName": "master",
+ "baseRefOid": "cad172d8f0962f47b08007beedc8f4637618ffe6",
+ "body": "The historic gnu bc behavior was to have ^D be delete next character if\r\nthere were any characters in the input, or end of file if there\r\nweren't. This matches what emacs users expect for editing the command\r\nline. Implement this by assuming end of file if the input is empty, and\r\ndelete forward character if it isn't.\r\n\r\nSigned-off-by: Warner Losh <imp@bsdimp.com>",
+ "changedFiles": 1,
+ "closed": true,
+ "closedAt": "2022-04-29T14:06:09Z",
+ "comments": [
+ {
+ "id": "IC_kwDOCL0xJc5CTTkH",
+ "author": {
+ "login": "bsdimp"
+ },
+ "authorAssociation": "CONTRIBUTOR",
+ "body": "I've been using this change in my tree for a while now since FreeBSD merged the Howard bc. If there's a way to write a test case for it, please let me know.\r\n\r\nTo test it out, it's simple: 123^B^D^M should leave you in bc and produce the answer 12. Current behavior is to exit after printing 123.",
+ "createdAt": "2022-04-28T15:36:16Z",
+ "includesCreatedEdit": true,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/51#issuecomment-1112357127",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc5CVMF_",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Thank you for this submission. I have tested it, and it works as advertised. It does require a tweak to the test suite to get all of the tests to pass.\r\n\r\nHowever, there are two hangups.\r\n\r\nFirst, if I do merge this, I'm not sure it warrants another release by itself; it might be better to wait until some other change comes along.\r\n\r\nSecond, this does change the behavior of `bc` and would require a major version bump. This is also the biggest reason for wanting at least one other change; this is a small change that requires a big version bump, and I'd want something big.\r\n\r\nBut the fact that it changes behavior also gives me pause. I don't know how many people depend on the `^D` behavior for end-of-file, even when there's input, and I hesitate to make this change without knowing the numbers.\r\n\r\nHowever, I would be satisfied with just a survey of FreeBSD users, which should be the biggest portion of my userbase. (Not many Linux distros have my `bc`, and history is not available on Windows.) That is, I would be satisfied if the survey showed that the vast majority of the FreeBSD users who respond either don't care or agree with the change.\r\n\r\nI would also be satisfied with the blessing of my regular FreeBSD contact, Stefan Esser, who I believe has a good handle on what changes would be okay to make.\r\n\r\nIf you can give me either one of those two things, I'll merge. If Stefan even gives a blessing for a release, I'll do that too.",
+ "createdAt": "2022-04-29T03:47:48Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/51#issuecomment-1112850815",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5CW3df",
+ "author": {
+ "login": "bsdimp"
+ },
+ "authorAssociation": "CONTRIBUTOR",
+ "body": "Since it's only in the command history editing, I'm having trouble understanding how it would affect tests or other input since that code path appears to be disabled when we aren't reading from a tty.\r\n\r\nAnd even if it didn't, ^D on Unix is only a tty signal of EOF. It's not used at the end of files to signify EOF, which is done by zero read.\r\n\r\nFreeBSD used gnu bc since 1998. It has the behavior I've implemented for the past at least 24 years, so it wouldn't be unreasonable to assume that it's what FreeBSD users that care one way or the other would want. Once you've started to edit the text on the line, you don't want to terminate the program and get an answer for the partially edited line.\r\n\r\nI'd argue that this is a mere bug fix to make things more compatible with bc and isn't such a gross behavior change as to warrant a major release bump, but that's just my sensibilities.\r\n\r\nI'm OK waiting on a release. I'll also chat with Stefan as well about this point.",
+ "createdAt": "2022-04-29T13:07:47Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/51#issuecomment-1113290591",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc5CXG7p",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "I have history tests, but they are only run when you run `make test_history` because they can be...flaky. No worries; I'll make the change to fix them. In fact, I might make a change to test this new behavior.\r\n\r\nYou make a compelling and convincing case that this is merely a bug fix. Stefan seems to agree that it is and that many old Unix users would expect this behavior change.\r\n\r\nIn fact, if old Unix users expect the change, then the fact that no one has said anything is probably a good indicator that no one has been using it, which means I can pull the rug out without making anybody mad.\r\n\r\nSo I'm more than satisfied. I'll make the change, and I'll make it in a bugfix release.",
+ "createdAt": "2022-04-29T14:06:00Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/51#issuecomment-1113353961",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5CXNGY",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "I do have a question: should I remove the EOF behavior entirely?",
+ "createdAt": "2022-04-29T14:22:22Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/51#issuecomment-1113379224",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc5CXQAy",
+ "author": {
+ "login": "bsdimp"
+ },
+ "authorAssociation": "CONTRIBUTOR",
+ "body": "> I do have a question: should I remove the EOF behavior entirely?\r\n\r\nNo. The behavior I implemented matches gnu bc's behavior.",
+ "createdAt": "2022-04-29T14:28:47Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [
+ {
+ "content": "THUMBS_UP",
+ "users": {
+ "totalCount": 1
+ }
+ }
+ ],
+ "url": "https://github.com/gavinhoward/bc/pull/51#issuecomment-1113391154",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "IC_kwDOCL0xJc5CXkZ_",
+ "author": {
+ "login": "stesser"
+ },
+ "authorAssociation": "CONTRIBUTOR",
+ "body": " I had sent a reply to Gavin a few hours ago, and I do see that the pull request has already been merged.\r\n\r\nJust a comment regarding compatibility with the previous bc version in FreeBSD:\r\n\r\nWhile GNU bc has the requested behavior, the previous bc in FreeBSD up to 12.x did not:\r\n\r\n$ bc -v\r\nbc (BSD bc) 1.1-FreeBSD\r\n$ bc\r\n12345^B^B^B <- cursor on 3\r\n^D <- bell sound, nothing deleted\r\n<DEL>\r\n1245 <- result after pressing DEL\r\n\r\nIn the FreeBSD bc (which had been obtained from OpenBSD) ^D did only signal EOF if the input line was empty, but it did not delete to the right.\r\n\r\nGNU bc actually has the line editing behavior created by this patch, and thus it is further step towards GNU bc compatibility. (And IIRC, GNU bc had been in the FreeBSD base system, a few decades ago ...)",
+ "createdAt": "2022-04-29T15:55:50Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [
+ {
+ "content": "THUMBS_UP",
+ "users": {
+ "totalCount": 1
+ }
+ }
+ ],
+ "url": "https://github.com/gavinhoward/bc/pull/51#issuecomment-1113474687",
+ "viewerDidAuthor": false
+ }
+ ],
+ "commits": [
+ {
+ "authoredDate": "2022-04-28T15:30:25Z",
+ "authors": [
+ {
+ "email": "imp@FreeBSD.org",
+ "id": "MDQ6VXNlcjQxMzkwMDY=",
+ "login": "bsdimp",
+ "name": "Warner Losh"
+ }
+ ],
+ "committedDate": "2022-04-28T15:35:21Z",
+ "messageBody": "The historic gnu bc behavior was to have ^D be delete next character if\nthere were any characters in the input, or end of file if there\nweren't. This matches what emacs users expect for editing the command\nline. Implement this by assuming end of file if the input is empty, and\ndelete forward character if it isn't.\n\nSigned-off-by: Warner Losh <imp@bsdimp.com>",
+ "messageHeadline": "Emacs delete-next-character",
+ "oid": "cee56074a8fe669901e0869ee8a6e23106d1634b"
+ }
+ ],
+ "createdAt": "2022-04-28T15:35:29Z",
+ "deletions": 4,
+ "files": [
+ {
+ "path": "src/history.c",
+ "additions": 11,
+ "deletions": 4
+ }
+ ],
+ "fullDatabaseId": "922011328",
+ "headRefName": "del-forward",
+ "headRefOid": "cee56074a8fe669901e0869ee8a6e23106d1634b",
+ "headRepository": {
+ "id": "R_kgDOHQG2MA",
+ "name": "bc"
+ },
+ "headRepositoryOwner": {
+ "id": "MDQ6VXNlcjQxMzkwMDY=",
+ "name": "Warner Losh",
+ "login": "bsdimp"
+ },
+ "id": "PR_kwDOCL0xJc429MbA",
+ "isCrossRepository": true,
+ "isDraft": false,
+ "labels": [],
+ "latestReviews": [],
+ "maintainerCanModify": false,
+ "mergeCommit": {
+ "oid": "f8fa6febf7521062f0c4ecb6ca4406c91c125360"
+ },
+ "mergeStateStatus": "UNKNOWN",
+ "mergeable": "UNKNOWN",
+ "mergedAt": "2022-04-29T14:06:09Z",
+ "mergedBy": {
+ "id": "MDQ6VXNlcjMxNzI2ODc=",
+ "is_bot": false,
+ "login": "gavinhoward",
+ "name": "Gavin D. Howard"
+ },
+ "milestone": null,
+ "number": 51,
+ "potentialMergeCommit": null,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "reviewDecision": "",
+ "reviewRequests": [],
+ "reviews": [],
+ "state": "MERGED",
+ "statusCheckRollup": [],
+ "title": "Emacs delete-next-character",
+ "updatedAt": "2022-04-29T15:55:50Z",
+ "url": "https://github.com/gavinhoward/bc/pull/51"
+ },
+ {
+ "additions": 1,
+ "assignees": [],
+ "author": {
+ "id": "MDQ6VXNlcjQ2MTYwNzI3",
+ "is_bot": false,
+ "login": "firasuke",
+ "name": "Firas Khalil Khana"
+ },
+ "autoMergeRequest": null,
+ "baseRefName": "master",
+ "baseRefOid": "c3b2d28b2d615ca2b84ba398d796040348c5f13a",
+ "body": "",
+ "changedFiles": 1,
+ "closed": true,
+ "closedAt": "2021-12-26T16:59:58Z",
+ "comments": [
+ {
+ "id": "IC_kwDOCL0xJc47rUun",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Whoops! Thanks for catching.",
+ "createdAt": "2021-12-26T17:00:08Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/49#issuecomment-1001212839",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "IC_kwDOCL0xJc47rVpi",
+ "author": {
+ "login": "firasuke"
+ },
+ "authorAssociation": "CONTRIBUTOR",
+ "body": "Glad I could help!",
+ "createdAt": "2021-12-26T17:27:15Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/49#issuecomment-1001216610",
+ "viewerDidAuthor": false
+ }
+ ],
+ "commits": [
+ {
+ "authoredDate": "2021-12-26T16:30:19Z",
+ "authors": [
+ {
+ "email": "firasuke@gmail.com",
+ "id": "MDQ6VXNlcjQ2MTYwNzI3",
+ "login": "firasuke",
+ "name": "Firas Khalil Khana"
+ }
+ ],
+ "committedDate": "2021-12-26T16:30:19Z",
+ "messageBody": "",
+ "messageHeadline": "Add missing newline character",
+ "oid": "ef5412b1bc939c0dbe18c7cf523e05de8561b25f"
+ }
+ ],
+ "createdAt": "2021-12-26T16:30:26Z",
+ "deletions": 1,
+ "files": [
+ {
+ "path": "configure.sh",
+ "additions": 1,
+ "deletions": 1
+ }
+ ],
+ "fullDatabaseId": "810041419",
+ "headRefName": "patch-1",
+ "headRefOid": "ef5412b1bc939c0dbe18c7cf523e05de8561b25f",
+ "headRepository": null,
+ "headRepositoryOwner": {
+ "id": "MDQ6VXNlcjQ2MTYwNzI3",
+ "name": "Firas Khalil Khana",
+ "login": "firasuke"
+ },
+ "id": "PR_kwDOCL0xJc4wSEBL",
+ "isCrossRepository": true,
+ "isDraft": false,
+ "labels": [],
+ "latestReviews": [],
+ "maintainerCanModify": false,
+ "mergeCommit": {
+ "oid": "232a12f6870eaf8fed95c12aa33fb3dbd7df8715"
+ },
+ "mergeStateStatus": "UNKNOWN",
+ "mergeable": "UNKNOWN",
+ "mergedAt": "2021-12-26T16:59:58Z",
+ "mergedBy": {
+ "id": "MDQ6VXNlcjMxNzI2ODc=",
+ "is_bot": false,
+ "login": "gavinhoward",
+ "name": "Gavin D. Howard"
+ },
+ "milestone": null,
+ "number": 49,
+ "potentialMergeCommit": null,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "reviewDecision": "",
+ "reviewRequests": [],
+ "reviews": [],
+ "state": "MERGED",
+ "statusCheckRollup": [],
+ "title": "Add missing newline character",
+ "updatedAt": "2021-12-26T17:27:19Z",
+ "url": "https://github.com/gavinhoward/bc/pull/49"
+ },
+ {
+ "additions": 7,
+ "assignees": [],
+ "author": {
+ "id": "MDQ6VXNlcjEzNTQxNjk5",
+ "is_bot": false,
+ "login": "depler",
+ "name": ""
+ },
+ "autoMergeRequest": null,
+ "baseRefName": "master",
+ "baseRefOid": "289ad4a08981c4eaf1328ab9d16f184ee0466a34",
+ "body": "",
+ "changedFiles": 1,
+ "closed": true,
+ "closedAt": "2021-09-29T15:05:03Z",
+ "comments": [
+ {
+ "id": "IC_kwDOCL0xJc43crZP",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Looks good! Thank you!",
+ "createdAt": "2021-09-29T15:05:16Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/44#issuecomment-930264655",
+ "viewerDidAuthor": true
+ }
+ ],
+ "commits": [
+ {
+ "authoredDate": "2021-09-29T09:33:47Z",
+ "authors": [
+ {
+ "email": "depler.mv@gmail.com",
+ "id": "MDQ6VXNlcjEzNTQxNjk5",
+ "login": "depler",
+ "name": "depler"
+ }
+ ],
+ "committedDate": "2021-09-29T09:33:47Z",
+ "messageBody": "",
+ "messageHeadline": "tests_bc.bat fix",
+ "oid": "2d05fde966b12cee7e02220f1466efd7a78a56c4"
+ }
+ ],
+ "createdAt": "2021-09-29T09:34:23Z",
+ "deletions": 7,
+ "files": [
+ {
+ "path": "vs/tests/tests_bc.bat",
+ "additions": 7,
+ "deletions": 7
+ }
+ ],
+ "fullDatabaseId": "745557688",
+ "headRefName": "master",
+ "headRefOid": "2d05fde966b12cee7e02220f1466efd7a78a56c4",
+ "headRepository": null,
+ "headRepositoryOwner": {
+ "id": "MDQ6VXNlcjEzNTQxNjk5",
+ "login": "depler"
+ },
+ "id": "PR_kwDOCL0xJc4scE64",
+ "isCrossRepository": true,
+ "isDraft": false,
+ "labels": [],
+ "latestReviews": [],
+ "maintainerCanModify": false,
+ "mergeCommit": {
+ "oid": "74d55ddfc79df8adeb70cdb2fc81dc05f8bacbde"
+ },
+ "mergeStateStatus": "UNKNOWN",
+ "mergeable": "UNKNOWN",
+ "mergedAt": "2021-09-29T15:05:03Z",
+ "mergedBy": {
+ "id": "MDQ6VXNlcjMxNzI2ODc=",
+ "is_bot": false,
+ "login": "gavinhoward",
+ "name": "Gavin D. Howard"
+ },
+ "milestone": null,
+ "number": 44,
+ "potentialMergeCommit": null,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "reviewDecision": "",
+ "reviewRequests": [],
+ "reviews": [],
+ "state": "MERGED",
+ "statusCheckRollup": [],
+ "title": "tests_bc.bat fix",
+ "updatedAt": "2021-09-29T15:05:16Z",
+ "url": "https://github.com/gavinhoward/bc/pull/44"
+ },
+ {
+ "additions": 1017,
+ "assignees": [],
+ "author": {
+ "id": "MDQ6VXNlcjEzNTQxNjk5",
+ "is_bot": false,
+ "login": "depler",
+ "name": ""
+ },
+ "autoMergeRequest": null,
+ "baseRefName": "master",
+ "baseRefOid": "da29d88f70dca79c8a7e274b1cb5dee3c4bccb6f",
+ "body": "Changes:\r\n- projects moved to subfolder `vs`\r\n- projects created from scratch\r\n- fixed release configuration in executable (code optimizations enabled)\r\n- executable is statically linked (should work without any dependencies starting on Vista and newer)\r\n- library contains configurations `ReleaseMD` and `ReleaseMT` (shared and static linkage)\r\n- added 'vs\\tests' subfolder with scripts for windows (uses original test files, see `tests_bc.bat`, `tests_dc.bat`)\r\n\r\nBy the way, did you test binaries on windows? I've found failed test for dc:\r\n\r\n```\r\nc:\\Data\\Sources\\bc\\vs\\tests>tests_dc.bat\r\nPASS: abs\r\nPASS: add\r\nPASS: arctangent\r\nPASS: arrays\r\nPASS: assignments\r\n\r\n...\r\n\r\nParse error: bad character 't'\r\n <stdin>:2\r\n\r\nFAIL_RUNTIME: vars\r\n```\r\n",
+ "changedFiles": 15,
+ "closed": true,
+ "closedAt": "2021-09-25T21:06:56Z",
+ "comments": [
+ {
+ "id": "IC_kwDOCL0xJc43Q7BQ",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "I'm pulling this now because I made the fixes for all issues based on your code.\r\n\r\nI'm probably going to need your help still.",
+ "createdAt": "2021-09-25T21:06:52Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/38#issuecomment-927182928",
+ "viewerDidAuthor": true
+ }
+ ],
+ "commits": [
+ {
+ "authoredDate": "2021-09-24T22:42:26Z",
+ "authors": [
+ {
+ "email": "depler.mv@gmail.com",
+ "id": "MDQ6VXNlcjEzNTQxNjk5",
+ "login": "depler",
+ "name": "depler"
+ }
+ ],
+ "committedDate": "2021-09-24T22:42:26Z",
+ "messageBody": "",
+ "messageHeadline": "new bc.vcxproj, static linkage, release configuration",
+ "oid": "ae20dd7c1fedf9a7534aca4fda9783356cd15f86"
+ },
+ {
+ "authoredDate": "2021-09-25T07:07:19Z",
+ "authors": [
+ {
+ "email": "depler.mv@gmail.com",
+ "id": "MDQ6VXNlcjEzNTQxNjk5",
+ "login": "depler",
+ "name": "depler"
+ }
+ ],
+ "committedDate": "2021-09-25T07:07:19Z",
+ "messageBody": "",
+ "messageHeadline": "bcl project",
+ "oid": "1c9479c50217773732f4111e4c3dfbc7e7415c1e"
+ },
+ {
+ "authoredDate": "2021-09-25T07:15:33Z",
+ "authors": [
+ {
+ "email": "depler.mv@gmail.com",
+ "id": "MDQ6VXNlcjEzNTQxNjk5",
+ "login": "depler",
+ "name": "depler"
+ }
+ ],
+ "committedDate": "2021-09-25T07:15:33Z",
+ "messageBody": "",
+ "messageHeadline": "ReleaseMD, ReleaseMT",
+ "oid": "99a58fea193b6bcc1bec4adcd02c53522da20c86"
+ },
+ {
+ "authoredDate": "2021-09-25T07:16:06Z",
+ "authors": [
+ {
+ "email": "depler.mv@gmail.com",
+ "id": "MDQ6VXNlcjEzNTQxNjk5",
+ "login": "depler",
+ "name": "depler"
+ }
+ ],
+ "committedDate": "2021-09-25T07:16:06Z",
+ "messageBody": "",
+ "messageHeadline": "cleanup",
+ "oid": "2230f31c8d4d53223a1dc13b0b399e374e57999f"
+ },
+ {
+ "authoredDate": "2021-09-25T07:20:11Z",
+ "authors": [
+ {
+ "email": "depler.mv@gmail.com",
+ "id": "MDQ6VXNlcjEzNTQxNjk5",
+ "login": "depler",
+ "name": "depler"
+ }
+ ],
+ "committedDate": "2021-09-25T07:20:11Z",
+ "messageBody": "",
+ "messageHeadline": "fix",
+ "oid": "0a08c1a2d382fdf1406eecfc6669d8279810c51a"
+ },
+ {
+ "authoredDate": "2021-09-25T10:48:31Z",
+ "authors": [
+ {
+ "email": "depler.mv@gmail.com",
+ "id": "MDQ6VXNlcjEzNTQxNjk5",
+ "login": "depler",
+ "name": "depler"
+ }
+ ],
+ "committedDate": "2021-09-25T10:48:31Z",
+ "messageBody": "",
+ "messageHeadline": "windows tests",
+ "oid": "2aaf6a7c624b441346200126504e713e1e324553"
+ }
+ ],
+ "createdAt": "2021-09-25T07:27:03Z",
+ "deletions": 760,
+ "files": [
+ {
+ "path": "bc.vcxproj",
+ "additions": 0,
+ "deletions": 278
+ },
+ {
+ "path": "bc.vcxproj.filters",
+ "additions": 0,
+ "deletions": 182
+ },
+ {
+ "path": "bcl.sln",
+ "additions": 0,
+ "deletions": 31
+ },
+ {
+ "path": "bcl.vcxproj",
+ "additions": 0,
+ "deletions": 161
+ },
+ {
+ "path": "bcl.vcxproj.filters",
+ "additions": 0,
+ "deletions": 96
+ },
+ {
+ "path": "src/program.c",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "vs/.gitignore",
+ "additions": 7,
+ "deletions": 0
+ },
+ {
+ "path": "vs/bc.sln",
+ "additions": 11,
+ "deletions": 11
+ },
+ {
+ "path": "vs/bc.vcxproj",
+ "additions": 298,
+ "deletions": 0
+ },
+ {
+ "path": "vs/bc.vcxproj.filters",
+ "additions": 173,
+ "deletions": 0
+ },
+ {
+ "path": "vs/bcl.sln",
+ "additions": 37,
+ "deletions": 0
+ },
+ {
+ "path": "vs/bcl.vcxproj",
+ "additions": 259,
+ "deletions": 0
+ },
+ {
+ "path": "vs/bcl.vcxproj.filters",
+ "additions": 90,
+ "deletions": 0
+ },
+ {
+ "path": "vs/tests/tests_bc.bat",
+ "additions": 81,
+ "deletions": 0
+ },
+ {
+ "path": "vs/tests/tests_dc.bat",
+ "additions": 60,
+ "deletions": 0
+ }
+ ],
+ "fullDatabaseId": "742640199",
+ "headRefName": "master",
+ "headRefOid": "2aaf6a7c624b441346200126504e713e1e324553",
+ "headRepository": null,
+ "headRepositoryOwner": {
+ "id": "MDQ6VXNlcjEzNTQxNjk5",
+ "login": "depler"
+ },
+ "id": "PR_kwDOCL0xJc4sQ8pH",
+ "isCrossRepository": true,
+ "isDraft": false,
+ "labels": [],
+ "latestReviews": [],
+ "maintainerCanModify": false,
+ "mergeCommit": {
+ "oid": "d3793fa5a17c198482baeee027464b98a3869270"
+ },
+ "mergeStateStatus": "UNKNOWN",
+ "mergeable": "UNKNOWN",
+ "mergedAt": "2021-09-25T21:06:56Z",
+ "mergedBy": {
+ "id": "MDQ6VXNlcjMxNzI2ODc=",
+ "is_bot": false,
+ "login": "gavinhoward",
+ "name": "Gavin D. Howard"
+ },
+ "milestone": null,
+ "number": 38,
+ "potentialMergeCommit": null,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "reviewDecision": "",
+ "reviewRequests": [],
+ "reviews": [],
+ "state": "MERGED",
+ "statusCheckRollup": [],
+ "title": "Visual Studio projects refactoring",
+ "updatedAt": "2021-09-25T21:06:56Z",
+ "url": "https://github.com/gavinhoward/bc/pull/38"
+ },
+ {
+ "additions": 1,
+ "assignees": [],
+ "author": {
+ "id": "MDQ6VXNlcjM4ODMzNTE=",
+ "is_bot": false,
+ "login": "ibara",
+ "name": "Brian Callahan"
+ },
+ "autoMergeRequest": null,
+ "baseRefName": "master",
+ "baseRefOid": "3317fc9138148ac48beb4886206be45ebac2a838",
+ "body": "on => one",
+ "changedFiles": 1,
+ "closed": true,
+ "closedAt": "2020-03-11T17:36:56Z",
+ "comments": [
+ {
+ "id": "MDEyOklzc3VlQ29tbWVudDU5NzcwNjM0Nw==",
+ "author": {
+ "login": "codecov"
+ },
+ "authorAssociation": "NONE",
+ "body": "# [Codecov](https://codecov.io/gh/gavinhoward/bc/pull/28?src=pr&el=h1) Report\n> Merging [#28](https://codecov.io/gh/gavinhoward/bc/pull/28?src=pr&el=desc) into [master](https://codecov.io/gh/gavinhoward/bc/commit/3317fc9138148ac48beb4886206be45ebac2a838&el=desc) will **not change** coverage by `%`.\n> The diff coverage is `n/a`.\n\n[![Impacted file tree graph](https://codecov.io/gh/gavinhoward/bc/pull/28/graphs/tree.svg?width=650&height=150&src=pr&token=7VDYTvCBQX)](https://codecov.io/gh/gavinhoward/bc/pull/28?src=pr&el=tree)\n\n```diff\n@@ Coverage Diff @@\n## master #28 +/- ##\n=======================================\n Coverage 99.65% 99.65% \n=======================================\n Files 16 16 \n Lines 3497 3497 \n=======================================\n Hits 3485 3485 \n Misses 12 12 \n```\n\n\n\n------\n\n[Continue to review full report at Codecov](https://codecov.io/gh/gavinhoward/bc/pull/28?src=pr&el=continue).\n> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)\n> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`\n> Powered by [Codecov](https://codecov.io/gh/gavinhoward/bc/pull/28?src=pr&el=footer). Last update [3317fc9...24c93ff](https://codecov.io/gh/gavinhoward/bc/pull/28?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).\n",
+ "createdAt": "2020-03-11T15:39:52Z",
+ "includesCreatedEdit": true,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/28#issuecomment-597706347",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "MDEyOklzc3VlQ29tbWVudDU5Nzc3MTUwMw==",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Thank you! Merged.",
+ "createdAt": "2020-03-11T17:39:45Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/28#issuecomment-597771503",
+ "viewerDidAuthor": true
+ }
+ ],
+ "commits": [
+ {
+ "authoredDate": "2020-03-11T15:37:45Z",
+ "authors": [
+ {
+ "email": "ibara@users.noreply.github.com",
+ "id": "MDQ6VXNlcjM4ODMzNTE=",
+ "login": "ibara",
+ "name": "Brian Callahan"
+ }
+ ],
+ "committedDate": "2020-03-11T15:37:45Z",
+ "messageBody": "on => one",
+ "messageHeadline": "Fix a typo in build.md",
+ "oid": "24c93ffc52bfcae55f3b694e9684b49a105834e7"
+ }
+ ],
+ "createdAt": "2020-03-11T15:37:54Z",
+ "deletions": 1,
+ "files": [
+ {
+ "path": "manuals/build.md",
+ "additions": 1,
+ "deletions": 1
+ }
+ ],
+ "fullDatabaseId": "386765314",
+ "headRefName": "patch-1",
+ "headRefOid": "24c93ffc52bfcae55f3b694e9684b49a105834e7",
+ "headRepository": null,
+ "headRepositoryOwner": {
+ "id": "MDQ6VXNlcjM4ODMzNTE=",
+ "name": "Brian Callahan",
+ "login": "ibara"
+ },
+ "id": "MDExOlB1bGxSZXF1ZXN0Mzg2NzY1MzE0",
+ "isCrossRepository": true,
+ "isDraft": false,
+ "labels": [],
+ "latestReviews": [],
+ "maintainerCanModify": false,
+ "mergeCommit": {
+ "oid": "725f14b41767d34e6be02fa07dc2db659fa55b02"
+ },
+ "mergeStateStatus": "UNKNOWN",
+ "mergeable": "UNKNOWN",
+ "mergedAt": "2020-03-11T17:36:56Z",
+ "mergedBy": {
+ "id": "MDQ6VXNlcjMxNzI2ODc=",
+ "is_bot": false,
+ "login": "gavinhoward",
+ "name": "Gavin D. Howard"
+ },
+ "milestone": null,
+ "number": 28,
+ "potentialMergeCommit": null,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "reviewDecision": "",
+ "reviewRequests": [],
+ "reviews": [],
+ "state": "MERGED",
+ "statusCheckRollup": [],
+ "title": "Fix a typo in build.md",
+ "updatedAt": "2020-03-11T18:18:12Z",
+ "url": "https://github.com/gavinhoward/bc/pull/28"
+ },
+ {
+ "additions": 86,
+ "assignees": [],
+ "author": {
+ "id": "MDQ6VXNlcjMwOTE2NjEz",
+ "is_bot": false,
+ "login": "zv-io",
+ "name": ""
+ },
+ "autoMergeRequest": null,
+ "baseRefName": "master",
+ "baseRefOid": "358951d4fa58db3077a5675a6547c44e41101de9",
+ "body": "This series addresses some bugs found on Solaris as discussed with the author in IRC earlier today. The following systems have been \"loosely\" tested to compile and pass the non-external-`bc` test suite:\r\n\r\n* Solaris 10 [sun4u] (GCC `5.5.0`) in both 32- and 64- bit modes;\r\n* Solaris 11 [sun4v] (GCC `4.8.2` and `7.5.0`) in both 32- and 64- bit modes;\r\n\r\nNote: Solaris 10 requires aliasing to a POSIX-compliant shell (e.g. `/usr/xpg4/bin/sh`) or modifying the top of any scripts citing `/bin/sh` to this value.",
+ "changedFiles": 62,
+ "closed": true,
+ "closedAt": "2020-01-15T22:33:33Z",
+ "comments": [
+ {
+ "id": "MDEyOklzc3VlQ29tbWVudDU3NDQ5NzI3Mw==",
+ "author": {
+ "login": "codecov"
+ },
+ "authorAssociation": "NONE",
+ "body": "# [Codecov](https://codecov.io/gh/gavinhoward/bc/pull/27?src=pr&el=h1) Report\n> Merging [#27](https://codecov.io/gh/gavinhoward/bc/pull/27?src=pr&el=desc) into [master](https://codecov.io/gh/gavinhoward/bc/commit/358951d4fa58db3077a5675a6547c44e41101de9?src=pr&el=desc) will **not change** coverage.\n> The diff coverage is `n/a`.\n\n[![Impacted file tree graph](https://codecov.io/gh/gavinhoward/bc/pull/27/graphs/tree.svg?width=650&token=7VDYTvCBQX&height=150&src=pr)](https://codecov.io/gh/gavinhoward/bc/pull/27?src=pr&el=tree)\n\n```diff\n@@ Coverage Diff @@\n## master #27 +/- ##\n=======================================\n Coverage 99.65% 99.65% \n=======================================\n Files 16 16 \n Lines 3496 3496 \n=======================================\n Hits 3484 3484 \n Misses 12 12\n```\n\n\n| [Impacted Files](https://codecov.io/gh/gavinhoward/bc/pull/27?src=pr&el=tree) | Coverage Δ | |\n|---|---|---|\n| [src/bc/parse.c](https://codecov.io/gh/gavinhoward/bc/pull/27/diff?src=pr&el=tree#diff-c3JjL2JjL3BhcnNlLmM=) | `100% <ø> (ø)` | :arrow_up: |\n| [src/parse.c](https://codecov.io/gh/gavinhoward/bc/pull/27/diff?src=pr&el=tree#diff-c3JjL3BhcnNlLmM=) | `100% <ø> (ø)` | :arrow_up: |\n| [src/read.c](https://codecov.io/gh/gavinhoward/bc/pull/27/diff?src=pr&el=tree#diff-c3JjL3JlYWQuYw==) | `100% <ø> (ø)` | :arrow_up: |\n| [src/vm.c](https://codecov.io/gh/gavinhoward/bc/pull/27/diff?src=pr&el=tree#diff-c3JjL3ZtLmM=) | `99.59% <ø> (ø)` | :arrow_up: |\n| [src/main.c](https://codecov.io/gh/gavinhoward/bc/pull/27/diff?src=pr&el=tree#diff-c3JjL21haW4uYw==) | `100% <ø> (ø)` | :arrow_up: |\n| [src/num.c](https://codecov.io/gh/gavinhoward/bc/pull/27/diff?src=pr&el=tree#diff-c3JjL251bS5j) | `99.41% <ø> (ø)` | :arrow_up: |\n| [src/lex.c](https://codecov.io/gh/gavinhoward/bc/pull/27/diff?src=pr&el=tree#diff-c3JjL2xleC5j) | `100% <ø> (ø)` | :arrow_up: |\n| [src/bc/bc.c](https://codecov.io/gh/gavinhoward/bc/pull/27/diff?src=pr&el=tree#diff-c3JjL2JjL2JjLmM=) | `100% <ø> (ø)` | :arrow_up: |\n| [src/bc/lex.c](https://codecov.io/gh/gavinhoward/bc/pull/27/diff?src=pr&el=tree#diff-c3JjL2JjL2xleC5j) | `100% <ø> (ø)` | :arrow_up: |\n| [src/lang.c](https://codecov.io/gh/gavinhoward/bc/pull/27/diff?src=pr&el=tree#diff-c3JjL2xhbmcuYw==) | `97.33% <ø> (ø)` | :arrow_up: |\n| ... and [6 more](https://codecov.io/gh/gavinhoward/bc/pull/27/diff?src=pr&el=tree-more) | |\n\n------\n\n[Continue to review full report at Codecov](https://codecov.io/gh/gavinhoward/bc/pull/27?src=pr&el=continue).\n> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)\n> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`\n> Powered by [Codecov](https://codecov.io/gh/gavinhoward/bc/pull/27?src=pr&el=footer). Last update [358951d...a756285](https://codecov.io/gh/gavinhoward/bc/pull/27?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).\n",
+ "createdAt": "2020-01-15T05:02:18Z",
+ "includesCreatedEdit": true,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/27#issuecomment-574497273",
+ "viewerDidAuthor": false
+ }
+ ],
+ "commits": [
+ {
+ "authoredDate": "2020-01-14T21:56:45Z",
+ "authors": [
+ {
+ "email": "me@zv.io",
+ "id": "MDQ6VXNlcjMwOTE2NjEz",
+ "login": "zv-io",
+ "name": "Zach van Rijn"
+ }
+ ],
+ "committedDate": "2020-01-14T22:17:19Z",
+ "messageBody": "",
+ "messageHeadline": "Fix bug caused by inconsistent preservation of line endings by 'sed'.",
+ "oid": "d3b3813c94d6553caad61752dd7ac99dd860e20c"
+ },
+ {
+ "authoredDate": "2020-01-14T22:03:42Z",
+ "authors": [
+ {
+ "email": "me@zv.io",
+ "id": "MDQ6VXNlcjMwOTE2NjEz",
+ "login": "zv-io",
+ "name": "Zach van Rijn"
+ }
+ ],
+ "committedDate": "2020-01-14T22:17:26Z",
+ "messageBody": "…'ls' command. Improve robustness of test conditions.",
+ "messageHeadline": "Switch from POSIX 2008 --> 2001 build flags. Remove use of dangerous …",
+ "oid": "72238e589bf7e8cb6a9bb380f367c1a5edcc8b82"
+ },
+ {
+ "authoredDate": "2020-01-14T22:05:02Z",
+ "authors": [
+ {
+ "email": "me@zv.io",
+ "id": "MDQ6VXNlcjMwOTE2NjEz",
+ "login": "zv-io",
+ "name": "Zach van Rijn"
+ }
+ ],
+ "committedDate": "2020-01-14T22:17:27Z",
+ "messageBody": "",
+ "messageHeadline": "Update copyright year 2019 --> 2020.",
+ "oid": "eb34ee5e9d5607c08e7080d9c44e03d393756493"
+ },
+ {
+ "authoredDate": "2020-01-14T22:24:01Z",
+ "authors": [
+ {
+ "email": "me@zv.io",
+ "id": "MDQ6VXNlcjMwOTE2NjEz",
+ "login": "zv-io",
+ "name": "Zach van Rijn"
+ }
+ ],
+ "committedDate": "2020-01-14T22:24:01Z",
+ "messageBody": "…sions.",
+ "messageHeadline": "Explicit '-std=c99' to avoid ambiguity across different compilers/ver…",
+ "oid": "a38469fd93c20d1de3f972c982de3c234000116a"
+ },
+ {
+ "authoredDate": "2020-01-14T22:57:04Z",
+ "authors": [
+ {
+ "email": "me@zv.io",
+ "id": "MDQ6VXNlcjMwOTE2NjEz",
+ "login": "zv-io",
+ "name": "Zach van Rijn"
+ }
+ ],
+ "committedDate": "2020-01-14T22:59:48Z",
+ "messageBody": "",
+ "messageHeadline": "Update documentation and prepare for release.",
+ "oid": "f022d5dedc841bb1d23fa2e03fdc97a639de700d"
+ },
+ {
+ "authoredDate": "2020-01-15T13:56:00Z",
+ "authors": [
+ {
+ "email": "me@zv.io",
+ "id": "MDQ6VXNlcjMwOTE2NjEz",
+ "login": "zv-io",
+ "name": "Zach van Rijn"
+ }
+ ],
+ "committedDate": "2020-01-15T13:56:00Z",
+ "messageBody": "…lers/versions.\"\n\nThis reverts commit a38469fd93c20d1de3f972c982de3c234000116a.",
+ "messageHeadline": "Revert \"Explicit '-std=c99' to avoid ambiguity across different compi…",
+ "oid": "8b83e334177701cd2c05fd10090a1f582bd7e9f8"
+ },
+ {
+ "authoredDate": "2020-01-15T13:57:24Z",
+ "authors": [
+ {
+ "email": "me@zv.io",
+ "id": "MDQ6VXNlcjMwOTE2NjEz",
+ "login": "zv-io",
+ "name": "Zach van Rijn"
+ }
+ ],
+ "committedDate": "2020-01-15T13:57:24Z",
+ "messageBody": "",
+ "messageHeadline": "Comply with versioning policy as per author feedback in PR 27.",
+ "oid": "a756285f60b2139fc629c3edf230c8b1f9a875ed"
+ }
+ ],
+ "createdAt": "2020-01-15T04:59:12Z",
+ "deletions": 79,
+ "files": [
+ {
+ "path": "Makefile.in",
+ "additions": 3,
+ "deletions": 3
+ },
+ {
+ "path": "NEWS.md",
+ "additions": 6,
+ "deletions": 0
+ },
+ {
+ "path": "README.md",
+ "additions": 3,
+ "deletions": 2
+ },
+ {
+ "path": "configure.sh",
+ "additions": 12,
+ "deletions": 12
+ },
+ {
+ "path": "functions.sh",
+ "additions": 3,
+ "deletions": 3
+ },
+ {
+ "path": "gen/bc_help.txt",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "gen/dc_help.txt",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "gen/lib.bc",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "gen/lib2.bc",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "gen/strgen.c",
+ "additions": 2,
+ "deletions": 2
+ },
+ {
+ "path": "include/args.h",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "include/bc.h",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "include/dc.h",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "include/history.h",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "include/lang.h",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "include/lex.h",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "include/num.h",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "include/parse.h",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "include/program.h",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "include/read.h",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "include/status.h",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "include/vector.h",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "include/vm.h",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "install.sh",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "karatsuba.py",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "link.sh",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "locale_install.sh",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "locale_uninstall.sh",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "locales/en_US.msg",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "locales/fr_FR.ISO8859-1.msg",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "locales/fr_FR.UTF-8.msg",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "locales/pt_PT.ISO-8859-1.msg",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "locales/pt_PT.UTF-8.msg",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "release.sh",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "src/args.c",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "src/bc/bc.c",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "src/bc/lex.c",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "src/bc/parse.c",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "src/data.c",
+ "additions": 2,
+ "deletions": 2
+ },
+ {
+ "path": "src/dc/dc.c",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "src/dc/lex.c",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "src/dc/parse.c",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "src/history/history.c",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "src/lang.c",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "src/lex.c",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "src/main.c",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "src/num.c",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "src/parse.c",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "src/program.c",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "src/read.c",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "src/vector.c",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "src/vm.c",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "tests/afl.py",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "tests/all.sh",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "tests/bc/timeconst.sh",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "tests/errors.sh",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "tests/randmath.py",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "tests/read.sh",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "tests/script.sh",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "tests/scripts.sh",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "tests/stdin.sh",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "tests/test.sh",
+ "additions": 1,
+ "deletions": 1
+ }
+ ],
+ "fullDatabaseId": "362957298",
+ "headRefName": "master",
+ "headRefOid": "a756285f60b2139fc629c3edf230c8b1f9a875ed",
+ "headRepository": null,
+ "headRepositoryOwner": {
+ "id": "MDQ6VXNlcjMwOTE2NjEz",
+ "login": "zv-io"
+ },
+ "id": "MDExOlB1bGxSZXF1ZXN0MzYyOTU3Mjk4",
+ "isCrossRepository": true,
+ "isDraft": false,
+ "labels": [],
+ "latestReviews": [
+ {
+ "id": "",
+ "author": {
+ "login": "michaelforney"
+ },
+ "authorAssociation": "CONTRIBUTOR",
+ "body": "",
+ "submittedAt": "2020-01-15T06:00:48Z",
+ "includesCreatedEdit": false,
+ "reactionGroups": [],
+ "state": "COMMENTED",
+ "commit": {
+ "oid": ""
+ }
+ },
+ {
+ "id": "",
+ "author": {
+ "login": "ibara"
+ },
+ "authorAssociation": "CONTRIBUTOR",
+ "body": "",
+ "submittedAt": "2020-01-15T20:20:03Z",
+ "includesCreatedEdit": false,
+ "reactionGroups": [],
+ "state": "COMMENTED",
+ "commit": {
+ "oid": ""
+ }
+ },
+ {
+ "id": "",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "",
+ "submittedAt": "2020-01-15T22:33:19Z",
+ "includesCreatedEdit": false,
+ "reactionGroups": [],
+ "state": "APPROVED",
+ "commit": {
+ "oid": ""
+ }
+ }
+ ],
+ "maintainerCanModify": false,
+ "mergeCommit": {
+ "oid": "063a155d1ee6c68bfba37733777ad1331810fdf1"
+ },
+ "mergeStateStatus": "UNKNOWN",
+ "mergeable": "UNKNOWN",
+ "mergedAt": "2020-01-15T22:33:33Z",
+ "mergedBy": {
+ "id": "MDQ6VXNlcjMxNzI2ODc=",
+ "is_bot": false,
+ "login": "gavinhoward",
+ "name": "Gavin D. Howard"
+ },
+ "milestone": null,
+ "number": 27,
+ "potentialMergeCommit": null,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "reviewDecision": "APPROVED",
+ "reviewRequests": [],
+ "reviews": [
+ {
+ "id": "MDE3OlB1bGxSZXF1ZXN0UmV2aWV3MzQyOTk0NDA3",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Thank you for this PR! Unfortunately, it needs some work before I can accept it. See reviews below.",
+ "submittedAt": "2020-01-15T05:32:13Z",
+ "includesCreatedEdit": false,
+ "reactionGroups": [],
+ "state": "CHANGES_REQUESTED",
+ "commit": {
+ "oid": "d3b3813c94d6553caad61752dd7ac99dd860e20c"
+ }
+ },
+ {
+ "id": "MDE3OlB1bGxSZXF1ZXN0UmV2aWV3MzQzMDAyMDEx",
+ "author": {
+ "login": "michaelforney"
+ },
+ "authorAssociation": "CONTRIBUTOR",
+ "body": "",
+ "submittedAt": "2020-01-15T06:00:48Z",
+ "includesCreatedEdit": false,
+ "reactionGroups": [],
+ "state": "COMMENTED",
+ "commit": {
+ "oid": "f022d5dedc841bb1d23fa2e03fdc97a639de700d"
+ }
+ },
+ {
+ "id": "MDE3OlB1bGxSZXF1ZXN0UmV2aWV3MzQzMDAyNTYx",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "",
+ "submittedAt": "2020-01-15T06:02:59Z",
+ "includesCreatedEdit": false,
+ "reactionGroups": [],
+ "state": "COMMENTED",
+ "commit": {
+ "oid": "f022d5dedc841bb1d23fa2e03fdc97a639de700d"
+ }
+ },
+ {
+ "id": "MDE3OlB1bGxSZXF1ZXN0UmV2aWV3MzQzMDEwNjM1",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "",
+ "submittedAt": "2020-01-15T06:34:47Z",
+ "includesCreatedEdit": false,
+ "reactionGroups": [],
+ "state": "COMMENTED",
+ "commit": {
+ "oid": "f022d5dedc841bb1d23fa2e03fdc97a639de700d"
+ }
+ },
+ {
+ "id": "MDE3OlB1bGxSZXF1ZXN0UmV2aWV3MzQzMjM5MTU3",
+ "author": {
+ "login": "zv-io"
+ },
+ "authorAssociation": "CONTRIBUTOR",
+ "body": "",
+ "submittedAt": "2020-01-15T14:04:17Z",
+ "includesCreatedEdit": false,
+ "reactionGroups": [],
+ "state": "COMMENTED",
+ "commit": {
+ "oid": "f022d5dedc841bb1d23fa2e03fdc97a639de700d"
+ }
+ },
+ {
+ "id": "MDE3OlB1bGxSZXF1ZXN0UmV2aWV3MzQzMjQ0ODg5",
+ "author": {
+ "login": "zv-io"
+ },
+ "authorAssociation": "CONTRIBUTOR",
+ "body": "",
+ "submittedAt": "2020-01-15T14:12:43Z",
+ "includesCreatedEdit": false,
+ "reactionGroups": [],
+ "state": "COMMENTED",
+ "commit": {
+ "oid": "f022d5dedc841bb1d23fa2e03fdc97a639de700d"
+ }
+ },
+ {
+ "id": "MDE3OlB1bGxSZXF1ZXN0UmV2aWV3MzQzMjU1NTQ0",
+ "author": {
+ "login": "zv-io"
+ },
+ "authorAssociation": "CONTRIBUTOR",
+ "body": "",
+ "submittedAt": "2020-01-15T14:27:10Z",
+ "includesCreatedEdit": false,
+ "reactionGroups": [],
+ "state": "COMMENTED",
+ "commit": {
+ "oid": "f022d5dedc841bb1d23fa2e03fdc97a639de700d"
+ }
+ },
+ {
+ "id": "MDE3OlB1bGxSZXF1ZXN0UmV2aWV3MzQzMjU2MjE0",
+ "author": {
+ "login": "zv-io"
+ },
+ "authorAssociation": "CONTRIBUTOR",
+ "body": "",
+ "submittedAt": "2020-01-15T14:28:02Z",
+ "includesCreatedEdit": false,
+ "reactionGroups": [],
+ "state": "COMMENTED",
+ "commit": {
+ "oid": "f022d5dedc841bb1d23fa2e03fdc97a639de700d"
+ }
+ },
+ {
+ "id": "MDE3OlB1bGxSZXF1ZXN0UmV2aWV3MzQzMjczNTgx",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "",
+ "submittedAt": "2020-01-15T14:50:50Z",
+ "includesCreatedEdit": false,
+ "reactionGroups": [],
+ "state": "COMMENTED",
+ "commit": {
+ "oid": "f022d5dedc841bb1d23fa2e03fdc97a639de700d"
+ }
+ },
+ {
+ "id": "MDE3OlB1bGxSZXF1ZXN0UmV2aWV3MzQzMjc3MTQ4",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "",
+ "submittedAt": "2020-01-15T14:55:18Z",
+ "includesCreatedEdit": false,
+ "reactionGroups": [],
+ "state": "COMMENTED",
+ "commit": {
+ "oid": "f022d5dedc841bb1d23fa2e03fdc97a639de700d"
+ }
+ },
+ {
+ "id": "MDE3OlB1bGxSZXF1ZXN0UmV2aWV3MzQzNDk1MzQ1",
+ "author": {
+ "login": "ibara"
+ },
+ "authorAssociation": "CONTRIBUTOR",
+ "body": "",
+ "submittedAt": "2020-01-15T20:20:03Z",
+ "includesCreatedEdit": false,
+ "reactionGroups": [],
+ "state": "COMMENTED",
+ "commit": {
+ "oid": "a756285f60b2139fc629c3edf230c8b1f9a875ed"
+ }
+ },
+ {
+ "id": "MDE3OlB1bGxSZXF1ZXN0UmV2aWV3MzQzNDk5ODcx",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "",
+ "submittedAt": "2020-01-15T20:28:14Z",
+ "includesCreatedEdit": false,
+ "reactionGroups": [],
+ "state": "COMMENTED",
+ "commit": {
+ "oid": "a756285f60b2139fc629c3edf230c8b1f9a875ed"
+ }
+ },
+ {
+ "id": "MDE3OlB1bGxSZXF1ZXN0UmV2aWV3MzQzNTY3ODE3",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "",
+ "submittedAt": "2020-01-15T22:33:01Z",
+ "includesCreatedEdit": false,
+ "reactionGroups": [],
+ "state": "COMMENTED",
+ "commit": {
+ "oid": "a756285f60b2139fc629c3edf230c8b1f9a875ed"
+ }
+ },
+ {
+ "id": "MDE3OlB1bGxSZXF1ZXN0UmV2aWV3MzQzNTY3OTcx",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "",
+ "submittedAt": "2020-01-15T22:33:19Z",
+ "includesCreatedEdit": false,
+ "reactionGroups": [],
+ "state": "APPROVED",
+ "commit": {
+ "oid": "a756285f60b2139fc629c3edf230c8b1f9a875ed"
+ }
+ }
+ ],
+ "state": "MERGED",
+ "statusCheckRollup": [],
+ "title": "Improve portability, enable build on Solaris SPARC.",
+ "updatedAt": "2020-01-15T22:33:33Z",
+ "url": "https://github.com/gavinhoward/bc/pull/27"
+ },
+ {
+ "additions": 218,
+ "assignees": [],
+ "author": {
+ "id": "MDQ6VXNlcjM5NzU3OTY3",
+ "is_bot": false,
+ "login": "bugcrazy",
+ "name": ""
+ },
+ "autoMergeRequest": null,
+ "baseRefName": "master",
+ "baseRefOid": "39b0dc440325511db730ad8fbd7012ba6f3f4b4d",
+ "body": "Portuguese Translations pt_PT and pt_BR",
+ "changedFiles": 8,
+ "closed": true,
+ "closedAt": "2020-01-14T15:08:19Z",
+ "comments": [
+ {
+ "id": "MDEyOklzc3VlQ29tbWVudDU3NDAyODE2Nw==",
+ "author": {
+ "login": "codecov"
+ },
+ "authorAssociation": "NONE",
+ "body": "# [Codecov](https://codecov.io/gh/gavinhoward/bc/pull/26?src=pr&el=h1) Report\n> Merging [#26](https://codecov.io/gh/gavinhoward/bc/pull/26?src=pr&el=desc) into [master](https://codecov.io/gh/gavinhoward/bc/commit/39b0dc440325511db730ad8fbd7012ba6f3f4b4d?src=pr&el=desc) will **not change** coverage.\n> The diff coverage is `n/a`.\n\n[![Impacted file tree graph](https://codecov.io/gh/gavinhoward/bc/pull/26/graphs/tree.svg?width=650&token=7VDYTvCBQX&height=150&src=pr)](https://codecov.io/gh/gavinhoward/bc/pull/26?src=pr&el=tree)\n\n```diff\n@@ Coverage Diff @@\n## master #26 +/- ##\n=======================================\n Coverage 99.65% 99.65% \n=======================================\n Files 16 16 \n Lines 3496 3496 \n=======================================\n Hits 3484 3484 \n Misses 12 12\n```\n\n\n\n------\n\n[Continue to review full report at Codecov](https://codecov.io/gh/gavinhoward/bc/pull/26?src=pr&el=continue).\n> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)\n> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`\n> Powered by [Codecov](https://codecov.io/gh/gavinhoward/bc/pull/26?src=pr&el=footer). Last update [39b0dc4...2572333](https://codecov.io/gh/gavinhoward/bc/pull/26?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).\n",
+ "createdAt": "2020-01-14T06:43:10Z",
+ "includesCreatedEdit": true,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/26#issuecomment-574028167",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "MDEyOklzc3VlQ29tbWVudDU3NDIxOTEzMg==",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Thank you for your translations! They look good. Release of Version `2.5.0` with the translations will happen in a few minutes.",
+ "createdAt": "2020-01-14T15:09:22Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/26#issuecomment-574219132",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "MDEyOklzc3VlQ29tbWVudDU3NDIyMzU0Mw==",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Version `2.5.0` is out with your translations. Thank you!",
+ "createdAt": "2020-01-14T15:17:19Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [
+ {
+ "content": "THUMBS_UP",
+ "users": {
+ "totalCount": 1
+ }
+ }
+ ],
+ "url": "https://github.com/gavinhoward/bc/pull/26#issuecomment-574223543",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "MDEyOklzc3VlQ29tbWVudDU3NDI0MTA2Mw==",
+ "author": {
+ "login": "bugcrazy"
+ },
+ "authorAssociation": "CONTRIBUTOR",
+ "body": "Thank you!",
+ "createdAt": "2020-01-14T15:52:18Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/26#issuecomment-574241063",
+ "viewerDidAuthor": false
+ }
+ ],
+ "commits": [
+ {
+ "authoredDate": "2020-01-14T06:32:59Z",
+ "authors": [
+ {
+ "email": "39757967+bugcrazy@users.noreply.github.com",
+ "id": "MDQ6VXNlcjM5NzU3OTY3",
+ "login": "bugcrazy",
+ "name": "bugcrazy"
+ }
+ ],
+ "committedDate": "2020-01-14T06:32:59Z",
+ "messageBody": "Portuguese Translations pt_PT and pt_BR",
+ "messageHeadline": "Portuguese Translations",
+ "oid": "25723337b998ced4976dc5beae8717d81056dc5f"
+ }
+ ],
+ "createdAt": "2020-01-14T06:40:25Z",
+ "deletions": 0,
+ "files": [
+ {
+ "path": "locales/pt_BR.ISO-8859-1.msg",
+ "additions": 1,
+ "deletions": 0
+ },
+ {
+ "path": "locales/pt_BR.ISO-8859-15.msg",
+ "additions": 1,
+ "deletions": 0
+ },
+ {
+ "path": "locales/pt_BR.UTF-8.msg",
+ "additions": 1,
+ "deletions": 0
+ },
+ {
+ "path": "locales/pt_BR.utf8.msg",
+ "additions": 1,
+ "deletions": 0
+ },
+ {
+ "path": "locales/pt_PT.ISO-8859-1.msg",
+ "additions": 106,
+ "deletions": 0
+ },
+ {
+ "path": "locales/pt_PT.ISO-8859-15.msg",
+ "additions": 1,
+ "deletions": 0
+ },
+ {
+ "path": "locales/pt_PT.UTF-8.msg",
+ "additions": 106,
+ "deletions": 0
+ },
+ {
+ "path": "locales/pt_PT.utf8.msg",
+ "additions": 1,
+ "deletions": 0
+ }
+ ],
+ "fullDatabaseId": "362470901",
+ "headRefName": "master",
+ "headRefOid": "25723337b998ced4976dc5beae8717d81056dc5f",
+ "headRepository": {
+ "id": "MDEwOlJlcG9zaXRvcnkyMzM3NjI4NjY=",
+ "name": "bc"
+ },
+ "headRepositoryOwner": {
+ "id": "MDQ6VXNlcjM5NzU3OTY3",
+ "login": "bugcrazy"
+ },
+ "id": "MDExOlB1bGxSZXF1ZXN0MzYyNDcwOTAx",
+ "isCrossRepository": true,
+ "isDraft": false,
+ "labels": [],
+ "latestReviews": [],
+ "maintainerCanModify": false,
+ "mergeCommit": {
+ "oid": "c322c80e62415e69c8d65c69904a0e1ea37f4da3"
+ },
+ "mergeStateStatus": "UNKNOWN",
+ "mergeable": "UNKNOWN",
+ "mergedAt": "2020-01-14T15:08:19Z",
+ "mergedBy": {
+ "id": "MDQ6VXNlcjMxNzI2ODc=",
+ "is_bot": false,
+ "login": "gavinhoward",
+ "name": "Gavin D. Howard"
+ },
+ "milestone": null,
+ "number": 26,
+ "potentialMergeCommit": null,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "reviewDecision": "",
+ "reviewRequests": [],
+ "reviews": [],
+ "state": "MERGED",
+ "statusCheckRollup": [],
+ "title": "Portuguese Translations",
+ "updatedAt": "2020-01-14T15:52:18Z",
+ "url": "https://github.com/gavinhoward/bc/pull/26"
+ },
+ {
+ "additions": 6,
+ "assignees": [],
+ "author": {
+ "id": "MDQ6VXNlcjIyMjk2NzA2",
+ "is_bot": false,
+ "login": "eg15",
+ "name": "Eugene Gladchenko"
+ },
+ "autoMergeRequest": null,
+ "baseRefName": "master",
+ "baseRefOid": "38cc5145cb66ba9f1706630d5f987d60a46fb1b1",
+ "body": "Looks like I found a bug.\r\n\"-1000000000 < -1\" returns 0 (false) on my machine which is obviously wrong.\r\nThe fix will follow.",
+ "changedFiles": 4,
+ "closed": true,
+ "closedAt": "2019-11-22T04:42:08Z",
+ "comments": [
+ {
+ "id": "MDEyOklzc3VlQ29tbWVudDU1NzE0ODY4Mw==",
+ "author": {
+ "login": "codecov"
+ },
+ "authorAssociation": "NONE",
+ "body": "# [Codecov](https://codecov.io/gh/gavinhoward/bc/pull/25?src=pr&el=h1) Report\n> Merging [#25](https://codecov.io/gh/gavinhoward/bc/pull/25?src=pr&el=desc) into [master](https://codecov.io/gh/gavinhoward/bc/commit/38cc5145cb66ba9f1706630d5f987d60a46fb1b1?src=pr&el=desc) will **not change** coverage.\n> The diff coverage is `100%`.\n\n[![Impacted file tree graph](https://codecov.io/gh/gavinhoward/bc/pull/25/graphs/tree.svg?width=650&token=7VDYTvCBQX&height=150&src=pr)](https://codecov.io/gh/gavinhoward/bc/pull/25?src=pr&el=tree)\n\n```diff\n@@ Coverage Diff @@\n## master #25 +/- ##\n=======================================\n Coverage 99.71% 99.71% \n=======================================\n Files 16 16 \n Lines 3478 3478 \n=======================================\n Hits 3468 3468 \n Misses 10 10\n```\n\n\n| [Impacted Files](https://codecov.io/gh/gavinhoward/bc/pull/25?src=pr&el=tree) | Coverage Δ | |\n|---|---|---|\n| [src/num.c](https://codecov.io/gh/gavinhoward/bc/pull/25/diff?src=pr&el=tree#diff-c3JjL251bS5j) | `99.41% <100%> (ø)` | :arrow_up: |\n\n------\n\n[Continue to review full report at Codecov](https://codecov.io/gh/gavinhoward/bc/pull/25?src=pr&el=continue).\n> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)\n> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`\n> Powered by [Codecov](https://codecov.io/gh/gavinhoward/bc/pull/25?src=pr&el=footer). Last update [38cc514...96518d8](https://codecov.io/gh/gavinhoward/bc/pull/25?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).\n",
+ "createdAt": "2019-11-21T15:56:12Z",
+ "includesCreatedEdit": true,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/25#issuecomment-557148683",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "MDEyOklzc3VlQ29tbWVudDU1NzE2MDgyMg==",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Yes, you did find a bug.\r\n\r\nYour fix looks great, and your tests do as well. I just have two requests:\r\n\r\n1. Move your tests and results into files called `comp.txt` and `comp_results.txt`.\r\n2. Change the name in `tests/bc/all.txt`.\r\n\r\nI want you to do this because I am going to write a more comprehensive set of tests for comparisons in general to make sure I did not miss anything else.\r\n\r\nIf you don't have the time to do this, I could always pull it and do the change myself.",
+ "createdAt": "2019-11-21T16:22:26Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/25#issuecomment-557160822",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "MDEyOklzc3VlQ29tbWVudDU1NzIwNjQwNw==",
+ "author": {
+ "login": "eg15"
+ },
+ "authorAssociation": "CONTRIBUTOR",
+ "body": "Just finished, thank you.\r\n\r\nBTW it looks like busybox guys took your code and kept the bug in their modified version.\r\nJust fixed it there as well: https://bugs.busybox.net/show_bug.cgi?id=12336\r\n\r\nThen it should be fixed in Alpine (that's where I encountered it this morning).",
+ "createdAt": "2019-11-21T18:10:24Z",
+ "includesCreatedEdit": true,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/25#issuecomment-557206407",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "MDEyOklzc3VlQ29tbWVudDU1NzIzMDk2NA==",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "`busybox` didn't exactly take my code; I gave them my code, when it had a lot more bugs as well. Unfortunately, they decided to go their own way with it, so I don't really contribute back. (They added even more bugs.)\r\n\r\nI will merge this when I get home from work. I should have a release out after testing, which could take up to a week. I don't expect this to take that long to test, since it's so small, but I may find other problems.",
+ "createdAt": "2019-11-21T19:14:33Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/25#issuecomment-557230964",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "MDEyOklzc3VlQ29tbWVudDU1NzM4NjYzOQ==",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "I pulled manually. Thank you for your help!",
+ "createdAt": "2019-11-22T04:44:22Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [
+ {
+ "content": "THUMBS_UP",
+ "users": {
+ "totalCount": 1
+ }
+ }
+ ],
+ "url": "https://github.com/gavinhoward/bc/pull/25#issuecomment-557386639",
+ "viewerDidAuthor": true
+ }
+ ],
+ "commits": [
+ {
+ "authoredDate": "2019-11-21T15:31:00Z",
+ "authors": [
+ {
+ "email": "gladchenko@gmail.com",
+ "id": "MDQ6VXNlcjIyMjk2NzA2",
+ "login": "eg15",
+ "name": "Eugene Gladchenko"
+ }
+ ],
+ "committedDate": "2019-11-21T15:31:00Z",
+ "messageBody": "",
+ "messageHeadline": "Add a test to compare negative numbers",
+ "oid": "888c422c21b558d3778d4ad215fd54f9387a137b"
+ },
+ {
+ "authoredDate": "2019-11-21T16:00:43Z",
+ "authors": [
+ {
+ "email": "gladchenko@gmail.com",
+ "id": "MDQ6VXNlcjIyMjk2NzA2",
+ "login": "eg15",
+ "name": "Eugene Gladchenko"
+ }
+ ],
+ "committedDate": "2019-11-21T16:00:43Z",
+ "messageBody": "",
+ "messageHeadline": "Fix a bug in comparing negative numbers",
+ "oid": "ddbecaa0601375b86dffb525973148cdd0dda8f7"
+ },
+ {
+ "authoredDate": "2019-11-21T18:05:15Z",
+ "authors": [
+ {
+ "email": "gladchenko@gmail.com",
+ "id": "MDQ6VXNlcjIyMjk2NzA2",
+ "login": "eg15",
+ "name": "Eugene Gladchenko"
+ }
+ ],
+ "committedDate": "2019-11-21T18:05:15Z",
+ "messageBody": "",
+ "messageHeadline": "The name of the test file changed",
+ "oid": "96518d8fd63fca1d01b82ac78a42251844f39201"
+ }
+ ],
+ "createdAt": "2019-11-21T15:52:02Z",
+ "deletions": 1,
+ "files": [
+ {
+ "path": "src/num.c",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "tests/bc/all.txt",
+ "additions": 1,
+ "deletions": 0
+ },
+ {
+ "path": "tests/bc/comp.txt",
+ "additions": 2,
+ "deletions": 0
+ },
+ {
+ "path": "tests/bc/comp_results.txt",
+ "additions": 2,
+ "deletions": 0
+ }
+ ],
+ "fullDatabaseId": "344102387",
+ "headRefName": "master",
+ "headRefOid": "96518d8fd63fca1d01b82ac78a42251844f39201",
+ "headRepository": {
+ "id": "MDEwOlJlcG9zaXRvcnkyMjMyMDIxNDg=",
+ "name": "bc"
+ },
+ "headRepositoryOwner": {
+ "id": "MDQ6VXNlcjIyMjk2NzA2",
+ "name": "Eugene Gladchenko",
+ "login": "eg15"
+ },
+ "id": "MDExOlB1bGxSZXF1ZXN0MzQ0MTAyMzg3",
+ "isCrossRepository": true,
+ "isDraft": false,
+ "labels": [],
+ "latestReviews": [],
+ "maintainerCanModify": false,
+ "mergeCommit": {
+ "oid": "96518d8fd63fca1d01b82ac78a42251844f39201"
+ },
+ "mergeStateStatus": "UNKNOWN",
+ "mergeable": "UNKNOWN",
+ "mergedAt": "2019-11-22T04:42:07Z",
+ "mergedBy": {
+ "id": "MDQ6VXNlcjMxNzI2ODc=",
+ "is_bot": false,
+ "login": "gavinhoward",
+ "name": "Gavin D. Howard"
+ },
+ "milestone": null,
+ "number": 25,
+ "potentialMergeCommit": null,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "reviewDecision": "",
+ "reviewRequests": [],
+ "reviews": [],
+ "state": "MERGED",
+ "statusCheckRollup": [],
+ "title": "Add a test to compare negative numbers",
+ "updatedAt": "2019-11-22T04:44:22Z",
+ "url": "https://github.com/gavinhoward/bc/pull/25"
+ },
+ {
+ "additions": 43,
+ "assignees": [],
+ "author": {
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "is_bot": false,
+ "login": "stesser",
+ "name": "Stefan Eßer"
+ },
+ "autoMergeRequest": null,
+ "baseRefName": "master",
+ "baseRefOid": "b6cc5efba0e127c5a344c25505c807865494cc66",
+ "body": "I do not know how to restrict the pull request to just the two commits I consider relevant (probably is the GIT way to create a temporary branch for that purpose?)\r\n\r\nThe relevant commits are: \r\n\r\nca56e06\r\n684e3f6",
+ "changedFiles": 3,
+ "closed": true,
+ "closedAt": "2019-06-14T01:43:15Z",
+ "comments": [
+ {
+ "id": "MDEyOklzc3VlQ29tbWVudDUwMTgzMTY1Ng==",
+ "author": {
+ "login": "codecov"
+ },
+ "authorAssociation": "NONE",
+ "body": "# [Codecov](https://codecov.io/gh/gavinhoward/bc/pull/24?src=pr&el=h1) Report\n> Merging [#24](https://codecov.io/gh/gavinhoward/bc/pull/24?src=pr&el=desc) into [master](https://codecov.io/gh/gavinhoward/bc/commit/b6cc5efba0e127c5a344c25505c807865494cc66?src=pr&el=desc) will **increase** coverage by `<.01%`.\n> The diff coverage is `100%`.\n\n[![Impacted file tree graph](https://codecov.io/gh/gavinhoward/bc/pull/24/graphs/tree.svg?width=650&token=7VDYTvCBQX&height=150&src=pr)](https://codecov.io/gh/gavinhoward/bc/pull/24?src=pr&el=tree)\n\n```diff\n@@ Coverage Diff @@\n## master #24 +/- ##\n==========================================\n+ Coverage 99.73% 99.73% +<.01% \n==========================================\n Files 16 16 \n Lines 3431 3435 +4 \n==========================================\n+ Hits 3422 3426 +4 \n Misses 9 9\n```\n\n\n| [Impacted Files](https://codecov.io/gh/gavinhoward/bc/pull/24?src=pr&el=tree) | Coverage Δ | |\n|---|---|---|\n| [src/bc/parse.c](https://codecov.io/gh/gavinhoward/bc/pull/24/diff?src=pr&el=tree#diff-c3JjL2JjL3BhcnNlLmM=) | `100% <ø> (ø)` | :arrow_up: |\n| [src/program.c](https://codecov.io/gh/gavinhoward/bc/pull/24/diff?src=pr&el=tree#diff-c3JjL3Byb2dyYW0uYw==) | `99.73% <100%> (ø)` | :arrow_up: |\n| [src/num.c](https://codecov.io/gh/gavinhoward/bc/pull/24/diff?src=pr&el=tree#diff-c3JjL251bS5j) | `99.8% <100%> (ø)` | :arrow_up: |\n\n------\n\n[Continue to review full report at Codecov](https://codecov.io/gh/gavinhoward/bc/pull/24?src=pr&el=continue).\n> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)\n> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`\n> Powered by [Codecov](https://codecov.io/gh/gavinhoward/bc/pull/24?src=pr&el=footer). Last update [b6cc5ef...ca56e06](https://codecov.io/gh/gavinhoward/bc/pull/24?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).\n",
+ "createdAt": "2019-06-13T18:43:42Z",
+ "includesCreatedEdit": true,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/24#issuecomment-501831656",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "MDEyOklzc3VlQ29tbWVudDUwMTg1MDQyMQ==",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "I have already made these changes in my local branch. I just haven't pushed them yet. I will push them when I get home from work, and I will close this then to remind me.\r\n\r\nThank you.",
+ "createdAt": "2019-06-13T19:39:54Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/24#issuecomment-501850421",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "MDEyOklzc3VlQ29tbWVudDUwMTkzNzA0Ng==",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Changes are pushed.",
+ "createdAt": "2019-06-14T01:43:15Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/24#issuecomment-501937046",
+ "viewerDidAuthor": true
+ }
+ ],
+ "commits": [
+ {
+ "authoredDate": "2019-06-09T11:10:39Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-06-09T11:10:39Z",
+ "messageBody": "",
+ "messageHeadline": "Small optimization: no data to copy for BC_RESULT_ONE or _LAST",
+ "oid": "b32b26ca8a533480155a427906025cbe9a0c061c"
+ },
+ {
+ "authoredDate": "2019-06-10T21:15:49Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-06-10T21:15:49Z",
+ "messageBody": "",
+ "messageHeadline": "Make limit for extension of division results depend on BC_BASE_DIGS",
+ "oid": "1846088a5d37f02e608228e4214ed76464abdd05"
+ },
+ {
+ "authoredDate": "2019-06-10T21:17:33Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-06-10T21:17:33Z",
+ "messageBody": "",
+ "messageHeadline": "Simplify and speed up parsing of decimal numbers",
+ "oid": "5954930eb013ed5e10bea2599735f06b05b89caa"
+ },
+ {
+ "authoredDate": "2019-06-12T10:16:50Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-06-12T10:16:50Z",
+ "messageBody": "Do not allocate one BcDig per character of the string passed in.\nFor ibase between 11 and 26 (decimal) more than one BcDig may be\nneeded to store the value expressed by BC_BASE_DIGS characters.\n\nTesting for that case will probably cause more overhead than just\napplying a fudge factor (not verified - special casing the ibase\nrange that needs this factor and using a factor of 1.5 instead of\n2 might be worth the extra code complexity).",
+ "messageHeadline": "Fix allocation size for parsed number",
+ "oid": "c2dc7941ee0115ba8e820685021d70ab2ab13a91"
+ },
+ {
+ "authoredDate": "2019-06-12T10:25:11Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-06-12T10:25:11Z",
+ "messageBody": "",
+ "messageHeadline": "Merge branch 'master' of https://github.com/gavinhoward/bc",
+ "oid": "036b112a9d04b56b98ba3697e1641afa6352bbeb"
+ },
+ {
+ "authoredDate": "2019-06-12T12:00:07Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-06-12T12:00:07Z",
+ "messageBody": "This change improves \"make test\" performance by more than 2%.",
+ "messageHeadline": "Better estimate of the number of BcDigs required to store a constant",
+ "oid": "43caceec40e4265edc8244b092bd39ba483361c5"
+ },
+ {
+ "authoredDate": "2019-06-12T21:43:54Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-06-12T21:43:54Z",
+ "messageBody": "This implementation is not optimized for speed, yet. I have plans to\nadd copy-on-write semantics for BcNums (i.e. add a reference count and\ncopy the BcNum only if its refcount is > 1 it is about to be\nmodified).\n\nThis version fails in the bc/stdin test, but diagnosing this problem\nwill have to wait for tomorrow ...",
+ "messageHeadline": "Implement caching of numeric constants in vectors of BcNums",
+ "oid": "515dfe44886667eb9fbea48c214b559549d69417"
+ },
+ {
+ "authoredDate": "2019-06-13T08:23:45Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-06-13T08:23:45Z",
+ "messageBody": "Beyond the changes performed by the automatic merge, remove references\nto the constvals field in struct BcFunc.",
+ "messageHeadline": "Merge branch 'master' of https://github.com/gavinhoward/bc",
+ "oid": "cd30020d2fe2d4cef802b2e65299f4e0e1a3972f"
+ },
+ {
+ "authoredDate": "2019-06-13T08:33:21Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-06-13T08:33:21Z",
+ "messageBody": "",
+ "messageHeadline": "Merge more changes from upstream",
+ "oid": "21e4395740342aa65e728d02e1a5d658a9a15bc0"
+ },
+ {
+ "authoredDate": "2019-06-13T08:49:54Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-06-13T08:49:54Z",
+ "messageBody": "",
+ "messageHeadline": "Adjust whitespace and use value of \"base\" instead of fetching it again",
+ "oid": "684e3f6f8a7dd8efb5b49211ef20948710cf6457"
+ },
+ {
+ "authoredDate": "2019-06-13T09:14:04Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-06-13T09:14:04Z",
+ "messageBody": "",
+ "messageHeadline": "Fix for failing scale(0.0000000000000000000) test case",
+ "oid": "25799a6fa21c69cb7f9e77e53329d3b735564c9b"
+ },
+ {
+ "authoredDate": "2019-06-13T11:05:20Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-06-13T11:05:20Z",
+ "messageBody": "FreeBSD uses strict checks for base system components and the build\nfailed due to the compiler warnings being treated as errors in base.",
+ "messageHeadline": "Remove unused local variable declarations (fix build in FreeBSD)",
+ "oid": "ca56e060a471ed5476a78b38c1b2d0584777d693"
+ }
+ ],
+ "createdAt": "2019-06-13T18:39:56Z",
+ "deletions": 49,
+ "files": [
+ {
+ "path": "src/bc/parse.c",
+ "additions": 0,
+ "deletions": 2
+ },
+ {
+ "path": "src/num.c",
+ "additions": 37,
+ "deletions": 39
+ },
+ {
+ "path": "src/program.c",
+ "additions": 6,
+ "deletions": 8
+ }
+ ],
+ "fullDatabaseId": "288048911",
+ "headRefName": "master",
+ "headRefOid": "ca56e060a471ed5476a78b38c1b2d0584777d693",
+ "headRepository": {
+ "id": "MDEwOlJlcG9zaXRvcnkxODAzNzI3NzY=",
+ "name": "bc"
+ },
+ "headRepositoryOwner": {
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "name": "Stefan Eßer",
+ "login": "stesser"
+ },
+ "id": "MDExOlB1bGxSZXF1ZXN0Mjg4MDQ4OTEx",
+ "isCrossRepository": true,
+ "isDraft": false,
+ "labels": [],
+ "latestReviews": [],
+ "maintainerCanModify": false,
+ "mergeCommit": null,
+ "mergeStateStatus": "DIRTY",
+ "mergeable": "CONFLICTING",
+ "mergedAt": null,
+ "mergedBy": null,
+ "milestone": null,
+ "number": 24,
+ "potentialMergeCommit": null,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "reviewDecision": "",
+ "reviewRequests": [],
+ "reviews": [],
+ "state": "CLOSED",
+ "statusCheckRollup": [],
+ "title": "Whitespace adjustment and removal of some unused variables",
+ "updatedAt": "2019-06-14T01:43:15Z",
+ "url": "https://github.com/gavinhoward/bc/pull/24"
+ },
+ {
+ "additions": 1,
+ "assignees": [],
+ "author": {
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "is_bot": false,
+ "login": "stesser",
+ "name": "Stefan Eßer"
+ },
+ "autoMergeRequest": null,
+ "baseRefName": "master",
+ "baseRefOid": "f555270c99e7d354704071628486df0870968286",
+ "body": "Benchmarks show a small but significant speed-up of \"make test\" if the\r\nlimit for the shifting of operands is made more strict. Many operands\r\nwill require a second iteration of the inner loop anyway, and shifting\r\nof the operands uses cycles for no gain, then.\r\n\r\nTo my surprise, BC_BASE_DIGS=4 and =9 seem to have near identical\r\nperformance after this change. Some operations (multiplication and\r\npower) ought to be faster with larger BcDigs, but at least for the\r\nperformance of \"make test\" on my system this does not seem to affect\r\nthe run-time by a significant amount.",
+ "changedFiles": 1,
+ "closed": true,
+ "closedAt": "2019-06-07T13:25:25Z",
+ "comments": [
+ {
+ "id": "MDEyOklzc3VlQ29tbWVudDQ5OTg4MzYwNQ==",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "I am not planning on future releases, except for bug fixes. I will merge this now.",
+ "createdAt": "2019-06-07T13:25:27Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/23#issuecomment-499883605",
+ "viewerDidAuthor": true
+ }
+ ],
+ "commits": [
+ {
+ "authoredDate": "2019-06-07T07:33:26Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-06-07T07:33:26Z",
+ "messageBody": "Benchmarks show a small but significant speed-up of \"make test\" if the\nlimit for the shifting of operands is made more strict. Many operands\nwill require a second iteration of the inner loop anyway, and shifting\nof the operands uses cycles for no gain, then.\n\nTo my surprise, BC_BASE_DIGS=4 and =9 seem to have near identical\nperformance after this change. Some operations (multiplication and\npower) ought to be faster with larger BcDigs, but at least for the\nperformance of \"make test\" on my system this does not seem to affect\nthe run-time by a significant amount.",
+ "messageHeadline": "Small performance optimization",
+ "oid": "d8526df195150f9f20ed4e8430dff3457e5d2913"
+ }
+ ],
+ "createdAt": "2019-06-07T08:28:48Z",
+ "deletions": 1,
+ "files": [
+ {
+ "path": "src/num.c",
+ "additions": 1,
+ "deletions": 1
+ }
+ ],
+ "fullDatabaseId": "286093193",
+ "headRefName": "master",
+ "headRefOid": "d8526df195150f9f20ed4e8430dff3457e5d2913",
+ "headRepository": {
+ "id": "MDEwOlJlcG9zaXRvcnkxODAzNzI3NzY=",
+ "name": "bc"
+ },
+ "headRepositoryOwner": {
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "name": "Stefan Eßer",
+ "login": "stesser"
+ },
+ "id": "MDExOlB1bGxSZXF1ZXN0Mjg2MDkzMTkz",
+ "isCrossRepository": true,
+ "isDraft": false,
+ "labels": [],
+ "latestReviews": [],
+ "maintainerCanModify": false,
+ "mergeCommit": {
+ "oid": "6f662190645f8fb84b36a513b1369a8bf61e215e"
+ },
+ "mergeStateStatus": "UNKNOWN",
+ "mergeable": "UNKNOWN",
+ "mergedAt": "2019-06-07T13:25:25Z",
+ "mergedBy": {
+ "id": "MDQ6VXNlcjMxNzI2ODc=",
+ "is_bot": false,
+ "login": "gavinhoward",
+ "name": "Gavin D. Howard"
+ },
+ "milestone": null,
+ "number": 23,
+ "potentialMergeCommit": null,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "reviewDecision": "",
+ "reviewRequests": [],
+ "reviews": [],
+ "state": "MERGED",
+ "statusCheckRollup": [],
+ "title": "Small performance optimization (perhaps for the next following release?)",
+ "updatedAt": "2019-06-07T13:25:28Z",
+ "url": "https://github.com/gavinhoward/bc/pull/23"
+ },
+ {
+ "additions": 38,
+ "assignees": [],
+ "author": {
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "is_bot": false,
+ "login": "stesser",
+ "name": "Stefan Eßer"
+ },
+ "autoMergeRequest": null,
+ "baseRefName": "master",
+ "baseRefOid": "7373e5f89c9ce873eb1a84f129c1f9be3b1ec400",
+ "body": "",
+ "changedFiles": 1,
+ "closed": true,
+ "closedAt": "2019-06-07T04:04:59Z",
+ "comments": [
+ {
+ "id": "MDEyOklzc3VlQ29tbWVudDQ5OTY2Nzc4Nw==",
+ "author": {
+ "login": "codecov"
+ },
+ "authorAssociation": "NONE",
+ "body": "# [Codecov](https://codecov.io/gh/gavinhoward/bc/pull/22?src=pr&el=h1) Report\n> Merging [#22](https://codecov.io/gh/gavinhoward/bc/pull/22?src=pr&el=desc) into [master](https://codecov.io/gh/gavinhoward/bc/commit/7373e5f89c9ce873eb1a84f129c1f9be3b1ec400?src=pr&el=desc) will **increase** coverage by `<.01%`.\n> The diff coverage is `100%`.\n\n[![Impacted file tree graph](https://codecov.io/gh/gavinhoward/bc/pull/22/graphs/tree.svg?width=650&token=7VDYTvCBQX&height=150&src=pr)](https://codecov.io/gh/gavinhoward/bc/pull/22?src=pr&el=tree)\n\n```diff\n@@ Coverage Diff @@\n## master #22 +/- ##\n==========================================\n+ Coverage 99.73% 99.73% +<.01% \n==========================================\n Files 16 16 \n Lines 3392 3409 +17 \n==========================================\n+ Hits 3383 3400 +17 \n Misses 9 9\n```\n\n\n| [Impacted Files](https://codecov.io/gh/gavinhoward/bc/pull/22?src=pr&el=tree) | Coverage Δ | |\n|---|---|---|\n| [src/num.c](https://codecov.io/gh/gavinhoward/bc/pull/22/diff?src=pr&el=tree#diff-c3JjL251bS5j) | `99.8% <100%> (ø)` | :arrow_up: |\n\n------\n\n[Continue to review full report at Codecov](https://codecov.io/gh/gavinhoward/bc/pull/22?src=pr&el=continue).\n> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)\n> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`\n> Powered by [Codecov](https://codecov.io/gh/gavinhoward/bc/pull/22?src=pr&el=footer). Last update [7373e5f...98719ee](https://codecov.io/gh/gavinhoward/bc/pull/22?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).\n",
+ "createdAt": "2019-06-06T21:06:55Z",
+ "includesCreatedEdit": true,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/22#issuecomment-499667787",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "MDEyOklzc3VlQ29tbWVudDQ5OTc0OTQ5MQ==",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Merging...for now. If it passes (which I think it will), it will stay.",
+ "createdAt": "2019-06-07T04:05:18Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/22#issuecomment-499749491",
+ "viewerDidAuthor": true
+ }
+ ],
+ "commits": [
+ {
+ "authoredDate": "2019-06-06T05:13:40Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-06-06T05:13:40Z",
+ "messageBody": "Since stdout is not affected by this debug print function, nchars\nshould not be modified, or printing of large numbers will be broken.",
+ "messageHeadline": "Resetting of nchars after printing to stderr corrupts the output",
+ "oid": "76794442357318437574ec8c0aa3f71903ec419a"
+ },
+ {
+ "authoredDate": "2019-06-06T06:14:43Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-06-06T06:14:43Z",
+ "messageBody": "If the top-most BcDig contains a small value and there are lower\nnon-zero BcDigs, the integer divisor is incremented to prevent\ndivision results that do not fit into a BcDig (are > BC_BASE_POW).\n\nE.g. for 1000 / 1.1 the integer divisor will become 2 and the first\nestimate of the result will be 1000 / 2 = 500, giving a reminder of\n1000 - 500 * 1.1 = 450. The next estimate will be 700 = 500 + 450 / 2\nand so on with the reminder slowly decreasing in each iteration.\n\nSince the relative error due to the approximation of the full division\nby an integer division becomes significant for small integer divisors,\nit makes sense to extend the divisor if it i ssmall. In fact, it seems\nto pay off for all divisors that are < BC_BASE_POW / 10.",
+ "messageHeadline": "Fix pathological behavior in case integer is rounded up a lot",
+ "oid": "035ace7d24afd722ce9a3325c90213d36772e5bd"
+ },
+ {
+ "authoredDate": "2019-06-06T06:25:29Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-06-06T06:25:29Z",
+ "messageBody": "",
+ "messageHeadline": "Merge branch 'div'",
+ "oid": "c1e655b2dd38ea27668b3a0c5b7c86ef06a8dd88"
+ },
+ {
+ "authoredDate": "2019-06-06T08:21:51Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-06-06T08:21:51Z",
+ "messageBody": "If there was a new-line character in the string, then strrchr will\nreturn a pointer to that character. If the length of the string\nstarting at the new-line position is 1, then no text follows on the\nnew line and nchars should be set to 0,not 2.",
+ "messageHeadline": "Fix calculation of nchars",
+ "oid": "54e3cd86494fd1e2c817ea3fa49c357738da5f82"
+ },
+ {
+ "authoredDate": "2019-06-06T11:32:50Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-06-06T11:32:50Z",
+ "messageBody": "This reverts commit 54e3cd86494fd1e2c817ea3fa49c357738da5f82.\n\nI had misread the code (thought the + 1 was outside the brackets).",
+ "messageHeadline": "Revert \"Fix calculation of nchars\"",
+ "oid": "210d9c5863215d0c371f0ec68a0fda2f601920f3"
+ },
+ {
+ "authoredDate": "2019-06-06T20:07:20Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-06-06T20:07:20Z",
+ "messageBody": "This version does not pass all tests (hangs in divide.bc).\nI'm commiting anyway, to have a basis that is ismilar to the upstream\nrepo for further testing.",
+ "messageHeadline": "Merge latest upstream version.",
+ "oid": "94fb480d06c4e082a635d2f4866682d4f6a7b6bb"
+ },
+ {
+ "authoredDate": "2019-06-06T20:50:44Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-06-06T20:50:44Z",
+ "messageBody": "",
+ "messageHeadline": "Remove debug dump of operands and intermediate results",
+ "oid": "59e2953c61615ff5f3deecd181f15c5249fe0bb4"
+ },
+ {
+ "authoredDate": "2019-06-06T20:59:42Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-06-06T20:59:42Z",
+ "messageBody": "",
+ "messageHeadline": "Merge branch 'master' of https://github.com/gavinhoward/bc",
+ "oid": "6fe9b2fba0e7359152598d4e38ff93020b24b8e7"
+ },
+ {
+ "authoredDate": "2019-06-06T21:01:19Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-06-06T21:01:19Z",
+ "messageBody": "",
+ "messageHeadline": "Reduce whitespace diff relative to upstream",
+ "oid": "595d84e4792bffa63ece31aa146ea7ccfc87ea4b"
+ },
+ {
+ "authoredDate": "2019-06-06T21:03:03Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-06-06T21:03:03Z",
+ "messageBody": "",
+ "messageHeadline": "Fix lines that resulted from a mis-merge of upstream",
+ "oid": "98719eecc5f599365f5d2c3aa042a9cb7cdb6d6c"
+ }
+ ],
+ "createdAt": "2019-06-06T20:53:49Z",
+ "deletions": 6,
+ "files": [
+ {
+ "path": "src/num.c",
+ "additions": 38,
+ "deletions": 6
+ }
+ ],
+ "fullDatabaseId": "285965666",
+ "headRefName": "master",
+ "headRefOid": "98719eecc5f599365f5d2c3aa042a9cb7cdb6d6c",
+ "headRepository": {
+ "id": "MDEwOlJlcG9zaXRvcnkxODAzNzI3NzY=",
+ "name": "bc"
+ },
+ "headRepositoryOwner": {
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "name": "Stefan Eßer",
+ "login": "stesser"
+ },
+ "id": "MDExOlB1bGxSZXF1ZXN0Mjg1OTY1NjY2",
+ "isCrossRepository": true,
+ "isDraft": false,
+ "labels": [],
+ "latestReviews": [],
+ "maintainerCanModify": false,
+ "mergeCommit": {
+ "oid": "016a5eed2e5cc0d7e65cea785e609a0bb8a0fa70"
+ },
+ "mergeStateStatus": "UNKNOWN",
+ "mergeable": "UNKNOWN",
+ "mergedAt": "2019-06-07T04:04:59Z",
+ "mergedBy": {
+ "id": "MDQ6VXNlcjMxNzI2ODc=",
+ "is_bot": false,
+ "login": "gavinhoward",
+ "name": "Gavin D. Howard"
+ },
+ "milestone": null,
+ "number": 22,
+ "potentialMergeCommit": null,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "reviewDecision": "",
+ "reviewRequests": [],
+ "reviews": [],
+ "state": "MERGED",
+ "statusCheckRollup": [],
+ "title": "This version completes all tests on my system",
+ "updatedAt": "2019-06-07T04:05:18Z",
+ "url": "https://github.com/gavinhoward/bc/pull/22"
+ },
+ {
+ "additions": 30,
+ "assignees": [],
+ "author": {
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "is_bot": false,
+ "login": "stesser",
+ "name": "Stefan Eßer"
+ },
+ "autoMergeRequest": null,
+ "baseRefName": "master",
+ "baseRefOid": "81eb6fba565695dfe054e0b3f651fa5fa089b605",
+ "body": "Reduce the number of iterations required for cases where the top-most BcDig of the divisor is a small value and there are lower non-zero BcDigs. This makes the performance of the division more even for all types of operands of a given length and speeds up \"make test\" by about 3% on my system. The worst case performance of the division is improved by about a factor of 20 (estimated, not measured) for operations like 1/1.000000001.",
+ "changedFiles": 1,
+ "closed": true,
+ "closedAt": "2019-06-06T15:34:13Z",
+ "comments": [
+ {
+ "id": "MDEyOklzc3VlQ29tbWVudDQ5OTM5OTU1Nw==",
+ "author": {
+ "login": "stesser"
+ },
+ "authorAssociation": "CONTRIBUTOR",
+ "body": "While here, fix calculation of vm->nchars in 2 places ...",
+ "createdAt": "2019-06-06T08:27:09Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/21#issuecomment-499399557",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "MDEyOklzc3VlQ29tbWVudDQ5OTU0NzEzMA==",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Rejected for reasons stated in an email.",
+ "createdAt": "2019-06-06T15:34:13Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/21#issuecomment-499547130",
+ "viewerDidAuthor": true
+ }
+ ],
+ "commits": [
+ {
+ "authoredDate": "2019-06-06T05:13:40Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-06-06T05:13:40Z",
+ "messageBody": "Since stdout is not affected by this debug print function, nchars\nshould not be modified, or printing of large numbers will be broken.",
+ "messageHeadline": "Resetting of nchars after printing to stderr corrupts the output",
+ "oid": "76794442357318437574ec8c0aa3f71903ec419a"
+ },
+ {
+ "authoredDate": "2019-06-06T06:14:43Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-06-06T06:14:43Z",
+ "messageBody": "If the top-most BcDig contains a small value and there are lower\nnon-zero BcDigs, the integer divisor is incremented to prevent\ndivision results that do not fit into a BcDig (are > BC_BASE_POW).\n\nE.g. for 1000 / 1.1 the integer divisor will become 2 and the first\nestimate of the result will be 1000 / 2 = 500, giving a reminder of\n1000 - 500 * 1.1 = 450. The next estimate will be 700 = 500 + 450 / 2\nand so on with the reminder slowly decreasing in each iteration.\n\nSince the relative error due to the approximation of the full division\nby an integer division becomes significant for small integer divisors,\nit makes sense to extend the divisor if it i ssmall. In fact, it seems\nto pay off for all divisors that are < BC_BASE_POW / 10.",
+ "messageHeadline": "Fix pathological behavior in case integer is rounded up a lot",
+ "oid": "035ace7d24afd722ce9a3325c90213d36772e5bd"
+ },
+ {
+ "authoredDate": "2019-06-06T06:25:29Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-06-06T06:25:29Z",
+ "messageBody": "",
+ "messageHeadline": "Merge branch 'div'",
+ "oid": "c1e655b2dd38ea27668b3a0c5b7c86ef06a8dd88"
+ },
+ {
+ "authoredDate": "2019-06-06T08:21:51Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-06-06T08:21:51Z",
+ "messageBody": "If there was a new-line character in the string, then strrchr will\nreturn a pointer to that character. If the length of the string\nstarting at the new-line position is 1, then no text follows on the\nnew line and nchars should be set to 0,not 2.",
+ "messageHeadline": "Fix calculation of nchars",
+ "oid": "54e3cd86494fd1e2c817ea3fa49c357738da5f82"
+ },
+ {
+ "authoredDate": "2019-06-06T11:32:50Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-06-06T11:32:50Z",
+ "messageBody": "This reverts commit 54e3cd86494fd1e2c817ea3fa49c357738da5f82.\n\nI had misread the code (thought the + 1 was outside the brackets).",
+ "messageHeadline": "Revert \"Fix calculation of nchars\"",
+ "oid": "210d9c5863215d0c371f0ec68a0fda2f601920f3"
+ }
+ ],
+ "createdAt": "2019-06-06T06:53:09Z",
+ "deletions": 8,
+ "files": [
+ {
+ "path": "src/num.c",
+ "additions": 30,
+ "deletions": 8
+ }
+ ],
+ "fullDatabaseId": "285665970",
+ "headRefName": "master",
+ "headRefOid": "210d9c5863215d0c371f0ec68a0fda2f601920f3",
+ "headRepository": {
+ "id": "MDEwOlJlcG9zaXRvcnkxODAzNzI3NzY=",
+ "name": "bc"
+ },
+ "headRepositoryOwner": {
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "name": "Stefan Eßer",
+ "login": "stesser"
+ },
+ "id": "MDExOlB1bGxSZXF1ZXN0Mjg1NjY1OTcw",
+ "isCrossRepository": true,
+ "isDraft": false,
+ "labels": [],
+ "latestReviews": [],
+ "maintainerCanModify": false,
+ "mergeCommit": null,
+ "mergeStateStatus": "DIRTY",
+ "mergeable": "CONFLICTING",
+ "mergedAt": null,
+ "mergedBy": null,
+ "milestone": null,
+ "number": 21,
+ "potentialMergeCommit": null,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "reviewDecision": "",
+ "reviewRequests": [],
+ "reviews": [],
+ "state": "CLOSED",
+ "statusCheckRollup": [],
+ "title": "Improve worst case behavior of the division",
+ "updatedAt": "2019-06-06T15:34:14Z",
+ "url": "https://github.com/gavinhoward/bc/pull/21"
+ },
+ {
+ "additions": 91,
+ "assignees": [],
+ "author": {
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "is_bot": false,
+ "login": "stesser",
+ "name": "Stefan Eßer"
+ },
+ "autoMergeRequest": null,
+ "baseRefName": "master",
+ "baseRefOid": "7be2294e3a91ae4f93bf4db1888d89a6c0d4537b",
+ "body": "I had not fully understood the current division algorithm when I suggested to \"overshoot\" subtractions and to attempt convergence from both sides.\r\n\r\nAfter analysis of the current implementation I have noticed that it can be significantly simplified and at the same time sped-up (by a factor of about 2 in my tests). The run-time of \"make test\" is reduced by some 15% on my system.",
+ "changedFiles": 1,
+ "closed": true,
+ "closedAt": "2019-06-06T00:26:39Z",
+ "comments": [
+ {
+ "id": "MDEyOklzc3VlQ29tbWVudDQ5OTIyMjQ0Mw==",
+ "author": {
+ "login": "codecov"
+ },
+ "authorAssociation": "NONE",
+ "body": "# [Codecov](https://codecov.io/gh/gavinhoward/bc/pull/20?src=pr&el=h1) Report\n> Merging [#20](https://codecov.io/gh/gavinhoward/bc/pull/20?src=pr&el=desc) into [master](https://codecov.io/gh/gavinhoward/bc/commit/7be2294e3a91ae4f93bf4db1888d89a6c0d4537b?src=pr&el=desc) will **not change** coverage.\n> The diff coverage is `100%`.\n\n[![Impacted file tree graph](https://codecov.io/gh/gavinhoward/bc/pull/20/graphs/tree.svg?width=650&token=7VDYTvCBQX&height=150&src=pr)](https://codecov.io/gh/gavinhoward/bc/pull/20?src=pr&el=tree)\n\n```diff\n@@ Coverage Diff @@\n## master #20 +/- ##\n=======================================\n Coverage 99.73% 99.73% \n=======================================\n Files 16 16 \n Lines 3405 3405 \n=======================================\n Hits 3396 3396 \n Misses 9 9\n```\n\n\n| [Impacted Files](https://codecov.io/gh/gavinhoward/bc/pull/20?src=pr&el=tree) | Coverage Δ | |\n|---|---|---|\n| [src/num.c](https://codecov.io/gh/gavinhoward/bc/pull/20/diff?src=pr&el=tree#diff-c3JjL251bS5j) | `99.8% <100%> (ø)` | :arrow_up: |\n\n------\n\n[Continue to review full report at Codecov](https://codecov.io/gh/gavinhoward/bc/pull/20?src=pr&el=continue).\n> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)\n> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`\n> Powered by [Codecov](https://codecov.io/gh/gavinhoward/bc/pull/20?src=pr&el=footer). Last update [7be2294...45edd42](https://codecov.io/gh/gavinhoward/bc/pull/20?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).\n",
+ "createdAt": "2019-06-05T19:28:56Z",
+ "includesCreatedEdit": true,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/20#issuecomment-499222443",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "MDEyOklzc3VlQ29tbWVudDQ5OTIzNzA4NA==",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Can you send me your tests?",
+ "createdAt": "2019-06-05T20:12:45Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/20#issuecomment-499237084",
+ "viewerDidAuthor": true
+ }
+ ],
+ "commits": [
+ {
+ "authoredDate": "2019-05-20T11:00:48Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-05-20T11:00:48Z",
+ "messageBody": "…mod per BcDig\n\nIn the last iteration of the per BcDig print loop, acc is known to be less than\npow, therefore the last div and mod operations can be skipped, resulting in a\nspeed-up of about 2% in my tests.",
+ "messageHeadline": "Slightly speed up printing for the obase!=10 case by skipping one div…",
+ "oid": "b2c2fe4f75bff27e183db6e8b4211e8f602b0e68"
+ },
+ {
+ "authoredDate": "2019-06-05T10:06:38Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-06-05T10:06:38Z",
+ "messageBody": "",
+ "messageHeadline": "Merge branch 'master' of github.com:stesser/bc",
+ "oid": "caf977797a95fecc39e78b35ef964e69d7851e8a"
+ },
+ {
+ "authoredDate": "2019-06-05T19:04:42Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-06-05T19:04:42Z",
+ "messageBody": "The run-time of \"make test\" is reduced by some 15% on my system.",
+ "messageHeadline": "Implement faster and simpler division algorithm",
+ "oid": "45edd42ef28c52287e664e2d00b5b7de31a30e22"
+ }
+ ],
+ "createdAt": "2019-06-05T19:25:44Z",
+ "deletions": 99,
+ "files": [
+ {
+ "path": "src/num.c",
+ "additions": 91,
+ "deletions": 99
+ }
+ ],
+ "fullDatabaseId": "285517299",
+ "headRefName": "master",
+ "headRefOid": "45edd42ef28c52287e664e2d00b5b7de31a30e22",
+ "headRepository": {
+ "id": "MDEwOlJlcG9zaXRvcnkxODAzNzI3NzY=",
+ "name": "bc"
+ },
+ "headRepositoryOwner": {
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "name": "Stefan Eßer",
+ "login": "stesser"
+ },
+ "id": "MDExOlB1bGxSZXF1ZXN0Mjg1NTE3Mjk5",
+ "isCrossRepository": true,
+ "isDraft": false,
+ "labels": [],
+ "latestReviews": [],
+ "maintainerCanModify": false,
+ "mergeCommit": {
+ "oid": "8904602cd9be7d6433f9ed6c2caab0f9c745dd9c"
+ },
+ "mergeStateStatus": "UNKNOWN",
+ "mergeable": "UNKNOWN",
+ "mergedAt": "2019-06-06T00:26:39Z",
+ "mergedBy": {
+ "id": "MDQ6VXNlcjMxNzI2ODc=",
+ "is_bot": false,
+ "login": "gavinhoward",
+ "name": "Gavin D. Howard"
+ },
+ "milestone": null,
+ "number": 20,
+ "potentialMergeCommit": null,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "reviewDecision": "",
+ "reviewRequests": [],
+ "reviews": [],
+ "state": "MERGED",
+ "statusCheckRollup": [],
+ "title": "Speed up and simplify division",
+ "updatedAt": "2019-06-06T00:26:40Z",
+ "url": "https://github.com/gavinhoward/bc/pull/20"
+ },
+ {
+ "additions": 10,
+ "assignees": [],
+ "author": {
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "is_bot": false,
+ "login": "stesser",
+ "name": "Stefan Eßer"
+ },
+ "autoMergeRequest": null,
+ "baseRefName": "master",
+ "baseRefOid": "e318529d852cee9ce40c06f1a91f0063d8e0bd62",
+ "body": "In the last iteration of the per BcDig print loop, \"acc\" is known to be less than \"pow\", therefore the last div and mod operations can be skipped, resulting in a speed-up of about 2% in my tests.",
+ "changedFiles": 1,
+ "closed": true,
+ "closedAt": "2019-05-20T14:08:11Z",
+ "comments": [
+ {
+ "id": "MDEyOklzc3VlQ29tbWVudDQ5Mzk1ODQwMw==",
+ "author": {
+ "login": "codecov"
+ },
+ "authorAssociation": "NONE",
+ "body": "# [Codecov](https://codecov.io/gh/gavinhoward/bc/pull/19?src=pr&el=h1) Report\n> Merging [#19](https://codecov.io/gh/gavinhoward/bc/pull/19?src=pr&el=desc) into [master](https://codecov.io/gh/gavinhoward/bc/commit/e318529d852cee9ce40c06f1a91f0063d8e0bd62?src=pr&el=desc) will **increase** coverage by `<.01%`.\n> The diff coverage is `100%`.\n\n[![Impacted file tree graph](https://codecov.io/gh/gavinhoward/bc/pull/19/graphs/tree.svg?width=650&token=7VDYTvCBQX&height=150&src=pr)](https://codecov.io/gh/gavinhoward/bc/pull/19?src=pr&el=tree)\n\n```diff\n@@ Coverage Diff @@\n## master #19 +/- ##\n==========================================\n+ Coverage 99.88% 99.88% +<.01% \n==========================================\n Files 16 16 \n Lines 3409 3411 +2 \n==========================================\n+ Hits 3405 3407 +2 \n Misses 4 4\n```\n\n\n| [Impacted Files](https://codecov.io/gh/gavinhoward/bc/pull/19?src=pr&el=tree) | Coverage Δ | |\n|---|---|---|\n| [src/num.c](https://codecov.io/gh/gavinhoward/bc/pull/19/diff?src=pr&el=tree#diff-c3JjL251bS5j) | `99.8% <100%> (ø)` | :arrow_up: |\n\n------\n\n[Continue to review full report at Codecov](https://codecov.io/gh/gavinhoward/bc/pull/19?src=pr&el=continue).\n> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)\n> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`\n> Powered by [Codecov](https://codecov.io/gh/gavinhoward/bc/pull/19?src=pr&el=footer). Last update [e318529...b2c2fe4](https://codecov.io/gh/gavinhoward/bc/pull/19?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).\n",
+ "createdAt": "2019-05-20T12:05:10Z",
+ "includesCreatedEdit": true,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/19#issuecomment-493958403",
+ "viewerDidAuthor": false
+ },
+ {
+ "id": "MDEyOklzc3VlQ29tbWVudDQ5NDAwMjk1MQ==",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Oh whoops. I did not notice this PR; I merged in the patch instead, though I did list you as the author of the commit. I hope that is okay.\r\n\r\nClosing since the code is already merged.",
+ "createdAt": "2019-05-20T14:08:11Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/19#issuecomment-494002951",
+ "viewerDidAuthor": true
+ }
+ ],
+ "commits": [
+ {
+ "authoredDate": "2019-05-20T11:00:48Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-05-20T11:00:48Z",
+ "messageBody": "…mod per BcDig\n\nIn the last iteration of the per BcDig print loop, acc is known to be less than\npow, therefore the last div and mod operations can be skipped, resulting in a\nspeed-up of about 2% in my tests.",
+ "messageHeadline": "Slightly speed up printing for the obase!=10 case by skipping one div…",
+ "oid": "b2c2fe4f75bff27e183db6e8b4211e8f602b0e68"
+ }
+ ],
+ "createdAt": "2019-05-20T12:00:46Z",
+ "deletions": 4,
+ "files": [
+ {
+ "path": "src/num.c",
+ "additions": 10,
+ "deletions": 4
+ }
+ ],
+ "fullDatabaseId": "280360533",
+ "headRefName": "d9",
+ "headRefOid": "b2c2fe4f75bff27e183db6e8b4211e8f602b0e68",
+ "headRepository": {
+ "id": "MDEwOlJlcG9zaXRvcnkxODAzNzI3NzY=",
+ "name": "bc"
+ },
+ "headRepositoryOwner": {
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "name": "Stefan Eßer",
+ "login": "stesser"
+ },
+ "id": "MDExOlB1bGxSZXF1ZXN0MjgwMzYwNTMz",
+ "isCrossRepository": true,
+ "isDraft": false,
+ "labels": [],
+ "latestReviews": [],
+ "maintainerCanModify": false,
+ "mergeCommit": null,
+ "mergeStateStatus": "DIRTY",
+ "mergeable": "CONFLICTING",
+ "mergedAt": null,
+ "mergedBy": null,
+ "milestone": null,
+ "number": 19,
+ "potentialMergeCommit": null,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "reviewDecision": "",
+ "reviewRequests": [],
+ "reviews": [],
+ "state": "CLOSED",
+ "statusCheckRollup": [],
+ "title": "Slightly speed up printing for the obase!=10 case",
+ "updatedAt": "2019-06-02T10:54:35Z",
+ "url": "https://github.com/gavinhoward/bc/pull/19"
+ },
+ {
+ "additions": 11813,
+ "assignees": [],
+ "author": {
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "is_bot": false,
+ "login": "stesser",
+ "name": "Stefan Eßer"
+ },
+ "autoMergeRequest": null,
+ "baseRefName": "master",
+ "baseRefOid": "b3ba59502ee5cc81e2019a724b94b648a35d7897",
+ "body": "All division and modulo test cases pass with this version.\r\n\r\nMaybe the scale could be reduced (for better performance) in parts of the algorithm, but for now the functionality is there. I have optimized the pow10 function by use of a look-up table. This table will be cached and the indexing faster than repetitive multiplication, IMHO. (Should be tested, but will not be significant in the overall picture, I guess.)\r\n\r\nI have introduced a function that rounds to a given precision. This was necessary to get the division match previous results to the last relevant digit. Just cutting off excess digits gives small errors in division and modulo results.\r\n\r\nAnother new function normalizes its argument to make len == rdx (i.e. to give a value strictly less than 1 but without 0 in the uppermost BcDig of num[]). This was required for the division, which operates on \"normalized\" values and adjusts the position of the decimal point as a final step.\r\n\r\nNeither of these functions has been added to num.h, since I was not sure about your policy with regard to having all static functions in num.c declared therein).",
+ "changedFiles": 17,
+ "closed": true,
+ "closedAt": "2019-05-06T13:42:00Z",
+ "comments": [
+ {
+ "id": "MDEyOklzc3VlQ29tbWVudDQ4NzU3MzUyNA==",
+ "author": {
+ "login": "stesser"
+ },
+ "authorAssociation": "CONTRIBUTOR",
+ "body": "The power has been fixed and passes all tests.\r\nThe sqrt operator gives results that do not pass the tests but differ only in the number of decimals or the precision of the calculation.",
+ "createdAt": "2019-04-29T13:11:00Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/18#issuecomment-487573524",
+ "viewerDidAuthor": false
+ }
+ ],
+ "commits": [
+ {
+ "authoredDate": "2019-04-19T08:06:46Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-19T08:06:46Z",
+ "messageBody": "…ot found.",
+ "messageHeadline": "The test should use the same file name as is created if the file is n…",
+ "oid": "d9bda13740c6d4f67c1d69955a4ea8e3477bad4f"
+ },
+ {
+ "authoredDate": "2019-04-19T14:09:03Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-19T14:09:03Z",
+ "messageBody": "The output has been verified to be identical to that of the C language\nversion for all files in this project by comparing the resulting .o files.",
+ "messageHeadline": "Provide shell version of strgen.c to allow use without compilation.",
+ "oid": "7d9fb3e6ffb7008afec4c7168498751366213b0f"
+ },
+ {
+ "authoredDate": "2019-04-19T14:11:23Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-19T14:11:23Z",
+ "messageBody": "",
+ "messageHeadline": "Merge branch 'master' of https://github.com/gavinhoward/bc",
+ "oid": "fddcd0f53021d8f3164f5618ba952ab36932d8fc"
+ },
+ {
+ "authoredDate": "2019-04-19T18:39:00Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-19T18:39:00Z",
+ "messageBody": "…nature.",
+ "messageHeadline": "Change parameter from char* to char** as required by the function sig…",
+ "oid": "a010b3e82408024f12e34030591349c4a344465b"
+ },
+ {
+ "authoredDate": "2019-04-19T18:41:07Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-19T18:41:07Z",
+ "messageBody": "",
+ "messageHeadline": "Fix test to work if empty or null string is passed.",
+ "oid": "22fd1e9277a34ead069e9dd64942ed3cd55c2ac6"
+ },
+ {
+ "authoredDate": "2019-04-20T08:34:13Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-20T08:34:13Z",
+ "messageBody": "",
+ "messageHeadline": "Merge branch 'master' of https://github.com/gavinhoward/bc",
+ "oid": "461c523fcbb8ff4c758ddb6ba2d9cfcf71a0a412"
+ },
+ {
+ "authoredDate": "2019-04-20T08:35:20Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-20T08:35:20Z",
+ "messageBody": "…e was broken,",
+ "messageHeadline": "Change reference to v.v to use bc_vec_item(&v, 0) - my previous chang…",
+ "oid": "52b52a81ea9ea993d9835900398ee48d6e1dc1ad"
+ },
+ {
+ "authoredDate": "2019-04-20T08:37:07Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-20T08:37:07Z",
+ "messageBody": "…nset",
+ "messageHeadline": "Split test into two parts to prevent parse error if the variable is u…",
+ "oid": "04ddff0bd1de2bde9c1af167cd59488a8fa3800c"
+ },
+ {
+ "authoredDate": "2019-04-20T15:11:30Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-20T15:11:30Z",
+ "messageBody": "",
+ "messageHeadline": "Merge branch 'master' of https://github.com/gavinhoward/bc",
+ "oid": "eb62a6521bcdaea48a8a69a346facf14a46604be"
+ },
+ {
+ "authoredDate": "2019-04-20T16:14:00Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-20T16:14:00Z",
+ "messageBody": "",
+ "messageHeadline": "Add information about recently added message catalogs.",
+ "oid": "b2950329c49de32e8dccf39ad3a64c3d210c9a46"
+ },
+ {
+ "authoredDate": "2019-04-20T16:16:06Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-20T16:16:06Z",
+ "messageBody": "",
+ "messageHeadline": "Add quotes around fr_CA and fr_CH.",
+ "oid": "eaf8bc5fa067eb2c55c96d14ad21ba952e83f6c5"
+ },
+ {
+ "authoredDate": "2019-04-20T16:17:04Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-20T16:17:04Z",
+ "messageBody": "",
+ "messageHeadline": "Fix quotes around fr_CA and fr_CH.",
+ "oid": "7c87d5892205451fcabb38edf42ca4c948427dd7"
+ },
+ {
+ "authoredDate": "2019-04-20T16:18:07Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-20T16:18:07Z",
+ "messageBody": "Anyway, I'm no native speaker of English and therefore the change is only\na crude example for what might be added ... ;-)",
+ "messageHeadline": "I think \"for\" is better than \"in\", here.",
+ "oid": "ecd6481f5aeabf178cc0fc7614840f08d74473b0"
+ },
+ {
+ "authoredDate": "2019-04-24T09:16:49Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-24T09:16:49Z",
+ "messageBody": "",
+ "messageHeadline": "Merge branch 'master' of https://github.com/gavinhoward/bc",
+ "oid": "004ad884ed95213a1a14cc7a5b05353ff91fcceb"
+ },
+ {
+ "authoredDate": "2019-04-24T21:28:32Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-24T21:28:32Z",
+ "messageBody": "…esent numbers\n\nThis code has been tested with add/sub, multiplicaton and power of integer values.\nNo tests have been performed for division and square root, yet.\n\nNumbers with fractional parts are not supported due to the missing scaling of the\n\"scale\" parameter.",
+ "messageHeadline": "Initial support for 32 bit integers instead of decimal digits to repr…",
+ "oid": "a299ffc466db2360d0cd21f8e872a4b9ccac8f0b"
+ },
+ {
+ "authoredDate": "2019-04-24T23:23:08Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Eßer"
+ }
+ ],
+ "committedDate": "2019-04-24T23:23:08Z",
+ "messageBody": "This patch starts a branch exploring the possibility of using a more\npacked representation of digits in BcNum's. I am not sure this will work\nout, but it can't hurt to try, especially since Stefan was so kind to\ncome up with a patch and send it.\n\nAs of right now, the things that are known to be broken:\n\n* Printing decimal numbers\n* Power (seg fault)",
+ "messageHeadline": "Commit an adjusted patch sent to me by Stefan Eßer",
+ "oid": "4b111c28c02ac4c999ad62de5a448eb21e01489b"
+ },
+ {
+ "authoredDate": "2019-04-24T23:31:23Z",
+ "authors": [
+ {
+ "email": "yzena.tech@gmail.com",
+ "id": "",
+ "login": "",
+ "name": "Gavin Howard"
+ }
+ ],
+ "committedDate": "2019-04-24T23:31:23Z",
+ "messageBody": "",
+ "messageHeadline": "Attempt to make Stefan's work more portable",
+ "oid": "0700efc9bb2aad8921d035f18bb30e4e211e0115"
+ },
+ {
+ "authoredDate": "2019-04-24T23:33:48Z",
+ "authors": [
+ {
+ "email": "yzena.tech@gmail.com",
+ "id": "",
+ "login": "",
+ "name": "Gavin Howard"
+ }
+ ],
+ "committedDate": "2019-04-24T23:33:48Z",
+ "messageBody": "",
+ "messageHeadline": "Fix a bit of code that is not portable to 8-bit arches",
+ "oid": "d7e4d6e8e6e0c46c3ab378c8066ceedf02fe2784"
+ },
+ {
+ "authoredDate": "2019-04-25T04:33:10Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-25T04:33:10Z",
+ "messageBody": "… of BcDig",
+ "messageHeadline": "Introduce 3 function to perform memcpy, memmove, and memset on arrays…",
+ "oid": "394a516fb6d95a9314783e4b920f9078b8d6217b"
+ },
+ {
+ "authoredDate": "2019-04-25T05:05:24Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-25T05:05:24Z",
+ "messageBody": "",
+ "messageHeadline": "Modify malloc and realloc functions to operate on BcDig arrays",
+ "oid": "5fd43a372b887cae774309dae0482410eaa64344"
+ },
+ {
+ "authoredDate": "2019-04-25T05:14:54Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-25T05:14:54Z",
+ "messageBody": "…ctions for use in vector.c",
+ "messageHeadline": "Add BcDig alloc functions to header and re-enable the plain alloc fun…",
+ "oid": "ca5a74eedef9edea724f90c386cf13322a972511"
+ },
+ {
+ "authoredDate": "2019-04-25T05:42:03Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-25T05:42:03Z",
+ "messageBody": "",
+ "messageHeadline": "Merge branch 'base9' of https://github.com/gavinhoward/bc into d9",
+ "oid": "14eff4647371265957e125e44ca8666cc845b54f"
+ },
+ {
+ "authoredDate": "2019-04-25T05:56:10Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-25T05:56:10Z",
+ "messageBody": "",
+ "messageHeadline": "Use bc_num_set instead of memset in merged code",
+ "oid": "887d6b9b2fb148490d373f60cc13f42fb3a47102"
+ },
+ {
+ "authoredDate": "2019-04-25T06:01:03Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-25T06:01:03Z",
+ "messageBody": "",
+ "messageHeadline": "Allow passing of -DBC_DEBUG_CODE on the command line",
+ "oid": "010dd2a01af69462ee1c94053e9f82cde65f171d"
+ },
+ {
+ "authoredDate": "2019-04-25T06:09:13Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-25T06:09:13Z",
+ "messageBody": "",
+ "messageHeadline": "Remove duplicate definitioon of DUMP_NUM",
+ "oid": "a3430556302146f917be5266b7c6021ecd2e1133"
+ },
+ {
+ "authoredDate": "2019-04-25T09:25:17Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-25T09:25:17Z",
+ "messageBody": "",
+ "messageHeadline": "Remove obsolete local variables",
+ "oid": "bf3b1b206e6e6a88c7ffc6ca7e10388f3fb1eb4a"
+ },
+ {
+ "authoredDate": "2019-04-25T09:50:39Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-25T09:50:39Z",
+ "messageBody": "",
+ "messageHeadline": "Fix printing of the decimal point (occured multiple times)",
+ "oid": "610a8ee9e89626f9789ba63a3e5692adc19a46ef"
+ },
+ {
+ "authoredDate": "2019-04-25T10:25:15Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-25T10:25:15Z",
+ "messageBody": "",
+ "messageHeadline": "Suppress printing of trailing zeroes of fractional parts of a result",
+ "oid": "0da1775703abdbd337f024a4b46208976b08508a"
+ },
+ {
+ "authoredDate": "2019-04-25T17:33:54Z",
+ "authors": [
+ {
+ "email": "yzena.tech@gmail.com",
+ "id": "",
+ "login": "",
+ "name": "Gavin Howard"
+ }
+ ],
+ "committedDate": "2019-04-25T17:33:54Z",
+ "messageBody": "",
+ "messageHeadline": "Fix a problem I introduced",
+ "oid": "b53a994e74fd2f226bac7756063b557dfd03f172"
+ },
+ {
+ "authoredDate": "2019-04-25T21:18:49Z",
+ "authors": [
+ {
+ "email": "yzena.tech@gmail.com",
+ "id": "",
+ "login": "",
+ "name": "Gavin D. Howard"
+ }
+ ],
+ "committedDate": "2019-04-25T21:18:49Z",
+ "messageBody": "A number of fixes and improvements ...",
+ "messageHeadline": "Merge pull request #15 from stesser/d9",
+ "oid": "7daf7e6cf26a6b3b11eeddd9d6bcc38bd52da3dc"
+ },
+ {
+ "authoredDate": "2019-04-26T01:35:41Z",
+ "authors": [
+ {
+ "email": "yzena.tech@gmail.com",
+ "id": "",
+ "login": "",
+ "name": "Gavin Howard"
+ }
+ ],
+ "committedDate": "2019-04-26T01:35:41Z",
+ "messageBody": "",
+ "messageHeadline": "Fix some style",
+ "oid": "af107d94c9352b37d4bf31815663fd68effa55b6"
+ },
+ {
+ "authoredDate": "2019-04-26T01:43:49Z",
+ "authors": [
+ {
+ "email": "yzena.tech@gmail.com",
+ "id": "",
+ "login": "",
+ "name": "Gavin Howard"
+ }
+ ],
+ "committedDate": "2019-04-26T01:43:49Z",
+ "messageBody": "",
+ "messageHeadline": "Fix some more style",
+ "oid": "f49fdaa396f2cda25379b056cf7ca9fffb0a68d6"
+ },
+ {
+ "authoredDate": "2019-04-26T02:12:19Z",
+ "authors": [
+ {
+ "email": "yzena.tech@gmail.com",
+ "id": "",
+ "login": "",
+ "name": "Gavin Howard"
+ }
+ ],
+ "committedDate": "2019-04-26T02:12:19Z",
+ "messageBody": "",
+ "messageHeadline": "Make some changes and mark functions that need work",
+ "oid": "fcf290110df0d7b09b5f89798888f05b8b371484"
+ },
+ {
+ "authoredDate": "2019-04-26T03:13:54Z",
+ "authors": [
+ {
+ "email": "yzena.tech@gmail.com",
+ "id": "",
+ "login": "",
+ "name": "Gavin Howard"
+ }
+ ],
+ "committedDate": "2019-04-26T03:13:54Z",
+ "messageBody": "There is still one bug: it allows in an extra 0 in the most significant\nspot if there is one, but it does produce the right numbers.",
+ "messageHeadline": "Make decimal parsing work",
+ "oid": "d43ec37d48c69660ba7098c88a8ef3002a49636d"
+ },
+ {
+ "authoredDate": "2019-04-26T14:48:19Z",
+ "authors": [
+ {
+ "email": "yzena.tech@gmail.com",
+ "id": "",
+ "login": "",
+ "name": "Gavin Howard"
+ }
+ ],
+ "committedDate": "2019-04-26T14:48:19Z",
+ "messageBody": "",
+ "messageHeadline": "Fix the definitions for BcDig",
+ "oid": "2eaeebffdce7fcd52b3a62d1537002b3580b6e28"
+ },
+ {
+ "authoredDate": "2019-04-26T14:48:44Z",
+ "authors": [
+ {
+ "email": "yzena.tech@gmail.com",
+ "id": "",
+ "login": "",
+ "name": "Gavin Howard"
+ }
+ ],
+ "committedDate": "2019-04-26T14:48:44Z",
+ "messageBody": "",
+ "messageHeadline": "Add more decimal tests",
+ "oid": "a0e90f3e79ef5b619b801753877d94992f6bdc95"
+ },
+ {
+ "authoredDate": "2019-04-26T14:49:05Z",
+ "authors": [
+ {
+ "email": "yzena.tech@gmail.com",
+ "id": "",
+ "login": "",
+ "name": "Gavin Howard"
+ }
+ ],
+ "committedDate": "2019-04-26T14:49:05Z",
+ "messageBody": "",
+ "messageHeadline": "Fix a warning",
+ "oid": "f196fbe426ea69bcf4fb3046cc4fedffd7fc54b7"
+ },
+ {
+ "authoredDate": "2019-04-26T14:49:17Z",
+ "authors": [
+ {
+ "email": "yzena.tech@gmail.com",
+ "id": "",
+ "login": "",
+ "name": "Gavin Howard"
+ }
+ ],
+ "committedDate": "2019-04-26T14:49:17Z",
+ "messageBody": "",
+ "messageHeadline": "Make parsing decimal work",
+ "oid": "3b60abb028266f4ff0e1aa3bdf42f440eae646c5"
+ },
+ {
+ "authoredDate": "2019-04-26T15:46:29Z",
+ "authors": [
+ {
+ "email": "yzena.tech@gmail.com",
+ "id": "",
+ "login": "",
+ "name": "Gavin Howard"
+ }
+ ],
+ "committedDate": "2019-04-26T15:46:29Z",
+ "messageBody": "",
+ "messageHeadline": "Fix a test result file",
+ "oid": "de5fcf3618c6a9ca342ed749459f395783de28d7"
+ },
+ {
+ "authoredDate": "2019-04-26T15:47:50Z",
+ "authors": [
+ {
+ "email": "yzena.tech@gmail.com",
+ "id": "",
+ "login": "",
+ "name": "Gavin Howard"
+ }
+ ],
+ "committedDate": "2019-04-26T15:47:50Z",
+ "messageBody": "",
+ "messageHeadline": "Fix another bug in the parse decimal procedure",
+ "oid": "6076dbb54bf4503abefeab22ee2a3edf6075e2e6"
+ },
+ {
+ "authoredDate": "2019-04-26T15:48:12Z",
+ "authors": [
+ {
+ "email": "yzena.tech@gmail.com",
+ "id": "",
+ "login": "",
+ "name": "Gavin Howard"
+ }
+ ],
+ "committedDate": "2019-04-26T15:48:25Z",
+ "messageBody": "This still uses an idea from Stefan Eßer and some code, mainly the\nbuffer for calculating the numbers. Other than that, I did it my way.\n\nThe reason for this is because I *really* want to make sure the radix is\non a BcDig boundary.",
+ "messageHeadline": "Make printing decimal numbers work",
+ "oid": "51cd7ea27608359bf4298766cbf1579da9c35242"
+ },
+ {
+ "authoredDate": "2019-04-26T15:50:35Z",
+ "authors": [
+ {
+ "email": "yzena.tech@gmail.com",
+ "id": "",
+ "login": "",
+ "name": "Gavin Howard"
+ }
+ ],
+ "committedDate": "2019-04-26T15:50:35Z",
+ "messageBody": "",
+ "messageHeadline": "Remove some TODO comments that are done",
+ "oid": "9cf865f8396f76015b63d5f719e1ba4f0504a629"
+ },
+ {
+ "authoredDate": "2019-04-26T16:07:21Z",
+ "authors": [
+ {
+ "email": "yzena.tech@gmail.com",
+ "id": "",
+ "login": "",
+ "name": "Gavin Howard"
+ }
+ ],
+ "committedDate": "2019-04-26T16:07:21Z",
+ "messageBody": "",
+ "messageHeadline": "Make add work",
+ "oid": "fba50a2a7d78923ffc98e593bff06913f283b9af"
+ },
+ {
+ "authoredDate": "2019-04-26T16:07:45Z",
+ "authors": [
+ {
+ "email": "yzena.tech@gmail.com",
+ "id": "",
+ "login": "",
+ "name": "Gavin Howard"
+ }
+ ],
+ "committedDate": "2019-04-26T16:07:45Z",
+ "messageBody": "",
+ "messageHeadline": "Add a useful #define",
+ "oid": "1c1697cdc5724c5e2a2969e59fb17059d84b2272"
+ },
+ {
+ "authoredDate": "2019-04-26T16:13:45Z",
+ "authors": [
+ {
+ "email": "yzena.tech@gmail.com",
+ "id": "",
+ "login": "",
+ "name": "Gavin Howard"
+ }
+ ],
+ "committedDate": "2019-04-26T16:13:45Z",
+ "messageBody": "",
+ "messageHeadline": "Make subtract work",
+ "oid": "e6f326ed2d30c6814fed8a26d57ac6a5847587c6"
+ },
+ {
+ "authoredDate": "2019-04-26T16:17:02Z",
+ "authors": [
+ {
+ "email": "yzena.tech@gmail.com",
+ "id": "",
+ "login": "",
+ "name": "Gavin Howard"
+ }
+ ],
+ "committedDate": "2019-04-26T16:17:02Z",
+ "messageBody": "",
+ "messageHeadline": "Get rid of some compiler warnings",
+ "oid": "6fccd22b21b7f8cbf7ab2b48af912dd1ffb8bf8c"
+ },
+ {
+ "authoredDate": "2019-04-26T16:17:57Z",
+ "authors": [
+ {
+ "email": "yzena.tech@gmail.com",
+ "id": "",
+ "login": "",
+ "name": "Gavin Howard"
+ }
+ ],
+ "committedDate": "2019-04-26T16:17:57Z",
+ "messageBody": "",
+ "messageHeadline": "Remove some done TODO comments",
+ "oid": "e2e67ecbda93721e0820a01ce4e5ce660010f7bc"
+ },
+ {
+ "authoredDate": "2019-04-26T16:40:02Z",
+ "authors": [
+ {
+ "email": "yzena.tech@gmail.com",
+ "id": "",
+ "login": "",
+ "name": "Gavin Howard"
+ }
+ ],
+ "committedDate": "2019-04-26T16:40:02Z",
+ "messageBody": "",
+ "messageHeadline": "Fix some compiler warnings",
+ "oid": "c4783ac369bf46f1fbc894979fee2040e29008eb"
+ },
+ {
+ "authoredDate": "2019-04-26T20:32:07Z",
+ "authors": [
+ {
+ "email": "yzena.tech@gmail.com",
+ "id": "",
+ "login": "",
+ "name": "Gavin Howard"
+ }
+ ],
+ "committedDate": "2019-04-26T20:32:07Z",
+ "messageBody": "",
+ "messageHeadline": "Some general cleanup on math",
+ "oid": "971a26711ea6c03cbd23fd22bc93d03ead700f14"
+ },
+ {
+ "authoredDate": "2019-04-26T20:32:55Z",
+ "authors": [
+ {
+ "email": "yzena.tech@gmail.com",
+ "id": "",
+ "login": "",
+ "name": "Gavin Howard"
+ }
+ ],
+ "committedDate": "2019-04-26T20:32:55Z",
+ "messageBody": "",
+ "messageHeadline": "Make shift work",
+ "oid": "b155866dbb923e50d104ec210d095e14083fa372"
+ },
+ {
+ "authoredDate": "2019-04-26T20:33:43Z",
+ "authors": [
+ {
+ "email": "yzena.tech@gmail.com",
+ "id": "",
+ "login": "",
+ "name": "Gavin Howard"
+ }
+ ],
+ "committedDate": "2019-04-26T20:33:43Z",
+ "messageBody": "",
+ "messageHeadline": "Remove a compiler warning",
+ "oid": "da6b543c1501f01e4b781c7b24d16b950cbf0bd1"
+ },
+ {
+ "authoredDate": "2019-04-26T20:46:52Z",
+ "authors": [
+ {
+ "email": "yzena.tech@gmail.com",
+ "id": "",
+ "login": "",
+ "name": "Gavin Howard"
+ }
+ ],
+ "committedDate": "2019-04-26T20:46:52Z",
+ "messageBody": "",
+ "messageHeadline": "Fix truncate and extend",
+ "oid": "c9bef2d421dda2b0796a9ccb6198fe98ff56dd34"
+ },
+ {
+ "authoredDate": "2019-04-26T21:39:46Z",
+ "authors": [
+ {
+ "email": "yzena.tech@gmail.com",
+ "id": "",
+ "login": "",
+ "name": "Gavin Howard"
+ }
+ ],
+ "committedDate": "2019-04-26T21:39:46Z",
+ "messageBody": "I don't think it is done, however; power still needs to be tested.",
+ "messageHeadline": "Make multiply pass its tests",
+ "oid": "48a0e4793be1f58638be5c1b8820944f92afc498"
+ },
+ {
+ "authoredDate": "2019-04-26T21:41:15Z",
+ "authors": [
+ {
+ "email": "yzena.tech@gmail.com",
+ "id": "",
+ "login": "",
+ "name": "Gavin Howard"
+ }
+ ],
+ "committedDate": "2019-04-26T21:41:15Z",
+ "messageBody": "",
+ "messageHeadline": "Remove some TODO comments that are done",
+ "oid": "74b16c3bc8ec25b8c1e562ce04d68045692e1864"
+ },
+ {
+ "authoredDate": "2019-04-26T21:47:25Z",
+ "authors": [
+ {
+ "email": "yzena.tech@gmail.com",
+ "id": "",
+ "login": "",
+ "name": "Gavin Howard"
+ }
+ ],
+ "committedDate": "2019-04-26T21:47:25Z",
+ "messageBody": "",
+ "messageHeadline": "Remove more TODO comments that are done",
+ "oid": "a3f260c53adfcdd0fcd01404e08260b65c87e1e7"
+ },
+ {
+ "authoredDate": "2019-04-26T21:59:15Z",
+ "authors": [
+ {
+ "email": "yzena.tech@gmail.com",
+ "id": "",
+ "login": "",
+ "name": "Gavin Howard"
+ }
+ ],
+ "committedDate": "2019-04-26T21:59:15Z",
+ "messageBody": "",
+ "messageHeadline": "Clean up some style",
+ "oid": "404ece77d2e3e5009511e77a5f9e56d4e1afb3f7"
+ },
+ {
+ "authoredDate": "2019-04-26T21:59:33Z",
+ "authors": [
+ {
+ "email": "yzena.tech@gmail.com",
+ "id": "",
+ "login": "",
+ "name": "Gavin Howard"
+ }
+ ],
+ "committedDate": "2019-04-26T21:59:33Z",
+ "messageBody": "",
+ "messageHeadline": "Fix various small issues",
+ "oid": "774454354a9354023b6fb7e3aa396b269a5562f7"
+ },
+ {
+ "authoredDate": "2019-04-26T23:29:01Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-26T23:29:01Z",
+ "messageBody": "",
+ "messageHeadline": "Iterative division based on Newton Raphson algorithm",
+ "oid": "193f8529ad93306e39914ca7a2aa877669383f6f"
+ },
+ {
+ "authoredDate": "2019-04-27T01:31:31Z",
+ "authors": [
+ {
+ "email": "yzena.tech@gmail.com",
+ "id": "",
+ "login": "",
+ "name": "Gavin Howard"
+ }
+ ],
+ "committedDate": "2019-04-27T01:31:31Z",
+ "messageBody": "",
+ "messageHeadline": "Change some code back to what it was (with adjustments)",
+ "oid": "c25fd61637390cc993c91258dda2c01404b37147"
+ },
+ {
+ "authoredDate": "2019-04-27T07:16:26Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-27T07:16:26Z",
+ "messageBody": "",
+ "messageHeadline": "Merge branch 'base9' of https://github.com/gavinhoward/bc into d9",
+ "oid": "87c27b5715646eb4deec4a6795c17b4c5facc408"
+ },
+ {
+ "authoredDate": "2019-04-27T08:39:00Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-27T08:39:00Z",
+ "messageBody": "",
+ "messageHeadline": "Fix mismerge regarding nBcDig vs. places",
+ "oid": "70f118574077b6e61cae08bd576ca7fccc167786"
+ },
+ {
+ "authoredDate": "2019-04-27T08:40:14Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-27T08:40:14Z",
+ "messageBody": "…e current rdx",
+ "messageHeadline": "Fix segmentation fault that occurs if the target rdx is lower than th…",
+ "oid": "6189d95036f123d48d505bf4d4a49ba11ca30aee"
+ },
+ {
+ "authoredDate": "2019-04-27T08:42:00Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-27T08:42:00Z",
+ "messageBody": "",
+ "messageHeadline": "Add simple debug print macro",
+ "oid": "cdda6e99974b8f9551ad1bc184b3c735afac6f4a"
+ },
+ {
+ "authoredDate": "2019-04-27T11:47:09Z",
+ "authors": [
+ {
+ "email": "root@StefanEsser.freebsd.org",
+ "id": "",
+ "login": "",
+ "name": "Charlie Root"
+ }
+ ],
+ "committedDate": "2019-04-27T11:47:09Z",
+ "messageBody": "",
+ "messageHeadline": "Fix calculation of scale in multiplications",
+ "oid": "ac244bdb3ebe6e6a2693f3d5c2ae13d7723b3517"
+ },
+ {
+ "authoredDate": "2019-04-27T12:36:28Z",
+ "authors": [
+ {
+ "email": "yzena.tech@gmail.com",
+ "id": "",
+ "login": "",
+ "name": "Gavin Howard"
+ }
+ ],
+ "committedDate": "2019-04-27T12:36:28Z",
+ "messageBody": "",
+ "messageHeadline": "Fix seg fault in bc_num_shiftLeft()",
+ "oid": "b1d29c24cb4bfa07f3bc3e665811ab8b100b0b5e"
+ },
+ {
+ "authoredDate": "2019-04-27T12:40:58Z",
+ "authors": [
+ {
+ "email": "yzena.tech@gmail.com",
+ "id": "",
+ "login": "",
+ "name": "Gavin D. Howard"
+ }
+ ],
+ "committedDate": "2019-04-27T12:40:58Z",
+ "messageBody": "Newton-Raphson based division",
+ "messageHeadline": "Merge pull request #16 from stesser/d9",
+ "oid": "769fcbed7a3a0005a60254c0ed5203406b533ae3"
+ },
+ {
+ "authoredDate": "2019-04-27T12:41:58Z",
+ "authors": [
+ {
+ "email": "yzena.tech@gmail.com",
+ "id": "",
+ "login": "",
+ "name": "Gavin Howard"
+ }
+ ],
+ "committedDate": "2019-04-27T12:41:58Z",
+ "messageBody": "",
+ "messageHeadline": "Fix some style",
+ "oid": "2e736dee1ae4ddadbe4305296fc98ecc9a917e45"
+ },
+ {
+ "authoredDate": "2019-04-27T12:43:57Z",
+ "authors": [
+ {
+ "email": "yzena.tech@gmail.com",
+ "id": "",
+ "login": "",
+ "name": "Gavin Howard"
+ }
+ ],
+ "committedDate": "2019-04-27T12:43:57Z",
+ "messageBody": "",
+ "messageHeadline": "Fix the multiplication scale back",
+ "oid": "cf9f246919f39e50c911c6552d9b16f05a0fc2cf"
+ },
+ {
+ "authoredDate": "2019-04-27T12:51:33Z",
+ "authors": [
+ {
+ "email": "yzena.tech@gmail.com",
+ "id": "",
+ "login": "",
+ "name": "Gavin Howard"
+ }
+ ],
+ "committedDate": "2019-04-27T12:51:33Z",
+ "messageBody": "",
+ "messageHeadline": "Merge branch 'master' into base9",
+ "oid": "5de0bd695347a0ef5bde2d526472a2a3ba6d2a17"
+ },
+ {
+ "authoredDate": "2019-04-27T13:26:35Z",
+ "authors": [
+ {
+ "email": "yzena.tech@gmail.com",
+ "id": "",
+ "login": "",
+ "name": "Gavin Howard"
+ }
+ ],
+ "committedDate": "2019-04-27T13:26:35Z",
+ "messageBody": "",
+ "messageHeadline": "Add another print debug function",
+ "oid": "5b40e402dc37897052129db14441695a25bd86f8"
+ },
+ {
+ "authoredDate": "2019-04-27T13:28:24Z",
+ "authors": [
+ {
+ "email": "yzena.tech@gmail.com",
+ "id": "",
+ "login": "",
+ "name": "Gavin Howard"
+ }
+ ],
+ "committedDate": "2019-04-27T13:28:24Z",
+ "messageBody": "",
+ "messageHeadline": "Change bc_num_printDigs() a bit",
+ "oid": "37118f4c4f26f64bb4becb0caa55149659ca8ac3"
+ },
+ {
+ "authoredDate": "2019-04-27T13:28:45Z",
+ "authors": [
+ {
+ "email": "yzena.tech@gmail.com",
+ "id": "",
+ "login": "",
+ "name": "Gavin Howard"
+ }
+ ],
+ "committedDate": "2019-04-27T13:28:45Z",
+ "messageBody": "",
+ "messageHeadline": "Fix a divide by 0",
+ "oid": "edd9d9dd1459746a4bb2012194a3d3c831414c68"
+ },
+ {
+ "authoredDate": "2019-04-27T13:28:56Z",
+ "authors": [
+ {
+ "email": "yzena.tech@gmail.com",
+ "id": "",
+ "login": "",
+ "name": "Gavin Howard"
+ }
+ ],
+ "committedDate": "2019-04-27T13:28:56Z",
+ "messageBody": "",
+ "messageHeadline": "Fix some style",
+ "oid": "034b5b9e16e57becf5121ed60ae1dac8b8dc4ece"
+ },
+ {
+ "authoredDate": "2019-04-27T13:33:16Z",
+ "authors": [
+ {
+ "email": "yzena.tech@gmail.com",
+ "id": "",
+ "login": "",
+ "name": "Gavin Howard"
+ }
+ ],
+ "committedDate": "2019-04-27T13:33:16Z",
+ "messageBody": "",
+ "messageHeadline": "Fix more style",
+ "oid": "c88e5231008923290d1ead34ff987fb2e17043c5"
+ },
+ {
+ "authoredDate": "2019-04-27T13:34:13Z",
+ "authors": [
+ {
+ "email": "yzena.tech@gmail.com",
+ "id": "",
+ "login": "",
+ "name": "Gavin Howard"
+ }
+ ],
+ "committedDate": "2019-04-27T13:34:13Z",
+ "messageBody": "",
+ "messageHeadline": "Fix even more style",
+ "oid": "20afaeab2ffca9a32e1fa65feeb33050d9c4e204"
+ },
+ {
+ "authoredDate": "2019-04-27T13:53:23Z",
+ "authors": [
+ {
+ "email": "yzena.tech@gmail.com",
+ "id": "",
+ "login": "",
+ "name": "Gavin Howard"
+ }
+ ],
+ "committedDate": "2019-04-27T13:53:23Z",
+ "messageBody": "",
+ "messageHeadline": "Move an item",
+ "oid": "948b639a0674f3ce836fe3ba843e2253ef798cd8"
+ },
+ {
+ "authoredDate": "2019-04-27T13:53:36Z",
+ "authors": [
+ {
+ "email": "yzena.tech@gmail.com",
+ "id": "",
+ "login": "",
+ "name": "Gavin Howard"
+ }
+ ],
+ "committedDate": "2019-04-27T13:53:36Z",
+ "messageBody": "",
+ "messageHeadline": "Remove two unused functions",
+ "oid": "2316cb42fdff7b768debf3d9c65b73c9c8fc6137"
+ },
+ {
+ "authoredDate": "2019-04-27T14:09:32Z",
+ "authors": [
+ {
+ "email": "yzena.tech@gmail.com",
+ "id": "",
+ "login": "",
+ "name": "Gavin Howard"
+ }
+ ],
+ "committedDate": "2019-04-27T14:09:32Z",
+ "messageBody": "",
+ "messageHeadline": "Add length and scale tests",
+ "oid": "805328cdb9342eb249dc398b4569190d7ca78747"
+ },
+ {
+ "authoredDate": "2019-04-27T14:30:10Z",
+ "authors": [
+ {
+ "email": "yzena.tech@gmail.com",
+ "id": "",
+ "login": "",
+ "name": "Gavin Howard"
+ }
+ ],
+ "committedDate": "2019-04-27T14:30:10Z",
+ "messageBody": "",
+ "messageHeadline": "Make length work",
+ "oid": "b41483b364bf8fe8841e8875febeb4d58743e32f"
+ },
+ {
+ "authoredDate": "2019-04-27T21:33:08Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-27T21:33:08Z",
+ "messageBody": "…upstream changes",
+ "messageHeadline": "Updated implementation of Newton-Raphson division algorithm to match …",
+ "oid": "b5aeb445a556de9d9b41a7b2d196b59bcaca90ae"
+ },
+ {
+ "authoredDate": "2019-04-27T21:41:21Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-27T21:41:21Z",
+ "messageBody": "",
+ "messageHeadline": "Small optimization of the division function",
+ "oid": "6e129fc621b358d219036e6e4d01f572adb133ff"
+ },
+ {
+ "authoredDate": "2019-04-27T21:43:02Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-27T21:43:02Z",
+ "messageBody": "",
+ "messageHeadline": "Small optimization of the division function",
+ "oid": "217379d6199e6bb8b586716b382731882c6096fe"
+ },
+ {
+ "authoredDate": "2019-04-27T21:45:48Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-27T21:45:48Z",
+ "messageBody": "",
+ "messageHeadline": "Merge branch 'base9' of https://github.com/gavinhoward/bc into d9",
+ "oid": "45a56123c3c6ede3cf157419e56427a01ff48e2a"
+ },
+ {
+ "authoredDate": "2019-04-28T00:04:02Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Eßer"
+ }
+ ],
+ "committedDate": "2019-04-28T00:04:02Z",
+ "messageBody": "He says that he made division work. Cool.",
+ "messageHeadline": "Import more code from Stefan",
+ "oid": "daaaaa795095115e813ad7c7afbae0937a046246"
+ },
+ {
+ "authoredDate": "2019-04-28T10:30:47Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-28T10:30:47Z",
+ "messageBody": "I had missed to set scsale values in the parameters passed in by the divide\nfunction and thus got results that had 0 fractional digits.",
+ "messageHeadline": "Undo change to scale calculation in bc_num_m",
+ "oid": "d793fa5c3a03370627e45d5f03968039b10f34c6"
+ },
+ {
+ "authoredDate": "2019-04-28T12:58:58Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-28T12:58:58Z",
+ "messageBody": "This function will be used in the division algorithm.",
+ "messageHeadline": "Add function to round number to a given number of decimal places",
+ "oid": "db970636e68f62f41c17c6053e5ccc242161e5a0"
+ },
+ {
+ "authoredDate": "2019-04-28T13:50:14Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-28T13:50:14Z",
+ "messageBody": "I had a local change that made bc_num_invert implicily extend this variable,\nbut noticed with that change reverted that the precision of the calculation\nwas reduced to BC_BASE_POWER decimals, leading to imprecise division results.",
+ "messageHeadline": "Fix division: Extend variable to required number of decimal places",
+ "oid": "70997150a28b4df49639924431cb0706a403207f"
+ },
+ {
+ "authoredDate": "2019-04-28T15:21:34Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-28T15:21:34Z",
+ "messageBody": "For now, keep the Newton-Raphson algorithm in an #if 0 block for reference and to\nallow to compare them in further tests.\n\nI have noticed segmentation faults when dividing very large numbers, which probably\nare due to missing calls of bc_num_extend() for destination variables of arithmetic\noperations. I'll try to fix the length calculations, but I do not know the exact\nsemantics of a number of low level calls.",
+ "messageHeadline": "Replace Newton-Raphson algorithm by Goldschmidt algorithm.",
+ "oid": "154b473a9c65f081d673556ce163c50754cf75de"
+ },
+ {
+ "authoredDate": "2019-04-28T18:19:51Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-28T18:19:51Z",
+ "messageBody": "",
+ "messageHeadline": "Merge branch 'base9' of https://github.com/gavinhoward/bc into d9",
+ "oid": "307ce024f26a0433460a84ad5ad22bd3ac6f094f"
+ },
+ {
+ "authoredDate": "2019-04-28T19:32:13Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-28T19:32:13Z",
+ "messageBody": "",
+ "messageHeadline": "Optimize bc_num_pow10",
+ "oid": "72109f49b8cd1e877c4b4252652d73f21d0ad689"
+ },
+ {
+ "authoredDate": "2019-04-28T22:59:32Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-28T22:59:32Z",
+ "messageBody": "This makes bc complete the tests without crash, but with a result that\nhas too few digits printed. This will need to be debugged next ...",
+ "messageHeadline": "Fix scale parameters and remove bogus bc_num_extend calls",
+ "oid": "4c1e43af931618094a761dff7d1e4b0374e7576d"
+ },
+ {
+ "authoredDate": "2019-04-29T10:56:32Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-29T10:56:32Z",
+ "messageBody": "Now all division and multiplication tests pass.",
+ "messageHeadline": "Fix position of decimal point when dividing by a very small number.",
+ "oid": "40b53d23eb92b69a9d079ead59def298cb089370"
+ },
+ {
+ "authoredDate": "2019-04-29T11:00:23Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-29T11:00:23Z",
+ "messageBody": "",
+ "messageHeadline": "Improve format of dumped BcNum values in debug traces",
+ "oid": "33a1c689f4d5cf2026c92a954c36a50f6e772c8e"
+ },
+ {
+ "authoredDate": "2019-04-29T12:24:31Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-29T12:24:31Z",
+ "messageBody": "There were 4 places where \"rdx\" had to be replaced by \"scale\".\nThis version passes all tests.",
+ "messageHeadline": "Fix \"power\" operator (bc_num_p).",
+ "oid": "c66e44871c84fc81a3595d21d9174082b62da1d3"
+ },
+ {
+ "authoredDate": "2019-04-29T13:06:00Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-29T13:06:00Z",
+ "messageBody": "Results seem to be correct except for the number of decimals calculated and returned.\nThe tests still fail for that reason, but show results that are near to what is expected.",
+ "messageHeadline": "Mostly fix the sqrt operation.",
+ "oid": "627693667ff6b0bc70f4a636bd282423af74df62"
+ },
+ {
+ "authoredDate": "2019-04-29T13:36:46Z",
+ "authors": [
+ {
+ "email": "yzena.tech@gmail.com",
+ "id": "",
+ "login": "",
+ "name": "Gavin Howard"
+ }
+ ],
+ "committedDate": "2019-04-29T13:36:46Z",
+ "messageBody": "",
+ "messageHeadline": "Start building Goldschmidt",
+ "oid": "fdbcd063e2eab65ca07be9f62bdd10cc965a220d"
+ },
+ {
+ "authoredDate": "2019-04-29T13:56:04Z",
+ "authors": [
+ {
+ "email": "yzena.tech@gmail.com",
+ "id": "",
+ "login": "",
+ "name": "Gavin Howard"
+ }
+ ],
+ "committedDate": "2019-04-29T13:56:04Z",
+ "messageBody": "",
+ "messageHeadline": "Add a useful function",
+ "oid": "08f7856b38ca730b120e450dea482d0e18fd2232"
+ },
+ {
+ "authoredDate": "2019-04-29T15:10:43Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-29T15:10:43Z",
+ "messageBody": "…orithm\n\nIntroduce and use a function bc_num_int_digits() that counts the number of digits\nto the left of the decimalpoint.",
+ "messageHeadline": "Fix and improve the calculation of the initial value for the sqrt alg…",
+ "oid": "cc7cd938dadfd8ecbaf61e79f7fba8060ff7194c"
+ },
+ {
+ "authoredDate": "2019-04-29T16:14:21Z",
+ "authors": [
+ {
+ "email": "yzena.tech@gmail.com",
+ "id": "",
+ "login": "",
+ "name": "Gavin Howard"
+ }
+ ],
+ "committedDate": "2019-04-29T16:14:21Z",
+ "messageBody": "There are still some cases that fail, but this is mostly working, and I\nwould like to save my progress.",
+ "messageHeadline": "Make most of divide work",
+ "oid": "36ec987c22af5d7ccc3b8aef0fe9758ce85c56a0"
+ },
+ {
+ "authoredDate": "2019-04-29T16:28:33Z",
+ "authors": [
+ {
+ "email": "yzena.tech@gmail.com",
+ "id": "",
+ "login": "",
+ "name": "Gavin Howard"
+ }
+ ],
+ "committedDate": "2019-04-29T16:28:33Z",
+ "messageBody": "",
+ "messageHeadline": "Make divide work",
+ "oid": "cef0397c810152aed4116fdc56dcf920d20eda32"
+ }
+ ],
+ "createdAt": "2019-04-29T11:21:03Z",
+ "deletions": 234,
+ "files": [
+ {
+ "path": "include/num.h",
+ "additions": 71,
+ "deletions": 4
+ },
+ {
+ "path": "include/status.h",
+ "additions": 2,
+ "deletions": 0
+ },
+ {
+ "path": "include/vm.h",
+ "additions": 0,
+ "deletions": 2
+ },
+ {
+ "path": "src/num.c",
+ "additions": 838,
+ "deletions": 223
+ },
+ {
+ "path": "src/program.c",
+ "additions": 2,
+ "deletions": 2
+ },
+ {
+ "path": "tests/all.sh",
+ "additions": 1,
+ "deletions": 1
+ },
+ {
+ "path": "tests/bc/all.txt",
+ "additions": 4,
+ "deletions": 2
+ },
+ {
+ "path": "tests/bc/decimal.txt",
+ "additions": 23,
+ "deletions": 0
+ },
+ {
+ "path": "tests/bc/decimal_results.txt",
+ "additions": 23,
+ "deletions": 0
+ },
+ {
+ "path": "tests/bc/divide.txt",
+ "additions": 31,
+ "deletions": 0
+ },
+ {
+ "path": "tests/bc/divide_results.txt",
+ "additions": 30,
+ "deletions": 0
+ },
+ {
+ "path": "tests/bc/length.txt",
+ "additions": 59,
+ "deletions": 0
+ },
+ {
+ "path": "tests/bc/length_results.txt",
+ "additions": 57,
+ "deletions": 0
+ },
+ {
+ "path": "tests/bc/scale.txt",
+ "additions": 56,
+ "deletions": 0
+ },
+ {
+ "path": "tests/bc/scale_results.txt",
+ "additions": 56,
+ "deletions": 0
+ },
+ {
+ "path": "tests/bc/shift.txt",
+ "additions": 5280,
+ "deletions": 0
+ },
+ {
+ "path": "tests/bc/shift_results.txt",
+ "additions": 5280,
+ "deletions": 0
+ }
+ ],
+ "fullDatabaseId": "274334078",
+ "headRefName": "d9",
+ "headRefOid": "914a7f66807b3567fa438c8d668a040e9d2bcc4a",
+ "headRepository": {
+ "id": "MDEwOlJlcG9zaXRvcnkxODAzNzI3NzY=",
+ "name": "bc"
+ },
+ "headRepositoryOwner": {
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "name": "Stefan Eßer",
+ "login": "stesser"
+ },
+ "id": "MDExOlB1bGxSZXF1ZXN0Mjc0MzM0MDc4",
+ "isCrossRepository": true,
+ "isDraft": false,
+ "labels": [],
+ "latestReviews": [],
+ "maintainerCanModify": false,
+ "mergeCommit": {
+ "oid": "914a7f66807b3567fa438c8d668a040e9d2bcc4a"
+ },
+ "mergeStateStatus": "UNKNOWN",
+ "mergeable": "UNKNOWN",
+ "mergedAt": "2019-05-06T13:42:00Z",
+ "mergedBy": {
+ "id": "MDQ6VXNlcjMxNzI2ODc=",
+ "is_bot": false,
+ "login": "gavinhoward",
+ "name": "Gavin D. Howard"
+ },
+ "milestone": null,
+ "number": 18,
+ "potentialMergeCommit": null,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "reviewDecision": "",
+ "reviewRequests": [],
+ "reviews": [],
+ "state": "MERGED",
+ "statusCheckRollup": [],
+ "title": "Division and modulo pass all tests",
+ "updatedAt": "2019-05-06T13:42:00Z",
+ "url": "https://github.com/gavinhoward/bc/pull/18"
+ },
+ {
+ "additions": 295,
+ "assignees": [],
+ "author": {
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "is_bot": false,
+ "login": "stesser",
+ "name": "Stefan Eßer"
+ },
+ "autoMergeRequest": null,
+ "baseRefName": "base9",
+ "baseRefOid": "e396dff5929071da830a84b64405f7a7c8e0113e",
+ "body": "I'm still not sure about correct scale values and situations in which bc_num_extend must be called to prepare a BcNum that is to receive the result of an arithmetic operation. This leads to overflow for large values (the algorithm is essentially correct, but variables need to be extended in some cases).",
+ "changedFiles": 4,
+ "closed": true,
+ "closedAt": "2019-05-06T13:42:50Z",
+ "comments": [
+ {
+ "id": "MDEyOklzc3VlQ29tbWVudDQ4NzM5MDI1OA==",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "How does this new algorithm compare to Newton-Raphson performance-wise?",
+ "createdAt": "2019-04-28T15:38:21Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/17#issuecomment-487390258",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "MDEyOklzc3VlQ29tbWVudDQ4NzQwNzQ5NA==",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Also, would it be possible for you to fix the crashes on both algorithms? The test suite crashes on both. It would also be nice if you could put them in a #if 0 #else #endif set so I can test the changes and see which one is faster. Thank you.",
+ "createdAt": "2019-04-28T19:14:59Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/17#issuecomment-487407494",
+ "viewerDidAuthor": true
+ },
+ {
+ "id": "MDEyOklzc3VlQ29tbWVudDQ4OTYyNTA2NA==",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Closed by manually merging.",
+ "createdAt": "2019-05-06T13:42:50Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/17#issuecomment-489625064",
+ "viewerDidAuthor": true
+ }
+ ],
+ "commits": [
+ {
+ "authoredDate": "2019-04-27T21:33:08Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-27T21:33:08Z",
+ "messageBody": "…upstream changes",
+ "messageHeadline": "Updated implementation of Newton-Raphson division algorithm to match …",
+ "oid": "b5aeb445a556de9d9b41a7b2d196b59bcaca90ae"
+ },
+ {
+ "authoredDate": "2019-04-27T21:41:21Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-27T21:41:21Z",
+ "messageBody": "",
+ "messageHeadline": "Small optimization of the division function",
+ "oid": "6e129fc621b358d219036e6e4d01f572adb133ff"
+ },
+ {
+ "authoredDate": "2019-04-27T21:43:02Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-27T21:43:02Z",
+ "messageBody": "",
+ "messageHeadline": "Small optimization of the division function",
+ "oid": "217379d6199e6bb8b586716b382731882c6096fe"
+ },
+ {
+ "authoredDate": "2019-04-27T21:45:48Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-27T21:45:48Z",
+ "messageBody": "",
+ "messageHeadline": "Merge branch 'base9' of https://github.com/gavinhoward/bc into d9",
+ "oid": "45a56123c3c6ede3cf157419e56427a01ff48e2a"
+ },
+ {
+ "authoredDate": "2019-04-28T10:30:47Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-28T10:30:47Z",
+ "messageBody": "I had missed to set scsale values in the parameters passed in by the divide\nfunction and thus got results that had 0 fractional digits.",
+ "messageHeadline": "Undo change to scale calculation in bc_num_m",
+ "oid": "d793fa5c3a03370627e45d5f03968039b10f34c6"
+ },
+ {
+ "authoredDate": "2019-04-28T12:58:58Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-28T12:58:58Z",
+ "messageBody": "This function will be used in the division algorithm.",
+ "messageHeadline": "Add function to round number to a given number of decimal places",
+ "oid": "db970636e68f62f41c17c6053e5ccc242161e5a0"
+ },
+ {
+ "authoredDate": "2019-04-28T13:50:14Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-28T13:50:14Z",
+ "messageBody": "I had a local change that made bc_num_invert implicily extend this variable,\nbut noticed with that change reverted that the precision of the calculation\nwas reduced to BC_BASE_POWER decimals, leading to imprecise division results.",
+ "messageHeadline": "Fix division: Extend variable to required number of decimal places",
+ "oid": "70997150a28b4df49639924431cb0706a403207f"
+ },
+ {
+ "authoredDate": "2019-04-28T15:21:34Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-28T15:21:34Z",
+ "messageBody": "For now, keep the Newton-Raphson algorithm in an #if 0 block for reference and to\nallow to compare them in further tests.\n\nI have noticed segmentation faults when dividing very large numbers, which probably\nare due to missing calls of bc_num_extend() for destination variables of arithmetic\noperations. I'll try to fix the length calculations, but I do not know the exact\nsemantics of a number of low level calls.",
+ "messageHeadline": "Replace Newton-Raphson algorithm by Goldschmidt algorithm.",
+ "oid": "154b473a9c65f081d673556ce163c50754cf75de"
+ },
+ {
+ "authoredDate": "2019-04-28T18:19:51Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-28T18:19:51Z",
+ "messageBody": "",
+ "messageHeadline": "Merge branch 'base9' of https://github.com/gavinhoward/bc into d9",
+ "oid": "307ce024f26a0433460a84ad5ad22bd3ac6f094f"
+ },
+ {
+ "authoredDate": "2019-04-28T19:32:13Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-28T19:32:13Z",
+ "messageBody": "",
+ "messageHeadline": "Optimize bc_num_pow10",
+ "oid": "72109f49b8cd1e877c4b4252652d73f21d0ad689"
+ },
+ {
+ "authoredDate": "2019-04-28T22:59:32Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-28T22:59:32Z",
+ "messageBody": "This makes bc complete the tests without crash, but with a result that\nhas too few digits printed. This will need to be debugged next ...",
+ "messageHeadline": "Fix scale parameters and remove bogus bc_num_extend calls",
+ "oid": "4c1e43af931618094a761dff7d1e4b0374e7576d"
+ },
+ {
+ "authoredDate": "2019-04-29T10:56:32Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-29T10:56:32Z",
+ "messageBody": "Now all division and multiplication tests pass.",
+ "messageHeadline": "Fix position of decimal point when dividing by a very small number.",
+ "oid": "40b53d23eb92b69a9d079ead59def298cb089370"
+ },
+ {
+ "authoredDate": "2019-04-29T11:00:23Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-29T11:00:23Z",
+ "messageBody": "",
+ "messageHeadline": "Improve format of dumped BcNum values in debug traces",
+ "oid": "33a1c689f4d5cf2026c92a954c36a50f6e772c8e"
+ },
+ {
+ "authoredDate": "2019-04-29T12:24:31Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-29T12:24:31Z",
+ "messageBody": "There were 4 places where \"rdx\" had to be replaced by \"scale\".\nThis version passes all tests.",
+ "messageHeadline": "Fix \"power\" operator (bc_num_p).",
+ "oid": "c66e44871c84fc81a3595d21d9174082b62da1d3"
+ },
+ {
+ "authoredDate": "2019-04-29T13:06:00Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-29T13:06:00Z",
+ "messageBody": "Results seem to be correct except for the number of decimals calculated and returned.\nThe tests still fail for that reason, but show results that are near to what is expected.",
+ "messageHeadline": "Mostly fix the sqrt operation.",
+ "oid": "627693667ff6b0bc70f4a636bd282423af74df62"
+ },
+ {
+ "authoredDate": "2019-04-29T15:10:43Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-29T15:10:43Z",
+ "messageBody": "…orithm\n\nIntroduce and use a function bc_num_int_digits() that counts the number of digits\nto the left of the decimalpoint.",
+ "messageHeadline": "Fix and improve the calculation of the initial value for the sqrt alg…",
+ "oid": "cc7cd938dadfd8ecbaf61e79f7fba8060ff7194c"
+ },
+ {
+ "authoredDate": "2019-04-29T18:13:51Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-29T18:13:51Z",
+ "messageBody": "",
+ "messageHeadline": "Make more Sqrt tests work",
+ "oid": "8f7c5a565c0c8cb4f674982c6fbb3174d6eaeee9"
+ },
+ {
+ "authoredDate": "2019-04-29T18:49:15Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-29T18:49:15Z",
+ "messageBody": "",
+ "messageHeadline": "Make all Sqrt tests work",
+ "oid": "1f0f69f0d249d14270fe14a993405926a536ce05"
+ },
+ {
+ "authoredDate": "2019-04-29T19:28:52Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-29T19:28:52Z",
+ "messageBody": "",
+ "messageHeadline": "Apply existing style to my changes - no functional change",
+ "oid": "f76f0365e272d675c73fb62dcfa09653960106fc"
+ },
+ {
+ "authoredDate": "2019-04-29T20:10:41Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-29T20:10:41Z",
+ "messageBody": "",
+ "messageHeadline": "Fix debug printing of BcNum variables",
+ "oid": "0dea79ffb116b3395b949b18d80473af9e969ced"
+ },
+ {
+ "authoredDate": "2019-04-30T08:47:06Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-30T08:47:06Z",
+ "messageBody": "…uns easier to decode",
+ "messageHeadline": "Make debug printing of BcNum variables use signed values to make over…",
+ "oid": "f8610b15aeed384d520d83ba66c964abd828d096"
+ },
+ {
+ "authoredDate": "2019-04-30T09:24:44Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-30T09:24:44Z",
+ "messageBody": "",
+ "messageHeadline": "Fix printing of numbers with obase != 10 (length of fractional part)",
+ "oid": "36220832c7341fd267fdfb4010c579c5bc840a60"
+ },
+ {
+ "authoredDate": "2019-05-02T08:43:02Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-05-02T08:43:02Z",
+ "messageBody": "",
+ "messageHeadline": "Merge branch 'base9' of https://github.com/gavinhoward/bc into d9",
+ "oid": "4534283defbcd20faf921666b0b32693633c3ec2"
+ },
+ {
+ "authoredDate": "2019-05-02T08:43:21Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-05-02T08:43:21Z",
+ "messageBody": "…results.\n\nThe implementation can be changed back to a macro that accesses the array\nwithout any further computation, if the function should take non-negligible\ntime. But the function will be inlined or optimized away in most cases and\nthus should not have a significant impact on the run-time ...",
+ "messageHeadline": "Revert bc_num_pow10 to a function instead of an array that holds the …",
+ "oid": "6167e2231df4f606278ca414931612a0e7d595e1"
+ },
+ {
+ "authoredDate": "2019-05-02T08:56:01Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-05-02T08:56:01Z",
+ "messageBody": "",
+ "messageHeadline": "Add back the forward declaration of bc_num_pow10.",
+ "oid": "5b2af58b477230aea07d7f69d401390d500b5a75"
+ },
+ {
+ "authoredDate": "2019-05-02T11:38:00Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-05-02T11:38:00Z",
+ "messageBody": "",
+ "messageHeadline": "Allow to pass BC_BASE_POWER on the compiler command line",
+ "oid": "4d90ccff6ac554b33b5832d79bca935cb8d03fc3"
+ },
+ {
+ "authoredDate": "2019-05-04T07:12:09Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-05-04T07:12:09Z",
+ "messageBody": "",
+ "messageHeadline": "Fix decoding of BcNum in debug print function",
+ "oid": "f28be31518134f169a64d4a8104414c335b5c947"
+ },
+ {
+ "authoredDate": "2019-05-04T07:14:02Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-05-04T07:14:02Z",
+ "messageBody": "",
+ "messageHeadline": "Fix merge errors in idivision based on Goldschmidt algorithm",
+ "oid": "10bca1171455d813a70b60a6bb05538b40831c1a"
+ },
+ {
+ "authoredDate": "2019-05-04T07:24:27Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-05-04T07:24:27Z",
+ "messageBody": "\"shift\" operations are used within all arithmetic operations\n\"print\" is a pre-requisite of generating the results file for the \"parse\" test",
+ "messageHeadline": "Change order of tests to first test features that later tests depend on",
+ "oid": "f213e28e1a70e1ca581f9d137a1498dedcf97095"
+ },
+ {
+ "authoredDate": "2019-05-04T13:58:41Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-05-04T13:58:41Z",
+ "messageBody": "…compiler warning",
+ "messageHeadline": "Change type of variables in debug print function to int to silence a …",
+ "oid": "013aeffa913df9dd96eec9c2dacfb64c9e766868"
+ },
+ {
+ "authoredDate": "2019-05-04T14:19:12Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-05-04T14:19:12Z",
+ "messageBody": "The correction is derived from the difference of the input parameter times it inverse.\nThis value could be used for a final Newton-Raphson step (at the cost of an additional\nmultiplication), but since we can assume that the input\tparameter was very near\tto 1.0,\nthe multiplication would only add non-zero digits beyond the current scale range.\n\nThe Goldschmidt\talgorithm is an\tinfinite series\tof ever smaller positive values\tand\nif the series is cut off at some point,\tthe result is known to be exact\tor smaller\nthan the exact value.\n\nThe correction applied is meant\tto compensate for the neglected\tseries elements.\nIf it is found that there are boundary cases where we over-compensate, then the\nNewton-Raphson step could be used to apply a slightly smaller correction.",
+ "messageHeadline": "Add correction to result of Goldschmidt\talgorithm",
+ "oid": "85f59a665e6b20380accdb7c9f5fbca0d39d693b"
+ },
+ {
+ "authoredDate": "2019-05-04T14:24:40Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-05-04T14:24:40Z",
+ "messageBody": "After a correction is applied to the returned reciprocal to account for the finite\nnumber of elements used in teh Goldschmidt algorithm, the result should be correct\nwithout the final rounding step.\n\nWhile here also remove the assignment of 1 to a variable that is not used for the\nspecific case anymore, anyway ...",
+ "messageHeadline": "Adapt division algorithm to use the updated bc_num_invert()",
+ "oid": "7e21b3cca2b93e7d5509e05dbf3d2076dcffa74d"
+ },
+ {
+ "authoredDate": "2019-05-04T15:01:21Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-05-04T15:01:21Z",
+ "messageBody": "",
+ "messageHeadline": "Fix width of value printed for base > 17",
+ "oid": "017f0c843a3439651ce310cdb6ffc90cc09f6203"
+ },
+ {
+ "authoredDate": "2019-05-04T15:05:12Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-05-04T15:05:12Z",
+ "messageBody": "",
+ "messageHeadline": "Fix printing of fractional parts for obase > 10",
+ "oid": "1a503e62ff9e642740577f8df68e439a1b9e1d10"
+ },
+ {
+ "authoredDate": "2019-05-04T16:02:15Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-05-04T16:02:15Z",
+ "messageBody": "It has been found to be too low e.g. when calculating l(256)/l(16) with scale=40.\nThe result is exact with this increased correction applied.",
+ "messageHeadline": "Double correction value applied in bc_num_invert()",
+ "oid": "b9db23ba3b452798cc5b4ded0125565e5940f3e3"
+ },
+ {
+ "authoredDate": "2019-05-04T18:12:43Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-05-04T18:12:43Z",
+ "messageBody": "The locations with these markers should be checked, although the tests seem to\nsucceed wíthiut them.\n\nCurrently test succeed until \"reference.bc2\", which returns zeroes for some of\nthe test arrays. But these marked locations are probably not related to that\ntest failing.",
+ "messageHeadline": "Mark a few places where changes might be required with // <se>",
+ "oid": "276de8c876ba72f1357dd0ce7cc1ef403902cedf"
+ },
+ {
+ "authoredDate": "2019-05-04T21:48:51Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-05-04T21:48:51Z",
+ "messageBody": "This change has been performed due to a source code analysis.\nNo tests failed with the old code (i.e., no test covered this case).",
+ "messageHeadline": "Fix length parameter which obviously should be scale not rdx",
+ "oid": "e6bd86bdb390d0a74b31aabdde56d5824f38741c"
+ },
+ {
+ "authoredDate": "2019-05-04T21:50:38Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-05-04T21:50:38Z",
+ "messageBody": "",
+ "messageHeadline": "Add comments to the division based on the Goldschmidt algorithm",
+ "oid": "28ffb91991cb5ca2c8687de2ff355161088d5997"
+ },
+ {
+ "authoredDate": "2019-05-05T19:54:54Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-05-05T19:54:54Z",
+ "messageBody": "… crashing\n\nThe cut-off value is arbitrary, a significantly lower limit could be applied.",
+ "messageHeadline": "Make shiftLeft return an error for too large shift amounts instead of…",
+ "oid": "e274dbe523657c7bfc4228ddbf2e6dfbbca36059"
+ },
+ {
+ "authoredDate": "2019-05-05T20:00:21Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-05-05T20:00:21Z",
+ "messageBody": "The Goldschmidt algorithm needs larger arguments than supported by the\narray.\n\nA larger array could be used, but a disassembly showed, that the array\naccess was converted to an inlined and rolled out chain of comparisons\nand assignments.",
+ "messageHeadline": "Optimize bc_num_pow10() and remove the unused bc_num_pow10 array",
+ "oid": "914a7f66807b3567fa438c8d668a040e9d2bcc4a"
+ }
+ ],
+ "createdAt": "2019-04-28T15:29:42Z",
+ "deletions": 63,
+ "files": [
+ {
+ "path": "include/num.h",
+ "additions": 33,
+ "deletions": 13
+ },
+ {
+ "path": "src/data.c",
+ "additions": 0,
+ "deletions": 19
+ },
+ {
+ "path": "src/num.c",
+ "additions": 260,
+ "deletions": 29
+ },
+ {
+ "path": "tests/bc/all.txt",
+ "additions": 2,
+ "deletions": 2
+ }
+ ],
+ "fullDatabaseId": "274187961",
+ "headRefName": "d9",
+ "headRefOid": "914a7f66807b3567fa438c8d668a040e9d2bcc4a",
+ "headRepository": {
+ "id": "MDEwOlJlcG9zaXRvcnkxODAzNzI3NzY=",
+ "name": "bc"
+ },
+ "headRepositoryOwner": {
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "name": "Stefan Eßer",
+ "login": "stesser"
+ },
+ "id": "MDExOlB1bGxSZXF1ZXN0Mjc0MTg3OTYx",
+ "isCrossRepository": true,
+ "isDraft": false,
+ "labels": [],
+ "latestReviews": [],
+ "maintainerCanModify": false,
+ "mergeCommit": null,
+ "mergeStateStatus": "DIRTY",
+ "mergeable": "CONFLICTING",
+ "mergedAt": null,
+ "mergedBy": null,
+ "milestone": null,
+ "number": 17,
+ "potentialMergeCommit": null,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "reviewDecision": "",
+ "reviewRequests": [],
+ "reviews": [],
+ "state": "CLOSED",
+ "statusCheckRollup": [],
+ "title": "Implementation of an alternate algorithm for reciprocal values",
+ "updatedAt": "2019-05-06T13:42:51Z",
+ "url": "https://github.com/gavinhoward/bc/pull/17"
+ },
+ {
+ "additions": 147,
+ "assignees": [],
+ "author": {
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "is_bot": false,
+ "login": "stesser",
+ "name": "Stefan Eßer"
+ },
+ "autoMergeRequest": null,
+ "baseRefName": "base9",
+ "baseRefOid": "c25fd61637390cc993c91258dda2c01404b37147",
+ "body": "The code is not cleaned up, but since you said your are waiting for it ...",
+ "changedFiles": 1,
+ "closed": true,
+ "closedAt": "2019-04-27T12:40:59Z",
+ "comments": [
+ {
+ "id": "MDEyOklzc3VlQ29tbWVudDQ4NzI4Mjk1Ng==",
+ "author": {
+ "login": "gavinhoward"
+ },
+ "authorAssociation": "OWNER",
+ "body": "Merged. Will get to work on it now.",
+ "createdAt": "2019-04-27T12:41:09Z",
+ "includesCreatedEdit": false,
+ "isMinimized": false,
+ "minimizedReason": "",
+ "reactionGroups": [],
+ "url": "https://github.com/gavinhoward/bc/pull/16#issuecomment-487282956",
+ "viewerDidAuthor": true
+ }
+ ],
+ "commits": [
+ {
+ "authoredDate": "2019-04-26T23:29:01Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-26T23:29:01Z",
+ "messageBody": "",
+ "messageHeadline": "Iterative division based on Newton Raphson algorithm",
+ "oid": "193f8529ad93306e39914ca7a2aa877669383f6f"
+ },
+ {
+ "authoredDate": "2019-04-27T07:16:26Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-27T07:16:26Z",
+ "messageBody": "",
+ "messageHeadline": "Merge branch 'base9' of https://github.com/gavinhoward/bc into d9",
+ "oid": "87c27b5715646eb4deec4a6795c17b4c5facc408"
+ },
+ {
+ "authoredDate": "2019-04-27T08:39:00Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-27T08:39:00Z",
+ "messageBody": "",
+ "messageHeadline": "Fix mismerge regarding nBcDig vs. places",
+ "oid": "70f118574077b6e61cae08bd576ca7fccc167786"
+ },
+ {
+ "authoredDate": "2019-04-27T08:40:14Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-27T08:40:14Z",
+ "messageBody": "…e current rdx",
+ "messageHeadline": "Fix segmentation fault that occurs if the target rdx is lower than th…",
+ "oid": "6189d95036f123d48d505bf4d4a49ba11ca30aee"
+ },
+ {
+ "authoredDate": "2019-04-27T08:42:00Z",
+ "authors": [
+ {
+ "email": "se@freebsd.org",
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "login": "stesser",
+ "name": "Stefan Esser"
+ }
+ ],
+ "committedDate": "2019-04-27T08:42:00Z",
+ "messageBody": "",
+ "messageHeadline": "Add simple debug print macro",
+ "oid": "cdda6e99974b8f9551ad1bc184b3c735afac6f4a"
+ },
+ {
+ "authoredDate": "2019-04-27T11:47:09Z",
+ "authors": [
+ {
+ "email": "root@StefanEsser.freebsd.org",
+ "id": "",
+ "login": "",
+ "name": "Charlie Root"
+ }
+ ],
+ "committedDate": "2019-04-27T11:47:09Z",
+ "messageBody": "",
+ "messageHeadline": "Fix calculation of scale in multiplications",
+ "oid": "ac244bdb3ebe6e6a2693f3d5c2ae13d7723b3517"
+ }
+ ],
+ "createdAt": "2019-04-26T23:33:21Z",
+ "deletions": 63,
+ "files": [
+ {
+ "path": "src/num.c",
+ "additions": 147,
+ "deletions": 63
+ }
+ ],
+ "fullDatabaseId": "274063201",
+ "headRefName": "d9",
+ "headRefOid": "ac244bdb3ebe6e6a2693f3d5c2ae13d7723b3517",
+ "headRepository": {
+ "id": "MDEwOlJlcG9zaXRvcnkxODAzNzI3NzY=",
+ "name": "bc"
+ },
+ "headRepositoryOwner": {
+ "id": "MDQ6VXNlcjYyNjQ1NDY=",
+ "name": "Stefan Eßer",
+ "login": "stesser"
+ },
+ "id": "MDExOlB1bGxSZXF1ZXN0Mjc0MDYzMjAx",
+ "isCrossRepository": true,
+ "isDraft": false,
+ "labels": [],
+ "latestReviews": [],
+ "maintainerCanModify": false,
+ "mergeCommit": {
+ "oid": "769fcbed7a3a0005a60254c0ed5203406b533ae3"
+ },
+ "mergeStateStatus": "DIRTY",
+ "mergeable": "CONFLICTING",
+ "mergedAt": "2019-04-27T12:40:59Z",
+ "mergedBy": {
+ "id": "MDQ6VXNlcjMxNzI2ODc=",
+ "is_bot": false,
+ "login": "gavinhoward",
+ "name": "Gavin D. Howard"
+ },
+ "milestone": null,
+ "number": 16,
+ "potentialMergeCommit": null,
+ "projectCards": [],
+ "projectItems": [],
+ "reactionGroups": [],
+ "reviewDecision": "",
+ "reviewRequests": [],
+ "reviews": [],
+ "state": "MERGED",
+ "statusCheckRollup": [],
+ "title": "Newton-Raphson based division",
+ "updatedAt": "2019-04-27T12:41:09Z",
+ "url": "https://github.com/gavinhoward/bc/pull/16"
+ }
+]
diff --git a/contrib/bc/project/issue10.md b/contrib/bc/project/issue10.md
new file mode 100644
index 000000000000..4c417f1922ab
--- /dev/null
+++ b/contrib/bc/project/issue10.md
@@ -0,0 +1,104 @@
+# "scale" not set correctly with -l when first command is a syntax error
+
+## `mathieu`
+
+I just hit a (small and unlikely to be triggered) problem when using the `-l` flag:
+
+```
+$ bc -l
+>>> 2+; # or any other syntax error it seems
+
+Parse error: bad expression
+ <stdin>:1
+
+>>> l(1000)
+6
+>>> scale
+0
+```
+
+The math library still gets loaded but `scale` doesn't get set (or gets reset)?
+
+The syntax error has to be on the first command and other kinds of errors (like say a divide by zero) don't seem to cause the problem.
+
+## `gavin`
+
+Hmm...let me investigate this and get back to you. This does seem like a bug.
+
+## `gavin`
+
+I'm not seeing the behavior. Can you send me the output of `bc -v`?
+
+## `gavin`
+
+I should also ask: what OS are you on? What version? What compiler did you use? Did you install from a package?
+
+Basically, send me as much info as you can. I would appreciate it.
+
+## `mathieu`
+
+Oh sorry yeah I should've given more details.
+
+That's on FreeBSD with the base system's bc, built with the default base compiler.
+
+On recent 12.2-STABLE:
+
+```
+$ bc -v
+bc 4.0.1
+Copyright (c) 2018-2021 Gavin D. Howard and contributors
+Report bugs at: https://git.yzena.com/gavin/bc
+
+This is free software with ABSOLUTELY NO WARRANTY.
+```
+
+Your bc is not default on 12.X yet but I enabled it with WITH_GH_BC=yes in /etc/src.conf to try it out.
+
+And on somewhat less recent 14-CURRENT:
+
+```
+$ bc -v
+bc 4.0.0
+Copyright (c) 2018-2021 Gavin D. Howard and contributors
+Report bugs at: https://git.yzena.com/gavin/bc
+
+This is free software with ABSOLUTELY NO WARRANTY.
+```
+
+Both amd64. Happens every time on both.
+
+I could give it a try on 13-STABLE too if that helps but I'd need to reboot something.
+
+The syntax error really has to be the FIRST input, even entering an empty line before it makes the problem not happen.
+
+I thought it could be an editline(3) problem since some programs end up using that pretty much only on the BSDs it seems, but that's not it.
+
+## `gavin`
+
+Yeah, my `bc` uses a custom history implementation, so it could be mine, but not `editline(3)`.
+
+I will pull up a FreeBSD VM and check it out. Sorry for the wait.
+
+## `gavin`
+
+I have confirmed the bug on FreeBSD with the port at version `4.0.1`. Since it is the port, and not the system one, I think the problem may lie with some incompatibility between my history implementation and what FreeBSD provides.
+
+This one may take me a long time to debug because I have to do it manually in the VM. Thank you for your patience.
+
+## `gavin`
+
+I found the problem!
+
+It is fixed in `299a4fd353`, but if you can pull that down and test, I would appreciate it.
+
+I will put out a release as soon as I can, and my FreeBSD contact will probably update 14-CURRENT soon thereafter. He will also update the port, but the version in 12.2 may not be updated for a bit.
+
+Feel free to reopen if the fix does not work for you.
+
+## `mathieu`
+
+Oof... yeah makes sense that an rc file could interfere with this.
+
+Yes, that fixes it here too. With that diff applied on both 12.2-STABLE and 14-CURRENT's versions. And everything else seems to still work fine too.
+
+Thanks for fixing this! You'd think it's really hard to trigger but I do enough typos and I start bc (with an alias with -l) often enough to make a quick calculation that I hit it twice and had decimals mysteriously missing before I started trying to reproduce it.
diff --git a/contrib/bc/scripts/exec-install.sh b/contrib/bc/scripts/exec-install.sh
index 581b6bd1ed24..835b7491ac66 100755
--- a/contrib/bc/scripts/exec-install.sh
+++ b/contrib/bc/scripts/exec-install.sh
@@ -2,7 +2,7 @@
#
# SPDX-License-Identifier: BSD-2-Clause
#
-# Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+# Copyright (c) 2018-2025 Gavin D. Howard and contributors.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/scripts/format.sh b/contrib/bc/scripts/format.sh
index f76aed378186..e8836c6ba1e0 100755
--- a/contrib/bc/scripts/format.sh
+++ b/contrib/bc/scripts/format.sh
@@ -2,7 +2,7 @@
#
# SPDX-License-Identifier: BSD-2-Clause
#
-# Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+# Copyright (c) 2018-2025 Gavin D. Howard and contributors.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/scripts/functions.sh b/contrib/bc/scripts/functions.sh
index 1599fea4847e..13ccbf8f3f31 100755
--- a/contrib/bc/scripts/functions.sh
+++ b/contrib/bc/scripts/functions.sh
@@ -2,7 +2,7 @@
#
# SPDX-License-Identifier: BSD-2-Clause
#
-# Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+# Copyright (c) 2018-2025 Gavin D. Howard and contributors.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/scripts/karatsuba.py b/contrib/bc/scripts/karatsuba.py
index 637887986ee8..c4e0720b3408 100755
--- a/contrib/bc/scripts/karatsuba.py
+++ b/contrib/bc/scripts/karatsuba.py
@@ -2,7 +2,7 @@
#
# SPDX-License-Identifier: BSD-2-Clause
#
-# Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+# Copyright (c) 2018-2025 Gavin D. Howard and contributors.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/scripts/link.sh b/contrib/bc/scripts/link.sh
index 772de27a08c2..4562677d7b38 100755
--- a/contrib/bc/scripts/link.sh
+++ b/contrib/bc/scripts/link.sh
@@ -2,7 +2,7 @@
#
# SPDX-License-Identifier: BSD-2-Clause
#
-# Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+# Copyright (c) 2018-2025 Gavin D. Howard and contributors.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/scripts/lint.sh b/contrib/bc/scripts/lint.sh
index 14cdc5c3afc8..611560ced31a 100755
--- a/contrib/bc/scripts/lint.sh
+++ b/contrib/bc/scripts/lint.sh
@@ -2,7 +2,7 @@
#
# SPDX-License-Identifier: BSD-2-Clause
#
-# Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+# Copyright (c) 2018-2025 Gavin D. Howard and contributors.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/scripts/locale_install.sh b/contrib/bc/scripts/locale_install.sh
index e891bf57db81..cec69d7201f4 100755
--- a/contrib/bc/scripts/locale_install.sh
+++ b/contrib/bc/scripts/locale_install.sh
@@ -2,7 +2,7 @@
#
# SPDX-License-Identifier: BSD-2-Clause
#
-# Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+# Copyright (c) 2018-2025 Gavin D. Howard and contributors.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/scripts/locale_uninstall.sh b/contrib/bc/scripts/locale_uninstall.sh
index 1bf292b801e6..274b6bcf49f1 100755
--- a/contrib/bc/scripts/locale_uninstall.sh
+++ b/contrib/bc/scripts/locale_uninstall.sh
@@ -2,7 +2,7 @@
#
# SPDX-License-Identifier: BSD-2-Clause
#
-# Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+# Copyright (c) 2018-2025 Gavin D. Howard and contributors.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/scripts/os.c b/contrib/bc/scripts/os.c
index 212a61772ccf..89c5b1b95b3f 100644
--- a/contrib/bc/scripts/os.c
+++ b/contrib/bc/scripts/os.c
@@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+ * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/scripts/release.pkg.yao b/contrib/bc/scripts/release.pkg.yao
new file mode 100644
index 000000000000..525709c28164
--- /dev/null
+++ b/contrib/bc/scripts/release.pkg.yao
@@ -0,0 +1,1410 @@
+/**
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+PROG: str = path.realpath(sys.program);
+REAL: str = path.realpath(PROG +~ "..");
+
+SCRIPTDIR: str = path.dirname(PROG);
+
+WINDOWS: bool = (host.os == "Windows");
+
+DEFOPT: str = if WINDOWS { "/D"; } else { "-D"; };
+C11OPT: str = if WINDOWS { "/std:c11"; } else { "-std=c11"; };
+C99OPT: str = if WINDOWS { "/std:c99"; } else { "-std=c99"; };
+
+/**
+ * Turns a string array into a string by joining all strings together with
+ * spaces.
+ * @param arr The array to join.
+ * @return The joined array.
+ */
+fn strv2str(arr: []str) -> str
+{
+ ret: !str = "";
+
+ for item: arr
+ {
+ if ret == ""
+ {
+ ret! = item;
+ }
+ else
+ {
+ ret! = ret +~ " " +~ item;
+ }
+ }
+
+ return ret;
+}
+
+opts: Gaml = @(gaml){
+ help: [
+ "Runs the testing part of the release process.\n"
+ "\n"
+ "Usage: ", $PROG, " [-h|--help] [<options> \n"
+ ]
+ options: {
+ no64: {
+ help: "Turn off building and testing 64-bit builds."
+ long: @no-64
+ type: @NONE
+ }
+ no128: {
+ help: "Turn off building and testing builds with 128-bit integers."
+ long: @no-128
+ type: @NONE
+ }
+ C: {
+ help: "Turn off building under Clang."
+ short: @C
+ long: @no-clang
+ type: @NONE
+ }
+ C11: {
+ help: "Turn off building under C11."
+ long: @no-c11
+ type: @NONE
+ }
+ G: {
+ help: "Turn off building under GCC."
+ short: @G
+ long: @no-gcc
+ type: @NONE
+ }
+ GEN: {
+ help: "Turn off running the gen script."
+ short: @G
+ long: @no-gen-script
+ type: @NONE
+ }
+ MAKE: {
+ help: "Turn off running the build with `configure.sh` and `make`."
+ long: @no-make
+ type: @NONE
+ }
+ RIG: {
+ help: "Turn off running the build with Rig."
+ long: @no-rig
+ type: @NONE
+ }
+ T: {
+ help: "Turn off running tests."
+ short: @T
+ long: @no-tests
+ type: @NONE
+ }
+ computed-goto: {
+ help: "Whether to test with computed goto or not."
+ long: @computed-goto
+ type: @NONE
+ }
+ e: {
+ help: "Whether to test with editline or not."
+ short: @e
+ long: @editline
+ type: @NONE
+ }
+ g: {
+ help: "Whether generate tests that are generated or not."
+ short: @g
+ long: @generate
+ type: @NONE
+ }
+ history: {
+ help: "Whether to test with history or not."
+ long: @history
+ type: @NONE
+ }
+ k: {
+ help: "Whether to run the Karatsuba tests or not."
+ short: @k
+ long: @karatsuba
+ type: @NONE
+ }
+ p: {
+ help: "Whether to run problematic tests or not."
+ short: @p
+ long: @problematic-tests
+ type: @NONE
+ }
+ r: {
+ help: "Whether to test with readline or not."
+ short: @r
+ long: @readline
+ type: @NONE
+ }
+ s: {
+ help: "Whether to run tests under sanitizers or not."
+ short: @s
+ long: @sanitizers
+ type: @NONE
+ }
+ settings: {
+ help: "Whether to test settings or not."
+ long: @settings
+ type: @NONE
+ }
+ v: {
+ help: "Whether to run under Valgrind or not."
+ short: @v
+ long: @valgrind
+ type: @NONE
+ }
+ }
+};
+
+// These are the booleans for the command-line flags.
+no64: !bool = false;
+no128: !bool = false;
+clang: !bool = true;
+c11: !bool = true;
+gcc: !bool = true;
+gen_host: !bool = true;
+run_make: !bool = !WINDOWS;
+run_rig: !bool = true;
+tests: !bool = true;
+computed_goto: !bool = false;
+editline: !bool = false;
+generated: !bool = false;
+history: !bool = false;
+karatsuba: !bool = false;
+problematic: !bool = false;
+readline: !bool = false;
+sanitizers: !bool = false;
+settings: !bool = false;
+valgrind: !bool = false;
+
+defcc: str = if clang { "clang"; } else if gcc { "gcc"; } else { "c99"; };
+defbits: usize = if no64 { usize(32); } else { usize(64); };
+
+cgoto_flags: []str = if !computed_goto { @[ "-DBC_NO_COMPUTED_GOTO" ]; };
+
+// Set some strict warning flags. Clang's -Weverything can be way too strict, so
+// we actually have to turn off some things.
+CLANG_FLAGS: []str = @[ "-Weverything", "-Wno-padded",
+ "-Wno-unsafe-buffer-usage",
+ "-Wno-poison-system-directories",
+ "-Wno-switch-default", "-Wno-unknown-warning-option",
+ "-Wno-pre-c11-compat" ] +~ cgoto_flags;
+GCC_FLAGS: []str = @[ "-Wno-clobbered" ] +~ cgoto_flags;
+
+CLANG_FLAGS_STR: str = strv2str(CLANG_FLAGS);
+
+GCC_FLAGS_STR: str = strv2str(GCC_FLAGS);
+
+// Common CFLAGS.
+CFLAGS: []str = @[ "-Wall", "-Wextra", "-Werror", "-pedantic" ];
+
+CFLAGS_STR: str = strv2str(CFLAGS);
+
+// Common debug and release flags.
+DEBUG_CFLAGS: []str = CFLAGS +~ @[ "-fno-omit-frame-pointer" ];
+DEBUG_CFLAGS_STR: str = CFLAGS_STR +~ " -fno-omit-frame-pointer";
+RELEASE_CFLAGS: []str = CFLAGS +~ @[ "-DNDEBUG" ];
+RELEASE_CFLAGS_STR: str = CFLAGS_STR +~ " -DNDEBUG";
+
+/**
+ * Print a header with a message. This is just to make it easy to track
+ * progress.
+ * @param msg The message to print in the header.
+ */
+fn header(msg: str) -> void
+{
+ io.eprint("\n*******************\n");
+ io.eprint(msg);
+ io.eprint("\n*******************\n\n");
+}
+
+/**
+ * An easy way to call `make`.
+ * @param args The arguments to pass to `make`, if any.
+ */
+fn do_make(args: []str) -> void
+{
+ $ make -j64 %(args);
+}
+
+/**
+ * An easy way to call Rig.
+ * @param args The arguments to pass to Rig, if any.
+ */
+fn do_rig(args: []str) -> void
+{
+ $ rig -j64 %(args);
+}
+
+/**
+ * Runs `configure.sh`.
+ * @param cflags The C compiler flags.
+ * @param cc The C compiler.
+ * @param flags The flags to pass to `configure.sh` itself.
+ * @param gen The setting for `GEN_HOST`.
+ * @param long_bit The number of bits in longs.
+ */
+fn configure(
+ cflags: str,
+ cc: str,
+ flags: str,
+ gen: bool,
+ long_bit: usize
+) -> void
+{
+ if !run_make
+ {
+ sys.panic("Cannot run `configure.sh` or make");
+ }
+
+ extra_flags: str =
+ if !generated
+ {
+ if !problematic { "-GP"; } else { "-G"; }
+ }
+ else if !problematic
+ {
+ "-P";
+ };
+
+ extra_cflags: str =
+ if cc == "clang"
+ {
+ // We need to quiet this warning from Clang because the configure.sh
+ // docs have this warning, so people should know. Also, I want this
+ // script to work.
+ CLANG_FLAGS_STR +~ (if !gen_host { " -Wno-overlength-strings"; });
+ }
+ else if cc == "gcc"
+ {
+ // We need to quiet this warning from GCC because the configure.sh docs
+ // have this warning, so people should know. Also, I want this script to
+ // work.
+ GCC_FLAGS_STR +~ (if !gen_host { "-Wno-overlength-strings"; });
+ };
+
+ all_flags: str = flags +~ " " +~ extra_flags;
+ all_cflags: str = cflags +~ " " +~ extra_cflags;
+
+ hdr := "Running configure.sh " +~ all_flags +~
+ "..." +~
+ "\n CC=\"" +~ cc +~ "\"\n" +~
+ "\n CFLAGS=\"" +~ all_cflags +~ "\"\n" +~
+ "\n LONG_BIT=" +~ str(long_bit) +~
+ "\n GEN_HOST=" +~ str(gen);
+
+ // Print the header and do the job.
+ header(hdr);
+
+ env.set env.str("CFLAGS", str(cflags +~ extra_cflags)),
+ env.str("CC", cc), env.str("GEN_HOST", str(gen)),
+ env.str("LONG_BIT", str(long_bit))
+ {
+ $ @(path.join(REAL, "configure.sh")) $flags $extra_flags > /dev/null
+ !> /dev/null;
+ }
+}
+
+/**
+ * Build with `make`. This function also captures and outputs any warnings if
+ * they exist because as far as I am concerned, warnings are not acceptable for
+ * release.
+ * @param cflags The C compiler flags.
+ * @param cc The C compiler.
+ * @param flags The flags to pass to `configure.sh` itself.
+ * @param gen The setting for `GEN_HOST`.
+ * @param long_bit The number of bits in longs.
+ */
+fn build_make(
+ cflags: str,
+ cc: str,
+ flags: str,
+ gen: bool,
+ long_bit: usize
+) -> void
+{
+ if !run_make
+ {
+ sys.panic("Cannot run `configure.sh` or make");
+ }
+
+ configure(cflags, cc, flags, gen, long_bit);
+
+ hdr := "Building with `make`...\n CC=" +~ cc +~
+ "\n CFLAGS=\"" +~ cflags +~
+ "\n LONG_BIT=" +~ str(long_bit) +~
+ "\n GEN_HOST=" +~ str(gen);
+
+ header(hdr);
+
+ res := $ make;
+
+ if res.stderr.len != 0
+ {
+ io.eprint(cc +~ "generated warning(s):\n\n");
+ io.eprint(str(res.stderr));
+ sys.exit(1);
+ }
+
+ if res.exitcode != 0
+ {
+ sys.exit(res.exitcode);
+ }
+}
+
+/**
+ * Build with Rig. This function also captures and outputs any warnings if they
+ * exist because as far as I am concerned, warnings are not acceptable for
+ * release.
+ * @param cflags The C compiler flags.
+ * @param cc The C compiler.
+ * @param flags The flags to pass to `configure.sh` itself.
+ * @param long_bit The number of bits in longs.
+ */
+fn build_rig(
+ cflags: []str,
+ cc: str,
+ flags: []str,
+ long_bit: usize
+) -> void
+{
+ if !run_rig
+ {
+ sys.panic("Cannot run Rig");
+ }
+
+ cflags_str: str = strv2str(cflags);
+
+ hdr := "Building with Rig...\n CC=" +~ cc +~
+ "\n CFLAGS=\"" +~ cflags_str +~
+ "\n LONG_BIT=" +~ str(long_bit);
+
+ header(hdr);
+
+ res := $ rig;
+
+ if res.stderr.len != 0
+ {
+ io.eprint(cc +~ "generated warning(s):\n\n");
+ io.eprint(str(res.stderr));
+ sys.exit(1);
+ }
+
+ if res.exitcode != 0
+ {
+ sys.exit(res.exitcode);
+ }
+}
+
+/**
+ * Run tests with `make`.
+ * @param args Any arguments to pass to `make`, if any.
+ */
+fn run_test_make(args: []str) -> void
+{
+ if !run_make
+ {
+ sys.panic("Cannot run `configure.sh` or make");
+ }
+
+ header("Running `make` tests");
+
+ if args.len > 0
+ {
+ do_make(args);
+ }
+ else
+ {
+ do_make(@[ "test" ]);
+
+ if history
+ {
+ do_make(@[ "test_history" ]);
+ }
+ }
+}
+
+/**
+ * Run tests with Rig.
+ * @param args Any arguments to pass to Rig, if any.
+ */
+fn run_test_rig(args: []str) -> void
+{
+ if !run_rig
+ {
+ sys.panic("Cannot run Rig");
+ }
+
+ header("Running Rig tests");
+
+ if args.len > 0
+ {
+ do_rig(args);
+ }
+ else
+ {
+ do_rig(@[ "test" ]);
+
+ if history
+ {
+ do_rig(@[ "test_history" ]);
+ }
+ }
+}
+
+/**
+ * Builds and runs tests using `make` with both calculators, then bc only, then
+ * dc only. If `tests` is false, then it just does the builds.
+ * @param cflags The C compiler flags.
+ * @param cc The C compiler.
+ * @param flags The flags to pass to `configure.sh` itself.
+ * @param gen The setting for `GEN_HOST`.
+ * @param long_bit The number of bits in longs.
+ */
+fn run_config_tests_make(
+ cflags: str,
+ cc: str,
+ flags: str,
+ gen: bool,
+ long_bit: usize,
+) -> void
+{
+ if !run_make
+ {
+ sys.panic("Cannot run `configure.sh` or make");
+ }
+
+ header_start: str =
+ if tests
+ {
+ "Running tests with configure flags and `make`";
+ }
+ else
+ {
+ "Building with configure flags and `make`";
+ };
+
+ header("\"" +~ header_start +~ "\" ...\n" +~
+ " CC=" +~ cc +~ "\n" +~
+ " CFLAGS=\"" +~ cflags +~ "\"\n" +~
+ " LONG_BIT=" +~ str(long_bit) +~ "\n" +~
+ " GEN_HOST=" +~ str(gen));
+
+ build_make(cflags, cc, flags, gen, long_bit);
+
+ if tests
+ {
+ run_test_make(@[]);
+ }
+
+ do_make(@[ "clean" ]);
+
+ build_make(cflags, cc, flags +~ " -b", gen, long_bit);
+
+ if tests
+ {
+ run_test_make(@[]);
+ }
+
+ do_make(@[ "clean" ]);
+
+ build_make(cflags, cc, flags +~ " -d", gen, long_bit);
+
+ if tests
+ {
+ run_test_make(@[]);
+ }
+
+ do_make(@[ "clean" ]);
+}
+
+/**
+ * Builds and runs tests using Rig with both calculators, then bc only, then dc
+ * only. If `tests` is false, then it just does the builds.
+ * @param cflags The C compiler flags.
+ * @param cc The C compiler.
+ * @param flags The flags to pass to Rig itself.
+ * @param long_bit The number of bits in longs.
+ */
+fn run_config_tests_rig(
+ cflags: []str,
+ cc: str,
+ flags: []str,
+ long_bit: usize,
+) -> void
+{
+ if !run_rig
+ {
+ sys.panic("Cannot run Rig");
+ }
+
+ header_start: str =
+ if tests
+ {
+ "Running tests with Rig";
+ }
+ else
+ {
+ "Building with Rig";
+ };
+
+ header("\"" +~ header_start +~ "\" ...\n" +~
+ " CC=" +~ cc +~ "\n" +~
+ " CFLAGS=\"" +~ strv2str(cflags) +~ "\"\n" +~
+ " flags=\"" +~ strv2str(flags) +~ "\"\n" +~
+ " LONG_BIT=" +~ str(long_bit));
+
+ build_rig(cflags, cc, flags, long_bit);
+
+ if tests
+ {
+ run_test_rig(@[]);
+ }
+
+ do_rig(@[ "clean" ]);
+
+ build_rig(cflags, cc, flags +~ @[ "-Dbuild_mode=bc" ], long_bit);
+
+ if tests
+ {
+ run_test_rig(@[]);
+ }
+
+ do_rig(@[ "clean" ]);
+
+ build_rig(cflags, cc, flags +~ @[ "-Dbuild_mode=dc" ], long_bit);
+
+ if tests
+ {
+ run_test_rig(@[]);
+ }
+
+ do_rig(@[ "clean" ]);
+}
+
+/**
+ * Builds and runs tests using `run_config_tests_make()` with both calculators,
+ * then bc only, then dc only. If `tests` is false, then it just does the
+ * builds.
+ * @param cflags The C compiler flags.
+ * @param cc The C compiler.
+ * @param flags The flags to pass to `configure.sh` itself.
+ */
+fn run_config_series_make(
+ cflags: str,
+ cc: str,
+ flags: str,
+) -> void
+{
+ if !run_make
+ {
+ sys.panic("Cannot run `configure.sh` or make");
+ }
+
+ if !no64
+ {
+ if !no128
+ {
+ run_config_tests_make(cflags, cc, flags, true, usize(64));
+ }
+
+ if gen_host
+ {
+ run_config_tests_make(cflags, cc, flags, false, usize(64));
+ }
+
+ run_config_tests_make(cflags +~ " " +~ DEFOPT +~ "BC_RAND_BUILTIN=0",
+ cc, flags, true, usize(64));
+
+ // Test Editline if history is not turned off.
+ if editline && !(flags contains "H")
+ {
+ run_config_tests_make(cflags +~ " " +~ DEFOPT +~
+ "BC_RAND_BUILTIN=0",
+ cc, flags +~ " -e", true, usize(64));
+ }
+
+ // Test Readline if history is not turned off.
+ if readline && !(flags contains "H")
+ {
+ run_config_tests_make(cflags +~ " " +~ DEFOPT +~
+ "BC_RAND_BUILTIN=0",
+ cc, flags +~ " -r", true, usize(64));
+ }
+ }
+
+ run_config_tests_make(cflags, cc, flags, true, usize(32));
+
+ if gen_host
+ {
+ run_config_tests_make(cflags, cc, flags, false, usize(32));
+ }
+}
+
+/**
+ * Builds and runs tests using `run_config_tests_rig()` with both calculators,
+ * then bc only, then dc only. If `tests` is false, then it just does the
+ * builds.
+ * @param cflags The C compiler flags.
+ * @param cc The C compiler.
+ * @param flags The flags to pass to Rig itself.
+ */
+fn run_config_series_rig(
+ cflags: []str,
+ cc: str,
+ flags: []str,
+) -> void
+{
+ if !run_rig
+ {
+ sys.panic("Cannot run Rig");
+ }
+
+ if !no64
+ {
+ if !no128
+ {
+ run_config_tests_rig(cflags, cc, flags, usize(64));
+ }
+
+ run_config_tests_rig(cflags +~ @[ DEFOPT +~ "BC_RAND_BUILTIN=0" ], cc,
+ flags, usize(64));
+
+ // Test Editline if history is not turned off.
+ if editline && !(flags contains "-Dhistory=none")
+ {
+ run_config_tests_rig(cflags +~ @[ DEFOPT +~ "BC_RAND_BUILTIN=0" ],
+ cc, flags +~ @[ "-Dhistory=editline" ],
+ usize(64));
+ }
+
+ // Test Readline if history is not turned off.
+ if readline && !(flags contains "-Dhistory=none")
+ {
+ run_config_tests_rig(cflags +~ @[ DEFOPT +~ "BC_RAND_BUILTIN=0" ],
+ cc, flags +~ @[ "-Dhistory=readline" ],
+ usize(64));
+ }
+ }
+
+ run_config_tests_rig(cflags, cc, flags, usize(32));
+}
+
+/**
+ * Builds and runs tests with each setting combo running
+ * `run_config_series_make()`. If `tests` is false, it just does the builds.
+ * @param cflags The C compiler flags.
+ * @param cc The C compiler.
+ * @param flags The flags to pass to `configure.sh` itself.
+ */
+fn run_settings_series_make(
+ cflags: str,
+ cc: str,
+ flags: str,
+) -> void
+{
+ if !run_make
+ {
+ sys.panic("Cannot run `configure.sh` or make");
+ }
+
+ if settings
+ {
+ settings_path: str = path.join(SCRIPTDIR, "release_settings_make.txt");
+ settings_txt: str = io.read_file(settings_path);
+ lines: []str = settings_txt.split("\n");
+
+ for line: lines
+ {
+ run_config_series_make(cflags, cc, flags +~ " " +~ line);
+ }
+ }
+ else
+ {
+ run_config_series_make(cflags, cc, flags);
+ }
+}
+
+/**
+ * Builds and runs tests with each setting combo running
+ * `run_config_series_rig()`. If `tests` is false, it just does the builds.
+ * @param cflags The C compiler flags.
+ * @param cc The C compiler.
+ * @param flags The flags to pass to Rig itself.
+ */
+fn run_settings_series_rig(
+ cflags: []str,
+ cc: str,
+ flags: []str,
+) -> void
+{
+ if !run_rig
+ {
+ sys.panic("Cannot run Rig");
+ }
+
+ if settings
+ {
+ settings_path: str = path.join(SCRIPTDIR, "release_settings_rig.txt");
+ settings_txt: str = io.read_file(settings_path);
+ lines: []str = settings_txt.split("\n");
+
+ for line: lines
+ {
+ opts_list: []str =
+ for opt: line.split(" ")
+ {
+ DEFOPT +~ opt;
+ };
+
+ run_config_series_rig(cflags, cc, flags +~ opts_list);
+ }
+ }
+ else
+ {
+ run_config_series_rig(cflags, cc, flags);
+ }
+}
+
+/**
+ * Builds and runs tests with each build type running
+ * `run_settings_series_make()`. If `tests` is false, it just does the builds.
+ * @param cflags The C compiler flags.
+ * @param cc The C compiler.
+ * @param flags The flags to pass to `configure.sh` itself.
+ */
+fn run_test_series_make(
+ cflags: str,
+ cc: str,
+ flags: str,
+) -> void
+{
+ if !run_make
+ {
+ sys.panic("Cannot run `configure.sh` or make");
+ }
+
+ series_flags: []str = @[ "E", "H", "N", "EH", "EN", "HN", "EHN" ];
+
+ run_settings_series_make(cflags, cc, flags);
+
+ for sflag: series_flags
+ {
+ run_settings_series_make(cflags, cc, flags +~ " -" +~ sflag);
+ }
+}
+
+/**
+ * Builds and runs tests with each build type running
+ * `run_settings_series_rig()`. If `tests` is false, it just does the builds.
+ * @param cflags The C compiler flags.
+ * @param cc The C compiler.
+ * @param flags The flags to pass to Rig itself.
+ */
+fn run_test_series_rig(
+ cflags: []str,
+ cc: str,
+ flags: []str,
+) -> void
+{
+ if !run_rig
+ {
+ sys.panic("Cannot run Rig");
+ }
+
+ series_path: str = path.join(SCRIPTDIR, "release_flags_rig.txt");
+ series_txt: str = io.read_file(series_path);
+ series_flags: []str = series_txt.split("\n");
+
+ run_settings_series_rig(cflags, cc, flags);
+
+ for line: series_flags
+ {
+ opts_list: []str =
+ for opt: line.split(" ")
+ {
+ DEFOPT +~ opt;
+ };
+
+ run_settings_series_rig(cflags, cc, flags +~ opts_list);
+ }
+}
+
+/**
+ * Builds and runs tests for `bcl` with `make`. If `tests` is false, it just
+ * does the builds.
+ * @param cflags The C compiler flags.
+ * @param cc The C compiler.
+ * @param flags The flags to pass to `configure.sh` itself.
+ */
+fn run_lib_tests_make(
+ cflags: str,
+ cc: str,
+ flags: str,
+) -> void
+{
+ if !run_make
+ {
+ sys.panic("Cannot run `configure.sh` or make");
+ }
+
+ build_make(cflags +~ " -a", cc, flags, true, usize(64));
+
+ if tests
+ {
+ run_test_make(@[ "test" ]);
+ }
+
+ build_make(cflags +~ " -a", cc, flags, true, usize(32));
+
+ if tests
+ {
+ run_test_make(@[ "test" ]);
+ }
+}
+
+/**
+ * Builds and runs tests for `bcl` with Rig. If `tests` is false, it just does
+ * the builds.
+ * @param cflags The C compiler flags.
+ * @param cc The C compiler.
+ * @param flags The flags to pass to Rig itself.
+ */
+fn run_lib_tests_rig(
+ cflags: []str,
+ cc: str,
+ flags: []str,
+) -> void
+{
+ if !run_rig
+ {
+ sys.panic("Cannot run Rig");
+ }
+
+ build_rig(cflags, cc, flags +~ @[ "-Dbuild_mode=library" ], usize(64));
+
+ if tests
+ {
+ run_test_rig(@[ "test" ]);
+ }
+
+ build_rig(cflags, cc, flags +~ @[ "-Dbuild_mode=library" ], usize(32));
+
+ if tests
+ {
+ run_test_rig(@[ "test" ]);
+ }
+}
+
+/**
+ * Builds and runs tests under C99, then C11, if requested, using
+ * `run_test_series_make()`. If run_tests is false, it just does the builds.
+ * @param cflags The C compiler flags.
+ * @param cc The C compiler.
+ * @param flags The flags to pass to Rig itself.
+ */
+fn run_tests_make(
+ cflags: str,
+ cc: str,
+ flags: str,
+) -> void
+{
+ if !run_make
+ {
+ sys.panic("Cannot run `configure.sh` or make");
+ }
+
+ run_test_series_make(cflags +~ " " +~ C99OPT, cc, flags);
+
+ if c11
+ {
+ run_test_series_make(cflags +~ " " +~ C11OPT, cc, flags);
+ }
+}
+
+/**
+ * Builds and runs tests under C99, then C11, if requested, using
+ * `run_test_series_rig()`. If run_tests is false, it just does the builds.
+ * @param cflags The C compiler flags.
+ * @param cc The C compiler.
+ * @param flags The flags to pass to Rig itself.
+ */
+fn run_tests_rig(
+ cflags: []str,
+ cc: str,
+ flags: []str,
+) -> void
+{
+ if !run_rig
+ {
+ sys.panic("Cannot run Rig");
+ }
+
+ run_test_series_rig(cflags +~ @[ C99OPT ], cc, flags);
+
+ if c11
+ {
+ run_test_series_rig(cflags +~ @[ C11OPT ], cc, flags);
+ }
+}
+
+/**
+ * Runs the Karatsuba tests with `make`.
+ */
+fn karatsuba_make() -> void
+{
+ if !run_make
+ {
+ sys.panic("Cannot run `configure.sh` or make");
+ }
+
+ header("Running Karatsuba tests");
+
+ do_make(@[ "karatsuba_test" ]);
+}
+
+/**
+ * Runs the Karatsuba tests with Rig.
+ */
+fn karatsuba_rig() -> void
+{
+ if !run_rig
+ {
+ sys.panic("Cannot run Rig");
+ }
+
+ header("Running Karatsuba tests");
+
+ build_rig(RELEASE_CFLAGS, defcc, @[ "-O3" ], usize(64));
+
+ do_rig(@[ "karatsuba_test" ]);
+}
+
+/**
+ * Builds with `make` and runs under valgrind. It runs both, bc only, then dc
+ * only, then the library.
+ */
+fn vg_make() -> void
+{
+ if !run_make
+ {
+ sys.panic("Cannot run `configure.sh` or make");
+ }
+
+ header("Running Valgrind under `make`");
+
+ build_make(DEBUG_CFLAGS_STR +~ " -std=c99", "gcc", "-O3 -gv", true,
+ defbits);
+ run_test_make(@[ "test" ]);
+
+ do_make(@[ "clean_config" ]);
+
+ build_make(DEBUG_CFLAGS_STR +~ " -std=c99", "gcc", "-O3 -gvb", true,
+ defbits);
+ run_test_make(@[ "test" ]);
+
+ do_make(@[ "clean_config" ]);
+
+ build_make(DEBUG_CFLAGS_STR +~ " -std=c99", "gcc", "-O3 -gvd", true,
+ defbits);
+ run_test_make(@[ "test" ]);
+
+ do_make(@[ "clean_config" ]);
+
+ build_make(DEBUG_CFLAGS_STR +~ " -std=c99", "gcc", "-O3 -gva", true,
+ defbits);
+ run_test_make(@[ "test" ]);
+
+ do_make(@[ "clean_config" ]);
+}
+
+fn vg_rig() -> void
+{
+ if !run_rig
+ {
+ sys.panic("Cannot run Rig");
+ }
+
+ header("Running Valgrind under Rig");
+
+ build_rig(DEBUG_CFLAGS +~ @[ "-std=c99" ], "gcc", @[ "-O3", "-gv" ],
+ defbits);
+ run_test_rig(@[ "test" ]);
+
+ do_rig(@[ "clean_config" ]);
+
+ build_rig(DEBUG_CFLAGS +~ @[ "-std=c99" ], "gcc", @[ "-O3", "-gvb" ],
+ defbits);
+ run_test_rig(@[ "test" ]);
+
+ do_rig(@[ "clean_config" ]);
+
+ build_rig(DEBUG_CFLAGS +~ @[ "-std=c99" ], "gcc", @[ "-O3", "-gvd" ],
+ defbits);
+ run_test_rig(@[ "test" ]);
+
+ do_rig(@[ "clean_config" ]);
+
+ build_rig(DEBUG_CFLAGS +~ @[ "-std=c99" ], "gcc", @[ "-O3", "-gva" ],
+ defbits);
+ run_test_rig(@[ "test" ]);
+
+ do_rig(@[ "clean_config" ]);
+}
+
+/**
+ * Builds the debug series with `make` and runs the tests if `tests` allows.
+ * @param cc The C compiler.
+ */
+fn debug_make(
+ cc: str,
+) -> void
+{
+ if !run_make
+ {
+ sys.panic("Cannot run `configure.sh` or make");
+ }
+
+ if cc == "clang" && sanitizers
+ {
+ run_tests_make(DEBUG_CFLAGS_STR +~ " -fsanitize=undefined", cc, "-mg");
+ }
+ else
+ {
+ run_tests_make(DEBUG_CFLAGS_STR, cc, "-g");
+ }
+}
+
+/**
+ * Builds the release series with `make` and runs the tests if `tests` allows.
+ * @param cc The C compiler.
+ */
+fn release_make(
+ cc: str,
+) -> void
+{
+ if !run_make
+ {
+ sys.panic("Cannot run `configure.sh` or make");
+ }
+
+ run_tests_make(RELEASE_CFLAGS_STR, cc, "-O3");
+ run_lib_tests_make(RELEASE_CFLAGS_STR, cc, "-O3");
+}
+
+/**
+ * Builds the release debug series with `make` and runs the tests if `tests`
+ * allows.
+ * @param cc The C compiler.
+ */
+fn reldebug_make(
+ cc: str,
+) -> void
+{
+ if !run_make
+ {
+ sys.panic("Cannot run `configure.sh` or make");
+ }
+
+ if cc == "clang" && sanitizers
+ {
+ run_tests_make(DEBUG_CFLAGS_STR +~ " -fsanitize=address", cc, "-mgO3");
+ run_tests_make(DEBUG_CFLAGS_STR +~ " -fsanitize=memory", cc, "-mgO3");
+ }
+ else
+ {
+ run_tests_make(DEBUG_CFLAGS_STR, cc, "-gO3");
+ }
+
+ if cc == "clang" && sanitizers
+ {
+ run_lib_tests_make(DEBUG_CFLAGS_STR +~ " -fsanitize=address", cc,
+ "-mgO3");
+ run_lib_tests_make(DEBUG_CFLAGS_STR +~ " -fsanitize=memory", cc,
+ "-mgO3");
+ }
+ else
+ {
+ run_lib_tests_make(DEBUG_CFLAGS_STR, cc, "-gO3");
+ }
+}
+
+/**
+ * Builds the min size release with `make` and runs the tests if `tests` allows.
+ * @param cc The C compiler.
+ */
+fn minsize_make(
+ cc: str,
+) -> void
+{
+ if !run_make
+ {
+ sys.panic("Cannot run `configure.sh` or make");
+ }
+
+ run_tests_make(RELEASE_CFLAGS_STR, cc, "-Os");
+ run_lib_tests_make(RELEASE_CFLAGS_STR, cc, "-Os");
+}
+
+/**
+ * Builds all sets with `make`: debug, release, release debug, and min size, and
+ * runs the tests if `tests` allows.
+ * @param cc The C compiler.
+ */
+fn build_set_make(
+ cc: str,
+) -> void
+{
+ if !run_make
+ {
+ sys.panic("Cannot run `configure.sh` or make");
+ }
+
+ debug_make(cc);
+ release_make(cc);
+ reldebug_make(cc);
+ minsize_make(cc);
+
+ if karatsuba
+ {
+ karatsuba_make();
+ }
+
+ if valgrind
+ {
+ vg_make();
+ }
+}
+
+/**
+ * Builds the debug series with Rig and runs the tests if `tests` allows.
+ * @param cc The C compiler.
+ */
+fn debug_rig(
+ cc: str,
+) -> void
+{
+ if !run_rig
+ {
+ sys.panic("Cannot run Rig");
+ }
+
+ if cc == "clang" && sanitizers
+ {
+ run_tests_rig(DEBUG_CFLAGS +~ @[ " -fsanitize=undefined" ], cc,
+ @[ "-Dmemcheck=1", "-Ddebug=1" ]);
+ }
+ else
+ {
+ run_tests_rig(DEBUG_CFLAGS, cc, @[ "-Ddebug=1" ]);
+ }
+}
+
+/**
+ * Builds the release series with Rig and runs the tests if `tests` allows.
+ * @param cc The C compiler.
+ */
+fn release_rig(
+ cc: str,
+) -> void
+{
+ if !run_rig
+ {
+ sys.panic("Cannot run Rig");
+ }
+
+ run_tests_rig(RELEASE_CFLAGS, cc, @[ "-Doptimization=3" ]);
+ run_lib_tests_rig(RELEASE_CFLAGS, cc, @[ "-Doptimization=3" ]);
+}
+
+/**
+ * Builds the release debug series with Rig and runs the tests if `tests`
+ * allows.
+ * @param cc The C compiler.
+ */
+fn reldebug_rig(
+ cc: str,
+) -> void
+{
+ if !run_rig
+ {
+ sys.panic("Cannot run Rig");
+ }
+
+ if cc == "clang" && sanitizers
+ {
+ run_tests_rig(DEBUG_CFLAGS +~ @[ " -fsanitize=address" ], cc,
+ @[ "-Dmemcheck=1", "-Ddebug=1", "-Doptimization=3" ]);
+ run_tests_rig(DEBUG_CFLAGS +~ @[ " -fsanitize=memory" ], cc,
+ @[ "-Dmemcheck=1", "-Ddebug=1", "-Doptimization=3" ]);
+ }
+ else
+ {
+ run_tests_rig(DEBUG_CFLAGS, cc, @[ "-gO3" ]);
+ }
+
+ if cc == "clang" && sanitizers
+ {
+ run_lib_tests_rig(DEBUG_CFLAGS +~ @[ " -fsanitize=address" ], cc,
+ @[ "-Dmemcheck=1", "-Ddebug=1", "-Doptimization=3" ]);
+ run_lib_tests_rig(DEBUG_CFLAGS +~ @[ " -fsanitize=memory" ], cc,
+ @[ "-Dmemcheck=1", "-Ddebug=1", "-Doptimization=3" ]);
+ }
+ else
+ {
+ run_lib_tests_rig(DEBUG_CFLAGS, cc,
+ @[ "-Ddebug=1", "-Doptimization=3" ]);
+ }
+}
+
+/**
+ * Builds the min size release with Rig and runs the tests if `tests` allows.
+ * @param cc The C compiler.
+ */
+fn minsize_rig(
+ cc: str,
+) -> void
+{
+ if !run_rig
+ {
+ sys.panic("Cannot run Rig");
+ }
+
+ run_tests_rig(RELEASE_CFLAGS, cc, @[ "-Doptimization=s" ]);
+ run_lib_tests_rig(RELEASE_CFLAGS, cc, @[ "-Doptimization=s" ]);
+}
+
+/**
+ * Builds all sets with Rig: debug, release, release debug, and min size, and
+ * runs the tests if `tests` allows.
+ * @param cc The C compiler.
+ */
+fn build_set_rig(
+ cc: str,
+) -> void
+{
+ if !run_rig
+ {
+ sys.panic("Cannot run Rig");
+ }
+
+ debug_rig(cc);
+ release_rig(cc);
+ reldebug_rig(cc);
+ minsize_rig(cc);
+
+ if karatsuba
+ {
+ karatsuba_rig();
+ }
+
+ if valgrind
+ {
+ vg_rig();
+ }
+}
+
+/**
+ * Builds all sets with `make` under all compilers.
+ */
+fn build_sets_make() -> void
+{
+ header("Running math library under --standard with Rig");
+
+ build_make(DEBUG_CFLAGS_STR +~ " -std=c99", defcc, "-g", true,
+ defbits);
+
+ $ bin/bc -ls << @("quit\n");
+
+ do_rig(@[ "clean_tests" ]);
+
+ if clang
+ {
+ build_set_make("clang");
+ }
+
+ if gcc
+ {
+ build_set_make("gcc");
+ }
+}
+
+/**
+ * Builds all sets with Rig under all compilers.
+ */
+fn build_sets_rig() -> void
+{
+ header("Running math library under --standard with Rig");
+
+ build_rig(DEBUG_CFLAGS +~ @[ "-std=c99" ], defcc, @[ "-Ddebug=1" ],
+ defbits);
+
+ $ build/bc -ls << @("quit\n");
+
+ do_rig(@[ "clean_tests" ]);
+
+ if clang
+ {
+ build_set_rig("clang");
+ }
+
+ if gcc
+ {
+ build_set_rig("gcc");
+ }
+}
+
+fn build_sets() -> void
+{
+ if !run_make
+ {
+ build_sets_rig();
+ }
+ else if !run_rig
+ {
+ build_sets_make();
+ }
+ else
+ {
+ parallel.map @[ "rig", "make" ]:
+ (builder: str) -> void
+ {
+ if builder == "rig"
+ {
+ build_sets_rig();
+ }
+ else if builder == "make"
+ {
+ build_sets_make();
+ }
+ else
+ {
+ sys.panic("Bad builder: " +~ builder +~ "\n");
+ }
+ }
+ }
+
+ io.eprint("\nTests successful.\n");
+}
diff --git a/contrib/bc/scripts/sqrt_frac_guess.bc b/contrib/bc/scripts/sqrt_frac_guess.bc
index acbcb368d2de..4591e95a216f 100644
--- a/contrib/bc/scripts/sqrt_frac_guess.bc
+++ b/contrib/bc/scripts/sqrt_frac_guess.bc
@@ -2,7 +2,7 @@
#
# SPDX-License-Identifier: BSD-2-Clause
#
-# Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+# Copyright (c) 2018-2025 Gavin D. Howard and contributors.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/scripts/sqrt_int_guess.bc b/contrib/bc/scripts/sqrt_int_guess.bc
index 925b7af7e103..9fabea874aeb 100644
--- a/contrib/bc/scripts/sqrt_int_guess.bc
+++ b/contrib/bc/scripts/sqrt_int_guess.bc
@@ -2,7 +2,7 @@
#
# SPDX-License-Identifier: BSD-2-Clause
#
-# Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+# Copyright (c) 2018-2025 Gavin D. Howard and contributors.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/scripts/sqrt_random.bc b/contrib/bc/scripts/sqrt_random.bc
index 1f58c2e30c5d..08eeddab07a3 100644
--- a/contrib/bc/scripts/sqrt_random.bc
+++ b/contrib/bc/scripts/sqrt_random.bc
@@ -2,7 +2,7 @@
#
# SPDX-License-Identifier: BSD-2-Clause
#
-# Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+# Copyright (c) 2018-2025 Gavin D. Howard and contributors.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/scripts/sqrt_random.sh b/contrib/bc/scripts/sqrt_random.sh
index e107ef532f6e..0f7013da0e0e 100755
--- a/contrib/bc/scripts/sqrt_random.sh
+++ b/contrib/bc/scripts/sqrt_random.sh
@@ -2,7 +2,7 @@
#
# SPDX-License-Identifier: BSD-2-Clause
#
-# Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+# Copyright (c) 2018-2025 Gavin D. Howard and contributors.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/src/args.c b/contrib/bc/src/args.c
index 6eba802d34ac..71a340bce1d9 100644
--- a/contrib/bc/src/args.c
+++ b/contrib/bc/src/args.c
@@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+ * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/src/bc.c b/contrib/bc/src/bc.c
index 572e42b1a16d..3e324cfb8ba0 100644
--- a/contrib/bc/src/bc.c
+++ b/contrib/bc/src/bc.c
@@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+ * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/src/bc_fuzzer.c b/contrib/bc/src/bc_fuzzer.c
index 7d7b3292b727..9c68d6acbd10 100644
--- a/contrib/bc/src/bc_fuzzer.c
+++ b/contrib/bc/src/bc_fuzzer.c
@@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+ * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -36,7 +36,6 @@
#include <setjmp.h>
#include <string.h>
-#include <version.h>
#include <status.h>
#include <ossfuzz.h>
#include <vm.h>
diff --git a/contrib/bc/src/bc_lex.c b/contrib/bc/src/bc_lex.c
index f83eaf731622..c53d583a0183 100644
--- a/contrib/bc/src/bc_lex.c
+++ b/contrib/bc/src/bc_lex.c
@@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+ * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/src/bc_parse.c b/contrib/bc/src/bc_parse.c
index cf4398709e58..f66e392a9e99 100644
--- a/contrib/bc/src/bc_parse.c
+++ b/contrib/bc/src/bc_parse.c
@@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+ * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/src/data.c b/contrib/bc/src/data.c
index bb1a6796f752..00f692a4599c 100644
--- a/contrib/bc/src/data.c
+++ b/contrib/bc/src/data.c
@@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+ * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -70,8 +70,8 @@ const uchar dc_sig_msg_len = (uchar) (sizeof(dc_sig_msg) - 1);
/// The copyright banner.
const char bc_copyright[] =
- "Copyright (c) 2018-2024 Gavin D. Howard and contributors\n"
- "Report bugs at: https://git.gavinhoward.com/gavin/bc\n\n"
+ "Copyright (c) 2018-2025 Gavin D. Howard and contributors\n"
+ "Report bugs at: https://github.com/gavinhoward/bc\n\n"
"This is free software with ABSOLUTELY NO WARRANTY.\n";
// clang-format on
@@ -850,51 +850,51 @@ const char bc_parse_one[2] = "1";
/// A list of keywords for bc. This needs to be updated if keywords change.
const BcLexKeyword bc_lex_kws[] = {
- BC_LEX_KW_ENTRY("auto", 4, true),
- BC_LEX_KW_ENTRY("break", 5, true),
- BC_LEX_KW_ENTRY("continue", 8, false),
- BC_LEX_KW_ENTRY("define", 6, true),
- BC_LEX_KW_ENTRY("for", 3, true),
- BC_LEX_KW_ENTRY("if", 2, true),
- BC_LEX_KW_ENTRY("limits", 6, false),
- BC_LEX_KW_ENTRY("return", 6, true),
- BC_LEX_KW_ENTRY("while", 5, true),
- BC_LEX_KW_ENTRY("halt", 4, false),
- BC_LEX_KW_ENTRY("last", 4, false),
- BC_LEX_KW_ENTRY("ibase", 5, true),
- BC_LEX_KW_ENTRY("obase", 5, true),
- BC_LEX_KW_ENTRY("scale", 5, true),
+ BC_LEX_KW_ENTRY("auto", 4, 1),
+ BC_LEX_KW_ENTRY("break", 5, 1),
+ BC_LEX_KW_ENTRY("continue", 8, 0),
+ BC_LEX_KW_ENTRY("define", 6, 1),
+ BC_LEX_KW_ENTRY("for", 3, 1),
+ BC_LEX_KW_ENTRY("if", 2, 1),
+ BC_LEX_KW_ENTRY("limits", 6, 0),
+ BC_LEX_KW_ENTRY("return", 6, 1),
+ BC_LEX_KW_ENTRY("while", 5, 1),
+ BC_LEX_KW_ENTRY("halt", 4, 0),
+ BC_LEX_KW_ENTRY("last", 4, 0),
+ BC_LEX_KW_ENTRY("ibase", 5, 1),
+ BC_LEX_KW_ENTRY("obase", 5, 1),
+ BC_LEX_KW_ENTRY("scale", 5, 1),
#if BC_ENABLE_EXTRA_MATH
- BC_LEX_KW_ENTRY("seed", 4, false),
+ BC_LEX_KW_ENTRY("seed", 4, 0),
#endif // BC_ENABLE_EXTRA_MATH
- BC_LEX_KW_ENTRY("length", 6, true),
- BC_LEX_KW_ENTRY("print", 5, false),
- BC_LEX_KW_ENTRY("sqrt", 4, true),
- BC_LEX_KW_ENTRY("abs", 3, false),
- BC_LEX_KW_ENTRY("is_number", 9, false),
- BC_LEX_KW_ENTRY("is_string", 9, false),
+ BC_LEX_KW_ENTRY("length", 6, 1),
+ BC_LEX_KW_ENTRY("print", 5, 0),
+ BC_LEX_KW_ENTRY("sqrt", 4, 1),
+ BC_LEX_KW_ENTRY("abs", 3, 0),
+ BC_LEX_KW_ENTRY("is_number", 9, 0),
+ BC_LEX_KW_ENTRY("is_string", 9, 0),
#if BC_ENABLE_EXTRA_MATH
- BC_LEX_KW_ENTRY("irand", 5, false),
+ BC_LEX_KW_ENTRY("irand", 5, 0),
#endif // BC_ENABLE_EXTRA_MATH
- BC_LEX_KW_ENTRY("asciify", 7, false),
- BC_LEX_KW_ENTRY("modexp", 6, false),
- BC_LEX_KW_ENTRY("divmod", 6, false),
- BC_LEX_KW_ENTRY("quit", 4, true),
- BC_LEX_KW_ENTRY("read", 4, false),
+ BC_LEX_KW_ENTRY("asciify", 7, 0),
+ BC_LEX_KW_ENTRY("modexp", 6, 0),
+ BC_LEX_KW_ENTRY("divmod", 6, 0),
+ BC_LEX_KW_ENTRY("quit", 4, 1),
+ BC_LEX_KW_ENTRY("read", 4, 0),
#if BC_ENABLE_EXTRA_MATH
- BC_LEX_KW_ENTRY("rand", 4, false),
+ BC_LEX_KW_ENTRY("rand", 4, 0),
#endif // BC_ENABLE_EXTRA_MATH
- BC_LEX_KW_ENTRY("maxibase", 8, false),
- BC_LEX_KW_ENTRY("maxobase", 8, false),
- BC_LEX_KW_ENTRY("maxscale", 8, false),
+ BC_LEX_KW_ENTRY("maxibase", 8, 0),
+ BC_LEX_KW_ENTRY("maxobase", 8, 0),
+ BC_LEX_KW_ENTRY("maxscale", 8, 0),
#if BC_ENABLE_EXTRA_MATH
- BC_LEX_KW_ENTRY("maxrand", 7, false),
+ BC_LEX_KW_ENTRY("maxrand", 7, 0),
#endif // BC_ENABLE_EXTRA_MATH
- BC_LEX_KW_ENTRY("line_length", 11, false),
- BC_LEX_KW_ENTRY("global_stacks", 13, false),
- BC_LEX_KW_ENTRY("leading_zero", 12, false),
- BC_LEX_KW_ENTRY("stream", 6, false),
- BC_LEX_KW_ENTRY("else", 4, false),
+ BC_LEX_KW_ENTRY("line_length", 11, 0),
+ BC_LEX_KW_ENTRY("global_stacks", 13, 0),
+ BC_LEX_KW_ENTRY("leading_zero", 12, 0),
+ BC_LEX_KW_ENTRY("stream", 6, 0),
+ BC_LEX_KW_ENTRY("else", 4, 0),
};
/// The length of the list of bc keywords.
@@ -917,64 +917,64 @@ _Static_assert(sizeof(bc_lex_kws) / sizeof(BcLexKeyword) == BC_LEX_NKWS,
const uint8_t bc_parse_exprs[] = {
// Starts with BC_LEX_EOF.
- BC_PARSE_EXPR_ENTRY(false, false, true, true, true, true, true, true),
+ BC_PARSE_EXPR_ENTRY(0, 0, 1, 1, 1, 1, 1, 1),
// Starts with BC_LEX_OP_MULTIPLY if extra math is enabled, BC_LEX_OP_DIVIDE
// otherwise.
- BC_PARSE_EXPR_ENTRY(true, true, true, true, true, true, true, true),
+ BC_PARSE_EXPR_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),
// Starts with BC_LEX_OP_REL_EQ if extra math is enabled, BC_LEX_OP_REL_LT
// otherwise.
- BC_PARSE_EXPR_ENTRY(true, true, true, true, true, true, true, true),
+ BC_PARSE_EXPR_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),
#if BC_ENABLE_EXTRA_MATH
// Starts with BC_LEX_OP_ASSIGN_POWER.
- BC_PARSE_EXPR_ENTRY(true, true, true, true, true, true, true, true),
+ BC_PARSE_EXPR_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),
// Starts with BC_LEX_OP_ASSIGN_RSHIFT.
- BC_PARSE_EXPR_ENTRY(true, true, false, false, true, true, false, false),
+ BC_PARSE_EXPR_ENTRY(1, 1, 0, 0, 1, 1, 0, 0),
// Starts with BC_LEX_RBRACKET.
- BC_PARSE_EXPR_ENTRY(false, false, false, false, true, true, true, false),
+ BC_PARSE_EXPR_ENTRY(0, 0, 0, 0, 1, 1, 1, 0),
// Starts with BC_LEX_KW_BREAK.
- BC_PARSE_EXPR_ENTRY(false, false, false, false, false, false, false, false),
+ BC_PARSE_EXPR_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
// Starts with BC_LEX_KW_HALT.
- BC_PARSE_EXPR_ENTRY(false, true, true, true, true, true, true, false),
+ BC_PARSE_EXPR_ENTRY(0, 1, 1, 1, 1, 1, 1, 0),
// Starts with BC_LEX_KW_SQRT.
- BC_PARSE_EXPR_ENTRY(true, true, true, true, true, true, true, true),
+ BC_PARSE_EXPR_ENTRY(1, 1, 1, 1, 1, 1, 1, 1),
// Starts with BC_LEX_KW_QUIT.
- BC_PARSE_EXPR_ENTRY(false, true, true, true, true, true, true, true),
+ BC_PARSE_EXPR_ENTRY(0, 1, 1, 1, 1, 1, 1, 1),
// Starts with BC_LEX_KW_GLOBAL_STACKS.
- BC_PARSE_EXPR_ENTRY(true, true, false, false, 0, 0, 0, 0)
+ BC_PARSE_EXPR_ENTRY(1, 1, 0, 0, 0, 0, 0, 0)
#else // BC_ENABLE_EXTRA_MATH
// Starts with BC_LEX_OP_ASSIGN_PLUS.
- BC_PARSE_EXPR_ENTRY(true, true, true, false, false, true, true, false),
+ BC_PARSE_EXPR_ENTRY(1, 1, 1, 0, 0, 1, 1, 0),
// Starts with BC_LEX_COMMA.
- BC_PARSE_EXPR_ENTRY(false, false, false, false, false, true, true, true),
+ BC_PARSE_EXPR_ENTRY(0, 0, 0, 0, 0, 1, 1, 1),
// Starts with BC_LEX_KW_AUTO.
- BC_PARSE_EXPR_ENTRY(false, false, false, false, false, false, false, false),
+ BC_PARSE_EXPR_ENTRY(0, 0, 0, 0, 0, 0, 0, 0),
// Starts with BC_LEX_KW_WHILE.
- BC_PARSE_EXPR_ENTRY(false, false, true, true, true, true, true, false),
+ BC_PARSE_EXPR_ENTRY(0, 0, 1, 1, 1, 1, 1, 0),
// Starts with BC_LEX_KW_SQRT.
- BC_PARSE_EXPR_ENTRY(true, true, true, true, true, true, true, false),
+ BC_PARSE_EXPR_ENTRY(1, 1, 1, 1, 1, 1, 1, 0),
// Starts with BC_LEX_KW_MAXIBASE.
- BC_PARSE_EXPR_ENTRY(true, true, true, true, true, true, true, false),
+ BC_PARSE_EXPR_ENTRY(1, 1, 1, 1, 1, 1, 1, 0),
// Starts with BC_LEX_KW_ELSE.
- BC_PARSE_EXPR_ENTRY(false, 0, 0, 0, 0, 0, 0, 0)
+ BC_PARSE_EXPR_ENTRY(0, 0, 0, 0, 0, 0, 0, 0)
#endif // BC_ENABLE_EXTRA_MATH
};
@@ -982,25 +982,25 @@ const uint8_t bc_parse_exprs[] = {
/// An array of data for operators that correspond to token types. Note that a
/// lower precedence *value* means a higher precedence.
const uchar bc_parse_ops[] = {
- BC_PARSE_OP(0, false), BC_PARSE_OP(0, false), BC_PARSE_OP(1, false),
- BC_PARSE_OP(1, false),
+ BC_PARSE_OP(0, 0), BC_PARSE_OP(0, 0), BC_PARSE_OP(1, 0),
+ BC_PARSE_OP(1, 0),
#if BC_ENABLE_EXTRA_MATH
- BC_PARSE_OP(2, false),
+ BC_PARSE_OP(2, 0),
#endif // BC_ENABLE_EXTRA_MATH
- BC_PARSE_OP(4, false), BC_PARSE_OP(5, true), BC_PARSE_OP(5, true),
- BC_PARSE_OP(5, true), BC_PARSE_OP(6, true), BC_PARSE_OP(6, true),
+ BC_PARSE_OP(4, 0), BC_PARSE_OP(5, 1), BC_PARSE_OP(5, 1),
+ BC_PARSE_OP(5, 1), BC_PARSE_OP(6, 1), BC_PARSE_OP(6, 1),
#if BC_ENABLE_EXTRA_MATH
- BC_PARSE_OP(3, false), BC_PARSE_OP(7, true), BC_PARSE_OP(7, true),
+ BC_PARSE_OP(3, 0), BC_PARSE_OP(7, 1), BC_PARSE_OP(7, 1),
#endif // BC_ENABLE_EXTRA_MATH
- BC_PARSE_OP(9, true), BC_PARSE_OP(9, true), BC_PARSE_OP(9, true),
- BC_PARSE_OP(9, true), BC_PARSE_OP(9, true), BC_PARSE_OP(9, true),
- BC_PARSE_OP(11, true), BC_PARSE_OP(10, true), BC_PARSE_OP(8, false),
- BC_PARSE_OP(8, false), BC_PARSE_OP(8, false), BC_PARSE_OP(8, false),
- BC_PARSE_OP(8, false), BC_PARSE_OP(8, false),
+ BC_PARSE_OP(9, 1), BC_PARSE_OP(9, 1), BC_PARSE_OP(9, 1),
+ BC_PARSE_OP(9, 1), BC_PARSE_OP(9, 1), BC_PARSE_OP(9, 1),
+ BC_PARSE_OP(11, 1), BC_PARSE_OP(10, 1), BC_PARSE_OP(8, 0),
+ BC_PARSE_OP(8, 0), BC_PARSE_OP(8, 0), BC_PARSE_OP(8, 0),
+ BC_PARSE_OP(8, 0), BC_PARSE_OP(8, 0),
#if BC_ENABLE_EXTRA_MATH
- BC_PARSE_OP(8, false), BC_PARSE_OP(8, false), BC_PARSE_OP(8, false),
+ BC_PARSE_OP(8, 0), BC_PARSE_OP(8, 0), BC_PARSE_OP(8, 0),
#endif // BC_ENABLE_EXTRA_MATH
- BC_PARSE_OP(8, false),
+ BC_PARSE_OP(8, 0),
};
// These identify what tokens can come after expressions in certain cases.
diff --git a/contrib/bc/src/dc.c b/contrib/bc/src/dc.c
index 37419acd4bd4..1376f5e00a95 100644
--- a/contrib/bc/src/dc.c
+++ b/contrib/bc/src/dc.c
@@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+ * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/src/dc_fuzzer.c b/contrib/bc/src/dc_fuzzer.c
index adaf486a668c..0cb12e4fdf4c 100644
--- a/contrib/bc/src/dc_fuzzer.c
+++ b/contrib/bc/src/dc_fuzzer.c
@@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+ * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -36,7 +36,6 @@
#include <setjmp.h>
#include <string.h>
-#include <version.h>
#include <status.h>
#include <ossfuzz.h>
#include <vm.h>
diff --git a/contrib/bc/src/dc_lex.c b/contrib/bc/src/dc_lex.c
index d5131b45331d..763957edc2ee 100644
--- a/contrib/bc/src/dc_lex.c
+++ b/contrib/bc/src/dc_lex.c
@@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+ * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/src/dc_parse.c b/contrib/bc/src/dc_parse.c
index 1996120461a8..b607bfd4c39f 100644
--- a/contrib/bc/src/dc_parse.c
+++ b/contrib/bc/src/dc_parse.c
@@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+ * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/src/file.c b/contrib/bc/src/file.c
index 9baea585603b..bc8097c5b8ce 100644
--- a/contrib/bc/src/file.c
+++ b/contrib/bc/src/file.c
@@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+ * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -291,11 +291,12 @@ bc_file_vprintf(BcFile* restrict f, const char* fmt, va_list args)
// This mess is to silence a warning.
#if BC_CLANG
+#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wformat-nonliteral"
#endif // BC_CLANG
r = vfprintf(f->f, fmt, args);
#if BC_CLANG
-#pragma clang diagnostic warning "-Wformat-nonliteral"
+#pragma clang diagnostic pop
#endif // BC_CLANG
// Just print and propagate the error.
diff --git a/contrib/bc/src/history.c b/contrib/bc/src/history.c
index 32a19f71d777..594cc4eb46de 100644
--- a/contrib/bc/src/history.c
+++ b/contrib/bc/src/history.c
@@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+ * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -199,6 +199,7 @@ bc_history_init(BcHistory* h)
h->el = el_init(vm->name, stdin, stdout, stderr);
if (BC_ERR(h->el == NULL)) bc_vm_fatalError(BC_ERR_FATAL_ALLOC_ERR);
+ el_set(h->el, EL_SIGNAL, 1);
// I want history and a prompt.
history(h->hist, &bc_history_event, H_SETSIZE, 100);
@@ -264,18 +265,7 @@ bc_history_line(BcHistory* h, BcVec* vec, const char* prompt)
errno = EINTR;
// Get the line.
- //
- // XXX: Why have a macro here? Because macOS needs to be special. Honestly,
- // it's starting to feel special like Windows at this point. Anyway, the
- // second SIGWINCH signal of multiple will return a valid line length on
- // macOS, so we need to allow for that on macOS. However, FreeBSD's editline
- // is different and will mess up the terminal if we do it that way.
- //
- // There is one limitation with this, however: Ctrl+D won't work on macOS.
- // But it's because of macOS that this problem exists, and I can't really do
- // anything about it. So macOS should fix their broken editline; once they
- // do, I'll fix Ctrl+D on macOS.
- while (BC_HISTORY_INVALID_LINE(line, len))
+ while (line == NULL && len == -1 && errno == EINTR)
{
line = el_gets(h->el, &len);
bc_history_use_prompt = false;
diff --git a/contrib/bc/src/lang.c b/contrib/bc/src/lang.c
index 7968bcbd9dfd..2a62509c98b0 100644
--- a/contrib/bc/src/lang.c
+++ b/contrib/bc/src/lang.c
@@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+ * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -73,9 +73,10 @@ bc_func_insert(BcFunc* f, BcProgram* p, char* name, BcType type, size_t line)
BcAuto* aptr = bc_vec_item(&f->autos, i);
// If they match, barf.
- if (BC_ERR(idx == aptr->idx && type == aptr->type))
+ if (BC_ERR(idx == aptr->idx &&
+ BC_IS_ARRAY(type) == BC_IS_ARRAY(aptr->type)))
{
- const char* array = type == BC_TYPE_ARRAY ? "[]" : "";
+ const char* array = BC_IS_ARRAY(type) ? "[]" : "";
bc_error(BC_ERR_PARSE_DUP_LOCAL, line, name, array);
}
diff --git a/contrib/bc/src/lex.c b/contrib/bc/src/lex.c
index 37e52c33fffd..6b639a65b6a8 100644
--- a/contrib/bc/src/lex.c
+++ b/contrib/bc/src/lex.c
@@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+ * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/src/library.c b/contrib/bc/src/library.c
index 5451e91684a2..282dddff27d2 100644
--- a/contrib/bc/src/library.c
+++ b/contrib/bc/src/library.c
@@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+ * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/src/main.c b/contrib/bc/src/main.c
index da4c27156029..749248048e78 100644
--- a/contrib/bc/src/main.c
+++ b/contrib/bc/src/main.c
@@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+ * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -47,7 +47,6 @@
#include <setjmp.h>
-#include <version.h>
#include <status.h>
#include <vm.h>
#include <bc.h>
diff --git a/contrib/bc/src/num.c b/contrib/bc/src/num.c
index 83f84edb91fc..2cdc6eea0087 100644
--- a/contrib/bc/src/num.c
+++ b/contrib/bc/src/num.c
@@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+ * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -1093,6 +1093,7 @@ bc_num_as(BcNum* a, BcNum* b, BcNum* restrict c, size_t sub)
max_len = max_int + max_rdx;
+ // Figure out the max length and also if we need to reverse the operation.
if (do_sub)
{
// Check whether b has to be subtracted from a or a from b.
diff --git a/contrib/bc/src/opt.c b/contrib/bc/src/opt.c
index a1c8e813b1ea..6a24291ac401 100644
--- a/contrib/bc/src/opt.c
+++ b/contrib/bc/src/opt.c
@@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+ * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/src/parse.c b/contrib/bc/src/parse.c
index 0107d4cdef0d..026607994984 100644
--- a/contrib/bc/src/parse.c
+++ b/contrib/bc/src/parse.c
@@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+ * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/src/program.c b/contrib/bc/src/program.c
index 3b6ebc003a3e..af032c5f1e86 100644
--- a/contrib/bc/src/program.c
+++ b/contrib/bc/src/program.c
@@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+ * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -623,6 +623,9 @@ bc_program_prepResult(BcProgram* p)
{
BcResult* res = bc_vec_pushEmpty(&p->results);
+ // Mark a result as not retired.
+ p->nresults += 1;
+
bc_result_clear(res);
return res;
@@ -646,6 +649,8 @@ bc_program_const(BcProgram* p, const char* code, size_t* bgn)
BcConst* c = bc_vec_item(&p->consts, bc_program_index(code, bgn));
BcBigDig base = BC_PROG_IBASE(p);
+ assert(p->nresults == 1);
+
// Only reparse if the base changed.
if (c->base != base)
{
@@ -673,6 +678,9 @@ bc_program_const(BcProgram* p, const char* code, size_t* bgn)
bc_num_createCopy(&r->d.n, &c->num);
BC_SIG_UNLOCK;
+
+ // XXX: Make sure to clear the number of results.
+ p->nresults -= 1;
}
/**
@@ -692,6 +700,8 @@ bc_program_op(BcProgram* p, uchar inst)
res = bc_program_prepResult(p);
+ assert(p->nresults == 1);
+
bc_program_binOpPrep(p, &opd1, &n1, &opd2, &n2, 1);
BC_SIG_LOCK;
@@ -709,7 +719,7 @@ bc_program_op(BcProgram* p, uchar inst)
// Run the operation. This also executes an item of an array.
bc_program_ops[idx](n1, n2, &res->d.n, BC_PROG_SCALE(p));
- bc_program_retire(p, 1, 2);
+ bc_program_retire(p, 2);
}
/**
@@ -1060,6 +1070,8 @@ bc_program_unary(BcProgram* p, uchar inst)
res = bc_program_prepResult(p);
+ assert(p->nresults == 1);
+
bc_program_prep(p, &ptr, &num, 1);
BC_SIG_LOCK;
@@ -1070,7 +1082,7 @@ bc_program_unary(BcProgram* p, uchar inst)
// This calls a function that is in an array.
bc_program_unarys[inst - BC_INST_NEG](res, num);
- bc_program_retire(p, 1, 1);
+ bc_program_retire(p, 1);
}
/**
@@ -1091,6 +1103,8 @@ bc_program_logical(BcProgram* p, uchar inst)
res = bc_program_prepResult(p);
+ assert(p->nresults == 1);
+
// All logical operators (except boolean not, which is taken care of by
// bc_program_unary()), are binary operators.
bc_program_binOpPrep(p, &opd1, &n1, &opd2, &n2, 1);
@@ -1165,7 +1179,7 @@ bc_program_logical(BcProgram* p, uchar inst)
if (cond) bc_num_one(&res->d.n);
- bc_program_retire(p, 1, 2);
+ bc_program_retire(p, 2);
}
/**
@@ -1880,6 +1894,8 @@ bc_program_return(BcProgram* p, uchar inst)
res = bc_program_prepResult(p);
+ assert(p->nresults == 1);
+
// If we are returning normally...
if (inst == BC_INST_RET)
{
@@ -1930,7 +1946,7 @@ bc_program_return(BcProgram* p, uchar inst)
BC_SIG_LOCK;
// When we retire, pop all of the unused results.
- bc_program_retire(p, 1, nresults);
+ bc_program_retire(p, nresults);
// Pop the globals, if necessary.
if (BC_G) bc_program_popGlobals(p, false);
@@ -1974,6 +1990,8 @@ bc_program_builtin(BcProgram* p, uchar inst)
res = bc_program_prepResult(p);
+ assert(p->nresults == 1);
+
bc_program_operand(p, &opd, &num, 1);
assert(num != NULL);
@@ -2102,7 +2120,7 @@ bc_program_builtin(BcProgram* p, uchar inst)
BC_SIG_UNLOCK;
}
- bc_program_retire(p, 1, 1);
+ bc_program_retire(p, 1);
}
/**
@@ -2127,6 +2145,7 @@ bc_program_divmod(BcProgram* p)
// the capacity is enough due to the line above.
res2 = bc_program_prepResult(p);
res = bc_program_prepResult(p);
+ assert(p->nresults == 2);
// Prepare the operands.
bc_program_binOpPrep(p, &opd1, &n1, &opd2, &n2, 2);
@@ -2144,7 +2163,7 @@ bc_program_divmod(BcProgram* p)
// Execute.
bc_num_divmod(n1, n2, &res2->d.n, &res->d.n, BC_PROG_SCALE(p));
- bc_program_retire(p, 2, 2);
+ bc_program_retire(p, 2);
}
/**
@@ -2176,6 +2195,8 @@ bc_program_modexp(BcProgram* p)
res = bc_program_prepResult(p);
+ assert(p->nresults == 1);
+
// Get the first operand and typecheck.
bc_program_operand(p, &r1, &n1, 3);
bc_program_type_num(r1, n1);
@@ -2198,7 +2219,7 @@ bc_program_modexp(BcProgram* p)
bc_num_modexp(n1, n2, n3, &res->d.n);
- bc_program_retire(p, 1, 3);
+ bc_program_retire(p, 3);
}
/**
@@ -2737,6 +2758,9 @@ bc_program_pushSeed(BcProgram* p)
BcResult* res;
res = bc_program_prepResult(p);
+
+ assert(p->nresults == 1);
+
res->t = BC_RESULT_SEED;
BC_SIG_LOCK;
@@ -2747,6 +2771,9 @@ bc_program_pushSeed(BcProgram* p)
BC_SIG_UNLOCK;
bc_num_createFromRNG(&res->d.n, &p->rng);
+
+ // XXX: Clear the number of results.
+ p->nresults = 0;
}
#endif // BC_ENABLE_EXTRA_MATH
@@ -2932,6 +2959,9 @@ bc_program_init(BcProgram* p)
bc_map_init(&p->const_map);
bc_vec_init(&p->strs, sizeof(char*), BC_DTOR_NONE);
bc_map_init(&p->str_map);
+
+ // XXX: Clear the number of results.
+ p->nresults = 0;
}
void
@@ -2985,13 +3015,18 @@ bc_program_reset(BcProgram* p)
if (BC_IS_DC) bc_vec_npop(&p->tail_calls, p->tail_calls.len - 1);
#endif // DC_ENABLED
-#if BC_ENABLED
// Clear the stack if we are in bc. We have to do this in bc because bc's
// stack is implicit.
//
// XXX: We don't do this in dc because other dc implementations don't.
- if (BC_IS_BC || !BC_I) bc_vec_popAll(&p->results);
+ // However, we *MUST* pop the items for results that are not retired yet.
+ if (BC_IS_DC && BC_I) bc_vec_npop(&p->results, p->nresults);
+ else bc_vec_popAll(&p->results);
+ // Now clear how many results there are.
+ p->nresults = 0;
+
+#if BC_ENABLED
// Clear the globals' stacks.
if (BC_G) bc_program_popGlobals(p, true);
#endif // BC_ENABLED
@@ -3039,10 +3074,12 @@ bc_program_exec(BcProgram* p)
#if BC_HAS_COMPUTED_GOTO
#if BC_GCC
+#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
#endif // BC_GCC
#if BC_CLANG
+#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu-label-as-value"
#endif // BC_CLANG
@@ -3050,11 +3087,11 @@ bc_program_exec(BcProgram* p)
BC_PROG_LBLS_ASSERT;
#if BC_CLANG
-#pragma clang diagnostic warning "-Wgnu-label-as-value"
+#pragma clang diagnostic pop
#endif // BC_CLANG
#if BC_GCC
-#pragma GCC diagnostic warning "-Wpedantic"
+#pragma GCC diagnostic pop
#endif // BC_GCC
// BC_INST_INVALID is a marker for the end so that we don't have to have an
@@ -3085,10 +3122,12 @@ bc_program_exec(BcProgram* p)
#if BC_HAS_COMPUTED_GOTO
#if BC_GCC
+#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpedantic"
#endif // BC_GCC
#if BC_CLANG
+#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu-label-as-value"
#endif // BC_CLANG
@@ -3711,11 +3750,11 @@ bc_program_exec(BcProgram* p)
#if BC_HAS_COMPUTED_GOTO
#if BC_CLANG
-#pragma clang diagnostic warning "-Wgnu-label-as-value"
+#pragma clang diagnostic pop
#endif // BC_CLANG
#if BC_GCC
-#pragma GCC diagnostic warning "-Wpedantic"
+#pragma GCC diagnostic pop
#endif // BC_GCC
#else // BC_HAS_COMPUTED_GOTO
diff --git a/contrib/bc/src/rand.c b/contrib/bc/src/rand.c
index 0f9950788f7c..3aaf905f8520 100644
--- a/contrib/bc/src/rand.c
+++ b/contrib/bc/src/rand.c
@@ -13,7 +13,7 @@
* This code is under the following license:
*
* Copyright (c) 2014-2017 Melissa O'Neill and PCG Project contributors
- * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+ * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
diff --git a/contrib/bc/src/read.c b/contrib/bc/src/read.c
index 01d804848945..650c2813bd1e 100644
--- a/contrib/bc/src/read.c
+++ b/contrib/bc/src/read.c
@@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+ * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -171,34 +171,23 @@ bc_read_chars(BcVec* vec, const char* prompt)
// If interupted...
if (errno == EINTR)
{
- int sig;
-
// Jump out if we are supposed to quit, which certain signals
// will require.
if (vm->status == (sig_atomic_t) BC_STATUS_QUIT) BC_JMP;
assert(vm->sig != 0);
- sig = (int) vm->sig;
-
// Clear the signal and status.
vm->sig = 0;
vm->status = (sig_atomic_t) BC_STATUS_SUCCESS;
-#ifndef _WIN32
- // We don't want to print anything on a SIGWINCH.
- if (sig != SIGWINCH)
-#endif // _WIN32
+ // Print the ready message and prompt again.
+ bc_file_puts(&vm->fout, bc_flush_none, bc_program_ready_msg);
+ if (BC_PROMPT)
{
- // Print the ready message and prompt again.
- bc_file_puts(&vm->fout, bc_flush_none,
- bc_program_ready_msg);
- if (BC_PROMPT)
- {
- bc_file_puts(&vm->fout, bc_flush_none, prompt);
- }
- bc_file_flush(&vm->fout, bc_flush_none);
+ bc_file_puts(&vm->fout, bc_flush_none, prompt);
}
+ bc_file_flush(&vm->fout, bc_flush_none);
BC_SIG_UNLOCK;
diff --git a/contrib/bc/src/vector.c b/contrib/bc/src/vector.c
index 4b49e61968df..7d5c0bc7376f 100644
--- a/contrib/bc/src/vector.c
+++ b/contrib/bc/src/vector.c
@@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+ * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/src/vm.c b/contrib/bc/src/vm.c
index 636cd4ba0c1b..95bfd73f19e5 100644
--- a/contrib/bc/src/vm.c
+++ b/contrib/bc/src/vm.c
@@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+ * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
@@ -124,27 +124,6 @@ bc_vm_jmp(void)
static void
bc_vm_sig(int sig)
{
-#if BC_ENABLE_EDITLINE
- // Editline needs this to resize the terminal. This also needs to come first
- // because a resize always needs to happen.
- if (sig == SIGWINCH)
- {
- if (BC_TTY)
- {
- el_resize(vm->history.el);
-
- // If the signal was a SIGWINCH, clear it because we don't need to
- // print a stack trace in that case.
- if (vm->sig == SIGWINCH)
- {
- vm->sig = 0;
- }
- }
-
- return;
- }
-#endif // BC_ENABLE_EDITLINE
-
// There is already a signal in flight if this is true.
if (vm->status == (sig_atomic_t) BC_STATUS_QUIT || vm->sig != 0)
{
@@ -217,27 +196,23 @@ bc_vm_sigaction(void)
struct sigaction sa;
sigemptyset(&sa.sa_mask);
- sa.sa_flags = BC_ENABLE_EDITLINE ? 0 : SA_NODEFER;
+ sa.sa_flags = SA_NODEFER;
// This mess is to silence a warning on Clang with regards to glibc's
// sigaction handler, which activates the warning here.
#if BC_CLANG
+#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdisabled-macro-expansion"
#endif // BC_CLANG
sa.sa_handler = bc_vm_sig;
#if BC_CLANG
-#pragma clang diagnostic warning "-Wdisabled-macro-expansion"
+#pragma clang diagnostic pop
#endif // BC_CLANG
sigaction(SIGTERM, &sa, NULL);
sigaction(SIGQUIT, &sa, NULL);
sigaction(SIGINT, &sa, NULL);
-#if BC_ENABLE_EDITLINE
- // Editline needs this to resize the terminal.
- if (BC_TTY) sigaction(SIGWINCH, &sa, NULL);
-#endif // BC_ENABLE_EDITLINE
-
#if BC_ENABLE_HISTORY
if (BC_TTY) sigaction(SIGHUP, &sa, NULL);
#endif // BC_ENABLE_HISTORY
@@ -544,7 +519,7 @@ bc_vm_envArgs(const char* const env_args_name, BcBigDig* scale, BcBigDig* ibase,
if (env_args == NULL) return;
- // Windows already allocates, so we don't need to.
+ // Windows already allocates, so we don't need to.
#ifndef _WIN32
start = buf = vm->env_args_buffer = bc_vm_strdup(env_args);
#else // _WIN32
diff --git a/contrib/bc/tests/all.sh b/contrib/bc/tests/all.sh
index 28631c048e71..d5d084341e36 100755
--- a/contrib/bc/tests/all.sh
+++ b/contrib/bc/tests/all.sh
@@ -2,7 +2,7 @@
#
# SPDX-License-Identifier: BSD-2-Clause
#
-# Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+# Copyright (c) 2018-2025 Gavin D. Howard and contributors.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
@@ -39,7 +39,7 @@ usage() {
if [ $# -eq 1 ]; then
printf '%s\n\n' "$1"
fi
- print 'usage: %s [-n] dir [run_extra_tests] [run_stack_tests] [gen_tests] [run_problematic_tests] [time_tests] [exec args...]\n' \
+ print 'usage: %s [-n] dir [run_extra_tests] [run_stack_tests] [gen_tests] [run_problematic_tests] [exec args...]\n' \
"$script"
exit 1
}
@@ -103,15 +103,6 @@ else
fi
if [ "$#" -lt 1 ]; then
- time_tests=0
- check_bool_arg "$time_tests"
-else
- time_tests="$1"
- shift
- check_bool_arg "$time_tests"
-fi
-
-if [ "$#" -lt 1 ]; then
exe="$testdir/../bin/$d"
check_exec_arg "$exe"
else
@@ -155,10 +146,10 @@ while read t; do
fi
if [ "$pll" -ne 0 ]; then
- sh "$testdir/test.sh" "$d" "$t" "$generate_tests" "$time_tests" "$exe" "$@" &
+ sh "$testdir/test.sh" "$d" "$t" "$generate_tests" "$exe" "$@" &
pids="$pids $!"
else
- sh "$testdir/test.sh" "$d" "$t" "$generate_tests" "$time_tests" "$exe" "$@"
+ sh "$testdir/test.sh" "$d" "$t" "$generate_tests" "$exe" "$@"
fi
done < "$testdir/$d/all.txt"
@@ -174,19 +165,11 @@ fi
# Script tests.
if [ "$pll" -ne 0 ]; then
sh "$testdir/scripts.sh" "$d" "$extra" "$run_stack_tests" "$generate_tests" \
- "$time_tests" "$exe" "$@" &
+ "$exe" "$@" &
pids="$pids $!"
else
sh "$testdir/scripts.sh" -n "$d" "$extra" "$run_stack_tests" "$generate_tests" \
- "$time_tests" "$exe" "$@"
-fi
-
-# Read tests.
-if [ "$pll" -ne 0 ]; then
- sh "$testdir/read.sh" "$d" "$exe" "$@" &
- pids="$pids $!"
-else
- sh "$testdir/read.sh" "$d" "$exe" "$@"
+ "$exe" "$@"
fi
# Error tests.
@@ -198,8 +181,9 @@ else
fi
# Test all the files in the errors directory. While the other error test (in
-# tests/errors.sh) does a test for every line, this does one test per file, but
-# it runs the file through stdin and as a file on the command-line.
+# tests/errors.sh) does a test for every line of certain error files in the main
+# directory, this does one test per file in the errors directory, but it runs
+# the file through stdin and as a file on the command-line.
for testfile in $testdir/$d/errors/*.txt; do
b=$(basename "$testfile")
@@ -213,14 +197,6 @@ for testfile in $testdir/$d/errors/*.txt; do
done
-# Other tests.
-if [ "$pll" -ne 0 ]; then
- sh "$testdir/other.sh" "$d" "$extra" "$exe" "$@" &
- pids="$pids $!"
-else
- sh "$testdir/other.sh" "$d" "$extra" "$exe" "$@"
-fi
-
if [ "$pll" -ne 0 ]; then
exit_err=0
diff --git a/contrib/bc/tests/bc/all.txt b/contrib/bc/tests/bc/all.txt
index c710534aac1b..2fd582118adb 100644
--- a/contrib/bc/tests/bc/all.txt
+++ b/contrib/bc/tests/bc/all.txt
@@ -1,7 +1,132 @@
decimal
-print
-parse
-lib2
+print_002
+print_003
+print_004
+print_005
+print_006
+print_007
+print_008
+print_009
+print_011
+print_012
+print_013
+print_014
+print_015
+print_016
+print_017
+print_018
+print_019
+print_020
+print_021
+print_022
+print_023
+print_024
+print_025
+print_026
+print_027
+print_028
+print_029
+print_030
+print_031
+print_032
+print_033
+print_034
+print_035
+print_036
+print_037
+print_038
+print_039
+print_040
+print_041
+print_042
+print_043
+print_044
+print_045
+print_046
+print_047
+print_048
+print_049
+print_050
+print_051
+print_052
+print_053
+print_054
+print_055
+print_056
+print_057
+print_058
+print_059
+print_060
+print_061
+print_062
+print_063
+print_064
+print_065
+print_066
+print_067
+print_068
+print_069
+print_070
+print_071
+print_072
+print_073
+print_074
+print_075
+print_076
+print_077
+print_078
+print_079
+print_080
+print_081
+print_082
+print_083
+print_084
+print_085
+print_086
+print_087
+print_088
+print_089
+print_090
+print_091
+print_092
+print_093
+print_094
+print_095
+print_096
+print_097
+print_098
+print_099
+print_100
+parse_02
+parse_03
+parse_04
+parse_05
+parse_06
+parse_07
+parse_08
+parse_09
+parse_11
+parse_12
+parse_13
+parse_14
+parse_15
+parse_16
+lib2_p
+lib2_r
+lib2_ceil
+lib2_log
+lib2_root
+lib2_gcd
+lib2_bytes
+lib2_pi
+lib2_tan
+lib2_a2
+lib2_r2d
+lib2_d2r
+lib2_fac
+lib2_perm
+lib2_uint
+lib2_rand
print2
length
scale
diff --git a/contrib/bc/tests/bc/errors/39.txt b/contrib/bc/tests/bc/errors/39.txt
new file mode 100644
index 000000000000..104d16136b22
--- /dev/null
+++ b/contrib/bc/tests/bc/errors/39.txt
@@ -0,0 +1 @@
+define a(*t[], t[]) {}
diff --git a/contrib/bc/tests/bc/lib2.txt b/contrib/bc/tests/bc/lib2.txt
deleted file mode 100644
index 74e1256d7bbf..000000000000
--- a/contrib/bc/tests/bc/lib2.txt
+++ /dev/null
@@ -1,477 +0,0 @@
-p(2, 8.0000)
-p(2, 8.0001)
-p(2, -8.0001)
-p(1024,32.1)
-r(0, 0)
-r(0, 1)
-r(0, 100)
-r(1, 0)
-r(1, 3)
-r(1.4, 0)
-r(1.5, 0)
-r(34.45, 2)
-r(64.1223, 4)
-r(283.1983893, 6)
-r(283.1983895, 6)
-r(283.1983899, 6)
-r(99.999999999, 5)
-r(-1, 0)
-r(-1, 3)
-r(-1.4, 0)
-r(-1.5, 0)
-r(-34.45, 2)
-r(-64.1223, 4)
-r(-283.1983893, 6)
-r(-283.1983895, 6)
-r(-283.1983899, 6)
-r(-99.999999999, 5)
-ceil(0, 0)
-ceil(0, 1)
-ceil(0, 100)
-ceil(1, 0)
-ceil(1, 3)
-ceil(1.4, 0)
-ceil(1.5, 0)
-ceil(34.45, 2)
-ceil(64.1223, 4)
-ceil(283.1983893, 6)
-ceil(283.1983895, 6)
-ceil(283.1983899, 6)
-ceil(99.999999999, 5)
-ceil(-1, 0)
-ceil(-1, 3)
-ceil(-1.4, 0)
-ceil(-1.5, 0)
-ceil(-34.45, 2)
-ceil(-64.1223, 4)
-ceil(-283.1983893, 6)
-ceil(-283.1983895, 6)
-ceil(-283.1983899, 6)
-ceil(-99.999999999, 5)
-ceil(8770735.0705156250000000000, 0)
-l2(0)
-l2(1)
-l2(2)
-l2(7)
-l2(7.9999999999999999999999)
-l2(8)
-l10(0)
-l10(1)
-l10(2)
-l10(5)
-l10(9)
-l10(9.999999999999999999999)
-l10(10)
-l10(11)
-l10(99)
-l10(99.99999999999999999999)
-l10(100)
-l2(-1)
-l2(-2)
-l2(-7)
-l2(-7.9999999999999999999999)
-l2(-8)
-l10(-1)
-l10(-2)
-l10(-5)
-l10(-9)
-l10(-9.999999999999999999999)
-l10(-10)
-l10(-11)
-l10(-99)
-l10(-99.99999999999999999999)
-l10(-100)
-cbrt(27)
-cbrt(-27)
-cbrt(4096)
-cbrt(-4096)
-root(0, 3)
-root(0, 4)
-root(0, 5)
-root(0.0000000000000, 3)
-root(0.0000000000000, 4)
-root(0.0000000000000, 5)
-root(16, 4)
-root(3125, 5)
-root(-3125, 5)
-gcd(285, 35)
-gcd(1, 6)
-gcd(5, 1)
-gcd(8, 12)
-gcd(40, 4096)
-lcm(40, 4096)
-lcm(555, 55)
-ubytes(0)
-ubytes(1)
-ubytes(2)
-ubytes(254)
-ubytes(255)
-ubytes(256)
-ubytes(65535)
-ubytes(65536)
-ubytes(131072)
-ubytes(4294967295)
-ubytes(4294967296)
-ubytes(18446744073709551615)
-ubytes(18446744073709551616)
-sbytes(0)
-sbytes(1)
-sbytes(-1)
-sbytes(2)
-sbytes(127)
-sbytes(128)
-sbytes(-127)
-sbytes(-128)
-sbytes(-129)
-sbytes(254)
-sbytes(255)
-sbytes(256)
-sbytes(32767)
-sbytes(32768)
-sbytes(-32767)
-sbytes(-32768)
-sbytes(65535)
-sbytes(65536)
-sbytes(131072)
-sbytes(2147483647)
-sbytes(2147483648)
-sbytes(2147483649)
-sbytes(-2147483647)
-sbytes(-2147483648)
-sbytes(-2147483649)
-sbytes(4294967295)
-sbytes(4294967296)
-sbytes(9223372036854775807)
-sbytes(9223372036854775808)
-sbytes(9223372036854775809)
-sbytes(-9223372036854775807)
-sbytes(-9223372036854775808)
-sbytes(-9223372036854775809)
-pi(0)
-pi(1)
-pi(2)
-pi(5)
-pi(100)
-p=pi(100)
-t(0)
-t(1)
-t(-1)
-t(2)
-t(-2)
-t(3)
-t(-3)
-t(p)
-t(-p)
-t(p/2)
-t(-p/2)
-t(p/3)
-t(-p/3)
-t(p/4)
-t(-p/4)
-t(p/5)
-t(-p/5)
-t(p/6)
-t(-p/6)
-t(p/7)
-t(-p/7)
-t(p/8)
-t(-p/8)
-t(p/9)
-t(-p/9)
-t(p/10)
-t(-p/10)
-t(p/15)
-t(-p/15)
-a2(0, 1)
-a2(1, 1)
-a2(2, 1)
-a2(1, 2)
-a2(0, -1)
-a2(1, -1)
-a2(2, -1)
-a2(1, -2)
-a2(-1, 1)
-a2(-2, 1)
-a2(-1, 2)
-a2(-1, -1)
-a2(-2, -1)
-a2(-1, -2)
-a2(1, 0)
-a2(2, 0)
-a2(-1, 0)
-a2(-2, 0)
-r2d(p)
-r2d(2 * p)
-r2d(p / 2)
-r2d(p / 4)
-r2d(p / 3)
-r2d(p / 5)
-r2d(p / 6)
-r2d(p / 10)
-r2d(-p)
-r2d(2 * -p)
-r2d(-p / 2)
-r2d(-p / 4)
-r2d(-p / 3)
-r2d(-p / 5)
-r2d(-p / 6)
-r2d(-p / 10)
-d2r(180)
-d2r(360)
-d2r(90)
-d2r(45)
-d2r(120)
-d2r(72)
-d2r(60)
-d2r(36)
-d2r(-180)
-d2r(-360)
-d2r(-90)
-d2r(-45)
-d2r(-120)
-d2r(-72)
-d2r(-60)
-d2r(-36)
-f(0)
-f(1)
-f(2)
-f(3)
-f(4)
-f(5)
-perm(10, 2)
-comb(10, 2)
-perm(6, 2)
-comb(6, 2)
-perm(12, 10)
-comb(12, 10)
-perm(24, 15)
-comb(24, 15)
-binary(0)
-hex(0)
-binary(1)
-hex(1)
-binary(2)
-hex(2)
-binary(15)
-hex(15)
-binary(16)
-hex(16)
-uint(0)
-int(0)
-uint(1)
-int(1)
-int(-1)
-uint(127)
-int(127)
-int(-127)
-uint(128)
-int(128)
-int(-128)
-uint(129)
-int(129)
-int(-129)
-uint(255)
-int(255)
-int(-255)
-uint(256)
-int(256)
-int(-256)
-uint(32767)
-int(32767)
-int(-32767)
-uint(32768)
-int(32768)
-int(-32768)
-uint(32769)
-int(32769)
-int(-32769)
-uint(65535)
-int(65535)
-int(-65535)
-uint(65536)
-int(65536)
-int(-65536)
-uint(2147483647)
-int(2147483647)
-int(-2147483647)
-uint(2147483648)
-int(2147483648)
-int(-2147483648)
-uint(2147483649)
-int(2147483649)
-int(-2147483649)
-uint(4294967295)
-int(4294967295)
-int(-4294967295)
-uint(4294967296)
-int(4294967296)
-int(-4294967296)
-uint8(0)
-int8(0)
-uint16(0)
-int16(0)
-uint32(0)
-int32(0)
-uint64(0)
-int64(0)
-uint8(1)
-int8(1)
-int8(-1)
-uint16(1)
-int16(1)
-int16(-1)
-uint32(1)
-int32(1)
-int32(-1)
-uint64(1)
-int64(1)
-int64(-1)
-uint8(127)
-int8(127)
-int8(-127)
-uint16(127)
-int16(127)
-int16(-127)
-uint32(127)
-int32(127)
-int32(-127)
-uint64(127)
-int64(127)
-int64(-127)
-uint8(128)
-int8(128)
-int8(-128)
-uint16(128)
-int16(128)
-int16(-128)
-uint32(128)
-int32(128)
-int32(-128)
-uint64(128)
-int64(128)
-int64(-128)
-uint8(129)
-int8(129)
-int8(-129)
-uint16(129)
-int16(129)
-int16(-129)
-uint32(129)
-int32(129)
-int32(-129)
-uint64(129)
-int64(129)
-int64(-129)
-uint8(255)
-int8(255)
-int8(-255)
-uint16(255)
-int16(255)
-int16(-255)
-uint32(255)
-int32(255)
-int32(-255)
-uint64(255)
-int64(255)
-int64(-255)
-uint8(256)
-int8(256)
-int8(-256)
-uint16(256)
-int16(256)
-int16(-256)
-uint32(256)
-int32(256)
-int32(-256)
-uint64(256)
-int64(256)
-int64(-256)
-uint16(32767)
-int16(32767)
-int16(-32767)
-uint32(32767)
-int32(32767)
-int32(-32767)
-uint64(32767)
-int64(32767)
-int64(-32767)
-uint16(32768)
-int16(32768)
-int16(-32768)
-uint32(32768)
-int32(32768)
-int32(-32768)
-uint64(32768)
-int64(32768)
-int64(-32768)
-uint16(32769)
-int16(32769)
-int16(-32769)
-uint32(32769)
-int32(32769)
-int32(-32769)
-uint64(32769)
-int64(32769)
-int64(-32769)
-uint16(65535)
-int16(65535)
-int16(-65535)
-uint32(65535)
-int32(65535)
-int32(-65535)
-uint64(65535)
-int64(65535)
-int64(-65535)
-uint16(65536)
-int16(65536)
-int16(-65536)
-uint32(65536)
-int32(65536)
-int32(-65536)
-uint64(65536)
-int64(65536)
-int64(-65536)
-uint32(2147483647)
-int32(2147483647)
-int32(-2147483647)
-uint64(2147483647)
-int64(2147483647)
-int64(-2147483647)
-uint32(2147483648)
-int32(2147483648)
-int32(-2147483648)
-uint64(2147483648)
-int64(2147483648)
-int64(-2147483648)
-uint32(2147483649)
-int32(2147483649)
-int32(-2147483649)
-uint64(2147483649)
-int64(2147483649)
-int64(-2147483649)
-uint32(4294967295)
-int32(4294967295)
-int32(-4294967295)
-uint64(4294967295)
-int64(4294967295)
-int64(-4294967295)
-uint32(4294967296)
-int32(4294967296)
-int32(-4294967296)
-uint64(4294967296)
-int64(4294967296)
-int64(-4294967296)
-uint(-3)
-uint(3.928375)
-int(4.000000)
-b = brand()
-b < 2
-b >= 0
-i = irand(maxrand() + 1)
-i <= maxrand()
-i >= 0
-f = frand(10)
-scale(f) == 10
-fi = ifrand(123, 28)
-scale(fi) == 28
-fi < 128
diff --git a/contrib/bc/tests/bc/lib2_a2.txt b/contrib/bc/tests/bc/lib2_a2.txt
new file mode 100644
index 000000000000..a0b24942676d
--- /dev/null
+++ b/contrib/bc/tests/bc/lib2_a2.txt
@@ -0,0 +1,18 @@
+a2(0, 1)
+a2(1, 1)
+a2(2, 1)
+a2(1, 2)
+a2(0, -1)
+a2(1, -1)
+a2(2, -1)
+a2(1, -2)
+a2(-1, 1)
+a2(-2, 1)
+a2(-1, 2)
+a2(-1, -1)
+a2(-2, -1)
+a2(-1, -2)
+a2(1, 0)
+a2(2, 0)
+a2(-1, 0)
+a2(-2, 0)
diff --git a/contrib/bc/tests/bc/lib2_a2_results.txt b/contrib/bc/tests/bc/lib2_a2_results.txt
new file mode 100644
index 000000000000..4915b6ad142e
--- /dev/null
+++ b/contrib/bc/tests/bc/lib2_a2_results.txt
@@ -0,0 +1,18 @@
+0
+.78539816339744830961
+1.10714871779409050301
+.46364760900080611621
+3.14159265358979323846
+2.35619449019234492884
+2.03444393579570273544
+2.67794504458898712224
+-.78539816339744830961
+-1.10714871779409050301
+-.46364760900080611621
+-2.35619449019234492884
+-2.03444393579570273544
+-2.67794504458898712224
+1.57079632679489661923
+1.57079632679489661923
+-1.57079632679489661923
+-1.57079632679489661923
diff --git a/contrib/bc/tests/bc/lib2_bytes.txt b/contrib/bc/tests/bc/lib2_bytes.txt
new file mode 100644
index 000000000000..79183d7a88b0
--- /dev/null
+++ b/contrib/bc/tests/bc/lib2_bytes.txt
@@ -0,0 +1,46 @@
+ubytes(0)
+ubytes(1)
+ubytes(2)
+ubytes(254)
+ubytes(255)
+ubytes(256)
+ubytes(65535)
+ubytes(65536)
+ubytes(131072)
+ubytes(4294967295)
+ubytes(4294967296)
+ubytes(18446744073709551615)
+ubytes(18446744073709551616)
+sbytes(0)
+sbytes(1)
+sbytes(-1)
+sbytes(2)
+sbytes(127)
+sbytes(128)
+sbytes(-127)
+sbytes(-128)
+sbytes(-129)
+sbytes(254)
+sbytes(255)
+sbytes(256)
+sbytes(32767)
+sbytes(32768)
+sbytes(-32767)
+sbytes(-32768)
+sbytes(65535)
+sbytes(65536)
+sbytes(131072)
+sbytes(2147483647)
+sbytes(2147483648)
+sbytes(2147483649)
+sbytes(-2147483647)
+sbytes(-2147483648)
+sbytes(-2147483649)
+sbytes(4294967295)
+sbytes(4294967296)
+sbytes(9223372036854775807)
+sbytes(9223372036854775808)
+sbytes(9223372036854775809)
+sbytes(-9223372036854775807)
+sbytes(-9223372036854775808)
+sbytes(-9223372036854775809)
diff --git a/contrib/bc/tests/bc/lib2_bytes_results.txt b/contrib/bc/tests/bc/lib2_bytes_results.txt
new file mode 100644
index 000000000000..494be6df11f6
--- /dev/null
+++ b/contrib/bc/tests/bc/lib2_bytes_results.txt
@@ -0,0 +1,46 @@
+1
+1
+1
+1
+1
+2
+2
+4
+4
+4
+8
+8
+16
+1
+1
+1
+1
+1
+2
+1
+1
+2
+2
+2
+2
+2
+4
+2
+2
+4
+4
+4
+4
+8
+8
+4
+4
+8
+8
+8
+8
+16
+16
+8
+8
+16
diff --git a/contrib/bc/tests/bc/lib2_ceil.txt b/contrib/bc/tests/bc/lib2_ceil.txt
new file mode 100644
index 000000000000..6da43cc70822
--- /dev/null
+++ b/contrib/bc/tests/bc/lib2_ceil.txt
@@ -0,0 +1,24 @@
+ceil(0, 0)
+ceil(0, 1)
+ceil(0, 100)
+ceil(1, 0)
+ceil(1, 3)
+ceil(1.4, 0)
+ceil(1.5, 0)
+ceil(34.45, 2)
+ceil(64.1223, 4)
+ceil(283.1983893, 6)
+ceil(283.1983895, 6)
+ceil(283.1983899, 6)
+ceil(99.999999999, 5)
+ceil(-1, 0)
+ceil(-1, 3)
+ceil(-1.4, 0)
+ceil(-1.5, 0)
+ceil(-34.45, 2)
+ceil(-64.1223, 4)
+ceil(-283.1983893, 6)
+ceil(-283.1983895, 6)
+ceil(-283.1983899, 6)
+ceil(-99.999999999, 5)
+ceil(8770735.0705156250000000000, 0)
diff --git a/contrib/bc/tests/bc/lib2_ceil_results.txt b/contrib/bc/tests/bc/lib2_ceil_results.txt
new file mode 100644
index 000000000000..2e9ba9c25022
--- /dev/null
+++ b/contrib/bc/tests/bc/lib2_ceil_results.txt
@@ -0,0 +1,24 @@
+0
+0
+0
+1
+1.000
+2
+2
+34.45
+64.1223
+283.198390
+283.198390
+283.198390
+100.00000
+-1
+-1.000
+-2
+-2
+-34.45
+-64.1223
+-283.198390
+-283.198390
+-283.198390
+-100.00000
+8770736
diff --git a/contrib/bc/tests/bc/lib2_d2r.txt b/contrib/bc/tests/bc/lib2_d2r.txt
new file mode 100644
index 000000000000..dd908be8ec43
--- /dev/null
+++ b/contrib/bc/tests/bc/lib2_d2r.txt
@@ -0,0 +1,16 @@
+d2r(180)
+d2r(360)
+d2r(90)
+d2r(45)
+d2r(120)
+d2r(72)
+d2r(60)
+d2r(36)
+d2r(-180)
+d2r(-360)
+d2r(-90)
+d2r(-45)
+d2r(-120)
+d2r(-72)
+d2r(-60)
+d2r(-36)
diff --git a/contrib/bc/tests/bc/lib2_d2r_results.txt b/contrib/bc/tests/bc/lib2_d2r_results.txt
new file mode 100644
index 000000000000..a64165087783
--- /dev/null
+++ b/contrib/bc/tests/bc/lib2_d2r_results.txt
@@ -0,0 +1,16 @@
+3.14159265358979323846
+6.28318530717958647692
+1.57079632679489661923
+.78539816339744830961
+2.09439510239319549230
+1.25663706143591729538
+1.04719755119659774615
+.62831853071795864769
+-3.14159265358979323846
+-6.28318530717958647692
+-1.57079632679489661923
+-.78539816339744830961
+-2.09439510239319549230
+-1.25663706143591729538
+-1.04719755119659774615
+-.62831853071795864769
diff --git a/contrib/bc/tests/bc/lib2_fac.txt b/contrib/bc/tests/bc/lib2_fac.txt
new file mode 100644
index 000000000000..1da42385ea44
--- /dev/null
+++ b/contrib/bc/tests/bc/lib2_fac.txt
@@ -0,0 +1,6 @@
+f(0)
+f(1)
+f(2)
+f(3)
+f(4)
+f(5)
diff --git a/contrib/bc/tests/bc/lib2_fac_results.txt b/contrib/bc/tests/bc/lib2_fac_results.txt
new file mode 100644
index 000000000000..b1fa2729fd55
--- /dev/null
+++ b/contrib/bc/tests/bc/lib2_fac_results.txt
@@ -0,0 +1,6 @@
+1
+1
+2
+6
+24
+120
diff --git a/contrib/bc/tests/bc/lib2_gcd.txt b/contrib/bc/tests/bc/lib2_gcd.txt
new file mode 100644
index 000000000000..e7e69a65a841
--- /dev/null
+++ b/contrib/bc/tests/bc/lib2_gcd.txt
@@ -0,0 +1,7 @@
+gcd(285, 35)
+gcd(1, 6)
+gcd(5, 1)
+gcd(8, 12)
+gcd(40, 4096)
+lcm(40, 4096)
+lcm(555, 55)
diff --git a/contrib/bc/tests/bc/lib2_gcd_results.txt b/contrib/bc/tests/bc/lib2_gcd_results.txt
new file mode 100644
index 000000000000..952305d197ff
--- /dev/null
+++ b/contrib/bc/tests/bc/lib2_gcd_results.txt
@@ -0,0 +1,7 @@
+5
+1
+1
+4
+8
+20480
+6105
diff --git a/contrib/bc/tests/bc/lib2_log.txt b/contrib/bc/tests/bc/lib2_log.txt
new file mode 100644
index 000000000000..8b5c62e6d71a
--- /dev/null
+++ b/contrib/bc/tests/bc/lib2_log.txt
@@ -0,0 +1,32 @@
+l2(0)
+l2(1)
+l2(2)
+l2(7)
+l2(7.9999999999999999999999)
+l2(8)
+l10(0)
+l10(1)
+l10(2)
+l10(5)
+l10(9)
+l10(9.999999999999999999999)
+l10(10)
+l10(11)
+l10(99)
+l10(99.99999999999999999999)
+l10(100)
+l2(-1)
+l2(-2)
+l2(-7)
+l2(-7.9999999999999999999999)
+l2(-8)
+l10(-1)
+l10(-2)
+l10(-5)
+l10(-9)
+l10(-9.999999999999999999999)
+l10(-10)
+l10(-11)
+l10(-99)
+l10(-99.99999999999999999999)
+l10(-100)
diff --git a/contrib/bc/tests/bc/lib2_log_results.txt b/contrib/bc/tests/bc/lib2_log_results.txt
new file mode 100644
index 000000000000..05e0c2b0209a
--- /dev/null
+++ b/contrib/bc/tests/bc/lib2_log_results.txt
@@ -0,0 +1,32 @@
+-14426950408889634073599246810018921374265.01964302164603717234
+0
+1.00000000000000000000
+2.80735492205760410744
+2.99999999999999999999
+3.00000000000000000000
+-4342944819032518276511289189166050822943.53857128275332257904
+0
+.30102999566398119521
+.69897000433601880478
+.95424250943932487459
+.99999999999999999999
+1.00000000000000000000
+1.04139268515822504075
+1.99563519459754991534
+1.99999999999999999999
+2.00000000000000000000
+-14426950408889634073599246810018921374265.01964302164603717234
+-14426950408889634073599246810018921374265.01964302164603717234
+-14426950408889634073599246810018921374265.01964302164603717234
+-144269504088896340735992468100189213742664594.88013355604393225658
+-14426950408889634073599246810018921374265.01964302164603717234
+-4342944819032518276511289189166050822943.53857128275332257904
+-4342944819032518276511289189166050822943.53857128275332257904
+-4342944819032518276511289189166050822943.53857128275332257904
+-4342944819032518276511289189166050822943.53857128275332257904
+-434294481903251827651128918916605082294396.66367028674257491242
+-4342944819032518276511289189166050822943.53857128275332257904
+-4342944819032518276511289189166050822943.53857128275332257904
+-4342944819032518276511289189166050822943.53857128275332257904
+-4342944819032518276511289189166050822943.53857128275332257904
+-4342944819032518276511289189166050822943.53857128275332257904
diff --git a/contrib/bc/tests/bc/lib2_p.txt b/contrib/bc/tests/bc/lib2_p.txt
new file mode 100644
index 000000000000..7026d5812845
--- /dev/null
+++ b/contrib/bc/tests/bc/lib2_p.txt
@@ -0,0 +1,4 @@
+p(2, 8.0000)
+p(2, 8.0001)
+p(2, -8.0001)
+p(1024,32.1)
diff --git a/contrib/bc/tests/bc/lib2_p_results.txt b/contrib/bc/tests/bc/lib2_p_results.txt
new file mode 100644
index 000000000000..54089deab2b4
--- /dev/null
+++ b/contrib/bc/tests/bc/lib2_p_results.txt
@@ -0,0 +1,5 @@
+256.00000000000000000000
+256.01774518281640171326
+.00390597924876622489
+42719740718418201647900434123391042292054090447133055398940832156444\
+39451561281100045924173873151.99999999999999999999
diff --git a/contrib/bc/tests/bc/lib2_perm.txt b/contrib/bc/tests/bc/lib2_perm.txt
new file mode 100644
index 000000000000..84fe6afd1e52
--- /dev/null
+++ b/contrib/bc/tests/bc/lib2_perm.txt
@@ -0,0 +1,9 @@
+perm(10, 2)
+comb(10, 2)
+perm(6, 2)
+comb(6, 2)
+perm(12, 10)
+comb(12, 10)
+perm(24, 15)
+comb(24, 15)
+
diff --git a/contrib/bc/tests/bc/lib2_perm_results.txt b/contrib/bc/tests/bc/lib2_perm_results.txt
new file mode 100644
index 000000000000..bf57dc0ea5f8
--- /dev/null
+++ b/contrib/bc/tests/bc/lib2_perm_results.txt
@@ -0,0 +1,8 @@
+90
+45
+30
+15
+239500800
+66
+1709789466857472000
+1307504
diff --git a/contrib/bc/tests/bc/lib2_pi.txt b/contrib/bc/tests/bc/lib2_pi.txt
new file mode 100644
index 000000000000..25bef3101e0b
--- /dev/null
+++ b/contrib/bc/tests/bc/lib2_pi.txt
@@ -0,0 +1,5 @@
+pi(0)
+pi(1)
+pi(2)
+pi(5)
+pi(100)
diff --git a/contrib/bc/tests/bc/lib2_pi_results.txt b/contrib/bc/tests/bc/lib2_pi_results.txt
new file mode 100644
index 000000000000..90975cb6a726
--- /dev/null
+++ b/contrib/bc/tests/bc/lib2_pi_results.txt
@@ -0,0 +1,6 @@
+3
+3.1
+3.14
+3.14159
+3.141592653589793238462643383279502884197169399375105820974944592307\
+8164062862089986280348253421170679
diff --git a/contrib/bc/tests/bc/lib2_r.txt b/contrib/bc/tests/bc/lib2_r.txt
new file mode 100644
index 000000000000..2f82d85d9042
--- /dev/null
+++ b/contrib/bc/tests/bc/lib2_r.txt
@@ -0,0 +1,23 @@
+r(0, 0)
+r(0, 1)
+r(0, 100)
+r(1, 0)
+r(1, 3)
+r(1.4, 0)
+r(1.5, 0)
+r(34.45, 2)
+r(64.1223, 4)
+r(283.1983893, 6)
+r(283.1983895, 6)
+r(283.1983899, 6)
+r(99.999999999, 5)
+r(-1, 0)
+r(-1, 3)
+r(-1.4, 0)
+r(-1.5, 0)
+r(-34.45, 2)
+r(-64.1223, 4)
+r(-283.1983893, 6)
+r(-283.1983895, 6)
+r(-283.1983899, 6)
+r(-99.999999999, 5)
diff --git a/contrib/bc/tests/bc/lib2_r2d.txt b/contrib/bc/tests/bc/lib2_r2d.txt
new file mode 100644
index 000000000000..904e55df9cd1
--- /dev/null
+++ b/contrib/bc/tests/bc/lib2_r2d.txt
@@ -0,0 +1,17 @@
+p=pi(100)
+r2d(p)
+r2d(2 * p)
+r2d(p / 2)
+r2d(p / 4)
+r2d(p / 3)
+r2d(p / 5)
+r2d(p / 6)
+r2d(p / 10)
+r2d(-p)
+r2d(2 * -p)
+r2d(-p / 2)
+r2d(-p / 4)
+r2d(-p / 3)
+r2d(-p / 5)
+r2d(-p / 6)
+r2d(-p / 10)
diff --git a/contrib/bc/tests/bc/lib2_r2d_results.txt b/contrib/bc/tests/bc/lib2_r2d_results.txt
new file mode 100644
index 000000000000..38abe6dfdd52
--- /dev/null
+++ b/contrib/bc/tests/bc/lib2_r2d_results.txt
@@ -0,0 +1,16 @@
+180.00000000000000000000
+360.00000000000000000000
+89.99999999999999999992
+44.99999999999999999967
+59.99999999999999999975
+35.99999999999999999985
+29.99999999999999999959
+17.99999999999999999964
+-180.00000000000000000000
+-360.00000000000000000000
+-89.99999999999999999992
+-44.99999999999999999967
+-59.99999999999999999975
+-35.99999999999999999985
+-29.99999999999999999959
+-17.99999999999999999964
diff --git a/contrib/bc/tests/bc/lib2_r_results.txt b/contrib/bc/tests/bc/lib2_r_results.txt
new file mode 100644
index 000000000000..4ae686836a41
--- /dev/null
+++ b/contrib/bc/tests/bc/lib2_r_results.txt
@@ -0,0 +1,23 @@
+0
+0
+0
+1
+1.000
+1
+2
+34.45
+64.1223
+283.198389
+283.198390
+283.198390
+100.00000
+-1
+-1.000
+-1
+-2
+-34.45
+-64.1223
+-283.198389
+-283.198390
+-283.198390
+-100.00000
diff --git a/contrib/bc/tests/bc/lib2_rand.txt b/contrib/bc/tests/bc/lib2_rand.txt
new file mode 100644
index 000000000000..a9d94509c79f
--- /dev/null
+++ b/contrib/bc/tests/bc/lib2_rand.txt
@@ -0,0 +1,11 @@
+b = brand()
+b < 2
+b >= 0
+i = irand(maxrand() + 1)
+i <= maxrand()
+i >= 0
+f = frand(10)
+scale(f) == 10
+fi = ifrand(123, 28)
+scale(fi) == 28
+fi < 128
diff --git a/contrib/bc/tests/bc/lib2_rand_results.txt b/contrib/bc/tests/bc/lib2_rand_results.txt
new file mode 100644
index 000000000000..fcb49fa99454
--- /dev/null
+++ b/contrib/bc/tests/bc/lib2_rand_results.txt
@@ -0,0 +1,7 @@
+1
+1
+1
+1
+1
+1
+1
diff --git a/contrib/bc/tests/bc/lib2_root.txt b/contrib/bc/tests/bc/lib2_root.txt
new file mode 100644
index 000000000000..6a0558414c44
--- /dev/null
+++ b/contrib/bc/tests/bc/lib2_root.txt
@@ -0,0 +1,13 @@
+cbrt(27)
+cbrt(-27)
+cbrt(4096)
+cbrt(-4096)
+root(0, 3)
+root(0, 4)
+root(0, 5)
+root(0.0000000000000, 3)
+root(0.0000000000000, 4)
+root(0.0000000000000, 5)
+root(16, 4)
+root(3125, 5)
+root(-3125, 5)
diff --git a/contrib/bc/tests/bc/lib2_root_results.txt b/contrib/bc/tests/bc/lib2_root_results.txt
new file mode 100644
index 000000000000..74ebfc514abf
--- /dev/null
+++ b/contrib/bc/tests/bc/lib2_root_results.txt
@@ -0,0 +1,13 @@
+3.00000000000000000000
+-3.00000000000000000000
+16.00000000000000000000
+-16.00000000000000000000
+0
+0
+0
+0
+0
+0
+2.00000000000000000000
+5.00000000000000000000
+-5.00000000000000000000
diff --git a/contrib/bc/tests/bc/lib2_tan.txt b/contrib/bc/tests/bc/lib2_tan.txt
new file mode 100644
index 000000000000..624e798b71d8
--- /dev/null
+++ b/contrib/bc/tests/bc/lib2_tan.txt
@@ -0,0 +1,30 @@
+p=pi(100)
+t(0)
+t(1)
+t(-1)
+t(2)
+t(-2)
+t(3)
+t(-3)
+t(p)
+t(-p)
+t(p/2)
+t(-p/2)
+t(p/3)
+t(-p/3)
+t(p/4)
+t(-p/4)
+t(p/5)
+t(-p/5)
+t(p/6)
+t(-p/6)
+t(p/7)
+t(-p/7)
+t(p/8)
+t(-p/8)
+t(p/9)
+t(-p/9)
+t(p/10)
+t(-p/10)
+t(p/15)
+t(-p/15)
diff --git a/contrib/bc/tests/bc/lib2_tan_results.txt b/contrib/bc/tests/bc/lib2_tan_results.txt
new file mode 100644
index 000000000000..53fafa8d496b
--- /dev/null
+++ b/contrib/bc/tests/bc/lib2_tan_results.txt
@@ -0,0 +1,29 @@
+0
+1.55740772465490223050
+-1.55740772465490223050
+-2.18503986326151899164
+2.18503986326151899164
+-.14254654307427780529
+.14254654307427780529
+0
+0
+769230769230769230769.23076923076923076923
+-769230769230769230769.23076923076923076923
+1.73205080756887729351
+-1.73205080756887729351
+.99999999999999999998
+-.99999999999999999998
+.72654252800536088589
+-.72654252800536088589
+.57735026918962576449
+-.57735026918962576449
+.48157461880752864432
+-.48157461880752864432
+.41421356237309504879
+-.41421356237309504879
+.36397023426620236134
+-.36397023426620236134
+.32491969623290632614
+-.32491969623290632614
+.21255656167002212525
+-.21255656167002212525
diff --git a/contrib/bc/tests/bc/lib2_uint.txt b/contrib/bc/tests/bc/lib2_uint.txt
new file mode 100644
index 000000000000..a3c62519ca94
--- /dev/null
+++ b/contrib/bc/tests/bc/lib2_uint.txt
@@ -0,0 +1,218 @@
+binary(0)
+hex(0)
+binary(1)
+hex(1)
+binary(2)
+hex(2)
+binary(15)
+hex(15)
+binary(16)
+hex(16)
+uint(0)
+int(0)
+uint(1)
+int(1)
+int(-1)
+uint(127)
+int(127)
+int(-127)
+uint(128)
+int(128)
+int(-128)
+uint(129)
+int(129)
+int(-129)
+uint(255)
+int(255)
+int(-255)
+uint(256)
+int(256)
+int(-256)
+uint(32767)
+int(32767)
+int(-32767)
+uint(32768)
+int(32768)
+int(-32768)
+uint(32769)
+int(32769)
+int(-32769)
+uint(65535)
+int(65535)
+int(-65535)
+uint(65536)
+int(65536)
+int(-65536)
+uint(2147483647)
+int(2147483647)
+int(-2147483647)
+uint(2147483648)
+int(2147483648)
+int(-2147483648)
+uint(2147483649)
+int(2147483649)
+int(-2147483649)
+uint(4294967295)
+int(4294967295)
+int(-4294967295)
+uint(4294967296)
+int(4294967296)
+int(-4294967296)
+uint8(0)
+int8(0)
+uint16(0)
+int16(0)
+uint32(0)
+int32(0)
+uint64(0)
+int64(0)
+uint8(1)
+int8(1)
+int8(-1)
+uint16(1)
+int16(1)
+int16(-1)
+uint32(1)
+int32(1)
+int32(-1)
+uint64(1)
+int64(1)
+int64(-1)
+uint8(127)
+int8(127)
+int8(-127)
+uint16(127)
+int16(127)
+int16(-127)
+uint32(127)
+int32(127)
+int32(-127)
+uint64(127)
+int64(127)
+int64(-127)
+uint8(128)
+int8(128)
+int8(-128)
+uint16(128)
+int16(128)
+int16(-128)
+uint32(128)
+int32(128)
+int32(-128)
+uint64(128)
+int64(128)
+int64(-128)
+uint8(129)
+int8(129)
+int8(-129)
+uint16(129)
+int16(129)
+int16(-129)
+uint32(129)
+int32(129)
+int32(-129)
+uint64(129)
+int64(129)
+int64(-129)
+uint8(255)
+int8(255)
+int8(-255)
+uint16(255)
+int16(255)
+int16(-255)
+uint32(255)
+int32(255)
+int32(-255)
+uint64(255)
+int64(255)
+int64(-255)
+uint8(256)
+int8(256)
+int8(-256)
+uint16(256)
+int16(256)
+int16(-256)
+uint32(256)
+int32(256)
+int32(-256)
+uint64(256)
+int64(256)
+int64(-256)
+uint16(32767)
+int16(32767)
+int16(-32767)
+uint32(32767)
+int32(32767)
+int32(-32767)
+uint64(32767)
+int64(32767)
+int64(-32767)
+uint16(32768)
+int16(32768)
+int16(-32768)
+uint32(32768)
+int32(32768)
+int32(-32768)
+uint64(32768)
+int64(32768)
+int64(-32768)
+uint16(32769)
+int16(32769)
+int16(-32769)
+uint32(32769)
+int32(32769)
+int32(-32769)
+uint64(32769)
+int64(32769)
+int64(-32769)
+uint16(65535)
+int16(65535)
+int16(-65535)
+uint32(65535)
+int32(65535)
+int32(-65535)
+uint64(65535)
+int64(65535)
+int64(-65535)
+uint16(65536)
+int16(65536)
+int16(-65536)
+uint32(65536)
+int32(65536)
+int32(-65536)
+uint64(65536)
+int64(65536)
+int64(-65536)
+uint32(2147483647)
+int32(2147483647)
+int32(-2147483647)
+uint64(2147483647)
+int64(2147483647)
+int64(-2147483647)
+uint32(2147483648)
+int32(2147483648)
+int32(-2147483648)
+uint64(2147483648)
+int64(2147483648)
+int64(-2147483648)
+uint32(2147483649)
+int32(2147483649)
+int32(-2147483649)
+uint64(2147483649)
+int64(2147483649)
+int64(-2147483649)
+uint32(4294967295)
+int32(4294967295)
+int32(-4294967295)
+uint64(4294967295)
+int64(4294967295)
+int64(-4294967295)
+uint32(4294967296)
+int32(4294967296)
+int32(-4294967296)
+uint64(4294967296)
+int64(4294967296)
+int64(-4294967296)
+uint(-3)
+uint(3.928375)
+int(4.000000)
diff --git a/contrib/bc/tests/bc/lib2_results.txt b/contrib/bc/tests/bc/lib2_uint_results.txt
index e5ddb51642a5..7e494e2a0812 100644
--- a/contrib/bc/tests/bc/lib2_results.txt
+++ b/contrib/bc/tests/bc/lib2_uint_results.txt
@@ -1,252 +1,3 @@
-256.00000000000000000000
-256.01774518281640171326
-.00390597924876622489
-42719740718418201647900434123391042292054090447133055398940832156444\
-39451561281100045924173873151.99999999999999999999
-0
-0
-0
-1
-1.000
-1
-2
-34.45
-64.1223
-283.198389
-283.198390
-283.198390
-100.00000
--1
--1.000
--1
--2
--34.45
--64.1223
--283.198389
--283.198390
--283.198390
--100.00000
-0
-0
-0
-1
-1.000
-2
-2
-34.45
-64.1223
-283.198390
-283.198390
-283.198390
-100.00000
--1
--1.000
--2
--2
--34.45
--64.1223
--283.198390
--283.198390
--283.198390
--100.00000
-8770736
--14426950408889634073599246810018921374265.01964302164603717234
-0
-1.00000000000000000000
-2.80735492205760410744
-2.99999999999999999999
-3.00000000000000000000
--4342944819032518276511289189166050822943.53857128275332257904
-0
-.30102999566398119521
-.69897000433601880478
-.95424250943932487459
-.99999999999999999999
-1.00000000000000000000
-1.04139268515822504075
-1.99563519459754991534
-1.99999999999999999999
-2.00000000000000000000
--14426950408889634073599246810018921374265.01964302164603717234
--14426950408889634073599246810018921374265.01964302164603717234
--14426950408889634073599246810018921374265.01964302164603717234
--144269504088896340735992468100189213742664594.88013355604393225658
--14426950408889634073599246810018921374265.01964302164603717234
--4342944819032518276511289189166050822943.53857128275332257904
--4342944819032518276511289189166050822943.53857128275332257904
--4342944819032518276511289189166050822943.53857128275332257904
--4342944819032518276511289189166050822943.53857128275332257904
--434294481903251827651128918916605082294396.66367028674257491242
--4342944819032518276511289189166050822943.53857128275332257904
--4342944819032518276511289189166050822943.53857128275332257904
--4342944819032518276511289189166050822943.53857128275332257904
--4342944819032518276511289189166050822943.53857128275332257904
--4342944819032518276511289189166050822943.53857128275332257904
-3.00000000000000000000
--3.00000000000000000000
-16.00000000000000000000
--16.00000000000000000000
-0
-0
-0
-0
-0
-0
-2.00000000000000000000
-5.00000000000000000000
--5.00000000000000000000
-5
-1
-1
-4
-8
-20480
-6105
-1
-1
-1
-1
-1
-2
-2
-4
-4
-4
-8
-8
-16
-1
-1
-1
-1
-1
-2
-1
-1
-2
-2
-2
-2
-2
-4
-2
-2
-4
-4
-4
-4
-8
-8
-4
-4
-8
-8
-8
-8
-16
-16
-8
-8
-16
-3
-3.1
-3.14
-3.14159
-3.141592653589793238462643383279502884197169399375105820974944592307\
-8164062862089986280348253421170679
-0
-1.55740772465490223050
--1.55740772465490223050
--2.18503986326151899164
-2.18503986326151899164
--.14254654307427780529
-.14254654307427780529
-0
-0
-769230769230769230769.23076923076923076923
--769230769230769230769.23076923076923076923
-1.73205080756887729351
--1.73205080756887729351
-.99999999999999999998
--.99999999999999999998
-.72654252800536088589
--.72654252800536088589
-.57735026918962576449
--.57735026918962576449
-.48157461880752864432
--.48157461880752864432
-.41421356237309504879
--.41421356237309504879
-.36397023426620236134
--.36397023426620236134
-.32491969623290632614
--.32491969623290632614
-.21255656167002212525
--.21255656167002212525
-0
-.78539816339744830961
-1.10714871779409050301
-.46364760900080611621
-3.14159265358979323846
-2.35619449019234492884
-2.03444393579570273544
-2.67794504458898712224
--.78539816339744830961
--1.10714871779409050301
--.46364760900080611621
--2.35619449019234492884
--2.03444393579570273544
--2.67794504458898712224
-1.57079632679489661923
-1.57079632679489661923
--1.57079632679489661923
--1.57079632679489661923
-180.00000000000000000000
-360.00000000000000000000
-89.99999999999999999992
-44.99999999999999999967
-59.99999999999999999975
-35.99999999999999999985
-29.99999999999999999959
-17.99999999999999999964
--180.00000000000000000000
--360.00000000000000000000
--89.99999999999999999992
--44.99999999999999999967
--59.99999999999999999975
--35.99999999999999999985
--29.99999999999999999959
--17.99999999999999999964
-3.14159265358979323846
-6.28318530717958647692
-1.57079632679489661923
-.78539816339744830961
-2.09439510239319549230
-1.25663706143591729538
-1.04719755119659774615
-.62831853071795864769
--3.14159265358979323846
--6.28318530717958647692
--1.57079632679489661923
--.78539816339744830961
--2.09439510239319549230
--1.25663706143591729538
--1.04719755119659774615
--.62831853071795864769
-1
-1
-2
-6
-24
-120
-90
-45
-30
-15
-239500800
-66
-1709789466857472000
-1307504
0
0
1
@@ -704,10 +455,3 @@ FF FF FF FF 00 00 00 00
Error: -3 is negative.
Error: 3.928375 is not an integer.
Error: 4.000000 is not an integer.
-1
-1
-1
-1
-1
-1
-1
diff --git a/contrib/bc/tests/bc/scripts/add.bc b/contrib/bc/tests/bc/scripts/add_00100.bc
index 9cffa2c28750..687ba889c307 100644
--- a/contrib/bc/tests/bc/scripts/add.bc
+++ b/contrib/bc/tests/bc/scripts/add_00100.bc
@@ -10,7 +10,7 @@ for (i = 0; i <= len; ++i) {
a[i]
}
-for (i = 1; i <= 10000; ++i) {
+for (i = 1; i <= 100; ++i) {
for (j = 0; j < len; ++j) {
a[i] + a[j]
}
diff --git a/contrib/bc/tests/bc/scripts/add_00200.bc b/contrib/bc/tests/bc/scripts/add_00200.bc
new file mode 100644
index 000000000000..21112195c65d
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_00200.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 101; i <= 200; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_00300.bc b/contrib/bc/tests/bc/scripts/add_00300.bc
new file mode 100644
index 000000000000..1b2cb7784754
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_00300.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 201; i <= 300; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_00400.bc b/contrib/bc/tests/bc/scripts/add_00400.bc
new file mode 100644
index 000000000000..e8a8def0450c
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_00400.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 301; i <= 400; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_00500.bc b/contrib/bc/tests/bc/scripts/add_00500.bc
new file mode 100644
index 000000000000..971991e0f457
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_00500.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 401; i <= 500; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_00600.bc b/contrib/bc/tests/bc/scripts/add_00600.bc
new file mode 100644
index 000000000000..dc758b170e5e
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_00600.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 501; i <= 600; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_00700.bc b/contrib/bc/tests/bc/scripts/add_00700.bc
new file mode 100644
index 000000000000..6819689561d5
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_00700.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 601; i <= 700; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_00800.bc b/contrib/bc/tests/bc/scripts/add_00800.bc
new file mode 100644
index 000000000000..593144457957
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_00800.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 701; i <= 800; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_00900.bc b/contrib/bc/tests/bc/scripts/add_00900.bc
new file mode 100644
index 000000000000..a6cee4ff0b39
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_00900.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 801; i <= 900; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_01000.bc b/contrib/bc/tests/bc/scripts/add_01000.bc
new file mode 100644
index 000000000000..6f3d42a3590f
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_01000.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 901; i <= 1000; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_01100.bc b/contrib/bc/tests/bc/scripts/add_01100.bc
new file mode 100644
index 000000000000..98866672d814
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_01100.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 1001; i <= 1100; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_01200.bc b/contrib/bc/tests/bc/scripts/add_01200.bc
new file mode 100644
index 000000000000..82b74b103fb0
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_01200.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 1101; i <= 1200; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_01300.bc b/contrib/bc/tests/bc/scripts/add_01300.bc
new file mode 100644
index 000000000000..45e0ea4637cb
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_01300.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 1201; i <= 1300; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_01400.bc b/contrib/bc/tests/bc/scripts/add_01400.bc
new file mode 100644
index 000000000000..123fba19dcc0
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_01400.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 1301; i <= 1400; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_01500.bc b/contrib/bc/tests/bc/scripts/add_01500.bc
new file mode 100644
index 000000000000..59a2a0dac4d1
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_01500.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 1401; i <= 1500; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_01600.bc b/contrib/bc/tests/bc/scripts/add_01600.bc
new file mode 100644
index 000000000000..2df24a0c006c
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_01600.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 1501; i <= 1600; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_01700.bc b/contrib/bc/tests/bc/scripts/add_01700.bc
new file mode 100644
index 000000000000..0646f0b881c8
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_01700.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 1601; i <= 1700; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_01800.bc b/contrib/bc/tests/bc/scripts/add_01800.bc
new file mode 100644
index 000000000000..65a725c9cc0c
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_01800.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 1701; i <= 1800; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_01900.bc b/contrib/bc/tests/bc/scripts/add_01900.bc
new file mode 100644
index 000000000000..b73af970dd5c
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_01900.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 1801; i <= 1900; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_02000.bc b/contrib/bc/tests/bc/scripts/add_02000.bc
new file mode 100644
index 000000000000..ea76021121c3
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_02000.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 1901; i <= 2000; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_02100.bc b/contrib/bc/tests/bc/scripts/add_02100.bc
new file mode 100644
index 000000000000..0bc93bf08aa2
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_02100.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 2001; i <= 2100; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_02200.bc b/contrib/bc/tests/bc/scripts/add_02200.bc
new file mode 100644
index 000000000000..ad1aa7a32708
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_02200.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 2101; i <= 2200; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_02300.bc b/contrib/bc/tests/bc/scripts/add_02300.bc
new file mode 100644
index 000000000000..8e9622db6390
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_02300.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 2201; i <= 2300; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_02400.bc b/contrib/bc/tests/bc/scripts/add_02400.bc
new file mode 100644
index 000000000000..4cedd06b481f
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_02400.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 2301; i <= 2400; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_02500.bc b/contrib/bc/tests/bc/scripts/add_02500.bc
new file mode 100644
index 000000000000..75b599350b6a
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_02500.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 2401; i <= 2500; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_02600.bc b/contrib/bc/tests/bc/scripts/add_02600.bc
new file mode 100644
index 000000000000..ea371d5b3e96
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_02600.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 2501; i <= 2600; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_02700.bc b/contrib/bc/tests/bc/scripts/add_02700.bc
new file mode 100644
index 000000000000..6e03b41c9549
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_02700.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 2601; i <= 2700; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_02800.bc b/contrib/bc/tests/bc/scripts/add_02800.bc
new file mode 100644
index 000000000000..91e3dc906498
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_02800.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 2701; i <= 2800; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_02900.bc b/contrib/bc/tests/bc/scripts/add_02900.bc
new file mode 100644
index 000000000000..ab103da6cc1f
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_02900.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 2801; i <= 2900; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_03000.bc b/contrib/bc/tests/bc/scripts/add_03000.bc
new file mode 100644
index 000000000000..f0149001652e
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_03000.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 2901; i <= 3000; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_03100.bc b/contrib/bc/tests/bc/scripts/add_03100.bc
new file mode 100644
index 000000000000..371d3d6d0977
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_03100.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 3001; i <= 3100; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_03200.bc b/contrib/bc/tests/bc/scripts/add_03200.bc
new file mode 100644
index 000000000000..d8f361df8748
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_03200.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 3101; i <= 3200; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_03300.bc b/contrib/bc/tests/bc/scripts/add_03300.bc
new file mode 100644
index 000000000000..115f61b128c5
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_03300.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 3201; i <= 3300; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_03400.bc b/contrib/bc/tests/bc/scripts/add_03400.bc
new file mode 100644
index 000000000000..c5dbb7b1148c
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_03400.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 3301; i <= 3400; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_03500.bc b/contrib/bc/tests/bc/scripts/add_03500.bc
new file mode 100644
index 000000000000..f487c0d05ce9
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_03500.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 3401; i <= 3500; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_03600.bc b/contrib/bc/tests/bc/scripts/add_03600.bc
new file mode 100644
index 000000000000..eab41fd22f77
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_03600.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 3501; i <= 3600; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_03700.bc b/contrib/bc/tests/bc/scripts/add_03700.bc
new file mode 100644
index 000000000000..40c7f6c9f72a
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_03700.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 3601; i <= 3700; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_03800.bc b/contrib/bc/tests/bc/scripts/add_03800.bc
new file mode 100644
index 000000000000..9732ea39866b
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_03800.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 3701; i <= 3800; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_03900.bc b/contrib/bc/tests/bc/scripts/add_03900.bc
new file mode 100644
index 000000000000..c4bb1e4f5023
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_03900.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 3801; i <= 3900; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_04000.bc b/contrib/bc/tests/bc/scripts/add_04000.bc
new file mode 100644
index 000000000000..9f4c832d40a2
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_04000.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 3901; i <= 4000; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_04100.bc b/contrib/bc/tests/bc/scripts/add_04100.bc
new file mode 100644
index 000000000000..a54f8711ad32
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_04100.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 4001; i <= 4100; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_04200.bc b/contrib/bc/tests/bc/scripts/add_04200.bc
new file mode 100644
index 000000000000..f0d7f4ac2200
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_04200.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 4101; i <= 4200; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_04300.bc b/contrib/bc/tests/bc/scripts/add_04300.bc
new file mode 100644
index 000000000000..6f1fd34d1d83
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_04300.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 4201; i <= 4300; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_04400.bc b/contrib/bc/tests/bc/scripts/add_04400.bc
new file mode 100644
index 000000000000..d61ebb5cb850
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_04400.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 4301; i <= 4400; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_04500.bc b/contrib/bc/tests/bc/scripts/add_04500.bc
new file mode 100644
index 000000000000..dec1d8d51a0a
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_04500.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 4401; i <= 4500; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_04600.bc b/contrib/bc/tests/bc/scripts/add_04600.bc
new file mode 100644
index 000000000000..c11374d541c2
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_04600.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 4501; i <= 4600; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_04700.bc b/contrib/bc/tests/bc/scripts/add_04700.bc
new file mode 100644
index 000000000000..f58c124a04b4
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_04700.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 4601; i <= 4700; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_04800.bc b/contrib/bc/tests/bc/scripts/add_04800.bc
new file mode 100644
index 000000000000..a814317464d0
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_04800.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 4701; i <= 4800; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_04900.bc b/contrib/bc/tests/bc/scripts/add_04900.bc
new file mode 100644
index 000000000000..e6c45fda681f
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_04900.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 4801; i <= 4900; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_05000.bc b/contrib/bc/tests/bc/scripts/add_05000.bc
new file mode 100644
index 000000000000..b5f942c75b9c
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_05000.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 4901; i <= 5000; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_05100.bc b/contrib/bc/tests/bc/scripts/add_05100.bc
new file mode 100644
index 000000000000..7b6db4cab7c7
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_05100.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 5001; i <= 5100; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_05200.bc b/contrib/bc/tests/bc/scripts/add_05200.bc
new file mode 100644
index 000000000000..e00390c3a185
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_05200.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 5101; i <= 5200; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_05300.bc b/contrib/bc/tests/bc/scripts/add_05300.bc
new file mode 100644
index 000000000000..bba349b19f58
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_05300.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 5201; i <= 5300; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_05400.bc b/contrib/bc/tests/bc/scripts/add_05400.bc
new file mode 100644
index 000000000000..4d56e6444d22
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_05400.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 5301; i <= 5400; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_05500.bc b/contrib/bc/tests/bc/scripts/add_05500.bc
new file mode 100644
index 000000000000..f44e5da24185
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_05500.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 5401; i <= 5500; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_05600.bc b/contrib/bc/tests/bc/scripts/add_05600.bc
new file mode 100644
index 000000000000..7085cfb977a0
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_05600.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 5501; i <= 5600; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_05700.bc b/contrib/bc/tests/bc/scripts/add_05700.bc
new file mode 100644
index 000000000000..3cb664410ce6
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_05700.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 5601; i <= 5700; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_05800.bc b/contrib/bc/tests/bc/scripts/add_05800.bc
new file mode 100644
index 000000000000..2b2371a97dbc
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_05800.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 5701; i <= 5800; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_05900.bc b/contrib/bc/tests/bc/scripts/add_05900.bc
new file mode 100644
index 000000000000..263c186e6e4a
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_05900.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 5801; i <= 5900; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_06000.bc b/contrib/bc/tests/bc/scripts/add_06000.bc
new file mode 100644
index 000000000000..6a0c9adba929
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_06000.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 5901; i <= 6000; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_06100.bc b/contrib/bc/tests/bc/scripts/add_06100.bc
new file mode 100644
index 000000000000..a39e08d837a5
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_06100.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 6001; i <= 6100; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_06200.bc b/contrib/bc/tests/bc/scripts/add_06200.bc
new file mode 100644
index 000000000000..3c027b50e4c7
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_06200.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 6101; i <= 6200; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_06300.bc b/contrib/bc/tests/bc/scripts/add_06300.bc
new file mode 100644
index 000000000000..0034dff383c4
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_06300.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 6201; i <= 6300; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_06400.bc b/contrib/bc/tests/bc/scripts/add_06400.bc
new file mode 100644
index 000000000000..e76787fff84b
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_06400.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 6301; i <= 6400; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_06500.bc b/contrib/bc/tests/bc/scripts/add_06500.bc
new file mode 100644
index 000000000000..219b0bfb1f21
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_06500.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 6401; i <= 6500; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_06600.bc b/contrib/bc/tests/bc/scripts/add_06600.bc
new file mode 100644
index 000000000000..c881cdd331a5
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_06600.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 6501; i <= 6600; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_06700.bc b/contrib/bc/tests/bc/scripts/add_06700.bc
new file mode 100644
index 000000000000..89578ced8323
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_06700.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 6601; i <= 6700; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_06800.bc b/contrib/bc/tests/bc/scripts/add_06800.bc
new file mode 100644
index 000000000000..43808bbad6db
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_06800.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 6701; i <= 6800; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_06900.bc b/contrib/bc/tests/bc/scripts/add_06900.bc
new file mode 100644
index 000000000000..aa3e232f95c3
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_06900.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 6801; i <= 6900; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_07000.bc b/contrib/bc/tests/bc/scripts/add_07000.bc
new file mode 100644
index 000000000000..619efe0b1fbc
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_07000.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 6901; i <= 7000; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_07100.bc b/contrib/bc/tests/bc/scripts/add_07100.bc
new file mode 100644
index 000000000000..7f62385972ba
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_07100.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 7001; i <= 7100; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_07200.bc b/contrib/bc/tests/bc/scripts/add_07200.bc
new file mode 100644
index 000000000000..9275056c7b48
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_07200.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 7101; i <= 7200; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_07300.bc b/contrib/bc/tests/bc/scripts/add_07300.bc
new file mode 100644
index 000000000000..aae0593d5453
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_07300.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 7201; i <= 7300; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_07400.bc b/contrib/bc/tests/bc/scripts/add_07400.bc
new file mode 100644
index 000000000000..5ec925f71453
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_07400.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 7301; i <= 7400; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_07500.bc b/contrib/bc/tests/bc/scripts/add_07500.bc
new file mode 100644
index 000000000000..79e3da3795ae
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_07500.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 7401; i <= 7500; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_07600.bc b/contrib/bc/tests/bc/scripts/add_07600.bc
new file mode 100644
index 000000000000..0a6787de4f9a
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_07600.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 7501; i <= 7600; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_07700.bc b/contrib/bc/tests/bc/scripts/add_07700.bc
new file mode 100644
index 000000000000..68a270cb2149
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_07700.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 7601; i <= 7700; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_07800.bc b/contrib/bc/tests/bc/scripts/add_07800.bc
new file mode 100644
index 000000000000..266fb799877e
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_07800.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 7701; i <= 7800; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_07900.bc b/contrib/bc/tests/bc/scripts/add_07900.bc
new file mode 100644
index 000000000000..34759324fbe9
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_07900.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 7801; i <= 7900; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_08000.bc b/contrib/bc/tests/bc/scripts/add_08000.bc
new file mode 100644
index 000000000000..7194e35553bd
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_08000.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 7901; i <= 8000; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_08100.bc b/contrib/bc/tests/bc/scripts/add_08100.bc
new file mode 100644
index 000000000000..899afff7c49f
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_08100.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 8001; i <= 8100; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_08200.bc b/contrib/bc/tests/bc/scripts/add_08200.bc
new file mode 100644
index 000000000000..7898c65c3f39
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_08200.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 8101; i <= 8200; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_08300.bc b/contrib/bc/tests/bc/scripts/add_08300.bc
new file mode 100644
index 000000000000..e524af4e6365
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_08300.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 8201; i <= 8300; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_08400.bc b/contrib/bc/tests/bc/scripts/add_08400.bc
new file mode 100644
index 000000000000..e1835790b535
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_08400.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 8301; i <= 8400; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_08500.bc b/contrib/bc/tests/bc/scripts/add_08500.bc
new file mode 100644
index 000000000000..33a1b69888fb
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_08500.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 8401; i <= 8500; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_08600.bc b/contrib/bc/tests/bc/scripts/add_08600.bc
new file mode 100644
index 000000000000..a0a85ed50d2e
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_08600.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 8501; i <= 8600; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_08700.bc b/contrib/bc/tests/bc/scripts/add_08700.bc
new file mode 100644
index 000000000000..cf453c39167c
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_08700.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 8601; i <= 8700; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_08800.bc b/contrib/bc/tests/bc/scripts/add_08800.bc
new file mode 100644
index 000000000000..0714a10c14dd
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_08800.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 8701; i <= 8800; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_08900.bc b/contrib/bc/tests/bc/scripts/add_08900.bc
new file mode 100644
index 000000000000..1cf95e2507f0
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_08900.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 8801; i <= 8900; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_09000.bc b/contrib/bc/tests/bc/scripts/add_09000.bc
new file mode 100644
index 000000000000..3838cb3e90e0
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_09000.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 8901; i <= 9000; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_09100.bc b/contrib/bc/tests/bc/scripts/add_09100.bc
new file mode 100644
index 000000000000..f5771ed8b8f9
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_09100.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 9001; i <= 9100; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_09200.bc b/contrib/bc/tests/bc/scripts/add_09200.bc
new file mode 100644
index 000000000000..4f9e0638fdd7
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_09200.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 9101; i <= 9200; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_09300.bc b/contrib/bc/tests/bc/scripts/add_09300.bc
new file mode 100644
index 000000000000..b4fc793980c6
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_09300.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 9201; i <= 9300; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_09400.bc b/contrib/bc/tests/bc/scripts/add_09400.bc
new file mode 100644
index 000000000000..302f328b3c16
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_09400.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 9301; i <= 9400; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_09500.bc b/contrib/bc/tests/bc/scripts/add_09500.bc
new file mode 100644
index 000000000000..a2fc67f64439
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_09500.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 9401; i <= 9500; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_09600.bc b/contrib/bc/tests/bc/scripts/add_09600.bc
new file mode 100644
index 000000000000..0b36a67aeff6
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_09600.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 9501; i <= 9600; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_09700.bc b/contrib/bc/tests/bc/scripts/add_09700.bc
new file mode 100644
index 000000000000..5f7c395c3a22
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_09700.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 9601; i <= 9700; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_09800.bc b/contrib/bc/tests/bc/scripts/add_09800.bc
new file mode 100644
index 000000000000..23c044c7dbc9
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_09800.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 9701; i <= 9800; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_09900.bc b/contrib/bc/tests/bc/scripts/add_09900.bc
new file mode 100644
index 000000000000..42d1a28d42b6
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_09900.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 9801; i <= 9900; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/add_10000.bc b/contrib/bc/tests/bc/scripts/add_10000.bc
new file mode 100644
index 000000000000..518e937c48a3
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/add_10000.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 9901; i <= 10000; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] + a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/all.txt b/contrib/bc/tests/bc/scripts/all.txt
index 7b49f7c4e77a..935f05cf9fb2 100644
--- a/contrib/bc/tests/bc/scripts/all.txt
+++ b/contrib/bc/tests/bc/scripts/all.txt
@@ -1,10 +1,404 @@
-multiply.bc
-divide.bc
-subtract.bc
-add.bc
-print.bc
+multiply_00100.bc
+multiply_00200.bc
+multiply_00300.bc
+multiply_00400.bc
+multiply_00500.bc
+multiply_00600.bc
+multiply_00700.bc
+multiply_00800.bc
+multiply_00900.bc
+multiply_01000.bc
+multiply_01100.bc
+multiply_01200.bc
+multiply_01300.bc
+multiply_01400.bc
+multiply_01500.bc
+multiply_01600.bc
+multiply_01700.bc
+multiply_01800.bc
+multiply_01900.bc
+multiply_02000.bc
+multiply_02100.bc
+multiply_02200.bc
+multiply_02300.bc
+multiply_02400.bc
+multiply_02500.bc
+multiply_02600.bc
+multiply_02700.bc
+multiply_02800.bc
+multiply_02900.bc
+multiply_03000.bc
+multiply_03100.bc
+multiply_03200.bc
+multiply_03300.bc
+multiply_03400.bc
+multiply_03500.bc
+multiply_03600.bc
+multiply_03700.bc
+multiply_03800.bc
+multiply_03900.bc
+multiply_04000.bc
+multiply_04100.bc
+multiply_04200.bc
+multiply_04300.bc
+multiply_04400.bc
+multiply_04500.bc
+multiply_04600.bc
+multiply_04700.bc
+multiply_04800.bc
+multiply_04900.bc
+multiply_05000.bc
+multiply_05100.bc
+multiply_05200.bc
+multiply_05300.bc
+multiply_05400.bc
+multiply_05500.bc
+multiply_05600.bc
+multiply_05700.bc
+multiply_05800.bc
+multiply_05900.bc
+multiply_06000.bc
+multiply_06100.bc
+multiply_06200.bc
+multiply_06300.bc
+multiply_06400.bc
+multiply_06500.bc
+multiply_06600.bc
+multiply_06700.bc
+multiply_06800.bc
+multiply_06900.bc
+multiply_07000.bc
+multiply_07100.bc
+multiply_07200.bc
+multiply_07300.bc
+multiply_07400.bc
+multiply_07500.bc
+multiply_07600.bc
+multiply_07700.bc
+multiply_07800.bc
+multiply_07900.bc
+multiply_08000.bc
+multiply_08100.bc
+multiply_08200.bc
+multiply_08300.bc
+multiply_08400.bc
+multiply_08500.bc
+multiply_08600.bc
+multiply_08700.bc
+multiply_08800.bc
+multiply_08900.bc
+multiply_09000.bc
+multiply_09100.bc
+multiply_09200.bc
+multiply_09300.bc
+multiply_09400.bc
+multiply_09500.bc
+multiply_09600.bc
+multiply_09700.bc
+multiply_09800.bc
+multiply_09900.bc
+multiply_10000.bc
+divide_00100.bc
+divide_00200.bc
+divide_00300.bc
+divide_00400.bc
+divide_00500.bc
+divide_00600.bc
+divide_00700.bc
+divide_00800.bc
+divide_00900.bc
+divide_01000.bc
+divide_01100.bc
+divide_01200.bc
+divide_01300.bc
+divide_01400.bc
+divide_01500.bc
+divide_01600.bc
+divide_01700.bc
+divide_01800.bc
+divide_01900.bc
+divide_02000.bc
+divide_02100.bc
+divide_02200.bc
+divide_02300.bc
+divide_02400.bc
+divide_02500.bc
+divide_02600.bc
+divide_02700.bc
+divide_02800.bc
+divide_02900.bc
+divide_03000.bc
+divide_03100.bc
+divide_03200.bc
+divide_03300.bc
+divide_03400.bc
+divide_03500.bc
+divide_03600.bc
+divide_03700.bc
+divide_03800.bc
+divide_03900.bc
+divide_04000.bc
+divide_04100.bc
+divide_04200.bc
+divide_04300.bc
+divide_04400.bc
+divide_04500.bc
+divide_04600.bc
+divide_04700.bc
+divide_04800.bc
+divide_04900.bc
+divide_05000.bc
+divide_05100.bc
+divide_05200.bc
+divide_05300.bc
+divide_05400.bc
+divide_05500.bc
+divide_05600.bc
+divide_05700.bc
+divide_05800.bc
+divide_05900.bc
+divide_06000.bc
+divide_06100.bc
+divide_06200.bc
+divide_06300.bc
+divide_06400.bc
+divide_06500.bc
+divide_06600.bc
+divide_06700.bc
+divide_06800.bc
+divide_06900.bc
+divide_07000.bc
+divide_07100.bc
+divide_07200.bc
+divide_07300.bc
+divide_07400.bc
+divide_07500.bc
+divide_07600.bc
+divide_07700.bc
+divide_07800.bc
+divide_07900.bc
+divide_08000.bc
+divide_08100.bc
+divide_08200.bc
+divide_08300.bc
+divide_08400.bc
+divide_08500.bc
+divide_08600.bc
+divide_08700.bc
+divide_08800.bc
+divide_08900.bc
+divide_09000.bc
+divide_09100.bc
+divide_09200.bc
+divide_09300.bc
+divide_09400.bc
+divide_09500.bc
+divide_09600.bc
+divide_09700.bc
+divide_09800.bc
+divide_09900.bc
+divide_10000.bc
+subtract_00100.bc
+subtract_00200.bc
+subtract_00300.bc
+subtract_00400.bc
+subtract_00500.bc
+subtract_00600.bc
+subtract_00700.bc
+subtract_00800.bc
+subtract_00900.bc
+subtract_01000.bc
+subtract_01100.bc
+subtract_01200.bc
+subtract_01300.bc
+subtract_01400.bc
+subtract_01500.bc
+subtract_01600.bc
+subtract_01700.bc
+subtract_01800.bc
+subtract_01900.bc
+subtract_02000.bc
+subtract_02100.bc
+subtract_02200.bc
+subtract_02300.bc
+subtract_02400.bc
+subtract_02500.bc
+subtract_02600.bc
+subtract_02700.bc
+subtract_02800.bc
+subtract_02900.bc
+subtract_03000.bc
+subtract_03100.bc
+subtract_03200.bc
+subtract_03300.bc
+subtract_03400.bc
+subtract_03500.bc
+subtract_03600.bc
+subtract_03700.bc
+subtract_03800.bc
+subtract_03900.bc
+subtract_04000.bc
+subtract_04100.bc
+subtract_04200.bc
+subtract_04300.bc
+subtract_04400.bc
+subtract_04500.bc
+subtract_04600.bc
+subtract_04700.bc
+subtract_04800.bc
+subtract_04900.bc
+subtract_05000.bc
+subtract_05100.bc
+subtract_05200.bc
+subtract_05300.bc
+subtract_05400.bc
+subtract_05500.bc
+subtract_05600.bc
+subtract_05700.bc
+subtract_05800.bc
+subtract_05900.bc
+subtract_06000.bc
+subtract_06100.bc
+subtract_06200.bc
+subtract_06300.bc
+subtract_06400.bc
+subtract_06500.bc
+subtract_06600.bc
+subtract_06700.bc
+subtract_06800.bc
+subtract_06900.bc
+subtract_07000.bc
+subtract_07100.bc
+subtract_07200.bc
+subtract_07300.bc
+subtract_07400.bc
+subtract_07500.bc
+subtract_07600.bc
+subtract_07700.bc
+subtract_07800.bc
+subtract_07900.bc
+subtract_08000.bc
+subtract_08100.bc
+subtract_08200.bc
+subtract_08300.bc
+subtract_08400.bc
+subtract_08500.bc
+subtract_08600.bc
+subtract_08700.bc
+subtract_08800.bc
+subtract_08900.bc
+subtract_09000.bc
+subtract_09100.bc
+subtract_09200.bc
+subtract_09300.bc
+subtract_09400.bc
+subtract_09500.bc
+subtract_09600.bc
+subtract_09700.bc
+subtract_09800.bc
+subtract_09900.bc
+subtract_10000.bc
+add_00100.bc
+add_00200.bc
+add_00300.bc
+add_00400.bc
+add_00500.bc
+add_00600.bc
+add_00700.bc
+add_00800.bc
+add_00900.bc
+add_01000.bc
+add_01100.bc
+add_01200.bc
+add_01300.bc
+add_01400.bc
+add_01500.bc
+add_01600.bc
+add_01700.bc
+add_01800.bc
+add_01900.bc
+add_02000.bc
+add_02100.bc
+add_02200.bc
+add_02300.bc
+add_02400.bc
+add_02500.bc
+add_02600.bc
+add_02700.bc
+add_02800.bc
+add_02900.bc
+add_03000.bc
+add_03100.bc
+add_03200.bc
+add_03300.bc
+add_03400.bc
+add_03500.bc
+add_03600.bc
+add_03700.bc
+add_03800.bc
+add_03900.bc
+add_04000.bc
+add_04100.bc
+add_04200.bc
+add_04300.bc
+add_04400.bc
+add_04500.bc
+add_04600.bc
+add_04700.bc
+add_04800.bc
+add_04900.bc
+add_05000.bc
+add_05100.bc
+add_05200.bc
+add_05300.bc
+add_05400.bc
+add_05500.bc
+add_05600.bc
+add_05700.bc
+add_05800.bc
+add_05900.bc
+add_06000.bc
+add_06100.bc
+add_06200.bc
+add_06300.bc
+add_06400.bc
+add_06500.bc
+add_06600.bc
+add_06700.bc
+add_06800.bc
+add_06900.bc
+add_07000.bc
+add_07100.bc
+add_07200.bc
+add_07300.bc
+add_07400.bc
+add_07500.bc
+add_07600.bc
+add_07700.bc
+add_07800.bc
+add_07900.bc
+add_08000.bc
+add_08100.bc
+add_08200.bc
+add_08300.bc
+add_08400.bc
+add_08500.bc
+add_08600.bc
+add_08700.bc
+add_08800.bc
+add_08900.bc
+add_09000.bc
+add_09100.bc
+add_09200.bc
+add_09300.bc
+add_09400.bc
+add_09500.bc
+add_09600.bc
+add_09700.bc
+add_09800.bc
+add_09900.bc
+add_10000.bc
print2.bc
-parse.bc
root.bc
array.bc
array2.bc
diff --git a/contrib/bc/tests/bc/scripts/divide.bc b/contrib/bc/tests/bc/scripts/divide_00100.bc
index 51a4c0082d6a..d2685faf9cd3 100644
--- a/contrib/bc/tests/bc/scripts/divide.bc
+++ b/contrib/bc/tests/bc/scripts/divide_00100.bc
@@ -12,7 +12,7 @@ for (i = 0; i <= len; ++i) {
a[i]
}
-for (i = 1; i <= 10000; ++i) {
+for (i = 1; i <= 100; ++i) {
for (j = 0; j < len; ++j) {
a[0] / a[j]
a[i] / a[j]
diff --git a/contrib/bc/tests/bc/scripts/divide_00200.bc b/contrib/bc/tests/bc/scripts/divide_00200.bc
new file mode 100644
index 000000000000..027642c504d0
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_00200.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 101; i <= 200; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_00300.bc b/contrib/bc/tests/bc/scripts/divide_00300.bc
new file mode 100644
index 000000000000..53a3fb2e3d3a
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_00300.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 201; i <= 300; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_00400.bc b/contrib/bc/tests/bc/scripts/divide_00400.bc
new file mode 100644
index 000000000000..15f8ba961e61
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_00400.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 301; i <= 400; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_00500.bc b/contrib/bc/tests/bc/scripts/divide_00500.bc
new file mode 100644
index 000000000000..732bd2867a23
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_00500.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 401; i <= 500; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_00600.bc b/contrib/bc/tests/bc/scripts/divide_00600.bc
new file mode 100644
index 000000000000..4ac87e33319f
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_00600.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 501; i <= 600; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_00700.bc b/contrib/bc/tests/bc/scripts/divide_00700.bc
new file mode 100644
index 000000000000..0b272167f0fb
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_00700.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 601; i <= 700; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_00800.bc b/contrib/bc/tests/bc/scripts/divide_00800.bc
new file mode 100644
index 000000000000..0e08bb7016e3
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_00800.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 701; i <= 800; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_00900.bc b/contrib/bc/tests/bc/scripts/divide_00900.bc
new file mode 100644
index 000000000000..cef55f474661
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_00900.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 801; i <= 900; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_01000.bc b/contrib/bc/tests/bc/scripts/divide_01000.bc
new file mode 100644
index 000000000000..5291d71fc316
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_01000.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 901; i <= 1000; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_01100.bc b/contrib/bc/tests/bc/scripts/divide_01100.bc
new file mode 100644
index 000000000000..ff9a49a6bd9a
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_01100.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 1001; i <= 1100; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_01200.bc b/contrib/bc/tests/bc/scripts/divide_01200.bc
new file mode 100644
index 000000000000..9dd0fadf3a4d
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_01200.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 1101; i <= 1200; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_01300.bc b/contrib/bc/tests/bc/scripts/divide_01300.bc
new file mode 100644
index 000000000000..5ac02c35a087
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_01300.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 1201; i <= 1300; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_01400.bc b/contrib/bc/tests/bc/scripts/divide_01400.bc
new file mode 100644
index 000000000000..9189a546b01c
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_01400.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 1301; i <= 1400; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_01500.bc b/contrib/bc/tests/bc/scripts/divide_01500.bc
new file mode 100644
index 000000000000..7feed3c7389d
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_01500.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 1401; i <= 1500; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_01600.bc b/contrib/bc/tests/bc/scripts/divide_01600.bc
new file mode 100644
index 000000000000..4847d9dbe62c
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_01600.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 1501; i <= 1600; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_01700.bc b/contrib/bc/tests/bc/scripts/divide_01700.bc
new file mode 100644
index 000000000000..81db9b7b4bbe
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_01700.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 1601; i <= 1700; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_01800.bc b/contrib/bc/tests/bc/scripts/divide_01800.bc
new file mode 100644
index 000000000000..25b7476e7d6e
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_01800.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 1701; i <= 1800; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_01900.bc b/contrib/bc/tests/bc/scripts/divide_01900.bc
new file mode 100644
index 000000000000..80a8292c0b52
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_01900.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 1801; i <= 1900; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_02000.bc b/contrib/bc/tests/bc/scripts/divide_02000.bc
new file mode 100644
index 000000000000..268f1636f65f
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_02000.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 1901; i <= 2000; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_02100.bc b/contrib/bc/tests/bc/scripts/divide_02100.bc
new file mode 100644
index 000000000000..cd5f57546856
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_02100.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 2001; i <= 2100; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_02200.bc b/contrib/bc/tests/bc/scripts/divide_02200.bc
new file mode 100644
index 000000000000..40eb46b58dc8
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_02200.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 2101; i <= 2200; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_02300.bc b/contrib/bc/tests/bc/scripts/divide_02300.bc
new file mode 100644
index 000000000000..50da630cdeb1
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_02300.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 2201; i <= 2300; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_02400.bc b/contrib/bc/tests/bc/scripts/divide_02400.bc
new file mode 100644
index 000000000000..744e082c95b5
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_02400.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 2301; i <= 2400; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_02500.bc b/contrib/bc/tests/bc/scripts/divide_02500.bc
new file mode 100644
index 000000000000..294f2677971f
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_02500.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 2401; i <= 2500; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_02600.bc b/contrib/bc/tests/bc/scripts/divide_02600.bc
new file mode 100644
index 000000000000..0adb8203d56b
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_02600.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 2501; i <= 2600; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_02700.bc b/contrib/bc/tests/bc/scripts/divide_02700.bc
new file mode 100644
index 000000000000..7107d6674bcf
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_02700.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 2601; i <= 2700; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_02800.bc b/contrib/bc/tests/bc/scripts/divide_02800.bc
new file mode 100644
index 000000000000..0a818c9461c6
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_02800.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 2701; i <= 2800; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_02900.bc b/contrib/bc/tests/bc/scripts/divide_02900.bc
new file mode 100644
index 000000000000..8113032c261b
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_02900.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 2801; i <= 2900; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_03000.bc b/contrib/bc/tests/bc/scripts/divide_03000.bc
new file mode 100644
index 000000000000..772d2e8cd2c3
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_03000.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 2901; i <= 3000; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_03100.bc b/contrib/bc/tests/bc/scripts/divide_03100.bc
new file mode 100644
index 000000000000..916759661db5
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_03100.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 3001; i <= 3100; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_03200.bc b/contrib/bc/tests/bc/scripts/divide_03200.bc
new file mode 100644
index 000000000000..52542b9b6dd1
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_03200.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 3101; i <= 3200; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_03300.bc b/contrib/bc/tests/bc/scripts/divide_03300.bc
new file mode 100644
index 000000000000..d2315cf2ad43
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_03300.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 3201; i <= 3300; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_03400.bc b/contrib/bc/tests/bc/scripts/divide_03400.bc
new file mode 100644
index 000000000000..03cf3e5c7d67
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_03400.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 3301; i <= 3400; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_03500.bc b/contrib/bc/tests/bc/scripts/divide_03500.bc
new file mode 100644
index 000000000000..8fac5cc5bb82
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_03500.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 3401; i <= 3500; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_03600.bc b/contrib/bc/tests/bc/scripts/divide_03600.bc
new file mode 100644
index 000000000000..a8a3af330a29
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_03600.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 3501; i <= 3600; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_03700.bc b/contrib/bc/tests/bc/scripts/divide_03700.bc
new file mode 100644
index 000000000000..9f38b3e61350
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_03700.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 3601; i <= 3700; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_03800.bc b/contrib/bc/tests/bc/scripts/divide_03800.bc
new file mode 100644
index 000000000000..db59cdd08602
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_03800.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 3701; i <= 3800; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_03900.bc b/contrib/bc/tests/bc/scripts/divide_03900.bc
new file mode 100644
index 000000000000..d7de3afba717
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_03900.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 3801; i <= 3900; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_04000.bc b/contrib/bc/tests/bc/scripts/divide_04000.bc
new file mode 100644
index 000000000000..86755a73ef5b
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_04000.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 3901; i <= 4000; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_04100.bc b/contrib/bc/tests/bc/scripts/divide_04100.bc
new file mode 100644
index 000000000000..03156cd3a747
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_04100.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 4001; i <= 4100; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_04200.bc b/contrib/bc/tests/bc/scripts/divide_04200.bc
new file mode 100644
index 000000000000..2df36aa73292
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_04200.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 4101; i <= 4200; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_04300.bc b/contrib/bc/tests/bc/scripts/divide_04300.bc
new file mode 100644
index 000000000000..527cbb191fa3
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_04300.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 4201; i <= 4300; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_04400.bc b/contrib/bc/tests/bc/scripts/divide_04400.bc
new file mode 100644
index 000000000000..358cf70a6a81
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_04400.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 4301; i <= 4400; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_04500.bc b/contrib/bc/tests/bc/scripts/divide_04500.bc
new file mode 100644
index 000000000000..d298c5ba1801
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_04500.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 4401; i <= 4500; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_04600.bc b/contrib/bc/tests/bc/scripts/divide_04600.bc
new file mode 100644
index 000000000000..b58a50a8c5a3
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_04600.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 4501; i <= 4600; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_04700.bc b/contrib/bc/tests/bc/scripts/divide_04700.bc
new file mode 100644
index 000000000000..05a561bd145b
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_04700.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 4601; i <= 4700; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_04800.bc b/contrib/bc/tests/bc/scripts/divide_04800.bc
new file mode 100644
index 000000000000..5ed04489a07f
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_04800.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 4701; i <= 4800; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_04900.bc b/contrib/bc/tests/bc/scripts/divide_04900.bc
new file mode 100644
index 000000000000..ff3b46812838
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_04900.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 4801; i <= 4900; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_05000.bc b/contrib/bc/tests/bc/scripts/divide_05000.bc
new file mode 100644
index 000000000000..a32273f5ac38
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_05000.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 4901; i <= 5000; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_05100.bc b/contrib/bc/tests/bc/scripts/divide_05100.bc
new file mode 100644
index 000000000000..9b5aa7fcf72c
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_05100.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 5001; i <= 5100; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_05200.bc b/contrib/bc/tests/bc/scripts/divide_05200.bc
new file mode 100644
index 000000000000..178dcac6ec5f
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_05200.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 5101; i <= 5200; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_05300.bc b/contrib/bc/tests/bc/scripts/divide_05300.bc
new file mode 100644
index 000000000000..49768eca0511
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_05300.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 5201; i <= 5300; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_05400.bc b/contrib/bc/tests/bc/scripts/divide_05400.bc
new file mode 100644
index 000000000000..c0078a2fac94
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_05400.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 5301; i <= 5400; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_05500.bc b/contrib/bc/tests/bc/scripts/divide_05500.bc
new file mode 100644
index 000000000000..9d289e0f6a2e
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_05500.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 5401; i <= 5500; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_05600.bc b/contrib/bc/tests/bc/scripts/divide_05600.bc
new file mode 100644
index 000000000000..66193069e42d
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_05600.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 5501; i <= 5600; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_05700.bc b/contrib/bc/tests/bc/scripts/divide_05700.bc
new file mode 100644
index 000000000000..483622bfc219
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_05700.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 5601; i <= 5700; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_05800.bc b/contrib/bc/tests/bc/scripts/divide_05800.bc
new file mode 100644
index 000000000000..c8bc59d63aaf
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_05800.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 5701; i <= 5800; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_05900.bc b/contrib/bc/tests/bc/scripts/divide_05900.bc
new file mode 100644
index 000000000000..13e5a0f2209b
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_05900.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 5801; i <= 5900; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_06000.bc b/contrib/bc/tests/bc/scripts/divide_06000.bc
new file mode 100644
index 000000000000..318fc5b25f5f
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_06000.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 5901; i <= 6000; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_06100.bc b/contrib/bc/tests/bc/scripts/divide_06100.bc
new file mode 100644
index 000000000000..57bf61d4730f
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_06100.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 6001; i <= 6100; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_06200.bc b/contrib/bc/tests/bc/scripts/divide_06200.bc
new file mode 100644
index 000000000000..ab1ac853431e
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_06200.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 6101; i <= 6200; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_06300.bc b/contrib/bc/tests/bc/scripts/divide_06300.bc
new file mode 100644
index 000000000000..e2a320b0abd2
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_06300.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 6201; i <= 6300; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_06400.bc b/contrib/bc/tests/bc/scripts/divide_06400.bc
new file mode 100644
index 000000000000..f3e0557b7e8b
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_06400.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 6301; i <= 6400; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_06500.bc b/contrib/bc/tests/bc/scripts/divide_06500.bc
new file mode 100644
index 000000000000..62c84d07f3cd
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_06500.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 6401; i <= 6500; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_06600.bc b/contrib/bc/tests/bc/scripts/divide_06600.bc
new file mode 100644
index 000000000000..04e1d9dcdf07
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_06600.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 6501; i <= 6600; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_06700.bc b/contrib/bc/tests/bc/scripts/divide_06700.bc
new file mode 100644
index 000000000000..af23039798d9
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_06700.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 6601; i <= 6700; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_06800.bc b/contrib/bc/tests/bc/scripts/divide_06800.bc
new file mode 100644
index 000000000000..6da3f2da386b
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_06800.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 6701; i <= 6800; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_06900.bc b/contrib/bc/tests/bc/scripts/divide_06900.bc
new file mode 100644
index 000000000000..769d57fb1656
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_06900.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 6801; i <= 6900; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_07000.bc b/contrib/bc/tests/bc/scripts/divide_07000.bc
new file mode 100644
index 000000000000..758b61fb99ec
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_07000.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 6901; i <= 7000; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_07100.bc b/contrib/bc/tests/bc/scripts/divide_07100.bc
new file mode 100644
index 000000000000..2ef9f19390b8
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_07100.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 7001; i <= 7100; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_07200.bc b/contrib/bc/tests/bc/scripts/divide_07200.bc
new file mode 100644
index 000000000000..dcab8948d540
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_07200.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 7101; i <= 7200; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_07300.bc b/contrib/bc/tests/bc/scripts/divide_07300.bc
new file mode 100644
index 000000000000..f8aeae59b336
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_07300.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 7201; i <= 7300; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_07400.bc b/contrib/bc/tests/bc/scripts/divide_07400.bc
new file mode 100644
index 000000000000..7a26ce5583fc
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_07400.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 7301; i <= 7400; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_07500.bc b/contrib/bc/tests/bc/scripts/divide_07500.bc
new file mode 100644
index 000000000000..0c197595ae9f
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_07500.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 7401; i <= 7500; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_07600.bc b/contrib/bc/tests/bc/scripts/divide_07600.bc
new file mode 100644
index 000000000000..868d0702f3db
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_07600.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 7501; i <= 7600; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_07700.bc b/contrib/bc/tests/bc/scripts/divide_07700.bc
new file mode 100644
index 000000000000..b30f66ebb5b8
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_07700.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 7601; i <= 7700; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_07800.bc b/contrib/bc/tests/bc/scripts/divide_07800.bc
new file mode 100644
index 000000000000..118df8fe112d
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_07800.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 7701; i <= 7800; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_07900.bc b/contrib/bc/tests/bc/scripts/divide_07900.bc
new file mode 100644
index 000000000000..42fd0771dca6
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_07900.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 7801; i <= 7900; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_08000.bc b/contrib/bc/tests/bc/scripts/divide_08000.bc
new file mode 100644
index 000000000000..3bdaa26e707e
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_08000.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 7901; i <= 8000; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_08100.bc b/contrib/bc/tests/bc/scripts/divide_08100.bc
new file mode 100644
index 000000000000..f0e4592382b2
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_08100.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 8001; i <= 8100; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_08200.bc b/contrib/bc/tests/bc/scripts/divide_08200.bc
new file mode 100644
index 000000000000..ef1bb329cff1
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_08200.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 8101; i <= 8200; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_08300.bc b/contrib/bc/tests/bc/scripts/divide_08300.bc
new file mode 100644
index 000000000000..f3fe8a69eeda
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_08300.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 8201; i <= 8300; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_08400.bc b/contrib/bc/tests/bc/scripts/divide_08400.bc
new file mode 100644
index 000000000000..5c9ec5c8fba2
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_08400.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 8301; i <= 8400; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_08500.bc b/contrib/bc/tests/bc/scripts/divide_08500.bc
new file mode 100644
index 000000000000..64059da94286
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_08500.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 8401; i <= 8500; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_08600.bc b/contrib/bc/tests/bc/scripts/divide_08600.bc
new file mode 100644
index 000000000000..72fe4114998b
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_08600.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 8501; i <= 8600; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_08700.bc b/contrib/bc/tests/bc/scripts/divide_08700.bc
new file mode 100644
index 000000000000..c1927841a49f
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_08700.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 8601; i <= 8700; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_08800.bc b/contrib/bc/tests/bc/scripts/divide_08800.bc
new file mode 100644
index 000000000000..4ae7fcab3dcd
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_08800.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 8701; i <= 8800; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_08900.bc b/contrib/bc/tests/bc/scripts/divide_08900.bc
new file mode 100644
index 000000000000..7e59baa4a150
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_08900.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 8801; i <= 8900; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_09000.bc b/contrib/bc/tests/bc/scripts/divide_09000.bc
new file mode 100644
index 000000000000..26d3f62ac6a1
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_09000.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 8901; i <= 9000; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_09100.bc b/contrib/bc/tests/bc/scripts/divide_09100.bc
new file mode 100644
index 000000000000..e8a372db80e2
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_09100.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 9001; i <= 9100; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_09200.bc b/contrib/bc/tests/bc/scripts/divide_09200.bc
new file mode 100644
index 000000000000..00d391459a46
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_09200.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 9101; i <= 9200; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_09300.bc b/contrib/bc/tests/bc/scripts/divide_09300.bc
new file mode 100644
index 000000000000..4a64b9347192
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_09300.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 9201; i <= 9300; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_09400.bc b/contrib/bc/tests/bc/scripts/divide_09400.bc
new file mode 100644
index 000000000000..55a70f7cbf41
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_09400.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 9301; i <= 9400; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_09500.bc b/contrib/bc/tests/bc/scripts/divide_09500.bc
new file mode 100644
index 000000000000..09df2d540454
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_09500.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 9401; i <= 9500; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_09600.bc b/contrib/bc/tests/bc/scripts/divide_09600.bc
new file mode 100644
index 000000000000..491e3c2caa33
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_09600.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 9501; i <= 9600; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_09700.bc b/contrib/bc/tests/bc/scripts/divide_09700.bc
new file mode 100644
index 000000000000..33a183e2493e
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_09700.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 9601; i <= 9700; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_09800.bc b/contrib/bc/tests/bc/scripts/divide_09800.bc
new file mode 100644
index 000000000000..ae007e8a7a00
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_09800.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 9701; i <= 9800; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_09900.bc b/contrib/bc/tests/bc/scripts/divide_09900.bc
new file mode 100644
index 000000000000..e8fdc75987b6
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_09900.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 9801; i <= 9900; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/divide_10000.bc b/contrib/bc/tests/bc/scripts/divide_10000.bc
new file mode 100644
index 000000000000..f4490d7d220d
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/divide_10000.bc
@@ -0,0 +1,23 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 * 10^(-scale)
+len = 1 + 2 * scale
+
+x
+scale += 10
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 9901; i <= 10000; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] / a[j]
+ a[i] / a[j]
+ (a[0] * i) / a[j]
+ a[0] / (a[j] * i)
+ (a[0] * i) / (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply.bc b/contrib/bc/tests/bc/scripts/multiply_00100.bc
index 3aa64cc5e031..bc979d176c2a 100644
--- a/contrib/bc/tests/bc/scripts/multiply.bc
+++ b/contrib/bc/tests/bc/scripts/multiply_00100.bc
@@ -9,7 +9,7 @@ for (i = 0; i <= len; ++i) {
a[i]
}
-for (i = 1; i <= 10000; ++i) {
+for (i = 1; i <= 100; ++i) {
for (j = 0; j < len; ++j) {
a[0] * a[j]
a[i] * a[j]
diff --git a/contrib/bc/tests/bc/scripts/multiply_00200.bc b/contrib/bc/tests/bc/scripts/multiply_00200.bc
new file mode 100644
index 000000000000..08c36d5dd4b3
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_00200.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 101; i <= 200; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_00300.bc b/contrib/bc/tests/bc/scripts/multiply_00300.bc
new file mode 100644
index 000000000000..3c56a569d2ee
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_00300.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 201; i <= 300; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_00400.bc b/contrib/bc/tests/bc/scripts/multiply_00400.bc
new file mode 100644
index 000000000000..3dc750947aee
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_00400.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 301; i <= 400; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_00500.bc b/contrib/bc/tests/bc/scripts/multiply_00500.bc
new file mode 100644
index 000000000000..a1f14e121cbb
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_00500.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 401; i <= 500; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_00600.bc b/contrib/bc/tests/bc/scripts/multiply_00600.bc
new file mode 100644
index 000000000000..21a017a7f7cb
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_00600.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 501; i <= 600; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_00700.bc b/contrib/bc/tests/bc/scripts/multiply_00700.bc
new file mode 100644
index 000000000000..3246b5e1a568
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_00700.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 601; i <= 700; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_00800.bc b/contrib/bc/tests/bc/scripts/multiply_00800.bc
new file mode 100644
index 000000000000..4bc39b0a1fef
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_00800.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 701; i <= 800; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_00900.bc b/contrib/bc/tests/bc/scripts/multiply_00900.bc
new file mode 100644
index 000000000000..febc77dead15
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_00900.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 801; i <= 900; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_01000.bc b/contrib/bc/tests/bc/scripts/multiply_01000.bc
new file mode 100644
index 000000000000..ba5c1d984f67
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_01000.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 901; i <= 1000; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_01100.bc b/contrib/bc/tests/bc/scripts/multiply_01100.bc
new file mode 100644
index 000000000000..a09c35252286
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_01100.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 1001; i <= 1100; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_01200.bc b/contrib/bc/tests/bc/scripts/multiply_01200.bc
new file mode 100644
index 000000000000..7a6b04c48b9e
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_01200.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 1101; i <= 1200; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_01300.bc b/contrib/bc/tests/bc/scripts/multiply_01300.bc
new file mode 100644
index 000000000000..4fab6beb0a62
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_01300.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 1201; i <= 1300; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_01400.bc b/contrib/bc/tests/bc/scripts/multiply_01400.bc
new file mode 100644
index 000000000000..954afe5473de
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_01400.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 1301; i <= 1400; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_01500.bc b/contrib/bc/tests/bc/scripts/multiply_01500.bc
new file mode 100644
index 000000000000..8d150418618a
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_01500.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 1401; i <= 1500; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_01600.bc b/contrib/bc/tests/bc/scripts/multiply_01600.bc
new file mode 100644
index 000000000000..45f170705f99
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_01600.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 1501; i <= 1600; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_01700.bc b/contrib/bc/tests/bc/scripts/multiply_01700.bc
new file mode 100644
index 000000000000..7eb34740139e
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_01700.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 1601; i <= 1700; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_01800.bc b/contrib/bc/tests/bc/scripts/multiply_01800.bc
new file mode 100644
index 000000000000..719db5b358a2
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_01800.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 1701; i <= 1800; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_01900.bc b/contrib/bc/tests/bc/scripts/multiply_01900.bc
new file mode 100644
index 000000000000..4a4a6c8b43c6
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_01900.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 1801; i <= 1900; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_02000.bc b/contrib/bc/tests/bc/scripts/multiply_02000.bc
new file mode 100644
index 000000000000..432e0d078dee
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_02000.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 1901; i <= 2000; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_02100.bc b/contrib/bc/tests/bc/scripts/multiply_02100.bc
new file mode 100644
index 000000000000..825b96a7c0d3
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_02100.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 2001; i <= 2100; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_02200.bc b/contrib/bc/tests/bc/scripts/multiply_02200.bc
new file mode 100644
index 000000000000..fd3d71684054
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_02200.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 2101; i <= 2200; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_02300.bc b/contrib/bc/tests/bc/scripts/multiply_02300.bc
new file mode 100644
index 000000000000..1e2adbe81fc0
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_02300.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 2201; i <= 2300; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_02400.bc b/contrib/bc/tests/bc/scripts/multiply_02400.bc
new file mode 100644
index 000000000000..6f648ae5eb9d
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_02400.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 2301; i <= 2400; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_02500.bc b/contrib/bc/tests/bc/scripts/multiply_02500.bc
new file mode 100644
index 000000000000..6990fa78af64
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_02500.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 2401; i <= 2500; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_02600.bc b/contrib/bc/tests/bc/scripts/multiply_02600.bc
new file mode 100644
index 000000000000..22d0eca373c9
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_02600.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 2501; i <= 2600; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_02700.bc b/contrib/bc/tests/bc/scripts/multiply_02700.bc
new file mode 100644
index 000000000000..ea8d632288ec
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_02700.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 2601; i <= 2700; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_02800.bc b/contrib/bc/tests/bc/scripts/multiply_02800.bc
new file mode 100644
index 000000000000..5cd01d5012c0
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_02800.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 2701; i <= 2800; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_02900.bc b/contrib/bc/tests/bc/scripts/multiply_02900.bc
new file mode 100644
index 000000000000..cdb1cb1e43c9
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_02900.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 2801; i <= 2900; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_03000.bc b/contrib/bc/tests/bc/scripts/multiply_03000.bc
new file mode 100644
index 000000000000..1afcd634ade1
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_03000.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 2901; i <= 3000; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_03100.bc b/contrib/bc/tests/bc/scripts/multiply_03100.bc
new file mode 100644
index 000000000000..a39ef85bdfc5
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_03100.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 3001; i <= 3100; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_03200.bc b/contrib/bc/tests/bc/scripts/multiply_03200.bc
new file mode 100644
index 000000000000..33f465a82bf6
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_03200.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 3101; i <= 3200; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_03300.bc b/contrib/bc/tests/bc/scripts/multiply_03300.bc
new file mode 100644
index 000000000000..2cc412b5837f
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_03300.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 3201; i <= 3300; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_03400.bc b/contrib/bc/tests/bc/scripts/multiply_03400.bc
new file mode 100644
index 000000000000..40f528023e17
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_03400.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 3301; i <= 3400; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_03500.bc b/contrib/bc/tests/bc/scripts/multiply_03500.bc
new file mode 100644
index 000000000000..dce2f326a54a
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_03500.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 3401; i <= 3500; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_03600.bc b/contrib/bc/tests/bc/scripts/multiply_03600.bc
new file mode 100644
index 000000000000..1223415dc675
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_03600.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 3501; i <= 3600; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_03700.bc b/contrib/bc/tests/bc/scripts/multiply_03700.bc
new file mode 100644
index 000000000000..80397b3d0cfd
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_03700.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 3601; i <= 3700; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_03800.bc b/contrib/bc/tests/bc/scripts/multiply_03800.bc
new file mode 100644
index 000000000000..0431d8b1c9d5
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_03800.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 3701; i <= 3800; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_03900.bc b/contrib/bc/tests/bc/scripts/multiply_03900.bc
new file mode 100644
index 000000000000..0f60df9aae71
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_03900.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 3801; i <= 3900; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_04000.bc b/contrib/bc/tests/bc/scripts/multiply_04000.bc
new file mode 100644
index 000000000000..ceeafea6975c
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_04000.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 3901; i <= 4000; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_04100.bc b/contrib/bc/tests/bc/scripts/multiply_04100.bc
new file mode 100644
index 000000000000..980fa7cb1c7e
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_04100.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 4001; i <= 4100; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_04200.bc b/contrib/bc/tests/bc/scripts/multiply_04200.bc
new file mode 100644
index 000000000000..1b16065ce434
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_04200.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 4101; i <= 4200; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_04300.bc b/contrib/bc/tests/bc/scripts/multiply_04300.bc
new file mode 100644
index 000000000000..ae2549f392b6
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_04300.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 4201; i <= 4300; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_04400.bc b/contrib/bc/tests/bc/scripts/multiply_04400.bc
new file mode 100644
index 000000000000..0b22a8b36cca
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_04400.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 4301; i <= 4400; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_04500.bc b/contrib/bc/tests/bc/scripts/multiply_04500.bc
new file mode 100644
index 000000000000..374b595501f4
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_04500.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 4401; i <= 4500; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_04600.bc b/contrib/bc/tests/bc/scripts/multiply_04600.bc
new file mode 100644
index 000000000000..f4e0150e100b
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_04600.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 4501; i <= 4600; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_04700.bc b/contrib/bc/tests/bc/scripts/multiply_04700.bc
new file mode 100644
index 000000000000..351cf2ae3bd7
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_04700.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 4601; i <= 4700; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_04800.bc b/contrib/bc/tests/bc/scripts/multiply_04800.bc
new file mode 100644
index 000000000000..75bab62c7467
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_04800.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 4701; i <= 4800; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_04900.bc b/contrib/bc/tests/bc/scripts/multiply_04900.bc
new file mode 100644
index 000000000000..bb127c4e685d
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_04900.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 4801; i <= 4900; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_05000.bc b/contrib/bc/tests/bc/scripts/multiply_05000.bc
new file mode 100644
index 000000000000..2b817f2e4f31
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_05000.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 4901; i <= 5000; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_05100.bc b/contrib/bc/tests/bc/scripts/multiply_05100.bc
new file mode 100644
index 000000000000..a5067e8d60e6
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_05100.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 5001; i <= 5100; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_05200.bc b/contrib/bc/tests/bc/scripts/multiply_05200.bc
new file mode 100644
index 000000000000..4cecc2212520
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_05200.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 5101; i <= 5200; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_05300.bc b/contrib/bc/tests/bc/scripts/multiply_05300.bc
new file mode 100644
index 000000000000..03c475b3de26
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_05300.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 5201; i <= 5300; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_05400.bc b/contrib/bc/tests/bc/scripts/multiply_05400.bc
new file mode 100644
index 000000000000..5bfeb9c4f048
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_05400.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 5301; i <= 5400; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_05500.bc b/contrib/bc/tests/bc/scripts/multiply_05500.bc
new file mode 100644
index 000000000000..1e12f9233a30
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_05500.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 5401; i <= 5500; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_05600.bc b/contrib/bc/tests/bc/scripts/multiply_05600.bc
new file mode 100644
index 000000000000..25a60e2d25a1
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_05600.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 5501; i <= 5600; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_05700.bc b/contrib/bc/tests/bc/scripts/multiply_05700.bc
new file mode 100644
index 000000000000..c824af19bacd
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_05700.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 5601; i <= 5700; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_05800.bc b/contrib/bc/tests/bc/scripts/multiply_05800.bc
new file mode 100644
index 000000000000..a7eedc9fb733
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_05800.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 5701; i <= 5800; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_05900.bc b/contrib/bc/tests/bc/scripts/multiply_05900.bc
new file mode 100644
index 000000000000..9bd0f7044428
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_05900.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 5801; i <= 5900; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_06000.bc b/contrib/bc/tests/bc/scripts/multiply_06000.bc
new file mode 100644
index 000000000000..6353c74cbf73
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_06000.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 5901; i <= 6000; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_06100.bc b/contrib/bc/tests/bc/scripts/multiply_06100.bc
new file mode 100644
index 000000000000..d49224821a06
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_06100.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 6001; i <= 6100; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_06200.bc b/contrib/bc/tests/bc/scripts/multiply_06200.bc
new file mode 100644
index 000000000000..c4483f401a2c
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_06200.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 6101; i <= 6200; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_06300.bc b/contrib/bc/tests/bc/scripts/multiply_06300.bc
new file mode 100644
index 000000000000..c86e62d3a87f
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_06300.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 6201; i <= 6300; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_06400.bc b/contrib/bc/tests/bc/scripts/multiply_06400.bc
new file mode 100644
index 000000000000..0140f164f1ac
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_06400.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 6301; i <= 6400; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_06500.bc b/contrib/bc/tests/bc/scripts/multiply_06500.bc
new file mode 100644
index 000000000000..e9ee4fe5a896
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_06500.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 6401; i <= 6500; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_06600.bc b/contrib/bc/tests/bc/scripts/multiply_06600.bc
new file mode 100644
index 000000000000..267dcbf8aa6b
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_06600.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 6501; i <= 6600; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_06700.bc b/contrib/bc/tests/bc/scripts/multiply_06700.bc
new file mode 100644
index 000000000000..851c771952e1
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_06700.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 6601; i <= 6700; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_06800.bc b/contrib/bc/tests/bc/scripts/multiply_06800.bc
new file mode 100644
index 000000000000..d2141d7c065c
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_06800.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 6701; i <= 6800; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_06900.bc b/contrib/bc/tests/bc/scripts/multiply_06900.bc
new file mode 100644
index 000000000000..5a9e9affde94
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_06900.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 6801; i <= 6900; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_07000.bc b/contrib/bc/tests/bc/scripts/multiply_07000.bc
new file mode 100644
index 000000000000..549a1df72211
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_07000.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 6901; i <= 7000; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_07100.bc b/contrib/bc/tests/bc/scripts/multiply_07100.bc
new file mode 100644
index 000000000000..0a664833487d
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_07100.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 7001; i <= 7100; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_07200.bc b/contrib/bc/tests/bc/scripts/multiply_07200.bc
new file mode 100644
index 000000000000..913f053eab19
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_07200.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 7101; i <= 7200; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_07300.bc b/contrib/bc/tests/bc/scripts/multiply_07300.bc
new file mode 100644
index 000000000000..50643873d43f
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_07300.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 7201; i <= 7300; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_07400.bc b/contrib/bc/tests/bc/scripts/multiply_07400.bc
new file mode 100644
index 000000000000..8c401f0da1dd
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_07400.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 7301; i <= 7400; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_07500.bc b/contrib/bc/tests/bc/scripts/multiply_07500.bc
new file mode 100644
index 000000000000..fedd47a45d45
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_07500.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 7401; i <= 7500; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_07600.bc b/contrib/bc/tests/bc/scripts/multiply_07600.bc
new file mode 100644
index 000000000000..b1b5727d0314
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_07600.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 7501; i <= 7600; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_07700.bc b/contrib/bc/tests/bc/scripts/multiply_07700.bc
new file mode 100644
index 000000000000..40531a676a12
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_07700.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 7601; i <= 7700; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_07800.bc b/contrib/bc/tests/bc/scripts/multiply_07800.bc
new file mode 100644
index 000000000000..dd847d26911d
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_07800.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 7701; i <= 7800; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_07900.bc b/contrib/bc/tests/bc/scripts/multiply_07900.bc
new file mode 100644
index 000000000000..8313633df2f9
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_07900.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 7801; i <= 7900; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_08000.bc b/contrib/bc/tests/bc/scripts/multiply_08000.bc
new file mode 100644
index 000000000000..e496cf78b42c
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_08000.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 7901; i <= 8000; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_08100.bc b/contrib/bc/tests/bc/scripts/multiply_08100.bc
new file mode 100644
index 000000000000..7f07ce4e3525
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_08100.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 8001; i <= 8100; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_08200.bc b/contrib/bc/tests/bc/scripts/multiply_08200.bc
new file mode 100644
index 000000000000..0916404d35d4
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_08200.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 8101; i <= 8200; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_08300.bc b/contrib/bc/tests/bc/scripts/multiply_08300.bc
new file mode 100644
index 000000000000..9de6c4997520
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_08300.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 8201; i <= 8300; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_08400.bc b/contrib/bc/tests/bc/scripts/multiply_08400.bc
new file mode 100644
index 000000000000..429bd41bd843
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_08400.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 8301; i <= 8400; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_08500.bc b/contrib/bc/tests/bc/scripts/multiply_08500.bc
new file mode 100644
index 000000000000..c84c3fb66c6e
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_08500.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 8401; i <= 8500; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_08600.bc b/contrib/bc/tests/bc/scripts/multiply_08600.bc
new file mode 100644
index 000000000000..30492744af01
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_08600.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 8501; i <= 8600; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_08700.bc b/contrib/bc/tests/bc/scripts/multiply_08700.bc
new file mode 100644
index 000000000000..06cc0e094cf5
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_08700.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 8601; i <= 8700; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_08800.bc b/contrib/bc/tests/bc/scripts/multiply_08800.bc
new file mode 100644
index 000000000000..9ae52a604a04
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_08800.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 8701; i <= 8800; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_08900.bc b/contrib/bc/tests/bc/scripts/multiply_08900.bc
new file mode 100644
index 000000000000..b1a1ccfa2dc1
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_08900.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 8801; i <= 8900; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_09000.bc b/contrib/bc/tests/bc/scripts/multiply_09000.bc
new file mode 100644
index 000000000000..487cb5a678bc
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_09000.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 8901; i <= 9000; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_09100.bc b/contrib/bc/tests/bc/scripts/multiply_09100.bc
new file mode 100644
index 000000000000..1c187475a5ed
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_09100.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 9001; i <= 9100; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_09200.bc b/contrib/bc/tests/bc/scripts/multiply_09200.bc
new file mode 100644
index 000000000000..2dbdb1bec1b9
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_09200.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 9101; i <= 9200; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_09300.bc b/contrib/bc/tests/bc/scripts/multiply_09300.bc
new file mode 100644
index 000000000000..ffc5b8cea907
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_09300.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 9201; i <= 9300; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_09400.bc b/contrib/bc/tests/bc/scripts/multiply_09400.bc
new file mode 100644
index 000000000000..6932c1dd73c7
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_09400.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 9301; i <= 9400; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_09500.bc b/contrib/bc/tests/bc/scripts/multiply_09500.bc
new file mode 100644
index 000000000000..2ac486b146a0
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_09500.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 9401; i <= 9500; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_09600.bc b/contrib/bc/tests/bc/scripts/multiply_09600.bc
new file mode 100644
index 000000000000..782176ba711b
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_09600.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 9501; i <= 9600; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_09700.bc b/contrib/bc/tests/bc/scripts/multiply_09700.bc
new file mode 100644
index 000000000000..fe4135de594d
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_09700.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 9601; i <= 9700; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_09800.bc b/contrib/bc/tests/bc/scripts/multiply_09800.bc
new file mode 100644
index 000000000000..3115b311f312
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_09800.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 9701; i <= 9800; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_09900.bc b/contrib/bc/tests/bc/scripts/multiply_09900.bc
new file mode 100644
index 000000000000..abbcfeefbbe9
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_09900.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 9801; i <= 9900; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/multiply_10000.bc b/contrib/bc/tests/bc/scripts/multiply_10000.bc
new file mode 100644
index 000000000000..e97e61623eac
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/multiply_10000.bc
@@ -0,0 +1,20 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 9901; i <= 10000; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[0] * a[j]
+ a[i] * a[j]
+ (a[0] * i) * a[j]
+ a[0] * (a[j] * i)
+ (a[0] * i) * (a[j] * i)
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/parse.bc b/contrib/bc/tests/bc/scripts/parse.bc
deleted file mode 100644
index 179daf116efd..000000000000
--- a/contrib/bc/tests/bc/scripts/parse.bc
+++ /dev/null
@@ -1,20 +0,0 @@
-#! /usr/bin/bc -q
-
-for (b = 2; b <= 16; ++b) {
- if (b == 10) continue
- obase = 10
- print "ibase = A; ibase = ", b, "\n"
- print "\qibase = \q\n"
- b
- obase = b
- for (i = 0; i <= 4096; ++i) {
- i
- print "0.", i, "\n"
- print ".", i, "\n"
- print "1.", i, "\n"
- print i, ".", "\n"
- print i, ".", i, "\n"
- }
-}
-
-halt
diff --git a/contrib/bc/tests/bc/scripts/parse_02.bc b/contrib/bc/tests/bc/scripts/parse_02.bc
new file mode 100644
index 000000000000..dc695e8a63ed
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/parse_02.bc
@@ -0,0 +1,19 @@
+#! /usr/bin/bc -q
+
+b = 2
+
+obase = 10
+print "ibase = A; ibase = ", b, "\n"
+print "\qibase = \q\n"
+b
+obase = b
+for (i = 0; i <= 4096; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/parse_03.bc b/contrib/bc/tests/bc/scripts/parse_03.bc
new file mode 100644
index 000000000000..8418c472184e
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/parse_03.bc
@@ -0,0 +1,19 @@
+#! /usr/bin/bc -q
+
+b = 3
+
+obase = 10
+print "ibase = A; ibase = ", b, "\n"
+print "\qibase = \q\n"
+b
+obase = b
+for (i = 0; i <= 4096; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/parse_04.bc b/contrib/bc/tests/bc/scripts/parse_04.bc
new file mode 100644
index 000000000000..6671dc3093e6
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/parse_04.bc
@@ -0,0 +1,19 @@
+#! /usr/bin/bc -q
+
+b = 4
+
+obase = 10
+print "ibase = A; ibase = ", b, "\n"
+print "\qibase = \q\n"
+b
+obase = b
+for (i = 0; i <= 4096; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/parse_05.bc b/contrib/bc/tests/bc/scripts/parse_05.bc
new file mode 100644
index 000000000000..868c003a507a
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/parse_05.bc
@@ -0,0 +1,19 @@
+#! /usr/bin/bc -q
+
+b = 5
+
+obase = 10
+print "ibase = A; ibase = ", b, "\n"
+print "\qibase = \q\n"
+b
+obase = b
+for (i = 0; i <= 4096; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/parse_06.bc b/contrib/bc/tests/bc/scripts/parse_06.bc
new file mode 100644
index 000000000000..ddef2fd58686
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/parse_06.bc
@@ -0,0 +1,19 @@
+#! /usr/bin/bc -q
+
+b = 6
+
+obase = 10
+print "ibase = A; ibase = ", b, "\n"
+print "\qibase = \q\n"
+b
+obase = b
+for (i = 0; i <= 4096; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/parse_07.bc b/contrib/bc/tests/bc/scripts/parse_07.bc
new file mode 100644
index 000000000000..83fb125d719a
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/parse_07.bc
@@ -0,0 +1,19 @@
+#! /usr/bin/bc -q
+
+b = 7
+
+obase = 10
+print "ibase = A; ibase = ", b, "\n"
+print "\qibase = \q\n"
+b
+obase = b
+for (i = 0; i <= 4096; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/parse_08.bc b/contrib/bc/tests/bc/scripts/parse_08.bc
new file mode 100644
index 000000000000..c8f798e4e82e
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/parse_08.bc
@@ -0,0 +1,19 @@
+#! /usr/bin/bc -q
+
+b = 8
+
+obase = 10
+print "ibase = A; ibase = ", b, "\n"
+print "\qibase = \q\n"
+b
+obase = b
+for (i = 0; i <= 4096; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/parse_09.bc b/contrib/bc/tests/bc/scripts/parse_09.bc
new file mode 100644
index 000000000000..98463b24edde
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/parse_09.bc
@@ -0,0 +1,19 @@
+#! /usr/bin/bc -q
+
+b = 9
+
+obase = 10
+print "ibase = A; ibase = ", b, "\n"
+print "\qibase = \q\n"
+b
+obase = b
+for (i = 0; i <= 4096; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/parse_11.bc b/contrib/bc/tests/bc/scripts/parse_11.bc
new file mode 100644
index 000000000000..efaf17293ce8
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/parse_11.bc
@@ -0,0 +1,19 @@
+#! /usr/bin/bc -q
+
+b = 11
+
+obase = 10
+print "ibase = A; ibase = ", b, "\n"
+print "\qibase = \q\n"
+b
+obase = b
+for (i = 0; i <= 4096; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/parse_12.bc b/contrib/bc/tests/bc/scripts/parse_12.bc
new file mode 100644
index 000000000000..a71a05a3b9dd
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/parse_12.bc
@@ -0,0 +1,19 @@
+#! /usr/bin/bc -q
+
+b = 12
+
+obase = 10
+print "ibase = A; ibase = ", b, "\n"
+print "\qibase = \q\n"
+b
+obase = b
+for (i = 0; i <= 4096; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/parse_13.bc b/contrib/bc/tests/bc/scripts/parse_13.bc
new file mode 100644
index 000000000000..37d88e7168f0
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/parse_13.bc
@@ -0,0 +1,19 @@
+#! /usr/bin/bc -q
+
+b = 13
+
+obase = 10
+print "ibase = A; ibase = ", b, "\n"
+print "\qibase = \q\n"
+b
+obase = b
+for (i = 0; i <= 4096; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/parse_14.bc b/contrib/bc/tests/bc/scripts/parse_14.bc
new file mode 100644
index 000000000000..e824db979639
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/parse_14.bc
@@ -0,0 +1,19 @@
+#! /usr/bin/bc -q
+
+b = 14
+
+obase = 10
+print "ibase = A; ibase = ", b, "\n"
+print "\qibase = \q\n"
+b
+obase = b
+for (i = 0; i <= 4096; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/parse_15.bc b/contrib/bc/tests/bc/scripts/parse_15.bc
new file mode 100644
index 000000000000..ad954a2ac4a8
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/parse_15.bc
@@ -0,0 +1,19 @@
+#! /usr/bin/bc -q
+
+b = 15
+
+obase = 10
+print "ibase = A; ibase = ", b, "\n"
+print "\qibase = \q\n"
+b
+obase = b
+for (i = 0; i <= 4096; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/parse_16.bc b/contrib/bc/tests/bc/scripts/parse_16.bc
new file mode 100644
index 000000000000..3a716cbe40ec
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/parse_16.bc
@@ -0,0 +1,19 @@
+#! /usr/bin/bc -q
+
+b = 16
+
+obase = 10
+print "ibase = A; ibase = ", b, "\n"
+print "\qibase = \q\n"
+b
+obase = b
+for (i = 0; i <= 4096; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print.bc b/contrib/bc/tests/bc/scripts/print.bc
deleted file mode 100644
index 9530cbdb3fc2..000000000000
--- a/contrib/bc/tests/bc/scripts/print.bc
+++ /dev/null
@@ -1,25 +0,0 @@
-#! /usr/bin/bc -q
-
-for (b = 2; b <= 100; ++b) {
-
- if (b == 10) continue
-
- s = b * b
-
- print "obase = ", b, "\n"
- print "\qobase = \q\n"
- b
-
- for (i = 0; i <= s; ++i) {
- i
- print "0.", i, "\n"
- print ".", i, "\n"
- print "1.", i, "\n"
- print i, ".", "\n"
- print i, ".", i, "\n"
- }
-
- 2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
-}
-
-halt
diff --git a/contrib/bc/tests/bc/scripts/print_002.bc b/contrib/bc/tests/bc/scripts/print_002.bc
new file mode 100644
index 000000000000..e96bd8d3f440
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_002.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 2
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_003.bc b/contrib/bc/tests/bc/scripts/print_003.bc
new file mode 100644
index 000000000000..46365153a3c8
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_003.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 3
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_004.bc b/contrib/bc/tests/bc/scripts/print_004.bc
new file mode 100644
index 000000000000..ff0b285930fe
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_004.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 4
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_005.bc b/contrib/bc/tests/bc/scripts/print_005.bc
new file mode 100644
index 000000000000..6a5982ae6a5e
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_005.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 5
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_006.bc b/contrib/bc/tests/bc/scripts/print_006.bc
new file mode 100644
index 000000000000..3b9bbccb2850
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_006.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 6
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_007.bc b/contrib/bc/tests/bc/scripts/print_007.bc
new file mode 100644
index 000000000000..f6e784792575
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_007.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 7
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_008.bc b/contrib/bc/tests/bc/scripts/print_008.bc
new file mode 100644
index 000000000000..a77d41751233
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_008.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 8
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_009.bc b/contrib/bc/tests/bc/scripts/print_009.bc
new file mode 100644
index 000000000000..1aef988112a1
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_009.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 9
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_011.bc b/contrib/bc/tests/bc/scripts/print_011.bc
new file mode 100644
index 000000000000..fb2d29293ad8
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_011.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 11
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_012.bc b/contrib/bc/tests/bc/scripts/print_012.bc
new file mode 100644
index 000000000000..466e64e2798e
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_012.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 12
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_013.bc b/contrib/bc/tests/bc/scripts/print_013.bc
new file mode 100644
index 000000000000..55525fd1398c
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_013.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 13
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_014.bc b/contrib/bc/tests/bc/scripts/print_014.bc
new file mode 100644
index 000000000000..dfa40541c7a3
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_014.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 14
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_015.bc b/contrib/bc/tests/bc/scripts/print_015.bc
new file mode 100644
index 000000000000..2426e409d94a
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_015.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 15
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_016.bc b/contrib/bc/tests/bc/scripts/print_016.bc
new file mode 100644
index 000000000000..44131bfe3ab7
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_016.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 16
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_017.bc b/contrib/bc/tests/bc/scripts/print_017.bc
new file mode 100644
index 000000000000..e6c802166d2a
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_017.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 17
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_018.bc b/contrib/bc/tests/bc/scripts/print_018.bc
new file mode 100644
index 000000000000..256c3436ab5a
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_018.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 18
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_019.bc b/contrib/bc/tests/bc/scripts/print_019.bc
new file mode 100644
index 000000000000..a2552236a144
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_019.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 19
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_020.bc b/contrib/bc/tests/bc/scripts/print_020.bc
new file mode 100644
index 000000000000..df15b92c727a
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_020.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 20
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_021.bc b/contrib/bc/tests/bc/scripts/print_021.bc
new file mode 100644
index 000000000000..83b3d2ad3c73
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_021.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 21
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_022.bc b/contrib/bc/tests/bc/scripts/print_022.bc
new file mode 100644
index 000000000000..9ab0d5e92f5c
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_022.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 22
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_023.bc b/contrib/bc/tests/bc/scripts/print_023.bc
new file mode 100644
index 000000000000..4641ee25d325
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_023.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 23
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_024.bc b/contrib/bc/tests/bc/scripts/print_024.bc
new file mode 100644
index 000000000000..7c02ba179d71
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_024.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 24
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_025.bc b/contrib/bc/tests/bc/scripts/print_025.bc
new file mode 100644
index 000000000000..95883f34338f
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_025.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 25
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_026.bc b/contrib/bc/tests/bc/scripts/print_026.bc
new file mode 100644
index 000000000000..152fe8053ddd
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_026.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 26
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_027.bc b/contrib/bc/tests/bc/scripts/print_027.bc
new file mode 100644
index 000000000000..60b56ca3112e
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_027.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 27
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_028.bc b/contrib/bc/tests/bc/scripts/print_028.bc
new file mode 100644
index 000000000000..b41b482aaec9
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_028.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 28
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_029.bc b/contrib/bc/tests/bc/scripts/print_029.bc
new file mode 100644
index 000000000000..3637407473e3
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_029.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 29
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_030.bc b/contrib/bc/tests/bc/scripts/print_030.bc
new file mode 100644
index 000000000000..066546061a72
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_030.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 30
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_031.bc b/contrib/bc/tests/bc/scripts/print_031.bc
new file mode 100644
index 000000000000..5f0ba04afdc3
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_031.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 31
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_032.bc b/contrib/bc/tests/bc/scripts/print_032.bc
new file mode 100644
index 000000000000..8c1f7b83b505
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_032.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 32
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_033.bc b/contrib/bc/tests/bc/scripts/print_033.bc
new file mode 100644
index 000000000000..d4af3e843772
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_033.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 33
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_034.bc b/contrib/bc/tests/bc/scripts/print_034.bc
new file mode 100644
index 000000000000..90607dac7a14
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_034.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 34
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_035.bc b/contrib/bc/tests/bc/scripts/print_035.bc
new file mode 100644
index 000000000000..175054b155ca
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_035.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 35
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_036.bc b/contrib/bc/tests/bc/scripts/print_036.bc
new file mode 100644
index 000000000000..63c212f3723d
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_036.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 36
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_037.bc b/contrib/bc/tests/bc/scripts/print_037.bc
new file mode 100644
index 000000000000..8e51d9af1c40
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_037.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 37
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_038.bc b/contrib/bc/tests/bc/scripts/print_038.bc
new file mode 100644
index 000000000000..7208ed1e8507
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_038.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 38
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_039.bc b/contrib/bc/tests/bc/scripts/print_039.bc
new file mode 100644
index 000000000000..0459f33f2e0d
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_039.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 39
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_040.bc b/contrib/bc/tests/bc/scripts/print_040.bc
new file mode 100644
index 000000000000..53df241dbaf0
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_040.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 40
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_041.bc b/contrib/bc/tests/bc/scripts/print_041.bc
new file mode 100644
index 000000000000..c539ad9592ee
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_041.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 41
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_042.bc b/contrib/bc/tests/bc/scripts/print_042.bc
new file mode 100644
index 000000000000..088c3b2f4cb4
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_042.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 42
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_043.bc b/contrib/bc/tests/bc/scripts/print_043.bc
new file mode 100644
index 000000000000..8646a8f2af4c
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_043.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 43
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_044.bc b/contrib/bc/tests/bc/scripts/print_044.bc
new file mode 100644
index 000000000000..1bd208266f4b
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_044.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 44
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_045.bc b/contrib/bc/tests/bc/scripts/print_045.bc
new file mode 100644
index 000000000000..7463c3e5575a
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_045.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 45
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_046.bc b/contrib/bc/tests/bc/scripts/print_046.bc
new file mode 100644
index 000000000000..dd78294c5bb1
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_046.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 46
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_047.bc b/contrib/bc/tests/bc/scripts/print_047.bc
new file mode 100644
index 000000000000..3b0654918876
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_047.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 47
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_048.bc b/contrib/bc/tests/bc/scripts/print_048.bc
new file mode 100644
index 000000000000..71dba3d7e536
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_048.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 48
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_049.bc b/contrib/bc/tests/bc/scripts/print_049.bc
new file mode 100644
index 000000000000..7ab19539ef2a
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_049.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 49
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_050.bc b/contrib/bc/tests/bc/scripts/print_050.bc
new file mode 100644
index 000000000000..93983f5f5c6f
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_050.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 50
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_051.bc b/contrib/bc/tests/bc/scripts/print_051.bc
new file mode 100644
index 000000000000..32f5e76a46b5
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_051.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 51
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_052.bc b/contrib/bc/tests/bc/scripts/print_052.bc
new file mode 100644
index 000000000000..662170d78452
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_052.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 52
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_053.bc b/contrib/bc/tests/bc/scripts/print_053.bc
new file mode 100644
index 000000000000..05986f27bd7e
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_053.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 53
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_054.bc b/contrib/bc/tests/bc/scripts/print_054.bc
new file mode 100644
index 000000000000..92e20b70329a
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_054.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 54
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_055.bc b/contrib/bc/tests/bc/scripts/print_055.bc
new file mode 100644
index 000000000000..014637b461c2
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_055.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 55
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_056.bc b/contrib/bc/tests/bc/scripts/print_056.bc
new file mode 100644
index 000000000000..42642cb8e29d
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_056.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 56
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_057.bc b/contrib/bc/tests/bc/scripts/print_057.bc
new file mode 100644
index 000000000000..e4dd3d1eb8f6
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_057.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 57
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_058.bc b/contrib/bc/tests/bc/scripts/print_058.bc
new file mode 100644
index 000000000000..cc2e5e5e1dae
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_058.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 58
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_059.bc b/contrib/bc/tests/bc/scripts/print_059.bc
new file mode 100644
index 000000000000..2abe27da21da
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_059.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 59
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_060.bc b/contrib/bc/tests/bc/scripts/print_060.bc
new file mode 100644
index 000000000000..c542d10d1e57
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_060.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 60
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_061.bc b/contrib/bc/tests/bc/scripts/print_061.bc
new file mode 100644
index 000000000000..bcd520e0f38e
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_061.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 61
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_062.bc b/contrib/bc/tests/bc/scripts/print_062.bc
new file mode 100644
index 000000000000..4b9875e077c3
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_062.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 62
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_063.bc b/contrib/bc/tests/bc/scripts/print_063.bc
new file mode 100644
index 000000000000..9f4d77042b23
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_063.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 63
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_064.bc b/contrib/bc/tests/bc/scripts/print_064.bc
new file mode 100644
index 000000000000..8878d0a8e05a
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_064.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 64
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_065.bc b/contrib/bc/tests/bc/scripts/print_065.bc
new file mode 100644
index 000000000000..faf3b4361d09
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_065.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 65
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_066.bc b/contrib/bc/tests/bc/scripts/print_066.bc
new file mode 100644
index 000000000000..07f30fa22c0f
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_066.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 66
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_067.bc b/contrib/bc/tests/bc/scripts/print_067.bc
new file mode 100644
index 000000000000..202604d755b0
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_067.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 67
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_068.bc b/contrib/bc/tests/bc/scripts/print_068.bc
new file mode 100644
index 000000000000..f90912bef26a
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_068.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 68
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_069.bc b/contrib/bc/tests/bc/scripts/print_069.bc
new file mode 100644
index 000000000000..175263e39074
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_069.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 69
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_070.bc b/contrib/bc/tests/bc/scripts/print_070.bc
new file mode 100644
index 000000000000..0786f4d3aa14
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_070.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 70
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_071.bc b/contrib/bc/tests/bc/scripts/print_071.bc
new file mode 100644
index 000000000000..292c38e91852
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_071.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 71
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_072.bc b/contrib/bc/tests/bc/scripts/print_072.bc
new file mode 100644
index 000000000000..e6506ee7ee37
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_072.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 72
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_073.bc b/contrib/bc/tests/bc/scripts/print_073.bc
new file mode 100644
index 000000000000..6b7d3efae5cd
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_073.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 73
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_074.bc b/contrib/bc/tests/bc/scripts/print_074.bc
new file mode 100644
index 000000000000..e61abaf98f94
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_074.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 74
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_075.bc b/contrib/bc/tests/bc/scripts/print_075.bc
new file mode 100644
index 000000000000..84704063a65e
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_075.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 75
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_076.bc b/contrib/bc/tests/bc/scripts/print_076.bc
new file mode 100644
index 000000000000..3efed22515d9
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_076.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 76
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_077.bc b/contrib/bc/tests/bc/scripts/print_077.bc
new file mode 100644
index 000000000000..ef01f150c1b0
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_077.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 77
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_078.bc b/contrib/bc/tests/bc/scripts/print_078.bc
new file mode 100644
index 000000000000..8f431f54c12f
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_078.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 78
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_079.bc b/contrib/bc/tests/bc/scripts/print_079.bc
new file mode 100644
index 000000000000..5eb213c19431
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_079.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 79
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_080.bc b/contrib/bc/tests/bc/scripts/print_080.bc
new file mode 100644
index 000000000000..46b1a547d3a3
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_080.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 80
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_081.bc b/contrib/bc/tests/bc/scripts/print_081.bc
new file mode 100644
index 000000000000..9b0f2415f19e
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_081.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 81
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_082.bc b/contrib/bc/tests/bc/scripts/print_082.bc
new file mode 100644
index 000000000000..ac1cc4028775
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_082.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 82
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_083.bc b/contrib/bc/tests/bc/scripts/print_083.bc
new file mode 100644
index 000000000000..8f6966e280f4
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_083.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 83
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_084.bc b/contrib/bc/tests/bc/scripts/print_084.bc
new file mode 100644
index 000000000000..e5e2c167c6f7
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_084.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 84
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_085.bc b/contrib/bc/tests/bc/scripts/print_085.bc
new file mode 100644
index 000000000000..3e5b50b591f7
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_085.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 85
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_086.bc b/contrib/bc/tests/bc/scripts/print_086.bc
new file mode 100644
index 000000000000..9cfcc8982fa7
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_086.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 86
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_087.bc b/contrib/bc/tests/bc/scripts/print_087.bc
new file mode 100644
index 000000000000..ed3c6687272b
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_087.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 87
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_088.bc b/contrib/bc/tests/bc/scripts/print_088.bc
new file mode 100644
index 000000000000..1e001883e576
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_088.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 88
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_089.bc b/contrib/bc/tests/bc/scripts/print_089.bc
new file mode 100644
index 000000000000..4234219f487d
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_089.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 89
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_090.bc b/contrib/bc/tests/bc/scripts/print_090.bc
new file mode 100644
index 000000000000..ebe4650c1696
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_090.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 90
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_091.bc b/contrib/bc/tests/bc/scripts/print_091.bc
new file mode 100644
index 000000000000..5c5fd5c95256
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_091.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 91
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_092.bc b/contrib/bc/tests/bc/scripts/print_092.bc
new file mode 100644
index 000000000000..992a738fee41
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_092.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 92
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_093.bc b/contrib/bc/tests/bc/scripts/print_093.bc
new file mode 100644
index 000000000000..7e91b2b5e8fd
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_093.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 93
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_094.bc b/contrib/bc/tests/bc/scripts/print_094.bc
new file mode 100644
index 000000000000..3567fe0bf69e
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_094.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 94
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_095.bc b/contrib/bc/tests/bc/scripts/print_095.bc
new file mode 100644
index 000000000000..1945f2daee1e
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_095.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 95
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_096.bc b/contrib/bc/tests/bc/scripts/print_096.bc
new file mode 100644
index 000000000000..837f87a57e63
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_096.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 96
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_097.bc b/contrib/bc/tests/bc/scripts/print_097.bc
new file mode 100644
index 000000000000..efcf4a096ece
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_097.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 97
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_098.bc b/contrib/bc/tests/bc/scripts/print_098.bc
new file mode 100644
index 000000000000..d14203d29656
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_098.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 98
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_099.bc b/contrib/bc/tests/bc/scripts/print_099.bc
new file mode 100644
index 000000000000..0bbb410318ee
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_099.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 99
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/print_100.bc b/contrib/bc/tests/bc/scripts/print_100.bc
new file mode 100644
index 000000000000..4ac46e3eba42
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/print_100.bc
@@ -0,0 +1,22 @@
+#! /usr/bin/bc -q
+
+b = 100
+
+s = b * b
+
+print "obase = ", b, "\n"
+print "\qobase = \q\n"
+b
+
+for (i = 0; i <= s; ++i) {
+ i
+ print "0.", i, "\n"
+ print ".", i, "\n"
+ print "1.", i, "\n"
+ print i, ".", "\n"
+ print i, ".", i, "\n"
+}
+
+2189432174861923048671023498128347619023487610234689172304.192748960128745108927461089237469018723460
+
+halt
diff --git a/contrib/bc/tests/bc/scripts/subtract.bc b/contrib/bc/tests/bc/scripts/subtract_00100.bc
index 1e592942cab3..93339780f2f7 100644
--- a/contrib/bc/tests/bc/scripts/subtract.bc
+++ b/contrib/bc/tests/bc/scripts/subtract_00100.bc
@@ -10,7 +10,7 @@ for (i = 0; i <= len; ++i) {
a[i]
}
-for (i = 1; i <= 10000; ++i) {
+for (i = 1; i <= 100; ++i) {
for (j = 0; j < len; ++j) {
a[i] - a[j]
}
diff --git a/contrib/bc/tests/bc/scripts/subtract_00200.bc b/contrib/bc/tests/bc/scripts/subtract_00200.bc
new file mode 100644
index 000000000000..2bcad94d76d1
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_00200.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 101; i <= 200; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_00300.bc b/contrib/bc/tests/bc/scripts/subtract_00300.bc
new file mode 100644
index 000000000000..5d5b5fd2bf61
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_00300.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 201; i <= 300; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_00400.bc b/contrib/bc/tests/bc/scripts/subtract_00400.bc
new file mode 100644
index 000000000000..9298eeec0007
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_00400.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 301; i <= 400; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_00500.bc b/contrib/bc/tests/bc/scripts/subtract_00500.bc
new file mode 100644
index 000000000000..581a8144fea7
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_00500.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 401; i <= 500; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_00600.bc b/contrib/bc/tests/bc/scripts/subtract_00600.bc
new file mode 100644
index 000000000000..2a1a5e6eb1ae
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_00600.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 501; i <= 600; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_00700.bc b/contrib/bc/tests/bc/scripts/subtract_00700.bc
new file mode 100644
index 000000000000..867277161d2c
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_00700.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 601; i <= 700; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_00800.bc b/contrib/bc/tests/bc/scripts/subtract_00800.bc
new file mode 100644
index 000000000000..68dfd28f1108
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_00800.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 701; i <= 800; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_00900.bc b/contrib/bc/tests/bc/scripts/subtract_00900.bc
new file mode 100644
index 000000000000..b343861ecf26
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_00900.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 801; i <= 900; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_01000.bc b/contrib/bc/tests/bc/scripts/subtract_01000.bc
new file mode 100644
index 000000000000..4e8e5951c8d9
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_01000.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 901; i <= 1000; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_01100.bc b/contrib/bc/tests/bc/scripts/subtract_01100.bc
new file mode 100644
index 000000000000..0b7d1a5ca314
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_01100.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 1001; i <= 1100; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_01200.bc b/contrib/bc/tests/bc/scripts/subtract_01200.bc
new file mode 100644
index 000000000000..bfa237043f89
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_01200.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 1101; i <= 1200; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_01300.bc b/contrib/bc/tests/bc/scripts/subtract_01300.bc
new file mode 100644
index 000000000000..c83483db9e8b
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_01300.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 1201; i <= 1300; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_01400.bc b/contrib/bc/tests/bc/scripts/subtract_01400.bc
new file mode 100644
index 000000000000..e979361f49f4
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_01400.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 1301; i <= 1400; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_01500.bc b/contrib/bc/tests/bc/scripts/subtract_01500.bc
new file mode 100644
index 000000000000..c2ef1ee1385e
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_01500.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 1401; i <= 1500; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_01600.bc b/contrib/bc/tests/bc/scripts/subtract_01600.bc
new file mode 100644
index 000000000000..2113ddefeb5f
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_01600.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 1501; i <= 1600; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_01700.bc b/contrib/bc/tests/bc/scripts/subtract_01700.bc
new file mode 100644
index 000000000000..ca9505c1983f
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_01700.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 1601; i <= 1700; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_01800.bc b/contrib/bc/tests/bc/scripts/subtract_01800.bc
new file mode 100644
index 000000000000..650cc6a7d551
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_01800.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 1701; i <= 1800; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_01900.bc b/contrib/bc/tests/bc/scripts/subtract_01900.bc
new file mode 100644
index 000000000000..72c17fd1af8a
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_01900.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 1801; i <= 1900; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_02000.bc b/contrib/bc/tests/bc/scripts/subtract_02000.bc
new file mode 100644
index 000000000000..579c84e67f8f
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_02000.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 1901; i <= 2000; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_02100.bc b/contrib/bc/tests/bc/scripts/subtract_02100.bc
new file mode 100644
index 000000000000..2ed9f5f0c8a9
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_02100.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 2001; i <= 2100; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_02200.bc b/contrib/bc/tests/bc/scripts/subtract_02200.bc
new file mode 100644
index 000000000000..0a11cab25d6e
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_02200.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 2101; i <= 2200; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_02300.bc b/contrib/bc/tests/bc/scripts/subtract_02300.bc
new file mode 100644
index 000000000000..29c66c1913fd
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_02300.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 2201; i <= 2300; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_02400.bc b/contrib/bc/tests/bc/scripts/subtract_02400.bc
new file mode 100644
index 000000000000..b0f2f197a2c6
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_02400.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 2301; i <= 2400; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_02500.bc b/contrib/bc/tests/bc/scripts/subtract_02500.bc
new file mode 100644
index 000000000000..7e9d46a8bc3b
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_02500.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 2401; i <= 2500; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_02600.bc b/contrib/bc/tests/bc/scripts/subtract_02600.bc
new file mode 100644
index 000000000000..49acabcf66e5
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_02600.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 2501; i <= 2600; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_02700.bc b/contrib/bc/tests/bc/scripts/subtract_02700.bc
new file mode 100644
index 000000000000..8d7473e93f75
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_02700.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 2601; i <= 2700; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_02800.bc b/contrib/bc/tests/bc/scripts/subtract_02800.bc
new file mode 100644
index 000000000000..588db5a26ee7
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_02800.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 2701; i <= 2800; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_02900.bc b/contrib/bc/tests/bc/scripts/subtract_02900.bc
new file mode 100644
index 000000000000..1e6a4fea0416
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_02900.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 2801; i <= 2900; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_03000.bc b/contrib/bc/tests/bc/scripts/subtract_03000.bc
new file mode 100644
index 000000000000..0eb1e3da7cf9
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_03000.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 2901; i <= 3000; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_03100.bc b/contrib/bc/tests/bc/scripts/subtract_03100.bc
new file mode 100644
index 000000000000..6513ee49fe93
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_03100.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 3001; i <= 3100; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_03200.bc b/contrib/bc/tests/bc/scripts/subtract_03200.bc
new file mode 100644
index 000000000000..310541d4de02
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_03200.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 3101; i <= 3200; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_03300.bc b/contrib/bc/tests/bc/scripts/subtract_03300.bc
new file mode 100644
index 000000000000..c984f7c73e04
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_03300.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 3201; i <= 3300; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_03400.bc b/contrib/bc/tests/bc/scripts/subtract_03400.bc
new file mode 100644
index 000000000000..608ab651fc2b
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_03400.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 3301; i <= 3400; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_03500.bc b/contrib/bc/tests/bc/scripts/subtract_03500.bc
new file mode 100644
index 000000000000..215b96d850d4
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_03500.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 3401; i <= 3500; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_03600.bc b/contrib/bc/tests/bc/scripts/subtract_03600.bc
new file mode 100644
index 000000000000..90d0d4d5144e
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_03600.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 3501; i <= 3600; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_03700.bc b/contrib/bc/tests/bc/scripts/subtract_03700.bc
new file mode 100644
index 000000000000..511bf1ed4f9d
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_03700.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 3601; i <= 3700; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_03800.bc b/contrib/bc/tests/bc/scripts/subtract_03800.bc
new file mode 100644
index 000000000000..99b69618b745
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_03800.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 3701; i <= 3800; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_03900.bc b/contrib/bc/tests/bc/scripts/subtract_03900.bc
new file mode 100644
index 000000000000..a438bdfb86be
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_03900.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 3801; i <= 3900; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_04000.bc b/contrib/bc/tests/bc/scripts/subtract_04000.bc
new file mode 100644
index 000000000000..7371044e02c3
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_04000.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 3901; i <= 4000; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_04100.bc b/contrib/bc/tests/bc/scripts/subtract_04100.bc
new file mode 100644
index 000000000000..f9f47d394f65
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_04100.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 4001; i <= 4100; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_04200.bc b/contrib/bc/tests/bc/scripts/subtract_04200.bc
new file mode 100644
index 000000000000..56ff98e521b1
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_04200.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 4101; i <= 4200; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_04300.bc b/contrib/bc/tests/bc/scripts/subtract_04300.bc
new file mode 100644
index 000000000000..2ee5767b2088
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_04300.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 4201; i <= 4300; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_04400.bc b/contrib/bc/tests/bc/scripts/subtract_04400.bc
new file mode 100644
index 000000000000..b355f189948f
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_04400.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 4301; i <= 4400; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_04500.bc b/contrib/bc/tests/bc/scripts/subtract_04500.bc
new file mode 100644
index 000000000000..aa34485891ce
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_04500.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 4401; i <= 4500; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_04600.bc b/contrib/bc/tests/bc/scripts/subtract_04600.bc
new file mode 100644
index 000000000000..cd5bea596488
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_04600.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 4501; i <= 4600; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_04700.bc b/contrib/bc/tests/bc/scripts/subtract_04700.bc
new file mode 100644
index 000000000000..36de8058bb93
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_04700.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 4601; i <= 4700; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_04800.bc b/contrib/bc/tests/bc/scripts/subtract_04800.bc
new file mode 100644
index 000000000000..cc714dd72fd9
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_04800.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 4701; i <= 4800; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_04900.bc b/contrib/bc/tests/bc/scripts/subtract_04900.bc
new file mode 100644
index 000000000000..6e8cfb8c931d
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_04900.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 4801; i <= 4900; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_05000.bc b/contrib/bc/tests/bc/scripts/subtract_05000.bc
new file mode 100644
index 000000000000..f4847219bdb9
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_05000.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 4901; i <= 5000; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_05100.bc b/contrib/bc/tests/bc/scripts/subtract_05100.bc
new file mode 100644
index 000000000000..bea4f5b09f2d
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_05100.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 5001; i <= 5100; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_05200.bc b/contrib/bc/tests/bc/scripts/subtract_05200.bc
new file mode 100644
index 000000000000..38a7b5c6d234
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_05200.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 5101; i <= 5200; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_05300.bc b/contrib/bc/tests/bc/scripts/subtract_05300.bc
new file mode 100644
index 000000000000..9004108f3e8d
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_05300.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 5201; i <= 5300; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_05400.bc b/contrib/bc/tests/bc/scripts/subtract_05400.bc
new file mode 100644
index 000000000000..eec164c504cb
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_05400.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 5301; i <= 5400; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_05500.bc b/contrib/bc/tests/bc/scripts/subtract_05500.bc
new file mode 100644
index 000000000000..864f05e70d25
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_05500.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 5401; i <= 5500; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_05600.bc b/contrib/bc/tests/bc/scripts/subtract_05600.bc
new file mode 100644
index 000000000000..874f45183b7c
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_05600.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 5501; i <= 5600; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_05700.bc b/contrib/bc/tests/bc/scripts/subtract_05700.bc
new file mode 100644
index 000000000000..6a5c351c574f
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_05700.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 5601; i <= 5700; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_05800.bc b/contrib/bc/tests/bc/scripts/subtract_05800.bc
new file mode 100644
index 000000000000..5fc99fe84503
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_05800.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 5701; i <= 5800; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_05900.bc b/contrib/bc/tests/bc/scripts/subtract_05900.bc
new file mode 100644
index 000000000000..cb12eba549b2
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_05900.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 5801; i <= 5900; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_06000.bc b/contrib/bc/tests/bc/scripts/subtract_06000.bc
new file mode 100644
index 000000000000..0c85a7267eb1
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_06000.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 5901; i <= 6000; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_06100.bc b/contrib/bc/tests/bc/scripts/subtract_06100.bc
new file mode 100644
index 000000000000..8f43fc2687d3
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_06100.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 6001; i <= 6100; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_06200.bc b/contrib/bc/tests/bc/scripts/subtract_06200.bc
new file mode 100644
index 000000000000..d508561b5838
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_06200.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 6101; i <= 6200; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_06300.bc b/contrib/bc/tests/bc/scripts/subtract_06300.bc
new file mode 100644
index 000000000000..3073a3f75dfc
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_06300.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 6201; i <= 6300; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_06400.bc b/contrib/bc/tests/bc/scripts/subtract_06400.bc
new file mode 100644
index 000000000000..ee25fc01bca3
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_06400.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 6301; i <= 6400; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_06500.bc b/contrib/bc/tests/bc/scripts/subtract_06500.bc
new file mode 100644
index 000000000000..ff59fcde38c2
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_06500.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 6401; i <= 6500; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_06600.bc b/contrib/bc/tests/bc/scripts/subtract_06600.bc
new file mode 100644
index 000000000000..8bd56c8d2df9
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_06600.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 6501; i <= 6600; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_06700.bc b/contrib/bc/tests/bc/scripts/subtract_06700.bc
new file mode 100644
index 000000000000..160a31bdf4a2
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_06700.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 6601; i <= 6700; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_06800.bc b/contrib/bc/tests/bc/scripts/subtract_06800.bc
new file mode 100644
index 000000000000..7d2bc99dec0d
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_06800.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 6701; i <= 6800; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_06900.bc b/contrib/bc/tests/bc/scripts/subtract_06900.bc
new file mode 100644
index 000000000000..716d7c273111
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_06900.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 6801; i <= 6900; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_07000.bc b/contrib/bc/tests/bc/scripts/subtract_07000.bc
new file mode 100644
index 000000000000..259dc55507f2
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_07000.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 6901; i <= 7000; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_07100.bc b/contrib/bc/tests/bc/scripts/subtract_07100.bc
new file mode 100644
index 000000000000..b992c30681ec
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_07100.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 7001; i <= 7100; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_07200.bc b/contrib/bc/tests/bc/scripts/subtract_07200.bc
new file mode 100644
index 000000000000..afa77522e912
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_07200.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 7101; i <= 7200; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_07300.bc b/contrib/bc/tests/bc/scripts/subtract_07300.bc
new file mode 100644
index 000000000000..5dd6997fbeff
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_07300.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 7201; i <= 7300; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_07400.bc b/contrib/bc/tests/bc/scripts/subtract_07400.bc
new file mode 100644
index 000000000000..70b88b1bc04b
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_07400.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 7301; i <= 7400; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_07500.bc b/contrib/bc/tests/bc/scripts/subtract_07500.bc
new file mode 100644
index 000000000000..806565dc5fa8
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_07500.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 7401; i <= 7500; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_07600.bc b/contrib/bc/tests/bc/scripts/subtract_07600.bc
new file mode 100644
index 000000000000..b063df122391
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_07600.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 7501; i <= 7600; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_07700.bc b/contrib/bc/tests/bc/scripts/subtract_07700.bc
new file mode 100644
index 000000000000..f31cb7b170a8
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_07700.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 7601; i <= 7700; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_07800.bc b/contrib/bc/tests/bc/scripts/subtract_07800.bc
new file mode 100644
index 000000000000..222b583de5c5
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_07800.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 7701; i <= 7800; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_07900.bc b/contrib/bc/tests/bc/scripts/subtract_07900.bc
new file mode 100644
index 000000000000..8a36cb2020c2
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_07900.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 7801; i <= 7900; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_08000.bc b/contrib/bc/tests/bc/scripts/subtract_08000.bc
new file mode 100644
index 000000000000..8b10e0644622
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_08000.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 7901; i <= 8000; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_08100.bc b/contrib/bc/tests/bc/scripts/subtract_08100.bc
new file mode 100644
index 000000000000..b7bf9481f6ff
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_08100.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 8001; i <= 8100; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_08200.bc b/contrib/bc/tests/bc/scripts/subtract_08200.bc
new file mode 100644
index 000000000000..2cf2b2282ca1
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_08200.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 8101; i <= 8200; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_08300.bc b/contrib/bc/tests/bc/scripts/subtract_08300.bc
new file mode 100644
index 000000000000..43e1090b2708
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_08300.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 8201; i <= 8300; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_08400.bc b/contrib/bc/tests/bc/scripts/subtract_08400.bc
new file mode 100644
index 000000000000..ecde1157ef8d
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_08400.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 8301; i <= 8400; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_08500.bc b/contrib/bc/tests/bc/scripts/subtract_08500.bc
new file mode 100644
index 000000000000..5e6e8ac95d34
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_08500.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 8401; i <= 8500; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_08600.bc b/contrib/bc/tests/bc/scripts/subtract_08600.bc
new file mode 100644
index 000000000000..f56db3c36a3b
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_08600.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 8501; i <= 8600; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_08700.bc b/contrib/bc/tests/bc/scripts/subtract_08700.bc
new file mode 100644
index 000000000000..3d5d9b94598b
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_08700.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 8601; i <= 8700; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_08800.bc b/contrib/bc/tests/bc/scripts/subtract_08800.bc
new file mode 100644
index 000000000000..87a5449472da
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_08800.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 8701; i <= 8800; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_08900.bc b/contrib/bc/tests/bc/scripts/subtract_08900.bc
new file mode 100644
index 000000000000..2ac715811882
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_08900.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 8801; i <= 8900; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_09000.bc b/contrib/bc/tests/bc/scripts/subtract_09000.bc
new file mode 100644
index 000000000000..7c39d922b736
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_09000.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 8901; i <= 9000; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_09100.bc b/contrib/bc/tests/bc/scripts/subtract_09100.bc
new file mode 100644
index 000000000000..5b8c99cf0312
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_09100.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 9001; i <= 9100; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_09200.bc b/contrib/bc/tests/bc/scripts/subtract_09200.bc
new file mode 100644
index 000000000000..173f64d70786
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_09200.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 9101; i <= 9200; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_09300.bc b/contrib/bc/tests/bc/scripts/subtract_09300.bc
new file mode 100644
index 000000000000..6226e6fecb1f
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_09300.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 9201; i <= 9300; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_09400.bc b/contrib/bc/tests/bc/scripts/subtract_09400.bc
new file mode 100644
index 000000000000..0e76fa04f14f
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_09400.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 9301; i <= 9400; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_09500.bc b/contrib/bc/tests/bc/scripts/subtract_09500.bc
new file mode 100644
index 000000000000..b5336018e368
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_09500.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 9401; i <= 9500; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_09600.bc b/contrib/bc/tests/bc/scripts/subtract_09600.bc
new file mode 100644
index 000000000000..479bfe29e361
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_09600.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 9501; i <= 9600; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_09700.bc b/contrib/bc/tests/bc/scripts/subtract_09700.bc
new file mode 100644
index 000000000000..14c5df67605e
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_09700.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 9601; i <= 9700; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_09800.bc b/contrib/bc/tests/bc/scripts/subtract_09800.bc
new file mode 100644
index 000000000000..5d62bae24a40
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_09800.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 9701; i <= 9800; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_09900.bc b/contrib/bc/tests/bc/scripts/subtract_09900.bc
new file mode 100644
index 000000000000..d2926980b543
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_09900.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 9801; i <= 9900; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/scripts/subtract_10000.bc b/contrib/bc/tests/bc/scripts/subtract_10000.bc
new file mode 100644
index 000000000000..32cafefd324f
--- /dev/null
+++ b/contrib/bc/tests/bc/scripts/subtract_10000.bc
@@ -0,0 +1,17 @@
+#! /usr/bin/bc -lq
+
+scale = 20
+x = 1234567890 / scale
+len = length(x) + 1 + scale
+len *= 2
+
+for (i = 0; i <= len; ++i) {
+ a[i] = x * (10^i)
+ a[i]
+}
+
+for (i = 9901; i <= 10000; ++i) {
+ for (j = 0; j < len; ++j) {
+ a[i] - a[j]
+ }
+}
diff --git a/contrib/bc/tests/bc/timeconst.sh b/contrib/bc/tests/bc/timeconst.sh
index 35bd80d56040..393268843649 100755
--- a/contrib/bc/tests/bc/timeconst.sh
+++ b/contrib/bc/tests/bc/timeconst.sh
@@ -1,6 +1,6 @@
#! /bin/sh
#
-# Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+# Copyright (c) 2018-2025 Gavin D. Howard and contributors.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
@@ -81,6 +81,9 @@ base=$(basename "$timeconst")
# If the script does not exist, just skip. Running this test is not necessary.
if [ ! -f "$timeconst" ]; then
printf 'Warning: %s does not exist\n' "$timeconst"
+ printf '%s is not part of this bc because of license incompatibility\n' "$timeconst"
+ printf 'Get it at https://github.com/torvalds/linux/blob/master/kernel/time/timeconst.bc\n'
+ printf 'if you want to test it\n'
printf 'Skipping...\n'
exit 0
fi
diff --git a/contrib/bc/tests/bcl.c b/contrib/bc/tests/bcl.c
index 3a2df4488c05..fb730e773c6d 100644
--- a/contrib/bc/tests/bcl.c
+++ b/contrib/bc/tests/bcl.c
@@ -3,7 +3,7 @@
*
* SPDX-License-Identifier: BSD-2-Clause
*
- * Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+ * Copyright (c) 2018-2025 Gavin D. Howard and contributors.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/tests/dc/scripts/easter.dc b/contrib/bc/tests/dc/scripts/easter.dc
new file mode 100644
index 000000000000..e2c126ba1129
--- /dev/null
+++ b/contrib/bc/tests/dc/scripts/easter.dc
@@ -0,0 +1,49 @@
+#! /usr/bin/dc
+
+# This is the actual script.
+[
+ ddsf
+ [
+ lfp
+ [too early
+ ]P
+ q
+ ]s@
+ 1583>@
+ ddd19%1+sg100/1+d3*4/12-sx8*5+25/5-sz5*4/lx-10-sdlg11*20+lz+lx-30%
+ d
+ [30+]s@
+ 0>@d
+ [
+ [1+]s@
+ lg11<@
+ ]s@
+ 25=@d
+ [1+]s@
+ 24=@se44le-d
+ [30+]s@
+ 21>@dld+7%-7+
+ [March ]sm
+ d
+ [
+ 31-
+ [April ]sm
+ ]s@
+ 31<@dn32PsnlmPdnsn1z>p
+ 10P
+]sp
+
+2021
+lpx
+
+2022
+lpx
+
+2023
+lpx
+
+2024
+lpx
+
+2025
+lpx
diff --git a/contrib/bc/tests/dc/scripts/easter.sh b/contrib/bc/tests/dc/scripts/easter.sh
deleted file mode 100755
index ee8fa1d94c81..000000000000
--- a/contrib/bc/tests/dc/scripts/easter.sh
+++ /dev/null
@@ -1,93 +0,0 @@
-#!/bin/sh
-#
-# SPDX-License-Identifier: BSD-2-Clause
-#
-# Copyright (c) 2018-2024 Gavin D. Howard and contributors.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-#
-# * Redistributions of source code must retain the above copyright notice, this
-# list of conditions and the following disclaimer.
-#
-# * Redistributions in binary form must reproduce the above copyright notice,
-# this list of conditions and the following disclaimer in the documentation
-# and/or other materials provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-# POSSIBILITY OF SUCH DAMAGE.
-#
-
-set -e
-
-script="$0"
-
-testdir=$(dirname "${script}")
-
-# Just print the usage and exit with an error. This can receive a message to
-# print.
-# @param 1 A message to print.
-usage() {
- if [ $# -eq 1 ]; then
- printf '%s\n\n' "$1"
- fi
- printf 'usage: %s dc_exec year [options...]\n' "$script"
- exit 1
-}
-
-. "$testdir/../../../scripts/functions.sh"
-
-if test $# -lt 2
-then
- usage "Not enough arguments; need 2"
-fi
-
-dc_exec="$1"
-shift
-check_exec_arg "$dc_exec"
-
-year="$1"
-shift
-
-echo $year '
-[
- ddsf
- [
- lfp
- [too early
- ]P
- q
- ]s@
- 1583>@
- ddd19%1+sg100/1+d3*4/12-sx8*5+25/5-sz5*4/lx-10-sdlg11*20+lz+lx-30%
- d
- [30+]s@
- 0>@d
- [
- [1+]s@
- lg11<@
- ]s@
- 25=@d
- [1+]s@
- 24=@se44le-d
- [30+]s@
- 21>@dld+7%-7+
- [March ]sm
- d
- [
- 31-
- [April ]sm
- ]s@
- 31<@psnlmPpsn1z>p
-]sp
-lpx' | "$dc_exec" "$@" | tr '\012' ' '
-echo ''
diff --git a/contrib/bc/tests/dc/scripts/easter.txt b/contrib/bc/tests/dc/scripts/easter.txt
new file mode 100644
index 000000000000..0aef9531346d
--- /dev/null
+++ b/contrib/bc/tests/dc/scripts/easter.txt
@@ -0,0 +1,5 @@
+4 April 2021
+17 April 2022
+9 April 2023
+31 March 2024
+20 April 2025
diff --git a/contrib/bc/tests/dc/scripts/prime.dc b/contrib/bc/tests/dc/scripts/prime.dc
index cc769d2bbee1..6902aaef8f0c 100644
--- a/contrib/bc/tests/dc/scripts/prime.dc
+++ b/contrib/bc/tests/dc/scripts/prime.dc
@@ -1 +1 @@
-0k2p3p[dl!d2+s!%0=@l!l^!<#]s#[s/0ds^]s@[p]s&[ddvs^3s!l# x0<&2+d100000>.]ds.x
+0k2p3p[dl!d2+s!%0=@l!l^!<#]s#[s/0ds^]s@[p]s&[ddvs^3s!l# x0<&2+d10000>.]ds.x
diff --git a/contrib/bc/tests/error.sh b/contrib/bc/tests/error.sh
index 15cbd5577ed6..aa229a916809 100755
--- a/contrib/bc/tests/error.sh
+++ b/contrib/bc/tests/error.sh
@@ -2,7 +2,7 @@
#
# SPDX-License-Identifier: BSD-2-Clause
#
-# Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+# Copyright (c) 2018-2025 Gavin D. Howard and contributors.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
@@ -100,8 +100,6 @@ fi
if [ "$d" = "bc" ]; then
opts="-l"
halt="halt"
- read_call="read()"
- read_expr="${read_call}\n5+5;"
else
opts="-x"
halt="q"
diff --git a/contrib/bc/tests/errors.sh b/contrib/bc/tests/errors.sh
index 47053f9c7b43..93e4f5d0dedf 100755
--- a/contrib/bc/tests/errors.sh
+++ b/contrib/bc/tests/errors.sh
@@ -2,7 +2,7 @@
#
# SPDX-License-Identifier: BSD-2-Clause
#
-# Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+# Copyright (c) 2018-2025 Gavin D. Howard and contributors.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
@@ -89,8 +89,6 @@ read_errors="read_errors"
if [ "$d" = "bc" ]; then
opts="-l"
halt="halt"
- read_call="read()"
- read_expr="${read_call}\n5+5;"
else
opts="-x"
halt="q"
diff --git a/contrib/bc/tests/extra_required.txt b/contrib/bc/tests/extra_required.txt
index 038e6775d644..b335af4d57bf 100644
--- a/contrib/bc/tests/extra_required.txt
+++ b/contrib/bc/tests/extra_required.txt
@@ -1,5 +1,20 @@
engineering
-lib2
+lib2_p
+lib2_r
+lib2_ceil
+lib2_log
+lib2_root
+lib2_gcd
+lib2_bytes
+lib2_pi
+lib2_tan
+lib2_a2
+lib2_r2d
+lib2_d2r
+lib2_fac
+lib2_perm
+lib2_uint
+lib2_rand
fib
places
rand
diff --git a/contrib/bc/tests/history.py b/contrib/bc/tests/history.py
index a3b722386a62..cf19174f6d90 100755
--- a/contrib/bc/tests/history.py
+++ b/contrib/bc/tests/history.py
@@ -2,7 +2,7 @@
#
# SPDX-License-Identifier: BSD-2-Clause
#
-# Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+# Copyright (c) 2018-2025 Gavin D. Howard and contributors.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
@@ -128,7 +128,7 @@ def write_str(child, s):
def bc_banner(child):
bc_banner1 = "bc [0-9]+\\.[0-9]+\\.[0-9]+\r\n"
bc_banner2 = "Copyright \\(c\\) 2018-[2-9][0-9][0-9][0-9] Gavin D. Howard and contributors\r\n"
- bc_banner3 = "Report bugs at: https://git.gavinhoward.com/gavin/bc\r\n\r\n"
+ bc_banner3 = "Report bugs at: https://github.com/gavinhoward/bc\r\n\r\n"
bc_banner4 = "This is free software with ABSOLUTELY NO WARRANTY.\r\n\r\n"
expect(child, bc_banner1)
expect(child, bc_banner2)
diff --git a/contrib/bc/tests/history.sh b/contrib/bc/tests/history.sh
index d06d3c6af104..514ee1769903 100755
--- a/contrib/bc/tests/history.sh
+++ b/contrib/bc/tests/history.sh
@@ -2,7 +2,7 @@
#
# SPDX-License-Identifier: BSD-2-Clause
#
-# Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+# Copyright (c) 2018-2025 Gavin D. Howard and contributors.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/tests/other.sh b/contrib/bc/tests/other.sh
deleted file mode 100755
index 1012fe919dea..000000000000
--- a/contrib/bc/tests/other.sh
+++ /dev/null
@@ -1,593 +0,0 @@
-#! /bin/sh
-#
-# SPDX-License-Identifier: BSD-2-Clause
-#
-# Copyright (c) 2018-2024 Gavin D. Howard and contributors.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-#
-# * Redistributions of source code must retain the above copyright notice, this
-# list of conditions and the following disclaimer.
-#
-# * Redistributions in binary form must reproduce the above copyright notice,
-# this list of conditions and the following disclaimer in the documentation
-# and/or other materials provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-# POSSIBILITY OF SUCH DAMAGE.
-#
-
-set -e
-
-script="$0"
-testdir=$(dirname "$script")
-
-. "$testdir/../scripts/functions.sh"
-
-outputdir=${BC_TEST_OUTPUT_DIR:-$testdir}
-
-# Just print the usage and exit with an error. This can receive a message to
-# print.
-# @param 1 A message to print.
-usage() {
- if [ $# -eq 1 ]; then
- printf '%s\n\n' "$1"
- fi
- printf 'usage: %s dir extra_math [exec args...]\n' "$script"
- exit 1
-}
-
-# Command-line processing.
-if [ "$#" -ge 2 ]; then
-
- d="$1"
- shift
- check_d_arg "$d"
-
- extra_math="$1"
- shift
- check_bool_arg "$extra_math"
-
-else
- usage "Not enough arguments; need 2"
-fi
-
-if [ "$#" -lt 1 ]; then
- exe="$testdir/../bin/$d"
- check_exec_arg "$exe"
-else
- exe="$1"
- shift
- check_exec_arg "$exe"
-fi
-
-if [ "$d" = "bc" ]; then
- halt="quit"
-else
- halt="q"
-fi
-
-mkdir -p "$outputdir"
-
-# For tests later.
-num=100000000000000000000000000000000000000000000000000000000000000000000000000000
-num2="$num"
-numres="$num"
-num70="10000000000000000000000000000000000000000000000000000000000000000000\\
-0000000000"
-
-# Set stuff for the correct calculator.
-if [ "$d" = "bc" ]; then
- halt="halt"
- opt="x"
- lopt="extended-register"
- line_var="BC_LINE_LENGTH"
- lltest="line_length()"
-else
- halt="q"
- opt="l"
- lopt="mathlib"
- line_var="DC_LINE_LENGTH"
- num="$num pR"
- lltest="glpR"
-fi
-
-# I use these, so unset them to make the tests work.
-unset BC_ENV_ARGS
-unset BC_LINE_LENGTH
-unset DC_ENV_ARGS
-unset DC_LINE_LENGTH
-
-set +e
-
-printf '\nRunning %s quit test...' "$d"
-
-printf '%s\n' "$halt" 2> /dev/null | "$exe" "$@" > /dev/null 2>&1
-
-checktest_retcode "$d" "$?" "quit"
-
-# bc has two halt or quit commands, so test the second as well.
-if [ "$d" = bc ]; then
-
- printf '%s\n' "quit" 2> /dev/null | "$exe" "$@" > /dev/null 2>&1
-
- checktest_retcode "$d" "$?" quit
-
- two=$("$exe" "$@" -e 1+1 -e quit)
-
- checktest_retcode "$d" "$?" quit
-
- if [ "$two" != "2" ]; then
- err_exit "$d failed test quit" 1
- fi
-fi
-
-printf 'pass\n'
-
-base=$(basename "$exe")
-
-printf 'Running %s environment var tests...' "$d"
-
-if [ "$d" = "bc" ]; then
-
- export BC_ENV_ARGS=" '-l' '' -q"
-
- printf 's(.02893)\n' 2> /dev/null | "$exe" "$@" > /dev/null
-
- checktest_retcode "$d" "$?" "environment var"
-
- printf 'halt\n' 2> /dev/null | "$exe" "$@" -e 4 > /dev/null
-
- err="$?"
- checktest_retcode "$d" "$?" "environment var"
-
- printf 'pass\n'
-
- printf 'Running keyword redefinition test...'
-
- unset BC_ENV_ARGS
-
- redefine_res="$outputdir/bc_outputs/redefine.txt"
- redefine_out="$outputdir/bc_outputs/redefine_results.txt"
-
- outdir=$(dirname "$redefine_out")
-
- if [ ! -d "$outdir" ]; then
- mkdir -p "$outdir"
- fi
-
- printf '5\n0\n' > "$redefine_res"
-
- printf 'halt\n' 2> /dev/null | "$exe" "$@" --redefine=print -e 'define print(x) { x }' -e 'print(5)' > "$redefine_out"
- err="$?"
-
- checktest "$d" "$err" "keyword redefinition" "$redefine_res" "$redefine_out"
-
- printf 'halt\n' 2> /dev/null | "$exe" "$@" -r "abs" -r "else" -e 'abs = 5;else = 0' -e 'abs;else' > "$redefine_out"
- err="$?"
-
- checktest "$d" "$err" "keyword redefinition" "$redefine_res" "$redefine_out"
-
- if [ "$extra_math" -ne 0 ]; then
-
- printf 'halt\n' 2> /dev/null | "$exe" "$@" -lr abs -e "perm(5, 1)" -e "0" > "$redefine_out"
- err="$?"
-
- checktest "$d" "$err" "keyword not redefined in builtin library" "$redefine_res" "$redefine_out"
-
- fi
-
- "$exe" "$@" -r "break" -e 'define break(x) { x }' 2> "$redefine_out"
- err="$?"
-
- checkerrtest "$d" "$err" "keyword redefinition error" "$redefine_out" "$d"
-
- "$exe" "$@" -e 'define read(x) { x }' 2> "$redefine_out"
- err="$?"
-
- checkerrtest "$d" "$err" "Keyword redefinition error without BC_REDEFINE_KEYWORDS" "$redefine_out" "$d"
-
- printf 'pass\n'
- printf 'Running multiline comment expression file test...'
-
- multiline_expr_res=""
- multiline_expr_out="$outputdir/bc_outputs/multiline_expr_results.txt"
-
- # tests/bc/misc1.txt happens to have a multiline comment in it.
- printf 'halt\n' 2> /dev/null | "$exe" "$@" -f "$testdir/bc/misc1.txt" > "$multiline_expr_out"
- err="$?"
-
- checktest "$d" "$err" "multiline comment in expression file" "$testdir/bc/misc1_results.txt" \
- "$multiline_expr_out"
-
- printf 'pass\n'
- printf 'Running multiline comment expression file error test...'
-
- printf 'halt\n' 2> /dev/null | "$exe" "$@" -f "$testdir/bc/errors/05.txt" 2> "$multiline_expr_out"
- err="$?"
-
- checkerrtest "$d" "$err" "multiline comment in expression file error" \
- "$multiline_expr_out" "$d"
-
- printf 'pass\n'
- printf 'Running multiline string expression file test...'
-
- # tests/bc/strings.txt happens to have a multiline string in it.
- printf 'halt\n' 2> /dev/null | "$exe" "$@" -f "$testdir/bc/strings.txt" > "$multiline_expr_out"
- err="$?"
-
- checktest "$d" "$err" "multiline string in expression file" "$testdir/bc/strings_results.txt" \
- "$multiline_expr_out"
-
- printf 'pass\n'
- printf 'Running multiline string expression file error test...'
-
- printf 'halt\n' 2> /dev/null | "$exe" "$@" -f "$testdir/bc/errors/16.txt" 2> "$multiline_expr_out"
- err="$?"
-
- checkerrtest "$d" "$err" "multiline string in expression file with backslash error" \
- "$multiline_expr_out" "$d"
-
- printf 'halt\n' 2> /dev/null | "$exe" "$@" -f "$testdir/bc/errors/04.txt" 2> "$multiline_expr_out"
- err="$?"
-
- checkerrtest "$d" "$err" "multiline string in expression file error" \
- "$multiline_expr_out" "$d"
-
- printf 'pass\n'
-
-else
-
- export DC_ENV_ARGS="'-x'"
- export DC_EXPR_EXIT="1"
-
- printf '4s stuff\n' 2> /dev/null | "$exe" "$@" > /dev/null
-
- checktest_retcode "$d" "$?" "environment var"
-
- "$exe" "$@" -e 4pR > /dev/null
-
- checktest_retcode "$d" "$?" "environment var"
-
- printf 'pass\n'
-
- set +e
-
- # dc has an extra test for a case that someone found running this easter.dc
- # script. It went into an infinite loop, so we want to check that we did not
- # regress.
- printf 'three\n' 2> /dev/null | cut -c1-3 > /dev/null
- err=$?
-
- if [ "$err" -eq 0 ]; then
-
- printf 'Running dc Easter script...'
-
- easter_out="$outputdir/dc_outputs/easter.txt"
- easter_res="$outputdir/dc_outputs/easter_results.txt"
-
- outdir=$(dirname "$easter_out")
-
- if [ ! -d "$outdir" ]; then
- mkdir -p "$outdir"
- fi
-
- printf '4 April 2021\n' > "$easter_res"
-
- "$testdir/dc/scripts/easter.sh" "$exe" 2021 "$@" 2> /dev/null | cut -c1-12 > "$easter_out"
- err="$?"
-
- checktest "$d" "$err" "Easter script" "$easter_out" "$easter_res"
-
- printf 'pass\n'
- fi
-
- unset DC_ENV_ARGS
- unset DC_EXPR_EXIT
-
- printf 'Running dc extended register command tests...'
-
- ext_reg_out="$outputdir/dc_outputs/ext_reg.txt"
- ext_reg_res="$outputdir/dc_outputs/ext_reg_results.txt"
-
- outdir=$(dirname "$ext_reg_out")
-
- if [ ! -d "$outdir" ]; then
- mkdir -p "$outdir"
- fi
-
- printf '0\n' > "$ext_reg_res"
-
- printf '%s\n' "$halt" | "$exe" "$@" -e "gxpR" 2> /dev/null > "$ext_reg_out"
- err="$?"
-
- checktest "$d" "$err" "Extended register command" "$ext_reg_out" "$ext_reg_res"
-
- printf '1\n' > "$ext_reg_res"
-
- printf '%s\n' "$halt" | "$exe" "$@" -x -e "gxpR" 2> /dev/null > "$ext_reg_out"
- err="$?"
-
- checktest "$d" "$err" "Extended register command" "$ext_reg_out" "$ext_reg_res"
-
- printf 'pass\n'
-
-fi
-
-out1="$outputdir/${d}_outputs/${d}_other.txt"
-out2="$outputdir/${d}_outputs/${d}_other_test.txt"
-
-printf 'Running %s line length tests...' "$d"
-
-printf '%s\n' "$numres" > "$out1"
-
-export "$line_var"=80
-printf '%s\n' "$num" 2> /dev/null | "$exe" "$@" > "$out2"
-
-checktest "$d" "$?" "line length" "$out1" "$out2"
-
-printf '%s\n' "$num70" > "$out1"
-
-export "$line_var"=2147483647
-printf '%s\n' "$num" 2> /dev/null | "$exe" "$@" > "$out2"
-
-checktest "$d" "$?" "line length 2" "$out1" "$out2"
-
-printf '%s\n' "$num2" > "$out1"
-
-export "$line_var"=62
-printf '%s\n' "$num" 2> /dev/null | "$exe" "$@" -L > "$out2"
-
-checktest "$d" "$?" "line length 3" "$out1" "$out2"
-
-printf '0\n' > "$out1"
-printf '%s\n' "$lltest" 2> /dev/null | "$exe" "$@" -L > "$out2"
-
-checktest "$d" "$?" "line length 3" "$out1" "$out2"
-
-printf 'pass\n'
-
-printf '%s\n' "$numres" > "$out1"
-export "$line_var"=2147483647
-
-printf 'Running %s arg tests...' "$d"
-
-f="$testdir/$d/add.txt"
-exprs=$(cat "$f")
-results=$(cat "$testdir/$d/add_results.txt")
-
-printf '%s\n%s\n%s\n%s\n' "$results" "$results" "$results" "$results" > "$out1"
-
-"$exe" "$@" -e "$exprs" -f "$f" --expression "$exprs" --file "$f" -e "$halt" > "$out2"
-
-checktest "$d" "$?" "arg" "$out1" "$out2"
-
-printf '%s\n' "$halt" 2> /dev/null | "$exe" "$@" -- "$f" "$f" "$f" "$f" > "$out2"
-
-checktest "$d" "$?" "arg" "$out1" "$out2"
-
-if [ "$d" = "bc" ]; then
- printf '%s\n' "$halt" 2> /dev/null | "$exe" "$@" -i > /dev/null 2>&1
-fi
-
-printf '%s\n' "$halt" 2> /dev/null | "$exe" "$@" -h > /dev/null
-checktest_retcode "$d" "$?" "arg"
-printf '%s\n' "$halt" 2> /dev/null | "$exe" "$@" -P > /dev/null
-checktest_retcode "$d" "$?" "arg"
-printf '%s\n' "$halt" 2> /dev/null | "$exe" "$@" -R > /dev/null
-checktest_retcode "$d" "$?" "arg"
-printf '%s\n' "$halt" 2> /dev/null | "$exe" "$@" -v > /dev/null
-checktest_retcode "$d" "$?" "arg"
-printf '%s\n' "$halt" 2> /dev/null | "$exe" "$@" -V > /dev/null
-checktest_retcode "$d" "$?" "arg"
-
-out=$(printf '0.1\n-0.1\n1.1\n-1.1\n0.1\n-0.1\n')
-printf '%s\n' "$out" > "$out1"
-
-if [ "$d" = "bc" ]; then
- data=$(printf '0.1\n-0.1\n1.1\n-1.1\n.1\n-.1\n')
-else
- data=$(printf '0.1pR\n_0.1pR\n1.1pR\n_1.1pR\n.1pR\n_.1pR\n')
-fi
-
-printf '%s\n' "$data" 2> /dev/null | "$exe" "$@" -z > "$out2"
-checktest "$d" "$?" "leading zero" "$out1" "$out2"
-
-if [ "$d" = "bc" ] && [ "$extra_math" -ne 0 ]; then
-
- printf '%s\n' "$halt" 2> /dev/null | "$exe" "$@" -lz "$testdir/bc/leadingzero.txt" > "$out2"
-
- checktest "$d" "$?" "leading zero script" "$testdir/bc/leadingzero_results.txt" "$out2"
-
-fi
-
-"$exe" "$@" -f "saotehasotnehasthistohntnsahxstnhalcrgxgrlpyasxtsaosysxsatnhoy.txt" > /dev/null 2> "$out2"
-err="$?"
-
-checkerrtest "$d" "$err" "invalid file argument" "$out2" "$d"
-
-"$exe" "$@" "-$opt" -e "$exprs" > /dev/null 2> "$out2"
-err="$?"
-
-checkerrtest "$d" "$err" "invalid option argument" "$out2" "$d"
-
-"$exe" "$@" "--$lopt" -e "$exprs" > /dev/null 2> "$out2"
-err="$?"
-
-checkerrtest "$d" "$err" "invalid long option argument" "$out2" "$d"
-
-"$exe" "$@" "-u" -e "$exprs" > /dev/null 2> "$out2"
-err="$?"
-
-checkerrtest "$d" "$err" "unrecognized option argument" "$out2" "$d"
-
-"$exe" "$@" "--uniform" -e "$exprs" > /dev/null 2> "$out2"
-err="$?"
-
-checkerrtest "$d" "$err" "unrecognized long option argument" "$out2" "$d"
-
-"$exe" "$@" -f > /dev/null 2> "$out2"
-err="$?"
-
-checkerrtest "$d" "$err" "missing required argument to short option" "$out2" "$d"
-
-"$exe" "$@" --file > /dev/null 2> "$out2"
-err="$?"
-
-checkerrtest "$d" "$err" "missing required argument to long option" "$out2" "$d"
-
-"$exe" "$@" --version=5 > /dev/null 2> "$out2"
-err="$?"
-
-checkerrtest "$d" "$err" "given argument to long option with no argument" "$out2" "$d"
-
-"$exe" "$@" -: > /dev/null 2> "$out2"
-err="$?"
-
-checkerrtest "$d" "$err" "colon short option" "$out2" "$d"
-
-"$exe" "$@" --: > /dev/null 2> "$out2"
-err="$?"
-
-checkerrtest "$d" "$err" "colon long option" "$out2" "$d"
-
-printf 'pass\n'
-
-printf 'Running %s builtin variable arg tests...' "$d"
-
-if [ "$extra_math" -ne 0 ]; then
-
- out=$(printf '14\n15\n16\n17.25\n')
- printf '%s\n' "$out" > "$out1"
-
- if [ "$d" = "bc" ]; then
- data=$(printf 's=scale;i=ibase;o=obase;t=seed@2;ibase=A;obase=A;s;i;o;t;')
- else
- data=$(printf 'J2@OIKAiAopRpRpRpR')
- fi
-
- printf '%s\n' "$data" 2> /dev/null | "$exe" "$@" -S14 -I15 -O16 -E17.25 > "$out2"
- checktest "$d" "$?" "builtin variable args" "$out1" "$out2"
-
- printf '%s\n' "$data" 2> /dev/null | "$exe" "$@" --scale=14 --ibase=15 --obase=16 --seed=17.25 > "$out2"
- checktest "$d" "$?" "builtin variable long args" "$out1" "$out2"
-
-else
-
- out=$(printf '14\n15\n16\n')
- printf '%s\n' "$out" > "$out1"
-
- if [ "$d" = "bc" ]; then
- data=$(printf 's=scale;i=ibase;o=obase;ibase=A;obase=A;s;i;o;')
- else
- data=$(printf 'OIKAiAopRpRpR')
- fi
-
- printf '%s\n' "$data" 2> /dev/null | "$exe" "$@" -S14 -I15 -O16 > "$out2"
- checktest "$d" "$?" "builtin variable args" "$out1" "$out2"
-
- printf '%s\n' "$data" 2> /dev/null | "$exe" "$@" --scale=14 --ibase=15 --obase=16 > "$out2"
- checktest "$d" "$?" "builtin variable long args" "$out1" "$out2"
-
-fi
-
-if [ "$d" = "bc" ]; then
-
- out=$(printf '100\n')
- printf '%s\n' "$out" > "$out1"
-
- printf 'scale\n' 2> /dev/null | "$exe" "$@" -S100 -l > "$out2"
- checktest "$d" "$?" "builtin variable args with math lib" "$out1" "$out2"
-
- printf 'scale\n' 2> /dev/null | "$exe" "$@" --scale=100 --mathlib > "$out2"
- checktest "$d" "$?" "builtin variable long args with math lib" "$out1" "$out2"
-
- export BC_ENV_ARGS="-l"
-
- printf 'scale\n' 2> /dev/null | "$exe" "$@" -S100 > "$out2"
- checktest "$d" "$?" "builtin variable args with math lib env arg" "$out1" "$out2"
-
- printf 'scale\n' 2> /dev/null | "$exe" "$@" --scale=100 > "$out2"
- checktest "$d" "$?" "builtin variable long args with math lib env arg" "$out1" "$out2"
-
- export BC_ENV_ARGS="-S100"
-
- printf 'scale\n' 2> /dev/null | "$exe" "$@" -l > "$out2"
- checktest "$d" "$?" "builtin variable args with math lib arg" "$out1" "$out2"
-
- export BC_ENV_ARGS="--scale=100"
-
- printf 'scale\n' 2> /dev/null | "$exe" "$@" -l > "$out2"
- checktest "$d" "$?" "builtin variable long args with math lib arg" "$out1" "$out2"
-
-fi
-
-printf 'scale\n' 2> /dev/null | "$exe" "$@" --scale=18923c.rlg > /dev/null 2> "$out2"
-err="$?"
-
-checkerrtest "$d" "$err" "invalid command-line arg for builtin variable" "$out2" "$d"
-
-if [ "$extra_math" -ne 0 ]; then
-
- printf 'seed\n' 2> /dev/null | "$exe" "$@" --seed=18923c.rlg > /dev/null 2> "$out2"
- err="$?"
-
- checkerrtest "$d" "$err" "invalid command-line arg for seed" "$out2" "$d"
-
-fi
-
-printf 'pass\n'
-
-printf 'Running %s directory test...' "$d"
-
-"$exe" "$@" "$testdir" > /dev/null 2> "$out2"
-err="$?"
-
-checkerrtest "$d" "$err" "directory" "$out2" "$d"
-
-printf 'pass\n'
-
-printf 'Running %s binary file test...' "$d"
-
-bin="/bin/sh"
-
-"$exe" "$@" "$bin" > /dev/null 2> "$out2"
-err="$?"
-
-checkerrtest "$d" "$err" "binary file" "$out2" "$d"
-
-printf 'pass\n'
-
-printf 'Running %s binary stdin test...' "$d"
-
-cat "$bin" 2> /dev/null | "$exe" "$@" > /dev/null 2> "$out2"
-err="$?"
-
-checkerrtest "$d" "$err" "binary stdin" "$out2" "$d"
-
-printf 'pass\n'
-
-if [ "$d" = "bc" ]; then
-
- printf 'Running %s limits tests...' "$d"
- printf 'limits\n' 2> /dev/null | "$exe" "$@" /dev/null > "$out2" 2>&1
-
- checktest_retcode "$d" "$?" "limits"
-
- if [ ! -s "$out2" ]; then
- err_exit "$d did not produce output on the limits test" 1
- fi
-
- exec printf 'pass\n'
-
-fi
diff --git a/contrib/bc/tests/read.sh b/contrib/bc/tests/read.sh
deleted file mode 100755
index fd4b9b6721a5..000000000000
--- a/contrib/bc/tests/read.sh
+++ /dev/null
@@ -1,166 +0,0 @@
-#! /bin/sh
-#
-# SPDX-License-Identifier: BSD-2-Clause
-#
-# Copyright (c) 2018-2024 Gavin D. Howard and contributors.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are met:
-#
-# * Redistributions of source code must retain the above copyright notice, this
-# list of conditions and the following disclaimer.
-#
-# * Redistributions in binary form must reproduce the above copyright notice,
-# this list of conditions and the following disclaimer in the documentation
-# and/or other materials provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
-# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-# POSSIBILITY OF SUCH DAMAGE.
-#
-
-set -e
-
-script="$0"
-testdir=$(dirname "$script")
-
-. "$testdir/../scripts/functions.sh"
-
-outputdir=${BC_TEST_OUTPUT_DIR:-$testdir}
-
-# Just print the usage and exit with an error. This can receive a message to
-# print.
-# @param 1 A message to print.
-usage() {
- if [ $# -eq 1 ]; then
- printf '%s\n\n' "$1"
- fi
- printf 'usage: %s dir [exe [args...]]\n' "$script"
- printf 'valid dirs are:\n'
- printf '\n'
- cat "$testdir/all.txt"
- printf '\n'
- exit 1
-}
-
-# Command-line processing.
-if [ "$#" -lt 1 ]; then
- usage "Not enough arguments"
-fi
-
-d="$1"
-shift
-check_d_arg "$d"
-
-if [ "$#" -gt 0 ]; then
- exe="$1"
- shift
- check_exec_arg "$exe"
-else
- exe="$testdir/../bin/$d"
- check_exec_arg "$exe"
-fi
-
-name="$testdir/$d/read.txt"
-results="$testdir/$d/read_results.txt"
-errors="$testdir/$d/read_errors.txt"
-
-out="$outputdir/${d}_outputs/read_results.txt"
-multiple_res="$outputdir/${d}_outputs/read_multiple_results.txt"
-outdir=$(dirname "$out")
-
-# Make sure the directory exists.
-if [ ! -d "$outdir" ]; then
- mkdir -p "$outdir"
-fi
-
-exebase=$(basename "$exe")
-
-# Set stuff for the correct calculator.
-if [ "$d" = "bc" ]; then
- options="-lq"
- halt="halt"
- read_call="read()"
- read_expr="${read_call}\n5+5;"
- read_multiple=$(printf '%s\n%s\n%s\n' "3" "2" "1")
-else
- options="-x"
- halt="q"
- read_call="?"
- read_expr="${read_call}"
- read_multiple=$(printf '%spR\n%spR\n%spR\n' "3" "2" "1")
-fi
-
-# I use these, so unset them to make the tests work.
-unset BC_ENV_ARGS
-unset BC_LINE_LENGTH
-unset DC_ENV_ARGS
-unset DC_LINE_LENGTH
-
-printf 'Running %s read...' "$d"
-
-set +e
-
-# Run read() on every line.
-while read line; do
-
- printf '%s\n%s\n' "$read_call" "$line" | "$exe" "$@" "$options" > "$out"
- checktest "$d" "$?" 'read' "$results" "$out"
-
-done < "$name"
-
-printf 'pass\n'
-
-printf 'Running %s read multiple...' "$d"
-
-printf '3\n2\n1\n' > "$multiple_res"
-
-# Run multiple read() calls.
-printf '%s\n' "$read_multiple" | "$exe" "$@" "$options" -e "$read_call" -e "$read_call" -e "$read_call" > "$out"
-checktest "$d" "$?" 'read multiple' "$multiple_res" "$out"
-
-printf 'pass\n'
-
-printf 'Running %s read errors...' "$d"
-
-# Run read on every line.
-while read line; do
-
- printf '%s\n%s\n' "$read_call" "$line" | "$exe" "$@" "$options" 2> "$out" > /dev/null
- err="$?"
-
- checkerrtest "$d" "$err" "$line" "$out" "$exebase"
-
-done < "$errors"
-
-printf 'pass\n'
-
-printf 'Running %s empty read...' "$d"
-
-read_test=$(printf '%s\n' "$read_call")
-
-printf '%s\n' "$read_test" | "$exe" "$@" "$opts" 2> "$out" > /dev/null
-err="$?"
-
-checkerrtest "$d" "$err" "$read_test" "$out" "$exebase"
-
-printf 'pass\n'
-
-printf 'Running %s read EOF...' "$d"
-
-read_test=$(printf '%s' "$read_call")
-
-printf '%s' "$read_test" | "$exe" "$@" "$opts" 2> "$out" > /dev/null
-err="$?"
-
-checkerrtest "$d" "$err" "$read_test" "$out" "$exebase"
-
-exec printf 'pass\n'
diff --git a/contrib/bc/tests/script.sh b/contrib/bc/tests/script.sh
index b1346ef09904..ce5cf19ee275 100755
--- a/contrib/bc/tests/script.sh
+++ b/contrib/bc/tests/script.sh
@@ -2,7 +2,7 @@
#
# SPDX-License-Identifier: BSD-2-Clause
#
-# Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+# Copyright (c) 2018-2025 Gavin D. Howard and contributors.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
@@ -44,7 +44,7 @@ usage() {
if [ $# -eq 1 ]; then
printf '%s\n\n' "$1"
fi
- printf 'usage: %s dir script [run_extra_tests] [run_stack_tests] [generate_tests] [time_tests] [exec args...]\n' "$script"
+ printf 'usage: %s dir script [run_extra_tests] [run_stack_tests] [generate_tests] [exec args...]\n' "$script"
exit 1
}
@@ -91,15 +91,6 @@ else
fi
if [ "$#" -gt 0 ]; then
- time_tests="$1"
- shift
- check_bool_arg "$time_tests"
-else
- time_tests=0
- check_bool_arg "$generate"
-fi
-
-if [ "$#" -gt 0 ]; then
exe="$1"
shift
check_exec_arg "$exe"
@@ -209,16 +200,8 @@ set +e
printf 'Running %s script %s...' "$d" "$f"
-# Yes this is poor timing, but it works.
-if [ "$time_tests" -ne 0 ]; then
- printf '\n'
- printf '%s\n' "$halt" 2> /dev/null | /usr/bin/time -p "$exe" "$@" $options "$s" > "$out"
- err="$?"
- printf '\n'
-else
- printf '%s\n' "$halt" 2> /dev/null | "$exe" "$@" $options "$s" > "$out"
- err="$?"
-fi
+printf '%s\n' "$halt" 2> /dev/null | "$exe" "$@" $options "$s" > "$out"
+err="$?"
checktest "$d" "$err" "script $f" "$res" "$out"
diff --git a/contrib/bc/tests/scripts.sh b/contrib/bc/tests/scripts.sh
index 2c8af6c06df0..eb2963e4a9ed 100755
--- a/contrib/bc/tests/scripts.sh
+++ b/contrib/bc/tests/scripts.sh
@@ -2,7 +2,7 @@
#
# SPDX-License-Identifier: BSD-2-Clause
#
-# Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+# Copyright (c) 2018-2025 Gavin D. Howard and contributors.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
@@ -40,7 +40,7 @@ usage() {
if [ $# -eq 1 ]; then
printf '%s\n\n' "$1"
fi
- printf 'usage: %s [-n] dir [run_extra_tests] [run_stack_tests] [generate_tests] [time_tests] [exec args...]\n' "$script"
+ printf 'usage: %s [-n] dir [run_extra_tests] [run_stack_tests] [generate_tests] [exec args...]\n' "$script"
exit 1
}
@@ -96,15 +96,6 @@ else
fi
if [ "$#" -gt 0 ]; then
- time_tests="$1"
- shift
- check_bool_arg "$time_tests"
-else
- time_tests=0
- check_bool_arg "$time_tests"
-fi
-
-if [ "$#" -gt 0 ]; then
exe="$1"
shift
check_exec_arg "$exe"
@@ -124,11 +115,11 @@ for s in $scripts; do
if [ "$pll" -ne 0 ]; then
sh "$testdir/script.sh" "$d" "$f" "$run_extra_tests" "$run_stack_tests" \
- "$generate" "$time_tests" "$exe" "$@" &
+ "$generate" "$exe" "$@" &
pids="$pids $!"
else
sh "$testdir/script.sh" "$d" "$f" "$run_extra_tests" "$run_stack_tests" \
- "$generate" "$time_tests" "$exe" "$@"
+ "$generate" "$exe" "$@"
fi
done
diff --git a/contrib/bc/tests/stdin.sh b/contrib/bc/tests/stdin.sh
index 7061e950367e..ba56606b18c4 100755
--- a/contrib/bc/tests/stdin.sh
+++ b/contrib/bc/tests/stdin.sh
@@ -2,7 +2,7 @@
#
# SPDX-License-Identifier: BSD-2-Clause
#
-# Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+# Copyright (c) 2018-2025 Gavin D. Howard and contributors.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
diff --git a/contrib/bc/tests/test.sh b/contrib/bc/tests/test.sh
index 7b5916f02990..3dd3e19963fa 100755
--- a/contrib/bc/tests/test.sh
+++ b/contrib/bc/tests/test.sh
@@ -2,7 +2,7 @@
#
# SPDX-License-Identifier: BSD-2-Clause
#
-# Copyright (c) 2018-2024 Gavin D. Howard and contributors.
+# Copyright (c) 2018-2025 Gavin D. Howard and contributors.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
@@ -44,7 +44,7 @@ usage() {
if [ $# -eq 1 ]; then
printf '%s\n\n' "$1"
fi
- printf 'usage: %s dir test [generate_tests] [time_tests] [exe [args...]]\n' "$0"
+ printf 'usage: %s dir test [generate_tests] [exe [args...]]\n' "$0"
printf 'valid dirs are:\n'
printf '\n'
cat "$testdir/all.txt"
@@ -78,15 +78,6 @@ else
fi
if [ "$#" -gt 0 ]; then
- time_tests="$1"
- shift
- check_bool_arg "$time_tests"
-else
- time_tests=0
- check_bool_arg "$time_tests"
-fi
-
-if [ "$#" -gt 0 ]; then
exe="$1"
shift
check_exec_arg "$exe"
@@ -155,15 +146,8 @@ set +e
printf 'Running %s %s...' "$d" "$t"
-if [ "$time_tests" -ne 0 ]; then
- printf '\n'
- printf '%s\n' "$halt" 2> /dev/null | /usr/bin/time -p "$exe" "$@" $options "$name" > "$out"
- err="$?"
- printf '\n'
-else
- printf '%s\n' "$halt" 2> /dev/null | "$exe" "$@" $options "$name" > "$out"
- err="$?"
-fi
+printf '%s\n' "$halt" 2> /dev/null | "$exe" "$@" $options "$name" > "$out"
+err="$?"
checktest "$d" "$err" "$t" "$results" "$out"
diff --git a/contrib/bc/vs/bc.vcxproj b/contrib/bc/vs/bc.vcxproj
index c98ebc6eee53..baffabbb2b4f 100644
--- a/contrib/bc/vs/bc.vcxproj
+++ b/contrib/bc/vs/bc.vcxproj
@@ -204,7 +204,6 @@
<ClInclude Include="..\include\read.h" />
<ClInclude Include="..\include\status.h" />
<ClInclude Include="..\include\vector.h" />
- <ClInclude Include="..\include\version.h" />
<ClInclude Include="..\include\vm.h" />
</ItemGroup>
<ItemGroup>
diff --git a/contrib/bc/vs/bc.vcxproj.filters b/contrib/bc/vs/bc.vcxproj.filters
index f26387253f27..caa30d9c7fa4 100644
--- a/contrib/bc/vs/bc.vcxproj.filters
+++ b/contrib/bc/vs/bc.vcxproj.filters
@@ -66,9 +66,6 @@
<ClInclude Include="..\include\vector.h">
<Filter>include</Filter>
</ClInclude>
- <ClInclude Include="..\include\version.h">
- <Filter>include</Filter>
- </ClInclude>
<ClInclude Include="..\include\vm.h">
<Filter>include</Filter>
</ClInclude>
diff --git a/contrib/bc/vs/bcl.vcxproj b/contrib/bc/vs/bcl.vcxproj
index f838cac7cbd1..96bd3ba2a552 100644
--- a/contrib/bc/vs/bcl.vcxproj
+++ b/contrib/bc/vs/bcl.vcxproj
@@ -256,7 +256,6 @@
<ClInclude Include="..\include\read.h" />
<ClInclude Include="..\include\status.h" />
<ClInclude Include="..\include\vector.h" />
- <ClInclude Include="..\include\version.h" />
<ClInclude Include="..\include\vm.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
diff --git a/contrib/bc/vs/bcl.vcxproj.filters b/contrib/bc/vs/bcl.vcxproj.filters
index b62d1899e2bf..ae3d24125dd6 100644
--- a/contrib/bc/vs/bcl.vcxproj.filters
+++ b/contrib/bc/vs/bcl.vcxproj.filters
@@ -80,9 +80,6 @@
<ClInclude Include="..\include\vector.h">
<Filter>include</Filter>
</ClInclude>
- <ClInclude Include="..\include\version.h">
- <Filter>include</Filter>
- </ClInclude>
<ClInclude Include="..\include\vm.h">
<Filter>include</Filter>
</ClInclude>
diff --git a/contrib/expat/Changes b/contrib/expat/Changes
index 9d6c64b6a460..01e54b676416 100644
--- a/contrib/expat/Changes
+++ b/contrib/expat/Changes
@@ -15,12 +15,16 @@
!! ClusterFuzz findings with few-days-max response times in communication !!
!! in order to (1) have a sound fix ready before the end of a 90 days !!
!! grace period and (2) in a sustainable manner, !!
-!! - helping CPython Expat bindings with supporting Expat's billion laughs !!
+!! - helping CPython Expat bindings with supporting Expat's amplification !!
!! attack protection API (https://github.com/python/cpython/issues/90949): !!
+!! - XML_SetAllocTrackerActivationThreshold !!
+!! - XML_SetAllocTrackerMaximumAmplification !!
!! - XML_SetBillionLaughsAttackProtectionActivationThreshold !!
!! - XML_SetBillionLaughsAttackProtectionMaximumAmplification !!
!! - helping Perl's XML::Parser Expat bindings with supporting Expat's !!
!! security API (https://github.com/cpan-authors/XML-Parser/issues/102): !!
+!! - XML_SetAllocTrackerActivationThreshold !!
+!! - XML_SetAllocTrackerMaximumAmplification !!
!! - XML_SetBillionLaughsAttackProtectionActivationThreshold !!
!! - XML_SetBillionLaughsAttackProtectionMaximumAmplification !!
!! - XML_SetReparseDeferralEnabled !!
@@ -37,6 +41,133 @@
!! THANK YOU! Sebastian Pipping -- Berlin, 2024-03-09 !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+Release 2.7.3 Wed September 24 2025
+ Security fixes:
+ #1046 #1048 Fix alignment of internal allocations for some non-amd64
+ architectures (e.g. sparc32); fixes up on the fix to
+ CVE-2025-59375 from #1034 (of Expat 2.7.2 and related
+ backports)
+ #1059 Fix a class of false positives where input should have been
+ rejected with error XML_ERROR_ASYNC_ENTITY; regression from
+ CVE-2024-8176 fix pull request #973 (of Expat 2.7.0 and
+ related backports). Please check the added unit tests for
+ example documents.
+
+ Other changes:
+ #1043 Prove and regression-proof absence of integer overflow
+ from function expat_realloc
+ #1062 Remove "harmless" cast that truncated a size_t to unsigned
+ #1049 Autotools: Remove "ln -s" discovery
+ #1054 docs: Be consistent with use of floating point around
+ XML_SetAllocTrackerMaximumAmplification
+ #1056 docs: Make it explicit that XML_GetCurrentColumnNumber
+ starts at 0
+ #1057 docs: Better integrate the effect of the activation
+ thresholds
+ #1058 docs: Fix an in-comment typo in expat.h
+ #1045 docs: Fix a typo in README.md
+ #1041 docs: Improve change log of release 2.7.2
+ #1053 xmlwf: Resolve use of functions XML_GetErrorLineNumber
+ and XML_GetErrorColumnNumber
+ #1032 Windows: Normalize .bat files to CRLF line endings
+ #1060 #1061 Version info bumped from 12:0:11 (libexpat*.so.1.11.0)
+ to 12:1:11 (libexpat*.so.1.11.1); see https://verbump.de/
+ for what these numbers do
+
+ Infrastructure:
+ #1047 #1050 CI: Cleanup UndefinedBehaviorSanitizer fatality
+ #1044 CI|Linux: Stop aborting at first job failure
+ #1052 CI|FreeBSD: Upgrade to FreeBSD 15.0
+ #1039 CI|FreeBSD: Do not install CMake meta-package
+
+ Special thanks to:
+ Bénédikt Tran
+ Berkay Eren Ürün
+ Daniel Engberg
+ Hanno Böck
+ Matthew Fernandez
+ Rolf Eike Beer
+ Sam James
+ Tim Bray
+ and
+ Clang/GCC UndefinedBehaviorSanitizer
+ OSS-Fuzz / ClusterFuzz
+ Z3 Theorem Prover
+
+Release 2.7.2 Tue September 16 2025
+ Security fixes:
+ #1018 #1034 CVE-2025-59375 -- Disallow use of disproportional amounts of
+ dynamic memory from within an Expat parser (e.g. previously
+ a ~250 KiB sized document was able to cause allocation of
+ ~800 MiB from the heap, i.e. an "amplification" of factor
+ ~3,300); once a threshold (that defaults to 64 MiB) is
+ reached, a maximum amplification factor (that defaults to
+ 100.0) is enforced, and violating documents are rejected
+ with an out-of-memory error.
+ There are two new API functions to fine-tune this new
+ behavior:
+ - XML_SetAllocTrackerActivationThreshold
+ - XML_SetAllocTrackerMaximumAmplification .
+ If you ever need to increase these defaults for non-attack
+ XML payload, please file a bug report with libexpat.
+ There is also a new environment variable
+ EXPAT_MALLOC_DEBUG=(0|1|2) to control the verbosity
+ of allocations debugging at runtime, disabled by default.
+ Known impact is (reliable and easy) denial of service:
+ CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H/E:H/RL:O/RC:C
+ (Base Score: 7.5, Temporal Score: 7.2)
+ Please note that a layer of compression around XML can
+ significantly reduce the minimum attack payload size.
+ Distributors intending to backport (or cherry-pick) the
+ fix need to copy 99% of the related pull request, not just
+ the "lib: Implement tracking of dynamic memory allocations"
+ commit, to not end up with a state that literally does both
+ too much and too little at the same time. Appending ".diff"
+ to the pull request URL could be of help.
+
+ Other changes:
+ #1008 #1017 Autotools|macOS: Sync CMake templates with CMake 3.31
+ #1007 CMake: Drop support for CMake <3.15
+ #1004 CMake: Fix off_t detection for -Werror
+ #1007 CMake|Windows: Fix -DEXPAT_MSVC_STATIC_CRT=ON
+ #1013 Windows: Drop support for Visual Studio <=16.0/2019
+ #1026 xmlwf: Mention supported environment variables in
+ --help output
+ #1024 xmlwf: Fix (internal) help generator
+ #1034 docs: Promote the contract to call function
+ XML_FreeContentModel when registering a custom
+ element declaration handler (via a call to function
+ XML_SetElementDeclHandler)
+ #1027 docs: Add missing <p>..</p> wrap
+ #994 docs: Drop AppVeyor badge
+ #1000 tests: Fix portable_strndup
+ #1036 Drop casts around malloc/free/realloc that C99 does not need
+ #1010 Replace empty for loops with while loops
+ #1011 Add const with internal XmlInitUnknownEncodingNS
+ #14 #1037 Drop an OpenVMS support leftover
+ #999 #1001 Address more clang-tidy warnings
+ #1030 #1038 Version info bumped from 11:2:10 (libexpat*.so.1.10.2)
+ to 12:0:11 (libexpat*.so.1.11.0); see https://verbump.de/
+ for what these numbers do
+
+ Infrastructure:
+ #1003 CI: Cover compilation on FreeBSD
+ #1009 #1035 CI: Upgrade Clang from 19 to 21
+ #1031 CI: Make calling Cppcheck without --suppress=objectIndex
+ and --suppress=unknownMacro possible
+ #1013 CI|Windows: Get off of deprecated image "windows-2019"
+ #1008 #1017 ..
+ #1023 #1025 CI: Adapt to breaking changes in GitHub Actions
+
+ Special thanks to:
+ Alexander Bluhm
+ Neil Pang
+ Theo Buehler
+ and
+ GNU Time
+ OSS-Fuzz / ClusterFuzz
+ Perl XML::Parser
+
Release 2.7.1 Thu March 27 2025
Bug fixes:
#980 #989 Restore event pointer behavior from Expat 2.6.4
@@ -54,7 +185,7 @@ Release 2.7.1 Thu March 27 2025
#983 #984 Fix printf format specifiers for 32bit Emscripten
#992 docs: Promote OpenSSF Best Practices self-certification
#978 tests/benchmark: Resolve mistaken double close
- #986 Address compiler warnings
+ #986 Address Frama-C warnings
#990 #993 Version info bumped from 11:1:10 (libexpat*.so.1.10.1)
to 11:2:10 (libexpat*.so.1.10.2); see https://verbump.de/
for what these numbers do
diff --git a/contrib/expat/Makefile.am b/contrib/expat/Makefile.am
index c20531a8d6c6..d612d432becb 100644
--- a/contrib/expat/Makefile.am
+++ b/contrib/expat/Makefile.am
@@ -58,10 +58,8 @@ pkgconfig_DATA = expat.pc
pkgconfigdir = $(libdir)/pkgconfig
-dist_cmake_DATA = \
- cmake/autotools/expat.cmake
-
nodist_cmake_DATA = \
+ cmake/autotools/expat.cmake \
cmake/autotools/expat-config-version.cmake \
cmake/autotools/expat-noconfig.cmake \
cmake/expat-config.cmake
@@ -70,6 +68,9 @@ cmakedir = $(libdir)/cmake/expat-@PACKAGE_VERSION@
_EXTRA_DIST_CMAKE = \
+ cmake/autotools/expat__linux.cmake.in \
+ cmake/autotools/expat__macos.cmake.in \
+ cmake/autotools/expat__windows.cmake.in \
cmake/autotools/expat-noconfig__linux.cmake.in \
cmake/autotools/expat-noconfig__macos.cmake.in \
cmake/autotools/expat-noconfig__windows.cmake.in \
diff --git a/contrib/expat/Makefile.in b/contrib/expat/Makefile.in
index 069ec4047eea..b799591f2fc2 100644
--- a/contrib/expat/Makefile.in
+++ b/contrib/expat/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.5 from Makefile.am.
+# Makefile.in generated by automake 1.18.1 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2021 Free Software Foundation, Inc.
+# Copyright (C) 1994-2025 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -105,6 +105,8 @@ am__make_running_with_option = \
test $$has_opt = yes
am__make_dryrun = (target_option=n; $(am__make_running_with_option))
am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+am__rm_f = rm -f $(am__rm_f_notfound)
+am__rm_rf = rm -rf $(am__rm_f_notfound)
pkgdatadir = $(datadir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
@@ -145,12 +147,13 @@ am__aclocal_m4_deps = $(top_srcdir)/m4/libtool.m4 \
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
DIST_COMMON = $(srcdir)/Makefile.am $(top_srcdir)/configure \
- $(am__configure_deps) $(dist_cmake_DATA) $(am__DIST_COMMON)
+ $(am__configure_deps) $(am__DIST_COMMON)
am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \
configure.lineno config.status.lineno
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = expat_config.h
CONFIG_CLEAN_FILES = expat.pc cmake/expat-config.cmake \
+ cmake/autotools/expat.cmake \
cmake/autotools/expat-config-version.cmake \
cmake/autotools/expat-noconfig.cmake run.sh
CONFIG_CLEAN_VPATH_FILES =
@@ -203,14 +206,12 @@ am__base_list = \
sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
am__uninstall_files_from_dir = { \
- test -z "$$files" \
- || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
- || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
- $(am__cd) "$$dir" && rm -f $$files; }; \
+ { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && echo $$files | $(am__xargs_n) 40 $(am__rm_f); }; \
}
-am__installdirs = "$(DESTDIR)$(cmakedir)" "$(DESTDIR)$(cmakedir)" \
- "$(DESTDIR)$(pkgconfigdir)"
-DATA = $(dist_cmake_DATA) $(nodist_cmake_DATA) $(pkgconfig_DATA)
+am__installdirs = "$(DESTDIR)$(cmakedir)" "$(DESTDIR)$(pkgconfigdir)"
+DATA = $(nodist_cmake_DATA) $(pkgconfig_DATA)
RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
distclean-recursive maintainer-clean-recursive
am__recursive_targets = \
@@ -256,8 +257,8 @@ distdir = $(PACKAGE)-$(VERSION)
top_distdir = $(distdir)
am__remove_distdir = \
if test -d "$(distdir)"; then \
- find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \
- && rm -rf "$(distdir)" \
+ find "$(distdir)" -type d ! -perm -700 -exec chmod u+rwx {} ';' \
+ ; rm -rf "$(distdir)" \
|| { sleep 5 && rm -rf "$(distdir)"; }; \
else :; fi
am__post_remove_distdir = $(am__remove_distdir)
@@ -288,14 +289,16 @@ am__relativize = \
reldir="$$dir2"
DIST_ARCHIVES = $(distdir).tar.gz $(distdir).tar.bz2 $(distdir).tar.lz \
$(distdir).tar.xz
-GZIP_ENV = --best
+GZIP_ENV = -9
DIST_TARGETS = dist-lzip dist-xz dist-bzip2 dist-gzip
# Exists only to be overridden by the user if desired.
AM_DISTCHECK_DVI_TARGET = dvi
distuninstallcheck_listfiles = find . -type f -print
am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \
| sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$'
-distcleancheck_listfiles = find . -type f -print
+distcleancheck_listfiles = \
+ find . \( -type f -a \! \
+ \( -name .nfs* -o -name .smb* -o -name .__afs* \) \) -print
ACLOCAL = @ACLOCAL@
AMTAR = @AMTAR@
AM_CFLAGS = @AM_CFLAGS@
@@ -403,8 +406,10 @@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
am__include = @am__include@
am__leading_dot = @am__leading_dot@
am__quote = @am__quote@
+am__rm_f_notfound = @am__rm_f_notfound@
am__tar = @am__tar@
am__untar = @am__untar@
+am__xargs_n = @am__xargs_n@
bindir = @bindir@
build = @build@
build_alias = @build_alias@
@@ -458,16 +463,17 @@ LIBTOOLFLAGS = --verbose
SUBDIRS = lib $(am__append_1) $(am__append_2) $(am__append_3)
pkgconfig_DATA = expat.pc
pkgconfigdir = $(libdir)/pkgconfig
-dist_cmake_DATA = \
- cmake/autotools/expat.cmake
-
nodist_cmake_DATA = \
+ cmake/autotools/expat.cmake \
cmake/autotools/expat-config-version.cmake \
cmake/autotools/expat-noconfig.cmake \
cmake/expat-config.cmake
cmakedir = $(libdir)/cmake/expat-@PACKAGE_VERSION@
_EXTRA_DIST_CMAKE = \
+ cmake/autotools/expat__linux.cmake.in \
+ cmake/autotools/expat__macos.cmake.in \
+ cmake/autotools/expat__windows.cmake.in \
cmake/autotools/expat-noconfig__linux.cmake.in \
cmake/autotools/expat-noconfig__macos.cmake.in \
cmake/autotools/expat-noconfig__windows.cmake.in \
@@ -552,12 +558,12 @@ expat_config.h: stamp-h1
@test -f $@ || $(MAKE) $(AM_MAKEFLAGS) stamp-h1
stamp-h1: $(srcdir)/expat_config.h.in $(top_builddir)/config.status
- @rm -f stamp-h1
- cd $(top_builddir) && $(SHELL) ./config.status expat_config.h
+ $(AM_V_at)rm -f stamp-h1
+ $(AM_V_GEN)cd $(top_builddir) && $(SHELL) ./config.status expat_config.h
$(srcdir)/expat_config.h.in: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
- ($(am__cd) $(top_srcdir) && $(AUTOHEADER))
- rm -f stamp-h1
- touch $@
+ $(AM_V_GEN)($(am__cd) $(top_srcdir) && $(AUTOHEADER))
+ $(AM_V_at)rm -f stamp-h1
+ $(AM_V_at)touch $@
distclean-hdr:
-rm -f expat_config.h stamp-h1
@@ -565,6 +571,8 @@ expat.pc: $(top_builddir)/config.status $(srcdir)/expat.pc.in
cd $(top_builddir) && $(SHELL) ./config.status $@
cmake/expat-config.cmake: $(top_builddir)/config.status $(top_srcdir)/cmake/expat-config.cmake.in
cd $(top_builddir) && $(SHELL) ./config.status $@
+cmake/autotools/expat.cmake: $(top_builddir)/config.status
+ cd $(top_builddir) && $(SHELL) ./config.status $@
cmake/autotools/expat-config-version.cmake: $(top_builddir)/config.status $(top_srcdir)/cmake/autotools/expat-config-version.cmake.in
cd $(top_builddir) && $(SHELL) ./config.status $@
cmake/autotools/expat-noconfig.cmake: $(top_builddir)/config.status
@@ -580,27 +588,6 @@ clean-libtool:
distclean-libtool:
-rm -f libtool config.lt
-install-dist_cmakeDATA: $(dist_cmake_DATA)
- @$(NORMAL_INSTALL)
- @list='$(dist_cmake_DATA)'; test -n "$(cmakedir)" || list=; \
- if test -n "$$list"; then \
- echo " $(MKDIR_P) '$(DESTDIR)$(cmakedir)'"; \
- $(MKDIR_P) "$(DESTDIR)$(cmakedir)" || exit 1; \
- fi; \
- for p in $$list; do \
- if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
- echo "$$d$$p"; \
- done | $(am__base_list) | \
- while read files; do \
- echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(cmakedir)'"; \
- $(INSTALL_DATA) $$files "$(DESTDIR)$(cmakedir)" || exit $$?; \
- done
-
-uninstall-dist_cmakeDATA:
- @$(NORMAL_UNINSTALL)
- @list='$(dist_cmake_DATA)'; test -n "$(cmakedir)" || list=; \
- files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
- dir='$(DESTDIR)$(cmakedir)'; $(am__uninstall_files_from_dir)
install-nodist_cmakeDATA: $(nodist_cmake_DATA)
@$(NORMAL_INSTALL)
@list='$(nodist_cmake_DATA)'; test -n "$(cmakedir)" || list=; \
@@ -749,12 +736,13 @@ cscopelist-am: $(am__tagged_files)
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
-rm -f cscope.out cscope.in.out cscope.po.out cscope.files
+
distdir: $(BUILT_SOURCES)
$(MAKE) $(AM_MAKEFLAGS) distdir-am
distdir-am: $(DISTFILES)
$(am__remove_distdir)
- test -d "$(distdir)" || mkdir "$(distdir)"
+ $(AM_V_at)$(MKDIR_P) "$(distdir)"
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
list='$(DISTFILES)'; \
@@ -822,6 +810,10 @@ dist-gzip: distdir
dist-bzip2: distdir
tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2
$(am__post_remove_distdir)
+
+dist-bzip3: distdir
+ tardir=$(distdir) && $(am__tar) | bzip3 -c >$(distdir).tar.bz3
+ $(am__post_remove_distdir)
dist-lzip: distdir
tardir=$(distdir) && $(am__tar) | lzip -c $${LZIP_OPT--9} >$(distdir).tar.lz
$(am__post_remove_distdir)
@@ -862,9 +854,11 @@ dist dist-all:
distcheck: dist
case '$(DIST_ARCHIVES)' in \
*.tar.gz*) \
- eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).tar.gz | $(am__untar) ;;\
+ eval GZIP= gzip -dc $(distdir).tar.gz | $(am__untar) ;;\
*.tar.bz2*) \
bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\
+ *.tar.bz3*) \
+ bzip3 -dc $(distdir).tar.bz3 | $(am__untar) ;;\
*.tar.lz*) \
lzip -dc $(distdir).tar.lz | $(am__untar) ;;\
*.tar.xz*) \
@@ -872,7 +866,7 @@ distcheck: dist
*.tar.Z*) \
uncompress -c $(distdir).tar.Z | $(am__untar) ;;\
*.shar.gz*) \
- eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).shar.gz | unshar ;;\
+ eval GZIP= gzip -dc $(distdir).shar.gz | unshar ;;\
*.zip*) \
unzip $(distdir).zip ;;\
*.tar.zst*) \
@@ -948,7 +942,7 @@ check: check-recursive
all-am: Makefile $(DATA) expat_config.h
installdirs: installdirs-recursive
installdirs-am:
- for dir in "$(DESTDIR)$(cmakedir)" "$(DESTDIR)$(cmakedir)" "$(DESTDIR)$(pkgconfigdir)"; do \
+ for dir in "$(DESTDIR)$(cmakedir)" "$(DESTDIR)$(pkgconfigdir)"; do \
test -z "$$dir" || $(MKDIR_P) "$$dir"; \
done
install: install-recursive
@@ -975,8 +969,8 @@ mostlyclean-generic:
clean-generic:
distclean-generic:
- -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
- -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+ -$(am__rm_f) $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || $(am__rm_f) $(CONFIG_CLEAN_VPATH_FILES)
maintainer-clean-generic:
@echo "This command is intended for maintainers to use"
@@ -1003,8 +997,7 @@ info: info-recursive
info-am:
-install-data-am: install-dist_cmakeDATA install-nodist_cmakeDATA \
- install-pkgconfigDATA
+install-data-am: install-nodist_cmakeDATA install-pkgconfigDATA
install-dvi: install-dvi-recursive
@@ -1050,29 +1043,27 @@ ps: ps-recursive
ps-am:
-uninstall-am: uninstall-dist_cmakeDATA uninstall-nodist_cmakeDATA \
- uninstall-pkgconfigDATA
+uninstall-am: uninstall-nodist_cmakeDATA uninstall-pkgconfigDATA
.MAKE: $(am__recursive_targets) all install-am install-strip
.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \
am--refresh check check-am clean clean-cscope clean-generic \
clean-libtool cscope cscopelist-am ctags ctags-am dist \
- dist-all dist-bzip2 dist-gzip dist-lzip dist-shar dist-tarZ \
- dist-xz dist-zip dist-zstd distcheck distclean \
+ dist-all dist-bzip2 dist-bzip3 dist-gzip dist-lzip dist-shar \
+ dist-tarZ dist-xz dist-zip dist-zstd distcheck distclean \
distclean-generic distclean-hdr distclean-libtool \
distclean-tags distcleancheck distdir distuninstallcheck dvi \
dvi-am html html-am info info-am install install-am \
- install-data install-data-am install-dist_cmakeDATA \
- install-dvi install-dvi-am install-exec install-exec-am \
- install-html install-html-am install-info install-info-am \
- install-man install-nodist_cmakeDATA install-pdf \
- install-pdf-am install-pkgconfigDATA install-ps install-ps-am \
- install-strip installcheck installcheck-am installdirs \
- installdirs-am maintainer-clean maintainer-clean-generic \
- mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \
- ps ps-am tags tags-am uninstall uninstall-am \
- uninstall-dist_cmakeDATA uninstall-nodist_cmakeDATA \
+ install-data install-data-am install-dvi install-dvi-am \
+ install-exec install-exec-am install-html install-html-am \
+ install-info install-info-am install-man \
+ install-nodist_cmakeDATA install-pdf install-pdf-am \
+ install-pkgconfigDATA install-ps install-ps-am install-strip \
+ installcheck installcheck-am installdirs installdirs-am \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am uninstall-nodist_cmakeDATA \
uninstall-pkgconfigDATA
.PRECIOUS: Makefile
@@ -1149,3 +1140,10 @@ qa:
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
.NOEXPORT:
+
+# Tell GNU make to disable its built-in pattern rules.
+%:: %,v
+%:: RCS/%,v
+%:: RCS/%
+%:: s.%
+%:: SCCS/s.%
diff --git a/contrib/expat/README.md b/contrib/expat/README.md
index 77c6bf27d307..c2f288ca1242 100644
--- a/contrib/expat/README.md
+++ b/contrib/expat/README.md
@@ -1,5 +1,4 @@
[![Run Linux CI tasks](https://github.com/libexpat/libexpat/actions/workflows/linux.yml/badge.svg)](https://github.com/libexpat/libexpat/actions/workflows/linux.yml)
-[![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/github/libexpat/libexpat?svg=true)](https://ci.appveyor.com/project/libexpat/libexpat)
[![Packaging status](https://repology.org/badge/tiny-repos/expat.svg)](https://repology.org/metapackage/expat/versions)
[![Downloads SourceForge](https://img.shields.io/sourceforge/dt/expat?label=Downloads%20SourceForge)](https://sourceforge.net/projects/expat/files/)
[![Downloads GitHub](https://img.shields.io/github/downloads/libexpat/libexpat/total?label=Downloads%20GitHub)](https://github.com/libexpat/libexpat/releases)
@@ -12,7 +11,7 @@
> at the top of the `Changes` file.
-# Expat, Release 2.7.1
+# Expat, Release 2.7.3
This is Expat, a C99 library for parsing
[XML 1.0 Fourth Edition](https://www.w3.org/TR/2006/REC-xml-20060816/), started by
@@ -27,7 +26,8 @@ Expat supports the following C99 compilers:
- GNU GCC >=4.5 (for use from C) or GNU GCC >=4.8.1 (for use from C++)
- LLVM Clang >=3.5
-- Microsoft Visual Studio >=16.0/2019 (rolling `${today} minus 5 years`)
+- Microsoft Visual Studio >=17.0/2022
+ (the oldest version supported by the [official GitHub Actions Windows images](https://github.com/actions/runner-images))
Windows users can use the
[`expat-win32bin-*.*.*.{exe,zip}` download](https://github.com/libexpat/libexpat/releases),
@@ -120,7 +120,7 @@ project(hello VERSION 1.0.0)
FetchContent_Declare(
expat
GIT_REPOSITORY https://github.com/libexpat/libexpat/
- GIT_TAG 000000000_GIT_COMMIT_SHA1_HERE_000000000 # i.e. Git tag R_0_Y_Z
+ GIT_TAG 000000000_GIT_COMMIT_SHA1_HERE_000000000 # i.e. Git tag R_X_Y_Z
SOURCE_SUBDIR expat/
)
diff --git a/contrib/expat/configure.ac b/contrib/expat/configure.ac
index 0c88b8867019..072fea41ee8c 100644
--- a/contrib/expat/configure.ac
+++ b/contrib/expat/configure.ac
@@ -24,6 +24,7 @@ dnl Copyright (c) 2019 Kishore Kunche <kishore.kunche@intel.com>
dnl Copyright (c) 2020 Jeffrey Walton <noloader@gmail.com>
dnl Copyright (c) 2024 Ferenc Géczi <ferenc.gm@gmail.com>
dnl Copyright (c) 2024 Dag-Erling Smørgrav <des@des.dev>
+dnl Copyright (c) 2025 Matthew Fernandez <matthew.fernandez@gmail.com>
dnl Licensed under the MIT license:
dnl
dnl Permission is hereby granted, free of charge, to any person obtaining
@@ -84,9 +85,9 @@ dnl
dnl If the API changes incompatibly set LIBAGE back to 0
dnl
-LIBCURRENT=11 # sync
-LIBREVISION=2 # with
-LIBAGE=10 # CMakeLists.txt!
+LIBCURRENT=12 # sync
+LIBREVISION=1 # with
+LIBAGE=11 # CMakeLists.txt!
AC_CONFIG_HEADERS([expat_config.h])
AH_TOP([#ifndef EXPAT_CONFIG_H
@@ -95,7 +96,6 @@ AH_BOTTOM([#endif // ndef EXPAT_CONFIG_H])
AM_PROG_AR
AC_PROG_INSTALL
-AC_PROG_LN_S
AC_PROG_MAKE_SET
LT_PREREQ([2.4])
@@ -440,12 +440,22 @@ AC_MSG_RESULT([${CMAKE_SHARED_LIBRARY_PREFIX}])
AC_SUBST([CMAKE_SHARED_LIBRARY_PREFIX])
AS_CASE("${host_os}",
- [darwin*], [CMAKE_NOCONFIG_SOURCE=cmake/autotools/expat-noconfig__macos.cmake.in],
- [mingw*|cygwin*], [CMAKE_NOCONFIG_SOURCE=cmake/autotools/expat-noconfig__windows.cmake.in],
- [CMAKE_NOCONFIG_SOURCE=cmake/autotools/expat-noconfig__linux.cmake.in])
+ [darwin*], [
+ CMAKE_SOURCE=cmake/autotools/expat__macos.cmake.in
+ CMAKE_NOCONFIG_SOURCE=cmake/autotools/expat-noconfig__macos.cmake.in
+ ],
+ [mingw*|cygwin*], [
+ CMAKE_SOURCE=cmake/autotools/expat__windows.cmake.in
+ CMAKE_NOCONFIG_SOURCE=cmake/autotools/expat-noconfig__windows.cmake.in
+ ],
+ [
+ CMAKE_SOURCE=cmake/autotools/expat__linux.cmake.in
+ CMAKE_NOCONFIG_SOURCE=cmake/autotools/expat-noconfig__linux.cmake.in
+ ])
AC_CONFIG_FILES([Makefile]
[expat.pc]
[cmake/expat-config.cmake]
+ [cmake/autotools/expat.cmake:${CMAKE_SOURCE}]
[cmake/autotools/expat-config-version.cmake]
[cmake/autotools/expat-noconfig.cmake:${CMAKE_NOCONFIG_SOURCE}]
[doc/Makefile]
diff --git a/contrib/expat/doc/Makefile.in b/contrib/expat/doc/Makefile.in
index 72deb0565d94..13be5107f89b 100644
--- a/contrib/expat/doc/Makefile.in
+++ b/contrib/expat/doc/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.5 from Makefile.am.
+# Makefile.in generated by automake 1.18.1 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2021 Free Software Foundation, Inc.
+# Copyright (C) 1994-2025 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -102,6 +102,8 @@ am__make_running_with_option = \
test $$has_opt = yes
am__make_dryrun = (target_option=n; $(am__make_running_with_option))
am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+am__rm_f = rm -f $(am__rm_f_notfound)
+am__rm_rf = rm -rf $(am__rm_f_notfound)
pkgdatadir = $(datadir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
@@ -184,10 +186,9 @@ am__base_list = \
sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
am__uninstall_files_from_dir = { \
- test -z "$$files" \
- || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
- || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
- $(am__cd) "$$dir" && rm -f $$files; }; \
+ { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && echo $$files | $(am__xargs_n) 40 $(am__rm_f); }; \
}
man1dir = $(mandir)/man1
am__installdirs = "$(DESTDIR)$(man1dir)"
@@ -303,8 +304,10 @@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
am__include = @am__include@
am__leading_dot = @am__leading_dot@
am__quote = @am__quote@
+am__rm_f_notfound = @am__rm_f_notfound@
am__tar = @am__tar@
am__untar = @am__untar@
+am__xargs_n = @am__xargs_n@
bindir = @bindir@
build = @build@
build_alias = @build_alias@
@@ -442,6 +445,7 @@ ctags CTAGS:
cscope cscopelist:
@WITH_DISTRIBUTABLE_MANPAGE_TRUE@dist-hook:
+
distdir: $(BUILT_SOURCES)
$(MAKE) $(AM_MAKEFLAGS) distdir-am
@@ -507,11 +511,11 @@ install-strip:
mostlyclean-generic:
clean-generic:
- -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+ -$(am__rm_f) $(CLEANFILES)
distclean-generic:
- -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
- -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+ -$(am__rm_f) $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || $(am__rm_f) $(CONFIG_CLEAN_VPATH_FILES)
maintainer-clean-generic:
@echo "This command is intended for maintainers to use"
@@ -616,3 +620,10 @@ uninstall-man: uninstall-man1
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
.NOEXPORT:
+
+# Tell GNU make to disable its built-in pattern rules.
+%:: %,v
+%:: RCS/%,v
+%:: RCS/%
+%:: s.%
+%:: SCCS/s.%
diff --git a/contrib/expat/doc/reference.html b/contrib/expat/doc/reference.html
index 2b3bd39580a9..d2dded499435 100644
--- a/contrib/expat/doc/reference.html
+++ b/contrib/expat/doc/reference.html
@@ -52,7 +52,7 @@
<div>
<h1>
The Expat XML Parser
- <small>Release 2.7.1</small>
+ <small>Release 2.7.3</small>
</h1>
</div>
<div class="content">
@@ -157,6 +157,8 @@ interface.</p>
<ul>
<li><a href="#XML_SetBillionLaughsAttackProtectionMaximumAmplification">XML_SetBillionLaughsAttackProtectionMaximumAmplification</a></li>
<li><a href="#XML_SetBillionLaughsAttackProtectionActivationThreshold">XML_SetBillionLaughsAttackProtectionActivationThreshold</a></li>
+ <li><a href="#XML_SetAllocTrackerMaximumAmplification">XML_SetAllocTrackerMaximumAmplification</a></li>
+ <li><a href="#XML_SetAllocTrackerActivationThreshold">XML_SetAllocTrackerActivationThreshold</a></li>
<li><a href="#XML_SetReparseDeferralEnabled">XML_SetReparseDeferralEnabled</a></li>
</ul>
</li>
@@ -319,7 +321,7 @@ directions in the next section. Otherwise if you have Microsoft's
Developer Studio installed,
you can use CMake to generate a <code>.sln</code> file, e.g.
<code>
-cmake -G"Visual Studio 16 2019" -DCMAKE_BUILD_TYPE=RelWithDebInfo .
+cmake -G"Visual Studio 17 2022" -DCMAKE_BUILD_TYPE=RelWithDebInfo .
</code>, and build Expat using <code>msbuild /m expat.sln</code> after.</p>
<p>Alternatively, you may download the Win32 binary package that
@@ -1905,7 +1907,7 @@ struct XML_cp {
<p>Sets a handler for element declarations in a DTD. The handler gets
called with the name of the element in the declaration and a pointer
to a structure that contains the element model. It's the user code's
-responsibility to free model when finished with it. See <code>
+responsibility to free model when finished with via a call to <code>
<a href="#XML_FreeContentModel">XML_FreeContentModel</a></code>.
There is no need to free the model from the handler, it can be kept
around and freed at a later stage.</p>
@@ -2135,8 +2137,8 @@ XML_Size XMLCALL
XML_GetCurrentColumnNumber(XML_Parser p);
</pre>
<div class="fcndef">
-Return the offset, from the beginning of the current line, of
-the position.
+Return the <em>offset</em>, from the beginning of the current line, of
+the position. The first column is reported as <code>0</code>.
</div>
<h4 id="XML_GetCurrentByteCount">XML_GetCurrentByteCount</h4>
@@ -2198,13 +2200,16 @@ XML_SetBillionLaughsAttackProtectionMaximumAmplification(XML_Parser p,
returns <code>XML_TRUE</code> upon success and <code>XML_FALSE</code> upon error.
</p>
- The amplification factor is calculated as ..
- <pre>
- amplification := (direct + indirect) / direct
- </pre>
- .. while parsing, whereas
- <code>direct</code> is the number of bytes read from the primary document in parsing and
- <code>indirect</code> is the number of bytes added by expanding entities and reading of external DTD files, combined.
+ <p>
+ Once the <a href="#XML_SetBillionLaughsAttackProtectionActivationThreshold">threshold for activation</a> is reached,
+ the amplification factor is calculated as ..
+ </p>
+ <pre>amplification := (direct + indirect) / direct</pre>
+ <p>
+ .. while parsing, whereas
+ <code>direct</code> is the number of bytes read from the primary document in parsing and
+ <code>indirect</code> is the number of bytes added by expanding entities and reading of external DTD files, combined.
+ </p>
<p>For a call to <code>XML_SetBillionLaughsAttackProtectionMaximumAmplification</code> to succeed:</p>
<ul>
@@ -2267,6 +2272,123 @@ XML_SetBillionLaughsAttackProtectionActivationThreshold(XML_Parser p,
</p>
</div>
+<h4 id="XML_SetAllocTrackerMaximumAmplification">XML_SetAllocTrackerMaximumAmplification</h4>
+<pre class="fcndec">
+/* Added in Expat 2.7.2. */
+XML_Bool
+XML_SetAllocTrackerMaximumAmplification(XML_Parser p,
+ float maximumAmplificationFactor);
+</pre>
+<div class="fcndef">
+ <p>
+ Sets the maximum tolerated amplification factor
+ between direct input and bytes of dynamic memory allocated
+ (default: <code>100.0</code>)
+ of parser <code>p</code> to <code>maximumAmplificationFactor</code>, and
+ returns <code>XML_TRUE</code> upon success and <code>XML_FALSE</code> upon error.
+ </p>
+
+ <p>
+ <strong>Note:</strong>
+ There are three types of allocations that intentionally bypass tracking and limiting:
+ </p>
+ <ul>
+ <li>
+ application calls to functions
+ <code><a href="#XML_MemMalloc">XML_MemMalloc</a></code>
+ and
+ <code><a href="#XML_MemRealloc">XML_MemRealloc</a></code>
+ &mdash;
+ <em>healthy</em> use of these two functions continues to be a responsibility
+ of the application using Expat
+ &mdash;,
+ </li>
+ <li>
+ the main character buffer used by functions
+ <code><a href="#XML_GetBuffer">XML_GetBuffer</a></code>
+ and
+ <code><a href="#XML_ParseBuffer">XML_ParseBuffer</a></code>
+ (and thus also by plain
+ <code><a href="#XML_Parse">XML_Parse</a></code>), and
+ </li>
+ <li>
+ the <a href="#XML_SetElementDeclHandler">content model memory</a>
+ (that is passed to the
+ <a href="#XML_SetElementDeclHandler">element declaration handler</a>
+ and freed by a call to
+ <code><a href="#XML_FreeContentModel">XML_FreeContentModel</a></code>).
+ </li>
+ </ul>
+
+ <p>
+ Once the <a href="#XML_SetAllocTrackerActivationThreshold">threshold for activation</a> is reached,
+ the amplification factor is calculated as ..
+ </p>
+ <pre>amplification := allocated / direct</pre>
+ <p>
+ .. while parsing, whereas
+ <code>direct</code> is the number of bytes read from the primary document in parsing and
+ <code>allocated</code> is the number of bytes of dynamic memory allocated in the parser hierarchy.
+ </p>
+
+ <p>For a call to <code>XML_SetAllocTrackerMaximumAmplification</code> to succeed:</p>
+ <ul>
+ <li>parser <code>p</code> must be a non-<code>NULL</code> root parser (without any parent parsers) and</li>
+ <li><code>maximumAmplificationFactor</code> must be non-<code>NaN</code> and greater than or equal to <code>1.0</code>.</li>
+ </ul>
+
+ <p>
+ <strong>Note:</strong>
+ If you ever need to increase this value for non-attack payload,
+ please <a href="https://github.com/libexpat/libexpat/issues">file a bug report</a>.
+ </p>
+
+ <p>
+ <strong>Note:</strong>
+ Amplifications factors greater than <code>100.0</code> can been observed near the start of parsing
+ even with benign files in practice.
+
+ So if you do reduce the maximum allowed amplification,
+ please make sure that the activation threshold is still big enough
+ to not end up with undesired false positives (i.e. benign files being rejected).
+ </p>
+</div>
+
+<h4 id="XML_SetAllocTrackerActivationThreshold">XML_SetAllocTrackerActivationThreshold</h4>
+<pre class="fcndec">
+/* Added in Expat 2.7.2. */
+XML_Bool
+XML_SetAllocTrackerActivationThreshold(XML_Parser p,
+ unsigned long long activationThresholdBytes);
+</pre>
+<div class="fcndef">
+ <p>
+ Sets number of allocated bytes of dynamic memory
+ needed to activate protection against disproportionate use of RAM
+ (default: <code>64 MiB</code>)
+ of parser <code>p</code> to <code>activationThresholdBytes</code>, and
+ returns <code>XML_TRUE</code> upon success and <code>XML_FALSE</code> upon error.
+ </p>
+
+ <p>
+ <strong>Note:</strong>
+ For types of allocations that intentionally bypass tracking and limiting, please see
+ <code><a href="#XML_SetAllocTrackerMaximumAmplification">XML_SetAllocTrackerMaximumAmplification</a></code>
+ above.
+ </p>
+
+ <p>For a call to <code>XML_SetAllocTrackerActivationThreshold</code> to succeed:</p>
+ <ul>
+ <li>parser <code>p</code> must be a non-<code>NULL</code> root parser (without any parent parsers).</li>
+ </ul>
+
+ <p>
+ <strong>Note:</strong>
+ If you ever need to increase this value for non-attack payload,
+ please <a href="https://github.com/libexpat/libexpat/issues">file a bug report</a>.
+ </p>
+</div>
+
<h4 id="XML_SetReparseDeferralEnabled">XML_SetReparseDeferralEnabled</h4>
<pre class="fcndec">
/* Added in Expat 2.6.0. */
diff --git a/contrib/expat/doc/xmlwf.1 b/contrib/expat/doc/xmlwf.1
index 76aa7e30d074..aa2e9c218007 100644
--- a/contrib/expat/doc/xmlwf.1
+++ b/contrib/expat/doc/xmlwf.1
@@ -5,7 +5,7 @@
\\$2 \(la\\$1\(ra\\$3
..
.if \n(.g .mso www.tmac
-.TH XMLWF 1 "March 27, 2025" "" ""
+.TH XMLWF 1 "September 24, 2025" "" ""
.SH NAME
xmlwf \- Determines if an XML document is well-formed
.SH SYNOPSIS
@@ -88,7 +88,11 @@ supports both.
.TP
\*(T<\fB\-a\fR\*(T> \fIfactor\fR
Sets the maximum tolerated amplification factor
-for protection against billion laughs attacks (default: 100.0).
+for protection against amplification attacks
+like the billion laughs attack
+(default: 100.0
+for the sum of direct and indirect output and also
+for allocations of dynamic memory).
The amplification factor is calculated as ..
.nf
@@ -97,12 +101,22 @@ The amplification factor is calculated as ..
.fi
-\&.. while parsing, whereas
+\&.. with regard to use of entities and ..
+
+.nf
+
+ amplification := allocated / direct
+
+.fi
+
+\&.. with regard to dynamic memory while parsing.
<direct> is the number of bytes read
-from the primary document in parsing and
+from the primary document in parsing,
<indirect> is the number of bytes
added by expanding entities and reading of external DTD files,
-combined.
+combined, and
+<allocated> is the total number of bytes of dynamic memory
+allocated (and not freed) per hierarchy of parsers.
\fINOTE\fR:
If you ever need to increase this value for non-attack payload,
@@ -110,8 +124,10 @@ please file a bug report.
.TP
\*(T<\fB\-b\fR\*(T> \fIbytes\fR
Sets the number of output bytes (including amplification)
-needed to activate protection against billion laughs attacks
-(default: 8 MiB).
+needed to activate protection against amplification attacks
+like billion laughs
+(default: 8 MiB for the sum of direct and indirect output,
+and 64 MiB for allocations of dynamic memory).
This can be thought of as an "activation threshold".
\fINOTE\fR:
diff --git a/contrib/expat/doc/xmlwf.xml b/contrib/expat/doc/xmlwf.xml
index 17e9cf51c191..01316bb16627 100644
--- a/contrib/expat/doc/xmlwf.xml
+++ b/contrib/expat/doc/xmlwf.xml
@@ -21,7 +21,7 @@
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" [
<!ENTITY dhfirstname "<firstname>Scott</firstname>">
<!ENTITY dhsurname "<surname>Bronson</surname>">
- <!ENTITY dhdate "<date>March 27, 2025</date>">
+ <!ENTITY dhdate "<date>September 24, 2025</date>">
<!-- Please adjust this^^ date whenever cutting a new release. -->
<!ENTITY dhsection "<manvolnum>1</manvolnum>">
<!ENTITY dhemail "<email>bronson@rinspin.com</email>">
@@ -158,19 +158,31 @@ supports both.
<listitem>
<para>
Sets the maximum tolerated amplification factor
- for protection against billion laughs attacks (default: 100.0).
+ for protection against amplification attacks
+ like the billion laughs attack
+ (default: 100.0
+ for the sum of direct and indirect output and also
+ for allocations of dynamic memory).
The amplification factor is calculated as ..
</para>
<literallayout>
amplification := (direct + indirect) / direct
</literallayout>
<para>
- .. while parsing, whereas
+ .. with regard to use of entities and ..
+ </para>
+ <literallayout>
+ amplification := allocated / direct
+ </literallayout>
+ <para>
+ .. with regard to dynamic memory while parsing.
&lt;direct&gt; is the number of bytes read
- from the primary document in parsing and
+ from the primary document in parsing,
&lt;indirect&gt; is the number of bytes
added by expanding entities and reading of external DTD files,
- combined.
+ combined, and
+ &lt;allocated&gt; is the total number of bytes of dynamic memory
+ allocated (and not freed) per hierarchy of parsers.
</para>
<para>
<emphasis>NOTE</emphasis>:
@@ -185,8 +197,10 @@ supports both.
<listitem>
<para>
Sets the number of output bytes (including amplification)
- needed to activate protection against billion laughs attacks
- (default: 8 MiB).
+ needed to activate protection against amplification attacks
+ like billion laughs
+ (default: 8 MiB for the sum of direct and indirect output,
+ and 64 MiB for allocations of dynamic memory).
This can be thought of as an &quot;activation threshold&quot;.
</para>
<para>
diff --git a/contrib/expat/examples/Makefile.in b/contrib/expat/examples/Makefile.in
index 044c9089c565..0e55052ce6e4 100644
--- a/contrib/expat/examples/Makefile.in
+++ b/contrib/expat/examples/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.5 from Makefile.am.
+# Makefile.in generated by automake 1.18.1 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2021 Free Software Foundation, Inc.
+# Copyright (C) 1994-2025 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -101,6 +101,8 @@ am__make_running_with_option = \
test $$has_opt = yes
am__make_dryrun = (target_option=n; $(am__make_running_with_option))
am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+am__rm_f = rm -f $(am__rm_f_notfound)
+am__rm_rf = rm -rf $(am__rm_f_notfound)
pkgdatadir = $(datadir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
@@ -330,8 +332,10 @@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
am__include = @am__include@
am__leading_dot = @am__leading_dot@
am__quote = @am__quote@
+am__rm_f_notfound = @am__rm_f_notfound@
am__tar = @am__tar@
am__untar = @am__untar@
+am__xargs_n = @am__xargs_n@
bindir = @bindir@
build = @build@
build_alias = @build_alias@
@@ -414,13 +418,8 @@ $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
$(am__aclocal_m4_deps):
clean-noinstPROGRAMS:
- @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \
- echo " rm -f" $$list; \
- rm -f $$list || exit $$?; \
- test -n "$(EXEEXT)" || exit 0; \
- list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
- echo " rm -f" $$list; \
- rm -f $$list
+ $(am__rm_f) $(noinst_PROGRAMS)
+ test -z "$(EXEEXT)" || $(am__rm_f) $(noinst_PROGRAMS:$(EXEEXT)=)
element_declarations$(EXEEXT): $(element_declarations_OBJECTS) $(element_declarations_DEPENDENCIES) $(EXTRA_element_declarations_DEPENDENCIES)
@rm -f element_declarations$(EXEEXT)
@@ -446,7 +445,7 @@ distclean-compile:
$(am__depfiles_remade):
@$(MKDIR_P) $(@D)
- @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+ @: >>$@
am--depfiles: $(am__depfiles_remade)
@@ -528,6 +527,7 @@ cscopelist-am: $(am__tagged_files)
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
distdir: $(BUILT_SOURCES)
$(MAKE) $(AM_MAKEFLAGS) distdir-am
@@ -589,8 +589,8 @@ mostlyclean-generic:
clean-generic:
distclean-generic:
- -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
- -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+ -$(am__rm_f) $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || $(am__rm_f) $(CONFIG_CLEAN_VPATH_FILES)
maintainer-clean-generic:
@echo "This command is intended for maintainers to use"
@@ -601,7 +601,7 @@ clean-am: clean-generic clean-libtool clean-noinstPROGRAMS \
mostlyclean-am
distclean: distclean-am
- -rm -f ./$(DEPDIR)/element_declarations.Po
+ -rm -f ./$(DEPDIR)/element_declarations.Po
-rm -f ./$(DEPDIR)/elements.Po
-rm -f ./$(DEPDIR)/outline.Po
-rm -f Makefile
@@ -649,7 +649,7 @@ install-ps-am:
installcheck-am:
maintainer-clean: maintainer-clean-am
- -rm -f ./$(DEPDIR)/element_declarations.Po
+ -rm -f ./$(DEPDIR)/element_declarations.Po
-rm -f ./$(DEPDIR)/elements.Po
-rm -f ./$(DEPDIR)/outline.Po
-rm -f Makefile
@@ -692,3 +692,10 @@ uninstall-am:
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
.NOEXPORT:
+
+# Tell GNU make to disable its built-in pattern rules.
+%:: %,v
+%:: RCS/%,v
+%:: RCS/%
+%:: s.%
+%:: SCCS/s.%
diff --git a/contrib/expat/expat_config.h.in b/contrib/expat/expat_config.h.in
index 67ef89c7171a..543db8252448 100644
--- a/contrib/expat/expat_config.h.in
+++ b/contrib/expat/expat_config.h.in
@@ -24,7 +24,7 @@
/* Define to 1 if you have the <fcntl.h> header file. */
#undef HAVE_FCNTL_H
-/* Define to 1 if you have the `getpagesize' function. */
+/* Define to 1 if you have the 'getpagesize' function. */
#undef HAVE_GETPAGESIZE
/* Define to 1 if you have the `getrandom' function. */
@@ -33,10 +33,10 @@
/* Define to 1 if you have the <inttypes.h> header file. */
#undef HAVE_INTTYPES_H
-/* Define to 1 if you have the `bsd' library (-lbsd). */
+/* Define to 1 if you have the 'bsd' library (-lbsd). */
#undef HAVE_LIBBSD
-/* Define to 1 if you have a working `mmap' system call. */
+/* Define to 1 if you have a working 'mmap' system call. */
#undef HAVE_MMAP
/* Define to 1 if you have the <stdint.h> header file. */
@@ -93,7 +93,7 @@
/* Define to the version of this package. */
#undef PACKAGE_VERSION
-/* Define to 1 if all of the C90 standard headers exist (not just the ones
+/* Define to 1 if all of the C89 standard headers exist (not just the ones
required in a freestanding environment). This macro is provided for
backward compatibility; new code need not use it. */
#undef STDC_HEADERS
@@ -133,10 +133,10 @@
/* Define to make XML Namespaces functionality available. */
#undef XML_NS
-/* Define to empty if `const' does not conform to ANSI C. */
+/* Define to empty if 'const' does not conform to ANSI C. */
#undef const
-/* Define to `long int' if <sys/types.h> does not define. */
+/* Define to 'long int' if <sys/types.h> does not define. */
#undef off_t
#endif // ndef EXPAT_CONFIG_H
diff --git a/contrib/expat/fuzz/xml_lpm_fuzzer.cpp b/contrib/expat/fuzz/xml_lpm_fuzzer.cpp
index f52ea7b21e40..719629a6b547 100644
--- a/contrib/expat/fuzz/xml_lpm_fuzzer.cpp
+++ b/contrib/expat/fuzz/xml_lpm_fuzzer.cpp
@@ -354,8 +354,10 @@ ExternalEntityRefHandler(XML_Parser parser, const XML_Char *context,
if (g_external_entity) {
XML_Parser ext_parser
= XML_ExternalEntityParserCreate(parser, context, g_encoding);
- rc = Parse(ext_parser, g_external_entity, g_external_entity_size, 1);
- XML_ParserFree(ext_parser);
+ if (ext_parser != NULL) {
+ rc = Parse(ext_parser, g_external_entity, g_external_entity_size, 1);
+ XML_ParserFree(ext_parser);
+ }
}
return rc;
diff --git a/contrib/expat/fuzz/xml_parse_fuzzer.c b/contrib/expat/fuzz/xml_parse_fuzzer.c
index 6a1affe2b1f6..29ab33ff79d9 100644
--- a/contrib/expat/fuzz/xml_parse_fuzzer.c
+++ b/contrib/expat/fuzz/xml_parse_fuzzer.c
@@ -15,6 +15,7 @@
*/
#include <assert.h>
+#include <limits.h> // for INT_MAX
#include <stdint.h>
#include "expat.h"
@@ -65,8 +66,9 @@ ParseOneInput(XML_Parser p, const uint8_t *data, size_t size) {
XML_SetUserData(p, p);
XML_SetElementHandler(p, start, end);
XML_SetCharacterDataHandler(p, may_stop_character_handler);
- XML_Parse(p, (const XML_Char *)data, size, 0);
- if (XML_Parse(p, (const XML_Char *)data, size, 1) == XML_STATUS_ERROR) {
+ assert(size <= INT_MAX);
+ XML_Parse(p, (const XML_Char *)data, (int)size, 0);
+ if (XML_Parse(p, (const XML_Char *)data, (int)size, 1) == XML_STATUS_ERROR) {
XML_ErrorString(XML_GetErrorCode(p));
}
XML_GetCurrentLineNumber(p);
@@ -89,15 +91,17 @@ LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
XML_Parser externalEntityParser
= XML_ExternalEntityParserCreate(parentParser, "e1", NULL);
- assert(externalEntityParser);
- ParseOneInput(externalEntityParser, data, size);
- XML_ParserFree(externalEntityParser);
+ if (externalEntityParser != NULL) {
+ ParseOneInput(externalEntityParser, data, size);
+ XML_ParserFree(externalEntityParser);
+ }
XML_Parser externalDtdParser
= XML_ExternalEntityParserCreate(parentParser, NULL, NULL);
- assert(externalDtdParser);
- ParseOneInput(externalDtdParser, data, size);
- XML_ParserFree(externalDtdParser);
+ if (externalDtdParser != NULL) {
+ ParseOneInput(externalDtdParser, data, size);
+ XML_ParserFree(externalDtdParser);
+ }
// finally frees this parser which served as parent
XML_ParserFree(parentParser);
diff --git a/contrib/expat/fuzz/xml_parsebuffer_fuzzer.c b/contrib/expat/fuzz/xml_parsebuffer_fuzzer.c
index cfc4af202851..38b9981b0b50 100644
--- a/contrib/expat/fuzz/xml_parsebuffer_fuzzer.c
+++ b/contrib/expat/fuzz/xml_parsebuffer_fuzzer.c
@@ -15,6 +15,7 @@
*/
#include <assert.h>
+#include <limits.h> // for INT_MAX
#include <stdint.h>
#include <string.h>
@@ -66,16 +67,17 @@ ParseOneInput(XML_Parser p, const uint8_t *data, size_t size) {
XML_SetUserData(p, p);
XML_SetElementHandler(p, start, end);
XML_SetCharacterDataHandler(p, may_stop_character_handler);
- void *buf = XML_GetBuffer(p, size);
+ assert(size <= INT_MAX);
+ void *buf = XML_GetBuffer(p, (int)size);
assert(buf);
memcpy(buf, data, size);
- XML_ParseBuffer(p, size, 0);
- buf = XML_GetBuffer(p, size);
+ XML_ParseBuffer(p, (int)size, 0);
+ buf = XML_GetBuffer(p, (int)size);
if (buf == NULL) {
return;
}
memcpy(buf, data, size);
- if (XML_ParseBuffer(p, size, 1) == XML_STATUS_ERROR) {
+ if (XML_ParseBuffer(p, (int)size, 1) == XML_STATUS_ERROR) {
XML_ErrorString(XML_GetErrorCode(p));
}
XML_GetCurrentLineNumber(p);
@@ -101,15 +103,17 @@ LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
XML_Parser externalEntityParser
= XML_ExternalEntityParserCreate(parentParser, "e1", NULL);
- assert(externalEntityParser);
- ParseOneInput(externalEntityParser, data, size);
- XML_ParserFree(externalEntityParser);
+ if (externalEntityParser != NULL) {
+ ParseOneInput(externalEntityParser, data, size);
+ XML_ParserFree(externalEntityParser);
+ }
XML_Parser externalDtdParser
= XML_ExternalEntityParserCreate(parentParser, NULL, NULL);
- assert(externalDtdParser);
- ParseOneInput(externalDtdParser, data, size);
- XML_ParserFree(externalDtdParser);
+ if (externalDtdParser != NULL) {
+ ParseOneInput(externalDtdParser, data, size);
+ XML_ParserFree(externalDtdParser);
+ }
// finally frees this parser which served as parent
XML_ParserFree(parentParser);
diff --git a/contrib/expat/lib/Makefile.in b/contrib/expat/lib/Makefile.in
index 1a97e85fc41f..d85f80dbdbba 100644
--- a/contrib/expat/lib/Makefile.in
+++ b/contrib/expat/lib/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.5 from Makefile.am.
+# Makefile.in generated by automake 1.18.1 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2021 Free Software Foundation, Inc.
+# Copyright (C) 1994-2025 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -104,6 +104,8 @@ am__make_running_with_option = \
test $$has_opt = yes
am__make_dryrun = (target_option=n; $(am__make_running_with_option))
am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+am__rm_f = rm -f $(am__rm_f_notfound)
+am__rm_rf = rm -rf $(am__rm_f_notfound)
pkgdatadir = $(datadir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
@@ -168,10 +170,9 @@ am__base_list = \
sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
am__uninstall_files_from_dir = { \
- test -z "$$files" \
- || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
- || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
- $(am__cd) "$$dir" && rm -f $$files; }; \
+ { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && echo $$files | $(am__xargs_n) 40 $(am__rm_f); }; \
}
am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(docdir)" \
"$(DESTDIR)$(includedir)"
@@ -368,8 +369,10 @@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
am__include = @am__include@
am__leading_dot = @am__leading_dot@
am__quote = @am__quote@
+am__rm_f_notfound = @am__rm_f_notfound@
am__tar = @am__tar@
am__untar = @am__untar@
+am__xargs_n = @am__xargs_n@
bindir = @bindir@
build = @build@
build_alias = @build_alias@
@@ -513,26 +516,22 @@ uninstall-libLTLIBRARIES:
done
clean-libLTLIBRARIES:
- -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+ -$(am__rm_f) $(lib_LTLIBRARIES)
@list='$(lib_LTLIBRARIES)'; \
locs=`for p in $$list; do echo $$p; done | \
sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
sort -u`; \
- test -z "$$locs" || { \
- echo rm -f $${locs}; \
- rm -f $${locs}; \
- }
+ echo rm -f $${locs}; \
+ $(am__rm_f) $${locs}
clean-noinstLTLIBRARIES:
- -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ -$(am__rm_f) $(noinst_LTLIBRARIES)
@list='$(noinst_LTLIBRARIES)'; \
locs=`for p in $$list; do echo $$p; done | \
sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \
sort -u`; \
- test -z "$$locs" || { \
- echo rm -f $${locs}; \
- rm -f $${locs}; \
- }
+ echo rm -f $${locs}; \
+ $(am__rm_f) $${locs}
libexpat.la: $(libexpat_la_OBJECTS) $(libexpat_la_DEPENDENCIES) $(EXTRA_libexpat_la_DEPENDENCIES)
$(AM_V_CCLD)$(libexpat_la_LINK) -rpath $(libdir) $(libexpat_la_OBJECTS) $(libexpat_la_LIBADD) $(LIBS)
@@ -555,7 +554,7 @@ distclean-compile:
$(am__depfiles_remade):
@$(MKDIR_P) $(@D)
- @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+ @: >>$@
am--depfiles: $(am__depfiles_remade)
@@ -700,6 +699,7 @@ cscopelist-am: $(am__tagged_files)
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
distdir: $(BUILT_SOURCES)
$(MAKE) $(AM_MAKEFLAGS) distdir-am
@@ -764,8 +764,8 @@ mostlyclean-generic:
clean-generic:
distclean-generic:
- -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
- -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+ -$(am__rm_f) $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || $(am__rm_f) $(CONFIG_CLEAN_VPATH_FILES)
maintainer-clean-generic:
@echo "This command is intended for maintainers to use"
@@ -776,7 +776,7 @@ clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
clean-noinstLTLIBRARIES mostlyclean-am
distclean: distclean-am
- -rm -f ./$(DEPDIR)/libtestpat_la-xmlparse.Plo
+ -rm -f ./$(DEPDIR)/libtestpat_la-xmlparse.Plo
-rm -f ./$(DEPDIR)/libtestpat_la-xmlrole.Plo
-rm -f ./$(DEPDIR)/libtestpat_la-xmltok.Plo
-rm -f ./$(DEPDIR)/xmlparse.Plo
@@ -828,7 +828,7 @@ install-ps-am:
installcheck-am:
maintainer-clean: maintainer-clean-am
- -rm -f ./$(DEPDIR)/libtestpat_la-xmlparse.Plo
+ -rm -f ./$(DEPDIR)/libtestpat_la-xmlparse.Plo
-rm -f ./$(DEPDIR)/libtestpat_la-xmlrole.Plo
-rm -f ./$(DEPDIR)/libtestpat_la-xmltok.Plo
-rm -f ./$(DEPDIR)/xmlparse.Plo
@@ -885,3 +885,10 @@ uninstall-local:
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
.NOEXPORT:
+
+# Tell GNU make to disable its built-in pattern rules.
+%:: %,v
+%:: RCS/%,v
+%:: RCS/%
+%:: s.%
+%:: SCCS/s.%
diff --git a/contrib/expat/lib/expat.h b/contrib/expat/lib/expat.h
index 610e1ddc0e94..290dfeb0f6dd 100644
--- a/contrib/expat/lib/expat.h
+++ b/contrib/expat/lib/expat.h
@@ -19,6 +19,7 @@
Copyright (c) 2023 Hanno Böck <hanno@gentoo.org>
Copyright (c) 2023 Sony Corporation / Snild Dolkow <snild@sony.com>
Copyright (c) 2024 Taichi Haradaguchi <20001722@ymail.ne.jp>
+ Copyright (c) 2025 Matthew Fernandez <matthew.fernandez@gmail.com>
Licensed under the MIT license:
Permission is hereby granted, free of charge, to any person obtaining
@@ -42,21 +43,21 @@
*/
#ifndef Expat_INCLUDED
-#define Expat_INCLUDED 1
+# define Expat_INCLUDED 1
-#include <stdlib.h>
-#include "expat_external.h"
+# include <stdlib.h>
+# include "expat_external.h"
-#ifdef __cplusplus
+# ifdef __cplusplus
extern "C" {
-#endif
+# endif
struct XML_ParserStruct;
typedef struct XML_ParserStruct *XML_Parser;
typedef unsigned char XML_Bool;
-#define XML_TRUE ((XML_Bool)1)
-#define XML_FALSE ((XML_Bool)0)
+# define XML_TRUE ((XML_Bool)1)
+# define XML_FALSE ((XML_Bool)0)
/* The XML_Status enum gives the possible return values for several
API functions. The preprocessor #defines are included so this
@@ -73,11 +74,11 @@ typedef unsigned char XML_Bool;
*/
enum XML_Status {
XML_STATUS_ERROR = 0,
-#define XML_STATUS_ERROR XML_STATUS_ERROR
+# define XML_STATUS_ERROR XML_STATUS_ERROR
XML_STATUS_OK = 1,
-#define XML_STATUS_OK XML_STATUS_OK
+# define XML_STATUS_OK XML_STATUS_OK
XML_STATUS_SUSPENDED = 2
-#define XML_STATUS_SUSPENDED XML_STATUS_SUSPENDED
+# define XML_STATUS_SUSPENDED XML_STATUS_SUSPENDED
};
enum XML_Error {
@@ -276,7 +277,7 @@ XML_ParserCreate_MM(const XML_Char *encoding,
/* Prepare a parser object to be reused. This is particularly
valuable when memory allocation overhead is disproportionately high,
- such as when a large number of small documnents need to be parsed.
+ such as when a large number of small documents need to be parsed.
All handlers are cleared from the parser, except for the
unknownEncodingHandler. The parser's external state is re-initialized
except for the values of ns and ns_triplets.
@@ -680,7 +681,7 @@ XMLPARSEAPI(void)
XML_SetUserData(XML_Parser parser, void *userData);
/* Returns the last value set by XML_SetUserData or NULL. */
-#define XML_GetUserData(parser) (*(void **)(parser))
+# define XML_GetUserData(parser) (*(void **)(parser))
/* This is equivalent to supplying an encoding argument to
XML_ParserCreate. On success XML_SetEncoding returns non-zero,
@@ -752,7 +753,7 @@ XML_GetSpecifiedAttributeCount(XML_Parser parser);
XMLPARSEAPI(int)
XML_GetIdAttributeIndex(XML_Parser parser);
-#ifdef XML_ATTR_INFO
+# ifdef XML_ATTR_INFO
/* Source file byte offsets for the start and end of attribute names and values.
The value indices are exclusive of surrounding quotes; thus in a UTF-8 source
file an attribute value of "blah" will yield:
@@ -773,7 +774,7 @@ typedef struct {
*/
XMLPARSEAPI(const XML_AttrInfo *)
XML_GetAttributeInfo(XML_Parser parser);
-#endif
+# endif
/* Parses some input. Returns XML_STATUS_ERROR if a fatal error is
detected. The last call to XML_Parse must have isFinal true; len
@@ -970,9 +971,9 @@ XMLPARSEAPI(const char *)
XML_GetInputContext(XML_Parser parser, int *offset, int *size);
/* For backwards compatibility with previous versions. */
-#define XML_GetErrorLineNumber XML_GetCurrentLineNumber
-#define XML_GetErrorColumnNumber XML_GetCurrentColumnNumber
-#define XML_GetErrorByteIndex XML_GetCurrentByteIndex
+# define XML_GetErrorLineNumber XML_GetCurrentLineNumber
+# define XML_GetErrorColumnNumber XML_GetCurrentColumnNumber
+# define XML_GetErrorByteIndex XML_GetCurrentByteIndex
/* Frees the content model passed to the element declaration handler */
XMLPARSEAPI(void)
@@ -1032,7 +1033,10 @@ enum XML_FeatureEnum {
XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT,
XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT,
/* Added in Expat 2.6.0. */
- XML_FEATURE_GE
+ XML_FEATURE_GE,
+ /* Added in Expat 2.7.2. */
+ XML_FEATURE_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT,
+ XML_FEATURE_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT,
/* Additional features must be added to the end of this enum. */
};
@@ -1045,7 +1049,7 @@ typedef struct {
XMLPARSEAPI(const XML_Feature *)
XML_GetFeatureList(void);
-#if defined(XML_DTD) || (defined(XML_GE) && XML_GE == 1)
+# if defined(XML_DTD) || (defined(XML_GE) && XML_GE == 1)
/* Added in Expat 2.4.0 for XML_DTD defined and
* added in Expat 2.6.0 for XML_GE == 1. */
XMLPARSEAPI(XML_Bool)
@@ -1057,7 +1061,17 @@ XML_SetBillionLaughsAttackProtectionMaximumAmplification(
XMLPARSEAPI(XML_Bool)
XML_SetBillionLaughsAttackProtectionActivationThreshold(
XML_Parser parser, unsigned long long activationThresholdBytes);
-#endif
+
+/* Added in Expat 2.7.2. */
+XMLPARSEAPI(XML_Bool)
+XML_SetAllocTrackerMaximumAmplification(XML_Parser parser,
+ float maximumAmplificationFactor);
+
+/* Added in Expat 2.7.2. */
+XMLPARSEAPI(XML_Bool)
+XML_SetAllocTrackerActivationThreshold(
+ XML_Parser parser, unsigned long long activationThresholdBytes);
+# endif
/* Added in Expat 2.6.0. */
XMLPARSEAPI(XML_Bool)
@@ -1066,12 +1080,12 @@ XML_SetReparseDeferralEnabled(XML_Parser parser, XML_Bool enabled);
/* Expat follows the semantic versioning convention.
See https://semver.org
*/
-#define XML_MAJOR_VERSION 2
-#define XML_MINOR_VERSION 7
-#define XML_MICRO_VERSION 1
+# define XML_MAJOR_VERSION 2
+# define XML_MINOR_VERSION 7
+# define XML_MICRO_VERSION 3
-#ifdef __cplusplus
+# ifdef __cplusplus
}
-#endif
+# endif
#endif /* not Expat_INCLUDED */
diff --git a/contrib/expat/lib/expat_external.h b/contrib/expat/lib/expat_external.h
index 8829f7709104..96f955eefb6b 100644
--- a/contrib/expat/lib/expat_external.h
+++ b/contrib/expat/lib/expat_external.h
@@ -38,7 +38,7 @@
*/
#ifndef Expat_External_INCLUDED
-#define Expat_External_INCLUDED 1
+# define Expat_External_INCLUDED 1
/* External API definitions */
@@ -64,12 +64,12 @@
compiled with the cdecl calling convention as the default since
system headers may assume the cdecl convention.
*/
-#ifndef XMLCALL
-# if defined(_MSC_VER)
-# define XMLCALL __cdecl
-# elif defined(__GNUC__) && defined(__i386) && ! defined(__INTEL_COMPILER)
-# define XMLCALL __attribute__((cdecl))
-# else
+# ifndef XMLCALL
+# if defined(_MSC_VER)
+# define XMLCALL __cdecl
+# elif defined(__GNUC__) && defined(__i386) && ! defined(__INTEL_COMPILER)
+# define XMLCALL __attribute__((cdecl))
+# else
/* For any platform which uses this definition and supports more than
one calling convention, we need to extend this definition to
declare the convention used on that platform, if it's possible to
@@ -80,86 +80,87 @@
pre-processor and how to specify the same calling convention as the
platform's malloc() implementation.
*/
-# define XMLCALL
-# endif
-#endif /* not defined XMLCALL */
+# define XMLCALL
+# endif
+# endif /* not defined XMLCALL */
-#if ! defined(XML_STATIC) && ! defined(XMLIMPORT)
-# ifndef XML_BUILDING_EXPAT
+# if ! defined(XML_STATIC) && ! defined(XMLIMPORT)
+# ifndef XML_BUILDING_EXPAT
/* using Expat from an application */
-# if defined(_MSC_EXTENSIONS) && ! defined(__BEOS__) && ! defined(__CYGWIN__)
-# define XMLIMPORT __declspec(dllimport)
+# if defined(_MSC_EXTENSIONS) && ! defined(__BEOS__) \
+ && ! defined(__CYGWIN__)
+# define XMLIMPORT __declspec(dllimport)
+# endif
+
# endif
+# endif /* not defined XML_STATIC */
+# ifndef XML_ENABLE_VISIBILITY
+# define XML_ENABLE_VISIBILITY 0
# endif
-#endif /* not defined XML_STATIC */
-
-#ifndef XML_ENABLE_VISIBILITY
-# define XML_ENABLE_VISIBILITY 0
-#endif
-#if ! defined(XMLIMPORT) && XML_ENABLE_VISIBILITY
-# define XMLIMPORT __attribute__((visibility("default")))
-#endif
+# if ! defined(XMLIMPORT) && XML_ENABLE_VISIBILITY
+# define XMLIMPORT __attribute__((visibility("default")))
+# endif
/* If we didn't define it above, define it away: */
-#ifndef XMLIMPORT
-# define XMLIMPORT
-#endif
-
-#if defined(__GNUC__) \
- && (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 96))
-# define XML_ATTR_MALLOC __attribute__((__malloc__))
-#else
-# define XML_ATTR_MALLOC
-#endif
-
-#if defined(__GNUC__) \
- && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
-# define XML_ATTR_ALLOC_SIZE(x) __attribute__((__alloc_size__(x)))
-#else
-# define XML_ATTR_ALLOC_SIZE(x)
-#endif
-
-#define XMLPARSEAPI(type) XMLIMPORT type XMLCALL
-
-#ifdef __cplusplus
-extern "C" {
-#endif
+# ifndef XMLIMPORT
+# define XMLIMPORT
+# endif
+
+# if defined(__GNUC__) \
+ && (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 96))
+# define XML_ATTR_MALLOC __attribute__((__malloc__))
+# else
+# define XML_ATTR_MALLOC
+# endif
-#ifdef XML_UNICODE_WCHAR_T
-# ifndef XML_UNICODE
-# define XML_UNICODE
+# if defined(__GNUC__) \
+ && ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
+# define XML_ATTR_ALLOC_SIZE(x) __attribute__((__alloc_size__(x)))
+# else
+# define XML_ATTR_ALLOC_SIZE(x)
# endif
-# if defined(__SIZEOF_WCHAR_T__) && (__SIZEOF_WCHAR_T__ != 2)
-# error "sizeof(wchar_t) != 2; Need -fshort-wchar for both Expat and libc"
+
+# define XMLPARSEAPI(type) XMLIMPORT type XMLCALL
+
+# ifdef __cplusplus
+extern "C" {
# endif
-#endif
-#ifdef XML_UNICODE /* Information is UTF-16 encoded. */
# ifdef XML_UNICODE_WCHAR_T
+# ifndef XML_UNICODE
+# define XML_UNICODE
+# endif
+# if defined(__SIZEOF_WCHAR_T__) && (__SIZEOF_WCHAR_T__ != 2)
+# error "sizeof(wchar_t) != 2; Need -fshort-wchar for both Expat and libc"
+# endif
+# endif
+
+# ifdef XML_UNICODE /* Information is UTF-16 encoded. */
+# ifdef XML_UNICODE_WCHAR_T
typedef wchar_t XML_Char;
typedef wchar_t XML_LChar;
-# else
+# else
typedef unsigned short XML_Char;
typedef char XML_LChar;
-# endif /* XML_UNICODE_WCHAR_T */
-#else /* Information is UTF-8 encoded. */
+# endif /* XML_UNICODE_WCHAR_T */
+# else /* Information is UTF-8 encoded. */
typedef char XML_Char;
typedef char XML_LChar;
-#endif /* XML_UNICODE */
+# endif /* XML_UNICODE */
-#ifdef XML_LARGE_SIZE /* Use large integers for file/stream positions. */
+# ifdef XML_LARGE_SIZE /* Use large integers for file/stream positions. */
typedef long long XML_Index;
typedef unsigned long long XML_Size;
-#else
+# else
typedef long XML_Index;
typedef unsigned long XML_Size;
-#endif /* XML_LARGE_SIZE */
+# endif /* XML_LARGE_SIZE */
-#ifdef __cplusplus
+# ifdef __cplusplus
}
-#endif
+# endif
#endif /* not Expat_External_INCLUDED */
diff --git a/contrib/expat/lib/internal.h b/contrib/expat/lib/internal.h
index 6bde6ae6b31d..8f5edf48ef7c 100644
--- a/contrib/expat/lib/internal.h
+++ b/contrib/expat/lib/internal.h
@@ -108,6 +108,7 @@
#endif
#include <limits.h> // ULONG_MAX
+#include <stddef.h> // size_t
#if defined(_WIN32) \
&& (! defined(__USE_MINGW_ANSI_STDIO) \
@@ -148,6 +149,16 @@
100.0f
#define EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT \
8388608 // 8 MiB, 2^23
+
+#define EXPAT_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT 100.0f
+#define EXPAT_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT \
+ 67108864 // 64 MiB, 2^26
+
+// NOTE: If function expat_alloc was user facing, EXPAT_MALLOC_ALIGNMENT would
+// have to take sizeof(long double) into account
+#define EXPAT_MALLOC_ALIGNMENT sizeof(long long) // largest parser (sub)member
+#define EXPAT_MALLOC_PADDING ((EXPAT_MALLOC_ALIGNMENT) - sizeof(size_t))
+
/* NOTE END */
#include "expat.h" // so we can use type XML_Parser below
@@ -171,6 +182,9 @@ extern
#endif
XML_Bool g_reparseDeferralEnabledDefault; // written ONLY in runtests.c
#if defined(XML_TESTING)
+void *expat_malloc(XML_Parser parser, size_t size, int sourceLine);
+void expat_free(XML_Parser parser, void *ptr, int sourceLine);
+void *expat_realloc(XML_Parser parser, void *ptr, size_t size, int sourceLine);
extern unsigned int g_bytesScanned; // used for testing only
#endif
diff --git a/contrib/expat/lib/xmlparse.c b/contrib/expat/lib/xmlparse.c
index 38a2d9657b6a..a187a3a18f19 100644
--- a/contrib/expat/lib/xmlparse.c
+++ b/contrib/expat/lib/xmlparse.c
@@ -1,4 +1,4 @@
-/* d19ae032c224863c1527ba44d228cc34b99192c3a4c5a27af1f4e054d45ee031 (2.7.1+)
+/* 28bcd8b1ba7eb595d82822908257fd9c3589b4243e3c922d0369f35bfcd7b506 (2.7.3+)
__ __ _
___\ \/ /_ __ __ _| |_
/ _ \\ /| '_ \ / _` | __|
@@ -41,6 +41,7 @@
Copyright (c) 2023-2024 Sony Corporation / Snild Dolkow <snild@sony.com>
Copyright (c) 2024-2025 Berkay Eren Ürün <berkay.ueruen@siemens.com>
Copyright (c) 2024 Hanno Böck <hanno@gentoo.org>
+ Copyright (c) 2025 Matthew Fernandez <matthew.fernandez@gmail.com>
Licensed under the MIT license:
Permission is hereby granted, free of charge, to any person obtaining
@@ -97,7 +98,7 @@
#include <stddef.h>
#include <string.h> /* memset(), memcpy() */
#include <assert.h>
-#include <limits.h> /* UINT_MAX */
+#include <limits.h> /* INT_MAX, UINT_MAX */
#include <stdio.h> /* fprintf */
#include <stdlib.h> /* getenv, rand_s */
#include <stdint.h> /* uintptr_t */
@@ -234,7 +235,7 @@ typedef struct {
unsigned char power;
size_t size;
size_t used;
- const XML_Memory_Handling_Suite *mem;
+ XML_Parser parser;
} HASH_TABLE;
static size_t keylen(KEY s);
@@ -357,7 +358,7 @@ typedef struct {
const XML_Char *end;
XML_Char *ptr;
XML_Char *start;
- const XML_Memory_Handling_Suite *mem;
+ XML_Parser parser;
} STRING_POOL;
/* The XML_Char before the name is used to determine whether
@@ -452,6 +453,14 @@ typedef struct accounting {
unsigned long long activationThresholdBytes;
} ACCOUNTING;
+typedef struct MALLOC_TRACKER {
+ XmlBigCount bytesAllocated;
+ XmlBigCount peakBytesAllocated; // updated live only for debug level >=2
+ unsigned long debugLevel;
+ float maximumAmplificationFactor; // >=1.0
+ XmlBigCount activationThresholdBytes;
+} MALLOC_TRACKER;
+
typedef struct entity_stats {
unsigned int countEverOpened;
unsigned int currentDepth;
@@ -555,27 +564,24 @@ static XML_Bool setContext(XML_Parser parser, const XML_Char *context);
static void FASTCALL normalizePublicId(XML_Char *s);
-static DTD *dtdCreate(const XML_Memory_Handling_Suite *ms);
+static DTD *dtdCreate(XML_Parser parser);
/* do not call if m_parentParser != NULL */
-static void dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms);
-static void dtdDestroy(DTD *p, XML_Bool isDocEntity,
- const XML_Memory_Handling_Suite *ms);
+static void dtdReset(DTD *p, XML_Parser parser);
+static void dtdDestroy(DTD *p, XML_Bool isDocEntity, XML_Parser parser);
static int dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd,
- const XML_Memory_Handling_Suite *ms);
+ XML_Parser parser);
static int copyEntityTable(XML_Parser oldParser, HASH_TABLE *newTable,
STRING_POOL *newPool, const HASH_TABLE *oldTable);
static NAMED *lookup(XML_Parser parser, HASH_TABLE *table, KEY name,
size_t createSize);
-static void FASTCALL hashTableInit(HASH_TABLE *table,
- const XML_Memory_Handling_Suite *ms);
+static void FASTCALL hashTableInit(HASH_TABLE *table, XML_Parser parser);
static void FASTCALL hashTableClear(HASH_TABLE *table);
static void FASTCALL hashTableDestroy(HASH_TABLE *table);
static void FASTCALL hashTableIterInit(HASH_TABLE_ITER *iter,
const HASH_TABLE *table);
static NAMED *FASTCALL hashTableIterNext(HASH_TABLE_ITER *iter);
-static void FASTCALL poolInit(STRING_POOL *pool,
- const XML_Memory_Handling_Suite *ms);
+static void FASTCALL poolInit(STRING_POOL *pool, XML_Parser parser);
static void FASTCALL poolClear(STRING_POOL *pool);
static void FASTCALL poolDestroy(STRING_POOL *pool);
static XML_Char *poolAppend(STRING_POOL *pool, const ENCODING *enc,
@@ -595,15 +601,15 @@ static XML_Content *build_model(XML_Parser parser);
static ELEMENT_TYPE *getElementType(XML_Parser parser, const ENCODING *enc,
const char *ptr, const char *end);
-static XML_Char *copyString(const XML_Char *s,
- const XML_Memory_Handling_Suite *memsuite);
+static XML_Char *copyString(const XML_Char *s, XML_Parser parser);
static unsigned long generate_hash_secret_salt(XML_Parser parser);
static XML_Bool startParsing(XML_Parser parser);
static XML_Parser parserCreate(const XML_Char *encodingName,
const XML_Memory_Handling_Suite *memsuite,
- const XML_Char *nameSep, DTD *dtd);
+ const XML_Char *nameSep, DTD *dtd,
+ XML_Parser parentParser);
static void parserInit(XML_Parser parser, const XML_Char *encodingName);
@@ -627,10 +633,10 @@ static void entityTrackingOnOpen(XML_Parser parser, ENTITY *entity,
int sourceLine);
static void entityTrackingOnClose(XML_Parser parser, ENTITY *entity,
int sourceLine);
+#endif /* XML_GE == 1 */
static XML_Parser getRootParserOf(XML_Parser parser,
unsigned int *outLevelDiff);
-#endif /* XML_GE == 1 */
static unsigned long getDebugLevel(const char *variableName,
unsigned long defaultDebugLevel);
@@ -773,14 +779,238 @@ struct XML_ParserStruct {
unsigned long m_hash_secret_salt;
#if XML_GE == 1
ACCOUNTING m_accounting;
+ MALLOC_TRACKER m_alloc_tracker;
ENTITY_STATS m_entity_stats;
#endif
XML_Bool m_reenter;
};
-#define MALLOC(parser, s) (parser->m_mem.malloc_fcn((s)))
-#define REALLOC(parser, p, s) (parser->m_mem.realloc_fcn((p), (s)))
-#define FREE(parser, p) (parser->m_mem.free_fcn((p)))
+#if XML_GE == 1
+# define MALLOC(parser, s) (expat_malloc((parser), (s), __LINE__))
+# define REALLOC(parser, p, s) (expat_realloc((parser), (p), (s), __LINE__))
+# define FREE(parser, p) (expat_free((parser), (p), __LINE__))
+#else
+# define MALLOC(parser, s) (parser->m_mem.malloc_fcn((s)))
+# define REALLOC(parser, p, s) (parser->m_mem.realloc_fcn((p), (s)))
+# define FREE(parser, p) (parser->m_mem.free_fcn((p)))
+#endif
+
+#if XML_GE == 1
+static void
+expat_heap_stat(XML_Parser rootParser, char operator, XmlBigCount absDiff,
+ XmlBigCount newTotal, XmlBigCount peakTotal, int sourceLine) {
+ // NOTE: This can be +infinity or -nan
+ const float amplification
+ = (float)newTotal / (float)rootParser->m_accounting.countBytesDirect;
+ fprintf(
+ stderr,
+ "expat: Allocations(%p): Direct " EXPAT_FMT_ULL("10") ", allocated %c" EXPAT_FMT_ULL(
+ "10") " to " EXPAT_FMT_ULL("10") " (" EXPAT_FMT_ULL("10") " peak), amplification %8.2f (xmlparse.c:%d)\n",
+ (void *)rootParser, rootParser->m_accounting.countBytesDirect, operator,
+ absDiff, newTotal, peakTotal, (double)amplification, sourceLine);
+}
+
+static bool
+expat_heap_increase_tolerable(XML_Parser rootParser, XmlBigCount increase,
+ int sourceLine) {
+ assert(rootParser != NULL);
+ assert(increase > 0);
+
+ XmlBigCount newTotal = 0;
+ bool tolerable = true;
+
+ // Detect integer overflow
+ if ((XmlBigCount)-1 - rootParser->m_alloc_tracker.bytesAllocated < increase) {
+ tolerable = false;
+ } else {
+ newTotal = rootParser->m_alloc_tracker.bytesAllocated + increase;
+
+ if (newTotal >= rootParser->m_alloc_tracker.activationThresholdBytes) {
+ assert(newTotal > 0);
+ // NOTE: This can be +infinity when dividing by zero but not -nan
+ const float amplification
+ = (float)newTotal / (float)rootParser->m_accounting.countBytesDirect;
+ if (amplification
+ > rootParser->m_alloc_tracker.maximumAmplificationFactor) {
+ tolerable = false;
+ }
+ }
+ }
+
+ if (! tolerable && (rootParser->m_alloc_tracker.debugLevel >= 1)) {
+ expat_heap_stat(rootParser, '+', increase, newTotal, newTotal, sourceLine);
+ }
+
+ return tolerable;
+}
+
+# if defined(XML_TESTING)
+void *
+# else
+static void *
+# endif
+expat_malloc(XML_Parser parser, size_t size, int sourceLine) {
+ // Detect integer overflow
+ if (SIZE_MAX - size < sizeof(size_t) + EXPAT_MALLOC_PADDING) {
+ return NULL;
+ }
+
+ const XML_Parser rootParser = getRootParserOf(parser, NULL);
+ assert(rootParser->m_parentParser == NULL);
+
+ const size_t bytesToAllocate = sizeof(size_t) + EXPAT_MALLOC_PADDING + size;
+
+ if ((XmlBigCount)-1 - rootParser->m_alloc_tracker.bytesAllocated
+ < bytesToAllocate) {
+ return NULL; // i.e. signal integer overflow as out-of-memory
+ }
+
+ if (! expat_heap_increase_tolerable(rootParser, bytesToAllocate,
+ sourceLine)) {
+ return NULL; // i.e. signal violation as out-of-memory
+ }
+
+ // Actually allocate
+ void *const mallocedPtr = parser->m_mem.malloc_fcn(bytesToAllocate);
+
+ if (mallocedPtr == NULL) {
+ return NULL;
+ }
+
+ // Update in-block recorded size
+ *(size_t *)mallocedPtr = size;
+
+ // Update accounting
+ rootParser->m_alloc_tracker.bytesAllocated += bytesToAllocate;
+
+ // Report as needed
+ if (rootParser->m_alloc_tracker.debugLevel >= 2) {
+ if (rootParser->m_alloc_tracker.bytesAllocated
+ > rootParser->m_alloc_tracker.peakBytesAllocated) {
+ rootParser->m_alloc_tracker.peakBytesAllocated
+ = rootParser->m_alloc_tracker.bytesAllocated;
+ }
+ expat_heap_stat(rootParser, '+', bytesToAllocate,
+ rootParser->m_alloc_tracker.bytesAllocated,
+ rootParser->m_alloc_tracker.peakBytesAllocated, sourceLine);
+ }
+
+ return (char *)mallocedPtr + sizeof(size_t) + EXPAT_MALLOC_PADDING;
+}
+
+# if defined(XML_TESTING)
+void
+# else
+static void
+# endif
+expat_free(XML_Parser parser, void *ptr, int sourceLine) {
+ assert(parser != NULL);
+
+ if (ptr == NULL) {
+ return;
+ }
+
+ const XML_Parser rootParser = getRootParserOf(parser, NULL);
+ assert(rootParser->m_parentParser == NULL);
+
+ // Extract size (to the eyes of malloc_fcn/realloc_fcn) and
+ // the original pointer returned by malloc/realloc
+ void *const mallocedPtr = (char *)ptr - EXPAT_MALLOC_PADDING - sizeof(size_t);
+ const size_t bytesAllocated
+ = sizeof(size_t) + EXPAT_MALLOC_PADDING + *(size_t *)mallocedPtr;
+
+ // Update accounting
+ assert(rootParser->m_alloc_tracker.bytesAllocated >= bytesAllocated);
+ rootParser->m_alloc_tracker.bytesAllocated -= bytesAllocated;
+
+ // Report as needed
+ if (rootParser->m_alloc_tracker.debugLevel >= 2) {
+ expat_heap_stat(rootParser, '-', bytesAllocated,
+ rootParser->m_alloc_tracker.bytesAllocated,
+ rootParser->m_alloc_tracker.peakBytesAllocated, sourceLine);
+ }
+
+ // NOTE: This may be freeing rootParser, so freeing has to come last
+ parser->m_mem.free_fcn(mallocedPtr);
+}
+
+# if defined(XML_TESTING)
+void *
+# else
+static void *
+# endif
+expat_realloc(XML_Parser parser, void *ptr, size_t size, int sourceLine) {
+ assert(parser != NULL);
+
+ if (ptr == NULL) {
+ return expat_malloc(parser, size, sourceLine);
+ }
+
+ if (size == 0) {
+ expat_free(parser, ptr, sourceLine);
+ return NULL;
+ }
+
+ const XML_Parser rootParser = getRootParserOf(parser, NULL);
+ assert(rootParser->m_parentParser == NULL);
+
+ // Extract original size (to the eyes of the caller) and the original
+ // pointer returned by malloc/realloc
+ void *mallocedPtr = (char *)ptr - EXPAT_MALLOC_PADDING - sizeof(size_t);
+ const size_t prevSize = *(size_t *)mallocedPtr;
+
+ // Classify upcoming change
+ const bool isIncrease = (size > prevSize);
+ const size_t absDiff
+ = (size > prevSize) ? (size - prevSize) : (prevSize - size);
+
+ // Ask for permission from accounting
+ if (isIncrease) {
+ if (! expat_heap_increase_tolerable(rootParser, absDiff, sourceLine)) {
+ return NULL; // i.e. signal violation as out-of-memory
+ }
+ }
+
+ // NOTE: Integer overflow detection has already been done for us
+ // by expat_heap_increase_tolerable(..) above
+ assert(SIZE_MAX - sizeof(size_t) - EXPAT_MALLOC_PADDING >= size);
+
+ // Actually allocate
+ mallocedPtr = parser->m_mem.realloc_fcn(
+ mallocedPtr, sizeof(size_t) + EXPAT_MALLOC_PADDING + size);
+
+ if (mallocedPtr == NULL) {
+ return NULL;
+ }
+
+ // Update accounting
+ if (isIncrease) {
+ assert((XmlBigCount)-1 - rootParser->m_alloc_tracker.bytesAllocated
+ >= absDiff);
+ rootParser->m_alloc_tracker.bytesAllocated += absDiff;
+ } else { // i.e. decrease
+ assert(rootParser->m_alloc_tracker.bytesAllocated >= absDiff);
+ rootParser->m_alloc_tracker.bytesAllocated -= absDiff;
+ }
+
+ // Report as needed
+ if (rootParser->m_alloc_tracker.debugLevel >= 2) {
+ if (rootParser->m_alloc_tracker.bytesAllocated
+ > rootParser->m_alloc_tracker.peakBytesAllocated) {
+ rootParser->m_alloc_tracker.peakBytesAllocated
+ = rootParser->m_alloc_tracker.bytesAllocated;
+ }
+ expat_heap_stat(rootParser, isIncrease ? '+' : '-', absDiff,
+ rootParser->m_alloc_tracker.bytesAllocated,
+ rootParser->m_alloc_tracker.peakBytesAllocated, sourceLine);
+ }
+
+ // Update in-block recorded size
+ *(size_t *)mallocedPtr = size;
+
+ return (char *)mallocedPtr + sizeof(size_t) + EXPAT_MALLOC_PADDING;
+}
+#endif // XML_GE == 1
XML_Parser XMLCALL
XML_ParserCreate(const XML_Char *encodingName) {
@@ -821,11 +1051,14 @@ writeRandomBytes_getrandom_nonblock(void *target, size_t count) {
void *const currentTarget = (void *)((char *)target + bytesWrittenTotal);
const size_t bytesToWrite = count - bytesWrittenTotal;
+ assert(bytesToWrite <= INT_MAX);
+
const int bytesWrittenMore =
# if defined(HAVE_GETRANDOM)
- getrandom(currentTarget, bytesToWrite, getrandomFlags);
+ (int)getrandom(currentTarget, bytesToWrite, getrandomFlags);
# else
- syscall(SYS_getrandom, currentTarget, bytesToWrite, getrandomFlags);
+ (int)syscall(SYS_getrandom, currentTarget, bytesToWrite,
+ getrandomFlags);
# endif
if (bytesWrittenMore > 0) {
@@ -1012,9 +1245,10 @@ generate_hash_secret_salt(XML_Parser parser) {
static unsigned long
get_hash_secret_salt(XML_Parser parser) {
- if (parser->m_parentParser != NULL)
- return get_hash_secret_salt(parser->m_parentParser);
- return parser->m_hash_secret_salt;
+ const XML_Parser rootParser = getRootParserOf(parser, NULL);
+ assert(! rootParser->m_parentParser);
+
+ return rootParser->m_hash_secret_salt;
}
static enum XML_Error
@@ -1100,19 +1334,43 @@ XML_Parser XMLCALL
XML_ParserCreate_MM(const XML_Char *encodingName,
const XML_Memory_Handling_Suite *memsuite,
const XML_Char *nameSep) {
- return parserCreate(encodingName, memsuite, nameSep, NULL);
+ return parserCreate(encodingName, memsuite, nameSep, NULL, NULL);
}
static XML_Parser
parserCreate(const XML_Char *encodingName,
const XML_Memory_Handling_Suite *memsuite, const XML_Char *nameSep,
- DTD *dtd) {
- XML_Parser parser;
+ DTD *dtd, XML_Parser parentParser) {
+ XML_Parser parser = NULL;
+
+#if XML_GE == 1
+ const size_t increase
+ = sizeof(size_t) + EXPAT_MALLOC_PADDING + sizeof(struct XML_ParserStruct);
+
+ if (parentParser != NULL) {
+ const XML_Parser rootParser = getRootParserOf(parentParser, NULL);
+ if (! expat_heap_increase_tolerable(rootParser, increase, __LINE__)) {
+ return NULL;
+ }
+ }
+#else
+ UNUSED_P(parentParser);
+#endif
if (memsuite) {
XML_Memory_Handling_Suite *mtemp;
+#if XML_GE == 1
+ void *const sizeAndParser
+ = memsuite->malloc_fcn(sizeof(size_t) + EXPAT_MALLOC_PADDING
+ + sizeof(struct XML_ParserStruct));
+ if (sizeAndParser != NULL) {
+ *(size_t *)sizeAndParser = sizeof(struct XML_ParserStruct);
+ parser = (XML_Parser)((char *)sizeAndParser + sizeof(size_t)
+ + EXPAT_MALLOC_PADDING);
+#else
parser = memsuite->malloc_fcn(sizeof(struct XML_ParserStruct));
if (parser != NULL) {
+#endif
mtemp = (XML_Memory_Handling_Suite *)&(parser->m_mem);
mtemp->malloc_fcn = memsuite->malloc_fcn;
mtemp->realloc_fcn = memsuite->realloc_fcn;
@@ -1120,39 +1378,86 @@ parserCreate(const XML_Char *encodingName,
}
} else {
XML_Memory_Handling_Suite *mtemp;
- parser = (XML_Parser)malloc(sizeof(struct XML_ParserStruct));
+#if XML_GE == 1
+ void *const sizeAndParser = malloc(sizeof(size_t) + EXPAT_MALLOC_PADDING
+ + sizeof(struct XML_ParserStruct));
+ if (sizeAndParser != NULL) {
+ *(size_t *)sizeAndParser = sizeof(struct XML_ParserStruct);
+ parser = (XML_Parser)((char *)sizeAndParser + sizeof(size_t)
+ + EXPAT_MALLOC_PADDING);
+#else
+ parser = malloc(sizeof(struct XML_ParserStruct));
if (parser != NULL) {
+#endif
mtemp = (XML_Memory_Handling_Suite *)&(parser->m_mem);
mtemp->malloc_fcn = malloc;
mtemp->realloc_fcn = realloc;
mtemp->free_fcn = free;
}
- }
+ } // cppcheck-suppress[memleak symbolName=sizeAndParser] // Cppcheck >=2.18.0
if (! parser)
return parser;
+#if XML_GE == 1
+ // Initialize .m_alloc_tracker
+ memset(&parser->m_alloc_tracker, 0, sizeof(MALLOC_TRACKER));
+ if (parentParser == NULL) {
+ parser->m_alloc_tracker.debugLevel
+ = getDebugLevel("EXPAT_MALLOC_DEBUG", 0u);
+ parser->m_alloc_tracker.maximumAmplificationFactor
+ = EXPAT_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT;
+ parser->m_alloc_tracker.activationThresholdBytes
+ = EXPAT_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT;
+
+ // NOTE: This initialization needs to come this early because these fields
+ // are read by allocation tracking code
+ parser->m_parentParser = NULL;
+ parser->m_accounting.countBytesDirect = 0;
+ } else {
+ parser->m_parentParser = parentParser;
+ }
+
+ // Record XML_ParserStruct allocation we did a few lines up before
+ const XML_Parser rootParser = getRootParserOf(parser, NULL);
+ assert(rootParser->m_parentParser == NULL);
+ assert(SIZE_MAX - rootParser->m_alloc_tracker.bytesAllocated >= increase);
+ rootParser->m_alloc_tracker.bytesAllocated += increase;
+
+ // Report on allocation
+ if (rootParser->m_alloc_tracker.debugLevel >= 2) {
+ if (rootParser->m_alloc_tracker.bytesAllocated
+ > rootParser->m_alloc_tracker.peakBytesAllocated) {
+ rootParser->m_alloc_tracker.peakBytesAllocated
+ = rootParser->m_alloc_tracker.bytesAllocated;
+ }
+
+ expat_heap_stat(rootParser, '+', increase,
+ rootParser->m_alloc_tracker.bytesAllocated,
+ rootParser->m_alloc_tracker.peakBytesAllocated, __LINE__);
+ }
+#else
+ parser->m_parentParser = NULL;
+#endif // XML_GE == 1
+
parser->m_buffer = NULL;
parser->m_bufferLim = NULL;
parser->m_attsSize = INIT_ATTS_SIZE;
- parser->m_atts
- = (ATTRIBUTE *)MALLOC(parser, parser->m_attsSize * sizeof(ATTRIBUTE));
+ parser->m_atts = MALLOC(parser, parser->m_attsSize * sizeof(ATTRIBUTE));
if (parser->m_atts == NULL) {
FREE(parser, parser);
return NULL;
}
#ifdef XML_ATTR_INFO
- parser->m_attInfo = (XML_AttrInfo *)MALLOC(
- parser, parser->m_attsSize * sizeof(XML_AttrInfo));
+ parser->m_attInfo = MALLOC(parser, parser->m_attsSize * sizeof(XML_AttrInfo));
if (parser->m_attInfo == NULL) {
FREE(parser, parser->m_atts);
FREE(parser, parser);
return NULL;
}
#endif
- parser->m_dataBuf
- = (XML_Char *)MALLOC(parser, INIT_DATA_BUF_SIZE * sizeof(XML_Char));
+ parser->m_dataBuf = MALLOC(parser, INIT_DATA_BUF_SIZE * sizeof(XML_Char));
if (parser->m_dataBuf == NULL) {
FREE(parser, parser->m_atts);
#ifdef XML_ATTR_INFO
@@ -1166,7 +1471,7 @@ parserCreate(const XML_Char *encodingName,
if (dtd)
parser->m_dtd = dtd;
else {
- parser->m_dtd = dtdCreate(&parser->m_mem);
+ parser->m_dtd = dtdCreate(parser);
if (parser->m_dtd == NULL) {
FREE(parser, parser->m_dataBuf);
FREE(parser, parser->m_atts);
@@ -1200,8 +1505,8 @@ parserCreate(const XML_Char *encodingName,
parser->m_protocolEncodingName = NULL;
- poolInit(&parser->m_tempPool, &(parser->m_mem));
- poolInit(&parser->m_temp2Pool, &(parser->m_mem));
+ poolInit(&parser->m_tempPool, parser);
+ poolInit(&parser->m_temp2Pool, parser);
parserInit(parser, encodingName);
if (encodingName && ! parser->m_protocolEncodingName) {
@@ -1233,7 +1538,7 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) {
parser->m_processor = prologInitProcessor;
XmlPrologStateInit(&parser->m_prologState);
if (encodingName != NULL) {
- parser->m_protocolEncodingName = copyString(encodingName, &(parser->m_mem));
+ parser->m_protocolEncodingName = copyString(encodingName, parser);
}
parser->m_curBase = NULL;
XmlInitEncoding(&parser->m_initEncoding, &parser->m_encoding, 0);
@@ -1295,7 +1600,6 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) {
parser->m_unknownEncodingMem = NULL;
parser->m_unknownEncodingRelease = NULL;
parser->m_unknownEncodingData = NULL;
- parser->m_parentParser = NULL;
parser->m_parsingStatus.parsing = XML_INITIALIZED;
// Reentry can only be triggered inside m_processor calls
parser->m_reenter = XML_FALSE;
@@ -1385,7 +1689,7 @@ XML_ParserReset(XML_Parser parser, const XML_Char *encodingName) {
FREE(parser, (void *)parser->m_protocolEncodingName);
parser->m_protocolEncodingName = NULL;
parserInit(parser, encodingName);
- dtdReset(parser->m_dtd, &parser->m_mem);
+ dtdReset(parser->m_dtd, parser);
return XML_TRUE;
}
@@ -1421,7 +1725,7 @@ XML_SetEncoding(XML_Parser parser, const XML_Char *encodingName) {
parser->m_protocolEncodingName = NULL;
else {
/* Copy the new encoding name into allocated memory */
- parser->m_protocolEncodingName = copyString(encodingName, &(parser->m_mem));
+ parser->m_protocolEncodingName = copyString(encodingName, parser);
if (! parser->m_protocolEncodingName)
return XML_STATUS_ERROR;
}
@@ -1530,9 +1834,10 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, const XML_Char *context,
*/
if (parser->m_ns) {
XML_Char tmp[2] = {parser->m_namespaceSeparator, 0};
- parser = parserCreate(encodingName, &parser->m_mem, tmp, newDtd);
+ parser = parserCreate(encodingName, &parser->m_mem, tmp, newDtd, oldParser);
} else {
- parser = parserCreate(encodingName, &parser->m_mem, NULL, newDtd);
+ parser
+ = parserCreate(encodingName, &parser->m_mem, NULL, newDtd, oldParser);
}
if (! parser)
@@ -1576,7 +1881,7 @@ XML_ExternalEntityParserCreate(XML_Parser oldParser, const XML_Char *context,
parser->m_prologState.inEntityValue = oldInEntityValue;
if (context) {
#endif /* XML_DTD */
- if (! dtdCopy(oldParser, parser->m_dtd, oldDtd, &parser->m_mem)
+ if (! dtdCopy(oldParser, parser->m_dtd, oldDtd, parser)
|| ! setContext(parser, context)) {
XML_ParserFree(parser);
return NULL;
@@ -1688,14 +1993,16 @@ XML_ParserFree(XML_Parser parser) {
#else
if (parser->m_dtd)
#endif /* XML_DTD */
- dtdDestroy(parser->m_dtd, (XML_Bool)! parser->m_parentParser,
- &parser->m_mem);
- FREE(parser, (void *)parser->m_atts);
+ dtdDestroy(parser->m_dtd, (XML_Bool)! parser->m_parentParser, parser);
+ FREE(parser, parser->m_atts);
#ifdef XML_ATTR_INFO
- FREE(parser, (void *)parser->m_attInfo);
+ FREE(parser, parser->m_attInfo);
#endif
FREE(parser, parser->m_groupConnector);
- FREE(parser, parser->m_buffer);
+ // NOTE: We are avoiding FREE(..) here because parser->m_buffer
+ // is not being allocated with MALLOC(..) but with plain
+ // .malloc_fcn(..).
+ parser->m_mem.free_fcn(parser->m_buffer);
FREE(parser, parser->m_dataBuf);
FREE(parser, parser->m_nsAtts);
FREE(parser, parser->m_unknownEncodingMem);
@@ -2014,12 +2321,14 @@ int XMLCALL
XML_SetHashSalt(XML_Parser parser, unsigned long hash_salt) {
if (parser == NULL)
return 0;
- if (parser->m_parentParser)
- return XML_SetHashSalt(parser->m_parentParser, hash_salt);
+
+ const XML_Parser rootParser = getRootParserOf(parser, NULL);
+ assert(! rootParser->m_parentParser);
+
/* block after XML_Parse()/XML_ParseBuffer() has been called */
- if (parserBusy(parser))
+ if (parserBusy(rootParser))
return 0;
- parser->m_hash_secret_salt = hash_salt;
+ rootParser->m_hash_secret_salt = hash_salt;
return 1;
}
@@ -2287,7 +2596,9 @@ XML_GetBuffer(XML_Parser parser, int len) {
parser->m_errorCode = XML_ERROR_NO_MEMORY;
return NULL;
}
- newBuf = (char *)MALLOC(parser, bufferSize);
+ // NOTE: We are avoiding MALLOC(..) here to leave limiting
+ // the input size to the application using Expat.
+ newBuf = parser->m_mem.malloc_fcn(bufferSize);
if (newBuf == 0) {
parser->m_errorCode = XML_ERROR_NO_MEMORY;
return NULL;
@@ -2298,7 +2609,10 @@ XML_GetBuffer(XML_Parser parser, int len) {
memcpy(newBuf, &parser->m_bufferPtr[-keep],
EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr)
+ keep);
- FREE(parser, parser->m_buffer);
+ // NOTE: We are avoiding FREE(..) here because parser->m_buffer
+ // is not being allocated with MALLOC(..) but with plain
+ // .malloc_fcn(..).
+ parser->m_mem.free_fcn(parser->m_buffer);
parser->m_buffer = newBuf;
parser->m_bufferEnd
= parser->m_buffer
@@ -2314,7 +2628,10 @@ XML_GetBuffer(XML_Parser parser, int len) {
if (parser->m_bufferPtr) {
memcpy(newBuf, parser->m_bufferPtr,
EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr));
- FREE(parser, parser->m_buffer);
+ // NOTE: We are avoiding FREE(..) here because parser->m_buffer
+ // is not being allocated with MALLOC(..) but with plain
+ // .malloc_fcn(..).
+ parser->m_mem.free_fcn(parser->m_buffer);
parser->m_bufferEnd
= newBuf
+ EXPAT_SAFE_PTR_DIFF(parser->m_bufferEnd, parser->m_bufferPtr);
@@ -2492,28 +2809,43 @@ XML_GetCurrentColumnNumber(XML_Parser parser) {
void XMLCALL
XML_FreeContentModel(XML_Parser parser, XML_Content *model) {
- if (parser != NULL)
- FREE(parser, model);
+ if (parser == NULL)
+ return;
+
+ // NOTE: We are avoiding FREE(..) here because the content model
+ // has been created using plain .malloc_fcn(..) rather than MALLOC(..).
+ parser->m_mem.free_fcn(model);
}
void *XMLCALL
XML_MemMalloc(XML_Parser parser, size_t size) {
if (parser == NULL)
return NULL;
- return MALLOC(parser, size);
+
+ // NOTE: We are avoiding MALLOC(..) here to not include
+ // user allocations with allocation tracking and limiting.
+ return parser->m_mem.malloc_fcn(size);
}
void *XMLCALL
XML_MemRealloc(XML_Parser parser, void *ptr, size_t size) {
if (parser == NULL)
return NULL;
- return REALLOC(parser, ptr, size);
+
+ // NOTE: We are avoiding REALLOC(..) here to not include
+ // user allocations with allocation tracking and limiting.
+ return parser->m_mem.realloc_fcn(ptr, size);
}
void XMLCALL
XML_MemFree(XML_Parser parser, void *ptr) {
- if (parser != NULL)
- FREE(parser, ptr);
+ if (parser == NULL)
+ return;
+
+ // NOTE: We are avoiding FREE(..) here because XML_MemMalloc and
+ // XML_MemRealloc are not using MALLOC(..) and REALLOC(..)
+ // but plain .malloc_fcn(..) and .realloc_fcn(..), internally.
+ parser->m_mem.free_fcn(ptr);
}
void XMLCALL
@@ -2713,6 +3045,13 @@ XML_GetFeatureList(void) {
EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT},
/* Added in Expat 2.6.0. */
{XML_FEATURE_GE, XML_L("XML_GE"), 0},
+ /* Added in Expat 2.7.2. */
+ {XML_FEATURE_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT,
+ XML_L("XML_AT_MAX_AMP"),
+ (long int)EXPAT_ALLOC_TRACKER_MAXIMUM_AMPLIFICATION_DEFAULT},
+ {XML_FEATURE_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT,
+ XML_L("XML_AT_ACT_THRES"),
+ (long int)EXPAT_ALLOC_TRACKER_ACTIVATION_THRESHOLD_DEFAULT},
#endif
{XML_FEATURE_END, NULL, 0}};
@@ -2741,6 +3080,29 @@ XML_SetBillionLaughsAttackProtectionActivationThreshold(
parser->m_accounting.activationThresholdBytes = activationThresholdBytes;
return XML_TRUE;
}
+
+XML_Bool XMLCALL
+XML_SetAllocTrackerMaximumAmplification(XML_Parser parser,
+ float maximumAmplificationFactor) {
+ if ((parser == NULL) || (parser->m_parentParser != NULL)
+ || isnan(maximumAmplificationFactor)
+ || (maximumAmplificationFactor < 1.0f)) {
+ return XML_FALSE;
+ }
+ parser->m_alloc_tracker.maximumAmplificationFactor
+ = maximumAmplificationFactor;
+ return XML_TRUE;
+}
+
+XML_Bool XMLCALL
+XML_SetAllocTrackerActivationThreshold(
+ XML_Parser parser, unsigned long long activationThresholdBytes) {
+ if ((parser == NULL) || (parser->m_parentParser != NULL)) {
+ return XML_FALSE;
+ }
+ parser->m_alloc_tracker.activationThresholdBytes = activationThresholdBytes;
+ return XML_TRUE;
+}
#endif /* XML_GE == 1 */
XML_Bool XMLCALL
@@ -2761,8 +3123,8 @@ static XML_Bool
storeRawNames(XML_Parser parser) {
TAG *tag = parser->m_tagStack;
while (tag) {
- int bufSize;
- int nameLen = sizeof(XML_Char) * (tag->name.strLen + 1);
+ size_t bufSize;
+ size_t nameLen = sizeof(XML_Char) * (tag->name.strLen + 1);
size_t rawNameLen;
char *rawNameBuf = tag->buf + nameLen;
/* Stop if already stored. Since m_tagStack is a stack, we can stop
@@ -2779,9 +3141,9 @@ storeRawNames(XML_Parser parser) {
/* Detect and prevent integer overflow. */
if (rawNameLen > (size_t)INT_MAX - nameLen)
return XML_FALSE;
- bufSize = nameLen + (int)rawNameLen;
- if (bufSize > tag->bufEnd - tag->buf) {
- char *temp = (char *)REALLOC(parser, tag->buf, bufSize);
+ bufSize = nameLen + rawNameLen;
+ if (bufSize > (size_t)(tag->bufEnd - tag->buf)) {
+ char *temp = REALLOC(parser, tag->buf, bufSize);
if (temp == NULL)
return XML_FALSE;
/* if tag->name.str points to tag->buf (only when namespace
@@ -3107,10 +3469,10 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
tag = parser->m_freeTagList;
parser->m_freeTagList = parser->m_freeTagList->parent;
} else {
- tag = (TAG *)MALLOC(parser, sizeof(TAG));
+ tag = MALLOC(parser, sizeof(TAG));
if (! tag)
return XML_ERROR_NO_MEMORY;
- tag->buf = (char *)MALLOC(parser, INIT_TAG_BUF_SIZE);
+ tag->buf = MALLOC(parser, INIT_TAG_BUF_SIZE);
if (! tag->buf) {
FREE(parser, tag);
return XML_ERROR_NO_MEMORY;
@@ -3143,7 +3505,7 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
}
bufSize = (int)(tag->bufEnd - tag->buf) << 1;
{
- char *temp = (char *)REALLOC(parser, tag->buf, bufSize);
+ char *temp = REALLOC(parser, tag->buf, bufSize);
if (temp == NULL)
return XML_ERROR_NO_MEMORY;
tag->buf = temp;
@@ -3522,8 +3884,8 @@ storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr,
}
#endif
- temp = (ATTRIBUTE *)REALLOC(parser, (void *)parser->m_atts,
- parser->m_attsSize * sizeof(ATTRIBUTE));
+ temp = REALLOC(parser, parser->m_atts,
+ parser->m_attsSize * sizeof(ATTRIBUTE));
if (temp == NULL) {
parser->m_attsSize = oldAttsSize;
return XML_ERROR_NO_MEMORY;
@@ -3541,8 +3903,8 @@ storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr,
}
# endif
- temp2 = (XML_AttrInfo *)REALLOC(parser, (void *)parser->m_attInfo,
- parser->m_attsSize * sizeof(XML_AttrInfo));
+ temp2 = REALLOC(parser, parser->m_attInfo,
+ parser->m_attsSize * sizeof(XML_AttrInfo));
if (temp2 == NULL) {
parser->m_attsSize = oldAttsSize;
return XML_ERROR_NO_MEMORY;
@@ -3677,7 +4039,7 @@ storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr,
and clear flags that say whether attributes were specified */
i = 0;
if (nPrefixes) {
- int j; /* hash table index */
+ unsigned int j; /* hash table index */
unsigned long version = parser->m_nsAttsVersion;
/* Detect and prevent invalid shift */
@@ -3718,8 +4080,7 @@ storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr,
}
#endif
- temp = (NS_ATT *)REALLOC(parser, parser->m_nsAtts,
- nsAttsSize * sizeof(NS_ATT));
+ temp = REALLOC(parser, parser->m_nsAtts, nsAttsSize * sizeof(NS_ATT));
if (! temp) {
/* Restore actual size of memory in m_nsAtts */
parser->m_nsAttsPower = oldNsAttsPower;
@@ -3772,7 +4133,7 @@ storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr,
if (! b)
return XML_ERROR_UNBOUND_PREFIX;
- for (j = 0; j < b->uriLen; j++) {
+ for (j = 0; j < (unsigned int)b->uriLen; j++) {
const XML_Char c = b->uri[j];
if (! poolAppendChar(&parser->m_tempPool, c))
return XML_ERROR_NO_MEMORY;
@@ -3866,7 +4227,7 @@ storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr,
return XML_ERROR_NONE;
prefixLen = 0;
if (parser->m_ns_triplets && binding->prefix->name) {
- for (; binding->prefix->name[prefixLen++];)
+ while (binding->prefix->name[prefixLen++])
; /* prefixLen includes null terminator */
}
tagNamePtr->localPart = localPart;
@@ -3900,7 +4261,7 @@ storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr,
}
#endif
- uri = (XML_Char *)MALLOC(parser, (n + EXPAND_SPARE) * sizeof(XML_Char));
+ uri = MALLOC(parser, (n + EXPAND_SPARE) * sizeof(XML_Char));
if (! uri)
return XML_ERROR_NO_MEMORY;
binding->uriAlloc = n + EXPAND_SPARE;
@@ -4146,8 +4507,8 @@ addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId,
}
#endif
- XML_Char *temp = (XML_Char *)REALLOC(
- parser, b->uri, sizeof(XML_Char) * (len + EXPAND_SPARE));
+ XML_Char *temp
+ = REALLOC(parser, b->uri, sizeof(XML_Char) * (len + EXPAND_SPARE));
if (temp == NULL)
return XML_ERROR_NO_MEMORY;
b->uri = temp;
@@ -4155,7 +4516,7 @@ addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId,
}
parser->m_freeBindingList = b->nextTagBinding;
} else {
- b = (BINDING *)MALLOC(parser, sizeof(BINDING));
+ b = MALLOC(parser, sizeof(BINDING));
if (! b)
return XML_ERROR_NO_MEMORY;
@@ -4173,8 +4534,7 @@ addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId,
}
#endif
- b->uri
- = (XML_Char *)MALLOC(parser, sizeof(XML_Char) * (len + EXPAND_SPARE));
+ b->uri = MALLOC(parser, sizeof(XML_Char) * (len + EXPAND_SPARE));
if (! b->uri) {
FREE(parser, b);
return XML_ERROR_NO_MEMORY;
@@ -5545,7 +5905,7 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
return XML_ERROR_NO_MEMORY;
}
- char *const new_connector = (char *)REALLOC(
+ char *const new_connector = REALLOC(
parser, parser->m_groupConnector, parser->m_groupSize *= 2);
if (new_connector == NULL) {
parser->m_groupSize /= 2;
@@ -5565,15 +5925,14 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
}
#endif
- int *const new_scaff_index = (int *)REALLOC(
+ int *const new_scaff_index = REALLOC(
parser, dtd->scaffIndex, parser->m_groupSize * sizeof(int));
if (new_scaff_index == NULL)
return XML_ERROR_NO_MEMORY;
dtd->scaffIndex = new_scaff_index;
}
} else {
- parser->m_groupConnector
- = (char *)MALLOC(parser, parser->m_groupSize = 32);
+ parser->m_groupConnector = MALLOC(parser, parser->m_groupSize = 32);
if (! parser->m_groupConnector) {
parser->m_groupSize = 0;
return XML_ERROR_NO_MEMORY;
@@ -5730,8 +6089,11 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
case XML_ROLE_CONTENT_EMPTY:
if (dtd->in_eldecl) {
if (parser->m_elementDeclHandler) {
- XML_Content *content
- = (XML_Content *)MALLOC(parser, sizeof(XML_Content));
+ // NOTE: We are avoiding MALLOC(..) here to so that
+ // applications that are not using XML_FreeContentModel but
+ // plain free(..) or .free_fcn() to free the content model's
+ // memory are safe.
+ XML_Content *content = parser->m_mem.malloc_fcn(sizeof(XML_Content));
if (! content)
return XML_ERROR_NO_MEMORY;
content->quant = XML_CQUANT_NONE;
@@ -5787,7 +6149,7 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
name = el->name;
dtd->scaffold[myindex].name = name;
nameLen = 0;
- for (; name[nameLen++];)
+ while (name[nameLen++])
;
/* Detect and prevent integer overflow */
@@ -6008,8 +6370,7 @@ processEntity(XML_Parser parser, ENTITY *entity, XML_Bool betweenDecl,
openEntity = *freeEntityList;
*freeEntityList = openEntity->next;
} else {
- openEntity
- = (OPEN_INTERNAL_ENTITY *)MALLOC(parser, sizeof(OPEN_INTERNAL_ENTITY));
+ openEntity = MALLOC(parser, sizeof(OPEN_INTERNAL_ENTITY));
if (! openEntity)
return XML_ERROR_NO_MEMORY;
}
@@ -6087,6 +6448,10 @@ internalEntityProcessor(XML_Parser parser, const char *s, const char *end,
// process its possible inner entities (which are added to the
// m_openInternalEntities during doProlog or doContent calls above)
entity->hasMore = XML_FALSE;
+ if (! entity->is_param
+ && (openEntity->startTagLevel != parser->m_tagLevel)) {
+ return XML_ERROR_ASYNC_ENTITY;
+ }
triggerReenter(parser);
return result;
} // End of entity processing, "if" block will return here
@@ -6277,7 +6642,7 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
case XML_TOK_ENTITY_REF: {
const XML_Char *name;
ENTITY *entity;
- char checkEntityDecl;
+ bool checkEntityDecl;
XML_Char ch = (XML_Char)XmlPredefinedEntityName(
enc, ptr + enc->minBytesPerChar, next - enc->minBytesPerChar);
if (ch) {
@@ -6804,8 +7169,8 @@ defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *attId, XML_Bool isCdata,
if (type->nDefaultAtts == type->allocDefaultAtts) {
if (type->allocDefaultAtts == 0) {
type->allocDefaultAtts = 8;
- type->defaultAtts = (DEFAULT_ATTRIBUTE *)MALLOC(
- parser, type->allocDefaultAtts * sizeof(DEFAULT_ATTRIBUTE));
+ type->defaultAtts
+ = MALLOC(parser, type->allocDefaultAtts * sizeof(DEFAULT_ATTRIBUTE));
if (! type->defaultAtts) {
type->allocDefaultAtts = 0;
return 0;
@@ -6830,8 +7195,8 @@ defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *attId, XML_Bool isCdata,
}
#endif
- temp = (DEFAULT_ATTRIBUTE *)REALLOC(parser, type->defaultAtts,
- (count * sizeof(DEFAULT_ATTRIBUTE)));
+ temp = REALLOC(parser, type->defaultAtts,
+ (count * sizeof(DEFAULT_ATTRIBUTE)));
if (temp == NULL)
return 0;
type->allocDefaultAtts = count;
@@ -7122,19 +7487,19 @@ normalizePublicId(XML_Char *publicId) {
}
static DTD *
-dtdCreate(const XML_Memory_Handling_Suite *ms) {
- DTD *p = ms->malloc_fcn(sizeof(DTD));
+dtdCreate(XML_Parser parser) {
+ DTD *p = MALLOC(parser, sizeof(DTD));
if (p == NULL)
return p;
- poolInit(&(p->pool), ms);
- poolInit(&(p->entityValuePool), ms);
- hashTableInit(&(p->generalEntities), ms);
- hashTableInit(&(p->elementTypes), ms);
- hashTableInit(&(p->attributeIds), ms);
- hashTableInit(&(p->prefixes), ms);
+ poolInit(&(p->pool), parser);
+ poolInit(&(p->entityValuePool), parser);
+ hashTableInit(&(p->generalEntities), parser);
+ hashTableInit(&(p->elementTypes), parser);
+ hashTableInit(&(p->attributeIds), parser);
+ hashTableInit(&(p->prefixes), parser);
#ifdef XML_DTD
p->paramEntityRead = XML_FALSE;
- hashTableInit(&(p->paramEntities), ms);
+ hashTableInit(&(p->paramEntities), parser);
#endif /* XML_DTD */
p->defaultPrefix.name = NULL;
p->defaultPrefix.binding = NULL;
@@ -7154,7 +7519,7 @@ dtdCreate(const XML_Memory_Handling_Suite *ms) {
}
static void
-dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms) {
+dtdReset(DTD *p, XML_Parser parser) {
HASH_TABLE_ITER iter;
hashTableIterInit(&iter, &(p->elementTypes));
for (;;) {
@@ -7162,7 +7527,7 @@ dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms) {
if (! e)
break;
if (e->allocDefaultAtts != 0)
- ms->free_fcn(e->defaultAtts);
+ FREE(parser, e->defaultAtts);
}
hashTableClear(&(p->generalEntities));
#ifdef XML_DTD
@@ -7179,9 +7544,9 @@ dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms) {
p->in_eldecl = XML_FALSE;
- ms->free_fcn(p->scaffIndex);
+ FREE(parser, p->scaffIndex);
p->scaffIndex = NULL;
- ms->free_fcn(p->scaffold);
+ FREE(parser, p->scaffold);
p->scaffold = NULL;
p->scaffLevel = 0;
@@ -7195,7 +7560,7 @@ dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms) {
}
static void
-dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms) {
+dtdDestroy(DTD *p, XML_Bool isDocEntity, XML_Parser parser) {
HASH_TABLE_ITER iter;
hashTableIterInit(&iter, &(p->elementTypes));
for (;;) {
@@ -7203,7 +7568,7 @@ dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms) {
if (! e)
break;
if (e->allocDefaultAtts != 0)
- ms->free_fcn(e->defaultAtts);
+ FREE(parser, e->defaultAtts);
}
hashTableDestroy(&(p->generalEntities));
#ifdef XML_DTD
@@ -7215,10 +7580,10 @@ dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms) {
poolDestroy(&(p->pool));
poolDestroy(&(p->entityValuePool));
if (isDocEntity) {
- ms->free_fcn(p->scaffIndex);
- ms->free_fcn(p->scaffold);
+ FREE(parser, p->scaffIndex);
+ FREE(parser, p->scaffold);
}
- ms->free_fcn(p);
+ FREE(parser, p);
}
/* Do a deep copy of the DTD. Return 0 for out of memory, non-zero otherwise.
@@ -7226,7 +7591,7 @@ dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms) {
*/
static int
dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd,
- const XML_Memory_Handling_Suite *ms) {
+ XML_Parser parser) {
HASH_TABLE_ITER iter;
/* Copy the prefix table. */
@@ -7307,7 +7672,7 @@ dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd,
}
#endif
newE->defaultAtts
- = ms->malloc_fcn(oldE->nDefaultAtts * sizeof(DEFAULT_ATTRIBUTE));
+ = MALLOC(parser, oldE->nDefaultAtts * sizeof(DEFAULT_ATTRIBUTE));
if (! newE->defaultAtts) {
return 0;
}
@@ -7469,7 +7834,7 @@ lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) {
/* table->size is a power of 2 */
table->size = (size_t)1 << INIT_POWER;
tsize = table->size * sizeof(NAMED *);
- table->v = table->mem->malloc_fcn(tsize);
+ table->v = MALLOC(table->parser, tsize);
if (! table->v) {
table->size = 0;
return NULL;
@@ -7509,7 +7874,7 @@ lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) {
}
size_t tsize = newSize * sizeof(NAMED *);
- NAMED **newV = table->mem->malloc_fcn(tsize);
+ NAMED **newV = MALLOC(table->parser, tsize);
if (! newV)
return NULL;
memset(newV, 0, tsize);
@@ -7525,7 +7890,7 @@ lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) {
}
newV[j] = table->v[i];
}
- table->mem->free_fcn(table->v);
+ FREE(table->parser, table->v);
table->v = newV;
table->power = newPower;
table->size = newSize;
@@ -7538,7 +7903,7 @@ lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) {
}
}
}
- table->v[i] = table->mem->malloc_fcn(createSize);
+ table->v[i] = MALLOC(table->parser, createSize);
if (! table->v[i])
return NULL;
memset(table->v[i], 0, createSize);
@@ -7551,7 +7916,7 @@ static void FASTCALL
hashTableClear(HASH_TABLE *table) {
size_t i;
for (i = 0; i < table->size; i++) {
- table->mem->free_fcn(table->v[i]);
+ FREE(table->parser, table->v[i]);
table->v[i] = NULL;
}
table->used = 0;
@@ -7561,17 +7926,17 @@ static void FASTCALL
hashTableDestroy(HASH_TABLE *table) {
size_t i;
for (i = 0; i < table->size; i++)
- table->mem->free_fcn(table->v[i]);
- table->mem->free_fcn(table->v);
+ FREE(table->parser, table->v[i]);
+ FREE(table->parser, table->v);
}
static void FASTCALL
-hashTableInit(HASH_TABLE *p, const XML_Memory_Handling_Suite *ms) {
+hashTableInit(HASH_TABLE *p, XML_Parser parser) {
p->power = 0;
p->size = 0;
p->used = 0;
p->v = NULL;
- p->mem = ms;
+ p->parser = parser;
}
static void FASTCALL
@@ -7591,13 +7956,13 @@ hashTableIterNext(HASH_TABLE_ITER *iter) {
}
static void FASTCALL
-poolInit(STRING_POOL *pool, const XML_Memory_Handling_Suite *ms) {
+poolInit(STRING_POOL *pool, XML_Parser parser) {
pool->blocks = NULL;
pool->freeBlocks = NULL;
pool->start = NULL;
pool->ptr = NULL;
pool->end = NULL;
- pool->mem = ms;
+ pool->parser = parser;
}
static void FASTCALL
@@ -7624,13 +7989,13 @@ poolDestroy(STRING_POOL *pool) {
BLOCK *p = pool->blocks;
while (p) {
BLOCK *tem = p->next;
- pool->mem->free_fcn(p);
+ FREE(pool->parser, p);
p = tem;
}
p = pool->freeBlocks;
while (p) {
BLOCK *tem = p->next;
- pool->mem->free_fcn(p);
+ FREE(pool->parser, p);
p = tem;
}
}
@@ -7785,8 +8150,7 @@ poolGrow(STRING_POOL *pool) {
if (bytesToAllocate == 0)
return XML_FALSE;
- temp = (BLOCK *)pool->mem->realloc_fcn(pool->blocks,
- (unsigned)bytesToAllocate);
+ temp = REALLOC(pool->parser, pool->blocks, bytesToAllocate);
if (temp == NULL)
return XML_FALSE;
pool->blocks = temp;
@@ -7826,7 +8190,7 @@ poolGrow(STRING_POOL *pool) {
if (bytesToAllocate == 0)
return XML_FALSE;
- tem = pool->mem->malloc_fcn(bytesToAllocate);
+ tem = MALLOC(pool->parser, bytesToAllocate);
if (! tem)
return XML_FALSE;
tem->size = blockSize;
@@ -7857,12 +8221,17 @@ nextScaffoldPart(XML_Parser parser) {
return -1;
}
#endif
- dtd->scaffIndex = (int *)MALLOC(parser, parser->m_groupSize * sizeof(int));
+ dtd->scaffIndex = MALLOC(parser, parser->m_groupSize * sizeof(int));
if (! dtd->scaffIndex)
return -1;
dtd->scaffIndex[0] = 0;
}
+ // Will casting to int be safe further down?
+ if (dtd->scaffCount > INT_MAX) {
+ return -1;
+ }
+
if (dtd->scaffCount >= dtd->scaffSize) {
CONTENT_SCAFFOLD *temp;
if (dtd->scaffold) {
@@ -7880,21 +8249,20 @@ nextScaffoldPart(XML_Parser parser) {
}
#endif
- temp = (CONTENT_SCAFFOLD *)REALLOC(
- parser, dtd->scaffold, dtd->scaffSize * 2 * sizeof(CONTENT_SCAFFOLD));
+ temp = REALLOC(parser, dtd->scaffold,
+ dtd->scaffSize * 2 * sizeof(CONTENT_SCAFFOLD));
if (temp == NULL)
return -1;
dtd->scaffSize *= 2;
} else {
- temp = (CONTENT_SCAFFOLD *)MALLOC(parser, INIT_SCAFFOLD_ELEMENTS
- * sizeof(CONTENT_SCAFFOLD));
+ temp = MALLOC(parser, INIT_SCAFFOLD_ELEMENTS * sizeof(CONTENT_SCAFFOLD));
if (temp == NULL)
return -1;
dtd->scaffSize = INIT_SCAFFOLD_ELEMENTS;
}
dtd->scaffold = temp;
}
- next = dtd->scaffCount++;
+ next = (int)dtd->scaffCount++;
me = &dtd->scaffold[next];
if (dtd->scaffLevel) {
CONTENT_SCAFFOLD *parent
@@ -7941,7 +8309,10 @@ build_model(XML_Parser parser) {
const size_t allocsize = (dtd->scaffCount * sizeof(XML_Content)
+ (dtd->contentStringLen * sizeof(XML_Char)));
- ret = (XML_Content *)MALLOC(parser, allocsize);
+ // NOTE: We are avoiding MALLOC(..) here to so that
+ // applications that are not using XML_FreeContentModel but plain
+ // free(..) or .free_fcn() to free the content model's memory are safe.
+ ret = parser->m_mem.malloc_fcn(allocsize);
if (! ret)
return NULL;
@@ -8062,7 +8433,7 @@ getElementType(XML_Parser parser, const ENCODING *enc, const char *ptr,
}
static XML_Char *
-copyString(const XML_Char *s, const XML_Memory_Handling_Suite *memsuite) {
+copyString(const XML_Char *s, XML_Parser parser) {
size_t charsRequired = 0;
XML_Char *result;
@@ -8074,7 +8445,7 @@ copyString(const XML_Char *s, const XML_Memory_Handling_Suite *memsuite) {
charsRequired++;
/* Now allocate space for the copy */
- result = memsuite->malloc_fcn(charsRequired * sizeof(XML_Char));
+ result = MALLOC(parser, charsRequired * sizeof(XML_Char));
if (result == NULL)
return NULL;
/* Copy the original into place */
@@ -8093,10 +8464,10 @@ accountingGetCurrentAmplification(XML_Parser rootParser) {
+ rootParser->m_accounting.countBytesIndirect;
const float amplificationFactor
= rootParser->m_accounting.countBytesDirect
- ? (countBytesOutput
+ ? ((float)countBytesOutput
/ (float)(rootParser->m_accounting.countBytesDirect))
- : ((lenOfShortestInclude
- + rootParser->m_accounting.countBytesIndirect)
+ : ((float)(lenOfShortestInclude
+ + rootParser->m_accounting.countBytesIndirect)
/ (float)lenOfShortestInclude);
assert(! rootParser->m_parentParser);
return amplificationFactor;
@@ -8280,6 +8651,8 @@ entityTrackingOnClose(XML_Parser originParser, ENTITY *entity, int sourceLine) {
rootParser->m_entity_stats.currentDepth--;
}
+#endif /* XML_GE == 1 */
+
static XML_Parser
getRootParserOf(XML_Parser parser, unsigned int *outLevelDiff) {
XML_Parser rootParser = parser;
@@ -8295,6 +8668,8 @@ getRootParserOf(XML_Parser parser, unsigned int *outLevelDiff) {
return rootParser;
}
+#if XML_GE == 1
+
const char *
unsignedCharToPrintable(unsigned char c) {
switch (c) {
diff --git a/contrib/expat/lib/xmlrole.h b/contrib/expat/lib/xmlrole.h
index a7904274c91d..9d0d4ff11b7f 100644
--- a/contrib/expat/lib/xmlrole.h
+++ b/contrib/expat/lib/xmlrole.h
@@ -10,7 +10,7 @@
Copyright (c) 2000 Clark Cooper <coopercc@users.sourceforge.net>
Copyright (c) 2002 Karl Waclawek <karl@waclawek.net>
Copyright (c) 2002 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
- Copyright (c) 2017-2024 Sebastian Pipping <sebastian@pipping.org>
+ Copyright (c) 2017-2025 Sebastian Pipping <sebastian@pipping.org>
Licensed under the MIT license:
Permission is hereby granted, free of charge, to any person obtaining
@@ -34,19 +34,13 @@
*/
#ifndef XmlRole_INCLUDED
-#define XmlRole_INCLUDED 1
+# define XmlRole_INCLUDED 1
-#ifdef __VMS
-/* 0 1 2 3 0 1 2 3
- 1234567890123456789012345678901 1234567890123456789012345678901 */
-# define XmlPrologStateInitExternalEntity XmlPrologStateInitExternalEnt
-#endif
+# include "xmltok.h"
-#include "xmltok.h"
-
-#ifdef __cplusplus
+# ifdef __cplusplus
extern "C" {
-#endif
+# endif
enum {
XML_ROLE_ERROR = -1,
@@ -107,11 +101,11 @@ enum {
XML_ROLE_CONTENT_ELEMENT_PLUS,
XML_ROLE_PI,
XML_ROLE_COMMENT,
-#ifdef XML_DTD
+# ifdef XML_DTD
XML_ROLE_TEXT_DECL,
XML_ROLE_IGNORE_SECT,
XML_ROLE_INNER_PARAM_ENTITY_REF,
-#endif /* XML_DTD */
+# endif /* XML_DTD */
XML_ROLE_PARAM_ENTITY_REF
};
@@ -120,23 +114,23 @@ typedef struct prolog_state {
const char *end, const ENCODING *enc);
unsigned level;
int role_none;
-#ifdef XML_DTD
+# ifdef XML_DTD
unsigned includeLevel;
int documentEntity;
int inEntityValue;
-#endif /* XML_DTD */
+# endif /* XML_DTD */
} PROLOG_STATE;
void XmlPrologStateInit(PROLOG_STATE *state);
-#ifdef XML_DTD
+# ifdef XML_DTD
void XmlPrologStateInitExternalEntity(PROLOG_STATE *state);
-#endif /* XML_DTD */
+# endif /* XML_DTD */
-#define XmlTokenRole(state, tok, ptr, end, enc) \
- (((state)->handler)(state, tok, ptr, end, enc))
+# define XmlTokenRole(state, tok, ptr, end, enc) \
+ (((state)->handler)(state, tok, ptr, end, enc))
-#ifdef __cplusplus
+# ifdef __cplusplus
}
-#endif
+# endif
#endif /* not XmlRole_INCLUDED */
diff --git a/contrib/expat/lib/xmltok.c b/contrib/expat/lib/xmltok.c
index 29a66d72ceea..95d5e84b67f1 100644
--- a/contrib/expat/lib/xmltok.c
+++ b/contrib/expat/lib/xmltok.c
@@ -1398,7 +1398,7 @@ unknown_toUtf16(const ENCODING *enc, const char **fromP, const char *fromLim,
}
ENCODING *
-XmlInitUnknownEncoding(void *mem, int *table, CONVERTER convert,
+XmlInitUnknownEncoding(void *mem, const int *table, CONVERTER convert,
void *userData) {
int i;
struct unknown_encoding *e = (struct unknown_encoding *)mem;
@@ -1661,7 +1661,7 @@ initScan(const ENCODING *const *encodingTable, const INIT_ENCODING *enc,
# undef ns
ENCODING *
-XmlInitUnknownEncodingNS(void *mem, int *table, CONVERTER convert,
+XmlInitUnknownEncodingNS(void *mem, const int *table, CONVERTER convert,
void *userData) {
ENCODING *enc = XmlInitUnknownEncoding(mem, table, convert, userData);
if (enc)
diff --git a/contrib/expat/lib/xmltok.h b/contrib/expat/lib/xmltok.h
index c51fce1ec151..79a9fb76871f 100644
--- a/contrib/expat/lib/xmltok.h
+++ b/contrib/expat/lib/xmltok.h
@@ -35,113 +35,113 @@
*/
#ifndef XmlTok_INCLUDED
-#define XmlTok_INCLUDED 1
+# define XmlTok_INCLUDED 1
-#ifdef __cplusplus
+# ifdef __cplusplus
extern "C" {
-#endif
+# endif
/* The following token may be returned by XmlContentTok */
-#define XML_TOK_TRAILING_RSQB \
- -5 /* ] or ]] at the end of the scan; might be \
- start of illegal ]]> sequence */
+# define XML_TOK_TRAILING_RSQB \
+ -5 /* ] or ]] at the end of the scan; might be \
+ start of illegal ]]> sequence */
/* The following tokens may be returned by both XmlPrologTok and
XmlContentTok.
*/
-#define XML_TOK_NONE -4 /* The string to be scanned is empty */
-#define XML_TOK_TRAILING_CR \
- -3 /* A CR at the end of the scan; \
- might be part of CRLF sequence */
-#define XML_TOK_PARTIAL_CHAR -2 /* only part of a multibyte sequence */
-#define XML_TOK_PARTIAL -1 /* only part of a token */
-#define XML_TOK_INVALID 0
+# define XML_TOK_NONE -4 /* The string to be scanned is empty */
+# define XML_TOK_TRAILING_CR \
+ -3 /* A CR at the end of the scan; \
+ might be part of CRLF sequence */
+# define XML_TOK_PARTIAL_CHAR -2 /* only part of a multibyte sequence */
+# define XML_TOK_PARTIAL -1 /* only part of a token */
+# define XML_TOK_INVALID 0
/* The following tokens are returned by XmlContentTok; some are also
returned by XmlAttributeValueTok, XmlEntityTok, XmlCdataSectionTok.
*/
-#define XML_TOK_START_TAG_WITH_ATTS 1
-#define XML_TOK_START_TAG_NO_ATTS 2
-#define XML_TOK_EMPTY_ELEMENT_WITH_ATTS 3 /* empty element tag <e/> */
-#define XML_TOK_EMPTY_ELEMENT_NO_ATTS 4
-#define XML_TOK_END_TAG 5
-#define XML_TOK_DATA_CHARS 6
-#define XML_TOK_DATA_NEWLINE 7
-#define XML_TOK_CDATA_SECT_OPEN 8
-#define XML_TOK_ENTITY_REF 9
-#define XML_TOK_CHAR_REF 10 /* numeric character reference */
+# define XML_TOK_START_TAG_WITH_ATTS 1
+# define XML_TOK_START_TAG_NO_ATTS 2
+# define XML_TOK_EMPTY_ELEMENT_WITH_ATTS 3 /* empty element tag <e/> */
+# define XML_TOK_EMPTY_ELEMENT_NO_ATTS 4
+# define XML_TOK_END_TAG 5
+# define XML_TOK_DATA_CHARS 6
+# define XML_TOK_DATA_NEWLINE 7
+# define XML_TOK_CDATA_SECT_OPEN 8
+# define XML_TOK_ENTITY_REF 9
+# define XML_TOK_CHAR_REF 10 /* numeric character reference */
/* The following tokens may be returned by both XmlPrologTok and
XmlContentTok.
*/
-#define XML_TOK_PI 11 /* processing instruction */
-#define XML_TOK_XML_DECL 12 /* XML decl or text decl */
-#define XML_TOK_COMMENT 13
-#define XML_TOK_BOM 14 /* Byte order mark */
+# define XML_TOK_PI 11 /* processing instruction */
+# define XML_TOK_XML_DECL 12 /* XML decl or text decl */
+# define XML_TOK_COMMENT 13
+# define XML_TOK_BOM 14 /* Byte order mark */
/* The following tokens are returned only by XmlPrologTok */
-#define XML_TOK_PROLOG_S 15
-#define XML_TOK_DECL_OPEN 16 /* <!foo */
-#define XML_TOK_DECL_CLOSE 17 /* > */
-#define XML_TOK_NAME 18
-#define XML_TOK_NMTOKEN 19
-#define XML_TOK_POUND_NAME 20 /* #name */
-#define XML_TOK_OR 21 /* | */
-#define XML_TOK_PERCENT 22
-#define XML_TOK_OPEN_PAREN 23
-#define XML_TOK_CLOSE_PAREN 24
-#define XML_TOK_OPEN_BRACKET 25
-#define XML_TOK_CLOSE_BRACKET 26
-#define XML_TOK_LITERAL 27
-#define XML_TOK_PARAM_ENTITY_REF 28
-#define XML_TOK_INSTANCE_START 29
+# define XML_TOK_PROLOG_S 15
+# define XML_TOK_DECL_OPEN 16 /* <!foo */
+# define XML_TOK_DECL_CLOSE 17 /* > */
+# define XML_TOK_NAME 18
+# define XML_TOK_NMTOKEN 19
+# define XML_TOK_POUND_NAME 20 /* #name */
+# define XML_TOK_OR 21 /* | */
+# define XML_TOK_PERCENT 22
+# define XML_TOK_OPEN_PAREN 23
+# define XML_TOK_CLOSE_PAREN 24
+# define XML_TOK_OPEN_BRACKET 25
+# define XML_TOK_CLOSE_BRACKET 26
+# define XML_TOK_LITERAL 27
+# define XML_TOK_PARAM_ENTITY_REF 28
+# define XML_TOK_INSTANCE_START 29
/* The following occur only in element type declarations */
-#define XML_TOK_NAME_QUESTION 30 /* name? */
-#define XML_TOK_NAME_ASTERISK 31 /* name* */
-#define XML_TOK_NAME_PLUS 32 /* name+ */
-#define XML_TOK_COND_SECT_OPEN 33 /* <![ */
-#define XML_TOK_COND_SECT_CLOSE 34 /* ]]> */
-#define XML_TOK_CLOSE_PAREN_QUESTION 35 /* )? */
-#define XML_TOK_CLOSE_PAREN_ASTERISK 36 /* )* */
-#define XML_TOK_CLOSE_PAREN_PLUS 37 /* )+ */
-#define XML_TOK_COMMA 38
+# define XML_TOK_NAME_QUESTION 30 /* name? */
+# define XML_TOK_NAME_ASTERISK 31 /* name* */
+# define XML_TOK_NAME_PLUS 32 /* name+ */
+# define XML_TOK_COND_SECT_OPEN 33 /* <![ */
+# define XML_TOK_COND_SECT_CLOSE 34 /* ]]> */
+# define XML_TOK_CLOSE_PAREN_QUESTION 35 /* )? */
+# define XML_TOK_CLOSE_PAREN_ASTERISK 36 /* )* */
+# define XML_TOK_CLOSE_PAREN_PLUS 37 /* )+ */
+# define XML_TOK_COMMA 38
/* The following token is returned only by XmlAttributeValueTok */
-#define XML_TOK_ATTRIBUTE_VALUE_S 39
+# define XML_TOK_ATTRIBUTE_VALUE_S 39
/* The following token is returned only by XmlCdataSectionTok */
-#define XML_TOK_CDATA_SECT_CLOSE 40
+# define XML_TOK_CDATA_SECT_CLOSE 40
/* With namespace processing this is returned by XmlPrologTok for a
name with a colon.
*/
-#define XML_TOK_PREFIXED_NAME 41
+# define XML_TOK_PREFIXED_NAME 41
-#ifdef XML_DTD
-# define XML_TOK_IGNORE_SECT 42
-#endif /* XML_DTD */
+# ifdef XML_DTD
+# define XML_TOK_IGNORE_SECT 42
+# endif /* XML_DTD */
-#ifdef XML_DTD
-# define XML_N_STATES 4
-#else /* not XML_DTD */
-# define XML_N_STATES 3
-#endif /* not XML_DTD */
+# ifdef XML_DTD
+# define XML_N_STATES 4
+# else /* not XML_DTD */
+# define XML_N_STATES 3
+# endif /* not XML_DTD */
-#define XML_PROLOG_STATE 0
-#define XML_CONTENT_STATE 1
-#define XML_CDATA_SECTION_STATE 2
-#ifdef XML_DTD
-# define XML_IGNORE_SECTION_STATE 3
-#endif /* XML_DTD */
+# define XML_PROLOG_STATE 0
+# define XML_CONTENT_STATE 1
+# define XML_CDATA_SECTION_STATE 2
+# ifdef XML_DTD
+# define XML_IGNORE_SECTION_STATE 3
+# endif /* XML_DTD */
-#define XML_N_LITERAL_TYPES 2
-#define XML_ATTRIBUTE_VALUE_LITERAL 0
-#define XML_ENTITY_VALUE_LITERAL 1
+# define XML_N_LITERAL_TYPES 2
+# define XML_ATTRIBUTE_VALUE_LITERAL 0
+# define XML_ENTITY_VALUE_LITERAL 1
/* The size of the buffer passed to XmlUtf8Encode must be at least this. */
-#define XML_UTF8_ENCODE_MAX 4
+# define XML_UTF8_ENCODE_MAX 4
/* The size of the buffer passed to XmlUtf16Encode must be at least this. */
-#define XML_UTF16_ENCODE_MAX 2
+# define XML_UTF16_ENCODE_MAX 2
typedef struct position {
/* first line and first column are 0 not 1 */
@@ -220,63 +220,63 @@ struct encoding {
the prolog outside literals, comments and processing instructions.
*/
-#define XmlTok(enc, state, ptr, end, nextTokPtr) \
- (((enc)->scanners[state])(enc, ptr, end, nextTokPtr))
+# define XmlTok(enc, state, ptr, end, nextTokPtr) \
+ (((enc)->scanners[state])(enc, ptr, end, nextTokPtr))
-#define XmlPrologTok(enc, ptr, end, nextTokPtr) \
- XmlTok(enc, XML_PROLOG_STATE, ptr, end, nextTokPtr)
+# define XmlPrologTok(enc, ptr, end, nextTokPtr) \
+ XmlTok(enc, XML_PROLOG_STATE, ptr, end, nextTokPtr)
-#define XmlContentTok(enc, ptr, end, nextTokPtr) \
- XmlTok(enc, XML_CONTENT_STATE, ptr, end, nextTokPtr)
+# define XmlContentTok(enc, ptr, end, nextTokPtr) \
+ XmlTok(enc, XML_CONTENT_STATE, ptr, end, nextTokPtr)
-#define XmlCdataSectionTok(enc, ptr, end, nextTokPtr) \
- XmlTok(enc, XML_CDATA_SECTION_STATE, ptr, end, nextTokPtr)
+# define XmlCdataSectionTok(enc, ptr, end, nextTokPtr) \
+ XmlTok(enc, XML_CDATA_SECTION_STATE, ptr, end, nextTokPtr)
-#ifdef XML_DTD
+# ifdef XML_DTD
-# define XmlIgnoreSectionTok(enc, ptr, end, nextTokPtr) \
- XmlTok(enc, XML_IGNORE_SECTION_STATE, ptr, end, nextTokPtr)
+# define XmlIgnoreSectionTok(enc, ptr, end, nextTokPtr) \
+ XmlTok(enc, XML_IGNORE_SECTION_STATE, ptr, end, nextTokPtr)
-#endif /* XML_DTD */
+# endif /* XML_DTD */
/* This is used for performing a 2nd-level tokenization on the content
of a literal that has already been returned by XmlTok.
*/
-#define XmlLiteralTok(enc, literalType, ptr, end, nextTokPtr) \
- (((enc)->literalScanners[literalType])(enc, ptr, end, nextTokPtr))
+# define XmlLiteralTok(enc, literalType, ptr, end, nextTokPtr) \
+ (((enc)->literalScanners[literalType])(enc, ptr, end, nextTokPtr))
-#define XmlAttributeValueTok(enc, ptr, end, nextTokPtr) \
- XmlLiteralTok(enc, XML_ATTRIBUTE_VALUE_LITERAL, ptr, end, nextTokPtr)
+# define XmlAttributeValueTok(enc, ptr, end, nextTokPtr) \
+ XmlLiteralTok(enc, XML_ATTRIBUTE_VALUE_LITERAL, ptr, end, nextTokPtr)
-#define XmlEntityValueTok(enc, ptr, end, nextTokPtr) \
- XmlLiteralTok(enc, XML_ENTITY_VALUE_LITERAL, ptr, end, nextTokPtr)
+# define XmlEntityValueTok(enc, ptr, end, nextTokPtr) \
+ XmlLiteralTok(enc, XML_ENTITY_VALUE_LITERAL, ptr, end, nextTokPtr)
-#define XmlNameMatchesAscii(enc, ptr1, end1, ptr2) \
- (((enc)->nameMatchesAscii)(enc, ptr1, end1, ptr2))
+# define XmlNameMatchesAscii(enc, ptr1, end1, ptr2) \
+ (((enc)->nameMatchesAscii)(enc, ptr1, end1, ptr2))
-#define XmlNameLength(enc, ptr) (((enc)->nameLength)(enc, ptr))
+# define XmlNameLength(enc, ptr) (((enc)->nameLength)(enc, ptr))
-#define XmlSkipS(enc, ptr) (((enc)->skipS)(enc, ptr))
+# define XmlSkipS(enc, ptr) (((enc)->skipS)(enc, ptr))
-#define XmlGetAttributes(enc, ptr, attsMax, atts) \
- (((enc)->getAtts)(enc, ptr, attsMax, atts))
+# define XmlGetAttributes(enc, ptr, attsMax, atts) \
+ (((enc)->getAtts)(enc, ptr, attsMax, atts))
-#define XmlCharRefNumber(enc, ptr) (((enc)->charRefNumber)(enc, ptr))
+# define XmlCharRefNumber(enc, ptr) (((enc)->charRefNumber)(enc, ptr))
-#define XmlPredefinedEntityName(enc, ptr, end) \
- (((enc)->predefinedEntityName)(enc, ptr, end))
+# define XmlPredefinedEntityName(enc, ptr, end) \
+ (((enc)->predefinedEntityName)(enc, ptr, end))
-#define XmlUpdatePosition(enc, ptr, end, pos) \
- (((enc)->updatePosition)(enc, ptr, end, pos))
+# define XmlUpdatePosition(enc, ptr, end, pos) \
+ (((enc)->updatePosition)(enc, ptr, end, pos))
-#define XmlIsPublicId(enc, ptr, end, badPtr) \
- (((enc)->isPublicId)(enc, ptr, end, badPtr))
+# define XmlIsPublicId(enc, ptr, end, badPtr) \
+ (((enc)->isPublicId)(enc, ptr, end, badPtr))
-#define XmlUtf8Convert(enc, fromP, fromLim, toP, toLim) \
- (((enc)->utf8Convert)(enc, fromP, fromLim, toP, toLim))
+# define XmlUtf8Convert(enc, fromP, fromLim, toP, toLim) \
+ (((enc)->utf8Convert)(enc, fromP, fromLim, toP, toLim))
-#define XmlUtf16Convert(enc, fromP, fromLim, toP, toLim) \
- (((enc)->utf16Convert)(enc, fromP, fromLim, toP, toLim))
+# define XmlUtf16Convert(enc, fromP, fromLim, toP, toLim) \
+ (((enc)->utf16Convert)(enc, fromP, fromLim, toP, toLim))
typedef struct {
ENCODING initEnc;
@@ -299,7 +299,7 @@ int XmlSizeOfUnknownEncoding(void);
typedef int(XMLCALL *CONVERTER)(void *userData, const char *p);
-ENCODING *XmlInitUnknownEncoding(void *mem, int *table, CONVERTER convert,
+ENCODING *XmlInitUnknownEncoding(void *mem, const int *table, CONVERTER convert,
void *userData);
int XmlParseXmlDeclNS(int isGeneralTextEntity, const ENCODING *enc,
@@ -312,10 +312,10 @@ int XmlInitEncodingNS(INIT_ENCODING *p, const ENCODING **encPtr,
const char *name);
const ENCODING *XmlGetUtf8InternalEncodingNS(void);
const ENCODING *XmlGetUtf16InternalEncodingNS(void);
-ENCODING *XmlInitUnknownEncodingNS(void *mem, int *table, CONVERTER convert,
- void *userData);
-#ifdef __cplusplus
+ENCODING *XmlInitUnknownEncodingNS(void *mem, const int *table,
+ CONVERTER convert, void *userData);
+# ifdef __cplusplus
}
-#endif
+# endif
#endif /* not XmlTok_INCLUDED */
diff --git a/contrib/expat/tests/Makefile.in b/contrib/expat/tests/Makefile.in
index eb00a068cbd2..830560e2daba 100644
--- a/contrib/expat/tests/Makefile.in
+++ b/contrib/expat/tests/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.5 from Makefile.am.
+# Makefile.in generated by automake 1.18.1 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2021 Free Software Foundation, Inc.
+# Copyright (C) 1994-2025 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -102,6 +102,8 @@ am__make_running_with_option = \
test $$has_opt = yes
am__make_dryrun = (target_option=n; $(am__make_running_with_option))
am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+am__rm_f = rm -f $(am__rm_f_notfound)
+am__rm_rf = rm -rf $(am__rm_f_notfound)
pkgdatadir = $(datadir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
@@ -324,10 +326,9 @@ am__base_list = \
sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
am__uninstall_files_from_dir = { \
- test -z "$$files" \
- || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
- || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
- $(am__cd) "$$dir" && rm -f $$files; }; \
+ { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && echo $$files | $(am__xargs_n) 40 $(am__rm_f); }; \
}
am__recheck_rx = ^[ ]*:recheck:[ ]*
am__global_test_result_rx = ^[ ]*:global-test-result:[ ]*
@@ -415,12 +416,13 @@ am__sh_e_setup = case $$- in *e*) set +e;; esac
# Default flags passed to test drivers.
am__common_driver_flags = \
--color-tests "$$am__color_tests" \
+ $$am__collect_skipped_logs \
--enable-hard-errors "$$am__enable_hard_errors" \
--expect-failure "$$am__expect_failure"
# To be inserted before the command running the test. Creates the
# directory for the log if needed. Stores in $dir the directory
# containing $f, in $tst the test, in $log the log. Executes the
-# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and
+# developer-defined test setup AM_TESTS_ENVIRONMENT (if any), and
# passes TESTS_ENVIRONMENT. Set up options for the wrapper that
# will run the test scripts (or their associated LOG_COMPILER, if
# thy have one).
@@ -439,6 +441,11 @@ if test -f "./$$f"; then dir=./; \
elif test -f "$$f"; then dir=; \
else dir="$(srcdir)/"; fi; \
tst=$$dir$$f; log='$@'; \
+if test -n '$(IGNORE_SKIPPED_LOGS)'; then \
+ am__collect_skipped_logs='--collect-skipped-logs no'; \
+else \
+ am__collect_skipped_logs=''; \
+fi; \
if test -n '$(DISABLE_HARD_ERRORS)'; then \
am__enable_hard_errors=no; \
else \
@@ -620,8 +627,10 @@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
am__include = @am__include@
am__leading_dot = @am__leading_dot@
am__quote = @am__quote@
+am__rm_f_notfound = @am__rm_f_notfound@
am__tar = @am__tar@
am__untar = @am__untar@
+am__xargs_n = @am__xargs_n@
bindir = @bindir@
build = @build@
build_alias = @build_alias@
@@ -757,13 +766,8 @@ $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
$(am__aclocal_m4_deps):
clean-checkPROGRAMS:
- @list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \
- echo " rm -f" $$list; \
- rm -f $$list || exit $$?; \
- test -n "$(EXEEXT)" || exit 0; \
- list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
- echo " rm -f" $$list; \
- rm -f $$list
+ $(am__rm_f) $(check_PROGRAMS)
+ test -z "$(EXEEXT)" || $(am__rm_f) $(check_PROGRAMS:$(EXEEXT)=)
runtests$(EXEEXT): $(runtests_OBJECTS) $(runtests_DEPENDENCIES) $(EXTRA_runtests_DEPENDENCIES)
@rm -f runtests$(EXEEXT)
@@ -810,7 +814,7 @@ distclean-compile:
$(am__depfiles_remade):
@$(MKDIR_P) $(@D)
- @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+ @: >>$@
am--depfiles: $(am__depfiles_remade)
@@ -974,7 +978,6 @@ distclean-tags:
am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck)
am--force-recheck:
@:
-
$(TEST_SUITE_LOG): $(TEST_LOGS)
@$(am__set_TESTS_bases); \
am__f_ok () { test -f "$$1" && test -r "$$1"; }; \
@@ -1050,10 +1053,37 @@ $(TEST_SUITE_LOG): $(TEST_LOGS)
result_count $$1 "XPASS:" $$xpass "$$red"; \
result_count $$1 "ERROR:" $$error "$$mgn"; \
}; \
+ output_system_information () \
+ { \
+ echo; \
+ { uname -a | $(AWK) '{ \
+ printf "System information (uname -a):"; \
+ for (i = 1; i < NF; ++i) \
+ { \
+ if (i != 2) \
+ printf " %s", $$i; \
+ } \
+ printf "\n"; \
+}'; } 2>&1; \
+ if test -r /etc/os-release; then \
+ echo "Distribution information (/etc/os-release):"; \
+ sed 8q /etc/os-release; \
+ elif test -r /etc/issue; then \
+ echo "Distribution information (/etc/issue):"; \
+ cat /etc/issue; \
+ fi; \
+ }; \
+ please_report () \
+ { \
+echo "Some test(s) failed. Please report this to $(PACKAGE_BUGREPORT),"; \
+echo "together with the test-suite.log file (gzipped) and your system"; \
+echo "information. Thanks."; \
+ }; \
{ \
echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \
$(am__rst_title); \
create_testsuite_report --no-color; \
+ output_system_information; \
echo; \
echo ".. contents:: :depth: 2"; \
echo; \
@@ -1073,26 +1103,25 @@ $(TEST_SUITE_LOG): $(TEST_LOGS)
create_testsuite_report --maybe-color; \
echo "$$col$$br$$std"; \
if $$success; then :; else \
- echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \
+ echo "$${col}See $(subdir)/$(TEST_SUITE_LOG) for debugging.$${std}";\
if test -n "$(PACKAGE_BUGREPORT)"; then \
- echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \
+ please_report | sed -e "s/^/$${col}/" -e s/'$$'/"$${std}"/; \
fi; \
echo "$$col$$br$$std"; \
fi; \
$$success || exit 1
check-TESTS: $(check_PROGRAMS)
- @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list
- @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list
- @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+ @$(am__rm_f) $(RECHECK_LOGS)
+ @$(am__rm_f) $(RECHECK_LOGS:.log=.trs)
+ @$(am__rm_f) $(TEST_SUITE_LOG)
@set +e; $(am__set_TESTS_bases); \
log_list=`for i in $$bases; do echo $$i.log; done`; \
- trs_list=`for i in $$bases; do echo $$i.trs; done`; \
- log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \
+ log_list=`echo $$log_list`; \
$(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \
exit $$?;
recheck: all $(check_PROGRAMS)
- @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+ @$(am__rm_f) $(TEST_SUITE_LOG)
@set +e; $(am__set_TESTS_bases); \
bases=`for i in $$bases; do echo $$i; done \
| $(am__list_recheck_tests)` || exit 1; \
@@ -1130,6 +1159,7 @@ runtests_cxx.log: runtests_cxx$(EXEEXT)
@am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \
@am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \
@am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT)
+
distdir: $(BUILT_SOURCES)
$(MAKE) $(AM_MAKEFLAGS) distdir-am
@@ -1215,15 +1245,15 @@ install-strip:
"INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
fi
mostlyclean-generic:
- -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS)
- -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs)
- -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
+ -$(am__rm_f) $(TEST_LOGS)
+ -$(am__rm_f) $(TEST_LOGS:.log=.trs)
+ -$(am__rm_f) $(TEST_SUITE_LOG)
clean-generic:
distclean-generic:
- -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
- -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+ -$(am__rm_f) $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || $(am__rm_f) $(CONFIG_CLEAN_VPATH_FILES)
maintainer-clean-generic:
@echo "This command is intended for maintainers to use"
@@ -1234,7 +1264,7 @@ clean-am: clean-checkPROGRAMS clean-generic clean-libtool \
mostlyclean-am
distclean: distclean-recursive
- -rm -f ./$(DEPDIR)/acc_tests.Po
+ -rm -f ./$(DEPDIR)/acc_tests.Po
-rm -f ./$(DEPDIR)/acc_tests_cxx.Po
-rm -f ./$(DEPDIR)/alloc_tests.Po
-rm -f ./$(DEPDIR)/alloc_tests_cxx.Po
@@ -1307,7 +1337,7 @@ install-ps-am:
installcheck-am:
maintainer-clean: maintainer-clean-recursive
- -rm -f ./$(DEPDIR)/acc_tests.Po
+ -rm -f ./$(DEPDIR)/acc_tests.Po
-rm -f ./$(DEPDIR)/acc_tests_cxx.Po
-rm -f ./$(DEPDIR)/alloc_tests.Po
-rm -f ./$(DEPDIR)/alloc_tests_cxx.Po
@@ -1376,3 +1406,10 @@ uninstall-am:
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
.NOEXPORT:
+
+# Tell GNU make to disable its built-in pattern rules.
+%:: %,v
+%:: RCS/%,v
+%:: RCS/%
+%:: s.%
+%:: SCCS/s.%
diff --git a/contrib/expat/tests/alloc_tests.c b/contrib/expat/tests/alloc_tests.c
index 12ea3b2a81d2..5ae6c6a72025 100644
--- a/contrib/expat/tests/alloc_tests.c
+++ b/contrib/expat/tests/alloc_tests.c
@@ -10,7 +10,7 @@
Copyright (c) 2003 Greg Stein <gstein@users.sourceforge.net>
Copyright (c) 2005-2007 Steven Solie <steven@solie.ca>
Copyright (c) 2005-2012 Karl Waclawek <karl@waclawek.net>
- Copyright (c) 2016-2023 Sebastian Pipping <sebastian@pipping.org>
+ Copyright (c) 2016-2025 Sebastian Pipping <sebastian@pipping.org>
Copyright (c) 2017-2022 Rhodri James <rhodri@wildebeest.org.uk>
Copyright (c) 2017 Joe Orton <jorton@redhat.com>
Copyright (c) 2017 José Gutiérrez de la Concha <jose@zeroc.com>
@@ -46,10 +46,16 @@
# undef NDEBUG /* because test suite relies on assert(...) at the moment */
#endif
+#include <math.h> /* NAN, INFINITY */
+#include <stdbool.h>
+#include <stdint.h> /* for SIZE_MAX */
#include <string.h>
#include <assert.h>
+#include "expat_config.h"
+
#include "expat.h"
+#include "internal.h"
#include "common.h"
#include "minicheck.h"
#include "dummy.h"
@@ -323,7 +329,7 @@ START_TEST(test_alloc_run_external_parser) {
XML_SetParamEntityParsing(g_parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
XML_SetUserData(g_parser, foo_text);
XML_SetExternalEntityRefHandler(g_parser, external_entity_null_loader);
- g_allocation_count = i;
+ g_allocation_count = (int)i;
if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
!= XML_STATUS_ERROR)
break;
@@ -434,7 +440,7 @@ START_TEST(test_alloc_internal_entity) {
const unsigned int max_alloc_count = 20;
for (i = 0; i < max_alloc_count; i++) {
- g_allocation_count = i;
+ g_allocation_count = (int)i;
XML_SetUnknownEncodingHandler(g_parser, unknown_released_encoding_handler,
NULL);
if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
@@ -2085,6 +2091,226 @@ START_TEST(test_alloc_reset_after_external_entity_parser_create_fail) {
}
END_TEST
+#if XML_GE == 1
+static size_t
+sizeRecordedFor(void *ptr) {
+ return *(size_t *)((char *)ptr - EXPAT_MALLOC_PADDING - sizeof(size_t));
+}
+#endif // XML_GE == 1
+
+START_TEST(test_alloc_tracker_size_recorded) {
+ XML_Memory_Handling_Suite memsuite = {malloc, realloc, free};
+
+ bool values[] = {true, false};
+ for (size_t i = 0; i < sizeof(values) / sizeof(values[0]); i++) {
+ const bool useMemSuite = values[i];
+ set_subtest("useMemSuite=%d", (int)useMemSuite);
+ XML_Parser parser = useMemSuite
+ ? XML_ParserCreate_MM(NULL, &memsuite, XCS("|"))
+ : XML_ParserCreate(NULL);
+
+#if XML_GE == 1
+ void *ptr = expat_malloc(parser, 10, -1);
+
+ assert_true(ptr != NULL);
+ assert_true(sizeRecordedFor(ptr) == 10);
+
+ assert_true(expat_realloc(parser, ptr, SIZE_MAX / 2, -1) == NULL);
+
+ assert_true(sizeRecordedFor(ptr) == 10); // i.e. unchanged
+
+ ptr = expat_realloc(parser, ptr, 20, -1);
+
+ assert_true(ptr != NULL);
+ assert_true(sizeRecordedFor(ptr) == 20);
+
+ expat_free(parser, ptr, -1);
+#endif
+
+ XML_ParserFree(parser);
+ }
+}
+END_TEST
+
+START_TEST(test_alloc_tracker_pointer_alignment) {
+ XML_Parser parser = XML_ParserCreate(NULL);
+#if XML_GE == 1
+ assert_true(sizeof(long long) >= sizeof(size_t)); // self-test
+ long long *const ptr
+ = (long long *)expat_malloc(parser, 4 * sizeof(long long), -1);
+ ptr[0] = 0LL;
+ ptr[1] = 1LL;
+ ptr[2] = 2LL;
+ ptr[3] = 3LL;
+ expat_free(parser, ptr, -1);
+#endif
+ XML_ParserFree(parser);
+}
+END_TEST
+
+START_TEST(test_alloc_tracker_maximum_amplification) {
+ if (g_reparseDeferralEnabledDefault == XML_TRUE) {
+ return;
+ }
+
+ XML_Parser parser = XML_ParserCreate(NULL);
+
+ // Get .m_accounting.countBytesDirect from 0 to 3
+ const char *const chunk = "<e>";
+ assert_true(_XML_Parse_SINGLE_BYTES(parser, chunk, (int)strlen(chunk),
+ /*isFinal=*/XML_FALSE)
+ == XML_STATUS_OK);
+
+#if XML_GE == 1
+ // Stop activation threshold from interfering
+ assert_true(XML_SetAllocTrackerActivationThreshold(parser, 0) == XML_TRUE);
+
+ // Exceed maximum amplification: should be rejected.
+ assert_true(expat_malloc(parser, 1000, -1) == NULL);
+
+ // Increase maximum amplification, and try the same amount once more: should
+ // work.
+ assert_true(XML_SetAllocTrackerMaximumAmplification(parser, 3000.0f)
+ == XML_TRUE);
+
+ void *const ptr = expat_malloc(parser, 1000, -1);
+ assert_true(ptr != NULL);
+ expat_free(parser, ptr, -1);
+#endif
+
+ XML_ParserFree(parser);
+}
+END_TEST
+
+START_TEST(test_alloc_tracker_threshold) {
+ XML_Parser parser = XML_ParserCreate(NULL);
+
+#if XML_GE == 1
+ // Exceed maximum amplification *before* (default) threshold: should work.
+ void *const ptr = expat_malloc(parser, 1000, -1);
+ assert_true(ptr != NULL);
+ expat_free(parser, ptr, -1);
+
+ // Exceed maximum amplification *after* threshold: should be rejected.
+ assert_true(XML_SetAllocTrackerActivationThreshold(parser, 999) == XML_TRUE);
+ assert_true(expat_malloc(parser, 1000, -1) == NULL);
+#endif
+
+ XML_ParserFree(parser);
+}
+END_TEST
+
+START_TEST(test_alloc_tracker_getbuffer_unlimited) {
+ XML_Parser parser = XML_ParserCreate(NULL);
+
+#if XML_GE == 1
+ // Artificially lower threshold
+ assert_true(XML_SetAllocTrackerActivationThreshold(parser, 0) == XML_TRUE);
+
+ // Self-test: Prove that threshold is as rejecting as expected
+ assert_true(expat_malloc(parser, 1000, -1) == NULL);
+#endif
+ // XML_GetBuffer should be allowed to pass, though
+ assert_true(XML_GetBuffer(parser, 1000) != NULL);
+
+ XML_ParserFree(parser);
+}
+END_TEST
+
+START_TEST(test_alloc_tracker_api) {
+ XML_Parser parserWithoutParent = XML_ParserCreate(NULL);
+ XML_Parser parserWithParent = XML_ExternalEntityParserCreate(
+ parserWithoutParent, XCS("entity123"), NULL);
+ if (parserWithoutParent == NULL)
+ fail("parserWithoutParent is NULL");
+ if (parserWithParent == NULL)
+ fail("parserWithParent is NULL");
+
+#if XML_GE == 1
+ // XML_SetAllocTrackerMaximumAmplification, error cases
+ if (XML_SetAllocTrackerMaximumAmplification(NULL, 123.0f) == XML_TRUE)
+ fail("Call with NULL parser is NOT supposed to succeed");
+ if (XML_SetAllocTrackerMaximumAmplification(parserWithParent, 123.0f)
+ == XML_TRUE)
+ fail("Call with non-root parser is NOT supposed to succeed");
+ if (XML_SetAllocTrackerMaximumAmplification(parserWithoutParent, NAN)
+ == XML_TRUE)
+ fail("Call with NaN limit is NOT supposed to succeed");
+ if (XML_SetAllocTrackerMaximumAmplification(parserWithoutParent, -1.0f)
+ == XML_TRUE)
+ fail("Call with negative limit is NOT supposed to succeed");
+ if (XML_SetAllocTrackerMaximumAmplification(parserWithoutParent, 0.9f)
+ == XML_TRUE)
+ fail("Call with positive limit <1.0 is NOT supposed to succeed");
+
+ // XML_SetAllocTrackerMaximumAmplification, success cases
+ if (XML_SetAllocTrackerMaximumAmplification(parserWithoutParent, 1.0f)
+ == XML_FALSE)
+ fail("Call with positive limit >=1.0 is supposed to succeed");
+ if (XML_SetAllocTrackerMaximumAmplification(parserWithoutParent, 123456.789f)
+ == XML_FALSE)
+ fail("Call with positive limit >=1.0 is supposed to succeed");
+ if (XML_SetAllocTrackerMaximumAmplification(parserWithoutParent, INFINITY)
+ == XML_FALSE)
+ fail("Call with positive limit >=1.0 is supposed to succeed");
+
+ // XML_SetAllocTrackerActivationThreshold, error cases
+ if (XML_SetAllocTrackerActivationThreshold(NULL, 123) == XML_TRUE)
+ fail("Call with NULL parser is NOT supposed to succeed");
+ if (XML_SetAllocTrackerActivationThreshold(parserWithParent, 123) == XML_TRUE)
+ fail("Call with non-root parser is NOT supposed to succeed");
+
+ // XML_SetAllocTrackerActivationThreshold, success cases
+ if (XML_SetAllocTrackerActivationThreshold(parserWithoutParent, 123)
+ == XML_FALSE)
+ fail("Call with non-NULL parentless parser is supposed to succeed");
+#endif // XML_GE == 1
+
+ XML_ParserFree(parserWithParent);
+ XML_ParserFree(parserWithoutParent);
+}
+END_TEST
+
+START_TEST(test_mem_api_cycle) {
+ XML_Parser parser = XML_ParserCreate(NULL);
+
+ void *ptr = XML_MemMalloc(parser, 10);
+
+ assert_true(ptr != NULL);
+ memset(ptr, 'x', 10); // assert writability, with ASan in mind
+
+ ptr = XML_MemRealloc(parser, ptr, 20);
+
+ assert_true(ptr != NULL);
+ memset(ptr, 'y', 20); // assert writability, with ASan in mind
+
+ XML_MemFree(parser, ptr);
+
+ XML_ParserFree(parser);
+}
+END_TEST
+
+START_TEST(test_mem_api_unlimited) {
+ XML_Parser parser = XML_ParserCreate(NULL);
+
+#if XML_GE == 1
+ assert_true(XML_SetAllocTrackerActivationThreshold(parser, 0) == XML_TRUE);
+#endif
+
+ void *ptr = XML_MemMalloc(parser, 1000);
+
+ assert_true(ptr != NULL);
+
+ ptr = XML_MemRealloc(parser, ptr, 2000);
+
+ assert_true(ptr != NULL);
+
+ XML_MemFree(parser, ptr);
+
+ XML_ParserFree(parser);
+}
+END_TEST
+
void
make_alloc_test_case(Suite *s) {
TCase *tc_alloc = tcase_create("allocation tests");
@@ -2151,4 +2377,14 @@ make_alloc_test_case(Suite *s) {
tcase_add_test__ifdef_xml_dtd(
tc_alloc, test_alloc_reset_after_external_entity_parser_create_fail);
+
+ tcase_add_test__if_xml_ge(tc_alloc, test_alloc_tracker_size_recorded);
+ tcase_add_test__if_xml_ge(tc_alloc, test_alloc_tracker_pointer_alignment);
+ tcase_add_test__if_xml_ge(tc_alloc, test_alloc_tracker_maximum_amplification);
+ tcase_add_test__if_xml_ge(tc_alloc, test_alloc_tracker_threshold);
+ tcase_add_test__if_xml_ge(tc_alloc, test_alloc_tracker_getbuffer_unlimited);
+ tcase_add_test__if_xml_ge(tc_alloc, test_alloc_tracker_api);
+
+ tcase_add_test(tc_alloc, test_mem_api_cycle);
+ tcase_add_test__if_xml_ge(tc_alloc, test_mem_api_unlimited);
}
diff --git a/contrib/expat/tests/basic_tests.c b/contrib/expat/tests/basic_tests.c
index e813df8b6fd2..0231e0949ee9 100644
--- a/contrib/expat/tests/basic_tests.c
+++ b/contrib/expat/tests/basic_tests.c
@@ -412,13 +412,13 @@ START_TEST(test_utf16_le_epilog_newline) {
if (first_chunk_bytes >= sizeof(text) - 1)
fail("bad value of first_chunk_bytes");
- if (_XML_Parse_SINGLE_BYTES(g_parser, text, first_chunk_bytes, XML_FALSE)
+ if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)first_chunk_bytes, XML_FALSE)
== XML_STATUS_ERROR)
xml_failure(g_parser);
else {
enum XML_Status rc;
rc = _XML_Parse_SINGLE_BYTES(g_parser, text + first_chunk_bytes,
- sizeof(text) - first_chunk_bytes - 1,
+ (int)(sizeof(text) - first_chunk_bytes - 1),
XML_TRUE);
if (rc == XML_STATUS_ERROR)
xml_failure(g_parser);
@@ -3123,6 +3123,10 @@ START_TEST(test_buffer_can_grow_to_max) {
for (int i = 0; i < num_prefixes; ++i) {
set_subtest("\"%s\"", prefixes[i]);
XML_Parser parser = XML_ParserCreate(NULL);
+#if XML_GE == 1
+ assert_true(XML_SetAllocTrackerActivationThreshold(parser, (size_t)-1)
+ == XML_TRUE); // i.e. deactivate
+#endif
const int prefix_len = (int)strlen(prefixes[i]);
const enum XML_Status s
= _XML_Parse_SINGLE_BYTES(parser, prefixes[i], prefix_len, XML_FALSE);
diff --git a/contrib/expat/tests/benchmark/Makefile.in b/contrib/expat/tests/benchmark/Makefile.in
index d0e6d0769db0..e72e901a39af 100644
--- a/contrib/expat/tests/benchmark/Makefile.in
+++ b/contrib/expat/tests/benchmark/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.5 from Makefile.am.
+# Makefile.in generated by automake 1.18.1 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2021 Free Software Foundation, Inc.
+# Copyright (C) 1994-2025 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -101,6 +101,8 @@ am__make_running_with_option = \
test $$has_opt = yes
am__make_dryrun = (target_option=n; $(am__make_running_with_option))
am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+am__rm_f = rm -f $(am__rm_f_notfound)
+am__rm_rf = rm -rf $(am__rm_f_notfound)
pkgdatadir = $(datadir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
@@ -320,8 +322,10 @@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
am__include = @am__include@
am__leading_dot = @am__leading_dot@
am__quote = @am__quote@
+am__rm_f_notfound = @am__rm_f_notfound@
am__tar = @am__tar@
am__untar = @am__untar@
+am__xargs_n = @am__xargs_n@
bindir = @bindir@
build = @build@
build_alias = @build_alias@
@@ -403,13 +407,8 @@ $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
$(am__aclocal_m4_deps):
clean-noinstPROGRAMS:
- @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \
- echo " rm -f" $$list; \
- rm -f $$list || exit $$?; \
- test -n "$(EXEEXT)" || exit 0; \
- list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
- echo " rm -f" $$list; \
- rm -f $$list
+ $(am__rm_f) $(noinst_PROGRAMS)
+ test -z "$(EXEEXT)" || $(am__rm_f) $(noinst_PROGRAMS:$(EXEEXT)=)
benchmark$(EXEEXT): $(benchmark_OBJECTS) $(benchmark_DEPENDENCIES) $(EXTRA_benchmark_DEPENDENCIES)
@rm -f benchmark$(EXEEXT)
@@ -425,7 +424,7 @@ distclean-compile:
$(am__depfiles_remade):
@$(MKDIR_P) $(@D)
- @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+ @: >>$@
am--depfiles: $(am__depfiles_remade)
@@ -507,6 +506,7 @@ cscopelist-am: $(am__tagged_files)
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
distdir: $(BUILT_SOURCES)
$(MAKE) $(AM_MAKEFLAGS) distdir-am
@@ -568,8 +568,8 @@ mostlyclean-generic:
clean-generic:
distclean-generic:
- -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
- -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+ -$(am__rm_f) $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || $(am__rm_f) $(CONFIG_CLEAN_VPATH_FILES)
maintainer-clean-generic:
@echo "This command is intended for maintainers to use"
@@ -580,7 +580,7 @@ clean-am: clean-generic clean-libtool clean-noinstPROGRAMS \
mostlyclean-am
distclean: distclean-am
- -rm -f ./$(DEPDIR)/benchmark.Po
+ -rm -f ./$(DEPDIR)/benchmark.Po
-rm -f Makefile
distclean-am: clean-am distclean-compile distclean-generic \
distclean-tags
@@ -626,7 +626,7 @@ install-ps-am:
installcheck-am:
maintainer-clean: maintainer-clean-am
- -rm -f ./$(DEPDIR)/benchmark.Po
+ -rm -f ./$(DEPDIR)/benchmark.Po
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
@@ -667,3 +667,10 @@ uninstall-am:
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
.NOEXPORT:
+
+# Tell GNU make to disable its built-in pattern rules.
+%:: %,v
+%:: RCS/%,v
+%:: RCS/%
+%:: s.%
+%:: SCCS/s.%
diff --git a/contrib/expat/tests/common.c b/contrib/expat/tests/common.c
index b158385f56a8..b2537d0deee1 100644
--- a/contrib/expat/tests/common.c
+++ b/contrib/expat/tests/common.c
@@ -303,7 +303,14 @@ duff_reallocator(void *ptr, size_t size) {
return realloc(ptr, size);
}
-// Portable remake of strndup(3) for C99; does not care about space efficiency
+// Portable remake of strnlen(3) for C99
+static size_t
+portable_strnlen(const char *s, size_t maxlen) {
+ const char *const end = (const char *)memchr(s, '\0', maxlen);
+ return (end == NULL) ? maxlen : (size_t)(end - s);
+}
+
+// Portable remake of strndup(3) for C99
char *
portable_strndup(const char *s, size_t n) {
if ((s == NULL) || (n == SIZE_MAX)) {
@@ -311,6 +318,8 @@ portable_strndup(const char *s, size_t n) {
return NULL;
}
+ n = portable_strnlen(s, n);
+
char *const buffer = (char *)malloc(n + 1);
if (buffer == NULL) {
errno = ENOMEM;
diff --git a/contrib/expat/tests/handlers.c b/contrib/expat/tests/handlers.c
index ac459507580b..5bca2b1f551e 100644
--- a/contrib/expat/tests/handlers.c
+++ b/contrib/expat/tests/handlers.c
@@ -10,7 +10,7 @@
Copyright (c) 2003 Greg Stein <gstein@users.sourceforge.net>
Copyright (c) 2005-2007 Steven Solie <steven@solie.ca>
Copyright (c) 2005-2012 Karl Waclawek <karl@waclawek.net>
- Copyright (c) 2016-2024 Sebastian Pipping <sebastian@pipping.org>
+ Copyright (c) 2016-2025 Sebastian Pipping <sebastian@pipping.org>
Copyright (c) 2017-2022 Rhodri James <rhodri@wildebeest.org.uk>
Copyright (c) 2017 Joe Orton <jorton@redhat.com>
Copyright (c) 2017 José Gutiérrez de la Concha <jose@zeroc.com>
@@ -89,15 +89,15 @@ start_element_event_handler2(void *userData, const XML_Char *name,
const XML_Char **attr) {
StructData *storage = (StructData *)userData;
UNUSED_P(attr);
- StructData_AddItem(storage, name, XML_GetCurrentColumnNumber(g_parser),
- XML_GetCurrentLineNumber(g_parser), STRUCT_START_TAG);
+ StructData_AddItem(storage, name, (int)XML_GetCurrentColumnNumber(g_parser),
+ (int)XML_GetCurrentLineNumber(g_parser), STRUCT_START_TAG);
}
void XMLCALL
end_element_event_handler2(void *userData, const XML_Char *name) {
StructData *storage = (StructData *)userData;
- StructData_AddItem(storage, name, XML_GetCurrentColumnNumber(g_parser),
- XML_GetCurrentLineNumber(g_parser), STRUCT_END_TAG);
+ StructData_AddItem(storage, name, (int)XML_GetCurrentColumnNumber(g_parser),
+ (int)XML_GetCurrentLineNumber(g_parser), STRUCT_END_TAG);
}
void XMLCALL
@@ -132,7 +132,7 @@ counting_start_element_handler(void *userData, const XML_Char *name,
fail("ID not present");
return;
}
- if (id != -1 && xcstrcmp(atts[id], info->id_name)) {
+ if (id != -1 && xcstrcmp(atts[id], info->id_name) != 0) {
fail("ID does not have the correct name");
return;
}
@@ -147,7 +147,7 @@ counting_start_element_handler(void *userData, const XML_Char *name,
fail("Attribute not recognised");
return;
}
- if (xcstrcmp(atts[1], attr->value)) {
+ if (xcstrcmp(atts[1], attr->value) != 0) {
fail("Attribute has wrong value");
return;
}
@@ -1110,7 +1110,7 @@ external_entity_devaluer(XML_Parser parser, const XML_Char *context,
UNUSED_P(publicId);
if (systemId == NULL || ! xcstrcmp(systemId, XCS("bar")))
return XML_STATUS_OK;
- if (xcstrcmp(systemId, XCS("foo")))
+ if (xcstrcmp(systemId, XCS("foo")) != 0)
fail("Unexpected system ID");
ext_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
if (ext_parser == NULL)
@@ -1276,7 +1276,7 @@ external_entity_duff_loader(XML_Parser parser, const XML_Char *context,
UNUSED_P(publicId);
/* Try a few different allocation levels */
for (i = 0; i < max_alloc_count; i++) {
- g_allocation_count = i;
+ g_allocation_count = (int)i;
new_parser = XML_ExternalEntityParserCreate(parser, context, NULL);
if (new_parser != NULL) {
XML_ParserFree(new_parser);
@@ -1552,15 +1552,16 @@ verify_attlist_decl_handler(void *userData, const XML_Char *element_name,
const XML_Char *default_value, int is_required) {
AttTest *at = (AttTest *)userData;
- if (xcstrcmp(element_name, at->element_name))
+ if (xcstrcmp(element_name, at->element_name) != 0)
fail("Unexpected element name in attribute declaration");
- if (xcstrcmp(attr_name, at->attr_name))
+ if (xcstrcmp(attr_name, at->attr_name) != 0)
fail("Unexpected attribute name in attribute declaration");
- if (xcstrcmp(attr_type, at->attr_type))
+ if (xcstrcmp(attr_type, at->attr_type) != 0)
fail("Unexpected attribute type in attribute declaration");
if ((default_value == NULL && at->default_value != NULL)
|| (default_value != NULL && at->default_value == NULL)
- || (default_value != NULL && xcstrcmp(default_value, at->default_value)))
+ || (default_value != NULL
+ && xcstrcmp(default_value, at->default_value) != 0))
fail("Unexpected default value in attribute declaration");
if (is_required != at->is_required)
fail("Requirement mismatch in attribute declaration");
@@ -1751,7 +1752,7 @@ param_entity_match_handler(void *userData, const XML_Char *entityName,
* going to overflow an int.
*/
if (value_length != (int)xcstrlen(entity_value_to_match)
- || xcstrncmp(value, entity_value_to_match, value_length)) {
+ || xcstrncmp(value, entity_value_to_match, value_length) != 0) {
entity_match_flag = ENTITY_MATCH_FAIL;
} else {
entity_match_flag = ENTITY_MATCH_SUCCESS;
diff --git a/contrib/expat/tests/minicheck.h b/contrib/expat/tests/minicheck.h
index 29ae4cb2420d..140eaaa5c105 100644
--- a/contrib/expat/tests/minicheck.h
+++ b/contrib/expat/tests/minicheck.h
@@ -134,8 +134,7 @@ void _check_set_test_info(char const *function, char const *filename,
__attribute__((noreturn))
# endif
# endif
-void
-_fail(const char *file, int line, const char *msg);
+void _fail(const char *file, int line, const char *msg);
Suite *suite_create(const char *name);
TCase *tcase_create(const char *name);
void suite_add_tcase(Suite *suite, TCase *tc);
diff --git a/contrib/expat/tests/misc_tests.c b/contrib/expat/tests/misc_tests.c
index fb95014b142f..2a8054546a12 100644
--- a/contrib/expat/tests/misc_tests.c
+++ b/contrib/expat/tests/misc_tests.c
@@ -70,7 +70,7 @@ START_TEST(test_misc_alloc_create_parser) {
/* Something this simple shouldn't need more than 10 allocations */
for (i = 0; i < max_alloc_count; i++) {
- g_allocation_count = i;
+ g_allocation_count = (int)i;
g_parser = XML_ParserCreate_MM(NULL, &memsuite, NULL);
if (g_parser != NULL)
break;
@@ -90,7 +90,7 @@ START_TEST(test_misc_alloc_create_parser_with_encoding) {
/* Try several levels of allocation */
for (i = 0; i < max_alloc_count; i++) {
- g_allocation_count = i;
+ g_allocation_count = (int)i;
g_parser = XML_ParserCreate_MM(XCS("us-ascii"), &memsuite, NULL);
if (g_parser != NULL)
break;
@@ -211,7 +211,8 @@ START_TEST(test_misc_version) {
if (! versions_equal(&read_version, &parsed_version))
fail("Version mismatch");
- if (xcstrcmp(version_text, XCS("expat_2.7.1"))) /* needs bump on releases */
+ if (xcstrcmp(version_text, XCS("expat_2.7.3"))
+ != 0) /* needs bump on releases */
fail("XML_*_VERSION in expat.h out of sync?\n");
}
END_TEST
@@ -678,6 +679,98 @@ START_TEST(test_misc_expected_event_ptr_issue_980) {
}
END_TEST
+START_TEST(test_misc_sync_entity_tolerated) {
+ const char *const doc = "<!DOCTYPE t0 [\n"
+ " <!ENTITY a '<t1></t1>'>\n"
+ " <!ENTITY b '<t2>two</t2>'>\n"
+ " <!ENTITY c '<t3>three<t4>four</t4>three</t3>'>\n"
+ " <!ENTITY d '<t5>&b;</t5>'>\n"
+ "]>\n"
+ "<t0>&a;&b;&c;&d;</t0>\n";
+ XML_Parser parser = XML_ParserCreate(NULL);
+
+ assert_true(_XML_Parse_SINGLE_BYTES(parser, doc, (int)strlen(doc),
+ /*isFinal=*/XML_TRUE)
+ == XML_STATUS_OK);
+
+ XML_ParserFree(parser);
+}
+END_TEST
+
+START_TEST(test_misc_async_entity_rejected) {
+ struct test_case {
+ const char *doc;
+ enum XML_Status expectedStatusNoGE;
+ enum XML_Error expectedErrorNoGE;
+ XML_Size expectedErrorLine;
+ XML_Size expectedErrorColumn;
+ };
+ const struct test_case cases[] = {
+ // Opened by one entity, closed by another
+ {"<!DOCTYPE t0 [\n"
+ " <!ENTITY open '<t1>'>\n"
+ " <!ENTITY close '</t1>'>\n"
+ "]>\n"
+ "<t0>&open;&close;</t0>\n",
+ XML_STATUS_OK, XML_ERROR_NONE, 5, 4},
+ // Opened by tag, closed by entity (non-root case)
+ {"<!DOCTYPE t0 [\n"
+ " <!ENTITY g0 ''>\n"
+ " <!ENTITY g1 '&g0;</t1>'>\n"
+ "]>\n"
+ "<t0><t1>&g1;</t0>\n",
+ XML_STATUS_ERROR, XML_ERROR_TAG_MISMATCH, 5, 8},
+ // Opened by tag, closed by entity (root case)
+ {"<!DOCTYPE t0 [\n"
+ " <!ENTITY g0 ''>\n"
+ " <!ENTITY g1 '&g0;</t0>'>\n"
+ "]>\n"
+ "<t0>&g1;\n",
+ XML_STATUS_ERROR, XML_ERROR_NO_ELEMENTS, 5, 4},
+ // Opened by entity, closed by tag <-- regression from 2.7.0
+ {"<!DOCTYPE t0 [\n"
+ " <!ENTITY g0 ''>\n"
+ " <!ENTITY g1 '<t1>&g0;'>\n"
+ "]>\n"
+ "<t0>&g1;</t1></t0>\n",
+ XML_STATUS_ERROR, XML_ERROR_TAG_MISMATCH, 5, 4},
+ // Opened by tag, closed by entity; then the other way around
+ {"<!DOCTYPE t0 [\n"
+ " <!ENTITY open '<t1>'>\n"
+ " <!ENTITY close '</t1>'>\n"
+ "]>\n"
+ "<t0><t1>&close;&open;</t1></t0>\n",
+ XML_STATUS_OK, XML_ERROR_NONE, 5, 8},
+ };
+
+ for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) {
+ const struct test_case testCase = cases[i];
+ set_subtest("cases[%d]", (int)i);
+
+ const char *const doc = testCase.doc;
+#if XML_GE == 1
+ const enum XML_Status expectedStatus = XML_STATUS_ERROR;
+ const enum XML_Error expectedError = XML_ERROR_ASYNC_ENTITY;
+#else
+ const enum XML_Status expectedStatus = testCase.expectedStatusNoGE;
+ const enum XML_Error expectedError = testCase.expectedErrorNoGE;
+#endif
+
+ XML_Parser parser = XML_ParserCreate(NULL);
+ assert_true(_XML_Parse_SINGLE_BYTES(parser, doc, (int)strlen(doc),
+ /*isFinal=*/XML_TRUE)
+ == expectedStatus);
+ assert_true(XML_GetErrorCode(parser) == expectedError);
+#if XML_GE == 1
+ assert_true(XML_GetCurrentLineNumber(parser) == testCase.expectedErrorLine);
+ assert_true(XML_GetCurrentColumnNumber(parser)
+ == testCase.expectedErrorColumn);
+#endif
+ XML_ParserFree(parser);
+ }
+}
+END_TEST
+
void
make_miscellaneous_test_case(Suite *s) {
TCase *tc_misc = tcase_create("miscellaneous tests");
@@ -706,4 +799,6 @@ make_miscellaneous_test_case(Suite *s) {
tcase_add_test(tc_misc, test_misc_stopparser_rejects_unstarted_parser);
tcase_add_test__if_xml_ge(tc_misc, test_renter_loop_finite_content);
tcase_add_test(tc_misc, test_misc_expected_event_ptr_issue_980);
+ tcase_add_test(tc_misc, test_misc_sync_entity_tolerated);
+ tcase_add_test(tc_misc, test_misc_async_entity_rejected);
}
diff --git a/contrib/expat/tests/nsalloc_tests.c b/contrib/expat/tests/nsalloc_tests.c
index ec88586af1d4..60fa87f83461 100644
--- a/contrib/expat/tests/nsalloc_tests.c
+++ b/contrib/expat/tests/nsalloc_tests.c
@@ -10,7 +10,7 @@
Copyright (c) 2003 Greg Stein <gstein@users.sourceforge.net>
Copyright (c) 2005-2007 Steven Solie <steven@solie.ca>
Copyright (c) 2005-2012 Karl Waclawek <karl@waclawek.net>
- Copyright (c) 2016-2023 Sebastian Pipping <sebastian@pipping.org>
+ Copyright (c) 2016-2025 Sebastian Pipping <sebastian@pipping.org>
Copyright (c) 2017-2022 Rhodri James <rhodri@wildebeest.org.uk>
Copyright (c) 2017 Joe Orton <jorton@redhat.com>
Copyright (c) 2017 José Gutiérrez de la Concha <jose@zeroc.com>
@@ -83,7 +83,7 @@ START_TEST(test_nsalloc_xmlns) {
const unsigned int max_alloc_count = 30;
for (i = 0; i < max_alloc_count; i++) {
- g_allocation_count = i;
+ g_allocation_count = (int)i;
/* Exercise more code paths with a default handler */
XML_SetDefaultHandler(g_parser, dummy_default_handler);
if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE)
@@ -454,10 +454,15 @@ START_TEST(test_nsalloc_realloc_attributes) {
nsalloc_teardown();
nsalloc_setup();
}
+#if XML_GE == 1
+ assert_true(
+ i == 0); // because expat_realloc relies on expat_malloc to some extent
+#else
if (i == 0)
fail("Parsing worked despite failing reallocations");
else if (i == max_realloc_count)
fail("Parsing failed at max reallocation count");
+#endif
}
END_TEST
@@ -523,7 +528,7 @@ START_TEST(test_nsalloc_realloc_binding_uri) {
/* Now repeat with a longer URI and a duff reallocator */
for (i = 0; i < max_realloc_count; i++) {
XML_ParserReset(g_parser, NULL);
- g_reallocation_count = i;
+ g_reallocation_count = (int)i;
if (_XML_Parse_SINGLE_BYTES(g_parser, second, (int)strlen(second), XML_TRUE)
!= XML_STATUS_ERROR)
break;
diff --git a/contrib/expat/xmlwf/Makefile.in b/contrib/expat/xmlwf/Makefile.in
index 480fd3e04103..07f2423aea10 100644
--- a/contrib/expat/xmlwf/Makefile.in
+++ b/contrib/expat/xmlwf/Makefile.in
@@ -1,7 +1,7 @@
-# Makefile.in generated by automake 1.16.5 from Makefile.am.
+# Makefile.in generated by automake 1.18.1 from Makefile.am.
# @configure_input@
-# Copyright (C) 1994-2021 Free Software Foundation, Inc.
+# Copyright (C) 1994-2025 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
@@ -100,6 +100,8 @@ am__make_running_with_option = \
test $$has_opt = yes
am__make_dryrun = (target_option=n; $(am__make_running_with_option))
am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+am__rm_f = rm -f $(am__rm_f_notfound)
+am__rm_rf = rm -rf $(am__rm_f_notfound)
pkgdatadir = $(datadir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
@@ -328,8 +330,10 @@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
am__include = @am__include@
am__leading_dot = @am__leading_dot@
am__quote = @am__quote@
+am__rm_f_notfound = @am__rm_f_notfound@
am__tar = @am__tar@
am__untar = @am__untar@
+am__xargs_n = @am__xargs_n@
bindir = @bindir@
build = @build@
build_alias = @build_alias@
@@ -464,16 +468,11 @@ uninstall-binPROGRAMS:
`; \
test -n "$$list" || exit 0; \
echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \
- cd "$(DESTDIR)$(bindir)" && rm -f $$files
+ cd "$(DESTDIR)$(bindir)" && $(am__rm_f) $$files
clean-binPROGRAMS:
- @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \
- echo " rm -f" $$list; \
- rm -f $$list || exit $$?; \
- test -n "$(EXEEXT)" || exit 0; \
- list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
- echo " rm -f" $$list; \
- rm -f $$list
+ $(am__rm_f) $(bin_PROGRAMS)
+ test -z "$(EXEEXT)" || $(am__rm_f) $(bin_PROGRAMS:$(EXEEXT)=)
xmlwf$(EXEEXT): $(xmlwf_OBJECTS) $(xmlwf_DEPENDENCIES) $(EXTRA_xmlwf_DEPENDENCIES)
@rm -f xmlwf$(EXEEXT)
@@ -492,7 +491,7 @@ distclean-compile:
$(am__depfiles_remade):
@$(MKDIR_P) $(@D)
- @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+ @: >>$@
am--depfiles: $(am__depfiles_remade)
@@ -630,6 +629,7 @@ cscopelist-am: $(am__tagged_files)
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
distdir: $(BUILT_SOURCES)
$(MAKE) $(AM_MAKEFLAGS) distdir-am
@@ -694,8 +694,8 @@ mostlyclean-generic:
clean-generic:
distclean-generic:
- -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
- -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+ -$(am__rm_f) $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || $(am__rm_f) $(CONFIG_CLEAN_VPATH_FILES)
maintainer-clean-generic:
@echo "This command is intended for maintainers to use"
@@ -705,7 +705,7 @@ clean: clean-am
clean-am: clean-binPROGRAMS clean-generic clean-libtool mostlyclean-am
distclean: distclean-am
- -rm -f ./$(DEPDIR)/xmlwf-@FILEMAP@.Po
+ -rm -f ./$(DEPDIR)/xmlwf-@FILEMAP@.Po
-rm -f ./$(DEPDIR)/xmlwf-codepage.Po
-rm -f ./$(DEPDIR)/xmlwf-xmlfile.Po
-rm -f ./$(DEPDIR)/xmlwf-xmlwf.Po
@@ -754,7 +754,7 @@ install-ps-am:
installcheck-am:
maintainer-clean: maintainer-clean-am
- -rm -f ./$(DEPDIR)/xmlwf-@FILEMAP@.Po
+ -rm -f ./$(DEPDIR)/xmlwf-@FILEMAP@.Po
-rm -f ./$(DEPDIR)/xmlwf-codepage.Po
-rm -f ./$(DEPDIR)/xmlwf-xmlfile.Po
-rm -f ./$(DEPDIR)/xmlwf-xmlwf.Po
@@ -798,3 +798,10 @@ uninstall-am: uninstall-binPROGRAMS
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
.NOEXPORT:
+
+# Tell GNU make to disable its built-in pattern rules.
+%:: %,v
+%:: RCS/%,v
+%:: RCS/%
+%:: s.%
+%:: SCCS/s.%
diff --git a/contrib/expat/xmlwf/unixfilemap.c b/contrib/expat/xmlwf/unixfilemap.c
index d0ce9cc60a8a..d2e0b174680b 100644
--- a/contrib/expat/xmlwf/unixfilemap.c
+++ b/contrib/expat/xmlwf/unixfilemap.c
@@ -10,7 +10,7 @@
Copyright (c) 2000 Clark Cooper <coopercc@users.sourceforge.net>
Copyright (c) 2001-2002 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
Copyright (c) 2006 Karl Waclawek <karl@waclawek.net>
- Copyright (c) 2016-2017 Sebastian Pipping <sebastian@pipping.org>
+ Copyright (c) 2016-2025 Sebastian Pipping <sebastian@pipping.org>
Copyright (c) 2017 Rhodri James <rhodri@wildebeest.org.uk>
Licensed under the MIT license:
@@ -41,6 +41,7 @@
#include <errno.h>
#include <string.h>
#include <stdio.h>
+#include <stdlib.h> // NULL
#include <unistd.h>
#ifndef MAP_FILE
@@ -93,8 +94,7 @@ filemap(const tchar *name,
close(fd);
return 1;
}
- p = (void *)mmap((void *)0, (size_t)nbytes, PROT_READ, MAP_FILE | MAP_PRIVATE,
- fd, (off_t)0);
+ p = mmap(NULL, nbytes, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, (off_t)0);
if (p == (void *)-1) {
tperror(name);
close(fd);
diff --git a/contrib/expat/xmlwf/xmlfile.c b/contrib/expat/xmlwf/xmlfile.c
index 9c4f7f8dbadd..ce0b61217ed7 100644
--- a/contrib/expat/xmlwf/xmlfile.c
+++ b/contrib/expat/xmlwf/xmlfile.c
@@ -11,7 +11,7 @@
Copyright (c) 2002-2003 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
Copyright (c) 2004-2006 Karl Waclawek <karl@waclawek.net>
Copyright (c) 2005-2007 Steven Solie <steven@solie.ca>
- Copyright (c) 2016-2023 Sebastian Pipping <sebastian@pipping.org>
+ Copyright (c) 2016-2025 Sebastian Pipping <sebastian@pipping.org>
Copyright (c) 2017 Rhodri James <rhodri@wildebeest.org.uk>
Copyright (c) 2019 David Loffredo <loffredo@steptools.com>
Copyright (c) 2021 Donghee Na <donghee.na@python.org>
@@ -56,12 +56,19 @@
#include "xmltchar.h"
#include "filemap.h"
+/* Function "read": */
#if defined(_MSC_VER)
# include <io.h>
-#endif
-
-#ifdef HAVE_UNISTD_H
+/* https://msdn.microsoft.com/en-us/library/wyssk1bs(v=vs.100).aspx */
+# define EXPAT_read _read
+# define EXPAT_read_count_t int
+# define EXPAT_read_req_t unsigned int
+#else /* POSIX */
# include <unistd.h>
+/* https://pubs.opengroup.org/onlinepubs/009695399/functions/read.html */
+# define EXPAT_read read
+# define EXPAT_read_count_t ssize_t
+# define EXPAT_read_req_t size_t
#endif
#ifndef O_BINARY
@@ -89,8 +96,8 @@ reportError(XML_Parser parser, const XML_Char *filename) {
ftprintf(stdout,
T("%s") T(":%") T(XML_FMT_INT_MOD) T("u") T(":%")
T(XML_FMT_INT_MOD) T("u") T(": %s\n"),
- filename, XML_GetErrorLineNumber(parser),
- XML_GetErrorColumnNumber(parser), message);
+ filename, XML_GetCurrentLineNumber(parser),
+ XML_GetCurrentColumnNumber(parser), message);
else
ftprintf(stderr, T("%s: (unknown message %u)\n"), filename,
(unsigned int)code);
@@ -192,7 +199,7 @@ processStream(const XML_Char *filename, XML_Parser parser) {
}
}
for (;;) {
- int nread;
+ EXPAT_read_count_t nread;
char *buf = (char *)XML_GetBuffer(parser, g_read_size_bytes);
if (! buf) {
if (filename != NULL)
@@ -201,14 +208,14 @@ processStream(const XML_Char *filename, XML_Parser parser) {
filename != NULL ? filename : T("xmlwf"));
return 0;
}
- nread = read(fd, buf, g_read_size_bytes);
+ nread = EXPAT_read(fd, buf, (EXPAT_read_req_t)g_read_size_bytes);
if (nread < 0) {
tperror(filename != NULL ? filename : T("STDIN"));
if (filename != NULL)
close(fd);
return 0;
}
- if (XML_ParseBuffer(parser, nread, nread == 0) == XML_STATUS_ERROR) {
+ if (XML_ParseBuffer(parser, (int)nread, nread == 0) == XML_STATUS_ERROR) {
reportError(parser, filename != NULL ? filename : T("STDIN"));
if (filename != NULL)
close(fd);
diff --git a/contrib/expat/xmlwf/xmlwf.c b/contrib/expat/xmlwf/xmlwf.c
index 7c0a8cd4d6a4..534f32170590 100644
--- a/contrib/expat/xmlwf/xmlwf.c
+++ b/contrib/expat/xmlwf/xmlwf.c
@@ -11,7 +11,7 @@
Copyright (c) 2001-2003 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
Copyright (c) 2004-2009 Karl Waclawek <karl@waclawek.net>
Copyright (c) 2005-2007 Steven Solie <steven@solie.ca>
- Copyright (c) 2016-2023 Sebastian Pipping <sebastian@pipping.org>
+ Copyright (c) 2016-2025 Sebastian Pipping <sebastian@pipping.org>
Copyright (c) 2017 Rhodri James <rhodri@wildebeest.org.uk>
Copyright (c) 2019 David Loffredo <loffredo@steptools.com>
Copyright (c) 2020 Joe Orton <jorton@redhat.com>
@@ -305,7 +305,7 @@ static XML_Char *
xcsdup(const XML_Char *s) {
XML_Char *result;
int count = 0;
- int numBytes;
+ size_t numBytes;
/* Get the length of the string, including terminator */
while (s[count++] != 0) {
@@ -913,11 +913,11 @@ usage(const XML_Char *prog, int rc) {
T(" -t write no XML output for [t]iming of plain parsing\n")
T(" -N enable adding doctype and [n]otation declarations\n")
T("\n")
- T("billion laughs attack protection:\n")
+ T("amplification attack protection (e.g. billion laughs):\n")
T(" NOTE: If you ever need to increase these values for non-attack payload, please file a bug report.\n")
T("\n")
T(" -a FACTOR set maximum tolerated [a]mplification factor (default: 100.0)\n")
- T(" -b BYTES set number of output [b]ytes needed to activate (default: 8 MiB)\n")
+ T(" -b BYTES set number of output [b]ytes needed to activate (default: 8 MiB/64 MiB)\n")
T("\n")
T("reparse deferral:\n")
T(" -q disable reparse deferral, and allow [q]uadratic parse runtime with large tokens\n")
@@ -926,6 +926,16 @@ usage(const XML_Char *prog, int rc) {
T(" -h, --help show this [h]elp message and exit\n")
T(" -v, --version show program's [v]ersion number and exit\n")
T("\n")
+ T("environment variables:\n")
+ T(" EXPAT_ACCOUNTING_DEBUG=(0|1|2|3)\n")
+ T(" Control verbosity of accounting debugging (default: 0)\n")
+ T(" EXPAT_ENTITY_DEBUG=(0|1)\n")
+ T(" Control verbosity of entity debugging (default: 0)\n")
+ T(" EXPAT_ENTROPY_DEBUG=(0|1)\n")
+ T(" Control verbosity of entropy debugging (default: 0)\n")
+ T(" EXPAT_MALLOC_DEBUG=(0|1|2)\n")
+ T(" Control verbosity of allocation tracker (default: 0)\n")
+ T("\n")
T("exit status:\n")
T(" 0 the input files are well-formed and the output (if requested) was written successfully\n")
T(" 1 could not allocate data structures, signals a serious problem with execution environment\n")
@@ -1171,12 +1181,15 @@ tmain(int argc, XML_Char **argv) {
#if XML_GE == 1
XML_SetBillionLaughsAttackProtectionMaximumAmplification(
parser, attackMaximumAmplification);
+ XML_SetAllocTrackerMaximumAmplification(parser,
+ attackMaximumAmplification);
#endif
}
if (attackThresholdGiven) {
#if XML_GE == 1
XML_SetBillionLaughsAttackProtectionActivationThreshold(
parser, attackThresholdBytes);
+ XML_SetAllocTrackerActivationThreshold(parser, attackThresholdBytes);
#else
(void)attackThresholdBytes; // silence -Wunused-but-set-variable
#endif
diff --git a/contrib/expat/xmlwf/xmlwf_helpgen.py b/contrib/expat/xmlwf/xmlwf_helpgen.py
index 3d32f5d148b7..71f7baa43396 100755
--- a/contrib/expat/xmlwf/xmlwf_helpgen.py
+++ b/contrib/expat/xmlwf/xmlwf_helpgen.py
@@ -6,7 +6,7 @@
# \___/_/\_\ .__/ \__,_|\__|
# |_| XML parser
#
-# Copyright (c) 2019-2023 Sebastian Pipping <sebastian@pipping.org>
+# Copyright (c) 2019-2025 Sebastian Pipping <sebastian@pipping.org>
# Copyright (c) 2021 Tim Bray <tbray@textuality.com>
# Licensed under the MIT license:
#
@@ -32,6 +32,16 @@
import argparse
epilog = """
+environment variables:
+ EXPAT_ACCOUNTING_DEBUG=(0|1|2|3)
+ Control verbosity of accounting debugging (default: 0)
+ EXPAT_ENTITY_DEBUG=(0|1)
+ Control verbosity of entity debugging (default: 0)
+ EXPAT_ENTROPY_DEBUG=(0|1)
+ Control verbosity of entropy debugging (default: 0)
+ EXPAT_MALLOC_DEBUG=(0|1|2)
+ Control verbosity of allocation tracker (default: 0)
+
exit status:
0 the input files are well-formed and the output (if requested) was written successfully
1 could not allocate data structures, signals a serious problem with execution environment
@@ -74,16 +84,16 @@ output_mode.add_argument('-m', action='store_true', help='write [m]eta XML, not
output_mode.add_argument('-t', action='store_true', help='write no XML output for [t]iming of plain parsing')
output_related.add_argument('-N', action='store_true', help='enable adding doctype and [n]otation declarations')
-billion_laughs = parser.add_argument_group('billion laughs attack protection',
+billion_laughs = parser.add_argument_group('amplification attack protection (e.g. billion laughs)',
description='NOTE: '
'If you ever need to increase these values '
'for non-attack payload, please file a bug report.')
billion_laughs.add_argument('-a', metavar='FACTOR',
help='set maximum tolerated [a]mplification factor (default: 100.0)')
-billion_laughs.add_argument('-b', metavar='BYTES', help='set number of output [b]ytes needed to activate (default: 8 MiB)')
+billion_laughs.add_argument('-b', metavar='BYTES', help='set number of output [b]ytes needed to activate (default: 8 MiB/64 MiB)')
reparse_deferral = parser.add_argument_group('reparse deferral')
-reparse_deferral.add_argument('-q', metavar='FACTOR',
+reparse_deferral.add_argument('-q', action='store_true',
help='disable reparse deferral, and allow [q]uadratic parse runtime with large tokens')
parser.add_argument('files', metavar='FILE', nargs='*', help='file to process (default: STDIN)')
diff --git a/contrib/jemalloc/ChangeLog b/contrib/jemalloc/ChangeLog
index e55813b7becc..32fde56247f6 100644
--- a/contrib/jemalloc/ChangeLog
+++ b/contrib/jemalloc/ChangeLog
@@ -4,6 +4,106 @@ brevity. Much more detail can be found in the git revision history:
https://github.com/jemalloc/jemalloc
+* 5.3.0 (May 6, 2022)
+
+ This release contains many speed and space optimizations, from micro
+ optimizations on common paths to rework of internal data structures and
+ locking schemes, and many more too detailed to list below. Multiple percent
+ of system level metric improvements were measured in tested production
+ workloads. The release has gone through large-scale production testing.
+
+ New features:
+ - Add the thread.idle mallctl which hints that the calling thread will be
+ idle for a nontrivial period of time. (@davidtgoldblatt)
+ - Allow small size classes to be the maximum size class to cache in the
+ thread-specific cache, through the opt.[lg_]tcache_max option. (@interwq,
+ @jordalgo)
+ - Make the behavior of realloc(ptr, 0) configurable with opt.zero_realloc.
+ (@davidtgoldblatt)
+ - Add 'make uninstall' support. (@sangshuduo, @Lapenkov)
+ - Support C++17 over-aligned allocation. (@marksantaniello)
+ - Add the thread.peak mallctl for approximate per-thread peak memory tracking.
+ (@davidtgoldblatt)
+ - Add interval-based stats output opt.stats_interval. (@interwq)
+ - Add prof.prefix to override filename prefixes for dumps. (@zhxchen17)
+ - Add high resolution timestamp support for profiling. (@tyroguru)
+ - Add the --collapsed flag to jeprof for flamegraph generation.
+ (@igorwwwwwwwwwwwwwwwwwwww)
+ - Add the --debug-syms-by-id option to jeprof for debug symbols discovery.
+ (@DeannaGelbart)
+ - Add the opt.prof_leak_error option to exit with error code when leak is
+ detected using opt.prof_final. (@yunxuo)
+ - Add opt.cache_oblivious as an runtime alternative to config.cache_oblivious.
+ (@interwq)
+ - Add mallctl interfaces:
+ + opt.zero_realloc (@davidtgoldblatt)
+ + opt.cache_oblivious (@interwq)
+ + opt.prof_leak_error (@yunxuo)
+ + opt.stats_interval (@interwq)
+ + opt.stats_interval_opts (@interwq)
+ + opt.tcache_max (@interwq)
+ + opt.trust_madvise (@azat)
+ + prof.prefix (@zhxchen17)
+ + stats.zero_reallocs (@davidtgoldblatt)
+ + thread.idle (@davidtgoldblatt)
+ + thread.peak.{read,reset} (@davidtgoldblatt)
+
+ Bug fixes:
+ - Fix the synchronization around explicit tcache creation which could cause
+ invalid tcache identifiers. This regression was first released in 5.0.0.
+ (@yoshinorim, @davidtgoldblatt)
+ - Fix a profiling biasing issue which could cause incorrect heap usage and
+ object counts. This issue existed in all previous releases with the heap
+ profiling feature. (@davidtgoldblatt)
+ - Fix the order of stats counter updating on large realloc which could cause
+ failed assertions. This regression was first released in 5.0.0. (@azat)
+ - Fix the locking on the arena destroy mallctl, which could cause concurrent
+ arena creations to fail. This functionality was first introduced in 5.0.0.
+ (@interwq)
+
+ Portability improvements:
+ - Remove nothrow from system function declarations on macOS and FreeBSD.
+ (@davidtgoldblatt, @fredemmott, @leres)
+ - Improve overcommit and page alignment settings on NetBSD. (@zoulasc)
+ - Improve CPU affinity support on BSD platforms. (@devnexen)
+ - Improve utrace detection and support. (@devnexen)
+ - Improve QEMU support with MADV_DONTNEED zeroed pages detection. (@azat)
+ - Add memcntl support on Solaris / illumos. (@devnexen)
+ - Improve CPU_SPINWAIT on ARM. (@AWSjswinney)
+ - Improve TSD cleanup on FreeBSD. (@Lapenkov)
+ - Disable percpu_arena if the CPU count cannot be reliably detected. (@azat)
+ - Add malloc_size(3) override support. (@devnexen)
+ - Add mmap VM_MAKE_TAG support. (@devnexen)
+ - Add support for MADV_[NO]CORE. (@devnexen)
+ - Add support for DragonFlyBSD. (@devnexen)
+ - Fix the QUANTUM setting on MIPS64. (@brooksdavis)
+ - Add the QUANTUM setting for ARC. (@vineetgarc)
+ - Add the QUANTUM setting for LoongArch. (@wangjl-uos)
+ - Add QNX support. (@jqian-aurora)
+ - Avoid atexit(3) calls unless the relevant profiling features are enabled.
+ (@BusyJay, @laiwei-rice, @interwq)
+ - Fix unknown option detection when using Clang. (@Lapenkov)
+ - Fix symbol conflict with musl libc. (@georgthegreat)
+ - Add -Wimplicit-fallthrough checks. (@nickdesaulniers)
+ - Add __forceinline support on MSVC. (@santagada)
+ - Improve FreeBSD and Windows CI support. (@Lapenkov)
+ - Add CI support for PPC64LE architecture. (@ezeeyahoo)
+
+ Incompatible changes:
+ - Maximum size class allowed in tcache (opt.[lg_]tcache_max) now has an upper
+ bound of 8MiB. (@interwq)
+
+ Optimizations and refactors (@davidtgoldblatt, @Lapenkov, @interwq):
+ - Optimize the common cases of the thread cache operations.
+ - Optimize internal data structures, including RB tree and pairing heap.
+ - Optimize the internal locking on extent management.
+ - Extract and refactor the internal page allocator and interface modules.
+
+ Documentation:
+ - Fix doc build with --with-install-suffix. (@lawmurray, @interwq)
+ - Add PROFILING_INTERNALS.md. (@davidtgoldblatt)
+ - Ensure the proper order of doc building and installation. (@Mingli-Yu)
+
* 5.2.1 (August 5, 2019)
This release is primarily about Windows. A critical virtual memory leak is
diff --git a/contrib/jemalloc/FREEBSD-upgrade b/contrib/jemalloc/FREEBSD-upgrade
index d3173b9d1f36..fcb66ea71786 100755
--- a/contrib/jemalloc/FREEBSD-upgrade
+++ b/contrib/jemalloc/FREEBSD-upgrade
@@ -1,189 +1,91 @@
#!/bin/sh
-#
-# Usage: cd /usr/src/contrib/jemalloc
-# ./FREEBSD-upgrade <command> [args]
-#
-# At least the following ports are required when importing jemalloc:
-# - devel/autoconf
-# - devel/git
-# - devel/gmake
-# - textproc/docbook-xsl
-# - textproc/libxslt
-#
-# The normal workflow for importing a new release is:
-#
-# cd /usr/src/contrib/jemalloc
-#
-# Merge local changes that were made since the previous import:
-#
-# ./FREEBSD-upgrade merge-changes
-# ./FREEBSD-upgrade rediff
-#
-# Extract latest jemalloc release.
-#
-# ./FREEBSD-upgrade extract <rev>
-#
-# Fix patch conflicts as necessary, then regenerate diffs to update line
-# offsets:
-#
-# ./FREEBSD-upgrade rediff
-# ./FREEBSD-upgrade extract <rev>
-#
-# Do multiple buildworld/installworld rounds. If problems arise and patches
-# are needed, edit the code in ${work} as necessary, then:
-#
-# ./FREEBSD-upgrade rediff
-# ./FREEBSD-upgrade extract <rev>
-#
-# The rediff/extract order is important because rediff saves the local
-# changes, then extract blows away the work tree and re-creates it with the
-# diffs applied.
-#
-# Finally, to clean up:
-#
-# ./FREEBSD-upgrade clean
-set -e
-set -x
+# Note: you need docbook installed, as well as gmake (we need it to
+# make private_namespace.h)
-if [ ! -x "FREEBSD-upgrade" ] ; then
- echo "Run from within src/contrib/jemalloc/" >&2
- exit 1
-fi
+# git subtree merge -- not committed at this time.
+ git subtree merge -P contrib/jemalloc vendor/jemalloc
+cd contrib/jemalloc
-if [ "x${JEMALLOC_REPO}" = "x" ] ; then
- JEMALLOC_REPO=https://github.com/jemalloc/jemalloc.git
-fi
+# Gut the tests, since they take up too much space.
+# Everything else can stay, but if not, add more to trim (there's
+# always a trade off between time and saved size.
+git rm -rf test msvc
+git commit --amend
-src=`pwd`
+# kill the tests with empty files so we don't have to hack configure.ac
+mkdir -p test/include/test
+touch test/include/test/jemalloc_test_defs.h.in
+touch test/include/test/jemalloc_test.h.in
+echo 'exit 0' > test/test.sh.in
-jemalloc_tmp="jemalloc.tmp"
-tmpdir="${src}/../${jemalloc_tmp}"
-bare_repo="${tmpdir}/jemalloc_bare.git"
-work="jemalloc_work.git"
-work_repo="${tmpdir}/${work}"
-namespace_repo="${tmpdir}/jemalloc_namespace.git"
-changes="${src}/FREEBSD-changes"
-
-do_fetch() {
- local rev=$1
- if [ ! -d "${bare_repo}" ] ; then
- mkdir -p "${bare_repo}"
- git clone --bare ${JEMALLOC_REPO} ${bare_repo}
- fi
- (
- cd ${bare_repo}
- git fetch origin ${rev}
- )
-}
+# Reconfigure -- needed only to regenerate the .h files... We don't
+# use all the files generated.
+#
+# Also note: 5.2 lacks --with-lg-page-sizes, but 5.3 has it.
+# Also, there's got to be a way to not hard-wire version / hash.
+./autogen.sh --enable-xmalloc --enable-fill --enable-lazy-lock --enable-stats \
+ --enable-utrace --with-malloc-conf=abort_conf:false \
+ --with-xslroot=/usr/local/share/xsl/docbook --with-private-namespace=__ \
+ --with-lg-page-sizes=12,13,14,15,16 \
+ --with-version=5.3.0-0-g54eaed1d8b56b1aa528be3bdd1877e59c56fa90c
-do_extract_helper() {
- local rev=$1
- local repo=$2
- do_fetch ${rev}
- rm -rf ${repo}
- git clone ${bare_repo} ${repo}
- (
- cd ${repo}
- if [ "x${rev}" != "x" ] ; then
- # Use optional rev argument to check out a revision other than HEAD on
- # master.
- git checkout ${rev}
- fi
- )
-}
+# Copy over the important generated .h files in configure
+cp ./include/jemalloc/jemalloc.h ../../lib/libc/stdlib/malloc/jemalloc/include/jemalloc
+git add ../../lib/libc/stdlib/malloc/jemalloc/include/jemalloc/jemalloc.h
+cp ./include/jemalloc/jemalloc_defs.h ../../lib/libc/stdlib/malloc/jemalloc/include/jemalloc
+git add ../../lib/libc/stdlib/malloc/jemalloc/include/jemalloc/jemalloc_defs.h
-do_autogen() {
- ./autogen.sh --enable-xmalloc --enable-utrace \
- --with-malloc-conf=abort_conf:false \
- --with-xslroot=/usr/local/share/xsl/docbook --with-private-namespace=__ \
- --with-lg-page-sizes=12,13,14,16
-}
+# need to make the namespace .h files, and copy a small subset into the tree
+# These are super-awkward to generate at buildworld time. Also, we assume we
+# only have to make one of these (currently true due to current unlikely to
+# change dependencies.
+gmake include/jemalloc/internal/private_namespace.h
+for i in private_namespace.h jemalloc_internal_defs.h public_namespace.h jemalloc_preamble.h; do
+ cp include/jemalloc/internal/$i ../../lib/libc/stdlib/malloc/jemalloc/include/jemalloc/internal/
+ git add ../../lib/libc/stdlib/malloc/jemalloc/include/jemalloc/internal/$i
+ rm include/jemalloc/internal/$i
+done
+# OK, commit all the generated files
+git add VERSION
+git commit --amend
-do_extract_diff() {
- local rev=$1
- local repo=$2
- do_extract_helper ${rev} ${repo}
- (
- cd ${repo}
- # Apply diffs before generating files.
- patch -p1 < "${src}/FREEBSD-diffs"
- find . -name '*.orig' -delete
- # Generate files.
- do_autogen
- gmake dist
- )
-}
+# Clean up the mess
+git clean -f .
-do_extract_namespace() {
- local rev=$1
- local repo=$2
- do_extract_helper ${rev} ${repo}
- (
- cd ${repo}
- # Generate files.
- do_autogen
- gmake include/jemalloc/internal/private_namespace.h
- )
-}
+# Save the cheat sheet
+cp ~/jemalloc-upd FREEBSD-upgrade
+git add FREEBSD-upgrade
+git commit --amend
-do_extract() {
- local rev=$1
- do_fetch ${rev}
- do_extract_diff ${rev} ${work_repo}
- do_extract_namespace ${rev} ${namespace_repo}
-}
+# Remove hash.c from lib/libc/stdlib/malloc/jemalloc/Makefile.inc
+# mutex_pool.c prng.c
+# Add
+# bin_info.c san.c san_bump.c counter.c prof_data.c prof_log.c prof_recent.c prof_stats.c prof_sys.c
+# emap.c edata.c edata_cache.c pa.c pa_extra.c pac.c decay.c hpa.c hpa_hooks.c fxp.c hpdata.c pai.c
+# ecache.c ehooks.c eset.c sec.c cache_bin.c peak_event.c psset.c inspect.c exp_grow.c thread_event.c
+#
-do_diff() {
- (
- cd ${work_repo}
- find . -name '*.orig' -delete
- find . -name '*.rej' -delete
- git add -A
- git diff --cached
- ) > FREEBSD-diffs
-}
+# Manually comment out the following in lib/libc/stdlib/malloc/jemalloc/include/jemalloc/jemalloc.h
+# /* #define JEMALLOC_HAVE_ATTR_FORMAT_GNU_PRINTF */
+# Add
+# #define JEMALLOC_OVERRIDE_VALLOC
+# and #include "jemalloc_FreeBSD.h"
+# With some adjustments to the old jemalloc_FreeBSD.h, but git can help
-command=$1
-shift
-case "${command}" in
- merge-changes) # Merge local changes that were made since the previous import.
- rev=`cat VERSION |tr 'g' ' ' |awk '{print $2}'`
- # Extract code corresponding to most recent import.
- do_extract ${rev}
- # Compute local differences to the upstream+patches and apply them.
- (
- cd ${tmpdir}
- diff -ru -X ${src}/FREEBSD-Xlist ${work} ../jemalloc > ${changes} || true
- )
- (
- cd ${work_repo}
- patch -p1 < ${changes} || true
- find . -name '*.orig' -delete
- )
- # Update diff.
- do_diff
- ;;
- extract) # Extract upstream sources, apply patches, copy to contrib/jemalloc.
- rev=$1
- do_extract ${rev}
- # Delete existing files so that cruft doesn't silently remain.
- rm -rf ChangeLog COPYING VERSION doc include src
- # Copy files over.
- tar cf - -C ${work_repo} -X FREEBSD-Xlist . |tar xvf -
- internal_dir="include/jemalloc/internal"
- grep -v ' isthreaded ' \
- "${namespace_repo}/${internal_dir}/private_namespace.h" \
- > "${internal_dir}/private_namespace.h"
- ;;
- rediff) # Regenerate diffs based on working tree.
- do_diff
- ;;
- clean) # Remove working tree and temporary files.
- rm -rf ${tmpdir} ${changes}
- ;;
- *)
- echo "Unsupported command: \"${command}\"" >&2
- exit 1
- ;;
-esac
+# Had to manually remove
+# -#define __malloc_options_1_0 JEMALLOC_N(__malloc_options_1_0)
+# -#define _malloc_first_thread JEMALLOC_N(_malloc_first_thread)
+# -#define __malloc_message_1_0 JEMALLOC_N(__malloc_message_1_0)
+# -#define isthreaded JEMALLOC_N(isthreaded)
+#
+# Also had to remove the following to fix jemalloc 3 ABI compat
+# -#define je_allocm JEMALLOC_N(je_allocm)
+# -#define je_dallocm JEMALLOC_N(je_dallocm)
+# -#define je_nallocm JEMALLOC_N(je_nallocm)
+# -#define je_rallocm JEMALLOC_N(je_rallocm)
+# -#define je_sallocm JEMALLOC_N(je_sallocm)
+# Without the diff you end up with non-exported _je_je*allocm symbols. With you get symbols of the form:
+# 365: 000000000018e2a0 406 FUNC WEAK DEFAULT 14 rallocm@FBSD_1.3 (5)
+# 657: 000000000018e2a0 406 FUNC GLOBAL DEFAULT 14 __rallocm@FBSD_1.3 (5)
+#
diff --git a/contrib/jemalloc/INSTALL.md b/contrib/jemalloc/INSTALL.md
new file mode 100644
index 000000000000..90da718d2b6a
--- /dev/null
+++ b/contrib/jemalloc/INSTALL.md
@@ -0,0 +1,424 @@
+Building and installing a packaged release of jemalloc can be as simple as
+typing the following while in the root directory of the source tree:
+
+ ./configure
+ make
+ make install
+
+If building from unpackaged developer sources, the simplest command sequence
+that might work is:
+
+ ./autogen.sh
+ make
+ make install
+
+You can uninstall the installed build artifacts like this:
+
+ make uninstall
+
+Notes:
+ - "autoconf" needs to be installed
+ - Documentation is built by the default target only when xsltproc is
+available. Build will warn but not stop if the dependency is missing.
+
+
+## Advanced configuration
+
+The 'configure' script supports numerous options that allow control of which
+functionality is enabled, where jemalloc is installed, etc. Optionally, pass
+any of the following arguments (not a definitive list) to 'configure':
+
+* `--help`
+
+ Print a definitive list of options.
+
+* `--prefix=<install-root-dir>`
+
+ Set the base directory in which to install. For example:
+
+ ./configure --prefix=/usr/local
+
+ will cause files to be installed into /usr/local/include, /usr/local/lib,
+ and /usr/local/man.
+
+* `--with-version=(<major>.<minor>.<bugfix>-<nrev>-g<gid>|VERSION)`
+
+ The VERSION file is mandatory for successful configuration, and the
+ following steps are taken to assure its presence:
+ 1) If --with-version=<major>.<minor>.<bugfix>-<nrev>-g<gid> is specified,
+ generate VERSION using the specified value.
+ 2) If --with-version is not specified in either form and the source
+ directory is inside a git repository, try to generate VERSION via 'git
+ describe' invocations that pattern-match release tags.
+ 3) If VERSION is missing, generate it with a bogus version:
+ 0.0.0-0-g0000000000000000000000000000000000000000
+
+ Note that --with-version=VERSION bypasses (1) and (2), which simplifies
+ VERSION configuration when embedding a jemalloc release into another
+ project's git repository.
+
+* `--with-rpath=<colon-separated-rpath>`
+
+ Embed one or more library paths, so that libjemalloc can find the libraries
+ it is linked to. This works only on ELF-based systems.
+
+* `--with-mangling=<map>`
+
+ Mangle public symbols specified in <map> which is a comma-separated list of
+ name:mangled pairs.
+
+ For example, to use ld's --wrap option as an alternative method for
+ overriding libc's malloc implementation, specify something like:
+
+ --with-mangling=malloc:__wrap_malloc,free:__wrap_free[...]
+
+ Note that mangling happens prior to application of the prefix specified by
+ --with-jemalloc-prefix, and mangled symbols are then ignored when applying
+ the prefix.
+
+* `--with-jemalloc-prefix=<prefix>`
+
+ Prefix all public APIs with <prefix>. For example, if <prefix> is
+ "prefix_", API changes like the following occur:
+
+ malloc() --> prefix_malloc()
+ malloc_conf --> prefix_malloc_conf
+ /etc/malloc.conf --> /etc/prefix_malloc.conf
+ MALLOC_CONF --> PREFIX_MALLOC_CONF
+
+ This makes it possible to use jemalloc at the same time as the system
+ allocator, or even to use multiple copies of jemalloc simultaneously.
+
+ By default, the prefix is "", except on OS X, where it is "je_". On OS X,
+ jemalloc overlays the default malloc zone, but makes no attempt to actually
+ replace the "malloc", "calloc", etc. symbols.
+
+* `--without-export`
+
+ Don't export public APIs. This can be useful when building jemalloc as a
+ static library, or to avoid exporting public APIs when using the zone
+ allocator on OSX.
+
+* `--with-private-namespace=<prefix>`
+
+ Prefix all library-private APIs with <prefix>je_. For shared libraries,
+ symbol visibility mechanisms prevent these symbols from being exported, but
+ for static libraries, naming collisions are a real possibility. By
+ default, <prefix> is empty, which results in a symbol prefix of je_ .
+
+* `--with-install-suffix=<suffix>`
+
+ Append <suffix> to the base name of all installed files, such that multiple
+ versions of jemalloc can coexist in the same installation directory. For
+ example, libjemalloc.so.0 becomes libjemalloc<suffix>.so.0.
+
+* `--with-malloc-conf=<malloc_conf>`
+
+ Embed `<malloc_conf>` as a run-time options string that is processed prior to
+ the malloc_conf global variable, the /etc/malloc.conf symlink, and the
+ MALLOC_CONF environment variable. For example, to change the default decay
+ time to 30 seconds:
+
+ --with-malloc-conf=decay_ms:30000
+
+* `--enable-debug`
+
+ Enable assertions and validation code. This incurs a substantial
+ performance hit, but is very useful during application development.
+
+* `--disable-stats`
+
+ Disable statistics gathering functionality. See the "opt.stats_print"
+ option documentation for usage details.
+
+* `--enable-prof`
+
+ Enable heap profiling and leak detection functionality. See the "opt.prof"
+ option documentation for usage details. When enabled, there are several
+ approaches to backtracing, and the configure script chooses the first one
+ in the following list that appears to function correctly:
+
+ + libunwind (requires --enable-prof-libunwind)
+ + libgcc (unless --disable-prof-libgcc)
+ + gcc intrinsics (unless --disable-prof-gcc)
+
+* `--enable-prof-libunwind`
+
+ Use the libunwind library (http://www.nongnu.org/libunwind/) for stack
+ backtracing.
+
+* `--disable-prof-libgcc`
+
+ Disable the use of libgcc's backtracing functionality.
+
+* `--disable-prof-gcc`
+
+ Disable the use of gcc intrinsics for backtracing.
+
+* `--with-static-libunwind=<libunwind.a>`
+
+ Statically link against the specified libunwind.a rather than dynamically
+ linking with -lunwind.
+
+* `--disable-fill`
+
+ Disable support for junk/zero filling of memory. See the "opt.junk" and
+ "opt.zero" option documentation for usage details.
+
+* `--disable-zone-allocator`
+
+ Disable zone allocator for Darwin. This means jemalloc won't be hooked as
+ the default allocator on OSX/iOS.
+
+* `--enable-utrace`
+
+ Enable utrace(2)-based allocation tracing. This feature is not broadly
+ portable (FreeBSD has it, but Linux and OS X do not).
+
+* `--enable-xmalloc`
+
+ Enable support for optional immediate termination due to out-of-memory
+ errors, as is commonly implemented by "xmalloc" wrapper function for malloc.
+ See the "opt.xmalloc" option documentation for usage details.
+
+* `--enable-lazy-lock`
+
+ Enable code that wraps pthread_create() to detect when an application
+ switches from single-threaded to multi-threaded mode, so that it can avoid
+ mutex locking/unlocking operations while in single-threaded mode. In
+ practice, this feature usually has little impact on performance unless
+ thread-specific caching is disabled.
+
+* `--disable-cache-oblivious`
+
+ Disable cache-oblivious large allocation alignment by default, for large
+ allocation requests with no alignment constraints. If this feature is
+ disabled, all large allocations are page-aligned as an implementation
+ artifact, which can severely harm CPU cache utilization. However, the
+ cache-oblivious layout comes at the cost of one extra page per large
+ allocation, which in the most extreme case increases physical memory usage
+ for the 16 KiB size class to 20 KiB.
+
+* `--disable-syscall`
+
+ Disable use of syscall(2) rather than {open,read,write,close}(2). This is
+ intended as a workaround for systems that place security limitations on
+ syscall(2).
+
+* `--disable-cxx`
+
+ Disable C++ integration. This will cause new and delete operator
+ implementations to be omitted.
+
+* `--with-xslroot=<path>`
+
+ Specify where to find DocBook XSL stylesheets when building the
+ documentation.
+
+* `--with-lg-page=<lg-page>`
+
+ Specify the base 2 log of the allocator page size, which must in turn be at
+ least as large as the system page size. By default the configure script
+ determines the host's page size and sets the allocator page size equal to
+ the system page size, so this option need not be specified unless the
+ system page size may change between configuration and execution, e.g. when
+ cross compiling.
+
+* `--with-lg-hugepage=<lg-hugepage>`
+
+ Specify the base 2 log of the system huge page size. This option is useful
+ when cross compiling, or when overriding the default for systems that do
+ not explicitly support huge pages.
+
+* `--with-lg-quantum=<lg-quantum>`
+
+ Specify the base 2 log of the minimum allocation alignment. jemalloc needs
+ to know the minimum alignment that meets the following C standard
+ requirement (quoted from the April 12, 2011 draft of the C11 standard):
+
+ > The pointer returned if the allocation succeeds is suitably aligned so
+ that it may be assigned to a pointer to any type of object with a
+ fundamental alignment requirement and then used to access such an object
+ or an array of such objects in the space allocated [...]
+
+ This setting is architecture-specific, and although jemalloc includes known
+ safe values for the most commonly used modern architectures, there is a
+ wrinkle related to GNU libc (glibc) that may impact your choice of
+ <lg-quantum>. On most modern architectures, this mandates 16-byte
+ alignment (<lg-quantum>=4), but the glibc developers chose not to meet this
+ requirement for performance reasons. An old discussion can be found at
+ <https://sourceware.org/bugzilla/show_bug.cgi?id=206> . Unlike glibc,
+ jemalloc does follow the C standard by default (caveat: jemalloc
+ technically cheats for size classes smaller than the quantum), but the fact
+ that Linux systems already work around this allocator noncompliance means
+ that it is generally safe in practice to let jemalloc's minimum alignment
+ follow glibc's lead. If you specify `--with-lg-quantum=3` during
+ configuration, jemalloc will provide additional size classes that are not
+ 16-byte-aligned (24, 40, and 56).
+
+* `--with-lg-vaddr=<lg-vaddr>`
+
+ Specify the number of significant virtual address bits. By default, the
+ configure script attempts to detect virtual address size on those platforms
+ where it knows how, and picks a default otherwise. This option may be
+ useful when cross-compiling.
+
+* `--disable-initial-exec-tls`
+
+ Disable the initial-exec TLS model for jemalloc's internal thread-local
+ storage (on those platforms that support explicit settings). This can allow
+ jemalloc to be dynamically loaded after program startup (e.g. using dlopen).
+ Note that in this case, there will be two malloc implementations operating
+ in the same process, which will almost certainly result in confusing runtime
+ crashes if pointers leak from one implementation to the other.
+
+* `--disable-libdl`
+
+ Disable the usage of libdl, namely dlsym(3) which is required by the lazy
+ lock option. This can allow building static binaries.
+
+The following environment variables (not a definitive list) impact configure's
+behavior:
+
+* `CFLAGS="?"`
+* `CXXFLAGS="?"`
+
+ Pass these flags to the C/C++ compiler. Any flags set by the configure
+ script are prepended, which means explicitly set flags generally take
+ precedence. Take care when specifying flags such as -Werror, because
+ configure tests may be affected in undesirable ways.
+
+* `EXTRA_CFLAGS="?"`
+* `EXTRA_CXXFLAGS="?"`
+
+ Append these flags to CFLAGS/CXXFLAGS, without passing them to the
+ compiler(s) during configuration. This makes it possible to add flags such
+ as -Werror, while allowing the configure script to determine what other
+ flags are appropriate for the specified configuration.
+
+* `CPPFLAGS="?"`
+
+ Pass these flags to the C preprocessor. Note that CFLAGS is not passed to
+ 'cpp' when 'configure' is looking for include files, so you must use
+ CPPFLAGS instead if you need to help 'configure' find header files.
+
+* `LD_LIBRARY_PATH="?"`
+
+ 'ld' uses this colon-separated list to find libraries.
+
+* `LDFLAGS="?"`
+
+ Pass these flags when linking.
+
+* `PATH="?"`
+
+ 'configure' uses this to find programs.
+
+In some cases it may be necessary to work around configuration results that do
+not match reality. For example, Linux 4.5 added support for the MADV_FREE flag
+to madvise(2), which can cause problems if building on a host with MADV_FREE
+support and deploying to a target without. To work around this, use a cache
+file to override the relevant configuration variable defined in configure.ac,
+e.g.:
+
+ echo "je_cv_madv_free=no" > config.cache && ./configure -C
+
+
+## Advanced compilation
+
+To build only parts of jemalloc, use the following targets:
+
+ build_lib_shared
+ build_lib_static
+ build_lib
+ build_doc_html
+ build_doc_man
+ build_doc
+
+To install only parts of jemalloc, use the following targets:
+
+ install_bin
+ install_include
+ install_lib_shared
+ install_lib_static
+ install_lib_pc
+ install_lib
+ install_doc_html
+ install_doc_man
+ install_doc
+
+To clean up build results to varying degrees, use the following make targets:
+
+ clean
+ distclean
+ relclean
+
+
+## Advanced installation
+
+Optionally, define make variables when invoking make, including (not
+exclusively):
+
+* `INCLUDEDIR="?"`
+
+ Use this as the installation prefix for header files.
+
+* `LIBDIR="?"`
+
+ Use this as the installation prefix for libraries.
+
+* `MANDIR="?"`
+
+ Use this as the installation prefix for man pages.
+
+* `DESTDIR="?"`
+
+ Prepend DESTDIR to INCLUDEDIR, LIBDIR, DATADIR, and MANDIR. This is useful
+ when installing to a different path than was specified via --prefix.
+
+* `CC="?"`
+
+ Use this to invoke the C compiler.
+
+* `CFLAGS="?"`
+
+ Pass these flags to the compiler.
+
+* `CPPFLAGS="?"`
+
+ Pass these flags to the C preprocessor.
+
+* `LDFLAGS="?"`
+
+ Pass these flags when linking.
+
+* `PATH="?"`
+
+ Use this to search for programs used during configuration and building.
+
+
+## Development
+
+If you intend to make non-trivial changes to jemalloc, use the 'autogen.sh'
+script rather than 'configure'. This re-generates 'configure', enables
+configuration dependency rules, and enables re-generation of automatically
+generated source files.
+
+The build system supports using an object directory separate from the source
+tree. For example, you can create an 'obj' directory, and from within that
+directory, issue configuration and build commands:
+
+ autoconf
+ mkdir obj
+ cd obj
+ ../configure --enable-autogen
+ make
+
+
+## Documentation
+
+The manual page is generated in both html and roff formats. Any web browser
+can be used to view the html manual. The roff manual page can be formatted
+prior to installation via the following command:
+
+ nroff -man -t doc/jemalloc.3
diff --git a/contrib/jemalloc/Makefile.in b/contrib/jemalloc/Makefile.in
new file mode 100644
index 000000000000..1193cd859c49
--- /dev/null
+++ b/contrib/jemalloc/Makefile.in
@@ -0,0 +1,762 @@
+# Clear out all vpaths, then set just one (default vpath) for the main build
+# directory.
+vpath
+vpath % .
+
+# Clear the default suffixes, so that built-in rules are not used.
+.SUFFIXES :
+
+SHELL := /bin/sh
+
+CC := @CC@
+CXX := @CXX@
+
+# Configuration parameters.
+DESTDIR =
+BINDIR := $(DESTDIR)@BINDIR@
+INCLUDEDIR := $(DESTDIR)@INCLUDEDIR@
+LIBDIR := $(DESTDIR)@LIBDIR@
+DATADIR := $(DESTDIR)@DATADIR@
+MANDIR := $(DESTDIR)@MANDIR@
+srcroot := @srcroot@
+objroot := @objroot@
+abs_srcroot := @abs_srcroot@
+abs_objroot := @abs_objroot@
+
+# Build parameters.
+CPPFLAGS := @CPPFLAGS@ -I$(objroot)include -I$(srcroot)include
+CONFIGURE_CFLAGS := @CONFIGURE_CFLAGS@
+SPECIFIED_CFLAGS := @SPECIFIED_CFLAGS@
+EXTRA_CFLAGS := @EXTRA_CFLAGS@
+CFLAGS := $(strip $(CONFIGURE_CFLAGS) $(SPECIFIED_CFLAGS) $(EXTRA_CFLAGS))
+CONFIGURE_CXXFLAGS := @CONFIGURE_CXXFLAGS@
+SPECIFIED_CXXFLAGS := @SPECIFIED_CXXFLAGS@
+EXTRA_CXXFLAGS := @EXTRA_CXXFLAGS@
+CXXFLAGS := $(strip $(CONFIGURE_CXXFLAGS) $(SPECIFIED_CXXFLAGS) $(EXTRA_CXXFLAGS))
+LDFLAGS := @LDFLAGS@
+EXTRA_LDFLAGS := @EXTRA_LDFLAGS@
+LIBS := @LIBS@
+RPATH_EXTRA := @RPATH_EXTRA@
+SO := @so@
+IMPORTLIB := @importlib@
+O := @o@
+A := @a@
+EXE := @exe@
+LIBPREFIX := @libprefix@
+REV := @rev@
+install_suffix := @install_suffix@
+ABI := @abi@
+XSLTPROC := @XSLTPROC@
+XSLROOT := @XSLROOT@
+AUTOCONF := @AUTOCONF@
+_RPATH = @RPATH@
+RPATH = $(if $(1),$(call _RPATH,$(1)))
+cfghdrs_in := $(addprefix $(srcroot),@cfghdrs_in@)
+cfghdrs_out := @cfghdrs_out@
+cfgoutputs_in := $(addprefix $(srcroot),@cfgoutputs_in@)
+cfgoutputs_out := @cfgoutputs_out@
+enable_autogen := @enable_autogen@
+enable_doc := @enable_doc@
+enable_shared := @enable_shared@
+enable_static := @enable_static@
+enable_prof := @enable_prof@
+enable_zone_allocator := @enable_zone_allocator@
+enable_experimental_smallocx := @enable_experimental_smallocx@
+MALLOC_CONF := @JEMALLOC_CPREFIX@MALLOC_CONF
+link_whole_archive := @link_whole_archive@
+DSO_LDFLAGS = @DSO_LDFLAGS@
+SOREV = @SOREV@
+PIC_CFLAGS = @PIC_CFLAGS@
+CTARGET = @CTARGET@
+LDTARGET = @LDTARGET@
+TEST_LD_MODE = @TEST_LD_MODE@
+MKLIB = @MKLIB@
+AR = @AR@
+ARFLAGS = @ARFLAGS@
+DUMP_SYMS = @DUMP_SYMS@
+AWK := @AWK@
+CC_MM = @CC_MM@
+LM := @LM@
+INSTALL = @INSTALL@
+
+ifeq (macho, $(ABI))
+TEST_LIBRARY_PATH := DYLD_FALLBACK_LIBRARY_PATH="$(objroot)lib"
+else
+ifeq (pecoff, $(ABI))
+TEST_LIBRARY_PATH := PATH="$(PATH):$(objroot)lib"
+else
+TEST_LIBRARY_PATH :=
+endif
+endif
+
+LIBJEMALLOC := $(LIBPREFIX)jemalloc$(install_suffix)
+
+# Lists of files.
+BINS := $(objroot)bin/jemalloc-config $(objroot)bin/jemalloc.sh $(objroot)bin/jeprof
+C_HDRS := $(objroot)include/jemalloc/jemalloc$(install_suffix).h
+C_SRCS := $(srcroot)src/jemalloc.c \
+ $(srcroot)src/arena.c \
+ $(srcroot)src/background_thread.c \
+ $(srcroot)src/base.c \
+ $(srcroot)src/bin.c \
+ $(srcroot)src/bin_info.c \
+ $(srcroot)src/bitmap.c \
+ $(srcroot)src/buf_writer.c \
+ $(srcroot)src/cache_bin.c \
+ $(srcroot)src/ckh.c \
+ $(srcroot)src/counter.c \
+ $(srcroot)src/ctl.c \
+ $(srcroot)src/decay.c \
+ $(srcroot)src/div.c \
+ $(srcroot)src/ecache.c \
+ $(srcroot)src/edata.c \
+ $(srcroot)src/edata_cache.c \
+ $(srcroot)src/ehooks.c \
+ $(srcroot)src/emap.c \
+ $(srcroot)src/eset.c \
+ $(srcroot)src/exp_grow.c \
+ $(srcroot)src/extent.c \
+ $(srcroot)src/extent_dss.c \
+ $(srcroot)src/extent_mmap.c \
+ $(srcroot)src/fxp.c \
+ $(srcroot)src/san.c \
+ $(srcroot)src/san_bump.c \
+ $(srcroot)src/hook.c \
+ $(srcroot)src/hpa.c \
+ $(srcroot)src/hpa_hooks.c \
+ $(srcroot)src/hpdata.c \
+ $(srcroot)src/inspect.c \
+ $(srcroot)src/large.c \
+ $(srcroot)src/log.c \
+ $(srcroot)src/malloc_io.c \
+ $(srcroot)src/mutex.c \
+ $(srcroot)src/nstime.c \
+ $(srcroot)src/pa.c \
+ $(srcroot)src/pa_extra.c \
+ $(srcroot)src/pai.c \
+ $(srcroot)src/pac.c \
+ $(srcroot)src/pages.c \
+ $(srcroot)src/peak_event.c \
+ $(srcroot)src/prof.c \
+ $(srcroot)src/prof_data.c \
+ $(srcroot)src/prof_log.c \
+ $(srcroot)src/prof_recent.c \
+ $(srcroot)src/prof_stats.c \
+ $(srcroot)src/prof_sys.c \
+ $(srcroot)src/psset.c \
+ $(srcroot)src/rtree.c \
+ $(srcroot)src/safety_check.c \
+ $(srcroot)src/sc.c \
+ $(srcroot)src/sec.c \
+ $(srcroot)src/stats.c \
+ $(srcroot)src/sz.c \
+ $(srcroot)src/tcache.c \
+ $(srcroot)src/test_hooks.c \
+ $(srcroot)src/thread_event.c \
+ $(srcroot)src/ticker.c \
+ $(srcroot)src/tsd.c \
+ $(srcroot)src/witness.c
+ifeq ($(enable_zone_allocator), 1)
+C_SRCS += $(srcroot)src/zone.c
+endif
+ifeq ($(IMPORTLIB),$(SO))
+STATIC_LIBS := $(objroot)lib/$(LIBJEMALLOC).$(A)
+endif
+ifdef PIC_CFLAGS
+STATIC_LIBS += $(objroot)lib/$(LIBJEMALLOC)_pic.$(A)
+else
+STATIC_LIBS += $(objroot)lib/$(LIBJEMALLOC)_s.$(A)
+endif
+DSOS := $(objroot)lib/$(LIBJEMALLOC).$(SOREV)
+ifneq ($(SOREV),$(SO))
+DSOS += $(objroot)lib/$(LIBJEMALLOC).$(SO)
+endif
+ifeq (1, $(link_whole_archive))
+LJEMALLOC := -Wl,--whole-archive -L$(objroot)lib -l$(LIBJEMALLOC) -Wl,--no-whole-archive
+else
+LJEMALLOC := $(objroot)lib/$(LIBJEMALLOC).$(IMPORTLIB)
+endif
+PC := $(objroot)jemalloc.pc
+DOCS_XML := $(objroot)doc/jemalloc$(install_suffix).xml
+DOCS_HTML := $(DOCS_XML:$(objroot)%.xml=$(objroot)%.html)
+DOCS_MAN3 := $(DOCS_XML:$(objroot)%.xml=$(objroot)%.3)
+DOCS := $(DOCS_HTML) $(DOCS_MAN3)
+C_TESTLIB_SRCS := $(srcroot)test/src/btalloc.c $(srcroot)test/src/btalloc_0.c \
+ $(srcroot)test/src/btalloc_1.c $(srcroot)test/src/math.c \
+ $(srcroot)test/src/mtx.c $(srcroot)test/src/sleep.c \
+ $(srcroot)test/src/SFMT.c $(srcroot)test/src/test.c \
+ $(srcroot)test/src/thd.c $(srcroot)test/src/timer.c
+ifeq (1, $(link_whole_archive))
+C_UTIL_INTEGRATION_SRCS :=
+C_UTIL_CPP_SRCS :=
+else
+C_UTIL_INTEGRATION_SRCS := $(srcroot)src/nstime.c $(srcroot)src/malloc_io.c \
+ $(srcroot)src/ticker.c
+C_UTIL_CPP_SRCS := $(srcroot)src/nstime.c $(srcroot)src/malloc_io.c
+endif
+TESTS_UNIT := \
+ $(srcroot)test/unit/a0.c \
+ $(srcroot)test/unit/arena_decay.c \
+ $(srcroot)test/unit/arena_reset.c \
+ $(srcroot)test/unit/atomic.c \
+ $(srcroot)test/unit/background_thread.c \
+ $(srcroot)test/unit/background_thread_enable.c \
+ $(srcroot)test/unit/base.c \
+ $(srcroot)test/unit/batch_alloc.c \
+ $(srcroot)test/unit/binshard.c \
+ $(srcroot)test/unit/bitmap.c \
+ $(srcroot)test/unit/bit_util.c \
+ $(srcroot)test/unit/buf_writer.c \
+ $(srcroot)test/unit/cache_bin.c \
+ $(srcroot)test/unit/ckh.c \
+ $(srcroot)test/unit/counter.c \
+ $(srcroot)test/unit/decay.c \
+ $(srcroot)test/unit/div.c \
+ $(srcroot)test/unit/double_free.c \
+ $(srcroot)test/unit/edata_cache.c \
+ $(srcroot)test/unit/emitter.c \
+ $(srcroot)test/unit/extent_quantize.c \
+ ${srcroot}test/unit/fb.c \
+ $(srcroot)test/unit/fork.c \
+ ${srcroot}test/unit/fxp.c \
+ ${srcroot}test/unit/san.c \
+ ${srcroot}test/unit/san_bump.c \
+ $(srcroot)test/unit/hash.c \
+ $(srcroot)test/unit/hook.c \
+ $(srcroot)test/unit/hpa.c \
+ $(srcroot)test/unit/hpa_background_thread.c \
+ $(srcroot)test/unit/hpdata.c \
+ $(srcroot)test/unit/huge.c \
+ $(srcroot)test/unit/inspect.c \
+ $(srcroot)test/unit/junk.c \
+ $(srcroot)test/unit/junk_alloc.c \
+ $(srcroot)test/unit/junk_free.c \
+ $(srcroot)test/unit/log.c \
+ $(srcroot)test/unit/mallctl.c \
+ $(srcroot)test/unit/malloc_conf_2.c \
+ $(srcroot)test/unit/malloc_io.c \
+ $(srcroot)test/unit/math.c \
+ $(srcroot)test/unit/mpsc_queue.c \
+ $(srcroot)test/unit/mq.c \
+ $(srcroot)test/unit/mtx.c \
+ $(srcroot)test/unit/nstime.c \
+ $(srcroot)test/unit/oversize_threshold.c \
+ $(srcroot)test/unit/pa.c \
+ $(srcroot)test/unit/pack.c \
+ $(srcroot)test/unit/pages.c \
+ $(srcroot)test/unit/peak.c \
+ $(srcroot)test/unit/ph.c \
+ $(srcroot)test/unit/prng.c \
+ $(srcroot)test/unit/prof_accum.c \
+ $(srcroot)test/unit/prof_active.c \
+ $(srcroot)test/unit/prof_gdump.c \
+ $(srcroot)test/unit/prof_hook.c \
+ $(srcroot)test/unit/prof_idump.c \
+ $(srcroot)test/unit/prof_log.c \
+ $(srcroot)test/unit/prof_mdump.c \
+ $(srcroot)test/unit/prof_recent.c \
+ $(srcroot)test/unit/prof_reset.c \
+ $(srcroot)test/unit/prof_stats.c \
+ $(srcroot)test/unit/prof_tctx.c \
+ $(srcroot)test/unit/prof_thread_name.c \
+ $(srcroot)test/unit/prof_sys_thread_name.c \
+ $(srcroot)test/unit/psset.c \
+ $(srcroot)test/unit/ql.c \
+ $(srcroot)test/unit/qr.c \
+ $(srcroot)test/unit/rb.c \
+ $(srcroot)test/unit/retained.c \
+ $(srcroot)test/unit/rtree.c \
+ $(srcroot)test/unit/safety_check.c \
+ $(srcroot)test/unit/sc.c \
+ $(srcroot)test/unit/sec.c \
+ $(srcroot)test/unit/seq.c \
+ $(srcroot)test/unit/SFMT.c \
+ $(srcroot)test/unit/size_check.c \
+ $(srcroot)test/unit/size_classes.c \
+ $(srcroot)test/unit/slab.c \
+ $(srcroot)test/unit/smoothstep.c \
+ $(srcroot)test/unit/spin.c \
+ $(srcroot)test/unit/stats.c \
+ $(srcroot)test/unit/stats_print.c \
+ $(srcroot)test/unit/sz.c \
+ $(srcroot)test/unit/tcache_max.c \
+ $(srcroot)test/unit/test_hooks.c \
+ $(srcroot)test/unit/thread_event.c \
+ $(srcroot)test/unit/ticker.c \
+ $(srcroot)test/unit/tsd.c \
+ $(srcroot)test/unit/uaf.c \
+ $(srcroot)test/unit/witness.c \
+ $(srcroot)test/unit/zero.c \
+ $(srcroot)test/unit/zero_realloc_abort.c \
+ $(srcroot)test/unit/zero_realloc_free.c \
+ $(srcroot)test/unit/zero_realloc_alloc.c \
+ $(srcroot)test/unit/zero_reallocs.c
+ifeq (@enable_prof@, 1)
+TESTS_UNIT += \
+ $(srcroot)test/unit/arena_reset_prof.c \
+ $(srcroot)test/unit/batch_alloc_prof.c
+endif
+TESTS_INTEGRATION := $(srcroot)test/integration/aligned_alloc.c \
+ $(srcroot)test/integration/allocated.c \
+ $(srcroot)test/integration/extent.c \
+ $(srcroot)test/integration/malloc.c \
+ $(srcroot)test/integration/mallocx.c \
+ $(srcroot)test/integration/MALLOCX_ARENA.c \
+ $(srcroot)test/integration/overflow.c \
+ $(srcroot)test/integration/posix_memalign.c \
+ $(srcroot)test/integration/rallocx.c \
+ $(srcroot)test/integration/sdallocx.c \
+ $(srcroot)test/integration/slab_sizes.c \
+ $(srcroot)test/integration/thread_arena.c \
+ $(srcroot)test/integration/thread_tcache_enabled.c \
+ $(srcroot)test/integration/xallocx.c
+ifeq (@enable_experimental_smallocx@, 1)
+TESTS_INTEGRATION += \
+ $(srcroot)test/integration/smallocx.c
+endif
+ifeq (@enable_cxx@, 1)
+CPP_SRCS := $(srcroot)src/jemalloc_cpp.cpp
+TESTS_INTEGRATION_CPP := $(srcroot)test/integration/cpp/basic.cpp \
+ $(srcroot)test/integration/cpp/infallible_new_true.cpp \
+ $(srcroot)test/integration/cpp/infallible_new_false.cpp
+else
+CPP_SRCS :=
+TESTS_INTEGRATION_CPP :=
+endif
+TESTS_ANALYZE := $(srcroot)test/analyze/prof_bias.c \
+ $(srcroot)test/analyze/rand.c \
+ $(srcroot)test/analyze/sizes.c
+TESTS_STRESS := $(srcroot)test/stress/batch_alloc.c \
+ $(srcroot)test/stress/fill_flush.c \
+ $(srcroot)test/stress/hookbench.c \
+ $(srcroot)test/stress/large_microbench.c \
+ $(srcroot)test/stress/mallctl.c \
+ $(srcroot)test/stress/microbench.c
+
+
+TESTS := $(TESTS_UNIT) $(TESTS_INTEGRATION) $(TESTS_INTEGRATION_CPP) \
+ $(TESTS_ANALYZE) $(TESTS_STRESS)
+
+PRIVATE_NAMESPACE_HDRS := $(objroot)include/jemalloc/internal/private_namespace.h $(objroot)include/jemalloc/internal/private_namespace_jet.h
+PRIVATE_NAMESPACE_GEN_HDRS := $(PRIVATE_NAMESPACE_HDRS:%.h=%.gen.h)
+C_SYM_OBJS := $(C_SRCS:$(srcroot)%.c=$(objroot)%.sym.$(O))
+C_SYMS := $(C_SRCS:$(srcroot)%.c=$(objroot)%.sym)
+C_OBJS := $(C_SRCS:$(srcroot)%.c=$(objroot)%.$(O))
+CPP_OBJS := $(CPP_SRCS:$(srcroot)%.cpp=$(objroot)%.$(O))
+C_PIC_OBJS := $(C_SRCS:$(srcroot)%.c=$(objroot)%.pic.$(O))
+CPP_PIC_OBJS := $(CPP_SRCS:$(srcroot)%.cpp=$(objroot)%.pic.$(O))
+C_JET_SYM_OBJS := $(C_SRCS:$(srcroot)%.c=$(objroot)%.jet.sym.$(O))
+C_JET_SYMS := $(C_SRCS:$(srcroot)%.c=$(objroot)%.jet.sym)
+C_JET_OBJS := $(C_SRCS:$(srcroot)%.c=$(objroot)%.jet.$(O))
+C_TESTLIB_UNIT_OBJS := $(C_TESTLIB_SRCS:$(srcroot)%.c=$(objroot)%.unit.$(O))
+C_TESTLIB_INTEGRATION_OBJS := $(C_TESTLIB_SRCS:$(srcroot)%.c=$(objroot)%.integration.$(O))
+C_UTIL_INTEGRATION_OBJS := $(C_UTIL_INTEGRATION_SRCS:$(srcroot)%.c=$(objroot)%.integration.$(O))
+C_TESTLIB_ANALYZE_OBJS := $(C_TESTLIB_SRCS:$(srcroot)%.c=$(objroot)%.analyze.$(O))
+C_TESTLIB_STRESS_OBJS := $(C_TESTLIB_SRCS:$(srcroot)%.c=$(objroot)%.stress.$(O))
+C_TESTLIB_OBJS := $(C_TESTLIB_UNIT_OBJS) $(C_TESTLIB_INTEGRATION_OBJS) \
+ $(C_UTIL_INTEGRATION_OBJS) $(C_TESTLIB_ANALYZE_OBJS) \
+ $(C_TESTLIB_STRESS_OBJS)
+
+TESTS_UNIT_OBJS := $(TESTS_UNIT:$(srcroot)%.c=$(objroot)%.$(O))
+TESTS_INTEGRATION_OBJS := $(TESTS_INTEGRATION:$(srcroot)%.c=$(objroot)%.$(O))
+TESTS_INTEGRATION_CPP_OBJS := $(TESTS_INTEGRATION_CPP:$(srcroot)%.cpp=$(objroot)%.$(O))
+TESTS_ANALYZE_OBJS := $(TESTS_ANALYZE:$(srcroot)%.c=$(objroot)%.$(O))
+TESTS_STRESS_OBJS := $(TESTS_STRESS:$(srcroot)%.c=$(objroot)%.$(O))
+TESTS_OBJS := $(TESTS_UNIT_OBJS) $(TESTS_INTEGRATION_OBJS) $(TESTS_ANALYZE_OBJS) \
+ $(TESTS_STRESS_OBJS)
+TESTS_CPP_OBJS := $(TESTS_INTEGRATION_CPP_OBJS)
+
+.PHONY: all dist build_doc_html build_doc_man build_doc
+.PHONY: install_bin install_include install_lib
+.PHONY: install_doc_html install_doc_man install_doc install
+.PHONY: tests check clean distclean relclean
+
+.SECONDARY : $(PRIVATE_NAMESPACE_GEN_HDRS) $(TESTS_OBJS) $(TESTS_CPP_OBJS)
+
+# Default target.
+all: build_lib
+
+dist: build_doc
+
+$(objroot)doc/%$(install_suffix).html : $(objroot)doc/%.xml $(srcroot)doc/stylesheet.xsl $(objroot)doc/html.xsl
+ifneq ($(XSLROOT),)
+ $(XSLTPROC) -o $@ $(objroot)doc/html.xsl $<
+else
+ifeq ($(wildcard $(DOCS_HTML)),)
+ @echo "<p>Missing xsltproc. Doc not built.</p>" > $@
+endif
+ @echo "Missing xsltproc. "$@" not (re)built."
+endif
+
+$(objroot)doc/%$(install_suffix).3 : $(objroot)doc/%.xml $(srcroot)doc/stylesheet.xsl $(objroot)doc/manpages.xsl
+ifneq ($(XSLROOT),)
+ $(XSLTPROC) -o $@ $(objroot)doc/manpages.xsl $<
+# The -o option (output filename) of xsltproc may not work (it uses the
+# <refname> in the .xml file). Manually add the suffix if so.
+ ifneq ($(install_suffix),)
+ @if [ -f $(objroot)doc/jemalloc.3 ]; then \
+ mv $(objroot)doc/jemalloc.3 $(objroot)doc/jemalloc$(install_suffix).3 ; \
+ fi
+ endif
+else
+ifeq ($(wildcard $(DOCS_MAN3)),)
+ @echo "Missing xsltproc. Doc not built." > $@
+endif
+ @echo "Missing xsltproc. "$@" not (re)built."
+endif
+
+build_doc_html: $(DOCS_HTML)
+build_doc_man: $(DOCS_MAN3)
+build_doc: $(DOCS)
+
+#
+# Include generated dependency files.
+#
+ifdef CC_MM
+-include $(C_SYM_OBJS:%.$(O)=%.d)
+-include $(C_OBJS:%.$(O)=%.d)
+-include $(CPP_OBJS:%.$(O)=%.d)
+-include $(C_PIC_OBJS:%.$(O)=%.d)
+-include $(CPP_PIC_OBJS:%.$(O)=%.d)
+-include $(C_JET_SYM_OBJS:%.$(O)=%.d)
+-include $(C_JET_OBJS:%.$(O)=%.d)
+-include $(C_TESTLIB_OBJS:%.$(O)=%.d)
+-include $(TESTS_OBJS:%.$(O)=%.d)
+-include $(TESTS_CPP_OBJS:%.$(O)=%.d)
+endif
+
+$(C_SYM_OBJS): $(objroot)src/%.sym.$(O): $(srcroot)src/%.c
+$(C_SYM_OBJS): CPPFLAGS += -DJEMALLOC_NO_PRIVATE_NAMESPACE
+$(C_SYMS): $(objroot)src/%.sym: $(objroot)src/%.sym.$(O)
+$(C_OBJS): $(objroot)src/%.$(O): $(srcroot)src/%.c
+$(CPP_OBJS): $(objroot)src/%.$(O): $(srcroot)src/%.cpp
+$(C_PIC_OBJS): $(objroot)src/%.pic.$(O): $(srcroot)src/%.c
+$(C_PIC_OBJS): CFLAGS += $(PIC_CFLAGS)
+$(CPP_PIC_OBJS): $(objroot)src/%.pic.$(O): $(srcroot)src/%.cpp
+$(CPP_PIC_OBJS): CXXFLAGS += $(PIC_CFLAGS)
+$(C_JET_SYM_OBJS): $(objroot)src/%.jet.sym.$(O): $(srcroot)src/%.c
+$(C_JET_SYM_OBJS): CPPFLAGS += -DJEMALLOC_JET -DJEMALLOC_NO_PRIVATE_NAMESPACE
+$(C_JET_SYMS): $(objroot)src/%.jet.sym: $(objroot)src/%.jet.sym.$(O)
+$(C_JET_OBJS): $(objroot)src/%.jet.$(O): $(srcroot)src/%.c
+$(C_JET_OBJS): CPPFLAGS += -DJEMALLOC_JET
+$(C_TESTLIB_UNIT_OBJS): $(objroot)test/src/%.unit.$(O): $(srcroot)test/src/%.c
+$(C_TESTLIB_UNIT_OBJS): CPPFLAGS += -DJEMALLOC_UNIT_TEST
+$(C_TESTLIB_INTEGRATION_OBJS): $(objroot)test/src/%.integration.$(O): $(srcroot)test/src/%.c
+$(C_TESTLIB_INTEGRATION_OBJS): CPPFLAGS += -DJEMALLOC_INTEGRATION_TEST
+$(C_UTIL_INTEGRATION_OBJS): $(objroot)src/%.integration.$(O): $(srcroot)src/%.c
+$(C_TESTLIB_ANALYZE_OBJS): $(objroot)test/src/%.analyze.$(O): $(srcroot)test/src/%.c
+$(C_TESTLIB_ANALYZE_OBJS): CPPFLAGS += -DJEMALLOC_ANALYZE_TEST
+$(C_TESTLIB_STRESS_OBJS): $(objroot)test/src/%.stress.$(O): $(srcroot)test/src/%.c
+$(C_TESTLIB_STRESS_OBJS): CPPFLAGS += -DJEMALLOC_STRESS_TEST -DJEMALLOC_STRESS_TESTLIB
+$(C_TESTLIB_OBJS): CPPFLAGS += -I$(srcroot)test/include -I$(objroot)test/include
+$(TESTS_UNIT_OBJS): CPPFLAGS += -DJEMALLOC_UNIT_TEST
+$(TESTS_INTEGRATION_OBJS): CPPFLAGS += -DJEMALLOC_INTEGRATION_TEST
+$(TESTS_INTEGRATION_CPP_OBJS): CPPFLAGS += -DJEMALLOC_INTEGRATION_CPP_TEST
+$(TESTS_ANALYZE_OBJS): CPPFLAGS += -DJEMALLOC_ANALYZE_TEST
+$(TESTS_STRESS_OBJS): CPPFLAGS += -DJEMALLOC_STRESS_TEST
+$(TESTS_OBJS): $(objroot)test/%.$(O): $(srcroot)test/%.c
+$(TESTS_CPP_OBJS): $(objroot)test/%.$(O): $(srcroot)test/%.cpp
+$(TESTS_OBJS): CPPFLAGS += -I$(srcroot)test/include -I$(objroot)test/include
+$(TESTS_CPP_OBJS): CPPFLAGS += -I$(srcroot)test/include -I$(objroot)test/include
+ifneq ($(IMPORTLIB),$(SO))
+$(CPP_OBJS) $(C_SYM_OBJS) $(C_OBJS) $(C_JET_SYM_OBJS) $(C_JET_OBJS): CPPFLAGS += -DDLLEXPORT
+endif
+
+# Dependencies.
+ifndef CC_MM
+HEADER_DIRS = $(srcroot)include/jemalloc/internal \
+ $(objroot)include/jemalloc $(objroot)include/jemalloc/internal
+HEADERS = $(filter-out $(PRIVATE_NAMESPACE_HDRS),$(wildcard $(foreach dir,$(HEADER_DIRS),$(dir)/*.h)))
+$(C_SYM_OBJS) $(C_OBJS) $(CPP_OBJS) $(C_PIC_OBJS) $(CPP_PIC_OBJS) $(C_JET_SYM_OBJS) $(C_JET_OBJS) $(C_TESTLIB_OBJS) $(TESTS_OBJS) $(TESTS_CPP_OBJS): $(HEADERS)
+$(TESTS_OBJS) $(TESTS_CPP_OBJS): $(objroot)test/include/test/jemalloc_test.h
+endif
+
+$(C_OBJS) $(CPP_OBJS) $(C_PIC_OBJS) $(CPP_PIC_OBJS) $(C_TESTLIB_INTEGRATION_OBJS) $(C_UTIL_INTEGRATION_OBJS) $(TESTS_INTEGRATION_OBJS) $(TESTS_INTEGRATION_CPP_OBJS): $(objroot)include/jemalloc/internal/private_namespace.h
+$(C_JET_OBJS) $(C_TESTLIB_UNIT_OBJS) $(C_TESTLIB_ANALYZE_OBJS) $(C_TESTLIB_STRESS_OBJS) $(TESTS_UNIT_OBJS) $(TESTS_ANALYZE_OBJS) $(TESTS_STRESS_OBJS): $(objroot)include/jemalloc/internal/private_namespace_jet.h
+
+$(C_SYM_OBJS) $(C_OBJS) $(C_PIC_OBJS) $(C_JET_SYM_OBJS) $(C_JET_OBJS) $(C_TESTLIB_OBJS) $(TESTS_OBJS): %.$(O):
+ @mkdir -p $(@D)
+ $(CC) $(CFLAGS) -c $(CPPFLAGS) $(CTARGET) $<
+ifdef CC_MM
+ @$(CC) -MM $(CPPFLAGS) -MT $@ -o $(@:%.$(O)=%.d) $<
+endif
+
+$(C_SYMS): %.sym:
+ @mkdir -p $(@D)
+ $(DUMP_SYMS) $< | $(AWK) -f $(objroot)include/jemalloc/internal/private_symbols.awk > $@
+
+$(C_JET_SYMS): %.sym:
+ @mkdir -p $(@D)
+ $(DUMP_SYMS) $< | $(AWK) -f $(objroot)include/jemalloc/internal/private_symbols_jet.awk > $@
+
+$(objroot)include/jemalloc/internal/private_namespace.gen.h: $(C_SYMS)
+ $(SHELL) $(srcroot)include/jemalloc/internal/private_namespace.sh $^ > $@
+
+$(objroot)include/jemalloc/internal/private_namespace_jet.gen.h: $(C_JET_SYMS)
+ $(SHELL) $(srcroot)include/jemalloc/internal/private_namespace.sh $^ > $@
+
+%.h: %.gen.h
+ @if ! `cmp -s $< $@` ; then echo "cp $< $@"; cp $< $@ ; fi
+
+$(CPP_OBJS) $(CPP_PIC_OBJS) $(TESTS_CPP_OBJS): %.$(O):
+ @mkdir -p $(@D)
+ $(CXX) $(CXXFLAGS) -c $(CPPFLAGS) $(CTARGET) $<
+ifdef CC_MM
+ @$(CXX) -MM $(CPPFLAGS) -MT $@ -o $(@:%.$(O)=%.d) $<
+endif
+
+ifneq ($(SOREV),$(SO))
+%.$(SO) : %.$(SOREV)
+ @mkdir -p $(@D)
+ ln -sf $(<F) $@
+endif
+
+$(objroot)lib/$(LIBJEMALLOC).$(SOREV) : $(if $(PIC_CFLAGS),$(C_PIC_OBJS),$(C_OBJS)) $(if $(PIC_CFLAGS),$(CPP_PIC_OBJS),$(CPP_OBJS))
+ @mkdir -p $(@D)
+ $(CC) $(DSO_LDFLAGS) $(call RPATH,$(RPATH_EXTRA)) $(LDTARGET) $+ $(LDFLAGS) $(LIBS) $(EXTRA_LDFLAGS)
+
+$(objroot)lib/$(LIBJEMALLOC)_pic.$(A) : $(C_PIC_OBJS) $(CPP_PIC_OBJS)
+$(objroot)lib/$(LIBJEMALLOC).$(A) : $(C_OBJS) $(CPP_OBJS)
+$(objroot)lib/$(LIBJEMALLOC)_s.$(A) : $(C_OBJS) $(CPP_OBJS)
+
+$(STATIC_LIBS):
+ @mkdir -p $(@D)
+ $(AR) $(ARFLAGS)@AROUT@ $+
+
+$(objroot)test/unit/%$(EXE): $(objroot)test/unit/%.$(O) $(C_JET_OBJS) $(C_TESTLIB_UNIT_OBJS)
+ @mkdir -p $(@D)
+ $(CC) $(LDTARGET) $(filter %.$(O),$^) $(call RPATH,$(objroot)lib) $(LDFLAGS) $(filter-out -lm,$(LIBS)) $(LM) $(EXTRA_LDFLAGS)
+
+$(objroot)test/integration/%$(EXE): $(objroot)test/integration/%.$(O) $(C_TESTLIB_INTEGRATION_OBJS) $(C_UTIL_INTEGRATION_OBJS) $(objroot)lib/$(LIBJEMALLOC).$(IMPORTLIB)
+ @mkdir -p $(@D)
+ $(CC) $(TEST_LD_MODE) $(LDTARGET) $(filter %.$(O),$^) $(call RPATH,$(objroot)lib) $(LJEMALLOC) $(LDFLAGS) $(filter-out -lm,$(filter -lrt -pthread -lstdc++,$(LIBS))) $(LM) $(EXTRA_LDFLAGS)
+
+$(objroot)test/integration/cpp/%$(EXE): $(objroot)test/integration/cpp/%.$(O) $(C_TESTLIB_INTEGRATION_OBJS) $(C_UTIL_INTEGRATION_OBJS) $(objroot)lib/$(LIBJEMALLOC).$(IMPORTLIB)
+ @mkdir -p $(@D)
+ $(CXX) $(LDTARGET) $(filter %.$(O),$^) $(call RPATH,$(objroot)lib) $(objroot)lib/$(LIBJEMALLOC).$(IMPORTLIB) $(LDFLAGS) $(filter-out -lm,$(LIBS)) -lm $(EXTRA_LDFLAGS)
+
+$(objroot)test/analyze/%$(EXE): $(objroot)test/analyze/%.$(O) $(C_JET_OBJS) $(C_TESTLIB_ANALYZE_OBJS)
+ @mkdir -p $(@D)
+ $(CC) $(LDTARGET) $(filter %.$(O),$^) $(call RPATH,$(objroot)lib) $(LDFLAGS) $(filter-out -lm,$(LIBS)) $(LM) $(EXTRA_LDFLAGS)
+
+$(objroot)test/stress/%$(EXE): $(objroot)test/stress/%.$(O) $(C_JET_OBJS) $(C_TESTLIB_STRESS_OBJS) $(objroot)lib/$(LIBJEMALLOC).$(IMPORTLIB)
+ @mkdir -p $(@D)
+ $(CC) $(TEST_LD_MODE) $(LDTARGET) $(filter %.$(O),$^) $(call RPATH,$(objroot)lib) $(objroot)lib/$(LIBJEMALLOC).$(IMPORTLIB) $(LDFLAGS) $(filter-out -lm,$(LIBS)) $(LM) $(EXTRA_LDFLAGS)
+
+build_lib_shared: $(DSOS)
+build_lib_static: $(STATIC_LIBS)
+ifeq ($(enable_shared), 1)
+build_lib: build_lib_shared
+endif
+ifeq ($(enable_static), 1)
+build_lib: build_lib_static
+endif
+
+install_bin:
+ $(INSTALL) -d $(BINDIR)
+ @for b in $(BINS); do \
+ $(INSTALL) -v -m 755 $$b $(BINDIR); \
+done
+
+install_include:
+ $(INSTALL) -d $(INCLUDEDIR)/jemalloc
+ @for h in $(C_HDRS); do \
+ $(INSTALL) -v -m 644 $$h $(INCLUDEDIR)/jemalloc; \
+done
+
+install_lib_shared: $(DSOS)
+ $(INSTALL) -d $(LIBDIR)
+ $(INSTALL) -v -m 755 $(objroot)lib/$(LIBJEMALLOC).$(SOREV) $(LIBDIR)
+ifneq ($(SOREV),$(SO))
+ ln -sf $(LIBJEMALLOC).$(SOREV) $(LIBDIR)/$(LIBJEMALLOC).$(SO)
+endif
+
+install_lib_static: $(STATIC_LIBS)
+ $(INSTALL) -d $(LIBDIR)
+ @for l in $(STATIC_LIBS); do \
+ $(INSTALL) -v -m 755 $$l $(LIBDIR); \
+done
+
+install_lib_pc: $(PC)
+ $(INSTALL) -d $(LIBDIR)/pkgconfig
+ @for l in $(PC); do \
+ $(INSTALL) -v -m 644 $$l $(LIBDIR)/pkgconfig; \
+done
+
+ifeq ($(enable_shared), 1)
+install_lib: install_lib_shared
+endif
+ifeq ($(enable_static), 1)
+install_lib: install_lib_static
+endif
+install_lib: install_lib_pc
+
+install_doc_html: build_doc_html
+ $(INSTALL) -d $(DATADIR)/doc/jemalloc$(install_suffix)
+ @for d in $(DOCS_HTML); do \
+ $(INSTALL) -v -m 644 $$d $(DATADIR)/doc/jemalloc$(install_suffix); \
+done
+
+install_doc_man: build_doc_man
+ $(INSTALL) -d $(MANDIR)/man3
+ @for d in $(DOCS_MAN3); do \
+ $(INSTALL) -v -m 644 $$d $(MANDIR)/man3; \
+done
+
+install_doc: install_doc_html install_doc_man
+
+install: install_bin install_include install_lib
+
+ifeq ($(enable_doc), 1)
+install: install_doc
+endif
+
+uninstall_bin:
+ $(RM) -v $(foreach b,$(notdir $(BINS)),$(BINDIR)/$(b))
+
+uninstall_include:
+ $(RM) -v $(foreach h,$(notdir $(C_HDRS)),$(INCLUDEDIR)/jemalloc/$(h))
+ rmdir -v $(INCLUDEDIR)/jemalloc
+
+uninstall_lib_shared:
+ $(RM) -v $(LIBDIR)/$(LIBJEMALLOC).$(SOREV)
+ifneq ($(SOREV),$(SO))
+ $(RM) -v $(LIBDIR)/$(LIBJEMALLOC).$(SO)
+endif
+
+uninstall_lib_static:
+ $(RM) -v $(foreach l,$(notdir $(STATIC_LIBS)),$(LIBDIR)/$(l))
+
+uninstall_lib_pc:
+ $(RM) -v $(foreach p,$(notdir $(PC)),$(LIBDIR)/pkgconfig/$(p))
+
+ifeq ($(enable_shared), 1)
+uninstall_lib: uninstall_lib_shared
+endif
+ifeq ($(enable_static), 1)
+uninstall_lib: uninstall_lib_static
+endif
+uninstall_lib: uninstall_lib_pc
+
+uninstall_doc_html:
+ $(RM) -v $(foreach d,$(notdir $(DOCS_HTML)),$(DATADIR)/doc/jemalloc$(install_suffix)/$(d))
+ rmdir -v $(DATADIR)/doc/jemalloc$(install_suffix)
+
+uninstall_doc_man:
+ $(RM) -v $(foreach d,$(notdir $(DOCS_MAN3)),$(MANDIR)/man3/$(d))
+
+uninstall_doc: uninstall_doc_html uninstall_doc_man
+
+uninstall: uninstall_bin uninstall_include uninstall_lib
+
+ifeq ($(enable_doc), 1)
+uninstall: uninstall_doc
+endif
+
+tests_unit: $(TESTS_UNIT:$(srcroot)%.c=$(objroot)%$(EXE))
+tests_integration: $(TESTS_INTEGRATION:$(srcroot)%.c=$(objroot)%$(EXE)) $(TESTS_INTEGRATION_CPP:$(srcroot)%.cpp=$(objroot)%$(EXE))
+tests_analyze: $(TESTS_ANALYZE:$(srcroot)%.c=$(objroot)%$(EXE))
+tests_stress: $(TESTS_STRESS:$(srcroot)%.c=$(objroot)%$(EXE))
+tests: tests_unit tests_integration tests_analyze tests_stress
+
+check_unit_dir:
+ @mkdir -p $(objroot)test/unit
+check_integration_dir:
+ @mkdir -p $(objroot)test/integration
+analyze_dir:
+ @mkdir -p $(objroot)test/analyze
+stress_dir:
+ @mkdir -p $(objroot)test/stress
+check_dir: check_unit_dir check_integration_dir
+
+check_unit: tests_unit check_unit_dir
+ $(SHELL) $(objroot)test/test.sh $(TESTS_UNIT:$(srcroot)%.c=$(objroot)%)
+check_integration_prof: tests_integration check_integration_dir
+ifeq ($(enable_prof), 1)
+ $(MALLOC_CONF)="prof:true" $(SHELL) $(objroot)test/test.sh $(TESTS_INTEGRATION:$(srcroot)%.c=$(objroot)%) $(TESTS_INTEGRATION_CPP:$(srcroot)%.cpp=$(objroot)%)
+ $(MALLOC_CONF)="prof:true,prof_active:false" $(SHELL) $(objroot)test/test.sh $(TESTS_INTEGRATION:$(srcroot)%.c=$(objroot)%) $(TESTS_INTEGRATION_CPP:$(srcroot)%.cpp=$(objroot)%)
+endif
+check_integration_decay: tests_integration check_integration_dir
+ $(MALLOC_CONF)="dirty_decay_ms:-1,muzzy_decay_ms:-1" $(SHELL) $(objroot)test/test.sh $(TESTS_INTEGRATION:$(srcroot)%.c=$(objroot)%) $(TESTS_INTEGRATION_CPP:$(srcroot)%.cpp=$(objroot)%)
+ $(MALLOC_CONF)="dirty_decay_ms:0,muzzy_decay_ms:0" $(SHELL) $(objroot)test/test.sh $(TESTS_INTEGRATION:$(srcroot)%.c=$(objroot)%) $(TESTS_INTEGRATION_CPP:$(srcroot)%.cpp=$(objroot)%)
+check_integration: tests_integration check_integration_dir
+ $(SHELL) $(objroot)test/test.sh $(TESTS_INTEGRATION:$(srcroot)%.c=$(objroot)%) $(TESTS_INTEGRATION_CPP:$(srcroot)%.cpp=$(objroot)%)
+analyze: tests_analyze analyze_dir
+ifeq ($(enable_prof), 1)
+ $(MALLOC_CONF)="prof:true" $(SHELL) $(objroot)test/test.sh $(TESTS_ANALYZE:$(srcroot)%.c=$(objroot)%)
+else
+ $(SHELL) $(objroot)test/test.sh $(TESTS_ANALYZE:$(srcroot)%.c=$(objroot)%)
+endif
+stress: tests_stress stress_dir
+ $(SHELL) $(objroot)test/test.sh $(TESTS_STRESS:$(srcroot)%.c=$(objroot)%)
+check: check_unit check_integration check_integration_decay check_integration_prof
+
+clean:
+ rm -f $(PRIVATE_NAMESPACE_HDRS)
+ rm -f $(PRIVATE_NAMESPACE_GEN_HDRS)
+ rm -f $(C_SYM_OBJS)
+ rm -f $(C_SYMS)
+ rm -f $(C_OBJS)
+ rm -f $(CPP_OBJS)
+ rm -f $(C_PIC_OBJS)
+ rm -f $(CPP_PIC_OBJS)
+ rm -f $(C_JET_SYM_OBJS)
+ rm -f $(C_JET_SYMS)
+ rm -f $(C_JET_OBJS)
+ rm -f $(C_TESTLIB_OBJS)
+ rm -f $(C_SYM_OBJS:%.$(O)=%.d)
+ rm -f $(C_OBJS:%.$(O)=%.d)
+ rm -f $(CPP_OBJS:%.$(O)=%.d)
+ rm -f $(C_PIC_OBJS:%.$(O)=%.d)
+ rm -f $(CPP_PIC_OBJS:%.$(O)=%.d)
+ rm -f $(C_JET_SYM_OBJS:%.$(O)=%.d)
+ rm -f $(C_JET_OBJS:%.$(O)=%.d)
+ rm -f $(C_TESTLIB_OBJS:%.$(O)=%.d)
+ rm -f $(TESTS_OBJS:%.$(O)=%$(EXE))
+ rm -f $(TESTS_OBJS)
+ rm -f $(TESTS_OBJS:%.$(O)=%.d)
+ rm -f $(TESTS_OBJS:%.$(O)=%.out)
+ rm -f $(TESTS_CPP_OBJS:%.$(O)=%$(EXE))
+ rm -f $(TESTS_CPP_OBJS)
+ rm -f $(TESTS_CPP_OBJS:%.$(O)=%.d)
+ rm -f $(TESTS_CPP_OBJS:%.$(O)=%.out)
+ rm -f $(DSOS) $(STATIC_LIBS)
+
+distclean: clean
+ rm -f $(objroot)bin/jemalloc-config
+ rm -f $(objroot)bin/jemalloc.sh
+ rm -f $(objroot)bin/jeprof
+ rm -f $(objroot)config.log
+ rm -f $(objroot)config.status
+ rm -f $(objroot)config.stamp
+ rm -f $(cfghdrs_out)
+ rm -f $(cfgoutputs_out)
+
+relclean: distclean
+ rm -f $(objroot)configure
+ rm -f $(objroot)VERSION
+ rm -f $(DOCS_HTML)
+ rm -f $(DOCS_MAN3)
+
+#===============================================================================
+# Re-configuration rules.
+
+ifeq ($(enable_autogen), 1)
+$(srcroot)configure : $(srcroot)configure.ac
+ cd ./$(srcroot) && $(AUTOCONF)
+
+$(objroot)config.status : $(srcroot)configure
+ ./$(objroot)config.status --recheck
+
+$(srcroot)config.stamp.in : $(srcroot)configure.ac
+ echo stamp > $(srcroot)config.stamp.in
+
+$(objroot)config.stamp : $(cfgoutputs_in) $(cfghdrs_in) $(srcroot)configure
+ ./$(objroot)config.status
+ @touch $@
+
+# There must be some action in order for make to re-read Makefile when it is
+# out of date.
+$(cfgoutputs_out) $(cfghdrs_out) : $(objroot)config.stamp
+ @true
+endif
diff --git a/contrib/jemalloc/README b/contrib/jemalloc/README
new file mode 100644
index 000000000000..3a6e0d272502
--- /dev/null
+++ b/contrib/jemalloc/README
@@ -0,0 +1,20 @@
+jemalloc is a general purpose malloc(3) implementation that emphasizes
+fragmentation avoidance and scalable concurrency support. jemalloc first came
+into use as the FreeBSD libc allocator in 2005, and since then it has found its
+way into numerous applications that rely on its predictable behavior. In 2010
+jemalloc development efforts broadened to include developer support features
+such as heap profiling and extensive monitoring/tuning hooks. Modern jemalloc
+releases continue to be integrated back into FreeBSD, and therefore versatility
+remains critical. Ongoing development efforts trend toward making jemalloc
+among the best allocators for a broad range of demanding applications, and
+eliminating/mitigating weaknesses that have practical repercussions for real
+world applications.
+
+The COPYING file contains copyright and licensing information.
+
+The INSTALL file contains information on how to configure, build, and install
+jemalloc.
+
+The ChangeLog file contains a brief summary of changes for each release.
+
+URL: http://jemalloc.net/
diff --git a/contrib/jemalloc/TUNING.md b/contrib/jemalloc/TUNING.md
new file mode 100644
index 000000000000..e96399d7c9b8
--- /dev/null
+++ b/contrib/jemalloc/TUNING.md
@@ -0,0 +1,129 @@
+This document summarizes the common approaches for performance fine tuning with
+jemalloc (as of 5.3.0). The default configuration of jemalloc tends to work
+reasonably well in practice, and most applications should not have to tune any
+options. However, in order to cover a wide range of applications and avoid
+pathological cases, the default setting is sometimes kept conservative and
+suboptimal, even for many common workloads. When jemalloc is properly tuned for
+a specific application / workload, it is common to improve system level metrics
+by a few percent, or make favorable trade-offs.
+
+
+## Notable runtime options for performance tuning
+
+Runtime options can be set via
+[malloc_conf](http://jemalloc.net/jemalloc.3.html#tuning).
+
+* [background_thread](http://jemalloc.net/jemalloc.3.html#background_thread)
+
+ Enabling jemalloc background threads generally improves the tail latency for
+ application threads, since unused memory purging is shifted to the dedicated
+ background threads. In addition, unintended purging delay caused by
+ application inactivity is avoided with background threads.
+
+ Suggested: `background_thread:true` when jemalloc managed threads can be
+ allowed.
+
+* [metadata_thp](http://jemalloc.net/jemalloc.3.html#opt.metadata_thp)
+
+ Allowing jemalloc to utilize transparent huge pages for its internal
+ metadata usually reduces TLB misses significantly, especially for programs
+ with large memory footprint and frequent allocation / deallocation
+ activities. Metadata memory usage may increase due to the use of huge
+ pages.
+
+ Suggested for allocation intensive programs: `metadata_thp:auto` or
+ `metadata_thp:always`, which is expected to improve CPU utilization at a
+ small memory cost.
+
+* [dirty_decay_ms](http://jemalloc.net/jemalloc.3.html#opt.dirty_decay_ms) and
+ [muzzy_decay_ms](http://jemalloc.net/jemalloc.3.html#opt.muzzy_decay_ms)
+
+ Decay time determines how fast jemalloc returns unused pages back to the
+ operating system, and therefore provides a fairly straightforward trade-off
+ between CPU and memory usage. Shorter decay time purges unused pages faster
+ to reduces memory usage (usually at the cost of more CPU cycles spent on
+ purging), and vice versa.
+
+ Suggested: tune the values based on the desired trade-offs.
+
+* [narenas](http://jemalloc.net/jemalloc.3.html#opt.narenas)
+
+ By default jemalloc uses multiple arenas to reduce internal lock contention.
+ However high arena count may also increase overall memory fragmentation,
+ since arenas manage memory independently. When high degree of parallelism
+ is not expected at the allocator level, lower number of arenas often
+ improves memory usage.
+
+ Suggested: if low parallelism is expected, try lower arena count while
+ monitoring CPU and memory usage.
+
+* [percpu_arena](http://jemalloc.net/jemalloc.3.html#opt.percpu_arena)
+
+ Enable dynamic thread to arena association based on running CPU. This has
+ the potential to improve locality, e.g. when thread to CPU affinity is
+ present.
+
+ Suggested: try `percpu_arena:percpu` or `percpu_arena:phycpu` if
+ thread migration between processors is expected to be infrequent.
+
+Examples:
+
+* High resource consumption application, prioritizing CPU utilization:
+
+ `background_thread:true,metadata_thp:auto` combined with relaxed decay time
+ (increased `dirty_decay_ms` and / or `muzzy_decay_ms`,
+ e.g. `dirty_decay_ms:30000,muzzy_decay_ms:30000`).
+
+* High resource consumption application, prioritizing memory usage:
+
+ `background_thread:true,tcache_max:4096` combined with shorter decay time
+ (decreased `dirty_decay_ms` and / or `muzzy_decay_ms`,
+ e.g. `dirty_decay_ms:5000,muzzy_decay_ms:5000`), and lower arena count
+ (e.g. number of CPUs).
+
+* Low resource consumption application:
+
+ `narenas:1,tcache_max:1024` combined with shorter decay time (decreased
+ `dirty_decay_ms` and / or `muzzy_decay_ms`,e.g.
+ `dirty_decay_ms:1000,muzzy_decay_ms:0`).
+
+* Extremely conservative -- minimize memory usage at all costs, only suitable when
+allocation activity is very rare:
+
+ `narenas:1,tcache:false,dirty_decay_ms:0,muzzy_decay_ms:0`
+
+Note that it is recommended to combine the options with `abort_conf:true` which
+aborts immediately on illegal options.
+
+## Beyond runtime options
+
+In addition to the runtime options, there are a number of programmatic ways to
+improve application performance with jemalloc.
+
+* [Explicit arenas](http://jemalloc.net/jemalloc.3.html#arenas.create)
+
+ Manually created arenas can help performance in various ways, e.g. by
+ managing locality and contention for specific usages. For example,
+ applications can explicitly allocate frequently accessed objects from a
+ dedicated arena with
+ [mallocx()](http://jemalloc.net/jemalloc.3.html#MALLOCX_ARENA) to improve
+ locality. In addition, explicit arenas often benefit from individually
+ tuned options, e.g. relaxed [decay
+ time](http://jemalloc.net/jemalloc.3.html#arena.i.dirty_decay_ms) if
+ frequent reuse is expected.
+
+* [Extent hooks](http://jemalloc.net/jemalloc.3.html#arena.i.extent_hooks)
+
+ Extent hooks allow customization for managing underlying memory. One use
+ case for performance purpose is to utilize huge pages -- for example,
+ [HHVM](https://github.com/facebook/hhvm/blob/master/hphp/util/alloc.cpp)
+ uses explicit arenas with customized extent hooks to manage 1GB huge pages
+ for frequently accessed data, which reduces TLB misses significantly.
+
+* [Explicit thread-to-arena
+ binding](http://jemalloc.net/jemalloc.3.html#thread.arena)
+
+ It is common for some threads in an application to have different memory
+ access / allocation patterns. Threads with heavy workloads often benefit
+ from explicit binding, e.g. binding very active threads to dedicated arenas
+ may reduce contention at the allocator level.
diff --git a/contrib/jemalloc/VERSION b/contrib/jemalloc/VERSION
index 428ded8c071f..1dcfea03fc1a 100644
--- a/contrib/jemalloc/VERSION
+++ b/contrib/jemalloc/VERSION
@@ -1 +1 @@
-5.2.1-0-gea6b3e973b477b8061e0076bb257dbd7f3faa756
+5.3.0-0-g54eaed1d8b56b1aa528be3bdd1877e59c56fa90c
diff --git a/contrib/jemalloc/autogen.sh b/contrib/jemalloc/autogen.sh
new file mode 100755
index 000000000000..75f32da6873c
--- /dev/null
+++ b/contrib/jemalloc/autogen.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+for i in autoconf; do
+ echo "$i"
+ $i
+ if [ $? -ne 0 ]; then
+ echo "Error $? in $i"
+ exit 1
+ fi
+done
+
+echo "./configure --enable-autogen $@"
+./configure --enable-autogen $@
+if [ $? -ne 0 ]; then
+ echo "Error $? in ./configure"
+ exit 1
+fi
diff --git a/contrib/jemalloc/bin/jemalloc-config.in b/contrib/jemalloc/bin/jemalloc-config.in
new file mode 100644
index 000000000000..80eca2e6437e
--- /dev/null
+++ b/contrib/jemalloc/bin/jemalloc-config.in
@@ -0,0 +1,83 @@
+#!/bin/sh
+
+usage() {
+ cat <<EOF
+Usage:
+ @BINDIR@/jemalloc-config <option>
+Options:
+ --help | -h : Print usage.
+ --version : Print jemalloc version.
+ --revision : Print shared library revision number.
+ --config : Print configure options used to build jemalloc.
+ --prefix : Print installation directory prefix.
+ --bindir : Print binary installation directory.
+ --datadir : Print data installation directory.
+ --includedir : Print include installation directory.
+ --libdir : Print library installation directory.
+ --mandir : Print manual page installation directory.
+ --cc : Print compiler used to build jemalloc.
+ --cflags : Print compiler flags used to build jemalloc.
+ --cppflags : Print preprocessor flags used to build jemalloc.
+ --cxxflags : Print C++ compiler flags used to build jemalloc.
+ --ldflags : Print library flags used to build jemalloc.
+ --libs : Print libraries jemalloc was linked against.
+EOF
+}
+
+prefix="@prefix@"
+exec_prefix="@exec_prefix@"
+
+case "$1" in
+--help | -h)
+ usage
+ exit 0
+ ;;
+--version)
+ echo "@jemalloc_version@"
+ ;;
+--revision)
+ echo "@rev@"
+ ;;
+--config)
+ echo "@CONFIG@"
+ ;;
+--prefix)
+ echo "@PREFIX@"
+ ;;
+--bindir)
+ echo "@BINDIR@"
+ ;;
+--datadir)
+ echo "@DATADIR@"
+ ;;
+--includedir)
+ echo "@INCLUDEDIR@"
+ ;;
+--libdir)
+ echo "@LIBDIR@"
+ ;;
+--mandir)
+ echo "@MANDIR@"
+ ;;
+--cc)
+ echo "@CC@"
+ ;;
+--cflags)
+ echo "@CFLAGS@"
+ ;;
+--cppflags)
+ echo "@CPPFLAGS@"
+ ;;
+--cxxflags)
+ echo "@CXXFLAGS@"
+ ;;
+--ldflags)
+ echo "@LDFLAGS@ @EXTRA_LDFLAGS@"
+ ;;
+--libs)
+ echo "@LIBS@"
+ ;;
+*)
+ usage
+ exit 1
+esac
diff --git a/contrib/jemalloc/bin/jemalloc.sh.in b/contrib/jemalloc/bin/jemalloc.sh.in
new file mode 100644
index 000000000000..cdf36737591a
--- /dev/null
+++ b/contrib/jemalloc/bin/jemalloc.sh.in
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+
+@LD_PRELOAD_VAR@=${libdir}/libjemalloc.@SOREV@
+export @LD_PRELOAD_VAR@
+exec "$@"
diff --git a/contrib/jemalloc/bin/jeprof.in b/contrib/jemalloc/bin/jeprof.in
new file mode 100644
index 000000000000..dbf6252b921e
--- /dev/null
+++ b/contrib/jemalloc/bin/jeprof.in
@@ -0,0 +1,5723 @@
+#! /usr/bin/env perl
+
+# Copyright (c) 1998-2007, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+# * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# ---
+# Program for printing the profile generated by common/profiler.cc,
+# or by the heap profiler (common/debugallocation.cc)
+#
+# The profile contains a sequence of entries of the form:
+# <count> <stack trace>
+# This program parses the profile, and generates user-readable
+# output.
+#
+# Examples:
+#
+# % tools/jeprof "program" "profile"
+# Enters "interactive" mode
+#
+# % tools/jeprof --text "program" "profile"
+# Generates one line per procedure
+#
+# % tools/jeprof --gv "program" "profile"
+# Generates annotated call-graph and displays via "gv"
+#
+# % tools/jeprof --gv --focus=Mutex "program" "profile"
+# Restrict to code paths that involve an entry that matches "Mutex"
+#
+# % tools/jeprof --gv --focus=Mutex --ignore=string "program" "profile"
+# Restrict to code paths that involve an entry that matches "Mutex"
+# and does not match "string"
+#
+# % tools/jeprof --list=IBF_CheckDocid "program" "profile"
+# Generates disassembly listing of all routines with at least one
+# sample that match the --list=<regexp> pattern. The listing is
+# annotated with the flat and cumulative sample counts at each line.
+#
+# % tools/jeprof --disasm=IBF_CheckDocid "program" "profile"
+# Generates disassembly listing of all routines with at least one
+# sample that match the --disasm=<regexp> pattern. The listing is
+# annotated with the flat and cumulative sample counts at each PC value.
+#
+# TODO: Use color to indicate files?
+
+use strict;
+use warnings;
+use Getopt::Long;
+use Cwd;
+
+my $JEPROF_VERSION = "@jemalloc_version@";
+my $PPROF_VERSION = "2.0";
+
+# These are the object tools we use which can come from a
+# user-specified location using --tools, from the JEPROF_TOOLS
+# environment variable, or from the environment.
+my %obj_tool_map = (
+ "objdump" => "objdump",
+ "nm" => "nm",
+ "addr2line" => "addr2line",
+ "c++filt" => "c++filt",
+ ## ConfigureObjTools may add architecture-specific entries:
+ #"nm_pdb" => "nm-pdb", # for reading windows (PDB-format) executables
+ #"addr2line_pdb" => "addr2line-pdb", # ditto
+ #"otool" => "otool", # equivalent of objdump on OS X
+);
+# NOTE: these are lists, so you can put in commandline flags if you want.
+my @DOT = ("dot"); # leave non-absolute, since it may be in /usr/local
+my @GV = ("gv");
+my @EVINCE = ("evince"); # could also be xpdf or perhaps acroread
+my @KCACHEGRIND = ("kcachegrind");
+my @PS2PDF = ("ps2pdf");
+# These are used for dynamic profiles
+my @URL_FETCHER = ("curl", "-s", "--fail");
+
+# These are the web pages that servers need to support for dynamic profiles
+my $HEAP_PAGE = "/pprof/heap";
+my $PROFILE_PAGE = "/pprof/profile"; # must support cgi-param "?seconds=#"
+my $PMUPROFILE_PAGE = "/pprof/pmuprofile(?:\\?.*)?"; # must support cgi-param
+ # ?seconds=#&event=x&period=n
+my $GROWTH_PAGE = "/pprof/growth";
+my $CONTENTION_PAGE = "/pprof/contention";
+my $WALL_PAGE = "/pprof/wall(?:\\?.*)?"; # accepts options like namefilter
+my $FILTEREDPROFILE_PAGE = "/pprof/filteredprofile(?:\\?.*)?";
+my $CENSUSPROFILE_PAGE = "/pprof/censusprofile(?:\\?.*)?"; # must support cgi-param
+ # "?seconds=#",
+ # "?tags_regexp=#" and
+ # "?type=#".
+my $SYMBOL_PAGE = "/pprof/symbol"; # must support symbol lookup via POST
+my $PROGRAM_NAME_PAGE = "/pprof/cmdline";
+
+# These are the web pages that can be named on the command line.
+# All the alternatives must begin with /.
+my $PROFILES = "($HEAP_PAGE|$PROFILE_PAGE|$PMUPROFILE_PAGE|" .
+ "$GROWTH_PAGE|$CONTENTION_PAGE|$WALL_PAGE|" .
+ "$FILTEREDPROFILE_PAGE|$CENSUSPROFILE_PAGE)";
+
+# default binary name
+my $UNKNOWN_BINARY = "(unknown)";
+
+# There is a pervasive dependency on the length (in hex characters,
+# i.e., nibbles) of an address, distinguishing between 32-bit and
+# 64-bit profiles. To err on the safe size, default to 64-bit here:
+my $address_length = 16;
+
+my $dev_null = "/dev/null";
+if (! -e $dev_null && $^O =~ /MSWin/) { # $^O is the OS perl was built for
+ $dev_null = "nul";
+}
+
+# A list of paths to search for shared object files
+my @prefix_list = ();
+
+# Special routine name that should not have any symbols.
+# Used as separator to parse "addr2line -i" output.
+my $sep_symbol = '_fini';
+my $sep_address = undef;
+
+##### Argument parsing #####
+
+sub usage_string {
+ return <<EOF;
+Usage:
+jeprof [options] <program> <profiles>
+ <profiles> is a space separated list of profile names.
+jeprof [options] <symbolized-profiles>
+ <symbolized-profiles> is a list of profile files where each file contains
+ the necessary symbol mappings as well as profile data (likely generated
+ with --raw).
+jeprof [options] <profile>
+ <profile> is a remote form. Symbols are obtained from host:port$SYMBOL_PAGE
+
+ Each name can be:
+ /path/to/profile - a path to a profile file
+ host:port[/<service>] - a location of a service to get profile from
+
+ The /<service> can be $HEAP_PAGE, $PROFILE_PAGE, /pprof/pmuprofile,
+ $GROWTH_PAGE, $CONTENTION_PAGE, /pprof/wall,
+ $CENSUSPROFILE_PAGE, or /pprof/filteredprofile.
+ For instance:
+ jeprof http://myserver.com:80$HEAP_PAGE
+ If /<service> is omitted, the service defaults to $PROFILE_PAGE (cpu profiling).
+jeprof --symbols <program>
+ Maps addresses to symbol names. In this mode, stdin should be a
+ list of library mappings, in the same format as is found in the heap-
+ and cpu-profile files (this loosely matches that of /proc/self/maps
+ on linux), followed by a list of hex addresses to map, one per line.
+
+ For more help with querying remote servers, including how to add the
+ necessary server-side support code, see this filename (or one like it):
+
+ /usr/doc/gperftools-$PPROF_VERSION/pprof_remote_servers.html
+
+Options:
+ --cum Sort by cumulative data
+ --base=<base> Subtract <base> from <profile> before display
+ --interactive Run in interactive mode (interactive "help" gives help) [default]
+ --seconds=<n> Length of time for dynamic profiles [default=30 secs]
+ --add_lib=<file> Read additional symbols and line info from the given library
+ --lib_prefix=<dir> Comma separated list of library path prefixes
+
+Reporting Granularity:
+ --addresses Report at address level
+ --lines Report at source line level
+ --functions Report at function level [default]
+ --files Report at source file level
+
+Output type:
+ --text Generate text report
+ --callgrind Generate callgrind format to stdout
+ --gv Generate Postscript and display
+ --evince Generate PDF and display
+ --web Generate SVG and display
+ --list=<regexp> Generate source listing of matching routines
+ --disasm=<regexp> Generate disassembly of matching routines
+ --symbols Print demangled symbol names found at given addresses
+ --dot Generate DOT file to stdout
+ --ps Generate Postcript to stdout
+ --pdf Generate PDF to stdout
+ --svg Generate SVG to stdout
+ --gif Generate GIF to stdout
+ --raw Generate symbolized jeprof data (useful with remote fetch)
+ --collapsed Generate collapsed stacks for building flame graphs
+ (see http://www.brendangregg.com/flamegraphs.html)
+
+Heap-Profile Options:
+ --inuse_space Display in-use (mega)bytes [default]
+ --inuse_objects Display in-use objects
+ --alloc_space Display allocated (mega)bytes
+ --alloc_objects Display allocated objects
+ --show_bytes Display space in bytes
+ --drop_negative Ignore negative differences
+
+Contention-profile options:
+ --total_delay Display total delay at each region [default]
+ --contentions Display number of delays at each region
+ --mean_delay Display mean delay at each region
+
+Call-graph Options:
+ --nodecount=<n> Show at most so many nodes [default=80]
+ --nodefraction=<f> Hide nodes below <f>*total [default=.005]
+ --edgefraction=<f> Hide edges below <f>*total [default=.001]
+ --maxdegree=<n> Max incoming/outgoing edges per node [default=8]
+ --focus=<regexp> Focus on backtraces with nodes matching <regexp>
+ --thread=<n> Show profile for thread <n>
+ --ignore=<regexp> Ignore backtraces with nodes matching <regexp>
+ --scale=<n> Set GV scaling [default=0]
+ --heapcheck Make nodes with non-0 object counts
+ (i.e. direct leak generators) more visible
+ --retain=<regexp> Retain only nodes that match <regexp>
+ --exclude=<regexp> Exclude all nodes that match <regexp>
+
+Miscellaneous:
+ --tools=<prefix or binary:fullpath>[,...] \$PATH for object tool pathnames
+ --test Run unit tests
+ --help This message
+ --version Version information
+ --debug-syms-by-id (Linux only) Find debug symbol files by build ID as well as by name
+
+Environment Variables:
+ JEPROF_TMPDIR Profiles directory. Defaults to \$HOME/jeprof
+ JEPROF_TOOLS Prefix for object tools pathnames
+
+Examples:
+
+jeprof /bin/ls ls.prof
+ Enters "interactive" mode
+jeprof --text /bin/ls ls.prof
+ Outputs one line per procedure
+jeprof --web /bin/ls ls.prof
+ Displays annotated call-graph in web browser
+jeprof --gv /bin/ls ls.prof
+ Displays annotated call-graph via 'gv'
+jeprof --gv --focus=Mutex /bin/ls ls.prof
+ Restricts to code paths including a .*Mutex.* entry
+jeprof --gv --focus=Mutex --ignore=string /bin/ls ls.prof
+ Code paths including Mutex but not string
+jeprof --list=getdir /bin/ls ls.prof
+ (Per-line) annotated source listing for getdir()
+jeprof --disasm=getdir /bin/ls ls.prof
+ (Per-PC) annotated disassembly for getdir()
+
+jeprof http://localhost:1234/
+ Enters "interactive" mode
+jeprof --text localhost:1234
+ Outputs one line per procedure for localhost:1234
+jeprof --raw localhost:1234 > ./local.raw
+jeprof --text ./local.raw
+ Fetches a remote profile for later analysis and then
+ analyzes it in text mode.
+EOF
+}
+
+sub version_string {
+ return <<EOF
+jeprof (part of jemalloc $JEPROF_VERSION)
+based on pprof (part of gperftools $PPROF_VERSION)
+
+Copyright 1998-2007 Google Inc.
+
+This is BSD licensed software; see the source for copying conditions
+and license information.
+There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE.
+EOF
+}
+
+sub usage {
+ my $msg = shift;
+ print STDERR "$msg\n\n";
+ print STDERR usage_string();
+ print STDERR "\nFATAL ERROR: $msg\n"; # just as a reminder
+ exit(1);
+}
+
+sub Init() {
+ # Setup tmp-file name and handler to clean it up.
+ # We do this in the very beginning so that we can use
+ # error() and cleanup() function anytime here after.
+ $main::tmpfile_sym = "/tmp/jeprof$$.sym";
+ $main::tmpfile_ps = "/tmp/jeprof$$";
+ $main::next_tmpfile = 0;
+ $SIG{'INT'} = \&sighandler;
+
+ # Cache from filename/linenumber to source code
+ $main::source_cache = ();
+
+ $main::opt_help = 0;
+ $main::opt_version = 0;
+
+ $main::opt_cum = 0;
+ $main::opt_base = '';
+ $main::opt_addresses = 0;
+ $main::opt_lines = 0;
+ $main::opt_functions = 0;
+ $main::opt_files = 0;
+ $main::opt_lib_prefix = "";
+
+ $main::opt_text = 0;
+ $main::opt_callgrind = 0;
+ $main::opt_list = "";
+ $main::opt_disasm = "";
+ $main::opt_symbols = 0;
+ $main::opt_gv = 0;
+ $main::opt_evince = 0;
+ $main::opt_web = 0;
+ $main::opt_dot = 0;
+ $main::opt_ps = 0;
+ $main::opt_pdf = 0;
+ $main::opt_gif = 0;
+ $main::opt_svg = 0;
+ $main::opt_raw = 0;
+ $main::opt_collapsed = 0;
+
+ $main::opt_nodecount = 80;
+ $main::opt_nodefraction = 0.005;
+ $main::opt_edgefraction = 0.001;
+ $main::opt_maxdegree = 8;
+ $main::opt_focus = '';
+ $main::opt_thread = undef;
+ $main::opt_ignore = '';
+ $main::opt_scale = 0;
+ $main::opt_heapcheck = 0;
+ $main::opt_retain = '';
+ $main::opt_exclude = '';
+ $main::opt_seconds = 30;
+ $main::opt_lib = "";
+
+ $main::opt_inuse_space = 0;
+ $main::opt_inuse_objects = 0;
+ $main::opt_alloc_space = 0;
+ $main::opt_alloc_objects = 0;
+ $main::opt_show_bytes = 0;
+ $main::opt_drop_negative = 0;
+ $main::opt_interactive = 0;
+
+ $main::opt_total_delay = 0;
+ $main::opt_contentions = 0;
+ $main::opt_mean_delay = 0;
+
+ $main::opt_tools = "";
+ $main::opt_debug = 0;
+ $main::opt_test = 0;
+ $main::opt_debug_syms_by_id = 0;
+
+ # These are undocumented flags used only by unittests.
+ $main::opt_test_stride = 0;
+
+ # Are we using $SYMBOL_PAGE?
+ $main::use_symbol_page = 0;
+
+ # Files returned by TempName.
+ %main::tempnames = ();
+
+ # Type of profile we are dealing with
+ # Supported types:
+ # cpu
+ # heap
+ # growth
+ # contention
+ $main::profile_type = ''; # Empty type means "unknown"
+
+ GetOptions("help!" => \$main::opt_help,
+ "version!" => \$main::opt_version,
+ "cum!" => \$main::opt_cum,
+ "base=s" => \$main::opt_base,
+ "seconds=i" => \$main::opt_seconds,
+ "add_lib=s" => \$main::opt_lib,
+ "lib_prefix=s" => \$main::opt_lib_prefix,
+ "functions!" => \$main::opt_functions,
+ "lines!" => \$main::opt_lines,
+ "addresses!" => \$main::opt_addresses,
+ "files!" => \$main::opt_files,
+ "text!" => \$main::opt_text,
+ "callgrind!" => \$main::opt_callgrind,
+ "list=s" => \$main::opt_list,
+ "disasm=s" => \$main::opt_disasm,
+ "symbols!" => \$main::opt_symbols,
+ "gv!" => \$main::opt_gv,
+ "evince!" => \$main::opt_evince,
+ "web!" => \$main::opt_web,
+ "dot!" => \$main::opt_dot,
+ "ps!" => \$main::opt_ps,
+ "pdf!" => \$main::opt_pdf,
+ "svg!" => \$main::opt_svg,
+ "gif!" => \$main::opt_gif,
+ "raw!" => \$main::opt_raw,
+ "collapsed!" => \$main::opt_collapsed,
+ "interactive!" => \$main::opt_interactive,
+ "nodecount=i" => \$main::opt_nodecount,
+ "nodefraction=f" => \$main::opt_nodefraction,
+ "edgefraction=f" => \$main::opt_edgefraction,
+ "maxdegree=i" => \$main::opt_maxdegree,
+ "focus=s" => \$main::opt_focus,
+ "thread=s" => \$main::opt_thread,
+ "ignore=s" => \$main::opt_ignore,
+ "scale=i" => \$main::opt_scale,
+ "heapcheck" => \$main::opt_heapcheck,
+ "retain=s" => \$main::opt_retain,
+ "exclude=s" => \$main::opt_exclude,
+ "inuse_space!" => \$main::opt_inuse_space,
+ "inuse_objects!" => \$main::opt_inuse_objects,
+ "alloc_space!" => \$main::opt_alloc_space,
+ "alloc_objects!" => \$main::opt_alloc_objects,
+ "show_bytes!" => \$main::opt_show_bytes,
+ "drop_negative!" => \$main::opt_drop_negative,
+ "total_delay!" => \$main::opt_total_delay,
+ "contentions!" => \$main::opt_contentions,
+ "mean_delay!" => \$main::opt_mean_delay,
+ "tools=s" => \$main::opt_tools,
+ "test!" => \$main::opt_test,
+ "debug!" => \$main::opt_debug,
+ "debug-syms-by-id!" => \$main::opt_debug_syms_by_id,
+ # Undocumented flags used only by unittests:
+ "test_stride=i" => \$main::opt_test_stride,
+ ) || usage("Invalid option(s)");
+
+ # Deal with the standard --help and --version
+ if ($main::opt_help) {
+ print usage_string();
+ exit(0);
+ }
+
+ if ($main::opt_version) {
+ print version_string();
+ exit(0);
+ }
+
+ # Disassembly/listing/symbols mode requires address-level info
+ if ($main::opt_disasm || $main::opt_list || $main::opt_symbols) {
+ $main::opt_functions = 0;
+ $main::opt_lines = 0;
+ $main::opt_addresses = 1;
+ $main::opt_files = 0;
+ }
+
+ # Check heap-profiling flags
+ if ($main::opt_inuse_space +
+ $main::opt_inuse_objects +
+ $main::opt_alloc_space +
+ $main::opt_alloc_objects > 1) {
+ usage("Specify at most on of --inuse/--alloc options");
+ }
+
+ # Check output granularities
+ my $grains =
+ $main::opt_functions +
+ $main::opt_lines +
+ $main::opt_addresses +
+ $main::opt_files +
+ 0;
+ if ($grains > 1) {
+ usage("Only specify one output granularity option");
+ }
+ if ($grains == 0) {
+ $main::opt_functions = 1;
+ }
+
+ # Check output modes
+ my $modes =
+ $main::opt_text +
+ $main::opt_callgrind +
+ ($main::opt_list eq '' ? 0 : 1) +
+ ($main::opt_disasm eq '' ? 0 : 1) +
+ ($main::opt_symbols == 0 ? 0 : 1) +
+ $main::opt_gv +
+ $main::opt_evince +
+ $main::opt_web +
+ $main::opt_dot +
+ $main::opt_ps +
+ $main::opt_pdf +
+ $main::opt_svg +
+ $main::opt_gif +
+ $main::opt_raw +
+ $main::opt_collapsed +
+ $main::opt_interactive +
+ 0;
+ if ($modes > 1) {
+ usage("Only specify one output mode");
+ }
+ if ($modes == 0) {
+ if (-t STDOUT) { # If STDOUT is a tty, activate interactive mode
+ $main::opt_interactive = 1;
+ } else {
+ $main::opt_text = 1;
+ }
+ }
+
+ if ($main::opt_test) {
+ RunUnitTests();
+ # Should not return
+ exit(1);
+ }
+
+ # Binary name and profile arguments list
+ $main::prog = "";
+ @main::pfile_args = ();
+
+ # Remote profiling without a binary (using $SYMBOL_PAGE instead)
+ if (@ARGV > 0) {
+ if (IsProfileURL($ARGV[0])) {
+ $main::use_symbol_page = 1;
+ } elsif (IsSymbolizedProfileFile($ARGV[0])) {
+ $main::use_symbolized_profile = 1;
+ $main::prog = $UNKNOWN_BINARY; # will be set later from the profile file
+ }
+ }
+
+ if ($main::use_symbol_page || $main::use_symbolized_profile) {
+ # We don't need a binary!
+ my %disabled = ('--lines' => $main::opt_lines,
+ '--disasm' => $main::opt_disasm);
+ for my $option (keys %disabled) {
+ usage("$option cannot be used without a binary") if $disabled{$option};
+ }
+ # Set $main::prog later...
+ scalar(@ARGV) || usage("Did not specify profile file");
+ } elsif ($main::opt_symbols) {
+ # --symbols needs a binary-name (to run nm on, etc) but not profiles
+ $main::prog = shift(@ARGV) || usage("Did not specify program");
+ } else {
+ $main::prog = shift(@ARGV) || usage("Did not specify program");
+ scalar(@ARGV) || usage("Did not specify profile file");
+ }
+
+ # Parse profile file/location arguments
+ foreach my $farg (@ARGV) {
+ if ($farg =~ m/(.*)\@([0-9]+)(|\/.*)$/ ) {
+ my $machine = $1;
+ my $num_machines = $2;
+ my $path = $3;
+ for (my $i = 0; $i < $num_machines; $i++) {
+ unshift(@main::pfile_args, "$i.$machine$path");
+ }
+ } else {
+ unshift(@main::pfile_args, $farg);
+ }
+ }
+
+ if ($main::use_symbol_page) {
+ unless (IsProfileURL($main::pfile_args[0])) {
+ error("The first profile should be a remote form to use $SYMBOL_PAGE\n");
+ }
+ CheckSymbolPage();
+ $main::prog = FetchProgramName();
+ } elsif (!$main::use_symbolized_profile) { # may not need objtools!
+ ConfigureObjTools($main::prog)
+ }
+
+ # Break the opt_lib_prefix into the prefix_list array
+ @prefix_list = split (',', $main::opt_lib_prefix);
+
+ # Remove trailing / from the prefixes, in the list to prevent
+ # searching things like /my/path//lib/mylib.so
+ foreach (@prefix_list) {
+ s|/+$||;
+ }
+
+ # Flag to prevent us from trying over and over to use
+ # elfutils if it's not installed (used only with
+ # --debug-syms-by-id option).
+ $main::gave_up_on_elfutils = 0;
+}
+
+sub FilterAndPrint {
+ my ($profile, $symbols, $libs, $thread) = @_;
+
+ # Get total data in profile
+ my $total = TotalProfile($profile);
+
+ # Remove uniniteresting stack items
+ $profile = RemoveUninterestingFrames($symbols, $profile);
+
+ # Focus?
+ if ($main::opt_focus ne '') {
+ $profile = FocusProfile($symbols, $profile, $main::opt_focus);
+ }
+
+ # Ignore?
+ if ($main::opt_ignore ne '') {
+ $profile = IgnoreProfile($symbols, $profile, $main::opt_ignore);
+ }
+
+ my $calls = ExtractCalls($symbols, $profile);
+
+ # Reduce profiles to required output granularity, and also clean
+ # each stack trace so a given entry exists at most once.
+ my $reduced = ReduceProfile($symbols, $profile);
+
+ # Get derived profiles
+ my $flat = FlatProfile($reduced);
+ my $cumulative = CumulativeProfile($reduced);
+
+ # Print
+ if (!$main::opt_interactive) {
+ if ($main::opt_disasm) {
+ PrintDisassembly($libs, $flat, $cumulative, $main::opt_disasm);
+ } elsif ($main::opt_list) {
+ PrintListing($total, $libs, $flat, $cumulative, $main::opt_list, 0);
+ } elsif ($main::opt_text) {
+ # Make sure the output is empty when have nothing to report
+ # (only matters when --heapcheck is given but we must be
+ # compatible with old branches that did not pass --heapcheck always):
+ if ($total != 0) {
+ printf("Total%s: %s %s\n",
+ (defined($thread) ? " (t$thread)" : ""),
+ Unparse($total), Units());
+ }
+ PrintText($symbols, $flat, $cumulative, -1);
+ } elsif ($main::opt_raw) {
+ PrintSymbolizedProfile($symbols, $profile, $main::prog);
+ } elsif ($main::opt_collapsed) {
+ PrintCollapsedStacks($symbols, $profile);
+ } elsif ($main::opt_callgrind) {
+ PrintCallgrind($calls);
+ } else {
+ if (PrintDot($main::prog, $symbols, $profile, $flat, $cumulative, $total)) {
+ if ($main::opt_gv) {
+ RunGV(TempName($main::next_tmpfile, "ps"), "");
+ } elsif ($main::opt_evince) {
+ RunEvince(TempName($main::next_tmpfile, "pdf"), "");
+ } elsif ($main::opt_web) {
+ my $tmp = TempName($main::next_tmpfile, "svg");
+ RunWeb($tmp);
+ # The command we run might hand the file name off
+ # to an already running browser instance and then exit.
+ # Normally, we'd remove $tmp on exit (right now),
+ # but fork a child to remove $tmp a little later, so that the
+ # browser has time to load it first.
+ delete $main::tempnames{$tmp};
+ if (fork() == 0) {
+ sleep 5;
+ unlink($tmp);
+ exit(0);
+ }
+ }
+ } else {
+ cleanup();
+ exit(1);
+ }
+ }
+ } else {
+ InteractiveMode($profile, $symbols, $libs, $total);
+ }
+}
+
+sub Main() {
+ Init();
+ $main::collected_profile = undef;
+ @main::profile_files = ();
+ $main::op_time = time();
+
+ # Printing symbols is special and requires a lot less info that most.
+ if ($main::opt_symbols) {
+ PrintSymbols(*STDIN); # Get /proc/maps and symbols output from stdin
+ return;
+ }
+
+ # Fetch all profile data
+ FetchDynamicProfiles();
+
+ # this will hold symbols that we read from the profile files
+ my $symbol_map = {};
+
+ # Read one profile, pick the last item on the list
+ my $data = ReadProfile($main::prog, pop(@main::profile_files));
+ my $profile = $data->{profile};
+ my $pcs = $data->{pcs};
+ my $libs = $data->{libs}; # Info about main program and shared libraries
+ $symbol_map = MergeSymbols($symbol_map, $data->{symbols});
+
+ # Add additional profiles, if available.
+ if (scalar(@main::profile_files) > 0) {
+ foreach my $pname (@main::profile_files) {
+ my $data2 = ReadProfile($main::prog, $pname);
+ $profile = AddProfile($profile, $data2->{profile});
+ $pcs = AddPcs($pcs, $data2->{pcs});
+ $symbol_map = MergeSymbols($symbol_map, $data2->{symbols});
+ }
+ }
+
+ # Subtract base from profile, if specified
+ if ($main::opt_base ne '') {
+ my $base = ReadProfile($main::prog, $main::opt_base);
+ $profile = SubtractProfile($profile, $base->{profile});
+ $pcs = AddPcs($pcs, $base->{pcs});
+ $symbol_map = MergeSymbols($symbol_map, $base->{symbols});
+ }
+
+ # Collect symbols
+ my $symbols;
+ if ($main::use_symbolized_profile) {
+ $symbols = FetchSymbols($pcs, $symbol_map);
+ } elsif ($main::use_symbol_page) {
+ $symbols = FetchSymbols($pcs);
+ } else {
+ # TODO(csilvers): $libs uses the /proc/self/maps data from profile1,
+ # which may differ from the data from subsequent profiles, especially
+ # if they were run on different machines. Use appropriate libs for
+ # each pc somehow.
+ $symbols = ExtractSymbols($libs, $pcs);
+ }
+
+ if (!defined($main::opt_thread)) {
+ FilterAndPrint($profile, $symbols, $libs);
+ }
+ if (defined($data->{threads})) {
+ foreach my $thread (sort { $a <=> $b } keys(%{$data->{threads}})) {
+ if (defined($main::opt_thread) &&
+ ($main::opt_thread eq '*' || $main::opt_thread == $thread)) {
+ my $thread_profile = $data->{threads}{$thread};
+ FilterAndPrint($thread_profile, $symbols, $libs, $thread);
+ }
+ }
+ }
+
+ cleanup();
+ exit(0);
+}
+
+##### Entry Point #####
+
+Main();
+
+# Temporary code to detect if we're running on a Goobuntu system.
+# These systems don't have the right stuff installed for the special
+# Readline libraries to work, so as a temporary workaround, we default
+# to using the normal stdio code, rather than the fancier readline-based
+# code
+sub ReadlineMightFail {
+ if (-e '/lib/libtermcap.so.2') {
+ return 0; # libtermcap exists, so readline should be okay
+ } else {
+ return 1;
+ }
+}
+
+sub RunGV {
+ my $fname = shift;
+ my $bg = shift; # "" or " &" if we should run in background
+ if (!system(ShellEscape(@GV, "--version") . " >$dev_null 2>&1")) {
+ # Options using double dash are supported by this gv version.
+ # Also, turn on noantialias to better handle bug in gv for
+ # postscript files with large dimensions.
+ # TODO: Maybe we should not pass the --noantialias flag
+ # if the gv version is known to work properly without the flag.
+ system(ShellEscape(@GV, "--scale=$main::opt_scale", "--noantialias", $fname)
+ . $bg);
+ } else {
+ # Old gv version - only supports options that use single dash.
+ print STDERR ShellEscape(@GV, "-scale", $main::opt_scale) . "\n";
+ system(ShellEscape(@GV, "-scale", "$main::opt_scale", $fname) . $bg);
+ }
+}
+
+sub RunEvince {
+ my $fname = shift;
+ my $bg = shift; # "" or " &" if we should run in background
+ system(ShellEscape(@EVINCE, $fname) . $bg);
+}
+
+sub RunWeb {
+ my $fname = shift;
+ print STDERR "Loading web page file:///$fname\n";
+
+ if (`uname` =~ /Darwin/) {
+ # OS X: open will use standard preference for SVG files.
+ system("/usr/bin/open", $fname);
+ return;
+ }
+
+ # Some kind of Unix; try generic symlinks, then specific browsers.
+ # (Stop once we find one.)
+ # Works best if the browser is already running.
+ my @alt = (
+ "/etc/alternatives/gnome-www-browser",
+ "/etc/alternatives/x-www-browser",
+ "google-chrome",
+ "firefox",
+ );
+ foreach my $b (@alt) {
+ if (system($b, $fname) == 0) {
+ return;
+ }
+ }
+
+ print STDERR "Could not load web browser.\n";
+}
+
+sub RunKcachegrind {
+ my $fname = shift;
+ my $bg = shift; # "" or " &" if we should run in background
+ print STDERR "Starting '@KCACHEGRIND " . $fname . $bg . "'\n";
+ system(ShellEscape(@KCACHEGRIND, $fname) . $bg);
+}
+
+
+##### Interactive helper routines #####
+
+sub InteractiveMode {
+ $| = 1; # Make output unbuffered for interactive mode
+ my ($orig_profile, $symbols, $libs, $total) = @_;
+
+ print STDERR "Welcome to jeprof! For help, type 'help'.\n";
+
+ # Use ReadLine if it's installed and input comes from a console.
+ if ( -t STDIN &&
+ !ReadlineMightFail() &&
+ defined(eval {require Term::ReadLine}) ) {
+ my $term = new Term::ReadLine 'jeprof';
+ while ( defined ($_ = $term->readline('(jeprof) '))) {
+ $term->addhistory($_) if /\S/;
+ if (!InteractiveCommand($orig_profile, $symbols, $libs, $total, $_)) {
+ last; # exit when we get an interactive command to quit
+ }
+ }
+ } else { # don't have readline
+ while (1) {
+ print STDERR "(jeprof) ";
+ $_ = <STDIN>;
+ last if ! defined $_ ;
+ s/\r//g; # turn windows-looking lines into unix-looking lines
+
+ # Save some flags that might be reset by InteractiveCommand()
+ my $save_opt_lines = $main::opt_lines;
+
+ if (!InteractiveCommand($orig_profile, $symbols, $libs, $total, $_)) {
+ last; # exit when we get an interactive command to quit
+ }
+
+ # Restore flags
+ $main::opt_lines = $save_opt_lines;
+ }
+ }
+}
+
+# Takes two args: orig profile, and command to run.
+# Returns 1 if we should keep going, or 0 if we were asked to quit
+sub InteractiveCommand {
+ my($orig_profile, $symbols, $libs, $total, $command) = @_;
+ $_ = $command; # just to make future m//'s easier
+ if (!defined($_)) {
+ print STDERR "\n";
+ return 0;
+ }
+ if (m/^\s*quit/) {
+ return 0;
+ }
+ if (m/^\s*help/) {
+ InteractiveHelpMessage();
+ return 1;
+ }
+ # Clear all the mode options -- mode is controlled by "$command"
+ $main::opt_text = 0;
+ $main::opt_callgrind = 0;
+ $main::opt_disasm = 0;
+ $main::opt_list = 0;
+ $main::opt_gv = 0;
+ $main::opt_evince = 0;
+ $main::opt_cum = 0;
+
+ if (m/^\s*(text|top)(\d*)\s*(.*)/) {
+ $main::opt_text = 1;
+
+ my $line_limit = ($2 ne "") ? int($2) : 10;
+
+ my $routine;
+ my $ignore;
+ ($routine, $ignore) = ParseInteractiveArgs($3);
+
+ my $profile = ProcessProfile($total, $orig_profile, $symbols, "", $ignore);
+ my $reduced = ReduceProfile($symbols, $profile);
+
+ # Get derived profiles
+ my $flat = FlatProfile($reduced);
+ my $cumulative = CumulativeProfile($reduced);
+
+ PrintText($symbols, $flat, $cumulative, $line_limit);
+ return 1;
+ }
+ if (m/^\s*callgrind\s*([^ \n]*)/) {
+ $main::opt_callgrind = 1;
+
+ # Get derived profiles
+ my $calls = ExtractCalls($symbols, $orig_profile);
+ my $filename = $1;
+ if ( $1 eq '' ) {
+ $filename = TempName($main::next_tmpfile, "callgrind");
+ }
+ PrintCallgrind($calls, $filename);
+ if ( $1 eq '' ) {
+ RunKcachegrind($filename, " & ");
+ $main::next_tmpfile++;
+ }
+
+ return 1;
+ }
+ if (m/^\s*(web)?list\s*(.+)/) {
+ my $html = (defined($1) && ($1 eq "web"));
+ $main::opt_list = 1;
+
+ my $routine;
+ my $ignore;
+ ($routine, $ignore) = ParseInteractiveArgs($2);
+
+ my $profile = ProcessProfile($total, $orig_profile, $symbols, "", $ignore);
+ my $reduced = ReduceProfile($symbols, $profile);
+
+ # Get derived profiles
+ my $flat = FlatProfile($reduced);
+ my $cumulative = CumulativeProfile($reduced);
+
+ PrintListing($total, $libs, $flat, $cumulative, $routine, $html);
+ return 1;
+ }
+ if (m/^\s*disasm\s*(.+)/) {
+ $main::opt_disasm = 1;
+
+ my $routine;
+ my $ignore;
+ ($routine, $ignore) = ParseInteractiveArgs($1);
+
+ # Process current profile to account for various settings
+ my $profile = ProcessProfile($total, $orig_profile, $symbols, "", $ignore);
+ my $reduced = ReduceProfile($symbols, $profile);
+
+ # Get derived profiles
+ my $flat = FlatProfile($reduced);
+ my $cumulative = CumulativeProfile($reduced);
+
+ PrintDisassembly($libs, $flat, $cumulative, $routine);
+ return 1;
+ }
+ if (m/^\s*(gv|web|evince)\s*(.*)/) {
+ $main::opt_gv = 0;
+ $main::opt_evince = 0;
+ $main::opt_web = 0;
+ if ($1 eq "gv") {
+ $main::opt_gv = 1;
+ } elsif ($1 eq "evince") {
+ $main::opt_evince = 1;
+ } elsif ($1 eq "web") {
+ $main::opt_web = 1;
+ }
+
+ my $focus;
+ my $ignore;
+ ($focus, $ignore) = ParseInteractiveArgs($2);
+
+ # Process current profile to account for various settings
+ my $profile = ProcessProfile($total, $orig_profile, $symbols,
+ $focus, $ignore);
+ my $reduced = ReduceProfile($symbols, $profile);
+
+ # Get derived profiles
+ my $flat = FlatProfile($reduced);
+ my $cumulative = CumulativeProfile($reduced);
+
+ if (PrintDot($main::prog, $symbols, $profile, $flat, $cumulative, $total)) {
+ if ($main::opt_gv) {
+ RunGV(TempName($main::next_tmpfile, "ps"), " &");
+ } elsif ($main::opt_evince) {
+ RunEvince(TempName($main::next_tmpfile, "pdf"), " &");
+ } elsif ($main::opt_web) {
+ RunWeb(TempName($main::next_tmpfile, "svg"));
+ }
+ $main::next_tmpfile++;
+ }
+ return 1;
+ }
+ if (m/^\s*$/) {
+ return 1;
+ }
+ print STDERR "Unknown command: try 'help'.\n";
+ return 1;
+}
+
+
+sub ProcessProfile {
+ my $total_count = shift;
+ my $orig_profile = shift;
+ my $symbols = shift;
+ my $focus = shift;
+ my $ignore = shift;
+
+ # Process current profile to account for various settings
+ my $profile = $orig_profile;
+ printf("Total: %s %s\n", Unparse($total_count), Units());
+ if ($focus ne '') {
+ $profile = FocusProfile($symbols, $profile, $focus);
+ my $focus_count = TotalProfile($profile);
+ printf("After focusing on '%s': %s %s of %s (%0.1f%%)\n",
+ $focus,
+ Unparse($focus_count), Units(),
+ Unparse($total_count), ($focus_count*100.0) / $total_count);
+ }
+ if ($ignore ne '') {
+ $profile = IgnoreProfile($symbols, $profile, $ignore);
+ my $ignore_count = TotalProfile($profile);
+ printf("After ignoring '%s': %s %s of %s (%0.1f%%)\n",
+ $ignore,
+ Unparse($ignore_count), Units(),
+ Unparse($total_count),
+ ($ignore_count*100.0) / $total_count);
+ }
+
+ return $profile;
+}
+
+sub InteractiveHelpMessage {
+ print STDERR <<ENDOFHELP;
+Interactive jeprof mode
+
+Commands:
+ gv
+ gv [focus] [-ignore1] [-ignore2]
+ Show graphical hierarchical display of current profile. Without
+ any arguments, shows all samples in the profile. With the optional
+ "focus" argument, restricts the samples shown to just those where
+ the "focus" regular expression matches a routine name on the stack
+ trace.
+
+ web
+ web [focus] [-ignore1] [-ignore2]
+ Like GV, but displays profile in your web browser instead of using
+ Ghostview. Works best if your web browser is already running.
+ To change the browser that gets used:
+ On Linux, set the /etc/alternatives/gnome-www-browser symlink.
+ On OS X, change the Finder association for SVG files.
+
+ list [routine_regexp] [-ignore1] [-ignore2]
+ Show source listing of routines whose names match "routine_regexp"
+
+ weblist [routine_regexp] [-ignore1] [-ignore2]
+ Displays a source listing of routines whose names match "routine_regexp"
+ in a web browser. You can click on source lines to view the
+ corresponding disassembly.
+
+ top [--cum] [-ignore1] [-ignore2]
+ top20 [--cum] [-ignore1] [-ignore2]
+ top37 [--cum] [-ignore1] [-ignore2]
+ Show top lines ordered by flat profile count, or cumulative count
+ if --cum is specified. If a number is present after 'top', the
+ top K routines will be shown (defaults to showing the top 10)
+
+ disasm [routine_regexp] [-ignore1] [-ignore2]
+ Show disassembly of routines whose names match "routine_regexp",
+ annotated with sample counts.
+
+ callgrind
+ callgrind [filename]
+ Generates callgrind file. If no filename is given, kcachegrind is called.
+
+ help - This listing
+ quit or ^D - End jeprof
+
+For commands that accept optional -ignore tags, samples where any routine in
+the stack trace matches the regular expression in any of the -ignore
+parameters will be ignored.
+
+Further pprof details are available at this location (or one similar):
+
+ /usr/doc/gperftools-$PPROF_VERSION/cpu_profiler.html
+ /usr/doc/gperftools-$PPROF_VERSION/heap_profiler.html
+
+ENDOFHELP
+}
+sub ParseInteractiveArgs {
+ my $args = shift;
+ my $focus = "";
+ my $ignore = "";
+ my @x = split(/ +/, $args);
+ foreach $a (@x) {
+ if ($a =~ m/^(--|-)lines$/) {
+ $main::opt_lines = 1;
+ } elsif ($a =~ m/^(--|-)cum$/) {
+ $main::opt_cum = 1;
+ } elsif ($a =~ m/^-(.*)/) {
+ $ignore .= (($ignore ne "") ? "|" : "" ) . $1;
+ } else {
+ $focus .= (($focus ne "") ? "|" : "" ) . $a;
+ }
+ }
+ if ($ignore ne "") {
+ print STDERR "Ignoring samples in call stacks that match '$ignore'\n";
+ }
+ return ($focus, $ignore);
+}
+
+##### Output code #####
+
+sub TempName {
+ my $fnum = shift;
+ my $ext = shift;
+ my $file = "$main::tmpfile_ps.$fnum.$ext";
+ $main::tempnames{$file} = 1;
+ return $file;
+}
+
+# Print profile data in packed binary format (64-bit) to standard out
+sub PrintProfileData {
+ my $profile = shift;
+
+ # print header (64-bit style)
+ # (zero) (header-size) (version) (sample-period) (zero)
+ print pack('L*', 0, 0, 3, 0, 0, 0, 1, 0, 0, 0);
+
+ foreach my $k (keys(%{$profile})) {
+ my $count = $profile->{$k};
+ my @addrs = split(/\n/, $k);
+ if ($#addrs >= 0) {
+ my $depth = $#addrs + 1;
+ # int(foo / 2**32) is the only reliable way to get rid of bottom
+ # 32 bits on both 32- and 64-bit systems.
+ print pack('L*', $count & 0xFFFFFFFF, int($count / 2**32));
+ print pack('L*', $depth & 0xFFFFFFFF, int($depth / 2**32));
+
+ foreach my $full_addr (@addrs) {
+ my $addr = $full_addr;
+ $addr =~ s/0x0*//; # strip off leading 0x, zeroes
+ if (length($addr) > 16) {
+ print STDERR "Invalid address in profile: $full_addr\n";
+ next;
+ }
+ my $low_addr = substr($addr, -8); # get last 8 hex chars
+ my $high_addr = substr($addr, -16, 8); # get up to 8 more hex chars
+ print pack('L*', hex('0x' . $low_addr), hex('0x' . $high_addr));
+ }
+ }
+ }
+}
+
+# Print symbols and profile data
+sub PrintSymbolizedProfile {
+ my $symbols = shift;
+ my $profile = shift;
+ my $prog = shift;
+
+ $SYMBOL_PAGE =~ m,[^/]+$,; # matches everything after the last slash
+ my $symbol_marker = $&;
+
+ print '--- ', $symbol_marker, "\n";
+ if (defined($prog)) {
+ print 'binary=', $prog, "\n";
+ }
+ while (my ($pc, $name) = each(%{$symbols})) {
+ my $sep = ' ';
+ print '0x', $pc;
+ # We have a list of function names, which include the inlined
+ # calls. They are separated (and terminated) by --, which is
+ # illegal in function names.
+ for (my $j = 2; $j <= $#{$name}; $j += 3) {
+ print $sep, $name->[$j];
+ $sep = '--';
+ }
+ print "\n";
+ }
+ print '---', "\n";
+
+ my $profile_marker;
+ if ($main::profile_type eq 'heap') {
+ $HEAP_PAGE =~ m,[^/]+$,; # matches everything after the last slash
+ $profile_marker = $&;
+ } elsif ($main::profile_type eq 'growth') {
+ $GROWTH_PAGE =~ m,[^/]+$,; # matches everything after the last slash
+ $profile_marker = $&;
+ } elsif ($main::profile_type eq 'contention') {
+ $CONTENTION_PAGE =~ m,[^/]+$,; # matches everything after the last slash
+ $profile_marker = $&;
+ } else { # elsif ($main::profile_type eq 'cpu')
+ $PROFILE_PAGE =~ m,[^/]+$,; # matches everything after the last slash
+ $profile_marker = $&;
+ }
+
+ print '--- ', $profile_marker, "\n";
+ if (defined($main::collected_profile)) {
+ # if used with remote fetch, simply dump the collected profile to output.
+ open(SRC, "<$main::collected_profile");
+ while (<SRC>) {
+ print $_;
+ }
+ close(SRC);
+ } else {
+ # --raw/http: For everything to work correctly for non-remote profiles, we
+ # would need to extend PrintProfileData() to handle all possible profile
+ # types, re-enable the code that is currently disabled in ReadCPUProfile()
+ # and FixCallerAddresses(), and remove the remote profile dumping code in
+ # the block above.
+ die "--raw/http: jeprof can only dump remote profiles for --raw\n";
+ # dump a cpu-format profile to standard out
+ PrintProfileData($profile);
+ }
+}
+
+# Print text output
+sub PrintText {
+ my $symbols = shift;
+ my $flat = shift;
+ my $cumulative = shift;
+ my $line_limit = shift;
+
+ my $total = TotalProfile($flat);
+
+ # Which profile to sort by?
+ my $s = $main::opt_cum ? $cumulative : $flat;
+
+ my $running_sum = 0;
+ my $lines = 0;
+ foreach my $k (sort { GetEntry($s, $b) <=> GetEntry($s, $a) || $a cmp $b }
+ keys(%{$cumulative})) {
+ my $f = GetEntry($flat, $k);
+ my $c = GetEntry($cumulative, $k);
+ $running_sum += $f;
+
+ my $sym = $k;
+ if (exists($symbols->{$k})) {
+ $sym = $symbols->{$k}->[0] . " " . $symbols->{$k}->[1];
+ if ($main::opt_addresses) {
+ $sym = $k . " " . $sym;
+ }
+ }
+
+ if ($f != 0 || $c != 0) {
+ printf("%8s %6s %6s %8s %6s %s\n",
+ Unparse($f),
+ Percent($f, $total),
+ Percent($running_sum, $total),
+ Unparse($c),
+ Percent($c, $total),
+ $sym);
+ }
+ $lines++;
+ last if ($line_limit >= 0 && $lines >= $line_limit);
+ }
+}
+
+# Callgrind format has a compression for repeated function and file
+# names. You show the name the first time, and just use its number
+# subsequently. This can cut down the file to about a third or a
+# quarter of its uncompressed size. $key and $val are the key/value
+# pair that would normally be printed by callgrind; $map is a map from
+# value to number.
+sub CompressedCGName {
+ my($key, $val, $map) = @_;
+ my $idx = $map->{$val};
+ # For very short keys, providing an index hurts rather than helps.
+ if (length($val) <= 3) {
+ return "$key=$val\n";
+ } elsif (defined($idx)) {
+ return "$key=($idx)\n";
+ } else {
+ # scalar(keys $map) gives the number of items in the map.
+ $idx = scalar(keys(%{$map})) + 1;
+ $map->{$val} = $idx;
+ return "$key=($idx) $val\n";
+ }
+}
+
+# Print the call graph in a way that's suiteable for callgrind.
+sub PrintCallgrind {
+ my $calls = shift;
+ my $filename;
+ my %filename_to_index_map;
+ my %fnname_to_index_map;
+
+ if ($main::opt_interactive) {
+ $filename = shift;
+ print STDERR "Writing callgrind file to '$filename'.\n"
+ } else {
+ $filename = "&STDOUT";
+ }
+ open(CG, ">$filename");
+ printf CG ("events: Hits\n\n");
+ foreach my $call ( map { $_->[0] }
+ sort { $a->[1] cmp $b ->[1] ||
+ $a->[2] <=> $b->[2] }
+ map { /([^:]+):(\d+):([^ ]+)( -> ([^:]+):(\d+):(.+))?/;
+ [$_, $1, $2] }
+ keys %$calls ) {
+ my $count = int($calls->{$call});
+ $call =~ /([^:]+):(\d+):([^ ]+)( -> ([^:]+):(\d+):(.+))?/;
+ my ( $caller_file, $caller_line, $caller_function,
+ $callee_file, $callee_line, $callee_function ) =
+ ( $1, $2, $3, $5, $6, $7 );
+
+ # TODO(csilvers): for better compression, collect all the
+ # caller/callee_files and functions first, before printing
+ # anything, and only compress those referenced more than once.
+ printf CG CompressedCGName("fl", $caller_file, \%filename_to_index_map);
+ printf CG CompressedCGName("fn", $caller_function, \%fnname_to_index_map);
+ if (defined $6) {
+ printf CG CompressedCGName("cfl", $callee_file, \%filename_to_index_map);
+ printf CG CompressedCGName("cfn", $callee_function, \%fnname_to_index_map);
+ printf CG ("calls=$count $callee_line\n");
+ }
+ printf CG ("$caller_line $count\n\n");
+ }
+}
+
+# Print disassembly for all all routines that match $main::opt_disasm
+sub PrintDisassembly {
+ my $libs = shift;
+ my $flat = shift;
+ my $cumulative = shift;
+ my $disasm_opts = shift;
+
+ my $total = TotalProfile($flat);
+
+ foreach my $lib (@{$libs}) {
+ my $symbol_table = GetProcedureBoundaries($lib->[0], $disasm_opts);
+ my $offset = AddressSub($lib->[1], $lib->[3]);
+ foreach my $routine (sort ByName keys(%{$symbol_table})) {
+ my $start_addr = $symbol_table->{$routine}->[0];
+ my $end_addr = $symbol_table->{$routine}->[1];
+ # See if there are any samples in this routine
+ my $length = hex(AddressSub($end_addr, $start_addr));
+ my $addr = AddressAdd($start_addr, $offset);
+ for (my $i = 0; $i < $length; $i++) {
+ if (defined($cumulative->{$addr})) {
+ PrintDisassembledFunction($lib->[0], $offset,
+ $routine, $flat, $cumulative,
+ $start_addr, $end_addr, $total);
+ last;
+ }
+ $addr = AddressInc($addr);
+ }
+ }
+ }
+}
+
+# Return reference to array of tuples of the form:
+# [start_address, filename, linenumber, instruction, limit_address]
+# E.g.,
+# ["0x806c43d", "/foo/bar.cc", 131, "ret", "0x806c440"]
+sub Disassemble {
+ my $prog = shift;
+ my $offset = shift;
+ my $start_addr = shift;
+ my $end_addr = shift;
+
+ my $objdump = $obj_tool_map{"objdump"};
+ my $cmd = ShellEscape($objdump, "-C", "-d", "-l", "--no-show-raw-insn",
+ "--start-address=0x$start_addr",
+ "--stop-address=0x$end_addr", $prog);
+ open(OBJDUMP, "$cmd |") || error("$cmd: $!\n");
+ my @result = ();
+ my $filename = "";
+ my $linenumber = -1;
+ my $last = ["", "", "", ""];
+ while (<OBJDUMP>) {
+ s/\r//g; # turn windows-looking lines into unix-looking lines
+ chop;
+ if (m|\s*([^:\s]+):(\d+)\s*$|) {
+ # Location line of the form:
+ # <filename>:<linenumber>
+ $filename = $1;
+ $linenumber = $2;
+ } elsif (m/^ +([0-9a-f]+):\s*(.*)/) {
+ # Disassembly line -- zero-extend address to full length
+ my $addr = HexExtend($1);
+ my $k = AddressAdd($addr, $offset);
+ $last->[4] = $k; # Store ending address for previous instruction
+ $last = [$k, $filename, $linenumber, $2, $end_addr];
+ push(@result, $last);
+ }
+ }
+ close(OBJDUMP);
+ return @result;
+}
+
+# The input file should contain lines of the form /proc/maps-like
+# output (same format as expected from the profiles) or that looks
+# like hex addresses (like "0xDEADBEEF"). We will parse all
+# /proc/maps output, and for all the hex addresses, we will output
+# "short" symbol names, one per line, in the same order as the input.
+sub PrintSymbols {
+ my $maps_and_symbols_file = shift;
+
+ # ParseLibraries expects pcs to be in a set. Fine by us...
+ my @pclist = (); # pcs in sorted order
+ my $pcs = {};
+ my $map = "";
+ foreach my $line (<$maps_and_symbols_file>) {
+ $line =~ s/\r//g; # turn windows-looking lines into unix-looking lines
+ if ($line =~ /\b(0x[0-9a-f]+)\b/i) {
+ push(@pclist, HexExtend($1));
+ $pcs->{$pclist[-1]} = 1;
+ } else {
+ $map .= $line;
+ }
+ }
+
+ my $libs = ParseLibraries($main::prog, $map, $pcs);
+ my $symbols = ExtractSymbols($libs, $pcs);
+
+ foreach my $pc (@pclist) {
+ # ->[0] is the shortname, ->[2] is the full name
+ print(($symbols->{$pc}->[0] || "??") . "\n");
+ }
+}
+
+
+# For sorting functions by name
+sub ByName {
+ return ShortFunctionName($a) cmp ShortFunctionName($b);
+}
+
+# Print source-listing for all all routines that match $list_opts
+sub PrintListing {
+ my $total = shift;
+ my $libs = shift;
+ my $flat = shift;
+ my $cumulative = shift;
+ my $list_opts = shift;
+ my $html = shift;
+
+ my $output = \*STDOUT;
+ my $fname = "";
+
+ if ($html) {
+ # Arrange to write the output to a temporary file
+ $fname = TempName($main::next_tmpfile, "html");
+ $main::next_tmpfile++;
+ if (!open(TEMP, ">$fname")) {
+ print STDERR "$fname: $!\n";
+ return;
+ }
+ $output = \*TEMP;
+ print $output HtmlListingHeader();
+ printf $output ("<div class=\"legend\">%s<br>Total: %s %s</div>\n",
+ $main::prog, Unparse($total), Units());
+ }
+
+ my $listed = 0;
+ foreach my $lib (@{$libs}) {
+ my $symbol_table = GetProcedureBoundaries($lib->[0], $list_opts);
+ my $offset = AddressSub($lib->[1], $lib->[3]);
+ foreach my $routine (sort ByName keys(%{$symbol_table})) {
+ # Print if there are any samples in this routine
+ my $start_addr = $symbol_table->{$routine}->[0];
+ my $end_addr = $symbol_table->{$routine}->[1];
+ my $length = hex(AddressSub($end_addr, $start_addr));
+ my $addr = AddressAdd($start_addr, $offset);
+ for (my $i = 0; $i < $length; $i++) {
+ if (defined($cumulative->{$addr})) {
+ $listed += PrintSource(
+ $lib->[0], $offset,
+ $routine, $flat, $cumulative,
+ $start_addr, $end_addr,
+ $html,
+ $output);
+ last;
+ }
+ $addr = AddressInc($addr);
+ }
+ }
+ }
+
+ if ($html) {
+ if ($listed > 0) {
+ print $output HtmlListingFooter();
+ close($output);
+ RunWeb($fname);
+ } else {
+ close($output);
+ unlink($fname);
+ }
+ }
+}
+
+sub HtmlListingHeader {
+ return <<'EOF';
+<DOCTYPE html>
+<html>
+<head>
+<title>Pprof listing</title>
+<style type="text/css">
+body {
+ font-family: sans-serif;
+}
+h1 {
+ font-size: 1.5em;
+ margin-bottom: 4px;
+}
+.legend {
+ font-size: 1.25em;
+}
+.line {
+ color: #aaaaaa;
+}
+.nop {
+ color: #aaaaaa;
+}
+.unimportant {
+ color: #cccccc;
+}
+.disasmloc {
+ color: #000000;
+}
+.deadsrc {
+ cursor: pointer;
+}
+.deadsrc:hover {
+ background-color: #eeeeee;
+}
+.livesrc {
+ color: #0000ff;
+ cursor: pointer;
+}
+.livesrc:hover {
+ background-color: #eeeeee;
+}
+.asm {
+ color: #008800;
+ display: none;
+}
+</style>
+<script type="text/javascript">
+function jeprof_toggle_asm(e) {
+ var target;
+ if (!e) e = window.event;
+ if (e.target) target = e.target;
+ else if (e.srcElement) target = e.srcElement;
+
+ if (target) {
+ var asm = target.nextSibling;
+ if (asm && asm.className == "asm") {
+ asm.style.display = (asm.style.display == "block" ? "" : "block");
+ e.preventDefault();
+ return false;
+ }
+ }
+}
+</script>
+</head>
+<body>
+EOF
+}
+
+sub HtmlListingFooter {
+ return <<'EOF';
+</body>
+</html>
+EOF
+}
+
+sub HtmlEscape {
+ my $text = shift;
+ $text =~ s/&/&amp;/g;
+ $text =~ s/</&lt;/g;
+ $text =~ s/>/&gt;/g;
+ return $text;
+}
+
+# Returns the indentation of the line, if it has any non-whitespace
+# characters. Otherwise, returns -1.
+sub Indentation {
+ my $line = shift;
+ if (m/^(\s*)\S/) {
+ return length($1);
+ } else {
+ return -1;
+ }
+}
+
+# If the symbol table contains inlining info, Disassemble() may tag an
+# instruction with a location inside an inlined function. But for
+# source listings, we prefer to use the location in the function we
+# are listing. So use MapToSymbols() to fetch full location
+# information for each instruction and then pick out the first
+# location from a location list (location list contains callers before
+# callees in case of inlining).
+#
+# After this routine has run, each entry in $instructions contains:
+# [0] start address
+# [1] filename for function we are listing
+# [2] line number for function we are listing
+# [3] disassembly
+# [4] limit address
+# [5] most specific filename (may be different from [1] due to inlining)
+# [6] most specific line number (may be different from [2] due to inlining)
+sub GetTopLevelLineNumbers {
+ my ($lib, $offset, $instructions) = @_;
+ my $pcs = [];
+ for (my $i = 0; $i <= $#{$instructions}; $i++) {
+ push(@{$pcs}, $instructions->[$i]->[0]);
+ }
+ my $symbols = {};
+ MapToSymbols($lib, $offset, $pcs, $symbols);
+ for (my $i = 0; $i <= $#{$instructions}; $i++) {
+ my $e = $instructions->[$i];
+ push(@{$e}, $e->[1]);
+ push(@{$e}, $e->[2]);
+ my $addr = $e->[0];
+ my $sym = $symbols->{$addr};
+ if (defined($sym)) {
+ if ($#{$sym} >= 2 && $sym->[1] =~ m/^(.*):(\d+)$/) {
+ $e->[1] = $1; # File name
+ $e->[2] = $2; # Line number
+ }
+ }
+ }
+}
+
+# Print source-listing for one routine
+sub PrintSource {
+ my $prog = shift;
+ my $offset = shift;
+ my $routine = shift;
+ my $flat = shift;
+ my $cumulative = shift;
+ my $start_addr = shift;
+ my $end_addr = shift;
+ my $html = shift;
+ my $output = shift;
+
+ # Disassemble all instructions (just to get line numbers)
+ my @instructions = Disassemble($prog, $offset, $start_addr, $end_addr);
+ GetTopLevelLineNumbers($prog, $offset, \@instructions);
+
+ # Hack 1: assume that the first source file encountered in the
+ # disassembly contains the routine
+ my $filename = undef;
+ for (my $i = 0; $i <= $#instructions; $i++) {
+ if ($instructions[$i]->[2] >= 0) {
+ $filename = $instructions[$i]->[1];
+ last;
+ }
+ }
+ if (!defined($filename)) {
+ print STDERR "no filename found in $routine\n";
+ return 0;
+ }
+
+ # Hack 2: assume that the largest line number from $filename is the
+ # end of the procedure. This is typically safe since if P1 contains
+ # an inlined call to P2, then P2 usually occurs earlier in the
+ # source file. If this does not work, we might have to compute a
+ # density profile or just print all regions we find.
+ my $lastline = 0;
+ for (my $i = 0; $i <= $#instructions; $i++) {
+ my $f = $instructions[$i]->[1];
+ my $l = $instructions[$i]->[2];
+ if (($f eq $filename) && ($l > $lastline)) {
+ $lastline = $l;
+ }
+ }
+
+ # Hack 3: assume the first source location from "filename" is the start of
+ # the source code.
+ my $firstline = 1;
+ for (my $i = 0; $i <= $#instructions; $i++) {
+ if ($instructions[$i]->[1] eq $filename) {
+ $firstline = $instructions[$i]->[2];
+ last;
+ }
+ }
+
+ # Hack 4: Extend last line forward until its indentation is less than
+ # the indentation we saw on $firstline
+ my $oldlastline = $lastline;
+ {
+ if (!open(FILE, "<$filename")) {
+ print STDERR "$filename: $!\n";
+ return 0;
+ }
+ my $l = 0;
+ my $first_indentation = -1;
+ while (<FILE>) {
+ s/\r//g; # turn windows-looking lines into unix-looking lines
+ $l++;
+ my $indent = Indentation($_);
+ if ($l >= $firstline) {
+ if ($first_indentation < 0 && $indent >= 0) {
+ $first_indentation = $indent;
+ last if ($first_indentation == 0);
+ }
+ }
+ if ($l >= $lastline && $indent >= 0) {
+ if ($indent >= $first_indentation) {
+ $lastline = $l+1;
+ } else {
+ last;
+ }
+ }
+ }
+ close(FILE);
+ }
+
+ # Assign all samples to the range $firstline,$lastline,
+ # Hack 4: If an instruction does not occur in the range, its samples
+ # are moved to the next instruction that occurs in the range.
+ my $samples1 = {}; # Map from line number to flat count
+ my $samples2 = {}; # Map from line number to cumulative count
+ my $running1 = 0; # Unassigned flat counts
+ my $running2 = 0; # Unassigned cumulative counts
+ my $total1 = 0; # Total flat counts
+ my $total2 = 0; # Total cumulative counts
+ my %disasm = (); # Map from line number to disassembly
+ my $running_disasm = ""; # Unassigned disassembly
+ my $skip_marker = "---\n";
+ if ($html) {
+ $skip_marker = "";
+ for (my $l = $firstline; $l <= $lastline; $l++) {
+ $disasm{$l} = "";
+ }
+ }
+ my $last_dis_filename = '';
+ my $last_dis_linenum = -1;
+ my $last_touched_line = -1; # To detect gaps in disassembly for a line
+ foreach my $e (@instructions) {
+ # Add up counts for all address that fall inside this instruction
+ my $c1 = 0;
+ my $c2 = 0;
+ for (my $a = $e->[0]; $a lt $e->[4]; $a = AddressInc($a)) {
+ $c1 += GetEntry($flat, $a);
+ $c2 += GetEntry($cumulative, $a);
+ }
+
+ if ($html) {
+ my $dis = sprintf(" %6s %6s \t\t%8s: %s ",
+ HtmlPrintNumber($c1),
+ HtmlPrintNumber($c2),
+ UnparseAddress($offset, $e->[0]),
+ CleanDisassembly($e->[3]));
+
+ # Append the most specific source line associated with this instruction
+ if (length($dis) < 80) { $dis .= (' ' x (80 - length($dis))) };
+ $dis = HtmlEscape($dis);
+ my $f = $e->[5];
+ my $l = $e->[6];
+ if ($f ne $last_dis_filename) {
+ $dis .= sprintf("<span class=disasmloc>%s:%d</span>",
+ HtmlEscape(CleanFileName($f)), $l);
+ } elsif ($l ne $last_dis_linenum) {
+ # De-emphasize the unchanged file name portion
+ $dis .= sprintf("<span class=unimportant>%s</span>" .
+ "<span class=disasmloc>:%d</span>",
+ HtmlEscape(CleanFileName($f)), $l);
+ } else {
+ # De-emphasize the entire location
+ $dis .= sprintf("<span class=unimportant>%s:%d</span>",
+ HtmlEscape(CleanFileName($f)), $l);
+ }
+ $last_dis_filename = $f;
+ $last_dis_linenum = $l;
+ $running_disasm .= $dis;
+ $running_disasm .= "\n";
+ }
+
+ $running1 += $c1;
+ $running2 += $c2;
+ $total1 += $c1;
+ $total2 += $c2;
+ my $file = $e->[1];
+ my $line = $e->[2];
+ if (($file eq $filename) &&
+ ($line >= $firstline) &&
+ ($line <= $lastline)) {
+ # Assign all accumulated samples to this line
+ AddEntry($samples1, $line, $running1);
+ AddEntry($samples2, $line, $running2);
+ $running1 = 0;
+ $running2 = 0;
+ if ($html) {
+ if ($line != $last_touched_line && $disasm{$line} ne '') {
+ $disasm{$line} .= "\n";
+ }
+ $disasm{$line} .= $running_disasm;
+ $running_disasm = '';
+ $last_touched_line = $line;
+ }
+ }
+ }
+
+ # Assign any leftover samples to $lastline
+ AddEntry($samples1, $lastline, $running1);
+ AddEntry($samples2, $lastline, $running2);
+ if ($html) {
+ if ($lastline != $last_touched_line && $disasm{$lastline} ne '') {
+ $disasm{$lastline} .= "\n";
+ }
+ $disasm{$lastline} .= $running_disasm;
+ }
+
+ if ($html) {
+ printf $output (
+ "<h1>%s</h1>%s\n<pre onClick=\"jeprof_toggle_asm()\">\n" .
+ "Total:%6s %6s (flat / cumulative %s)\n",
+ HtmlEscape(ShortFunctionName($routine)),
+ HtmlEscape(CleanFileName($filename)),
+ Unparse($total1),
+ Unparse($total2),
+ Units());
+ } else {
+ printf $output (
+ "ROUTINE ====================== %s in %s\n" .
+ "%6s %6s Total %s (flat / cumulative)\n",
+ ShortFunctionName($routine),
+ CleanFileName($filename),
+ Unparse($total1),
+ Unparse($total2),
+ Units());
+ }
+ if (!open(FILE, "<$filename")) {
+ print STDERR "$filename: $!\n";
+ return 0;
+ }
+ my $l = 0;
+ while (<FILE>) {
+ s/\r//g; # turn windows-looking lines into unix-looking lines
+ $l++;
+ if ($l >= $firstline - 5 &&
+ (($l <= $oldlastline + 5) || ($l <= $lastline))) {
+ chop;
+ my $text = $_;
+ if ($l == $firstline) { print $output $skip_marker; }
+ my $n1 = GetEntry($samples1, $l);
+ my $n2 = GetEntry($samples2, $l);
+ if ($html) {
+ # Emit a span that has one of the following classes:
+ # livesrc -- has samples
+ # deadsrc -- has disassembly, but with no samples
+ # nop -- has no matching disasembly
+ # Also emit an optional span containing disassembly.
+ my $dis = $disasm{$l};
+ my $asm = "";
+ if (defined($dis) && $dis ne '') {
+ $asm = "<span class=\"asm\">" . $dis . "</span>";
+ }
+ my $source_class = (($n1 + $n2 > 0)
+ ? "livesrc"
+ : (($asm ne "") ? "deadsrc" : "nop"));
+ printf $output (
+ "<span class=\"line\">%5d</span> " .
+ "<span class=\"%s\">%6s %6s %s</span>%s\n",
+ $l, $source_class,
+ HtmlPrintNumber($n1),
+ HtmlPrintNumber($n2),
+ HtmlEscape($text),
+ $asm);
+ } else {
+ printf $output(
+ "%6s %6s %4d: %s\n",
+ UnparseAlt($n1),
+ UnparseAlt($n2),
+ $l,
+ $text);
+ }
+ if ($l == $lastline) { print $output $skip_marker; }
+ };
+ }
+ close(FILE);
+ if ($html) {
+ print $output "</pre>\n";
+ }
+ return 1;
+}
+
+# Return the source line for the specified file/linenumber.
+# Returns undef if not found.
+sub SourceLine {
+ my $file = shift;
+ my $line = shift;
+
+ # Look in cache
+ if (!defined($main::source_cache{$file})) {
+ if (100 < scalar keys(%main::source_cache)) {
+ # Clear the cache when it gets too big
+ $main::source_cache = ();
+ }
+
+ # Read all lines from the file
+ if (!open(FILE, "<$file")) {
+ print STDERR "$file: $!\n";
+ $main::source_cache{$file} = []; # Cache the negative result
+ return undef;
+ }
+ my $lines = [];
+ push(@{$lines}, ""); # So we can use 1-based line numbers as indices
+ while (<FILE>) {
+ push(@{$lines}, $_);
+ }
+ close(FILE);
+
+ # Save the lines in the cache
+ $main::source_cache{$file} = $lines;
+ }
+
+ my $lines = $main::source_cache{$file};
+ if (($line < 0) || ($line > $#{$lines})) {
+ return undef;
+ } else {
+ return $lines->[$line];
+ }
+}
+
+# Print disassembly for one routine with interspersed source if available
+sub PrintDisassembledFunction {
+ my $prog = shift;
+ my $offset = shift;
+ my $routine = shift;
+ my $flat = shift;
+ my $cumulative = shift;
+ my $start_addr = shift;
+ my $end_addr = shift;
+ my $total = shift;
+
+ # Disassemble all instructions
+ my @instructions = Disassemble($prog, $offset, $start_addr, $end_addr);
+
+ # Make array of counts per instruction
+ my @flat_count = ();
+ my @cum_count = ();
+ my $flat_total = 0;
+ my $cum_total = 0;
+ foreach my $e (@instructions) {
+ # Add up counts for all address that fall inside this instruction
+ my $c1 = 0;
+ my $c2 = 0;
+ for (my $a = $e->[0]; $a lt $e->[4]; $a = AddressInc($a)) {
+ $c1 += GetEntry($flat, $a);
+ $c2 += GetEntry($cumulative, $a);
+ }
+ push(@flat_count, $c1);
+ push(@cum_count, $c2);
+ $flat_total += $c1;
+ $cum_total += $c2;
+ }
+
+ # Print header with total counts
+ printf("ROUTINE ====================== %s\n" .
+ "%6s %6s %s (flat, cumulative) %.1f%% of total\n",
+ ShortFunctionName($routine),
+ Unparse($flat_total),
+ Unparse($cum_total),
+ Units(),
+ ($cum_total * 100.0) / $total);
+
+ # Process instructions in order
+ my $current_file = "";
+ for (my $i = 0; $i <= $#instructions; ) {
+ my $e = $instructions[$i];
+
+ # Print the new file name whenever we switch files
+ if ($e->[1] ne $current_file) {
+ $current_file = $e->[1];
+ my $fname = $current_file;
+ $fname =~ s|^\./||; # Trim leading "./"
+
+ # Shorten long file names
+ if (length($fname) >= 58) {
+ $fname = "..." . substr($fname, -55);
+ }
+ printf("-------------------- %s\n", $fname);
+ }
+
+ # TODO: Compute range of lines to print together to deal with
+ # small reorderings.
+ my $first_line = $e->[2];
+ my $last_line = $first_line;
+ my %flat_sum = ();
+ my %cum_sum = ();
+ for (my $l = $first_line; $l <= $last_line; $l++) {
+ $flat_sum{$l} = 0;
+ $cum_sum{$l} = 0;
+ }
+
+ # Find run of instructions for this range of source lines
+ my $first_inst = $i;
+ while (($i <= $#instructions) &&
+ ($instructions[$i]->[2] >= $first_line) &&
+ ($instructions[$i]->[2] <= $last_line)) {
+ $e = $instructions[$i];
+ $flat_sum{$e->[2]} += $flat_count[$i];
+ $cum_sum{$e->[2]} += $cum_count[$i];
+ $i++;
+ }
+ my $last_inst = $i - 1;
+
+ # Print source lines
+ for (my $l = $first_line; $l <= $last_line; $l++) {
+ my $line = SourceLine($current_file, $l);
+ if (!defined($line)) {
+ $line = "?\n";
+ next;
+ } else {
+ $line =~ s/^\s+//;
+ }
+ printf("%6s %6s %5d: %s",
+ UnparseAlt($flat_sum{$l}),
+ UnparseAlt($cum_sum{$l}),
+ $l,
+ $line);
+ }
+
+ # Print disassembly
+ for (my $x = $first_inst; $x <= $last_inst; $x++) {
+ my $e = $instructions[$x];
+ printf("%6s %6s %8s: %6s\n",
+ UnparseAlt($flat_count[$x]),
+ UnparseAlt($cum_count[$x]),
+ UnparseAddress($offset, $e->[0]),
+ CleanDisassembly($e->[3]));
+ }
+ }
+}
+
+# Print DOT graph
+sub PrintDot {
+ my $prog = shift;
+ my $symbols = shift;
+ my $raw = shift;
+ my $flat = shift;
+ my $cumulative = shift;
+ my $overall_total = shift;
+
+ # Get total
+ my $local_total = TotalProfile($flat);
+ my $nodelimit = int($main::opt_nodefraction * $local_total);
+ my $edgelimit = int($main::opt_edgefraction * $local_total);
+ my $nodecount = $main::opt_nodecount;
+
+ # Find nodes to include
+ my @list = (sort { abs(GetEntry($cumulative, $b)) <=>
+ abs(GetEntry($cumulative, $a))
+ || $a cmp $b }
+ keys(%{$cumulative}));
+ my $last = $nodecount - 1;
+ if ($last > $#list) {
+ $last = $#list;
+ }
+ while (($last >= 0) &&
+ (abs(GetEntry($cumulative, $list[$last])) <= $nodelimit)) {
+ $last--;
+ }
+ if ($last < 0) {
+ print STDERR "No nodes to print\n";
+ return 0;
+ }
+
+ if ($nodelimit > 0 || $edgelimit > 0) {
+ printf STDERR ("Dropping nodes with <= %s %s; edges with <= %s abs(%s)\n",
+ Unparse($nodelimit), Units(),
+ Unparse($edgelimit), Units());
+ }
+
+ # Open DOT output file
+ my $output;
+ my $escaped_dot = ShellEscape(@DOT);
+ my $escaped_ps2pdf = ShellEscape(@PS2PDF);
+ if ($main::opt_gv) {
+ my $escaped_outfile = ShellEscape(TempName($main::next_tmpfile, "ps"));
+ $output = "| $escaped_dot -Tps2 >$escaped_outfile";
+ } elsif ($main::opt_evince) {
+ my $escaped_outfile = ShellEscape(TempName($main::next_tmpfile, "pdf"));
+ $output = "| $escaped_dot -Tps2 | $escaped_ps2pdf - $escaped_outfile";
+ } elsif ($main::opt_ps) {
+ $output = "| $escaped_dot -Tps2";
+ } elsif ($main::opt_pdf) {
+ $output = "| $escaped_dot -Tps2 | $escaped_ps2pdf - -";
+ } elsif ($main::opt_web || $main::opt_svg) {
+ # We need to post-process the SVG, so write to a temporary file always.
+ my $escaped_outfile = ShellEscape(TempName($main::next_tmpfile, "svg"));
+ $output = "| $escaped_dot -Tsvg >$escaped_outfile";
+ } elsif ($main::opt_gif) {
+ $output = "| $escaped_dot -Tgif";
+ } else {
+ $output = ">&STDOUT";
+ }
+ open(DOT, $output) || error("$output: $!\n");
+
+ # Title
+ printf DOT ("digraph \"%s; %s %s\" {\n",
+ $prog,
+ Unparse($overall_total),
+ Units());
+ if ($main::opt_pdf) {
+ # The output is more printable if we set the page size for dot.
+ printf DOT ("size=\"8,11\"\n");
+ }
+ printf DOT ("node [width=0.375,height=0.25];\n");
+
+ # Print legend
+ printf DOT ("Legend [shape=box,fontsize=24,shape=plaintext," .
+ "label=\"%s\\l%s\\l%s\\l%s\\l%s\\l\"];\n",
+ $prog,
+ sprintf("Total %s: %s", Units(), Unparse($overall_total)),
+ sprintf("Focusing on: %s", Unparse($local_total)),
+ sprintf("Dropped nodes with <= %s abs(%s)",
+ Unparse($nodelimit), Units()),
+ sprintf("Dropped edges with <= %s %s",
+ Unparse($edgelimit), Units())
+ );
+
+ # Print nodes
+ my %node = ();
+ my $nextnode = 1;
+ foreach my $a (@list[0..$last]) {
+ # Pick font size
+ my $f = GetEntry($flat, $a);
+ my $c = GetEntry($cumulative, $a);
+
+ my $fs = 8;
+ if ($local_total > 0) {
+ $fs = 8 + (50.0 * sqrt(abs($f * 1.0 / $local_total)));
+ }
+
+ $node{$a} = $nextnode++;
+ my $sym = $a;
+ $sym =~ s/\s+/\\n/g;
+ $sym =~ s/::/\\n/g;
+
+ # Extra cumulative info to print for non-leaves
+ my $extra = "";
+ if ($f != $c) {
+ $extra = sprintf("\\rof %s (%s)",
+ Unparse($c),
+ Percent($c, $local_total));
+ }
+ my $style = "";
+ if ($main::opt_heapcheck) {
+ if ($f > 0) {
+ # make leak-causing nodes more visible (add a background)
+ $style = ",style=filled,fillcolor=gray"
+ } elsif ($f < 0) {
+ # make anti-leak-causing nodes (which almost never occur)
+ # stand out as well (triple border)
+ $style = ",peripheries=3"
+ }
+ }
+
+ printf DOT ("N%d [label=\"%s\\n%s (%s)%s\\r" .
+ "\",shape=box,fontsize=%.1f%s];\n",
+ $node{$a},
+ $sym,
+ Unparse($f),
+ Percent($f, $local_total),
+ $extra,
+ $fs,
+ $style,
+ );
+ }
+
+ # Get edges and counts per edge
+ my %edge = ();
+ my $n;
+ my $fullname_to_shortname_map = {};
+ FillFullnameToShortnameMap($symbols, $fullname_to_shortname_map);
+ foreach my $k (keys(%{$raw})) {
+ # TODO: omit low %age edges
+ $n = $raw->{$k};
+ my @translated = TranslateStack($symbols, $fullname_to_shortname_map, $k);
+ for (my $i = 1; $i <= $#translated; $i++) {
+ my $src = $translated[$i];
+ my $dst = $translated[$i-1];
+ #next if ($src eq $dst); # Avoid self-edges?
+ if (exists($node{$src}) && exists($node{$dst})) {
+ my $edge_label = "$src\001$dst";
+ if (!exists($edge{$edge_label})) {
+ $edge{$edge_label} = 0;
+ }
+ $edge{$edge_label} += $n;
+ }
+ }
+ }
+
+ # Print edges (process in order of decreasing counts)
+ my %indegree = (); # Number of incoming edges added per node so far
+ my %outdegree = (); # Number of outgoing edges added per node so far
+ foreach my $e (sort { $edge{$b} <=> $edge{$a} } keys(%edge)) {
+ my @x = split(/\001/, $e);
+ $n = $edge{$e};
+
+ # Initialize degree of kept incoming and outgoing edges if necessary
+ my $src = $x[0];
+ my $dst = $x[1];
+ if (!exists($outdegree{$src})) { $outdegree{$src} = 0; }
+ if (!exists($indegree{$dst})) { $indegree{$dst} = 0; }
+
+ my $keep;
+ if ($indegree{$dst} == 0) {
+ # Keep edge if needed for reachability
+ $keep = 1;
+ } elsif (abs($n) <= $edgelimit) {
+ # Drop if we are below --edgefraction
+ $keep = 0;
+ } elsif ($outdegree{$src} >= $main::opt_maxdegree ||
+ $indegree{$dst} >= $main::opt_maxdegree) {
+ # Keep limited number of in/out edges per node
+ $keep = 0;
+ } else {
+ $keep = 1;
+ }
+
+ if ($keep) {
+ $outdegree{$src}++;
+ $indegree{$dst}++;
+
+ # Compute line width based on edge count
+ my $fraction = abs($local_total ? (3 * ($n / $local_total)) : 0);
+ if ($fraction > 1) { $fraction = 1; }
+ my $w = $fraction * 2;
+ if ($w < 1 && ($main::opt_web || $main::opt_svg)) {
+ # SVG output treats line widths < 1 poorly.
+ $w = 1;
+ }
+
+ # Dot sometimes segfaults if given edge weights that are too large, so
+ # we cap the weights at a large value
+ my $edgeweight = abs($n) ** 0.7;
+ if ($edgeweight > 100000) { $edgeweight = 100000; }
+ $edgeweight = int($edgeweight);
+
+ my $style = sprintf("setlinewidth(%f)", $w);
+ if ($x[1] =~ m/\(inline\)/) {
+ $style .= ",dashed";
+ }
+
+ # Use a slightly squashed function of the edge count as the weight
+ printf DOT ("N%s -> N%s [label=%s, weight=%d, style=\"%s\"];\n",
+ $node{$x[0]},
+ $node{$x[1]},
+ Unparse($n),
+ $edgeweight,
+ $style);
+ }
+ }
+
+ print DOT ("}\n");
+ close(DOT);
+
+ if ($main::opt_web || $main::opt_svg) {
+ # Rewrite SVG to be more usable inside web browser.
+ RewriteSvg(TempName($main::next_tmpfile, "svg"));
+ }
+
+ return 1;
+}
+
+sub RewriteSvg {
+ my $svgfile = shift;
+
+ open(SVG, $svgfile) || die "open temp svg: $!";
+ my @svg = <SVG>;
+ close(SVG);
+ unlink $svgfile;
+ my $svg = join('', @svg);
+
+ # Dot's SVG output is
+ #
+ # <svg width="___" height="___"
+ # viewBox="___" xmlns=...>
+ # <g id="graph0" transform="...">
+ # ...
+ # </g>
+ # </svg>
+ #
+ # Change it to
+ #
+ # <svg width="100%" height="100%"
+ # xmlns=...>
+ # $svg_javascript
+ # <g id="viewport" transform="translate(0,0)">
+ # <g id="graph0" transform="...">
+ # ...
+ # </g>
+ # </g>
+ # </svg>
+
+ # Fix width, height; drop viewBox.
+ $svg =~ s/(?s)<svg width="[^"]+" height="[^"]+"(.*?)viewBox="[^"]+"/<svg width="100%" height="100%"$1/;
+
+ # Insert script, viewport <g> above first <g>
+ my $svg_javascript = SvgJavascript();
+ my $viewport = "<g id=\"viewport\" transform=\"translate(0,0)\">\n";
+ $svg =~ s/<g id="graph\d"/$svg_javascript$viewport$&/;
+
+ # Insert final </g> above </svg>.
+ $svg =~ s/(.*)(<\/svg>)/$1<\/g>$2/;
+ $svg =~ s/<g id="graph\d"(.*?)/<g id="viewport"$1/;
+
+ if ($main::opt_svg) {
+ # --svg: write to standard output.
+ print $svg;
+ } else {
+ # Write back to temporary file.
+ open(SVG, ">$svgfile") || die "open $svgfile: $!";
+ print SVG $svg;
+ close(SVG);
+ }
+}
+
+sub SvgJavascript {
+ return <<'EOF';
+<script type="text/ecmascript"><![CDATA[
+// SVGPan
+// http://www.cyberz.org/blog/2009/12/08/svgpan-a-javascript-svg-panzoomdrag-library/
+// Local modification: if(true || ...) below to force panning, never moving.
+
+/**
+ * SVGPan library 1.2
+ * ====================
+ *
+ * Given an unique existing element with id "viewport", including the
+ * the library into any SVG adds the following capabilities:
+ *
+ * - Mouse panning
+ * - Mouse zooming (using the wheel)
+ * - Object dargging
+ *
+ * Known issues:
+ *
+ * - Zooming (while panning) on Safari has still some issues
+ *
+ * Releases:
+ *
+ * 1.2, Sat Mar 20 08:42:50 GMT 2010, Zeng Xiaohui
+ * Fixed a bug with browser mouse handler interaction
+ *
+ * 1.1, Wed Feb 3 17:39:33 GMT 2010, Zeng Xiaohui
+ * Updated the zoom code to support the mouse wheel on Safari/Chrome
+ *
+ * 1.0, Andrea Leofreddi
+ * First release
+ *
+ * This code is licensed under the following BSD license:
+ *
+ * Copyright 2009-2010 Andrea Leofreddi <a.leofreddi@itcharm.com>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Andrea Leofreddi ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Andrea Leofreddi OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of Andrea Leofreddi.
+ */
+
+var root = document.documentElement;
+
+var state = 'none', stateTarget, stateOrigin, stateTf;
+
+setupHandlers(root);
+
+/**
+ * Register handlers
+ */
+function setupHandlers(root){
+ setAttributes(root, {
+ "onmouseup" : "add(evt)",
+ "onmousedown" : "handleMouseDown(evt)",
+ "onmousemove" : "handleMouseMove(evt)",
+ "onmouseup" : "handleMouseUp(evt)",
+ //"onmouseout" : "handleMouseUp(evt)", // Decomment this to stop the pan functionality when dragging out of the SVG element
+ });
+
+ if(navigator.userAgent.toLowerCase().indexOf('webkit') >= 0)
+ window.addEventListener('mousewheel', handleMouseWheel, false); // Chrome/Safari
+ else
+ window.addEventListener('DOMMouseScroll', handleMouseWheel, false); // Others
+
+ var g = svgDoc.getElementById("svg");
+ g.width = "100%";
+ g.height = "100%";
+}
+
+/**
+ * Instance an SVGPoint object with given event coordinates.
+ */
+function getEventPoint(evt) {
+ var p = root.createSVGPoint();
+
+ p.x = evt.clientX;
+ p.y = evt.clientY;
+
+ return p;
+}
+
+/**
+ * Sets the current transform matrix of an element.
+ */
+function setCTM(element, matrix) {
+ var s = "matrix(" + matrix.a + "," + matrix.b + "," + matrix.c + "," + matrix.d + "," + matrix.e + "," + matrix.f + ")";
+
+ element.setAttribute("transform", s);
+}
+
+/**
+ * Dumps a matrix to a string (useful for debug).
+ */
+function dumpMatrix(matrix) {
+ var s = "[ " + matrix.a + ", " + matrix.c + ", " + matrix.e + "\n " + matrix.b + ", " + matrix.d + ", " + matrix.f + "\n 0, 0, 1 ]";
+
+ return s;
+}
+
+/**
+ * Sets attributes of an element.
+ */
+function setAttributes(element, attributes){
+ for (i in attributes)
+ element.setAttributeNS(null, i, attributes[i]);
+}
+
+/**
+ * Handle mouse move event.
+ */
+function handleMouseWheel(evt) {
+ if(evt.preventDefault)
+ evt.preventDefault();
+
+ evt.returnValue = false;
+
+ var svgDoc = evt.target.ownerDocument;
+
+ var delta;
+
+ if(evt.wheelDelta)
+ delta = evt.wheelDelta / 3600; // Chrome/Safari
+ else
+ delta = evt.detail / -90; // Mozilla
+
+ var z = 1 + delta; // Zoom factor: 0.9/1.1
+
+ var g = svgDoc.getElementById("viewport");
+
+ var p = getEventPoint(evt);
+
+ p = p.matrixTransform(g.getCTM().inverse());
+
+ // Compute new scale matrix in current mouse position
+ var k = root.createSVGMatrix().translate(p.x, p.y).scale(z).translate(-p.x, -p.y);
+
+ setCTM(g, g.getCTM().multiply(k));
+
+ stateTf = stateTf.multiply(k.inverse());
+}
+
+/**
+ * Handle mouse move event.
+ */
+function handleMouseMove(evt) {
+ if(evt.preventDefault)
+ evt.preventDefault();
+
+ evt.returnValue = false;
+
+ var svgDoc = evt.target.ownerDocument;
+
+ var g = svgDoc.getElementById("viewport");
+
+ if(state == 'pan') {
+ // Pan mode
+ var p = getEventPoint(evt).matrixTransform(stateTf);
+
+ setCTM(g, stateTf.inverse().translate(p.x - stateOrigin.x, p.y - stateOrigin.y));
+ } else if(state == 'move') {
+ // Move mode
+ var p = getEventPoint(evt).matrixTransform(g.getCTM().inverse());
+
+ setCTM(stateTarget, root.createSVGMatrix().translate(p.x - stateOrigin.x, p.y - stateOrigin.y).multiply(g.getCTM().inverse()).multiply(stateTarget.getCTM()));
+
+ stateOrigin = p;
+ }
+}
+
+/**
+ * Handle click event.
+ */
+function handleMouseDown(evt) {
+ if(evt.preventDefault)
+ evt.preventDefault();
+
+ evt.returnValue = false;
+
+ var svgDoc = evt.target.ownerDocument;
+
+ var g = svgDoc.getElementById("viewport");
+
+ if(true || evt.target.tagName == "svg") {
+ // Pan mode
+ state = 'pan';
+
+ stateTf = g.getCTM().inverse();
+
+ stateOrigin = getEventPoint(evt).matrixTransform(stateTf);
+ } else {
+ // Move mode
+ state = 'move';
+
+ stateTarget = evt.target;
+
+ stateTf = g.getCTM().inverse();
+
+ stateOrigin = getEventPoint(evt).matrixTransform(stateTf);
+ }
+}
+
+/**
+ * Handle mouse button release event.
+ */
+function handleMouseUp(evt) {
+ if(evt.preventDefault)
+ evt.preventDefault();
+
+ evt.returnValue = false;
+
+ var svgDoc = evt.target.ownerDocument;
+
+ if(state == 'pan' || state == 'move') {
+ // Quit pan mode
+ state = '';
+ }
+}
+
+]]></script>
+EOF
+}
+
+# Provides a map from fullname to shortname for cases where the
+# shortname is ambiguous. The symlist has both the fullname and
+# shortname for all symbols, which is usually fine, but sometimes --
+# such as overloaded functions -- two different fullnames can map to
+# the same shortname. In that case, we use the address of the
+# function to disambiguate the two. This function fills in a map that
+# maps fullnames to modified shortnames in such cases. If a fullname
+# is not present in the map, the 'normal' shortname provided by the
+# symlist is the appropriate one to use.
+sub FillFullnameToShortnameMap {
+ my $symbols = shift;
+ my $fullname_to_shortname_map = shift;
+ my $shortnames_seen_once = {};
+ my $shortnames_seen_more_than_once = {};
+
+ foreach my $symlist (values(%{$symbols})) {
+ # TODO(csilvers): deal with inlined symbols too.
+ my $shortname = $symlist->[0];
+ my $fullname = $symlist->[2];
+ if ($fullname !~ /<[0-9a-fA-F]+>$/) { # fullname doesn't end in an address
+ next; # the only collisions we care about are when addresses differ
+ }
+ if (defined($shortnames_seen_once->{$shortname}) &&
+ $shortnames_seen_once->{$shortname} ne $fullname) {
+ $shortnames_seen_more_than_once->{$shortname} = 1;
+ } else {
+ $shortnames_seen_once->{$shortname} = $fullname;
+ }
+ }
+
+ foreach my $symlist (values(%{$symbols})) {
+ my $shortname = $symlist->[0];
+ my $fullname = $symlist->[2];
+ # TODO(csilvers): take in a list of addresses we care about, and only
+ # store in the map if $symlist->[1] is in that list. Saves space.
+ next if defined($fullname_to_shortname_map->{$fullname});
+ if (defined($shortnames_seen_more_than_once->{$shortname})) {
+ if ($fullname =~ /<0*([^>]*)>$/) { # fullname has address at end of it
+ $fullname_to_shortname_map->{$fullname} = "$shortname\@$1";
+ }
+ }
+ }
+}
+
+# Return a small number that identifies the argument.
+# Multiple calls with the same argument will return the same number.
+# Calls with different arguments will return different numbers.
+sub ShortIdFor {
+ my $key = shift;
+ my $id = $main::uniqueid{$key};
+ if (!defined($id)) {
+ $id = keys(%main::uniqueid) + 1;
+ $main::uniqueid{$key} = $id;
+ }
+ return $id;
+}
+
+# Translate a stack of addresses into a stack of symbols
+sub TranslateStack {
+ my $symbols = shift;
+ my $fullname_to_shortname_map = shift;
+ my $k = shift;
+
+ my @addrs = split(/\n/, $k);
+ my @result = ();
+ for (my $i = 0; $i <= $#addrs; $i++) {
+ my $a = $addrs[$i];
+
+ # Skip large addresses since they sometimes show up as fake entries on RH9
+ if (length($a) > 8 && $a gt "7fffffffffffffff") {
+ next;
+ }
+
+ if ($main::opt_disasm || $main::opt_list) {
+ # We want just the address for the key
+ push(@result, $a);
+ next;
+ }
+
+ my $symlist = $symbols->{$a};
+ if (!defined($symlist)) {
+ $symlist = [$a, "", $a];
+ }
+
+ # We can have a sequence of symbols for a particular entry
+ # (more than one symbol in the case of inlining). Callers
+ # come before callees in symlist, so walk backwards since
+ # the translated stack should contain callees before callers.
+ for (my $j = $#{$symlist}; $j >= 2; $j -= 3) {
+ my $func = $symlist->[$j-2];
+ my $fileline = $symlist->[$j-1];
+ my $fullfunc = $symlist->[$j];
+ if (defined($fullname_to_shortname_map->{$fullfunc})) {
+ $func = $fullname_to_shortname_map->{$fullfunc};
+ }
+ if ($j > 2) {
+ $func = "$func (inline)";
+ }
+
+ # Do not merge nodes corresponding to Callback::Run since that
+ # causes confusing cycles in dot display. Instead, we synthesize
+ # a unique name for this frame per caller.
+ if ($func =~ m/Callback.*::Run$/) {
+ my $caller = ($i > 0) ? $addrs[$i-1] : 0;
+ $func = "Run#" . ShortIdFor($caller);
+ }
+
+ if ($main::opt_addresses) {
+ push(@result, "$a $func $fileline");
+ } elsif ($main::opt_lines) {
+ if ($func eq '??' && $fileline eq '??:0') {
+ push(@result, "$a");
+ } else {
+ push(@result, "$func $fileline");
+ }
+ } elsif ($main::opt_functions) {
+ if ($func eq '??') {
+ push(@result, "$a");
+ } else {
+ push(@result, $func);
+ }
+ } elsif ($main::opt_files) {
+ if ($fileline eq '??:0' || $fileline eq '') {
+ push(@result, "$a");
+ } else {
+ my $f = $fileline;
+ $f =~ s/:\d+$//;
+ push(@result, $f);
+ }
+ } else {
+ push(@result, $a);
+ last; # Do not print inlined info
+ }
+ }
+ }
+
+ # print join(",", @addrs), " => ", join(",", @result), "\n";
+ return @result;
+}
+
+# Generate percent string for a number and a total
+sub Percent {
+ my $num = shift;
+ my $tot = shift;
+ if ($tot != 0) {
+ return sprintf("%.1f%%", $num * 100.0 / $tot);
+ } else {
+ return ($num == 0) ? "nan" : (($num > 0) ? "+inf" : "-inf");
+ }
+}
+
+# Generate pretty-printed form of number
+sub Unparse {
+ my $num = shift;
+ if ($main::profile_type eq 'heap' || $main::profile_type eq 'growth') {
+ if ($main::opt_inuse_objects || $main::opt_alloc_objects) {
+ return sprintf("%d", $num);
+ } else {
+ if ($main::opt_show_bytes) {
+ return sprintf("%d", $num);
+ } else {
+ return sprintf("%.1f", $num / 1048576.0);
+ }
+ }
+ } elsif ($main::profile_type eq 'contention' && !$main::opt_contentions) {
+ return sprintf("%.3f", $num / 1e9); # Convert nanoseconds to seconds
+ } else {
+ return sprintf("%d", $num);
+ }
+}
+
+# Alternate pretty-printed form: 0 maps to "."
+sub UnparseAlt {
+ my $num = shift;
+ if ($num == 0) {
+ return ".";
+ } else {
+ return Unparse($num);
+ }
+}
+
+# Alternate pretty-printed form: 0 maps to ""
+sub HtmlPrintNumber {
+ my $num = shift;
+ if ($num == 0) {
+ return "";
+ } else {
+ return Unparse($num);
+ }
+}
+
+# Return output units
+sub Units {
+ if ($main::profile_type eq 'heap' || $main::profile_type eq 'growth') {
+ if ($main::opt_inuse_objects || $main::opt_alloc_objects) {
+ return "objects";
+ } else {
+ if ($main::opt_show_bytes) {
+ return "B";
+ } else {
+ return "MB";
+ }
+ }
+ } elsif ($main::profile_type eq 'contention' && !$main::opt_contentions) {
+ return "seconds";
+ } else {
+ return "samples";
+ }
+}
+
+##### Profile manipulation code #####
+
+# Generate flattened profile:
+# If count is charged to stack [a,b,c,d], in generated profile,
+# it will be charged to [a]
+sub FlatProfile {
+ my $profile = shift;
+ my $result = {};
+ foreach my $k (keys(%{$profile})) {
+ my $count = $profile->{$k};
+ my @addrs = split(/\n/, $k);
+ if ($#addrs >= 0) {
+ AddEntry($result, $addrs[0], $count);
+ }
+ }
+ return $result;
+}
+
+# Generate cumulative profile:
+# If count is charged to stack [a,b,c,d], in generated profile,
+# it will be charged to [a], [b], [c], [d]
+sub CumulativeProfile {
+ my $profile = shift;
+ my $result = {};
+ foreach my $k (keys(%{$profile})) {
+ my $count = $profile->{$k};
+ my @addrs = split(/\n/, $k);
+ foreach my $a (@addrs) {
+ AddEntry($result, $a, $count);
+ }
+ }
+ return $result;
+}
+
+# If the second-youngest PC on the stack is always the same, returns
+# that pc. Otherwise, returns undef.
+sub IsSecondPcAlwaysTheSame {
+ my $profile = shift;
+
+ my $second_pc = undef;
+ foreach my $k (keys(%{$profile})) {
+ my @addrs = split(/\n/, $k);
+ if ($#addrs < 1) {
+ return undef;
+ }
+ if (not defined $second_pc) {
+ $second_pc = $addrs[1];
+ } else {
+ if ($second_pc ne $addrs[1]) {
+ return undef;
+ }
+ }
+ }
+ return $second_pc;
+}
+
+sub ExtractSymbolNameInlineStack {
+ my $symbols = shift;
+ my $address = shift;
+
+ my @stack = ();
+
+ if (exists $symbols->{$address}) {
+ my @localinlinestack = @{$symbols->{$address}};
+ for (my $i = $#localinlinestack; $i > 0; $i-=3) {
+ my $file = $localinlinestack[$i-1];
+ my $fn = $localinlinestack[$i-0];
+
+ if ($file eq "?" || $file eq ":0") {
+ $file = "??:0";
+ }
+ if ($fn eq '??') {
+ # If we can't get the symbol name, at least use the file information.
+ $fn = $file;
+ }
+ my $suffix = "[inline]";
+ if ($i == 2) {
+ $suffix = "";
+ }
+ push (@stack, $fn.$suffix);
+ }
+ }
+ else {
+ # If we can't get a symbol name, at least fill in the address.
+ push (@stack, $address);
+ }
+
+ return @stack;
+}
+
+sub ExtractSymbolLocation {
+ my $symbols = shift;
+ my $address = shift;
+ # 'addr2line' outputs "??:0" for unknown locations; we do the
+ # same to be consistent.
+ my $location = "??:0:unknown";
+ if (exists $symbols->{$address}) {
+ my $file = $symbols->{$address}->[1];
+ if ($file eq "?") {
+ $file = "??:0"
+ }
+ $location = $file . ":" . $symbols->{$address}->[0];
+ }
+ return $location;
+}
+
+# Extracts a graph of calls.
+sub ExtractCalls {
+ my $symbols = shift;
+ my $profile = shift;
+
+ my $calls = {};
+ while( my ($stack_trace, $count) = each %$profile ) {
+ my @address = split(/\n/, $stack_trace);
+ my $destination = ExtractSymbolLocation($symbols, $address[0]);
+ AddEntry($calls, $destination, $count);
+ for (my $i = 1; $i <= $#address; $i++) {
+ my $source = ExtractSymbolLocation($symbols, $address[$i]);
+ my $call = "$source -> $destination";
+ AddEntry($calls, $call, $count);
+ $destination = $source;
+ }
+ }
+
+ return $calls;
+}
+
+sub FilterFrames {
+ my $symbols = shift;
+ my $profile = shift;
+
+ if ($main::opt_retain eq '' && $main::opt_exclude eq '') {
+ return $profile;
+ }
+
+ my $result = {};
+ foreach my $k (keys(%{$profile})) {
+ my $count = $profile->{$k};
+ my @addrs = split(/\n/, $k);
+ my @path = ();
+ foreach my $a (@addrs) {
+ my $sym;
+ if (exists($symbols->{$a})) {
+ $sym = $symbols->{$a}->[0];
+ } else {
+ $sym = $a;
+ }
+ if ($main::opt_retain ne '' && $sym !~ m/$main::opt_retain/) {
+ next;
+ }
+ if ($main::opt_exclude ne '' && $sym =~ m/$main::opt_exclude/) {
+ next;
+ }
+ push(@path, $a);
+ }
+ if (scalar(@path) > 0) {
+ my $reduced_path = join("\n", @path);
+ AddEntry($result, $reduced_path, $count);
+ }
+ }
+
+ return $result;
+}
+
+sub PrintCollapsedStacks {
+ my $symbols = shift;
+ my $profile = shift;
+
+ while (my ($stack_trace, $count) = each %$profile) {
+ my @address = split(/\n/, $stack_trace);
+ my @names = reverse ( map { ExtractSymbolNameInlineStack($symbols, $_) } @address );
+ printf("%s %d\n", join(";", @names), $count);
+ }
+}
+
+sub RemoveUninterestingFrames {
+ my $symbols = shift;
+ my $profile = shift;
+
+ # List of function names to skip
+ my %skip = ();
+ my $skip_regexp = 'NOMATCH';
+ if ($main::profile_type eq 'heap' || $main::profile_type eq 'growth') {
+ foreach my $name ('@JEMALLOC_PREFIX@calloc',
+ 'cfree',
+ '@JEMALLOC_PREFIX@malloc',
+ 'newImpl',
+ 'void* newImpl',
+ '@JEMALLOC_PREFIX@free',
+ '@JEMALLOC_PREFIX@memalign',
+ '@JEMALLOC_PREFIX@posix_memalign',
+ '@JEMALLOC_PREFIX@aligned_alloc',
+ 'pvalloc',
+ '@JEMALLOC_PREFIX@valloc',
+ '@JEMALLOC_PREFIX@realloc',
+ '@JEMALLOC_PREFIX@mallocx',
+ '@JEMALLOC_PREFIX@rallocx',
+ '@JEMALLOC_PREFIX@xallocx',
+ '@JEMALLOC_PREFIX@dallocx',
+ '@JEMALLOC_PREFIX@sdallocx',
+ '@JEMALLOC_PREFIX@sdallocx_noflags',
+ 'tc_calloc',
+ 'tc_cfree',
+ 'tc_malloc',
+ 'tc_free',
+ 'tc_memalign',
+ 'tc_posix_memalign',
+ 'tc_pvalloc',
+ 'tc_valloc',
+ 'tc_realloc',
+ 'tc_new',
+ 'tc_delete',
+ 'tc_newarray',
+ 'tc_deletearray',
+ 'tc_new_nothrow',
+ 'tc_newarray_nothrow',
+ 'do_malloc',
+ '::do_malloc', # new name -- got moved to an unnamed ns
+ '::do_malloc_or_cpp_alloc',
+ 'DoSampledAllocation',
+ 'simple_alloc::allocate',
+ '__malloc_alloc_template::allocate',
+ '__builtin_delete',
+ '__builtin_new',
+ '__builtin_vec_delete',
+ '__builtin_vec_new',
+ 'operator new',
+ 'operator new[]',
+ # The entry to our memory-allocation routines on OS X
+ 'malloc_zone_malloc',
+ 'malloc_zone_calloc',
+ 'malloc_zone_valloc',
+ 'malloc_zone_realloc',
+ 'malloc_zone_memalign',
+ 'malloc_zone_free',
+ # These mark the beginning/end of our custom sections
+ '__start_google_malloc',
+ '__stop_google_malloc',
+ '__start_malloc_hook',
+ '__stop_malloc_hook') {
+ $skip{$name} = 1;
+ $skip{"_" . $name} = 1; # Mach (OS X) adds a _ prefix to everything
+ }
+ # TODO: Remove TCMalloc once everything has been
+ # moved into the tcmalloc:: namespace and we have flushed
+ # old code out of the system.
+ $skip_regexp = "TCMalloc|^tcmalloc::";
+ } elsif ($main::profile_type eq 'contention') {
+ foreach my $vname ('base::RecordLockProfileData',
+ 'base::SubmitMutexProfileData',
+ 'base::SubmitSpinLockProfileData',
+ 'Mutex::Unlock',
+ 'Mutex::UnlockSlow',
+ 'Mutex::ReaderUnlock',
+ 'MutexLock::~MutexLock',
+ 'SpinLock::Unlock',
+ 'SpinLock::SlowUnlock',
+ 'SpinLockHolder::~SpinLockHolder') {
+ $skip{$vname} = 1;
+ }
+ } elsif ($main::profile_type eq 'cpu') {
+ # Drop signal handlers used for CPU profile collection
+ # TODO(dpeng): this should not be necessary; it's taken
+ # care of by the general 2nd-pc mechanism below.
+ foreach my $name ('ProfileData::Add', # historical
+ 'ProfileData::prof_handler', # historical
+ 'CpuProfiler::prof_handler',
+ '__FRAME_END__',
+ '__pthread_sighandler',
+ '__restore') {
+ $skip{$name} = 1;
+ }
+ } else {
+ # Nothing skipped for unknown types
+ }
+
+ if ($main::profile_type eq 'cpu') {
+ # If all the second-youngest program counters are the same,
+ # this STRONGLY suggests that it is an artifact of measurement,
+ # i.e., stack frames pushed by the CPU profiler signal handler.
+ # Hence, we delete them.
+ # (The topmost PC is read from the signal structure, not from
+ # the stack, so it does not get involved.)
+ while (my $second_pc = IsSecondPcAlwaysTheSame($profile)) {
+ my $result = {};
+ my $func = '';
+ if (exists($symbols->{$second_pc})) {
+ $second_pc = $symbols->{$second_pc}->[0];
+ }
+ print STDERR "Removing $second_pc from all stack traces.\n";
+ foreach my $k (keys(%{$profile})) {
+ my $count = $profile->{$k};
+ my @addrs = split(/\n/, $k);
+ splice @addrs, 1, 1;
+ my $reduced_path = join("\n", @addrs);
+ AddEntry($result, $reduced_path, $count);
+ }
+ $profile = $result;
+ }
+ }
+
+ my $result = {};
+ foreach my $k (keys(%{$profile})) {
+ my $count = $profile->{$k};
+ my @addrs = split(/\n/, $k);
+ my @path = ();
+ foreach my $a (@addrs) {
+ if (exists($symbols->{$a})) {
+ my $func = $symbols->{$a}->[0];
+ if ($skip{$func} || ($func =~ m/$skip_regexp/)) {
+ # Throw away the portion of the backtrace seen so far, under the
+ # assumption that previous frames were for functions internal to the
+ # allocator.
+ @path = ();
+ next;
+ }
+ }
+ push(@path, $a);
+ }
+ my $reduced_path = join("\n", @path);
+ AddEntry($result, $reduced_path, $count);
+ }
+
+ $result = FilterFrames($symbols, $result);
+
+ return $result;
+}
+
+# Reduce profile to granularity given by user
+sub ReduceProfile {
+ my $symbols = shift;
+ my $profile = shift;
+ my $result = {};
+ my $fullname_to_shortname_map = {};
+ FillFullnameToShortnameMap($symbols, $fullname_to_shortname_map);
+ foreach my $k (keys(%{$profile})) {
+ my $count = $profile->{$k};
+ my @translated = TranslateStack($symbols, $fullname_to_shortname_map, $k);
+ my @path = ();
+ my %seen = ();
+ $seen{''} = 1; # So that empty keys are skipped
+ foreach my $e (@translated) {
+ # To avoid double-counting due to recursion, skip a stack-trace
+ # entry if it has already been seen
+ if (!$seen{$e}) {
+ $seen{$e} = 1;
+ push(@path, $e);
+ }
+ }
+ my $reduced_path = join("\n", @path);
+ AddEntry($result, $reduced_path, $count);
+ }
+ return $result;
+}
+
+# Does the specified symbol array match the regexp?
+sub SymbolMatches {
+ my $sym = shift;
+ my $re = shift;
+ if (defined($sym)) {
+ for (my $i = 0; $i < $#{$sym}; $i += 3) {
+ if ($sym->[$i] =~ m/$re/ || $sym->[$i+1] =~ m/$re/) {
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+# Focus only on paths involving specified regexps
+sub FocusProfile {
+ my $symbols = shift;
+ my $profile = shift;
+ my $focus = shift;
+ my $result = {};
+ foreach my $k (keys(%{$profile})) {
+ my $count = $profile->{$k};
+ my @addrs = split(/\n/, $k);
+ foreach my $a (@addrs) {
+ # Reply if it matches either the address/shortname/fileline
+ if (($a =~ m/$focus/) || SymbolMatches($symbols->{$a}, $focus)) {
+ AddEntry($result, $k, $count);
+ last;
+ }
+ }
+ }
+ return $result;
+}
+
+# Focus only on paths not involving specified regexps
+sub IgnoreProfile {
+ my $symbols = shift;
+ my $profile = shift;
+ my $ignore = shift;
+ my $result = {};
+ foreach my $k (keys(%{$profile})) {
+ my $count = $profile->{$k};
+ my @addrs = split(/\n/, $k);
+ my $matched = 0;
+ foreach my $a (@addrs) {
+ # Reply if it matches either the address/shortname/fileline
+ if (($a =~ m/$ignore/) || SymbolMatches($symbols->{$a}, $ignore)) {
+ $matched = 1;
+ last;
+ }
+ }
+ if (!$matched) {
+ AddEntry($result, $k, $count);
+ }
+ }
+ return $result;
+}
+
+# Get total count in profile
+sub TotalProfile {
+ my $profile = shift;
+ my $result = 0;
+ foreach my $k (keys(%{$profile})) {
+ $result += $profile->{$k};
+ }
+ return $result;
+}
+
+# Add A to B
+sub AddProfile {
+ my $A = shift;
+ my $B = shift;
+
+ my $R = {};
+ # add all keys in A
+ foreach my $k (keys(%{$A})) {
+ my $v = $A->{$k};
+ AddEntry($R, $k, $v);
+ }
+ # add all keys in B
+ foreach my $k (keys(%{$B})) {
+ my $v = $B->{$k};
+ AddEntry($R, $k, $v);
+ }
+ return $R;
+}
+
+# Merges symbol maps
+sub MergeSymbols {
+ my $A = shift;
+ my $B = shift;
+
+ my $R = {};
+ foreach my $k (keys(%{$A})) {
+ $R->{$k} = $A->{$k};
+ }
+ if (defined($B)) {
+ foreach my $k (keys(%{$B})) {
+ $R->{$k} = $B->{$k};
+ }
+ }
+ return $R;
+}
+
+
+# Add A to B
+sub AddPcs {
+ my $A = shift;
+ my $B = shift;
+
+ my $R = {};
+ # add all keys in A
+ foreach my $k (keys(%{$A})) {
+ $R->{$k} = 1
+ }
+ # add all keys in B
+ foreach my $k (keys(%{$B})) {
+ $R->{$k} = 1
+ }
+ return $R;
+}
+
+# Subtract B from A
+sub SubtractProfile {
+ my $A = shift;
+ my $B = shift;
+
+ my $R = {};
+ foreach my $k (keys(%{$A})) {
+ my $v = $A->{$k} - GetEntry($B, $k);
+ if ($v < 0 && $main::opt_drop_negative) {
+ $v = 0;
+ }
+ AddEntry($R, $k, $v);
+ }
+ if (!$main::opt_drop_negative) {
+ # Take care of when subtracted profile has more entries
+ foreach my $k (keys(%{$B})) {
+ if (!exists($A->{$k})) {
+ AddEntry($R, $k, 0 - $B->{$k});
+ }
+ }
+ }
+ return $R;
+}
+
+# Get entry from profile; zero if not present
+sub GetEntry {
+ my $profile = shift;
+ my $k = shift;
+ if (exists($profile->{$k})) {
+ return $profile->{$k};
+ } else {
+ return 0;
+ }
+}
+
+# Add entry to specified profile
+sub AddEntry {
+ my $profile = shift;
+ my $k = shift;
+ my $n = shift;
+ if (!exists($profile->{$k})) {
+ $profile->{$k} = 0;
+ }
+ $profile->{$k} += $n;
+}
+
+# Add a stack of entries to specified profile, and add them to the $pcs
+# list.
+sub AddEntries {
+ my $profile = shift;
+ my $pcs = shift;
+ my $stack = shift;
+ my $count = shift;
+ my @k = ();
+
+ foreach my $e (split(/\s+/, $stack)) {
+ my $pc = HexExtend($e);
+ $pcs->{$pc} = 1;
+ push @k, $pc;
+ }
+ AddEntry($profile, (join "\n", @k), $count);
+}
+
+##### Code to profile a server dynamically #####
+
+sub CheckSymbolPage {
+ my $url = SymbolPageURL();
+ my $command = ShellEscape(@URL_FETCHER, $url);
+ open(SYMBOL, "$command |") or error($command);
+ my $line = <SYMBOL>;
+ $line =~ s/\r//g; # turn windows-looking lines into unix-looking lines
+ close(SYMBOL);
+ unless (defined($line)) {
+ error("$url doesn't exist\n");
+ }
+
+ if ($line =~ /^num_symbols:\s+(\d+)$/) {
+ if ($1 == 0) {
+ error("Stripped binary. No symbols available.\n");
+ }
+ } else {
+ error("Failed to get the number of symbols from $url\n");
+ }
+}
+
+sub IsProfileURL {
+ my $profile_name = shift;
+ if (-f $profile_name) {
+ printf STDERR "Using local file $profile_name.\n";
+ return 0;
+ }
+ return 1;
+}
+
+sub ParseProfileURL {
+ my $profile_name = shift;
+
+ if (!defined($profile_name) || $profile_name eq "") {
+ return ();
+ }
+
+ # Split profile URL - matches all non-empty strings, so no test.
+ $profile_name =~ m,^(https?://)?([^/]+)(.*?)(/|$PROFILES)?$,;
+
+ my $proto = $1 || "http://";
+ my $hostport = $2;
+ my $prefix = $3;
+ my $profile = $4 || "/";
+
+ my $host = $hostport;
+ $host =~ s/:.*//;
+
+ my $baseurl = "$proto$hostport$prefix";
+ return ($host, $baseurl, $profile);
+}
+
+# We fetch symbols from the first profile argument.
+sub SymbolPageURL {
+ my ($host, $baseURL, $path) = ParseProfileURL($main::pfile_args[0]);
+ return "$baseURL$SYMBOL_PAGE";
+}
+
+sub FetchProgramName() {
+ my ($host, $baseURL, $path) = ParseProfileURL($main::pfile_args[0]);
+ my $url = "$baseURL$PROGRAM_NAME_PAGE";
+ my $command_line = ShellEscape(@URL_FETCHER, $url);
+ open(CMDLINE, "$command_line |") or error($command_line);
+ my $cmdline = <CMDLINE>;
+ $cmdline =~ s/\r//g; # turn windows-looking lines into unix-looking lines
+ close(CMDLINE);
+ error("Failed to get program name from $url\n") unless defined($cmdline);
+ $cmdline =~ s/\x00.+//; # Remove argv[1] and latters.
+ $cmdline =~ s!\n!!g; # Remove LFs.
+ return $cmdline;
+}
+
+# Gee, curl's -L (--location) option isn't reliable at least
+# with its 7.12.3 version. Curl will forget to post data if
+# there is a redirection. This function is a workaround for
+# curl. Redirection happens on borg hosts.
+sub ResolveRedirectionForCurl {
+ my $url = shift;
+ my $command_line = ShellEscape(@URL_FETCHER, "--head", $url);
+ open(CMDLINE, "$command_line |") or error($command_line);
+ while (<CMDLINE>) {
+ s/\r//g; # turn windows-looking lines into unix-looking lines
+ if (/^Location: (.*)/) {
+ $url = $1;
+ }
+ }
+ close(CMDLINE);
+ return $url;
+}
+
+# Add a timeout flat to URL_FETCHER. Returns a new list.
+sub AddFetchTimeout {
+ my $timeout = shift;
+ my @fetcher = @_;
+ if (defined($timeout)) {
+ if (join(" ", @fetcher) =~ m/\bcurl -s/) {
+ push(@fetcher, "--max-time", sprintf("%d", $timeout));
+ } elsif (join(" ", @fetcher) =~ m/\brpcget\b/) {
+ push(@fetcher, sprintf("--deadline=%d", $timeout));
+ }
+ }
+ return @fetcher;
+}
+
+# Reads a symbol map from the file handle name given as $1, returning
+# the resulting symbol map. Also processes variables relating to symbols.
+# Currently, the only variable processed is 'binary=<value>' which updates
+# $main::prog to have the correct program name.
+sub ReadSymbols {
+ my $in = shift;
+ my $map = {};
+ while (<$in>) {
+ s/\r//g; # turn windows-looking lines into unix-looking lines
+ # Removes all the leading zeroes from the symbols, see comment below.
+ if (m/^0x0*([0-9a-f]+)\s+(.+)/) {
+ $map->{$1} = $2;
+ } elsif (m/^---/) {
+ last;
+ } elsif (m/^([a-z][^=]*)=(.*)$/ ) {
+ my ($variable, $value) = ($1, $2);
+ for ($variable, $value) {
+ s/^\s+//;
+ s/\s+$//;
+ }
+ if ($variable eq "binary") {
+ if ($main::prog ne $UNKNOWN_BINARY && $main::prog ne $value) {
+ printf STDERR ("Warning: Mismatched binary name '%s', using '%s'.\n",
+ $main::prog, $value);
+ }
+ $main::prog = $value;
+ } else {
+ printf STDERR ("Ignoring unknown variable in symbols list: " .
+ "'%s' = '%s'\n", $variable, $value);
+ }
+ }
+ }
+ return $map;
+}
+
+sub URLEncode {
+ my $str = shift;
+ $str =~ s/([^A-Za-z0-9\-_.!~*'()])/ sprintf "%%%02x", ord $1 /eg;
+ return $str;
+}
+
+sub AppendSymbolFilterParams {
+ my $url = shift;
+ my @params = ();
+ if ($main::opt_retain ne '') {
+ push(@params, sprintf("retain=%s", URLEncode($main::opt_retain)));
+ }
+ if ($main::opt_exclude ne '') {
+ push(@params, sprintf("exclude=%s", URLEncode($main::opt_exclude)));
+ }
+ if (scalar @params > 0) {
+ $url = sprintf("%s?%s", $url, join("&", @params));
+ }
+ return $url;
+}
+
+# Fetches and processes symbols to prepare them for use in the profile output
+# code. If the optional 'symbol_map' arg is not given, fetches symbols from
+# $SYMBOL_PAGE for all PC values found in profile. Otherwise, the raw symbols
+# are assumed to have already been fetched into 'symbol_map' and are simply
+# extracted and processed.
+sub FetchSymbols {
+ my $pcset = shift;
+ my $symbol_map = shift;
+
+ my %seen = ();
+ my @pcs = grep { !$seen{$_}++ } keys(%$pcset); # uniq
+
+ if (!defined($symbol_map)) {
+ my $post_data = join("+", sort((map {"0x" . "$_"} @pcs)));
+
+ open(POSTFILE, ">$main::tmpfile_sym");
+ print POSTFILE $post_data;
+ close(POSTFILE);
+
+ my $url = SymbolPageURL();
+
+ my $command_line;
+ if (join(" ", @URL_FETCHER) =~ m/\bcurl -s/) {
+ $url = ResolveRedirectionForCurl($url);
+ $url = AppendSymbolFilterParams($url);
+ $command_line = ShellEscape(@URL_FETCHER, "-d", "\@$main::tmpfile_sym",
+ $url);
+ } else {
+ $url = AppendSymbolFilterParams($url);
+ $command_line = (ShellEscape(@URL_FETCHER, "--post", $url)
+ . " < " . ShellEscape($main::tmpfile_sym));
+ }
+ # We use c++filt in case $SYMBOL_PAGE gives us mangled symbols.
+ my $escaped_cppfilt = ShellEscape($obj_tool_map{"c++filt"});
+ open(SYMBOL, "$command_line | $escaped_cppfilt |") or error($command_line);
+ $symbol_map = ReadSymbols(*SYMBOL{IO});
+ close(SYMBOL);
+ }
+
+ my $symbols = {};
+ foreach my $pc (@pcs) {
+ my $fullname;
+ # For 64 bits binaries, symbols are extracted with 8 leading zeroes.
+ # Then /symbol reads the long symbols in as uint64, and outputs
+ # the result with a "0x%08llx" format which get rid of the zeroes.
+ # By removing all the leading zeroes in both $pc and the symbols from
+ # /symbol, the symbols match and are retrievable from the map.
+ my $shortpc = $pc;
+ $shortpc =~ s/^0*//;
+ # Each line may have a list of names, which includes the function
+ # and also other functions it has inlined. They are separated (in
+ # PrintSymbolizedProfile), by --, which is illegal in function names.
+ my $fullnames;
+ if (defined($symbol_map->{$shortpc})) {
+ $fullnames = $symbol_map->{$shortpc};
+ } else {
+ $fullnames = "0x" . $pc; # Just use addresses
+ }
+ my $sym = [];
+ $symbols->{$pc} = $sym;
+ foreach my $fullname (split("--", $fullnames)) {
+ my $name = ShortFunctionName($fullname);
+ push(@{$sym}, $name, "?", $fullname);
+ }
+ }
+ return $symbols;
+}
+
+sub BaseName {
+ my $file_name = shift;
+ $file_name =~ s!^.*/!!; # Remove directory name
+ return $file_name;
+}
+
+sub MakeProfileBaseName {
+ my ($binary_name, $profile_name) = @_;
+ my ($host, $baseURL, $path) = ParseProfileURL($profile_name);
+ my $binary_shortname = BaseName($binary_name);
+ return sprintf("%s.%s.%s",
+ $binary_shortname, $main::op_time, $host);
+}
+
+sub FetchDynamicProfile {
+ my $binary_name = shift;
+ my $profile_name = shift;
+ my $fetch_name_only = shift;
+ my $encourage_patience = shift;
+
+ if (!IsProfileURL($profile_name)) {
+ return $profile_name;
+ } else {
+ my ($host, $baseURL, $path) = ParseProfileURL($profile_name);
+ if ($path eq "" || $path eq "/") {
+ # Missing type specifier defaults to cpu-profile
+ $path = $PROFILE_PAGE;
+ }
+
+ my $profile_file = MakeProfileBaseName($binary_name, $profile_name);
+
+ my $url = "$baseURL$path";
+ my $fetch_timeout = undef;
+ if ($path =~ m/$PROFILE_PAGE|$PMUPROFILE_PAGE/) {
+ if ($path =~ m/[?]/) {
+ $url .= "&";
+ } else {
+ $url .= "?";
+ }
+ $url .= sprintf("seconds=%d", $main::opt_seconds);
+ $fetch_timeout = $main::opt_seconds * 1.01 + 60;
+ # Set $profile_type for consumption by PrintSymbolizedProfile.
+ $main::profile_type = 'cpu';
+ } else {
+ # For non-CPU profiles, we add a type-extension to
+ # the target profile file name.
+ my $suffix = $path;
+ $suffix =~ s,/,.,g;
+ $profile_file .= $suffix;
+ # Set $profile_type for consumption by PrintSymbolizedProfile.
+ if ($path =~ m/$HEAP_PAGE/) {
+ $main::profile_type = 'heap';
+ } elsif ($path =~ m/$GROWTH_PAGE/) {
+ $main::profile_type = 'growth';
+ } elsif ($path =~ m/$CONTENTION_PAGE/) {
+ $main::profile_type = 'contention';
+ }
+ }
+
+ my $profile_dir = $ENV{"JEPROF_TMPDIR"} || ($ENV{HOME} . "/jeprof");
+ if (! -d $profile_dir) {
+ mkdir($profile_dir)
+ || die("Unable to create profile directory $profile_dir: $!\n");
+ }
+ my $tmp_profile = "$profile_dir/.tmp.$profile_file";
+ my $real_profile = "$profile_dir/$profile_file";
+
+ if ($fetch_name_only > 0) {
+ return $real_profile;
+ }
+
+ my @fetcher = AddFetchTimeout($fetch_timeout, @URL_FETCHER);
+ my $cmd = ShellEscape(@fetcher, $url) . " > " . ShellEscape($tmp_profile);
+ if ($path =~ m/$PROFILE_PAGE|$PMUPROFILE_PAGE|$CENSUSPROFILE_PAGE/){
+ print STDERR "Gathering CPU profile from $url for $main::opt_seconds seconds to\n ${real_profile}\n";
+ if ($encourage_patience) {
+ print STDERR "Be patient...\n";
+ }
+ } else {
+ print STDERR "Fetching $path profile from $url to\n ${real_profile}\n";
+ }
+
+ (system($cmd) == 0) || error("Failed to get profile: $cmd: $!\n");
+ (system("mv", $tmp_profile, $real_profile) == 0) || error("Unable to rename profile\n");
+ print STDERR "Wrote profile to $real_profile\n";
+ $main::collected_profile = $real_profile;
+ return $main::collected_profile;
+ }
+}
+
+# Collect profiles in parallel
+sub FetchDynamicProfiles {
+ my $items = scalar(@main::pfile_args);
+ my $levels = log($items) / log(2);
+
+ if ($items == 1) {
+ $main::profile_files[0] = FetchDynamicProfile($main::prog, $main::pfile_args[0], 0, 1);
+ } else {
+ # math rounding issues
+ if ((2 ** $levels) < $items) {
+ $levels++;
+ }
+ my $count = scalar(@main::pfile_args);
+ for (my $i = 0; $i < $count; $i++) {
+ $main::profile_files[$i] = FetchDynamicProfile($main::prog, $main::pfile_args[$i], 1, 0);
+ }
+ print STDERR "Fetching $count profiles, Be patient...\n";
+ FetchDynamicProfilesRecurse($levels, 0, 0);
+ $main::collected_profile = join(" \\\n ", @main::profile_files);
+ }
+}
+
+# Recursively fork a process to get enough processes
+# collecting profiles
+sub FetchDynamicProfilesRecurse {
+ my $maxlevel = shift;
+ my $level = shift;
+ my $position = shift;
+
+ if (my $pid = fork()) {
+ $position = 0 | ($position << 1);
+ TryCollectProfile($maxlevel, $level, $position);
+ wait;
+ } else {
+ $position = 1 | ($position << 1);
+ TryCollectProfile($maxlevel, $level, $position);
+ cleanup();
+ exit(0);
+ }
+}
+
+# Collect a single profile
+sub TryCollectProfile {
+ my $maxlevel = shift;
+ my $level = shift;
+ my $position = shift;
+
+ if ($level >= ($maxlevel - 1)) {
+ if ($position < scalar(@main::pfile_args)) {
+ FetchDynamicProfile($main::prog, $main::pfile_args[$position], 0, 0);
+ }
+ } else {
+ FetchDynamicProfilesRecurse($maxlevel, $level+1, $position);
+ }
+}
+
+##### Parsing code #####
+
+# Provide a small streaming-read module to handle very large
+# cpu-profile files. Stream in chunks along a sliding window.
+# Provides an interface to get one 'slot', correctly handling
+# endian-ness differences. A slot is one 32-bit or 64-bit word
+# (depending on the input profile). We tell endianness and bit-size
+# for the profile by looking at the first 8 bytes: in cpu profiles,
+# the second slot is always 3 (we'll accept anything that's not 0).
+BEGIN {
+ package CpuProfileStream;
+
+ sub new {
+ my ($class, $file, $fname) = @_;
+ my $self = { file => $file,
+ base => 0,
+ stride => 512 * 1024, # must be a multiple of bitsize/8
+ slots => [],
+ unpack_code => "", # N for big-endian, V for little
+ perl_is_64bit => 1, # matters if profile is 64-bit
+ };
+ bless $self, $class;
+ # Let unittests adjust the stride
+ if ($main::opt_test_stride > 0) {
+ $self->{stride} = $main::opt_test_stride;
+ }
+ # Read the first two slots to figure out bitsize and endianness.
+ my $slots = $self->{slots};
+ my $str;
+ read($self->{file}, $str, 8);
+ # Set the global $address_length based on what we see here.
+ # 8 is 32-bit (8 hexadecimal chars); 16 is 64-bit (16 hexadecimal chars).
+ $address_length = ($str eq (chr(0)x8)) ? 16 : 8;
+ if ($address_length == 8) {
+ if (substr($str, 6, 2) eq chr(0)x2) {
+ $self->{unpack_code} = 'V'; # Little-endian.
+ } elsif (substr($str, 4, 2) eq chr(0)x2) {
+ $self->{unpack_code} = 'N'; # Big-endian
+ } else {
+ ::error("$fname: header size >= 2**16\n");
+ }
+ @$slots = unpack($self->{unpack_code} . "*", $str);
+ } else {
+ # If we're a 64-bit profile, check if we're a 64-bit-capable
+ # perl. Otherwise, each slot will be represented as a float
+ # instead of an int64, losing precision and making all the
+ # 64-bit addresses wrong. We won't complain yet, but will
+ # later if we ever see a value that doesn't fit in 32 bits.
+ my $has_q = 0;
+ eval { $has_q = pack("Q", "1") ? 1 : 1; };
+ if (!$has_q) {
+ $self->{perl_is_64bit} = 0;
+ }
+ read($self->{file}, $str, 8);
+ if (substr($str, 4, 4) eq chr(0)x4) {
+ # We'd love to use 'Q', but it's a) not universal, b) not endian-proof.
+ $self->{unpack_code} = 'V'; # Little-endian.
+ } elsif (substr($str, 0, 4) eq chr(0)x4) {
+ $self->{unpack_code} = 'N'; # Big-endian
+ } else {
+ ::error("$fname: header size >= 2**32\n");
+ }
+ my @pair = unpack($self->{unpack_code} . "*", $str);
+ # Since we know one of the pair is 0, it's fine to just add them.
+ @$slots = (0, $pair[0] + $pair[1]);
+ }
+ return $self;
+ }
+
+ # Load more data when we access slots->get(X) which is not yet in memory.
+ sub overflow {
+ my ($self) = @_;
+ my $slots = $self->{slots};
+ $self->{base} += $#$slots + 1; # skip over data we're replacing
+ my $str;
+ read($self->{file}, $str, $self->{stride});
+ if ($address_length == 8) { # the 32-bit case
+ # This is the easy case: unpack provides 32-bit unpacking primitives.
+ @$slots = unpack($self->{unpack_code} . "*", $str);
+ } else {
+ # We need to unpack 32 bits at a time and combine.
+ my @b32_values = unpack($self->{unpack_code} . "*", $str);
+ my @b64_values = ();
+ for (my $i = 0; $i < $#b32_values; $i += 2) {
+ # TODO(csilvers): if this is a 32-bit perl, the math below
+ # could end up in a too-large int, which perl will promote
+ # to a double, losing necessary precision. Deal with that.
+ # Right now, we just die.
+ my ($lo, $hi) = ($b32_values[$i], $b32_values[$i+1]);
+ if ($self->{unpack_code} eq 'N') { # big-endian
+ ($lo, $hi) = ($hi, $lo);
+ }
+ my $value = $lo + $hi * (2**32);
+ if (!$self->{perl_is_64bit} && # check value is exactly represented
+ (($value % (2**32)) != $lo || int($value / (2**32)) != $hi)) {
+ ::error("Need a 64-bit perl to process this 64-bit profile.\n");
+ }
+ push(@b64_values, $value);
+ }
+ @$slots = @b64_values;
+ }
+ }
+
+ # Access the i-th long in the file (logically), or -1 at EOF.
+ sub get {
+ my ($self, $idx) = @_;
+ my $slots = $self->{slots};
+ while ($#$slots >= 0) {
+ if ($idx < $self->{base}) {
+ # The only time we expect a reference to $slots[$i - something]
+ # after referencing $slots[$i] is reading the very first header.
+ # Since $stride > |header|, that shouldn't cause any lookback
+ # errors. And everything after the header is sequential.
+ print STDERR "Unexpected look-back reading CPU profile";
+ return -1; # shrug, don't know what better to return
+ } elsif ($idx > $self->{base} + $#$slots) {
+ $self->overflow();
+ } else {
+ return $slots->[$idx - $self->{base}];
+ }
+ }
+ # If we get here, $slots is [], which means we've reached EOF
+ return -1; # unique since slots is supposed to hold unsigned numbers
+ }
+}
+
+# Reads the top, 'header' section of a profile, and returns the last
+# line of the header, commonly called a 'header line'. The header
+# section of a profile consists of zero or more 'command' lines that
+# are instructions to jeprof, which jeprof executes when reading the
+# header. All 'command' lines start with a %. After the command
+# lines is the 'header line', which is a profile-specific line that
+# indicates what type of profile it is, and perhaps other global
+# information about the profile. For instance, here's a header line
+# for a heap profile:
+# heap profile: 53: 38236 [ 5525: 1284029] @ heapprofile
+# For historical reasons, the CPU profile does not contain a text-
+# readable header line. If the profile looks like a CPU profile,
+# this function returns "". If no header line could be found, this
+# function returns undef.
+#
+# The following commands are recognized:
+# %warn -- emit the rest of this line to stderr, prefixed by 'WARNING:'
+#
+# The input file should be in binmode.
+sub ReadProfileHeader {
+ local *PROFILE = shift;
+ my $firstchar = "";
+ my $line = "";
+ read(PROFILE, $firstchar, 1);
+ seek(PROFILE, -1, 1); # unread the firstchar
+ if ($firstchar !~ /[[:print:]]/) { # is not a text character
+ return "";
+ }
+ while (defined($line = <PROFILE>)) {
+ $line =~ s/\r//g; # turn windows-looking lines into unix-looking lines
+ if ($line =~ /^%warn\s+(.*)/) { # 'warn' command
+ # Note this matches both '%warn blah\n' and '%warn\n'.
+ print STDERR "WARNING: $1\n"; # print the rest of the line
+ } elsif ($line =~ /^%/) {
+ print STDERR "Ignoring unknown command from profile header: $line";
+ } else {
+ # End of commands, must be the header line.
+ return $line;
+ }
+ }
+ return undef; # got to EOF without seeing a header line
+}
+
+sub IsSymbolizedProfileFile {
+ my $file_name = shift;
+ if (!(-e $file_name) || !(-r $file_name)) {
+ return 0;
+ }
+ # Check if the file contains a symbol-section marker.
+ open(TFILE, "<$file_name");
+ binmode TFILE;
+ my $firstline = ReadProfileHeader(*TFILE);
+ close(TFILE);
+ if (!$firstline) {
+ return 0;
+ }
+ $SYMBOL_PAGE =~ m,[^/]+$,; # matches everything after the last slash
+ my $symbol_marker = $&;
+ return $firstline =~ /^--- *$symbol_marker/;
+}
+
+# Parse profile generated by common/profiler.cc and return a reference
+# to a map:
+# $result->{version} Version number of profile file
+# $result->{period} Sampling period (in microseconds)
+# $result->{profile} Profile object
+# $result->{threads} Map of thread IDs to profile objects
+# $result->{map} Memory map info from profile
+# $result->{pcs} Hash of all PC values seen, key is hex address
+sub ReadProfile {
+ my $prog = shift;
+ my $fname = shift;
+ my $result; # return value
+
+ $CONTENTION_PAGE =~ m,[^/]+$,; # matches everything after the last slash
+ my $contention_marker = $&;
+ $GROWTH_PAGE =~ m,[^/]+$,; # matches everything after the last slash
+ my $growth_marker = $&;
+ $SYMBOL_PAGE =~ m,[^/]+$,; # matches everything after the last slash
+ my $symbol_marker = $&;
+ $PROFILE_PAGE =~ m,[^/]+$,; # matches everything after the last slash
+ my $profile_marker = $&;
+ $HEAP_PAGE =~ m,[^/]+$,; # matches everything after the last slash
+ my $heap_marker = $&;
+
+ # Look at first line to see if it is a heap or a CPU profile.
+ # CPU profile may start with no header at all, and just binary data
+ # (starting with \0\0\0\0) -- in that case, don't try to read the
+ # whole firstline, since it may be gigabytes(!) of data.
+ open(PROFILE, "<$fname") || error("$fname: $!\n");
+ binmode PROFILE; # New perls do UTF-8 processing
+ my $header = ReadProfileHeader(*PROFILE);
+ if (!defined($header)) { # means "at EOF"
+ error("Profile is empty.\n");
+ }
+
+ my $symbols;
+ if ($header =~ m/^--- *$symbol_marker/o) {
+ # Verify that the user asked for a symbolized profile
+ if (!$main::use_symbolized_profile) {
+ # we have both a binary and symbolized profiles, abort
+ error("FATAL ERROR: Symbolized profile\n $fname\ncannot be used with " .
+ "a binary arg. Try again without passing\n $prog\n");
+ }
+ # Read the symbol section of the symbolized profile file.
+ $symbols = ReadSymbols(*PROFILE{IO});
+ # Read the next line to get the header for the remaining profile.
+ $header = ReadProfileHeader(*PROFILE) || "";
+ }
+
+ if ($header =~ m/^--- *($heap_marker|$growth_marker)/o) {
+ # Skip "--- ..." line for profile types that have their own headers.
+ $header = ReadProfileHeader(*PROFILE) || "";
+ }
+
+ $main::profile_type = '';
+
+ if ($header =~ m/^heap profile:.*$growth_marker/o) {
+ $main::profile_type = 'growth';
+ $result = ReadHeapProfile($prog, *PROFILE, $header);
+ } elsif ($header =~ m/^heap profile:/) {
+ $main::profile_type = 'heap';
+ $result = ReadHeapProfile($prog, *PROFILE, $header);
+ } elsif ($header =~ m/^heap/) {
+ $main::profile_type = 'heap';
+ $result = ReadThreadedHeapProfile($prog, $fname, $header);
+ } elsif ($header =~ m/^--- *$contention_marker/o) {
+ $main::profile_type = 'contention';
+ $result = ReadSynchProfile($prog, *PROFILE);
+ } elsif ($header =~ m/^--- *Stacks:/) {
+ print STDERR
+ "Old format contention profile: mistakenly reports " .
+ "condition variable signals as lock contentions.\n";
+ $main::profile_type = 'contention';
+ $result = ReadSynchProfile($prog, *PROFILE);
+ } elsif ($header =~ m/^--- *$profile_marker/) {
+ # the binary cpu profile data starts immediately after this line
+ $main::profile_type = 'cpu';
+ $result = ReadCPUProfile($prog, $fname, *PROFILE);
+ } else {
+ if (defined($symbols)) {
+ # a symbolized profile contains a format we don't recognize, bail out
+ error("$fname: Cannot recognize profile section after symbols.\n");
+ }
+ # no ascii header present -- must be a CPU profile
+ $main::profile_type = 'cpu';
+ $result = ReadCPUProfile($prog, $fname, *PROFILE);
+ }
+
+ close(PROFILE);
+
+ # if we got symbols along with the profile, return those as well
+ if (defined($symbols)) {
+ $result->{symbols} = $symbols;
+ }
+
+ return $result;
+}
+
+# Subtract one from caller pc so we map back to call instr.
+# However, don't do this if we're reading a symbolized profile
+# file, in which case the subtract-one was done when the file
+# was written.
+#
+# We apply the same logic to all readers, though ReadCPUProfile uses an
+# independent implementation.
+sub FixCallerAddresses {
+ my $stack = shift;
+ # --raw/http: Always subtract one from pc's, because PrintSymbolizedProfile()
+ # dumps unadjusted profiles.
+ {
+ $stack =~ /(\s)/;
+ my $delimiter = $1;
+ my @addrs = split(' ', $stack);
+ my @fixedaddrs;
+ $#fixedaddrs = $#addrs;
+ if ($#addrs >= 0) {
+ $fixedaddrs[0] = $addrs[0];
+ }
+ for (my $i = 1; $i <= $#addrs; $i++) {
+ $fixedaddrs[$i] = AddressSub($addrs[$i], "0x1");
+ }
+ return join $delimiter, @fixedaddrs;
+ }
+}
+
+# CPU profile reader
+sub ReadCPUProfile {
+ my $prog = shift;
+ my $fname = shift; # just used for logging
+ local *PROFILE = shift;
+ my $version;
+ my $period;
+ my $i;
+ my $profile = {};
+ my $pcs = {};
+
+ # Parse string into array of slots.
+ my $slots = CpuProfileStream->new(*PROFILE, $fname);
+
+ # Read header. The current header version is a 5-element structure
+ # containing:
+ # 0: header count (always 0)
+ # 1: header "words" (after this one: 3)
+ # 2: format version (0)
+ # 3: sampling period (usec)
+ # 4: unused padding (always 0)
+ if ($slots->get(0) != 0 ) {
+ error("$fname: not a profile file, or old format profile file\n");
+ }
+ $i = 2 + $slots->get(1);
+ $version = $slots->get(2);
+ $period = $slots->get(3);
+ # Do some sanity checking on these header values.
+ if ($version > (2**32) || $period > (2**32) || $i > (2**32) || $i < 5) {
+ error("$fname: not a profile file, or corrupted profile file\n");
+ }
+
+ # Parse profile
+ while ($slots->get($i) != -1) {
+ my $n = $slots->get($i++);
+ my $d = $slots->get($i++);
+ if ($d > (2**16)) { # TODO(csilvers): what's a reasonable max-stack-depth?
+ my $addr = sprintf("0%o", $i * ($address_length == 8 ? 4 : 8));
+ print STDERR "At index $i (address $addr):\n";
+ error("$fname: stack trace depth >= 2**32\n");
+ }
+ if ($slots->get($i) == 0) {
+ # End of profile data marker
+ $i += $d;
+ last;
+ }
+
+ # Make key out of the stack entries
+ my @k = ();
+ for (my $j = 0; $j < $d; $j++) {
+ my $pc = $slots->get($i+$j);
+ # Subtract one from caller pc so we map back to call instr.
+ $pc--;
+ $pc = sprintf("%0*x", $address_length, $pc);
+ $pcs->{$pc} = 1;
+ push @k, $pc;
+ }
+
+ AddEntry($profile, (join "\n", @k), $n);
+ $i += $d;
+ }
+
+ # Parse map
+ my $map = '';
+ seek(PROFILE, $i * 4, 0);
+ read(PROFILE, $map, (stat PROFILE)[7]);
+
+ my $r = {};
+ $r->{version} = $version;
+ $r->{period} = $period;
+ $r->{profile} = $profile;
+ $r->{libs} = ParseLibraries($prog, $map, $pcs);
+ $r->{pcs} = $pcs;
+
+ return $r;
+}
+
+sub HeapProfileIndex {
+ my $index = 1;
+ if ($main::opt_inuse_space) {
+ $index = 1;
+ } elsif ($main::opt_inuse_objects) {
+ $index = 0;
+ } elsif ($main::opt_alloc_space) {
+ $index = 3;
+ } elsif ($main::opt_alloc_objects) {
+ $index = 2;
+ }
+ return $index;
+}
+
+sub ReadMappedLibraries {
+ my $fh = shift;
+ my $map = "";
+ # Read the /proc/self/maps data
+ while (<$fh>) {
+ s/\r//g; # turn windows-looking lines into unix-looking lines
+ $map .= $_;
+ }
+ return $map;
+}
+
+sub ReadMemoryMap {
+ my $fh = shift;
+ my $map = "";
+ # Read /proc/self/maps data as formatted by DumpAddressMap()
+ my $buildvar = "";
+ while (<PROFILE>) {
+ s/\r//g; # turn windows-looking lines into unix-looking lines
+ # Parse "build=<dir>" specification if supplied
+ if (m/^\s*build=(.*)\n/) {
+ $buildvar = $1;
+ }
+
+ # Expand "$build" variable if available
+ $_ =~ s/\$build\b/$buildvar/g;
+
+ $map .= $_;
+ }
+ return $map;
+}
+
+sub AdjustSamples {
+ my ($sample_adjustment, $sampling_algorithm, $n1, $s1, $n2, $s2) = @_;
+ if ($sample_adjustment) {
+ if ($sampling_algorithm == 2) {
+ # Remote-heap version 2
+ # The sampling frequency is the rate of a Poisson process.
+ # This means that the probability of sampling an allocation of
+ # size X with sampling rate Y is 1 - exp(-X/Y)
+ if ($n1 != 0) {
+ my $ratio = (($s1*1.0)/$n1)/($sample_adjustment);
+ my $scale_factor = 1/(1 - exp(-$ratio));
+ $n1 *= $scale_factor;
+ $s1 *= $scale_factor;
+ }
+ if ($n2 != 0) {
+ my $ratio = (($s2*1.0)/$n2)/($sample_adjustment);
+ my $scale_factor = 1/(1 - exp(-$ratio));
+ $n2 *= $scale_factor;
+ $s2 *= $scale_factor;
+ }
+ } else {
+ # Remote-heap version 1
+ my $ratio;
+ $ratio = (($s1*1.0)/$n1)/($sample_adjustment);
+ if ($ratio < 1) {
+ $n1 /= $ratio;
+ $s1 /= $ratio;
+ }
+ $ratio = (($s2*1.0)/$n2)/($sample_adjustment);
+ if ($ratio < 1) {
+ $n2 /= $ratio;
+ $s2 /= $ratio;
+ }
+ }
+ }
+ return ($n1, $s1, $n2, $s2);
+}
+
+sub ReadHeapProfile {
+ my $prog = shift;
+ local *PROFILE = shift;
+ my $header = shift;
+
+ my $index = HeapProfileIndex();
+
+ # Find the type of this profile. The header line looks like:
+ # heap profile: 1246: 8800744 [ 1246: 8800744] @ <heap-url>/266053
+ # There are two pairs <count: size>, the first inuse objects/space, and the
+ # second allocated objects/space. This is followed optionally by a profile
+ # type, and if that is present, optionally by a sampling frequency.
+ # For remote heap profiles (v1):
+ # The interpretation of the sampling frequency is that the profiler, for
+ # each sample, calculates a uniformly distributed random integer less than
+ # the given value, and records the next sample after that many bytes have
+ # been allocated. Therefore, the expected sample interval is half of the
+ # given frequency. By default, if not specified, the expected sample
+ # interval is 128KB. Only remote-heap-page profiles are adjusted for
+ # sample size.
+ # For remote heap profiles (v2):
+ # The sampling frequency is the rate of a Poisson process. This means that
+ # the probability of sampling an allocation of size X with sampling rate Y
+ # is 1 - exp(-X/Y)
+ # For version 2, a typical header line might look like this:
+ # heap profile: 1922: 127792360 [ 1922: 127792360] @ <heap-url>_v2/524288
+ # the trailing number (524288) is the sampling rate. (Version 1 showed
+ # double the 'rate' here)
+ my $sampling_algorithm = 0;
+ my $sample_adjustment = 0;
+ chomp($header);
+ my $type = "unknown";
+ if ($header =~ m"^heap profile:\s*(\d+):\s+(\d+)\s+\[\s*(\d+):\s+(\d+)\](\s*@\s*([^/]*)(/(\d+))?)?") {
+ if (defined($6) && ($6 ne '')) {
+ $type = $6;
+ my $sample_period = $8;
+ # $type is "heapprofile" for profiles generated by the
+ # heap-profiler, and either "heap" or "heap_v2" for profiles
+ # generated by sampling directly within tcmalloc. It can also
+ # be "growth" for heap-growth profiles. The first is typically
+ # found for profiles generated locally, and the others for
+ # remote profiles.
+ if (($type eq "heapprofile") || ($type !~ /heap/) ) {
+ # No need to adjust for the sampling rate with heap-profiler-derived data
+ $sampling_algorithm = 0;
+ } elsif ($type =~ /_v2/) {
+ $sampling_algorithm = 2; # version 2 sampling
+ if (defined($sample_period) && ($sample_period ne '')) {
+ $sample_adjustment = int($sample_period);
+ }
+ } else {
+ $sampling_algorithm = 1; # version 1 sampling
+ if (defined($sample_period) && ($sample_period ne '')) {
+ $sample_adjustment = int($sample_period)/2;
+ }
+ }
+ } else {
+ # We detect whether or not this is a remote-heap profile by checking
+ # that the total-allocated stats ($n2,$s2) are exactly the
+ # same as the in-use stats ($n1,$s1). It is remotely conceivable
+ # that a non-remote-heap profile may pass this check, but it is hard
+ # to imagine how that could happen.
+ # In this case it's so old it's guaranteed to be remote-heap version 1.
+ my ($n1, $s1, $n2, $s2) = ($1, $2, $3, $4);
+ if (($n1 == $n2) && ($s1 == $s2)) {
+ # This is likely to be a remote-heap based sample profile
+ $sampling_algorithm = 1;
+ }
+ }
+ }
+
+ if ($sampling_algorithm > 0) {
+ # For remote-heap generated profiles, adjust the counts and sizes to
+ # account for the sample rate (we sample once every 128KB by default).
+ if ($sample_adjustment == 0) {
+ # Turn on profile adjustment.
+ $sample_adjustment = 128*1024;
+ print STDERR "Adjusting heap profiles for 1-in-128KB sampling rate\n";
+ } else {
+ printf STDERR ("Adjusting heap profiles for 1-in-%d sampling rate\n",
+ $sample_adjustment);
+ }
+ if ($sampling_algorithm > 1) {
+ # We don't bother printing anything for the original version (version 1)
+ printf STDERR "Heap version $sampling_algorithm\n";
+ }
+ }
+
+ my $profile = {};
+ my $pcs = {};
+ my $map = "";
+
+ while (<PROFILE>) {
+ s/\r//g; # turn windows-looking lines into unix-looking lines
+ if (/^MAPPED_LIBRARIES:/) {
+ $map .= ReadMappedLibraries(*PROFILE);
+ last;
+ }
+
+ if (/^--- Memory map:/) {
+ $map .= ReadMemoryMap(*PROFILE);
+ last;
+ }
+
+ # Read entry of the form:
+ # <count1>: <bytes1> [<count2>: <bytes2>] @ a1 a2 a3 ... an
+ s/^\s*//;
+ s/\s*$//;
+ if (m/^\s*(\d+):\s+(\d+)\s+\[\s*(\d+):\s+(\d+)\]\s+@\s+(.*)$/) {
+ my $stack = $5;
+ my ($n1, $s1, $n2, $s2) = ($1, $2, $3, $4);
+ my @counts = AdjustSamples($sample_adjustment, $sampling_algorithm,
+ $n1, $s1, $n2, $s2);
+ AddEntries($profile, $pcs, FixCallerAddresses($stack), $counts[$index]);
+ }
+ }
+
+ my $r = {};
+ $r->{version} = "heap";
+ $r->{period} = 1;
+ $r->{profile} = $profile;
+ $r->{libs} = ParseLibraries($prog, $map, $pcs);
+ $r->{pcs} = $pcs;
+ return $r;
+}
+
+sub ReadThreadedHeapProfile {
+ my ($prog, $fname, $header) = @_;
+
+ my $index = HeapProfileIndex();
+ my $sampling_algorithm = 0;
+ my $sample_adjustment = 0;
+ chomp($header);
+ my $type = "unknown";
+ # Assuming a very specific type of header for now.
+ if ($header =~ m"^heap_v2/(\d+)") {
+ $type = "_v2";
+ $sampling_algorithm = 2;
+ $sample_adjustment = int($1);
+ }
+ if ($type ne "_v2" || !defined($sample_adjustment)) {
+ die "Threaded heap profiles require v2 sampling with a sample rate\n";
+ }
+
+ my $profile = {};
+ my $thread_profiles = {};
+ my $pcs = {};
+ my $map = "";
+ my $stack = "";
+
+ while (<PROFILE>) {
+ s/\r//g;
+ if (/^MAPPED_LIBRARIES:/) {
+ $map .= ReadMappedLibraries(*PROFILE);
+ last;
+ }
+
+ if (/^--- Memory map:/) {
+ $map .= ReadMemoryMap(*PROFILE);
+ last;
+ }
+
+ # Read entry of the form:
+ # @ a1 a2 ... an
+ # t*: <count1>: <bytes1> [<count2>: <bytes2>]
+ # t1: <count1>: <bytes1> [<count2>: <bytes2>]
+ # ...
+ # tn: <count1>: <bytes1> [<count2>: <bytes2>]
+ s/^\s*//;
+ s/\s*$//;
+ if (m/^@\s+(.*)$/) {
+ $stack = $1;
+ } elsif (m/^\s*(t(\*|\d+)):\s+(\d+):\s+(\d+)\s+\[\s*(\d+):\s+(\d+)\]$/) {
+ if ($stack eq "") {
+ # Still in the header, so this is just a per-thread summary.
+ next;
+ }
+ my $thread = $2;
+ my ($n1, $s1, $n2, $s2) = ($3, $4, $5, $6);
+ my @counts = AdjustSamples($sample_adjustment, $sampling_algorithm,
+ $n1, $s1, $n2, $s2);
+ if ($thread eq "*") {
+ AddEntries($profile, $pcs, FixCallerAddresses($stack), $counts[$index]);
+ } else {
+ if (!exists($thread_profiles->{$thread})) {
+ $thread_profiles->{$thread} = {};
+ }
+ AddEntries($thread_profiles->{$thread}, $pcs,
+ FixCallerAddresses($stack), $counts[$index]);
+ }
+ }
+ }
+
+ my $r = {};
+ $r->{version} = "heap";
+ $r->{period} = 1;
+ $r->{profile} = $profile;
+ $r->{threads} = $thread_profiles;
+ $r->{libs} = ParseLibraries($prog, $map, $pcs);
+ $r->{pcs} = $pcs;
+ return $r;
+}
+
+sub ReadSynchProfile {
+ my $prog = shift;
+ local *PROFILE = shift;
+ my $header = shift;
+
+ my $map = '';
+ my $profile = {};
+ my $pcs = {};
+ my $sampling_period = 1;
+ my $cyclespernanosec = 2.8; # Default assumption for old binaries
+ my $seen_clockrate = 0;
+ my $line;
+
+ my $index = 0;
+ if ($main::opt_total_delay) {
+ $index = 0;
+ } elsif ($main::opt_contentions) {
+ $index = 1;
+ } elsif ($main::opt_mean_delay) {
+ $index = 2;
+ }
+
+ while ( $line = <PROFILE> ) {
+ $line =~ s/\r//g; # turn windows-looking lines into unix-looking lines
+ if ( $line =~ /^\s*(\d+)\s+(\d+) \@\s*(.*?)\s*$/ ) {
+ my ($cycles, $count, $stack) = ($1, $2, $3);
+
+ # Convert cycles to nanoseconds
+ $cycles /= $cyclespernanosec;
+
+ # Adjust for sampling done by application
+ $cycles *= $sampling_period;
+ $count *= $sampling_period;
+
+ my @values = ($cycles, $count, $cycles / $count);
+ AddEntries($profile, $pcs, FixCallerAddresses($stack), $values[$index]);
+
+ } elsif ( $line =~ /^(slow release).*thread \d+ \@\s*(.*?)\s*$/ ||
+ $line =~ /^\s*(\d+) \@\s*(.*?)\s*$/ ) {
+ my ($cycles, $stack) = ($1, $2);
+ if ($cycles !~ /^\d+$/) {
+ next;
+ }
+
+ # Convert cycles to nanoseconds
+ $cycles /= $cyclespernanosec;
+
+ # Adjust for sampling done by application
+ $cycles *= $sampling_period;
+
+ AddEntries($profile, $pcs, FixCallerAddresses($stack), $cycles);
+
+ } elsif ( $line =~ m/^([a-z][^=]*)=(.*)$/ ) {
+ my ($variable, $value) = ($1,$2);
+ for ($variable, $value) {
+ s/^\s+//;
+ s/\s+$//;
+ }
+ if ($variable eq "cycles/second") {
+ $cyclespernanosec = $value / 1e9;
+ $seen_clockrate = 1;
+ } elsif ($variable eq "sampling period") {
+ $sampling_period = $value;
+ } elsif ($variable eq "ms since reset") {
+ # Currently nothing is done with this value in jeprof
+ # So we just silently ignore it for now
+ } elsif ($variable eq "discarded samples") {
+ # Currently nothing is done with this value in jeprof
+ # So we just silently ignore it for now
+ } else {
+ printf STDERR ("Ignoring unnknown variable in /contention output: " .
+ "'%s' = '%s'\n",$variable,$value);
+ }
+ } else {
+ # Memory map entry
+ $map .= $line;
+ }
+ }
+
+ if (!$seen_clockrate) {
+ printf STDERR ("No cycles/second entry in profile; Guessing %.1f GHz\n",
+ $cyclespernanosec);
+ }
+
+ my $r = {};
+ $r->{version} = 0;
+ $r->{period} = $sampling_period;
+ $r->{profile} = $profile;
+ $r->{libs} = ParseLibraries($prog, $map, $pcs);
+ $r->{pcs} = $pcs;
+ return $r;
+}
+
+# Given a hex value in the form "0x1abcd" or "1abcd", return either
+# "0001abcd" or "000000000001abcd", depending on the current (global)
+# address length.
+sub HexExtend {
+ my $addr = shift;
+
+ $addr =~ s/^(0x)?0*//;
+ my $zeros_needed = $address_length - length($addr);
+ if ($zeros_needed < 0) {
+ printf STDERR "Warning: address $addr is longer than address length $address_length\n";
+ return $addr;
+ }
+ return ("0" x $zeros_needed) . $addr;
+}
+
+##### Symbol extraction #####
+
+# Aggressively search the lib_prefix values for the given library
+# If all else fails, just return the name of the library unmodified.
+# If the lib_prefix is "/my/path,/other/path" and $file is "/lib/dir/mylib.so"
+# it will search the following locations in this order, until it finds a file:
+# /my/path/lib/dir/mylib.so
+# /other/path/lib/dir/mylib.so
+# /my/path/dir/mylib.so
+# /other/path/dir/mylib.so
+# /my/path/mylib.so
+# /other/path/mylib.so
+# /lib/dir/mylib.so (returned as last resort)
+sub FindLibrary {
+ my $file = shift;
+ my $suffix = $file;
+
+ # Search for the library as described above
+ do {
+ foreach my $prefix (@prefix_list) {
+ my $fullpath = $prefix . $suffix;
+ if (-e $fullpath) {
+ return $fullpath;
+ }
+ }
+ } while ($suffix =~ s|^/[^/]+/|/|);
+ return $file;
+}
+
+# Return path to library with debugging symbols.
+# For libc libraries, the copy in /usr/lib/debug contains debugging symbols
+sub DebuggingLibrary {
+ my $file = shift;
+
+ if ($file !~ m|^/|) {
+ return undef;
+ }
+
+ # Find debug symbol file if it's named after the library's name.
+
+ if (-f "/usr/lib/debug$file") {
+ if($main::opt_debug) { print STDERR "found debug info for $file in /usr/lib/debug$file\n"; }
+ return "/usr/lib/debug$file";
+ } elsif (-f "/usr/lib/debug$file.debug") {
+ if($main::opt_debug) { print STDERR "found debug info for $file in /usr/lib/debug$file.debug\n"; }
+ return "/usr/lib/debug$file.debug";
+ }
+
+ if(!$main::opt_debug_syms_by_id) {
+ if($main::opt_debug) { print STDERR "no debug symbols found for $file\n" };
+ return undef;
+ }
+
+ # Find debug file if it's named after the library's build ID.
+
+ my $readelf = '';
+ if (!$main::gave_up_on_elfutils) {
+ $readelf = qx/eu-readelf -n ${file}/;
+ if ($?) {
+ print STDERR "Cannot run eu-readelf. To use --debug-syms-by-id you must be on Linux, with elfutils installed.\n";
+ $main::gave_up_on_elfutils = 1;
+ return undef;
+ }
+ my $buildID = $1 if $readelf =~ /Build ID: ([A-Fa-f0-9]+)/s;
+ if (defined $buildID && length $buildID > 0) {
+ my $symbolFile = '/usr/lib/debug/.build-id/' . substr($buildID, 0, 2) . '/' . substr($buildID, 2) . '.debug';
+ if (-e $symbolFile) {
+ if($main::opt_debug) { print STDERR "found debug symbol file $symbolFile for $file\n" };
+ return $symbolFile;
+ } else {
+ if($main::opt_debug) { print STDERR "no debug symbol file found for $file, build ID: $buildID\n" };
+ return undef;
+ }
+ }
+ }
+
+ if($main::opt_debug) { print STDERR "no debug symbols found for $file, build ID unknown\n" };
+ return undef;
+}
+
+
+# Parse text section header of a library using objdump
+sub ParseTextSectionHeaderFromObjdump {
+ my $lib = shift;
+
+ my $size = undef;
+ my $vma;
+ my $file_offset;
+ # Get objdump output from the library file to figure out how to
+ # map between mapped addresses and addresses in the library.
+ my $cmd = ShellEscape($obj_tool_map{"objdump"}, "-h", $lib);
+ open(OBJDUMP, "$cmd |") || error("$cmd: $!\n");
+ while (<OBJDUMP>) {
+ s/\r//g; # turn windows-looking lines into unix-looking lines
+ # Idx Name Size VMA LMA File off Algn
+ # 10 .text 00104b2c 420156f0 420156f0 000156f0 2**4
+ # For 64-bit objects, VMA and LMA will be 16 hex digits, size and file
+ # offset may still be 8. But AddressSub below will still handle that.
+ my @x = split;
+ if (($#x >= 6) && ($x[1] eq '.text')) {
+ $size = $x[2];
+ $vma = $x[3];
+ $file_offset = $x[5];
+ last;
+ }
+ }
+ close(OBJDUMP);
+
+ if (!defined($size)) {
+ return undef;
+ }
+
+ my $r = {};
+ $r->{size} = $size;
+ $r->{vma} = $vma;
+ $r->{file_offset} = $file_offset;
+
+ return $r;
+}
+
+# Parse text section header of a library using otool (on OS X)
+sub ParseTextSectionHeaderFromOtool {
+ my $lib = shift;
+
+ my $size = undef;
+ my $vma = undef;
+ my $file_offset = undef;
+ # Get otool output from the library file to figure out how to
+ # map between mapped addresses and addresses in the library.
+ my $command = ShellEscape($obj_tool_map{"otool"}, "-l", $lib);
+ open(OTOOL, "$command |") || error("$command: $!\n");
+ my $cmd = "";
+ my $sectname = "";
+ my $segname = "";
+ foreach my $line (<OTOOL>) {
+ $line =~ s/\r//g; # turn windows-looking lines into unix-looking lines
+ # Load command <#>
+ # cmd LC_SEGMENT
+ # [...]
+ # Section
+ # sectname __text
+ # segname __TEXT
+ # addr 0x000009f8
+ # size 0x00018b9e
+ # offset 2552
+ # align 2^2 (4)
+ # We will need to strip off the leading 0x from the hex addresses,
+ # and convert the offset into hex.
+ if ($line =~ /Load command/) {
+ $cmd = "";
+ $sectname = "";
+ $segname = "";
+ } elsif ($line =~ /Section/) {
+ $sectname = "";
+ $segname = "";
+ } elsif ($line =~ /cmd (\w+)/) {
+ $cmd = $1;
+ } elsif ($line =~ /sectname (\w+)/) {
+ $sectname = $1;
+ } elsif ($line =~ /segname (\w+)/) {
+ $segname = $1;
+ } elsif (!(($cmd eq "LC_SEGMENT" || $cmd eq "LC_SEGMENT_64") &&
+ $sectname eq "__text" &&
+ $segname eq "__TEXT")) {
+ next;
+ } elsif ($line =~ /\baddr 0x([0-9a-fA-F]+)/) {
+ $vma = $1;
+ } elsif ($line =~ /\bsize 0x([0-9a-fA-F]+)/) {
+ $size = $1;
+ } elsif ($line =~ /\boffset ([0-9]+)/) {
+ $file_offset = sprintf("%016x", $1);
+ }
+ if (defined($vma) && defined($size) && defined($file_offset)) {
+ last;
+ }
+ }
+ close(OTOOL);
+
+ if (!defined($vma) || !defined($size) || !defined($file_offset)) {
+ return undef;
+ }
+
+ my $r = {};
+ $r->{size} = $size;
+ $r->{vma} = $vma;
+ $r->{file_offset} = $file_offset;
+
+ return $r;
+}
+
+sub ParseTextSectionHeader {
+ # obj_tool_map("otool") is only defined if we're in a Mach-O environment
+ if (defined($obj_tool_map{"otool"})) {
+ my $r = ParseTextSectionHeaderFromOtool(@_);
+ if (defined($r)){
+ return $r;
+ }
+ }
+ # If otool doesn't work, or we don't have it, fall back to objdump
+ return ParseTextSectionHeaderFromObjdump(@_);
+}
+
+# Split /proc/pid/maps dump into a list of libraries
+sub ParseLibraries {
+ return if $main::use_symbol_page; # We don't need libraries info.
+ my $prog = Cwd::abs_path(shift);
+ my $map = shift;
+ my $pcs = shift;
+
+ my $result = [];
+ my $h = "[a-f0-9]+";
+ my $zero_offset = HexExtend("0");
+
+ my $buildvar = "";
+ foreach my $l (split("\n", $map)) {
+ if ($l =~ m/^\s*build=(.*)$/) {
+ $buildvar = $1;
+ }
+
+ my $start;
+ my $finish;
+ my $offset;
+ my $lib;
+ if ($l =~ /^($h)-($h)\s+..x.\s+($h)\s+\S+:\S+\s+\d+\s+(\S+\.(so|dll|dylib|bundle)((\.\d+)+\w*(\.\d+){0,3})?)$/i) {
+ # Full line from /proc/self/maps. Example:
+ # 40000000-40015000 r-xp 00000000 03:01 12845071 /lib/ld-2.3.2.so
+ $start = HexExtend($1);
+ $finish = HexExtend($2);
+ $offset = HexExtend($3);
+ $lib = $4;
+ $lib =~ s|\\|/|g; # turn windows-style paths into unix-style paths
+ } elsif ($l =~ /^\s*($h)-($h):\s*(\S+\.so(\.\d+)*)/) {
+ # Cooked line from DumpAddressMap. Example:
+ # 40000000-40015000: /lib/ld-2.3.2.so
+ $start = HexExtend($1);
+ $finish = HexExtend($2);
+ $offset = $zero_offset;
+ $lib = $3;
+ } elsif (($l =~ /^($h)-($h)\s+..x.\s+($h)\s+\S+:\S+\s+\d+\s+(\S+)$/i) && ($4 eq $prog)) {
+ # PIEs and address space randomization do not play well with our
+ # default assumption that main executable is at lowest
+ # addresses. So we're detecting main executable in
+ # /proc/self/maps as well.
+ $start = HexExtend($1);
+ $finish = HexExtend($2);
+ $offset = HexExtend($3);
+ $lib = $4;
+ $lib =~ s|\\|/|g; # turn windows-style paths into unix-style paths
+ }
+ # FreeBSD 10.0 virtual memory map /proc/curproc/map as defined in
+ # function procfs_doprocmap (sys/fs/procfs/procfs_map.c)
+ #
+ # Example:
+ # 0x800600000 0x80061a000 26 0 0xfffff800035a0000 r-x 75 33 0x1004 COW NC vnode /libexec/ld-elf.s
+ # o.1 NCH -1
+ elsif ($l =~ /^(0x$h)\s(0x$h)\s\d+\s\d+\s0x$h\sr-x\s\d+\s\d+\s0x\d+\s(COW|NCO)\s(NC|NNC)\svnode\s(\S+\.so(\.\d+)*)/) {
+ $start = HexExtend($1);
+ $finish = HexExtend($2);
+ $offset = $zero_offset;
+ $lib = FindLibrary($5);
+
+ } else {
+ next;
+ }
+
+ # Expand "$build" variable if available
+ $lib =~ s/\$build\b/$buildvar/g;
+
+ $lib = FindLibrary($lib);
+
+ # Check for pre-relocated libraries, which use pre-relocated symbol tables
+ # and thus require adjusting the offset that we'll use to translate
+ # VM addresses into symbol table addresses.
+ # Only do this if we're not going to fetch the symbol table from a
+ # debugging copy of the library.
+ if (!DebuggingLibrary($lib)) {
+ my $text = ParseTextSectionHeader($lib);
+ if (defined($text)) {
+ my $vma_offset = AddressSub($text->{vma}, $text->{file_offset});
+ $offset = AddressAdd($offset, $vma_offset);
+ }
+ }
+
+ if($main::opt_debug) { printf STDERR "$start:$finish ($offset) $lib\n"; }
+ push(@{$result}, [$lib, $start, $finish, $offset]);
+ }
+
+ # Append special entry for additional library (not relocated)
+ if ($main::opt_lib ne "") {
+ my $text = ParseTextSectionHeader($main::opt_lib);
+ if (defined($text)) {
+ my $start = $text->{vma};
+ my $finish = AddressAdd($start, $text->{size});
+
+ push(@{$result}, [$main::opt_lib, $start, $finish, $start]);
+ }
+ }
+
+ # Append special entry for the main program. This covers
+ # 0..max_pc_value_seen, so that we assume pc values not found in one
+ # of the library ranges will be treated as coming from the main
+ # program binary.
+ my $min_pc = HexExtend("0");
+ my $max_pc = $min_pc; # find the maximal PC value in any sample
+ foreach my $pc (keys(%{$pcs})) {
+ if (HexExtend($pc) gt $max_pc) { $max_pc = HexExtend($pc); }
+ }
+ push(@{$result}, [$prog, $min_pc, $max_pc, $zero_offset]);
+
+ return $result;
+}
+
+# Add two hex addresses of length $address_length.
+# Run jeprof --test for unit test if this is changed.
+sub AddressAdd {
+ my $addr1 = shift;
+ my $addr2 = shift;
+ my $sum;
+
+ if ($address_length == 8) {
+ # Perl doesn't cope with wraparound arithmetic, so do it explicitly:
+ $sum = (hex($addr1)+hex($addr2)) % (0x10000000 * 16);
+ return sprintf("%08x", $sum);
+
+ } else {
+ # Do the addition in 7-nibble chunks to trivialize carry handling.
+
+ if ($main::opt_debug and $main::opt_test) {
+ print STDERR "AddressAdd $addr1 + $addr2 = ";
+ }
+
+ my $a1 = substr($addr1,-7);
+ $addr1 = substr($addr1,0,-7);
+ my $a2 = substr($addr2,-7);
+ $addr2 = substr($addr2,0,-7);
+ $sum = hex($a1) + hex($a2);
+ my $c = 0;
+ if ($sum > 0xfffffff) {
+ $c = 1;
+ $sum -= 0x10000000;
+ }
+ my $r = sprintf("%07x", $sum);
+
+ $a1 = substr($addr1,-7);
+ $addr1 = substr($addr1,0,-7);
+ $a2 = substr($addr2,-7);
+ $addr2 = substr($addr2,0,-7);
+ $sum = hex($a1) + hex($a2) + $c;
+ $c = 0;
+ if ($sum > 0xfffffff) {
+ $c = 1;
+ $sum -= 0x10000000;
+ }
+ $r = sprintf("%07x", $sum) . $r;
+
+ $sum = hex($addr1) + hex($addr2) + $c;
+ if ($sum > 0xff) { $sum -= 0x100; }
+ $r = sprintf("%02x", $sum) . $r;
+
+ if ($main::opt_debug and $main::opt_test) { print STDERR "$r\n"; }
+
+ return $r;
+ }
+}
+
+
+# Subtract two hex addresses of length $address_length.
+# Run jeprof --test for unit test if this is changed.
+sub AddressSub {
+ my $addr1 = shift;
+ my $addr2 = shift;
+ my $diff;
+
+ if ($address_length == 8) {
+ # Perl doesn't cope with wraparound arithmetic, so do it explicitly:
+ $diff = (hex($addr1)-hex($addr2)) % (0x10000000 * 16);
+ return sprintf("%08x", $diff);
+
+ } else {
+ # Do the addition in 7-nibble chunks to trivialize borrow handling.
+ # if ($main::opt_debug) { print STDERR "AddressSub $addr1 - $addr2 = "; }
+
+ my $a1 = hex(substr($addr1,-7));
+ $addr1 = substr($addr1,0,-7);
+ my $a2 = hex(substr($addr2,-7));
+ $addr2 = substr($addr2,0,-7);
+ my $b = 0;
+ if ($a2 > $a1) {
+ $b = 1;
+ $a1 += 0x10000000;
+ }
+ $diff = $a1 - $a2;
+ my $r = sprintf("%07x", $diff);
+
+ $a1 = hex(substr($addr1,-7));
+ $addr1 = substr($addr1,0,-7);
+ $a2 = hex(substr($addr2,-7)) + $b;
+ $addr2 = substr($addr2,0,-7);
+ $b = 0;
+ if ($a2 > $a1) {
+ $b = 1;
+ $a1 += 0x10000000;
+ }
+ $diff = $a1 - $a2;
+ $r = sprintf("%07x", $diff) . $r;
+
+ $a1 = hex($addr1);
+ $a2 = hex($addr2) + $b;
+ if ($a2 > $a1) { $a1 += 0x100; }
+ $diff = $a1 - $a2;
+ $r = sprintf("%02x", $diff) . $r;
+
+ # if ($main::opt_debug) { print STDERR "$r\n"; }
+
+ return $r;
+ }
+}
+
+# Increment a hex addresses of length $address_length.
+# Run jeprof --test for unit test if this is changed.
+sub AddressInc {
+ my $addr = shift;
+ my $sum;
+
+ if ($address_length == 8) {
+ # Perl doesn't cope with wraparound arithmetic, so do it explicitly:
+ $sum = (hex($addr)+1) % (0x10000000 * 16);
+ return sprintf("%08x", $sum);
+
+ } else {
+ # Do the addition in 7-nibble chunks to trivialize carry handling.
+ # We are always doing this to step through the addresses in a function,
+ # and will almost never overflow the first chunk, so we check for this
+ # case and exit early.
+
+ # if ($main::opt_debug) { print STDERR "AddressInc $addr1 = "; }
+
+ my $a1 = substr($addr,-7);
+ $addr = substr($addr,0,-7);
+ $sum = hex($a1) + 1;
+ my $r = sprintf("%07x", $sum);
+ if ($sum <= 0xfffffff) {
+ $r = $addr . $r;
+ # if ($main::opt_debug) { print STDERR "$r\n"; }
+ return HexExtend($r);
+ } else {
+ $r = "0000000";
+ }
+
+ $a1 = substr($addr,-7);
+ $addr = substr($addr,0,-7);
+ $sum = hex($a1) + 1;
+ $r = sprintf("%07x", $sum) . $r;
+ if ($sum <= 0xfffffff) {
+ $r = $addr . $r;
+ # if ($main::opt_debug) { print STDERR "$r\n"; }
+ return HexExtend($r);
+ } else {
+ $r = "00000000000000";
+ }
+
+ $sum = hex($addr) + 1;
+ if ($sum > 0xff) { $sum -= 0x100; }
+ $r = sprintf("%02x", $sum) . $r;
+
+ # if ($main::opt_debug) { print STDERR "$r\n"; }
+ return $r;
+ }
+}
+
+# Extract symbols for all PC values found in profile
+sub ExtractSymbols {
+ my $libs = shift;
+ my $pcset = shift;
+
+ my $symbols = {};
+
+ # Map each PC value to the containing library. To make this faster,
+ # we sort libraries by their starting pc value (highest first), and
+ # advance through the libraries as we advance the pc. Sometimes the
+ # addresses of libraries may overlap with the addresses of the main
+ # binary, so to make sure the libraries 'win', we iterate over the
+ # libraries in reverse order (which assumes the binary doesn't start
+ # in the middle of a library, which seems a fair assumption).
+ my @pcs = (sort { $a cmp $b } keys(%{$pcset})); # pcset is 0-extended strings
+ foreach my $lib (sort {$b->[1] cmp $a->[1]} @{$libs}) {
+ my $libname = $lib->[0];
+ my $start = $lib->[1];
+ my $finish = $lib->[2];
+ my $offset = $lib->[3];
+
+ # Use debug library if it exists
+ my $debug_libname = DebuggingLibrary($libname);
+ if ($debug_libname) {
+ $libname = $debug_libname;
+ }
+
+ # Get list of pcs that belong in this library.
+ my $contained = [];
+ my ($start_pc_index, $finish_pc_index);
+ # Find smallest finish_pc_index such that $finish < $pc[$finish_pc_index].
+ for ($finish_pc_index = $#pcs + 1; $finish_pc_index > 0;
+ $finish_pc_index--) {
+ last if $pcs[$finish_pc_index - 1] le $finish;
+ }
+ # Find smallest start_pc_index such that $start <= $pc[$start_pc_index].
+ for ($start_pc_index = $finish_pc_index; $start_pc_index > 0;
+ $start_pc_index--) {
+ last if $pcs[$start_pc_index - 1] lt $start;
+ }
+ # This keeps PC values higher than $pc[$finish_pc_index] in @pcs,
+ # in case there are overlaps in libraries and the main binary.
+ @{$contained} = splice(@pcs, $start_pc_index,
+ $finish_pc_index - $start_pc_index);
+ # Map to symbols
+ MapToSymbols($libname, AddressSub($start, $offset), $contained, $symbols);
+ }
+
+ return $symbols;
+}
+
+# Map list of PC values to symbols for a given image
+sub MapToSymbols {
+ my $image = shift;
+ my $offset = shift;
+ my $pclist = shift;
+ my $symbols = shift;
+
+ my $debug = 0;
+
+ # Ignore empty binaries
+ if ($#{$pclist} < 0) { return; }
+
+ # Figure out the addr2line command to use
+ my $addr2line = $obj_tool_map{"addr2line"};
+ my $cmd = ShellEscape($addr2line, "-f", "-C", "-e", $image);
+ if (exists $obj_tool_map{"addr2line_pdb"}) {
+ $addr2line = $obj_tool_map{"addr2line_pdb"};
+ $cmd = ShellEscape($addr2line, "--demangle", "-f", "-C", "-e", $image);
+ }
+
+ # If "addr2line" isn't installed on the system at all, just use
+ # nm to get what info we can (function names, but not line numbers).
+ if (system(ShellEscape($addr2line, "--help") . " >$dev_null 2>&1") != 0) {
+ MapSymbolsWithNM($image, $offset, $pclist, $symbols);
+ return;
+ }
+
+ # "addr2line -i" can produce a variable number of lines per input
+ # address, with no separator that allows us to tell when data for
+ # the next address starts. So we find the address for a special
+ # symbol (_fini) and interleave this address between all real
+ # addresses passed to addr2line. The name of this special symbol
+ # can then be used as a separator.
+ $sep_address = undef; # May be filled in by MapSymbolsWithNM()
+ my $nm_symbols = {};
+ MapSymbolsWithNM($image, $offset, $pclist, $nm_symbols);
+ if (defined($sep_address)) {
+ # Only add " -i" to addr2line if the binary supports it.
+ # addr2line --help returns 0, but not if it sees an unknown flag first.
+ if (system("$cmd -i --help >$dev_null 2>&1") == 0) {
+ $cmd .= " -i";
+ } else {
+ $sep_address = undef; # no need for sep_address if we don't support -i
+ }
+ }
+
+ # Make file with all PC values with intervening 'sep_address' so
+ # that we can reliably detect the end of inlined function list
+ open(ADDRESSES, ">$main::tmpfile_sym") || error("$main::tmpfile_sym: $!\n");
+ if ($debug) { print("---- $image ---\n"); }
+ for (my $i = 0; $i <= $#{$pclist}; $i++) {
+ # addr2line always reads hex addresses, and does not need '0x' prefix.
+ if ($debug) { printf STDERR ("%s\n", $pclist->[$i]); }
+ printf ADDRESSES ("%s\n", AddressSub($pclist->[$i], $offset));
+ if (defined($sep_address)) {
+ printf ADDRESSES ("%s\n", $sep_address);
+ }
+ }
+ close(ADDRESSES);
+ if ($debug) {
+ print("----\n");
+ system("cat", $main::tmpfile_sym);
+ print("----\n");
+ system("$cmd < " . ShellEscape($main::tmpfile_sym));
+ print("----\n");
+ }
+
+ open(SYMBOLS, "$cmd <" . ShellEscape($main::tmpfile_sym) . " |")
+ || error("$cmd: $!\n");
+ my $count = 0; # Index in pclist
+ while (<SYMBOLS>) {
+ # Read fullfunction and filelineinfo from next pair of lines
+ s/\r?\n$//g;
+ my $fullfunction = $_;
+ $_ = <SYMBOLS>;
+ s/\r?\n$//g;
+ my $filelinenum = $_;
+
+ if (defined($sep_address) && $fullfunction eq $sep_symbol) {
+ # Terminating marker for data for this address
+ $count++;
+ next;
+ }
+
+ $filelinenum =~ s|\\|/|g; # turn windows-style paths into unix-style paths
+
+ my $pcstr = $pclist->[$count];
+ my $function = ShortFunctionName($fullfunction);
+ my $nms = $nm_symbols->{$pcstr};
+ if (defined($nms)) {
+ if ($fullfunction eq '??') {
+ # nm found a symbol for us.
+ $function = $nms->[0];
+ $fullfunction = $nms->[2];
+ } else {
+ # MapSymbolsWithNM tags each routine with its starting address,
+ # useful in case the image has multiple occurrences of this
+ # routine. (It uses a syntax that resembles template parameters,
+ # that are automatically stripped out by ShortFunctionName().)
+ # addr2line does not provide the same information. So we check
+ # if nm disambiguated our symbol, and if so take the annotated
+ # (nm) version of the routine-name. TODO(csilvers): this won't
+ # catch overloaded, inlined symbols, which nm doesn't see.
+ # Better would be to do a check similar to nm's, in this fn.
+ if ($nms->[2] =~ m/^\Q$function\E/) { # sanity check it's the right fn
+ $function = $nms->[0];
+ $fullfunction = $nms->[2];
+ }
+ }
+ }
+
+ # Prepend to accumulated symbols for pcstr
+ # (so that caller comes before callee)
+ my $sym = $symbols->{$pcstr};
+ if (!defined($sym)) {
+ $sym = [];
+ $symbols->{$pcstr} = $sym;
+ }
+ unshift(@{$sym}, $function, $filelinenum, $fullfunction);
+ if ($debug) { printf STDERR ("%s => [%s]\n", $pcstr, join(" ", @{$sym})); }
+ if (!defined($sep_address)) {
+ # Inlining is off, so this entry ends immediately
+ $count++;
+ }
+ }
+ close(SYMBOLS);
+}
+
+# Use nm to map the list of referenced PCs to symbols. Return true iff we
+# are able to read procedure information via nm.
+sub MapSymbolsWithNM {
+ my $image = shift;
+ my $offset = shift;
+ my $pclist = shift;
+ my $symbols = shift;
+
+ # Get nm output sorted by increasing address
+ my $symbol_table = GetProcedureBoundaries($image, ".");
+ if (!%{$symbol_table}) {
+ return 0;
+ }
+ # Start addresses are already the right length (8 or 16 hex digits).
+ my @names = sort { $symbol_table->{$a}->[0] cmp $symbol_table->{$b}->[0] }
+ keys(%{$symbol_table});
+
+ if ($#names < 0) {
+ # No symbols: just use addresses
+ foreach my $pc (@{$pclist}) {
+ my $pcstr = "0x" . $pc;
+ $symbols->{$pc} = [$pcstr, "?", $pcstr];
+ }
+ return 0;
+ }
+
+ # Sort addresses so we can do a join against nm output
+ my $index = 0;
+ my $fullname = $names[0];
+ my $name = ShortFunctionName($fullname);
+ foreach my $pc (sort { $a cmp $b } @{$pclist}) {
+ # Adjust for mapped offset
+ my $mpc = AddressSub($pc, $offset);
+ while (($index < $#names) && ($mpc ge $symbol_table->{$fullname}->[1])){
+ $index++;
+ $fullname = $names[$index];
+ $name = ShortFunctionName($fullname);
+ }
+ if ($mpc lt $symbol_table->{$fullname}->[1]) {
+ $symbols->{$pc} = [$name, "?", $fullname];
+ } else {
+ my $pcstr = "0x" . $pc;
+ $symbols->{$pc} = [$pcstr, "?", $pcstr];
+ }
+ }
+ return 1;
+}
+
+sub ShortFunctionName {
+ my $function = shift;
+ while ($function =~ s/\([^()]*\)(\s*const)?//g) { } # Argument types
+ while ($function =~ s/<[^<>]*>//g) { } # Remove template arguments
+ $function =~ s/^.*\s+(\w+::)/$1/; # Remove leading type
+ return $function;
+}
+
+# Trim overly long symbols found in disassembler output
+sub CleanDisassembly {
+ my $d = shift;
+ while ($d =~ s/\([^()%]*\)(\s*const)?//g) { } # Argument types, not (%rax)
+ while ($d =~ s/(\w+)<[^<>]*>/$1/g) { } # Remove template arguments
+ return $d;
+}
+
+# Clean file name for display
+sub CleanFileName {
+ my ($f) = @_;
+ $f =~ s|^/proc/self/cwd/||;
+ $f =~ s|^\./||;
+ return $f;
+}
+
+# Make address relative to section and clean up for display
+sub UnparseAddress {
+ my ($offset, $address) = @_;
+ $address = AddressSub($address, $offset);
+ $address =~ s/^0x//;
+ $address =~ s/^0*//;
+ return $address;
+}
+
+##### Miscellaneous #####
+
+# Find the right versions of the above object tools to use. The
+# argument is the program file being analyzed, and should be an ELF
+# 32-bit or ELF 64-bit executable file. The location of the tools
+# is determined by considering the following options in this order:
+# 1) --tools option, if set
+# 2) JEPROF_TOOLS environment variable, if set
+# 3) the environment
+sub ConfigureObjTools {
+ my $prog_file = shift;
+
+ # Check for the existence of $prog_file because /usr/bin/file does not
+ # predictably return error status in prod.
+ (-e $prog_file) || error("$prog_file does not exist.\n");
+
+ my $file_type = undef;
+ if (-e "/usr/bin/file") {
+ # Follow symlinks (at least for systems where "file" supports that).
+ my $escaped_prog_file = ShellEscape($prog_file);
+ $file_type = `/usr/bin/file -L $escaped_prog_file 2>$dev_null ||
+ /usr/bin/file $escaped_prog_file`;
+ } elsif ($^O == "MSWin32") {
+ $file_type = "MS Windows";
+ } else {
+ print STDERR "WARNING: Can't determine the file type of $prog_file";
+ }
+
+ if ($file_type =~ /64-bit/) {
+ # Change $address_length to 16 if the program file is ELF 64-bit.
+ # We can't detect this from many (most?) heap or lock contention
+ # profiles, since the actual addresses referenced are generally in low
+ # memory even for 64-bit programs.
+ $address_length = 16;
+ }
+
+ if ($file_type =~ /MS Windows/) {
+ # For windows, we provide a version of nm and addr2line as part of
+ # the opensource release, which is capable of parsing
+ # Windows-style PDB executables. It should live in the path, or
+ # in the same directory as jeprof.
+ $obj_tool_map{"nm_pdb"} = "nm-pdb";
+ $obj_tool_map{"addr2line_pdb"} = "addr2line-pdb";
+ }
+
+ if ($file_type =~ /Mach-O/) {
+ # OS X uses otool to examine Mach-O files, rather than objdump.
+ $obj_tool_map{"otool"} = "otool";
+ $obj_tool_map{"addr2line"} = "false"; # no addr2line
+ $obj_tool_map{"objdump"} = "false"; # no objdump
+ }
+
+ # Go fill in %obj_tool_map with the pathnames to use:
+ foreach my $tool (keys %obj_tool_map) {
+ $obj_tool_map{$tool} = ConfigureTool($obj_tool_map{$tool});
+ }
+}
+
+# Returns the path of a caller-specified object tool. If --tools or
+# JEPROF_TOOLS are specified, then returns the full path to the tool
+# with that prefix. Otherwise, returns the path unmodified (which
+# means we will look for it on PATH).
+sub ConfigureTool {
+ my $tool = shift;
+ my $path;
+
+ # --tools (or $JEPROF_TOOLS) is a comma separated list, where each
+ # item is either a) a pathname prefix, or b) a map of the form
+ # <tool>:<path>. First we look for an entry of type (b) for our
+ # tool. If one is found, we use it. Otherwise, we consider all the
+ # pathname prefixes in turn, until one yields an existing file. If
+ # none does, we use a default path.
+ my $tools = $main::opt_tools || $ENV{"JEPROF_TOOLS"} || "";
+ if ($tools =~ m/(,|^)\Q$tool\E:([^,]*)/) {
+ $path = $2;
+ # TODO(csilvers): sanity-check that $path exists? Hard if it's relative.
+ } elsif ($tools ne '') {
+ foreach my $prefix (split(',', $tools)) {
+ next if ($prefix =~ /:/); # ignore "tool:fullpath" entries in the list
+ if (-x $prefix . $tool) {
+ $path = $prefix . $tool;
+ last;
+ }
+ }
+ if (!$path) {
+ error("No '$tool' found with prefix specified by " .
+ "--tools (or \$JEPROF_TOOLS) '$tools'\n");
+ }
+ } else {
+ # ... otherwise use the version that exists in the same directory as
+ # jeprof. If there's nothing there, use $PATH.
+ $0 =~ m,[^/]*$,; # this is everything after the last slash
+ my $dirname = $`; # this is everything up to and including the last slash
+ if (-x "$dirname$tool") {
+ $path = "$dirname$tool";
+ } else {
+ $path = $tool;
+ }
+ }
+ if ($main::opt_debug) { print STDERR "Using '$path' for '$tool'.\n"; }
+ return $path;
+}
+
+sub ShellEscape {
+ my @escaped_words = ();
+ foreach my $word (@_) {
+ my $escaped_word = $word;
+ if ($word =~ m![^a-zA-Z0-9/.,_=-]!) { # check for anything not in whitelist
+ $escaped_word =~ s/'/'\\''/;
+ $escaped_word = "'$escaped_word'";
+ }
+ push(@escaped_words, $escaped_word);
+ }
+ return join(" ", @escaped_words);
+}
+
+sub cleanup {
+ unlink($main::tmpfile_sym);
+ unlink(keys %main::tempnames);
+
+ # We leave any collected profiles in $HOME/jeprof in case the user wants
+ # to look at them later. We print a message informing them of this.
+ if ((scalar(@main::profile_files) > 0) &&
+ defined($main::collected_profile)) {
+ if (scalar(@main::profile_files) == 1) {
+ print STDERR "Dynamically gathered profile is in $main::collected_profile\n";
+ }
+ print STDERR "If you want to investigate this profile further, you can do:\n";
+ print STDERR "\n";
+ print STDERR " jeprof \\\n";
+ print STDERR " $main::prog \\\n";
+ print STDERR " $main::collected_profile\n";
+ print STDERR "\n";
+ }
+}
+
+sub sighandler {
+ cleanup();
+ exit(1);
+}
+
+sub error {
+ my $msg = shift;
+ print STDERR $msg;
+ cleanup();
+ exit(1);
+}
+
+
+# Run $nm_command and get all the resulting procedure boundaries whose
+# names match "$regexp" and returns them in a hashtable mapping from
+# procedure name to a two-element vector of [start address, end address]
+sub GetProcedureBoundariesViaNm {
+ my $escaped_nm_command = shift; # shell-escaped
+ my $regexp = shift;
+
+ my $symbol_table = {};
+ open(NM, "$escaped_nm_command |") || error("$escaped_nm_command: $!\n");
+ my $last_start = "0";
+ my $routine = "";
+ while (<NM>) {
+ s/\r//g; # turn windows-looking lines into unix-looking lines
+ if (m/^\s*([0-9a-f]+) (.) (..*)/) {
+ my $start_val = $1;
+ my $type = $2;
+ my $this_routine = $3;
+
+ # It's possible for two symbols to share the same address, if
+ # one is a zero-length variable (like __start_google_malloc) or
+ # one symbol is a weak alias to another (like __libc_malloc).
+ # In such cases, we want to ignore all values except for the
+ # actual symbol, which in nm-speak has type "T". The logic
+ # below does this, though it's a bit tricky: what happens when
+ # we have a series of lines with the same address, is the first
+ # one gets queued up to be processed. However, it won't
+ # *actually* be processed until later, when we read a line with
+ # a different address. That means that as long as we're reading
+ # lines with the same address, we have a chance to replace that
+ # item in the queue, which we do whenever we see a 'T' entry --
+ # that is, a line with type 'T'. If we never see a 'T' entry,
+ # we'll just go ahead and process the first entry (which never
+ # got touched in the queue), and ignore the others.
+ if ($start_val eq $last_start && $type =~ /t/i) {
+ # We are the 'T' symbol at this address, replace previous symbol.
+ $routine = $this_routine;
+ next;
+ } elsif ($start_val eq $last_start) {
+ # We're not the 'T' symbol at this address, so ignore us.
+ next;
+ }
+
+ if ($this_routine eq $sep_symbol) {
+ $sep_address = HexExtend($start_val);
+ }
+
+ # Tag this routine with the starting address in case the image
+ # has multiple occurrences of this routine. We use a syntax
+ # that resembles template parameters that are automatically
+ # stripped out by ShortFunctionName()
+ $this_routine .= "<$start_val>";
+
+ if (defined($routine) && $routine =~ m/$regexp/) {
+ $symbol_table->{$routine} = [HexExtend($last_start),
+ HexExtend($start_val)];
+ }
+ $last_start = $start_val;
+ $routine = $this_routine;
+ } elsif (m/^Loaded image name: (.+)/) {
+ # The win32 nm workalike emits information about the binary it is using.
+ if ($main::opt_debug) { print STDERR "Using Image $1\n"; }
+ } elsif (m/^PDB file name: (.+)/) {
+ # The win32 nm workalike emits information about the pdb it is using.
+ if ($main::opt_debug) { print STDERR "Using PDB $1\n"; }
+ }
+ }
+ close(NM);
+ # Handle the last line in the nm output. Unfortunately, we don't know
+ # how big this last symbol is, because we don't know how big the file
+ # is. For now, we just give it a size of 0.
+ # TODO(csilvers): do better here.
+ if (defined($routine) && $routine =~ m/$regexp/) {
+ $symbol_table->{$routine} = [HexExtend($last_start),
+ HexExtend($last_start)];
+ }
+ return $symbol_table;
+}
+
+# Gets the procedure boundaries for all routines in "$image" whose names
+# match "$regexp" and returns them in a hashtable mapping from procedure
+# name to a two-element vector of [start address, end address].
+# Will return an empty map if nm is not installed or not working properly.
+sub GetProcedureBoundaries {
+ my $image = shift;
+ my $regexp = shift;
+
+ # If $image doesn't start with /, then put ./ in front of it. This works
+ # around an obnoxious bug in our probing of nm -f behavior.
+ # "nm -f $image" is supposed to fail on GNU nm, but if:
+ #
+ # a. $image starts with [BbSsPp] (for example, bin/foo/bar), AND
+ # b. you have a.out in your current directory (a not uncommon occurrence)
+ #
+ # then "nm -f $image" succeeds because -f only looks at the first letter of
+ # the argument, which looks valid because it's [BbSsPp], and then since
+ # there's no image provided, it looks for a.out and finds it.
+ #
+ # This regex makes sure that $image starts with . or /, forcing the -f
+ # parsing to fail since . and / are not valid formats.
+ $image =~ s#^[^/]#./$&#;
+
+ # For libc libraries, the copy in /usr/lib/debug contains debugging symbols
+ my $debugging = DebuggingLibrary($image);
+ if ($debugging) {
+ $image = $debugging;
+ }
+
+ my $nm = $obj_tool_map{"nm"};
+ my $cppfilt = $obj_tool_map{"c++filt"};
+
+ # nm can fail for two reasons: 1) $image isn't a debug library; 2) nm
+ # binary doesn't support --demangle. In addition, for OS X we need
+ # to use the -f flag to get 'flat' nm output (otherwise we don't sort
+ # properly and get incorrect results). Unfortunately, GNU nm uses -f
+ # in an incompatible way. So first we test whether our nm supports
+ # --demangle and -f.
+ my $demangle_flag = "";
+ my $cppfilt_flag = "";
+ my $to_devnull = ">$dev_null 2>&1";
+ if (system(ShellEscape($nm, "--demangle", $image) . $to_devnull) == 0) {
+ # In this mode, we do "nm --demangle <foo>"
+ $demangle_flag = "--demangle";
+ $cppfilt_flag = "";
+ } elsif (system(ShellEscape($cppfilt, $image) . $to_devnull) == 0) {
+ # In this mode, we do "nm <foo> | c++filt"
+ $cppfilt_flag = " | " . ShellEscape($cppfilt);
+ };
+ my $flatten_flag = "";
+ if (system(ShellEscape($nm, "-f", $image) . $to_devnull) == 0) {
+ $flatten_flag = "-f";
+ }
+
+ # Finally, in the case $imagie isn't a debug library, we try again with
+ # -D to at least get *exported* symbols. If we can't use --demangle,
+ # we use c++filt instead, if it exists on this system.
+ my @nm_commands = (ShellEscape($nm, "-n", $flatten_flag, $demangle_flag,
+ $image) . " 2>$dev_null $cppfilt_flag",
+ ShellEscape($nm, "-D", "-n", $flatten_flag, $demangle_flag,
+ $image) . " 2>$dev_null $cppfilt_flag",
+ # 6nm is for Go binaries
+ ShellEscape("6nm", "$image") . " 2>$dev_null | sort",
+ );
+
+ # If the executable is an MS Windows PDB-format executable, we'll
+ # have set up obj_tool_map("nm_pdb"). In this case, we actually
+ # want to use both unix nm and windows-specific nm_pdb, since
+ # PDB-format executables can apparently include dwarf .o files.
+ if (exists $obj_tool_map{"nm_pdb"}) {
+ push(@nm_commands,
+ ShellEscape($obj_tool_map{"nm_pdb"}, "--demangle", $image)
+ . " 2>$dev_null");
+ }
+
+ foreach my $nm_command (@nm_commands) {
+ my $symbol_table = GetProcedureBoundariesViaNm($nm_command, $regexp);
+ return $symbol_table if (%{$symbol_table});
+ }
+ my $symbol_table = {};
+ return $symbol_table;
+}
+
+
+# The test vectors for AddressAdd/Sub/Inc are 8-16-nibble hex strings.
+# To make them more readable, we add underscores at interesting places.
+# This routine removes the underscores, producing the canonical representation
+# used by jeprof to represent addresses, particularly in the tested routines.
+sub CanonicalHex {
+ my $arg = shift;
+ return join '', (split '_',$arg);
+}
+
+
+# Unit test for AddressAdd:
+sub AddressAddUnitTest {
+ my $test_data_8 = shift;
+ my $test_data_16 = shift;
+ my $error_count = 0;
+ my $fail_count = 0;
+ my $pass_count = 0;
+ # print STDERR "AddressAddUnitTest: ", 1+$#{$test_data_8}, " tests\n";
+
+ # First a few 8-nibble addresses. Note that this implementation uses
+ # plain old arithmetic, so a quick sanity check along with verifying what
+ # happens to overflow (we want it to wrap):
+ $address_length = 8;
+ foreach my $row (@{$test_data_8}) {
+ if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; }
+ my $sum = AddressAdd ($row->[0], $row->[1]);
+ if ($sum ne $row->[2]) {
+ printf STDERR "ERROR: %s != %s + %s = %s\n", $sum,
+ $row->[0], $row->[1], $row->[2];
+ ++$fail_count;
+ } else {
+ ++$pass_count;
+ }
+ }
+ printf STDERR "AddressAdd 32-bit tests: %d passes, %d failures\n",
+ $pass_count, $fail_count;
+ $error_count = $fail_count;
+ $fail_count = 0;
+ $pass_count = 0;
+
+ # Now 16-nibble addresses.
+ $address_length = 16;
+ foreach my $row (@{$test_data_16}) {
+ if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; }
+ my $sum = AddressAdd (CanonicalHex($row->[0]), CanonicalHex($row->[1]));
+ my $expected = join '', (split '_',$row->[2]);
+ if ($sum ne CanonicalHex($row->[2])) {
+ printf STDERR "ERROR: %s != %s + %s = %s\n", $sum,
+ $row->[0], $row->[1], $row->[2];
+ ++$fail_count;
+ } else {
+ ++$pass_count;
+ }
+ }
+ printf STDERR "AddressAdd 64-bit tests: %d passes, %d failures\n",
+ $pass_count, $fail_count;
+ $error_count += $fail_count;
+
+ return $error_count;
+}
+
+
+# Unit test for AddressSub:
+sub AddressSubUnitTest {
+ my $test_data_8 = shift;
+ my $test_data_16 = shift;
+ my $error_count = 0;
+ my $fail_count = 0;
+ my $pass_count = 0;
+ # print STDERR "AddressSubUnitTest: ", 1+$#{$test_data_8}, " tests\n";
+
+ # First a few 8-nibble addresses. Note that this implementation uses
+ # plain old arithmetic, so a quick sanity check along with verifying what
+ # happens to overflow (we want it to wrap):
+ $address_length = 8;
+ foreach my $row (@{$test_data_8}) {
+ if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; }
+ my $sum = AddressSub ($row->[0], $row->[1]);
+ if ($sum ne $row->[3]) {
+ printf STDERR "ERROR: %s != %s - %s = %s\n", $sum,
+ $row->[0], $row->[1], $row->[3];
+ ++$fail_count;
+ } else {
+ ++$pass_count;
+ }
+ }
+ printf STDERR "AddressSub 32-bit tests: %d passes, %d failures\n",
+ $pass_count, $fail_count;
+ $error_count = $fail_count;
+ $fail_count = 0;
+ $pass_count = 0;
+
+ # Now 16-nibble addresses.
+ $address_length = 16;
+ foreach my $row (@{$test_data_16}) {
+ if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; }
+ my $sum = AddressSub (CanonicalHex($row->[0]), CanonicalHex($row->[1]));
+ if ($sum ne CanonicalHex($row->[3])) {
+ printf STDERR "ERROR: %s != %s - %s = %s\n", $sum,
+ $row->[0], $row->[1], $row->[3];
+ ++$fail_count;
+ } else {
+ ++$pass_count;
+ }
+ }
+ printf STDERR "AddressSub 64-bit tests: %d passes, %d failures\n",
+ $pass_count, $fail_count;
+ $error_count += $fail_count;
+
+ return $error_count;
+}
+
+
+# Unit test for AddressInc:
+sub AddressIncUnitTest {
+ my $test_data_8 = shift;
+ my $test_data_16 = shift;
+ my $error_count = 0;
+ my $fail_count = 0;
+ my $pass_count = 0;
+ # print STDERR "AddressIncUnitTest: ", 1+$#{$test_data_8}, " tests\n";
+
+ # First a few 8-nibble addresses. Note that this implementation uses
+ # plain old arithmetic, so a quick sanity check along with verifying what
+ # happens to overflow (we want it to wrap):
+ $address_length = 8;
+ foreach my $row (@{$test_data_8}) {
+ if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; }
+ my $sum = AddressInc ($row->[0]);
+ if ($sum ne $row->[4]) {
+ printf STDERR "ERROR: %s != %s + 1 = %s\n", $sum,
+ $row->[0], $row->[4];
+ ++$fail_count;
+ } else {
+ ++$pass_count;
+ }
+ }
+ printf STDERR "AddressInc 32-bit tests: %d passes, %d failures\n",
+ $pass_count, $fail_count;
+ $error_count = $fail_count;
+ $fail_count = 0;
+ $pass_count = 0;
+
+ # Now 16-nibble addresses.
+ $address_length = 16;
+ foreach my $row (@{$test_data_16}) {
+ if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; }
+ my $sum = AddressInc (CanonicalHex($row->[0]));
+ if ($sum ne CanonicalHex($row->[4])) {
+ printf STDERR "ERROR: %s != %s + 1 = %s\n", $sum,
+ $row->[0], $row->[4];
+ ++$fail_count;
+ } else {
+ ++$pass_count;
+ }
+ }
+ printf STDERR "AddressInc 64-bit tests: %d passes, %d failures\n",
+ $pass_count, $fail_count;
+ $error_count += $fail_count;
+
+ return $error_count;
+}
+
+
+# Driver for unit tests.
+# Currently just the address add/subtract/increment routines for 64-bit.
+sub RunUnitTests {
+ my $error_count = 0;
+
+ # This is a list of tuples [a, b, a+b, a-b, a+1]
+ my $unit_test_data_8 = [
+ [qw(aaaaaaaa 50505050 fafafafa 5a5a5a5a aaaaaaab)],
+ [qw(50505050 aaaaaaaa fafafafa a5a5a5a6 50505051)],
+ [qw(ffffffff aaaaaaaa aaaaaaa9 55555555 00000000)],
+ [qw(00000001 ffffffff 00000000 00000002 00000002)],
+ [qw(00000001 fffffff0 fffffff1 00000011 00000002)],
+ ];
+ my $unit_test_data_16 = [
+ # The implementation handles data in 7-nibble chunks, so those are the
+ # interesting boundaries.
+ [qw(aaaaaaaa 50505050
+ 00_000000f_afafafa 00_0000005_a5a5a5a 00_000000a_aaaaaab)],
+ [qw(50505050 aaaaaaaa
+ 00_000000f_afafafa ff_ffffffa_5a5a5a6 00_0000005_0505051)],
+ [qw(ffffffff aaaaaaaa
+ 00_000001a_aaaaaa9 00_0000005_5555555 00_0000010_0000000)],
+ [qw(00000001 ffffffff
+ 00_0000010_0000000 ff_ffffff0_0000002 00_0000000_0000002)],
+ [qw(00000001 fffffff0
+ 00_000000f_ffffff1 ff_ffffff0_0000011 00_0000000_0000002)],
+
+ [qw(00_a00000a_aaaaaaa 50505050
+ 00_a00000f_afafafa 00_a000005_a5a5a5a 00_a00000a_aaaaaab)],
+ [qw(0f_fff0005_0505050 aaaaaaaa
+ 0f_fff000f_afafafa 0f_ffefffa_5a5a5a6 0f_fff0005_0505051)],
+ [qw(00_000000f_fffffff 01_800000a_aaaaaaa
+ 01_800001a_aaaaaa9 fe_8000005_5555555 00_0000010_0000000)],
+ [qw(00_0000000_0000001 ff_fffffff_fffffff
+ 00_0000000_0000000 00_0000000_0000002 00_0000000_0000002)],
+ [qw(00_0000000_0000001 ff_fffffff_ffffff0
+ ff_fffffff_ffffff1 00_0000000_0000011 00_0000000_0000002)],
+ ];
+
+ $error_count += AddressAddUnitTest($unit_test_data_8, $unit_test_data_16);
+ $error_count += AddressSubUnitTest($unit_test_data_8, $unit_test_data_16);
+ $error_count += AddressIncUnitTest($unit_test_data_8, $unit_test_data_16);
+ if ($error_count > 0) {
+ print STDERR $error_count, " errors: FAILED\n";
+ } else {
+ print STDERR "PASS\n";
+ }
+ exit ($error_count);
+}
diff --git a/contrib/jemalloc/build-aux/config.guess b/contrib/jemalloc/build-aux/config.guess
new file mode 100755
index 000000000000..f7727026b706
--- /dev/null
+++ b/contrib/jemalloc/build-aux/config.guess
@@ -0,0 +1,1701 @@
+#! /bin/sh
+# Attempt to guess a canonical system name.
+# Copyright 1992-2021 Free Software Foundation, Inc.
+
+timestamp='2021-01-01'
+
+# This file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses/>.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that
+# program. This Exception is an additional permission under section 7
+# of the GNU General Public License, version 3 ("GPLv3").
+#
+# Originally written by Per Bothner; maintained since 2000 by Ben Elliston.
+#
+# You can get the latest version of this script from:
+# https://git.savannah.gnu.org/cgit/config.git/plain/config.guess
+#
+# Please send patches to <config-patches@gnu.org>.
+
+
+me=$(echo "$0" | sed -e 's,.*/,,')
+
+usage="\
+Usage: $0 [OPTION]
+
+Output the configuration name of the system \`$me' is run on.
+
+Options:
+ -h, --help print this help, then exit
+ -t, --time-stamp print date of last modification, then exit
+ -v, --version print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.guess ($timestamp)
+
+Originally written by Per Bothner.
+Copyright 1992-2021 Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+ case $1 in
+ --time-stamp | --time* | -t )
+ echo "$timestamp" ; exit ;;
+ --version | -v )
+ echo "$version" ; exit ;;
+ --help | --h* | -h )
+ echo "$usage"; exit ;;
+ -- ) # Stop option processing
+ shift; break ;;
+ - ) # Use stdin as input.
+ break ;;
+ -* )
+ echo "$me: invalid option $1$help" >&2
+ exit 1 ;;
+ * )
+ break ;;
+ esac
+done
+
+if test $# != 0; then
+ echo "$me: too many arguments$help" >&2
+ exit 1
+fi
+
+# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
+# compiler to aid in system detection is discouraged as it requires
+# temporary files to be created and, as you can see below, it is a
+# headache to deal with in a portable fashion.
+
+# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still
+# use `HOST_CC' if defined, but it is deprecated.
+
+# Portable tmp directory creation inspired by the Autoconf team.
+
+tmp=
+# shellcheck disable=SC2172
+trap 'test -z "$tmp" || rm -fr "$tmp"' 0 1 2 13 15
+
+set_cc_for_build() {
+ # prevent multiple calls if $tmp is already set
+ test "$tmp" && return 0
+ : "${TMPDIR=/tmp}"
+ # shellcheck disable=SC2039
+ { tmp=$( (umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null) && test -n "$tmp" && test -d "$tmp" ; } ||
+ { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir "$tmp" 2>/dev/null) ; } ||
+ { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir "$tmp" 2>/dev/null) && echo "Warning: creating insecure temp directory" >&2 ; } ||
+ { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; }
+ dummy=$tmp/dummy
+ case ${CC_FOR_BUILD-},${HOST_CC-},${CC-} in
+ ,,) echo "int x;" > "$dummy.c"
+ for driver in cc gcc c89 c99 ; do
+ if ($driver -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then
+ CC_FOR_BUILD="$driver"
+ break
+ fi
+ done
+ if test x"$CC_FOR_BUILD" = x ; then
+ CC_FOR_BUILD=no_compiler_found
+ fi
+ ;;
+ ,,*) CC_FOR_BUILD=$CC ;;
+ ,*,*) CC_FOR_BUILD=$HOST_CC ;;
+ esac
+}
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 1994-08-24)
+if test -f /.attbin/uname ; then
+ PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=$( (uname -m) 2>/dev/null) || UNAME_MACHINE=unknown
+UNAME_RELEASE=$( (uname -r) 2>/dev/null) || UNAME_RELEASE=unknown
+UNAME_SYSTEM=$( (uname -s) 2>/dev/null) || UNAME_SYSTEM=unknown
+UNAME_VERSION=$( (uname -v) 2>/dev/null) || UNAME_VERSION=unknown
+
+case "$UNAME_SYSTEM" in
+Linux|GNU|GNU/*)
+ LIBC=unknown
+
+ set_cc_for_build
+ cat <<-EOF > "$dummy.c"
+ #include <features.h>
+ #if defined(__UCLIBC__)
+ LIBC=uclibc
+ #elif defined(__dietlibc__)
+ LIBC=dietlibc
+ #elif defined(__GLIBC__)
+ LIBC=gnu
+ #else
+ #include <stdarg.h>
+ /* First heuristic to detect musl libc. */
+ #ifdef __DEFINED_va_list
+ LIBC=musl
+ #endif
+ #endif
+ EOF
+ eval "$($CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g')"
+
+ # Second heuristic to detect musl libc.
+ if [ "$LIBC" = unknown ] &&
+ command -v ldd >/dev/null &&
+ ldd --version 2>&1 | grep -q ^musl; then
+ LIBC=musl
+ fi
+
+ # If the system lacks a compiler, then just pick glibc.
+ # We could probably try harder.
+ if [ "$LIBC" = unknown ]; then
+ LIBC=gnu
+ fi
+ ;;
+esac
+
+# Note: order is significant - the case branches are not exclusive.
+
+case "$UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION" in
+ *:NetBSD:*:*)
+ # NetBSD (nbsd) targets should (where applicable) match one or
+ # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*,
+ # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently
+ # switched to ELF, *-*-netbsd* would select the old
+ # object file format. This provides both forward
+ # compatibility and a consistent mechanism for selecting the
+ # object file format.
+ #
+ # Note: NetBSD doesn't particularly care about the vendor
+ # portion of the name. We always set it to "unknown".
+ sysctl="sysctl -n hw.machine_arch"
+ UNAME_MACHINE_ARCH=$( (uname -p 2>/dev/null || \
+ "/sbin/$sysctl" 2>/dev/null || \
+ "/usr/sbin/$sysctl" 2>/dev/null || \
+ echo unknown))
+ case "$UNAME_MACHINE_ARCH" in
+ aarch64eb) machine=aarch64_be-unknown ;;
+ armeb) machine=armeb-unknown ;;
+ arm*) machine=arm-unknown ;;
+ sh3el) machine=shl-unknown ;;
+ sh3eb) machine=sh-unknown ;;
+ sh5el) machine=sh5le-unknown ;;
+ earmv*)
+ arch=$(echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,')
+ endian=$(echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p')
+ machine="${arch}${endian}"-unknown
+ ;;
+ *) machine="$UNAME_MACHINE_ARCH"-unknown ;;
+ esac
+ # The Operating System including object format, if it has switched
+ # to ELF recently (or will in the future) and ABI.
+ case "$UNAME_MACHINE_ARCH" in
+ earm*)
+ os=netbsdelf
+ ;;
+ arm*|i386|m68k|ns32k|sh3*|sparc|vax)
+ set_cc_for_build
+ if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep -q __ELF__
+ then
+ # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
+ # Return netbsd for either. FIX?
+ os=netbsd
+ else
+ os=netbsdelf
+ fi
+ ;;
+ *)
+ os=netbsd
+ ;;
+ esac
+ # Determine ABI tags.
+ case "$UNAME_MACHINE_ARCH" in
+ earm*)
+ expr='s/^earmv[0-9]/-eabi/;s/eb$//'
+ abi=$(echo "$UNAME_MACHINE_ARCH" | sed -e "$expr")
+ ;;
+ esac
+ # The OS release
+ # Debian GNU/NetBSD machines have a different userland, and
+ # thus, need a distinct triplet. However, they do not need
+ # kernel version information, so it can be replaced with a
+ # suitable tag, in the style of linux-gnu.
+ case "$UNAME_VERSION" in
+ Debian*)
+ release='-gnu'
+ ;;
+ *)
+ release=$(echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2)
+ ;;
+ esac
+ # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
+ # contains redundant information, the shorter form:
+ # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
+ echo "$machine-${os}${release}${abi-}"
+ exit ;;
+ *:Bitrig:*:*)
+ UNAME_MACHINE_ARCH=$(arch | sed 's/Bitrig.//')
+ echo "$UNAME_MACHINE_ARCH"-unknown-bitrig"$UNAME_RELEASE"
+ exit ;;
+ *:OpenBSD:*:*)
+ UNAME_MACHINE_ARCH=$(arch | sed 's/OpenBSD.//')
+ echo "$UNAME_MACHINE_ARCH"-unknown-openbsd"$UNAME_RELEASE"
+ exit ;;
+ *:LibertyBSD:*:*)
+ UNAME_MACHINE_ARCH=$(arch | sed 's/^.*BSD\.//')
+ echo "$UNAME_MACHINE_ARCH"-unknown-libertybsd"$UNAME_RELEASE"
+ exit ;;
+ *:MidnightBSD:*:*)
+ echo "$UNAME_MACHINE"-unknown-midnightbsd"$UNAME_RELEASE"
+ exit ;;
+ *:ekkoBSD:*:*)
+ echo "$UNAME_MACHINE"-unknown-ekkobsd"$UNAME_RELEASE"
+ exit ;;
+ *:SolidBSD:*:*)
+ echo "$UNAME_MACHINE"-unknown-solidbsd"$UNAME_RELEASE"
+ exit ;;
+ *:OS108:*:*)
+ echo "$UNAME_MACHINE"-unknown-os108_"$UNAME_RELEASE"
+ exit ;;
+ macppc:MirBSD:*:*)
+ echo powerpc-unknown-mirbsd"$UNAME_RELEASE"
+ exit ;;
+ *:MirBSD:*:*)
+ echo "$UNAME_MACHINE"-unknown-mirbsd"$UNAME_RELEASE"
+ exit ;;
+ *:Sortix:*:*)
+ echo "$UNAME_MACHINE"-unknown-sortix
+ exit ;;
+ *:Twizzler:*:*)
+ echo "$UNAME_MACHINE"-unknown-twizzler
+ exit ;;
+ *:Redox:*:*)
+ echo "$UNAME_MACHINE"-unknown-redox
+ exit ;;
+ mips:OSF1:*.*)
+ echo mips-dec-osf1
+ exit ;;
+ alpha:OSF1:*:*)
+ case $UNAME_RELEASE in
+ *4.0)
+ UNAME_RELEASE=$(/usr/sbin/sizer -v | awk '{print $3}')
+ ;;
+ *5.*)
+ UNAME_RELEASE=$(/usr/sbin/sizer -v | awk '{print $4}')
+ ;;
+ esac
+ # According to Compaq, /usr/sbin/psrinfo has been available on
+ # OSF/1 and Tru64 systems produced since 1995. I hope that
+ # covers most systems running today. This code pipes the CPU
+ # types through head -n 1, so we only detect the type of CPU 0.
+ ALPHA_CPU_TYPE=$(/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1)
+ case "$ALPHA_CPU_TYPE" in
+ "EV4 (21064)")
+ UNAME_MACHINE=alpha ;;
+ "EV4.5 (21064)")
+ UNAME_MACHINE=alpha ;;
+ "LCA4 (21066/21068)")
+ UNAME_MACHINE=alpha ;;
+ "EV5 (21164)")
+ UNAME_MACHINE=alphaev5 ;;
+ "EV5.6 (21164A)")
+ UNAME_MACHINE=alphaev56 ;;
+ "EV5.6 (21164PC)")
+ UNAME_MACHINE=alphapca56 ;;
+ "EV5.7 (21164PC)")
+ UNAME_MACHINE=alphapca57 ;;
+ "EV6 (21264)")
+ UNAME_MACHINE=alphaev6 ;;
+ "EV6.7 (21264A)")
+ UNAME_MACHINE=alphaev67 ;;
+ "EV6.8CB (21264C)")
+ UNAME_MACHINE=alphaev68 ;;
+ "EV6.8AL (21264B)")
+ UNAME_MACHINE=alphaev68 ;;
+ "EV6.8CX (21264D)")
+ UNAME_MACHINE=alphaev68 ;;
+ "EV6.9A (21264/EV69A)")
+ UNAME_MACHINE=alphaev69 ;;
+ "EV7 (21364)")
+ UNAME_MACHINE=alphaev7 ;;
+ "EV7.9 (21364A)")
+ UNAME_MACHINE=alphaev79 ;;
+ esac
+ # A Pn.n version is a patched version.
+ # A Vn.n version is a released version.
+ # A Tn.n version is a released field test version.
+ # A Xn.n version is an unreleased experimental baselevel.
+ # 1.2 uses "1.2" for uname -r.
+ echo "$UNAME_MACHINE"-dec-osf"$(echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz)"
+ # Reset EXIT trap before exiting to avoid spurious non-zero exit code.
+ exitcode=$?
+ trap '' 0
+ exit $exitcode ;;
+ Amiga*:UNIX_System_V:4.0:*)
+ echo m68k-unknown-sysv4
+ exit ;;
+ *:[Aa]miga[Oo][Ss]:*:*)
+ echo "$UNAME_MACHINE"-unknown-amigaos
+ exit ;;
+ *:[Mm]orph[Oo][Ss]:*:*)
+ echo "$UNAME_MACHINE"-unknown-morphos
+ exit ;;
+ *:OS/390:*:*)
+ echo i370-ibm-openedition
+ exit ;;
+ *:z/VM:*:*)
+ echo s390-ibm-zvmoe
+ exit ;;
+ *:OS400:*:*)
+ echo powerpc-ibm-os400
+ exit ;;
+ arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+ echo arm-acorn-riscix"$UNAME_RELEASE"
+ exit ;;
+ arm*:riscos:*:*|arm*:RISCOS:*:*)
+ echo arm-unknown-riscos
+ exit ;;
+ SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
+ echo hppa1.1-hitachi-hiuxmpp
+ exit ;;
+ Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
+ # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+ if test "$( (/bin/universe) 2>/dev/null)" = att ; then
+ echo pyramid-pyramid-sysv3
+ else
+ echo pyramid-pyramid-bsd
+ fi
+ exit ;;
+ NILE*:*:*:dcosx)
+ echo pyramid-pyramid-svr4
+ exit ;;
+ DRS?6000:unix:4.0:6*)
+ echo sparc-icl-nx6
+ exit ;;
+ DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*)
+ case $(/usr/bin/uname -p) in
+ sparc) echo sparc-icl-nx7; exit ;;
+ esac ;;
+ s390x:SunOS:*:*)
+ echo "$UNAME_MACHINE"-ibm-solaris2"$(echo "$UNAME_RELEASE" | sed -e 's/[^.]*//')"
+ exit ;;
+ sun4H:SunOS:5.*:*)
+ echo sparc-hal-solaris2"$(echo "$UNAME_RELEASE"|sed -e 's/[^.]*//')"
+ exit ;;
+ sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+ echo sparc-sun-solaris2"$(echo "$UNAME_RELEASE" | sed -e 's/[^.]*//')"
+ exit ;;
+ i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*)
+ echo i386-pc-auroraux"$UNAME_RELEASE"
+ exit ;;
+ i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)
+ set_cc_for_build
+ SUN_ARCH=i386
+ # If there is a compiler, see if it is configured for 64-bit objects.
+ # Note that the Sun cc does not turn __LP64__ into 1 like gcc does.
+ # This test works for both compilers.
+ if test "$CC_FOR_BUILD" != no_compiler_found; then
+ if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \
+ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
+ grep IS_64BIT_ARCH >/dev/null
+ then
+ SUN_ARCH=x86_64
+ fi
+ fi
+ echo "$SUN_ARCH"-pc-solaris2"$(echo "$UNAME_RELEASE"|sed -e 's/[^.]*//')"
+ exit ;;
+ sun4*:SunOS:6*:*)
+ # According to config.sub, this is the proper way to canonicalize
+ # SunOS6. Hard to guess exactly what SunOS6 will be like, but
+ # it's likely to be more like Solaris than SunOS4.
+ echo sparc-sun-solaris3"$(echo "$UNAME_RELEASE"|sed -e 's/[^.]*//')"
+ exit ;;
+ sun4*:SunOS:*:*)
+ case "$(/usr/bin/arch -k)" in
+ Series*|S4*)
+ UNAME_RELEASE=$(uname -v)
+ ;;
+ esac
+ # Japanese Language versions have a version number like `4.1.3-JL'.
+ echo sparc-sun-sunos"$(echo "$UNAME_RELEASE"|sed -e 's/-/_/')"
+ exit ;;
+ sun3*:SunOS:*:*)
+ echo m68k-sun-sunos"$UNAME_RELEASE"
+ exit ;;
+ sun*:*:4.2BSD:*)
+ UNAME_RELEASE=$( (sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null)
+ test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3
+ case "$(/bin/arch)" in
+ sun3)
+ echo m68k-sun-sunos"$UNAME_RELEASE"
+ ;;
+ sun4)
+ echo sparc-sun-sunos"$UNAME_RELEASE"
+ ;;
+ esac
+ exit ;;
+ aushp:SunOS:*:*)
+ echo sparc-auspex-sunos"$UNAME_RELEASE"
+ exit ;;
+ # The situation for MiNT is a little confusing. The machine name
+ # can be virtually everything (everything which is not
+ # "atarist" or "atariste" at least should have a processor
+ # > m68000). The system name ranges from "MiNT" over "FreeMiNT"
+ # to the lowercase version "mint" (or "freemint"). Finally
+ # the system name "TOS" denotes a system which is actually not
+ # MiNT. But MiNT is downward compatible to TOS, so this should
+ # be no problem.
+ atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint"$UNAME_RELEASE"
+ exit ;;
+ atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+ echo m68k-atari-mint"$UNAME_RELEASE"
+ exit ;;
+ *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+ echo m68k-atari-mint"$UNAME_RELEASE"
+ exit ;;
+ milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+ echo m68k-milan-mint"$UNAME_RELEASE"
+ exit ;;
+ hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+ echo m68k-hades-mint"$UNAME_RELEASE"
+ exit ;;
+ *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+ echo m68k-unknown-mint"$UNAME_RELEASE"
+ exit ;;
+ m68k:machten:*:*)
+ echo m68k-apple-machten"$UNAME_RELEASE"
+ exit ;;
+ powerpc:machten:*:*)
+ echo powerpc-apple-machten"$UNAME_RELEASE"
+ exit ;;
+ RISC*:Mach:*:*)
+ echo mips-dec-mach_bsd4.3
+ exit ;;
+ RISC*:ULTRIX:*:*)
+ echo mips-dec-ultrix"$UNAME_RELEASE"
+ exit ;;
+ VAX*:ULTRIX*:*:*)
+ echo vax-dec-ultrix"$UNAME_RELEASE"
+ exit ;;
+ 2020:CLIX:*:* | 2430:CLIX:*:*)
+ echo clipper-intergraph-clix"$UNAME_RELEASE"
+ exit ;;
+ mips:*:*:UMIPS | mips:*:*:RISCos)
+ set_cc_for_build
+ sed 's/^ //' << EOF > "$dummy.c"
+#ifdef __cplusplus
+#include <stdio.h> /* for printf() prototype */
+ int main (int argc, char *argv[]) {
+#else
+ int main (argc, argv) int argc; char *argv[]; {
+#endif
+ #if defined (host_mips) && defined (MIPSEB)
+ #if defined (SYSTYPE_SYSV)
+ printf ("mips-mips-riscos%ssysv\\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_SVR4)
+ printf ("mips-mips-riscos%ssvr4\\n", argv[1]); exit (0);
+ #endif
+ #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+ printf ("mips-mips-riscos%sbsd\\n", argv[1]); exit (0);
+ #endif
+ #endif
+ exit (-1);
+ }
+EOF
+ $CC_FOR_BUILD -o "$dummy" "$dummy.c" &&
+ dummyarg=$(echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p') &&
+ SYSTEM_NAME=$("$dummy" "$dummyarg") &&
+ { echo "$SYSTEM_NAME"; exit; }
+ echo mips-mips-riscos"$UNAME_RELEASE"
+ exit ;;
+ Motorola:PowerMAX_OS:*:*)
+ echo powerpc-motorola-powermax
+ exit ;;
+ Motorola:*:4.3:PL8-*)
+ echo powerpc-harris-powermax
+ exit ;;
+ Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
+ echo powerpc-harris-powermax
+ exit ;;
+ Night_Hawk:Power_UNIX:*:*)
+ echo powerpc-harris-powerunix
+ exit ;;
+ m88k:CX/UX:7*:*)
+ echo m88k-harris-cxux7
+ exit ;;
+ m88k:*:4*:R4*)
+ echo m88k-motorola-sysv4
+ exit ;;
+ m88k:*:3*:R3*)
+ echo m88k-motorola-sysv3
+ exit ;;
+ AViiON:dgux:*:*)
+ # DG/UX returns AViiON for all architectures
+ UNAME_PROCESSOR=$(/usr/bin/uname -p)
+ if test "$UNAME_PROCESSOR" = mc88100 || test "$UNAME_PROCESSOR" = mc88110
+ then
+ if test "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx || \
+ test "$TARGET_BINARY_INTERFACE"x = x
+ then
+ echo m88k-dg-dgux"$UNAME_RELEASE"
+ else
+ echo m88k-dg-dguxbcs"$UNAME_RELEASE"
+ fi
+ else
+ echo i586-dg-dgux"$UNAME_RELEASE"
+ fi
+ exit ;;
+ M88*:DolphinOS:*:*) # DolphinOS (SVR3)
+ echo m88k-dolphin-sysv3
+ exit ;;
+ M88*:*:R3*:*)
+ # Delta 88k system running SVR3
+ echo m88k-motorola-sysv3
+ exit ;;
+ XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+ echo m88k-tektronix-sysv3
+ exit ;;
+ Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+ echo m68k-tektronix-bsd
+ exit ;;
+ *:IRIX*:*:*)
+ echo mips-sgi-irix"$(echo "$UNAME_RELEASE"|sed -e 's/-/_/g')"
+ exit ;;
+ ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+ echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id
+ exit ;; # Note that: echo "'$(uname -s)'" gives 'AIX '
+ i*86:AIX:*:*)
+ echo i386-ibm-aix
+ exit ;;
+ ia64:AIX:*:*)
+ if test -x /usr/bin/oslevel ; then
+ IBM_REV=$(/usr/bin/oslevel)
+ else
+ IBM_REV="$UNAME_VERSION.$UNAME_RELEASE"
+ fi
+ echo "$UNAME_MACHINE"-ibm-aix"$IBM_REV"
+ exit ;;
+ *:AIX:2:3)
+ if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+ set_cc_for_build
+ sed 's/^ //' << EOF > "$dummy.c"
+ #include <sys/systemcfg.h>
+
+ main()
+ {
+ if (!__power_pc())
+ exit(1);
+ puts("powerpc-ibm-aix3.2.5");
+ exit(0);
+ }
+EOF
+ if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=$("$dummy")
+ then
+ echo "$SYSTEM_NAME"
+ else
+ echo rs6000-ibm-aix3.2.5
+ fi
+ elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+ echo rs6000-ibm-aix3.2.4
+ else
+ echo rs6000-ibm-aix3.2
+ fi
+ exit ;;
+ *:AIX:*:[4567])
+ IBM_CPU_ID=$(/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }')
+ if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then
+ IBM_ARCH=rs6000
+ else
+ IBM_ARCH=powerpc
+ fi
+ if test -x /usr/bin/lslpp ; then
+ IBM_REV=$(/usr/bin/lslpp -Lqc bos.rte.libc |
+ awk -F: '{ print $3 }' | sed s/[0-9]*$/0/)
+ else
+ IBM_REV="$UNAME_VERSION.$UNAME_RELEASE"
+ fi
+ echo "$IBM_ARCH"-ibm-aix"$IBM_REV"
+ exit ;;
+ *:AIX:*:*)
+ echo rs6000-ibm-aix
+ exit ;;
+ ibmrt:4.4BSD:*|romp-ibm:4.4BSD:*)
+ echo romp-ibm-bsd4.4
+ exit ;;
+ ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and
+ echo romp-ibm-bsd"$UNAME_RELEASE" # 4.3 with uname added to
+ exit ;; # report: romp-ibm BSD 4.3
+ *:BOSX:*:*)
+ echo rs6000-bull-bosx
+ exit ;;
+ DPX/2?00:B.O.S.:*:*)
+ echo m68k-bull-sysv3
+ exit ;;
+ 9000/[34]??:4.3bsd:1.*:*)
+ echo m68k-hp-bsd
+ exit ;;
+ hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+ echo m68k-hp-bsd4.4
+ exit ;;
+ 9000/[34678]??:HP-UX:*:*)
+ HPUX_REV=$(echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//')
+ case "$UNAME_MACHINE" in
+ 9000/31?) HP_ARCH=m68000 ;;
+ 9000/[34]??) HP_ARCH=m68k ;;
+ 9000/[678][0-9][0-9])
+ if test -x /usr/bin/getconf; then
+ sc_cpu_version=$(/usr/bin/getconf SC_CPU_VERSION 2>/dev/null)
+ sc_kernel_bits=$(/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null)
+ case "$sc_cpu_version" in
+ 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0
+ 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1
+ 532) # CPU_PA_RISC2_0
+ case "$sc_kernel_bits" in
+ 32) HP_ARCH=hppa2.0n ;;
+ 64) HP_ARCH=hppa2.0w ;;
+ '') HP_ARCH=hppa2.0 ;; # HP-UX 10.20
+ esac ;;
+ esac
+ fi
+ if test "$HP_ARCH" = ""; then
+ set_cc_for_build
+ sed 's/^ //' << EOF > "$dummy.c"
+
+ #define _HPUX_SOURCE
+ #include <stdlib.h>
+ #include <unistd.h>
+
+ int main ()
+ {
+ #if defined(_SC_KERNEL_BITS)
+ long bits = sysconf(_SC_KERNEL_BITS);
+ #endif
+ long cpu = sysconf (_SC_CPU_VERSION);
+
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+ case CPU_PA_RISC2_0:
+ #if defined(_SC_KERNEL_BITS)
+ switch (bits)
+ {
+ case 64: puts ("hppa2.0w"); break;
+ case 32: puts ("hppa2.0n"); break;
+ default: puts ("hppa2.0"); break;
+ } break;
+ #else /* !defined(_SC_KERNEL_BITS) */
+ puts ("hppa2.0"); break;
+ #endif
+ default: puts ("hppa1.0"); break;
+ }
+ exit (0);
+ }
+EOF
+ (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=$("$dummy")
+ test -z "$HP_ARCH" && HP_ARCH=hppa
+ fi ;;
+ esac
+ if test "$HP_ARCH" = hppa2.0w
+ then
+ set_cc_for_build
+
+ # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating
+ # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler
+ # generating 64-bit code. GNU and HP use different nomenclature:
+ #
+ # $ CC_FOR_BUILD=cc ./config.guess
+ # => hppa2.0w-hp-hpux11.23
+ # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess
+ # => hppa64-hp-hpux11.23
+
+ if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) |
+ grep -q __LP64__
+ then
+ HP_ARCH=hppa2.0w
+ else
+ HP_ARCH=hppa64
+ fi
+ fi
+ echo "$HP_ARCH"-hp-hpux"$HPUX_REV"
+ exit ;;
+ ia64:HP-UX:*:*)
+ HPUX_REV=$(echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//')
+ echo ia64-hp-hpux"$HPUX_REV"
+ exit ;;
+ 3050*:HI-UX:*:*)
+ set_cc_for_build
+ sed 's/^ //' << EOF > "$dummy.c"
+ #include <unistd.h>
+ int
+ main ()
+ {
+ long cpu = sysconf (_SC_CPU_VERSION);
+ /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+ true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct
+ results, however. */
+ if (CPU_IS_PA_RISC (cpu))
+ {
+ switch (cpu)
+ {
+ case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+ case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+ default: puts ("hppa-hitachi-hiuxwe2"); break;
+ }
+ }
+ else if (CPU_IS_HP_MC68K (cpu))
+ puts ("m68k-hitachi-hiuxwe2");
+ else puts ("unknown-hitachi-hiuxwe2");
+ exit (0);
+ }
+EOF
+ $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=$("$dummy") &&
+ { echo "$SYSTEM_NAME"; exit; }
+ echo unknown-hitachi-hiuxwe2
+ exit ;;
+ 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:*)
+ echo hppa1.1-hp-bsd
+ exit ;;
+ 9000/8??:4.3bsd:*:*)
+ echo hppa1.0-hp-bsd
+ exit ;;
+ *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
+ echo hppa1.0-hp-mpeix
+ exit ;;
+ hp7??:OSF1:*:* | hp8?[79]:OSF1:*:*)
+ echo hppa1.1-hp-osf
+ exit ;;
+ hp8??:OSF1:*:*)
+ echo hppa1.0-hp-osf
+ exit ;;
+ i*86:OSF1:*:*)
+ if test -x /usr/sbin/sysversion ; then
+ echo "$UNAME_MACHINE"-unknown-osf1mk
+ else
+ echo "$UNAME_MACHINE"-unknown-osf1
+ fi
+ exit ;;
+ parisc*:Lites*:*:*)
+ echo hppa1.1-hp-lites
+ exit ;;
+ C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+ echo c1-convex-bsd
+ exit ;;
+ C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+ if getsysinfo -f scalar_acc
+ then echo c32-convex-bsd
+ else echo c2-convex-bsd
+ fi
+ exit ;;
+ C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+ echo c34-convex-bsd
+ exit ;;
+ C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+ echo c38-convex-bsd
+ exit ;;
+ C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+ echo c4-convex-bsd
+ exit ;;
+ CRAY*Y-MP:*:*:*)
+ echo ymp-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*[A-Z]90:*:*:*)
+ echo "$UNAME_MACHINE"-cray-unicos"$UNAME_RELEASE" \
+ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
+ -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*TS:*:*:*)
+ echo t90-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*T3E:*:*:*)
+ echo alphaev5-cray-unicosmk"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ CRAY*SV1:*:*:*)
+ echo sv1-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ *:UNICOS/mp:*:*)
+ echo craynv-cray-unicosmp"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
+ exit ;;
+ F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
+ FUJITSU_PROC=$(uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz)
+ FUJITSU_SYS=$(uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///')
+ FUJITSU_REL=$(echo "$UNAME_RELEASE" | sed -e 's/ /_/')
+ echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ exit ;;
+ 5000:UNIX_System_V:4.*:*)
+ FUJITSU_SYS=$(uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///')
+ FUJITSU_REL=$(echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/')
+ echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}"
+ exit ;;
+ i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
+ echo "$UNAME_MACHINE"-pc-bsdi"$UNAME_RELEASE"
+ exit ;;
+ sparc*:BSD/OS:*:*)
+ echo sparc-unknown-bsdi"$UNAME_RELEASE"
+ exit ;;
+ *:BSD/OS:*:*)
+ echo "$UNAME_MACHINE"-unknown-bsdi"$UNAME_RELEASE"
+ exit ;;
+ arm:FreeBSD:*:*)
+ UNAME_PROCESSOR=$(uname -p)
+ set_cc_for_build
+ if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep -q __ARM_PCS_VFP
+ then
+ echo "${UNAME_PROCESSOR}"-unknown-freebsd"$(echo ${UNAME_RELEASE}|sed -e 's/[-(].*//')"-gnueabi
+ else
+ echo "${UNAME_PROCESSOR}"-unknown-freebsd"$(echo ${UNAME_RELEASE}|sed -e 's/[-(].*//')"-gnueabihf
+ fi
+ exit ;;
+ *:FreeBSD:*:*)
+ UNAME_PROCESSOR=$(/usr/bin/uname -p)
+ case "$UNAME_PROCESSOR" in
+ amd64)
+ UNAME_PROCESSOR=x86_64 ;;
+ i386)
+ UNAME_PROCESSOR=i586 ;;
+ esac
+ echo "$UNAME_PROCESSOR"-unknown-freebsd"$(echo "$UNAME_RELEASE"|sed -e 's/[-(].*//')"
+ exit ;;
+ i*:CYGWIN*:*)
+ echo "$UNAME_MACHINE"-pc-cygwin
+ exit ;;
+ *:MINGW64*:*)
+ echo "$UNAME_MACHINE"-pc-mingw64
+ exit ;;
+ *:MINGW*:*)
+ echo "$UNAME_MACHINE"-pc-mingw32
+ exit ;;
+ *:MSYS*:*)
+ echo "$UNAME_MACHINE"-pc-msys
+ exit ;;
+ i*:PW*:*)
+ echo "$UNAME_MACHINE"-pc-pw32
+ exit ;;
+ *:Interix*:*)
+ case "$UNAME_MACHINE" in
+ x86)
+ echo i586-pc-interix"$UNAME_RELEASE"
+ exit ;;
+ authenticamd | genuineintel | EM64T)
+ echo x86_64-unknown-interix"$UNAME_RELEASE"
+ exit ;;
+ IA64)
+ echo ia64-unknown-interix"$UNAME_RELEASE"
+ exit ;;
+ esac ;;
+ i*:UWIN*:*)
+ echo "$UNAME_MACHINE"-pc-uwin
+ exit ;;
+ amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*)
+ echo x86_64-pc-cygwin
+ exit ;;
+ prep*:SunOS:5.*:*)
+ echo powerpcle-unknown-solaris2"$(echo "$UNAME_RELEASE"|sed -e 's/[^.]*//')"
+ exit ;;
+ *:GNU:*:*)
+ # the GNU system
+ echo "$(echo "$UNAME_MACHINE"|sed -e 's,[-/].*$,,')-unknown-$LIBC$(echo "$UNAME_RELEASE"|sed -e 's,/.*$,,')"
+ exit ;;
+ *:GNU/*:*:*)
+ # other systems with GNU libc and userland
+ echo "$UNAME_MACHINE-unknown-$(echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]")$(echo "$UNAME_RELEASE"|sed -e 's/[-(].*//')-$LIBC"
+ exit ;;
+ *:Minix:*:*)
+ echo "$UNAME_MACHINE"-unknown-minix
+ exit ;;
+ aarch64:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ aarch64_be:Linux:*:*)
+ UNAME_MACHINE=aarch64_be
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ alpha:Linux:*:*)
+ case $(sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' /proc/cpuinfo 2>/dev/null) in
+ EV5) UNAME_MACHINE=alphaev5 ;;
+ EV56) UNAME_MACHINE=alphaev56 ;;
+ PCA56) UNAME_MACHINE=alphapca56 ;;
+ PCA57) UNAME_MACHINE=alphapca56 ;;
+ EV6) UNAME_MACHINE=alphaev6 ;;
+ EV67) UNAME_MACHINE=alphaev67 ;;
+ EV68*) UNAME_MACHINE=alphaev68 ;;
+ esac
+ objdump --private-headers /bin/sh | grep -q ld.so.1
+ if test "$?" = 0 ; then LIBC=gnulibc1 ; fi
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ arc:Linux:*:* | arceb:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ arm*:Linux:*:*)
+ set_cc_for_build
+ if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep -q __ARM_EABI__
+ then
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ else
+ if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
+ | grep -q __ARM_PCS_VFP
+ then
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabi
+ else
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabihf
+ fi
+ fi
+ exit ;;
+ avr32*:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ cris:Linux:*:*)
+ echo "$UNAME_MACHINE"-axis-linux-"$LIBC"
+ exit ;;
+ crisv32:Linux:*:*)
+ echo "$UNAME_MACHINE"-axis-linux-"$LIBC"
+ exit ;;
+ e2k:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ frv:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ hexagon:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ i*86:Linux:*:*)
+ echo "$UNAME_MACHINE"-pc-linux-"$LIBC"
+ exit ;;
+ ia64:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ k1om:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ loongarch32:Linux:*:* | loongarch64:Linux:*:* | loongarchx32:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ m32r*:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ m68*:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ mips:Linux:*:* | mips64:Linux:*:*)
+ set_cc_for_build
+ IS_GLIBC=0
+ test x"${LIBC}" = xgnu && IS_GLIBC=1
+ sed 's/^ //' << EOF > "$dummy.c"
+ #undef CPU
+ #undef mips
+ #undef mipsel
+ #undef mips64
+ #undef mips64el
+ #if ${IS_GLIBC} && defined(_ABI64)
+ LIBCABI=gnuabi64
+ #else
+ #if ${IS_GLIBC} && defined(_ABIN32)
+ LIBCABI=gnuabin32
+ #else
+ LIBCABI=${LIBC}
+ #endif
+ #endif
+
+ #if ${IS_GLIBC} && defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6
+ CPU=mipsisa64r6
+ #else
+ #if ${IS_GLIBC} && !defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6
+ CPU=mipsisa32r6
+ #else
+ #if defined(__mips64)
+ CPU=mips64
+ #else
+ CPU=mips
+ #endif
+ #endif
+ #endif
+
+ #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+ MIPS_ENDIAN=el
+ #else
+ #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+ MIPS_ENDIAN=
+ #else
+ MIPS_ENDIAN=
+ #endif
+ #endif
+EOF
+ eval "$($CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU\|^MIPS_ENDIAN\|^LIBCABI')"
+ test "x$CPU" != x && { echo "$CPU${MIPS_ENDIAN}-unknown-linux-$LIBCABI"; exit; }
+ ;;
+ mips64el:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ openrisc*:Linux:*:*)
+ echo or1k-unknown-linux-"$LIBC"
+ exit ;;
+ or32:Linux:*:* | or1k*:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ padre:Linux:*:*)
+ echo sparc-unknown-linux-"$LIBC"
+ exit ;;
+ parisc64:Linux:*:* | hppa64:Linux:*:*)
+ echo hppa64-unknown-linux-"$LIBC"
+ exit ;;
+ parisc:Linux:*:* | hppa:Linux:*:*)
+ # Look for CPU level
+ case $(grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2) in
+ PA7*) echo hppa1.1-unknown-linux-"$LIBC" ;;
+ PA8*) echo hppa2.0-unknown-linux-"$LIBC" ;;
+ *) echo hppa-unknown-linux-"$LIBC" ;;
+ esac
+ exit ;;
+ ppc64:Linux:*:*)
+ echo powerpc64-unknown-linux-"$LIBC"
+ exit ;;
+ ppc:Linux:*:*)
+ echo powerpc-unknown-linux-"$LIBC"
+ exit ;;
+ ppc64le:Linux:*:*)
+ echo powerpc64le-unknown-linux-"$LIBC"
+ exit ;;
+ ppcle:Linux:*:*)
+ echo powerpcle-unknown-linux-"$LIBC"
+ exit ;;
+ riscv32:Linux:*:* | riscv32be:Linux:*:* | riscv64:Linux:*:* | riscv64be:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ s390:Linux:*:* | s390x:Linux:*:*)
+ echo "$UNAME_MACHINE"-ibm-linux-"$LIBC"
+ exit ;;
+ sh64*:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ sh*:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ sparc:Linux:*:* | sparc64:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ tile*:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ vax:Linux:*:*)
+ echo "$UNAME_MACHINE"-dec-linux-"$LIBC"
+ exit ;;
+ x86_64:Linux:*:*)
+ set_cc_for_build
+ LIBCABI=$LIBC
+ if test "$CC_FOR_BUILD" != no_compiler_found; then
+ if (echo '#ifdef __ILP32__'; echo IS_X32; echo '#endif') | \
+ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
+ grep IS_X32 >/dev/null
+ then
+ LIBCABI="$LIBC"x32
+ fi
+ fi
+ echo "$UNAME_MACHINE"-pc-linux-"$LIBCABI"
+ exit ;;
+ xtensa*:Linux:*:*)
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
+ exit ;;
+ i*86:DYNIX/ptx:4*:*)
+ # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
+ # earlier versions are messed up and put the nodename in both
+ # sysname and nodename.
+ echo i386-sequent-sysv4
+ exit ;;
+ i*86:UNIX_SV:4.2MP:2.*)
+ # Unixware is an offshoot of SVR4, but it has its own version
+ # number series starting with 2...
+ # I am not positive that other SVR4 systems won't match this,
+ # I just have to hope. -- rms.
+ # Use sysv4.2uw... so that sysv4* matches it.
+ echo "$UNAME_MACHINE"-pc-sysv4.2uw"$UNAME_VERSION"
+ exit ;;
+ i*86:OS/2:*:*)
+ # If we were able to find `uname', then EMX Unix compatibility
+ # is probably installed.
+ echo "$UNAME_MACHINE"-pc-os2-emx
+ exit ;;
+ i*86:XTS-300:*:STOP)
+ echo "$UNAME_MACHINE"-unknown-stop
+ exit ;;
+ i*86:atheos:*:*)
+ echo "$UNAME_MACHINE"-unknown-atheos
+ exit ;;
+ i*86:syllable:*:*)
+ echo "$UNAME_MACHINE"-pc-syllable
+ exit ;;
+ i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*)
+ echo i386-unknown-lynxos"$UNAME_RELEASE"
+ exit ;;
+ i*86:*DOS:*:*)
+ echo "$UNAME_MACHINE"-pc-msdosdjgpp
+ exit ;;
+ i*86:*:4.*:*)
+ UNAME_REL=$(echo "$UNAME_RELEASE" | sed 's/\/MP$//')
+ if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+ echo "$UNAME_MACHINE"-univel-sysv"$UNAME_REL"
+ else
+ echo "$UNAME_MACHINE"-pc-sysv"$UNAME_REL"
+ fi
+ exit ;;
+ i*86:*:5:[678]*)
+ # UnixWare 7.x, OpenUNIX and OpenServer 6.
+ case $(/bin/uname -X | grep "^Machine") in
+ *486*) UNAME_MACHINE=i486 ;;
+ *Pentium) UNAME_MACHINE=i586 ;;
+ *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
+ esac
+ echo "$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}"
+ exit ;;
+ i*86:*:3.2:*)
+ if test -f /usr/options/cb.name; then
+ UNAME_REL=$(sed -n 's/.*Version //p' </usr/options/cb.name)
+ echo "$UNAME_MACHINE"-pc-isc"$UNAME_REL"
+ elif /bin/uname -X 2>/dev/null >/dev/null ; then
+ UNAME_REL=$( (/bin/uname -X|grep Release|sed -e 's/.*= //'))
+ (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
+ (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
+ && UNAME_MACHINE=i586
+ (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
+ && UNAME_MACHINE=i686
+ (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
+ && UNAME_MACHINE=i686
+ echo "$UNAME_MACHINE"-pc-sco"$UNAME_REL"
+ else
+ echo "$UNAME_MACHINE"-pc-sysv32
+ fi
+ exit ;;
+ pc:*:*:*)
+ # Left here for compatibility:
+ # uname -m prints for DJGPP always 'pc', but it prints nothing about
+ # the processor, so we play safe by assuming i586.
+ # Note: whatever this is, it MUST be the same as what config.sub
+ # prints for the "djgpp" host, or else GDB configure will decide that
+ # this is a cross-build.
+ echo i586-pc-msdosdjgpp
+ exit ;;
+ Intel:Mach:3*:*)
+ echo i386-pc-mach3
+ exit ;;
+ paragon:*:*:*)
+ echo i860-intel-osf1
+ exit ;;
+ i860:*:4.*:*) # i860-SVR4
+ if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+ echo i860-stardent-sysv"$UNAME_RELEASE" # Stardent Vistra i860-SVR4
+ else # Add other i860-SVR4 vendors below as they are discovered.
+ echo i860-unknown-sysv"$UNAME_RELEASE" # Unknown i860-SVR4
+ fi
+ exit ;;
+ mini*:CTIX:SYS*5:*)
+ # "miniframe"
+ echo m68010-convergent-sysv
+ exit ;;
+ mc68k:UNIX:SYSTEM5:3.51m)
+ echo m68k-convergent-sysv
+ exit ;;
+ M680?0:D-NIX:5.3:*)
+ echo m68k-diab-dnix
+ exit ;;
+ M68*:*:R3V[5678]*:*)
+ test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;;
+ 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0)
+ OS_REL=''
+ test -r /etc/.relid \
+ && OS_REL=.$(sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid)
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && { echo i486-ncr-sysv4.3"$OS_REL"; exit; }
+ /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;;
+ 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && { echo i486-ncr-sysv4; exit; } ;;
+ NCR*:*:4.2:* | MPRAS*:*:4.2:*)
+ OS_REL='.3'
+ test -r /etc/.relid \
+ && OS_REL=.$(sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid)
+ /bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+ && { echo i486-ncr-sysv4.3"$OS_REL"; exit; }
+ /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; }
+ /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \
+ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;;
+ m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
+ echo m68k-unknown-lynxos"$UNAME_RELEASE"
+ exit ;;
+ mc68030:UNIX_System_V:4.*:*)
+ echo m68k-atari-sysv4
+ exit ;;
+ TSUNAMI:LynxOS:2.*:*)
+ echo sparc-unknown-lynxos"$UNAME_RELEASE"
+ exit ;;
+ rs6000:LynxOS:2.*:*)
+ echo rs6000-unknown-lynxos"$UNAME_RELEASE"
+ exit ;;
+ PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*)
+ echo powerpc-unknown-lynxos"$UNAME_RELEASE"
+ exit ;;
+ SM[BE]S:UNIX_SV:*:*)
+ echo mips-dde-sysv"$UNAME_RELEASE"
+ exit ;;
+ RM*:ReliantUNIX-*:*:*)
+ echo mips-sni-sysv4
+ exit ;;
+ RM*:SINIX-*:*:*)
+ echo mips-sni-sysv4
+ exit ;;
+ *:SINIX-*:*:*)
+ if uname -p 2>/dev/null >/dev/null ; then
+ UNAME_MACHINE=$( (uname -p) 2>/dev/null)
+ echo "$UNAME_MACHINE"-sni-sysv4
+ else
+ echo ns32k-sni-sysv
+ fi
+ exit ;;
+ PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort
+ # says <Richard.M.Bartel@ccMail.Census.GOV>
+ echo i586-unisys-sysv4
+ exit ;;
+ *:UNIX_System_V:4*:FTX*)
+ # From Gerald Hewes <hewes@openmarket.com>.
+ # How about differentiating between stratus architectures? -djm
+ echo hppa1.1-stratus-sysv4
+ exit ;;
+ *:*:*:FTX*)
+ # From seanf@swdc.stratus.com.
+ echo i860-stratus-sysv4
+ exit ;;
+ i*86:VOS:*:*)
+ # From Paul.Green@stratus.com.
+ echo "$UNAME_MACHINE"-stratus-vos
+ exit ;;
+ *:VOS:*:*)
+ # From Paul.Green@stratus.com.
+ echo hppa1.1-stratus-vos
+ exit ;;
+ mc68*:A/UX:*:*)
+ echo m68k-apple-aux"$UNAME_RELEASE"
+ exit ;;
+ news*:NEWS-OS:6*:*)
+ echo mips-sony-newsos6
+ exit ;;
+ R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
+ if test -d /usr/nec; then
+ echo mips-nec-sysv"$UNAME_RELEASE"
+ else
+ echo mips-unknown-sysv"$UNAME_RELEASE"
+ fi
+ exit ;;
+ BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only.
+ echo powerpc-be-beos
+ exit ;;
+ BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only.
+ echo powerpc-apple-beos
+ exit ;;
+ BePC:BeOS:*:*) # BeOS running on Intel PC compatible.
+ echo i586-pc-beos
+ exit ;;
+ BePC:Haiku:*:*) # Haiku running on Intel PC compatible.
+ echo i586-pc-haiku
+ exit ;;
+ x86_64:Haiku:*:*)
+ echo x86_64-unknown-haiku
+ exit ;;
+ SX-4:SUPER-UX:*:*)
+ echo sx4-nec-superux"$UNAME_RELEASE"
+ exit ;;
+ SX-5:SUPER-UX:*:*)
+ echo sx5-nec-superux"$UNAME_RELEASE"
+ exit ;;
+ SX-6:SUPER-UX:*:*)
+ echo sx6-nec-superux"$UNAME_RELEASE"
+ exit ;;
+ SX-7:SUPER-UX:*:*)
+ echo sx7-nec-superux"$UNAME_RELEASE"
+ exit ;;
+ SX-8:SUPER-UX:*:*)
+ echo sx8-nec-superux"$UNAME_RELEASE"
+ exit ;;
+ SX-8R:SUPER-UX:*:*)
+ echo sx8r-nec-superux"$UNAME_RELEASE"
+ exit ;;
+ SX-ACE:SUPER-UX:*:*)
+ echo sxace-nec-superux"$UNAME_RELEASE"
+ exit ;;
+ Power*:Rhapsody:*:*)
+ echo powerpc-apple-rhapsody"$UNAME_RELEASE"
+ exit ;;
+ *:Rhapsody:*:*)
+ echo "$UNAME_MACHINE"-apple-rhapsody"$UNAME_RELEASE"
+ exit ;;
+ arm64:Darwin:*:*)
+ echo aarch64-apple-darwin"$UNAME_RELEASE"
+ exit ;;
+ *:Darwin:*:*)
+ UNAME_PROCESSOR=$(uname -p)
+ case $UNAME_PROCESSOR in
+ unknown) UNAME_PROCESSOR=powerpc ;;
+ esac
+ if command -v xcode-select > /dev/null 2> /dev/null && \
+ ! xcode-select --print-path > /dev/null 2> /dev/null ; then
+ # Avoid executing cc if there is no toolchain installed as
+ # cc will be a stub that puts up a graphical alert
+ # prompting the user to install developer tools.
+ CC_FOR_BUILD=no_compiler_found
+ else
+ set_cc_for_build
+ fi
+ if test "$CC_FOR_BUILD" != no_compiler_found; then
+ if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
+ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
+ grep IS_64BIT_ARCH >/dev/null
+ then
+ case $UNAME_PROCESSOR in
+ i386) UNAME_PROCESSOR=x86_64 ;;
+ powerpc) UNAME_PROCESSOR=powerpc64 ;;
+ esac
+ fi
+ # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc
+ if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \
+ (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
+ grep IS_PPC >/dev/null
+ then
+ UNAME_PROCESSOR=powerpc
+ fi
+ elif test "$UNAME_PROCESSOR" = i386 ; then
+ # uname -m returns i386 or x86_64
+ UNAME_PROCESSOR=$UNAME_MACHINE
+ fi
+ echo "$UNAME_PROCESSOR"-apple-darwin"$UNAME_RELEASE"
+ exit ;;
+ *:procnto*:*:* | *:QNX:[0123456789]*:*)
+ UNAME_PROCESSOR=$(uname -p)
+ if test "$UNAME_PROCESSOR" = x86; then
+ UNAME_PROCESSOR=i386
+ UNAME_MACHINE=pc
+ fi
+ echo "$UNAME_PROCESSOR"-"$UNAME_MACHINE"-nto-qnx"$UNAME_RELEASE"
+ exit ;;
+ *:QNX:*:4*)
+ echo i386-pc-qnx
+ exit ;;
+ NEO-*:NONSTOP_KERNEL:*:*)
+ echo neo-tandem-nsk"$UNAME_RELEASE"
+ exit ;;
+ NSE-*:NONSTOP_KERNEL:*:*)
+ echo nse-tandem-nsk"$UNAME_RELEASE"
+ exit ;;
+ NSR-*:NONSTOP_KERNEL:*:*)
+ echo nsr-tandem-nsk"$UNAME_RELEASE"
+ exit ;;
+ NSV-*:NONSTOP_KERNEL:*:*)
+ echo nsv-tandem-nsk"$UNAME_RELEASE"
+ exit ;;
+ NSX-*:NONSTOP_KERNEL:*:*)
+ echo nsx-tandem-nsk"$UNAME_RELEASE"
+ exit ;;
+ *:NonStop-UX:*:*)
+ echo mips-compaq-nonstopux
+ exit ;;
+ BS2000:POSIX*:*:*)
+ echo bs2000-siemens-sysv
+ exit ;;
+ DS/*:UNIX_System_V:*:*)
+ echo "$UNAME_MACHINE"-"$UNAME_SYSTEM"-"$UNAME_RELEASE"
+ exit ;;
+ *:Plan9:*:*)
+ # "uname -m" is not consistent, so use $cputype instead. 386
+ # is converted to i386 for consistency with other x86
+ # operating systems.
+ # shellcheck disable=SC2154
+ if test "$cputype" = 386; then
+ UNAME_MACHINE=i386
+ else
+ UNAME_MACHINE="$cputype"
+ fi
+ echo "$UNAME_MACHINE"-unknown-plan9
+ exit ;;
+ *:TOPS-10:*:*)
+ echo pdp10-unknown-tops10
+ exit ;;
+ *:TENEX:*:*)
+ echo pdp10-unknown-tenex
+ exit ;;
+ KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
+ echo pdp10-dec-tops20
+ exit ;;
+ XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
+ echo pdp10-xkl-tops20
+ exit ;;
+ *:TOPS-20:*:*)
+ echo pdp10-unknown-tops20
+ exit ;;
+ *:ITS:*:*)
+ echo pdp10-unknown-its
+ exit ;;
+ SEI:*:*:SEIUX)
+ echo mips-sei-seiux"$UNAME_RELEASE"
+ exit ;;
+ *:DragonFly:*:*)
+ echo "$UNAME_MACHINE"-unknown-dragonfly"$(echo "$UNAME_RELEASE"|sed -e 's/[-(].*//')"
+ exit ;;
+ *:*VMS:*:*)
+ UNAME_MACHINE=$( (uname -p) 2>/dev/null)
+ case "$UNAME_MACHINE" in
+ A*) echo alpha-dec-vms ; exit ;;
+ I*) echo ia64-dec-vms ; exit ;;
+ V*) echo vax-dec-vms ; exit ;;
+ esac ;;
+ *:XENIX:*:SysV)
+ echo i386-pc-xenix
+ exit ;;
+ i*86:skyos:*:*)
+ echo "$UNAME_MACHINE"-pc-skyos"$(echo "$UNAME_RELEASE" | sed -e 's/ .*$//')"
+ exit ;;
+ i*86:rdos:*:*)
+ echo "$UNAME_MACHINE"-pc-rdos
+ exit ;;
+ i*86:AROS:*:*)
+ echo "$UNAME_MACHINE"-pc-aros
+ exit ;;
+ x86_64:VMkernel:*:*)
+ echo "$UNAME_MACHINE"-unknown-esx
+ exit ;;
+ amd64:Isilon\ OneFS:*:*)
+ echo x86_64-unknown-onefs
+ exit ;;
+ *:Unleashed:*:*)
+ echo "$UNAME_MACHINE"-unknown-unleashed"$UNAME_RELEASE"
+ exit ;;
+esac
+
+# No uname command or uname output not recognized.
+set_cc_for_build
+cat > "$dummy.c" <<EOF
+#ifdef _SEQUENT_
+#include <sys/types.h>
+#include <sys/utsname.h>
+#endif
+#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__)
+#if defined (vax) || defined (__vax) || defined (__vax__) || defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__)
+#include <signal.h>
+#if defined(_SIZE_T_) || defined(SIGLOST)
+#include <sys/utsname.h>
+#endif
+#endif
+#endif
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+ /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed,
+ I don't know.... */
+ printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+ printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+ "4"
+#else
+ ""
+#endif
+ ); exit (0);
+#endif
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+ int version;
+ version=$( (hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null);
+ if (version < 4)
+ printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+ else
+ printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
+ exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+ printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+ printf ("ns32k-encore-mach\n"); exit (0);
+#else
+ printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+ printf ("i386-pc-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+ printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+ printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+ struct utsname un;
+
+ uname(&un);
+ if (strncmp(un.version, "V2", 2) == 0) {
+ printf ("i386-sequent-ptx2\n"); exit (0);
+ }
+ if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+ printf ("i386-sequent-ptx1\n"); exit (0);
+ }
+ printf ("i386-sequent-ptx\n"); exit (0);
+#endif
+
+#if defined (vax)
+#if !defined (ultrix)
+#include <sys/param.h>
+#if defined (BSD)
+#if BSD == 43
+ printf ("vax-dec-bsd4.3\n"); exit (0);
+#else
+#if BSD == 199006
+ printf ("vax-dec-bsd4.3reno\n"); exit (0);
+#else
+ printf ("vax-dec-bsd\n"); exit (0);
+#endif
+#endif
+#else
+ printf ("vax-dec-bsd\n"); exit (0);
+#endif
+#else
+#if defined(_SIZE_T_) || defined(SIGLOST)
+ struct utsname un;
+ uname (&un);
+ printf ("vax-dec-ultrix%s\n", un.release); exit (0);
+#else
+ printf ("vax-dec-ultrix\n"); exit (0);
+#endif
+#endif
+#endif
+#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__)
+#if defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__)
+#if defined(_SIZE_T_) || defined(SIGLOST)
+ struct utsname *un;
+ uname (&un);
+ printf ("mips-dec-ultrix%s\n", un.release); exit (0);
+#else
+ printf ("mips-dec-ultrix\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (alliant) && defined (i860)
+ printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+ exit (1);
+}
+EOF
+
+$CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null && SYSTEM_NAME=$($dummy) &&
+ { echo "$SYSTEM_NAME"; exit; }
+
+# Apollos put the system type in the environment.
+test -d /usr/apollo && { echo "$ISP-apollo-$SYSTYPE"; exit; }
+
+echo "$0: unable to guess system type" >&2
+
+case "$UNAME_MACHINE:$UNAME_SYSTEM" in
+ mips:Linux | mips64:Linux)
+ # If we got here on MIPS GNU/Linux, output extra information.
+ cat >&2 <<EOF
+
+NOTE: MIPS GNU/Linux systems require a C compiler to fully recognize
+the system type. Please install a C compiler and try again.
+EOF
+ ;;
+esac
+
+cat >&2 <<EOF
+
+This script (version $timestamp), has failed to recognize the
+operating system you are using. If your script is old, overwrite *all*
+copies of config.guess and config.sub with the latest versions from:
+
+ https://git.savannah.gnu.org/cgit/config.git/plain/config.guess
+and
+ https://git.savannah.gnu.org/cgit/config.git/plain/config.sub
+EOF
+
+year=$(echo $timestamp | sed 's,-.*,,')
+# shellcheck disable=SC2003
+if test "$(expr "$(date +%Y)" - "$year")" -lt 3 ; then
+ cat >&2 <<EOF
+
+If $0 has already been updated, send the following data and any
+information you think might be pertinent to config-patches@gnu.org to
+provide the necessary information to handle your system.
+
+config.guess timestamp = $timestamp
+
+uname -m = $( (uname -m) 2>/dev/null || echo unknown)
+uname -r = $( (uname -r) 2>/dev/null || echo unknown)
+uname -s = $( (uname -s) 2>/dev/null || echo unknown)
+uname -v = $( (uname -v) 2>/dev/null || echo unknown)
+
+/usr/bin/uname -p = $( (/usr/bin/uname -p) 2>/dev/null)
+/bin/uname -X = $( (/bin/uname -X) 2>/dev/null)
+
+hostinfo = $( (hostinfo) 2>/dev/null)
+/bin/universe = $( (/bin/universe) 2>/dev/null)
+/usr/bin/arch -k = $( (/usr/bin/arch -k) 2>/dev/null)
+/bin/arch = $( (/bin/arch) 2>/dev/null)
+/usr/bin/oslevel = $( (/usr/bin/oslevel) 2>/dev/null)
+/usr/convex/getsysinfo = $( (/usr/convex/getsysinfo) 2>/dev/null)
+
+UNAME_MACHINE = "$UNAME_MACHINE"
+UNAME_RELEASE = "$UNAME_RELEASE"
+UNAME_SYSTEM = "$UNAME_SYSTEM"
+UNAME_VERSION = "$UNAME_VERSION"
+EOF
+fi
+
+exit 1
+
+# Local variables:
+# eval: (add-hook 'before-save-hook 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/contrib/jemalloc/build-aux/config.sub b/contrib/jemalloc/build-aux/config.sub
new file mode 100755
index 000000000000..b0f8492348d7
--- /dev/null
+++ b/contrib/jemalloc/build-aux/config.sub
@@ -0,0 +1,1855 @@
+#! /bin/sh
+# Configuration validation subroutine script.
+# Copyright 1992-2021 Free Software Foundation, Inc.
+
+timestamp='2021-01-07'
+
+# This file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses/>.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that
+# program. This Exception is an additional permission under section 7
+# of the GNU General Public License, version 3 ("GPLv3").
+
+
+# Please send patches to <config-patches@gnu.org>.
+#
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# You can get the latest version of this script from:
+# https://git.savannah.gnu.org/cgit/config.git/plain/config.sub
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support. The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+me=$(echo "$0" | sed -e 's,.*/,,')
+
+usage="\
+Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS
+
+Canonicalize a configuration name.
+
+Options:
+ -h, --help print this help, then exit
+ -t, --time-stamp print date of last modification, then exit
+ -v, --version print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.sub ($timestamp)
+
+Copyright 1992-2021 Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try \`$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+ case $1 in
+ --time-stamp | --time* | -t )
+ echo "$timestamp" ; exit ;;
+ --version | -v )
+ echo "$version" ; exit ;;
+ --help | --h* | -h )
+ echo "$usage"; exit ;;
+ -- ) # Stop option processing
+ shift; break ;;
+ - ) # Use stdin as input.
+ break ;;
+ -* )
+ echo "$me: invalid option $1$help" >&2
+ exit 1 ;;
+
+ *local*)
+ # First pass through any local machine types.
+ echo "$1"
+ exit ;;
+
+ * )
+ break ;;
+ esac
+done
+
+case $# in
+ 0) echo "$me: missing argument$help" >&2
+ exit 1;;
+ 1) ;;
+ *) echo "$me: too many arguments$help" >&2
+ exit 1;;
+esac
+
+# Split fields of configuration type
+# shellcheck disable=SC2162
+IFS="-" read field1 field2 field3 field4 <<EOF
+$1
+EOF
+
+# Separate into logical components for further validation
+case $1 in
+ *-*-*-*-*)
+ echo Invalid configuration \`"$1"\': more than four components >&2
+ exit 1
+ ;;
+ *-*-*-*)
+ basic_machine=$field1-$field2
+ basic_os=$field3-$field4
+ ;;
+ *-*-*)
+ # Ambiguous whether COMPANY is present, or skipped and KERNEL-OS is two
+ # parts
+ maybe_os=$field2-$field3
+ case $maybe_os in
+ nto-qnx* | linux-* | uclinux-uclibc* \
+ | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* \
+ | netbsd*-eabi* | kopensolaris*-gnu* | cloudabi*-eabi* \
+ | storm-chaos* | os2-emx* | rtmk-nova*)
+ basic_machine=$field1
+ basic_os=$maybe_os
+ ;;
+ android-linux)
+ basic_machine=$field1-unknown
+ basic_os=linux-android
+ ;;
+ *)
+ basic_machine=$field1-$field2
+ basic_os=$field3
+ ;;
+ esac
+ ;;
+ *-*)
+ # A lone config we happen to match not fitting any pattern
+ case $field1-$field2 in
+ decstation-3100)
+ basic_machine=mips-dec
+ basic_os=
+ ;;
+ *-*)
+ # Second component is usually, but not always the OS
+ case $field2 in
+ # Prevent following clause from handling this valid os
+ sun*os*)
+ basic_machine=$field1
+ basic_os=$field2
+ ;;
+ # Manufacturers
+ dec* | mips* | sequent* | encore* | pc533* | sgi* | sony* \
+ | att* | 7300* | 3300* | delta* | motorola* | sun[234]* \
+ | unicom* | ibm* | next | hp | isi* | apollo | altos* \
+ | convergent* | ncr* | news | 32* | 3600* | 3100* \
+ | hitachi* | c[123]* | convex* | sun | crds | omron* | dg \
+ | ultra | tti* | harris | dolphin | highlevel | gould \
+ | cbm | ns | masscomp | apple | axis | knuth | cray \
+ | microblaze* | sim | cisco \
+ | oki | wec | wrs | winbond)
+ basic_machine=$field1-$field2
+ basic_os=
+ ;;
+ *)
+ basic_machine=$field1
+ basic_os=$field2
+ ;;
+ esac
+ ;;
+ esac
+ ;;
+ *)
+ # Convert single-component short-hands not valid as part of
+ # multi-component configurations.
+ case $field1 in
+ 386bsd)
+ basic_machine=i386-pc
+ basic_os=bsd
+ ;;
+ a29khif)
+ basic_machine=a29k-amd
+ basic_os=udi
+ ;;
+ adobe68k)
+ basic_machine=m68010-adobe
+ basic_os=scout
+ ;;
+ alliant)
+ basic_machine=fx80-alliant
+ basic_os=
+ ;;
+ altos | altos3068)
+ basic_machine=m68k-altos
+ basic_os=
+ ;;
+ am29k)
+ basic_machine=a29k-none
+ basic_os=bsd
+ ;;
+ amdahl)
+ basic_machine=580-amdahl
+ basic_os=sysv
+ ;;
+ amiga)
+ basic_machine=m68k-unknown
+ basic_os=
+ ;;
+ amigaos | amigados)
+ basic_machine=m68k-unknown
+ basic_os=amigaos
+ ;;
+ amigaunix | amix)
+ basic_machine=m68k-unknown
+ basic_os=sysv4
+ ;;
+ apollo68)
+ basic_machine=m68k-apollo
+ basic_os=sysv
+ ;;
+ apollo68bsd)
+ basic_machine=m68k-apollo
+ basic_os=bsd
+ ;;
+ aros)
+ basic_machine=i386-pc
+ basic_os=aros
+ ;;
+ aux)
+ basic_machine=m68k-apple
+ basic_os=aux
+ ;;
+ balance)
+ basic_machine=ns32k-sequent
+ basic_os=dynix
+ ;;
+ blackfin)
+ basic_machine=bfin-unknown
+ basic_os=linux
+ ;;
+ cegcc)
+ basic_machine=arm-unknown
+ basic_os=cegcc
+ ;;
+ convex-c1)
+ basic_machine=c1-convex
+ basic_os=bsd
+ ;;
+ convex-c2)
+ basic_machine=c2-convex
+ basic_os=bsd
+ ;;
+ convex-c32)
+ basic_machine=c32-convex
+ basic_os=bsd
+ ;;
+ convex-c34)
+ basic_machine=c34-convex
+ basic_os=bsd
+ ;;
+ convex-c38)
+ basic_machine=c38-convex
+ basic_os=bsd
+ ;;
+ cray)
+ basic_machine=j90-cray
+ basic_os=unicos
+ ;;
+ crds | unos)
+ basic_machine=m68k-crds
+ basic_os=
+ ;;
+ da30)
+ basic_machine=m68k-da30
+ basic_os=
+ ;;
+ decstation | pmax | pmin | dec3100 | decstatn)
+ basic_machine=mips-dec
+ basic_os=
+ ;;
+ delta88)
+ basic_machine=m88k-motorola
+ basic_os=sysv3
+ ;;
+ dicos)
+ basic_machine=i686-pc
+ basic_os=dicos
+ ;;
+ djgpp)
+ basic_machine=i586-pc
+ basic_os=msdosdjgpp
+ ;;
+ ebmon29k)
+ basic_machine=a29k-amd
+ basic_os=ebmon
+ ;;
+ es1800 | OSE68k | ose68k | ose | OSE)
+ basic_machine=m68k-ericsson
+ basic_os=ose
+ ;;
+ gmicro)
+ basic_machine=tron-gmicro
+ basic_os=sysv
+ ;;
+ go32)
+ basic_machine=i386-pc
+ basic_os=go32
+ ;;
+ h8300hms)
+ basic_machine=h8300-hitachi
+ basic_os=hms
+ ;;
+ h8300xray)
+ basic_machine=h8300-hitachi
+ basic_os=xray
+ ;;
+ h8500hms)
+ basic_machine=h8500-hitachi
+ basic_os=hms
+ ;;
+ harris)
+ basic_machine=m88k-harris
+ basic_os=sysv3
+ ;;
+ hp300 | hp300hpux)
+ basic_machine=m68k-hp
+ basic_os=hpux
+ ;;
+ hp300bsd)
+ basic_machine=m68k-hp
+ basic_os=bsd
+ ;;
+ hppaosf)
+ basic_machine=hppa1.1-hp
+ basic_os=osf
+ ;;
+ hppro)
+ basic_machine=hppa1.1-hp
+ basic_os=proelf
+ ;;
+ i386mach)
+ basic_machine=i386-mach
+ basic_os=mach
+ ;;
+ isi68 | isi)
+ basic_machine=m68k-isi
+ basic_os=sysv
+ ;;
+ m68knommu)
+ basic_machine=m68k-unknown
+ basic_os=linux
+ ;;
+ magnum | m3230)
+ basic_machine=mips-mips
+ basic_os=sysv
+ ;;
+ merlin)
+ basic_machine=ns32k-utek
+ basic_os=sysv
+ ;;
+ mingw64)
+ basic_machine=x86_64-pc
+ basic_os=mingw64
+ ;;
+ mingw32)
+ basic_machine=i686-pc
+ basic_os=mingw32
+ ;;
+ mingw32ce)
+ basic_machine=arm-unknown
+ basic_os=mingw32ce
+ ;;
+ monitor)
+ basic_machine=m68k-rom68k
+ basic_os=coff
+ ;;
+ morphos)
+ basic_machine=powerpc-unknown
+ basic_os=morphos
+ ;;
+ moxiebox)
+ basic_machine=moxie-unknown
+ basic_os=moxiebox
+ ;;
+ msdos)
+ basic_machine=i386-pc
+ basic_os=msdos
+ ;;
+ msys)
+ basic_machine=i686-pc
+ basic_os=msys
+ ;;
+ mvs)
+ basic_machine=i370-ibm
+ basic_os=mvs
+ ;;
+ nacl)
+ basic_machine=le32-unknown
+ basic_os=nacl
+ ;;
+ ncr3000)
+ basic_machine=i486-ncr
+ basic_os=sysv4
+ ;;
+ netbsd386)
+ basic_machine=i386-pc
+ basic_os=netbsd
+ ;;
+ netwinder)
+ basic_machine=armv4l-rebel
+ basic_os=linux
+ ;;
+ news | news700 | news800 | news900)
+ basic_machine=m68k-sony
+ basic_os=newsos
+ ;;
+ news1000)
+ basic_machine=m68030-sony
+ basic_os=newsos
+ ;;
+ necv70)
+ basic_machine=v70-nec
+ basic_os=sysv
+ ;;
+ nh3000)
+ basic_machine=m68k-harris
+ basic_os=cxux
+ ;;
+ nh[45]000)
+ basic_machine=m88k-harris
+ basic_os=cxux
+ ;;
+ nindy960)
+ basic_machine=i960-intel
+ basic_os=nindy
+ ;;
+ mon960)
+ basic_machine=i960-intel
+ basic_os=mon960
+ ;;
+ nonstopux)
+ basic_machine=mips-compaq
+ basic_os=nonstopux
+ ;;
+ os400)
+ basic_machine=powerpc-ibm
+ basic_os=os400
+ ;;
+ OSE68000 | ose68000)
+ basic_machine=m68000-ericsson
+ basic_os=ose
+ ;;
+ os68k)
+ basic_machine=m68k-none
+ basic_os=os68k
+ ;;
+ paragon)
+ basic_machine=i860-intel
+ basic_os=osf
+ ;;
+ parisc)
+ basic_machine=hppa-unknown
+ basic_os=linux
+ ;;
+ psp)
+ basic_machine=mipsallegrexel-sony
+ basic_os=psp
+ ;;
+ pw32)
+ basic_machine=i586-unknown
+ basic_os=pw32
+ ;;
+ rdos | rdos64)
+ basic_machine=x86_64-pc
+ basic_os=rdos
+ ;;
+ rdos32)
+ basic_machine=i386-pc
+ basic_os=rdos
+ ;;
+ rom68k)
+ basic_machine=m68k-rom68k
+ basic_os=coff
+ ;;
+ sa29200)
+ basic_machine=a29k-amd
+ basic_os=udi
+ ;;
+ sei)
+ basic_machine=mips-sei
+ basic_os=seiux
+ ;;
+ sequent)
+ basic_machine=i386-sequent
+ basic_os=
+ ;;
+ sps7)
+ basic_machine=m68k-bull
+ basic_os=sysv2
+ ;;
+ st2000)
+ basic_machine=m68k-tandem
+ basic_os=
+ ;;
+ stratus)
+ basic_machine=i860-stratus
+ basic_os=sysv4
+ ;;
+ sun2)
+ basic_machine=m68000-sun
+ basic_os=
+ ;;
+ sun2os3)
+ basic_machine=m68000-sun
+ basic_os=sunos3
+ ;;
+ sun2os4)
+ basic_machine=m68000-sun
+ basic_os=sunos4
+ ;;
+ sun3)
+ basic_machine=m68k-sun
+ basic_os=
+ ;;
+ sun3os3)
+ basic_machine=m68k-sun
+ basic_os=sunos3
+ ;;
+ sun3os4)
+ basic_machine=m68k-sun
+ basic_os=sunos4
+ ;;
+ sun4)
+ basic_machine=sparc-sun
+ basic_os=
+ ;;
+ sun4os3)
+ basic_machine=sparc-sun
+ basic_os=sunos3
+ ;;
+ sun4os4)
+ basic_machine=sparc-sun
+ basic_os=sunos4
+ ;;
+ sun4sol2)
+ basic_machine=sparc-sun
+ basic_os=solaris2
+ ;;
+ sun386 | sun386i | roadrunner)
+ basic_machine=i386-sun
+ basic_os=
+ ;;
+ sv1)
+ basic_machine=sv1-cray
+ basic_os=unicos
+ ;;
+ symmetry)
+ basic_machine=i386-sequent
+ basic_os=dynix
+ ;;
+ t3e)
+ basic_machine=alphaev5-cray
+ basic_os=unicos
+ ;;
+ t90)
+ basic_machine=t90-cray
+ basic_os=unicos
+ ;;
+ toad1)
+ basic_machine=pdp10-xkl
+ basic_os=tops20
+ ;;
+ tpf)
+ basic_machine=s390x-ibm
+ basic_os=tpf
+ ;;
+ udi29k)
+ basic_machine=a29k-amd
+ basic_os=udi
+ ;;
+ ultra3)
+ basic_machine=a29k-nyu
+ basic_os=sym1
+ ;;
+ v810 | necv810)
+ basic_machine=v810-nec
+ basic_os=none
+ ;;
+ vaxv)
+ basic_machine=vax-dec
+ basic_os=sysv
+ ;;
+ vms)
+ basic_machine=vax-dec
+ basic_os=vms
+ ;;
+ vsta)
+ basic_machine=i386-pc
+ basic_os=vsta
+ ;;
+ vxworks960)
+ basic_machine=i960-wrs
+ basic_os=vxworks
+ ;;
+ vxworks68)
+ basic_machine=m68k-wrs
+ basic_os=vxworks
+ ;;
+ vxworks29k)
+ basic_machine=a29k-wrs
+ basic_os=vxworks
+ ;;
+ xbox)
+ basic_machine=i686-pc
+ basic_os=mingw32
+ ;;
+ ymp)
+ basic_machine=ymp-cray
+ basic_os=unicos
+ ;;
+ *)
+ basic_machine=$1
+ basic_os=
+ ;;
+ esac
+ ;;
+esac
+
+# Decode 1-component or ad-hoc basic machines
+case $basic_machine in
+ # Here we handle the default manufacturer of certain CPU types. It is in
+ # some cases the only manufacturer, in others, it is the most popular.
+ w89k)
+ cpu=hppa1.1
+ vendor=winbond
+ ;;
+ op50n)
+ cpu=hppa1.1
+ vendor=oki
+ ;;
+ op60c)
+ cpu=hppa1.1
+ vendor=oki
+ ;;
+ ibm*)
+ cpu=i370
+ vendor=ibm
+ ;;
+ orion105)
+ cpu=clipper
+ vendor=highlevel
+ ;;
+ mac | mpw | mac-mpw)
+ cpu=m68k
+ vendor=apple
+ ;;
+ pmac | pmac-mpw)
+ cpu=powerpc
+ vendor=apple
+ ;;
+
+ # Recognize the various machine names and aliases which stand
+ # for a CPU type and a company and sometimes even an OS.
+ 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+ cpu=m68000
+ vendor=att
+ ;;
+ 3b*)
+ cpu=we32k
+ vendor=att
+ ;;
+ bluegene*)
+ cpu=powerpc
+ vendor=ibm
+ basic_os=cnk
+ ;;
+ decsystem10* | dec10*)
+ cpu=pdp10
+ vendor=dec
+ basic_os=tops10
+ ;;
+ decsystem20* | dec20*)
+ cpu=pdp10
+ vendor=dec
+ basic_os=tops20
+ ;;
+ delta | 3300 | motorola-3300 | motorola-delta \
+ | 3300-motorola | delta-motorola)
+ cpu=m68k
+ vendor=motorola
+ ;;
+ dpx2*)
+ cpu=m68k
+ vendor=bull
+ basic_os=sysv3
+ ;;
+ encore | umax | mmax)
+ cpu=ns32k
+ vendor=encore
+ ;;
+ elxsi)
+ cpu=elxsi
+ vendor=elxsi
+ basic_os=${basic_os:-bsd}
+ ;;
+ fx2800)
+ cpu=i860
+ vendor=alliant
+ ;;
+ genix)
+ cpu=ns32k
+ vendor=ns
+ ;;
+ h3050r* | hiux*)
+ cpu=hppa1.1
+ vendor=hitachi
+ basic_os=hiuxwe2
+ ;;
+ hp3k9[0-9][0-9] | hp9[0-9][0-9])
+ cpu=hppa1.0
+ vendor=hp
+ ;;
+ hp9k2[0-9][0-9] | hp9k31[0-9])
+ cpu=m68000
+ vendor=hp
+ ;;
+ hp9k3[2-9][0-9])
+ cpu=m68k
+ vendor=hp
+ ;;
+ hp9k6[0-9][0-9] | hp6[0-9][0-9])
+ cpu=hppa1.0
+ vendor=hp
+ ;;
+ hp9k7[0-79][0-9] | hp7[0-79][0-9])
+ cpu=hppa1.1
+ vendor=hp
+ ;;
+ hp9k78[0-9] | hp78[0-9])
+ # FIXME: really hppa2.0-hp
+ cpu=hppa1.1
+ vendor=hp
+ ;;
+ hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
+ # FIXME: really hppa2.0-hp
+ cpu=hppa1.1
+ vendor=hp
+ ;;
+ hp9k8[0-9][13679] | hp8[0-9][13679])
+ cpu=hppa1.1
+ vendor=hp
+ ;;
+ hp9k8[0-9][0-9] | hp8[0-9][0-9])
+ cpu=hppa1.0
+ vendor=hp
+ ;;
+ i*86v32)
+ cpu=$(echo "$1" | sed -e 's/86.*/86/')
+ vendor=pc
+ basic_os=sysv32
+ ;;
+ i*86v4*)
+ cpu=$(echo "$1" | sed -e 's/86.*/86/')
+ vendor=pc
+ basic_os=sysv4
+ ;;
+ i*86v)
+ cpu=$(echo "$1" | sed -e 's/86.*/86/')
+ vendor=pc
+ basic_os=sysv
+ ;;
+ i*86sol2)
+ cpu=$(echo "$1" | sed -e 's/86.*/86/')
+ vendor=pc
+ basic_os=solaris2
+ ;;
+ j90 | j90-cray)
+ cpu=j90
+ vendor=cray
+ basic_os=${basic_os:-unicos}
+ ;;
+ iris | iris4d)
+ cpu=mips
+ vendor=sgi
+ case $basic_os in
+ irix*)
+ ;;
+ *)
+ basic_os=irix4
+ ;;
+ esac
+ ;;
+ miniframe)
+ cpu=m68000
+ vendor=convergent
+ ;;
+ *mint | mint[0-9]* | *MiNT | *MiNT[0-9]*)
+ cpu=m68k
+ vendor=atari
+ basic_os=mint
+ ;;
+ news-3600 | risc-news)
+ cpu=mips
+ vendor=sony
+ basic_os=newsos
+ ;;
+ next | m*-next)
+ cpu=m68k
+ vendor=next
+ case $basic_os in
+ openstep*)
+ ;;
+ nextstep*)
+ ;;
+ ns2*)
+ basic_os=nextstep2
+ ;;
+ *)
+ basic_os=nextstep3
+ ;;
+ esac
+ ;;
+ np1)
+ cpu=np1
+ vendor=gould
+ ;;
+ op50n-* | op60c-*)
+ cpu=hppa1.1
+ vendor=oki
+ basic_os=proelf
+ ;;
+ pa-hitachi)
+ cpu=hppa1.1
+ vendor=hitachi
+ basic_os=hiuxwe2
+ ;;
+ pbd)
+ cpu=sparc
+ vendor=tti
+ ;;
+ pbb)
+ cpu=m68k
+ vendor=tti
+ ;;
+ pc532)
+ cpu=ns32k
+ vendor=pc532
+ ;;
+ pn)
+ cpu=pn
+ vendor=gould
+ ;;
+ power)
+ cpu=power
+ vendor=ibm
+ ;;
+ ps2)
+ cpu=i386
+ vendor=ibm
+ ;;
+ rm[46]00)
+ cpu=mips
+ vendor=siemens
+ ;;
+ rtpc | rtpc-*)
+ cpu=romp
+ vendor=ibm
+ ;;
+ sde)
+ cpu=mipsisa32
+ vendor=sde
+ basic_os=${basic_os:-elf}
+ ;;
+ simso-wrs)
+ cpu=sparclite
+ vendor=wrs
+ basic_os=vxworks
+ ;;
+ tower | tower-32)
+ cpu=m68k
+ vendor=ncr
+ ;;
+ vpp*|vx|vx-*)
+ cpu=f301
+ vendor=fujitsu
+ ;;
+ w65)
+ cpu=w65
+ vendor=wdc
+ ;;
+ w89k-*)
+ cpu=hppa1.1
+ vendor=winbond
+ basic_os=proelf
+ ;;
+ none)
+ cpu=none
+ vendor=none
+ ;;
+ leon|leon[3-9])
+ cpu=sparc
+ vendor=$basic_machine
+ ;;
+ leon-*|leon[3-9]-*)
+ cpu=sparc
+ vendor=$(echo "$basic_machine" | sed 's/-.*//')
+ ;;
+
+ *-*)
+ # shellcheck disable=SC2162
+ IFS="-" read cpu vendor <<EOF
+$basic_machine
+EOF
+ ;;
+ # We use `pc' rather than `unknown'
+ # because (1) that's what they normally are, and
+ # (2) the word "unknown" tends to confuse beginning users.
+ i*86 | x86_64)
+ cpu=$basic_machine
+ vendor=pc
+ ;;
+ # These rules are duplicated from below for sake of the special case above;
+ # i.e. things that normalized to x86 arches should also default to "pc"
+ pc98)
+ cpu=i386
+ vendor=pc
+ ;;
+ x64 | amd64)
+ cpu=x86_64
+ vendor=pc
+ ;;
+ # Recognize the basic CPU types without company name.
+ *)
+ cpu=$basic_machine
+ vendor=unknown
+ ;;
+esac
+
+unset -v basic_machine
+
+# Decode basic machines in the full and proper CPU-Company form.
+case $cpu-$vendor in
+ # Here we handle the default manufacturer of certain CPU types in canonical form. It is in
+ # some cases the only manufacturer, in others, it is the most popular.
+ craynv-unknown)
+ vendor=cray
+ basic_os=${basic_os:-unicosmp}
+ ;;
+ c90-unknown | c90-cray)
+ vendor=cray
+ basic_os=${Basic_os:-unicos}
+ ;;
+ fx80-unknown)
+ vendor=alliant
+ ;;
+ romp-unknown)
+ vendor=ibm
+ ;;
+ mmix-unknown)
+ vendor=knuth
+ ;;
+ microblaze-unknown | microblazeel-unknown)
+ vendor=xilinx
+ ;;
+ rs6000-unknown)
+ vendor=ibm
+ ;;
+ vax-unknown)
+ vendor=dec
+ ;;
+ pdp11-unknown)
+ vendor=dec
+ ;;
+ we32k-unknown)
+ vendor=att
+ ;;
+ cydra-unknown)
+ vendor=cydrome
+ ;;
+ i370-ibm*)
+ vendor=ibm
+ ;;
+ orion-unknown)
+ vendor=highlevel
+ ;;
+ xps-unknown | xps100-unknown)
+ cpu=xps100
+ vendor=honeywell
+ ;;
+
+ # Here we normalize CPU types with a missing or matching vendor
+ dpx20-unknown | dpx20-bull)
+ cpu=rs6000
+ vendor=bull
+ basic_os=${basic_os:-bosx}
+ ;;
+
+ # Here we normalize CPU types irrespective of the vendor
+ amd64-*)
+ cpu=x86_64
+ ;;
+ blackfin-*)
+ cpu=bfin
+ basic_os=linux
+ ;;
+ c54x-*)
+ cpu=tic54x
+ ;;
+ c55x-*)
+ cpu=tic55x
+ ;;
+ c6x-*)
+ cpu=tic6x
+ ;;
+ e500v[12]-*)
+ cpu=powerpc
+ basic_os=${basic_os}"spe"
+ ;;
+ mips3*-*)
+ cpu=mips64
+ ;;
+ ms1-*)
+ cpu=mt
+ ;;
+ m68knommu-*)
+ cpu=m68k
+ basic_os=linux
+ ;;
+ m9s12z-* | m68hcs12z-* | hcs12z-* | s12z-*)
+ cpu=s12z
+ ;;
+ openrisc-*)
+ cpu=or32
+ ;;
+ parisc-*)
+ cpu=hppa
+ basic_os=linux
+ ;;
+ pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
+ cpu=i586
+ ;;
+ pentiumpro-* | p6-* | 6x86-* | athlon-* | athalon_*-*)
+ cpu=i686
+ ;;
+ pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
+ cpu=i686
+ ;;
+ pentium4-*)
+ cpu=i786
+ ;;
+ pc98-*)
+ cpu=i386
+ ;;
+ ppc-* | ppcbe-*)
+ cpu=powerpc
+ ;;
+ ppcle-* | powerpclittle-*)
+ cpu=powerpcle
+ ;;
+ ppc64-*)
+ cpu=powerpc64
+ ;;
+ ppc64le-* | powerpc64little-*)
+ cpu=powerpc64le
+ ;;
+ sb1-*)
+ cpu=mipsisa64sb1
+ ;;
+ sb1el-*)
+ cpu=mipsisa64sb1el
+ ;;
+ sh5e[lb]-*)
+ cpu=$(echo "$cpu" | sed 's/^\(sh.\)e\(.\)$/\1\2e/')
+ ;;
+ spur-*)
+ cpu=spur
+ ;;
+ strongarm-* | thumb-*)
+ cpu=arm
+ ;;
+ tx39-*)
+ cpu=mipstx39
+ ;;
+ tx39el-*)
+ cpu=mipstx39el
+ ;;
+ x64-*)
+ cpu=x86_64
+ ;;
+ xscale-* | xscalee[bl]-*)
+ cpu=$(echo "$cpu" | sed 's/^xscale/arm/')
+ ;;
+ arm64-*)
+ cpu=aarch64
+ ;;
+
+ # Recognize the canonical CPU Types that limit and/or modify the
+ # company names they are paired with.
+ cr16-*)
+ basic_os=${basic_os:-elf}
+ ;;
+ crisv32-* | etraxfs*-*)
+ cpu=crisv32
+ vendor=axis
+ ;;
+ cris-* | etrax*-*)
+ cpu=cris
+ vendor=axis
+ ;;
+ crx-*)
+ basic_os=${basic_os:-elf}
+ ;;
+ neo-tandem)
+ cpu=neo
+ vendor=tandem
+ ;;
+ nse-tandem)
+ cpu=nse
+ vendor=tandem
+ ;;
+ nsr-tandem)
+ cpu=nsr
+ vendor=tandem
+ ;;
+ nsv-tandem)
+ cpu=nsv
+ vendor=tandem
+ ;;
+ nsx-tandem)
+ cpu=nsx
+ vendor=tandem
+ ;;
+ mipsallegrexel-sony)
+ cpu=mipsallegrexel
+ vendor=sony
+ ;;
+ tile*-*)
+ basic_os=${basic_os:-linux-gnu}
+ ;;
+
+ *)
+ # Recognize the canonical CPU types that are allowed with any
+ # company name.
+ case $cpu in
+ 1750a | 580 \
+ | a29k \
+ | aarch64 | aarch64_be \
+ | abacus \
+ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] \
+ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] \
+ | alphapca5[67] | alpha64pca5[67] \
+ | am33_2.0 \
+ | amdgcn \
+ | arc | arceb \
+ | arm | arm[lb]e | arme[lb] | armv* \
+ | avr | avr32 \
+ | asmjs \
+ | ba \
+ | be32 | be64 \
+ | bfin | bpf | bs2000 \
+ | c[123]* | c30 | [cjt]90 | c4x \
+ | c8051 | clipper | craynv | csky | cydra \
+ | d10v | d30v | dlx | dsp16xx \
+ | e2k | elxsi | epiphany \
+ | f30[01] | f700 | fido | fr30 | frv | ft32 | fx80 \
+ | h8300 | h8500 \
+ | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
+ | hexagon \
+ | i370 | i*86 | i860 | i960 | ia16 | ia64 \
+ | ip2k | iq2000 \
+ | k1om \
+ | le32 | le64 \
+ | lm32 \
+ | loongarch32 | loongarch64 | loongarchx32 \
+ | m32c | m32r | m32rle \
+ | m5200 | m68000 | m680[012346]0 | m68360 | m683?2 | m68k \
+ | m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x \
+ | m88110 | m88k | maxq | mb | mcore | mep | metag \
+ | microblaze | microblazeel \
+ | mips | mipsbe | mipseb | mipsel | mipsle \
+ | mips16 \
+ | mips64 | mips64eb | mips64el \
+ | mips64octeon | mips64octeonel \
+ | mips64orion | mips64orionel \
+ | mips64r5900 | mips64r5900el \
+ | mips64vr | mips64vrel \
+ | mips64vr4100 | mips64vr4100el \
+ | mips64vr4300 | mips64vr4300el \
+ | mips64vr5000 | mips64vr5000el \
+ | mips64vr5900 | mips64vr5900el \
+ | mipsisa32 | mipsisa32el \
+ | mipsisa32r2 | mipsisa32r2el \
+ | mipsisa32r6 | mipsisa32r6el \
+ | mipsisa64 | mipsisa64el \
+ | mipsisa64r2 | mipsisa64r2el \
+ | mipsisa64r6 | mipsisa64r6el \
+ | mipsisa64sb1 | mipsisa64sb1el \
+ | mipsisa64sr71k | mipsisa64sr71kel \
+ | mipsr5900 | mipsr5900el \
+ | mipstx39 | mipstx39el \
+ | mmix \
+ | mn10200 | mn10300 \
+ | moxie \
+ | mt \
+ | msp430 \
+ | nds32 | nds32le | nds32be \
+ | nfp \
+ | nios | nios2 | nios2eb | nios2el \
+ | none | np1 | ns16k | ns32k | nvptx \
+ | open8 \
+ | or1k* \
+ | or32 \
+ | orion \
+ | picochip \
+ | pdp10 | pdp11 | pj | pjl | pn | power \
+ | powerpc | powerpc64 | powerpc64le | powerpcle | powerpcspe \
+ | pru \
+ | pyramid \
+ | riscv | riscv32 | riscv32be | riscv64 | riscv64be \
+ | rl78 | romp | rs6000 | rx \
+ | s390 | s390x \
+ | score \
+ | sh | shl \
+ | sh[1234] | sh[24]a | sh[24]ae[lb] | sh[23]e | she[lb] | sh[lb]e \
+ | sh[1234]e[lb] | sh[12345][lb]e | sh[23]ele | sh64 | sh64le \
+ | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet \
+ | sparclite \
+ | sparcv8 | sparcv9 | sparcv9b | sparcv9v | sv1 | sx* \
+ | spu \
+ | tahoe \
+ | thumbv7* \
+ | tic30 | tic4x | tic54x | tic55x | tic6x | tic80 \
+ | tron \
+ | ubicom32 \
+ | v70 | v850 | v850e | v850e1 | v850es | v850e2 | v850e2v3 \
+ | vax \
+ | visium \
+ | w65 \
+ | wasm32 | wasm64 \
+ | we32k \
+ | x86 | x86_64 | xc16x | xgate | xps100 \
+ | xstormy16 | xtensa* \
+ | ymp \
+ | z8k | z80)
+ ;;
+
+ *)
+ echo Invalid configuration \`"$1"\': machine \`"$cpu-$vendor"\' not recognized 1>&2
+ exit 1
+ ;;
+ esac
+ ;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $vendor in
+ digital*)
+ vendor=dec
+ ;;
+ commodore*)
+ vendor=cbm
+ ;;
+ *)
+ ;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if test x$basic_os != x
+then
+
+# First recognize some ad-hoc caes, or perhaps split kernel-os, or else just
+# set os.
+case $basic_os in
+ gnu/linux*)
+ kernel=linux
+ os=$(echo $basic_os | sed -e 's|gnu/linux|gnu|')
+ ;;
+ os2-emx)
+ kernel=os2
+ os=$(echo $basic_os | sed -e 's|os2-emx|emx|')
+ ;;
+ nto-qnx*)
+ kernel=nto
+ os=$(echo $basic_os | sed -e 's|nto-qnx|qnx|')
+ ;;
+ *-*)
+ # shellcheck disable=SC2162
+ IFS="-" read kernel os <<EOF
+$basic_os
+EOF
+ ;;
+ # Default OS when just kernel was specified
+ nto*)
+ kernel=nto
+ os=$(echo $basic_os | sed -e 's|nto|qnx|')
+ ;;
+ linux*)
+ kernel=linux
+ os=$(echo $basic_os | sed -e 's|linux|gnu|')
+ ;;
+ *)
+ kernel=
+ os=$basic_os
+ ;;
+esac
+
+# Now, normalize the OS (knowing we just have one component, it's not a kernel,
+# etc.)
+case $os in
+ # First match some system type aliases that might get confused
+ # with valid system types.
+ # solaris* is a basic system type, with this one exception.
+ auroraux)
+ os=auroraux
+ ;;
+ bluegene*)
+ os=cnk
+ ;;
+ solaris1 | solaris1.*)
+ os=$(echo $os | sed -e 's|solaris1|sunos4|')
+ ;;
+ solaris)
+ os=solaris2
+ ;;
+ unixware*)
+ os=sysv4.2uw
+ ;;
+ # es1800 is here to avoid being matched by es* (a different OS)
+ es1800*)
+ os=ose
+ ;;
+ # Some version numbers need modification
+ chorusos*)
+ os=chorusos
+ ;;
+ isc)
+ os=isc2.2
+ ;;
+ sco6)
+ os=sco5v6
+ ;;
+ sco5)
+ os=sco3.2v5
+ ;;
+ sco4)
+ os=sco3.2v4
+ ;;
+ sco3.2.[4-9]*)
+ os=$(echo $os | sed -e 's/sco3.2./sco3.2v/')
+ ;;
+ sco*v* | scout)
+ # Don't match below
+ ;;
+ sco*)
+ os=sco3.2v2
+ ;;
+ psos*)
+ os=psos
+ ;;
+ qnx*)
+ os=qnx
+ ;;
+ hiux*)
+ os=hiuxwe2
+ ;;
+ lynx*178)
+ os=lynxos178
+ ;;
+ lynx*5)
+ os=lynxos5
+ ;;
+ lynxos*)
+ # don't get caught up in next wildcard
+ ;;
+ lynx*)
+ os=lynxos
+ ;;
+ mac[0-9]*)
+ os=$(echo "$os" | sed -e 's|mac|macos|')
+ ;;
+ opened*)
+ os=openedition
+ ;;
+ os400*)
+ os=os400
+ ;;
+ sunos5*)
+ os=$(echo "$os" | sed -e 's|sunos5|solaris2|')
+ ;;
+ sunos6*)
+ os=$(echo "$os" | sed -e 's|sunos6|solaris3|')
+ ;;
+ wince*)
+ os=wince
+ ;;
+ utek*)
+ os=bsd
+ ;;
+ dynix*)
+ os=bsd
+ ;;
+ acis*)
+ os=aos
+ ;;
+ atheos*)
+ os=atheos
+ ;;
+ syllable*)
+ os=syllable
+ ;;
+ 386bsd)
+ os=bsd
+ ;;
+ ctix* | uts*)
+ os=sysv
+ ;;
+ nova*)
+ os=rtmk-nova
+ ;;
+ ns2)
+ os=nextstep2
+ ;;
+ # Preserve the version number of sinix5.
+ sinix5.*)
+ os=$(echo $os | sed -e 's|sinix|sysv|')
+ ;;
+ sinix*)
+ os=sysv4
+ ;;
+ tpf*)
+ os=tpf
+ ;;
+ triton*)
+ os=sysv3
+ ;;
+ oss*)
+ os=sysv3
+ ;;
+ svr4*)
+ os=sysv4
+ ;;
+ svr3)
+ os=sysv3
+ ;;
+ sysvr4)
+ os=sysv4
+ ;;
+ ose*)
+ os=ose
+ ;;
+ *mint | mint[0-9]* | *MiNT | MiNT[0-9]*)
+ os=mint
+ ;;
+ dicos*)
+ os=dicos
+ ;;
+ pikeos*)
+ # Until real need of OS specific support for
+ # particular features comes up, bare metal
+ # configurations are quite functional.
+ case $cpu in
+ arm*)
+ os=eabi
+ ;;
+ *)
+ os=elf
+ ;;
+ esac
+ ;;
+ *)
+ # No normalization, but not necessarily accepted, that comes below.
+ ;;
+esac
+
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system. Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+kernel=
+case $cpu-$vendor in
+ score-*)
+ os=elf
+ ;;
+ spu-*)
+ os=elf
+ ;;
+ *-acorn)
+ os=riscix1.2
+ ;;
+ arm*-rebel)
+ kernel=linux
+ os=gnu
+ ;;
+ arm*-semi)
+ os=aout
+ ;;
+ c4x-* | tic4x-*)
+ os=coff
+ ;;
+ c8051-*)
+ os=elf
+ ;;
+ clipper-intergraph)
+ os=clix
+ ;;
+ hexagon-*)
+ os=elf
+ ;;
+ tic54x-*)
+ os=coff
+ ;;
+ tic55x-*)
+ os=coff
+ ;;
+ tic6x-*)
+ os=coff
+ ;;
+ # This must come before the *-dec entry.
+ pdp10-*)
+ os=tops20
+ ;;
+ pdp11-*)
+ os=none
+ ;;
+ *-dec | vax-*)
+ os=ultrix4.2
+ ;;
+ m68*-apollo)
+ os=domain
+ ;;
+ i386-sun)
+ os=sunos4.0.2
+ ;;
+ m68000-sun)
+ os=sunos3
+ ;;
+ m68*-cisco)
+ os=aout
+ ;;
+ mep-*)
+ os=elf
+ ;;
+ mips*-cisco)
+ os=elf
+ ;;
+ mips*-*)
+ os=elf
+ ;;
+ or32-*)
+ os=coff
+ ;;
+ *-tti) # must be before sparc entry or we get the wrong os.
+ os=sysv3
+ ;;
+ sparc-* | *-sun)
+ os=sunos4.1.1
+ ;;
+ pru-*)
+ os=elf
+ ;;
+ *-be)
+ os=beos
+ ;;
+ *-ibm)
+ os=aix
+ ;;
+ *-knuth)
+ os=mmixware
+ ;;
+ *-wec)
+ os=proelf
+ ;;
+ *-winbond)
+ os=proelf
+ ;;
+ *-oki)
+ os=proelf
+ ;;
+ *-hp)
+ os=hpux
+ ;;
+ *-hitachi)
+ os=hiux
+ ;;
+ i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+ os=sysv
+ ;;
+ *-cbm)
+ os=amigaos
+ ;;
+ *-dg)
+ os=dgux
+ ;;
+ *-dolphin)
+ os=sysv3
+ ;;
+ m68k-ccur)
+ os=rtu
+ ;;
+ m88k-omron*)
+ os=luna
+ ;;
+ *-next)
+ os=nextstep
+ ;;
+ *-sequent)
+ os=ptx
+ ;;
+ *-crds)
+ os=unos
+ ;;
+ *-ns)
+ os=genix
+ ;;
+ i370-*)
+ os=mvs
+ ;;
+ *-gould)
+ os=sysv
+ ;;
+ *-highlevel)
+ os=bsd
+ ;;
+ *-encore)
+ os=bsd
+ ;;
+ *-sgi)
+ os=irix
+ ;;
+ *-siemens)
+ os=sysv4
+ ;;
+ *-masscomp)
+ os=rtu
+ ;;
+ f30[01]-fujitsu | f700-fujitsu)
+ os=uxpv
+ ;;
+ *-rom68k)
+ os=coff
+ ;;
+ *-*bug)
+ os=coff
+ ;;
+ *-apple)
+ os=macos
+ ;;
+ *-atari*)
+ os=mint
+ ;;
+ *-wrs)
+ os=vxworks
+ ;;
+ *)
+ os=none
+ ;;
+esac
+
+fi
+
+# Now, validate our (potentially fixed-up) OS.
+case $os in
+ # Sometimes we do "kernel-abi", so those need to count as OSes.
+ musl* | newlib* | uclibc*)
+ ;;
+ # Likewise for "kernel-libc"
+ eabi* | gnueabi*)
+ ;;
+ # Now accept the basic system types.
+ # The portable systems comes first.
+ # Each alternative MUST end in a * to match a version number.
+ gnu* | android* | bsd* | mach* | minix* | genix* | ultrix* | irix* \
+ | *vms* | esix* | aix* | cnk* | sunos | sunos[34]* \
+ | hpux* | unos* | osf* | luna* | dgux* | auroraux* | solaris* \
+ | sym* | plan9* | psp* | sim* | xray* | os68k* | v88r* \
+ | hiux* | abug | nacl* | netware* | windows* \
+ | os9* | macos* | osx* | ios* \
+ | mpw* | magic* | mmixware* | mon960* | lnews* \
+ | amigaos* | amigados* | msdos* | newsos* | unicos* | aof* \
+ | aos* | aros* | cloudabi* | sortix* | twizzler* \
+ | nindy* | vxsim* | vxworks* | ebmon* | hms* | mvs* \
+ | clix* | riscos* | uniplus* | iris* | isc* | rtu* | xenix* \
+ | mirbsd* | netbsd* | dicos* | openedition* | ose* \
+ | bitrig* | openbsd* | solidbsd* | libertybsd* | os108* \
+ | ekkobsd* | freebsd* | riscix* | lynxos* | os400* \
+ | bosx* | nextstep* | cxux* | aout* | elf* | oabi* \
+ | ptx* | coff* | ecoff* | winnt* | domain* | vsta* \
+ | udi* | lites* | ieee* | go32* | aux* | hcos* \
+ | chorusrdb* | cegcc* | glidix* \
+ | cygwin* | msys* | pe* | moss* | proelf* | rtems* \
+ | midipix* | mingw32* | mingw64* | mint* \
+ | uxpv* | beos* | mpeix* | udk* | moxiebox* \
+ | interix* | uwin* | mks* | rhapsody* | darwin* \
+ | openstep* | oskit* | conix* | pw32* | nonstopux* \
+ | storm-chaos* | tops10* | tenex* | tops20* | its* \
+ | os2* | vos* | palmos* | uclinux* | nucleus* | morphos* \
+ | scout* | superux* | sysv* | rtmk* | tpf* | windiss* \
+ | powermax* | dnix* | nx6 | nx7 | sei* | dragonfly* \
+ | skyos* | haiku* | rdos* | toppers* | drops* | es* \
+ | onefs* | tirtos* | phoenix* | fuchsia* | redox* | bme* \
+ | midnightbsd* | amdhsa* | unleashed* | emscripten* | wasi* \
+ | nsk* | powerunix* | genode* | zvmoe* | qnx* | emx*)
+ ;;
+ # This one is extra strict with allowed versions
+ sco3.2v2 | sco3.2v[4-9]* | sco5v6*)
+ # Don't forget version if it is 3.2v4 or newer.
+ ;;
+ none)
+ ;;
+ *)
+ echo Invalid configuration \`"$1"\': OS \`"$os"\' not recognized 1>&2
+ exit 1
+ ;;
+esac
+
+# As a final step for OS-related things, validate the OS-kernel combination
+# (given a valid OS), if there is a kernel.
+case $kernel-$os in
+ linux-gnu* | linux-dietlibc* | linux-android* | linux-newlib* | linux-musl* | linux-uclibc* )
+ ;;
+ uclinux-uclibc* )
+ ;;
+ -dietlibc* | -newlib* | -musl* | -uclibc* )
+ # These are just libc implementations, not actual OSes, and thus
+ # require a kernel.
+ echo "Invalid configuration \`$1': libc \`$os' needs explicit kernel." 1>&2
+ exit 1
+ ;;
+ kfreebsd*-gnu* | kopensolaris*-gnu*)
+ ;;
+ nto-qnx*)
+ ;;
+ os2-emx)
+ ;;
+ *-eabi* | *-gnueabi*)
+ ;;
+ -*)
+ # Blank kernel with real OS is always fine.
+ ;;
+ *-*)
+ echo "Invalid configuration \`$1': Kernel \`$kernel' not known to work with OS \`$os'." 1>&2
+ exit 1
+ ;;
+esac
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer. We pick the logical manufacturer.
+case $vendor in
+ unknown)
+ case $cpu-$os in
+ *-riscix*)
+ vendor=acorn
+ ;;
+ *-sunos*)
+ vendor=sun
+ ;;
+ *-cnk* | *-aix*)
+ vendor=ibm
+ ;;
+ *-beos*)
+ vendor=be
+ ;;
+ *-hpux*)
+ vendor=hp
+ ;;
+ *-mpeix*)
+ vendor=hp
+ ;;
+ *-hiux*)
+ vendor=hitachi
+ ;;
+ *-unos*)
+ vendor=crds
+ ;;
+ *-dgux*)
+ vendor=dg
+ ;;
+ *-luna*)
+ vendor=omron
+ ;;
+ *-genix*)
+ vendor=ns
+ ;;
+ *-clix*)
+ vendor=intergraph
+ ;;
+ *-mvs* | *-opened*)
+ vendor=ibm
+ ;;
+ *-os400*)
+ vendor=ibm
+ ;;
+ s390-* | s390x-*)
+ vendor=ibm
+ ;;
+ *-ptx*)
+ vendor=sequent
+ ;;
+ *-tpf*)
+ vendor=ibm
+ ;;
+ *-vxsim* | *-vxworks* | *-windiss*)
+ vendor=wrs
+ ;;
+ *-aux*)
+ vendor=apple
+ ;;
+ *-hms*)
+ vendor=hitachi
+ ;;
+ *-mpw* | *-macos*)
+ vendor=apple
+ ;;
+ *-*mint | *-mint[0-9]* | *-*MiNT | *-MiNT[0-9]*)
+ vendor=atari
+ ;;
+ *-vos*)
+ vendor=stratus
+ ;;
+ esac
+ ;;
+esac
+
+echo "$cpu-$vendor-${kernel:+$kernel-}$os"
+exit
+
+# Local variables:
+# eval: (add-hook 'before-save-hook 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/contrib/jemalloc/build-aux/install-sh b/contrib/jemalloc/build-aux/install-sh
new file mode 100755
index 000000000000..ebc66913e940
--- /dev/null
+++ b/contrib/jemalloc/build-aux/install-sh
@@ -0,0 +1,250 @@
+#! /bin/sh
+#
+# install - install a program, script, or datafile
+# This comes from X11R5 (mit/util/scripts/install.sh).
+#
+# Copyright 1991 by the Massachusetts Institute of Technology
+#
+# Permission to use, copy, modify, distribute, and sell this software and its
+# documentation for any purpose is hereby granted without fee, provided that
+# the above copyright notice appear in all copies and that both that
+# copyright notice and this permission notice appear in supporting
+# documentation, and that the name of M.I.T. not be used in advertising or
+# publicity pertaining to distribution of the software without specific,
+# written prior permission. M.I.T. makes no representations about the
+# suitability of this software for any purpose. It is provided "as is"
+# without express or implied warranty.
+#
+# Calling this script install-sh is preferred over install.sh, to prevent
+# `make' implicit rules from creating a file called install from it
+# when there is no Makefile.
+#
+# This script is compatible with the BSD install script, but was written
+# from scratch. It can only install one file at a time, a restriction
+# shared with many OS's install programs.
+
+
+# set DOITPROG to echo to test this script
+
+# Don't use :- since 4.3BSD and earlier shells don't like it.
+doit="${DOITPROG-}"
+
+
+# put in absolute paths if you don't have them in your path; or use env. vars.
+
+mvprog="${MVPROG-mv}"
+cpprog="${CPPROG-cp}"
+chmodprog="${CHMODPROG-chmod}"
+chownprog="${CHOWNPROG-chown}"
+chgrpprog="${CHGRPPROG-chgrp}"
+stripprog="${STRIPPROG-strip}"
+rmprog="${RMPROG-rm}"
+mkdirprog="${MKDIRPROG-mkdir}"
+
+transformbasename=""
+transform_arg=""
+instcmd="$mvprog"
+chmodcmd="$chmodprog 0755"
+chowncmd=""
+chgrpcmd=""
+stripcmd=""
+rmcmd="$rmprog -f"
+mvcmd="$mvprog"
+src=""
+dst=""
+dir_arg=""
+
+while [ x"$1" != x ]; do
+ case $1 in
+ -c) instcmd="$cpprog"
+ shift
+ continue;;
+
+ -d) dir_arg=true
+ shift
+ continue;;
+
+ -m) chmodcmd="$chmodprog $2"
+ shift
+ shift
+ continue;;
+
+ -o) chowncmd="$chownprog $2"
+ shift
+ shift
+ continue;;
+
+ -g) chgrpcmd="$chgrpprog $2"
+ shift
+ shift
+ continue;;
+
+ -s) stripcmd="$stripprog"
+ shift
+ continue;;
+
+ -t=*) transformarg=`echo $1 | sed 's/-t=//'`
+ shift
+ continue;;
+
+ -b=*) transformbasename=`echo $1 | sed 's/-b=//'`
+ shift
+ continue;;
+
+ *) if [ x"$src" = x ]
+ then
+ src=$1
+ else
+ # this colon is to work around a 386BSD /bin/sh bug
+ :
+ dst=$1
+ fi
+ shift
+ continue;;
+ esac
+done
+
+if [ x"$src" = x ]
+then
+ echo "install: no input file specified"
+ exit 1
+else
+ true
+fi
+
+if [ x"$dir_arg" != x ]; then
+ dst=$src
+ src=""
+
+ if [ -d $dst ]; then
+ instcmd=:
+ else
+ instcmd=mkdir
+ fi
+else
+
+# Waiting for this to be detected by the "$instcmd $src $dsttmp" command
+# might cause directories to be created, which would be especially bad
+# if $src (and thus $dsttmp) contains '*'.
+
+ if [ -f $src -o -d $src ]
+ then
+ true
+ else
+ echo "install: $src does not exist"
+ exit 1
+ fi
+
+ if [ x"$dst" = x ]
+ then
+ echo "install: no destination specified"
+ exit 1
+ else
+ true
+ fi
+
+# If destination is a directory, append the input filename; if your system
+# does not like double slashes in filenames, you may need to add some logic
+
+ if [ -d $dst ]
+ then
+ dst="$dst"/`basename $src`
+ else
+ true
+ fi
+fi
+
+## this sed command emulates the dirname command
+dstdir=`echo $dst | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'`
+
+# Make sure that the destination directory exists.
+# this part is taken from Noah Friedman's mkinstalldirs script
+
+# Skip lots of stat calls in the usual case.
+if [ ! -d "$dstdir" ]; then
+defaultIFS='
+'
+IFS="${IFS-${defaultIFS}}"
+
+oIFS="${IFS}"
+# Some sh's can't handle IFS=/ for some reason.
+IFS='%'
+set - `echo ${dstdir} | sed -e 's@/@%@g' -e 's@^%@/@'`
+IFS="${oIFS}"
+
+pathcomp=''
+
+while [ $# -ne 0 ] ; do
+ pathcomp="${pathcomp}${1}"
+ shift
+
+ if [ ! -d "${pathcomp}" ] ;
+ then
+ $mkdirprog "${pathcomp}"
+ else
+ true
+ fi
+
+ pathcomp="${pathcomp}/"
+done
+fi
+
+if [ x"$dir_arg" != x ]
+then
+ $doit $instcmd $dst &&
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dst; else true ; fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dst; else true ; fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dst; else true ; fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dst; else true ; fi
+else
+
+# If we're going to rename the final executable, determine the name now.
+
+ if [ x"$transformarg" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ dstfile=`basename $dst $transformbasename |
+ sed $transformarg`$transformbasename
+ fi
+
+# don't allow the sed command to completely eliminate the filename
+
+ if [ x"$dstfile" = x ]
+ then
+ dstfile=`basename $dst`
+ else
+ true
+ fi
+
+# Make a temp file name in the proper directory.
+
+ dsttmp=$dstdir/#inst.$$#
+
+# Move or copy the file name to the temp name
+
+ $doit $instcmd $src $dsttmp &&
+
+ trap "rm -f ${dsttmp}" 0 &&
+
+# and set any options; do chmod last to preserve setuid bits
+
+# If any of these fail, we abort the whole thing. If we want to
+# ignore errors from any of these, just make sure not to ignore
+# errors from the above "$doit $instcmd $src $dsttmp" command.
+
+ if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; else true;fi &&
+ if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; else true;fi &&
+ if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; else true;fi &&
+ if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; else true;fi &&
+
+# Now rename the file to the real destination.
+
+ $doit $rmcmd -f $dstdir/$dstfile &&
+ $doit $mvcmd $dsttmp $dstdir/$dstfile
+
+fi &&
+
+
+exit 0
diff --git a/contrib/libucl/python/tests/__init__.py b/contrib/jemalloc/config.stamp.in
index e69de29bb2d1..e69de29bb2d1 100644
--- a/contrib/libucl/python/tests/__init__.py
+++ b/contrib/jemalloc/config.stamp.in
diff --git a/contrib/jemalloc/configure.ac b/contrib/jemalloc/configure.ac
new file mode 100644
index 000000000000..f6d25f3345ee
--- /dev/null
+++ b/contrib/jemalloc/configure.ac
@@ -0,0 +1,2669 @@
+dnl Process this file with autoconf to produce a configure script.
+AC_PREREQ(2.68)
+AC_INIT([Makefile.in])
+
+AC_CONFIG_AUX_DIR([build-aux])
+
+dnl ============================================================================
+dnl Custom macro definitions.
+
+dnl JE_CONCAT_VVV(r, a, b)
+dnl
+dnl Set $r to the concatenation of $a and $b, with a space separating them iff
+dnl both $a and $b are non-empty.
+AC_DEFUN([JE_CONCAT_VVV],
+if test "x[$]{$2}" = "x" -o "x[$]{$3}" = "x" ; then
+ $1="[$]{$2}[$]{$3}"
+else
+ $1="[$]{$2} [$]{$3}"
+fi
+)
+
+dnl JE_APPEND_VS(a, b)
+dnl
+dnl Set $a to the concatenation of $a and b, with a space separating them iff
+dnl both $a and b are non-empty.
+AC_DEFUN([JE_APPEND_VS],
+ T_APPEND_V=$2
+ JE_CONCAT_VVV($1, $1, T_APPEND_V)
+)
+
+CONFIGURE_CFLAGS=
+SPECIFIED_CFLAGS="${CFLAGS}"
+dnl JE_CFLAGS_ADD(cflag)
+dnl
+dnl CFLAGS is the concatenation of CONFIGURE_CFLAGS and SPECIFIED_CFLAGS
+dnl (ignoring EXTRA_CFLAGS, which does not impact configure tests. This macro
+dnl appends to CONFIGURE_CFLAGS and regenerates CFLAGS.
+AC_DEFUN([JE_CFLAGS_ADD],
+[
+AC_MSG_CHECKING([whether compiler supports $1])
+T_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
+JE_APPEND_VS(CONFIGURE_CFLAGS, $1)
+JE_CONCAT_VVV(CFLAGS, CONFIGURE_CFLAGS, SPECIFIED_CFLAGS)
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
+[[
+]], [[
+ return 0;
+]])],
+ [je_cv_cflags_added=$1]
+ AC_MSG_RESULT([yes]),
+ [je_cv_cflags_added=]
+ AC_MSG_RESULT([no])
+ [CONFIGURE_CFLAGS="${T_CONFIGURE_CFLAGS}"]
+)
+JE_CONCAT_VVV(CFLAGS, CONFIGURE_CFLAGS, SPECIFIED_CFLAGS)
+])
+
+dnl JE_CFLAGS_SAVE()
+dnl JE_CFLAGS_RESTORE()
+dnl
+dnl Save/restore CFLAGS. Nesting is not supported.
+AC_DEFUN([JE_CFLAGS_SAVE],
+SAVED_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
+)
+AC_DEFUN([JE_CFLAGS_RESTORE],
+CONFIGURE_CFLAGS="${SAVED_CONFIGURE_CFLAGS}"
+JE_CONCAT_VVV(CFLAGS, CONFIGURE_CFLAGS, SPECIFIED_CFLAGS)
+)
+
+CONFIGURE_CXXFLAGS=
+SPECIFIED_CXXFLAGS="${CXXFLAGS}"
+dnl JE_CXXFLAGS_ADD(cxxflag)
+AC_DEFUN([JE_CXXFLAGS_ADD],
+[
+AC_MSG_CHECKING([whether compiler supports $1])
+T_CONFIGURE_CXXFLAGS="${CONFIGURE_CXXFLAGS}"
+JE_APPEND_VS(CONFIGURE_CXXFLAGS, $1)
+JE_CONCAT_VVV(CXXFLAGS, CONFIGURE_CXXFLAGS, SPECIFIED_CXXFLAGS)
+AC_LANG_PUSH([C++])
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
+[[
+]], [[
+ return 0;
+]])],
+ [je_cv_cxxflags_added=$1]
+ AC_MSG_RESULT([yes]),
+ [je_cv_cxxflags_added=]
+ AC_MSG_RESULT([no])
+ [CONFIGURE_CXXFLAGS="${T_CONFIGURE_CXXFLAGS}"]
+)
+AC_LANG_POP([C++])
+JE_CONCAT_VVV(CXXFLAGS, CONFIGURE_CXXFLAGS, SPECIFIED_CXXFLAGS)
+])
+
+dnl JE_COMPILABLE(label, hcode, mcode, rvar)
+dnl
+dnl Use AC_LINK_IFELSE() rather than AC_COMPILE_IFELSE() so that linker errors
+dnl cause failure.
+AC_DEFUN([JE_COMPILABLE],
+[
+AC_CACHE_CHECK([whether $1 is compilable],
+ [$4],
+ [AC_LINK_IFELSE([AC_LANG_PROGRAM([$2],
+ [$3])],
+ [$4=yes],
+ [$4=no])])
+])
+
+dnl ============================================================================
+
+CONFIG=`echo ${ac_configure_args} | sed -e 's#'"'"'\([^ ]*\)'"'"'#\1#g'`
+AC_SUBST([CONFIG])
+
+dnl Library revision.
+rev=2
+AC_SUBST([rev])
+
+srcroot=$srcdir
+if test "x${srcroot}" = "x." ; then
+ srcroot=""
+else
+ srcroot="${srcroot}/"
+fi
+AC_SUBST([srcroot])
+abs_srcroot="`cd \"${srcdir}\"; pwd`/"
+AC_SUBST([abs_srcroot])
+
+objroot=""
+AC_SUBST([objroot])
+abs_objroot="`pwd`/"
+AC_SUBST([abs_objroot])
+
+dnl Munge install path variables.
+case "$prefix" in
+ *\ * ) AC_MSG_ERROR([Prefix should not contain spaces]) ;;
+ "NONE" ) prefix="/usr/local" ;;
+esac
+case "$exec_prefix" in
+ *\ * ) AC_MSG_ERROR([Exec prefix should not contain spaces]) ;;
+ "NONE" ) exec_prefix=$prefix ;;
+esac
+PREFIX=$prefix
+AC_SUBST([PREFIX])
+BINDIR=`eval echo $bindir`
+BINDIR=`eval echo $BINDIR`
+AC_SUBST([BINDIR])
+INCLUDEDIR=`eval echo $includedir`
+INCLUDEDIR=`eval echo $INCLUDEDIR`
+AC_SUBST([INCLUDEDIR])
+LIBDIR=`eval echo $libdir`
+LIBDIR=`eval echo $LIBDIR`
+AC_SUBST([LIBDIR])
+DATADIR=`eval echo $datadir`
+DATADIR=`eval echo $DATADIR`
+AC_SUBST([DATADIR])
+MANDIR=`eval echo $mandir`
+MANDIR=`eval echo $MANDIR`
+AC_SUBST([MANDIR])
+
+dnl Support for building documentation.
+AC_PATH_PROG([XSLTPROC], [xsltproc], [false], [$PATH])
+if test -d "/usr/share/xml/docbook/stylesheet/docbook-xsl" ; then
+ DEFAULT_XSLROOT="/usr/share/xml/docbook/stylesheet/docbook-xsl"
+elif test -d "/usr/share/sgml/docbook/xsl-stylesheets" ; then
+ DEFAULT_XSLROOT="/usr/share/sgml/docbook/xsl-stylesheets"
+else
+ dnl Documentation building will fail if this default gets used.
+ DEFAULT_XSLROOT=""
+fi
+AC_ARG_WITH([xslroot],
+ [AS_HELP_STRING([--with-xslroot=<path>], [XSL stylesheet root path])], [
+if test "x$with_xslroot" = "xno" ; then
+ XSLROOT="${DEFAULT_XSLROOT}"
+else
+ XSLROOT="${with_xslroot}"
+fi
+],
+ XSLROOT="${DEFAULT_XSLROOT}"
+)
+if test "x$XSLTPROC" = "xfalse" ; then
+ XSLROOT=""
+fi
+AC_SUBST([XSLROOT])
+
+dnl If CFLAGS isn't defined, set CFLAGS to something reasonable. Otherwise,
+dnl just prevent autoconf from molesting CFLAGS.
+CFLAGS=$CFLAGS
+AC_PROG_CC
+
+if test "x$GCC" != "xyes" ; then
+ AC_CACHE_CHECK([whether compiler is MSVC],
+ [je_cv_msvc],
+ [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],
+ [
+#ifndef _MSC_VER
+ int fail[-1];
+#endif
+])],
+ [je_cv_msvc=yes],
+ [je_cv_msvc=no])])
+fi
+
+dnl check if a cray prgenv wrapper compiler is being used
+je_cv_cray_prgenv_wrapper=""
+if test "x${PE_ENV}" != "x" ; then
+ case "${CC}" in
+ CC|cc)
+ je_cv_cray_prgenv_wrapper="yes"
+ ;;
+ *)
+ ;;
+ esac
+fi
+
+AC_CACHE_CHECK([whether compiler is cray],
+ [je_cv_cray],
+ [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],
+ [
+#ifndef _CRAYC
+ int fail[-1];
+#endif
+])],
+ [je_cv_cray=yes],
+ [je_cv_cray=no])])
+
+if test "x${je_cv_cray}" = "xyes" ; then
+ AC_CACHE_CHECK([whether cray compiler version is 8.4],
+ [je_cv_cray_84],
+ [AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],
+ [
+#if !(_RELEASE_MAJOR == 8 && _RELEASE_MINOR == 4)
+ int fail[-1];
+#endif
+])],
+ [je_cv_cray_84=yes],
+ [je_cv_cray_84=no])])
+fi
+
+if test "x$GCC" = "xyes" ; then
+ JE_CFLAGS_ADD([-std=gnu11])
+ if test "x$je_cv_cflags_added" = "x-std=gnu11" ; then
+ AC_DEFINE_UNQUOTED([JEMALLOC_HAS_RESTRICT], [ ], [ ])
+ else
+ JE_CFLAGS_ADD([-std=gnu99])
+ if test "x$je_cv_cflags_added" = "x-std=gnu99" ; then
+ AC_DEFINE_UNQUOTED([JEMALLOC_HAS_RESTRICT], [ ], [ ])
+ fi
+ fi
+ JE_CFLAGS_ADD([-Werror=unknown-warning-option])
+ JE_CFLAGS_ADD([-Wall])
+ JE_CFLAGS_ADD([-Wextra])
+ JE_CFLAGS_ADD([-Wshorten-64-to-32])
+ JE_CFLAGS_ADD([-Wsign-compare])
+ JE_CFLAGS_ADD([-Wundef])
+ JE_CFLAGS_ADD([-Wno-format-zero-length])
+ JE_CFLAGS_ADD([-Wpointer-arith])
+ dnl This warning triggers on the use of the universal zero initializer, which
+ dnl is a very handy idiom for things like the tcache static initializer (which
+ dnl has lots of nested structs). See the discussion at.
+ dnl https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53119
+ JE_CFLAGS_ADD([-Wno-missing-braces])
+ dnl This one too.
+ JE_CFLAGS_ADD([-Wno-missing-field-initializers])
+ JE_CFLAGS_ADD([-Wno-missing-attributes])
+ JE_CFLAGS_ADD([-pipe])
+ JE_CFLAGS_ADD([-g3])
+elif test "x$je_cv_msvc" = "xyes" ; then
+ CC="$CC -nologo"
+ JE_CFLAGS_ADD([-Zi])
+ JE_CFLAGS_ADD([-MT])
+ JE_CFLAGS_ADD([-W3])
+ JE_CFLAGS_ADD([-FS])
+ JE_APPEND_VS(CPPFLAGS, -I${srcdir}/include/msvc_compat)
+fi
+if test "x$je_cv_cray" = "xyes" ; then
+ dnl cray compiler 8.4 has an inlining bug
+ if test "x$je_cv_cray_84" = "xyes" ; then
+ JE_CFLAGS_ADD([-hipa2])
+ JE_CFLAGS_ADD([-hnognu])
+ fi
+ dnl ignore unreachable code warning
+ JE_CFLAGS_ADD([-hnomessage=128])
+ dnl ignore redefinition of "malloc", "free", etc warning
+ JE_CFLAGS_ADD([-hnomessage=1357])
+fi
+AC_SUBST([CONFIGURE_CFLAGS])
+AC_SUBST([SPECIFIED_CFLAGS])
+AC_SUBST([EXTRA_CFLAGS])
+AC_PROG_CPP
+
+AC_ARG_ENABLE([cxx],
+ [AS_HELP_STRING([--disable-cxx], [Disable C++ integration])],
+if test "x$enable_cxx" = "xno" ; then
+ enable_cxx="0"
+else
+ enable_cxx="1"
+fi
+,
+enable_cxx="1"
+)
+if test "x$enable_cxx" = "x1" ; then
+ dnl Require at least c++14, which is the first version to support sized
+ dnl deallocation. C++ support is not compiled otherwise.
+ m4_include([m4/ax_cxx_compile_stdcxx.m4])
+ AX_CXX_COMPILE_STDCXX([17], [noext], [optional])
+ if test "x${HAVE_CXX17}" != "x1"; then
+ AX_CXX_COMPILE_STDCXX([14], [noext], [optional])
+ fi
+ if test "x${HAVE_CXX14}" = "x1" -o "x${HAVE_CXX17}" = "x1"; then
+ JE_CXXFLAGS_ADD([-Wall])
+ JE_CXXFLAGS_ADD([-Wextra])
+ JE_CXXFLAGS_ADD([-g3])
+
+ SAVED_LIBS="${LIBS}"
+ JE_APPEND_VS(LIBS, -lstdc++)
+ JE_COMPILABLE([libstdc++ linkage], [
+#include <stdlib.h>
+], [[
+ int *arr = (int *)malloc(sizeof(int) * 42);
+ if (arr == NULL)
+ return 1;
+]], [je_cv_libstdcxx])
+ if test "x${je_cv_libstdcxx}" = "xno" ; then
+ LIBS="${SAVED_LIBS}"
+ fi
+ else
+ enable_cxx="0"
+ fi
+fi
+if test "x$enable_cxx" = "x1"; then
+ AC_DEFINE([JEMALLOC_ENABLE_CXX], [ ], [ ])
+fi
+AC_SUBST([enable_cxx])
+AC_SUBST([CONFIGURE_CXXFLAGS])
+AC_SUBST([SPECIFIED_CXXFLAGS])
+AC_SUBST([EXTRA_CXXFLAGS])
+
+AC_C_BIGENDIAN([ac_cv_big_endian=1], [ac_cv_big_endian=0])
+if test "x${ac_cv_big_endian}" = "x1" ; then
+ AC_DEFINE_UNQUOTED([JEMALLOC_BIG_ENDIAN], [ ], [ ])
+fi
+
+if test "x${je_cv_msvc}" = "xyes" -a "x${ac_cv_header_inttypes_h}" = "xno"; then
+ JE_APPEND_VS(CPPFLAGS, -I${srcdir}/include/msvc_compat/C99)
+fi
+
+if test "x${je_cv_msvc}" = "xyes" ; then
+ LG_SIZEOF_PTR=LG_SIZEOF_PTR_WIN
+ AC_MSG_RESULT([Using a predefined value for sizeof(void *): 4 for 32-bit, 8 for 64-bit])
+else
+ AC_CHECK_SIZEOF([void *])
+ if test "x${ac_cv_sizeof_void_p}" = "x8" ; then
+ LG_SIZEOF_PTR=3
+ elif test "x${ac_cv_sizeof_void_p}" = "x4" ; then
+ LG_SIZEOF_PTR=2
+ else
+ AC_MSG_ERROR([Unsupported pointer size: ${ac_cv_sizeof_void_p}])
+ fi
+fi
+AC_DEFINE_UNQUOTED([LG_SIZEOF_PTR], [$LG_SIZEOF_PTR], [ ])
+
+AC_CHECK_SIZEOF([int])
+if test "x${ac_cv_sizeof_int}" = "x8" ; then
+ LG_SIZEOF_INT=3
+elif test "x${ac_cv_sizeof_int}" = "x4" ; then
+ LG_SIZEOF_INT=2
+else
+ AC_MSG_ERROR([Unsupported int size: ${ac_cv_sizeof_int}])
+fi
+AC_DEFINE_UNQUOTED([LG_SIZEOF_INT], [$LG_SIZEOF_INT], [ ])
+
+AC_CHECK_SIZEOF([long])
+if test "x${ac_cv_sizeof_long}" = "x8" ; then
+ LG_SIZEOF_LONG=3
+elif test "x${ac_cv_sizeof_long}" = "x4" ; then
+ LG_SIZEOF_LONG=2
+else
+ AC_MSG_ERROR([Unsupported long size: ${ac_cv_sizeof_long}])
+fi
+AC_DEFINE_UNQUOTED([LG_SIZEOF_LONG], [$LG_SIZEOF_LONG], [ ])
+
+AC_CHECK_SIZEOF([long long])
+if test "x${ac_cv_sizeof_long_long}" = "x8" ; then
+ LG_SIZEOF_LONG_LONG=3
+elif test "x${ac_cv_sizeof_long_long}" = "x4" ; then
+ LG_SIZEOF_LONG_LONG=2
+else
+ AC_MSG_ERROR([Unsupported long long size: ${ac_cv_sizeof_long_long}])
+fi
+AC_DEFINE_UNQUOTED([LG_SIZEOF_LONG_LONG], [$LG_SIZEOF_LONG_LONG], [ ])
+
+AC_CHECK_SIZEOF([intmax_t])
+if test "x${ac_cv_sizeof_intmax_t}" = "x16" ; then
+ LG_SIZEOF_INTMAX_T=4
+elif test "x${ac_cv_sizeof_intmax_t}" = "x8" ; then
+ LG_SIZEOF_INTMAX_T=3
+elif test "x${ac_cv_sizeof_intmax_t}" = "x4" ; then
+ LG_SIZEOF_INTMAX_T=2
+else
+ AC_MSG_ERROR([Unsupported intmax_t size: ${ac_cv_sizeof_intmax_t}])
+fi
+AC_DEFINE_UNQUOTED([LG_SIZEOF_INTMAX_T], [$LG_SIZEOF_INTMAX_T], [ ])
+
+AC_CANONICAL_HOST
+dnl CPU-specific settings.
+CPU_SPINWAIT=""
+case "${host_cpu}" in
+ i686|x86_64)
+ HAVE_CPU_SPINWAIT=1
+ if test "x${je_cv_msvc}" = "xyes" ; then
+ AC_CACHE_VAL([je_cv_pause_msvc],
+ [JE_COMPILABLE([pause instruction MSVC], [],
+ [[_mm_pause(); return 0;]],
+ [je_cv_pause_msvc])])
+ if test "x${je_cv_pause_msvc}" = "xyes" ; then
+ CPU_SPINWAIT='_mm_pause()'
+ fi
+ else
+ AC_CACHE_VAL([je_cv_pause],
+ [JE_COMPILABLE([pause instruction], [],
+ [[__asm__ volatile("pause"); return 0;]],
+ [je_cv_pause])])
+ if test "x${je_cv_pause}" = "xyes" ; then
+ CPU_SPINWAIT='__asm__ volatile("pause")'
+ fi
+ fi
+ ;;
+ aarch64|arm*)
+ HAVE_CPU_SPINWAIT=1
+ dnl isb is a better equivalent to the pause instruction on x86.
+ AC_CACHE_VAL([je_cv_isb],
+ [JE_COMPILABLE([isb instruction], [],
+ [[__asm__ volatile("isb"); return 0;]],
+ [je_cv_isb])])
+ if test "x${je_cv_isb}" = "xyes" ; then
+ CPU_SPINWAIT='__asm__ volatile("isb")'
+ fi
+ ;;
+ *)
+ HAVE_CPU_SPINWAIT=0
+ ;;
+esac
+AC_DEFINE_UNQUOTED([HAVE_CPU_SPINWAIT], [$HAVE_CPU_SPINWAIT], [ ])
+AC_DEFINE_UNQUOTED([CPU_SPINWAIT], [$CPU_SPINWAIT], [ ])
+
+AC_ARG_WITH([lg_vaddr],
+ [AS_HELP_STRING([--with-lg-vaddr=<lg-vaddr>], [Number of significant virtual address bits])],
+ [LG_VADDR="$with_lg_vaddr"], [LG_VADDR="detect"])
+
+case "${host_cpu}" in
+ aarch64)
+ if test "x$LG_VADDR" = "xdetect"; then
+ AC_MSG_CHECKING([number of significant virtual address bits])
+ if test "x${LG_SIZEOF_PTR}" = "x2" ; then
+ #aarch64 ILP32
+ LG_VADDR=32
+ else
+ #aarch64 LP64
+ LG_VADDR=48
+ fi
+ AC_MSG_RESULT([$LG_VADDR])
+ fi
+ ;;
+ x86_64)
+ if test "x$LG_VADDR" = "xdetect"; then
+ AC_CACHE_CHECK([number of significant virtual address bits],
+ [je_cv_lg_vaddr],
+ AC_RUN_IFELSE([AC_LANG_PROGRAM(
+[[
+#include <stdio.h>
+#ifdef _WIN32
+#include <limits.h>
+#include <intrin.h>
+typedef unsigned __int32 uint32_t;
+#else
+#include <stdint.h>
+#endif
+]], [[
+ uint32_t r[[4]];
+ uint32_t eax_in = 0x80000008U;
+#ifdef _WIN32
+ __cpuid((int *)r, (int)eax_in);
+#else
+ asm volatile ("cpuid"
+ : "=a" (r[[0]]), "=b" (r[[1]]), "=c" (r[[2]]), "=d" (r[[3]])
+ : "a" (eax_in), "c" (0)
+ );
+#endif
+ uint32_t eax_out = r[[0]];
+ uint32_t vaddr = ((eax_out & 0x0000ff00U) >> 8);
+ FILE *f = fopen("conftest.out", "w");
+ if (f == NULL) {
+ return 1;
+ }
+ if (vaddr > (sizeof(void *) << 3)) {
+ vaddr = sizeof(void *) << 3;
+ }
+ fprintf(f, "%u", vaddr);
+ fclose(f);
+ return 0;
+]])],
+ [je_cv_lg_vaddr=`cat conftest.out`],
+ [je_cv_lg_vaddr=error],
+ [je_cv_lg_vaddr=57]))
+ if test "x${je_cv_lg_vaddr}" != "x" ; then
+ LG_VADDR="${je_cv_lg_vaddr}"
+ fi
+ if test "x${LG_VADDR}" != "xerror" ; then
+ AC_DEFINE_UNQUOTED([LG_VADDR], [$LG_VADDR], [ ])
+ else
+ AC_MSG_ERROR([cannot determine number of significant virtual address bits])
+ fi
+ fi
+ ;;
+ *)
+ if test "x$LG_VADDR" = "xdetect"; then
+ AC_MSG_CHECKING([number of significant virtual address bits])
+ if test "x${LG_SIZEOF_PTR}" = "x3" ; then
+ LG_VADDR=64
+ elif test "x${LG_SIZEOF_PTR}" = "x2" ; then
+ LG_VADDR=32
+ elif test "x${LG_SIZEOF_PTR}" = "xLG_SIZEOF_PTR_WIN" ; then
+ LG_VADDR="(1U << (LG_SIZEOF_PTR_WIN+3))"
+ else
+ AC_MSG_ERROR([Unsupported lg(pointer size): ${LG_SIZEOF_PTR}])
+ fi
+ AC_MSG_RESULT([$LG_VADDR])
+ fi
+ ;;
+esac
+AC_DEFINE_UNQUOTED([LG_VADDR], [$LG_VADDR], [ ])
+
+LD_PRELOAD_VAR="LD_PRELOAD"
+so="so"
+importlib="${so}"
+o="$ac_objext"
+a="a"
+exe="$ac_exeext"
+libprefix="lib"
+link_whole_archive="0"
+DSO_LDFLAGS='-shared -Wl,-soname,$(@F)'
+RPATH='-Wl,-rpath,$(1)'
+SOREV="${so}.${rev}"
+PIC_CFLAGS='-fPIC -DPIC'
+CTARGET='-o $@'
+LDTARGET='-o $@'
+TEST_LD_MODE=
+EXTRA_LDFLAGS=
+ARFLAGS='crus'
+AROUT=' $@'
+CC_MM=1
+
+if test "x$je_cv_cray_prgenv_wrapper" = "xyes" ; then
+ TEST_LD_MODE='-dynamic'
+fi
+
+if test "x${je_cv_cray}" = "xyes" ; then
+ CC_MM=
+fi
+
+AN_MAKEVAR([AR], [AC_PROG_AR])
+AN_PROGRAM([ar], [AC_PROG_AR])
+AC_DEFUN([AC_PROG_AR], [AC_CHECK_TOOL(AR, ar, :)])
+AC_PROG_AR
+
+AN_MAKEVAR([NM], [AC_PROG_NM])
+AN_PROGRAM([nm], [AC_PROG_NM])
+AC_DEFUN([AC_PROG_NM], [AC_CHECK_TOOL(NM, nm, :)])
+AC_PROG_NM
+
+AC_PROG_AWK
+
+dnl ============================================================================
+dnl jemalloc version.
+dnl
+
+AC_ARG_WITH([version],
+ [AS_HELP_STRING([--with-version=<major>.<minor>.<bugfix>-<nrev>-g<gid>],
+ [Version string])],
+ [
+ echo "${with_version}" | grep ['^[0-9]\+\.[0-9]\+\.[0-9]\+-[0-9]\+-g[0-9a-f]\+$'] 2>&1 1>/dev/null
+ if test $? -eq 0 ; then
+ echo "$with_version" > "${objroot}VERSION"
+ else
+ echo "${with_version}" | grep ['^VERSION$'] 2>&1 1>/dev/null
+ if test $? -ne 0 ; then
+ AC_MSG_ERROR([${with_version} does not match <major>.<minor>.<bugfix>-<nrev>-g<gid> or VERSION])
+ fi
+ fi
+ ], [
+ dnl Set VERSION if source directory is inside a git repository.
+ if test "x`test ! \"${srcroot}\" && cd \"${srcroot}\"; git rev-parse --is-inside-work-tree 2>/dev/null`" = "xtrue" ; then
+ dnl Pattern globs aren't powerful enough to match both single- and
+ dnl double-digit version numbers, so iterate over patterns to support up
+ dnl to version 99.99.99 without any accidental matches.
+ for pattern in ['[0-9].[0-9].[0-9]' '[0-9].[0-9].[0-9][0-9]' \
+ '[0-9].[0-9][0-9].[0-9]' '[0-9].[0-9][0-9].[0-9][0-9]' \
+ '[0-9][0-9].[0-9].[0-9]' '[0-9][0-9].[0-9].[0-9][0-9]' \
+ '[0-9][0-9].[0-9][0-9].[0-9]' \
+ '[0-9][0-9].[0-9][0-9].[0-9][0-9]']; do
+ (test ! "${srcroot}" && cd "${srcroot}"; git describe --long --abbrev=40 --match="${pattern}") > "${objroot}VERSION.tmp" 2>/dev/null
+ if test $? -eq 0 ; then
+ mv "${objroot}VERSION.tmp" "${objroot}VERSION"
+ break
+ fi
+ done
+ fi
+ rm -f "${objroot}VERSION.tmp"
+ ])
+
+if test ! -e "${objroot}VERSION" ; then
+ if test ! -e "${srcroot}VERSION" ; then
+ AC_MSG_RESULT(
+ [Missing VERSION file, and unable to generate it; creating bogus VERSION])
+ echo "0.0.0-0-g000000missing_version_try_git_fetch_tags" > "${objroot}VERSION"
+ else
+ cp ${srcroot}VERSION ${objroot}VERSION
+ fi
+fi
+jemalloc_version=`cat "${objroot}VERSION"`
+jemalloc_version_major=`echo ${jemalloc_version} | tr ".g-" " " | awk '{print [$]1}'`
+jemalloc_version_minor=`echo ${jemalloc_version} | tr ".g-" " " | awk '{print [$]2}'`
+jemalloc_version_bugfix=`echo ${jemalloc_version} | tr ".g-" " " | awk '{print [$]3}'`
+jemalloc_version_nrev=`echo ${jemalloc_version} | tr ".g-" " " | awk '{print [$]4}'`
+jemalloc_version_gid=`echo ${jemalloc_version} | tr ".g-" " " | awk '{print [$]5}'`
+AC_SUBST([jemalloc_version])
+AC_SUBST([jemalloc_version_major])
+AC_SUBST([jemalloc_version_minor])
+AC_SUBST([jemalloc_version_bugfix])
+AC_SUBST([jemalloc_version_nrev])
+AC_SUBST([jemalloc_version_gid])
+
+dnl Platform-specific settings. abi and RPATH can probably be determined
+dnl programmatically, but doing so is error-prone, which makes it generally
+dnl not worth the trouble.
+dnl
+dnl Define cpp macros in CPPFLAGS, rather than doing AC_DEFINE(macro), since the
+dnl definitions need to be seen before any headers are included, which is a pain
+dnl to make happen otherwise.
+default_retain="0"
+zero_realloc_default_free="0"
+maps_coalesce="1"
+DUMP_SYMS="${NM} -a"
+SYM_PREFIX=""
+case "${host}" in
+ *-*-darwin* | *-*-ios*)
+ abi="macho"
+ RPATH=""
+ LD_PRELOAD_VAR="DYLD_INSERT_LIBRARIES"
+ so="dylib"
+ importlib="${so}"
+ force_tls="0"
+ DSO_LDFLAGS='-shared -Wl,-install_name,$(LIBDIR)/$(@F)'
+ SOREV="${rev}.${so}"
+ sbrk_deprecated="1"
+ SYM_PREFIX="_"
+ ;;
+ *-*-freebsd*)
+ JE_APPEND_VS(CPPFLAGS, -D_BSD_SOURCE)
+ abi="elf"
+ AC_DEFINE([JEMALLOC_SYSCTL_VM_OVERCOMMIT], [ ], [ ])
+ force_lazy_lock="1"
+ ;;
+ *-*-dragonfly*)
+ abi="elf"
+ ;;
+ *-*-openbsd*)
+ abi="elf"
+ force_tls="0"
+ ;;
+ *-*-bitrig*)
+ abi="elf"
+ ;;
+ *-*-linux-android*)
+ dnl syscall(2) and secure_getenv(3) are exposed by _GNU_SOURCE.
+ JE_APPEND_VS(CPPFLAGS, -D_GNU_SOURCE)
+ abi="elf"
+ glibc="0"
+ AC_DEFINE([JEMALLOC_PURGE_MADVISE_DONTNEED_ZEROS], [ ], [ ])
+ AC_DEFINE([JEMALLOC_HAS_ALLOCA_H], [ ], [ ])
+ AC_DEFINE([JEMALLOC_PROC_SYS_VM_OVERCOMMIT_MEMORY], [ ], [ ])
+ AC_DEFINE([JEMALLOC_THREADED_INIT], [ ], [ ])
+ AC_DEFINE([JEMALLOC_C11_ATOMICS], [ ], [ ])
+ force_tls="0"
+ if test "${LG_SIZEOF_PTR}" = "3"; then
+ default_retain="1"
+ fi
+ zero_realloc_default_free="1"
+ ;;
+ *-*-linux*)
+ dnl syscall(2) and secure_getenv(3) are exposed by _GNU_SOURCE.
+ JE_APPEND_VS(CPPFLAGS, -D_GNU_SOURCE)
+ abi="elf"
+ glibc="1"
+ AC_DEFINE([JEMALLOC_PURGE_MADVISE_DONTNEED_ZEROS], [ ], [ ])
+ AC_DEFINE([JEMALLOC_HAS_ALLOCA_H], [ ], [ ])
+ AC_DEFINE([JEMALLOC_PROC_SYS_VM_OVERCOMMIT_MEMORY], [ ], [ ])
+ AC_DEFINE([JEMALLOC_THREADED_INIT], [ ], [ ])
+ AC_DEFINE([JEMALLOC_USE_CXX_THROW], [ ], [ ])
+ if test "${LG_SIZEOF_PTR}" = "3"; then
+ default_retain="1"
+ fi
+ zero_realloc_default_free="1"
+ ;;
+ *-*-kfreebsd*)
+ dnl syscall(2) and secure_getenv(3) are exposed by _GNU_SOURCE.
+ JE_APPEND_VS(CPPFLAGS, -D_GNU_SOURCE)
+ abi="elf"
+ AC_DEFINE([JEMALLOC_HAS_ALLOCA_H], [ ], [ ])
+ AC_DEFINE([JEMALLOC_SYSCTL_VM_OVERCOMMIT], [ ], [ ])
+ AC_DEFINE([JEMALLOC_THREADED_INIT], [ ], [ ])
+ AC_DEFINE([JEMALLOC_USE_CXX_THROW], [ ], [ ])
+ ;;
+ *-*-netbsd*)
+ AC_MSG_CHECKING([ABI])
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
+[[#ifdef __ELF__
+/* ELF */
+#else
+#error aout
+#endif
+]])],
+ [abi="elf"],
+ [abi="aout"])
+ AC_MSG_RESULT([$abi])
+ ;;
+ *-*-solaris2*)
+ abi="elf"
+ RPATH='-Wl,-R,$(1)'
+ dnl Solaris needs this for sigwait().
+ JE_APPEND_VS(CPPFLAGS, -D_POSIX_PTHREAD_SEMANTICS)
+ JE_APPEND_VS(LIBS, -lposix4 -lsocket -lnsl)
+ ;;
+ *-ibm-aix*)
+ if test "${LG_SIZEOF_PTR}" = "3"; then
+ dnl 64bit AIX
+ LD_PRELOAD_VAR="LDR_PRELOAD64"
+ else
+ dnl 32bit AIX
+ LD_PRELOAD_VAR="LDR_PRELOAD"
+ fi
+ abi="xcoff"
+ ;;
+ *-*-mingw* | *-*-cygwin*)
+ abi="pecoff"
+ force_tls="0"
+ maps_coalesce="0"
+ RPATH=""
+ so="dll"
+ if test "x$je_cv_msvc" = "xyes" ; then
+ importlib="lib"
+ DSO_LDFLAGS="-LD"
+ EXTRA_LDFLAGS="-link -DEBUG"
+ CTARGET='-Fo$@'
+ LDTARGET='-Fe$@'
+ AR='lib'
+ ARFLAGS='-nologo -out:'
+ AROUT='$@'
+ CC_MM=
+ else
+ importlib="${so}"
+ DSO_LDFLAGS="-shared"
+ link_whole_archive="1"
+ fi
+ case "${host}" in
+ *-*-cygwin*)
+ DUMP_SYMS="dumpbin /SYMBOLS"
+ ;;
+ *)
+ ;;
+ esac
+ a="lib"
+ libprefix=""
+ SOREV="${so}"
+ PIC_CFLAGS=""
+ if test "${LG_SIZEOF_PTR}" = "3"; then
+ default_retain="1"
+ fi
+ zero_realloc_default_free="1"
+ ;;
+ *-*-nto-qnx)
+ abi="elf"
+ force_tls="0"
+ AC_DEFINE([JEMALLOC_HAS_ALLOCA_H], [ ], [ ])
+ ;;
+ *)
+ AC_MSG_RESULT([Unsupported operating system: ${host}])
+ abi="elf"
+ ;;
+esac
+
+JEMALLOC_USABLE_SIZE_CONST=const
+AC_CHECK_HEADERS([malloc.h], [
+ AC_MSG_CHECKING([whether malloc_usable_size definition can use const argument])
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
+ [#include <malloc.h>
+ #include <stddef.h>
+ size_t malloc_usable_size(const void *ptr);
+ ],
+ [])],[
+ AC_MSG_RESULT([yes])
+ ],[
+ JEMALLOC_USABLE_SIZE_CONST=
+ AC_MSG_RESULT([no])
+ ])
+])
+AC_DEFINE_UNQUOTED([JEMALLOC_USABLE_SIZE_CONST], [$JEMALLOC_USABLE_SIZE_CONST], [ ])
+AC_SUBST([abi])
+AC_SUBST([RPATH])
+AC_SUBST([LD_PRELOAD_VAR])
+AC_SUBST([so])
+AC_SUBST([importlib])
+AC_SUBST([o])
+AC_SUBST([a])
+AC_SUBST([exe])
+AC_SUBST([libprefix])
+AC_SUBST([link_whole_archive])
+AC_SUBST([DSO_LDFLAGS])
+AC_SUBST([EXTRA_LDFLAGS])
+AC_SUBST([SOREV])
+AC_SUBST([PIC_CFLAGS])
+AC_SUBST([CTARGET])
+AC_SUBST([LDTARGET])
+AC_SUBST([TEST_LD_MODE])
+AC_SUBST([MKLIB])
+AC_SUBST([ARFLAGS])
+AC_SUBST([AROUT])
+AC_SUBST([DUMP_SYMS])
+AC_SUBST([CC_MM])
+
+dnl Determine whether libm must be linked to use e.g. log(3).
+AC_SEARCH_LIBS([log], [m], , [AC_MSG_ERROR([Missing math functions])])
+if test "x$ac_cv_search_log" != "xnone required" ; then
+ LM="$ac_cv_search_log"
+else
+ LM=
+fi
+AC_SUBST(LM)
+
+JE_COMPILABLE([__attribute__ syntax],
+ [static __attribute__((unused)) void foo(void){}],
+ [],
+ [je_cv_attribute])
+if test "x${je_cv_attribute}" = "xyes" ; then
+ AC_DEFINE([JEMALLOC_HAVE_ATTR], [ ], [ ])
+ if test "x${GCC}" = "xyes" -a "x${abi}" = "xelf"; then
+ JE_CFLAGS_ADD([-fvisibility=hidden])
+ JE_CXXFLAGS_ADD([-fvisibility=hidden])
+ fi
+fi
+dnl Check for tls_model attribute support (clang 3.0 still lacks support).
+JE_CFLAGS_SAVE()
+JE_CFLAGS_ADD([-Werror])
+JE_CFLAGS_ADD([-herror_on_warning])
+JE_COMPILABLE([tls_model attribute], [],
+ [static __thread int
+ __attribute__((tls_model("initial-exec"), unused)) foo;
+ foo = 0;],
+ [je_cv_tls_model])
+JE_CFLAGS_RESTORE()
+dnl (Setting of JEMALLOC_TLS_MODEL is done later, after we've checked for
+dnl --disable-initial-exec-tls)
+
+dnl Check for alloc_size attribute support.
+JE_CFLAGS_SAVE()
+JE_CFLAGS_ADD([-Werror])
+JE_CFLAGS_ADD([-herror_on_warning])
+JE_COMPILABLE([alloc_size attribute], [#include <stdlib.h>],
+ [void *foo(size_t size) __attribute__((alloc_size(1)));],
+ [je_cv_alloc_size])
+JE_CFLAGS_RESTORE()
+if test "x${je_cv_alloc_size}" = "xyes" ; then
+ AC_DEFINE([JEMALLOC_HAVE_ATTR_ALLOC_SIZE], [ ], [ ])
+fi
+dnl Check for format(gnu_printf, ...) attribute support.
+JE_CFLAGS_SAVE()
+JE_CFLAGS_ADD([-Werror])
+JE_CFLAGS_ADD([-herror_on_warning])
+JE_COMPILABLE([format(gnu_printf, ...) attribute], [#include <stdlib.h>],
+ [void *foo(const char *format, ...) __attribute__((format(gnu_printf, 1, 2)));],
+ [je_cv_format_gnu_printf])
+JE_CFLAGS_RESTORE()
+if test "x${je_cv_format_gnu_printf}" = "xyes" ; then
+ AC_DEFINE([JEMALLOC_HAVE_ATTR_FORMAT_GNU_PRINTF], [ ], [ ])
+fi
+dnl Check for format(printf, ...) attribute support.
+JE_CFLAGS_SAVE()
+JE_CFLAGS_ADD([-Werror])
+JE_CFLAGS_ADD([-herror_on_warning])
+JE_COMPILABLE([format(printf, ...) attribute], [#include <stdlib.h>],
+ [void *foo(const char *format, ...) __attribute__((format(printf, 1, 2)));],
+ [je_cv_format_printf])
+JE_CFLAGS_RESTORE()
+if test "x${je_cv_format_printf}" = "xyes" ; then
+ AC_DEFINE([JEMALLOC_HAVE_ATTR_FORMAT_PRINTF], [ ], [ ])
+fi
+
+dnl Check for format_arg(...) attribute support.
+JE_CFLAGS_SAVE()
+JE_CFLAGS_ADD([-Werror])
+JE_CFLAGS_ADD([-herror_on_warning])
+JE_COMPILABLE([format(printf, ...) attribute], [#include <stdlib.h>],
+ [const char * __attribute__((__format_arg__(1))) foo(const char *format);],
+ [je_cv_format_arg])
+JE_CFLAGS_RESTORE()
+if test "x${je_cv_format_arg}" = "xyes" ; then
+ AC_DEFINE([JEMALLOC_HAVE_ATTR_FORMAT_ARG], [ ], [ ])
+fi
+
+dnl Check for fallthrough attribute support.
+JE_CFLAGS_SAVE()
+JE_CFLAGS_ADD([-Wimplicit-fallthrough])
+JE_COMPILABLE([fallthrough attribute],
+ [#if !__has_attribute(fallthrough)
+ #error "foo"
+ #endif],
+ [int x = 0;
+ switch (x) {
+ case 0: __attribute__((__fallthrough__));
+ case 1: return 1;
+ }],
+ [je_cv_fallthrough])
+JE_CFLAGS_RESTORE()
+if test "x${je_cv_fallthrough}" = "xyes" ; then
+ AC_DEFINE([JEMALLOC_HAVE_ATTR_FALLTHROUGH], [ ], [ ])
+ JE_CFLAGS_ADD([-Wimplicit-fallthrough])
+ JE_CXXFLAGS_ADD([-Wimplicit-fallthrough])
+fi
+
+dnl Check for cold attribute support.
+JE_CFLAGS_SAVE()
+JE_CFLAGS_ADD([-Werror])
+JE_CFLAGS_ADD([-herror_on_warning])
+JE_COMPILABLE([cold attribute], [],
+ [__attribute__((__cold__)) void foo();],
+ [je_cv_cold])
+JE_CFLAGS_RESTORE()
+if test "x${je_cv_cold}" = "xyes" ; then
+ AC_DEFINE([JEMALLOC_HAVE_ATTR_COLD], [ ], [ ])
+fi
+
+dnl Check for VM_MAKE_TAG for mmap support.
+JE_COMPILABLE([vm_make_tag],
+ [#include <sys/mman.h>
+ #include <mach/vm_statistics.h>],
+ [void *p;
+ p = mmap(0, 16, PROT_READ, MAP_ANON|MAP_PRIVATE, VM_MAKE_TAG(1), 0);
+ munmap(p, 16);],
+ [je_cv_vm_make_tag])
+if test "x${je_cv_vm_make_tag}" = "xyes" ; then
+ AC_DEFINE([JEMALLOC_HAVE_VM_MAKE_TAG], [ ], [ ])
+fi
+
+dnl Support optional additions to rpath.
+AC_ARG_WITH([rpath],
+ [AS_HELP_STRING([--with-rpath=<rpath>], [Colon-separated rpath (ELF systems only)])],
+if test "x$with_rpath" = "xno" ; then
+ RPATH_EXTRA=
+else
+ RPATH_EXTRA="`echo $with_rpath | tr \":\" \" \"`"
+fi,
+ RPATH_EXTRA=
+)
+AC_SUBST([RPATH_EXTRA])
+
+dnl Disable rules that do automatic regeneration of configure output by default.
+AC_ARG_ENABLE([autogen],
+ [AS_HELP_STRING([--enable-autogen], [Automatically regenerate configure output])],
+if test "x$enable_autogen" = "xno" ; then
+ enable_autogen="0"
+else
+ enable_autogen="1"
+fi
+,
+enable_autogen="0"
+)
+AC_SUBST([enable_autogen])
+
+AC_PROG_INSTALL
+AC_PROG_RANLIB
+AC_PATH_PROG([LD], [ld], [false], [$PATH])
+AC_PATH_PROG([AUTOCONF], [autoconf], [false], [$PATH])
+
+dnl Enable documentation
+AC_ARG_ENABLE([doc],
+ [AS_HELP_STRING([--enable-doc], [Build documentation])],
+if test "x$enable_doc" = "xno" ; then
+ enable_doc="0"
+else
+ enable_doc="1"
+fi
+,
+enable_doc="1"
+)
+AC_SUBST([enable_doc])
+
+dnl Enable shared libs
+AC_ARG_ENABLE([shared],
+ [AS_HELP_STRING([--enable-shared], [Build shared libaries])],
+if test "x$enable_shared" = "xno" ; then
+ enable_shared="0"
+else
+ enable_shared="1"
+fi
+,
+enable_shared="1"
+)
+AC_SUBST([enable_shared])
+
+dnl Enable static libs
+AC_ARG_ENABLE([static],
+ [AS_HELP_STRING([--enable-static], [Build static libaries])],
+if test "x$enable_static" = "xno" ; then
+ enable_static="0"
+else
+ enable_static="1"
+fi
+,
+enable_static="1"
+)
+AC_SUBST([enable_static])
+
+if test "$enable_shared$enable_static" = "00" ; then
+ AC_MSG_ERROR([Please enable one of shared or static builds])
+fi
+
+dnl Perform no name mangling by default.
+AC_ARG_WITH([mangling],
+ [AS_HELP_STRING([--with-mangling=<map>], [Mangle symbols in <map>])],
+ [mangling_map="$with_mangling"], [mangling_map=""])
+
+dnl Do not prefix public APIs by default.
+AC_ARG_WITH([jemalloc_prefix],
+ [AS_HELP_STRING([--with-jemalloc-prefix=<prefix>], [Prefix to prepend to all public APIs])],
+ [JEMALLOC_PREFIX="$with_jemalloc_prefix"],
+ [if test "x$abi" != "xmacho" -a "x$abi" != "xpecoff"; then
+ JEMALLOC_PREFIX=""
+else
+ JEMALLOC_PREFIX="je_"
+fi]
+)
+if test "x$JEMALLOC_PREFIX" = "x" ; then
+ AC_DEFINE([JEMALLOC_IS_MALLOC], [ ], [ ])
+else
+ JEMALLOC_CPREFIX=`echo ${JEMALLOC_PREFIX} | tr "a-z" "A-Z"`
+ AC_DEFINE_UNQUOTED([JEMALLOC_PREFIX], ["$JEMALLOC_PREFIX"], [ ])
+ AC_DEFINE_UNQUOTED([JEMALLOC_CPREFIX], ["$JEMALLOC_CPREFIX"], [ ])
+fi
+AC_SUBST([JEMALLOC_PREFIX])
+AC_SUBST([JEMALLOC_CPREFIX])
+
+AC_ARG_WITH([export],
+ [AS_HELP_STRING([--without-export], [disable exporting jemalloc public APIs])],
+ [if test "x$with_export" = "xno"; then
+ AC_DEFINE([JEMALLOC_EXPORT],[], [ ])
+fi]
+)
+
+public_syms="aligned_alloc calloc dallocx free mallctl mallctlbymib mallctlnametomib malloc malloc_conf malloc_conf_2_conf_harder malloc_message malloc_stats_print malloc_usable_size mallocx smallocx_${jemalloc_version_gid} nallocx posix_memalign rallocx realloc sallocx sdallocx xallocx"
+dnl Check for additional platform-specific public API functions.
+AC_CHECK_FUNC([memalign],
+ [AC_DEFINE([JEMALLOC_OVERRIDE_MEMALIGN], [ ], [ ])
+ public_syms="${public_syms} memalign"])
+AC_CHECK_FUNC([valloc],
+ [AC_DEFINE([JEMALLOC_OVERRIDE_VALLOC], [ ], [ ])
+ public_syms="${public_syms} valloc"])
+AC_CHECK_FUNC([malloc_size],
+ [AC_DEFINE([JEMALLOC_HAVE_MALLOC_SIZE], [ ], [ ])
+ public_syms="${public_syms} malloc_size"])
+
+dnl Check for allocator-related functions that should be wrapped.
+wrap_syms=
+if test "x${JEMALLOC_PREFIX}" = "x" ; then
+ AC_CHECK_FUNC([__libc_calloc],
+ [AC_DEFINE([JEMALLOC_OVERRIDE___LIBC_CALLOC], [ ], [ ])
+ wrap_syms="${wrap_syms} __libc_calloc"])
+ AC_CHECK_FUNC([__libc_free],
+ [AC_DEFINE([JEMALLOC_OVERRIDE___LIBC_FREE], [ ], [ ])
+ wrap_syms="${wrap_syms} __libc_free"])
+ AC_CHECK_FUNC([__libc_malloc],
+ [AC_DEFINE([JEMALLOC_OVERRIDE___LIBC_MALLOC], [ ], [ ])
+ wrap_syms="${wrap_syms} __libc_malloc"])
+ AC_CHECK_FUNC([__libc_memalign],
+ [AC_DEFINE([JEMALLOC_OVERRIDE___LIBC_MEMALIGN], [ ], [ ])
+ wrap_syms="${wrap_syms} __libc_memalign"])
+ AC_CHECK_FUNC([__libc_realloc],
+ [AC_DEFINE([JEMALLOC_OVERRIDE___LIBC_REALLOC], [ ], [ ])
+ wrap_syms="${wrap_syms} __libc_realloc"])
+ AC_CHECK_FUNC([__libc_valloc],
+ [AC_DEFINE([JEMALLOC_OVERRIDE___LIBC_VALLOC], [ ], [ ])
+ wrap_syms="${wrap_syms} __libc_valloc"])
+ AC_CHECK_FUNC([__posix_memalign],
+ [AC_DEFINE([JEMALLOC_OVERRIDE___POSIX_MEMALIGN], [ ], [ ])
+ wrap_syms="${wrap_syms} __posix_memalign"])
+fi
+
+case "${host}" in
+ *-*-mingw* | *-*-cygwin*)
+ wrap_syms="${wrap_syms} tls_callback"
+ ;;
+ *)
+ ;;
+esac
+
+dnl Mangle library-private APIs.
+AC_ARG_WITH([private_namespace],
+ [AS_HELP_STRING([--with-private-namespace=<prefix>], [Prefix to prepend to all library-private APIs])],
+ [JEMALLOC_PRIVATE_NAMESPACE="${with_private_namespace}je_"],
+ [JEMALLOC_PRIVATE_NAMESPACE="je_"]
+)
+AC_DEFINE_UNQUOTED([JEMALLOC_PRIVATE_NAMESPACE], [$JEMALLOC_PRIVATE_NAMESPACE], [ ])
+private_namespace="$JEMALLOC_PRIVATE_NAMESPACE"
+AC_SUBST([private_namespace])
+
+dnl Do not add suffix to installed files by default.
+AC_ARG_WITH([install_suffix],
+ [AS_HELP_STRING([--with-install-suffix=<suffix>], [Suffix to append to all installed files])],
+ [case "$with_install_suffix" in
+ *\ * ) AC_MSG_ERROR([Install suffix should not contain spaces]) ;;
+ * ) INSTALL_SUFFIX="$with_install_suffix" ;;
+esac],
+ [INSTALL_SUFFIX=]
+)
+install_suffix="$INSTALL_SUFFIX"
+AC_SUBST([install_suffix])
+
+dnl Specify default malloc_conf.
+AC_ARG_WITH([malloc_conf],
+ [AS_HELP_STRING([--with-malloc-conf=<malloc_conf>], [config.malloc_conf options string])],
+ [JEMALLOC_CONFIG_MALLOC_CONF="$with_malloc_conf"],
+ [JEMALLOC_CONFIG_MALLOC_CONF=""]
+)
+config_malloc_conf="$JEMALLOC_CONFIG_MALLOC_CONF"
+AC_DEFINE_UNQUOTED([JEMALLOC_CONFIG_MALLOC_CONF], ["$config_malloc_conf"], [ ])
+
+dnl Substitute @je_@ in jemalloc_protos.h.in, primarily to make generation of
+dnl jemalloc_protos_jet.h easy.
+je_="je_"
+AC_SUBST([je_])
+
+cfgoutputs_in="Makefile.in"
+cfgoutputs_in="${cfgoutputs_in} jemalloc.pc.in"
+cfgoutputs_in="${cfgoutputs_in} doc/html.xsl.in"
+cfgoutputs_in="${cfgoutputs_in} doc/manpages.xsl.in"
+cfgoutputs_in="${cfgoutputs_in} doc/jemalloc.xml.in"
+cfgoutputs_in="${cfgoutputs_in} include/jemalloc/jemalloc_macros.h.in"
+cfgoutputs_in="${cfgoutputs_in} include/jemalloc/jemalloc_protos.h.in"
+cfgoutputs_in="${cfgoutputs_in} include/jemalloc/jemalloc_typedefs.h.in"
+cfgoutputs_in="${cfgoutputs_in} include/jemalloc/internal/jemalloc_preamble.h.in"
+cfgoutputs_in="${cfgoutputs_in} test/test.sh.in"
+cfgoutputs_in="${cfgoutputs_in} test/include/test/jemalloc_test.h.in"
+
+cfgoutputs_out="Makefile"
+cfgoutputs_out="${cfgoutputs_out} jemalloc.pc"
+cfgoutputs_out="${cfgoutputs_out} doc/html.xsl"
+cfgoutputs_out="${cfgoutputs_out} doc/manpages.xsl"
+cfgoutputs_out="${cfgoutputs_out} doc/jemalloc.xml"
+cfgoutputs_out="${cfgoutputs_out} include/jemalloc/jemalloc_macros.h"
+cfgoutputs_out="${cfgoutputs_out} include/jemalloc/jemalloc_protos.h"
+cfgoutputs_out="${cfgoutputs_out} include/jemalloc/jemalloc_typedefs.h"
+cfgoutputs_out="${cfgoutputs_out} include/jemalloc/internal/jemalloc_preamble.h"
+cfgoutputs_out="${cfgoutputs_out} test/test.sh"
+cfgoutputs_out="${cfgoutputs_out} test/include/test/jemalloc_test.h"
+
+cfgoutputs_tup="Makefile"
+cfgoutputs_tup="${cfgoutputs_tup} jemalloc.pc:jemalloc.pc.in"
+cfgoutputs_tup="${cfgoutputs_tup} doc/html.xsl:doc/html.xsl.in"
+cfgoutputs_tup="${cfgoutputs_tup} doc/manpages.xsl:doc/manpages.xsl.in"
+cfgoutputs_tup="${cfgoutputs_tup} doc/jemalloc.xml:doc/jemalloc.xml.in"
+cfgoutputs_tup="${cfgoutputs_tup} include/jemalloc/jemalloc_macros.h:include/jemalloc/jemalloc_macros.h.in"
+cfgoutputs_tup="${cfgoutputs_tup} include/jemalloc/jemalloc_protos.h:include/jemalloc/jemalloc_protos.h.in"
+cfgoutputs_tup="${cfgoutputs_tup} include/jemalloc/jemalloc_typedefs.h:include/jemalloc/jemalloc_typedefs.h.in"
+cfgoutputs_tup="${cfgoutputs_tup} include/jemalloc/internal/jemalloc_preamble.h"
+cfgoutputs_tup="${cfgoutputs_tup} test/test.sh:test/test.sh.in"
+cfgoutputs_tup="${cfgoutputs_tup} test/include/test/jemalloc_test.h:test/include/test/jemalloc_test.h.in"
+
+cfghdrs_in="include/jemalloc/jemalloc_defs.h.in"
+cfghdrs_in="${cfghdrs_in} include/jemalloc/internal/jemalloc_internal_defs.h.in"
+cfghdrs_in="${cfghdrs_in} include/jemalloc/internal/private_symbols.sh"
+cfghdrs_in="${cfghdrs_in} include/jemalloc/internal/private_namespace.sh"
+cfghdrs_in="${cfghdrs_in} include/jemalloc/internal/public_namespace.sh"
+cfghdrs_in="${cfghdrs_in} include/jemalloc/internal/public_unnamespace.sh"
+cfghdrs_in="${cfghdrs_in} include/jemalloc/jemalloc_rename.sh"
+cfghdrs_in="${cfghdrs_in} include/jemalloc/jemalloc_mangle.sh"
+cfghdrs_in="${cfghdrs_in} include/jemalloc/jemalloc.sh"
+cfghdrs_in="${cfghdrs_in} test/include/test/jemalloc_test_defs.h.in"
+
+cfghdrs_out="include/jemalloc/jemalloc_defs.h"
+cfghdrs_out="${cfghdrs_out} include/jemalloc/jemalloc${install_suffix}.h"
+cfghdrs_out="${cfghdrs_out} include/jemalloc/internal/private_symbols.awk"
+cfghdrs_out="${cfghdrs_out} include/jemalloc/internal/private_symbols_jet.awk"
+cfghdrs_out="${cfghdrs_out} include/jemalloc/internal/public_symbols.txt"
+cfghdrs_out="${cfghdrs_out} include/jemalloc/internal/public_namespace.h"
+cfghdrs_out="${cfghdrs_out} include/jemalloc/internal/public_unnamespace.h"
+cfghdrs_out="${cfghdrs_out} include/jemalloc/jemalloc_protos_jet.h"
+cfghdrs_out="${cfghdrs_out} include/jemalloc/jemalloc_rename.h"
+cfghdrs_out="${cfghdrs_out} include/jemalloc/jemalloc_mangle.h"
+cfghdrs_out="${cfghdrs_out} include/jemalloc/jemalloc_mangle_jet.h"
+cfghdrs_out="${cfghdrs_out} include/jemalloc/internal/jemalloc_internal_defs.h"
+cfghdrs_out="${cfghdrs_out} test/include/test/jemalloc_test_defs.h"
+
+cfghdrs_tup="include/jemalloc/jemalloc_defs.h:include/jemalloc/jemalloc_defs.h.in"
+cfghdrs_tup="${cfghdrs_tup} include/jemalloc/internal/jemalloc_internal_defs.h:include/jemalloc/internal/jemalloc_internal_defs.h.in"
+cfghdrs_tup="${cfghdrs_tup} test/include/test/jemalloc_test_defs.h:test/include/test/jemalloc_test_defs.h.in"
+
+dnl ============================================================================
+dnl jemalloc build options.
+dnl
+
+dnl Do not compile with debugging by default.
+AC_ARG_ENABLE([debug],
+ [AS_HELP_STRING([--enable-debug],
+ [Build debugging code])],
+[if test "x$enable_debug" = "xno" ; then
+ enable_debug="0"
+else
+ enable_debug="1"
+fi
+],
+[enable_debug="0"]
+)
+if test "x$enable_debug" = "x1" ; then
+ AC_DEFINE([JEMALLOC_DEBUG], [ ], [ ])
+fi
+AC_SUBST([enable_debug])
+
+dnl Only optimize if not debugging.
+if test "x$enable_debug" = "x0" ; then
+ if test "x$GCC" = "xyes" ; then
+ JE_CFLAGS_ADD([-O3])
+ JE_CXXFLAGS_ADD([-O3])
+ JE_CFLAGS_ADD([-funroll-loops])
+ elif test "x$je_cv_msvc" = "xyes" ; then
+ JE_CFLAGS_ADD([-O2])
+ JE_CXXFLAGS_ADD([-O2])
+ else
+ JE_CFLAGS_ADD([-O])
+ JE_CXXFLAGS_ADD([-O])
+ fi
+fi
+
+dnl Enable statistics calculation by default.
+AC_ARG_ENABLE([stats],
+ [AS_HELP_STRING([--disable-stats],
+ [Disable statistics calculation/reporting])],
+[if test "x$enable_stats" = "xno" ; then
+ enable_stats="0"
+else
+ enable_stats="1"
+fi
+],
+[enable_stats="1"]
+)
+if test "x$enable_stats" = "x1" ; then
+ AC_DEFINE([JEMALLOC_STATS], [ ], [ ])
+fi
+AC_SUBST([enable_stats])
+
+dnl Do not enable smallocx by default.
+AC_ARG_ENABLE([experimental_smallocx],
+ [AS_HELP_STRING([--enable-experimental-smallocx], [Enable experimental smallocx API])],
+[if test "x$enable_experimental_smallocx" = "xno" ; then
+enable_experimental_smallocx="0"
+else
+enable_experimental_smallocx="1"
+fi
+],
+[enable_experimental_smallocx="0"]
+)
+if test "x$enable_experimental_smallocx" = "x1" ; then
+ AC_DEFINE([JEMALLOC_EXPERIMENTAL_SMALLOCX_API], [ ], [ ])
+fi
+AC_SUBST([enable_experimental_smallocx])
+
+dnl Do not enable profiling by default.
+AC_ARG_ENABLE([prof],
+ [AS_HELP_STRING([--enable-prof], [Enable allocation profiling])],
+[if test "x$enable_prof" = "xno" ; then
+ enable_prof="0"
+else
+ enable_prof="1"
+fi
+],
+[enable_prof="0"]
+)
+if test "x$enable_prof" = "x1" ; then
+ backtrace_method=""
+else
+ backtrace_method="N/A"
+fi
+
+AC_ARG_ENABLE([prof-libunwind],
+ [AS_HELP_STRING([--enable-prof-libunwind], [Use libunwind for backtracing])],
+[if test "x$enable_prof_libunwind" = "xno" ; then
+ enable_prof_libunwind="0"
+else
+ enable_prof_libunwind="1"
+ if test "x$enable_prof" = "x0" ; then
+ AC_MSG_ERROR([--enable-prof-libunwind should only be used with --enable-prof])
+ fi
+fi
+],
+[enable_prof_libunwind="0"]
+)
+AC_ARG_WITH([static_libunwind],
+ [AS_HELP_STRING([--with-static-libunwind=<libunwind.a>],
+ [Path to static libunwind library; use rather than dynamically linking])],
+if test "x$with_static_libunwind" = "xno" ; then
+ LUNWIND="-lunwind"
+else
+ if test ! -f "$with_static_libunwind" ; then
+ AC_MSG_ERROR([Static libunwind not found: $with_static_libunwind])
+ fi
+ LUNWIND="$with_static_libunwind"
+fi,
+ LUNWIND="-lunwind"
+)
+if test "x$backtrace_method" = "x" -a "x$enable_prof_libunwind" = "x1" ; then
+ AC_CHECK_HEADERS([libunwind.h], , [enable_prof_libunwind="0"])
+ if test "x$LUNWIND" = "x-lunwind" ; then
+ AC_CHECK_LIB([unwind], [unw_backtrace], [JE_APPEND_VS(LIBS, $LUNWIND)],
+ [enable_prof_libunwind="0"])
+ else
+ JE_APPEND_VS(LIBS, $LUNWIND)
+ fi
+ if test "x${enable_prof_libunwind}" = "x1" ; then
+ backtrace_method="libunwind"
+ AC_DEFINE([JEMALLOC_PROF_LIBUNWIND], [ ], [ ])
+ fi
+fi
+
+AC_ARG_ENABLE([prof-libgcc],
+ [AS_HELP_STRING([--disable-prof-libgcc],
+ [Do not use libgcc for backtracing])],
+[if test "x$enable_prof_libgcc" = "xno" ; then
+ enable_prof_libgcc="0"
+else
+ enable_prof_libgcc="1"
+fi
+],
+[enable_prof_libgcc="1"]
+)
+if test "x$backtrace_method" = "x" -a "x$enable_prof_libgcc" = "x1" \
+ -a "x$GCC" = "xyes" ; then
+ AC_CHECK_HEADERS([unwind.h], , [enable_prof_libgcc="0"])
+ if test "x${enable_prof_libgcc}" = "x1" ; then
+ AC_CHECK_LIB([gcc], [_Unwind_Backtrace], [JE_APPEND_VS(LIBS, -lgcc)], [enable_prof_libgcc="0"])
+ fi
+ if test "x${enable_prof_libgcc}" = "x1" ; then
+ backtrace_method="libgcc"
+ AC_DEFINE([JEMALLOC_PROF_LIBGCC], [ ], [ ])
+ fi
+else
+ enable_prof_libgcc="0"
+fi
+
+AC_ARG_ENABLE([prof-gcc],
+ [AS_HELP_STRING([--disable-prof-gcc],
+ [Do not use gcc intrinsics for backtracing])],
+[if test "x$enable_prof_gcc" = "xno" ; then
+ enable_prof_gcc="0"
+else
+ enable_prof_gcc="1"
+fi
+],
+[enable_prof_gcc="1"]
+)
+if test "x$backtrace_method" = "x" -a "x$enable_prof_gcc" = "x1" \
+ -a "x$GCC" = "xyes" ; then
+ JE_CFLAGS_ADD([-fno-omit-frame-pointer])
+ backtrace_method="gcc intrinsics"
+ AC_DEFINE([JEMALLOC_PROF_GCC], [ ], [ ])
+else
+ enable_prof_gcc="0"
+fi
+
+if test "x$backtrace_method" = "x" ; then
+ backtrace_method="none (disabling profiling)"
+ enable_prof="0"
+fi
+AC_MSG_CHECKING([configured backtracing method])
+AC_MSG_RESULT([$backtrace_method])
+if test "x$enable_prof" = "x1" ; then
+ dnl Heap profiling uses the log(3) function.
+ JE_APPEND_VS(LIBS, $LM)
+
+ AC_DEFINE([JEMALLOC_PROF], [ ], [ ])
+fi
+AC_SUBST([enable_prof])
+
+dnl Indicate whether adjacent virtual memory mappings automatically coalesce
+dnl (and fragment on demand).
+if test "x${maps_coalesce}" = "x1" ; then
+ AC_DEFINE([JEMALLOC_MAPS_COALESCE], [ ], [ ])
+fi
+
+dnl Indicate whether to retain memory (rather than using munmap()) by default.
+if test "x$default_retain" = "x1" ; then
+ AC_DEFINE([JEMALLOC_RETAIN], [ ], [ ])
+fi
+
+dnl Indicate whether realloc(ptr, 0) defaults to the "alloc" behavior.
+if test "x$zero_realloc_default_free" = "x1" ; then
+ AC_DEFINE([JEMALLOC_ZERO_REALLOC_DEFAULT_FREE], [ ], [ ])
+fi
+
+dnl Enable allocation from DSS if supported by the OS.
+have_dss="1"
+dnl Check whether the BSD/SUSv1 sbrk() exists. If not, disable DSS support.
+AC_CHECK_FUNC([sbrk], [have_sbrk="1"], [have_sbrk="0"])
+if test "x$have_sbrk" = "x1" ; then
+ if test "x$sbrk_deprecated" = "x1" ; then
+ AC_MSG_RESULT([Disabling dss allocation because sbrk is deprecated])
+ have_dss="0"
+ fi
+else
+ have_dss="0"
+fi
+
+if test "x$have_dss" = "x1" ; then
+ AC_DEFINE([JEMALLOC_DSS], [ ], [ ])
+fi
+
+dnl Support the junk/zero filling option by default.
+AC_ARG_ENABLE([fill],
+ [AS_HELP_STRING([--disable-fill], [Disable support for junk/zero filling])],
+[if test "x$enable_fill" = "xno" ; then
+ enable_fill="0"
+else
+ enable_fill="1"
+fi
+],
+[enable_fill="1"]
+)
+if test "x$enable_fill" = "x1" ; then
+ AC_DEFINE([JEMALLOC_FILL], [ ], [ ])
+fi
+AC_SUBST([enable_fill])
+
+dnl Disable utrace(2)-based tracing by default.
+AC_ARG_ENABLE([utrace],
+ [AS_HELP_STRING([--enable-utrace], [Enable utrace(2)-based tracing])],
+[if test "x$enable_utrace" = "xno" ; then
+ enable_utrace="0"
+else
+ enable_utrace="1"
+fi
+],
+[enable_utrace="0"]
+)
+JE_COMPILABLE([utrace(2)], [
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <sys/ktrace.h>
+], [
+ utrace((void *)0, 0);
+], [je_cv_utrace])
+if test "x${je_cv_utrace}" = "xno" ; then
+ JE_COMPILABLE([utrace(2) with label], [
+ #include <sys/types.h>
+ #include <sys/param.h>
+ #include <sys/time.h>
+ #include <sys/uio.h>
+ #include <sys/ktrace.h>
+ ], [
+ utrace((void *)0, (void *)0, 0);
+ ], [je_cv_utrace_label])
+ if test "x${je_cv_utrace_label}" = "xno"; then
+ enable_utrace="0"
+ fi
+ if test "x$enable_utrace" = "x1" ; then
+ AC_DEFINE([JEMALLOC_UTRACE_LABEL], [ ], [ ])
+ fi
+else
+ if test "x$enable_utrace" = "x1" ; then
+ AC_DEFINE([JEMALLOC_UTRACE], [ ], [ ])
+ fi
+fi
+AC_SUBST([enable_utrace])
+
+dnl Do not support the xmalloc option by default.
+AC_ARG_ENABLE([xmalloc],
+ [AS_HELP_STRING([--enable-xmalloc], [Support xmalloc option])],
+[if test "x$enable_xmalloc" = "xno" ; then
+ enable_xmalloc="0"
+else
+ enable_xmalloc="1"
+fi
+],
+[enable_xmalloc="0"]
+)
+if test "x$enable_xmalloc" = "x1" ; then
+ AC_DEFINE([JEMALLOC_XMALLOC], [ ], [ ])
+fi
+AC_SUBST([enable_xmalloc])
+
+dnl Support cache-oblivious allocation alignment by default.
+AC_ARG_ENABLE([cache-oblivious],
+ [AS_HELP_STRING([--disable-cache-oblivious],
+ [Disable support for cache-oblivious allocation alignment])],
+[if test "x$enable_cache_oblivious" = "xno" ; then
+ enable_cache_oblivious="0"
+else
+ enable_cache_oblivious="1"
+fi
+],
+[enable_cache_oblivious="1"]
+)
+if test "x$enable_cache_oblivious" = "x1" ; then
+ AC_DEFINE([JEMALLOC_CACHE_OBLIVIOUS], [ ], [ ])
+fi
+AC_SUBST([enable_cache_oblivious])
+
+dnl Do not log by default.
+AC_ARG_ENABLE([log],
+ [AS_HELP_STRING([--enable-log], [Support debug logging])],
+[if test "x$enable_log" = "xno" ; then
+ enable_log="0"
+else
+ enable_log="1"
+fi
+],
+[enable_log="0"]
+)
+if test "x$enable_log" = "x1" ; then
+ AC_DEFINE([JEMALLOC_LOG], [ ], [ ])
+fi
+AC_SUBST([enable_log])
+
+dnl Do not use readlinkat by default
+AC_ARG_ENABLE([readlinkat],
+ [AS_HELP_STRING([--enable-readlinkat], [Use readlinkat over readlink])],
+[if test "x$enable_readlinkat" = "xno" ; then
+ enable_readlinkat="0"
+else
+ enable_readlinkat="1"
+fi
+],
+[enable_readlinkat="0"]
+)
+if test "x$enable_readlinkat" = "x1" ; then
+ AC_DEFINE([JEMALLOC_READLINKAT], [ ], [ ])
+fi
+AC_SUBST([enable_readlinkat])
+
+dnl Avoid extra safety checks by default
+AC_ARG_ENABLE([opt-safety-checks],
+ [AS_HELP_STRING([--enable-opt-safety-checks],
+ [Perform certain low-overhead checks, even in opt mode])],
+[if test "x$enable_opt_safety_checks" = "xno" ; then
+ enable_opt_safety_checks="0"
+else
+ enable_opt_safety_checks="1"
+fi
+],
+[enable_opt_safety_checks="0"]
+)
+if test "x$enable_opt_safety_checks" = "x1" ; then
+ AC_DEFINE([JEMALLOC_OPT_SAFETY_CHECKS], [ ], [ ])
+fi
+AC_SUBST([enable_opt_safety_checks])
+
+dnl Look for sized-deallocation bugs while otherwise being in opt mode.
+AC_ARG_ENABLE([opt-size-checks],
+ [AS_HELP_STRING([--enable-opt-size-checks],
+ [Perform sized-deallocation argument checks, even in opt mode])],
+[if test "x$enable_opt_size_checks" = "xno" ; then
+ enable_opt_size_checks="0"
+else
+ enable_opt_size_checks="1"
+fi
+],
+[enable_opt_size_checks="0"]
+)
+if test "x$enable_opt_size_checks" = "x1" ; then
+ AC_DEFINE([JEMALLOC_OPT_SIZE_CHECKS], [ ], [ ])
+fi
+AC_SUBST([enable_opt_size_checks])
+
+dnl Do not check for use-after-free by default.
+AC_ARG_ENABLE([uaf-detection],
+ [AS_HELP_STRING([--enable-uaf-detection],
+ [Allow sampled junk-filling on deallocation to detect use-after-free])],
+[if test "x$enable_uaf_detection" = "xno" ; then
+ enable_uaf_detection="0"
+else
+ enable_uaf_detection="1"
+fi
+],
+[enable_uaf_detection="0"]
+)
+if test "x$enable_uaf_detection" = "x1" ; then
+ AC_DEFINE([JEMALLOC_UAF_DETECTION], [ ])
+fi
+AC_SUBST([enable_uaf_detection])
+
+JE_COMPILABLE([a program using __builtin_unreachable], [
+void foo (void) {
+ __builtin_unreachable();
+}
+], [
+ {
+ foo();
+ }
+], [je_cv_gcc_builtin_unreachable])
+if test "x${je_cv_gcc_builtin_unreachable}" = "xyes" ; then
+ AC_DEFINE([JEMALLOC_INTERNAL_UNREACHABLE], [__builtin_unreachable], [ ])
+else
+ AC_DEFINE([JEMALLOC_INTERNAL_UNREACHABLE], [abort], [ ])
+fi
+
+dnl ============================================================================
+dnl Check for __builtin_ffsl(), then ffsl(3), and fail if neither are found.
+dnl One of those two functions should (theoretically) exist on all platforms
+dnl that jemalloc currently has a chance of functioning on without modification.
+dnl We additionally assume ffs[ll]() or __builtin_ffs[ll]() are defined if
+dnl ffsl() or __builtin_ffsl() are defined, respectively.
+JE_COMPILABLE([a program using __builtin_ffsl], [
+#include <stdio.h>
+#include <strings.h>
+#include <string.h>
+], [
+ {
+ int rv = __builtin_ffsl(0x08);
+ printf("%d\n", rv);
+ }
+], [je_cv_gcc_builtin_ffsl])
+if test "x${je_cv_gcc_builtin_ffsl}" = "xyes" ; then
+ AC_DEFINE([JEMALLOC_INTERNAL_FFSLL], [__builtin_ffsll], [ ])
+ AC_DEFINE([JEMALLOC_INTERNAL_FFSL], [__builtin_ffsl], [ ])
+ AC_DEFINE([JEMALLOC_INTERNAL_FFS], [__builtin_ffs], [ ])
+else
+ JE_COMPILABLE([a program using ffsl], [
+ #include <stdio.h>
+ #include <strings.h>
+ #include <string.h>
+ ], [
+ {
+ int rv = ffsl(0x08);
+ printf("%d\n", rv);
+ }
+ ], [je_cv_function_ffsl])
+ if test "x${je_cv_function_ffsl}" = "xyes" ; then
+ AC_DEFINE([JEMALLOC_INTERNAL_FFSLL], [ffsll], [ ])
+ AC_DEFINE([JEMALLOC_INTERNAL_FFSL], [ffsl], [ ])
+ AC_DEFINE([JEMALLOC_INTERNAL_FFS], [ffs], [ ])
+ else
+ AC_MSG_ERROR([Cannot build without ffsl(3) or __builtin_ffsl()])
+ fi
+fi
+
+JE_COMPILABLE([a program using __builtin_popcountl], [
+#include <stdio.h>
+#include <strings.h>
+#include <string.h>
+], [
+ {
+ int rv = __builtin_popcountl(0x08);
+ printf("%d\n", rv);
+ }
+], [je_cv_gcc_builtin_popcountl])
+if test "x${je_cv_gcc_builtin_popcountl}" = "xyes" ; then
+ AC_DEFINE([JEMALLOC_INTERNAL_POPCOUNT], [__builtin_popcount], [ ])
+ AC_DEFINE([JEMALLOC_INTERNAL_POPCOUNTL], [__builtin_popcountl], [ ])
+ AC_DEFINE([JEMALLOC_INTERNAL_POPCOUNTLL], [__builtin_popcountll], [ ])
+fi
+
+AC_ARG_WITH([lg_quantum],
+ [AS_HELP_STRING([--with-lg-quantum=<lg-quantum>],
+ [Base 2 log of minimum allocation alignment])])
+if test "x$with_lg_quantum" != "x" ; then
+ AC_DEFINE_UNQUOTED([LG_QUANTUM], [$with_lg_quantum], [ ])
+fi
+
+AC_ARG_WITH([lg_slab_maxregs],
+ [AS_HELP_STRING([--with-lg-slab-maxregs=<lg-slab-maxregs>],
+ [Base 2 log of maximum number of regions in a slab (used with malloc_conf slab_sizes)])],
+ [CONFIG_LG_SLAB_MAXREGS="with_lg_slab_maxregs"],
+ [CONFIG_LG_SLAB_MAXREGS=""])
+if test "x$with_lg_slab_maxregs" != "x" ; then
+ AC_DEFINE_UNQUOTED([CONFIG_LG_SLAB_MAXREGS], [$with_lg_slab_maxregs], [ ])
+fi
+
+AC_ARG_WITH([lg_page],
+ [AS_HELP_STRING([--with-lg-page=<lg-page>], [Base 2 log of system page size])],
+ [LG_PAGE="$with_lg_page"], [LG_PAGE="detect"])
+case "${host}" in
+ aarch64-apple-darwin*)
+ dnl When cross-compile for Apple M1 and no page size specified, use the
+ dnl default and skip detecting the page size (which is likely incorrect).
+ if test "x${host}" != "x${build}" -a "x$LG_PAGE" = "xdetect"; then
+ LG_PAGE=14
+ fi
+ ;;
+esac
+if test "x$LG_PAGE" = "xdetect"; then
+ AC_CACHE_CHECK([LG_PAGE],
+ [je_cv_lg_page],
+ AC_RUN_IFELSE([AC_LANG_PROGRAM(
+[[
+#include <strings.h>
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <unistd.h>
+#endif
+#include <stdio.h>
+]],
+[[
+ int result;
+ FILE *f;
+
+#ifdef _WIN32
+ SYSTEM_INFO si;
+ GetSystemInfo(&si);
+ result = si.dwPageSize;
+#else
+ result = sysconf(_SC_PAGESIZE);
+#endif
+ if (result == -1) {
+ return 1;
+ }
+ result = JEMALLOC_INTERNAL_FFSL(result) - 1;
+
+ f = fopen("conftest.out", "w");
+ if (f == NULL) {
+ return 1;
+ }
+ fprintf(f, "%d", result);
+ fclose(f);
+
+ return 0;
+]])],
+ [je_cv_lg_page=`cat conftest.out`],
+ [je_cv_lg_page=undefined],
+ [je_cv_lg_page=12]))
+fi
+if test "x${je_cv_lg_page}" != "x" ; then
+ LG_PAGE="${je_cv_lg_page}"
+fi
+if test "x${LG_PAGE}" != "xundefined" ; then
+ AC_DEFINE_UNQUOTED([LG_PAGE], [$LG_PAGE], [ ])
+else
+ AC_MSG_ERROR([cannot determine value for LG_PAGE])
+fi
+
+AC_ARG_WITH([lg_hugepage],
+ [AS_HELP_STRING([--with-lg-hugepage=<lg-hugepage>],
+ [Base 2 log of system huge page size])],
+ [je_cv_lg_hugepage="${with_lg_hugepage}"],
+ [je_cv_lg_hugepage=""])
+if test "x${je_cv_lg_hugepage}" = "x" ; then
+ dnl Look in /proc/meminfo (Linux-specific) for information on the default huge
+ dnl page size, if any. The relevant line looks like:
+ dnl
+ dnl Hugepagesize: 2048 kB
+ if test -e "/proc/meminfo" ; then
+ hpsk=[`cat /proc/meminfo 2>/dev/null | \
+ grep -e '^Hugepagesize:[[:space:]]\+[0-9]\+[[:space:]]kB$' | \
+ awk '{print $2}'`]
+ if test "x${hpsk}" != "x" ; then
+ je_cv_lg_hugepage=10
+ while test "${hpsk}" -gt 1 ; do
+ hpsk="$((hpsk / 2))"
+ je_cv_lg_hugepage="$((je_cv_lg_hugepage + 1))"
+ done
+ fi
+ fi
+
+ dnl Set default if unable to automatically configure.
+ if test "x${je_cv_lg_hugepage}" = "x" ; then
+ je_cv_lg_hugepage=21
+ fi
+fi
+if test "x${LG_PAGE}" != "xundefined" -a \
+ "${je_cv_lg_hugepage}" -lt "${LG_PAGE}" ; then
+ AC_MSG_ERROR([Huge page size (2^${je_cv_lg_hugepage}) must be at least page size (2^${LG_PAGE})])
+fi
+AC_DEFINE_UNQUOTED([LG_HUGEPAGE], [${je_cv_lg_hugepage}], [ ])
+
+dnl ============================================================================
+dnl Enable libdl by default.
+AC_ARG_ENABLE([libdl],
+ [AS_HELP_STRING([--disable-libdl],
+ [Do not use libdl])],
+[if test "x$enable_libdl" = "xno" ; then
+ enable_libdl="0"
+else
+ enable_libdl="1"
+fi
+],
+[enable_libdl="1"]
+)
+AC_SUBST([libdl])
+
+dnl ============================================================================
+dnl Configure pthreads.
+
+if test "x$abi" != "xpecoff" ; then
+ AC_DEFINE([JEMALLOC_HAVE_PTHREAD], [ ], [ ])
+ AC_CHECK_HEADERS([pthread.h], , [AC_MSG_ERROR([pthread.h is missing])])
+ dnl Some systems may embed pthreads functionality in libc; check for libpthread
+ dnl first, but try libc too before failing.
+ AC_CHECK_LIB([pthread], [pthread_create], [JE_APPEND_VS(LIBS, -pthread)],
+ [AC_SEARCH_LIBS([pthread_create], , ,
+ AC_MSG_ERROR([libpthread is missing]))])
+ wrap_syms="${wrap_syms} pthread_create"
+ have_pthread="1"
+
+dnl Check if we have dlsym support.
+ if test "x$enable_libdl" = "x1" ; then
+ have_dlsym="1"
+ AC_CHECK_HEADERS([dlfcn.h],
+ AC_CHECK_FUNC([dlsym], [],
+ [AC_CHECK_LIB([dl], [dlsym], [LIBS="$LIBS -ldl"], [have_dlsym="0"])]),
+ [have_dlsym="0"])
+ if test "x$have_dlsym" = "x1" ; then
+ AC_DEFINE([JEMALLOC_HAVE_DLSYM], [ ], [ ])
+ fi
+ else
+ have_dlsym="0"
+ fi
+
+ JE_COMPILABLE([pthread_atfork(3)], [
+#include <pthread.h>
+], [
+ pthread_atfork((void *)0, (void *)0, (void *)0);
+], [je_cv_pthread_atfork])
+ if test "x${je_cv_pthread_atfork}" = "xyes" ; then
+ AC_DEFINE([JEMALLOC_HAVE_PTHREAD_ATFORK], [ ], [ ])
+ fi
+ dnl Check if pthread_setname_np is available with the expected API.
+ JE_COMPILABLE([pthread_setname_np(3)], [
+#include <pthread.h>
+], [
+ pthread_setname_np(pthread_self(), "setname_test");
+], [je_cv_pthread_setname_np])
+ if test "x${je_cv_pthread_setname_np}" = "xyes" ; then
+ AC_DEFINE([JEMALLOC_HAVE_PTHREAD_SETNAME_NP], [ ], [ ])
+ fi
+ dnl Check if pthread_getname_np is not necessarily present despite
+ dnl the pthread_setname_np counterpart
+ JE_COMPILABLE([pthread_getname_np(3)], [
+#include <pthread.h>
+#include <stdlib.h>
+], [
+ {
+ char *name = malloc(16);
+ pthread_getname_np(pthread_self(), name, 16);
+ free(name);
+ }
+], [je_cv_pthread_getname_np])
+ if test "x${je_cv_pthread_getname_np}" = "xyes" ; then
+ AC_DEFINE([JEMALLOC_HAVE_PTHREAD_GETNAME_NP], [ ], [ ])
+ fi
+ dnl Check if pthread_get_name_np is not necessarily present despite
+ dnl the pthread_set_name_np counterpart
+ JE_COMPILABLE([pthread_get_name_np(3)], [
+#include <pthread.h>
+#include <pthread_np.h>
+#include <stdlib.h>
+], [
+ {
+ char *name = malloc(16);
+ pthread_get_name_np(pthread_self(), name, 16);
+ free(name);
+ }
+], [je_cv_pthread_get_name_np])
+ if test "x${je_cv_pthread_get_name_np}" = "xyes" ; then
+ AC_DEFINE([JEMALLOC_HAVE_PTHREAD_GET_NAME_NP], [ ], [ ])
+ fi
+fi
+
+JE_APPEND_VS(CPPFLAGS, -D_REENTRANT)
+
+dnl Check whether clock_gettime(2) is in libc or librt.
+AC_SEARCH_LIBS([clock_gettime], [rt])
+
+dnl Cray wrapper compiler often adds `-lrt` when using `-static`. Check with
+dnl `-dynamic` as well in case a user tries to dynamically link in jemalloc
+if test "x$je_cv_cray_prgenv_wrapper" = "xyes" ; then
+ if test "$ac_cv_search_clock_gettime" != "-lrt"; then
+ JE_CFLAGS_SAVE()
+
+ unset ac_cv_search_clock_gettime
+ JE_CFLAGS_ADD([-dynamic])
+ AC_SEARCH_LIBS([clock_gettime], [rt])
+
+ JE_CFLAGS_RESTORE()
+ fi
+fi
+
+dnl check for CLOCK_MONOTONIC_COARSE (Linux-specific).
+JE_COMPILABLE([clock_gettime(CLOCK_MONOTONIC_COARSE, ...)], [
+#include <time.h>
+], [
+ struct timespec ts;
+
+ clock_gettime(CLOCK_MONOTONIC_COARSE, &ts);
+], [je_cv_clock_monotonic_coarse])
+if test "x${je_cv_clock_monotonic_coarse}" = "xyes" ; then
+ AC_DEFINE([JEMALLOC_HAVE_CLOCK_MONOTONIC_COARSE], [ ], [ ])
+fi
+
+dnl check for CLOCK_MONOTONIC.
+JE_COMPILABLE([clock_gettime(CLOCK_MONOTONIC, ...)], [
+#include <unistd.h>
+#include <time.h>
+], [
+ struct timespec ts;
+
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+#if !defined(_POSIX_MONOTONIC_CLOCK) || _POSIX_MONOTONIC_CLOCK < 0
+# error _POSIX_MONOTONIC_CLOCK missing/invalid
+#endif
+], [je_cv_clock_monotonic])
+if test "x${je_cv_clock_monotonic}" = "xyes" ; then
+ AC_DEFINE([JEMALLOC_HAVE_CLOCK_MONOTONIC], [ ], [ ])
+fi
+
+dnl Check for mach_absolute_time().
+JE_COMPILABLE([mach_absolute_time()], [
+#include <mach/mach_time.h>
+], [
+ mach_absolute_time();
+], [je_cv_mach_absolute_time])
+if test "x${je_cv_mach_absolute_time}" = "xyes" ; then
+ AC_DEFINE([JEMALLOC_HAVE_MACH_ABSOLUTE_TIME], [ ], [ ])
+fi
+
+dnl check for CLOCK_REALTIME (always should be available on Linux)
+JE_COMPILABLE([clock_gettime(CLOCK_REALTIME, ...)], [
+#include <time.h>
+], [
+ struct timespec ts;
+
+ clock_gettime(CLOCK_REALTIME, &ts);
+], [je_cv_clock_realtime])
+if test "x${je_cv_clock_realtime}" = "xyes" ; then
+ AC_DEFINE([JEMALLOC_HAVE_CLOCK_REALTIME], [ ], [ ])
+fi
+
+dnl Use syscall(2) (if available) by default.
+AC_ARG_ENABLE([syscall],
+ [AS_HELP_STRING([--disable-syscall], [Disable use of syscall(2)])],
+[if test "x$enable_syscall" = "xno" ; then
+ enable_syscall="0"
+else
+ enable_syscall="1"
+fi
+],
+[enable_syscall="1"]
+)
+if test "x$enable_syscall" = "x1" ; then
+ dnl Check if syscall(2) is usable. Treat warnings as errors, so that e.g. OS
+ dnl X 10.12's deprecation warning prevents use.
+ JE_CFLAGS_SAVE()
+ JE_CFLAGS_ADD([-Werror])
+ JE_COMPILABLE([syscall(2)], [
+#include <sys/syscall.h>
+#include <unistd.h>
+], [
+ syscall(SYS_write, 2, "hello", 5);
+],
+ [je_cv_syscall])
+ JE_CFLAGS_RESTORE()
+ if test "x$je_cv_syscall" = "xyes" ; then
+ AC_DEFINE([JEMALLOC_USE_SYSCALL], [ ], [ ])
+ fi
+fi
+
+dnl Check if the GNU-specific secure_getenv function exists.
+AC_CHECK_FUNC([secure_getenv],
+ [have_secure_getenv="1"],
+ [have_secure_getenv="0"]
+ )
+if test "x$have_secure_getenv" = "x1" ; then
+ AC_DEFINE([JEMALLOC_HAVE_SECURE_GETENV], [ ], [ ])
+fi
+
+dnl Check if the GNU-specific sched_getcpu function exists.
+AC_CHECK_FUNC([sched_getcpu],
+ [have_sched_getcpu="1"],
+ [have_sched_getcpu="0"]
+ )
+if test "x$have_sched_getcpu" = "x1" ; then
+ AC_DEFINE([JEMALLOC_HAVE_SCHED_GETCPU], [ ], [ ])
+fi
+
+dnl Check if the GNU-specific sched_setaffinity function exists.
+AC_CHECK_FUNC([sched_setaffinity],
+ [have_sched_setaffinity="1"],
+ [have_sched_setaffinity="0"]
+ )
+if test "x$have_sched_setaffinity" = "x1" ; then
+ AC_DEFINE([JEMALLOC_HAVE_SCHED_SETAFFINITY], [ ], [ ])
+fi
+
+dnl Check if the Solaris/BSD issetugid function exists.
+AC_CHECK_FUNC([issetugid],
+ [have_issetugid="1"],
+ [have_issetugid="0"]
+ )
+if test "x$have_issetugid" = "x1" ; then
+ AC_DEFINE([JEMALLOC_HAVE_ISSETUGID], [ ], [ ])
+fi
+
+dnl Check whether the BSD-specific _malloc_thread_cleanup() exists. If so, use
+dnl it rather than pthreads TSD cleanup functions to support cleanup during
+dnl thread exit, in order to avoid pthreads library recursion during
+dnl bootstrapping.
+AC_CHECK_FUNC([_malloc_thread_cleanup],
+ [have__malloc_thread_cleanup="1"],
+ [have__malloc_thread_cleanup="0"]
+ )
+if test "x$have__malloc_thread_cleanup" = "x1" ; then
+ AC_DEFINE([JEMALLOC_MALLOC_THREAD_CLEANUP], [ ], [ ])
+ wrap_syms="${wrap_syms} _malloc_thread_cleanup _malloc_tsd_cleanup_register"
+ force_tls="1"
+fi
+
+dnl Check whether the BSD-specific _pthread_mutex_init_calloc_cb() exists. If
+dnl so, mutex initialization causes allocation, and we need to implement this
+dnl callback function in order to prevent recursive allocation.
+AC_CHECK_FUNC([_pthread_mutex_init_calloc_cb],
+ [have__pthread_mutex_init_calloc_cb="1"],
+ [have__pthread_mutex_init_calloc_cb="0"]
+ )
+if test "x$have__pthread_mutex_init_calloc_cb" = "x1" ; then
+ AC_DEFINE([JEMALLOC_MUTEX_INIT_CB], [ ], [ ])
+ wrap_syms="${wrap_syms} _malloc_prefork _malloc_postfork"
+fi
+
+AC_CHECK_FUNC([memcntl],
+ [have_memcntl="1"],
+ [have_memcntl="0"],
+ )
+if test "x$have_memcntl" = "x1" ; then
+ AC_DEFINE([JEMALLOC_HAVE_MEMCNTL], [ ], [ ])
+fi
+
+dnl Disable lazy locking by default.
+AC_ARG_ENABLE([lazy_lock],
+ [AS_HELP_STRING([--enable-lazy-lock],
+ [Enable lazy locking (only lock when multi-threaded)])],
+[if test "x$enable_lazy_lock" = "xno" ; then
+ enable_lazy_lock="0"
+else
+ enable_lazy_lock="1"
+fi
+],
+[enable_lazy_lock=""]
+)
+if test "x${enable_lazy_lock}" = "x" ; then
+ if test "x${force_lazy_lock}" = "x1" ; then
+ AC_MSG_RESULT([Forcing lazy-lock to avoid allocator/threading bootstrap issues])
+ enable_lazy_lock="1"
+ else
+ enable_lazy_lock="0"
+ fi
+fi
+if test "x${enable_lazy_lock}" = "x1" -a "x${abi}" = "xpecoff" ; then
+ AC_MSG_RESULT([Forcing no lazy-lock because thread creation monitoring is unimplemented])
+ enable_lazy_lock="0"
+fi
+if test "x$enable_lazy_lock" = "x1" ; then
+ if test "x$have_dlsym" = "x1" ; then
+ AC_DEFINE([JEMALLOC_LAZY_LOCK], [ ], [ ])
+ else
+ AC_MSG_ERROR([Missing dlsym support: lazy-lock cannot be enabled.])
+ fi
+fi
+AC_SUBST([enable_lazy_lock])
+
+dnl Automatically configure TLS.
+if test "x${force_tls}" = "x1" ; then
+ enable_tls="1"
+elif test "x${force_tls}" = "x0" ; then
+ enable_tls="0"
+else
+ enable_tls="1"
+fi
+if test "x${enable_tls}" = "x1" ; then
+AC_MSG_CHECKING([for TLS])
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM(
+[[
+ __thread int x;
+]], [[
+ x = 42;
+
+ return 0;
+]])],
+ AC_MSG_RESULT([yes]),
+ AC_MSG_RESULT([no])
+ enable_tls="0")
+else
+ enable_tls="0"
+fi
+AC_SUBST([enable_tls])
+if test "x${enable_tls}" = "x1" ; then
+ AC_DEFINE_UNQUOTED([JEMALLOC_TLS], [ ], [ ])
+fi
+
+dnl ============================================================================
+dnl Check for C11 atomics.
+
+JE_COMPILABLE([C11 atomics], [
+#include <stdint.h>
+#if (__STDC_VERSION__ >= 201112L) && !defined(__STDC_NO_ATOMICS__)
+#include <stdatomic.h>
+#else
+#error Atomics not available
+#endif
+], [
+ uint64_t *p = (uint64_t *)0;
+ uint64_t x = 1;
+ volatile atomic_uint_least64_t *a = (volatile atomic_uint_least64_t *)p;
+ uint64_t r = atomic_fetch_add(a, x) + x;
+ return r == 0;
+], [je_cv_c11_atomics])
+if test "x${je_cv_c11_atomics}" = "xyes" ; then
+ AC_DEFINE([JEMALLOC_C11_ATOMICS], [ ], [ ])
+fi
+
+dnl ============================================================================
+dnl Check for GCC-style __atomic atomics.
+
+JE_COMPILABLE([GCC __atomic atomics], [
+], [
+ int x = 0;
+ int val = 1;
+ int y = __atomic_fetch_add(&x, val, __ATOMIC_RELAXED);
+ int after_add = x;
+ return after_add == 1;
+], [je_cv_gcc_atomic_atomics])
+if test "x${je_cv_gcc_atomic_atomics}" = "xyes" ; then
+ AC_DEFINE([JEMALLOC_GCC_ATOMIC_ATOMICS], [ ], [ ])
+
+ dnl check for 8-bit atomic support
+ JE_COMPILABLE([GCC 8-bit __atomic atomics], [
+ ], [
+ unsigned char x = 0;
+ int val = 1;
+ int y = __atomic_fetch_add(&x, val, __ATOMIC_RELAXED);
+ int after_add = (int)x;
+ return after_add == 1;
+ ], [je_cv_gcc_u8_atomic_atomics])
+ if test "x${je_cv_gcc_u8_atomic_atomics}" = "xyes" ; then
+ AC_DEFINE([JEMALLOC_GCC_U8_ATOMIC_ATOMICS], [ ], [ ])
+ fi
+fi
+
+dnl ============================================================================
+dnl Check for GCC-style __sync atomics.
+
+JE_COMPILABLE([GCC __sync atomics], [
+], [
+ int x = 0;
+ int before_add = __sync_fetch_and_add(&x, 1);
+ int after_add = x;
+ return (before_add == 0) && (after_add == 1);
+], [je_cv_gcc_sync_atomics])
+if test "x${je_cv_gcc_sync_atomics}" = "xyes" ; then
+ AC_DEFINE([JEMALLOC_GCC_SYNC_ATOMICS], [ ], [ ])
+
+ dnl check for 8-bit atomic support
+ JE_COMPILABLE([GCC 8-bit __sync atomics], [
+ ], [
+ unsigned char x = 0;
+ int before_add = __sync_fetch_and_add(&x, 1);
+ int after_add = (int)x;
+ return (before_add == 0) && (after_add == 1);
+ ], [je_cv_gcc_u8_sync_atomics])
+ if test "x${je_cv_gcc_u8_sync_atomics}" = "xyes" ; then
+ AC_DEFINE([JEMALLOC_GCC_U8_SYNC_ATOMICS], [ ], [ ])
+ fi
+fi
+
+dnl ============================================================================
+dnl Check for atomic(3) operations as provided on Darwin.
+dnl We need this not for the atomic operations (which are provided above), but
+dnl rather for the OS_unfair_lock type it exposes.
+
+JE_COMPILABLE([Darwin OSAtomic*()], [
+#include <libkern/OSAtomic.h>
+#include <inttypes.h>
+], [
+ {
+ int32_t x32 = 0;
+ volatile int32_t *x32p = &x32;
+ OSAtomicAdd32(1, x32p);
+ }
+ {
+ int64_t x64 = 0;
+ volatile int64_t *x64p = &x64;
+ OSAtomicAdd64(1, x64p);
+ }
+], [je_cv_osatomic])
+if test "x${je_cv_osatomic}" = "xyes" ; then
+ AC_DEFINE([JEMALLOC_OSATOMIC], [ ], [ ])
+fi
+
+dnl ============================================================================
+dnl Check for madvise(2).
+
+JE_COMPILABLE([madvise(2)], [
+#include <sys/mman.h>
+], [
+ madvise((void *)0, 0, 0);
+], [je_cv_madvise])
+if test "x${je_cv_madvise}" = "xyes" ; then
+ AC_DEFINE([JEMALLOC_HAVE_MADVISE], [ ], [ ])
+
+ dnl Check for madvise(..., MADV_FREE).
+ JE_COMPILABLE([madvise(..., MADV_FREE)], [
+#include <sys/mman.h>
+], [
+ madvise((void *)0, 0, MADV_FREE);
+], [je_cv_madv_free])
+ if test "x${je_cv_madv_free}" = "xyes" ; then
+ AC_DEFINE([JEMALLOC_PURGE_MADVISE_FREE], [ ], [ ])
+ elif test "x${je_cv_madvise}" = "xyes" ; then
+ case "${host_cpu}" in i686|x86_64)
+ case "${host}" in *-*-linux*)
+ AC_DEFINE([JEMALLOC_PURGE_MADVISE_FREE], [ ], [ ])
+ AC_DEFINE([JEMALLOC_DEFINE_MADVISE_FREE], [ ], [ ])
+ ;;
+ esac
+ ;;
+ esac
+ fi
+
+ dnl Check for madvise(..., MADV_DONTNEED).
+ JE_COMPILABLE([madvise(..., MADV_DONTNEED)], [
+#include <sys/mman.h>
+], [
+ madvise((void *)0, 0, MADV_DONTNEED);
+], [je_cv_madv_dontneed])
+ if test "x${je_cv_madv_dontneed}" = "xyes" ; then
+ AC_DEFINE([JEMALLOC_PURGE_MADVISE_DONTNEED], [ ], [ ])
+ fi
+
+ dnl Check for madvise(..., MADV_DO[NT]DUMP).
+ JE_COMPILABLE([madvise(..., MADV_DO[[NT]]DUMP)], [
+#include <sys/mman.h>
+], [
+ madvise((void *)0, 0, MADV_DONTDUMP);
+ madvise((void *)0, 0, MADV_DODUMP);
+], [je_cv_madv_dontdump])
+ if test "x${je_cv_madv_dontdump}" = "xyes" ; then
+ AC_DEFINE([JEMALLOC_MADVISE_DONTDUMP], [ ], [ ])
+ fi
+
+ dnl Check for madvise(..., MADV_[NO]HUGEPAGE).
+ JE_COMPILABLE([madvise(..., MADV_[[NO]]HUGEPAGE)], [
+#include <sys/mman.h>
+], [
+ madvise((void *)0, 0, MADV_HUGEPAGE);
+ madvise((void *)0, 0, MADV_NOHUGEPAGE);
+], [je_cv_thp])
+ dnl Check for madvise(..., MADV_[NO]CORE).
+ JE_COMPILABLE([madvise(..., MADV_[[NO]]CORE)], [
+#include <sys/mman.h>
+], [
+ madvise((void *)0, 0, MADV_NOCORE);
+ madvise((void *)0, 0, MADV_CORE);
+], [je_cv_madv_nocore])
+ if test "x${je_cv_madv_nocore}" = "xyes" ; then
+ AC_DEFINE([JEMALLOC_MADVISE_NOCORE], [ ], [ ])
+ fi
+case "${host_cpu}" in
+ arm*)
+ ;;
+ *)
+ if test "x${je_cv_thp}" = "xyes" ; then
+ AC_DEFINE([JEMALLOC_HAVE_MADVISE_HUGE], [ ], [ ])
+ fi
+ ;;
+esac
+else
+ dnl Check for posix_madvise.
+ JE_COMPILABLE([posix_madvise], [
+ #include <sys/mman.h>
+ ], [
+ posix_madvise((void *)0, 0, 0);
+ ], [je_cv_posix_madvise])
+ if test "x${je_cv_posix_madvise}" = "xyes" ; then
+ AC_DEFINE([JEMALLOC_HAVE_POSIX_MADVISE], [ ], [ ])
+
+ dnl Check for posix_madvise(..., POSIX_MADV_DONTNEED).
+ JE_COMPILABLE([posix_madvise(..., POSIX_MADV_DONTNEED)], [
+ #include <sys/mman.h>
+ ], [
+ posix_madvise((void *)0, 0, POSIX_MADV_DONTNEED);
+ ], [je_cv_posix_madv_dontneed])
+ if test "x${je_cv_posix_madv_dontneed}" = "xyes" ; then
+ AC_DEFINE([JEMALLOC_PURGE_POSIX_MADVISE_DONTNEED], [ ], [ ])
+ fi
+ fi
+fi
+
+dnl ============================================================================
+dnl Check for mprotect(2).
+
+JE_COMPILABLE([mprotect(2)], [
+#include <sys/mman.h>
+], [
+ mprotect((void *)0, 0, PROT_NONE);
+], [je_cv_mprotect])
+if test "x${je_cv_mprotect}" = "xyes" ; then
+ AC_DEFINE([JEMALLOC_HAVE_MPROTECT], [ ], [ ])
+fi
+
+dnl ============================================================================
+dnl Check for __builtin_clz(), __builtin_clzl(), and __builtin_clzll().
+
+AC_CACHE_CHECK([for __builtin_clz],
+ [je_cv_builtin_clz],
+ [AC_LINK_IFELSE([AC_LANG_PROGRAM([],
+ [
+ {
+ unsigned x = 0;
+ int y = __builtin_clz(x);
+ }
+ {
+ unsigned long x = 0;
+ int y = __builtin_clzl(x);
+ }
+ {
+ unsigned long long x = 0;
+ int y = __builtin_clzll(x);
+ }
+ ])],
+ [je_cv_builtin_clz=yes],
+ [je_cv_builtin_clz=no])])
+
+if test "x${je_cv_builtin_clz}" = "xyes" ; then
+ AC_DEFINE([JEMALLOC_HAVE_BUILTIN_CLZ], [ ], [ ])
+fi
+
+dnl ============================================================================
+dnl Check for os_unfair_lock operations as provided on Darwin.
+
+JE_COMPILABLE([Darwin os_unfair_lock_*()], [
+#include <os/lock.h>
+#include <AvailabilityMacros.h>
+], [
+ #if MAC_OS_X_VERSION_MIN_REQUIRED < 101200
+ #error "os_unfair_lock is not supported"
+ #else
+ os_unfair_lock lock = OS_UNFAIR_LOCK_INIT;
+ os_unfair_lock_lock(&lock);
+ os_unfair_lock_unlock(&lock);
+ #endif
+], [je_cv_os_unfair_lock])
+if test "x${je_cv_os_unfair_lock}" = "xyes" ; then
+ AC_DEFINE([JEMALLOC_OS_UNFAIR_LOCK], [ ], [ ])
+fi
+
+dnl ============================================================================
+dnl Darwin-related configuration.
+
+AC_ARG_ENABLE([zone-allocator],
+ [AS_HELP_STRING([--disable-zone-allocator],
+ [Disable zone allocator for Darwin])],
+[if test "x$enable_zone_allocator" = "xno" ; then
+ enable_zone_allocator="0"
+else
+ enable_zone_allocator="1"
+fi
+],
+[if test "x${abi}" = "xmacho"; then
+ enable_zone_allocator="1"
+fi
+]
+)
+AC_SUBST([enable_zone_allocator])
+
+if test "x${enable_zone_allocator}" = "x1" ; then
+ if test "x${abi}" != "xmacho"; then
+ AC_MSG_ERROR([--enable-zone-allocator is only supported on Darwin])
+ fi
+ AC_DEFINE([JEMALLOC_ZONE], [ ], [ ])
+fi
+
+dnl ============================================================================
+dnl Use initial-exec TLS by default.
+AC_ARG_ENABLE([initial-exec-tls],
+ [AS_HELP_STRING([--disable-initial-exec-tls],
+ [Disable the initial-exec tls model])],
+[if test "x$enable_initial_exec_tls" = "xno" ; then
+ enable_initial_exec_tls="0"
+else
+ enable_initial_exec_tls="1"
+fi
+],
+[enable_initial_exec_tls="1"]
+)
+AC_SUBST([enable_initial_exec_tls])
+
+if test "x${je_cv_tls_model}" = "xyes" -a \
+ "x${enable_initial_exec_tls}" = "x1" ; then
+ AC_DEFINE([JEMALLOC_TLS_MODEL],
+ [__attribute__((tls_model("initial-exec")))],
+ [ ])
+else
+ AC_DEFINE([JEMALLOC_TLS_MODEL], [ ], [ ])
+fi
+
+dnl ============================================================================
+dnl Enable background threads if possible.
+
+if test "x${have_pthread}" = "x1" -a "x${je_cv_os_unfair_lock}" != "xyes" -a \
+ "x${abi}" != "xmacho" ; then
+ AC_DEFINE([JEMALLOC_BACKGROUND_THREAD], [ ], [ ])
+fi
+
+dnl ============================================================================
+dnl Check for glibc malloc hooks
+
+if test "x$glibc" = "x1" ; then
+ JE_COMPILABLE([glibc malloc hook], [
+ #include <stddef.h>
+
+ extern void (* __free_hook)(void *ptr);
+ extern void *(* __malloc_hook)(size_t size);
+ extern void *(* __realloc_hook)(void *ptr, size_t size);
+], [
+ void *ptr = 0L;
+ if (__malloc_hook) ptr = __malloc_hook(1);
+ if (__realloc_hook) ptr = __realloc_hook(ptr, 2);
+ if (__free_hook && ptr) __free_hook(ptr);
+], [je_cv_glibc_malloc_hook])
+ if test "x${je_cv_glibc_malloc_hook}" = "xyes" ; then
+ if test "x${JEMALLOC_PREFIX}" = "x" ; then
+ AC_DEFINE([JEMALLOC_GLIBC_MALLOC_HOOK], [ ], [ ])
+ wrap_syms="${wrap_syms} __free_hook __malloc_hook __realloc_hook"
+ fi
+ fi
+
+ JE_COMPILABLE([glibc memalign hook], [
+ #include <stddef.h>
+
+ extern void *(* __memalign_hook)(size_t alignment, size_t size);
+], [
+ void *ptr = 0L;
+ if (__memalign_hook) ptr = __memalign_hook(16, 7);
+], [je_cv_glibc_memalign_hook])
+ if test "x${je_cv_glibc_memalign_hook}" = "xyes" ; then
+ if test "x${JEMALLOC_PREFIX}" = "x" ; then
+ AC_DEFINE([JEMALLOC_GLIBC_MEMALIGN_HOOK], [ ], [ ])
+ wrap_syms="${wrap_syms} __memalign_hook"
+ fi
+ fi
+fi
+
+JE_COMPILABLE([pthreads adaptive mutexes], [
+#include <pthread.h>
+], [
+ pthread_mutexattr_t attr;
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP);
+ pthread_mutexattr_destroy(&attr);
+], [je_cv_pthread_mutex_adaptive_np])
+if test "x${je_cv_pthread_mutex_adaptive_np}" = "xyes" ; then
+ AC_DEFINE([JEMALLOC_HAVE_PTHREAD_MUTEX_ADAPTIVE_NP], [ ], [ ])
+fi
+
+JE_CFLAGS_SAVE()
+JE_CFLAGS_ADD([-D_GNU_SOURCE])
+JE_CFLAGS_ADD([-Werror])
+JE_CFLAGS_ADD([-herror_on_warning])
+JE_COMPILABLE([strerror_r returns char with gnu source], [
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+], [
+ char *buffer = (char *) malloc(100);
+ char *error = strerror_r(EINVAL, buffer, 100);
+ printf("%s\n", error);
+], [je_cv_strerror_r_returns_char_with_gnu_source])
+JE_CFLAGS_RESTORE()
+if test "x${je_cv_strerror_r_returns_char_with_gnu_source}" = "xyes" ; then
+ AC_DEFINE([JEMALLOC_STRERROR_R_RETURNS_CHAR_WITH_GNU_SOURCE], [ ], [ ])
+fi
+
+dnl ============================================================================
+dnl Check for typedefs, structures, and compiler characteristics.
+AC_HEADER_STDBOOL
+
+dnl ============================================================================
+dnl Define commands that generate output files.
+
+AC_CONFIG_COMMANDS([include/jemalloc/internal/public_symbols.txt], [
+ f="${objroot}include/jemalloc/internal/public_symbols.txt"
+ mkdir -p "${objroot}include/jemalloc/internal"
+ cp /dev/null "${f}"
+ for nm in `echo ${mangling_map} |tr ',' ' '` ; do
+ n=`echo ${nm} |tr ':' ' ' |awk '{print $[]1}'`
+ m=`echo ${nm} |tr ':' ' ' |awk '{print $[]2}'`
+ echo "${n}:${m}" >> "${f}"
+ dnl Remove name from public_syms so that it isn't redefined later.
+ public_syms=`for sym in ${public_syms}; do echo "${sym}"; done |grep -v "^${n}\$" |tr '\n' ' '`
+ done
+ for sym in ${public_syms} ; do
+ n="${sym}"
+ m="${JEMALLOC_PREFIX}${sym}"
+ echo "${n}:${m}" >> "${f}"
+ done
+], [
+ srcdir="${srcdir}"
+ objroot="${objroot}"
+ mangling_map="${mangling_map}"
+ public_syms="${public_syms}"
+ JEMALLOC_PREFIX="${JEMALLOC_PREFIX}"
+])
+AC_CONFIG_COMMANDS([include/jemalloc/internal/private_symbols.awk], [
+ f="${objroot}include/jemalloc/internal/private_symbols.awk"
+ mkdir -p "${objroot}include/jemalloc/internal"
+ export_syms=`for sym in ${public_syms}; do echo "${JEMALLOC_PREFIX}${sym}"; done; for sym in ${wrap_syms}; do echo "${sym}"; done;`
+ "${srcdir}/include/jemalloc/internal/private_symbols.sh" "${SYM_PREFIX}" ${export_syms} > "${objroot}include/jemalloc/internal/private_symbols.awk"
+], [
+ srcdir="${srcdir}"
+ objroot="${objroot}"
+ public_syms="${public_syms}"
+ wrap_syms="${wrap_syms}"
+ SYM_PREFIX="${SYM_PREFIX}"
+ JEMALLOC_PREFIX="${JEMALLOC_PREFIX}"
+])
+AC_CONFIG_COMMANDS([include/jemalloc/internal/private_symbols_jet.awk], [
+ f="${objroot}include/jemalloc/internal/private_symbols_jet.awk"
+ mkdir -p "${objroot}include/jemalloc/internal"
+ export_syms=`for sym in ${public_syms}; do echo "jet_${sym}"; done; for sym in ${wrap_syms}; do echo "${sym}"; done;`
+ "${srcdir}/include/jemalloc/internal/private_symbols.sh" "${SYM_PREFIX}" ${export_syms} > "${objroot}include/jemalloc/internal/private_symbols_jet.awk"
+], [
+ srcdir="${srcdir}"
+ objroot="${objroot}"
+ public_syms="${public_syms}"
+ wrap_syms="${wrap_syms}"
+ SYM_PREFIX="${SYM_PREFIX}"
+])
+AC_CONFIG_COMMANDS([include/jemalloc/internal/public_namespace.h], [
+ mkdir -p "${objroot}include/jemalloc/internal"
+ "${srcdir}/include/jemalloc/internal/public_namespace.sh" "${objroot}include/jemalloc/internal/public_symbols.txt" > "${objroot}include/jemalloc/internal/public_namespace.h"
+], [
+ srcdir="${srcdir}"
+ objroot="${objroot}"
+])
+AC_CONFIG_COMMANDS([include/jemalloc/internal/public_unnamespace.h], [
+ mkdir -p "${objroot}include/jemalloc/internal"
+ "${srcdir}/include/jemalloc/internal/public_unnamespace.sh" "${objroot}include/jemalloc/internal/public_symbols.txt" > "${objroot}include/jemalloc/internal/public_unnamespace.h"
+], [
+ srcdir="${srcdir}"
+ objroot="${objroot}"
+])
+AC_CONFIG_COMMANDS([include/jemalloc/jemalloc_protos_jet.h], [
+ mkdir -p "${objroot}include/jemalloc"
+ cat "${srcdir}/include/jemalloc/jemalloc_protos.h.in" | sed -e 's/@je_@/jet_/g' > "${objroot}include/jemalloc/jemalloc_protos_jet.h"
+], [
+ srcdir="${srcdir}"
+ objroot="${objroot}"
+])
+AC_CONFIG_COMMANDS([include/jemalloc/jemalloc_rename.h], [
+ mkdir -p "${objroot}include/jemalloc"
+ "${srcdir}/include/jemalloc/jemalloc_rename.sh" "${objroot}include/jemalloc/internal/public_symbols.txt" > "${objroot}include/jemalloc/jemalloc_rename.h"
+], [
+ srcdir="${srcdir}"
+ objroot="${objroot}"
+])
+AC_CONFIG_COMMANDS([include/jemalloc/jemalloc_mangle.h], [
+ mkdir -p "${objroot}include/jemalloc"
+ "${srcdir}/include/jemalloc/jemalloc_mangle.sh" "${objroot}include/jemalloc/internal/public_symbols.txt" je_ > "${objroot}include/jemalloc/jemalloc_mangle.h"
+], [
+ srcdir="${srcdir}"
+ objroot="${objroot}"
+])
+AC_CONFIG_COMMANDS([include/jemalloc/jemalloc_mangle_jet.h], [
+ mkdir -p "${objroot}include/jemalloc"
+ "${srcdir}/include/jemalloc/jemalloc_mangle.sh" "${objroot}include/jemalloc/internal/public_symbols.txt" jet_ > "${objroot}include/jemalloc/jemalloc_mangle_jet.h"
+], [
+ srcdir="${srcdir}"
+ objroot="${objroot}"
+])
+AC_CONFIG_COMMANDS([include/jemalloc/jemalloc.h], [
+ mkdir -p "${objroot}include/jemalloc"
+ "${srcdir}/include/jemalloc/jemalloc.sh" "${objroot}" > "${objroot}include/jemalloc/jemalloc${install_suffix}.h"
+], [
+ srcdir="${srcdir}"
+ objroot="${objroot}"
+ install_suffix="${install_suffix}"
+])
+
+dnl Process .in files.
+AC_SUBST([cfghdrs_in])
+AC_SUBST([cfghdrs_out])
+AC_CONFIG_HEADERS([$cfghdrs_tup])
+
+dnl ============================================================================
+dnl Generate outputs.
+
+AC_CONFIG_FILES([$cfgoutputs_tup config.stamp bin/jemalloc-config bin/jemalloc.sh bin/jeprof])
+AC_SUBST([cfgoutputs_in])
+AC_SUBST([cfgoutputs_out])
+AC_OUTPUT
+
+dnl ============================================================================
+dnl Print out the results of configuration.
+AC_MSG_RESULT([===============================================================================])
+AC_MSG_RESULT([jemalloc version : ${jemalloc_version}])
+AC_MSG_RESULT([library revision : ${rev}])
+AC_MSG_RESULT([])
+AC_MSG_RESULT([CONFIG : ${CONFIG}])
+AC_MSG_RESULT([CC : ${CC}])
+AC_MSG_RESULT([CONFIGURE_CFLAGS : ${CONFIGURE_CFLAGS}])
+AC_MSG_RESULT([SPECIFIED_CFLAGS : ${SPECIFIED_CFLAGS}])
+AC_MSG_RESULT([EXTRA_CFLAGS : ${EXTRA_CFLAGS}])
+AC_MSG_RESULT([CPPFLAGS : ${CPPFLAGS}])
+AC_MSG_RESULT([CXX : ${CXX}])
+AC_MSG_RESULT([CONFIGURE_CXXFLAGS : ${CONFIGURE_CXXFLAGS}])
+AC_MSG_RESULT([SPECIFIED_CXXFLAGS : ${SPECIFIED_CXXFLAGS}])
+AC_MSG_RESULT([EXTRA_CXXFLAGS : ${EXTRA_CXXFLAGS}])
+AC_MSG_RESULT([LDFLAGS : ${LDFLAGS}])
+AC_MSG_RESULT([EXTRA_LDFLAGS : ${EXTRA_LDFLAGS}])
+AC_MSG_RESULT([DSO_LDFLAGS : ${DSO_LDFLAGS}])
+AC_MSG_RESULT([LIBS : ${LIBS}])
+AC_MSG_RESULT([RPATH_EXTRA : ${RPATH_EXTRA}])
+AC_MSG_RESULT([])
+AC_MSG_RESULT([XSLTPROC : ${XSLTPROC}])
+AC_MSG_RESULT([XSLROOT : ${XSLROOT}])
+AC_MSG_RESULT([])
+AC_MSG_RESULT([PREFIX : ${PREFIX}])
+AC_MSG_RESULT([BINDIR : ${BINDIR}])
+AC_MSG_RESULT([DATADIR : ${DATADIR}])
+AC_MSG_RESULT([INCLUDEDIR : ${INCLUDEDIR}])
+AC_MSG_RESULT([LIBDIR : ${LIBDIR}])
+AC_MSG_RESULT([MANDIR : ${MANDIR}])
+AC_MSG_RESULT([])
+AC_MSG_RESULT([srcroot : ${srcroot}])
+AC_MSG_RESULT([abs_srcroot : ${abs_srcroot}])
+AC_MSG_RESULT([objroot : ${objroot}])
+AC_MSG_RESULT([abs_objroot : ${abs_objroot}])
+AC_MSG_RESULT([])
+AC_MSG_RESULT([JEMALLOC_PREFIX : ${JEMALLOC_PREFIX}])
+AC_MSG_RESULT([JEMALLOC_PRIVATE_NAMESPACE])
+AC_MSG_RESULT([ : ${JEMALLOC_PRIVATE_NAMESPACE}])
+AC_MSG_RESULT([install_suffix : ${install_suffix}])
+AC_MSG_RESULT([malloc_conf : ${config_malloc_conf}])
+AC_MSG_RESULT([documentation : ${enable_doc}])
+AC_MSG_RESULT([shared libs : ${enable_shared}])
+AC_MSG_RESULT([static libs : ${enable_static}])
+AC_MSG_RESULT([autogen : ${enable_autogen}])
+AC_MSG_RESULT([debug : ${enable_debug}])
+AC_MSG_RESULT([stats : ${enable_stats}])
+AC_MSG_RESULT([experimental_smallocx : ${enable_experimental_smallocx}])
+AC_MSG_RESULT([prof : ${enable_prof}])
+AC_MSG_RESULT([prof-libunwind : ${enable_prof_libunwind}])
+AC_MSG_RESULT([prof-libgcc : ${enable_prof_libgcc}])
+AC_MSG_RESULT([prof-gcc : ${enable_prof_gcc}])
+AC_MSG_RESULT([fill : ${enable_fill}])
+AC_MSG_RESULT([utrace : ${enable_utrace}])
+AC_MSG_RESULT([xmalloc : ${enable_xmalloc}])
+AC_MSG_RESULT([log : ${enable_log}])
+AC_MSG_RESULT([lazy_lock : ${enable_lazy_lock}])
+AC_MSG_RESULT([cache-oblivious : ${enable_cache_oblivious}])
+AC_MSG_RESULT([cxx : ${enable_cxx}])
+AC_MSG_RESULT([===============================================================================])
diff --git a/contrib/jemalloc/doc/html.xsl.in b/contrib/jemalloc/doc/html.xsl.in
new file mode 100644
index 000000000000..ec4fa6552bee
--- /dev/null
+++ b/contrib/jemalloc/doc/html.xsl.in
@@ -0,0 +1,5 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+ <xsl:import href="@XSLROOT@/html/docbook.xsl"/>
+ <xsl:import href="@abs_srcroot@doc/stylesheet.xsl"/>
+ <xsl:output method="xml" encoding="utf-8"/>
+</xsl:stylesheet>
diff --git a/contrib/jemalloc/doc/jemalloc.xml.in b/contrib/jemalloc/doc/jemalloc.xml.in
new file mode 100644
index 000000000000..4f5d27996816
--- /dev/null
+++ b/contrib/jemalloc/doc/jemalloc.xml.in
@@ -0,0 +1,3788 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<?xml-stylesheet type="text/xsl"
+ href="http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl"?>
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
+ "http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
+]>
+
+<refentry>
+ <refentryinfo>
+ <title>User Manual</title>
+ <productname>jemalloc</productname>
+ <releaseinfo role="version">@jemalloc_version@</releaseinfo>
+ <authorgroup>
+ <author>
+ <firstname>Jason</firstname>
+ <surname>Evans</surname>
+ <personblurb>Author</personblurb>
+ </author>
+ </authorgroup>
+ </refentryinfo>
+ <refmeta>
+ <refentrytitle>JEMALLOC</refentrytitle>
+ <manvolnum>3</manvolnum>
+ </refmeta>
+ <refnamediv>
+ <refdescriptor>jemalloc</refdescriptor>
+ <refname>jemalloc</refname>
+ <!-- Each refname causes a man page file to be created. Only if this were
+ the system malloc(3) implementation would these files be appropriate.
+ <refname>malloc</refname>
+ <refname>calloc</refname>
+ <refname>posix_memalign</refname>
+ <refname>aligned_alloc</refname>
+ <refname>realloc</refname>
+ <refname>free</refname>
+ <refname>mallocx</refname>
+ <refname>rallocx</refname>
+ <refname>xallocx</refname>
+ <refname>sallocx</refname>
+ <refname>dallocx</refname>
+ <refname>sdallocx</refname>
+ <refname>nallocx</refname>
+ <refname>mallctl</refname>
+ <refname>mallctlnametomib</refname>
+ <refname>mallctlbymib</refname>
+ <refname>malloc_stats_print</refname>
+ <refname>malloc_usable_size</refname>
+ -->
+ <refpurpose>general purpose memory allocation functions</refpurpose>
+ </refnamediv>
+ <refsect1 id="library">
+ <title>LIBRARY</title>
+ <para>This manual describes jemalloc @jemalloc_version@. More information
+ can be found at the <ulink
+ url="http://jemalloc.net/">jemalloc website</ulink>.</para>
+
+ <para>The following configuration options are enabled in libc's built-in
+ jemalloc: <option>--enable-fill</option>,
+ <option>--enable-lazy-lock</option>, <option>--enable-stats</option>,
+ <option>--enable-utrace</option>, <option>--enable-xmalloc</option>, and
+ <option>--with-malloc-conf=abort_conf:false</option>.
+ Additionally, <option>--enable-debug</option> is enabled in development
+ versions of FreeBSD (controlled by the
+ <constant>MK_MALLOC_PRODUCTION</constant> make variable).</para>
+
+ </refsect1>
+ <refsynopsisdiv>
+ <title>SYNOPSIS</title>
+ <funcsynopsis>
+ <funcsynopsisinfo>#include &lt;<filename class="headerfile">stdlib.h</filename>&gt;
+#include &lt;<filename class="headerfile">malloc_np.h</filename>&gt;</funcsynopsisinfo>
+ <refsect2>
+ <title>Standard API</title>
+ <funcprototype>
+ <funcdef>void *<function>malloc</function></funcdef>
+ <paramdef>size_t <parameter>size</parameter></paramdef>
+ </funcprototype>
+ <funcprototype>
+ <funcdef>void *<function>calloc</function></funcdef>
+ <paramdef>size_t <parameter>number</parameter></paramdef>
+ <paramdef>size_t <parameter>size</parameter></paramdef>
+ </funcprototype>
+ <funcprototype>
+ <funcdef>int <function>posix_memalign</function></funcdef>
+ <paramdef>void **<parameter>ptr</parameter></paramdef>
+ <paramdef>size_t <parameter>alignment</parameter></paramdef>
+ <paramdef>size_t <parameter>size</parameter></paramdef>
+ </funcprototype>
+ <funcprototype>
+ <funcdef>void *<function>aligned_alloc</function></funcdef>
+ <paramdef>size_t <parameter>alignment</parameter></paramdef>
+ <paramdef>size_t <parameter>size</parameter></paramdef>
+ </funcprototype>
+ <funcprototype>
+ <funcdef>void *<function>realloc</function></funcdef>
+ <paramdef>void *<parameter>ptr</parameter></paramdef>
+ <paramdef>size_t <parameter>size</parameter></paramdef>
+ </funcprototype>
+ <funcprototype>
+ <funcdef>void <function>free</function></funcdef>
+ <paramdef>void *<parameter>ptr</parameter></paramdef>
+ </funcprototype>
+ </refsect2>
+ <refsect2>
+ <title>Non-standard API</title>
+ <funcprototype>
+ <funcdef>void *<function>mallocx</function></funcdef>
+ <paramdef>size_t <parameter>size</parameter></paramdef>
+ <paramdef>int <parameter>flags</parameter></paramdef>
+ </funcprototype>
+ <funcprototype>
+ <funcdef>void *<function>rallocx</function></funcdef>
+ <paramdef>void *<parameter>ptr</parameter></paramdef>
+ <paramdef>size_t <parameter>size</parameter></paramdef>
+ <paramdef>int <parameter>flags</parameter></paramdef>
+ </funcprototype>
+ <funcprototype>
+ <funcdef>size_t <function>xallocx</function></funcdef>
+ <paramdef>void *<parameter>ptr</parameter></paramdef>
+ <paramdef>size_t <parameter>size</parameter></paramdef>
+ <paramdef>size_t <parameter>extra</parameter></paramdef>
+ <paramdef>int <parameter>flags</parameter></paramdef>
+ </funcprototype>
+ <funcprototype>
+ <funcdef>size_t <function>sallocx</function></funcdef>
+ <paramdef>void *<parameter>ptr</parameter></paramdef>
+ <paramdef>int <parameter>flags</parameter></paramdef>
+ </funcprototype>
+ <funcprototype>
+ <funcdef>void <function>dallocx</function></funcdef>
+ <paramdef>void *<parameter>ptr</parameter></paramdef>
+ <paramdef>int <parameter>flags</parameter></paramdef>
+ </funcprototype>
+ <funcprototype>
+ <funcdef>void <function>sdallocx</function></funcdef>
+ <paramdef>void *<parameter>ptr</parameter></paramdef>
+ <paramdef>size_t <parameter>size</parameter></paramdef>
+ <paramdef>int <parameter>flags</parameter></paramdef>
+ </funcprototype>
+ <funcprototype>
+ <funcdef>size_t <function>nallocx</function></funcdef>
+ <paramdef>size_t <parameter>size</parameter></paramdef>
+ <paramdef>int <parameter>flags</parameter></paramdef>
+ </funcprototype>
+ <funcprototype>
+ <funcdef>int <function>mallctl</function></funcdef>
+ <paramdef>const char *<parameter>name</parameter></paramdef>
+ <paramdef>void *<parameter>oldp</parameter></paramdef>
+ <paramdef>size_t *<parameter>oldlenp</parameter></paramdef>
+ <paramdef>void *<parameter>newp</parameter></paramdef>
+ <paramdef>size_t <parameter>newlen</parameter></paramdef>
+ </funcprototype>
+ <funcprototype>
+ <funcdef>int <function>mallctlnametomib</function></funcdef>
+ <paramdef>const char *<parameter>name</parameter></paramdef>
+ <paramdef>size_t *<parameter>mibp</parameter></paramdef>
+ <paramdef>size_t *<parameter>miblenp</parameter></paramdef>
+ </funcprototype>
+ <funcprototype>
+ <funcdef>int <function>mallctlbymib</function></funcdef>
+ <paramdef>const size_t *<parameter>mib</parameter></paramdef>
+ <paramdef>size_t <parameter>miblen</parameter></paramdef>
+ <paramdef>void *<parameter>oldp</parameter></paramdef>
+ <paramdef>size_t *<parameter>oldlenp</parameter></paramdef>
+ <paramdef>void *<parameter>newp</parameter></paramdef>
+ <paramdef>size_t <parameter>newlen</parameter></paramdef>
+ </funcprototype>
+ <funcprototype>
+ <funcdef>void <function>malloc_stats_print</function></funcdef>
+ <paramdef>void <parameter>(*write_cb)</parameter>
+ <funcparams>void *, const char *</funcparams>
+ </paramdef>
+ <paramdef>void *<parameter>cbopaque</parameter></paramdef>
+ <paramdef>const char *<parameter>opts</parameter></paramdef>
+ </funcprototype>
+ <funcprototype>
+ <funcdef>size_t <function>malloc_usable_size</function></funcdef>
+ <paramdef>const void *<parameter>ptr</parameter></paramdef>
+ </funcprototype>
+ <funcprototype>
+ <funcdef>void <function>(*malloc_message)</function></funcdef>
+ <paramdef>void *<parameter>cbopaque</parameter></paramdef>
+ <paramdef>const char *<parameter>s</parameter></paramdef>
+ </funcprototype>
+ <para><type>const char *</type><varname>malloc_conf</varname>;</para>
+ </refsect2>
+ </funcsynopsis>
+ </refsynopsisdiv>
+ <refsect1 id="description">
+ <title>DESCRIPTION</title>
+ <refsect2>
+ <title>Standard API</title>
+
+ <para>The <function>malloc()</function> function allocates
+ <parameter>size</parameter> bytes of uninitialized memory. The allocated
+ space is suitably aligned (after possible pointer coercion) for storage
+ of any type of object.</para>
+
+ <para>The <function>calloc()</function> function allocates
+ space for <parameter>number</parameter> objects, each
+ <parameter>size</parameter> bytes in length. The result is identical to
+ calling <function>malloc()</function> with an argument of
+ <parameter>number</parameter> * <parameter>size</parameter>, with the
+ exception that the allocated memory is explicitly initialized to zero
+ bytes.</para>
+
+ <para>The <function>posix_memalign()</function> function
+ allocates <parameter>size</parameter> bytes of memory such that the
+ allocation's base address is a multiple of
+ <parameter>alignment</parameter>, and returns the allocation in the value
+ pointed to by <parameter>ptr</parameter>. The requested
+ <parameter>alignment</parameter> must be a power of 2 at least as large as
+ <code language="C">sizeof(<type>void *</type>)</code>.</para>
+
+ <para>The <function>aligned_alloc()</function> function
+ allocates <parameter>size</parameter> bytes of memory such that the
+ allocation's base address is a multiple of
+ <parameter>alignment</parameter>. The requested
+ <parameter>alignment</parameter> must be a power of 2. Behavior is
+ undefined if <parameter>size</parameter> is not an integral multiple of
+ <parameter>alignment</parameter>.</para>
+
+ <para>The <function>realloc()</function> function changes the
+ size of the previously allocated memory referenced by
+ <parameter>ptr</parameter> to <parameter>size</parameter> bytes. The
+ contents of the memory are unchanged up to the lesser of the new and old
+ sizes. If the new size is larger, the contents of the newly allocated
+ portion of the memory are undefined. Upon success, the memory referenced
+ by <parameter>ptr</parameter> is freed and a pointer to the newly
+ allocated memory is returned. Note that
+ <function>realloc()</function> may move the memory allocation,
+ resulting in a different return value than <parameter>ptr</parameter>.
+ If <parameter>ptr</parameter> is <constant>NULL</constant>, the
+ <function>realloc()</function> function behaves identically to
+ <function>malloc()</function> for the specified size.</para>
+
+ <para>The <function>free()</function> function causes the
+ allocated memory referenced by <parameter>ptr</parameter> to be made
+ available for future allocations. If <parameter>ptr</parameter> is
+ <constant>NULL</constant>, no action occurs.</para>
+ </refsect2>
+ <refsect2>
+ <title>Non-standard API</title>
+ <para>The <function>mallocx()</function>,
+ <function>rallocx()</function>,
+ <function>xallocx()</function>,
+ <function>sallocx()</function>,
+ <function>dallocx()</function>,
+ <function>sdallocx()</function>, and
+ <function>nallocx()</function> functions all have a
+ <parameter>flags</parameter> argument that can be used to specify
+ options. The functions only check the options that are contextually
+ relevant. Use bitwise or (<code language="C">|</code>) operations to
+ specify one or more of the following:
+ <variablelist>
+ <varlistentry id="MALLOCX_LG_ALIGN">
+ <term><constant>MALLOCX_LG_ALIGN(<parameter>la</parameter>)
+ </constant></term>
+
+ <listitem><para>Align the memory allocation to start at an address
+ that is a multiple of <code language="C">(1 &lt;&lt;
+ <parameter>la</parameter>)</code>. This macro does not validate
+ that <parameter>la</parameter> is within the valid
+ range.</para></listitem>
+ </varlistentry>
+ <varlistentry id="MALLOCX_ALIGN">
+ <term><constant>MALLOCX_ALIGN(<parameter>a</parameter>)
+ </constant></term>
+
+ <listitem><para>Align the memory allocation to start at an address
+ that is a multiple of <parameter>a</parameter>, where
+ <parameter>a</parameter> is a power of two. This macro does not
+ validate that <parameter>a</parameter> is a power of 2.
+ </para></listitem>
+ </varlistentry>
+ <varlistentry id="MALLOCX_ZERO">
+ <term><constant>MALLOCX_ZERO</constant></term>
+
+ <listitem><para>Initialize newly allocated memory to contain zero
+ bytes. In the growing reallocation case, the real size prior to
+ reallocation defines the boundary between untouched bytes and those
+ that are initialized to contain zero bytes. If this macro is
+ absent, newly allocated memory is uninitialized.</para></listitem>
+ </varlistentry>
+ <varlistentry id="MALLOCX_TCACHE">
+ <term><constant>MALLOCX_TCACHE(<parameter>tc</parameter>)
+ </constant></term>
+
+ <listitem><para>Use the thread-specific cache (tcache) specified by
+ the identifier <parameter>tc</parameter>, which must have been
+ acquired via the <link
+ linkend="tcache.create"><mallctl>tcache.create</mallctl></link>
+ mallctl. This macro does not validate that
+ <parameter>tc</parameter> specifies a valid
+ identifier.</para></listitem>
+ </varlistentry>
+ <varlistentry id="MALLOC_TCACHE_NONE">
+ <term><constant>MALLOCX_TCACHE_NONE</constant></term>
+
+ <listitem><para>Do not use a thread-specific cache (tcache). Unless
+ <constant>MALLOCX_TCACHE(<parameter>tc</parameter>)</constant> or
+ <constant>MALLOCX_TCACHE_NONE</constant> is specified, an
+ automatically managed tcache will be used under many circumstances.
+ This macro cannot be used in the same <parameter>flags</parameter>
+ argument as
+ <constant>MALLOCX_TCACHE(<parameter>tc</parameter>)</constant>.</para></listitem>
+ </varlistentry>
+ <varlistentry id="MALLOCX_ARENA">
+ <term><constant>MALLOCX_ARENA(<parameter>a</parameter>)
+ </constant></term>
+
+ <listitem><para>Use the arena specified by the index
+ <parameter>a</parameter>. This macro has no effect for regions that
+ were allocated via an arena other than the one specified. This
+ macro does not validate that <parameter>a</parameter> specifies an
+ arena index in the valid range.</para></listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+
+ <para>The <function>mallocx()</function> function allocates at
+ least <parameter>size</parameter> bytes of memory, and returns a pointer
+ to the base address of the allocation. Behavior is undefined if
+ <parameter>size</parameter> is <constant>0</constant>.</para>
+
+ <para>The <function>rallocx()</function> function resizes the
+ allocation at <parameter>ptr</parameter> to be at least
+ <parameter>size</parameter> bytes, and returns a pointer to the base
+ address of the resulting allocation, which may or may not have moved from
+ its original location. Behavior is undefined if
+ <parameter>size</parameter> is <constant>0</constant>.</para>
+
+ <para>The <function>xallocx()</function> function resizes the
+ allocation at <parameter>ptr</parameter> in place to be at least
+ <parameter>size</parameter> bytes, and returns the real size of the
+ allocation. If <parameter>extra</parameter> is non-zero, an attempt is
+ made to resize the allocation to be at least <code
+ language="C">(<parameter>size</parameter> +
+ <parameter>extra</parameter>)</code> bytes, though inability to allocate
+ the extra byte(s) will not by itself result in failure to resize.
+ Behavior is undefined if <parameter>size</parameter> is
+ <constant>0</constant>, or if <code
+ language="C">(<parameter>size</parameter> + <parameter>extra</parameter>
+ &gt; <constant>SIZE_T_MAX</constant>)</code>.</para>
+
+ <para>The <function>sallocx()</function> function returns the
+ real size of the allocation at <parameter>ptr</parameter>.</para>
+
+ <para>The <function>dallocx()</function> function causes the
+ memory referenced by <parameter>ptr</parameter> to be made available for
+ future allocations.</para>
+
+ <para>The <function>sdallocx()</function> function is an
+ extension of <function>dallocx()</function> with a
+ <parameter>size</parameter> parameter to allow the caller to pass in the
+ allocation size as an optimization. The minimum valid input size is the
+ original requested size of the allocation, and the maximum valid input
+ size is the corresponding value returned by
+ <function>nallocx()</function> or
+ <function>sallocx()</function>.</para>
+
+ <para>The <function>nallocx()</function> function allocates no
+ memory, but it performs the same size computation as the
+ <function>mallocx()</function> function, and returns the real
+ size of the allocation that would result from the equivalent
+ <function>mallocx()</function> function call, or
+ <constant>0</constant> if the inputs exceed the maximum supported size
+ class and/or alignment. Behavior is undefined if
+ <parameter>size</parameter> is <constant>0</constant>.</para>
+
+ <para>The <function>mallctl()</function> function provides a
+ general interface for introspecting the memory allocator, as well as
+ setting modifiable parameters and triggering actions. The
+ period-separated <parameter>name</parameter> argument specifies a
+ location in a tree-structured namespace; see the <xref
+ linkend="mallctl_namespace" xrefstyle="template:%t"/> section for
+ documentation on the tree contents. To read a value, pass a pointer via
+ <parameter>oldp</parameter> to adequate space to contain the value, and a
+ pointer to its length via <parameter>oldlenp</parameter>; otherwise pass
+ <constant>NULL</constant> and <constant>NULL</constant>. Similarly, to
+ write a value, pass a pointer to the value via
+ <parameter>newp</parameter>, and its length via
+ <parameter>newlen</parameter>; otherwise pass <constant>NULL</constant>
+ and <constant>0</constant>.</para>
+
+ <para>The <function>mallctlnametomib()</function> function
+ provides a way to avoid repeated name lookups for applications that
+ repeatedly query the same portion of the namespace, by translating a name
+ to a <quote>Management Information Base</quote> (MIB) that can be passed
+ repeatedly to <function>mallctlbymib()</function>. Upon
+ successful return from <function>mallctlnametomib()</function>,
+ <parameter>mibp</parameter> contains an array of
+ <parameter>*miblenp</parameter> integers, where
+ <parameter>*miblenp</parameter> is the lesser of the number of components
+ in <parameter>name</parameter> and the input value of
+ <parameter>*miblenp</parameter>. Thus it is possible to pass a
+ <parameter>*miblenp</parameter> that is smaller than the number of
+ period-separated name components, which results in a partial MIB that can
+ be used as the basis for constructing a complete MIB. For name
+ components that are integers (e.g. the 2 in
+ <link
+ linkend="arenas.bin.i.size"><mallctl>arenas.bin.2.size</mallctl></link>),
+ the corresponding MIB component will always be that integer. Therefore,
+ it is legitimate to construct code like the following: <programlisting
+ language="C"><![CDATA[
+unsigned nbins, i;
+size_t mib[4];
+size_t len, miblen;
+
+len = sizeof(nbins);
+mallctl("arenas.nbins", &nbins, &len, NULL, 0);
+
+miblen = 4;
+mallctlnametomib("arenas.bin.0.size", mib, &miblen);
+for (i = 0; i < nbins; i++) {
+ size_t bin_size;
+
+ mib[2] = i;
+ len = sizeof(bin_size);
+ mallctlbymib(mib, miblen, (void *)&bin_size, &len, NULL, 0);
+ /* Do something with bin_size... */
+}]]></programlisting></para>
+
+ <varlistentry id="malloc_stats_print_opts">
+ </varlistentry>
+ <para>The <function>malloc_stats_print()</function> function writes
+ summary statistics via the <parameter>write_cb</parameter> callback
+ function pointer and <parameter>cbopaque</parameter> data passed to
+ <parameter>write_cb</parameter>, or <function>malloc_message()</function>
+ if <parameter>write_cb</parameter> is <constant>NULL</constant>. The
+ statistics are presented in human-readable form unless <quote>J</quote> is
+ specified as a character within the <parameter>opts</parameter> string, in
+ which case the statistics are presented in <ulink
+ url="http://www.json.org/">JSON format</ulink>. This function can be
+ called repeatedly. General information that never changes during
+ execution can be omitted by specifying <quote>g</quote> as a character
+ within the <parameter>opts</parameter> string. Note that
+ <function>malloc_stats_print()</function> uses the
+ <function>mallctl*()</function> functions internally, so inconsistent
+ statistics can be reported if multiple threads use these functions
+ simultaneously. If <option>--enable-stats</option> is specified during
+ configuration, <quote>m</quote>, <quote>d</quote>, and <quote>a</quote>
+ can be specified to omit merged arena, destroyed merged arena, and per
+ arena statistics, respectively; <quote>b</quote> and <quote>l</quote> can
+ be specified to omit per size class statistics for bins and large objects,
+ respectively; <quote>x</quote> can be specified to omit all mutex
+ statistics; <quote>e</quote> can be used to omit extent statistics.
+ Unrecognized characters are silently ignored. Note that thread caching
+ may prevent some statistics from being completely up to date, since extra
+ locking would be required to merge counters that track thread cache
+ operations.</para>
+
+ <para>The <function>malloc_usable_size()</function> function
+ returns the usable size of the allocation pointed to by
+ <parameter>ptr</parameter>. The return value may be larger than the size
+ that was requested during allocation. The
+ <function>malloc_usable_size()</function> function is not a
+ mechanism for in-place <function>realloc()</function>; rather
+ it is provided solely as a tool for introspection purposes. Any
+ discrepancy between the requested allocation size and the size reported
+ by <function>malloc_usable_size()</function> should not be
+ depended on, since such behavior is entirely implementation-dependent.
+ </para>
+ </refsect2>
+ </refsect1>
+ <refsect1 id="tuning">
+ <title>TUNING</title>
+ <para>Once, when the first call is made to one of the memory allocation
+ routines, the allocator initializes its internals based in part on various
+ options that can be specified at compile- or run-time.</para>
+
+ <para>The string specified via <option>--with-malloc-conf</option>, the
+ string pointed to by the global variable <varname>malloc_conf</varname>, the
+ <quote>name</quote> of the file referenced by the symbolic link named
+ <filename class="symlink">/etc/malloc.conf</filename>, and the value of the
+ environment variable <envar>MALLOC_CONF</envar>, will be interpreted, in
+ that order, from left to right as options. Note that
+ <varname>malloc_conf</varname> may be read before
+ <function>main()</function> is entered, so the declaration of
+ <varname>malloc_conf</varname> should specify an initializer that contains
+ the final value to be read by jemalloc. <option>--with-malloc-conf</option>
+ and <varname>malloc_conf</varname> are compile-time mechanisms, whereas
+ <filename class="symlink">/etc/malloc.conf</filename> and
+ <envar>MALLOC_CONF</envar> can be safely set any time prior to program
+ invocation.</para>
+
+ <para>An options string is a comma-separated list of option:value pairs.
+ There is one key corresponding to each <link
+ linkend="opt.abort"><mallctl>opt.*</mallctl></link> mallctl (see the <xref
+ linkend="mallctl_namespace" xrefstyle="template:%t"/> section for options
+ documentation). For example, <literal>abort:true,narenas:1</literal> sets
+ the <link linkend="opt.abort"><mallctl>opt.abort</mallctl></link> and <link
+ linkend="opt.narenas"><mallctl>opt.narenas</mallctl></link> options. Some
+ options have boolean values (true/false), others have integer values (base
+ 8, 10, or 16, depending on prefix), and yet others have raw string
+ values.</para>
+ </refsect1>
+ <refsect1 id="implementation_notes">
+ <title>IMPLEMENTATION NOTES</title>
+ <para>Traditionally, allocators have used
+ <citerefentry><refentrytitle>sbrk</refentrytitle>
+ <manvolnum>2</manvolnum></citerefentry> to obtain memory, which is
+ suboptimal for several reasons, including race conditions, increased
+ fragmentation, and artificial limitations on maximum usable memory. If
+ <citerefentry><refentrytitle>sbrk</refentrytitle>
+ <manvolnum>2</manvolnum></citerefentry> is supported by the operating
+ system, this allocator uses both
+ <citerefentry><refentrytitle>mmap</refentrytitle>
+ <manvolnum>2</manvolnum></citerefentry> and
+ <citerefentry><refentrytitle>sbrk</refentrytitle>
+ <manvolnum>2</manvolnum></citerefentry>, in that order of preference;
+ otherwise only <citerefentry><refentrytitle>mmap</refentrytitle>
+ <manvolnum>2</manvolnum></citerefentry> is used.</para>
+
+ <para>This allocator uses multiple arenas in order to reduce lock
+ contention for threaded programs on multi-processor systems. This works
+ well with regard to threading scalability, but incurs some costs. There is
+ a small fixed per-arena overhead, and additionally, arenas manage memory
+ completely independently of each other, which means a small fixed increase
+ in overall memory fragmentation. These overheads are not generally an
+ issue, given the number of arenas normally used. Note that using
+ substantially more arenas than the default is not likely to improve
+ performance, mainly due to reduced cache performance. However, it may make
+ sense to reduce the number of arenas if an application does not make much
+ use of the allocation functions.</para>
+
+ <para>In addition to multiple arenas, this allocator supports
+ thread-specific caching, in order to make it possible to completely avoid
+ synchronization for most allocation requests. Such caching allows very fast
+ allocation in the common case, but it increases memory usage and
+ fragmentation, since a bounded number of objects can remain allocated in
+ each thread cache.</para>
+
+ <para>Memory is conceptually broken into extents. Extents are always
+ aligned to multiples of the page size. This alignment makes it possible to
+ find metadata for user objects quickly. User objects are broken into two
+ categories according to size: small and large. Contiguous small objects
+ comprise a slab, which resides within a single extent, whereas large objects
+ each have their own extents backing them.</para>
+
+ <para>Small objects are managed in groups by slabs. Each slab maintains
+ a bitmap to track which regions are in use. Allocation requests that are no
+ more than half the quantum (8 or 16, depending on architecture) are rounded
+ up to the nearest power of two that is at least <code
+ language="C">sizeof(<type>double</type>)</code>. All other object size
+ classes are multiples of the quantum, spaced such that there are four size
+ classes for each doubling in size, which limits internal fragmentation to
+ approximately 20% for all but the smallest size classes. Small size classes
+ are smaller than four times the page size, and large size classes extend
+ from four times the page size up to the largest size class that does not
+ exceed <constant>PTRDIFF_MAX</constant>.</para>
+
+ <para>Allocations are packed tightly together, which can be an issue for
+ multi-threaded applications. If you need to assure that allocations do not
+ suffer from cacheline sharing, round your allocation requests up to the
+ nearest multiple of the cacheline size, or specify cacheline alignment when
+ allocating.</para>
+
+ <para>The <function>realloc()</function>,
+ <function>rallocx()</function>, and
+ <function>xallocx()</function> functions may resize allocations
+ without moving them under limited circumstances. Unlike the
+ <function>*allocx()</function> API, the standard API does not
+ officially round up the usable size of an allocation to the nearest size
+ class, so technically it is necessary to call
+ <function>realloc()</function> to grow e.g. a 9-byte allocation to
+ 16 bytes, or shrink a 16-byte allocation to 9 bytes. Growth and shrinkage
+ trivially succeeds in place as long as the pre-size and post-size both round
+ up to the same size class. No other API guarantees are made regarding
+ in-place resizing, but the current implementation also tries to resize large
+ allocations in place, as long as the pre-size and post-size are both large.
+ For shrinkage to succeed, the extent allocator must support splitting (see
+ <link
+ linkend="arena.i.extent_hooks"><mallctl>arena.&lt;i&gt;.extent_hooks</mallctl></link>).
+ Growth only succeeds if the trailing memory is currently available, and the
+ extent allocator supports merging.</para>
+
+ <para>Assuming 4 KiB pages and a 16-byte quantum on a 64-bit system, the
+ size classes in each category are as shown in <xref linkend="size_classes"
+ xrefstyle="template:Table %n"/>.</para>
+
+ <table xml:id="size_classes" frame="all">
+ <title>Size classes</title>
+ <tgroup cols="3" colsep="1" rowsep="1">
+ <colspec colname="c1" align="left"/>
+ <colspec colname="c2" align="right"/>
+ <colspec colname="c3" align="left"/>
+ <thead>
+ <row>
+ <entry>Category</entry>
+ <entry>Spacing</entry>
+ <entry>Size</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry morerows="8">Small</entry>
+ <entry>lg</entry>
+ <entry>[8]</entry>
+ </row>
+ <row>
+ <entry>16</entry>
+ <entry>[16, 32, 48, 64, 80, 96, 112, 128]</entry>
+ </row>
+ <row>
+ <entry>32</entry>
+ <entry>[160, 192, 224, 256]</entry>
+ </row>
+ <row>
+ <entry>64</entry>
+ <entry>[320, 384, 448, 512]</entry>
+ </row>
+ <row>
+ <entry>128</entry>
+ <entry>[640, 768, 896, 1024]</entry>
+ </row>
+ <row>
+ <entry>256</entry>
+ <entry>[1280, 1536, 1792, 2048]</entry>
+ </row>
+ <row>
+ <entry>512</entry>
+ <entry>[2560, 3072, 3584, 4096]</entry>
+ </row>
+ <row>
+ <entry>1 KiB</entry>
+ <entry>[5 KiB, 6 KiB, 7 KiB, 8 KiB]</entry>
+ </row>
+ <row>
+ <entry>2 KiB</entry>
+ <entry>[10 KiB, 12 KiB, 14 KiB]</entry>
+ </row>
+ <row>
+ <entry morerows="15">Large</entry>
+ <entry>2 KiB</entry>
+ <entry>[16 KiB]</entry>
+ </row>
+ <row>
+ <entry>4 KiB</entry>
+ <entry>[20 KiB, 24 KiB, 28 KiB, 32 KiB]</entry>
+ </row>
+ <row>
+ <entry>8 KiB</entry>
+ <entry>[40 KiB, 48 KiB, 56 KiB, 64 KiB]</entry>
+ </row>
+ <row>
+ <entry>16 KiB</entry>
+ <entry>[80 KiB, 96 KiB, 112 KiB, 128 KiB]</entry>
+ </row>
+ <row>
+ <entry>32 KiB</entry>
+ <entry>[160 KiB, 192 KiB, 224 KiB, 256 KiB]</entry>
+ </row>
+ <row>
+ <entry>64 KiB</entry>
+ <entry>[320 KiB, 384 KiB, 448 KiB, 512 KiB]</entry>
+ </row>
+ <row>
+ <entry>128 KiB</entry>
+ <entry>[640 KiB, 768 KiB, 896 KiB, 1 MiB]</entry>
+ </row>
+ <row>
+ <entry>256 KiB</entry>
+ <entry>[1280 KiB, 1536 KiB, 1792 KiB, 2 MiB]</entry>
+ </row>
+ <row>
+ <entry>512 KiB</entry>
+ <entry>[2560 KiB, 3 MiB, 3584 KiB, 4 MiB]</entry>
+ </row>
+ <row>
+ <entry>1 MiB</entry>
+ <entry>[5 MiB, 6 MiB, 7 MiB, 8 MiB]</entry>
+ </row>
+ <row>
+ <entry>2 MiB</entry>
+ <entry>[10 MiB, 12 MiB, 14 MiB, 16 MiB]</entry>
+ </row>
+ <row>
+ <entry>4 MiB</entry>
+ <entry>[20 MiB, 24 MiB, 28 MiB, 32 MiB]</entry>
+ </row>
+ <row>
+ <entry>8 MiB</entry>
+ <entry>[40 MiB, 48 MiB, 56 MiB, 64 MiB]</entry>
+ </row>
+ <row>
+ <entry>...</entry>
+ <entry>...</entry>
+ </row>
+ <row>
+ <entry>512 PiB</entry>
+ <entry>[2560 PiB, 3 EiB, 3584 PiB, 4 EiB]</entry>
+ </row>
+ <row>
+ <entry>1 EiB</entry>
+ <entry>[5 EiB, 6 EiB, 7 EiB]</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </refsect1>
+ <refsect1 id="mallctl_namespace">
+ <title>MALLCTL NAMESPACE</title>
+ <para>The following names are defined in the namespace accessible via the
+ <function>mallctl*()</function> functions. Value types are specified in
+ parentheses, their readable/writable statuses are encoded as
+ <literal>rw</literal>, <literal>r-</literal>, <literal>-w</literal>, or
+ <literal>--</literal>, and required build configuration flags follow, if
+ any. A name element encoded as <literal>&lt;i&gt;</literal> or
+ <literal>&lt;j&gt;</literal> indicates an integer component, where the
+ integer varies from 0 to some upper value that must be determined via
+ introspection. In the case of <mallctl>stats.arenas.&lt;i&gt;.*</mallctl>
+ and <mallctl>arena.&lt;i&gt;.{initialized,purge,decay,dss}</mallctl>,
+ <literal>&lt;i&gt;</literal> equal to
+ <constant>MALLCTL_ARENAS_ALL</constant> can be used to operate on all arenas
+ or access the summation of statistics from all arenas; similarly
+ <literal>&lt;i&gt;</literal> equal to
+ <constant>MALLCTL_ARENAS_DESTROYED</constant> can be used to access the
+ summation of statistics from all destroyed arenas. These constants can be
+ utilized either via <function>mallctlnametomib()</function> followed by
+ <function>mallctlbymib()</function>, or via code such as the following:
+ <programlisting language="C"><![CDATA[
+#define STRINGIFY_HELPER(x) #x
+#define STRINGIFY(x) STRINGIFY_HELPER(x)
+
+mallctl("arena." STRINGIFY(MALLCTL_ARENAS_ALL) ".decay",
+ NULL, NULL, NULL, 0);]]></programlisting>
+ Take special note of the <link
+ linkend="epoch"><mallctl>epoch</mallctl></link> mallctl, which controls
+ refreshing of cached dynamic statistics.</para>
+
+ <variablelist>
+ <varlistentry id="version">
+ <term>
+ <mallctl>version</mallctl>
+ (<type>const char *</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para>Return the jemalloc version string.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="epoch">
+ <term>
+ <mallctl>epoch</mallctl>
+ (<type>uint64_t</type>)
+ <literal>rw</literal>
+ </term>
+ <listitem><para>If a value is passed in, refresh the data from which
+ the <function>mallctl*()</function> functions report values,
+ and increment the epoch. Return the current epoch. This is useful for
+ detecting whether another thread caused a refresh.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="background_thread">
+ <term>
+ <mallctl>background_thread</mallctl>
+ (<type>bool</type>)
+ <literal>rw</literal>
+ </term>
+ <listitem><para>Enable/disable internal background worker threads. When
+ set to true, background threads are created on demand (the number of
+ background threads will be no more than the number of CPUs or active
+ arenas). Threads run periodically, and handle <link
+ linkend="arena.i.decay">purging</link> asynchronously. When switching
+ off, background threads are terminated synchronously. Note that after
+ <citerefentry><refentrytitle>fork</refentrytitle><manvolnum>2</manvolnum></citerefentry>
+ function, the state in the child process will be disabled regardless
+ the state in parent process. See <link
+ linkend="stats.background_thread.num_threads"><mallctl>stats.background_thread</mallctl></link>
+ for related stats. <link
+ linkend="opt.background_thread"><mallctl>opt.background_thread</mallctl></link>
+ can be used to set the default option. This option is only available on
+ selected pthread-based platforms.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="max_background_threads">
+ <term>
+ <mallctl>max_background_threads</mallctl>
+ (<type>size_t</type>)
+ <literal>rw</literal>
+ </term>
+ <listitem><para>Maximum number of background worker threads that will
+ be created. This value is capped at <link
+ linkend="opt.max_background_threads"><mallctl>opt.max_background_threads</mallctl></link> at
+ startup.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="config.cache_oblivious">
+ <term>
+ <mallctl>config.cache_oblivious</mallctl>
+ (<type>bool</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para><option>--enable-cache-oblivious</option> was specified
+ during build configuration.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="config.debug">
+ <term>
+ <mallctl>config.debug</mallctl>
+ (<type>bool</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para><option>--enable-debug</option> was specified during
+ build configuration.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="config.fill">
+ <term>
+ <mallctl>config.fill</mallctl>
+ (<type>bool</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para><option>--enable-fill</option> was specified during
+ build configuration.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="config.lazy_lock">
+ <term>
+ <mallctl>config.lazy_lock</mallctl>
+ (<type>bool</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para><option>--enable-lazy-lock</option> was specified
+ during build configuration.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="config.malloc_conf">
+ <term>
+ <mallctl>config.malloc_conf</mallctl>
+ (<type>const char *</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para>Embedded configure-time-specified run-time options
+ string, empty unless <option>--with-malloc-conf</option> was specified
+ during build configuration.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="config.prof">
+ <term>
+ <mallctl>config.prof</mallctl>
+ (<type>bool</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para><option>--enable-prof</option> was specified during
+ build configuration.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="config.prof_libgcc">
+ <term>
+ <mallctl>config.prof_libgcc</mallctl>
+ (<type>bool</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para><option>--disable-prof-libgcc</option> was not
+ specified during build configuration.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="config.prof_libunwind">
+ <term>
+ <mallctl>config.prof_libunwind</mallctl>
+ (<type>bool</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para><option>--enable-prof-libunwind</option> was specified
+ during build configuration.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="config.stats">
+ <term>
+ <mallctl>config.stats</mallctl>
+ (<type>bool</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para><option>--enable-stats</option> was specified during
+ build configuration.</para></listitem>
+ </varlistentry>
+
+
+ <varlistentry id="config.utrace">
+ <term>
+ <mallctl>config.utrace</mallctl>
+ (<type>bool</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para><option>--enable-utrace</option> was specified during
+ build configuration.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="config.xmalloc">
+ <term>
+ <mallctl>config.xmalloc</mallctl>
+ (<type>bool</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para><option>--enable-xmalloc</option> was specified during
+ build configuration.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="opt.abort">
+ <term>
+ <mallctl>opt.abort</mallctl>
+ (<type>bool</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para>Abort-on-warning enabled/disabled. If true, most
+ warnings are fatal. Note that runtime option warnings are not included
+ (see <link
+ linkend="opt.abort_conf"><mallctl>opt.abort_conf</mallctl></link> for
+ that). The process will call
+ <citerefentry><refentrytitle>abort</refentrytitle>
+ <manvolnum>3</manvolnum></citerefentry> in these cases. This option is
+ disabled by default unless <option>--enable-debug</option> is
+ specified during configuration, in which case it is enabled by default.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry id="opt.confirm_conf">
+ <term>
+ <mallctl>opt.confirm_conf</mallctl>
+ (<type>bool</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para>Confirm-runtime-options-when-program-starts
+ enabled/disabled. If true, the string specified via
+ <option>--with-malloc-conf</option>, the string pointed to by the
+ global variable <varname>malloc_conf</varname>, the <quote>name</quote>
+ of the file referenced by the symbolic link named
+ <filename class="symlink">/etc/malloc.conf</filename>, and the value of
+ the environment variable <envar>MALLOC_CONF</envar>, will be printed in
+ order. Then, each option being set will be individually printed. This
+ option is disabled by default.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="opt.abort_conf">
+ <term>
+ <mallctl>opt.abort_conf</mallctl>
+ (<type>bool</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para>Abort-on-invalid-configuration enabled/disabled. If
+ true, invalid runtime options are fatal. The process will call
+ <citerefentry><refentrytitle>abort</refentrytitle>
+ <manvolnum>3</manvolnum></citerefentry> in these cases. This option is
+ disabled by default unless <option>--enable-debug</option> is
+ specified during configuration, in which case it is enabled by default.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry id="opt.cache_oblivious">
+ <term>
+ <mallctl>opt.cache_oblivious</mallctl>
+ (<type>bool</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para>Enable / Disable cache-oblivious large allocation
+ alignment, for large requests with no alignment constraints. If this
+ feature is disabled, all large allocations are page-aligned as an
+ implementation artifact, which can severely harm CPU cache utilization.
+ However, the cache-oblivious layout comes at the cost of one extra page
+ per large allocation, which in the most extreme case increases physical
+ memory usage for the 16 KiB size class to 20 KiB. This option is enabled
+ by default.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="opt.metadata_thp">
+ <term>
+ <mallctl>opt.metadata_thp</mallctl>
+ (<type>const char *</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para>Controls whether to allow jemalloc to use transparent
+ huge page (THP) for internal metadata (see <link
+ linkend="stats.metadata">stats.metadata</link>). <quote>always</quote>
+ allows such usage. <quote>auto</quote> uses no THP initially, but may
+ begin to do so when metadata usage reaches certain level. The default
+ is <quote>disabled</quote>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="opt.trust_madvise">
+ <term>
+ <mallctl>opt.trust_madvise</mallctl>
+ (<type>bool</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para>If true, do not perform runtime check for MADV_DONTNEED,
+ to check that it actually zeros pages. The default is disabled on Linux
+ and enabled elsewhere.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="opt.retain">
+ <term>
+ <mallctl>opt.retain</mallctl>
+ (<type>bool</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para>If true, retain unused virtual memory for later reuse
+ rather than discarding it by calling
+ <citerefentry><refentrytitle>munmap</refentrytitle>
+ <manvolnum>2</manvolnum></citerefentry> or equivalent (see <link
+ linkend="stats.retained">stats.retained</link> for related details).
+ It also makes jemalloc use <citerefentry>
+ <refentrytitle>mmap</refentrytitle><manvolnum>2</manvolnum>
+ </citerefentry> or equivalent in a more greedy way, mapping larger
+ chunks in one go. This option is disabled by default unless discarding
+ virtual memory is known to trigger platform-specific performance
+ problems, namely 1) for [64-bit] Linux, which has a quirk in its virtual
+ memory allocation algorithm that causes semi-permanent VM map holes
+ under normal jemalloc operation; and 2) for [64-bit] Windows, which
+ disallows split / merged regions with
+ <parameter><constant>MEM_RELEASE</constant></parameter>. Although the
+ same issues may present on 32-bit platforms as well, retaining virtual
+ memory for 32-bit Linux and Windows is disabled by default due to the
+ practical possibility of address space exhaustion. </para></listitem>
+ </varlistentry>
+
+ <varlistentry id="opt.dss">
+ <term>
+ <mallctl>opt.dss</mallctl>
+ (<type>const char *</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para>dss (<citerefentry><refentrytitle>sbrk</refentrytitle>
+ <manvolnum>2</manvolnum></citerefentry>) allocation precedence as
+ related to <citerefentry><refentrytitle>mmap</refentrytitle>
+ <manvolnum>2</manvolnum></citerefentry> allocation. The following
+ settings are supported if
+ <citerefentry><refentrytitle>sbrk</refentrytitle>
+ <manvolnum>2</manvolnum></citerefentry> is supported by the operating
+ system: <quote>disabled</quote>, <quote>primary</quote>, and
+ <quote>secondary</quote>; otherwise only <quote>disabled</quote> is
+ supported. The default is <quote>secondary</quote> if
+ <citerefentry><refentrytitle>sbrk</refentrytitle>
+ <manvolnum>2</manvolnum></citerefentry> is supported by the operating
+ system; <quote>disabled</quote> otherwise.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry id="opt.narenas">
+ <term>
+ <mallctl>opt.narenas</mallctl>
+ (<type>unsigned</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para>Maximum number of arenas to use for automatic
+ multiplexing of threads and arenas. The default is four times the
+ number of CPUs, or one if there is a single CPU.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="opt.oversize_threshold">
+ <term>
+ <mallctl>opt.oversize_threshold</mallctl>
+ (<type>size_t</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para>The threshold in bytes of which requests are considered
+ oversize. Allocation requests with greater sizes are fulfilled from a
+ dedicated arena (automatically managed, however not within
+ <literal>narenas</literal>), in order to reduce fragmentation by not
+ mixing huge allocations with small ones. In addition, the decay API
+ guarantees on the extents greater than the specified threshold may be
+ overridden. Note that requests with arena index specified via
+ <constant>MALLOCX_ARENA</constant>, or threads associated with explicit
+ arenas will not be considered. The default threshold is 8MiB. Values
+ not within large size classes disables this feature.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="opt.percpu_arena">
+ <term>
+ <mallctl>opt.percpu_arena</mallctl>
+ (<type>const char *</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para>Per CPU arena mode. Use the <quote>percpu</quote>
+ setting to enable this feature, which uses number of CPUs to determine
+ number of arenas, and bind threads to arenas dynamically based on the
+ CPU the thread runs on currently. <quote>phycpu</quote> setting uses
+ one arena per physical CPU, which means the two hyper threads on the
+ same CPU share one arena. Note that no runtime checking regarding the
+ availability of hyper threading is done at the moment. When set to
+ <quote>disabled</quote>, narenas and thread to arena association will
+ not be impacted by this option. The default is <quote>disabled</quote>.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry id="opt.background_thread">
+ <term>
+ <mallctl>opt.background_thread</mallctl>
+ (<type>bool</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para>Internal background worker threads enabled/disabled.
+ Because of potential circular dependencies, enabling background thread
+ using this option may cause crash or deadlock during initialization. For
+ a reliable way to use this feature, see <link
+ linkend="background_thread">background_thread</link> for dynamic control
+ options and details. This option is disabled by
+ default.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="opt.max_background_threads">
+ <term>
+ <mallctl>opt.max_background_threads</mallctl>
+ (<type>size_t</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para>Maximum number of background threads that will be created
+ if <link linkend="background_thread">background_thread</link> is set.
+ Defaults to number of cpus.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="opt.dirty_decay_ms">
+ <term>
+ <mallctl>opt.dirty_decay_ms</mallctl>
+ (<type>ssize_t</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para>Approximate time in milliseconds from the creation of a
+ set of unused dirty pages until an equivalent set of unused dirty pages
+ is purged (i.e. converted to muzzy via e.g.
+ <function>madvise(<parameter>...</parameter><parameter><constant>MADV_FREE</constant></parameter>)</function>
+ if supported by the operating system, or converted to clean otherwise)
+ and/or reused. Dirty pages are defined as previously having been
+ potentially written to by the application, and therefore consuming
+ physical memory, yet having no current use. The pages are incrementally
+ purged according to a sigmoidal decay curve that starts and ends with
+ zero purge rate. A decay time of 0 causes all unused dirty pages to be
+ purged immediately upon creation. A decay time of -1 disables purging.
+ The default decay time is 10 seconds. See <link
+ linkend="arenas.dirty_decay_ms"><mallctl>arenas.dirty_decay_ms</mallctl></link>
+ and <link
+ linkend="arena.i.dirty_decay_ms"><mallctl>arena.&lt;i&gt;.dirty_decay_ms</mallctl></link>
+ for related dynamic control options. See <link
+ linkend="opt.muzzy_decay_ms"><mallctl>opt.muzzy_decay_ms</mallctl></link>
+ for a description of muzzy pages.for a description of muzzy pages. Note
+ that when the <link
+ linkend="opt.oversize_threshold"><mallctl>oversize_threshold</mallctl></link>
+ feature is enabled, the arenas reserved for oversize requests may have
+ its own default decay settings.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="opt.muzzy_decay_ms">
+ <term>
+ <mallctl>opt.muzzy_decay_ms</mallctl>
+ (<type>ssize_t</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para>Approximate time in milliseconds from the creation of a
+ set of unused muzzy pages until an equivalent set of unused muzzy pages
+ is purged (i.e. converted to clean) and/or reused. Muzzy pages are
+ defined as previously having been unused dirty pages that were
+ subsequently purged in a manner that left them subject to the
+ reclamation whims of the operating system (e.g.
+ <function>madvise(<parameter>...</parameter><parameter><constant>MADV_FREE</constant></parameter>)</function>),
+ and therefore in an indeterminate state. The pages are incrementally
+ purged according to a sigmoidal decay curve that starts and ends with
+ zero purge rate. A decay time of 0 causes all unused muzzy pages to be
+ purged immediately upon creation. A decay time of -1 disables purging.
+ The default decay time is 10 seconds. See <link
+ linkend="arenas.muzzy_decay_ms"><mallctl>arenas.muzzy_decay_ms</mallctl></link>
+ and <link
+ linkend="arena.i.muzzy_decay_ms"><mallctl>arena.&lt;i&gt;.muzzy_decay_ms</mallctl></link>
+ for related dynamic control options.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="opt.lg_extent_max_active_fit">
+ <term>
+ <mallctl>opt.lg_extent_max_active_fit</mallctl>
+ (<type>size_t</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para>When reusing dirty extents, this determines the (log
+ base 2 of the) maximum ratio between the size of the active extent
+ selected (to split off from) and the size of the requested allocation.
+ This prevents the splitting of large active extents for smaller
+ allocations, which can reduce fragmentation over the long run
+ (especially for non-active extents). Lower value may reduce
+ fragmentation, at the cost of extra active extents. The default value
+ is 6, which gives a maximum ratio of 64 (2^6).</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="opt.stats_print">
+ <term>
+ <mallctl>opt.stats_print</mallctl>
+ (<type>bool</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para>Enable/disable statistics printing at exit. If
+ enabled, the <function>malloc_stats_print()</function>
+ function is called at program exit via an
+ <citerefentry><refentrytitle>atexit</refentrytitle>
+ <manvolnum>3</manvolnum></citerefentry> function. <link
+ linkend="opt.stats_print_opts"><mallctl>opt.stats_print_opts</mallctl></link>
+ can be combined to specify output options. If
+ <option>--enable-stats</option> is specified during configuration, this
+ has the potential to cause deadlock for a multi-threaded process that
+ exits while one or more threads are executing in the memory allocation
+ functions. Furthermore, <function>atexit()</function> may
+ allocate memory during application initialization and then deadlock
+ internally when jemalloc in turn calls
+ <function>atexit()</function>, so this option is not
+ universally usable (though the application can register its own
+ <function>atexit()</function> function with equivalent
+ functionality). Therefore, this option should only be used with care;
+ it is primarily intended as a performance tuning aid during application
+ development. This option is disabled by default.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="opt.stats_print_opts">
+ <term>
+ <mallctl>opt.stats_print_opts</mallctl>
+ (<type>const char *</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para>Options (the <parameter>opts</parameter> string) to pass
+ to the <function>malloc_stats_print()</function> at exit (enabled
+ through <link
+ linkend="opt.stats_print"><mallctl>opt.stats_print</mallctl></link>). See
+ available options in <link
+ linkend="malloc_stats_print_opts"><function>malloc_stats_print()</function></link>.
+ Has no effect unless <link
+ linkend="opt.stats_print"><mallctl>opt.stats_print</mallctl></link> is
+ enabled. The default is <quote></quote>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="opt.stats_interval">
+ <term>
+ <mallctl>opt.stats_interval</mallctl>
+ (<type>int64_t</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para>Average interval between statistics outputs, as measured
+ in bytes of allocation activity. The actual interval may be sporadic
+ because decentralized event counters are used to avoid synchronization
+ bottlenecks. The output may be triggered on any thread, which then
+ calls <function>malloc_stats_print()</function>. <link
+ linkend="opt.stats_interval_opts"><mallctl>opt.stats_interval_opts</mallctl></link>
+ can be combined to specify output options. By default,
+ interval-triggered stats output is disabled (encoded as
+ -1).</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="opt.stats_interval_opts">
+ <term>
+ <mallctl>opt.stats_interval_opts</mallctl>
+ (<type>const char *</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para>Options (the <parameter>opts</parameter> string) to pass
+ to the <function>malloc_stats_print()</function> for interval based
+ statistics printing (enabled
+ through <link
+ linkend="opt.stats_interval"><mallctl>opt.stats_interval</mallctl></link>). See
+ available options in <link
+ linkend="malloc_stats_print_opts"><function>malloc_stats_print()</function></link>.
+ Has no effect unless <link
+ linkend="opt.stats_interval"><mallctl>opt.stats_interval</mallctl></link> is
+ enabled. The default is <quote></quote>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="opt.junk">
+ <term>
+ <mallctl>opt.junk</mallctl>
+ (<type>const char *</type>)
+ <literal>r-</literal>
+ [<option>--enable-fill</option>]
+ </term>
+ <listitem><para>Junk filling. If set to <quote>alloc</quote>, each byte
+ of uninitialized allocated memory will be initialized to
+ <literal>0xa5</literal>. If set to <quote>free</quote>, all deallocated
+ memory will be initialized to <literal>0x5a</literal>. If set to
+ <quote>true</quote>, both allocated and deallocated memory will be
+ initialized, and if set to <quote>false</quote>, junk filling be
+ disabled entirely. This is intended for debugging and will impact
+ performance negatively. This option is <quote>false</quote> by default
+ unless <option>--enable-debug</option> is specified during
+ configuration, in which case it is <quote>true</quote> by
+ default.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="opt.zero">
+ <term>
+ <mallctl>opt.zero</mallctl>
+ (<type>bool</type>)
+ <literal>r-</literal>
+ [<option>--enable-fill</option>]
+ </term>
+ <listitem><para>Zero filling enabled/disabled. If enabled, each byte
+ of uninitialized allocated memory will be initialized to 0. Note that
+ this initialization only happens once for each byte, so
+ <function>realloc()</function> and
+ <function>rallocx()</function> calls do not zero memory that
+ was previously allocated. This is intended for debugging and will
+ impact performance negatively. This option is disabled by default.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry id="opt.utrace">
+ <term>
+ <mallctl>opt.utrace</mallctl>
+ (<type>bool</type>)
+ <literal>r-</literal>
+ [<option>--enable-utrace</option>]
+ </term>
+ <listitem><para>Allocation tracing based on
+ <citerefentry><refentrytitle>utrace</refentrytitle>
+ <manvolnum>2</manvolnum></citerefentry> enabled/disabled. This option
+ is disabled by default.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="opt.xmalloc">
+ <term>
+ <mallctl>opt.xmalloc</mallctl>
+ (<type>bool</type>)
+ <literal>r-</literal>
+ [<option>--enable-xmalloc</option>]
+ </term>
+ <listitem><para>Abort-on-out-of-memory enabled/disabled. If enabled,
+ rather than returning failure for any allocation function, display a
+ diagnostic message on <constant>STDERR_FILENO</constant> and cause the
+ program to drop core (using
+ <citerefentry><refentrytitle>abort</refentrytitle>
+ <manvolnum>3</manvolnum></citerefentry>). If an application is
+ designed to depend on this behavior, set the option at compile time by
+ including the following in the source code:
+ <programlisting language="C"><![CDATA[
+malloc_conf = "xmalloc:true";]]></programlisting>
+ This option is disabled by default.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="opt.tcache">
+ <term>
+ <mallctl>opt.tcache</mallctl>
+ (<type>bool</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para>Thread-specific caching (tcache) enabled/disabled. When
+ there are multiple threads, each thread uses a tcache for objects up to
+ a certain size. Thread-specific caching allows many allocations to be
+ satisfied without performing any thread synchronization, at the cost of
+ increased memory use. See the <link
+ linkend="opt.tcache_max"><mallctl>opt.tcache_max</mallctl></link>
+ option for related tuning information. This option is enabled by
+ default.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="opt.tcache_max">
+ <term>
+ <mallctl>opt.tcache_max</mallctl>
+ (<type>size_t</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para>Maximum size class to cache in the thread-specific cache
+ (tcache). At a minimum, the first size class is cached; and at a
+ maximum, size classes up to 8 MiB can be cached. The default maximum is
+ 32 KiB (2^15). As a convenience, this may also be set by specifying
+ lg_tcache_max, which will be taken to be the base-2 logarithm of the
+ setting of tcache_max.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="opt.thp">
+ <term>
+ <mallctl>opt.thp</mallctl>
+ (<type>const char *</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para>Transparent hugepage (THP) mode. Settings "always",
+ "never" and "default" are available if THP is supported by the operating
+ system. The "always" setting enables transparent hugepage for all user
+ memory mappings with
+ <parameter><constant>MADV_HUGEPAGE</constant></parameter>; "never"
+ ensures no transparent hugepage with
+ <parameter><constant>MADV_NOHUGEPAGE</constant></parameter>; the default
+ setting "default" makes no changes. Note that: this option does not
+ affect THP for jemalloc internal metadata (see <link
+ linkend="opt.metadata_thp"><mallctl>opt.metadata_thp</mallctl></link>);
+ in addition, for arenas with customized <link
+ linkend="arena.i.extent_hooks"><mallctl>extent_hooks</mallctl></link>,
+ this option is bypassed as it is implemented as part of the default
+ extent hooks.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="opt.prof">
+ <term>
+ <mallctl>opt.prof</mallctl>
+ (<type>bool</type>)
+ <literal>r-</literal>
+ [<option>--enable-prof</option>]
+ </term>
+ <listitem><para>Memory profiling enabled/disabled. If enabled, profile
+ memory allocation activity. See the <link
+ linkend="opt.prof_active"><mallctl>opt.prof_active</mallctl></link>
+ option for on-the-fly activation/deactivation. See the <link
+ linkend="opt.lg_prof_sample"><mallctl>opt.lg_prof_sample</mallctl></link>
+ option for probabilistic sampling control. See the <link
+ linkend="opt.prof_accum"><mallctl>opt.prof_accum</mallctl></link>
+ option for control of cumulative sample reporting. See the <link
+ linkend="opt.lg_prof_interval"><mallctl>opt.lg_prof_interval</mallctl></link>
+ option for information on interval-triggered profile dumping, the <link
+ linkend="opt.prof_gdump"><mallctl>opt.prof_gdump</mallctl></link>
+ option for information on high-water-triggered profile dumping, and the
+ <link linkend="opt.prof_final"><mallctl>opt.prof_final</mallctl></link>
+ option for final profile dumping. Profile output is compatible with
+ the <command>jeprof</command> command, which is based on the
+ <command>pprof</command> that is developed as part of the <ulink
+ url="http://code.google.com/p/gperftools/">gperftools
+ package</ulink>. See <link linkend="heap_profile_format">HEAP PROFILE
+ FORMAT</link> for heap profile format documentation.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="opt.prof_prefix">
+ <term>
+ <mallctl>opt.prof_prefix</mallctl>
+ (<type>const char *</type>)
+ <literal>r-</literal>
+ [<option>--enable-prof</option>]
+ </term>
+ <listitem><para>Filename prefix for profile dumps. If the prefix is
+ set to the empty string, no automatic dumps will occur; this is
+ primarily useful for disabling the automatic final heap dump (which
+ also disables leak reporting, if enabled). The default prefix is
+ <filename>jeprof</filename>. This prefix value can be overridden by
+ <link linkend="prof.prefix"><mallctl>prof.prefix</mallctl></link>.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry id="opt.prof_active">
+ <term>
+ <mallctl>opt.prof_active</mallctl>
+ (<type>bool</type>)
+ <literal>r-</literal>
+ [<option>--enable-prof</option>]
+ </term>
+ <listitem><para>Profiling activated/deactivated. This is a secondary
+ control mechanism that makes it possible to start the application with
+ profiling enabled (see the <link
+ linkend="opt.prof"><mallctl>opt.prof</mallctl></link> option) but
+ inactive, then toggle profiling at any time during program execution
+ with the <link
+ linkend="prof.active"><mallctl>prof.active</mallctl></link> mallctl.
+ This option is enabled by default.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="opt.prof_thread_active_init">
+ <term>
+ <mallctl>opt.prof_thread_active_init</mallctl>
+ (<type>bool</type>)
+ <literal>r-</literal>
+ [<option>--enable-prof</option>]
+ </term>
+ <listitem><para>Initial setting for <link
+ linkend="thread.prof.active"><mallctl>thread.prof.active</mallctl></link>
+ in newly created threads. The initial setting for newly created threads
+ can also be changed during execution via the <link
+ linkend="prof.thread_active_init"><mallctl>prof.thread_active_init</mallctl></link>
+ mallctl. This option is enabled by default.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="opt.lg_prof_sample">
+ <term>
+ <mallctl>opt.lg_prof_sample</mallctl>
+ (<type>size_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-prof</option>]
+ </term>
+ <listitem><para>Average interval (log base 2) between allocation
+ samples, as measured in bytes of allocation activity. Increasing the
+ sampling interval decreases profile fidelity, but also decreases the
+ computational overhead. The default sample interval is 512 KiB (2^19
+ B).</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="opt.prof_accum">
+ <term>
+ <mallctl>opt.prof_accum</mallctl>
+ (<type>bool</type>)
+ <literal>r-</literal>
+ [<option>--enable-prof</option>]
+ </term>
+ <listitem><para>Reporting of cumulative object/byte counts in profile
+ dumps enabled/disabled. If this option is enabled, every unique
+ backtrace must be stored for the duration of execution. Depending on
+ the application, this can impose a large memory overhead, and the
+ cumulative counts are not always of interest. This option is disabled
+ by default.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="opt.lg_prof_interval">
+ <term>
+ <mallctl>opt.lg_prof_interval</mallctl>
+ (<type>ssize_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-prof</option>]
+ </term>
+ <listitem><para>Average interval (log base 2) between memory profile
+ dumps, as measured in bytes of allocation activity. The actual
+ interval between dumps may be sporadic because decentralized allocation
+ counters are used to avoid synchronization bottlenecks. Profiles are
+ dumped to files named according to the pattern
+ <filename>&lt;prefix&gt;.&lt;pid&gt;.&lt;seq&gt;.i&lt;iseq&gt;.heap</filename>,
+ where <literal>&lt;prefix&gt;</literal> is controlled by the
+ <link
+ linkend="opt.prof_prefix"><mallctl>opt.prof_prefix</mallctl></link> and
+ <link linkend="prof.prefix"><mallctl>prof.prefix</mallctl></link>
+ options. By default, interval-triggered profile dumping is disabled
+ (encoded as -1).
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry id="opt.prof_gdump">
+ <term>
+ <mallctl>opt.prof_gdump</mallctl>
+ (<type>bool</type>)
+ <literal>r-</literal>
+ [<option>--enable-prof</option>]
+ </term>
+ <listitem><para>Set the initial state of <link
+ linkend="prof.gdump"><mallctl>prof.gdump</mallctl></link>, which when
+ enabled triggers a memory profile dump every time the total virtual
+ memory exceeds the previous maximum. This option is disabled by
+ default.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="opt.prof_final">
+ <term>
+ <mallctl>opt.prof_final</mallctl>
+ (<type>bool</type>)
+ <literal>r-</literal>
+ [<option>--enable-prof</option>]
+ </term>
+ <listitem><para>Use an
+ <citerefentry><refentrytitle>atexit</refentrytitle>
+ <manvolnum>3</manvolnum></citerefentry> function to dump final memory
+ usage to a file named according to the pattern
+ <filename>&lt;prefix&gt;.&lt;pid&gt;.&lt;seq&gt;.f.heap</filename>,
+ where <literal>&lt;prefix&gt;</literal> is controlled by the <link
+ linkend="opt.prof_prefix"><mallctl>opt.prof_prefix</mallctl></link> and
+ <link linkend="prof.prefix"><mallctl>prof.prefix</mallctl></link>
+ options. Note that <function>atexit()</function> may allocate
+ memory during application initialization and then deadlock internally
+ when jemalloc in turn calls <function>atexit()</function>, so
+ this option is not universally usable (though the application can
+ register its own <function>atexit()</function> function with
+ equivalent functionality). This option is disabled by
+ default.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="opt.prof_leak">
+ <term>
+ <mallctl>opt.prof_leak</mallctl>
+ (<type>bool</type>)
+ <literal>r-</literal>
+ [<option>--enable-prof</option>]
+ </term>
+ <listitem><para>Leak reporting enabled/disabled. If enabled, use an
+ <citerefentry><refentrytitle>atexit</refentrytitle>
+ <manvolnum>3</manvolnum></citerefentry> function to report memory leaks
+ detected by allocation sampling. See the
+ <link linkend="opt.prof"><mallctl>opt.prof</mallctl></link> option for
+ information on analyzing heap profile output. Works only when combined
+ with <link linkend="opt.prof_final"><mallctl>opt.prof_final</mallctl>
+ </link>, otherwise does nothing. This option is disabled by default.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry id="opt.prof_leak_error">
+ <term>
+ <mallctl>opt.prof_leak_error</mallctl>
+ (<type>bool</type>)
+ <literal>r-</literal>
+ [<option>--enable-prof</option>]
+ </term>
+ <listitem><para>Similar to <link linkend="opt.prof_leak"><mallctl>
+ opt.prof_leak</mallctl></link>, but makes the process exit with error
+ code 1 if a memory leak is detected. This option supersedes
+ <link linkend="opt.prof_leak"><mallctl>opt.prof_leak</mallctl></link>,
+ meaning that if both are specified, this option takes precedence. When
+ enabled, also enables <link linkend="opt.prof_leak"><mallctl>
+ opt.prof_leak</mallctl></link>. Works only when combined with
+ <link linkend="opt.prof_final"><mallctl>opt.prof_final</mallctl></link>,
+ otherwise does nothing. This option is disabled by default.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry id="opt.zero_realloc">
+ <term>
+ <mallctl>opt.zero_realloc</mallctl>
+ (<type>const char *</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para> Determines the behavior of
+ <function>realloc()</function> when passed a value of zero for the new
+ size. <quote>alloc</quote> treats this as an allocation of size zero
+ (and returns a non-null result except in case of resource exhaustion).
+ <quote>free</quote> treats this as a deallocation of the pointer, and
+ returns <constant>NULL</constant> without setting
+ <varname>errno</varname>. <quote>abort</quote> aborts the process if
+ zero is passed. The default is <quote>free</quote> on Linux and
+ Windows, and <quote>alloc</quote> elsewhere.</para>
+
+ <para>There is considerable divergence of behaviors across
+ implementations in handling this case. Many have the behavior of
+ <quote>free</quote>. This can introduce security vulnerabilities, since
+ a <constant>NULL</constant> return value indicates failure, and the
+ continued validity of the passed-in pointer (per POSIX and C11).
+ <quote>alloc</quote> is safe, but can cause leaks in programs that
+ expect the common behavior. Programs intended to be portable and
+ leak-free cannot assume either behavior, and must therefore never call
+ realloc with a size of 0. The <quote>abort</quote> option enables these
+ testing this behavior.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="thread.arena">
+ <term>
+ <mallctl>thread.arena</mallctl>
+ (<type>unsigned</type>)
+ <literal>rw</literal>
+ </term>
+ <listitem><para>Get or set the arena associated with the calling
+ thread. If the specified arena was not initialized beforehand (see the
+ <link
+ linkend="arena.i.initialized"><mallctl>arena.i.initialized</mallctl></link>
+ mallctl), it will be automatically initialized as a side effect of
+ calling this interface.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="thread.allocated">
+ <term>
+ <mallctl>thread.allocated</mallctl>
+ (<type>uint64_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Get the total number of bytes ever allocated by the
+ calling thread. This counter has the potential to wrap around; it is
+ up to the application to appropriately interpret the counter in such
+ cases.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="thread.allocatedp">
+ <term>
+ <mallctl>thread.allocatedp</mallctl>
+ (<type>uint64_t *</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Get a pointer to the the value that is returned by the
+ <link
+ linkend="thread.allocated"><mallctl>thread.allocated</mallctl></link>
+ mallctl. This is useful for avoiding the overhead of repeated
+ <function>mallctl*()</function> calls. Note that the underlying counter
+ should not be modified by the application.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="thread.deallocated">
+ <term>
+ <mallctl>thread.deallocated</mallctl>
+ (<type>uint64_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Get the total number of bytes ever deallocated by the
+ calling thread. This counter has the potential to wrap around; it is
+ up to the application to appropriately interpret the counter in such
+ cases.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="thread.deallocatedp">
+ <term>
+ <mallctl>thread.deallocatedp</mallctl>
+ (<type>uint64_t *</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Get a pointer to the the value that is returned by the
+ <link
+ linkend="thread.deallocated"><mallctl>thread.deallocated</mallctl></link>
+ mallctl. This is useful for avoiding the overhead of repeated
+ <function>mallctl*()</function> calls. Note that the underlying counter
+ should not be modified by the application.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="thread.peak.read">
+ <term>
+ <mallctl>thread.peak.read</mallctl>
+ (<type>uint64_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Get an approximation of the maximum value of the
+ difference between the number of bytes allocated and the number of bytes
+ deallocated by the calling thread since the last call to <link
+ linkend="thread.peak.reset"><mallctl>thread.peak.reset</mallctl></link>,
+ or since the thread's creation if it has not called <link
+ linkend="thread.peak.reset"><mallctl>thread.peak.reset</mallctl></link>.
+ No guarantees are made about the quality of the approximation, but
+ jemalloc currently endeavors to maintain accuracy to within one hundred
+ kilobytes.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry id="thread.peak.reset">
+ <term>
+ <mallctl>thread.peak.reset</mallctl>
+ (<type>void</type>)
+ <literal>--</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Resets the counter for net bytes allocated in the calling
+ thread to zero. This affects subsequent calls to <link
+ linkend="thread.peak.read"><mallctl>thread.peak.read</mallctl></link>,
+ but not the values returned by <link
+ linkend="thread.allocated"><mallctl>thread.allocated</mallctl></link>
+ or <link
+ linkend="thread.deallocated"><mallctl>thread.deallocated</mallctl></link>.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry id="thread.tcache.enabled">
+ <term>
+ <mallctl>thread.tcache.enabled</mallctl>
+ (<type>bool</type>)
+ <literal>rw</literal>
+ </term>
+ <listitem><para>Enable/disable calling thread's tcache. The tcache is
+ implicitly flushed as a side effect of becoming
+ disabled (see <link
+ linkend="thread.tcache.flush"><mallctl>thread.tcache.flush</mallctl></link>).
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry id="thread.tcache.flush">
+ <term>
+ <mallctl>thread.tcache.flush</mallctl>
+ (<type>void</type>)
+ <literal>--</literal>
+ </term>
+ <listitem><para>Flush calling thread's thread-specific cache (tcache).
+ This interface releases all cached objects and internal data structures
+ associated with the calling thread's tcache. Ordinarily, this interface
+ need not be called, since automatic periodic incremental garbage
+ collection occurs, and the thread cache is automatically discarded when
+ a thread exits. However, garbage collection is triggered by allocation
+ activity, so it is possible for a thread that stops
+ allocating/deallocating to retain its cache indefinitely, in which case
+ the developer may find manual flushing useful.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="thread.prof.name">
+ <term>
+ <mallctl>thread.prof.name</mallctl>
+ (<type>const char *</type>)
+ <literal>r-</literal> or
+ <literal>-w</literal>
+ [<option>--enable-prof</option>]
+ </term>
+ <listitem><para>Get/set the descriptive name associated with the calling
+ thread in memory profile dumps. An internal copy of the name string is
+ created, so the input string need not be maintained after this interface
+ completes execution. The output string of this interface should be
+ copied for non-ephemeral uses, because multiple implementation details
+ can cause asynchronous string deallocation. Furthermore, each
+ invocation of this interface can only read or write; simultaneous
+ read/write is not supported due to string lifetime limitations. The
+ name string must be nil-terminated and comprised only of characters in
+ the sets recognized
+ by <citerefentry><refentrytitle>isgraph</refentrytitle>
+ <manvolnum>3</manvolnum></citerefentry> and
+ <citerefentry><refentrytitle>isblank</refentrytitle>
+ <manvolnum>3</manvolnum></citerefentry>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="thread.prof.active">
+ <term>
+ <mallctl>thread.prof.active</mallctl>
+ (<type>bool</type>)
+ <literal>rw</literal>
+ [<option>--enable-prof</option>]
+ </term>
+ <listitem><para>Control whether sampling is currently active for the
+ calling thread. This is an activation mechanism in addition to <link
+ linkend="prof.active"><mallctl>prof.active</mallctl></link>; both must
+ be active for the calling thread to sample. This flag is enabled by
+ default.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="thread.idle">
+ <term>
+ <mallctl>thread.idle</mallctl>
+ (<type>void</type>)
+ <literal>--</literal>
+ </term>
+ <listitem><para>Hints to jemalloc that the calling thread will be idle
+ for some nontrivial period of time (say, on the order of seconds), and
+ that doing some cleanup operations may be beneficial. There are no
+ guarantees as to what specific operations will be performed; currently
+ this flushes the caller's tcache and may (according to some heuristic)
+ purge its associated arena.</para>
+ <para>This is not intended to be a general-purpose background activity
+ mechanism, and threads should not wake up multiple times solely to call
+ it. Rather, a thread waiting for a task should do a timed wait first,
+ call <link linkend="thread.idle"><mallctl>thread.idle</mallctl></link>
+ if no task appears in the timeout interval, and then do an untimed wait.
+ For such a background activity mechanism, see
+ <link linkend="background_thread"><mallctl>background_thread</mallctl></link>.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry id="tcache.create">
+ <term>
+ <mallctl>tcache.create</mallctl>
+ (<type>unsigned</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para>Create an explicit thread-specific cache (tcache) and
+ return an identifier that can be passed to the <link
+ linkend="MALLOCX_TCACHE"><constant>MALLOCX_TCACHE(<parameter>tc</parameter>)</constant></link>
+ macro to explicitly use the specified cache rather than the
+ automatically managed one that is used by default. Each explicit cache
+ can be used by only one thread at a time; the application must assure
+ that this constraint holds.
+ </para>
+
+ <para>If the amount of space supplied for storing the thread-specific
+ cache identifier does not equal
+ <code language="C">sizeof(<type>unsigned</type>)</code>, no
+ thread-specific cache will be created, no data will be written to the
+ space pointed by <parameter>oldp</parameter>, and
+ <parameter>*oldlenp</parameter> will be set to 0.
+ </para></listitem>
+
+ </varlistentry>
+
+ <varlistentry id="tcache.flush">
+ <term>
+ <mallctl>tcache.flush</mallctl>
+ (<type>unsigned</type>)
+ <literal>-w</literal>
+ </term>
+ <listitem><para>Flush the specified thread-specific cache (tcache). The
+ same considerations apply to this interface as to <link
+ linkend="thread.tcache.flush"><mallctl>thread.tcache.flush</mallctl></link>,
+ except that the tcache will never be automatically discarded.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry id="tcache.destroy">
+ <term>
+ <mallctl>tcache.destroy</mallctl>
+ (<type>unsigned</type>)
+ <literal>-w</literal>
+ </term>
+ <listitem><para>Flush the specified thread-specific cache (tcache) and
+ make the identifier available for use during a future tcache creation.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry id="arena.i.initialized">
+ <term>
+ <mallctl>arena.&lt;i&gt;.initialized</mallctl>
+ (<type>bool</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para>Get whether the specified arena's statistics are
+ initialized (i.e. the arena was initialized prior to the current epoch).
+ This interface can also be nominally used to query whether the merged
+ statistics corresponding to <constant>MALLCTL_ARENAS_ALL</constant> are
+ initialized (always true).</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="arena.i.decay">
+ <term>
+ <mallctl>arena.&lt;i&gt;.decay</mallctl>
+ (<type>void</type>)
+ <literal>--</literal>
+ </term>
+ <listitem><para>Trigger decay-based purging of unused dirty/muzzy pages
+ for arena &lt;i&gt;, or for all arenas if &lt;i&gt; equals
+ <constant>MALLCTL_ARENAS_ALL</constant>. The proportion of unused
+ dirty/muzzy pages to be purged depends on the current time; see <link
+ linkend="opt.dirty_decay_ms"><mallctl>opt.dirty_decay_ms</mallctl></link>
+ and <link
+ linkend="opt.muzzy_decay_ms"><mallctl>opt.muzy_decay_ms</mallctl></link>
+ for details.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="arena.i.purge">
+ <term>
+ <mallctl>arena.&lt;i&gt;.purge</mallctl>
+ (<type>void</type>)
+ <literal>--</literal>
+ </term>
+ <listitem><para>Purge all unused dirty pages for arena &lt;i&gt;, or for
+ all arenas if &lt;i&gt; equals <constant>MALLCTL_ARENAS_ALL</constant>.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry id="arena.i.reset">
+ <term>
+ <mallctl>arena.&lt;i&gt;.reset</mallctl>
+ (<type>void</type>)
+ <literal>--</literal>
+ </term>
+ <listitem><para>Discard all of the arena's extant allocations. This
+ interface can only be used with arenas explicitly created via <link
+ linkend="arenas.create"><mallctl>arenas.create</mallctl></link>. None
+ of the arena's discarded/cached allocations may accessed afterward. As
+ part of this requirement, all thread caches which were used to
+ allocate/deallocate in conjunction with the arena must be flushed
+ beforehand.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="arena.i.destroy">
+ <term>
+ <mallctl>arena.&lt;i&gt;.destroy</mallctl>
+ (<type>void</type>)
+ <literal>--</literal>
+ </term>
+ <listitem><para>Destroy the arena. Discard all of the arena's extant
+ allocations using the same mechanism as for <link
+ linkend="arena.i.reset"><mallctl>arena.&lt;i&gt;.reset</mallctl></link>
+ (with all the same constraints and side effects), merge the arena stats
+ into those accessible at arena index
+ <constant>MALLCTL_ARENAS_DESTROYED</constant>, and then completely
+ discard all metadata associated with the arena. Future calls to <link
+ linkend="arenas.create"><mallctl>arenas.create</mallctl></link> may
+ recycle the arena index. Destruction will fail if any threads are
+ currently associated with the arena as a result of calls to <link
+ linkend="thread.arena"><mallctl>thread.arena</mallctl></link>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="arena.i.dss">
+ <term>
+ <mallctl>arena.&lt;i&gt;.dss</mallctl>
+ (<type>const char *</type>)
+ <literal>rw</literal>
+ </term>
+ <listitem><para>Set the precedence of dss allocation as related to mmap
+ allocation for arena &lt;i&gt;, or for all arenas if &lt;i&gt; equals
+ <constant>MALLCTL_ARENAS_ALL</constant>. See <link
+ linkend="opt.dss"><mallctl>opt.dss</mallctl></link> for supported
+ settings.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="arena.i.dirty_decay_ms">
+ <term>
+ <mallctl>arena.&lt;i&gt;.dirty_decay_ms</mallctl>
+ (<type>ssize_t</type>)
+ <literal>rw</literal>
+ </term>
+ <listitem><para>Current per-arena approximate time in milliseconds from
+ the creation of a set of unused dirty pages until an equivalent set of
+ unused dirty pages is purged and/or reused. Each time this interface is
+ set, all currently unused dirty pages are considered to have fully
+ decayed, which causes immediate purging of all unused dirty pages unless
+ the decay time is set to -1 (i.e. purging disabled). See <link
+ linkend="opt.dirty_decay_ms"><mallctl>opt.dirty_decay_ms</mallctl></link>
+ for additional information.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="arena.i.muzzy_decay_ms">
+ <term>
+ <mallctl>arena.&lt;i&gt;.muzzy_decay_ms</mallctl>
+ (<type>ssize_t</type>)
+ <literal>rw</literal>
+ </term>
+ <listitem><para>Current per-arena approximate time in milliseconds from
+ the creation of a set of unused muzzy pages until an equivalent set of
+ unused muzzy pages is purged and/or reused. Each time this interface is
+ set, all currently unused muzzy pages are considered to have fully
+ decayed, which causes immediate purging of all unused muzzy pages unless
+ the decay time is set to -1 (i.e. purging disabled). See <link
+ linkend="opt.muzzy_decay_ms"><mallctl>opt.muzzy_decay_ms</mallctl></link>
+ for additional information.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="arena.i.retain_grow_limit">
+ <term>
+ <mallctl>arena.&lt;i&gt;.retain_grow_limit</mallctl>
+ (<type>size_t</type>)
+ <literal>rw</literal>
+ </term>
+ <listitem><para>Maximum size to grow retained region (only relevant when
+ <link linkend="opt.retain"><mallctl>opt.retain</mallctl></link> is
+ enabled). This controls the maximum increment to expand virtual memory,
+ or allocation through <link
+ linkend="arena.i.extent_hooks"><mallctl>arena.&lt;i&gt;extent_hooks</mallctl></link>.
+ In particular, if customized extent hooks reserve physical memory
+ (e.g. 1G huge pages), this is useful to control the allocation hook's
+ input size. The default is no limit.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="arena.i.extent_hooks">
+ <term>
+ <mallctl>arena.&lt;i&gt;.extent_hooks</mallctl>
+ (<type>extent_hooks_t *</type>)
+ <literal>rw</literal>
+ </term>
+ <listitem><para>Get or set the extent management hook functions for
+ arena &lt;i&gt;. The functions must be capable of operating on all
+ extant extents associated with arena &lt;i&gt;, usually by passing
+ unknown extents to the replaced functions. In practice, it is feasible
+ to control allocation for arenas explicitly created via <link
+ linkend="arenas.create"><mallctl>arenas.create</mallctl></link> such
+ that all extents originate from an application-supplied extent allocator
+ (by specifying the custom extent hook functions during arena creation).
+ However, the API guarantees for the automatically created arenas may be
+ relaxed -- hooks set there may be called in a "best effort" fashion; in
+ addition there may be extents created prior to the application having an
+ opportunity to take over extent allocation.</para>
+
+ <programlisting language="C"><![CDATA[
+typedef extent_hooks_s extent_hooks_t;
+struct extent_hooks_s {
+ extent_alloc_t *alloc;
+ extent_dalloc_t *dalloc;
+ extent_destroy_t *destroy;
+ extent_commit_t *commit;
+ extent_decommit_t *decommit;
+ extent_purge_t *purge_lazy;
+ extent_purge_t *purge_forced;
+ extent_split_t *split;
+ extent_merge_t *merge;
+};]]></programlisting>
+ <para>The <type>extent_hooks_t</type> structure comprises function
+ pointers which are described individually below. jemalloc uses these
+ functions to manage extent lifetime, which starts off with allocation of
+ mapped committed memory, in the simplest case followed by deallocation.
+ However, there are performance and platform reasons to retain extents
+ for later reuse. Cleanup attempts cascade from deallocation to decommit
+ to forced purging to lazy purging, which gives the extent management
+ functions opportunities to reject the most permanent cleanup operations
+ in favor of less permanent (and often less costly) operations. All
+ operations except allocation can be universally opted out of by setting
+ the hook pointers to <constant>NULL</constant>, or selectively opted out
+ of by returning failure. Note that once the extent hook is set, the
+ structure is accessed directly by the associated arenas, so it must
+ remain valid for the entire lifetime of the arenas.</para>
+
+ <funcsynopsis><funcprototype>
+ <funcdef>typedef void *<function>(extent_alloc_t)</function></funcdef>
+ <paramdef>extent_hooks_t *<parameter>extent_hooks</parameter></paramdef>
+ <paramdef>void *<parameter>new_addr</parameter></paramdef>
+ <paramdef>size_t <parameter>size</parameter></paramdef>
+ <paramdef>size_t <parameter>alignment</parameter></paramdef>
+ <paramdef>bool *<parameter>zero</parameter></paramdef>
+ <paramdef>bool *<parameter>commit</parameter></paramdef>
+ <paramdef>unsigned <parameter>arena_ind</parameter></paramdef>
+ </funcprototype></funcsynopsis>
+ <literallayout></literallayout>
+ <para>An extent allocation function conforms to the
+ <type>extent_alloc_t</type> type and upon success returns a pointer to
+ <parameter>size</parameter> bytes of mapped memory on behalf of arena
+ <parameter>arena_ind</parameter> such that the extent's base address is
+ a multiple of <parameter>alignment</parameter>, as well as setting
+ <parameter>*zero</parameter> to indicate whether the extent is zeroed
+ and <parameter>*commit</parameter> to indicate whether the extent is
+ committed. Upon error the function returns <constant>NULL</constant>
+ and leaves <parameter>*zero</parameter> and
+ <parameter>*commit</parameter> unmodified. The
+ <parameter>size</parameter> parameter is always a multiple of the page
+ size. The <parameter>alignment</parameter> parameter is always a power
+ of two at least as large as the page size. Zeroing is mandatory if
+ <parameter>*zero</parameter> is true upon function entry. Committing is
+ mandatory if <parameter>*commit</parameter> is true upon function entry.
+ If <parameter>new_addr</parameter> is not <constant>NULL</constant>, the
+ returned pointer must be <parameter>new_addr</parameter> on success or
+ <constant>NULL</constant> on error. Committed memory may be committed
+ in absolute terms as on a system that does not overcommit, or in
+ implicit terms as on a system that overcommits and satisfies physical
+ memory needs on demand via soft page faults. Note that replacing the
+ default extent allocation function makes the arena's <link
+ linkend="arena.i.dss"><mallctl>arena.&lt;i&gt;.dss</mallctl></link>
+ setting irrelevant.</para>
+
+ <funcsynopsis><funcprototype>
+ <funcdef>typedef bool <function>(extent_dalloc_t)</function></funcdef>
+ <paramdef>extent_hooks_t *<parameter>extent_hooks</parameter></paramdef>
+ <paramdef>void *<parameter>addr</parameter></paramdef>
+ <paramdef>size_t <parameter>size</parameter></paramdef>
+ <paramdef>bool <parameter>committed</parameter></paramdef>
+ <paramdef>unsigned <parameter>arena_ind</parameter></paramdef>
+ </funcprototype></funcsynopsis>
+ <literallayout></literallayout>
+ <para>
+ An extent deallocation function conforms to the
+ <type>extent_dalloc_t</type> type and deallocates an extent at given
+ <parameter>addr</parameter> and <parameter>size</parameter> with
+ <parameter>committed</parameter>/decommited memory as indicated, on
+ behalf of arena <parameter>arena_ind</parameter>, returning false upon
+ success. If the function returns true, this indicates opt-out from
+ deallocation; the virtual memory mapping associated with the extent
+ remains mapped, in the same commit state, and available for future use,
+ in which case it will be automatically retained for later reuse.</para>
+
+ <funcsynopsis><funcprototype>
+ <funcdef>typedef void <function>(extent_destroy_t)</function></funcdef>
+ <paramdef>extent_hooks_t *<parameter>extent_hooks</parameter></paramdef>
+ <paramdef>void *<parameter>addr</parameter></paramdef>
+ <paramdef>size_t <parameter>size</parameter></paramdef>
+ <paramdef>bool <parameter>committed</parameter></paramdef>
+ <paramdef>unsigned <parameter>arena_ind</parameter></paramdef>
+ </funcprototype></funcsynopsis>
+ <literallayout></literallayout>
+ <para>
+ An extent destruction function conforms to the
+ <type>extent_destroy_t</type> type and unconditionally destroys an
+ extent at given <parameter>addr</parameter> and
+ <parameter>size</parameter> with
+ <parameter>committed</parameter>/decommited memory as indicated, on
+ behalf of arena <parameter>arena_ind</parameter>. This function may be
+ called to destroy retained extents during arena destruction (see <link
+ linkend="arena.i.destroy"><mallctl>arena.&lt;i&gt;.destroy</mallctl></link>).</para>
+
+ <funcsynopsis><funcprototype>
+ <funcdef>typedef bool <function>(extent_commit_t)</function></funcdef>
+ <paramdef>extent_hooks_t *<parameter>extent_hooks</parameter></paramdef>
+ <paramdef>void *<parameter>addr</parameter></paramdef>
+ <paramdef>size_t <parameter>size</parameter></paramdef>
+ <paramdef>size_t <parameter>offset</parameter></paramdef>
+ <paramdef>size_t <parameter>length</parameter></paramdef>
+ <paramdef>unsigned <parameter>arena_ind</parameter></paramdef>
+ </funcprototype></funcsynopsis>
+ <literallayout></literallayout>
+ <para>An extent commit function conforms to the
+ <type>extent_commit_t</type> type and commits zeroed physical memory to
+ back pages within an extent at given <parameter>addr</parameter> and
+ <parameter>size</parameter> at <parameter>offset</parameter> bytes,
+ extending for <parameter>length</parameter> on behalf of arena
+ <parameter>arena_ind</parameter>, returning false upon success.
+ Committed memory may be committed in absolute terms as on a system that
+ does not overcommit, or in implicit terms as on a system that
+ overcommits and satisfies physical memory needs on demand via soft page
+ faults. If the function returns true, this indicates insufficient
+ physical memory to satisfy the request.</para>
+
+ <funcsynopsis><funcprototype>
+ <funcdef>typedef bool <function>(extent_decommit_t)</function></funcdef>
+ <paramdef>extent_hooks_t *<parameter>extent_hooks</parameter></paramdef>
+ <paramdef>void *<parameter>addr</parameter></paramdef>
+ <paramdef>size_t <parameter>size</parameter></paramdef>
+ <paramdef>size_t <parameter>offset</parameter></paramdef>
+ <paramdef>size_t <parameter>length</parameter></paramdef>
+ <paramdef>unsigned <parameter>arena_ind</parameter></paramdef>
+ </funcprototype></funcsynopsis>
+ <literallayout></literallayout>
+ <para>An extent decommit function conforms to the
+ <type>extent_decommit_t</type> type and decommits any physical memory
+ that is backing pages within an extent at given
+ <parameter>addr</parameter> and <parameter>size</parameter> at
+ <parameter>offset</parameter> bytes, extending for
+ <parameter>length</parameter> on behalf of arena
+ <parameter>arena_ind</parameter>, returning false upon success, in which
+ case the pages will be committed via the extent commit function before
+ being reused. If the function returns true, this indicates opt-out from
+ decommit; the memory remains committed and available for future use, in
+ which case it will be automatically retained for later reuse.</para>
+
+ <funcsynopsis><funcprototype>
+ <funcdef>typedef bool <function>(extent_purge_t)</function></funcdef>
+ <paramdef>extent_hooks_t *<parameter>extent_hooks</parameter></paramdef>
+ <paramdef>void *<parameter>addr</parameter></paramdef>
+ <paramdef>size_t <parameter>size</parameter></paramdef>
+ <paramdef>size_t <parameter>offset</parameter></paramdef>
+ <paramdef>size_t <parameter>length</parameter></paramdef>
+ <paramdef>unsigned <parameter>arena_ind</parameter></paramdef>
+ </funcprototype></funcsynopsis>
+ <literallayout></literallayout>
+ <para>An extent purge function conforms to the
+ <type>extent_purge_t</type> type and discards physical pages
+ within the virtual memory mapping associated with an extent at given
+ <parameter>addr</parameter> and <parameter>size</parameter> at
+ <parameter>offset</parameter> bytes, extending for
+ <parameter>length</parameter> on behalf of arena
+ <parameter>arena_ind</parameter>. A lazy extent purge function (e.g.
+ implemented via
+ <function>madvise(<parameter>...</parameter><parameter><constant>MADV_FREE</constant></parameter>)</function>)
+ can delay purging indefinitely and leave the pages within the purged
+ virtual memory range in an indeterminite state, whereas a forced extent
+ purge function immediately purges, and the pages within the virtual
+ memory range will be zero-filled the next time they are accessed. If
+ the function returns true, this indicates failure to purge.</para>
+
+ <funcsynopsis><funcprototype>
+ <funcdef>typedef bool <function>(extent_split_t)</function></funcdef>
+ <paramdef>extent_hooks_t *<parameter>extent_hooks</parameter></paramdef>
+ <paramdef>void *<parameter>addr</parameter></paramdef>
+ <paramdef>size_t <parameter>size</parameter></paramdef>
+ <paramdef>size_t <parameter>size_a</parameter></paramdef>
+ <paramdef>size_t <parameter>size_b</parameter></paramdef>
+ <paramdef>bool <parameter>committed</parameter></paramdef>
+ <paramdef>unsigned <parameter>arena_ind</parameter></paramdef>
+ </funcprototype></funcsynopsis>
+ <literallayout></literallayout>
+ <para>An extent split function conforms to the
+ <type>extent_split_t</type> type and optionally splits an extent at
+ given <parameter>addr</parameter> and <parameter>size</parameter> into
+ two adjacent extents, the first of <parameter>size_a</parameter> bytes,
+ and the second of <parameter>size_b</parameter> bytes, operating on
+ <parameter>committed</parameter>/decommitted memory as indicated, on
+ behalf of arena <parameter>arena_ind</parameter>, returning false upon
+ success. If the function returns true, this indicates that the extent
+ remains unsplit and therefore should continue to be operated on as a
+ whole.</para>
+
+ <funcsynopsis><funcprototype>
+ <funcdef>typedef bool <function>(extent_merge_t)</function></funcdef>
+ <paramdef>extent_hooks_t *<parameter>extent_hooks</parameter></paramdef>
+ <paramdef>void *<parameter>addr_a</parameter></paramdef>
+ <paramdef>size_t <parameter>size_a</parameter></paramdef>
+ <paramdef>void *<parameter>addr_b</parameter></paramdef>
+ <paramdef>size_t <parameter>size_b</parameter></paramdef>
+ <paramdef>bool <parameter>committed</parameter></paramdef>
+ <paramdef>unsigned <parameter>arena_ind</parameter></paramdef>
+ </funcprototype></funcsynopsis>
+ <literallayout></literallayout>
+ <para>An extent merge function conforms to the
+ <type>extent_merge_t</type> type and optionally merges adjacent extents,
+ at given <parameter>addr_a</parameter> and <parameter>size_a</parameter>
+ with given <parameter>addr_b</parameter> and
+ <parameter>size_b</parameter> into one contiguous extent, operating on
+ <parameter>committed</parameter>/decommitted memory as indicated, on
+ behalf of arena <parameter>arena_ind</parameter>, returning false upon
+ success. If the function returns true, this indicates that the extents
+ remain distinct mappings and therefore should continue to be operated on
+ independently.</para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="arenas.narenas">
+ <term>
+ <mallctl>arenas.narenas</mallctl>
+ (<type>unsigned</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para>Current limit on number of arenas.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="arenas.dirty_decay_ms">
+ <term>
+ <mallctl>arenas.dirty_decay_ms</mallctl>
+ (<type>ssize_t</type>)
+ <literal>rw</literal>
+ </term>
+ <listitem><para>Current default per-arena approximate time in
+ milliseconds from the creation of a set of unused dirty pages until an
+ equivalent set of unused dirty pages is purged and/or reused, used to
+ initialize <link
+ linkend="arena.i.dirty_decay_ms"><mallctl>arena.&lt;i&gt;.dirty_decay_ms</mallctl></link>
+ during arena creation. See <link
+ linkend="opt.dirty_decay_ms"><mallctl>opt.dirty_decay_ms</mallctl></link>
+ for additional information.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="arenas.muzzy_decay_ms">
+ <term>
+ <mallctl>arenas.muzzy_decay_ms</mallctl>
+ (<type>ssize_t</type>)
+ <literal>rw</literal>
+ </term>
+ <listitem><para>Current default per-arena approximate time in
+ milliseconds from the creation of a set of unused muzzy pages until an
+ equivalent set of unused muzzy pages is purged and/or reused, used to
+ initialize <link
+ linkend="arena.i.muzzy_decay_ms"><mallctl>arena.&lt;i&gt;.muzzy_decay_ms</mallctl></link>
+ during arena creation. See <link
+ linkend="opt.muzzy_decay_ms"><mallctl>opt.muzzy_decay_ms</mallctl></link>
+ for additional information.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="arenas.quantum">
+ <term>
+ <mallctl>arenas.quantum</mallctl>
+ (<type>size_t</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para>Quantum size.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="arenas.page">
+ <term>
+ <mallctl>arenas.page</mallctl>
+ (<type>size_t</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para>Page size.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="arenas.tcache_max">
+ <term>
+ <mallctl>arenas.tcache_max</mallctl>
+ (<type>size_t</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para>Maximum thread-cached size class.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="arenas.nbins">
+ <term>
+ <mallctl>arenas.nbins</mallctl>
+ (<type>unsigned</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para>Number of bin size classes.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="arenas.nhbins">
+ <term>
+ <mallctl>arenas.nhbins</mallctl>
+ (<type>unsigned</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para>Total number of thread cache bin size
+ classes.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="arenas.bin.i.size">
+ <term>
+ <mallctl>arenas.bin.&lt;i&gt;.size</mallctl>
+ (<type>size_t</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para>Maximum size supported by size class.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="arenas.bin.i.nregs">
+ <term>
+ <mallctl>arenas.bin.&lt;i&gt;.nregs</mallctl>
+ (<type>uint32_t</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para>Number of regions per slab.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="arenas.bin.i.slab_size">
+ <term>
+ <mallctl>arenas.bin.&lt;i&gt;.slab_size</mallctl>
+ (<type>size_t</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para>Number of bytes per slab.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="arenas.nlextents">
+ <term>
+ <mallctl>arenas.nlextents</mallctl>
+ (<type>unsigned</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para>Total number of large size classes.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="arenas.lextent.i.size">
+ <term>
+ <mallctl>arenas.lextent.&lt;i&gt;.size</mallctl>
+ (<type>size_t</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para>Maximum size supported by this large size
+ class.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="arenas.create">
+ <term>
+ <mallctl>arenas.create</mallctl>
+ (<type>unsigned</type>, <type>extent_hooks_t *</type>)
+ <literal>rw</literal>
+ </term>
+ <listitem><para>Explicitly create a new arena outside the range of
+ automatically managed arenas, with optionally specified extent hooks,
+ and return the new arena index.</para>
+
+ <para>If the amount of space supplied for storing the arena index does
+ not equal <code language="C">sizeof(<type>unsigned</type>)</code>, no
+ arena will be created, no data will be written to the space pointed by
+ <parameter>oldp</parameter>, and <parameter>*oldlenp</parameter> will
+ be set to 0.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry id="arenas.lookup">
+ <term>
+ <mallctl>arenas.lookup</mallctl>
+ (<type>unsigned</type>, <type>void*</type>)
+ <literal>rw</literal>
+ </term>
+ <listitem><para>Index of the arena to which an allocation belongs to.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="prof.thread_active_init">
+ <term>
+ <mallctl>prof.thread_active_init</mallctl>
+ (<type>bool</type>)
+ <literal>rw</literal>
+ [<option>--enable-prof</option>]
+ </term>
+ <listitem><para>Control the initial setting for <link
+ linkend="thread.prof.active"><mallctl>thread.prof.active</mallctl></link>
+ in newly created threads. See the <link
+ linkend="opt.prof_thread_active_init"><mallctl>opt.prof_thread_active_init</mallctl></link>
+ option for additional information.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="prof.active">
+ <term>
+ <mallctl>prof.active</mallctl>
+ (<type>bool</type>)
+ <literal>rw</literal>
+ [<option>--enable-prof</option>]
+ </term>
+ <listitem><para>Control whether sampling is currently active. See the
+ <link
+ linkend="opt.prof_active"><mallctl>opt.prof_active</mallctl></link>
+ option for additional information, as well as the interrelated <link
+ linkend="thread.prof.active"><mallctl>thread.prof.active</mallctl></link>
+ mallctl.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="prof.dump">
+ <term>
+ <mallctl>prof.dump</mallctl>
+ (<type>const char *</type>)
+ <literal>-w</literal>
+ [<option>--enable-prof</option>]
+ </term>
+ <listitem><para>Dump a memory profile to the specified file, or if NULL
+ is specified, to a file according to the pattern
+ <filename>&lt;prefix&gt;.&lt;pid&gt;.&lt;seq&gt;.m&lt;mseq&gt;.heap</filename>,
+ where <literal>&lt;prefix&gt;</literal> is controlled by the
+ <link linkend="opt.prof_prefix"><mallctl>opt.prof_prefix</mallctl></link>
+ and <link linkend="prof.prefix"><mallctl>prof.prefix</mallctl></link>
+ options.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="prof.prefix">
+ <term>
+ <mallctl>prof.prefix</mallctl>
+ (<type>const char *</type>)
+ <literal>-w</literal>
+ [<option>--enable-prof</option>]
+ </term>
+ <listitem><para>Set the filename prefix for profile dumps. See
+ <link
+ linkend="opt.prof_prefix"><mallctl>opt.prof_prefix</mallctl></link>
+ for the default setting. This can be useful to differentiate profile
+ dumps such as from forked processes.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry id="prof.gdump">
+ <term>
+ <mallctl>prof.gdump</mallctl>
+ (<type>bool</type>)
+ <literal>rw</literal>
+ [<option>--enable-prof</option>]
+ </term>
+ <listitem><para>When enabled, trigger a memory profile dump every time
+ the total virtual memory exceeds the previous maximum. Profiles are
+ dumped to files named according to the pattern
+ <filename>&lt;prefix&gt;.&lt;pid&gt;.&lt;seq&gt;.u&lt;useq&gt;.heap</filename>,
+ where <literal>&lt;prefix&gt;</literal> is controlled by the <link
+ linkend="opt.prof_prefix"><mallctl>opt.prof_prefix</mallctl></link> and
+ <link linkend="prof.prefix"><mallctl>prof.prefix</mallctl></link>
+ options.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="prof.reset">
+ <term>
+ <mallctl>prof.reset</mallctl>
+ (<type>size_t</type>)
+ <literal>-w</literal>
+ [<option>--enable-prof</option>]
+ </term>
+ <listitem><para>Reset all memory profile statistics, and optionally
+ update the sample rate (see <link
+ linkend="opt.lg_prof_sample"><mallctl>opt.lg_prof_sample</mallctl></link>
+ and <link
+ linkend="prof.lg_sample"><mallctl>prof.lg_sample</mallctl></link>).
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry id="prof.lg_sample">
+ <term>
+ <mallctl>prof.lg_sample</mallctl>
+ (<type>size_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-prof</option>]
+ </term>
+ <listitem><para>Get the current sample rate (see <link
+ linkend="opt.lg_prof_sample"><mallctl>opt.lg_prof_sample</mallctl></link>).
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry id="prof.interval">
+ <term>
+ <mallctl>prof.interval</mallctl>
+ (<type>uint64_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-prof</option>]
+ </term>
+ <listitem><para>Average number of bytes allocated between
+ interval-based profile dumps. See the
+ <link
+ linkend="opt.lg_prof_interval"><mallctl>opt.lg_prof_interval</mallctl></link>
+ option for additional information.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.allocated">
+ <term>
+ <mallctl>stats.allocated</mallctl>
+ (<type>size_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Total number of bytes allocated by the
+ application.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.active">
+ <term>
+ <mallctl>stats.active</mallctl>
+ (<type>size_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Total number of bytes in active pages allocated by the
+ application. This is a multiple of the page size, and greater than or
+ equal to <link
+ linkend="stats.allocated"><mallctl>stats.allocated</mallctl></link>.
+ This does not include <link linkend="stats.arenas.i.pdirty">
+ <mallctl>stats.arenas.&lt;i&gt;.pdirty</mallctl></link>,
+ <link linkend="stats.arenas.i.pmuzzy">
+ <mallctl>stats.arenas.&lt;i&gt;.pmuzzy</mallctl></link>, nor pages
+ entirely devoted to allocator metadata.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.metadata">
+ <term>
+ <mallctl>stats.metadata</mallctl>
+ (<type>size_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Total number of bytes dedicated to metadata, which
+ comprise base allocations used for bootstrap-sensitive allocator
+ metadata structures (see <link
+ linkend="stats.arenas.i.base"><mallctl>stats.arenas.&lt;i&gt;.base</mallctl></link>)
+ and internal allocations (see <link
+ linkend="stats.arenas.i.internal"><mallctl>stats.arenas.&lt;i&gt;.internal</mallctl></link>).
+ Transparent huge page (enabled with <link
+ linkend="opt.metadata_thp">opt.metadata_thp</link>) usage is not
+ considered.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.metadata_thp">
+ <term>
+ <mallctl>stats.metadata_thp</mallctl>
+ (<type>size_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Number of transparent huge pages (THP) used for
+ metadata. See <link
+ linkend="stats.metadata"><mallctl>stats.metadata</mallctl></link> and
+ <link linkend="opt.metadata_thp">opt.metadata_thp</link>) for
+ details.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.resident">
+ <term>
+ <mallctl>stats.resident</mallctl>
+ (<type>size_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Maximum number of bytes in physically resident data
+ pages mapped by the allocator, comprising all pages dedicated to
+ allocator metadata, pages backing active allocations, and unused dirty
+ pages. This is a maximum rather than precise because pages may not
+ actually be physically resident if they correspond to demand-zeroed
+ virtual memory that has not yet been touched. This is a multiple of the
+ page size, and is larger than <link
+ linkend="stats.active"><mallctl>stats.active</mallctl></link>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.mapped">
+ <term>
+ <mallctl>stats.mapped</mallctl>
+ (<type>size_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Total number of bytes in active extents mapped by the
+ allocator. This is larger than <link
+ linkend="stats.active"><mallctl>stats.active</mallctl></link>. This
+ does not include inactive extents, even those that contain unused dirty
+ pages, which means that there is no strict ordering between this and
+ <link
+ linkend="stats.resident"><mallctl>stats.resident</mallctl></link>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.retained">
+ <term>
+ <mallctl>stats.retained</mallctl>
+ (<type>size_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Total number of bytes in virtual memory mappings that
+ were retained rather than being returned to the operating system via
+ e.g. <citerefentry><refentrytitle>munmap</refentrytitle>
+ <manvolnum>2</manvolnum></citerefentry> or similar. Retained virtual
+ memory is typically untouched, decommitted, or purged, so it has no
+ strongly associated physical memory (see <link
+ linkend="arena.i.extent_hooks">extent hooks</link> for details).
+ Retained memory is excluded from mapped memory statistics, e.g. <link
+ linkend="stats.mapped"><mallctl>stats.mapped</mallctl></link>.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.zero_reallocs">
+ <term>
+ <mallctl>stats.zero_reallocs</mallctl>
+ (<type>size_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Number of times that the <function>realloc()</function>
+ was called with a non-<constant>NULL</constant> pointer argument and a
+ <constant>0</constant> size argument. This is a fundamentally unsafe
+ pattern in portable programs; see <link linkend="opt.zero_realloc">
+ <mallctl>opt.zero_realloc</mallctl></link> for details.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.background_thread.num_threads">
+ <term>
+ <mallctl>stats.background_thread.num_threads</mallctl>
+ (<type>size_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para> Number of <link linkend="background_thread">background
+ threads</link> running currently.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.background_thread.num_runs">
+ <term>
+ <mallctl>stats.background_thread.num_runs</mallctl>
+ (<type>uint64_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para> Total number of runs from all <link
+ linkend="background_thread">background threads</link>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.background_thread.run_interval">
+ <term>
+ <mallctl>stats.background_thread.run_interval</mallctl>
+ (<type>uint64_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para> Average run interval in nanoseconds of <link
+ linkend="background_thread">background threads</link>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.mutexes.ctl">
+ <term>
+ <mallctl>stats.mutexes.ctl.{counter};</mallctl>
+ (<type>counter specific type</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Statistics on <varname>ctl</varname> mutex (global
+ scope; mallctl related). <mallctl>{counter}</mallctl> is one of the
+ counters below:</para>
+ <varlistentry id="mutex_counters">
+ <listitem><para><varname>num_ops</varname> (<type>uint64_t</type>):
+ Total number of lock acquisition operations on this mutex.</para>
+
+ <para><varname>num_spin_acq</varname> (<type>uint64_t</type>): Number
+ of times the mutex was spin-acquired. When the mutex is currently
+ locked and cannot be acquired immediately, a short period of
+ spin-retry within jemalloc will be performed. Acquired through spin
+ generally means the contention was lightweight and not causing context
+ switches.</para>
+
+ <para><varname>num_wait</varname> (<type>uint64_t</type>): Number of
+ times the mutex was wait-acquired, which means the mutex contention
+ was not solved by spin-retry, and blocking operation was likely
+ involved in order to acquire the mutex. This event generally implies
+ higher cost / longer delay, and should be investigated if it happens
+ often.</para>
+
+ <para><varname>max_wait_time</varname> (<type>uint64_t</type>):
+ Maximum length of time in nanoseconds spent on a single wait-acquired
+ lock operation. Note that to avoid profiling overhead on the common
+ path, this does not consider spin-acquired cases.</para>
+
+ <para><varname>total_wait_time</varname> (<type>uint64_t</type>):
+ Cumulative time in nanoseconds spent on wait-acquired lock operations.
+ Similarly, spin-acquired cases are not considered.</para>
+
+ <para><varname>max_num_thds</varname> (<type>uint32_t</type>): Maximum
+ number of threads waiting on this mutex simultaneously. Similarly,
+ spin-acquired cases are not considered.</para>
+
+ <para><varname>num_owner_switch</varname> (<type>uint64_t</type>):
+ Number of times the current mutex owner is different from the previous
+ one. This event does not generally imply an issue; rather it is an
+ indicator of how often the protected data are accessed by different
+ threads.
+ </para>
+ </listitem>
+ </varlistentry>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.mutexes.background_thread">
+ <term>
+ <mallctl>stats.mutexes.background_thread.{counter}</mallctl>
+ (<type>counter specific type</type>) <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Statistics on <varname>background_thread</varname> mutex
+ (global scope; <link
+ linkend="background_thread"><mallctl>background_thread</mallctl></link>
+ related). <mallctl>{counter}</mallctl> is one of the counters in <link
+ linkend="mutex_counters">mutex profiling
+ counters</link>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.mutexes.prof">
+ <term>
+ <mallctl>stats.mutexes.prof.{counter}</mallctl>
+ (<type>counter specific type</type>) <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Statistics on <varname>prof</varname> mutex (global
+ scope; profiling related). <mallctl>{counter}</mallctl> is one of the
+ counters in <link linkend="mutex_counters">mutex profiling
+ counters</link>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.mutexes.prof_thds_data">
+ <term>
+ <mallctl>stats.mutexes.prof_thds_data.{counter}</mallctl>
+ (<type>counter specific type</type>) <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Statistics on <varname>prof</varname> threads data mutex
+ (global scope; profiling related). <mallctl>{counter}</mallctl> is one
+ of the counters in <link linkend="mutex_counters">mutex profiling
+ counters</link>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.mutexes.prof_dump">
+ <term>
+ <mallctl>stats.mutexes.prof_dump.{counter}</mallctl>
+ (<type>counter specific type</type>) <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Statistics on <varname>prof</varname> dumping mutex
+ (global scope; profiling related). <mallctl>{counter}</mallctl> is one
+ of the counters in <link linkend="mutex_counters">mutex profiling
+ counters</link>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.mutexes.reset">
+ <term>
+ <mallctl>stats.mutexes.reset</mallctl>
+ (<type>void</type>) <literal>--</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Reset all mutex profile statistics, including global
+ mutexes, arena mutexes and bin mutexes.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.dss">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.dss</mallctl>
+ (<type>const char *</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para>dss (<citerefentry><refentrytitle>sbrk</refentrytitle>
+ <manvolnum>2</manvolnum></citerefentry>) allocation precedence as
+ related to <citerefentry><refentrytitle>mmap</refentrytitle>
+ <manvolnum>2</manvolnum></citerefentry> allocation. See <link
+ linkend="opt.dss"><mallctl>opt.dss</mallctl></link> for details.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.dirty_decay_ms">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.dirty_decay_ms</mallctl>
+ (<type>ssize_t</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para>Approximate time in milliseconds from the creation of a
+ set of unused dirty pages until an equivalent set of unused dirty pages
+ is purged and/or reused. See <link
+ linkend="opt.dirty_decay_ms"><mallctl>opt.dirty_decay_ms</mallctl></link>
+ for details.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.muzzy_decay_ms">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.muzzy_decay_ms</mallctl>
+ (<type>ssize_t</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para>Approximate time in milliseconds from the creation of a
+ set of unused muzzy pages until an equivalent set of unused muzzy pages
+ is purged and/or reused. See <link
+ linkend="opt.muzzy_decay_ms"><mallctl>opt.muzzy_decay_ms</mallctl></link>
+ for details.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.nthreads">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.nthreads</mallctl>
+ (<type>unsigned</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para>Number of threads currently assigned to
+ arena.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.uptime">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.uptime</mallctl>
+ (<type>uint64_t</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para>Time elapsed (in nanoseconds) since the arena was
+ created. If &lt;i&gt; equals <constant>0</constant> or
+ <constant>MALLCTL_ARENAS_ALL</constant>, this is the uptime since malloc
+ initialization.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.pactive">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.pactive</mallctl>
+ (<type>size_t</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para>Number of pages in active extents.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.pdirty">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.pdirty</mallctl>
+ (<type>size_t</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para>Number of pages within unused extents that are
+ potentially dirty, and for which <function>madvise()</function> or
+ similar has not been called. See <link
+ linkend="opt.dirty_decay_ms"><mallctl>opt.dirty_decay_ms</mallctl></link>
+ for a description of dirty pages.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.pmuzzy">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.pmuzzy</mallctl>
+ (<type>size_t</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para>Number of pages within unused extents that are muzzy.
+ See <link
+ linkend="opt.muzzy_decay_ms"><mallctl>opt.muzzy_decay_ms</mallctl></link>
+ for a description of muzzy pages.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.mapped">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.mapped</mallctl>
+ (<type>size_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Number of mapped bytes.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.retained">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.retained</mallctl>
+ (<type>size_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Number of retained bytes. See <link
+ linkend="stats.retained"><mallctl>stats.retained</mallctl></link> for
+ details.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.extent_avail">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.extent_avail</mallctl>
+ (<type>size_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Number of allocated (but unused) extent structs in this
+ arena.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.base">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.base</mallctl>
+ (<type>size_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>
+ Number of bytes dedicated to bootstrap-sensitive allocator metadata
+ structures.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.internal">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.internal</mallctl>
+ (<type>size_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Number of bytes dedicated to internal allocations.
+ Internal allocations differ from application-originated allocations in
+ that they are for internal use, and that they are omitted from heap
+ profiles.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.metadata_thp">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.metadata_thp</mallctl>
+ (<type>size_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Number of transparent huge pages (THP) used for
+ metadata. See <link linkend="opt.metadata_thp">opt.metadata_thp</link>
+ for details.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.resident">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.resident</mallctl>
+ (<type>size_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Maximum number of bytes in physically resident data
+ pages mapped by the arena, comprising all pages dedicated to allocator
+ metadata, pages backing active allocations, and unused dirty pages.
+ This is a maximum rather than precise because pages may not actually be
+ physically resident if they correspond to demand-zeroed virtual memory
+ that has not yet been touched. This is a multiple of the page
+ size.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.dirty_npurge">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.dirty_npurge</mallctl>
+ (<type>uint64_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Number of dirty page purge sweeps performed.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.dirty_nmadvise">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.dirty_nmadvise</mallctl>
+ (<type>uint64_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Number of <function>madvise()</function> or similar
+ calls made to purge dirty pages.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.dirty_purged">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.dirty_purged</mallctl>
+ (<type>uint64_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Number of dirty pages purged.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.muzzy_npurge">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.muzzy_npurge</mallctl>
+ (<type>uint64_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Number of muzzy page purge sweeps performed.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.muzzy_nmadvise">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.muzzy_nmadvise</mallctl>
+ (<type>uint64_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Number of <function>madvise()</function> or similar
+ calls made to purge muzzy pages.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.muzzy_purged">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.muzzy_purged</mallctl>
+ (<type>uint64_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Number of muzzy pages purged.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.small.allocated">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.small.allocated</mallctl>
+ (<type>size_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Number of bytes currently allocated by small objects.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.small.nmalloc">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.small.nmalloc</mallctl>
+ (<type>uint64_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Cumulative number of times a small allocation was
+ requested from the arena's bins, whether to fill the relevant tcache if
+ <link linkend="opt.tcache"><mallctl>opt.tcache</mallctl></link> is
+ enabled, or to directly satisfy an allocation request
+ otherwise.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.small.ndalloc">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.small.ndalloc</mallctl>
+ (<type>uint64_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Cumulative number of times a small allocation was
+ returned to the arena's bins, whether to flush the relevant tcache if
+ <link linkend="opt.tcache"><mallctl>opt.tcache</mallctl></link> is
+ enabled, or to directly deallocate an allocation
+ otherwise.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.small.nrequests">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.small.nrequests</mallctl>
+ (<type>uint64_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Cumulative number of allocation requests satisfied by
+ all bin size classes.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.small.nfills">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.small.nfills</mallctl>
+ (<type>uint64_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Cumulative number of tcache fills by all small size
+ classes.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.small.nflushes">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.small.nflushes</mallctl>
+ (<type>uint64_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Cumulative number of tcache flushes by all small size
+ classes.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.large.allocated">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.large.allocated</mallctl>
+ (<type>size_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Number of bytes currently allocated by large objects.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.large.nmalloc">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.large.nmalloc</mallctl>
+ (<type>uint64_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Cumulative number of times a large extent was allocated
+ from the arena, whether to fill the relevant tcache if <link
+ linkend="opt.tcache"><mallctl>opt.tcache</mallctl></link> is enabled and
+ the size class is within the range being cached, or to directly satisfy
+ an allocation request otherwise.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.large.ndalloc">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.large.ndalloc</mallctl>
+ (<type>uint64_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Cumulative number of times a large extent was returned
+ to the arena, whether to flush the relevant tcache if <link
+ linkend="opt.tcache"><mallctl>opt.tcache</mallctl></link> is enabled and
+ the size class is within the range being cached, or to directly
+ deallocate an allocation otherwise.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.large.nrequests">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.large.nrequests</mallctl>
+ (<type>uint64_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Cumulative number of allocation requests satisfied by
+ all large size classes.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.large.nfills">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.large.nfills</mallctl>
+ (<type>uint64_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Cumulative number of tcache fills by all large size
+ classes.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.large.nflushes">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.large.nflushes</mallctl>
+ (<type>uint64_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Cumulative number of tcache flushes by all large size
+ classes.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.bins.j.nmalloc">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.bins.&lt;j&gt;.nmalloc</mallctl>
+ (<type>uint64_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Cumulative number of times a bin region of the
+ corresponding size class was allocated from the arena, whether to fill
+ the relevant tcache if <link
+ linkend="opt.tcache"><mallctl>opt.tcache</mallctl></link> is enabled, or
+ to directly satisfy an allocation request otherwise.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.bins.j.ndalloc">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.bins.&lt;j&gt;.ndalloc</mallctl>
+ (<type>uint64_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Cumulative number of times a bin region of the
+ corresponding size class was returned to the arena, whether to flush the
+ relevant tcache if <link
+ linkend="opt.tcache"><mallctl>opt.tcache</mallctl></link> is enabled, or
+ to directly deallocate an allocation otherwise.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.bins.j.nrequests">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.bins.&lt;j&gt;.nrequests</mallctl>
+ (<type>uint64_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Cumulative number of allocation requests satisfied by
+ bin regions of the corresponding size class.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.bins.j.curregs">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.bins.&lt;j&gt;.curregs</mallctl>
+ (<type>size_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Current number of regions for this size
+ class.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.bins.j.nfills">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.bins.&lt;j&gt;.nfills</mallctl>
+ (<type>uint64_t</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para>Cumulative number of tcache fills.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.bins.j.nflushes">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.bins.&lt;j&gt;.nflushes</mallctl>
+ (<type>uint64_t</type>)
+ <literal>r-</literal>
+ </term>
+ <listitem><para>Cumulative number of tcache flushes.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.bins.j.nslabs">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.bins.&lt;j&gt;.nslabs</mallctl>
+ (<type>uint64_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Cumulative number of slabs created.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.bins.j.nreslabs">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.bins.&lt;j&gt;.nreslabs</mallctl>
+ (<type>uint64_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Cumulative number of times the current slab from which
+ to allocate changed.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.bins.j.curslabs">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.bins.&lt;j&gt;.curslabs</mallctl>
+ (<type>size_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Current number of slabs.</para></listitem>
+ </varlistentry>
+
+
+ <varlistentry id="stats.arenas.i.bins.j.nonfull_slabs">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.bins.&lt;j&gt;.nonfull_slabs</mallctl>
+ (<type>size_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Current number of nonfull slabs.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.bins.mutex">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.bins.&lt;j&gt;.mutex.{counter}</mallctl>
+ (<type>counter specific type</type>) <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Statistics on
+ <varname>arena.&lt;i&gt;.bins.&lt;j&gt;</varname> mutex (arena bin
+ scope; bin operation related). <mallctl>{counter}</mallctl> is one of
+ the counters in <link linkend="mutex_counters">mutex profiling
+ counters</link>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.extents.n">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.extents.&lt;j&gt;.n{extent_type}</mallctl>
+ (<type>size_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para> Number of extents of the given type in this arena in
+ the bucket corresponding to page size index &lt;j&gt;. The extent type
+ is one of dirty, muzzy, or retained.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.extents.bytes">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.extents.&lt;j&gt;.{extent_type}_bytes</mallctl>
+ (<type>size_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para> Sum of the bytes managed by extents of the given type
+ in this arena in the bucket corresponding to page size index &lt;j&gt;.
+ The extent type is one of dirty, muzzy, or retained.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.lextents.j.nmalloc">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.lextents.&lt;j&gt;.nmalloc</mallctl>
+ (<type>uint64_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Cumulative number of times a large extent of the
+ corresponding size class was allocated from the arena, whether to fill
+ the relevant tcache if <link
+ linkend="opt.tcache"><mallctl>opt.tcache</mallctl></link> is enabled and
+ the size class is within the range being cached, or to directly satisfy
+ an allocation request otherwise.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.lextents.j.ndalloc">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.lextents.&lt;j&gt;.ndalloc</mallctl>
+ (<type>uint64_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Cumulative number of times a large extent of the
+ corresponding size class was returned to the arena, whether to flush the
+ relevant tcache if <link
+ linkend="opt.tcache"><mallctl>opt.tcache</mallctl></link> is enabled and
+ the size class is within the range being cached, or to directly
+ deallocate an allocation otherwise.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.lextents.j.nrequests">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.lextents.&lt;j&gt;.nrequests</mallctl>
+ (<type>uint64_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Cumulative number of allocation requests satisfied by
+ large extents of the corresponding size class.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.lextents.j.curlextents">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.lextents.&lt;j&gt;.curlextents</mallctl>
+ (<type>size_t</type>)
+ <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Current number of large allocations for this size class.
+ </para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.mutexes.large">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.mutexes.large.{counter}</mallctl>
+ (<type>counter specific type</type>) <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Statistics on <varname>arena.&lt;i&gt;.large</varname>
+ mutex (arena scope; large allocation related).
+ <mallctl>{counter}</mallctl> is one of the counters in <link
+ linkend="mutex_counters">mutex profiling
+ counters</link>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.mutexes.extent_avail">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.mutexes.extent_avail.{counter}</mallctl>
+ (<type>counter specific type</type>) <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Statistics on <varname>arena.&lt;i&gt;.extent_avail
+ </varname> mutex (arena scope; extent avail related).
+ <mallctl>{counter}</mallctl> is one of the counters in <link
+ linkend="mutex_counters">mutex profiling
+ counters</link>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.mutexes.extents_dirty">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.mutexes.extents_dirty.{counter}</mallctl>
+ (<type>counter specific type</type>) <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Statistics on <varname>arena.&lt;i&gt;.extents_dirty
+ </varname> mutex (arena scope; dirty extents related).
+ <mallctl>{counter}</mallctl> is one of the counters in <link
+ linkend="mutex_counters">mutex profiling
+ counters</link>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.mutexes.extents_muzzy">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.mutexes.extents_muzzy.{counter}</mallctl>
+ (<type>counter specific type</type>) <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Statistics on <varname>arena.&lt;i&gt;.extents_muzzy
+ </varname> mutex (arena scope; muzzy extents related).
+ <mallctl>{counter}</mallctl> is one of the counters in <link
+ linkend="mutex_counters">mutex profiling
+ counters</link>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.mutexes.extents_retained">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.mutexes.extents_retained.{counter}</mallctl>
+ (<type>counter specific type</type>) <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Statistics on <varname>arena.&lt;i&gt;.extents_retained
+ </varname> mutex (arena scope; retained extents related).
+ <mallctl>{counter}</mallctl> is one of the counters in <link
+ linkend="mutex_counters">mutex profiling
+ counters</link>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.mutexes.decay_dirty">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.mutexes.decay_dirty.{counter}</mallctl>
+ (<type>counter specific type</type>) <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Statistics on <varname>arena.&lt;i&gt;.decay_dirty
+ </varname> mutex (arena scope; decay for dirty pages related).
+ <mallctl>{counter}</mallctl> is one of the counters in <link
+ linkend="mutex_counters">mutex profiling
+ counters</link>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.mutexes.decay_muzzy">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.mutexes.decay_muzzy.{counter}</mallctl>
+ (<type>counter specific type</type>) <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Statistics on <varname>arena.&lt;i&gt;.decay_muzzy
+ </varname> mutex (arena scope; decay for muzzy pages related).
+ <mallctl>{counter}</mallctl> is one of the counters in <link
+ linkend="mutex_counters">mutex profiling
+ counters</link>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.mutexes.base">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.mutexes.base.{counter}</mallctl>
+ (<type>counter specific type</type>) <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Statistics on <varname>arena.&lt;i&gt;.base</varname>
+ mutex (arena scope; base allocator related).
+ <mallctl>{counter}</mallctl> is one of the counters in <link
+ linkend="mutex_counters">mutex profiling
+ counters</link>.</para></listitem>
+ </varlistentry>
+
+ <varlistentry id="stats.arenas.i.mutexes.tcache_list">
+ <term>
+ <mallctl>stats.arenas.&lt;i&gt;.mutexes.tcache_list.{counter}</mallctl>
+ (<type>counter specific type</type>) <literal>r-</literal>
+ [<option>--enable-stats</option>]
+ </term>
+ <listitem><para>Statistics on
+ <varname>arena.&lt;i&gt;.tcache_list</varname> mutex (arena scope;
+ tcache to arena association related). This mutex is expected to be
+ accessed less often. <mallctl>{counter}</mallctl> is one of the
+ counters in <link linkend="mutex_counters">mutex profiling
+ counters</link>.</para></listitem>
+ </varlistentry>
+
+ </variablelist>
+ </refsect1>
+ <refsect1 id="heap_profile_format">
+ <title>HEAP PROFILE FORMAT</title>
+ <para>Although the heap profiling functionality was originally designed to
+ be compatible with the
+ <command>pprof</command> command that is developed as part of the <ulink
+ url="http://code.google.com/p/gperftools/">gperftools
+ package</ulink>, the addition of per thread heap profiling functionality
+ required a different heap profile format. The <command>jeprof</command>
+ command is derived from <command>pprof</command>, with enhancements to
+ support the heap profile format described here.</para>
+
+ <para>In the following hypothetical heap profile, <constant>[...]</constant>
+ indicates elision for the sake of compactness. <programlisting><![CDATA[
+heap_v2/524288
+ t*: 28106: 56637512 [0: 0]
+ [...]
+ t3: 352: 16777344 [0: 0]
+ [...]
+ t99: 17754: 29341640 [0: 0]
+ [...]
+@ 0x5f86da8 0x5f5a1dc [...] 0x29e4d4e 0xa200316 0xabb2988 [...]
+ t*: 13: 6688 [0: 0]
+ t3: 12: 6496 [0: 0]
+ t99: 1: 192 [0: 0]
+[...]
+
+MAPPED_LIBRARIES:
+[...]]]></programlisting> The following matches the above heap profile, but most
+tokens are replaced with <constant>&lt;description&gt;</constant> to indicate
+descriptions of the corresponding fields. <programlisting><![CDATA[
+<heap_profile_format_version>/<mean_sample_interval>
+ <aggregate>: <curobjs>: <curbytes> [<cumobjs>: <cumbytes>]
+ [...]
+ <thread_3_aggregate>: <curobjs>: <curbytes> [<cumobjs>: <cumbytes>]
+ [...]
+ <thread_99_aggregate>: <curobjs>: <curbytes> [<cumobjs>: <cumbytes>]
+ [...]
+@ <top_frame> <frame> [...] <frame> <frame> <frame> [...]
+ <backtrace_aggregate>: <curobjs>: <curbytes> [<cumobjs>: <cumbytes>]
+ <backtrace_thread_3>: <curobjs>: <curbytes> [<cumobjs>: <cumbytes>]
+ <backtrace_thread_99>: <curobjs>: <curbytes> [<cumobjs>: <cumbytes>]
+[...]
+
+MAPPED_LIBRARIES:
+</proc/<pid>/maps>]]></programlisting></para>
+ </refsect1>
+
+ <refsect1 id="debugging_malloc_problems">
+ <title>DEBUGGING MALLOC PROBLEMS</title>
+ <para>When debugging, it is a good idea to configure/build jemalloc with
+ the <option>--enable-debug</option> and <option>--enable-fill</option>
+ options, and recompile the program with suitable options and symbols for
+ debugger support. When so configured, jemalloc incorporates a wide variety
+ of run-time assertions that catch application errors such as double-free,
+ write-after-free, etc.</para>
+
+ <para>Programs often accidentally depend on <quote>uninitialized</quote>
+ memory actually being filled with zero bytes. Junk filling
+ (see the <link linkend="opt.junk"><mallctl>opt.junk</mallctl></link>
+ option) tends to expose such bugs in the form of obviously incorrect
+ results and/or coredumps. Conversely, zero
+ filling (see the <link
+ linkend="opt.zero"><mallctl>opt.zero</mallctl></link> option) eliminates
+ the symptoms of such bugs. Between these two options, it is usually
+ possible to quickly detect, diagnose, and eliminate such bugs.</para>
+
+ <para>This implementation does not provide much detail about the problems
+ it detects, because the performance impact for storing such information
+ would be prohibitive.</para>
+ </refsect1>
+ <refsect1 id="diagnostic_messages">
+ <title>DIAGNOSTIC MESSAGES</title>
+ <para>If any of the memory allocation/deallocation functions detect an
+ error or warning condition, a message will be printed to file descriptor
+ <constant>STDERR_FILENO</constant>. Errors will result in the process
+ dumping core. If the <link
+ linkend="opt.abort"><mallctl>opt.abort</mallctl></link> option is set, most
+ warnings are treated as errors.</para>
+
+ <para>The <varname>malloc_message</varname> variable allows the programmer
+ to override the function which emits the text strings forming the errors
+ and warnings if for some reason the <constant>STDERR_FILENO</constant> file
+ descriptor is not suitable for this.
+ <function>malloc_message()</function> takes the
+ <parameter>cbopaque</parameter> pointer argument that is
+ <constant>NULL</constant> unless overridden by the arguments in a call to
+ <function>malloc_stats_print()</function>, followed by a string
+ pointer. Please note that doing anything which tries to allocate memory in
+ this function is likely to result in a crash or deadlock.</para>
+
+ <para>All messages are prefixed by
+ <quote><computeroutput>&lt;jemalloc&gt;: </computeroutput></quote>.</para>
+ </refsect1>
+ <refsect1 id="return_values">
+ <title>RETURN VALUES</title>
+ <refsect2>
+ <title>Standard API</title>
+ <para>The <function>malloc()</function> and
+ <function>calloc()</function> functions return a pointer to the
+ allocated memory if successful; otherwise a <constant>NULL</constant>
+ pointer is returned and <varname>errno</varname> is set to
+ <errorname>ENOMEM</errorname>.</para>
+
+ <para>The <function>posix_memalign()</function> function
+ returns the value 0 if successful; otherwise it returns an error value.
+ The <function>posix_memalign()</function> function will fail
+ if:
+ <variablelist>
+ <varlistentry>
+ <term><errorname>EINVAL</errorname></term>
+
+ <listitem><para>The <parameter>alignment</parameter> parameter is
+ not a power of 2 at least as large as
+ <code language="C">sizeof(<type>void *</type>)</code>.
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><errorname>ENOMEM</errorname></term>
+
+ <listitem><para>Memory allocation error.</para></listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+
+ <para>The <function>aligned_alloc()</function> function returns
+ a pointer to the allocated memory if successful; otherwise a
+ <constant>NULL</constant> pointer is returned and
+ <varname>errno</varname> is set. The
+ <function>aligned_alloc()</function> function will fail if:
+ <variablelist>
+ <varlistentry>
+ <term><errorname>EINVAL</errorname></term>
+
+ <listitem><para>The <parameter>alignment</parameter> parameter is
+ not a power of 2.
+ </para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><errorname>ENOMEM</errorname></term>
+
+ <listitem><para>Memory allocation error.</para></listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+
+ <para>The <function>realloc()</function> function returns a
+ pointer, possibly identical to <parameter>ptr</parameter>, to the
+ allocated memory if successful; otherwise a <constant>NULL</constant>
+ pointer is returned, and <varname>errno</varname> is set to
+ <errorname>ENOMEM</errorname> if the error was the result of an
+ allocation failure. The <function>realloc()</function>
+ function always leaves the original buffer intact when an error occurs.
+ </para>
+
+ <para>The <function>free()</function> function returns no
+ value.</para>
+ </refsect2>
+ <refsect2>
+ <title>Non-standard API</title>
+ <para>The <function>mallocx()</function> and
+ <function>rallocx()</function> functions return a pointer to
+ the allocated memory if successful; otherwise a <constant>NULL</constant>
+ pointer is returned to indicate insufficient contiguous memory was
+ available to service the allocation request. </para>
+
+ <para>The <function>xallocx()</function> function returns the
+ real size of the resulting resized allocation pointed to by
+ <parameter>ptr</parameter>, which is a value less than
+ <parameter>size</parameter> if the allocation could not be adequately
+ grown in place. </para>
+
+ <para>The <function>sallocx()</function> function returns the
+ real size of the allocation pointed to by <parameter>ptr</parameter>.
+ </para>
+
+ <para>The <function>nallocx()</function> returns the real size
+ that would result from a successful equivalent
+ <function>mallocx()</function> function call, or zero if
+ insufficient memory is available to perform the size computation. </para>
+
+ <para>The <function>mallctl()</function>,
+ <function>mallctlnametomib()</function>, and
+ <function>mallctlbymib()</function> functions return 0 on
+ success; otherwise they return an error value. The functions will fail
+ if:
+ <variablelist>
+ <varlistentry>
+ <term><errorname>EINVAL</errorname></term>
+
+ <listitem><para><parameter>newp</parameter> is not
+ <constant>NULL</constant>, and <parameter>newlen</parameter> is too
+ large or too small. Alternatively, <parameter>*oldlenp</parameter>
+ is too large or too small; when it happens, except for a very few
+ cases explicitly documented otherwise, as much data as possible
+ are read despite the error, with the amount of data read being
+ recorded in <parameter>*oldlenp</parameter>.</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><errorname>ENOENT</errorname></term>
+
+ <listitem><para><parameter>name</parameter> or
+ <parameter>mib</parameter> specifies an unknown/invalid
+ value.</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><errorname>EPERM</errorname></term>
+
+ <listitem><para>Attempt to read or write void value, or attempt to
+ write read-only value.</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><errorname>EAGAIN</errorname></term>
+
+ <listitem><para>A memory allocation failure
+ occurred.</para></listitem>
+ </varlistentry>
+ <varlistentry>
+ <term><errorname>EFAULT</errorname></term>
+
+ <listitem><para>An interface with side effects failed in some way
+ not directly related to <function>mallctl*()</function>
+ read/write processing.</para></listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+
+ <para>The <function>malloc_usable_size()</function> function
+ returns the usable size of the allocation pointed to by
+ <parameter>ptr</parameter>. </para>
+ </refsect2>
+ </refsect1>
+ <refsect1 id="environment">
+ <title>ENVIRONMENT</title>
+ <para>The following environment variable affects the execution of the
+ allocation functions:
+ <variablelist>
+ <varlistentry>
+ <term><envar>MALLOC_CONF</envar></term>
+
+ <listitem><para>If the environment variable
+ <envar>MALLOC_CONF</envar> is set, the characters it contains
+ will be interpreted as options.</para></listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+ </refsect1>
+ <refsect1 id="examples">
+ <title>EXAMPLES</title>
+ <para>To dump core whenever a problem occurs:
+ <screen>ln -s 'abort:true' /etc/malloc.conf</screen>
+ </para>
+ <para>To specify in the source that only one arena should be automatically
+ created:
+ <programlisting language="C"><![CDATA[
+malloc_conf = "narenas:1";]]></programlisting></para>
+ </refsect1>
+ <refsect1 id="see_also">
+ <title>SEE ALSO</title>
+ <para><citerefentry><refentrytitle>madvise</refentrytitle>
+ <manvolnum>2</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>mmap</refentrytitle>
+ <manvolnum>2</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>sbrk</refentrytitle>
+ <manvolnum>2</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>utrace</refentrytitle>
+ <manvolnum>2</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>alloca</refentrytitle>
+ <manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>atexit</refentrytitle>
+ <manvolnum>3</manvolnum></citerefentry>,
+ <citerefentry><refentrytitle>getpagesize</refentrytitle>
+ <manvolnum>3</manvolnum></citerefentry></para>
+ </refsect1>
+ <refsect1 id="standards">
+ <title>STANDARDS</title>
+ <para>The <function>malloc()</function>,
+ <function>calloc()</function>,
+ <function>realloc()</function>, and
+ <function>free()</function> functions conform to ISO/IEC
+ 9899:1990 (<quote>ISO C90</quote>).</para>
+
+ <para>The <function>posix_memalign()</function> function conforms
+ to IEEE Std 1003.1-2001 (<quote>POSIX.1</quote>).</para>
+ </refsect1>
+ <refsect1 id="history">
+ <title>HISTORY</title>
+ <para>The <function>malloc_usable_size()</function> and
+ <function>posix_memalign()</function> functions first appeared in FreeBSD
+ 7.0.</para>
+
+ <para>The <function>aligned_alloc()</function>,
+ <function>malloc_stats_print()</function>, and
+ <function>mallctl*()</function> functions first appeared in FreeBSD
+ 10.0.</para>
+
+ <para>The <function>*allocx()</function> functions first appeared in FreeBSD
+ 11.0.</para>
+ </refsect1>
+</refentry>
diff --git a/contrib/jemalloc/doc/manpages.xsl.in b/contrib/jemalloc/doc/manpages.xsl.in
new file mode 100644
index 000000000000..88b2626b9581
--- /dev/null
+++ b/contrib/jemalloc/doc/manpages.xsl.in
@@ -0,0 +1,4 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+ <xsl:import href="@XSLROOT@/manpages/docbook.xsl"/>
+ <xsl:import href="@abs_srcroot@doc/stylesheet.xsl"/>
+</xsl:stylesheet>
diff --git a/contrib/jemalloc/doc/stylesheet.xsl b/contrib/jemalloc/doc/stylesheet.xsl
new file mode 100644
index 000000000000..619365d825c7
--- /dev/null
+++ b/contrib/jemalloc/doc/stylesheet.xsl
@@ -0,0 +1,10 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+ <xsl:param name="funcsynopsis.style">ansi</xsl:param>
+ <xsl:param name="function.parens" select="0"/>
+ <xsl:template match="function">
+ <xsl:call-template name="inline.monoseq"/>
+ </xsl:template>
+ <xsl:template match="mallctl">
+ <quote><xsl:call-template name="inline.monoseq"/></quote>
+ </xsl:template>
+</xsl:stylesheet>
diff --git a/contrib/jemalloc/doc_internal/PROFILING_INTERNALS.md b/contrib/jemalloc/doc_internal/PROFILING_INTERNALS.md
new file mode 100644
index 000000000000..0a9f31c0c9c0
--- /dev/null
+++ b/contrib/jemalloc/doc_internal/PROFILING_INTERNALS.md
@@ -0,0 +1,127 @@
+# jemalloc profiling
+This describes the mathematical basis behind jemalloc's profiling implementation, as well as the implementation tricks that make it effective. Historically, the jemalloc profiling design simply copied tcmalloc's. The implementation has since diverged, due to both the desire to record additional information, and to correct some biasing bugs.
+
+Note: this document is markdown with embedded LaTeX; different markdown renderers may not produce the expected output. Viewing with `pandoc -s PROFILING_INTERNALS.md -o PROFILING_INTERNALS.pdf` is recommended.
+
+## Some tricks in our implementation toolbag
+
+### Sampling
+Recording our metadata is quite expensive; we need to walk up the stack to get a stack trace. On top of that, we need to allocate storage to record that stack trace, and stick it somewhere where a profile-dumping call can find it. That call might happen on another thread, so we'll probably need to take a lock to do so. These costs are quite large compared to the average cost of an allocation. To manage this, we'll only sample some fraction of allocations. This will miss some of them, so our data will be incomplete, but we'll try to make up for it. We can tune our sampling rate to balance accuracy and performance.
+
+### Fast Bernoulli sampling
+Compared to our fast paths, even a `coinflip(p)` function can be quite expensive. Having to do a random-number generation and some floating point operations would be a sizeable relative cost. However (as pointed out in [[Vitter, 1987](https://dl.acm.org/doi/10.1145/23002.23003)]), if we can orchestrate our algorithm so that many of our `coinflip` calls share their parameter value, we can do better. We can sample from the geometric distribution, and initialize a counter with the result. When the counter hits 0, the `coinflip` function returns true (and reinitializes its internal counter).
+This can let us do a random-number generation once per (logical) coinflip that comes up heads, rather than once per (logical) coinflip. Since we expect to sample relatively rarely, this can be a large win.
+
+### Fast-path / slow-path thinking
+Most programs have a skewed distribution of allocations. Smaller allocations are much more frequent than large ones, but shorter lived and less common as a fraction of program memory. "Small" and "large" are necessarily sort of fuzzy terms, but if we define "small" as "allocations jemalloc puts into slabs" and "large" as the others, then it's not uncommon for small allocations to be hundreds of times more frequent than large ones, but take up around half the amount of heap space as large ones. Moreover, small allocations tend to be much cheaper than large ones (often by a factor of 20-30): they're more likely to hit in thread caches, less likely to have to do an mmap, and cheaper to fill (by the user) once the allocation has been returned.
+
+## An unbiased estimator of space consumption from (almost) arbitrary sampling strategies
+Suppose we have a sampling strategy that meets the following criteria:
+
+ - One allocation being sampled is independent of other allocations being sampled.
+ - Each allocation has a non-zero probability of being sampled.
+
+We can then estimate the bytes in live allocations through some particular stack trace as:
+
+$$ \sum_i S_i I_i \frac{1}{\mathrm{E}[I_i]} $$
+
+where the sum ranges over some index variable of live allocations from that stack, $S_i$ is the size of the $i$'th allocation, and $I_i$ is an indicator random variable for whether or not the $i'th$ allocation is sampled. $S_i$ and $\mathrm{E}[I_i]$ are constants (the program allocations are fixed; the random variables are the sampling decisions), so taking the expectation we get
+
+$$ \sum_i S_i \mathrm{E}[I_i] \frac{1}{\mathrm{E}[I_i]}.$$
+
+This is of course $\sum_i S_i$, as we want (and, a similar calculation could be done for allocation counts as well).
+This is a fairly general strategy; note that while we require that sampling decisions be independent of one another's outcomes, they don't have to be independent of previous allocations, total bytes allocated, etc. You can imagine strategies that:
+
+ - Sample allocations at program startup at a higher rate than subsequent allocations
+ - Sample even-indexed allocations more frequently than odd-indexed ones (so long as no allocation has zero sampling probability)
+ - Let threads declare themselves as high-sampling-priority, and sample their allocations at an increased rate.
+
+These can all be fit into this framework to give an unbiased estimator.
+
+## Evaluating sampling strategies
+Not all strategies for picking allocations to sample are equally good, of course. Among unbiased estimators, the lower the variance, the lower the mean squared error. Using the estimator above, the variance is:
+
+$$
+\begin{aligned}
+& \mathrm{Var}[\sum_i S_i I_i \frac{1}{\mathrm{E}[I_i]}] \\
+=& \sum_i \mathrm{Var}[S_i I_i \frac{1}{\mathrm{E}[I_i]}] \\
+=& \sum_i \frac{S_i^2}{\mathrm{E}[I_i]^2} \mathrm{Var}[I_i] \\
+=& \sum_i \frac{S_i^2}{\mathrm{E}[I_i]^2} \mathrm{Var}[I_i] \\
+=& \sum_i \frac{S_i^2}{\mathrm{E}[I_i]^2} \mathrm{E}[I_i](1 - \mathrm{E}[I_i]) \\
+=& \sum_i S_i^2 \frac{1 - \mathrm{E}[I_i]}{\mathrm{E}[I_i]}.
+\end{aligned}
+$$
+
+We can use this formula to compare various strategy choices. All else being equal, lower-variance strategies are better.
+
+## Possible sampling strategies
+Because of the desire to avoid the fast-path costs, we'd like to use our Bernoulli trick if possible. There are two obvious counters to use: a coinflip per allocation, and a coinflip per byte allocated.
+
+### Bernoulli sampling per-allocation
+An obvious strategy is to pick some large $N$, and give each allocation a $1/N$ chance of being sampled. This would let us use our Bernoulli-via-Geometric trick. Using the formula from above, we can compute the variance as:
+
+$$ \sum_i S_i^2 \frac{1 - \frac{1}{N}}{\frac{1}{N}} = (N-1) \sum_i S_i^2.$$
+
+That is, an allocation of size $Z$ contributes a term of $(N-1)Z^2$ to the variance.
+
+### Bernoulli sampling per-byte
+Another option we have is to pick some rate $R$, and give each byte a $1/R$ chance of being picked for sampling (at which point we would sample its contained allocation). The chance of an allocation of size $Z$ being sampled, then, is
+
+$$1-(1-\frac{1}{R})^{Z}$$
+
+and an allocation of size $Z$ contributes a term of
+
+$$Z^2 \frac{(1-\frac{1}{R})^{Z}}{1-(1-\frac{1}{R})^{Z}}.$$
+
+In practical settings, $R$ is large, and so this is well-approximated by
+
+$$Z^2 \frac{e^{-Z/R}}{1 - e^{-Z/R}} .$$
+
+Just to get a sense of the dynamics here, let's look at the behavior for various values of $Z$. When $Z$ is small relative to $R$, we can use $e^z \approx 1 + x$, and conclude that the variance contributed by a small-$Z$ allocation is around
+
+$$Z^2 \frac{1-Z/R}{Z/R} \approx RZ.$$
+
+When $Z$ is comparable to $R$, the variance term is near $Z^2$ (we have $\frac{e^{-Z/R}}{1 - e^{-Z/R}} = 1$ when $Z/R = \ln 2 \approx 0.693$). When $Z$ is large relative to $R$, the variance term goes to zero.
+
+## Picking a sampling strategy
+The fast-path/slow-path dynamics of allocation patterns point us towards the per-byte sampling approach:
+
+ - The quadratic increase in variance per allocation in the first approach is quite costly when heaps have a non-negligible portion of their bytes in those allocations, which is practically often the case.
+ - The Bernoulli-per-byte approach shifts more of its samples towards large allocations, which are already a slow-path.
+ - We drive several tickers (e.g. tcache gc) by bytes allocated, and report bytes-allocated as a user-visible statistic, so we have to do all the necessary bookkeeping anyways.
+
+Indeed, this is the approach we use in jemalloc. Our heap dumps record the size of the allocation and the sampling rate $R$, and jeprof unbiases by dividing by $1 - e^{-Z/R}$. The framework above would suggest dividing by $1-(1-1/R)^Z$; instead, we use the fact that $R$ is large in practical situations, and so $e^{-Z/R}$ is a good approximation (and faster to compute). (Equivalently, we may also see this as the factor that falls out from viewing sampling as a Poisson process directly).
+
+## Consequences for heap dump consumers
+Using this approach means that there are a few things users need to be aware of.
+
+### Stack counts are not proportional to allocation frequencies
+If one stack appears twice as often as another, this by itself does not imply that it allocates twice as often. Consider the case in which there are only two types of allocating call stacks in a program. Stack A allocates 8 bytes, and occurs a million times in a program. Stack B allocates 8 MB, and occurs just once in a program. If our sampling rate $R$ is about 1MB, we expect stack A to show up about 8 times, and stack B to show up once. Stack A isn't 8 times more frequent than stack B, though; it's a million times more frequent.
+
+### Aggregation must be done after unbiasing samples
+Some tools manually parse heap dump output, and aggregate across stacks (or across program runs) to provide wider-scale data analyses. When doing this aggregation, though, it's important to unbias-and-then-sum, rather than sum-and-then-unbias. Reusing our example from the previous section: suppose we collect heap dumps of the program from a million machines. We then have 8 million occurs of stack A (each of 8 bytes), and a million occurrences of stack B (each of 8 MB). If we sum first, we'll attribute 64 MB to stack A, and 8 TB to stack B. Unbiasing changes these numbers by an infinitesimal amount, so that sum-then-unbias dramatically underreports the amount of memory allocated by stack A.
+
+## An avenue for future exploration
+While the framework we laid out above is pretty general, as an engineering decision we're only interested in fairly simple approaches (i.e. ones for which the chance of an allocation being sampled depends only on its size). Our job is then: for each size class $Z$, pick a probability $p_Z$ that an allocation of that size will be sampled. We made some handwave-y references to statistical distributions to justify our choices, but there's no reason we need to pick them that way. Any set of non-zero probabilities is a valid choice.
+The real limiting factor in our ability to reduce estimator variance is that fact that sampling is expensive; we want to make sure we only do it on a small fraction of allocations. Our goal, then, is to pick the $p_Z$ to minimize variance given some maximum sampling rate $P$. If we define $a_Z$ to be the fraction of allocations of size $Z$, and $l_Z$ to be the fraction of allocations of size $Z$ still alive at the time of a heap dump, then we can phrase this as an optimization problem over the choices of $p_Z$:
+
+Minimize
+
+$$ \sum_Z Z^2 l_Z \frac{1-p_Z}{p_Z} $$
+
+subject to
+
+$$ \sum_Z a_Z p_Z \leq P $$
+
+Ignoring a term that doesn't depend on $p_Z$, the objective is minimized whenever
+
+$$ \sum_Z Z^2 l_Z \frac{1}{p_Z} $$
+
+is. For a particular program, $l_Z$ and $a_Z$ are just numbers that can be obtained (exactly) from existing stats introspection facilities, and we have a fairly tractable convex optimization problem (it can be framed as a second-order cone program). It would be interesting to evaluate, for various common allocation patterns, how well our current strategy adapts. Do our actual choices for $p_Z$ closely correspond to the optimal ones? How close is the variance of our choices to the variance of the optimal strategy?
+You can imagine an implementation that actually goes all the way, and makes $p_Z$ selections a tuning parameter. I don't think this is a good use of development time for the foreseeable future; but I do wonder about the answers to some of these questions.
+
+## Implementation realities
+
+The nice story above is at least partially a lie. Initially, jeprof (copying its logic from pprof) had the sum-then-unbias error described above. The current version of jemalloc does the unbiasing step on a per-allocation basis internally, so that we're always tracking what the unbiased numbers "should" be. The problem is, actually surfacing those unbiased numbers would require a breaking change to jeprof (and the various already-deployed tools that have copied its logic). Instead, we use a little bit more trickery. Since we know at dump time the numbers we want jeprof to report, we simply choose the values we'll output so that the jeprof numbers will match the true numbers. The math is described in `src/prof_data.c` (where the only cleverness is a change of variables that lets the exponentials fall out).
+
+This has the effect of making the output of jeprof (and related tools) correct, while making its inputs incorrect. This can be annoying to human readers of raw profiling dump output.
diff --git a/contrib/jemalloc/doc_internal/jemalloc.svg b/contrib/jemalloc/doc_internal/jemalloc.svg
new file mode 100644
index 000000000000..5e77327e66b1
--- /dev/null
+++ b/contrib/jemalloc/doc_internal/jemalloc.svg
@@ -0,0 +1 @@
+<svg id="Layer_3" data-name="Layer 3" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 499 184.27"><defs><style>.cls-1,.cls-3{fill:none;}.cls-2{clip-path:url(#clip-path);}.cls-3{stroke:#262262;stroke-linecap:round;stroke-linejoin:round;stroke-width:4px;}</style><clipPath id="clip-path" transform="translate(-100.66 -259.87)"><path class="cls-1" d="M144.57,396c0,18.2-9.37,27.83-37.33,23.55V400.1c11.11,2.14,12.18-.27,12.18-11.5V324.11h25Zm-12.71-78.66c-9,0-15.52-1.48-15.52-12.71S122.9,292,131.86,292s15.52,1.2,15.52,12.58C147.38,315.55,141,317.29,131.86,317.29Zm50.57,76.39c-30.64,0-35.85-18.86-35.85-35.59s5.61-35.32,35.72-35.32c35.32,0,33.44,28,33.44,40.67H170.12c.54,9.5,4,14.05,11.37,14.05,6.83,0,9.64-3.34,10-7.89l24.75.13C215.48,383.38,205.84,393.68,182.43,393.68Zm-1.47-55c-6.69,0-10,2.81-10.84,12h21.41C190.73,341.9,188.18,338.69,181,338.69Zm112.78,53.65V351.4c0-4.15-1.33-8.16-6-8.16-5,0-6,3.75-6,8.16v40.94H256.42V351.4c0-4.15-.81-8.16-5.89-8.16s-6.29,3.75-6.29,8.16v40.94H219.09V324.11h14l4.15,8c2.67-4.69,10.56-9.37,18.86-9.37,7.36,0,16.19,2.14,21,9.1,3.48-5.22,11.11-9.1,20.21-9.1,19.13,0,21.54,11.37,21.54,27.16v42.41Zm83.09,0L372.41,383c-5.48,7.22-13.11,10.7-24.35,10.7-14.85,0-26.75-6-26.75-19.93,0-15.26,12.44-20.88,44.28-23,0-9.5-1.61-12.57-8.83-12.57-6.69,0-8.56,3.48-8.56,9.9H323.45c0-12.85,6.82-25.29,32.64-25.29,30,0,34.65,14.45,34.65,31.17v38.4Zm-21.54-28.63c-6.29.94-8.3,4.28-8.3,7.36,0,4.28,2.41,6.69,8.3,6.69s10.17-4.82,10.17-15.12ZM396,392.34V297.75h24.75v94.59Zm30.77,0V297.75h24.75v94.59Zm62.21,1.34c-28.09,0-34.11-18.6-34.11-35.32s6.29-35.59,34.38-35.59c27.7,0,34.12,19,34.12,35.59C523.33,375.22,516.91,393.68,488.94,393.68Zm.27-50.84c-7.89,0-11.37,4.82-11.37,15.52s3.61,15.39,11.1,15.39c7.9,0,11.38-4.42,11.38-15.39C500.32,347.79,497.24,342.84,489.21,342.84Zm69.17,50.84c-28.9,0-34.52-18.6-34.52-35.32s5.76-35.59,34.12-35.59c21.14,0,34.52,10.84,34.52,31.17H568.42c0-9.23-5.49-11.23-10.17-11.23-7,0-11.11,4.54-11.11,15.38s4,15.52,11.11,15.52c4.81,0,10-2.41,10-10.57H592.5C592.5,383.38,579,393.68,558.38,393.68Z"/></clipPath></defs><title>jemalloc Final Logo</title><g class="cls-2"><line class="cls-3" x1="345" y1="182.27" x2="345" y2="2"/><line class="cls-3" x1="225" y1="182.27" x2="225" y2="2"/><line class="cls-3" x1="105" y1="182.27" x2="105" y2="2"/><line class="cls-3" x1="43" y1="182.27" x2="43" y2="2"/><line class="cls-3" x1="475" y1="182.27" x2="475" y2="2"/><line class="cls-3" x1="195" y1="182.27" x2="195" y2="2"/><line class="cls-3" x1="75" y1="182.27" x2="75" y2="2"/><line class="cls-3" x1="337" y1="182.27" x2="337" y2="2"/><line class="cls-3" x1="215" y1="182.27" x2="215" y2="2"/><line class="cls-3" x1="95" y1="182.27" x2="95" y2="2"/><line class="cls-3" x1="415" y1="182.27" x2="415" y2="2"/><line class="cls-3" x1="385" y1="182.27" x2="385" y2="2"/><line class="cls-3" x1="183" y1="182.27" x2="183" y2="2"/><line class="cls-3" x1="65" y1="182.27" x2="65" y2="2"/><line class="cls-3" x1="173" y1="182.27" x2="173" y2="2"/><line class="cls-3" x1="145" y1="182.27" x2="145" y2="2"/><line class="cls-3" x1="163" y1="182.27" x2="163" y2="2"/><line class="cls-3" x1="460" y1="182.27" x2="460" y2="2"/><line class="cls-3" x1="281" y1="182.27" x2="281" y2="2"/><line class="cls-3" x1="313" y1="182.27" x2="313" y2="2"/><line class="cls-3" x1="252" y1="182.27" x2="252" y2="2"/><line class="cls-3" x1="450" y1="182.27" x2="450" y2="2"/><line class="cls-3" x1="271" y1="182.27" x2="271" y2="2"/><line class="cls-3" x1="332" y1="182.27" x2="332" y2="2"/><line class="cls-3" x1="203" y1="182.27" x2="203" y2="2"/><line class="cls-3" x1="13" y1="182.27" x2="13" y2="2"/><line class="cls-3" x1="373" y1="182.27" x2="373" y2="2"/><line class="cls-3" x1="354" y1="182.27" x2="354" y2="2"/><line class="cls-3" x1="235" y1="182.27" x2="235" y2="2"/><line class="cls-3" x1="115" y1="182.27" x2="115" y2="2"/><line class="cls-3" x1="53" y1="182.27" x2="53" y2="2"/><line class="cls-3" x1="484" y1="182.27" x2="484" y2="2"/><line class="cls-3" x1="405" y1="182.27" x2="405" y2="2"/><line class="cls-3" x1="85" y1="182.27" x2="85" y2="2"/><line class="cls-3" x1="225" y1="182.27" x2="225" y2="2"/><line class="cls-3" x1="105" y1="182.27" x2="105" y2="2"/><line class="cls-3" x1="43" y1="182.27" x2="43" y2="2"/><line class="cls-3" x1="435" y1="182.27" x2="435" y2="2"/><line class="cls-3" x1="123" y1="182.27" x2="123" y2="2"/><line class="cls-3" x1="75" y1="182.27" x2="75" y2="2"/><line class="cls-3" x1="183" y1="182.27" x2="183" y2="2"/><line class="cls-3" x1="155" y1="182.27" x2="155" y2="2"/><line class="cls-3" x1="173" y1="182.27" x2="173" y2="2"/><line class="cls-3" x1="145" y1="182.27" x2="145" y2="2"/><line class="cls-3" x1="470" y1="182.27" x2="470" y2="2"/><line class="cls-3" x1="292" y1="182.27" x2="292" y2="2"/><line class="cls-3" x1="262" y1="182.27" x2="262" y2="2"/><line class="cls-3" x1="460" y1="182.27" x2="460" y2="2"/><line class="cls-3" x1="281" y1="182.27" x2="281" y2="2"/><line class="cls-3" x1="313" y1="182.27" x2="313" y2="2"/><line class="cls-3" x1="243" y1="182.27" x2="243" y2="2"/><line class="cls-3" x1="22" y1="182.27" x2="22" y2="2"/><line class="cls-3" x1="383" y1="182.27" x2="383" y2="2"/><line class="cls-3" x1="5" y1="182.27" x2="5" y2="2"/><line class="cls-3" x1="133" y1="182.27" x2="133" y2="2"/><line class="cls-3" x1="362" y1="182.27" x2="362" y2="2"/><line class="cls-3" x1="288" y1="182.27" x2="288" y2="2"/><line class="cls-3" x1="298" y1="182.27" x2="298" y2="2"/><line class="cls-3" x1="423" y1="182.27" x2="423" y2="2"/><line class="cls-3" x1="369" y1="182.27" x2="369" y2="2"/><line class="cls-3" x1="490" y1="182.27" x2="490" y2="2"/><line class="cls-3" x1="2" y1="182.27" x2="2" y2="2"/><line class="cls-3" x1="493" y1="182.27" x2="493" y2="2"/><line class="cls-3" x1="225" y1="182.27" x2="225" y2="2"/><line class="cls-3" x1="105" y1="182.27" x2="105" y2="2"/><line class="cls-3" x1="43" y1="182.27" x2="43" y2="2"/><line class="cls-3" x1="475" y1="182.27" x2="475" y2="2"/><line class="cls-3" x1="195" y1="182.27" x2="195" y2="2"/><line class="cls-3" x1="75" y1="182.27" x2="75" y2="2"/><line class="cls-3" x1="337" y1="182.27" x2="337" y2="2"/><line class="cls-3" x1="215" y1="182.27" x2="215" y2="2"/><line class="cls-3" x1="95" y1="182.27" x2="95" y2="2"/><line class="cls-3" x1="415" y1="182.27" x2="415" y2="2"/><line class="cls-3" x1="385" y1="182.27" x2="385" y2="2"/><line class="cls-3" x1="183" y1="182.27" x2="183" y2="2"/><line class="cls-3" x1="65" y1="182.27" x2="65" y2="2"/><line class="cls-3" x1="173" y1="182.27" x2="173" y2="2"/><line class="cls-3" x1="145" y1="182.27" x2="145" y2="2"/><line class="cls-3" x1="163" y1="182.27" x2="163" y2="2"/><line class="cls-3" x1="460" y1="182.27" x2="460" y2="2"/><line class="cls-3" x1="281" y1="182.27" x2="281" y2="2"/><line class="cls-3" x1="313" y1="182.27" x2="313" y2="2"/><line class="cls-3" x1="252" y1="182.27" x2="252" y2="2"/><line class="cls-3" x1="450" y1="182.27" x2="450" y2="2"/><line class="cls-3" x1="271" y1="182.27" x2="271" y2="2"/><line class="cls-3" x1="306" y1="182.27" x2="306" y2="2"/><line class="cls-3" x1="203" y1="182.27" x2="203" y2="2"/><line class="cls-3" x1="13" y1="182.27" x2="13" y2="2"/><line class="cls-3" x1="373" y1="182.27" x2="373" y2="2"/><line class="cls-3" x1="354" y1="182.27" x2="354" y2="2"/><line class="cls-3" x1="235" y1="182.27" x2="235" y2="2"/><line class="cls-3" x1="115" y1="182.27" x2="115" y2="2"/><line class="cls-3" x1="53" y1="182.27" x2="53" y2="2"/><line class="cls-3" x1="484" y1="182.27" x2="484" y2="2"/><line class="cls-3" x1="405" y1="182.27" x2="405" y2="2"/><line class="cls-3" x1="85" y1="182.27" x2="85" y2="2"/><line class="cls-3" x1="225" y1="182.27" x2="225" y2="2"/><line class="cls-3" x1="105" y1="182.27" x2="105" y2="2"/><line class="cls-3" x1="43" y1="182.27" x2="43" y2="2"/><line class="cls-3" x1="435" y1="182.27" x2="435" y2="2"/><line class="cls-3" x1="123" y1="182.27" x2="123" y2="2"/><line class="cls-3" x1="75" y1="182.27" x2="75" y2="2"/><line class="cls-3" x1="183" y1="182.27" x2="183" y2="2"/><line class="cls-3" x1="155" y1="182.27" x2="155" y2="2"/><line class="cls-3" x1="173" y1="182.27" x2="173" y2="2"/><line class="cls-3" x1="145" y1="182.27" x2="145" y2="2"/><line class="cls-3" x1="470" y1="182.27" x2="470" y2="2"/><line class="cls-3" x1="292" y1="182.27" x2="292" y2="2"/><line class="cls-3" x1="262" y1="182.27" x2="262" y2="2"/><line class="cls-3" x1="460" y1="182.27" x2="460" y2="2"/><line class="cls-3" x1="281" y1="182.27" x2="281" y2="2"/><line class="cls-3" x1="328" y1="182.27" x2="328" y2="2"/><line class="cls-3" x1="243" y1="182.27" x2="243" y2="2"/><line class="cls-3" x1="22" y1="182.27" x2="22" y2="2"/><line class="cls-3" x1="383" y1="182.27" x2="383" y2="2"/><line class="cls-3" x1="5" y1="182.27" x2="5" y2="2"/><line class="cls-3" x1="32" y1="182.27" x2="32" y2="2"/><line class="cls-3" x1="133" y1="182.27" x2="133" y2="2"/><line class="cls-3" x1="362" y1="182.27" x2="362" y2="2"/><line class="cls-3" x1="288" y1="182.27" x2="288" y2="2"/><line class="cls-3" x1="298" y1="182.27" x2="298" y2="2"/><line class="cls-3" x1="423" y1="182.27" x2="423" y2="2"/><line class="cls-3" x1="369" y1="182.27" x2="369" y2="2"/><line class="cls-3" x1="490" y1="182.27" x2="490" y2="2"/><line class="cls-3" x1="2" y1="182.27" x2="2" y2="2"/><line class="cls-3" x1="493" y1="182.27" x2="493" y2="2"/><line class="cls-3" x1="349" y1="182.27" x2="349" y2="2"/><line class="cls-3" x1="229" y1="182.27" x2="229" y2="2"/><line class="cls-3" x1="109" y1="182.27" x2="109" y2="2"/><line class="cls-3" x1="47" y1="182.27" x2="47" y2="2"/><line class="cls-3" x1="479" y1="182.27" x2="479" y2="2"/><line class="cls-3" x1="399" y1="182.27" x2="399" y2="2"/><line class="cls-3" x1="199" y1="182.27" x2="199" y2="2"/><line class="cls-3" x1="79" y1="182.27" x2="79" y2="2"/><line class="cls-3" x1="341" y1="182.27" x2="341" y2="2"/><line class="cls-3" x1="219" y1="182.27" x2="219" y2="2"/><line class="cls-3" x1="99" y1="182.27" x2="99" y2="2"/><line class="cls-3" x1="41" y1="182.27" x2="41" y2="2"/><line class="cls-3" x1="419" y1="182.27" x2="419" y2="2"/><line class="cls-3" x1="389" y1="182.27" x2="389" y2="2"/><line class="cls-3" x1="187" y1="182.27" x2="187" y2="2"/><line class="cls-3" x1="69" y1="182.27" x2="69" y2="2"/><line class="cls-3" x1="177" y1="182.27" x2="177" y2="2"/><line class="cls-3" x1="149" y1="182.27" x2="149" y2="2"/><line class="cls-3" x1="464" y1="182.27" x2="464" y2="2"/><line class="cls-3" x1="285" y1="182.27" x2="285" y2="2"/><line class="cls-3" x1="317" y1="182.27" x2="317" y2="2"/><line class="cls-3" x1="454" y1="182.27" x2="454" y2="2"/><line class="cls-3" x1="275" y1="182.27" x2="275" y2="2"/><line class="cls-3" x1="308" y1="182.27" x2="308" y2="2"/><line class="cls-3" x1="207" y1="182.27" x2="207" y2="2"/><line class="cls-3" x1="17" y1="182.27" x2="17" y2="2"/><line class="cls-3" x1="377" y1="182.27" x2="377" y2="2"/><line class="cls-3" x1="358" y1="182.27" x2="358" y2="2"/><line class="cls-3" x1="238" y1="182.27" x2="238" y2="2"/><line class="cls-3" x1="119" y1="182.27" x2="119" y2="2"/><line class="cls-3" x1="488" y1="182.27" x2="488" y2="2"/><line class="cls-3" x1="409" y1="182.27" x2="409" y2="2"/><line class="cls-3" x1="229" y1="182.27" x2="229" y2="2"/><line class="cls-3" x1="109" y1="182.27" x2="109" y2="2"/><line class="cls-3" x1="47" y1="182.27" x2="47" y2="2"/><line class="cls-3" x1="439" y1="182.27" x2="439" y2="2"/><line class="cls-3" x1="399" y1="182.27" x2="399" y2="2"/><line class="cls-3" x1="127" y1="182.27" x2="127" y2="2"/><line class="cls-3" x1="79" y1="182.27" x2="79" y2="2"/><line class="cls-3" x1="187" y1="182.27" x2="187" y2="2"/><line class="cls-3" x1="159" y1="182.27" x2="159" y2="2"/><line class="cls-3" x1="177" y1="182.27" x2="177" y2="2"/><line class="cls-3" x1="149" y1="182.27" x2="149" y2="2"/><line class="cls-3" x1="474" y1="182.27" x2="474" y2="2"/><line class="cls-3" x1="266" y1="182.27" x2="266" y2="2"/><line class="cls-3" x1="464" y1="182.27" x2="464" y2="2"/><line class="cls-3" x1="285" y1="182.27" x2="285" y2="2"/><line class="cls-3" x1="317" y1="182.27" x2="317" y2="2"/><line class="cls-3" x1="247" y1="182.27" x2="247" y2="2"/><line class="cls-3" x1="26" y1="182.27" x2="26" y2="2"/><line class="cls-3" x1="387" y1="182.27" x2="387" y2="2"/><line class="cls-3" x1="9" y1="182.27" x2="9" y2="2"/><line class="cls-3" x1="137" y1="182.27" x2="137" y2="2"/><line class="cls-3" x1="292" y1="182.27" x2="292" y2="2"/><line class="cls-3" x1="373" y1="182.27" x2="373" y2="2"/><line class="cls-3" x1="56" y1="182.27" x2="56" y2="2"/><line class="cls-3" x1="494" y1="182.27" x2="494" y2="2"/><line class="cls-3" x1="497" y1="182.27" x2="497" y2="2"/><line class="cls-3" x1="349" y1="182.27" x2="349" y2="2"/><line class="cls-3" x1="229" y1="182.27" x2="229" y2="2"/><line class="cls-3" x1="109" y1="182.27" x2="109" y2="2"/><line class="cls-3" x1="47" y1="182.27" x2="47" y2="2"/><line class="cls-3" x1="479" y1="182.27" x2="479" y2="2"/><line class="cls-3" x1="399" y1="182.27" x2="399" y2="2"/><line class="cls-3" x1="199" y1="182.27" x2="199" y2="2"/><line class="cls-3" x1="79" y1="182.27" x2="79" y2="2"/><line class="cls-3" x1="341" y1="182.27" x2="341" y2="2"/><line class="cls-3" x1="219" y1="182.27" x2="219" y2="2"/><line class="cls-3" x1="99" y1="182.27" x2="99" y2="2"/><line class="cls-3" x1="41" y1="182.27" x2="41" y2="2"/><line class="cls-3" x1="419" y1="182.27" x2="419" y2="2"/><line class="cls-3" x1="389" y1="182.27" x2="389" y2="2"/><line class="cls-3" x1="187" y1="182.27" x2="187" y2="2"/><line class="cls-3" x1="69" y1="182.27" x2="69" y2="2"/><line class="cls-3" x1="177" y1="182.27" x2="177" y2="2"/><line class="cls-3" x1="149" y1="182.27" x2="149" y2="2"/><line class="cls-3" x1="141" y1="182.27" x2="141" y2="2"/><line class="cls-3" x1="464" y1="182.27" x2="464" y2="2"/><line class="cls-3" x1="285" y1="182.27" x2="285" y2="2"/><line class="cls-3" x1="317" y1="182.27" x2="317" y2="2"/><line class="cls-3" x1="454" y1="182.27" x2="454" y2="2"/><line class="cls-3" x1="275" y1="182.27" x2="275" y2="2"/><line class="cls-3" x1="308" y1="182.27" x2="308" y2="2"/><line class="cls-3" x1="207" y1="182.27" x2="207" y2="2"/><line class="cls-3" x1="17" y1="182.27" x2="17" y2="2"/><line class="cls-3" x1="377" y1="182.27" x2="377" y2="2"/><line class="cls-3" x1="119" y1="182.27" x2="119" y2="2"/><line class="cls-3" x1="488" y1="182.27" x2="488" y2="2"/><line class="cls-3" x1="409" y1="182.27" x2="409" y2="2"/><line class="cls-3" x1="229" y1="182.27" x2="229" y2="2"/><line class="cls-3" x1="109" y1="182.27" x2="109" y2="2"/><line class="cls-3" x1="47" y1="182.27" x2="47" y2="2"/><line class="cls-3" x1="439" y1="182.27" x2="439" y2="2"/><line class="cls-3" x1="399" y1="182.27" x2="399" y2="2"/><line class="cls-3" x1="127" y1="182.27" x2="127" y2="2"/><line class="cls-3" x1="79" y1="182.27" x2="79" y2="2"/><line class="cls-3" x1="187" y1="182.27" x2="187" y2="2"/><line class="cls-3" x1="159" y1="182.27" x2="159" y2="2"/><line class="cls-3" x1="177" y1="182.27" x2="177" y2="2"/><line class="cls-3" x1="149" y1="182.27" x2="149" y2="2"/><line class="cls-3" x1="474" y1="182.27" x2="474" y2="2"/><line class="cls-3" x1="295" y1="182.27" x2="295" y2="2"/><line class="cls-3" x1="266" y1="182.27" x2="266" y2="2"/><line class="cls-3" x1="464" y1="182.27" x2="464" y2="2"/><line class="cls-3" x1="285" y1="182.27" x2="285" y2="2"/><line class="cls-3" x1="317" y1="182.27" x2="317" y2="2"/><line class="cls-3" x1="247" y1="182.27" x2="247" y2="2"/><line class="cls-3" x1="58" y1="182.27" x2="58" y2="2"/><line class="cls-3" x1="387" y1="182.27" x2="387" y2="2"/><line class="cls-3" x1="9" y1="182.27" x2="9" y2="2"/><line class="cls-3" x1="292" y1="182.27" x2="292" y2="2"/><line class="cls-3" x1="301" y1="182.27" x2="301" y2="2"/><line class="cls-3" x1="428" y1="182.27" x2="428" y2="2"/><line class="cls-3" x1="373" y1="182.27" x2="373" y2="2"/><line class="cls-3" x1="56" y1="182.27" x2="56" y2="2"/><line class="cls-3" x1="494" y1="182.27" x2="494" y2="2"/><line class="cls-3" x1="497" y1="182.27" x2="497" y2="2"/></g></svg> \ No newline at end of file
diff --git a/contrib/jemalloc/include/jemalloc/internal/activity_callback.h b/contrib/jemalloc/include/jemalloc/internal/activity_callback.h
new file mode 100644
index 000000000000..6c2e84e3180d
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/internal/activity_callback.h
@@ -0,0 +1,23 @@
+#ifndef JEMALLOC_INTERNAL_ACTIVITY_CALLBACK_H
+#define JEMALLOC_INTERNAL_ACTIVITY_CALLBACK_H
+
+/*
+ * The callback to be executed "periodically", in response to some amount of
+ * allocator activity.
+ *
+ * This callback need not be computing any sort of peak (although that's the
+ * intended first use case), but we drive it from the peak counter, so it's
+ * keeps things tidy to keep it here.
+ *
+ * The calls to this thunk get driven by the peak_event module.
+ */
+#define ACTIVITY_CALLBACK_THUNK_INITIALIZER {NULL, NULL}
+typedef void (*activity_callback_t)(void *uctx, uint64_t allocated,
+ uint64_t deallocated);
+typedef struct activity_callback_thunk_s activity_callback_thunk_t;
+struct activity_callback_thunk_s {
+ activity_callback_t callback;
+ void *uctx;
+};
+
+#endif /* JEMALLOC_INTERNAL_ACTIVITY_CALLBACK_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/arena_externs.h b/contrib/jemalloc/include/jemalloc/internal/arena_externs.h
index a4523ae0c494..e6fceaafea50 100644
--- a/contrib/jemalloc/include/jemalloc/internal/arena_externs.h
+++ b/contrib/jemalloc/include/jemalloc/internal/arena_externs.h
@@ -2,59 +2,67 @@
#define JEMALLOC_INTERNAL_ARENA_EXTERNS_H
#include "jemalloc/internal/bin.h"
+#include "jemalloc/internal/div.h"
#include "jemalloc/internal/extent_dss.h"
#include "jemalloc/internal/hook.h"
#include "jemalloc/internal/pages.h"
#include "jemalloc/internal/stats.h"
+/*
+ * When the amount of pages to be purged exceeds this amount, deferred purge
+ * should happen.
+ */
+#define ARENA_DEFERRED_PURGE_NPAGES_THRESHOLD UINT64_C(1024)
+
extern ssize_t opt_dirty_decay_ms;
extern ssize_t opt_muzzy_decay_ms;
extern percpu_arena_mode_t opt_percpu_arena;
extern const char *percpu_arena_mode_names[];
-extern const uint64_t h_steps[SMOOTHSTEP_NSTEPS];
+extern div_info_t arena_binind_div_info[SC_NBINS];
+
extern malloc_mutex_t arenas_lock;
+extern emap_t arena_emap_global;
extern size_t opt_oversize_threshold;
extern size_t oversize_threshold;
+/*
+ * arena_bin_offsets[binind] is the offset of the first bin shard for size class
+ * binind.
+ */
+extern uint32_t arena_bin_offsets[SC_NBINS];
+
void arena_basic_stats_merge(tsdn_t *tsdn, arena_t *arena,
unsigned *nthreads, const char **dss, ssize_t *dirty_decay_ms,
ssize_t *muzzy_decay_ms, size_t *nactive, size_t *ndirty, size_t *nmuzzy);
void arena_stats_merge(tsdn_t *tsdn, arena_t *arena, unsigned *nthreads,
const char **dss, ssize_t *dirty_decay_ms, ssize_t *muzzy_decay_ms,
size_t *nactive, size_t *ndirty, size_t *nmuzzy, arena_stats_t *astats,
- bin_stats_t *bstats, arena_stats_large_t *lstats,
- arena_stats_extents_t *estats);
-void arena_extents_dirty_dalloc(tsdn_t *tsdn, arena_t *arena,
- extent_hooks_t **r_extent_hooks, extent_t *extent);
-#ifdef JEMALLOC_JET
-size_t arena_slab_regind(extent_t *slab, szind_t binind, const void *ptr);
-#endif
-extent_t *arena_extent_alloc_large(tsdn_t *tsdn, arena_t *arena,
- size_t usize, size_t alignment, bool *zero);
+ bin_stats_data_t *bstats, arena_stats_large_t *lstats,
+ pac_estats_t *estats, hpa_shard_stats_t *hpastats, sec_stats_t *secstats);
+void arena_handle_deferred_work(tsdn_t *tsdn, arena_t *arena);
+edata_t *arena_extent_alloc_large(tsdn_t *tsdn, arena_t *arena,
+ size_t usize, size_t alignment, bool zero);
void arena_extent_dalloc_large_prep(tsdn_t *tsdn, arena_t *arena,
- extent_t *extent);
+ edata_t *edata);
void arena_extent_ralloc_large_shrink(tsdn_t *tsdn, arena_t *arena,
- extent_t *extent, size_t oldsize);
+ edata_t *edata, size_t oldsize);
void arena_extent_ralloc_large_expand(tsdn_t *tsdn, arena_t *arena,
- extent_t *extent, size_t oldsize);
-ssize_t arena_dirty_decay_ms_get(arena_t *arena);
-bool arena_dirty_decay_ms_set(tsdn_t *tsdn, arena_t *arena, ssize_t decay_ms);
-ssize_t arena_muzzy_decay_ms_get(arena_t *arena);
-bool arena_muzzy_decay_ms_set(tsdn_t *tsdn, arena_t *arena, ssize_t decay_ms);
+ edata_t *edata, size_t oldsize);
+bool arena_decay_ms_set(tsdn_t *tsdn, arena_t *arena, extent_state_t state,
+ ssize_t decay_ms);
+ssize_t arena_decay_ms_get(arena_t *arena, extent_state_t state);
void arena_decay(tsdn_t *tsdn, arena_t *arena, bool is_background_thread,
bool all);
+uint64_t arena_time_until_deferred(tsdn_t *tsdn, arena_t *arena);
+void arena_do_deferred_work(tsdn_t *tsdn, arena_t *arena);
void arena_reset(tsd_t *tsd, arena_t *arena);
void arena_destroy(tsd_t *tsd, arena_t *arena);
-void arena_tcache_fill_small(tsdn_t *tsdn, arena_t *arena, tcache_t *tcache,
- cache_bin_t *tbin, szind_t binind, uint64_t prof_accumbytes);
-void arena_alloc_junk_small(void *ptr, const bin_info_t *bin_info,
- bool zero);
-
-typedef void (arena_dalloc_junk_small_t)(void *, const bin_info_t *);
-extern arena_dalloc_junk_small_t *JET_MUTABLE arena_dalloc_junk_small;
+void arena_cache_bin_fill_small(tsdn_t *tsdn, arena_t *arena,
+ cache_bin_t *cache_bin, cache_bin_info_t *cache_bin_info, szind_t binind,
+ const unsigned nfill);
void *arena_malloc_hard(tsdn_t *tsdn, arena_t *arena, size_t size,
szind_t ind, bool zero);
@@ -63,8 +71,12 @@ void *arena_palloc(tsdn_t *tsdn, arena_t *arena, size_t usize,
void arena_prof_promote(tsdn_t *tsdn, void *ptr, size_t usize);
void arena_dalloc_promoted(tsdn_t *tsdn, void *ptr, tcache_t *tcache,
bool slow_path);
-void arena_dalloc_bin_junked_locked(tsdn_t *tsdn, arena_t *arena, bin_t *bin,
- szind_t binind, extent_t *extent, void *ptr);
+void arena_slab_dalloc(tsdn_t *tsdn, arena_t *arena, edata_t *slab);
+
+void arena_dalloc_bin_locked_handle_newly_empty(tsdn_t *tsdn, arena_t *arena,
+ edata_t *slab, bin_t *bin);
+void arena_dalloc_bin_locked_handle_newly_nonempty(tsdn_t *tsdn, arena_t *arena,
+ edata_t *slab, bin_t *bin);
void arena_dalloc_small(tsdn_t *tsdn, void *ptr);
bool arena_ralloc_no_move(tsdn_t *tsdn, void *ptr, size_t oldsize, size_t size,
size_t extra, bool zero, size_t *newsize);
@@ -72,6 +84,9 @@ void *arena_ralloc(tsdn_t *tsdn, arena_t *arena, void *ptr, size_t oldsize,
size_t size, size_t alignment, bool zero, tcache_t *tcache,
hook_ralloc_args_t *hook_args);
dss_prec_t arena_dss_prec_get(arena_t *arena);
+ehooks_t *arena_get_ehooks(arena_t *arena);
+extent_hooks_t *arena_set_extent_hooks(tsd_t *tsd, arena_t *arena,
+ extent_hooks_t *extent_hooks);
bool arena_dss_prec_set(arena_t *arena, dss_prec_t dss_prec);
ssize_t arena_dirty_decay_ms_default_get(void);
bool arena_dirty_decay_ms_default_set(ssize_t decay_ms);
@@ -82,14 +97,15 @@ bool arena_retain_grow_limit_get_set(tsd_t *tsd, arena_t *arena,
unsigned arena_nthreads_get(arena_t *arena, bool internal);
void arena_nthreads_inc(arena_t *arena, bool internal);
void arena_nthreads_dec(arena_t *arena, bool internal);
-size_t arena_extent_sn_next(arena_t *arena);
-arena_t *arena_new(tsdn_t *tsdn, unsigned ind, extent_hooks_t *extent_hooks);
+arena_t *arena_new(tsdn_t *tsdn, unsigned ind, const arena_config_t *config);
bool arena_init_huge(void);
bool arena_is_huge(unsigned arena_ind);
arena_t *arena_choose_huge(tsd_t *tsd);
-bin_t *arena_bin_choose_lock(tsdn_t *tsdn, arena_t *arena, szind_t binind,
+bin_t *arena_bin_choose(tsdn_t *tsdn, arena_t *arena, szind_t binind,
unsigned *binshard);
-void arena_boot(sc_data_t *sc_data);
+size_t arena_fill_small_fresh(tsdn_t *tsdn, arena_t *arena, szind_t binind,
+ void **ptrs, size_t nfill, bool zero);
+bool arena_boot(sc_data_t *sc_data, base_t *base, bool hpa);
void arena_prefork0(tsdn_t *tsdn, arena_t *arena);
void arena_prefork1(tsdn_t *tsdn, arena_t *arena);
void arena_prefork2(tsdn_t *tsdn, arena_t *arena);
@@ -98,6 +114,7 @@ void arena_prefork4(tsdn_t *tsdn, arena_t *arena);
void arena_prefork5(tsdn_t *tsdn, arena_t *arena);
void arena_prefork6(tsdn_t *tsdn, arena_t *arena);
void arena_prefork7(tsdn_t *tsdn, arena_t *arena);
+void arena_prefork8(tsdn_t *tsdn, arena_t *arena);
void arena_postfork_parent(tsdn_t *tsdn, arena_t *arena);
void arena_postfork_child(tsdn_t *tsdn, arena_t *arena);
diff --git a/contrib/jemalloc/include/jemalloc/internal/arena_inlines_a.h b/contrib/jemalloc/include/jemalloc/internal/arena_inlines_a.h
index 9abf7f6ac702..8568358c7fb6 100644
--- a/contrib/jemalloc/include/jemalloc/internal/arena_inlines_a.h
+++ b/contrib/jemalloc/include/jemalloc/internal/arena_inlines_a.h
@@ -3,7 +3,7 @@
static inline unsigned
arena_ind_get(const arena_t *arena) {
- return base_ind_get(arena->base);
+ return arena->ind;
}
static inline void
@@ -21,37 +21,4 @@ arena_internal_get(arena_t *arena) {
return atomic_load_zu(&arena->stats.internal, ATOMIC_RELAXED);
}
-static inline bool
-arena_prof_accum(tsdn_t *tsdn, arena_t *arena, uint64_t accumbytes) {
- cassert(config_prof);
-
- if (likely(prof_interval == 0 || !prof_active_get_unlocked())) {
- return false;
- }
-
- return prof_accum_add(tsdn, &arena->prof_accum, accumbytes);
-}
-
-static inline void
-percpu_arena_update(tsd_t *tsd, unsigned cpu) {
- assert(have_percpu_arena);
- arena_t *oldarena = tsd_arena_get(tsd);
- assert(oldarena != NULL);
- unsigned oldind = arena_ind_get(oldarena);
-
- if (oldind != cpu) {
- unsigned newind = cpu;
- arena_t *newarena = arena_get(tsd_tsdn(tsd), newind, true);
- assert(newarena != NULL);
-
- /* Set new arena/tcache associations. */
- arena_migrate(tsd, oldind, newind);
- tcache_t *tcache = tcache_get(tsd);
- if (tcache != NULL) {
- tcache_arena_reassociate(tsd_tsdn(tsd), tcache,
- newarena);
- }
- }
-}
-
#endif /* JEMALLOC_INTERNAL_ARENA_INLINES_A_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/arena_inlines_b.h b/contrib/jemalloc/include/jemalloc/internal/arena_inlines_b.h
index dd926575fc8a..fa81537c469d 100644
--- a/contrib/jemalloc/include/jemalloc/internal/arena_inlines_b.h
+++ b/contrib/jemalloc/include/jemalloc/internal/arena_inlines_b.h
@@ -1,16 +1,20 @@
#ifndef JEMALLOC_INTERNAL_ARENA_INLINES_B_H
#define JEMALLOC_INTERNAL_ARENA_INLINES_B_H
+#include "jemalloc/internal/div.h"
+#include "jemalloc/internal/emap.h"
#include "jemalloc/internal/jemalloc_internal_types.h"
#include "jemalloc/internal/mutex.h"
#include "jemalloc/internal/rtree.h"
+#include "jemalloc/internal/safety_check.h"
#include "jemalloc/internal/sc.h"
#include "jemalloc/internal/sz.h"
#include "jemalloc/internal/ticker.h"
-JEMALLOC_ALWAYS_INLINE bool
-arena_has_default_hooks(arena_t *arena) {
- return (extent_hooks_get(arena) == &extent_hooks_default);
+static inline arena_t *
+arena_get_from_edata(edata_t *edata) {
+ return (arena_t *)atomic_load_p(&arenas[edata_arena_ind_get(edata)],
+ ATOMIC_RELAXED);
}
JEMALLOC_ALWAYS_INLINE arena_t *
@@ -34,127 +38,109 @@ arena_choose_maybe_huge(tsd_t *tsd, arena_t *arena, size_t size) {
return arena_choose(tsd, NULL);
}
-JEMALLOC_ALWAYS_INLINE prof_tctx_t *
-arena_prof_tctx_get(tsdn_t *tsdn, const void *ptr, alloc_ctx_t *alloc_ctx) {
+JEMALLOC_ALWAYS_INLINE void
+arena_prof_info_get(tsd_t *tsd, const void *ptr, emap_alloc_ctx_t *alloc_ctx,
+ prof_info_t *prof_info, bool reset_recent) {
cassert(config_prof);
assert(ptr != NULL);
+ assert(prof_info != NULL);
+
+ edata_t *edata = NULL;
+ bool is_slab;
/* Static check. */
if (alloc_ctx == NULL) {
- const extent_t *extent = iealloc(tsdn, ptr);
- if (unlikely(!extent_slab_get(extent))) {
- return large_prof_tctx_get(tsdn, extent);
- }
+ edata = emap_edata_lookup(tsd_tsdn(tsd), &arena_emap_global,
+ ptr);
+ is_slab = edata_slab_get(edata);
+ } else if (unlikely(!(is_slab = alloc_ctx->slab))) {
+ edata = emap_edata_lookup(tsd_tsdn(tsd), &arena_emap_global,
+ ptr);
+ }
+
+ if (unlikely(!is_slab)) {
+ /* edata must have been initialized at this point. */
+ assert(edata != NULL);
+ large_prof_info_get(tsd, edata, prof_info, reset_recent);
} else {
- if (unlikely(!alloc_ctx->slab)) {
- return large_prof_tctx_get(tsdn, iealloc(tsdn, ptr));
- }
+ prof_info->alloc_tctx = (prof_tctx_t *)(uintptr_t)1U;
+ /*
+ * No need to set other fields in prof_info; they will never be
+ * accessed if (uintptr_t)alloc_tctx == (uintptr_t)1U.
+ */
}
- return (prof_tctx_t *)(uintptr_t)1U;
}
JEMALLOC_ALWAYS_INLINE void
-arena_prof_tctx_set(tsdn_t *tsdn, const void *ptr, size_t usize,
- alloc_ctx_t *alloc_ctx, prof_tctx_t *tctx) {
+arena_prof_tctx_reset(tsd_t *tsd, const void *ptr,
+ emap_alloc_ctx_t *alloc_ctx) {
cassert(config_prof);
assert(ptr != NULL);
/* Static check. */
if (alloc_ctx == NULL) {
- extent_t *extent = iealloc(tsdn, ptr);
- if (unlikely(!extent_slab_get(extent))) {
- large_prof_tctx_set(tsdn, extent, tctx);
+ edata_t *edata = emap_edata_lookup(tsd_tsdn(tsd),
+ &arena_emap_global, ptr);
+ if (unlikely(!edata_slab_get(edata))) {
+ large_prof_tctx_reset(edata);
}
} else {
if (unlikely(!alloc_ctx->slab)) {
- large_prof_tctx_set(tsdn, iealloc(tsdn, ptr), tctx);
+ edata_t *edata = emap_edata_lookup(tsd_tsdn(tsd),
+ &arena_emap_global, ptr);
+ large_prof_tctx_reset(edata);
}
}
}
-static inline void
-arena_prof_tctx_reset(tsdn_t *tsdn, const void *ptr, prof_tctx_t *tctx) {
+JEMALLOC_ALWAYS_INLINE void
+arena_prof_tctx_reset_sampled(tsd_t *tsd, const void *ptr) {
cassert(config_prof);
assert(ptr != NULL);
- extent_t *extent = iealloc(tsdn, ptr);
- assert(!extent_slab_get(extent));
+ edata_t *edata = emap_edata_lookup(tsd_tsdn(tsd), &arena_emap_global,
+ ptr);
+ assert(!edata_slab_get(edata));
- large_prof_tctx_reset(tsdn, extent);
-}
-
-JEMALLOC_ALWAYS_INLINE nstime_t
-arena_prof_alloc_time_get(tsdn_t *tsdn, const void *ptr,
- alloc_ctx_t *alloc_ctx) {
- cassert(config_prof);
- assert(ptr != NULL);
-
- extent_t *extent = iealloc(tsdn, ptr);
- /*
- * Unlike arena_prof_prof_tctx_{get, set}, we only call this once we're
- * sure we have a sampled allocation.
- */
- assert(!extent_slab_get(extent));
- return large_prof_alloc_time_get(extent);
+ large_prof_tctx_reset(edata);
}
JEMALLOC_ALWAYS_INLINE void
-arena_prof_alloc_time_set(tsdn_t *tsdn, const void *ptr, alloc_ctx_t *alloc_ctx,
- nstime_t t) {
+arena_prof_info_set(tsd_t *tsd, edata_t *edata, prof_tctx_t *tctx,
+ size_t size) {
cassert(config_prof);
- assert(ptr != NULL);
- extent_t *extent = iealloc(tsdn, ptr);
- assert(!extent_slab_get(extent));
- large_prof_alloc_time_set(extent, t);
+ assert(!edata_slab_get(edata));
+ large_prof_info_set(edata, tctx, size);
}
JEMALLOC_ALWAYS_INLINE void
arena_decay_ticks(tsdn_t *tsdn, arena_t *arena, unsigned nticks) {
- tsd_t *tsd;
- ticker_t *decay_ticker;
-
if (unlikely(tsdn_null(tsdn))) {
return;
}
- tsd = tsdn_tsd(tsdn);
- decay_ticker = decay_ticker_get(tsd, arena_ind_get(arena));
- if (unlikely(decay_ticker == NULL)) {
- return;
- }
- if (unlikely(ticker_ticks(decay_ticker, nticks))) {
+ tsd_t *tsd = tsdn_tsd(tsdn);
+ /*
+ * We use the ticker_geom_t to avoid having per-arena state in the tsd.
+ * Instead of having a countdown-until-decay timer running for every
+ * arena in every thread, we flip a coin once per tick, whose
+ * probability of coming up heads is 1/nticks; this is effectively the
+ * operation of the ticker_geom_t. Each arena has the same chance of a
+ * coinflip coming up heads (1/ARENA_DECAY_NTICKS_PER_UPDATE), so we can
+ * use a single ticker for all of them.
+ */
+ ticker_geom_t *decay_ticker = tsd_arena_decay_tickerp_get(tsd);
+ uint64_t *prng_state = tsd_prng_statep_get(tsd);
+ if (unlikely(ticker_geom_ticks(decay_ticker, prng_state, nticks))) {
arena_decay(tsdn, arena, false, false);
}
}
JEMALLOC_ALWAYS_INLINE void
arena_decay_tick(tsdn_t *tsdn, arena_t *arena) {
- malloc_mutex_assert_not_owner(tsdn, &arena->decay_dirty.mtx);
- malloc_mutex_assert_not_owner(tsdn, &arena->decay_muzzy.mtx);
-
arena_decay_ticks(tsdn, arena, 1);
}
-/* Purge a single extent to retained / unmapped directly. */
-JEMALLOC_ALWAYS_INLINE void
-arena_decay_extent(tsdn_t *tsdn,arena_t *arena, extent_hooks_t **r_extent_hooks,
- extent_t *extent) {
- size_t extent_size = extent_size_get(extent);
- extent_dalloc_wrapper(tsdn, arena,
- r_extent_hooks, extent);
- if (config_stats) {
- /* Update stats accordingly. */
- arena_stats_lock(tsdn, &arena->stats);
- arena_stats_add_u64(tsdn, &arena->stats,
- &arena->decay_dirty.stats->nmadvise, 1);
- arena_stats_add_u64(tsdn, &arena->stats,
- &arena->decay_dirty.stats->purged, extent_size >> LG_PAGE);
- arena_stats_sub_zu(tsdn, &arena->stats, &arena->stats.mapped,
- extent_size);
- arena_stats_unlock(tsdn, &arena->stats);
- }
-}
-
JEMALLOC_ALWAYS_INLINE void *
arena_malloc(tsdn_t *tsdn, arena_t *arena, size_t size, szind_t ind, bool zero,
tcache_t *tcache, bool slow_path) {
@@ -178,21 +164,19 @@ arena_malloc(tsdn_t *tsdn, arena_t *arena, size_t size, szind_t ind, bool zero,
JEMALLOC_ALWAYS_INLINE arena_t *
arena_aalloc(tsdn_t *tsdn, const void *ptr) {
- return extent_arena_get(iealloc(tsdn, ptr));
+ edata_t *edata = emap_edata_lookup(tsdn, &arena_emap_global, ptr);
+ unsigned arena_ind = edata_arena_ind_get(edata);
+ return (arena_t *)atomic_load_p(&arenas[arena_ind], ATOMIC_RELAXED);
}
JEMALLOC_ALWAYS_INLINE size_t
arena_salloc(tsdn_t *tsdn, const void *ptr) {
assert(ptr != NULL);
+ emap_alloc_ctx_t alloc_ctx;
+ emap_alloc_ctx_lookup(tsdn, &arena_emap_global, ptr, &alloc_ctx);
+ assert(alloc_ctx.szind != SC_NSIZES);
- rtree_ctx_t rtree_ctx_fallback;
- rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);
-
- szind_t szind = rtree_szind_read(tsdn, &extents_rtree, rtree_ctx,
- (uintptr_t)ptr, true);
- assert(szind != SC_NSIZES);
-
- return sz_index2size(szind);
+ return sz_index2size(alloc_ctx.szind);
}
JEMALLOC_ALWAYS_INLINE size_t
@@ -206,26 +190,53 @@ arena_vsalloc(tsdn_t *tsdn, const void *ptr) {
* failure.
*/
- rtree_ctx_t rtree_ctx_fallback;
- rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);
-
- extent_t *extent;
- szind_t szind;
- if (rtree_extent_szind_read(tsdn, &extents_rtree, rtree_ctx,
- (uintptr_t)ptr, false, &extent, &szind)) {
+ emap_full_alloc_ctx_t full_alloc_ctx;
+ bool missing = emap_full_alloc_ctx_try_lookup(tsdn, &arena_emap_global,
+ ptr, &full_alloc_ctx);
+ if (missing) {
return 0;
}
- if (extent == NULL) {
+ if (full_alloc_ctx.edata == NULL) {
return 0;
}
- assert(extent_state_get(extent) == extent_state_active);
+ assert(edata_state_get(full_alloc_ctx.edata) == extent_state_active);
/* Only slab members should be looked up via interior pointers. */
- assert(extent_addr_get(extent) == ptr || extent_slab_get(extent));
+ assert(edata_addr_get(full_alloc_ctx.edata) == ptr
+ || edata_slab_get(full_alloc_ctx.edata));
+
+ assert(full_alloc_ctx.szind != SC_NSIZES);
+
+ return sz_index2size(full_alloc_ctx.szind);
+}
- assert(szind != SC_NSIZES);
+JEMALLOC_ALWAYS_INLINE bool
+large_dalloc_safety_checks(edata_t *edata, void *ptr, szind_t szind) {
+ if (!config_opt_safety_checks) {
+ return false;
+ }
+
+ /*
+ * Eagerly detect double free and sized dealloc bugs for large sizes.
+ * The cost is low enough (as edata will be accessed anyway) to be
+ * enabled all the time.
+ */
+ if (unlikely(edata == NULL ||
+ edata_state_get(edata) != extent_state_active)) {
+ safety_check_fail("Invalid deallocation detected: "
+ "pages being freed (%p) not currently active, "
+ "possibly caused by double free bugs.",
+ (uintptr_t)edata_addr_get(edata));
+ return true;
+ }
+ size_t input_size = sz_index2size(szind);
+ if (unlikely(input_size != edata_usize_get(edata))) {
+ safety_check_fail_sized_dealloc(/* current_dealloc */ true, ptr,
+ /* true_size */ edata_usize_get(edata), input_size);
+ return true;
+ }
- return sz_index2size(szind);
+ return false;
}
static inline void
@@ -233,8 +244,13 @@ arena_dalloc_large_no_tcache(tsdn_t *tsdn, void *ptr, szind_t szind) {
if (config_prof && unlikely(szind < SC_NBINS)) {
arena_dalloc_promoted(tsdn, ptr, NULL, true);
} else {
- extent_t *extent = iealloc(tsdn, ptr);
- large_dalloc(tsdn, extent);
+ edata_t *edata = emap_edata_lookup(tsdn, &arena_emap_global,
+ ptr);
+ if (large_dalloc_safety_checks(edata, ptr, szind)) {
+ /* See the comment in isfree. */
+ return;
+ }
+ large_dalloc(tsdn, edata);
}
}
@@ -242,27 +258,22 @@ static inline void
arena_dalloc_no_tcache(tsdn_t *tsdn, void *ptr) {
assert(ptr != NULL);
- rtree_ctx_t rtree_ctx_fallback;
- rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);
-
- szind_t szind;
- bool slab;
- rtree_szind_slab_read(tsdn, &extents_rtree, rtree_ctx, (uintptr_t)ptr,
- true, &szind, &slab);
+ emap_alloc_ctx_t alloc_ctx;
+ emap_alloc_ctx_lookup(tsdn, &arena_emap_global, ptr, &alloc_ctx);
if (config_debug) {
- extent_t *extent = rtree_extent_read(tsdn, &extents_rtree,
- rtree_ctx, (uintptr_t)ptr, true);
- assert(szind == extent_szind_get(extent));
- assert(szind < SC_NSIZES);
- assert(slab == extent_slab_get(extent));
+ edata_t *edata = emap_edata_lookup(tsdn, &arena_emap_global,
+ ptr);
+ assert(alloc_ctx.szind == edata_szind_get(edata));
+ assert(alloc_ctx.szind < SC_NSIZES);
+ assert(alloc_ctx.slab == edata_slab_get(edata));
}
- if (likely(slab)) {
+ if (likely(alloc_ctx.slab)) {
/* Small allocation. */
arena_dalloc_small(tsdn, ptr);
} else {
- arena_dalloc_large_no_tcache(tsdn, ptr, szind);
+ arena_dalloc_large_no_tcache(tsdn, ptr, alloc_ctx.szind);
}
}
@@ -277,14 +288,19 @@ arena_dalloc_large(tsdn_t *tsdn, void *ptr, tcache_t *tcache, szind_t szind,
slow_path);
}
} else {
- extent_t *extent = iealloc(tsdn, ptr);
- large_dalloc(tsdn, extent);
+ edata_t *edata = emap_edata_lookup(tsdn, &arena_emap_global,
+ ptr);
+ if (large_dalloc_safety_checks(edata, ptr, szind)) {
+ /* See the comment in isfree. */
+ return;
+ }
+ large_dalloc(tsdn, edata);
}
}
JEMALLOC_ALWAYS_INLINE void
arena_dalloc(tsdn_t *tsdn, void *ptr, tcache_t *tcache,
- alloc_ctx_t *alloc_ctx, bool slow_path) {
+ emap_alloc_ctx_t *caller_alloc_ctx, bool slow_path) {
assert(!tsdn_null(tsdn) || tcache == NULL);
assert(ptr != NULL);
@@ -293,34 +309,30 @@ arena_dalloc(tsdn_t *tsdn, void *ptr, tcache_t *tcache,
return;
}
- szind_t szind;
- bool slab;
- rtree_ctx_t *rtree_ctx;
- if (alloc_ctx != NULL) {
- szind = alloc_ctx->szind;
- slab = alloc_ctx->slab;
- assert(szind != SC_NSIZES);
+ emap_alloc_ctx_t alloc_ctx;
+ if (caller_alloc_ctx != NULL) {
+ alloc_ctx = *caller_alloc_ctx;
} else {
- rtree_ctx = tsd_rtree_ctx(tsdn_tsd(tsdn));
- rtree_szind_slab_read(tsdn, &extents_rtree, rtree_ctx,
- (uintptr_t)ptr, true, &szind, &slab);
+ util_assume(!tsdn_null(tsdn));
+ emap_alloc_ctx_lookup(tsdn, &arena_emap_global, ptr,
+ &alloc_ctx);
}
if (config_debug) {
- rtree_ctx = tsd_rtree_ctx(tsdn_tsd(tsdn));
- extent_t *extent = rtree_extent_read(tsdn, &extents_rtree,
- rtree_ctx, (uintptr_t)ptr, true);
- assert(szind == extent_szind_get(extent));
- assert(szind < SC_NSIZES);
- assert(slab == extent_slab_get(extent));
+ edata_t *edata = emap_edata_lookup(tsdn, &arena_emap_global,
+ ptr);
+ assert(alloc_ctx.szind == edata_szind_get(edata));
+ assert(alloc_ctx.szind < SC_NSIZES);
+ assert(alloc_ctx.slab == edata_slab_get(edata));
}
- if (likely(slab)) {
+ if (likely(alloc_ctx.slab)) {
/* Small allocation. */
- tcache_dalloc_small(tsdn_tsd(tsdn), tcache, ptr, szind,
- slow_path);
+ tcache_dalloc_small(tsdn_tsd(tsdn), tcache, ptr,
+ alloc_ctx.szind, slow_path);
} else {
- arena_dalloc_large(tsdn, ptr, tcache, szind, slow_path);
+ arena_dalloc_large(tsdn, ptr, tcache, alloc_ctx.szind,
+ slow_path);
}
}
@@ -329,47 +341,43 @@ arena_sdalloc_no_tcache(tsdn_t *tsdn, void *ptr, size_t size) {
assert(ptr != NULL);
assert(size <= SC_LARGE_MAXCLASS);
- szind_t szind;
- bool slab;
+ emap_alloc_ctx_t alloc_ctx;
if (!config_prof || !opt_prof) {
/*
* There is no risk of being confused by a promoted sampled
* object, so base szind and slab on the given size.
*/
- szind = sz_size2index(size);
- slab = (szind < SC_NBINS);
+ alloc_ctx.szind = sz_size2index(size);
+ alloc_ctx.slab = (alloc_ctx.szind < SC_NBINS);
}
if ((config_prof && opt_prof) || config_debug) {
- rtree_ctx_t rtree_ctx_fallback;
- rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn,
- &rtree_ctx_fallback);
-
- rtree_szind_slab_read(tsdn, &extents_rtree, rtree_ctx,
- (uintptr_t)ptr, true, &szind, &slab);
+ emap_alloc_ctx_lookup(tsdn, &arena_emap_global, ptr,
+ &alloc_ctx);
- assert(szind == sz_size2index(size));
- assert((config_prof && opt_prof) || slab == (szind < SC_NBINS));
+ assert(alloc_ctx.szind == sz_size2index(size));
+ assert((config_prof && opt_prof)
+ || alloc_ctx.slab == (alloc_ctx.szind < SC_NBINS));
if (config_debug) {
- extent_t *extent = rtree_extent_read(tsdn,
- &extents_rtree, rtree_ctx, (uintptr_t)ptr, true);
- assert(szind == extent_szind_get(extent));
- assert(slab == extent_slab_get(extent));
+ edata_t *edata = emap_edata_lookup(tsdn,
+ &arena_emap_global, ptr);
+ assert(alloc_ctx.szind == edata_szind_get(edata));
+ assert(alloc_ctx.slab == edata_slab_get(edata));
}
}
- if (likely(slab)) {
+ if (likely(alloc_ctx.slab)) {
/* Small allocation. */
arena_dalloc_small(tsdn, ptr);
} else {
- arena_dalloc_large_no_tcache(tsdn, ptr, szind);
+ arena_dalloc_large_no_tcache(tsdn, ptr, alloc_ctx.szind);
}
}
JEMALLOC_ALWAYS_INLINE void
arena_sdalloc(tsdn_t *tsdn, void *ptr, size_t size, tcache_t *tcache,
- alloc_ctx_t *alloc_ctx, bool slow_path) {
+ emap_alloc_ctx_t *caller_alloc_ctx, bool slow_path) {
assert(!tsdn_null(tsdn) || tcache == NULL);
assert(ptr != NULL);
assert(size <= SC_LARGE_MAXCLASS);
@@ -379,49 +387,164 @@ arena_sdalloc(tsdn_t *tsdn, void *ptr, size_t size, tcache_t *tcache,
return;
}
- szind_t szind;
- bool slab;
- alloc_ctx_t local_ctx;
+ emap_alloc_ctx_t alloc_ctx;
if (config_prof && opt_prof) {
- if (alloc_ctx == NULL) {
+ if (caller_alloc_ctx == NULL) {
/* Uncommon case and should be a static check. */
- rtree_ctx_t rtree_ctx_fallback;
- rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn,
- &rtree_ctx_fallback);
- rtree_szind_slab_read(tsdn, &extents_rtree, rtree_ctx,
- (uintptr_t)ptr, true, &local_ctx.szind,
- &local_ctx.slab);
- assert(local_ctx.szind == sz_size2index(size));
- alloc_ctx = &local_ctx;
+ emap_alloc_ctx_lookup(tsdn, &arena_emap_global, ptr,
+ &alloc_ctx);
+ assert(alloc_ctx.szind == sz_size2index(size));
+ } else {
+ alloc_ctx = *caller_alloc_ctx;
}
- slab = alloc_ctx->slab;
- szind = alloc_ctx->szind;
} else {
/*
* There is no risk of being confused by a promoted sampled
* object, so base szind and slab on the given size.
*/
- szind = sz_size2index(size);
- slab = (szind < SC_NBINS);
+ alloc_ctx.szind = sz_size2index(size);
+ alloc_ctx.slab = (alloc_ctx.szind < SC_NBINS);
}
if (config_debug) {
- rtree_ctx_t *rtree_ctx = tsd_rtree_ctx(tsdn_tsd(tsdn));
- rtree_szind_slab_read(tsdn, &extents_rtree, rtree_ctx,
- (uintptr_t)ptr, true, &szind, &slab);
- extent_t *extent = rtree_extent_read(tsdn,
- &extents_rtree, rtree_ctx, (uintptr_t)ptr, true);
- assert(szind == extent_szind_get(extent));
- assert(slab == extent_slab_get(extent));
+ edata_t *edata = emap_edata_lookup(tsdn, &arena_emap_global,
+ ptr);
+ assert(alloc_ctx.szind == edata_szind_get(edata));
+ assert(alloc_ctx.slab == edata_slab_get(edata));
}
- if (likely(slab)) {
+ if (likely(alloc_ctx.slab)) {
/* Small allocation. */
- tcache_dalloc_small(tsdn_tsd(tsdn), tcache, ptr, szind,
- slow_path);
+ tcache_dalloc_small(tsdn_tsd(tsdn), tcache, ptr,
+ alloc_ctx.szind, slow_path);
} else {
- arena_dalloc_large(tsdn, ptr, tcache, szind, slow_path);
+ arena_dalloc_large(tsdn, ptr, tcache, alloc_ctx.szind,
+ slow_path);
+ }
+}
+
+static inline void
+arena_cache_oblivious_randomize(tsdn_t *tsdn, arena_t *arena, edata_t *edata,
+ size_t alignment) {
+ assert(edata_base_get(edata) == edata_addr_get(edata));
+
+ if (alignment < PAGE) {
+ unsigned lg_range = LG_PAGE -
+ lg_floor(CACHELINE_CEILING(alignment));
+ size_t r;
+ if (!tsdn_null(tsdn)) {
+ tsd_t *tsd = tsdn_tsd(tsdn);
+ r = (size_t)prng_lg_range_u64(
+ tsd_prng_statep_get(tsd), lg_range);
+ } else {
+ uint64_t stack_value = (uint64_t)(uintptr_t)&r;
+ r = (size_t)prng_lg_range_u64(&stack_value, lg_range);
+ }
+ uintptr_t random_offset = ((uintptr_t)r) << (LG_PAGE -
+ lg_range);
+ edata->e_addr = (void *)((uintptr_t)edata->e_addr +
+ random_offset);
+ assert(ALIGNMENT_ADDR2BASE(edata->e_addr, alignment) ==
+ edata->e_addr);
+ }
+}
+
+/*
+ * The dalloc bin info contains just the information that the common paths need
+ * during tcache flushes. By force-inlining these paths, and using local copies
+ * of data (so that the compiler knows it's constant), we avoid a whole bunch of
+ * redundant loads and stores by leaving this information in registers.
+ */
+typedef struct arena_dalloc_bin_locked_info_s arena_dalloc_bin_locked_info_t;
+struct arena_dalloc_bin_locked_info_s {
+ div_info_t div_info;
+ uint32_t nregs;
+ uint64_t ndalloc;
+};
+
+JEMALLOC_ALWAYS_INLINE size_t
+arena_slab_regind(arena_dalloc_bin_locked_info_t *info, szind_t binind,
+ edata_t *slab, const void *ptr) {
+ size_t diff, regind;
+
+ /* Freeing a pointer outside the slab can cause assertion failure. */
+ assert((uintptr_t)ptr >= (uintptr_t)edata_addr_get(slab));
+ assert((uintptr_t)ptr < (uintptr_t)edata_past_get(slab));
+ /* Freeing an interior pointer can cause assertion failure. */
+ assert(((uintptr_t)ptr - (uintptr_t)edata_addr_get(slab)) %
+ (uintptr_t)bin_infos[binind].reg_size == 0);
+
+ diff = (size_t)((uintptr_t)ptr - (uintptr_t)edata_addr_get(slab));
+
+ /* Avoid doing division with a variable divisor. */
+ regind = div_compute(&info->div_info, diff);
+
+ assert(regind < bin_infos[binind].nregs);
+
+ return regind;
+}
+
+JEMALLOC_ALWAYS_INLINE void
+arena_dalloc_bin_locked_begin(arena_dalloc_bin_locked_info_t *info,
+ szind_t binind) {
+ info->div_info = arena_binind_div_info[binind];
+ info->nregs = bin_infos[binind].nregs;
+ info->ndalloc = 0;
+}
+
+/*
+ * Does the deallocation work associated with freeing a single pointer (a
+ * "step") in between a arena_dalloc_bin_locked begin and end call.
+ *
+ * Returns true if arena_slab_dalloc must be called on slab. Doesn't do
+ * stats updates, which happen during finish (this lets running counts get left
+ * in a register).
+ */
+JEMALLOC_ALWAYS_INLINE bool
+arena_dalloc_bin_locked_step(tsdn_t *tsdn, arena_t *arena, bin_t *bin,
+ arena_dalloc_bin_locked_info_t *info, szind_t binind, edata_t *slab,
+ void *ptr) {
+ const bin_info_t *bin_info = &bin_infos[binind];
+ size_t regind = arena_slab_regind(info, binind, slab, ptr);
+ slab_data_t *slab_data = edata_slab_data_get(slab);
+
+ assert(edata_nfree_get(slab) < bin_info->nregs);
+ /* Freeing an unallocated pointer can cause assertion failure. */
+ assert(bitmap_get(slab_data->bitmap, &bin_info->bitmap_info, regind));
+
+ bitmap_unset(slab_data->bitmap, &bin_info->bitmap_info, regind);
+ edata_nfree_inc(slab);
+
+ if (config_stats) {
+ info->ndalloc++;
+ }
+
+ unsigned nfree = edata_nfree_get(slab);
+ if (nfree == bin_info->nregs) {
+ arena_dalloc_bin_locked_handle_newly_empty(tsdn, arena, slab,
+ bin);
+ return true;
+ } else if (nfree == 1 && slab != bin->slabcur) {
+ arena_dalloc_bin_locked_handle_newly_nonempty(tsdn, arena, slab,
+ bin);
}
+ return false;
+}
+
+JEMALLOC_ALWAYS_INLINE void
+arena_dalloc_bin_locked_finish(tsdn_t *tsdn, arena_t *arena, bin_t *bin,
+ arena_dalloc_bin_locked_info_t *info) {
+ if (config_stats) {
+ bin->stats.ndalloc += info->ndalloc;
+ assert(bin->stats.curregs >= (size_t)info->ndalloc);
+ bin->stats.curregs -= (size_t)info->ndalloc;
+ }
+}
+
+static inline bin_t *
+arena_get_bin(arena_t *arena, szind_t binind, unsigned binshard) {
+ bin_t *shard0 = (bin_t *)((uintptr_t)arena + arena_bin_offsets[binind]);
+ return shard0 + binshard;
}
#endif /* JEMALLOC_INTERNAL_ARENA_INLINES_B_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/arena_stats.h b/contrib/jemalloc/include/jemalloc/internal/arena_stats.h
index 23949ed92616..15f1d345f9e8 100644
--- a/contrib/jemalloc/include/jemalloc/internal/arena_stats.h
+++ b/contrib/jemalloc/include/jemalloc/internal/arena_stats.h
@@ -2,77 +2,41 @@
#define JEMALLOC_INTERNAL_ARENA_STATS_H
#include "jemalloc/internal/atomic.h"
+#include "jemalloc/internal/lockedint.h"
#include "jemalloc/internal/mutex.h"
#include "jemalloc/internal/mutex_prof.h"
+#include "jemalloc/internal/pa.h"
#include "jemalloc/internal/sc.h"
JEMALLOC_DIAGNOSTIC_DISABLE_SPURIOUS
-/*
- * In those architectures that support 64-bit atomics, we use atomic updates for
- * our 64-bit values. Otherwise, we use a plain uint64_t and synchronize
- * externally.
- */
-#ifdef JEMALLOC_ATOMIC_U64
-typedef atomic_u64_t arena_stats_u64_t;
-#else
-/* Must hold the arena stats mutex while reading atomically. */
-typedef uint64_t arena_stats_u64_t;
-#endif
-
typedef struct arena_stats_large_s arena_stats_large_t;
struct arena_stats_large_s {
/*
* Total number of allocation/deallocation requests served directly by
* the arena.
*/
- arena_stats_u64_t nmalloc;
- arena_stats_u64_t ndalloc;
+ locked_u64_t nmalloc;
+ locked_u64_t ndalloc;
/*
* Number of allocation requests that correspond to this size class.
* This includes requests served by tcache, though tcache only
* periodically merges into this counter.
*/
- arena_stats_u64_t nrequests; /* Partially derived. */
+ locked_u64_t nrequests; /* Partially derived. */
/*
* Number of tcache fills / flushes for large (similarly, periodically
* merged). Note that there is no large tcache batch-fill currently
* (i.e. only fill 1 at a time); however flush may be batched.
*/
- arena_stats_u64_t nfills; /* Partially derived. */
- arena_stats_u64_t nflushes; /* Partially derived. */
+ locked_u64_t nfills; /* Partially derived. */
+ locked_u64_t nflushes; /* Partially derived. */
/* Current number of allocations of this size class. */
size_t curlextents; /* Derived. */
};
-typedef struct arena_stats_decay_s arena_stats_decay_t;
-struct arena_stats_decay_s {
- /* Total number of purge sweeps. */
- arena_stats_u64_t npurge;
- /* Total number of madvise calls made. */
- arena_stats_u64_t nmadvise;
- /* Total number of pages purged. */
- arena_stats_u64_t purged;
-};
-
-typedef struct arena_stats_extents_s arena_stats_extents_t;
-struct arena_stats_extents_s {
- /*
- * Stats for a given index in the range [0, SC_NPSIZES] in an extents_t.
- * We track both bytes and # of extents: two extents in the same bucket
- * may have different sizes if adjacent size classes differ by more than
- * a page, so bytes cannot always be derived from # of extents.
- */
- atomic_zu_t ndirty;
- atomic_zu_t dirty_bytes;
- atomic_zu_t nmuzzy;
- atomic_zu_t muzzy_bytes;
- atomic_zu_t nretained;
- atomic_zu_t retained_bytes;
-};
-
/*
* Arena stats. Note that fields marked "derived" are not directly maintained
* within the arena code; rather their values are derived during stats merge
@@ -80,43 +44,36 @@ struct arena_stats_extents_s {
*/
typedef struct arena_stats_s arena_stats_t;
struct arena_stats_s {
-#ifndef JEMALLOC_ATOMIC_U64
- malloc_mutex_t mtx;
-#endif
-
- /* Number of bytes currently mapped, excluding retained memory. */
- atomic_zu_t mapped; /* Partially derived. */
+ LOCKEDINT_MTX_DECLARE(mtx)
/*
- * Number of unused virtual memory bytes currently retained. Retained
- * bytes are technically mapped (though always decommitted or purged),
- * but they are excluded from the mapped statistic (above).
+ * resident includes the base stats -- that's why it lives here and not
+ * in pa_shard_stats_t.
*/
- atomic_zu_t retained; /* Derived. */
-
- /* Number of extent_t structs allocated by base, but not being used. */
- atomic_zu_t extent_avail;
-
- arena_stats_decay_t decay_dirty;
- arena_stats_decay_t decay_muzzy;
+ size_t base; /* Derived. */
+ size_t resident; /* Derived. */
+ size_t metadata_thp; /* Derived. */
+ size_t mapped; /* Derived. */
- atomic_zu_t base; /* Derived. */
atomic_zu_t internal;
- atomic_zu_t resident; /* Derived. */
- atomic_zu_t metadata_thp;
- atomic_zu_t allocated_large; /* Derived. */
- arena_stats_u64_t nmalloc_large; /* Derived. */
- arena_stats_u64_t ndalloc_large; /* Derived. */
- arena_stats_u64_t nfills_large; /* Derived. */
- arena_stats_u64_t nflushes_large; /* Derived. */
- arena_stats_u64_t nrequests_large; /* Derived. */
+ size_t allocated_large; /* Derived. */
+ uint64_t nmalloc_large; /* Derived. */
+ uint64_t ndalloc_large; /* Derived. */
+ uint64_t nfills_large; /* Derived. */
+ uint64_t nflushes_large; /* Derived. */
+ uint64_t nrequests_large; /* Derived. */
- /* VM space had to be leaked (undocumented). Normally 0. */
- atomic_zu_t abandoned_vm;
+ /*
+ * The stats logically owned by the pa_shard in the same arena. This
+ * lives here only because it's convenient for the purposes of the ctl
+ * module -- it only knows about the single arena_stats.
+ */
+ pa_shard_stats_t pa_shard_stats;
/* Number of bytes cached in tcache associated with this arena. */
- atomic_zu_t tcache_bytes; /* Derived. */
+ size_t tcache_bytes; /* Derived. */
+ size_t tcache_stashed_bytes; /* Derived. */
mutex_prof_data_t mutex_prof_data[mutex_prof_num_arena_mutexes];
@@ -134,138 +91,24 @@ arena_stats_init(tsdn_t *tsdn, arena_stats_t *arena_stats) {
assert(((char *)arena_stats)[i] == 0);
}
}
-#ifndef JEMALLOC_ATOMIC_U64
- if (malloc_mutex_init(&arena_stats->mtx, "arena_stats",
+ if (LOCKEDINT_MTX_INIT(arena_stats->mtx, "arena_stats",
WITNESS_RANK_ARENA_STATS, malloc_mutex_rank_exclusive)) {
return true;
}
-#endif
/* Memory is zeroed, so there is no need to clear stats. */
return false;
}
static inline void
-arena_stats_lock(tsdn_t *tsdn, arena_stats_t *arena_stats) {
-#ifndef JEMALLOC_ATOMIC_U64
- malloc_mutex_lock(tsdn, &arena_stats->mtx);
-#endif
-}
-
-static inline void
-arena_stats_unlock(tsdn_t *tsdn, arena_stats_t *arena_stats) {
-#ifndef JEMALLOC_ATOMIC_U64
- malloc_mutex_unlock(tsdn, &arena_stats->mtx);
-#endif
-}
-
-static inline uint64_t
-arena_stats_read_u64(tsdn_t *tsdn, arena_stats_t *arena_stats,
- arena_stats_u64_t *p) {
-#ifdef JEMALLOC_ATOMIC_U64
- return atomic_load_u64(p, ATOMIC_RELAXED);
-#else
- malloc_mutex_assert_owner(tsdn, &arena_stats->mtx);
- return *p;
-#endif
-}
-
-static inline void
-arena_stats_add_u64(tsdn_t *tsdn, arena_stats_t *arena_stats,
- arena_stats_u64_t *p, uint64_t x) {
-#ifdef JEMALLOC_ATOMIC_U64
- atomic_fetch_add_u64(p, x, ATOMIC_RELAXED);
-#else
- malloc_mutex_assert_owner(tsdn, &arena_stats->mtx);
- *p += x;
-#endif
-}
-
-static inline void
-arena_stats_sub_u64(tsdn_t *tsdn, arena_stats_t *arena_stats,
- arena_stats_u64_t *p, uint64_t x) {
-#ifdef JEMALLOC_ATOMIC_U64
- uint64_t r = atomic_fetch_sub_u64(p, x, ATOMIC_RELAXED);
- assert(r - x <= r);
-#else
- malloc_mutex_assert_owner(tsdn, &arena_stats->mtx);
- *p -= x;
- assert(*p + x >= *p);
-#endif
-}
-
-/*
- * Non-atomically sets *dst += src. *dst needs external synchronization.
- * This lets us avoid the cost of a fetch_add when its unnecessary (note that
- * the types here are atomic).
- */
-static inline void
-arena_stats_accum_u64(arena_stats_u64_t *dst, uint64_t src) {
-#ifdef JEMALLOC_ATOMIC_U64
- uint64_t cur_dst = atomic_load_u64(dst, ATOMIC_RELAXED);
- atomic_store_u64(dst, src + cur_dst, ATOMIC_RELAXED);
-#else
- *dst += src;
-#endif
-}
-
-static inline size_t
-arena_stats_read_zu(tsdn_t *tsdn, arena_stats_t *arena_stats,
- atomic_zu_t *p) {
-#ifdef JEMALLOC_ATOMIC_U64
- return atomic_load_zu(p, ATOMIC_RELAXED);
-#else
- malloc_mutex_assert_owner(tsdn, &arena_stats->mtx);
- return atomic_load_zu(p, ATOMIC_RELAXED);
-#endif
-}
-
-static inline void
-arena_stats_add_zu(tsdn_t *tsdn, arena_stats_t *arena_stats,
- atomic_zu_t *p, size_t x) {
-#ifdef JEMALLOC_ATOMIC_U64
- atomic_fetch_add_zu(p, x, ATOMIC_RELAXED);
-#else
- malloc_mutex_assert_owner(tsdn, &arena_stats->mtx);
- size_t cur = atomic_load_zu(p, ATOMIC_RELAXED);
- atomic_store_zu(p, cur + x, ATOMIC_RELAXED);
-#endif
-}
-
-static inline void
-arena_stats_sub_zu(tsdn_t *tsdn, arena_stats_t *arena_stats,
- atomic_zu_t *p, size_t x) {
-#ifdef JEMALLOC_ATOMIC_U64
- size_t r = atomic_fetch_sub_zu(p, x, ATOMIC_RELAXED);
- assert(r - x <= r);
-#else
- malloc_mutex_assert_owner(tsdn, &arena_stats->mtx);
- size_t cur = atomic_load_zu(p, ATOMIC_RELAXED);
- atomic_store_zu(p, cur - x, ATOMIC_RELAXED);
-#endif
-}
-
-/* Like the _u64 variant, needs an externally synchronized *dst. */
-static inline void
-arena_stats_accum_zu(atomic_zu_t *dst, size_t src) {
- size_t cur_dst = atomic_load_zu(dst, ATOMIC_RELAXED);
- atomic_store_zu(dst, src + cur_dst, ATOMIC_RELAXED);
-}
-
-static inline void
arena_stats_large_flush_nrequests_add(tsdn_t *tsdn, arena_stats_t *arena_stats,
szind_t szind, uint64_t nrequests) {
- arena_stats_lock(tsdn, arena_stats);
+ LOCKEDINT_MTX_LOCK(tsdn, arena_stats->mtx);
arena_stats_large_t *lstats = &arena_stats->lstats[szind - SC_NBINS];
- arena_stats_add_u64(tsdn, arena_stats, &lstats->nrequests, nrequests);
- arena_stats_add_u64(tsdn, arena_stats, &lstats->nflushes, 1);
- arena_stats_unlock(tsdn, arena_stats);
-}
-
-static inline void
-arena_stats_mapped_add(tsdn_t *tsdn, arena_stats_t *arena_stats, size_t size) {
- arena_stats_lock(tsdn, arena_stats);
- arena_stats_add_zu(tsdn, arena_stats, &arena_stats->mapped, size);
- arena_stats_unlock(tsdn, arena_stats);
+ locked_inc_u64(tsdn, LOCKEDINT_MTX(arena_stats->mtx),
+ &lstats->nrequests, nrequests);
+ locked_inc_u64(tsdn, LOCKEDINT_MTX(arena_stats->mtx),
+ &lstats->nflushes, 1);
+ LOCKEDINT_MTX_UNLOCK(tsdn, arena_stats->mtx);
}
#endif /* JEMALLOC_INTERNAL_ARENA_STATS_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/arena_structs.h b/contrib/jemalloc/include/jemalloc/internal/arena_structs.h
new file mode 100644
index 000000000000..e2a5a4087bcc
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/internal/arena_structs.h
@@ -0,0 +1,101 @@
+#ifndef JEMALLOC_INTERNAL_ARENA_STRUCTS_H
+#define JEMALLOC_INTERNAL_ARENA_STRUCTS_H
+
+#include "jemalloc/internal/arena_stats.h"
+#include "jemalloc/internal/atomic.h"
+#include "jemalloc/internal/bin.h"
+#include "jemalloc/internal/bitmap.h"
+#include "jemalloc/internal/counter.h"
+#include "jemalloc/internal/ecache.h"
+#include "jemalloc/internal/edata_cache.h"
+#include "jemalloc/internal/extent_dss.h"
+#include "jemalloc/internal/jemalloc_internal_types.h"
+#include "jemalloc/internal/mutex.h"
+#include "jemalloc/internal/nstime.h"
+#include "jemalloc/internal/pa.h"
+#include "jemalloc/internal/ql.h"
+#include "jemalloc/internal/sc.h"
+#include "jemalloc/internal/ticker.h"
+
+struct arena_s {
+ /*
+ * Number of threads currently assigned to this arena. Each thread has
+ * two distinct assignments, one for application-serving allocation, and
+ * the other for internal metadata allocation. Internal metadata must
+ * not be allocated from arenas explicitly created via the arenas.create
+ * mallctl, because the arena.<i>.reset mallctl indiscriminately
+ * discards all allocations for the affected arena.
+ *
+ * 0: Application allocation.
+ * 1: Internal metadata allocation.
+ *
+ * Synchronization: atomic.
+ */
+ atomic_u_t nthreads[2];
+
+ /* Next bin shard for binding new threads. Synchronization: atomic. */
+ atomic_u_t binshard_next;
+
+ /*
+ * When percpu_arena is enabled, to amortize the cost of reading /
+ * updating the current CPU id, track the most recent thread accessing
+ * this arena, and only read CPU if there is a mismatch.
+ */
+ tsdn_t *last_thd;
+
+ /* Synchronization: internal. */
+ arena_stats_t stats;
+
+ /*
+ * Lists of tcaches and cache_bin_array_descriptors for extant threads
+ * associated with this arena. Stats from these are merged
+ * incrementally, and at exit if opt_stats_print is enabled.
+ *
+ * Synchronization: tcache_ql_mtx.
+ */
+ ql_head(tcache_slow_t) tcache_ql;
+ ql_head(cache_bin_array_descriptor_t) cache_bin_array_descriptor_ql;
+ malloc_mutex_t tcache_ql_mtx;
+
+ /*
+ * Represents a dss_prec_t, but atomically.
+ *
+ * Synchronization: atomic.
+ */
+ atomic_u_t dss_prec;
+
+ /*
+ * Extant large allocations.
+ *
+ * Synchronization: large_mtx.
+ */
+ edata_list_active_t large;
+ /* Synchronizes all large allocation/update/deallocation. */
+ malloc_mutex_t large_mtx;
+
+ /* The page-level allocator shard this arena uses. */
+ pa_shard_t pa_shard;
+
+ /*
+ * A cached copy of base->ind. This can get accessed on hot paths;
+ * looking it up in base requires an extra pointer hop / cache miss.
+ */
+ unsigned ind;
+
+ /*
+ * Base allocator, from which arena metadata are allocated.
+ *
+ * Synchronization: internal.
+ */
+ base_t *base;
+ /* Used to determine uptime. Read-only after initialization. */
+ nstime_t create_time;
+
+ /*
+ * The arena is allocated alongside its bins; really this is a
+ * dynamically sized array determined by the binshard settings.
+ */
+ bin_t bins[0];
+};
+
+#endif /* JEMALLOC_INTERNAL_ARENA_STRUCTS_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/arena_structs_a.h b/contrib/jemalloc/include/jemalloc/internal/arena_structs_a.h
deleted file mode 100644
index 46aa77c884b7..000000000000
--- a/contrib/jemalloc/include/jemalloc/internal/arena_structs_a.h
+++ /dev/null
@@ -1,11 +0,0 @@
-#ifndef JEMALLOC_INTERNAL_ARENA_STRUCTS_A_H
-#define JEMALLOC_INTERNAL_ARENA_STRUCTS_A_H
-
-#include "jemalloc/internal/bitmap.h"
-
-struct arena_slab_data_s {
- /* Per region allocated/deallocated bitmap. */
- bitmap_t bitmap[BITMAP_GROUPS_MAX];
-};
-
-#endif /* JEMALLOC_INTERNAL_ARENA_STRUCTS_A_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/arena_structs_b.h b/contrib/jemalloc/include/jemalloc/internal/arena_structs_b.h
deleted file mode 100644
index eeab57fd6e5c..000000000000
--- a/contrib/jemalloc/include/jemalloc/internal/arena_structs_b.h
+++ /dev/null
@@ -1,232 +0,0 @@
-#ifndef JEMALLOC_INTERNAL_ARENA_STRUCTS_B_H
-#define JEMALLOC_INTERNAL_ARENA_STRUCTS_B_H
-
-#include "jemalloc/internal/arena_stats.h"
-#include "jemalloc/internal/atomic.h"
-#include "jemalloc/internal/bin.h"
-#include "jemalloc/internal/bitmap.h"
-#include "jemalloc/internal/extent_dss.h"
-#include "jemalloc/internal/jemalloc_internal_types.h"
-#include "jemalloc/internal/mutex.h"
-#include "jemalloc/internal/nstime.h"
-#include "jemalloc/internal/ql.h"
-#include "jemalloc/internal/sc.h"
-#include "jemalloc/internal/smoothstep.h"
-#include "jemalloc/internal/ticker.h"
-
-struct arena_decay_s {
- /* Synchronizes all non-atomic fields. */
- malloc_mutex_t mtx;
- /*
- * True if a thread is currently purging the extents associated with
- * this decay structure.
- */
- bool purging;
- /*
- * Approximate time in milliseconds from the creation of a set of unused
- * dirty pages until an equivalent set of unused dirty pages is purged
- * and/or reused.
- */
- atomic_zd_t time_ms;
- /* time / SMOOTHSTEP_NSTEPS. */
- nstime_t interval;
- /*
- * Time at which the current decay interval logically started. We do
- * not actually advance to a new epoch until sometime after it starts
- * because of scheduling and computation delays, and it is even possible
- * to completely skip epochs. In all cases, during epoch advancement we
- * merge all relevant activity into the most recently recorded epoch.
- */
- nstime_t epoch;
- /* Deadline randomness generator. */
- uint64_t jitter_state;
- /*
- * Deadline for current epoch. This is the sum of interval and per
- * epoch jitter which is a uniform random variable in [0..interval).
- * Epochs always advance by precise multiples of interval, but we
- * randomize the deadline to reduce the likelihood of arenas purging in
- * lockstep.
- */
- nstime_t deadline;
- /*
- * Number of unpurged pages at beginning of current epoch. During epoch
- * advancement we use the delta between arena->decay_*.nunpurged and
- * extents_npages_get(&arena->extents_*) to determine how many dirty
- * pages, if any, were generated.
- */
- size_t nunpurged;
- /*
- * Trailing log of how many unused dirty pages were generated during
- * each of the past SMOOTHSTEP_NSTEPS decay epochs, where the last
- * element is the most recent epoch. Corresponding epoch times are
- * relative to epoch.
- */
- size_t backlog[SMOOTHSTEP_NSTEPS];
-
- /*
- * Pointer to associated stats. These stats are embedded directly in
- * the arena's stats due to how stats structures are shared between the
- * arena and ctl code.
- *
- * Synchronization: Same as associated arena's stats field. */
- arena_stats_decay_t *stats;
- /* Peak number of pages in associated extents. Used for debug only. */
- uint64_t ceil_npages;
-};
-
-struct arena_s {
- /*
- * Number of threads currently assigned to this arena. Each thread has
- * two distinct assignments, one for application-serving allocation, and
- * the other for internal metadata allocation. Internal metadata must
- * not be allocated from arenas explicitly created via the arenas.create
- * mallctl, because the arena.<i>.reset mallctl indiscriminately
- * discards all allocations for the affected arena.
- *
- * 0: Application allocation.
- * 1: Internal metadata allocation.
- *
- * Synchronization: atomic.
- */
- atomic_u_t nthreads[2];
-
- /* Next bin shard for binding new threads. Synchronization: atomic. */
- atomic_u_t binshard_next;
-
- /*
- * When percpu_arena is enabled, to amortize the cost of reading /
- * updating the current CPU id, track the most recent thread accessing
- * this arena, and only read CPU if there is a mismatch.
- */
- tsdn_t *last_thd;
-
- /* Synchronization: internal. */
- arena_stats_t stats;
-
- /*
- * Lists of tcaches and cache_bin_array_descriptors for extant threads
- * associated with this arena. Stats from these are merged
- * incrementally, and at exit if opt_stats_print is enabled.
- *
- * Synchronization: tcache_ql_mtx.
- */
- ql_head(tcache_t) tcache_ql;
- ql_head(cache_bin_array_descriptor_t) cache_bin_array_descriptor_ql;
- malloc_mutex_t tcache_ql_mtx;
-
- /* Synchronization: internal. */
- prof_accum_t prof_accum;
-
- /*
- * PRNG state for cache index randomization of large allocation base
- * pointers.
- *
- * Synchronization: atomic.
- */
- atomic_zu_t offset_state;
-
- /*
- * Extent serial number generator state.
- *
- * Synchronization: atomic.
- */
- atomic_zu_t extent_sn_next;
-
- /*
- * Represents a dss_prec_t, but atomically.
- *
- * Synchronization: atomic.
- */
- atomic_u_t dss_prec;
-
- /*
- * Number of pages in active extents.
- *
- * Synchronization: atomic.
- */
- atomic_zu_t nactive;
-
- /*
- * Extant large allocations.
- *
- * Synchronization: large_mtx.
- */
- extent_list_t large;
- /* Synchronizes all large allocation/update/deallocation. */
- malloc_mutex_t large_mtx;
-
- /*
- * Collections of extents that were previously allocated. These are
- * used when allocating extents, in an attempt to re-use address space.
- *
- * Synchronization: internal.
- */
- extents_t extents_dirty;
- extents_t extents_muzzy;
- extents_t extents_retained;
-
- /*
- * Decay-based purging state, responsible for scheduling extent state
- * transitions.
- *
- * Synchronization: internal.
- */
- arena_decay_t decay_dirty; /* dirty --> muzzy */
- arena_decay_t decay_muzzy; /* muzzy --> retained */
-
- /*
- * Next extent size class in a growing series to use when satisfying a
- * request via the extent hooks (only if opt_retain). This limits the
- * number of disjoint virtual memory ranges so that extent merging can
- * be effective even if multiple arenas' extent allocation requests are
- * highly interleaved.
- *
- * retain_grow_limit is the max allowed size ind to expand (unless the
- * required size is greater). Default is no limit, and controlled
- * through mallctl only.
- *
- * Synchronization: extent_grow_mtx
- */
- pszind_t extent_grow_next;
- pszind_t retain_grow_limit;
- malloc_mutex_t extent_grow_mtx;
-
- /*
- * Available extent structures that were allocated via
- * base_alloc_extent().
- *
- * Synchronization: extent_avail_mtx.
- */
- extent_tree_t extent_avail;
- atomic_zu_t extent_avail_cnt;
- malloc_mutex_t extent_avail_mtx;
-
- /*
- * bins is used to store heaps of free regions.
- *
- * Synchronization: internal.
- */
- bins_t bins[SC_NBINS];
-
- /*
- * Base allocator, from which arena metadata are allocated.
- *
- * Synchronization: internal.
- */
- base_t *base;
- /* Used to determine uptime. Read-only after initialization. */
- nstime_t create_time;
-};
-
-/* Used in conjunction with tsd for fast arena-related context lookup. */
-struct arena_tdata_s {
- ticker_t decay_ticker;
-};
-
-/* Used to pass rtree lookup context down the path. */
-struct alloc_ctx_s {
- szind_t szind;
- bool slab;
-};
-
-#endif /* JEMALLOC_INTERNAL_ARENA_STRUCTS_B_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/arena_types.h b/contrib/jemalloc/include/jemalloc/internal/arena_types.h
index 624937e4f596..d0e1291762a6 100644
--- a/contrib/jemalloc/include/jemalloc/internal/arena_types.h
+++ b/contrib/jemalloc/include/jemalloc/internal/arena_types.h
@@ -3,21 +3,14 @@
#include "jemalloc/internal/sc.h"
-/* Maximum number of regions in one slab. */
-#define LG_SLAB_MAXREGS (LG_PAGE - SC_LG_TINY_MIN)
-#define SLAB_MAXREGS (1U << LG_SLAB_MAXREGS)
-
/* Default decay times in milliseconds. */
#define DIRTY_DECAY_MS_DEFAULT ZD(10 * 1000)
#define MUZZY_DECAY_MS_DEFAULT (0)
/* Number of event ticks between time checks. */
-#define DECAY_NTICKS_PER_UPDATE 1000
+#define ARENA_DECAY_NTICKS_PER_UPDATE 1000
-typedef struct arena_slab_data_s arena_slab_data_t;
typedef struct arena_decay_s arena_decay_t;
typedef struct arena_s arena_t;
-typedef struct arena_tdata_s arena_tdata_t;
-typedef struct alloc_ctx_s alloc_ctx_t;
typedef enum {
percpu_arena_mode_names_base = 0, /* Used for options processing. */
@@ -48,4 +41,18 @@ typedef enum {
*/
#define OVERSIZE_THRESHOLD_DEFAULT (8 << 20)
+struct arena_config_s {
+ /* extent hooks to be used for the arena */
+ extent_hooks_t *extent_hooks;
+
+ /*
+ * Use extent hooks for metadata (base) allocations when true.
+ */
+ bool metadata_use_hooks;
+};
+
+typedef struct arena_config_s arena_config_t;
+
+extern const arena_config_t arena_config_default;
+
#endif /* JEMALLOC_INTERNAL_ARENA_TYPES_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/atomic.h b/contrib/jemalloc/include/jemalloc/internal/atomic.h
index a76f54cee3f4..c0f73122accd 100644
--- a/contrib/jemalloc/include/jemalloc/internal/atomic.h
+++ b/contrib/jemalloc/include/jemalloc/internal/atomic.h
@@ -52,6 +52,27 @@
#define ATOMIC_SEQ_CST atomic_memory_order_seq_cst
/*
+ * Another convenience -- simple atomic helper functions.
+ */
+#define JEMALLOC_GENERATE_EXPANDED_INT_ATOMICS(type, short_type, \
+ lg_size) \
+ JEMALLOC_GENERATE_INT_ATOMICS(type, short_type, lg_size) \
+ ATOMIC_INLINE void \
+ atomic_load_add_store_##short_type(atomic_##short_type##_t *a, \
+ type inc) { \
+ type oldval = atomic_load_##short_type(a, ATOMIC_RELAXED); \
+ type newval = oldval + inc; \
+ atomic_store_##short_type(a, newval, ATOMIC_RELAXED); \
+ } \
+ ATOMIC_INLINE void \
+ atomic_load_sub_store_##short_type(atomic_##short_type##_t *a, \
+ type inc) { \
+ type oldval = atomic_load_##short_type(a, ATOMIC_RELAXED); \
+ type newval = oldval - inc; \
+ atomic_store_##short_type(a, newval, ATOMIC_RELAXED); \
+ }
+
+/*
* Not all platforms have 64-bit atomics. If we do, this #define exposes that
* fact.
*/
@@ -67,18 +88,18 @@ JEMALLOC_GENERATE_ATOMICS(void *, p, LG_SIZEOF_PTR)
*/
JEMALLOC_GENERATE_ATOMICS(bool, b, 0)
-JEMALLOC_GENERATE_INT_ATOMICS(unsigned, u, LG_SIZEOF_INT)
+JEMALLOC_GENERATE_EXPANDED_INT_ATOMICS(unsigned, u, LG_SIZEOF_INT)
-JEMALLOC_GENERATE_INT_ATOMICS(size_t, zu, LG_SIZEOF_PTR)
+JEMALLOC_GENERATE_EXPANDED_INT_ATOMICS(size_t, zu, LG_SIZEOF_PTR)
-JEMALLOC_GENERATE_INT_ATOMICS(ssize_t, zd, LG_SIZEOF_PTR)
+JEMALLOC_GENERATE_EXPANDED_INT_ATOMICS(ssize_t, zd, LG_SIZEOF_PTR)
-JEMALLOC_GENERATE_INT_ATOMICS(uint8_t, u8, 0)
+JEMALLOC_GENERATE_EXPANDED_INT_ATOMICS(uint8_t, u8, 0)
-JEMALLOC_GENERATE_INT_ATOMICS(uint32_t, u32, 2)
+JEMALLOC_GENERATE_EXPANDED_INT_ATOMICS(uint32_t, u32, 2)
#ifdef JEMALLOC_ATOMIC_U64
-JEMALLOC_GENERATE_INT_ATOMICS(uint64_t, u64, 3)
+JEMALLOC_GENERATE_EXPANDED_INT_ATOMICS(uint64_t, u64, 3)
#endif
#undef ATOMIC_INLINE
diff --git a/contrib/jemalloc/include/jemalloc/internal/atomic_msvc.h b/contrib/jemalloc/include/jemalloc/internal/atomic_msvc.h
new file mode 100644
index 000000000000..67057ce50895
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/internal/atomic_msvc.h
@@ -0,0 +1,158 @@
+#ifndef JEMALLOC_INTERNAL_ATOMIC_MSVC_H
+#define JEMALLOC_INTERNAL_ATOMIC_MSVC_H
+
+#define ATOMIC_INIT(...) {__VA_ARGS__}
+
+typedef enum {
+ atomic_memory_order_relaxed,
+ atomic_memory_order_acquire,
+ atomic_memory_order_release,
+ atomic_memory_order_acq_rel,
+ atomic_memory_order_seq_cst
+} atomic_memory_order_t;
+
+typedef char atomic_repr_0_t;
+typedef short atomic_repr_1_t;
+typedef long atomic_repr_2_t;
+typedef __int64 atomic_repr_3_t;
+
+ATOMIC_INLINE void
+atomic_fence(atomic_memory_order_t mo) {
+ _ReadWriteBarrier();
+# if defined(_M_ARM) || defined(_M_ARM64)
+ /* ARM needs a barrier for everything but relaxed. */
+ if (mo != atomic_memory_order_relaxed) {
+ MemoryBarrier();
+ }
+# elif defined(_M_IX86) || defined (_M_X64)
+ /* x86 needs a barrier only for seq_cst. */
+ if (mo == atomic_memory_order_seq_cst) {
+ MemoryBarrier();
+ }
+# else
+# error "Don't know how to create atomics for this platform for MSVC."
+# endif
+ _ReadWriteBarrier();
+}
+
+#define ATOMIC_INTERLOCKED_REPR(lg_size) atomic_repr_ ## lg_size ## _t
+
+#define ATOMIC_CONCAT(a, b) ATOMIC_RAW_CONCAT(a, b)
+#define ATOMIC_RAW_CONCAT(a, b) a ## b
+
+#define ATOMIC_INTERLOCKED_NAME(base_name, lg_size) ATOMIC_CONCAT( \
+ base_name, ATOMIC_INTERLOCKED_SUFFIX(lg_size))
+
+#define ATOMIC_INTERLOCKED_SUFFIX(lg_size) \
+ ATOMIC_CONCAT(ATOMIC_INTERLOCKED_SUFFIX_, lg_size)
+
+#define ATOMIC_INTERLOCKED_SUFFIX_0 8
+#define ATOMIC_INTERLOCKED_SUFFIX_1 16
+#define ATOMIC_INTERLOCKED_SUFFIX_2
+#define ATOMIC_INTERLOCKED_SUFFIX_3 64
+
+#define JEMALLOC_GENERATE_ATOMICS(type, short_type, lg_size) \
+typedef struct { \
+ ATOMIC_INTERLOCKED_REPR(lg_size) repr; \
+} atomic_##short_type##_t; \
+ \
+ATOMIC_INLINE type \
+atomic_load_##short_type(const atomic_##short_type##_t *a, \
+ atomic_memory_order_t mo) { \
+ ATOMIC_INTERLOCKED_REPR(lg_size) ret = a->repr; \
+ if (mo != atomic_memory_order_relaxed) { \
+ atomic_fence(atomic_memory_order_acquire); \
+ } \
+ return (type) ret; \
+} \
+ \
+ATOMIC_INLINE void \
+atomic_store_##short_type(atomic_##short_type##_t *a, \
+ type val, atomic_memory_order_t mo) { \
+ if (mo != atomic_memory_order_relaxed) { \
+ atomic_fence(atomic_memory_order_release); \
+ } \
+ a->repr = (ATOMIC_INTERLOCKED_REPR(lg_size)) val; \
+ if (mo == atomic_memory_order_seq_cst) { \
+ atomic_fence(atomic_memory_order_seq_cst); \
+ } \
+} \
+ \
+ATOMIC_INLINE type \
+atomic_exchange_##short_type(atomic_##short_type##_t *a, type val, \
+ atomic_memory_order_t mo) { \
+ return (type)ATOMIC_INTERLOCKED_NAME(_InterlockedExchange, \
+ lg_size)(&a->repr, (ATOMIC_INTERLOCKED_REPR(lg_size))val); \
+} \
+ \
+ATOMIC_INLINE bool \
+atomic_compare_exchange_weak_##short_type(atomic_##short_type##_t *a, \
+ type *expected, type desired, atomic_memory_order_t success_mo, \
+ atomic_memory_order_t failure_mo) { \
+ ATOMIC_INTERLOCKED_REPR(lg_size) e = \
+ (ATOMIC_INTERLOCKED_REPR(lg_size))*expected; \
+ ATOMIC_INTERLOCKED_REPR(lg_size) d = \
+ (ATOMIC_INTERLOCKED_REPR(lg_size))desired; \
+ ATOMIC_INTERLOCKED_REPR(lg_size) old = \
+ ATOMIC_INTERLOCKED_NAME(_InterlockedCompareExchange, \
+ lg_size)(&a->repr, d, e); \
+ if (old == e) { \
+ return true; \
+ } else { \
+ *expected = (type)old; \
+ return false; \
+ } \
+} \
+ \
+ATOMIC_INLINE bool \
+atomic_compare_exchange_strong_##short_type(atomic_##short_type##_t *a, \
+ type *expected, type desired, atomic_memory_order_t success_mo, \
+ atomic_memory_order_t failure_mo) { \
+ /* We implement the weak version with strong semantics. */ \
+ return atomic_compare_exchange_weak_##short_type(a, expected, \
+ desired, success_mo, failure_mo); \
+}
+
+
+#define JEMALLOC_GENERATE_INT_ATOMICS(type, short_type, lg_size) \
+JEMALLOC_GENERATE_ATOMICS(type, short_type, lg_size) \
+ \
+ATOMIC_INLINE type \
+atomic_fetch_add_##short_type(atomic_##short_type##_t *a, \
+ type val, atomic_memory_order_t mo) { \
+ return (type)ATOMIC_INTERLOCKED_NAME(_InterlockedExchangeAdd, \
+ lg_size)(&a->repr, (ATOMIC_INTERLOCKED_REPR(lg_size))val); \
+} \
+ \
+ATOMIC_INLINE type \
+atomic_fetch_sub_##short_type(atomic_##short_type##_t *a, \
+ type val, atomic_memory_order_t mo) { \
+ /* \
+ * MSVC warns on negation of unsigned operands, but for us it \
+ * gives exactly the right semantics (MAX_TYPE + 1 - operand). \
+ */ \
+ __pragma(warning(push)) \
+ __pragma(warning(disable: 4146)) \
+ return atomic_fetch_add_##short_type(a, -val, mo); \
+ __pragma(warning(pop)) \
+} \
+ATOMIC_INLINE type \
+atomic_fetch_and_##short_type(atomic_##short_type##_t *a, \
+ type val, atomic_memory_order_t mo) { \
+ return (type)ATOMIC_INTERLOCKED_NAME(_InterlockedAnd, lg_size)( \
+ &a->repr, (ATOMIC_INTERLOCKED_REPR(lg_size))val); \
+} \
+ATOMIC_INLINE type \
+atomic_fetch_or_##short_type(atomic_##short_type##_t *a, \
+ type val, atomic_memory_order_t mo) { \
+ return (type)ATOMIC_INTERLOCKED_NAME(_InterlockedOr, lg_size)( \
+ &a->repr, (ATOMIC_INTERLOCKED_REPR(lg_size))val); \
+} \
+ATOMIC_INLINE type \
+atomic_fetch_xor_##short_type(atomic_##short_type##_t *a, \
+ type val, atomic_memory_order_t mo) { \
+ return (type)ATOMIC_INTERLOCKED_NAME(_InterlockedXor, lg_size)( \
+ &a->repr, (ATOMIC_INTERLOCKED_REPR(lg_size))val); \
+}
+
+#endif /* JEMALLOC_INTERNAL_ATOMIC_MSVC_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/background_thread_externs.h b/contrib/jemalloc/include/jemalloc/internal/background_thread_externs.h
index 0f997e18beec..6ae3c8d89d4b 100644
--- a/contrib/jemalloc/include/jemalloc/internal/background_thread_externs.h
+++ b/contrib/jemalloc/include/jemalloc/internal/background_thread_externs.h
@@ -12,8 +12,9 @@ extern background_thread_info_t *background_thread_info;
bool background_thread_create(tsd_t *tsd, unsigned arena_ind);
bool background_threads_enable(tsd_t *tsd);
bool background_threads_disable(tsd_t *tsd);
-void background_thread_interval_check(tsdn_t *tsdn, arena_t *arena,
- arena_decay_t *decay, size_t npages_new);
+bool background_thread_is_started(background_thread_info_t* info);
+void background_thread_wakeup_early(background_thread_info_t *info,
+ nstime_t *remaining_sleep);
void background_thread_prefork0(tsdn_t *tsdn);
void background_thread_prefork1(tsdn_t *tsdn);
void background_thread_postfork_parent(tsdn_t *tsdn);
@@ -27,6 +28,6 @@ extern int pthread_create_wrapper(pthread_t *__restrict, const pthread_attr_t *,
void *(*)(void *), void *__restrict);
#endif
bool background_thread_boot0(void);
-bool background_thread_boot1(tsdn_t *tsdn);
+bool background_thread_boot1(tsdn_t *tsdn, base_t *base);
#endif /* JEMALLOC_INTERNAL_BACKGROUND_THREAD_EXTERNS_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/background_thread_inlines.h b/contrib/jemalloc/include/jemalloc/internal/background_thread_inlines.h
index f85e86fa375b..92c5febe70fd 100644
--- a/contrib/jemalloc/include/jemalloc/internal/background_thread_inlines.h
+++ b/contrib/jemalloc/include/jemalloc/internal/background_thread_inlines.h
@@ -45,18 +45,4 @@ background_thread_indefinite_sleep(background_thread_info_t *info) {
return atomic_load_b(&info->indefinite_sleep, ATOMIC_ACQUIRE);
}
-JEMALLOC_ALWAYS_INLINE void
-arena_background_thread_inactivity_check(tsdn_t *tsdn, arena_t *arena,
- bool is_background_thread) {
- if (!background_thread_enabled() || is_background_thread) {
- return;
- }
- background_thread_info_t *info =
- arena_background_thread_info_get(arena);
- if (background_thread_indefinite_sleep(info)) {
- background_thread_interval_check(tsdn, arena,
- &arena->decay_dirty, 0);
- }
-}
-
#endif /* JEMALLOC_INTERNAL_BACKGROUND_THREAD_INLINES_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/background_thread_structs.h b/contrib/jemalloc/include/jemalloc/internal/background_thread_structs.h
index c02aa434c7da..83a919846029 100644
--- a/contrib/jemalloc/include/jemalloc/internal/background_thread_structs.h
+++ b/contrib/jemalloc/include/jemalloc/internal/background_thread_structs.h
@@ -11,6 +11,17 @@
#define MAX_BACKGROUND_THREAD_LIMIT MALLOCX_ARENA_LIMIT
#define DEFAULT_NUM_BACKGROUND_THREAD 4
+/*
+ * These exist only as a transitional state. Eventually, deferral should be
+ * part of the PAI, and each implementation can indicate wait times with more
+ * specificity.
+ */
+#define BACKGROUND_THREAD_HPA_INTERVAL_MAX_UNINITIALIZED (-2)
+#define BACKGROUND_THREAD_HPA_INTERVAL_MAX_DEFAULT_WHEN_ENABLED 5000
+
+#define BACKGROUND_THREAD_DEFERRED_MIN UINT64_C(0)
+#define BACKGROUND_THREAD_DEFERRED_MAX UINT64_MAX
+
typedef enum {
background_thread_stopped,
background_thread_started,
@@ -48,6 +59,7 @@ struct background_thread_stats_s {
size_t num_threads;
uint64_t num_runs;
nstime_t run_interval;
+ mutex_prof_data_t max_counter_per_bg_thd;
};
typedef struct background_thread_stats_s background_thread_stats_t;
diff --git a/contrib/jemalloc/include/jemalloc/internal/base.h b/contrib/jemalloc/include/jemalloc/internal/base.h
new file mode 100644
index 000000000000..9b2c9fb10b99
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/internal/base.h
@@ -0,0 +1,110 @@
+#ifndef JEMALLOC_INTERNAL_BASE_H
+#define JEMALLOC_INTERNAL_BASE_H
+
+#include "jemalloc/internal/edata.h"
+#include "jemalloc/internal/ehooks.h"
+#include "jemalloc/internal/mutex.h"
+
+enum metadata_thp_mode_e {
+ metadata_thp_disabled = 0,
+ /*
+ * Lazily enable hugepage for metadata. To avoid high RSS caused by THP
+ * + low usage arena (i.e. THP becomes a significant percentage), the
+ * "auto" option only starts using THP after a base allocator used up
+ * the first THP region. Starting from the second hugepage (in a single
+ * arena), "auto" behaves the same as "always", i.e. madvise hugepage
+ * right away.
+ */
+ metadata_thp_auto = 1,
+ metadata_thp_always = 2,
+ metadata_thp_mode_limit = 3
+};
+typedef enum metadata_thp_mode_e metadata_thp_mode_t;
+
+#define METADATA_THP_DEFAULT metadata_thp_disabled
+extern metadata_thp_mode_t opt_metadata_thp;
+extern const char *metadata_thp_mode_names[];
+
+
+/* Embedded at the beginning of every block of base-managed virtual memory. */
+typedef struct base_block_s base_block_t;
+struct base_block_s {
+ /* Total size of block's virtual memory mapping. */
+ size_t size;
+
+ /* Next block in list of base's blocks. */
+ base_block_t *next;
+
+ /* Tracks unused trailing space. */
+ edata_t edata;
+};
+
+typedef struct base_s base_t;
+struct base_s {
+ /*
+ * User-configurable extent hook functions.
+ */
+ ehooks_t ehooks;
+
+ /*
+ * User-configurable extent hook functions for metadata allocations.
+ */
+ ehooks_t ehooks_base;
+
+ /* Protects base_alloc() and base_stats_get() operations. */
+ malloc_mutex_t mtx;
+
+ /* Using THP when true (metadata_thp auto mode). */
+ bool auto_thp_switched;
+ /*
+ * Most recent size class in the series of increasingly large base
+ * extents. Logarithmic spacing between subsequent allocations ensures
+ * that the total number of distinct mappings remains small.
+ */
+ pszind_t pind_last;
+
+ /* Serial number generation state. */
+ size_t extent_sn_next;
+
+ /* Chain of all blocks associated with base. */
+ base_block_t *blocks;
+
+ /* Heap of extents that track unused trailing space within blocks. */
+ edata_heap_t avail[SC_NSIZES];
+
+ /* Stats, only maintained if config_stats. */
+ size_t allocated;
+ size_t resident;
+ size_t mapped;
+ /* Number of THP regions touched. */
+ size_t n_thp;
+};
+
+static inline unsigned
+base_ind_get(const base_t *base) {
+ return ehooks_ind_get(&base->ehooks);
+}
+
+static inline bool
+metadata_thp_enabled(void) {
+ return (opt_metadata_thp != metadata_thp_disabled);
+}
+
+base_t *b0get(void);
+base_t *base_new(tsdn_t *tsdn, unsigned ind,
+ const extent_hooks_t *extent_hooks, bool metadata_use_hooks);
+void base_delete(tsdn_t *tsdn, base_t *base);
+ehooks_t *base_ehooks_get(base_t *base);
+ehooks_t *base_ehooks_get_for_metadata(base_t *base);
+extent_hooks_t *base_extent_hooks_set(base_t *base,
+ extent_hooks_t *extent_hooks);
+void *base_alloc(tsdn_t *tsdn, base_t *base, size_t size, size_t alignment);
+edata_t *base_alloc_edata(tsdn_t *tsdn, base_t *base);
+void base_stats_get(tsdn_t *tsdn, base_t *base, size_t *allocated,
+ size_t *resident, size_t *mapped, size_t *n_thp);
+void base_prefork(tsdn_t *tsdn, base_t *base);
+void base_postfork_parent(tsdn_t *tsdn, base_t *base);
+void base_postfork_child(tsdn_t *tsdn, base_t *base);
+bool base_boot(tsdn_t *tsdn);
+
+#endif /* JEMALLOC_INTERNAL_BASE_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/base_externs.h b/contrib/jemalloc/include/jemalloc/internal/base_externs.h
deleted file mode 100644
index 7b705c9b4d1c..000000000000
--- a/contrib/jemalloc/include/jemalloc/internal/base_externs.h
+++ /dev/null
@@ -1,22 +0,0 @@
-#ifndef JEMALLOC_INTERNAL_BASE_EXTERNS_H
-#define JEMALLOC_INTERNAL_BASE_EXTERNS_H
-
-extern metadata_thp_mode_t opt_metadata_thp;
-extern const char *metadata_thp_mode_names[];
-
-base_t *b0get(void);
-base_t *base_new(tsdn_t *tsdn, unsigned ind, extent_hooks_t *extent_hooks);
-void base_delete(tsdn_t *tsdn, base_t *base);
-extent_hooks_t *base_extent_hooks_get(base_t *base);
-extent_hooks_t *base_extent_hooks_set(base_t *base,
- extent_hooks_t *extent_hooks);
-void *base_alloc(tsdn_t *tsdn, base_t *base, size_t size, size_t alignment);
-extent_t *base_alloc_extent(tsdn_t *tsdn, base_t *base);
-void base_stats_get(tsdn_t *tsdn, base_t *base, size_t *allocated,
- size_t *resident, size_t *mapped, size_t *n_thp);
-void base_prefork(tsdn_t *tsdn, base_t *base);
-void base_postfork_parent(tsdn_t *tsdn, base_t *base);
-void base_postfork_child(tsdn_t *tsdn, base_t *base);
-bool base_boot(tsdn_t *tsdn);
-
-#endif /* JEMALLOC_INTERNAL_BASE_EXTERNS_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/base_inlines.h b/contrib/jemalloc/include/jemalloc/internal/base_inlines.h
deleted file mode 100644
index aec0e2e1e1c5..000000000000
--- a/contrib/jemalloc/include/jemalloc/internal/base_inlines.h
+++ /dev/null
@@ -1,13 +0,0 @@
-#ifndef JEMALLOC_INTERNAL_BASE_INLINES_H
-#define JEMALLOC_INTERNAL_BASE_INLINES_H
-
-static inline unsigned
-base_ind_get(const base_t *base) {
- return base->ind;
-}
-
-static inline bool
-metadata_thp_enabled(void) {
- return (opt_metadata_thp != metadata_thp_disabled);
-}
-#endif /* JEMALLOC_INTERNAL_BASE_INLINES_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/base_structs.h b/contrib/jemalloc/include/jemalloc/internal/base_structs.h
deleted file mode 100644
index 07f214eb2f2a..000000000000
--- a/contrib/jemalloc/include/jemalloc/internal/base_structs.h
+++ /dev/null
@@ -1,59 +0,0 @@
-#ifndef JEMALLOC_INTERNAL_BASE_STRUCTS_H
-#define JEMALLOC_INTERNAL_BASE_STRUCTS_H
-
-#include "jemalloc/internal/jemalloc_internal_types.h"
-#include "jemalloc/internal/mutex.h"
-#include "jemalloc/internal/sc.h"
-
-/* Embedded at the beginning of every block of base-managed virtual memory. */
-struct base_block_s {
- /* Total size of block's virtual memory mapping. */
- size_t size;
-
- /* Next block in list of base's blocks. */
- base_block_t *next;
-
- /* Tracks unused trailing space. */
- extent_t extent;
-};
-
-struct base_s {
- /* Associated arena's index within the arenas array. */
- unsigned ind;
-
- /*
- * User-configurable extent hook functions. Points to an
- * extent_hooks_t.
- */
- atomic_p_t extent_hooks;
-
- /* Protects base_alloc() and base_stats_get() operations. */
- malloc_mutex_t mtx;
-
- /* Using THP when true (metadata_thp auto mode). */
- bool auto_thp_switched;
- /*
- * Most recent size class in the series of increasingly large base
- * extents. Logarithmic spacing between subsequent allocations ensures
- * that the total number of distinct mappings remains small.
- */
- pszind_t pind_last;
-
- /* Serial number generation state. */
- size_t extent_sn_next;
-
- /* Chain of all blocks associated with base. */
- base_block_t *blocks;
-
- /* Heap of extents that track unused trailing space within blocks. */
- extent_heap_t avail[SC_NSIZES];
-
- /* Stats, only maintained if config_stats. */
- size_t allocated;
- size_t resident;
- size_t mapped;
- /* Number of THP regions touched. */
- size_t n_thp;
-};
-
-#endif /* JEMALLOC_INTERNAL_BASE_STRUCTS_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/base_types.h b/contrib/jemalloc/include/jemalloc/internal/base_types.h
deleted file mode 100644
index b6db77df7c6c..000000000000
--- a/contrib/jemalloc/include/jemalloc/internal/base_types.h
+++ /dev/null
@@ -1,33 +0,0 @@
-#ifndef JEMALLOC_INTERNAL_BASE_TYPES_H
-#define JEMALLOC_INTERNAL_BASE_TYPES_H
-
-typedef struct base_block_s base_block_t;
-typedef struct base_s base_t;
-
-#define METADATA_THP_DEFAULT metadata_thp_disabled
-
-/*
- * In auto mode, arenas switch to huge pages for the base allocator on the
- * second base block. a0 switches to thp on the 5th block (after 20 megabytes
- * of metadata), since more metadata (e.g. rtree nodes) come from a0's base.
- */
-
-#define BASE_AUTO_THP_THRESHOLD 2
-#define BASE_AUTO_THP_THRESHOLD_A0 5
-
-typedef enum {
- metadata_thp_disabled = 0,
- /*
- * Lazily enable hugepage for metadata. To avoid high RSS caused by THP
- * + low usage arena (i.e. THP becomes a significant percentage), the
- * "auto" option only starts using THP after a base allocator used up
- * the first THP region. Starting from the second hugepage (in a single
- * arena), "auto" behaves the same as "always", i.e. madvise hugepage
- * right away.
- */
- metadata_thp_auto = 1,
- metadata_thp_always = 2,
- metadata_thp_mode_limit = 3
-} metadata_thp_mode_t;
-
-#endif /* JEMALLOC_INTERNAL_BASE_TYPES_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/bin.h b/contrib/jemalloc/include/jemalloc/internal/bin.h
index 8547e89309bc..63f97395e91d 100644
--- a/contrib/jemalloc/include/jemalloc/internal/bin.h
+++ b/contrib/jemalloc/include/jemalloc/internal/bin.h
@@ -3,8 +3,7 @@
#include "jemalloc/internal/bin_stats.h"
#include "jemalloc/internal/bin_types.h"
-#include "jemalloc/internal/extent_types.h"
-#include "jemalloc/internal/extent_structs.h"
+#include "jemalloc/internal/edata.h"
#include "jemalloc/internal/mutex.h"
#include "jemalloc/internal/sc.h"
@@ -12,74 +11,34 @@
* A bin contains a set of extents that are currently being used for slab
* allocations.
*/
-
-/*
- * Read-only information associated with each element of arena_t's bins array
- * is stored separately, partly to reduce memory usage (only one copy, rather
- * than one per arena), but mainly to avoid false cacheline sharing.
- *
- * Each slab has the following layout:
- *
- * /--------------------\
- * | region 0 |
- * |--------------------|
- * | region 1 |
- * |--------------------|
- * | ... |
- * | ... |
- * | ... |
- * |--------------------|
- * | region nregs-1 |
- * \--------------------/
- */
-typedef struct bin_info_s bin_info_t;
-struct bin_info_s {
- /* Size of regions in a slab for this bin's size class. */
- size_t reg_size;
-
- /* Total size of a slab for this bin's size class. */
- size_t slab_size;
-
- /* Total number of regions in a slab for this bin's size class. */
- uint32_t nregs;
-
- /* Number of sharded bins in each arena for this size class. */
- uint32_t n_shards;
-
- /*
- * Metadata used to manipulate bitmaps for slabs associated with this
- * bin.
- */
- bitmap_info_t bitmap_info;
-};
-
-extern bin_info_t bin_infos[SC_NBINS];
-
typedef struct bin_s bin_t;
struct bin_s {
/* All operations on bin_t fields require lock ownership. */
malloc_mutex_t lock;
/*
+ * Bin statistics. These get touched every time the lock is acquired,
+ * so put them close by in the hopes of getting some cache locality.
+ */
+ bin_stats_t stats;
+
+ /*
* Current slab being used to service allocations of this bin's size
* class. slabcur is independent of slabs_{nonfull,full}; whenever
* slabcur is reassigned, the previous slab must be deallocated or
* inserted into slabs_{nonfull,full}.
*/
- extent_t *slabcur;
+ edata_t *slabcur;
/*
* Heap of non-full slabs. This heap is used to assure that new
* allocations come from the non-full slab that is oldest/lowest in
* memory.
*/
- extent_heap_t slabs_nonfull;
+ edata_heap_t slabs_nonfull;
/* List used to track full slabs. */
- extent_list_t slabs_full;
-
- /* Bin statistics. */
- bin_stats_t stats;
+ edata_list_active_t slabs_full;
};
/* A set of sharded bins of the same size class. */
@@ -92,7 +51,6 @@ struct bins_s {
void bin_shard_sizes_boot(unsigned bin_shards[SC_NBINS]);
bool bin_update_shard_size(unsigned bin_shards[SC_NBINS], size_t start_size,
size_t end_size, size_t nshards);
-void bin_boot(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS]);
/* Initializes a bin to empty. Returns true on error. */
bool bin_init(bin_t *bin);
@@ -104,19 +62,20 @@ void bin_postfork_child(tsdn_t *tsdn, bin_t *bin);
/* Stats. */
static inline void
-bin_stats_merge(tsdn_t *tsdn, bin_stats_t *dst_bin_stats, bin_t *bin) {
+bin_stats_merge(tsdn_t *tsdn, bin_stats_data_t *dst_bin_stats, bin_t *bin) {
malloc_mutex_lock(tsdn, &bin->lock);
malloc_mutex_prof_accum(tsdn, &dst_bin_stats->mutex_data, &bin->lock);
- dst_bin_stats->nmalloc += bin->stats.nmalloc;
- dst_bin_stats->ndalloc += bin->stats.ndalloc;
- dst_bin_stats->nrequests += bin->stats.nrequests;
- dst_bin_stats->curregs += bin->stats.curregs;
- dst_bin_stats->nfills += bin->stats.nfills;
- dst_bin_stats->nflushes += bin->stats.nflushes;
- dst_bin_stats->nslabs += bin->stats.nslabs;
- dst_bin_stats->reslabs += bin->stats.reslabs;
- dst_bin_stats->curslabs += bin->stats.curslabs;
- dst_bin_stats->nonfull_slabs += bin->stats.nonfull_slabs;
+ bin_stats_t *stats = &dst_bin_stats->stats_data;
+ stats->nmalloc += bin->stats.nmalloc;
+ stats->ndalloc += bin->stats.ndalloc;
+ stats->nrequests += bin->stats.nrequests;
+ stats->curregs += bin->stats.curregs;
+ stats->nfills += bin->stats.nfills;
+ stats->nflushes += bin->stats.nflushes;
+ stats->nslabs += bin->stats.nslabs;
+ stats->reslabs += bin->stats.reslabs;
+ stats->curslabs += bin->stats.curslabs;
+ stats->nonfull_slabs += bin->stats.nonfull_slabs;
malloc_mutex_unlock(tsdn, &bin->lock);
}
diff --git a/contrib/jemalloc/include/jemalloc/internal/bin_info.h b/contrib/jemalloc/include/jemalloc/internal/bin_info.h
new file mode 100644
index 000000000000..7fe65c866a10
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/internal/bin_info.h
@@ -0,0 +1,50 @@
+#ifndef JEMALLOC_INTERNAL_BIN_INFO_H
+#define JEMALLOC_INTERNAL_BIN_INFO_H
+
+#include "jemalloc/internal/bitmap.h"
+
+/*
+ * Read-only information associated with each element of arena_t's bins array
+ * is stored separately, partly to reduce memory usage (only one copy, rather
+ * than one per arena), but mainly to avoid false cacheline sharing.
+ *
+ * Each slab has the following layout:
+ *
+ * /--------------------\
+ * | region 0 |
+ * |--------------------|
+ * | region 1 |
+ * |--------------------|
+ * | ... |
+ * | ... |
+ * | ... |
+ * |--------------------|
+ * | region nregs-1 |
+ * \--------------------/
+ */
+typedef struct bin_info_s bin_info_t;
+struct bin_info_s {
+ /* Size of regions in a slab for this bin's size class. */
+ size_t reg_size;
+
+ /* Total size of a slab for this bin's size class. */
+ size_t slab_size;
+
+ /* Total number of regions in a slab for this bin's size class. */
+ uint32_t nregs;
+
+ /* Number of sharded bins in each arena for this size class. */
+ uint32_t n_shards;
+
+ /*
+ * Metadata used to manipulate bitmaps for slabs associated with this
+ * bin.
+ */
+ bitmap_info_t bitmap_info;
+};
+
+extern bin_info_t bin_infos[SC_NBINS];
+
+void bin_info_boot(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS]);
+
+#endif /* JEMALLOC_INTERNAL_BIN_INFO_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/bin_stats.h b/contrib/jemalloc/include/jemalloc/internal/bin_stats.h
index d04519c8244c..0b99297c070a 100644
--- a/contrib/jemalloc/include/jemalloc/internal/bin_stats.h
+++ b/contrib/jemalloc/include/jemalloc/internal/bin_stats.h
@@ -47,8 +47,11 @@ struct bin_stats_s {
/* Current size of nonfull slabs heap in this bin. */
size_t nonfull_slabs;
+};
+typedef struct bin_stats_data_s bin_stats_data_t;
+struct bin_stats_data_s {
+ bin_stats_t stats_data;
mutex_prof_data_t mutex_data;
};
-
#endif /* JEMALLOC_INTERNAL_BIN_STATS_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/bin_types.h b/contrib/jemalloc/include/jemalloc/internal/bin_types.h
index 3533606b90c4..945e8326c63d 100644
--- a/contrib/jemalloc/include/jemalloc/internal/bin_types.h
+++ b/contrib/jemalloc/include/jemalloc/internal/bin_types.h
@@ -3,7 +3,7 @@
#include "jemalloc/internal/sc.h"
-#define BIN_SHARDS_MAX (1 << EXTENT_BITS_BINSHARD_WIDTH)
+#define BIN_SHARDS_MAX (1 << EDATA_BITS_BINSHARD_WIDTH)
#define N_BIN_SHARDS_DEFAULT 1
/* Used in TSD static initializer only. Real init in arena_bind(). */
diff --git a/contrib/jemalloc/include/jemalloc/internal/bit_util.h b/contrib/jemalloc/include/jemalloc/internal/bit_util.h
index c045eb86878a..bac59140fbb0 100644
--- a/contrib/jemalloc/include/jemalloc/internal/bit_util.h
+++ b/contrib/jemalloc/include/jemalloc/internal/bit_util.h
@@ -3,144 +3,383 @@
#include "jemalloc/internal/assert.h"
-#define BIT_UTIL_INLINE static inline
-
/* Sanity check. */
#if !defined(JEMALLOC_INTERNAL_FFSLL) || !defined(JEMALLOC_INTERNAL_FFSL) \
|| !defined(JEMALLOC_INTERNAL_FFS)
# error JEMALLOC_INTERNAL_FFS{,L,LL} should have been defined by configure
#endif
+/*
+ * Unlike the builtins and posix ffs functions, our ffs requires a non-zero
+ * input, and returns the position of the lowest bit set (as opposed to the
+ * posix versions, which return 1 larger than that position and use a return
+ * value of zero as a sentinel. This tends to simplify logic in callers, and
+ * allows for consistency with the builtins we build fls on top of.
+ */
+static inline unsigned
+ffs_llu(unsigned long long x) {
+ util_assume(x != 0);
+ return JEMALLOC_INTERNAL_FFSLL(x) - 1;
+}
-BIT_UTIL_INLINE unsigned
-ffs_llu(unsigned long long bitmap) {
- return JEMALLOC_INTERNAL_FFSLL(bitmap);
+static inline unsigned
+ffs_lu(unsigned long x) {
+ util_assume(x != 0);
+ return JEMALLOC_INTERNAL_FFSL(x) - 1;
}
-BIT_UTIL_INLINE unsigned
-ffs_lu(unsigned long bitmap) {
- return JEMALLOC_INTERNAL_FFSL(bitmap);
+static inline unsigned
+ffs_u(unsigned x) {
+ util_assume(x != 0);
+ return JEMALLOC_INTERNAL_FFS(x) - 1;
}
-BIT_UTIL_INLINE unsigned
-ffs_u(unsigned bitmap) {
- return JEMALLOC_INTERNAL_FFS(bitmap);
+#define DO_FLS_SLOW(x, suffix) do { \
+ util_assume(x != 0); \
+ x |= (x >> 1); \
+ x |= (x >> 2); \
+ x |= (x >> 4); \
+ x |= (x >> 8); \
+ x |= (x >> 16); \
+ if (sizeof(x) > 4) { \
+ /* \
+ * If sizeof(x) is 4, then the expression "x >> 32" \
+ * will generate compiler warnings even if the code \
+ * never executes. This circumvents the warning, and \
+ * gets compiled out in optimized builds. \
+ */ \
+ int constant_32 = sizeof(x) * 4; \
+ x |= (x >> constant_32); \
+ } \
+ x++; \
+ if (x == 0) { \
+ return 8 * sizeof(x) - 1; \
+ } \
+ return ffs_##suffix(x) - 1; \
+} while(0)
+
+static inline unsigned
+fls_llu_slow(unsigned long long x) {
+ DO_FLS_SLOW(x, llu);
}
-#ifdef JEMALLOC_INTERNAL_POPCOUNTL
-BIT_UTIL_INLINE unsigned
+static inline unsigned
+fls_lu_slow(unsigned long x) {
+ DO_FLS_SLOW(x, lu);
+}
+
+static inline unsigned
+fls_u_slow(unsigned x) {
+ DO_FLS_SLOW(x, u);
+}
+
+#undef DO_FLS_SLOW
+
+#ifdef JEMALLOC_HAVE_BUILTIN_CLZ
+static inline unsigned
+fls_llu(unsigned long long x) {
+ util_assume(x != 0);
+ /*
+ * Note that the xor here is more naturally written as subtraction; the
+ * last bit set is the number of bits in the type minus the number of
+ * leading zero bits. But GCC implements that as:
+ * bsr edi, edi
+ * mov eax, 31
+ * xor edi, 31
+ * sub eax, edi
+ * If we write it as xor instead, then we get
+ * bsr eax, edi
+ * as desired.
+ */
+ return (8 * sizeof(x) - 1) ^ __builtin_clzll(x);
+}
+
+static inline unsigned
+fls_lu(unsigned long x) {
+ util_assume(x != 0);
+ return (8 * sizeof(x) - 1) ^ __builtin_clzl(x);
+}
+
+static inline unsigned
+fls_u(unsigned x) {
+ util_assume(x != 0);
+ return (8 * sizeof(x) - 1) ^ __builtin_clz(x);
+}
+#elif defined(_MSC_VER)
+
+#if LG_SIZEOF_PTR == 3
+#define DO_BSR64(bit, x) _BitScanReverse64(&bit, x)
+#else
+/*
+ * This never actually runs; we're just dodging a compiler error for the
+ * never-taken branch where sizeof(void *) == 8.
+ */
+#define DO_BSR64(bit, x) bit = 0; unreachable()
+#endif
+
+#define DO_FLS(x) do { \
+ if (x == 0) { \
+ return 8 * sizeof(x); \
+ } \
+ unsigned long bit; \
+ if (sizeof(x) == 4) { \
+ _BitScanReverse(&bit, (unsigned)x); \
+ return (unsigned)bit; \
+ } \
+ if (sizeof(x) == 8 && sizeof(void *) == 8) { \
+ DO_BSR64(bit, x); \
+ return (unsigned)bit; \
+ } \
+ if (sizeof(x) == 8 && sizeof(void *) == 4) { \
+ /* Dodge a compiler warning, as above. */ \
+ int constant_32 = sizeof(x) * 4; \
+ if (_BitScanReverse(&bit, \
+ (unsigned)(x >> constant_32))) { \
+ return 32 + (unsigned)bit; \
+ } else { \
+ _BitScanReverse(&bit, (unsigned)x); \
+ return (unsigned)bit; \
+ } \
+ } \
+ unreachable(); \
+} while (0)
+
+static inline unsigned
+fls_llu(unsigned long long x) {
+ DO_FLS(x);
+}
+
+static inline unsigned
+fls_lu(unsigned long x) {
+ DO_FLS(x);
+}
+
+static inline unsigned
+fls_u(unsigned x) {
+ DO_FLS(x);
+}
+
+#undef DO_FLS
+#undef DO_BSR64
+#else
+
+static inline unsigned
+fls_llu(unsigned long long x) {
+ return fls_llu_slow(x);
+}
+
+static inline unsigned
+fls_lu(unsigned long x) {
+ return fls_lu_slow(x);
+}
+
+static inline unsigned
+fls_u(unsigned x) {
+ return fls_u_slow(x);
+}
+#endif
+
+#if LG_SIZEOF_LONG_LONG > 3
+# error "Haven't implemented popcount for 16-byte ints."
+#endif
+
+#define DO_POPCOUNT(x, type) do { \
+ /* \
+ * Algorithm from an old AMD optimization reference manual. \
+ * We're putting a little bit more work than you might expect \
+ * into the no-instrinsic case, since we only support the \
+ * GCC intrinsics spelling of popcount (for now). Detecting \
+ * whether or not the popcount builtin is actually useable in \
+ * MSVC is nontrivial. \
+ */ \
+ \
+ type bmul = (type)0x0101010101010101ULL; \
+ \
+ /* \
+ * Replace each 2 bits with the sideways sum of the original \
+ * values. 0x5 = 0b0101. \
+ * \
+ * You might expect this to be: \
+ * x = (x & 0x55...) + ((x >> 1) & 0x55...). \
+ * That costs an extra mask relative to this, though. \
+ */ \
+ x = x - ((x >> 1) & (0x55U * bmul)); \
+ /* Replace each 4 bits with their sideays sum. 0x3 = 0b0011. */\
+ x = (x & (bmul * 0x33U)) + ((x >> 2) & (bmul * 0x33U)); \
+ /* \
+ * Replace each 8 bits with their sideways sum. Note that we \
+ * can't overflow within each 4-bit sum here, so we can skip \
+ * the initial mask. \
+ */ \
+ x = (x + (x >> 4)) & (bmul * 0x0FU); \
+ /* \
+ * None of the partial sums in this multiplication (viewed in \
+ * base-256) can overflow into the next digit. So the least \
+ * significant byte of the product will be the least \
+ * significant byte of the original value, the second least \
+ * significant byte will be the sum of the two least \
+ * significant bytes of the original value, and so on. \
+ * Importantly, the high byte will be the byte-wise sum of all \
+ * the bytes of the original value. \
+ */ \
+ x = x * bmul; \
+ x >>= ((sizeof(x) - 1) * 8); \
+ return (unsigned)x; \
+} while(0)
+
+static inline unsigned
+popcount_u_slow(unsigned bitmap) {
+ DO_POPCOUNT(bitmap, unsigned);
+}
+
+static inline unsigned
+popcount_lu_slow(unsigned long bitmap) {
+ DO_POPCOUNT(bitmap, unsigned long);
+}
+
+static inline unsigned
+popcount_llu_slow(unsigned long long bitmap) {
+ DO_POPCOUNT(bitmap, unsigned long long);
+}
+
+#undef DO_POPCOUNT
+
+static inline unsigned
+popcount_u(unsigned bitmap) {
+#ifdef JEMALLOC_INTERNAL_POPCOUNT
+ return JEMALLOC_INTERNAL_POPCOUNT(bitmap);
+#else
+ return popcount_u_slow(bitmap);
+#endif
+}
+
+static inline unsigned
popcount_lu(unsigned long bitmap) {
- return JEMALLOC_INTERNAL_POPCOUNTL(bitmap);
+#ifdef JEMALLOC_INTERNAL_POPCOUNTL
+ return JEMALLOC_INTERNAL_POPCOUNTL(bitmap);
+#else
+ return popcount_lu_slow(bitmap);
+#endif
}
+
+static inline unsigned
+popcount_llu(unsigned long long bitmap) {
+#ifdef JEMALLOC_INTERNAL_POPCOUNTLL
+ return JEMALLOC_INTERNAL_POPCOUNTLL(bitmap);
+#else
+ return popcount_llu_slow(bitmap);
#endif
+}
/*
* Clears first unset bit in bitmap, and returns
* place of bit. bitmap *must not* be 0.
*/
-BIT_UTIL_INLINE size_t
+static inline size_t
cfs_lu(unsigned long* bitmap) {
- size_t bit = ffs_lu(*bitmap) - 1;
+ util_assume(*bitmap != 0);
+ size_t bit = ffs_lu(*bitmap);
*bitmap ^= ZU(1) << bit;
return bit;
}
-BIT_UTIL_INLINE unsigned
-ffs_zu(size_t bitmap) {
+static inline unsigned
+ffs_zu(size_t x) {
#if LG_SIZEOF_PTR == LG_SIZEOF_INT
- return ffs_u(bitmap);
+ return ffs_u(x);
#elif LG_SIZEOF_PTR == LG_SIZEOF_LONG
- return ffs_lu(bitmap);
+ return ffs_lu(x);
#elif LG_SIZEOF_PTR == LG_SIZEOF_LONG_LONG
- return ffs_llu(bitmap);
+ return ffs_llu(x);
#else
#error No implementation for size_t ffs()
#endif
}
-BIT_UTIL_INLINE unsigned
-ffs_u64(uint64_t bitmap) {
+static inline unsigned
+fls_zu(size_t x) {
+#if LG_SIZEOF_PTR == LG_SIZEOF_INT
+ return fls_u(x);
+#elif LG_SIZEOF_PTR == LG_SIZEOF_LONG
+ return fls_lu(x);
+#elif LG_SIZEOF_PTR == LG_SIZEOF_LONG_LONG
+ return fls_llu(x);
+#else
+#error No implementation for size_t fls()
+#endif
+}
+
+
+static inline unsigned
+ffs_u64(uint64_t x) {
#if LG_SIZEOF_LONG == 3
- return ffs_lu(bitmap);
+ return ffs_lu(x);
#elif LG_SIZEOF_LONG_LONG == 3
- return ffs_llu(bitmap);
+ return ffs_llu(x);
#else
#error No implementation for 64-bit ffs()
#endif
}
-BIT_UTIL_INLINE unsigned
-ffs_u32(uint32_t bitmap) {
+static inline unsigned
+fls_u64(uint64_t x) {
+#if LG_SIZEOF_LONG == 3
+ return fls_lu(x);
+#elif LG_SIZEOF_LONG_LONG == 3
+ return fls_llu(x);
+#else
+#error No implementation for 64-bit fls()
+#endif
+}
+
+static inline unsigned
+ffs_u32(uint32_t x) {
#if LG_SIZEOF_INT == 2
- return ffs_u(bitmap);
+ return ffs_u(x);
#else
#error No implementation for 32-bit ffs()
#endif
- return ffs_u(bitmap);
+ return ffs_u(x);
+}
+
+static inline unsigned
+fls_u32(uint32_t x) {
+#if LG_SIZEOF_INT == 2
+ return fls_u(x);
+#else
+#error No implementation for 32-bit fls()
+#endif
+ return fls_u(x);
}
-BIT_UTIL_INLINE uint64_t
+static inline uint64_t
pow2_ceil_u64(uint64_t x) {
-#if (defined(__amd64__) || defined(__x86_64__) || defined(JEMALLOC_HAVE_BUILTIN_CLZ))
- if(unlikely(x <= 1)) {
+ if (unlikely(x <= 1)) {
return x;
}
- size_t msb_on_index;
-#if (defined(__amd64__) || defined(__x86_64__))
- asm ("bsrq %1, %0"
- : "=r"(msb_on_index) // Outputs.
- : "r"(x-1) // Inputs.
- );
-#elif (defined(JEMALLOC_HAVE_BUILTIN_CLZ))
- msb_on_index = (63 ^ __builtin_clzll(x - 1));
-#endif
+ size_t msb_on_index = fls_u64(x - 1);
+ /*
+ * Range-check; it's on the callers to ensure that the result of this
+ * call won't overflow.
+ */
assert(msb_on_index < 63);
return 1ULL << (msb_on_index + 1);
-#else
- x--;
- x |= x >> 1;
- x |= x >> 2;
- x |= x >> 4;
- x |= x >> 8;
- x |= x >> 16;
- x |= x >> 32;
- x++;
- return x;
-#endif
}
-BIT_UTIL_INLINE uint32_t
+static inline uint32_t
pow2_ceil_u32(uint32_t x) {
-#if ((defined(__i386__) || defined(JEMALLOC_HAVE_BUILTIN_CLZ)) && (!defined(__s390__)))
- if(unlikely(x <= 1)) {
- return x;
+ if (unlikely(x <= 1)) {
+ return x;
}
- size_t msb_on_index;
-#if (defined(__i386__))
- asm ("bsr %1, %0"
- : "=r"(msb_on_index) // Outputs.
- : "r"(x-1) // Inputs.
- );
-#elif (defined(JEMALLOC_HAVE_BUILTIN_CLZ))
- msb_on_index = (31 ^ __builtin_clz(x - 1));
-#endif
+ size_t msb_on_index = fls_u32(x - 1);
+ /* As above. */
assert(msb_on_index < 31);
return 1U << (msb_on_index + 1);
-#else
- x--;
- x |= x >> 1;
- x |= x >> 2;
- x |= x >> 4;
- x |= x >> 8;
- x |= x >> 16;
- x++;
- return x;
-#endif
}
/* Compute the smallest power of 2 that is >= x. */
-BIT_UTIL_INLINE size_t
+static inline size_t
pow2_ceil_zu(size_t x) {
#if (LG_SIZEOF_PTR == 3)
return pow2_ceil_u64(x);
@@ -149,77 +388,21 @@ pow2_ceil_zu(size_t x) {
#endif
}
-#if (defined(__i386__) || defined(__amd64__) || defined(__x86_64__))
-BIT_UTIL_INLINE unsigned
-lg_floor(size_t x) {
- size_t ret;
- assert(x != 0);
-
- asm ("bsr %1, %0"
- : "=r"(ret) // Outputs.
- : "r"(x) // Inputs.
- );
- assert(ret < UINT_MAX);
- return (unsigned)ret;
-}
-#elif (defined(_MSC_VER))
-BIT_UTIL_INLINE unsigned
+static inline unsigned
lg_floor(size_t x) {
- unsigned long ret;
-
- assert(x != 0);
-
+ util_assume(x != 0);
#if (LG_SIZEOF_PTR == 3)
- _BitScanReverse64(&ret, x);
-#elif (LG_SIZEOF_PTR == 2)
- _BitScanReverse(&ret, x);
+ return fls_u64(x);
#else
-# error "Unsupported type size for lg_floor()"
+ return fls_u32(x);
#endif
- assert(ret < UINT_MAX);
- return (unsigned)ret;
}
-#elif (defined(JEMALLOC_HAVE_BUILTIN_CLZ))
-BIT_UTIL_INLINE unsigned
-lg_floor(size_t x) {
- assert(x != 0);
-#if (LG_SIZEOF_PTR == LG_SIZEOF_INT)
- return ((8 << LG_SIZEOF_PTR) - 1) - __builtin_clz(x);
-#elif (LG_SIZEOF_PTR == LG_SIZEOF_LONG)
- return ((8 << LG_SIZEOF_PTR) - 1) - __builtin_clzl(x);
-#else
-# error "Unsupported type size for lg_floor()"
-#endif
-}
-#else
-BIT_UTIL_INLINE unsigned
-lg_floor(size_t x) {
- assert(x != 0);
-
- x |= (x >> 1);
- x |= (x >> 2);
- x |= (x >> 4);
- x |= (x >> 8);
- x |= (x >> 16);
-#if (LG_SIZEOF_PTR == 3)
- x |= (x >> 32);
-#endif
- if (x == SIZE_T_MAX) {
- return (8 << LG_SIZEOF_PTR) - 1;
- }
- x++;
- return ffs_zu(x) - 2;
-}
-#endif
-
-BIT_UTIL_INLINE unsigned
+static inline unsigned
lg_ceil(size_t x) {
return lg_floor(x) + ((x & (x - 1)) == 0 ? 0 : 1);
}
-#undef BIT_UTIL_INLINE
-
/* A compile-time version of lg_floor and lg_ceil. */
#define LG_FLOOR_1(x) 0
#define LG_FLOOR_2(x) (x < (1ULL << 1) ? LG_FLOOR_1(x) : 1 + LG_FLOOR_1(x >> 1))
diff --git a/contrib/jemalloc/include/jemalloc/internal/bitmap.h b/contrib/jemalloc/include/jemalloc/internal/bitmap.h
index c3f9cb490f67..dc19454d46ee 100644
--- a/contrib/jemalloc/include/jemalloc/internal/bitmap.h
+++ b/contrib/jemalloc/include/jemalloc/internal/bitmap.h
@@ -1,7 +1,6 @@
#ifndef JEMALLOC_INTERNAL_BITMAP_H
#define JEMALLOC_INTERNAL_BITMAP_H
-#include "jemalloc/internal/arena_types.h"
#include "jemalloc/internal/bit_util.h"
#include "jemalloc/internal/sc.h"
@@ -9,9 +8,9 @@ typedef unsigned long bitmap_t;
#define LG_SIZEOF_BITMAP LG_SIZEOF_LONG
/* Maximum bitmap bit count is 2^LG_BITMAP_MAXBITS. */
-#if LG_SLAB_MAXREGS > LG_CEIL(SC_NSIZES)
+#if SC_LG_SLAB_MAXREGS > LG_CEIL(SC_NSIZES)
/* Maximum bitmap bit count is determined by maximum regions per slab. */
-# define LG_BITMAP_MAXBITS LG_SLAB_MAXREGS
+# define LG_BITMAP_MAXBITS SC_LG_SLAB_MAXREGS
#else
/* Maximum bitmap bit count is determined by number of extent size classes. */
# define LG_BITMAP_MAXBITS LG_CEIL(SC_NSIZES)
@@ -273,7 +272,7 @@ bitmap_ffu(const bitmap_t *bitmap, const bitmap_info_t *binfo, size_t min_bit) {
}
return bitmap_ffu(bitmap, binfo, sib_base);
}
- bit += ((size_t)(ffs_lu(group_masked) - 1)) <<
+ bit += ((size_t)ffs_lu(group_masked)) <<
(lg_bits_per_group - LG_BITMAP_GROUP_NBITS);
}
assert(bit >= min_bit);
@@ -285,9 +284,9 @@ bitmap_ffu(const bitmap_t *bitmap, const bitmap_info_t *binfo, size_t min_bit) {
- 1);
size_t bit;
do {
- bit = ffs_lu(g);
- if (bit != 0) {
- return (i << LG_BITMAP_GROUP_NBITS) + (bit - 1);
+ if (g != 0) {
+ bit = ffs_lu(g);
+ return (i << LG_BITMAP_GROUP_NBITS) + bit;
}
i++;
g = bitmap[i];
@@ -308,20 +307,20 @@ bitmap_sfu(bitmap_t *bitmap, const bitmap_info_t *binfo) {
#ifdef BITMAP_USE_TREE
i = binfo->nlevels - 1;
g = bitmap[binfo->levels[i].group_offset];
- bit = ffs_lu(g) - 1;
+ bit = ffs_lu(g);
while (i > 0) {
i--;
g = bitmap[binfo->levels[i].group_offset + bit];
- bit = (bit << LG_BITMAP_GROUP_NBITS) + (ffs_lu(g) - 1);
+ bit = (bit << LG_BITMAP_GROUP_NBITS) + ffs_lu(g);
}
#else
i = 0;
g = bitmap[0];
- while ((bit = ffs_lu(g)) == 0) {
+ while (g == 0) {
i++;
g = bitmap[i];
}
- bit = (i << LG_BITMAP_GROUP_NBITS) + (bit - 1);
+ bit = (i << LG_BITMAP_GROUP_NBITS) + ffs_lu(g);
#endif
bitmap_set(bitmap, binfo, bit);
return bit;
diff --git a/contrib/jemalloc/include/jemalloc/internal/buf_writer.h b/contrib/jemalloc/include/jemalloc/internal/buf_writer.h
new file mode 100644
index 000000000000..37aa6de5b3c3
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/internal/buf_writer.h
@@ -0,0 +1,32 @@
+#ifndef JEMALLOC_INTERNAL_BUF_WRITER_H
+#define JEMALLOC_INTERNAL_BUF_WRITER_H
+
+/*
+ * Note: when using the buffered writer, cbopaque is passed to write_cb only
+ * when the buffer is flushed. It would make a difference if cbopaque points
+ * to something that's changing for each write_cb call, or something that
+ * affects write_cb in a way dependent on the content of the output string.
+ * However, the most typical usage case in practice is that cbopaque points to
+ * some "option like" content for the write_cb, so it doesn't matter.
+ */
+
+typedef struct {
+ write_cb_t *write_cb;
+ void *cbopaque;
+ char *buf;
+ size_t buf_size;
+ size_t buf_end;
+ bool internal_buf;
+} buf_writer_t;
+
+bool buf_writer_init(tsdn_t *tsdn, buf_writer_t *buf_writer,
+ write_cb_t *write_cb, void *cbopaque, char *buf, size_t buf_len);
+void buf_writer_flush(buf_writer_t *buf_writer);
+write_cb_t buf_writer_cb;
+void buf_writer_terminate(tsdn_t *tsdn, buf_writer_t *buf_writer);
+
+typedef ssize_t (read_cb_t)(void *read_cbopaque, void *buf, size_t limit);
+void buf_writer_pipe(buf_writer_t *buf_writer, read_cb_t *read_cb,
+ void *read_cbopaque);
+
+#endif /* JEMALLOC_INTERNAL_BUF_WRITER_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/cache_bin.h b/contrib/jemalloc/include/jemalloc/internal/cache_bin.h
index d14556a3da8a..caf5be338d6a 100644
--- a/contrib/jemalloc/include/jemalloc/internal/cache_bin.h
+++ b/contrib/jemalloc/include/jemalloc/internal/cache_bin.h
@@ -2,6 +2,7 @@
#define JEMALLOC_INTERNAL_CACHE_BIN_H
#include "jemalloc/internal/ql.h"
+#include "jemalloc/internal/sz.h"
/*
* The cache_bins are the mechanism that the tcache and the arena use to
@@ -13,14 +14,38 @@
* of the tcache at all.
*/
+/*
+ * The size in bytes of each cache bin stack. We also use this to indicate
+ * *counts* of individual objects.
+ */
+typedef uint16_t cache_bin_sz_t;
/*
- * The count of the number of cached allocations in a bin. We make this signed
- * so that negative numbers can encode "invalid" states (e.g. a low water mark
- * of -1 for a cache that has been depleted).
+ * Leave a noticeable mark pattern on the cache bin stack boundaries, in case a
+ * bug starts leaking those. Make it look like the junk pattern but be distinct
+ * from it.
*/
-typedef int32_t cache_bin_sz_t;
+static const uintptr_t cache_bin_preceding_junk =
+ (uintptr_t)0x7a7a7a7a7a7a7a7aULL;
+/* Note: a7 vs. 7a above -- this tells you which pointer leaked. */
+static const uintptr_t cache_bin_trailing_junk =
+ (uintptr_t)0xa7a7a7a7a7a7a7a7ULL;
+/*
+ * That implies the following value, for the maximum number of items in any
+ * individual bin. The cache bins track their bounds looking just at the low
+ * bits of a pointer, compared against a cache_bin_sz_t. So that's
+ * 1 << (sizeof(cache_bin_sz_t) * 8)
+ * bytes spread across pointer sized objects to get the maximum.
+ */
+#define CACHE_BIN_NCACHED_MAX (((size_t)1 << sizeof(cache_bin_sz_t) * 8) \
+ / sizeof(void *) - 1)
+
+/*
+ * This lives inside the cache_bin (for locality reasons), and is initialized
+ * alongside it, but is otherwise not modified by any cache bin operations.
+ * It's logically public and maintained by its callers.
+ */
typedef struct cache_bin_stats_s cache_bin_stats_t;
struct cache_bin_stats_s {
/*
@@ -36,34 +61,75 @@ struct cache_bin_stats_s {
*/
typedef struct cache_bin_info_s cache_bin_info_t;
struct cache_bin_info_s {
- /* Upper limit on ncached. */
cache_bin_sz_t ncached_max;
};
+/*
+ * Responsible for caching allocations associated with a single size.
+ *
+ * Several pointers are used to track the stack. To save on metadata bytes,
+ * only the stack_head is a full sized pointer (which is dereferenced on the
+ * fastpath), while the others store only the low 16 bits -- this is correct
+ * because a single stack never takes more space than 2^16 bytes, and at the
+ * same time only equality checks are performed on the low bits.
+ *
+ * (low addr) (high addr)
+ * |------stashed------|------available------|------cached-----|
+ * ^ ^ ^ ^
+ * low_bound(derived) low_bits_full stack_head low_bits_empty
+ */
typedef struct cache_bin_s cache_bin_t;
struct cache_bin_s {
- /* Min # cached since last GC. */
- cache_bin_sz_t low_water;
- /* # of cached objects. */
- cache_bin_sz_t ncached;
/*
- * ncached and stats are both modified frequently. Let's keep them
+ * The stack grows down. Whenever the bin is nonempty, the head points
+ * to an array entry containing a valid allocation. When it is empty,
+ * the head points to one element past the owned array.
+ */
+ void **stack_head;
+ /*
+ * cur_ptr and stats are both modified frequently. Let's keep them
* close so that they have a higher chance of being on the same
* cacheline, thus less write-backs.
*/
cache_bin_stats_t tstats;
+
/*
- * Stack of available objects.
+ * The low bits of the address of the first item in the stack that
+ * hasn't been used since the last GC, to track the low water mark (min
+ * # of cached items).
*
- * To make use of adjacent cacheline prefetch, the items in the avail
- * stack goes to higher address for newer allocations. avail points
- * just above the available space, which means that
- * avail[-ncached, ... -1] are available items and the lowest item will
- * be allocated first.
+ * Since the stack grows down, this is a higher address than
+ * low_bits_full.
*/
- void **avail;
+ uint16_t low_bits_low_water;
+
+ /*
+ * The low bits of the value that stack_head will take on when the array
+ * is full (of cached & stashed items). But remember that stack_head
+ * always points to a valid item when the array is nonempty -- this is
+ * in the array.
+ *
+ * Recall that since the stack grows down, this is the lowest available
+ * address in the array for caching. Only adjusted when stashing items.
+ */
+ uint16_t low_bits_full;
+
+ /*
+ * The low bits of the value that stack_head will take on when the array
+ * is empty.
+ *
+ * The stack grows down -- this is one past the highest address in the
+ * array. Immutable after initialization.
+ */
+ uint16_t low_bits_empty;
};
+/*
+ * The cache_bins live inside the tcache, but the arena (by design) isn't
+ * supposed to know much about tcache internals. To let the arena iterate over
+ * associated bins, we keep (with the tcache) a linked list of
+ * cache_bin_array_descriptor_ts that tell the arena how to find the bins.
+ */
typedef struct cache_bin_array_descriptor_s cache_bin_array_descriptor_t;
struct cache_bin_array_descriptor_s {
/*
@@ -72,37 +138,214 @@ struct cache_bin_array_descriptor_s {
*/
ql_elm(cache_bin_array_descriptor_t) link;
/* Pointers to the tcache bins. */
- cache_bin_t *bins_small;
- cache_bin_t *bins_large;
+ cache_bin_t *bins;
};
static inline void
cache_bin_array_descriptor_init(cache_bin_array_descriptor_t *descriptor,
- cache_bin_t *bins_small, cache_bin_t *bins_large) {
+ cache_bin_t *bins) {
ql_elm_new(descriptor, link);
- descriptor->bins_small = bins_small;
- descriptor->bins_large = bins_large;
+ descriptor->bins = bins;
}
-JEMALLOC_ALWAYS_INLINE void *
-cache_bin_alloc_easy(cache_bin_t *bin, bool *success) {
- void *ret;
+JEMALLOC_ALWAYS_INLINE bool
+cache_bin_nonfast_aligned(const void *ptr) {
+ if (!config_uaf_detection) {
+ return false;
+ }
+ /*
+ * Currently we use alignment to decide which pointer to junk & stash on
+ * dealloc (for catching use-after-free). In some common cases a
+ * page-aligned check is needed already (sdalloc w/ config_prof), so we
+ * are getting it more or less for free -- no added instructions on
+ * free_fastpath.
+ *
+ * Another way of deciding which pointer to sample, is adding another
+ * thread_event to pick one every N bytes. That also adds no cost on
+ * the fastpath, however it will tend to pick large allocations which is
+ * not the desired behavior.
+ */
+ return ((uintptr_t)ptr & san_cache_bin_nonfast_mask) == 0;
+}
+
+/* Returns ncached_max: Upper limit on ncached. */
+static inline cache_bin_sz_t
+cache_bin_info_ncached_max(cache_bin_info_t *info) {
+ return info->ncached_max;
+}
+
+/*
+ * Internal.
+ *
+ * Asserts that the pointer associated with earlier is <= the one associated
+ * with later.
+ */
+static inline void
+cache_bin_assert_earlier(cache_bin_t *bin, uint16_t earlier, uint16_t later) {
+ if (earlier > later) {
+ assert(bin->low_bits_full > bin->low_bits_empty);
+ }
+}
- bin->ncached--;
+/*
+ * Internal.
+ *
+ * Does difference calculations that handle wraparound correctly. Earlier must
+ * be associated with the position earlier in memory.
+ */
+static inline uint16_t
+cache_bin_diff(cache_bin_t *bin, uint16_t earlier, uint16_t later, bool racy) {
+ /*
+ * When it's racy, bin->low_bits_full can be modified concurrently. It
+ * can cross the uint16_t max value and become less than
+ * bin->low_bits_empty at the time of the check.
+ */
+ if (!racy) {
+ cache_bin_assert_earlier(bin, earlier, later);
+ }
+ return later - earlier;
+}
+/*
+ * Number of items currently cached in the bin, without checking ncached_max.
+ * We require specifying whether or not the request is racy or not (i.e. whether
+ * or not concurrent modifications are possible).
+ */
+static inline cache_bin_sz_t
+cache_bin_ncached_get_internal(cache_bin_t *bin, bool racy) {
+ cache_bin_sz_t diff = cache_bin_diff(bin,
+ (uint16_t)(uintptr_t)bin->stack_head, bin->low_bits_empty, racy);
+ cache_bin_sz_t n = diff / sizeof(void *);
/*
- * Check for both bin->ncached == 0 and ncached < low_water
- * in a single branch.
+ * We have undefined behavior here; if this function is called from the
+ * arena stats updating code, then stack_head could change from the
+ * first line to the next one. Morally, these loads should be atomic,
+ * but compilers won't currently generate comparisons with in-memory
+ * operands against atomics, and these variables get accessed on the
+ * fast paths. This should still be "safe" in the sense of generating
+ * the correct assembly for the foreseeable future, though.
*/
- if (unlikely(bin->ncached <= bin->low_water)) {
- bin->low_water = bin->ncached;
- if (bin->ncached == -1) {
- bin->ncached = 0;
- *success = false;
- return NULL;
- }
+ assert(n == 0 || *(bin->stack_head) != NULL || racy);
+ return n;
+}
+
+/*
+ * Number of items currently cached in the bin, with checking ncached_max. The
+ * caller must know that no concurrent modification of the cache_bin is
+ * possible.
+ */
+static inline cache_bin_sz_t
+cache_bin_ncached_get_local(cache_bin_t *bin, cache_bin_info_t *info) {
+ cache_bin_sz_t n = cache_bin_ncached_get_internal(bin,
+ /* racy */ false);
+ assert(n <= cache_bin_info_ncached_max(info));
+ return n;
+}
+
+/*
+ * Internal.
+ *
+ * A pointer to the position one past the end of the backing array.
+ *
+ * Do not call if racy, because both 'bin->stack_head' and 'bin->low_bits_full'
+ * are subject to concurrent modifications.
+ */
+static inline void **
+cache_bin_empty_position_get(cache_bin_t *bin) {
+ cache_bin_sz_t diff = cache_bin_diff(bin,
+ (uint16_t)(uintptr_t)bin->stack_head, bin->low_bits_empty,
+ /* racy */ false);
+ uintptr_t empty_bits = (uintptr_t)bin->stack_head + diff;
+ void **ret = (void **)empty_bits;
+
+ assert(ret >= bin->stack_head);
+
+ return ret;
+}
+
+/*
+ * Internal.
+ *
+ * Calculates low bits of the lower bound of the usable cache bin's range (see
+ * cache_bin_t visual representation above).
+ *
+ * No values are concurrently modified, so should be safe to read in a
+ * multithreaded environment. Currently concurrent access happens only during
+ * arena statistics collection.
+ */
+static inline uint16_t
+cache_bin_low_bits_low_bound_get(cache_bin_t *bin, cache_bin_info_t *info) {
+ return (uint16_t)bin->low_bits_empty -
+ info->ncached_max * sizeof(void *);
+}
+
+/*
+ * Internal.
+ *
+ * A pointer to the position with the lowest address of the backing array.
+ */
+static inline void **
+cache_bin_low_bound_get(cache_bin_t *bin, cache_bin_info_t *info) {
+ cache_bin_sz_t ncached_max = cache_bin_info_ncached_max(info);
+ void **ret = cache_bin_empty_position_get(bin) - ncached_max;
+ assert(ret <= bin->stack_head);
+
+ return ret;
+}
+
+/*
+ * As the name implies. This is important since it's not correct to try to
+ * batch fill a nonempty cache bin.
+ */
+static inline void
+cache_bin_assert_empty(cache_bin_t *bin, cache_bin_info_t *info) {
+ assert(cache_bin_ncached_get_local(bin, info) == 0);
+ assert(cache_bin_empty_position_get(bin) == bin->stack_head);
+}
+
+/*
+ * Get low water, but without any of the correctness checking we do for the
+ * caller-usable version, if we are temporarily breaking invariants (like
+ * ncached >= low_water during flush).
+ */
+static inline cache_bin_sz_t
+cache_bin_low_water_get_internal(cache_bin_t *bin) {
+ return cache_bin_diff(bin, bin->low_bits_low_water,
+ bin->low_bits_empty, /* racy */ false) / sizeof(void *);
+}
+
+/* Returns the numeric value of low water in [0, ncached]. */
+static inline cache_bin_sz_t
+cache_bin_low_water_get(cache_bin_t *bin, cache_bin_info_t *info) {
+ cache_bin_sz_t low_water = cache_bin_low_water_get_internal(bin);
+ assert(low_water <= cache_bin_info_ncached_max(info));
+ assert(low_water <= cache_bin_ncached_get_local(bin, info));
+
+ cache_bin_assert_earlier(bin, (uint16_t)(uintptr_t)bin->stack_head,
+ bin->low_bits_low_water);
+
+ return low_water;
+}
+
+/*
+ * Indicates that the current cache bin position should be the low water mark
+ * going forward.
+ */
+static inline void
+cache_bin_low_water_set(cache_bin_t *bin) {
+ bin->low_bits_low_water = (uint16_t)(uintptr_t)bin->stack_head;
+}
+
+static inline void
+cache_bin_low_water_adjust(cache_bin_t *bin) {
+ if (cache_bin_ncached_get_internal(bin, /* racy */ false)
+ < cache_bin_low_water_get_internal(bin)) {
+ cache_bin_low_water_set(bin);
}
+}
+JEMALLOC_ALWAYS_INLINE void *
+cache_bin_alloc_impl(cache_bin_t *bin, bool *success, bool adjust_low_water) {
/*
* success (instead of ret) should be checked upon the return of this
* function. We avoid checking (ret == NULL) because there is never a
@@ -110,22 +353,318 @@ cache_bin_alloc_easy(cache_bin_t *bin, bool *success) {
* and eagerly checking ret would cause pipeline stall (waiting for the
* cacheline).
*/
- *success = true;
- ret = *(bin->avail - (bin->ncached + 1));
- return ret;
+ /*
+ * This may read from the empty position; however the loaded value won't
+ * be used. It's safe because the stack has one more slot reserved.
+ */
+ void *ret = *bin->stack_head;
+ uint16_t low_bits = (uint16_t)(uintptr_t)bin->stack_head;
+ void **new_head = bin->stack_head + 1;
+
+ /*
+ * Note that the low water mark is at most empty; if we pass this check,
+ * we know we're non-empty.
+ */
+ if (likely(low_bits != bin->low_bits_low_water)) {
+ bin->stack_head = new_head;
+ *success = true;
+ return ret;
+ }
+ if (!adjust_low_water) {
+ *success = false;
+ return NULL;
+ }
+ /*
+ * In the fast-path case where we call alloc_easy and then alloc, the
+ * previous checking and computation is optimized away -- we didn't
+ * actually commit any of our operations.
+ */
+ if (likely(low_bits != bin->low_bits_empty)) {
+ bin->stack_head = new_head;
+ bin->low_bits_low_water = (uint16_t)(uintptr_t)new_head;
+ *success = true;
+ return ret;
+ }
+ *success = false;
+ return NULL;
+}
+
+/*
+ * Allocate an item out of the bin, failing if we're at the low-water mark.
+ */
+JEMALLOC_ALWAYS_INLINE void *
+cache_bin_alloc_easy(cache_bin_t *bin, bool *success) {
+ /* We don't look at info if we're not adjusting low-water. */
+ return cache_bin_alloc_impl(bin, success, false);
+}
+
+/*
+ * Allocate an item out of the bin, even if we're currently at the low-water
+ * mark (and failing only if the bin is empty).
+ */
+JEMALLOC_ALWAYS_INLINE void *
+cache_bin_alloc(cache_bin_t *bin, bool *success) {
+ return cache_bin_alloc_impl(bin, success, true);
+}
+
+JEMALLOC_ALWAYS_INLINE cache_bin_sz_t
+cache_bin_alloc_batch(cache_bin_t *bin, size_t num, void **out) {
+ cache_bin_sz_t n = cache_bin_ncached_get_internal(bin,
+ /* racy */ false);
+ if (n > num) {
+ n = (cache_bin_sz_t)num;
+ }
+ memcpy(out, bin->stack_head, n * sizeof(void *));
+ bin->stack_head += n;
+ cache_bin_low_water_adjust(bin);
+
+ return n;
}
JEMALLOC_ALWAYS_INLINE bool
-cache_bin_dalloc_easy(cache_bin_t *bin, cache_bin_info_t *bin_info, void *ptr) {
- if (unlikely(bin->ncached == bin_info->ncached_max)) {
+cache_bin_full(cache_bin_t *bin) {
+ return ((uint16_t)(uintptr_t)bin->stack_head == bin->low_bits_full);
+}
+
+/*
+ * Free an object into the given bin. Fails only if the bin is full.
+ */
+JEMALLOC_ALWAYS_INLINE bool
+cache_bin_dalloc_easy(cache_bin_t *bin, void *ptr) {
+ if (unlikely(cache_bin_full(bin))) {
return false;
}
- assert(bin->ncached < bin_info->ncached_max);
- bin->ncached++;
- *(bin->avail - bin->ncached) = ptr;
+
+ bin->stack_head--;
+ *bin->stack_head = ptr;
+ cache_bin_assert_earlier(bin, bin->low_bits_full,
+ (uint16_t)(uintptr_t)bin->stack_head);
return true;
}
+/* Returns false if failed to stash (i.e. bin is full). */
+JEMALLOC_ALWAYS_INLINE bool
+cache_bin_stash(cache_bin_t *bin, void *ptr) {
+ if (cache_bin_full(bin)) {
+ return false;
+ }
+
+ /* Stash at the full position, in the [full, head) range. */
+ uint16_t low_bits_head = (uint16_t)(uintptr_t)bin->stack_head;
+ /* Wraparound handled as well. */
+ uint16_t diff = cache_bin_diff(bin, bin->low_bits_full, low_bits_head,
+ /* racy */ false);
+ *(void **)((uintptr_t)bin->stack_head - diff) = ptr;
+
+ assert(!cache_bin_full(bin));
+ bin->low_bits_full += sizeof(void *);
+ cache_bin_assert_earlier(bin, bin->low_bits_full, low_bits_head);
+
+ return true;
+}
+
+/*
+ * Get the number of stashed pointers.
+ *
+ * When called from a thread not owning the TLS (i.e. racy = true), it's
+ * important to keep in mind that 'bin->stack_head' and 'bin->low_bits_full' can
+ * be modified concurrently and almost none assertions about their values can be
+ * made.
+ */
+JEMALLOC_ALWAYS_INLINE cache_bin_sz_t
+cache_bin_nstashed_get_internal(cache_bin_t *bin, cache_bin_info_t *info,
+ bool racy) {
+ cache_bin_sz_t ncached_max = cache_bin_info_ncached_max(info);
+ uint16_t low_bits_low_bound = cache_bin_low_bits_low_bound_get(bin,
+ info);
+
+ cache_bin_sz_t n = cache_bin_diff(bin, low_bits_low_bound,
+ bin->low_bits_full, racy) / sizeof(void *);
+ assert(n <= ncached_max);
+
+ if (!racy) {
+ /* Below are for assertions only. */
+ void **low_bound = cache_bin_low_bound_get(bin, info);
+
+ assert((uint16_t)(uintptr_t)low_bound == low_bits_low_bound);
+ void *stashed = *(low_bound + n - 1);
+ bool aligned = cache_bin_nonfast_aligned(stashed);
+#ifdef JEMALLOC_JET
+ /* Allow arbitrary pointers to be stashed in tests. */
+ aligned = true;
+#endif
+ assert(n == 0 || (stashed != NULL && aligned));
+ }
+
+ return n;
+}
+
+JEMALLOC_ALWAYS_INLINE cache_bin_sz_t
+cache_bin_nstashed_get_local(cache_bin_t *bin, cache_bin_info_t *info) {
+ cache_bin_sz_t n = cache_bin_nstashed_get_internal(bin, info,
+ /* racy */ false);
+ assert(n <= cache_bin_info_ncached_max(info));
+ return n;
+}
+
+/*
+ * Obtain a racy view of the number of items currently in the cache bin, in the
+ * presence of possible concurrent modifications.
+ */
+static inline void
+cache_bin_nitems_get_remote(cache_bin_t *bin, cache_bin_info_t *info,
+ cache_bin_sz_t *ncached, cache_bin_sz_t *nstashed) {
+ cache_bin_sz_t n = cache_bin_ncached_get_internal(bin, /* racy */ true);
+ assert(n <= cache_bin_info_ncached_max(info));
+ *ncached = n;
+
+ n = cache_bin_nstashed_get_internal(bin, info, /* racy */ true);
+ assert(n <= cache_bin_info_ncached_max(info));
+ *nstashed = n;
+ /* Note that cannot assert ncached + nstashed <= ncached_max (racy). */
+}
+
+/*
+ * Filling and flushing are done in batch, on arrays of void *s. For filling,
+ * the arrays go forward, and can be accessed with ordinary array arithmetic.
+ * For flushing, we work from the end backwards, and so need to use special
+ * accessors that invert the usual ordering.
+ *
+ * This is important for maintaining first-fit; the arena code fills with
+ * earliest objects first, and so those are the ones we should return first for
+ * cache_bin_alloc calls. When flushing, we should flush the objects that we
+ * wish to return later; those at the end of the array. This is better for the
+ * first-fit heuristic as well as for cache locality; the most recently freed
+ * objects are the ones most likely to still be in cache.
+ *
+ * This all sounds very hand-wavey and theoretical, but reverting the ordering
+ * on one or the other pathway leads to measurable slowdowns.
+ */
+
+typedef struct cache_bin_ptr_array_s cache_bin_ptr_array_t;
+struct cache_bin_ptr_array_s {
+ cache_bin_sz_t n;
+ void **ptr;
+};
+
+/*
+ * Declare a cache_bin_ptr_array_t sufficient for nval items.
+ *
+ * In the current implementation, this could be just part of a
+ * cache_bin_ptr_array_init_... call, since we reuse the cache bin stack memory.
+ * Indirecting behind a macro, though, means experimenting with linked-list
+ * representations is easy (since they'll require an alloca in the calling
+ * frame).
+ */
+#define CACHE_BIN_PTR_ARRAY_DECLARE(name, nval) \
+ cache_bin_ptr_array_t name; \
+ name.n = (nval)
+
+/*
+ * Start a fill. The bin must be empty, and This must be followed by a
+ * finish_fill call before doing any alloc/dalloc operations on the bin.
+ */
+static inline void
+cache_bin_init_ptr_array_for_fill(cache_bin_t *bin, cache_bin_info_t *info,
+ cache_bin_ptr_array_t *arr, cache_bin_sz_t nfill) {
+ cache_bin_assert_empty(bin, info);
+ arr->ptr = cache_bin_empty_position_get(bin) - nfill;
+}
+
+/*
+ * While nfill in cache_bin_init_ptr_array_for_fill is the number we *intend* to
+ * fill, nfilled here is the number we actually filled (which may be less, in
+ * case of OOM.
+ */
+static inline void
+cache_bin_finish_fill(cache_bin_t *bin, cache_bin_info_t *info,
+ cache_bin_ptr_array_t *arr, cache_bin_sz_t nfilled) {
+ cache_bin_assert_empty(bin, info);
+ void **empty_position = cache_bin_empty_position_get(bin);
+ if (nfilled < arr->n) {
+ memmove(empty_position - nfilled, empty_position - arr->n,
+ nfilled * sizeof(void *));
+ }
+ bin->stack_head = empty_position - nfilled;
+}
+
+/*
+ * Same deal, but with flush. Unlike fill (which can fail), the user must flush
+ * everything we give them.
+ */
+static inline void
+cache_bin_init_ptr_array_for_flush(cache_bin_t *bin, cache_bin_info_t *info,
+ cache_bin_ptr_array_t *arr, cache_bin_sz_t nflush) {
+ arr->ptr = cache_bin_empty_position_get(bin) - nflush;
+ assert(cache_bin_ncached_get_local(bin, info) == 0
+ || *arr->ptr != NULL);
+}
+
+static inline void
+cache_bin_finish_flush(cache_bin_t *bin, cache_bin_info_t *info,
+ cache_bin_ptr_array_t *arr, cache_bin_sz_t nflushed) {
+ unsigned rem = cache_bin_ncached_get_local(bin, info) - nflushed;
+ memmove(bin->stack_head + nflushed, bin->stack_head,
+ rem * sizeof(void *));
+ bin->stack_head = bin->stack_head + nflushed;
+ cache_bin_low_water_adjust(bin);
+}
+
+static inline void
+cache_bin_init_ptr_array_for_stashed(cache_bin_t *bin, szind_t binind,
+ cache_bin_info_t *info, cache_bin_ptr_array_t *arr,
+ cache_bin_sz_t nstashed) {
+ assert(nstashed > 0);
+ assert(cache_bin_nstashed_get_local(bin, info) == nstashed);
+
+ void **low_bound = cache_bin_low_bound_get(bin, info);
+ arr->ptr = low_bound;
+ assert(*arr->ptr != NULL);
+}
+
+static inline void
+cache_bin_finish_flush_stashed(cache_bin_t *bin, cache_bin_info_t *info) {
+ void **low_bound = cache_bin_low_bound_get(bin, info);
+
+ /* Reset the bin local full position. */
+ bin->low_bits_full = (uint16_t)(uintptr_t)low_bound;
+ assert(cache_bin_nstashed_get_local(bin, info) == 0);
+}
+
+/*
+ * Initialize a cache_bin_info to represent up to the given number of items in
+ * the cache_bins it is associated with.
+ */
+void cache_bin_info_init(cache_bin_info_t *bin_info,
+ cache_bin_sz_t ncached_max);
+/*
+ * Given an array of initialized cache_bin_info_ts, determine how big an
+ * allocation is required to initialize a full set of cache_bin_ts.
+ */
+void cache_bin_info_compute_alloc(cache_bin_info_t *infos, szind_t ninfos,
+ size_t *size, size_t *alignment);
+
+/*
+ * Actually initialize some cache bins. Callers should allocate the backing
+ * memory indicated by a call to cache_bin_compute_alloc. They should then
+ * preincrement, call init once for each bin and info, and then call
+ * cache_bin_postincrement. *alloc_cur will then point immediately past the end
+ * of the allocation.
+ */
+void cache_bin_preincrement(cache_bin_info_t *infos, szind_t ninfos,
+ void *alloc, size_t *cur_offset);
+void cache_bin_postincrement(cache_bin_info_t *infos, szind_t ninfos,
+ void *alloc, size_t *cur_offset);
+void cache_bin_init(cache_bin_t *bin, cache_bin_info_t *info, void *alloc,
+ size_t *cur_offset);
+
+/*
+ * If a cache bin was zero initialized (either because it lives in static or
+ * thread-local storage, or was memset to 0), this function indicates whether or
+ * not cache_bin_init was called on it.
+ */
+bool cache_bin_still_zero_initialized(cache_bin_t *bin);
+
#endif /* JEMALLOC_INTERNAL_CACHE_BIN_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/counter.h b/contrib/jemalloc/include/jemalloc/internal/counter.h
new file mode 100644
index 000000000000..79abf0648b87
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/internal/counter.h
@@ -0,0 +1,34 @@
+#ifndef JEMALLOC_INTERNAL_COUNTER_H
+#define JEMALLOC_INTERNAL_COUNTER_H
+
+#include "jemalloc/internal/mutex.h"
+
+typedef struct counter_accum_s {
+ LOCKEDINT_MTX_DECLARE(mtx)
+ locked_u64_t accumbytes;
+ uint64_t interval;
+} counter_accum_t;
+
+JEMALLOC_ALWAYS_INLINE bool
+counter_accum(tsdn_t *tsdn, counter_accum_t *counter, uint64_t bytes) {
+ uint64_t interval = counter->interval;
+ assert(interval > 0);
+ LOCKEDINT_MTX_LOCK(tsdn, counter->mtx);
+ /*
+ * If the event moves fast enough (and/or if the event handling is slow
+ * enough), extreme overflow can cause counter trigger coalescing.
+ * This is an intentional mechanism that avoids rate-limiting
+ * allocation.
+ */
+ bool overflow = locked_inc_mod_u64(tsdn, LOCKEDINT_MTX(counter->mtx),
+ &counter->accumbytes, bytes, interval);
+ LOCKEDINT_MTX_UNLOCK(tsdn, counter->mtx);
+ return overflow;
+}
+
+bool counter_accum_init(counter_accum_t *counter, uint64_t interval);
+void counter_prefork(tsdn_t *tsdn, counter_accum_t *counter);
+void counter_postfork_parent(tsdn_t *tsdn, counter_accum_t *counter);
+void counter_postfork_child(tsdn_t *tsdn, counter_accum_t *counter);
+
+#endif /* JEMALLOC_INTERNAL_COUNTER_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/ctl.h b/contrib/jemalloc/include/jemalloc/internal/ctl.h
index 1d1aacc6f417..63d27f8aad37 100644
--- a/contrib/jemalloc/include/jemalloc/internal/ctl.h
+++ b/contrib/jemalloc/include/jemalloc/internal/ctl.h
@@ -42,9 +42,11 @@ typedef struct ctl_arena_stats_s {
uint64_t nfills_small;
uint64_t nflushes_small;
- bin_stats_t bstats[SC_NBINS];
+ bin_stats_data_t bstats[SC_NBINS];
arena_stats_large_t lstats[SC_NSIZES - SC_NBINS];
- arena_stats_extents_t estats[SC_NPSIZES];
+ pac_estats_t estats[SC_NPSIZES];
+ hpa_shard_stats_t hpastats;
+ sec_stats_t secstats;
} ctl_arena_stats_t;
typedef struct ctl_stats_s {
@@ -96,13 +98,17 @@ typedef struct ctl_arenas_s {
int ctl_byname(tsd_t *tsd, const char *name, void *oldp, size_t *oldlenp,
void *newp, size_t newlen);
int ctl_nametomib(tsd_t *tsd, const char *name, size_t *mibp, size_t *miblenp);
-
int ctl_bymib(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,
size_t *oldlenp, void *newp, size_t newlen);
+int ctl_mibnametomib(tsd_t *tsd, size_t *mib, size_t miblen, const char *name,
+ size_t *miblenp);
+int ctl_bymibname(tsd_t *tsd, size_t *mib, size_t miblen, const char *name,
+ size_t *miblenp, void *oldp, size_t *oldlenp, void *newp, size_t newlen);
bool ctl_boot(void);
void ctl_prefork(tsdn_t *tsdn);
void ctl_postfork_parent(tsdn_t *tsdn);
void ctl_postfork_child(tsdn_t *tsdn);
+void ctl_mtx_assert_held(tsdn_t *tsdn);
#define xmallctl(name, oldp, oldlenp, newp, newlen) do { \
if (je_mallctl(name, oldp, oldlenp, newp, newlen) \
@@ -131,4 +137,23 @@ void ctl_postfork_child(tsdn_t *tsdn);
} \
} while (0)
+#define xmallctlmibnametomib(mib, miblen, name, miblenp) do { \
+ if (ctl_mibnametomib(tsd_fetch(), mib, miblen, name, miblenp) \
+ != 0) { \
+ malloc_write( \
+ "<jemalloc>: Failure in ctl_mibnametomib()\n"); \
+ abort(); \
+ } \
+} while (0)
+
+#define xmallctlbymibname(mib, miblen, name, miblenp, oldp, oldlenp, \
+ newp, newlen) do { \
+ if (ctl_bymibname(tsd_fetch(), mib, miblen, name, miblenp, \
+ oldp, oldlenp, newp, newlen) != 0) { \
+ malloc_write( \
+ "<jemalloc>: Failure in ctl_bymibname()\n"); \
+ abort(); \
+ } \
+} while (0)
+
#endif /* JEMALLOC_INTERNAL_CTL_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/decay.h b/contrib/jemalloc/include/jemalloc/internal/decay.h
new file mode 100644
index 000000000000..cf6a9d22c010
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/internal/decay.h
@@ -0,0 +1,186 @@
+#ifndef JEMALLOC_INTERNAL_DECAY_H
+#define JEMALLOC_INTERNAL_DECAY_H
+
+#include "jemalloc/internal/smoothstep.h"
+
+#define DECAY_UNBOUNDED_TIME_TO_PURGE ((uint64_t)-1)
+
+/*
+ * The decay_t computes the number of pages we should purge at any given time.
+ * Page allocators inform a decay object when pages enter a decay-able state
+ * (i.e. dirty or muzzy), and query it to determine how many pages should be
+ * purged at any given time.
+ *
+ * This is mostly a single-threaded data structure and doesn't care about
+ * synchronization at all; it's the caller's responsibility to manage their
+ * synchronization on their own. There are two exceptions:
+ * 1) It's OK to racily call decay_ms_read (i.e. just the simplest state query).
+ * 2) The mtx and purging fields live (and are initialized) here, but are
+ * logically owned by the page allocator. This is just a convenience (since
+ * those fields would be duplicated for both the dirty and muzzy states
+ * otherwise).
+ */
+typedef struct decay_s decay_t;
+struct decay_s {
+ /* Synchronizes all non-atomic fields. */
+ malloc_mutex_t mtx;
+ /*
+ * True if a thread is currently purging the extents associated with
+ * this decay structure.
+ */
+ bool purging;
+ /*
+ * Approximate time in milliseconds from the creation of a set of unused
+ * dirty pages until an equivalent set of unused dirty pages is purged
+ * and/or reused.
+ */
+ atomic_zd_t time_ms;
+ /* time / SMOOTHSTEP_NSTEPS. */
+ nstime_t interval;
+ /*
+ * Time at which the current decay interval logically started. We do
+ * not actually advance to a new epoch until sometime after it starts
+ * because of scheduling and computation delays, and it is even possible
+ * to completely skip epochs. In all cases, during epoch advancement we
+ * merge all relevant activity into the most recently recorded epoch.
+ */
+ nstime_t epoch;
+ /* Deadline randomness generator. */
+ uint64_t jitter_state;
+ /*
+ * Deadline for current epoch. This is the sum of interval and per
+ * epoch jitter which is a uniform random variable in [0..interval).
+ * Epochs always advance by precise multiples of interval, but we
+ * randomize the deadline to reduce the likelihood of arenas purging in
+ * lockstep.
+ */
+ nstime_t deadline;
+ /*
+ * The number of pages we cap ourselves at in the current epoch, per
+ * decay policies. Updated on an epoch change. After an epoch change,
+ * the caller should take steps to try to purge down to this amount.
+ */
+ size_t npages_limit;
+ /*
+ * Number of unpurged pages at beginning of current epoch. During epoch
+ * advancement we use the delta between arena->decay_*.nunpurged and
+ * ecache_npages_get(&arena->ecache_*) to determine how many dirty pages,
+ * if any, were generated.
+ */
+ size_t nunpurged;
+ /*
+ * Trailing log of how many unused dirty pages were generated during
+ * each of the past SMOOTHSTEP_NSTEPS decay epochs, where the last
+ * element is the most recent epoch. Corresponding epoch times are
+ * relative to epoch.
+ *
+ * Updated only on epoch advance, triggered by
+ * decay_maybe_advance_epoch, below.
+ */
+ size_t backlog[SMOOTHSTEP_NSTEPS];
+
+ /* Peak number of pages in associated extents. Used for debug only. */
+ uint64_t ceil_npages;
+};
+
+/*
+ * The current decay time setting. This is the only public access to a decay_t
+ * that's allowed without holding mtx.
+ */
+static inline ssize_t
+decay_ms_read(const decay_t *decay) {
+ return atomic_load_zd(&decay->time_ms, ATOMIC_RELAXED);
+}
+
+/*
+ * See the comment on the struct field -- the limit on pages we should allow in
+ * this decay state this epoch.
+ */
+static inline size_t
+decay_npages_limit_get(const decay_t *decay) {
+ return decay->npages_limit;
+}
+
+/* How many unused dirty pages were generated during the last epoch. */
+static inline size_t
+decay_epoch_npages_delta(const decay_t *decay) {
+ return decay->backlog[SMOOTHSTEP_NSTEPS - 1];
+}
+
+/*
+ * Current epoch duration, in nanoseconds. Given that new epochs are started
+ * somewhat haphazardly, this is not necessarily exactly the time between any
+ * two calls to decay_maybe_advance_epoch; see the comments on fields in the
+ * decay_t.
+ */
+static inline uint64_t
+decay_epoch_duration_ns(const decay_t *decay) {
+ return nstime_ns(&decay->interval);
+}
+
+static inline bool
+decay_immediately(const decay_t *decay) {
+ ssize_t decay_ms = decay_ms_read(decay);
+ return decay_ms == 0;
+}
+
+static inline bool
+decay_disabled(const decay_t *decay) {
+ ssize_t decay_ms = decay_ms_read(decay);
+ return decay_ms < 0;
+}
+
+/* Returns true if decay is enabled and done gradually. */
+static inline bool
+decay_gradually(const decay_t *decay) {
+ ssize_t decay_ms = decay_ms_read(decay);
+ return decay_ms > 0;
+}
+
+/*
+ * Returns true if the passed in decay time setting is valid.
+ * < -1 : invalid
+ * -1 : never decay
+ * 0 : decay immediately
+ * > 0 : some positive decay time, up to a maximum allowed value of
+ * NSTIME_SEC_MAX * 1000, which corresponds to decaying somewhere in the early
+ * 27th century. By that time, we expect to have implemented alternate purging
+ * strategies.
+ */
+bool decay_ms_valid(ssize_t decay_ms);
+
+/*
+ * As a precondition, the decay_t must be zeroed out (as if with memset).
+ *
+ * Returns true on error.
+ */
+bool decay_init(decay_t *decay, nstime_t *cur_time, ssize_t decay_ms);
+
+/*
+ * Given an already-initialized decay_t, reinitialize it with the given decay
+ * time. The decay_t must have previously been initialized (and should not then
+ * be zeroed).
+ */
+void decay_reinit(decay_t *decay, nstime_t *cur_time, ssize_t decay_ms);
+
+/*
+ * Compute how many of 'npages_new' pages we would need to purge in 'time'.
+ */
+uint64_t decay_npages_purge_in(decay_t *decay, nstime_t *time,
+ size_t npages_new);
+
+/* Returns true if the epoch advanced and there are pages to purge. */
+bool decay_maybe_advance_epoch(decay_t *decay, nstime_t *new_time,
+ size_t current_npages);
+
+/*
+ * Calculates wait time until a number of pages in the interval
+ * [0.5 * npages_threshold .. 1.5 * npages_threshold] should be purged.
+ *
+ * Returns number of nanoseconds or DECAY_UNBOUNDED_TIME_TO_PURGE in case of
+ * indefinite wait.
+ */
+uint64_t decay_ns_until_purge(decay_t *decay, size_t npages_current,
+ uint64_t npages_threshold);
+
+#endif /* JEMALLOC_INTERNAL_DECAY_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/ecache.h b/contrib/jemalloc/include/jemalloc/internal/ecache.h
new file mode 100644
index 000000000000..71cae3e34c38
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/internal/ecache.h
@@ -0,0 +1,55 @@
+#ifndef JEMALLOC_INTERNAL_ECACHE_H
+#define JEMALLOC_INTERNAL_ECACHE_H
+
+#include "jemalloc/internal/eset.h"
+#include "jemalloc/internal/san.h"
+#include "jemalloc/internal/mutex.h"
+
+typedef struct ecache_s ecache_t;
+struct ecache_s {
+ malloc_mutex_t mtx;
+ eset_t eset;
+ eset_t guarded_eset;
+ /* All stored extents must be in the same state. */
+ extent_state_t state;
+ /* The index of the ehooks the ecache is associated with. */
+ unsigned ind;
+ /*
+ * If true, delay coalescing until eviction; otherwise coalesce during
+ * deallocation.
+ */
+ bool delay_coalesce;
+};
+
+static inline size_t
+ecache_npages_get(ecache_t *ecache) {
+ return eset_npages_get(&ecache->eset) +
+ eset_npages_get(&ecache->guarded_eset);
+}
+
+/* Get the number of extents in the given page size index. */
+static inline size_t
+ecache_nextents_get(ecache_t *ecache, pszind_t ind) {
+ return eset_nextents_get(&ecache->eset, ind) +
+ eset_nextents_get(&ecache->guarded_eset, ind);
+}
+
+/* Get the sum total bytes of the extents in the given page size index. */
+static inline size_t
+ecache_nbytes_get(ecache_t *ecache, pszind_t ind) {
+ return eset_nbytes_get(&ecache->eset, ind) +
+ eset_nbytes_get(&ecache->guarded_eset, ind);
+}
+
+static inline unsigned
+ecache_ind_get(ecache_t *ecache) {
+ return ecache->ind;
+}
+
+bool ecache_init(tsdn_t *tsdn, ecache_t *ecache, extent_state_t state,
+ unsigned ind, bool delay_coalesce);
+void ecache_prefork(tsdn_t *tsdn, ecache_t *ecache);
+void ecache_postfork_parent(tsdn_t *tsdn, ecache_t *ecache);
+void ecache_postfork_child(tsdn_t *tsdn, ecache_t *ecache);
+
+#endif /* JEMALLOC_INTERNAL_ECACHE_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/edata.h b/contrib/jemalloc/include/jemalloc/internal/edata.h
new file mode 100644
index 000000000000..af039ea734af
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/internal/edata.h
@@ -0,0 +1,698 @@
+#ifndef JEMALLOC_INTERNAL_EDATA_H
+#define JEMALLOC_INTERNAL_EDATA_H
+
+#include "jemalloc/internal/atomic.h"
+#include "jemalloc/internal/bin_info.h"
+#include "jemalloc/internal/bit_util.h"
+#include "jemalloc/internal/hpdata.h"
+#include "jemalloc/internal/nstime.h"
+#include "jemalloc/internal/ph.h"
+#include "jemalloc/internal/ql.h"
+#include "jemalloc/internal/sc.h"
+#include "jemalloc/internal/slab_data.h"
+#include "jemalloc/internal/sz.h"
+#include "jemalloc/internal/typed_list.h"
+
+/*
+ * sizeof(edata_t) is 128 bytes on 64-bit architectures. Ensure the alignment
+ * to free up the low bits in the rtree leaf.
+ */
+#define EDATA_ALIGNMENT 128
+
+enum extent_state_e {
+ extent_state_active = 0,
+ extent_state_dirty = 1,
+ extent_state_muzzy = 2,
+ extent_state_retained = 3,
+ extent_state_transition = 4, /* States below are intermediate. */
+ extent_state_merging = 5,
+ extent_state_max = 5 /* Sanity checking only. */
+};
+typedef enum extent_state_e extent_state_t;
+
+enum extent_head_state_e {
+ EXTENT_NOT_HEAD,
+ EXTENT_IS_HEAD /* See comments in ehooks_default_merge_impl(). */
+};
+typedef enum extent_head_state_e extent_head_state_t;
+
+/*
+ * Which implementation of the page allocator interface, (PAI, defined in
+ * pai.h) owns the given extent?
+ */
+enum extent_pai_e {
+ EXTENT_PAI_PAC = 0,
+ EXTENT_PAI_HPA = 1
+};
+typedef enum extent_pai_e extent_pai_t;
+
+struct e_prof_info_s {
+ /* Time when this was allocated. */
+ nstime_t e_prof_alloc_time;
+ /* Allocation request size. */
+ size_t e_prof_alloc_size;
+ /* Points to a prof_tctx_t. */
+ atomic_p_t e_prof_tctx;
+ /*
+ * Points to a prof_recent_t for the allocation; NULL
+ * means the recent allocation record no longer exists.
+ * Protected by prof_recent_alloc_mtx.
+ */
+ atomic_p_t e_prof_recent_alloc;
+};
+typedef struct e_prof_info_s e_prof_info_t;
+
+/*
+ * The information about a particular edata that lives in an emap. Space is
+ * more precious there (the information, plus the edata pointer, has to live in
+ * a 64-bit word if we want to enable a packed representation.
+ *
+ * There are two things that are special about the information here:
+ * - It's quicker to access. You have one fewer pointer hop, since finding the
+ * edata_t associated with an item always requires accessing the rtree leaf in
+ * which this data is stored.
+ * - It can be read unsynchronized, and without worrying about lifetime issues.
+ */
+typedef struct edata_map_info_s edata_map_info_t;
+struct edata_map_info_s {
+ bool slab;
+ szind_t szind;
+};
+
+typedef struct edata_cmp_summary_s edata_cmp_summary_t;
+struct edata_cmp_summary_s {
+ uint64_t sn;
+ uintptr_t addr;
+};
+
+/* Extent (span of pages). Use accessor functions for e_* fields. */
+typedef struct edata_s edata_t;
+ph_structs(edata_avail, edata_t);
+ph_structs(edata_heap, edata_t);
+struct edata_s {
+ /*
+ * Bitfield containing several fields:
+ *
+ * a: arena_ind
+ * b: slab
+ * c: committed
+ * p: pai
+ * z: zeroed
+ * g: guarded
+ * t: state
+ * i: szind
+ * f: nfree
+ * s: bin_shard
+ *
+ * 00000000 ... 0000ssss ssffffff ffffiiii iiiitttg zpcbaaaa aaaaaaaa
+ *
+ * arena_ind: Arena from which this extent came, or all 1 bits if
+ * unassociated.
+ *
+ * slab: The slab flag indicates whether the extent is used for a slab
+ * of small regions. This helps differentiate small size classes,
+ * and it indicates whether interior pointers can be looked up via
+ * iealloc().
+ *
+ * committed: The committed flag indicates whether physical memory is
+ * committed to the extent, whether explicitly or implicitly
+ * as on a system that overcommits and satisfies physical
+ * memory needs on demand via soft page faults.
+ *
+ * pai: The pai flag is an extent_pai_t.
+ *
+ * zeroed: The zeroed flag is used by extent recycling code to track
+ * whether memory is zero-filled.
+ *
+ * guarded: The guarded flag is use by the sanitizer to track whether
+ * the extent has page guards around it.
+ *
+ * state: The state flag is an extent_state_t.
+ *
+ * szind: The szind flag indicates usable size class index for
+ * allocations residing in this extent, regardless of whether the
+ * extent is a slab. Extent size and usable size often differ
+ * even for non-slabs, either due to sz_large_pad or promotion of
+ * sampled small regions.
+ *
+ * nfree: Number of free regions in slab.
+ *
+ * bin_shard: the shard of the bin from which this extent came.
+ */
+ uint64_t e_bits;
+#define MASK(CURRENT_FIELD_WIDTH, CURRENT_FIELD_SHIFT) ((((((uint64_t)0x1U) << (CURRENT_FIELD_WIDTH)) - 1)) << (CURRENT_FIELD_SHIFT))
+
+#define EDATA_BITS_ARENA_WIDTH MALLOCX_ARENA_BITS
+#define EDATA_BITS_ARENA_SHIFT 0
+#define EDATA_BITS_ARENA_MASK MASK(EDATA_BITS_ARENA_WIDTH, EDATA_BITS_ARENA_SHIFT)
+
+#define EDATA_BITS_SLAB_WIDTH 1
+#define EDATA_BITS_SLAB_SHIFT (EDATA_BITS_ARENA_WIDTH + EDATA_BITS_ARENA_SHIFT)
+#define EDATA_BITS_SLAB_MASK MASK(EDATA_BITS_SLAB_WIDTH, EDATA_BITS_SLAB_SHIFT)
+
+#define EDATA_BITS_COMMITTED_WIDTH 1
+#define EDATA_BITS_COMMITTED_SHIFT (EDATA_BITS_SLAB_WIDTH + EDATA_BITS_SLAB_SHIFT)
+#define EDATA_BITS_COMMITTED_MASK MASK(EDATA_BITS_COMMITTED_WIDTH, EDATA_BITS_COMMITTED_SHIFT)
+
+#define EDATA_BITS_PAI_WIDTH 1
+#define EDATA_BITS_PAI_SHIFT (EDATA_BITS_COMMITTED_WIDTH + EDATA_BITS_COMMITTED_SHIFT)
+#define EDATA_BITS_PAI_MASK MASK(EDATA_BITS_PAI_WIDTH, EDATA_BITS_PAI_SHIFT)
+
+#define EDATA_BITS_ZEROED_WIDTH 1
+#define EDATA_BITS_ZEROED_SHIFT (EDATA_BITS_PAI_WIDTH + EDATA_BITS_PAI_SHIFT)
+#define EDATA_BITS_ZEROED_MASK MASK(EDATA_BITS_ZEROED_WIDTH, EDATA_BITS_ZEROED_SHIFT)
+
+#define EDATA_BITS_GUARDED_WIDTH 1
+#define EDATA_BITS_GUARDED_SHIFT (EDATA_BITS_ZEROED_WIDTH + EDATA_BITS_ZEROED_SHIFT)
+#define EDATA_BITS_GUARDED_MASK MASK(EDATA_BITS_GUARDED_WIDTH, EDATA_BITS_GUARDED_SHIFT)
+
+#define EDATA_BITS_STATE_WIDTH 3
+#define EDATA_BITS_STATE_SHIFT (EDATA_BITS_GUARDED_WIDTH + EDATA_BITS_GUARDED_SHIFT)
+#define EDATA_BITS_STATE_MASK MASK(EDATA_BITS_STATE_WIDTH, EDATA_BITS_STATE_SHIFT)
+
+#define EDATA_BITS_SZIND_WIDTH LG_CEIL(SC_NSIZES)
+#define EDATA_BITS_SZIND_SHIFT (EDATA_BITS_STATE_WIDTH + EDATA_BITS_STATE_SHIFT)
+#define EDATA_BITS_SZIND_MASK MASK(EDATA_BITS_SZIND_WIDTH, EDATA_BITS_SZIND_SHIFT)
+
+#define EDATA_BITS_NFREE_WIDTH (SC_LG_SLAB_MAXREGS + 1)
+#define EDATA_BITS_NFREE_SHIFT (EDATA_BITS_SZIND_WIDTH + EDATA_BITS_SZIND_SHIFT)
+#define EDATA_BITS_NFREE_MASK MASK(EDATA_BITS_NFREE_WIDTH, EDATA_BITS_NFREE_SHIFT)
+
+#define EDATA_BITS_BINSHARD_WIDTH 6
+#define EDATA_BITS_BINSHARD_SHIFT (EDATA_BITS_NFREE_WIDTH + EDATA_BITS_NFREE_SHIFT)
+#define EDATA_BITS_BINSHARD_MASK MASK(EDATA_BITS_BINSHARD_WIDTH, EDATA_BITS_BINSHARD_SHIFT)
+
+#define EDATA_BITS_IS_HEAD_WIDTH 1
+#define EDATA_BITS_IS_HEAD_SHIFT (EDATA_BITS_BINSHARD_WIDTH + EDATA_BITS_BINSHARD_SHIFT)
+#define EDATA_BITS_IS_HEAD_MASK MASK(EDATA_BITS_IS_HEAD_WIDTH, EDATA_BITS_IS_HEAD_SHIFT)
+
+ /* Pointer to the extent that this structure is responsible for. */
+ void *e_addr;
+
+ union {
+ /*
+ * Extent size and serial number associated with the extent
+ * structure (different than the serial number for the extent at
+ * e_addr).
+ *
+ * ssssssss [...] ssssssss ssssnnnn nnnnnnnn
+ */
+ size_t e_size_esn;
+ #define EDATA_SIZE_MASK ((size_t)~(PAGE-1))
+ #define EDATA_ESN_MASK ((size_t)PAGE-1)
+ /* Base extent size, which may not be a multiple of PAGE. */
+ size_t e_bsize;
+ };
+
+ /*
+ * If this edata is a user allocation from an HPA, it comes out of some
+ * pageslab (we don't yet support huegpage allocations that don't fit
+ * into pageslabs). This tracks it.
+ */
+ hpdata_t *e_ps;
+
+ /*
+ * Serial number. These are not necessarily unique; splitting an extent
+ * results in two extents with the same serial number.
+ */
+ uint64_t e_sn;
+
+ union {
+ /*
+ * List linkage used when the edata_t is active; either in
+ * arena's large allocations or bin_t's slabs_full.
+ */
+ ql_elm(edata_t) ql_link_active;
+ /*
+ * Pairing heap linkage. Used whenever the extent is inactive
+ * (in the page allocators), or when it is active and in
+ * slabs_nonfull, or when the edata_t is unassociated with an
+ * extent and sitting in an edata_cache.
+ */
+ union {
+ edata_heap_link_t heap_link;
+ edata_avail_link_t avail_link;
+ };
+ };
+
+ union {
+ /*
+ * List linkage used when the extent is inactive:
+ * - Stashed dirty extents
+ * - Ecache LRU functionality.
+ */
+ ql_elm(edata_t) ql_link_inactive;
+ /* Small region slab metadata. */
+ slab_data_t e_slab_data;
+
+ /* Profiling data, used for large objects. */
+ e_prof_info_t e_prof_info;
+ };
+};
+
+TYPED_LIST(edata_list_active, edata_t, ql_link_active)
+TYPED_LIST(edata_list_inactive, edata_t, ql_link_inactive)
+
+static inline unsigned
+edata_arena_ind_get(const edata_t *edata) {
+ unsigned arena_ind = (unsigned)((edata->e_bits &
+ EDATA_BITS_ARENA_MASK) >> EDATA_BITS_ARENA_SHIFT);
+ assert(arena_ind < MALLOCX_ARENA_LIMIT);
+
+ return arena_ind;
+}
+
+static inline szind_t
+edata_szind_get_maybe_invalid(const edata_t *edata) {
+ szind_t szind = (szind_t)((edata->e_bits & EDATA_BITS_SZIND_MASK) >>
+ EDATA_BITS_SZIND_SHIFT);
+ assert(szind <= SC_NSIZES);
+ return szind;
+}
+
+static inline szind_t
+edata_szind_get(const edata_t *edata) {
+ szind_t szind = edata_szind_get_maybe_invalid(edata);
+ assert(szind < SC_NSIZES); /* Never call when "invalid". */
+ return szind;
+}
+
+static inline size_t
+edata_usize_get(const edata_t *edata) {
+ return sz_index2size(edata_szind_get(edata));
+}
+
+static inline unsigned
+edata_binshard_get(const edata_t *edata) {
+ unsigned binshard = (unsigned)((edata->e_bits &
+ EDATA_BITS_BINSHARD_MASK) >> EDATA_BITS_BINSHARD_SHIFT);
+ assert(binshard < bin_infos[edata_szind_get(edata)].n_shards);
+ return binshard;
+}
+
+static inline uint64_t
+edata_sn_get(const edata_t *edata) {
+ return edata->e_sn;
+}
+
+static inline extent_state_t
+edata_state_get(const edata_t *edata) {
+ return (extent_state_t)((edata->e_bits & EDATA_BITS_STATE_MASK) >>
+ EDATA_BITS_STATE_SHIFT);
+}
+
+static inline bool
+edata_guarded_get(const edata_t *edata) {
+ return (bool)((edata->e_bits & EDATA_BITS_GUARDED_MASK) >>
+ EDATA_BITS_GUARDED_SHIFT);
+}
+
+static inline bool
+edata_zeroed_get(const edata_t *edata) {
+ return (bool)((edata->e_bits & EDATA_BITS_ZEROED_MASK) >>
+ EDATA_BITS_ZEROED_SHIFT);
+}
+
+static inline bool
+edata_committed_get(const edata_t *edata) {
+ return (bool)((edata->e_bits & EDATA_BITS_COMMITTED_MASK) >>
+ EDATA_BITS_COMMITTED_SHIFT);
+}
+
+static inline extent_pai_t
+edata_pai_get(const edata_t *edata) {
+ return (extent_pai_t)((edata->e_bits & EDATA_BITS_PAI_MASK) >>
+ EDATA_BITS_PAI_SHIFT);
+}
+
+static inline bool
+edata_slab_get(const edata_t *edata) {
+ return (bool)((edata->e_bits & EDATA_BITS_SLAB_MASK) >>
+ EDATA_BITS_SLAB_SHIFT);
+}
+
+static inline unsigned
+edata_nfree_get(const edata_t *edata) {
+ assert(edata_slab_get(edata));
+ return (unsigned)((edata->e_bits & EDATA_BITS_NFREE_MASK) >>
+ EDATA_BITS_NFREE_SHIFT);
+}
+
+static inline void *
+edata_base_get(const edata_t *edata) {
+ assert(edata->e_addr == PAGE_ADDR2BASE(edata->e_addr) ||
+ !edata_slab_get(edata));
+ return PAGE_ADDR2BASE(edata->e_addr);
+}
+
+static inline void *
+edata_addr_get(const edata_t *edata) {
+ assert(edata->e_addr == PAGE_ADDR2BASE(edata->e_addr) ||
+ !edata_slab_get(edata));
+ return edata->e_addr;
+}
+
+static inline size_t
+edata_size_get(const edata_t *edata) {
+ return (edata->e_size_esn & EDATA_SIZE_MASK);
+}
+
+static inline size_t
+edata_esn_get(const edata_t *edata) {
+ return (edata->e_size_esn & EDATA_ESN_MASK);
+}
+
+static inline size_t
+edata_bsize_get(const edata_t *edata) {
+ return edata->e_bsize;
+}
+
+static inline hpdata_t *
+edata_ps_get(const edata_t *edata) {
+ assert(edata_pai_get(edata) == EXTENT_PAI_HPA);
+ return edata->e_ps;
+}
+
+static inline void *
+edata_before_get(const edata_t *edata) {
+ return (void *)((uintptr_t)edata_base_get(edata) - PAGE);
+}
+
+static inline void *
+edata_last_get(const edata_t *edata) {
+ return (void *)((uintptr_t)edata_base_get(edata) +
+ edata_size_get(edata) - PAGE);
+}
+
+static inline void *
+edata_past_get(const edata_t *edata) {
+ return (void *)((uintptr_t)edata_base_get(edata) +
+ edata_size_get(edata));
+}
+
+static inline slab_data_t *
+edata_slab_data_get(edata_t *edata) {
+ assert(edata_slab_get(edata));
+ return &edata->e_slab_data;
+}
+
+static inline const slab_data_t *
+edata_slab_data_get_const(const edata_t *edata) {
+ assert(edata_slab_get(edata));
+ return &edata->e_slab_data;
+}
+
+static inline prof_tctx_t *
+edata_prof_tctx_get(const edata_t *edata) {
+ return (prof_tctx_t *)atomic_load_p(&edata->e_prof_info.e_prof_tctx,
+ ATOMIC_ACQUIRE);
+}
+
+static inline const nstime_t *
+edata_prof_alloc_time_get(const edata_t *edata) {
+ return &edata->e_prof_info.e_prof_alloc_time;
+}
+
+static inline size_t
+edata_prof_alloc_size_get(const edata_t *edata) {
+ return edata->e_prof_info.e_prof_alloc_size;
+}
+
+static inline prof_recent_t *
+edata_prof_recent_alloc_get_dont_call_directly(const edata_t *edata) {
+ return (prof_recent_t *)atomic_load_p(
+ &edata->e_prof_info.e_prof_recent_alloc, ATOMIC_RELAXED);
+}
+
+static inline void
+edata_arena_ind_set(edata_t *edata, unsigned arena_ind) {
+ edata->e_bits = (edata->e_bits & ~EDATA_BITS_ARENA_MASK) |
+ ((uint64_t)arena_ind << EDATA_BITS_ARENA_SHIFT);
+}
+
+static inline void
+edata_binshard_set(edata_t *edata, unsigned binshard) {
+ /* The assertion assumes szind is set already. */
+ assert(binshard < bin_infos[edata_szind_get(edata)].n_shards);
+ edata->e_bits = (edata->e_bits & ~EDATA_BITS_BINSHARD_MASK) |
+ ((uint64_t)binshard << EDATA_BITS_BINSHARD_SHIFT);
+}
+
+static inline void
+edata_addr_set(edata_t *edata, void *addr) {
+ edata->e_addr = addr;
+}
+
+static inline void
+edata_size_set(edata_t *edata, size_t size) {
+ assert((size & ~EDATA_SIZE_MASK) == 0);
+ edata->e_size_esn = size | (edata->e_size_esn & ~EDATA_SIZE_MASK);
+}
+
+static inline void
+edata_esn_set(edata_t *edata, size_t esn) {
+ edata->e_size_esn = (edata->e_size_esn & ~EDATA_ESN_MASK) | (esn &
+ EDATA_ESN_MASK);
+}
+
+static inline void
+edata_bsize_set(edata_t *edata, size_t bsize) {
+ edata->e_bsize = bsize;
+}
+
+static inline void
+edata_ps_set(edata_t *edata, hpdata_t *ps) {
+ assert(edata_pai_get(edata) == EXTENT_PAI_HPA);
+ edata->e_ps = ps;
+}
+
+static inline void
+edata_szind_set(edata_t *edata, szind_t szind) {
+ assert(szind <= SC_NSIZES); /* SC_NSIZES means "invalid". */
+ edata->e_bits = (edata->e_bits & ~EDATA_BITS_SZIND_MASK) |
+ ((uint64_t)szind << EDATA_BITS_SZIND_SHIFT);
+}
+
+static inline void
+edata_nfree_set(edata_t *edata, unsigned nfree) {
+ assert(edata_slab_get(edata));
+ edata->e_bits = (edata->e_bits & ~EDATA_BITS_NFREE_MASK) |
+ ((uint64_t)nfree << EDATA_BITS_NFREE_SHIFT);
+}
+
+static inline void
+edata_nfree_binshard_set(edata_t *edata, unsigned nfree, unsigned binshard) {
+ /* The assertion assumes szind is set already. */
+ assert(binshard < bin_infos[edata_szind_get(edata)].n_shards);
+ edata->e_bits = (edata->e_bits &
+ (~EDATA_BITS_NFREE_MASK & ~EDATA_BITS_BINSHARD_MASK)) |
+ ((uint64_t)binshard << EDATA_BITS_BINSHARD_SHIFT) |
+ ((uint64_t)nfree << EDATA_BITS_NFREE_SHIFT);
+}
+
+static inline void
+edata_nfree_inc(edata_t *edata) {
+ assert(edata_slab_get(edata));
+ edata->e_bits += ((uint64_t)1U << EDATA_BITS_NFREE_SHIFT);
+}
+
+static inline void
+edata_nfree_dec(edata_t *edata) {
+ assert(edata_slab_get(edata));
+ edata->e_bits -= ((uint64_t)1U << EDATA_BITS_NFREE_SHIFT);
+}
+
+static inline void
+edata_nfree_sub(edata_t *edata, uint64_t n) {
+ assert(edata_slab_get(edata));
+ edata->e_bits -= (n << EDATA_BITS_NFREE_SHIFT);
+}
+
+static inline void
+edata_sn_set(edata_t *edata, uint64_t sn) {
+ edata->e_sn = sn;
+}
+
+static inline void
+edata_state_set(edata_t *edata, extent_state_t state) {
+ edata->e_bits = (edata->e_bits & ~EDATA_BITS_STATE_MASK) |
+ ((uint64_t)state << EDATA_BITS_STATE_SHIFT);
+}
+
+static inline void
+edata_guarded_set(edata_t *edata, bool guarded) {
+ edata->e_bits = (edata->e_bits & ~EDATA_BITS_GUARDED_MASK) |
+ ((uint64_t)guarded << EDATA_BITS_GUARDED_SHIFT);
+}
+
+static inline void
+edata_zeroed_set(edata_t *edata, bool zeroed) {
+ edata->e_bits = (edata->e_bits & ~EDATA_BITS_ZEROED_MASK) |
+ ((uint64_t)zeroed << EDATA_BITS_ZEROED_SHIFT);
+}
+
+static inline void
+edata_committed_set(edata_t *edata, bool committed) {
+ edata->e_bits = (edata->e_bits & ~EDATA_BITS_COMMITTED_MASK) |
+ ((uint64_t)committed << EDATA_BITS_COMMITTED_SHIFT);
+}
+
+static inline void
+edata_pai_set(edata_t *edata, extent_pai_t pai) {
+ edata->e_bits = (edata->e_bits & ~EDATA_BITS_PAI_MASK) |
+ ((uint64_t)pai << EDATA_BITS_PAI_SHIFT);
+}
+
+static inline void
+edata_slab_set(edata_t *edata, bool slab) {
+ edata->e_bits = (edata->e_bits & ~EDATA_BITS_SLAB_MASK) |
+ ((uint64_t)slab << EDATA_BITS_SLAB_SHIFT);
+}
+
+static inline void
+edata_prof_tctx_set(edata_t *edata, prof_tctx_t *tctx) {
+ atomic_store_p(&edata->e_prof_info.e_prof_tctx, tctx, ATOMIC_RELEASE);
+}
+
+static inline void
+edata_prof_alloc_time_set(edata_t *edata, nstime_t *t) {
+ nstime_copy(&edata->e_prof_info.e_prof_alloc_time, t);
+}
+
+static inline void
+edata_prof_alloc_size_set(edata_t *edata, size_t size) {
+ edata->e_prof_info.e_prof_alloc_size = size;
+}
+
+static inline void
+edata_prof_recent_alloc_set_dont_call_directly(edata_t *edata,
+ prof_recent_t *recent_alloc) {
+ atomic_store_p(&edata->e_prof_info.e_prof_recent_alloc, recent_alloc,
+ ATOMIC_RELAXED);
+}
+
+static inline bool
+edata_is_head_get(edata_t *edata) {
+ return (bool)((edata->e_bits & EDATA_BITS_IS_HEAD_MASK) >>
+ EDATA_BITS_IS_HEAD_SHIFT);
+}
+
+static inline void
+edata_is_head_set(edata_t *edata, bool is_head) {
+ edata->e_bits = (edata->e_bits & ~EDATA_BITS_IS_HEAD_MASK) |
+ ((uint64_t)is_head << EDATA_BITS_IS_HEAD_SHIFT);
+}
+
+static inline bool
+edata_state_in_transition(extent_state_t state) {
+ return state >= extent_state_transition;
+}
+
+/*
+ * Because this function is implemented as a sequence of bitfield modifications,
+ * even though each individual bit is properly initialized, we technically read
+ * uninitialized data within it. This is mostly fine, since most callers get
+ * their edatas from zeroing sources, but callers who make stack edata_ts need
+ * to manually zero them.
+ */
+static inline void
+edata_init(edata_t *edata, unsigned arena_ind, void *addr, size_t size,
+ bool slab, szind_t szind, uint64_t sn, extent_state_t state, bool zeroed,
+ bool committed, extent_pai_t pai, extent_head_state_t is_head) {
+ assert(addr == PAGE_ADDR2BASE(addr) || !slab);
+
+ edata_arena_ind_set(edata, arena_ind);
+ edata_addr_set(edata, addr);
+ edata_size_set(edata, size);
+ edata_slab_set(edata, slab);
+ edata_szind_set(edata, szind);
+ edata_sn_set(edata, sn);
+ edata_state_set(edata, state);
+ edata_guarded_set(edata, false);
+ edata_zeroed_set(edata, zeroed);
+ edata_committed_set(edata, committed);
+ edata_pai_set(edata, pai);
+ edata_is_head_set(edata, is_head == EXTENT_IS_HEAD);
+ if (config_prof) {
+ edata_prof_tctx_set(edata, NULL);
+ }
+}
+
+static inline void
+edata_binit(edata_t *edata, void *addr, size_t bsize, uint64_t sn) {
+ edata_arena_ind_set(edata, (1U << MALLOCX_ARENA_BITS) - 1);
+ edata_addr_set(edata, addr);
+ edata_bsize_set(edata, bsize);
+ edata_slab_set(edata, false);
+ edata_szind_set(edata, SC_NSIZES);
+ edata_sn_set(edata, sn);
+ edata_state_set(edata, extent_state_active);
+ edata_guarded_set(edata, false);
+ edata_zeroed_set(edata, true);
+ edata_committed_set(edata, true);
+ /*
+ * This isn't strictly true, but base allocated extents never get
+ * deallocated and can't be looked up in the emap, but no sense in
+ * wasting a state bit to encode this fact.
+ */
+ edata_pai_set(edata, EXTENT_PAI_PAC);
+}
+
+static inline int
+edata_esn_comp(const edata_t *a, const edata_t *b) {
+ size_t a_esn = edata_esn_get(a);
+ size_t b_esn = edata_esn_get(b);
+
+ return (a_esn > b_esn) - (a_esn < b_esn);
+}
+
+static inline int
+edata_ead_comp(const edata_t *a, const edata_t *b) {
+ uintptr_t a_eaddr = (uintptr_t)a;
+ uintptr_t b_eaddr = (uintptr_t)b;
+
+ return (a_eaddr > b_eaddr) - (a_eaddr < b_eaddr);
+}
+
+static inline edata_cmp_summary_t
+edata_cmp_summary_get(const edata_t *edata) {
+ return (edata_cmp_summary_t){edata_sn_get(edata),
+ (uintptr_t)edata_addr_get(edata)};
+}
+
+static inline int
+edata_cmp_summary_comp(edata_cmp_summary_t a, edata_cmp_summary_t b) {
+ int ret;
+ ret = (a.sn > b.sn) - (a.sn < b.sn);
+ if (ret != 0) {
+ return ret;
+ }
+ ret = (a.addr > b.addr) - (a.addr < b.addr);
+ return ret;
+}
+
+static inline int
+edata_snad_comp(const edata_t *a, const edata_t *b) {
+ edata_cmp_summary_t a_cmp = edata_cmp_summary_get(a);
+ edata_cmp_summary_t b_cmp = edata_cmp_summary_get(b);
+
+ return edata_cmp_summary_comp(a_cmp, b_cmp);
+}
+
+static inline int
+edata_esnead_comp(const edata_t *a, const edata_t *b) {
+ int ret;
+
+ ret = edata_esn_comp(a, b);
+ if (ret != 0) {
+ return ret;
+ }
+
+ ret = edata_ead_comp(a, b);
+ return ret;
+}
+
+ph_proto(, edata_avail, edata_t)
+ph_proto(, edata_heap, edata_t)
+
+#endif /* JEMALLOC_INTERNAL_EDATA_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/edata_cache.h b/contrib/jemalloc/include/jemalloc/internal/edata_cache.h
new file mode 100644
index 000000000000..8b6c0ef79499
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/internal/edata_cache.h
@@ -0,0 +1,49 @@
+#ifndef JEMALLOC_INTERNAL_EDATA_CACHE_H
+#define JEMALLOC_INTERNAL_EDATA_CACHE_H
+
+#include "jemalloc/internal/base.h"
+
+/* For tests only. */
+#define EDATA_CACHE_FAST_FILL 4
+
+/*
+ * A cache of edata_t structures allocated via base_alloc_edata (as opposed to
+ * the underlying extents they describe). The contents of returned edata_t
+ * objects are garbage and cannot be relied upon.
+ */
+
+typedef struct edata_cache_s edata_cache_t;
+struct edata_cache_s {
+ edata_avail_t avail;
+ atomic_zu_t count;
+ malloc_mutex_t mtx;
+ base_t *base;
+};
+
+bool edata_cache_init(edata_cache_t *edata_cache, base_t *base);
+edata_t *edata_cache_get(tsdn_t *tsdn, edata_cache_t *edata_cache);
+void edata_cache_put(tsdn_t *tsdn, edata_cache_t *edata_cache, edata_t *edata);
+
+void edata_cache_prefork(tsdn_t *tsdn, edata_cache_t *edata_cache);
+void edata_cache_postfork_parent(tsdn_t *tsdn, edata_cache_t *edata_cache);
+void edata_cache_postfork_child(tsdn_t *tsdn, edata_cache_t *edata_cache);
+
+/*
+ * An edata_cache_small is like an edata_cache, but it relies on external
+ * synchronization and avoids first-fit strategies.
+ */
+
+typedef struct edata_cache_fast_s edata_cache_fast_t;
+struct edata_cache_fast_s {
+ edata_list_inactive_t list;
+ edata_cache_t *fallback;
+ bool disabled;
+};
+
+void edata_cache_fast_init(edata_cache_fast_t *ecs, edata_cache_t *fallback);
+edata_t *edata_cache_fast_get(tsdn_t *tsdn, edata_cache_fast_t *ecs);
+void edata_cache_fast_put(tsdn_t *tsdn, edata_cache_fast_t *ecs,
+ edata_t *edata);
+void edata_cache_fast_disable(tsdn_t *tsdn, edata_cache_fast_t *ecs);
+
+#endif /* JEMALLOC_INTERNAL_EDATA_CACHE_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/ehooks.h b/contrib/jemalloc/include/jemalloc/internal/ehooks.h
new file mode 100644
index 000000000000..8d9513e258a8
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/internal/ehooks.h
@@ -0,0 +1,412 @@
+#ifndef JEMALLOC_INTERNAL_EHOOKS_H
+#define JEMALLOC_INTERNAL_EHOOKS_H
+
+#include "jemalloc/internal/atomic.h"
+#include "jemalloc/internal/extent_mmap.h"
+
+/*
+ * This module is the internal interface to the extent hooks (both
+ * user-specified and external). Eventually, this will give us the flexibility
+ * to use multiple different versions of user-visible extent-hook APIs under a
+ * single user interface.
+ *
+ * Current API expansions (not available to anyone but the default hooks yet):
+ * - Head state tracking. Hooks can decide whether or not to merge two
+ * extents based on whether or not one of them is the head (i.e. was
+ * allocated on its own). The later extent loses its "head" status.
+ */
+
+extern const extent_hooks_t ehooks_default_extent_hooks;
+
+typedef struct ehooks_s ehooks_t;
+struct ehooks_s {
+ /*
+ * The user-visible id that goes with the ehooks (i.e. that of the base
+ * they're a part of, the associated arena's index within the arenas
+ * array).
+ */
+ unsigned ind;
+ /* Logically an extent_hooks_t *. */
+ atomic_p_t ptr;
+};
+
+extern const extent_hooks_t ehooks_default_extent_hooks;
+
+/*
+ * These are not really part of the public API. Each hook has a fast-path for
+ * the default-hooks case that can avoid various small inefficiencies:
+ * - Forgetting tsd and then calling tsd_get within the hook.
+ * - Getting more state than necessary out of the extent_t.
+ * - Doing arena_ind -> arena -> arena_ind lookups.
+ * By making the calls to these functions visible to the compiler, it can move
+ * those extra bits of computation down below the fast-paths where they get ignored.
+ */
+void *ehooks_default_alloc_impl(tsdn_t *tsdn, void *new_addr, size_t size,
+ size_t alignment, bool *zero, bool *commit, unsigned arena_ind);
+bool ehooks_default_dalloc_impl(void *addr, size_t size);
+void ehooks_default_destroy_impl(void *addr, size_t size);
+bool ehooks_default_commit_impl(void *addr, size_t offset, size_t length);
+bool ehooks_default_decommit_impl(void *addr, size_t offset, size_t length);
+#ifdef PAGES_CAN_PURGE_LAZY
+bool ehooks_default_purge_lazy_impl(void *addr, size_t offset, size_t length);
+#endif
+#ifdef PAGES_CAN_PURGE_FORCED
+bool ehooks_default_purge_forced_impl(void *addr, size_t offset, size_t length);
+#endif
+bool ehooks_default_split_impl();
+/*
+ * Merge is the only default extent hook we declare -- see the comment in
+ * ehooks_merge.
+ */
+bool ehooks_default_merge(extent_hooks_t *extent_hooks, void *addr_a,
+ size_t size_a, void *addr_b, size_t size_b, bool committed,
+ unsigned arena_ind);
+bool ehooks_default_merge_impl(tsdn_t *tsdn, void *addr_a, void *addr_b);
+void ehooks_default_zero_impl(void *addr, size_t size);
+void ehooks_default_guard_impl(void *guard1, void *guard2);
+void ehooks_default_unguard_impl(void *guard1, void *guard2);
+
+/*
+ * We don't officially support reentrancy from wtihin the extent hooks. But
+ * various people who sit within throwing distance of the jemalloc team want
+ * that functionality in certain limited cases. The default reentrancy guards
+ * assert that we're not reentrant from a0 (since it's the bootstrap arena,
+ * where reentrant allocations would be redirected), which we would incorrectly
+ * trigger in cases where a0 has extent hooks (those hooks themselves can't be
+ * reentrant, then, but there are reasonable uses for such functionality, like
+ * putting internal metadata on hugepages). Therefore, we use the raw
+ * reentrancy guards.
+ *
+ * Eventually, we need to think more carefully about whether and where we
+ * support allocating from within extent hooks (and what that means for things
+ * like profiling, stats collection, etc.), and document what the guarantee is.
+ */
+static inline void
+ehooks_pre_reentrancy(tsdn_t *tsdn) {
+ tsd_t *tsd = tsdn_null(tsdn) ? tsd_fetch() : tsdn_tsd(tsdn);
+ tsd_pre_reentrancy_raw(tsd);
+}
+
+static inline void
+ehooks_post_reentrancy(tsdn_t *tsdn) {
+ tsd_t *tsd = tsdn_null(tsdn) ? tsd_fetch() : tsdn_tsd(tsdn);
+ tsd_post_reentrancy_raw(tsd);
+}
+
+/* Beginning of the public API. */
+void ehooks_init(ehooks_t *ehooks, extent_hooks_t *extent_hooks, unsigned ind);
+
+static inline unsigned
+ehooks_ind_get(const ehooks_t *ehooks) {
+ return ehooks->ind;
+}
+
+static inline void
+ehooks_set_extent_hooks_ptr(ehooks_t *ehooks, extent_hooks_t *extent_hooks) {
+ atomic_store_p(&ehooks->ptr, extent_hooks, ATOMIC_RELEASE);
+}
+
+static inline extent_hooks_t *
+ehooks_get_extent_hooks_ptr(ehooks_t *ehooks) {
+ return (extent_hooks_t *)atomic_load_p(&ehooks->ptr, ATOMIC_ACQUIRE);
+}
+
+static inline bool
+ehooks_are_default(ehooks_t *ehooks) {
+ return ehooks_get_extent_hooks_ptr(ehooks) ==
+ &ehooks_default_extent_hooks;
+}
+
+/*
+ * In some cases, a caller needs to allocate resources before attempting to call
+ * a hook. If that hook is doomed to fail, this is wasteful. We therefore
+ * include some checks for such cases.
+ */
+static inline bool
+ehooks_dalloc_will_fail(ehooks_t *ehooks) {
+ if (ehooks_are_default(ehooks)) {
+ return opt_retain;
+ } else {
+ return ehooks_get_extent_hooks_ptr(ehooks)->dalloc == NULL;
+ }
+}
+
+static inline bool
+ehooks_split_will_fail(ehooks_t *ehooks) {
+ return ehooks_get_extent_hooks_ptr(ehooks)->split == NULL;
+}
+
+static inline bool
+ehooks_merge_will_fail(ehooks_t *ehooks) {
+ return ehooks_get_extent_hooks_ptr(ehooks)->merge == NULL;
+}
+
+static inline bool
+ehooks_guard_will_fail(ehooks_t *ehooks) {
+ /*
+ * Before the guard hooks are officially introduced, limit the use to
+ * the default hooks only.
+ */
+ return !ehooks_are_default(ehooks);
+}
+
+/*
+ * Some hooks are required to return zeroed memory in certain situations. In
+ * debug mode, we do some heuristic checks that they did what they were supposed
+ * to.
+ *
+ * This isn't really ehooks-specific (i.e. anyone can check for zeroed memory).
+ * But incorrect zero information indicates an ehook bug.
+ */
+static inline void
+ehooks_debug_zero_check(void *addr, size_t size) {
+ assert(((uintptr_t)addr & PAGE_MASK) == 0);
+ assert((size & PAGE_MASK) == 0);
+ assert(size > 0);
+ if (config_debug) {
+ /* Check the whole first page. */
+ size_t *p = (size_t *)addr;
+ for (size_t i = 0; i < PAGE / sizeof(size_t); i++) {
+ assert(p[i] == 0);
+ }
+ /*
+ * And 4 spots within. There's a tradeoff here; the larger
+ * this number, the more likely it is that we'll catch a bug
+ * where ehooks return a sparsely non-zero range. But
+ * increasing the number of checks also increases the number of
+ * page faults in debug mode. FreeBSD does much of their
+ * day-to-day development work in debug mode, so we don't want
+ * even the debug builds to be too slow.
+ */
+ const size_t nchecks = 4;
+ assert(PAGE >= sizeof(size_t) * nchecks);
+ for (size_t i = 0; i < nchecks; ++i) {
+ assert(p[i * (size / sizeof(size_t) / nchecks)] == 0);
+ }
+ }
+}
+
+
+static inline void *
+ehooks_alloc(tsdn_t *tsdn, ehooks_t *ehooks, void *new_addr, size_t size,
+ size_t alignment, bool *zero, bool *commit) {
+ bool orig_zero = *zero;
+ void *ret;
+ extent_hooks_t *extent_hooks = ehooks_get_extent_hooks_ptr(ehooks);
+ if (extent_hooks == &ehooks_default_extent_hooks) {
+ ret = ehooks_default_alloc_impl(tsdn, new_addr, size,
+ alignment, zero, commit, ehooks_ind_get(ehooks));
+ } else {
+ ehooks_pre_reentrancy(tsdn);
+ ret = extent_hooks->alloc(extent_hooks, new_addr, size,
+ alignment, zero, commit, ehooks_ind_get(ehooks));
+ ehooks_post_reentrancy(tsdn);
+ }
+ assert(new_addr == NULL || ret == NULL || new_addr == ret);
+ assert(!orig_zero || *zero);
+ if (*zero && ret != NULL) {
+ ehooks_debug_zero_check(ret, size);
+ }
+ return ret;
+}
+
+static inline bool
+ehooks_dalloc(tsdn_t *tsdn, ehooks_t *ehooks, void *addr, size_t size,
+ bool committed) {
+ extent_hooks_t *extent_hooks = ehooks_get_extent_hooks_ptr(ehooks);
+ if (extent_hooks == &ehooks_default_extent_hooks) {
+ return ehooks_default_dalloc_impl(addr, size);
+ } else if (extent_hooks->dalloc == NULL) {
+ return true;
+ } else {
+ ehooks_pre_reentrancy(tsdn);
+ bool err = extent_hooks->dalloc(extent_hooks, addr, size,
+ committed, ehooks_ind_get(ehooks));
+ ehooks_post_reentrancy(tsdn);
+ return err;
+ }
+}
+
+static inline void
+ehooks_destroy(tsdn_t *tsdn, ehooks_t *ehooks, void *addr, size_t size,
+ bool committed) {
+ extent_hooks_t *extent_hooks = ehooks_get_extent_hooks_ptr(ehooks);
+ if (extent_hooks == &ehooks_default_extent_hooks) {
+ ehooks_default_destroy_impl(addr, size);
+ } else if (extent_hooks->destroy == NULL) {
+ /* Do nothing. */
+ } else {
+ ehooks_pre_reentrancy(tsdn);
+ extent_hooks->destroy(extent_hooks, addr, size, committed,
+ ehooks_ind_get(ehooks));
+ ehooks_post_reentrancy(tsdn);
+ }
+}
+
+static inline bool
+ehooks_commit(tsdn_t *tsdn, ehooks_t *ehooks, void *addr, size_t size,
+ size_t offset, size_t length) {
+ extent_hooks_t *extent_hooks = ehooks_get_extent_hooks_ptr(ehooks);
+ bool err;
+ if (extent_hooks == &ehooks_default_extent_hooks) {
+ err = ehooks_default_commit_impl(addr, offset, length);
+ } else if (extent_hooks->commit == NULL) {
+ err = true;
+ } else {
+ ehooks_pre_reentrancy(tsdn);
+ err = extent_hooks->commit(extent_hooks, addr, size,
+ offset, length, ehooks_ind_get(ehooks));
+ ehooks_post_reentrancy(tsdn);
+ }
+ if (!err) {
+ ehooks_debug_zero_check(addr, size);
+ }
+ return err;
+}
+
+static inline bool
+ehooks_decommit(tsdn_t *tsdn, ehooks_t *ehooks, void *addr, size_t size,
+ size_t offset, size_t length) {
+ extent_hooks_t *extent_hooks = ehooks_get_extent_hooks_ptr(ehooks);
+ if (extent_hooks == &ehooks_default_extent_hooks) {
+ return ehooks_default_decommit_impl(addr, offset, length);
+ } else if (extent_hooks->decommit == NULL) {
+ return true;
+ } else {
+ ehooks_pre_reentrancy(tsdn);
+ bool err = extent_hooks->decommit(extent_hooks, addr, size,
+ offset, length, ehooks_ind_get(ehooks));
+ ehooks_post_reentrancy(tsdn);
+ return err;
+ }
+}
+
+static inline bool
+ehooks_purge_lazy(tsdn_t *tsdn, ehooks_t *ehooks, void *addr, size_t size,
+ size_t offset, size_t length) {
+ extent_hooks_t *extent_hooks = ehooks_get_extent_hooks_ptr(ehooks);
+#ifdef PAGES_CAN_PURGE_LAZY
+ if (extent_hooks == &ehooks_default_extent_hooks) {
+ return ehooks_default_purge_lazy_impl(addr, offset, length);
+ }
+#endif
+ if (extent_hooks->purge_lazy == NULL) {
+ return true;
+ } else {
+ ehooks_pre_reentrancy(tsdn);
+ bool err = extent_hooks->purge_lazy(extent_hooks, addr, size,
+ offset, length, ehooks_ind_get(ehooks));
+ ehooks_post_reentrancy(tsdn);
+ return err;
+ }
+}
+
+static inline bool
+ehooks_purge_forced(tsdn_t *tsdn, ehooks_t *ehooks, void *addr, size_t size,
+ size_t offset, size_t length) {
+ extent_hooks_t *extent_hooks = ehooks_get_extent_hooks_ptr(ehooks);
+ /*
+ * It would be correct to have a ehooks_debug_zero_check call at the end
+ * of this function; purge_forced is required to zero. But checking
+ * would touch the page in question, which may have performance
+ * consequences (imagine the hooks are using hugepages, with a global
+ * zero page off). Even in debug mode, it's usually a good idea to
+ * avoid cases that can dramatically increase memory consumption.
+ */
+#ifdef PAGES_CAN_PURGE_FORCED
+ if (extent_hooks == &ehooks_default_extent_hooks) {
+ return ehooks_default_purge_forced_impl(addr, offset, length);
+ }
+#endif
+ if (extent_hooks->purge_forced == NULL) {
+ return true;
+ } else {
+ ehooks_pre_reentrancy(tsdn);
+ bool err = extent_hooks->purge_forced(extent_hooks, addr, size,
+ offset, length, ehooks_ind_get(ehooks));
+ ehooks_post_reentrancy(tsdn);
+ return err;
+ }
+}
+
+static inline bool
+ehooks_split(tsdn_t *tsdn, ehooks_t *ehooks, void *addr, size_t size,
+ size_t size_a, size_t size_b, bool committed) {
+ extent_hooks_t *extent_hooks = ehooks_get_extent_hooks_ptr(ehooks);
+ if (ehooks_are_default(ehooks)) {
+ return ehooks_default_split_impl();
+ } else if (extent_hooks->split == NULL) {
+ return true;
+ } else {
+ ehooks_pre_reentrancy(tsdn);
+ bool err = extent_hooks->split(extent_hooks, addr, size, size_a,
+ size_b, committed, ehooks_ind_get(ehooks));
+ ehooks_post_reentrancy(tsdn);
+ return err;
+ }
+}
+
+static inline bool
+ehooks_merge(tsdn_t *tsdn, ehooks_t *ehooks, void *addr_a, size_t size_a,
+ void *addr_b, size_t size_b, bool committed) {
+ extent_hooks_t *extent_hooks = ehooks_get_extent_hooks_ptr(ehooks);
+ if (extent_hooks == &ehooks_default_extent_hooks) {
+ return ehooks_default_merge_impl(tsdn, addr_a, addr_b);
+ } else if (extent_hooks->merge == NULL) {
+ return true;
+ } else {
+ ehooks_pre_reentrancy(tsdn);
+ bool err = extent_hooks->merge(extent_hooks, addr_a, size_a,
+ addr_b, size_b, committed, ehooks_ind_get(ehooks));
+ ehooks_post_reentrancy(tsdn);
+ return err;
+ }
+}
+
+static inline void
+ehooks_zero(tsdn_t *tsdn, ehooks_t *ehooks, void *addr, size_t size) {
+ extent_hooks_t *extent_hooks = ehooks_get_extent_hooks_ptr(ehooks);
+ if (extent_hooks == &ehooks_default_extent_hooks) {
+ ehooks_default_zero_impl(addr, size);
+ } else {
+ /*
+ * It would be correct to try using the user-provided purge
+ * hooks (since they are required to have zeroed the extent if
+ * they indicate success), but we don't necessarily know their
+ * cost. We'll be conservative and use memset.
+ */
+ memset(addr, 0, size);
+ }
+}
+
+static inline bool
+ehooks_guard(tsdn_t *tsdn, ehooks_t *ehooks, void *guard1, void *guard2) {
+ bool err;
+ extent_hooks_t *extent_hooks = ehooks_get_extent_hooks_ptr(ehooks);
+
+ if (extent_hooks == &ehooks_default_extent_hooks) {
+ ehooks_default_guard_impl(guard1, guard2);
+ err = false;
+ } else {
+ err = true;
+ }
+
+ return err;
+}
+
+static inline bool
+ehooks_unguard(tsdn_t *tsdn, ehooks_t *ehooks, void *guard1, void *guard2) {
+ bool err;
+ extent_hooks_t *extent_hooks = ehooks_get_extent_hooks_ptr(ehooks);
+
+ if (extent_hooks == &ehooks_default_extent_hooks) {
+ ehooks_default_unguard_impl(guard1, guard2);
+ err = false;
+ } else {
+ err = true;
+ }
+
+ return err;
+}
+
+#endif /* JEMALLOC_INTERNAL_EHOOKS_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/emap.h b/contrib/jemalloc/include/jemalloc/internal/emap.h
new file mode 100644
index 000000000000..847af3278de3
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/internal/emap.h
@@ -0,0 +1,357 @@
+#ifndef JEMALLOC_INTERNAL_EMAP_H
+#define JEMALLOC_INTERNAL_EMAP_H
+
+#include "jemalloc/internal/base.h"
+#include "jemalloc/internal/rtree.h"
+
+/*
+ * Note: Ends without at semicolon, so that
+ * EMAP_DECLARE_RTREE_CTX;
+ * in uses will avoid empty-statement warnings.
+ */
+#define EMAP_DECLARE_RTREE_CTX \
+ rtree_ctx_t rtree_ctx_fallback; \
+ rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback)
+
+typedef struct emap_s emap_t;
+struct emap_s {
+ rtree_t rtree;
+};
+
+/* Used to pass rtree lookup context down the path. */
+typedef struct emap_alloc_ctx_t emap_alloc_ctx_t;
+struct emap_alloc_ctx_t {
+ szind_t szind;
+ bool slab;
+};
+
+typedef struct emap_full_alloc_ctx_s emap_full_alloc_ctx_t;
+struct emap_full_alloc_ctx_s {
+ szind_t szind;
+ bool slab;
+ edata_t *edata;
+};
+
+bool emap_init(emap_t *emap, base_t *base, bool zeroed);
+
+void emap_remap(tsdn_t *tsdn, emap_t *emap, edata_t *edata, szind_t szind,
+ bool slab);
+
+void emap_update_edata_state(tsdn_t *tsdn, emap_t *emap, edata_t *edata,
+ extent_state_t state);
+
+/*
+ * The two acquire functions below allow accessing neighbor edatas, if it's safe
+ * and valid to do so (i.e. from the same arena, of the same state, etc.). This
+ * is necessary because the ecache locks are state based, and only protect
+ * edatas with the same state. Therefore the neighbor edata's state needs to be
+ * verified first, before chasing the edata pointer. The returned edata will be
+ * in an acquired state, meaning other threads will be prevented from accessing
+ * it, even if technically the edata can still be discovered from the rtree.
+ *
+ * This means, at any moment when holding pointers to edata, either one of the
+ * state based locks is held (and the edatas are all of the protected state), or
+ * the edatas are in an acquired state (e.g. in active or merging state). The
+ * acquire operation itself (changing the edata to an acquired state) is done
+ * under the state locks.
+ */
+edata_t *emap_try_acquire_edata_neighbor(tsdn_t *tsdn, emap_t *emap,
+ edata_t *edata, extent_pai_t pai, extent_state_t expected_state,
+ bool forward);
+edata_t *emap_try_acquire_edata_neighbor_expand(tsdn_t *tsdn, emap_t *emap,
+ edata_t *edata, extent_pai_t pai, extent_state_t expected_state);
+void emap_release_edata(tsdn_t *tsdn, emap_t *emap, edata_t *edata,
+ extent_state_t new_state);
+
+/*
+ * Associate the given edata with its beginning and end address, setting the
+ * szind and slab info appropriately.
+ * Returns true on error (i.e. resource exhaustion).
+ */
+bool emap_register_boundary(tsdn_t *tsdn, emap_t *emap, edata_t *edata,
+ szind_t szind, bool slab);
+
+/*
+ * Does the same thing, but with the interior of the range, for slab
+ * allocations.
+ *
+ * You might wonder why we don't just have a single emap_register function that
+ * does both depending on the value of 'slab'. The answer is twofold:
+ * - As a practical matter, in places like the extract->split->commit pathway,
+ * we defer the interior operation until we're sure that the commit won't fail
+ * (but we have to register the split boundaries there).
+ * - In general, we're trying to move to a world where the page-specific
+ * allocator doesn't know as much about how the pages it allocates will be
+ * used, and passing a 'slab' parameter everywhere makes that more
+ * complicated.
+ *
+ * Unlike the boundary version, this function can't fail; this is because slabs
+ * can't get big enough to touch a new page that neither of the boundaries
+ * touched, so no allocation is necessary to fill the interior once the boundary
+ * has been touched.
+ */
+void emap_register_interior(tsdn_t *tsdn, emap_t *emap, edata_t *edata,
+ szind_t szind);
+
+void emap_deregister_boundary(tsdn_t *tsdn, emap_t *emap, edata_t *edata);
+void emap_deregister_interior(tsdn_t *tsdn, emap_t *emap, edata_t *edata);
+
+typedef struct emap_prepare_s emap_prepare_t;
+struct emap_prepare_s {
+ rtree_leaf_elm_t *lead_elm_a;
+ rtree_leaf_elm_t *lead_elm_b;
+ rtree_leaf_elm_t *trail_elm_a;
+ rtree_leaf_elm_t *trail_elm_b;
+};
+
+/**
+ * These functions the emap metadata management for merging, splitting, and
+ * reusing extents. In particular, they set the boundary mappings from
+ * addresses to edatas. If the result is going to be used as a slab, you
+ * still need to call emap_register_interior on it, though.
+ *
+ * Remap simply changes the szind and slab status of an extent's boundary
+ * mappings. If the extent is not a slab, it doesn't bother with updating the
+ * end mapping (since lookups only occur in the interior of an extent for
+ * slabs). Since the szind and slab status only make sense for active extents,
+ * this should only be called while activating or deactivating an extent.
+ *
+ * Split and merge have a "prepare" and a "commit" portion. The prepare portion
+ * does the operations that can be done without exclusive access to the extent
+ * in question, while the commit variant requires exclusive access to maintain
+ * the emap invariants. The only function that can fail is emap_split_prepare,
+ * and it returns true on failure (at which point the caller shouldn't commit).
+ *
+ * In all cases, "lead" refers to the lower-addressed extent, and trail to the
+ * higher-addressed one. It's the caller's responsibility to set the edata
+ * state appropriately.
+ */
+bool emap_split_prepare(tsdn_t *tsdn, emap_t *emap, emap_prepare_t *prepare,
+ edata_t *edata, size_t size_a, edata_t *trail, size_t size_b);
+void emap_split_commit(tsdn_t *tsdn, emap_t *emap, emap_prepare_t *prepare,
+ edata_t *lead, size_t size_a, edata_t *trail, size_t size_b);
+void emap_merge_prepare(tsdn_t *tsdn, emap_t *emap, emap_prepare_t *prepare,
+ edata_t *lead, edata_t *trail);
+void emap_merge_commit(tsdn_t *tsdn, emap_t *emap, emap_prepare_t *prepare,
+ edata_t *lead, edata_t *trail);
+
+/* Assert that the emap's view of the given edata matches the edata's view. */
+void emap_do_assert_mapped(tsdn_t *tsdn, emap_t *emap, edata_t *edata);
+static inline void
+emap_assert_mapped(tsdn_t *tsdn, emap_t *emap, edata_t *edata) {
+ if (config_debug) {
+ emap_do_assert_mapped(tsdn, emap, edata);
+ }
+}
+
+/* Assert that the given edata isn't in the map. */
+void emap_do_assert_not_mapped(tsdn_t *tsdn, emap_t *emap, edata_t *edata);
+static inline void
+emap_assert_not_mapped(tsdn_t *tsdn, emap_t *emap, edata_t *edata) {
+ if (config_debug) {
+ emap_do_assert_not_mapped(tsdn, emap, edata);
+ }
+}
+
+JEMALLOC_ALWAYS_INLINE bool
+emap_edata_in_transition(tsdn_t *tsdn, emap_t *emap, edata_t *edata) {
+ assert(config_debug);
+ emap_assert_mapped(tsdn, emap, edata);
+
+ EMAP_DECLARE_RTREE_CTX;
+ rtree_contents_t contents = rtree_read(tsdn, &emap->rtree, rtree_ctx,
+ (uintptr_t)edata_base_get(edata));
+
+ return edata_state_in_transition(contents.metadata.state);
+}
+
+JEMALLOC_ALWAYS_INLINE bool
+emap_edata_is_acquired(tsdn_t *tsdn, emap_t *emap, edata_t *edata) {
+ if (!config_debug) {
+ /* For assertions only. */
+ return false;
+ }
+
+ /*
+ * The edata is considered acquired if no other threads will attempt to
+ * read / write any fields from it. This includes a few cases:
+ *
+ * 1) edata not hooked into emap yet -- This implies the edata just got
+ * allocated or initialized.
+ *
+ * 2) in an active or transition state -- In both cases, the edata can
+ * be discovered from the emap, however the state tracked in the rtree
+ * will prevent other threads from accessing the actual edata.
+ */
+ EMAP_DECLARE_RTREE_CTX;
+ rtree_leaf_elm_t *elm = rtree_leaf_elm_lookup(tsdn, &emap->rtree,
+ rtree_ctx, (uintptr_t)edata_base_get(edata), /* dependent */ true,
+ /* init_missing */ false);
+ if (elm == NULL) {
+ return true;
+ }
+ rtree_contents_t contents = rtree_leaf_elm_read(tsdn, &emap->rtree, elm,
+ /* dependent */ true);
+ if (contents.edata == NULL ||
+ contents.metadata.state == extent_state_active ||
+ edata_state_in_transition(contents.metadata.state)) {
+ return true;
+ }
+
+ return false;
+}
+
+JEMALLOC_ALWAYS_INLINE void
+extent_assert_can_coalesce(const edata_t *inner, const edata_t *outer) {
+ assert(edata_arena_ind_get(inner) == edata_arena_ind_get(outer));
+ assert(edata_pai_get(inner) == edata_pai_get(outer));
+ assert(edata_committed_get(inner) == edata_committed_get(outer));
+ assert(edata_state_get(inner) == extent_state_active);
+ assert(edata_state_get(outer) == extent_state_merging);
+ assert(!edata_guarded_get(inner) && !edata_guarded_get(outer));
+ assert(edata_base_get(inner) == edata_past_get(outer) ||
+ edata_base_get(outer) == edata_past_get(inner));
+}
+
+JEMALLOC_ALWAYS_INLINE void
+extent_assert_can_expand(const edata_t *original, const edata_t *expand) {
+ assert(edata_arena_ind_get(original) == edata_arena_ind_get(expand));
+ assert(edata_pai_get(original) == edata_pai_get(expand));
+ assert(edata_state_get(original) == extent_state_active);
+ assert(edata_state_get(expand) == extent_state_merging);
+ assert(edata_past_get(original) == edata_base_get(expand));
+}
+
+JEMALLOC_ALWAYS_INLINE edata_t *
+emap_edata_lookup(tsdn_t *tsdn, emap_t *emap, const void *ptr) {
+ EMAP_DECLARE_RTREE_CTX;
+
+ return rtree_read(tsdn, &emap->rtree, rtree_ctx, (uintptr_t)ptr).edata;
+}
+
+/* Fills in alloc_ctx with the info in the map. */
+JEMALLOC_ALWAYS_INLINE void
+emap_alloc_ctx_lookup(tsdn_t *tsdn, emap_t *emap, const void *ptr,
+ emap_alloc_ctx_t *alloc_ctx) {
+ EMAP_DECLARE_RTREE_CTX;
+
+ rtree_metadata_t metadata = rtree_metadata_read(tsdn, &emap->rtree,
+ rtree_ctx, (uintptr_t)ptr);
+ alloc_ctx->szind = metadata.szind;
+ alloc_ctx->slab = metadata.slab;
+}
+
+/* The pointer must be mapped. */
+JEMALLOC_ALWAYS_INLINE void
+emap_full_alloc_ctx_lookup(tsdn_t *tsdn, emap_t *emap, const void *ptr,
+ emap_full_alloc_ctx_t *full_alloc_ctx) {
+ EMAP_DECLARE_RTREE_CTX;
+
+ rtree_contents_t contents = rtree_read(tsdn, &emap->rtree, rtree_ctx,
+ (uintptr_t)ptr);
+ full_alloc_ctx->edata = contents.edata;
+ full_alloc_ctx->szind = contents.metadata.szind;
+ full_alloc_ctx->slab = contents.metadata.slab;
+}
+
+/*
+ * The pointer is allowed to not be mapped.
+ *
+ * Returns true when the pointer is not present.
+ */
+JEMALLOC_ALWAYS_INLINE bool
+emap_full_alloc_ctx_try_lookup(tsdn_t *tsdn, emap_t *emap, const void *ptr,
+ emap_full_alloc_ctx_t *full_alloc_ctx) {
+ EMAP_DECLARE_RTREE_CTX;
+
+ rtree_contents_t contents;
+ bool err = rtree_read_independent(tsdn, &emap->rtree, rtree_ctx,
+ (uintptr_t)ptr, &contents);
+ if (err) {
+ return true;
+ }
+ full_alloc_ctx->edata = contents.edata;
+ full_alloc_ctx->szind = contents.metadata.szind;
+ full_alloc_ctx->slab = contents.metadata.slab;
+ return false;
+}
+
+/*
+ * Only used on the fastpath of free. Returns true when cannot be fulfilled by
+ * fast path, e.g. when the metadata key is not cached.
+ */
+JEMALLOC_ALWAYS_INLINE bool
+emap_alloc_ctx_try_lookup_fast(tsd_t *tsd, emap_t *emap, const void *ptr,
+ emap_alloc_ctx_t *alloc_ctx) {
+ /* Use the unsafe getter since this may gets called during exit. */
+ rtree_ctx_t *rtree_ctx = tsd_rtree_ctxp_get_unsafe(tsd);
+
+ rtree_metadata_t metadata;
+ bool err = rtree_metadata_try_read_fast(tsd_tsdn(tsd), &emap->rtree,
+ rtree_ctx, (uintptr_t)ptr, &metadata);
+ if (err) {
+ return true;
+ }
+ alloc_ctx->szind = metadata.szind;
+ alloc_ctx->slab = metadata.slab;
+ return false;
+}
+
+/*
+ * We want to do batch lookups out of the cache bins, which use
+ * cache_bin_ptr_array_get to access the i'th element of the bin (since they
+ * invert usual ordering in deciding what to flush). This lets the emap avoid
+ * caring about its caller's ordering.
+ */
+typedef const void *(*emap_ptr_getter)(void *ctx, size_t ind);
+/*
+ * This allows size-checking assertions, which we can only do while we're in the
+ * process of edata lookups.
+ */
+typedef void (*emap_metadata_visitor)(void *ctx, emap_full_alloc_ctx_t *alloc_ctx);
+
+typedef union emap_batch_lookup_result_u emap_batch_lookup_result_t;
+union emap_batch_lookup_result_u {
+ edata_t *edata;
+ rtree_leaf_elm_t *rtree_leaf;
+};
+
+JEMALLOC_ALWAYS_INLINE void
+emap_edata_lookup_batch(tsd_t *tsd, emap_t *emap, size_t nptrs,
+ emap_ptr_getter ptr_getter, void *ptr_getter_ctx,
+ emap_metadata_visitor metadata_visitor, void *metadata_visitor_ctx,
+ emap_batch_lookup_result_t *result) {
+ /* Avoids null-checking tsdn in the loop below. */
+ util_assume(tsd != NULL);
+ rtree_ctx_t *rtree_ctx = tsd_rtree_ctxp_get(tsd);
+
+ for (size_t i = 0; i < nptrs; i++) {
+ const void *ptr = ptr_getter(ptr_getter_ctx, i);
+ /*
+ * Reuse the edatas array as a temp buffer, lying a little about
+ * the types.
+ */
+ result[i].rtree_leaf = rtree_leaf_elm_lookup(tsd_tsdn(tsd),
+ &emap->rtree, rtree_ctx, (uintptr_t)ptr,
+ /* dependent */ true, /* init_missing */ false);
+ }
+
+ for (size_t i = 0; i < nptrs; i++) {
+ rtree_leaf_elm_t *elm = result[i].rtree_leaf;
+ rtree_contents_t contents = rtree_leaf_elm_read(tsd_tsdn(tsd),
+ &emap->rtree, elm, /* dependent */ true);
+ result[i].edata = contents.edata;
+ emap_full_alloc_ctx_t alloc_ctx;
+ /*
+ * Not all these fields are read in practice by the metadata
+ * visitor. But the compiler can easily optimize away the ones
+ * that aren't, so no sense in being incomplete.
+ */
+ alloc_ctx.szind = contents.metadata.szind;
+ alloc_ctx.slab = contents.metadata.slab;
+ alloc_ctx.edata = contents.edata;
+ metadata_visitor(metadata_visitor_ctx, &alloc_ctx);
+ }
+}
+
+#endif /* JEMALLOC_INTERNAL_EMAP_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/emitter.h b/contrib/jemalloc/include/jemalloc/internal/emitter.h
index 542bc79c36d0..9482f68bc5c3 100644
--- a/contrib/jemalloc/include/jemalloc/internal/emitter.h
+++ b/contrib/jemalloc/include/jemalloc/internal/emitter.h
@@ -6,6 +6,7 @@
typedef enum emitter_output_e emitter_output_t;
enum emitter_output_e {
emitter_output_json,
+ emitter_output_json_compact,
emitter_output_table
};
@@ -21,6 +22,7 @@ typedef enum emitter_type_e emitter_type_t;
enum emitter_type_e {
emitter_type_bool,
emitter_type_int,
+ emitter_type_int64,
emitter_type_unsigned,
emitter_type_uint32,
emitter_type_uint64,
@@ -66,7 +68,7 @@ typedef struct emitter_s emitter_t;
struct emitter_s {
emitter_output_t output;
/* The output information. */
- void (*write_cb)(void *, const char *);
+ write_cb_t *write_cb;
void *cbopaque;
int nesting_depth;
/* True if we've already emitted a value at the given depth. */
@@ -75,6 +77,12 @@ struct emitter_s {
bool emitted_key;
};
+static inline bool
+emitter_outputs_json(emitter_t *emitter) {
+ return emitter->output == emitter_output_json ||
+ emitter->output == emitter_output_json_compact;
+}
+
/* Internal convenience function. Write to the emitter the given string. */
JEMALLOC_FORMAT_PRINTF(2, 3)
static inline void
@@ -135,13 +143,16 @@ emitter_print_value(emitter_t *emitter, emitter_justify_t justify, int width,
switch (value_type) {
case emitter_type_bool:
- emitter_printf(emitter,
+ emitter_printf(emitter,
emitter_gen_fmt(fmt, FMT_SIZE, "%s", justify, width),
*(const bool *)value ? "true" : "false");
break;
case emitter_type_int:
EMIT_SIMPLE(int, "%d")
break;
+ case emitter_type_int64:
+ EMIT_SIMPLE(int64_t, "%" FMTd64)
+ break;
case emitter_type_unsigned:
EMIT_SIMPLE(unsigned, "%u")
break;
@@ -159,7 +170,7 @@ emitter_print_value(emitter_t *emitter, emitter_justify_t justify, int width,
* anywhere near the fmt size.
*/
assert(str_written < BUF_SIZE);
- emitter_printf(emitter,
+ emitter_printf(emitter,
emitter_gen_fmt(fmt, FMT_SIZE, "%s", justify, width), buf);
break;
case emitter_type_uint32:
@@ -196,6 +207,7 @@ static inline void
emitter_indent(emitter_t *emitter) {
int amount = emitter->nesting_depth;
const char *indent_str;
+ assert(emitter->output != emitter_output_json_compact);
if (emitter->output == emitter_output_json) {
indent_str = "\t";
} else {
@@ -209,12 +221,18 @@ emitter_indent(emitter_t *emitter) {
static inline void
emitter_json_key_prefix(emitter_t *emitter) {
+ assert(emitter_outputs_json(emitter));
if (emitter->emitted_key) {
emitter->emitted_key = false;
return;
}
- emitter_printf(emitter, "%s\n", emitter->item_at_depth ? "," : "");
- emitter_indent(emitter);
+ if (emitter->item_at_depth) {
+ emitter_printf(emitter, ",");
+ }
+ if (emitter->output != emitter_output_json_compact) {
+ emitter_printf(emitter, "\n");
+ emitter_indent(emitter);
+ }
}
/******************************************************************************/
@@ -222,27 +240,28 @@ emitter_json_key_prefix(emitter_t *emitter) {
static inline void
emitter_init(emitter_t *emitter, emitter_output_t emitter_output,
- void (*write_cb)(void *, const char *), void *cbopaque) {
+ write_cb_t *write_cb, void *cbopaque) {
emitter->output = emitter_output;
emitter->write_cb = write_cb;
emitter->cbopaque = cbopaque;
emitter->item_at_depth = false;
- emitter->emitted_key = false;
+ emitter->emitted_key = false;
emitter->nesting_depth = 0;
}
/******************************************************************************/
/* JSON public API. */
-/*
+/*
* Emits a key (e.g. as appears in an object). The next json entity emitted will
* be the corresponding value.
*/
static inline void
emitter_json_key(emitter_t *emitter, const char *json_key) {
- if (emitter->output == emitter_output_json) {
+ if (emitter_outputs_json(emitter)) {
emitter_json_key_prefix(emitter);
- emitter_printf(emitter, "\"%s\": ", json_key);
+ emitter_printf(emitter, "\"%s\":%s", json_key,
+ emitter->output == emitter_output_json_compact ? "" : " ");
emitter->emitted_key = true;
}
}
@@ -250,7 +269,7 @@ emitter_json_key(emitter_t *emitter, const char *json_key) {
static inline void
emitter_json_value(emitter_t *emitter, emitter_type_t value_type,
const void *value) {
- if (emitter->output == emitter_output_json) {
+ if (emitter_outputs_json(emitter)) {
emitter_json_key_prefix(emitter);
emitter_print_value(emitter, emitter_justify_none, -1,
value_type, value);
@@ -268,7 +287,7 @@ emitter_json_kv(emitter_t *emitter, const char *json_key,
static inline void
emitter_json_array_begin(emitter_t *emitter) {
- if (emitter->output == emitter_output_json) {
+ if (emitter_outputs_json(emitter)) {
emitter_json_key_prefix(emitter);
emitter_printf(emitter, "[");
emitter_nest_inc(emitter);
@@ -284,18 +303,20 @@ emitter_json_array_kv_begin(emitter_t *emitter, const char *json_key) {
static inline void
emitter_json_array_end(emitter_t *emitter) {
- if (emitter->output == emitter_output_json) {
+ if (emitter_outputs_json(emitter)) {
assert(emitter->nesting_depth > 0);
emitter_nest_dec(emitter);
- emitter_printf(emitter, "\n");
- emitter_indent(emitter);
+ if (emitter->output != emitter_output_json_compact) {
+ emitter_printf(emitter, "\n");
+ emitter_indent(emitter);
+ }
emitter_printf(emitter, "]");
}
}
static inline void
emitter_json_object_begin(emitter_t *emitter) {
- if (emitter->output == emitter_output_json) {
+ if (emitter_outputs_json(emitter)) {
emitter_json_key_prefix(emitter);
emitter_printf(emitter, "{");
emitter_nest_inc(emitter);
@@ -311,11 +332,13 @@ emitter_json_object_kv_begin(emitter_t *emitter, const char *json_key) {
static inline void
emitter_json_object_end(emitter_t *emitter) {
- if (emitter->output == emitter_output_json) {
+ if (emitter_outputs_json(emitter)) {
assert(emitter->nesting_depth > 0);
emitter_nest_dec(emitter);
- emitter_printf(emitter, "\n");
- emitter_indent(emitter);
+ if (emitter->output != emitter_output_json_compact) {
+ emitter_printf(emitter, "\n");
+ emitter_indent(emitter);
+ }
emitter_printf(emitter, "}");
}
}
@@ -420,7 +443,7 @@ emitter_kv_note(emitter_t *emitter, const char *json_key, const char *table_key,
emitter_type_t value_type, const void *value,
const char *table_note_key, emitter_type_t table_note_value_type,
const void *table_note_value) {
- if (emitter->output == emitter_output_json) {
+ if (emitter_outputs_json(emitter)) {
emitter_json_key(emitter, json_key);
emitter_json_value(emitter, value_type, value);
} else {
@@ -440,7 +463,7 @@ emitter_kv(emitter_t *emitter, const char *json_key, const char *table_key,
static inline void
emitter_dict_begin(emitter_t *emitter, const char *json_key,
const char *table_header) {
- if (emitter->output == emitter_output_json) {
+ if (emitter_outputs_json(emitter)) {
emitter_json_key(emitter, json_key);
emitter_json_object_begin(emitter);
} else {
@@ -450,7 +473,7 @@ emitter_dict_begin(emitter_t *emitter, const char *json_key,
static inline void
emitter_dict_end(emitter_t *emitter) {
- if (emitter->output == emitter_output_json) {
+ if (emitter_outputs_json(emitter)) {
emitter_json_object_end(emitter);
} else {
emitter_table_dict_end(emitter);
@@ -459,7 +482,7 @@ emitter_dict_end(emitter_t *emitter) {
static inline void
emitter_begin(emitter_t *emitter) {
- if (emitter->output == emitter_output_json) {
+ if (emitter_outputs_json(emitter)) {
assert(emitter->nesting_depth == 0);
emitter_printf(emitter, "{");
emitter_nest_inc(emitter);
@@ -476,10 +499,11 @@ emitter_begin(emitter_t *emitter) {
static inline void
emitter_end(emitter_t *emitter) {
- if (emitter->output == emitter_output_json) {
+ if (emitter_outputs_json(emitter)) {
assert(emitter->nesting_depth == 1);
emitter_nest_dec(emitter);
- emitter_printf(emitter, "\n}\n");
+ emitter_printf(emitter, "%s", emitter->output ==
+ emitter_output_json_compact ? "}" : "\n}\n");
}
}
diff --git a/contrib/jemalloc/include/jemalloc/internal/eset.h b/contrib/jemalloc/include/jemalloc/internal/eset.h
new file mode 100644
index 000000000000..4f689b47d881
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/internal/eset.h
@@ -0,0 +1,77 @@
+#ifndef JEMALLOC_INTERNAL_ESET_H
+#define JEMALLOC_INTERNAL_ESET_H
+
+#include "jemalloc/internal/atomic.h"
+#include "jemalloc/internal/fb.h"
+#include "jemalloc/internal/edata.h"
+#include "jemalloc/internal/mutex.h"
+
+/*
+ * An eset ("extent set") is a quantized collection of extents, with built-in
+ * LRU queue.
+ *
+ * This class is not thread-safe; synchronization must be done externally if
+ * there are mutating operations. One exception is the stats counters, which
+ * may be read without any locking.
+ */
+
+typedef struct eset_bin_s eset_bin_t;
+struct eset_bin_s {
+ edata_heap_t heap;
+ /*
+ * We do first-fit across multiple size classes. If we compared against
+ * the min element in each heap directly, we'd take a cache miss per
+ * extent we looked at. If we co-locate the edata summaries, we only
+ * take a miss on the edata we're actually going to return (which is
+ * inevitable anyways).
+ */
+ edata_cmp_summary_t heap_min;
+};
+
+typedef struct eset_bin_stats_s eset_bin_stats_t;
+struct eset_bin_stats_s {
+ atomic_zu_t nextents;
+ atomic_zu_t nbytes;
+};
+
+typedef struct eset_s eset_t;
+struct eset_s {
+ /* Bitmap for which set bits correspond to non-empty heaps. */
+ fb_group_t bitmap[FB_NGROUPS(SC_NPSIZES + 1)];
+
+ /* Quantized per size class heaps of extents. */
+ eset_bin_t bins[SC_NPSIZES + 1];
+
+ eset_bin_stats_t bin_stats[SC_NPSIZES + 1];
+
+ /* LRU of all extents in heaps. */
+ edata_list_inactive_t lru;
+
+ /* Page sum for all extents in heaps. */
+ atomic_zu_t npages;
+
+ /*
+ * A duplication of the data in the containing ecache. We use this only
+ * for assertions on the states of the passed-in extents.
+ */
+ extent_state_t state;
+};
+
+void eset_init(eset_t *eset, extent_state_t state);
+
+size_t eset_npages_get(eset_t *eset);
+/* Get the number of extents in the given page size index. */
+size_t eset_nextents_get(eset_t *eset, pszind_t ind);
+/* Get the sum total bytes of the extents in the given page size index. */
+size_t eset_nbytes_get(eset_t *eset, pszind_t ind);
+
+void eset_insert(eset_t *eset, edata_t *edata);
+void eset_remove(eset_t *eset, edata_t *edata);
+/*
+ * Select an extent from this eset of the given size and alignment. Returns
+ * null if no such item could be found.
+ */
+edata_t *eset_fit(eset_t *eset, size_t esize, size_t alignment, bool exact_only,
+ unsigned lg_max_fit);
+
+#endif /* JEMALLOC_INTERNAL_ESET_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/exp_grow.h b/contrib/jemalloc/include/jemalloc/internal/exp_grow.h
new file mode 100644
index 000000000000..8566b8a4c6a4
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/internal/exp_grow.h
@@ -0,0 +1,50 @@
+#ifndef JEMALLOC_INTERNAL_EXP_GROW_H
+#define JEMALLOC_INTERNAL_EXP_GROW_H
+
+typedef struct exp_grow_s exp_grow_t;
+struct exp_grow_s {
+ /*
+ * Next extent size class in a growing series to use when satisfying a
+ * request via the extent hooks (only if opt_retain). This limits the
+ * number of disjoint virtual memory ranges so that extent merging can
+ * be effective even if multiple arenas' extent allocation requests are
+ * highly interleaved.
+ *
+ * retain_grow_limit is the max allowed size ind to expand (unless the
+ * required size is greater). Default is no limit, and controlled
+ * through mallctl only.
+ */
+ pszind_t next;
+ pszind_t limit;
+};
+
+static inline bool
+exp_grow_size_prepare(exp_grow_t *exp_grow, size_t alloc_size_min,
+ size_t *r_alloc_size, pszind_t *r_skip) {
+ *r_skip = 0;
+ *r_alloc_size = sz_pind2sz(exp_grow->next + *r_skip);
+ while (*r_alloc_size < alloc_size_min) {
+ (*r_skip)++;
+ if (exp_grow->next + *r_skip >=
+ sz_psz2ind(SC_LARGE_MAXCLASS)) {
+ /* Outside legal range. */
+ return true;
+ }
+ *r_alloc_size = sz_pind2sz(exp_grow->next + *r_skip);
+ }
+ return false;
+}
+
+static inline void
+exp_grow_size_commit(exp_grow_t *exp_grow, pszind_t skip) {
+ if (exp_grow->next + skip + 1 <= exp_grow->limit) {
+ exp_grow->next += skip + 1;
+ } else {
+ exp_grow->next = exp_grow->limit;
+ }
+
+}
+
+void exp_grow_init(exp_grow_t *exp_grow);
+
+#endif /* JEMALLOC_INTERNAL_EXP_GROW_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/extent.h b/contrib/jemalloc/include/jemalloc/internal/extent.h
new file mode 100644
index 000000000000..1d51d41097e3
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/internal/extent.h
@@ -0,0 +1,137 @@
+#ifndef JEMALLOC_INTERNAL_EXTENT_H
+#define JEMALLOC_INTERNAL_EXTENT_H
+
+#include "jemalloc/internal/ecache.h"
+#include "jemalloc/internal/ehooks.h"
+#include "jemalloc/internal/ph.h"
+#include "jemalloc/internal/rtree.h"
+
+/*
+ * This module contains the page-level allocator. It chooses the addresses that
+ * allocations requested by other modules will inhabit, and updates the global
+ * metadata to reflect allocation/deallocation/purging decisions.
+ */
+
+/*
+ * When reuse (and split) an active extent, (1U << opt_lg_extent_max_active_fit)
+ * is the max ratio between the size of the active extent and the new extent.
+ */
+#define LG_EXTENT_MAX_ACTIVE_FIT_DEFAULT 6
+extern size_t opt_lg_extent_max_active_fit;
+
+edata_t *ecache_alloc(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
+ ecache_t *ecache, edata_t *expand_edata, size_t size, size_t alignment,
+ bool zero, bool guarded);
+edata_t *ecache_alloc_grow(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
+ ecache_t *ecache, edata_t *expand_edata, size_t size, size_t alignment,
+ bool zero, bool guarded);
+void ecache_dalloc(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
+ ecache_t *ecache, edata_t *edata);
+edata_t *ecache_evict(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
+ ecache_t *ecache, size_t npages_min);
+
+void extent_gdump_add(tsdn_t *tsdn, const edata_t *edata);
+void extent_record(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, ecache_t *ecache,
+ edata_t *edata);
+void extent_dalloc_gap(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
+ edata_t *edata);
+edata_t *extent_alloc_wrapper(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
+ void *new_addr, size_t size, size_t alignment, bool zero, bool *commit,
+ bool growing_retained);
+void extent_dalloc_wrapper(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
+ edata_t *edata);
+void extent_destroy_wrapper(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
+ edata_t *edata);
+bool extent_commit_wrapper(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata,
+ size_t offset, size_t length);
+bool extent_decommit_wrapper(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata,
+ size_t offset, size_t length);
+bool extent_purge_lazy_wrapper(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata,
+ size_t offset, size_t length);
+bool extent_purge_forced_wrapper(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata,
+ size_t offset, size_t length);
+edata_t *extent_split_wrapper(tsdn_t *tsdn, pac_t *pac,
+ ehooks_t *ehooks, edata_t *edata, size_t size_a, size_t size_b,
+ bool holding_core_locks);
+bool extent_merge_wrapper(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
+ edata_t *a, edata_t *b);
+bool extent_commit_zero(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata,
+ bool commit, bool zero, bool growing_retained);
+size_t extent_sn_next(pac_t *pac);
+bool extent_boot(void);
+
+JEMALLOC_ALWAYS_INLINE bool
+extent_neighbor_head_state_mergeable(bool edata_is_head,
+ bool neighbor_is_head, bool forward) {
+ /*
+ * Head states checking: disallow merging if the higher addr extent is a
+ * head extent. This helps preserve first-fit, and more importantly
+ * makes sure no merge across arenas.
+ */
+ if (forward) {
+ if (neighbor_is_head) {
+ return false;
+ }
+ } else {
+ if (edata_is_head) {
+ return false;
+ }
+ }
+ return true;
+}
+
+JEMALLOC_ALWAYS_INLINE bool
+extent_can_acquire_neighbor(edata_t *edata, rtree_contents_t contents,
+ extent_pai_t pai, extent_state_t expected_state, bool forward,
+ bool expanding) {
+ edata_t *neighbor = contents.edata;
+ if (neighbor == NULL) {
+ return false;
+ }
+ /* It's not safe to access *neighbor yet; must verify states first. */
+ bool neighbor_is_head = contents.metadata.is_head;
+ if (!extent_neighbor_head_state_mergeable(edata_is_head_get(edata),
+ neighbor_is_head, forward)) {
+ return false;
+ }
+ extent_state_t neighbor_state = contents.metadata.state;
+ if (pai == EXTENT_PAI_PAC) {
+ if (neighbor_state != expected_state) {
+ return false;
+ }
+ /* From this point, it's safe to access *neighbor. */
+ if (!expanding && (edata_committed_get(edata) !=
+ edata_committed_get(neighbor))) {
+ /*
+ * Some platforms (e.g. Windows) require an explicit
+ * commit step (and writing to uncommitted memory is not
+ * allowed).
+ */
+ return false;
+ }
+ } else {
+ if (neighbor_state == extent_state_active) {
+ return false;
+ }
+ /* From this point, it's safe to access *neighbor. */
+ }
+
+ assert(edata_pai_get(edata) == pai);
+ if (edata_pai_get(neighbor) != pai) {
+ return false;
+ }
+ if (opt_retain) {
+ assert(edata_arena_ind_get(edata) ==
+ edata_arena_ind_get(neighbor));
+ } else {
+ if (edata_arena_ind_get(edata) !=
+ edata_arena_ind_get(neighbor)) {
+ return false;
+ }
+ }
+ assert(!edata_guarded_get(edata) && !edata_guarded_get(neighbor));
+
+ return true;
+}
+
+#endif /* JEMALLOC_INTERNAL_EXTENT_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/extent_externs.h b/contrib/jemalloc/include/jemalloc/internal/extent_externs.h
deleted file mode 100644
index 8aba57633a34..000000000000
--- a/contrib/jemalloc/include/jemalloc/internal/extent_externs.h
+++ /dev/null
@@ -1,83 +0,0 @@
-#ifndef JEMALLOC_INTERNAL_EXTENT_EXTERNS_H
-#define JEMALLOC_INTERNAL_EXTENT_EXTERNS_H
-
-#include "jemalloc/internal/mutex.h"
-#include "jemalloc/internal/mutex_pool.h"
-#include "jemalloc/internal/ph.h"
-#include "jemalloc/internal/rtree.h"
-
-extern size_t opt_lg_extent_max_active_fit;
-
-extern rtree_t extents_rtree;
-extern const extent_hooks_t extent_hooks_default;
-extern mutex_pool_t extent_mutex_pool;
-
-extent_t *extent_alloc(tsdn_t *tsdn, arena_t *arena);
-void extent_dalloc(tsdn_t *tsdn, arena_t *arena, extent_t *extent);
-
-extent_hooks_t *extent_hooks_get(arena_t *arena);
-extent_hooks_t *extent_hooks_set(tsd_t *tsd, arena_t *arena,
- extent_hooks_t *extent_hooks);
-
-#ifdef JEMALLOC_JET
-size_t extent_size_quantize_floor(size_t size);
-size_t extent_size_quantize_ceil(size_t size);
-#endif
-
-ph_proto(, extent_avail_, extent_tree_t, extent_t)
-ph_proto(, extent_heap_, extent_heap_t, extent_t)
-
-bool extents_init(tsdn_t *tsdn, extents_t *extents, extent_state_t state,
- bool delay_coalesce);
-extent_state_t extents_state_get(const extents_t *extents);
-size_t extents_npages_get(extents_t *extents);
-/* Get the number of extents in the given page size index. */
-size_t extents_nextents_get(extents_t *extents, pszind_t ind);
-/* Get the sum total bytes of the extents in the given page size index. */
-size_t extents_nbytes_get(extents_t *extents, pszind_t ind);
-extent_t *extents_alloc(tsdn_t *tsdn, arena_t *arena,
- extent_hooks_t **r_extent_hooks, extents_t *extents, void *new_addr,
- size_t size, size_t pad, size_t alignment, bool slab, szind_t szind,
- bool *zero, bool *commit);
-void extents_dalloc(tsdn_t *tsdn, arena_t *arena,
- extent_hooks_t **r_extent_hooks, extents_t *extents, extent_t *extent);
-extent_t *extents_evict(tsdn_t *tsdn, arena_t *arena,
- extent_hooks_t **r_extent_hooks, extents_t *extents, size_t npages_min);
-void extents_prefork(tsdn_t *tsdn, extents_t *extents);
-void extents_postfork_parent(tsdn_t *tsdn, extents_t *extents);
-void extents_postfork_child(tsdn_t *tsdn, extents_t *extents);
-extent_t *extent_alloc_wrapper(tsdn_t *tsdn, arena_t *arena,
- extent_hooks_t **r_extent_hooks, void *new_addr, size_t size, size_t pad,
- size_t alignment, bool slab, szind_t szind, bool *zero, bool *commit);
-void extent_dalloc_gap(tsdn_t *tsdn, arena_t *arena, extent_t *extent);
-void extent_dalloc_wrapper(tsdn_t *tsdn, arena_t *arena,
- extent_hooks_t **r_extent_hooks, extent_t *extent);
-void extent_destroy_wrapper(tsdn_t *tsdn, arena_t *arena,
- extent_hooks_t **r_extent_hooks, extent_t *extent);
-bool extent_commit_wrapper(tsdn_t *tsdn, arena_t *arena,
- extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset,
- size_t length);
-bool extent_decommit_wrapper(tsdn_t *tsdn, arena_t *arena,
- extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset,
- size_t length);
-bool extent_purge_lazy_wrapper(tsdn_t *tsdn, arena_t *arena,
- extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset,
- size_t length);
-bool extent_purge_forced_wrapper(tsdn_t *tsdn, arena_t *arena,
- extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset,
- size_t length);
-extent_t *extent_split_wrapper(tsdn_t *tsdn, arena_t *arena,
- extent_hooks_t **r_extent_hooks, extent_t *extent, size_t size_a,
- szind_t szind_a, bool slab_a, size_t size_b, szind_t szind_b, bool slab_b);
-bool extent_merge_wrapper(tsdn_t *tsdn, arena_t *arena,
- extent_hooks_t **r_extent_hooks, extent_t *a, extent_t *b);
-
-bool extent_boot(void);
-
-void extent_util_stats_get(tsdn_t *tsdn, const void *ptr,
- size_t *nfree, size_t *nregs, size_t *size);
-void extent_util_stats_verbose_get(tsdn_t *tsdn, const void *ptr,
- size_t *nfree, size_t *nregs, size_t *size,
- size_t *bin_nfree, size_t *bin_nregs, void **slabcur_addr);
-
-#endif /* JEMALLOC_INTERNAL_EXTENT_EXTERNS_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/extent_inlines.h b/contrib/jemalloc/include/jemalloc/internal/extent_inlines.h
deleted file mode 100644
index 77fa4c4a29a7..000000000000
--- a/contrib/jemalloc/include/jemalloc/internal/extent_inlines.h
+++ /dev/null
@@ -1,501 +0,0 @@
-#ifndef JEMALLOC_INTERNAL_EXTENT_INLINES_H
-#define JEMALLOC_INTERNAL_EXTENT_INLINES_H
-
-#include "jemalloc/internal/mutex.h"
-#include "jemalloc/internal/mutex_pool.h"
-#include "jemalloc/internal/pages.h"
-#include "jemalloc/internal/prng.h"
-#include "jemalloc/internal/ql.h"
-#include "jemalloc/internal/sc.h"
-#include "jemalloc/internal/sz.h"
-
-static inline void
-extent_lock(tsdn_t *tsdn, extent_t *extent) {
- assert(extent != NULL);
- mutex_pool_lock(tsdn, &extent_mutex_pool, (uintptr_t)extent);
-}
-
-static inline void
-extent_unlock(tsdn_t *tsdn, extent_t *extent) {
- assert(extent != NULL);
- mutex_pool_unlock(tsdn, &extent_mutex_pool, (uintptr_t)extent);
-}
-
-static inline void
-extent_lock2(tsdn_t *tsdn, extent_t *extent1, extent_t *extent2) {
- assert(extent1 != NULL && extent2 != NULL);
- mutex_pool_lock2(tsdn, &extent_mutex_pool, (uintptr_t)extent1,
- (uintptr_t)extent2);
-}
-
-static inline void
-extent_unlock2(tsdn_t *tsdn, extent_t *extent1, extent_t *extent2) {
- assert(extent1 != NULL && extent2 != NULL);
- mutex_pool_unlock2(tsdn, &extent_mutex_pool, (uintptr_t)extent1,
- (uintptr_t)extent2);
-}
-
-static inline unsigned
-extent_arena_ind_get(const extent_t *extent) {
- unsigned arena_ind = (unsigned)((extent->e_bits &
- EXTENT_BITS_ARENA_MASK) >> EXTENT_BITS_ARENA_SHIFT);
- assert(arena_ind < MALLOCX_ARENA_LIMIT);
-
- return arena_ind;
-}
-
-static inline arena_t *
-extent_arena_get(const extent_t *extent) {
- unsigned arena_ind = extent_arena_ind_get(extent);
-
- return (arena_t *)atomic_load_p(&arenas[arena_ind], ATOMIC_ACQUIRE);
-}
-
-static inline szind_t
-extent_szind_get_maybe_invalid(const extent_t *extent) {
- szind_t szind = (szind_t)((extent->e_bits & EXTENT_BITS_SZIND_MASK) >>
- EXTENT_BITS_SZIND_SHIFT);
- assert(szind <= SC_NSIZES);
- return szind;
-}
-
-static inline szind_t
-extent_szind_get(const extent_t *extent) {
- szind_t szind = extent_szind_get_maybe_invalid(extent);
- assert(szind < SC_NSIZES); /* Never call when "invalid". */
- return szind;
-}
-
-static inline size_t
-extent_usize_get(const extent_t *extent) {
- return sz_index2size(extent_szind_get(extent));
-}
-
-static inline unsigned
-extent_binshard_get(const extent_t *extent) {
- unsigned binshard = (unsigned)((extent->e_bits &
- EXTENT_BITS_BINSHARD_MASK) >> EXTENT_BITS_BINSHARD_SHIFT);
- assert(binshard < bin_infos[extent_szind_get(extent)].n_shards);
- return binshard;
-}
-
-static inline size_t
-extent_sn_get(const extent_t *extent) {
- return (size_t)((extent->e_bits & EXTENT_BITS_SN_MASK) >>
- EXTENT_BITS_SN_SHIFT);
-}
-
-static inline extent_state_t
-extent_state_get(const extent_t *extent) {
- return (extent_state_t)((extent->e_bits & EXTENT_BITS_STATE_MASK) >>
- EXTENT_BITS_STATE_SHIFT);
-}
-
-static inline bool
-extent_zeroed_get(const extent_t *extent) {
- return (bool)((extent->e_bits & EXTENT_BITS_ZEROED_MASK) >>
- EXTENT_BITS_ZEROED_SHIFT);
-}
-
-static inline bool
-extent_committed_get(const extent_t *extent) {
- return (bool)((extent->e_bits & EXTENT_BITS_COMMITTED_MASK) >>
- EXTENT_BITS_COMMITTED_SHIFT);
-}
-
-static inline bool
-extent_dumpable_get(const extent_t *extent) {
- return (bool)((extent->e_bits & EXTENT_BITS_DUMPABLE_MASK) >>
- EXTENT_BITS_DUMPABLE_SHIFT);
-}
-
-static inline bool
-extent_slab_get(const extent_t *extent) {
- return (bool)((extent->e_bits & EXTENT_BITS_SLAB_MASK) >>
- EXTENT_BITS_SLAB_SHIFT);
-}
-
-static inline unsigned
-extent_nfree_get(const extent_t *extent) {
- assert(extent_slab_get(extent));
- return (unsigned)((extent->e_bits & EXTENT_BITS_NFREE_MASK) >>
- EXTENT_BITS_NFREE_SHIFT);
-}
-
-static inline void *
-extent_base_get(const extent_t *extent) {
- assert(extent->e_addr == PAGE_ADDR2BASE(extent->e_addr) ||
- !extent_slab_get(extent));
- return PAGE_ADDR2BASE(extent->e_addr);
-}
-
-static inline void *
-extent_addr_get(const extent_t *extent) {
- assert(extent->e_addr == PAGE_ADDR2BASE(extent->e_addr) ||
- !extent_slab_get(extent));
- return extent->e_addr;
-}
-
-static inline size_t
-extent_size_get(const extent_t *extent) {
- return (extent->e_size_esn & EXTENT_SIZE_MASK);
-}
-
-static inline size_t
-extent_esn_get(const extent_t *extent) {
- return (extent->e_size_esn & EXTENT_ESN_MASK);
-}
-
-static inline size_t
-extent_bsize_get(const extent_t *extent) {
- return extent->e_bsize;
-}
-
-static inline void *
-extent_before_get(const extent_t *extent) {
- return (void *)((uintptr_t)extent_base_get(extent) - PAGE);
-}
-
-static inline void *
-extent_last_get(const extent_t *extent) {
- return (void *)((uintptr_t)extent_base_get(extent) +
- extent_size_get(extent) - PAGE);
-}
-
-static inline void *
-extent_past_get(const extent_t *extent) {
- return (void *)((uintptr_t)extent_base_get(extent) +
- extent_size_get(extent));
-}
-
-static inline arena_slab_data_t *
-extent_slab_data_get(extent_t *extent) {
- assert(extent_slab_get(extent));
- return &extent->e_slab_data;
-}
-
-static inline const arena_slab_data_t *
-extent_slab_data_get_const(const extent_t *extent) {
- assert(extent_slab_get(extent));
- return &extent->e_slab_data;
-}
-
-static inline prof_tctx_t *
-extent_prof_tctx_get(const extent_t *extent) {
- return (prof_tctx_t *)atomic_load_p(&extent->e_prof_tctx,
- ATOMIC_ACQUIRE);
-}
-
-static inline nstime_t
-extent_prof_alloc_time_get(const extent_t *extent) {
- return extent->e_alloc_time;
-}
-
-static inline void
-extent_arena_set(extent_t *extent, arena_t *arena) {
- unsigned arena_ind = (arena != NULL) ? arena_ind_get(arena) : ((1U <<
- MALLOCX_ARENA_BITS) - 1);
- extent->e_bits = (extent->e_bits & ~EXTENT_BITS_ARENA_MASK) |
- ((uint64_t)arena_ind << EXTENT_BITS_ARENA_SHIFT);
-}
-
-static inline void
-extent_binshard_set(extent_t *extent, unsigned binshard) {
- /* The assertion assumes szind is set already. */
- assert(binshard < bin_infos[extent_szind_get(extent)].n_shards);
- extent->e_bits = (extent->e_bits & ~EXTENT_BITS_BINSHARD_MASK) |
- ((uint64_t)binshard << EXTENT_BITS_BINSHARD_SHIFT);
-}
-
-static inline void
-extent_addr_set(extent_t *extent, void *addr) {
- extent->e_addr = addr;
-}
-
-static inline void
-extent_addr_randomize(tsdn_t *tsdn, extent_t *extent, size_t alignment) {
- assert(extent_base_get(extent) == extent_addr_get(extent));
-
- if (alignment < PAGE) {
- unsigned lg_range = LG_PAGE -
- lg_floor(CACHELINE_CEILING(alignment));
- size_t r;
- if (!tsdn_null(tsdn)) {
- tsd_t *tsd = tsdn_tsd(tsdn);
- r = (size_t)prng_lg_range_u64(
- tsd_offset_statep_get(tsd), lg_range);
- } else {
- r = prng_lg_range_zu(
- &extent_arena_get(extent)->offset_state,
- lg_range, true);
- }
- uintptr_t random_offset = ((uintptr_t)r) << (LG_PAGE -
- lg_range);
- extent->e_addr = (void *)((uintptr_t)extent->e_addr +
- random_offset);
- assert(ALIGNMENT_ADDR2BASE(extent->e_addr, alignment) ==
- extent->e_addr);
- }
-}
-
-static inline void
-extent_size_set(extent_t *extent, size_t size) {
- assert((size & ~EXTENT_SIZE_MASK) == 0);
- extent->e_size_esn = size | (extent->e_size_esn & ~EXTENT_SIZE_MASK);
-}
-
-static inline void
-extent_esn_set(extent_t *extent, size_t esn) {
- extent->e_size_esn = (extent->e_size_esn & ~EXTENT_ESN_MASK) | (esn &
- EXTENT_ESN_MASK);
-}
-
-static inline void
-extent_bsize_set(extent_t *extent, size_t bsize) {
- extent->e_bsize = bsize;
-}
-
-static inline void
-extent_szind_set(extent_t *extent, szind_t szind) {
- assert(szind <= SC_NSIZES); /* SC_NSIZES means "invalid". */
- extent->e_bits = (extent->e_bits & ~EXTENT_BITS_SZIND_MASK) |
- ((uint64_t)szind << EXTENT_BITS_SZIND_SHIFT);
-}
-
-static inline void
-extent_nfree_set(extent_t *extent, unsigned nfree) {
- assert(extent_slab_get(extent));
- extent->e_bits = (extent->e_bits & ~EXTENT_BITS_NFREE_MASK) |
- ((uint64_t)nfree << EXTENT_BITS_NFREE_SHIFT);
-}
-
-static inline void
-extent_nfree_binshard_set(extent_t *extent, unsigned nfree, unsigned binshard) {
- /* The assertion assumes szind is set already. */
- assert(binshard < bin_infos[extent_szind_get(extent)].n_shards);
- extent->e_bits = (extent->e_bits &
- (~EXTENT_BITS_NFREE_MASK & ~EXTENT_BITS_BINSHARD_MASK)) |
- ((uint64_t)binshard << EXTENT_BITS_BINSHARD_SHIFT) |
- ((uint64_t)nfree << EXTENT_BITS_NFREE_SHIFT);
-}
-
-static inline void
-extent_nfree_inc(extent_t *extent) {
- assert(extent_slab_get(extent));
- extent->e_bits += ((uint64_t)1U << EXTENT_BITS_NFREE_SHIFT);
-}
-
-static inline void
-extent_nfree_dec(extent_t *extent) {
- assert(extent_slab_get(extent));
- extent->e_bits -= ((uint64_t)1U << EXTENT_BITS_NFREE_SHIFT);
-}
-
-static inline void
-extent_nfree_sub(extent_t *extent, uint64_t n) {
- assert(extent_slab_get(extent));
- extent->e_bits -= (n << EXTENT_BITS_NFREE_SHIFT);
-}
-
-static inline void
-extent_sn_set(extent_t *extent, size_t sn) {
- extent->e_bits = (extent->e_bits & ~EXTENT_BITS_SN_MASK) |
- ((uint64_t)sn << EXTENT_BITS_SN_SHIFT);
-}
-
-static inline void
-extent_state_set(extent_t *extent, extent_state_t state) {
- extent->e_bits = (extent->e_bits & ~EXTENT_BITS_STATE_MASK) |
- ((uint64_t)state << EXTENT_BITS_STATE_SHIFT);
-}
-
-static inline void
-extent_zeroed_set(extent_t *extent, bool zeroed) {
- extent->e_bits = (extent->e_bits & ~EXTENT_BITS_ZEROED_MASK) |
- ((uint64_t)zeroed << EXTENT_BITS_ZEROED_SHIFT);
-}
-
-static inline void
-extent_committed_set(extent_t *extent, bool committed) {
- extent->e_bits = (extent->e_bits & ~EXTENT_BITS_COMMITTED_MASK) |
- ((uint64_t)committed << EXTENT_BITS_COMMITTED_SHIFT);
-}
-
-static inline void
-extent_dumpable_set(extent_t *extent, bool dumpable) {
- extent->e_bits = (extent->e_bits & ~EXTENT_BITS_DUMPABLE_MASK) |
- ((uint64_t)dumpable << EXTENT_BITS_DUMPABLE_SHIFT);
-}
-
-static inline void
-extent_slab_set(extent_t *extent, bool slab) {
- extent->e_bits = (extent->e_bits & ~EXTENT_BITS_SLAB_MASK) |
- ((uint64_t)slab << EXTENT_BITS_SLAB_SHIFT);
-}
-
-static inline void
-extent_prof_tctx_set(extent_t *extent, prof_tctx_t *tctx) {
- atomic_store_p(&extent->e_prof_tctx, tctx, ATOMIC_RELEASE);
-}
-
-static inline void
-extent_prof_alloc_time_set(extent_t *extent, nstime_t t) {
- nstime_copy(&extent->e_alloc_time, &t);
-}
-
-static inline bool
-extent_is_head_get(extent_t *extent) {
- if (maps_coalesce) {
- not_reached();
- }
-
- return (bool)((extent->e_bits & EXTENT_BITS_IS_HEAD_MASK) >>
- EXTENT_BITS_IS_HEAD_SHIFT);
-}
-
-static inline void
-extent_is_head_set(extent_t *extent, bool is_head) {
- if (maps_coalesce) {
- not_reached();
- }
-
- extent->e_bits = (extent->e_bits & ~EXTENT_BITS_IS_HEAD_MASK) |
- ((uint64_t)is_head << EXTENT_BITS_IS_HEAD_SHIFT);
-}
-
-static inline void
-extent_init(extent_t *extent, arena_t *arena, void *addr, size_t size,
- bool slab, szind_t szind, size_t sn, extent_state_t state, bool zeroed,
- bool committed, bool dumpable, extent_head_state_t is_head) {
- assert(addr == PAGE_ADDR2BASE(addr) || !slab);
-
- extent_arena_set(extent, arena);
- extent_addr_set(extent, addr);
- extent_size_set(extent, size);
- extent_slab_set(extent, slab);
- extent_szind_set(extent, szind);
- extent_sn_set(extent, sn);
- extent_state_set(extent, state);
- extent_zeroed_set(extent, zeroed);
- extent_committed_set(extent, committed);
- extent_dumpable_set(extent, dumpable);
- ql_elm_new(extent, ql_link);
- if (!maps_coalesce) {
- extent_is_head_set(extent, (is_head == EXTENT_IS_HEAD) ? true :
- false);
- }
- if (config_prof) {
- extent_prof_tctx_set(extent, NULL);
- }
-}
-
-static inline void
-extent_binit(extent_t *extent, void *addr, size_t bsize, size_t sn) {
- extent_arena_set(extent, NULL);
- extent_addr_set(extent, addr);
- extent_bsize_set(extent, bsize);
- extent_slab_set(extent, false);
- extent_szind_set(extent, SC_NSIZES);
- extent_sn_set(extent, sn);
- extent_state_set(extent, extent_state_active);
- extent_zeroed_set(extent, true);
- extent_committed_set(extent, true);
- extent_dumpable_set(extent, true);
-}
-
-static inline void
-extent_list_init(extent_list_t *list) {
- ql_new(list);
-}
-
-static inline extent_t *
-extent_list_first(const extent_list_t *list) {
- return ql_first(list);
-}
-
-static inline extent_t *
-extent_list_last(const extent_list_t *list) {
- return ql_last(list, ql_link);
-}
-
-static inline void
-extent_list_append(extent_list_t *list, extent_t *extent) {
- ql_tail_insert(list, extent, ql_link);
-}
-
-static inline void
-extent_list_prepend(extent_list_t *list, extent_t *extent) {
- ql_head_insert(list, extent, ql_link);
-}
-
-static inline void
-extent_list_replace(extent_list_t *list, extent_t *to_remove,
- extent_t *to_insert) {
- ql_after_insert(to_remove, to_insert, ql_link);
- ql_remove(list, to_remove, ql_link);
-}
-
-static inline void
-extent_list_remove(extent_list_t *list, extent_t *extent) {
- ql_remove(list, extent, ql_link);
-}
-
-static inline int
-extent_sn_comp(const extent_t *a, const extent_t *b) {
- size_t a_sn = extent_sn_get(a);
- size_t b_sn = extent_sn_get(b);
-
- return (a_sn > b_sn) - (a_sn < b_sn);
-}
-
-static inline int
-extent_esn_comp(const extent_t *a, const extent_t *b) {
- size_t a_esn = extent_esn_get(a);
- size_t b_esn = extent_esn_get(b);
-
- return (a_esn > b_esn) - (a_esn < b_esn);
-}
-
-static inline int
-extent_ad_comp(const extent_t *a, const extent_t *b) {
- uintptr_t a_addr = (uintptr_t)extent_addr_get(a);
- uintptr_t b_addr = (uintptr_t)extent_addr_get(b);
-
- return (a_addr > b_addr) - (a_addr < b_addr);
-}
-
-static inline int
-extent_ead_comp(const extent_t *a, const extent_t *b) {
- uintptr_t a_eaddr = (uintptr_t)a;
- uintptr_t b_eaddr = (uintptr_t)b;
-
- return (a_eaddr > b_eaddr) - (a_eaddr < b_eaddr);
-}
-
-static inline int
-extent_snad_comp(const extent_t *a, const extent_t *b) {
- int ret;
-
- ret = extent_sn_comp(a, b);
- if (ret != 0) {
- return ret;
- }
-
- ret = extent_ad_comp(a, b);
- return ret;
-}
-
-static inline int
-extent_esnead_comp(const extent_t *a, const extent_t *b) {
- int ret;
-
- ret = extent_esn_comp(a, b);
- if (ret != 0) {
- return ret;
- }
-
- ret = extent_ead_comp(a, b);
- return ret;
-}
-
-#endif /* JEMALLOC_INTERNAL_EXTENT_INLINES_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/extent_structs.h b/contrib/jemalloc/include/jemalloc/internal/extent_structs.h
deleted file mode 100644
index 767cd8930fbe..000000000000
--- a/contrib/jemalloc/include/jemalloc/internal/extent_structs.h
+++ /dev/null
@@ -1,256 +0,0 @@
-#ifndef JEMALLOC_INTERNAL_EXTENT_STRUCTS_H
-#define JEMALLOC_INTERNAL_EXTENT_STRUCTS_H
-
-#include "jemalloc/internal/atomic.h"
-#include "jemalloc/internal/bit_util.h"
-#include "jemalloc/internal/bitmap.h"
-#include "jemalloc/internal/mutex.h"
-#include "jemalloc/internal/ql.h"
-#include "jemalloc/internal/ph.h"
-#include "jemalloc/internal/sc.h"
-
-typedef enum {
- extent_state_active = 0,
- extent_state_dirty = 1,
- extent_state_muzzy = 2,
- extent_state_retained = 3
-} extent_state_t;
-
-/* Extent (span of pages). Use accessor functions for e_* fields. */
-struct extent_s {
- /*
- * Bitfield containing several fields:
- *
- * a: arena_ind
- * b: slab
- * c: committed
- * d: dumpable
- * z: zeroed
- * t: state
- * i: szind
- * f: nfree
- * s: bin_shard
- * n: sn
- *
- * nnnnnnnn ... nnnnnnss ssssffff ffffffii iiiiiitt zdcbaaaa aaaaaaaa
- *
- * arena_ind: Arena from which this extent came, or all 1 bits if
- * unassociated.
- *
- * slab: The slab flag indicates whether the extent is used for a slab
- * of small regions. This helps differentiate small size classes,
- * and it indicates whether interior pointers can be looked up via
- * iealloc().
- *
- * committed: The committed flag indicates whether physical memory is
- * committed to the extent, whether explicitly or implicitly
- * as on a system that overcommits and satisfies physical
- * memory needs on demand via soft page faults.
- *
- * dumpable: The dumpable flag indicates whether or not we've set the
- * memory in question to be dumpable. Note that this
- * interacts somewhat subtly with user-specified extent hooks,
- * since we don't know if *they* are fiddling with
- * dumpability (in which case, we don't want to undo whatever
- * they're doing). To deal with this scenario, we:
- * - Make dumpable false only for memory allocated with the
- * default hooks.
- * - Only allow memory to go from non-dumpable to dumpable,
- * and only once.
- * - Never make the OS call to allow dumping when the
- * dumpable bit is already set.
- * These three constraints mean that we will never
- * accidentally dump user memory that the user meant to set
- * nondumpable with their extent hooks.
- *
- *
- * zeroed: The zeroed flag is used by extent recycling code to track
- * whether memory is zero-filled.
- *
- * state: The state flag is an extent_state_t.
- *
- * szind: The szind flag indicates usable size class index for
- * allocations residing in this extent, regardless of whether the
- * extent is a slab. Extent size and usable size often differ
- * even for non-slabs, either due to sz_large_pad or promotion of
- * sampled small regions.
- *
- * nfree: Number of free regions in slab.
- *
- * bin_shard: the shard of the bin from which this extent came.
- *
- * sn: Serial number (potentially non-unique).
- *
- * Serial numbers may wrap around if !opt_retain, but as long as
- * comparison functions fall back on address comparison for equal
- * serial numbers, stable (if imperfect) ordering is maintained.
- *
- * Serial numbers may not be unique even in the absence of
- * wrap-around, e.g. when splitting an extent and assigning the same
- * serial number to both resulting adjacent extents.
- */
- uint64_t e_bits;
-#define MASK(CURRENT_FIELD_WIDTH, CURRENT_FIELD_SHIFT) ((((((uint64_t)0x1U) << (CURRENT_FIELD_WIDTH)) - 1)) << (CURRENT_FIELD_SHIFT))
-
-#define EXTENT_BITS_ARENA_WIDTH MALLOCX_ARENA_BITS
-#define EXTENT_BITS_ARENA_SHIFT 0
-#define EXTENT_BITS_ARENA_MASK MASK(EXTENT_BITS_ARENA_WIDTH, EXTENT_BITS_ARENA_SHIFT)
-
-#define EXTENT_BITS_SLAB_WIDTH 1
-#define EXTENT_BITS_SLAB_SHIFT (EXTENT_BITS_ARENA_WIDTH + EXTENT_BITS_ARENA_SHIFT)
-#define EXTENT_BITS_SLAB_MASK MASK(EXTENT_BITS_SLAB_WIDTH, EXTENT_BITS_SLAB_SHIFT)
-
-#define EXTENT_BITS_COMMITTED_WIDTH 1
-#define EXTENT_BITS_COMMITTED_SHIFT (EXTENT_BITS_SLAB_WIDTH + EXTENT_BITS_SLAB_SHIFT)
-#define EXTENT_BITS_COMMITTED_MASK MASK(EXTENT_BITS_COMMITTED_WIDTH, EXTENT_BITS_COMMITTED_SHIFT)
-
-#define EXTENT_BITS_DUMPABLE_WIDTH 1
-#define EXTENT_BITS_DUMPABLE_SHIFT (EXTENT_BITS_COMMITTED_WIDTH + EXTENT_BITS_COMMITTED_SHIFT)
-#define EXTENT_BITS_DUMPABLE_MASK MASK(EXTENT_BITS_DUMPABLE_WIDTH, EXTENT_BITS_DUMPABLE_SHIFT)
-
-#define EXTENT_BITS_ZEROED_WIDTH 1
-#define EXTENT_BITS_ZEROED_SHIFT (EXTENT_BITS_DUMPABLE_WIDTH + EXTENT_BITS_DUMPABLE_SHIFT)
-#define EXTENT_BITS_ZEROED_MASK MASK(EXTENT_BITS_ZEROED_WIDTH, EXTENT_BITS_ZEROED_SHIFT)
-
-#define EXTENT_BITS_STATE_WIDTH 2
-#define EXTENT_BITS_STATE_SHIFT (EXTENT_BITS_ZEROED_WIDTH + EXTENT_BITS_ZEROED_SHIFT)
-#define EXTENT_BITS_STATE_MASK MASK(EXTENT_BITS_STATE_WIDTH, EXTENT_BITS_STATE_SHIFT)
-
-#define EXTENT_BITS_SZIND_WIDTH LG_CEIL(SC_NSIZES)
-#define EXTENT_BITS_SZIND_SHIFT (EXTENT_BITS_STATE_WIDTH + EXTENT_BITS_STATE_SHIFT)
-#define EXTENT_BITS_SZIND_MASK MASK(EXTENT_BITS_SZIND_WIDTH, EXTENT_BITS_SZIND_SHIFT)
-
-#define EXTENT_BITS_NFREE_WIDTH (LG_SLAB_MAXREGS + 1)
-#define EXTENT_BITS_NFREE_SHIFT (EXTENT_BITS_SZIND_WIDTH + EXTENT_BITS_SZIND_SHIFT)
-#define EXTENT_BITS_NFREE_MASK MASK(EXTENT_BITS_NFREE_WIDTH, EXTENT_BITS_NFREE_SHIFT)
-
-#define EXTENT_BITS_BINSHARD_WIDTH 6
-#define EXTENT_BITS_BINSHARD_SHIFT (EXTENT_BITS_NFREE_WIDTH + EXTENT_BITS_NFREE_SHIFT)
-#define EXTENT_BITS_BINSHARD_MASK MASK(EXTENT_BITS_BINSHARD_WIDTH, EXTENT_BITS_BINSHARD_SHIFT)
-
-#define EXTENT_BITS_IS_HEAD_WIDTH 1
-#define EXTENT_BITS_IS_HEAD_SHIFT (EXTENT_BITS_BINSHARD_WIDTH + EXTENT_BITS_BINSHARD_SHIFT)
-#define EXTENT_BITS_IS_HEAD_MASK MASK(EXTENT_BITS_IS_HEAD_WIDTH, EXTENT_BITS_IS_HEAD_SHIFT)
-
-#define EXTENT_BITS_SN_SHIFT (EXTENT_BITS_IS_HEAD_WIDTH + EXTENT_BITS_IS_HEAD_SHIFT)
-#define EXTENT_BITS_SN_MASK (UINT64_MAX << EXTENT_BITS_SN_SHIFT)
-
- /* Pointer to the extent that this structure is responsible for. */
- void *e_addr;
-
- union {
- /*
- * Extent size and serial number associated with the extent
- * structure (different than the serial number for the extent at
- * e_addr).
- *
- * ssssssss [...] ssssssss ssssnnnn nnnnnnnn
- */
- size_t e_size_esn;
- #define EXTENT_SIZE_MASK ((size_t)~(PAGE-1))
- #define EXTENT_ESN_MASK ((size_t)PAGE-1)
- /* Base extent size, which may not be a multiple of PAGE. */
- size_t e_bsize;
- };
-
- /*
- * List linkage, used by a variety of lists:
- * - bin_t's slabs_full
- * - extents_t's LRU
- * - stashed dirty extents
- * - arena's large allocations
- */
- ql_elm(extent_t) ql_link;
-
- /*
- * Linkage for per size class sn/address-ordered heaps, and
- * for extent_avail
- */
- phn(extent_t) ph_link;
-
- union {
- /* Small region slab metadata. */
- arena_slab_data_t e_slab_data;
-
- /* Profiling data, used for large objects. */
- struct {
- /* Time when this was allocated. */
- nstime_t e_alloc_time;
- /* Points to a prof_tctx_t. */
- atomic_p_t e_prof_tctx;
- };
- };
-};
-typedef ql_head(extent_t) extent_list_t;
-typedef ph(extent_t) extent_tree_t;
-typedef ph(extent_t) extent_heap_t;
-
-/* Quantized collection of extents, with built-in LRU queue. */
-struct extents_s {
- malloc_mutex_t mtx;
-
- /*
- * Quantized per size class heaps of extents.
- *
- * Synchronization: mtx.
- */
- extent_heap_t heaps[SC_NPSIZES + 1];
- atomic_zu_t nextents[SC_NPSIZES + 1];
- atomic_zu_t nbytes[SC_NPSIZES + 1];
-
- /*
- * Bitmap for which set bits correspond to non-empty heaps.
- *
- * Synchronization: mtx.
- */
- bitmap_t bitmap[BITMAP_GROUPS(SC_NPSIZES + 1)];
-
- /*
- * LRU of all extents in heaps.
- *
- * Synchronization: mtx.
- */
- extent_list_t lru;
-
- /*
- * Page sum for all extents in heaps.
- *
- * The synchronization here is a little tricky. Modifications to npages
- * must hold mtx, but reads need not (though, a reader who sees npages
- * without holding the mutex can't assume anything about the rest of the
- * state of the extents_t).
- */
- atomic_zu_t npages;
-
- /* All stored extents must be in the same state. */
- extent_state_t state;
-
- /*
- * If true, delay coalescing until eviction; otherwise coalesce during
- * deallocation.
- */
- bool delay_coalesce;
-};
-
-/*
- * The following two structs are for experimental purposes. See
- * experimental_utilization_query_ctl and
- * experimental_utilization_batch_query_ctl in src/ctl.c.
- */
-
-struct extent_util_stats_s {
- size_t nfree;
- size_t nregs;
- size_t size;
-};
-
-struct extent_util_stats_verbose_s {
- void *slabcur_addr;
- size_t nfree;
- size_t nregs;
- size_t size;
- size_t bin_nfree;
- size_t bin_nregs;
-};
-
-#endif /* JEMALLOC_INTERNAL_EXTENT_STRUCTS_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/extent_types.h b/contrib/jemalloc/include/jemalloc/internal/extent_types.h
deleted file mode 100644
index 96925cf95887..000000000000
--- a/contrib/jemalloc/include/jemalloc/internal/extent_types.h
+++ /dev/null
@@ -1,23 +0,0 @@
-#ifndef JEMALLOC_INTERNAL_EXTENT_TYPES_H
-#define JEMALLOC_INTERNAL_EXTENT_TYPES_H
-
-typedef struct extent_s extent_t;
-typedef struct extents_s extents_t;
-
-typedef struct extent_util_stats_s extent_util_stats_t;
-typedef struct extent_util_stats_verbose_s extent_util_stats_verbose_t;
-
-#define EXTENT_HOOKS_INITIALIZER NULL
-
-/*
- * When reuse (and split) an active extent, (1U << opt_lg_extent_max_active_fit)
- * is the max ratio between the size of the active extent and the new extent.
- */
-#define LG_EXTENT_MAX_ACTIVE_FIT_DEFAULT 6
-
-typedef enum {
- EXTENT_NOT_HEAD,
- EXTENT_IS_HEAD /* Only relevant for Windows && opt.retain. */
-} extent_head_state_t;
-
-#endif /* JEMALLOC_INTERNAL_EXTENT_TYPES_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/fb.h b/contrib/jemalloc/include/jemalloc/internal/fb.h
new file mode 100644
index 000000000000..90c4091ff6cc
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/internal/fb.h
@@ -0,0 +1,373 @@
+#ifndef JEMALLOC_INTERNAL_FB_H
+#define JEMALLOC_INTERNAL_FB_H
+
+/*
+ * The flat bitmap module. This has a larger API relative to the bitmap module
+ * (supporting things like backwards searches, and searching for both set and
+ * unset bits), at the cost of slower operations for very large bitmaps.
+ *
+ * Initialized flat bitmaps start at all-zeros (all bits unset).
+ */
+
+typedef unsigned long fb_group_t;
+#define FB_GROUP_BITS (ZU(1) << (LG_SIZEOF_LONG + 3))
+#define FB_NGROUPS(nbits) ((nbits) / FB_GROUP_BITS \
+ + ((nbits) % FB_GROUP_BITS == 0 ? 0 : 1))
+
+static inline void
+fb_init(fb_group_t *fb, size_t nbits) {
+ size_t ngroups = FB_NGROUPS(nbits);
+ memset(fb, 0, ngroups * sizeof(fb_group_t));
+}
+
+static inline bool
+fb_empty(fb_group_t *fb, size_t nbits) {
+ size_t ngroups = FB_NGROUPS(nbits);
+ for (size_t i = 0; i < ngroups; i++) {
+ if (fb[i] != 0) {
+ return false;
+ }
+ }
+ return true;
+}
+
+static inline bool
+fb_full(fb_group_t *fb, size_t nbits) {
+ size_t ngroups = FB_NGROUPS(nbits);
+ size_t trailing_bits = nbits % FB_GROUP_BITS;
+ size_t limit = (trailing_bits == 0 ? ngroups : ngroups - 1);
+ for (size_t i = 0; i < limit; i++) {
+ if (fb[i] != ~(fb_group_t)0) {
+ return false;
+ }
+ }
+ if (trailing_bits == 0) {
+ return true;
+ }
+ return fb[ngroups - 1] == ((fb_group_t)1 << trailing_bits) - 1;
+}
+
+static inline bool
+fb_get(fb_group_t *fb, size_t nbits, size_t bit) {
+ assert(bit < nbits);
+ size_t group_ind = bit / FB_GROUP_BITS;
+ size_t bit_ind = bit % FB_GROUP_BITS;
+ return (bool)(fb[group_ind] & ((fb_group_t)1 << bit_ind));
+}
+
+static inline void
+fb_set(fb_group_t *fb, size_t nbits, size_t bit) {
+ assert(bit < nbits);
+ size_t group_ind = bit / FB_GROUP_BITS;
+ size_t bit_ind = bit % FB_GROUP_BITS;
+ fb[group_ind] |= ((fb_group_t)1 << bit_ind);
+}
+
+static inline void
+fb_unset(fb_group_t *fb, size_t nbits, size_t bit) {
+ assert(bit < nbits);
+ size_t group_ind = bit / FB_GROUP_BITS;
+ size_t bit_ind = bit % FB_GROUP_BITS;
+ fb[group_ind] &= ~((fb_group_t)1 << bit_ind);
+}
+
+
+/*
+ * Some implementation details. This visitation function lets us apply a group
+ * visitor to each group in the bitmap (potentially modifying it). The mask
+ * indicates which bits are logically part of the visitation.
+ */
+typedef void (*fb_group_visitor_t)(void *ctx, fb_group_t *fb, fb_group_t mask);
+JEMALLOC_ALWAYS_INLINE void
+fb_visit_impl(fb_group_t *fb, size_t nbits, fb_group_visitor_t visit, void *ctx,
+ size_t start, size_t cnt) {
+ assert(cnt > 0);
+ assert(start + cnt <= nbits);
+ size_t group_ind = start / FB_GROUP_BITS;
+ size_t start_bit_ind = start % FB_GROUP_BITS;
+ /*
+ * The first group is special; it's the only one we don't start writing
+ * to from bit 0.
+ */
+ size_t first_group_cnt = (start_bit_ind + cnt > FB_GROUP_BITS
+ ? FB_GROUP_BITS - start_bit_ind : cnt);
+ /*
+ * We can basically split affected words into:
+ * - The first group, where we touch only the high bits
+ * - The last group, where we touch only the low bits
+ * - The middle, where we set all the bits to the same thing.
+ * We treat each case individually. The last two could be merged, but
+ * this can lead to bad codegen for those middle words.
+ */
+ /* First group */
+ fb_group_t mask = ((~(fb_group_t)0)
+ >> (FB_GROUP_BITS - first_group_cnt))
+ << start_bit_ind;
+ visit(ctx, &fb[group_ind], mask);
+
+ cnt -= first_group_cnt;
+ group_ind++;
+ /* Middle groups */
+ while (cnt > FB_GROUP_BITS) {
+ visit(ctx, &fb[group_ind], ~(fb_group_t)0);
+ cnt -= FB_GROUP_BITS;
+ group_ind++;
+ }
+ /* Last group */
+ if (cnt != 0) {
+ mask = (~(fb_group_t)0) >> (FB_GROUP_BITS - cnt);
+ visit(ctx, &fb[group_ind], mask);
+ }
+}
+
+JEMALLOC_ALWAYS_INLINE void
+fb_assign_visitor(void *ctx, fb_group_t *fb, fb_group_t mask) {
+ bool val = *(bool *)ctx;
+ if (val) {
+ *fb |= mask;
+ } else {
+ *fb &= ~mask;
+ }
+}
+
+/* Sets the cnt bits starting at position start. Must not have a 0 count. */
+static inline void
+fb_set_range(fb_group_t *fb, size_t nbits, size_t start, size_t cnt) {
+ bool val = true;
+ fb_visit_impl(fb, nbits, &fb_assign_visitor, &val, start, cnt);
+}
+
+/* Unsets the cnt bits starting at position start. Must not have a 0 count. */
+static inline void
+fb_unset_range(fb_group_t *fb, size_t nbits, size_t start, size_t cnt) {
+ bool val = false;
+ fb_visit_impl(fb, nbits, &fb_assign_visitor, &val, start, cnt);
+}
+
+JEMALLOC_ALWAYS_INLINE void
+fb_scount_visitor(void *ctx, fb_group_t *fb, fb_group_t mask) {
+ size_t *scount = (size_t *)ctx;
+ *scount += popcount_lu(*fb & mask);
+}
+
+/* Finds the number of set bit in the of length cnt starting at start. */
+JEMALLOC_ALWAYS_INLINE size_t
+fb_scount(fb_group_t *fb, size_t nbits, size_t start, size_t cnt) {
+ size_t scount = 0;
+ fb_visit_impl(fb, nbits, &fb_scount_visitor, &scount, start, cnt);
+ return scount;
+}
+
+/* Finds the number of unset bit in the of length cnt starting at start. */
+JEMALLOC_ALWAYS_INLINE size_t
+fb_ucount(fb_group_t *fb, size_t nbits, size_t start, size_t cnt) {
+ size_t scount = fb_scount(fb, nbits, start, cnt);
+ return cnt - scount;
+}
+
+/*
+ * An implementation detail; find the first bit at position >= min_bit with the
+ * value val.
+ *
+ * Returns the number of bits in the bitmap if no such bit exists.
+ */
+JEMALLOC_ALWAYS_INLINE ssize_t
+fb_find_impl(fb_group_t *fb, size_t nbits, size_t start, bool val,
+ bool forward) {
+ assert(start < nbits);
+ size_t ngroups = FB_NGROUPS(nbits);
+ ssize_t group_ind = start / FB_GROUP_BITS;
+ size_t bit_ind = start % FB_GROUP_BITS;
+
+ fb_group_t maybe_invert = (val ? 0 : (fb_group_t)-1);
+
+ fb_group_t group = fb[group_ind];
+ group ^= maybe_invert;
+ if (forward) {
+ /* Only keep ones in bits bit_ind and above. */
+ group &= ~((1LU << bit_ind) - 1);
+ } else {
+ /*
+ * Only keep ones in bits bit_ind and below. You might more
+ * naturally express this as (1 << (bit_ind + 1)) - 1, but
+ * that shifts by an invalid amount if bit_ind is one less than
+ * FB_GROUP_BITS.
+ */
+ group &= ((2LU << bit_ind) - 1);
+ }
+ ssize_t group_ind_bound = forward ? (ssize_t)ngroups : -1;
+ while (group == 0) {
+ group_ind += forward ? 1 : -1;
+ if (group_ind == group_ind_bound) {
+ return forward ? (ssize_t)nbits : (ssize_t)-1;
+ }
+ group = fb[group_ind];
+ group ^= maybe_invert;
+ }
+ assert(group != 0);
+ size_t bit = forward ? ffs_lu(group) : fls_lu(group);
+ size_t pos = group_ind * FB_GROUP_BITS + bit;
+ /*
+ * The high bits of a partially filled last group are zeros, so if we're
+ * looking for zeros we don't want to report an invalid result.
+ */
+ if (forward && !val && pos > nbits) {
+ return nbits;
+ }
+ return pos;
+}
+
+/*
+ * Find the first set bit in the bitmap with an index >= min_bit. Returns the
+ * number of bits in the bitmap if no such bit exists.
+ */
+static inline size_t
+fb_ffu(fb_group_t *fb, size_t nbits, size_t min_bit) {
+ return (size_t)fb_find_impl(fb, nbits, min_bit, /* val */ false,
+ /* forward */ true);
+}
+
+/* The same, but looks for an unset bit. */
+static inline size_t
+fb_ffs(fb_group_t *fb, size_t nbits, size_t min_bit) {
+ return (size_t)fb_find_impl(fb, nbits, min_bit, /* val */ true,
+ /* forward */ true);
+}
+
+/*
+ * Find the last set bit in the bitmap with an index <= max_bit. Returns -1 if
+ * no such bit exists.
+ */
+static inline ssize_t
+fb_flu(fb_group_t *fb, size_t nbits, size_t max_bit) {
+ return fb_find_impl(fb, nbits, max_bit, /* val */ false,
+ /* forward */ false);
+}
+
+static inline ssize_t
+fb_fls(fb_group_t *fb, size_t nbits, size_t max_bit) {
+ return fb_find_impl(fb, nbits, max_bit, /* val */ true,
+ /* forward */ false);
+}
+
+/* Returns whether or not we found a range. */
+JEMALLOC_ALWAYS_INLINE bool
+fb_iter_range_impl(fb_group_t *fb, size_t nbits, size_t start, size_t *r_begin,
+ size_t *r_len, bool val, bool forward) {
+ assert(start < nbits);
+ ssize_t next_range_begin = fb_find_impl(fb, nbits, start, val, forward);
+ if ((forward && next_range_begin == (ssize_t)nbits)
+ || (!forward && next_range_begin == (ssize_t)-1)) {
+ return false;
+ }
+ /* Half open range; the set bits are [begin, end). */
+ ssize_t next_range_end = fb_find_impl(fb, nbits, next_range_begin, !val,
+ forward);
+ if (forward) {
+ *r_begin = next_range_begin;
+ *r_len = next_range_end - next_range_begin;
+ } else {
+ *r_begin = next_range_end + 1;
+ *r_len = next_range_begin - next_range_end;
+ }
+ return true;
+}
+
+/*
+ * Used to iterate through ranges of set bits.
+ *
+ * Tries to find the next contiguous sequence of set bits with a first index >=
+ * start. If one exists, puts the earliest bit of the range in *r_begin, its
+ * length in *r_len, and returns true. Otherwise, returns false (without
+ * touching *r_begin or *r_end).
+ */
+static inline bool
+fb_srange_iter(fb_group_t *fb, size_t nbits, size_t start, size_t *r_begin,
+ size_t *r_len) {
+ return fb_iter_range_impl(fb, nbits, start, r_begin, r_len,
+ /* val */ true, /* forward */ true);
+}
+
+/*
+ * The same as fb_srange_iter, but searches backwards from start rather than
+ * forwards. (The position returned is still the earliest bit in the range).
+ */
+static inline bool
+fb_srange_riter(fb_group_t *fb, size_t nbits, size_t start, size_t *r_begin,
+ size_t *r_len) {
+ return fb_iter_range_impl(fb, nbits, start, r_begin, r_len,
+ /* val */ true, /* forward */ false);
+}
+
+/* Similar to fb_srange_iter, but searches for unset bits. */
+static inline bool
+fb_urange_iter(fb_group_t *fb, size_t nbits, size_t start, size_t *r_begin,
+ size_t *r_len) {
+ return fb_iter_range_impl(fb, nbits, start, r_begin, r_len,
+ /* val */ false, /* forward */ true);
+}
+
+/* Similar to fb_srange_riter, but searches for unset bits. */
+static inline bool
+fb_urange_riter(fb_group_t *fb, size_t nbits, size_t start, size_t *r_begin,
+ size_t *r_len) {
+ return fb_iter_range_impl(fb, nbits, start, r_begin, r_len,
+ /* val */ false, /* forward */ false);
+}
+
+JEMALLOC_ALWAYS_INLINE size_t
+fb_range_longest_impl(fb_group_t *fb, size_t nbits, bool val) {
+ size_t begin = 0;
+ size_t longest_len = 0;
+ size_t len = 0;
+ while (begin < nbits && fb_iter_range_impl(fb, nbits, begin, &begin,
+ &len, val, /* forward */ true)) {
+ if (len > longest_len) {
+ longest_len = len;
+ }
+ begin += len;
+ }
+ return longest_len;
+}
+
+static inline size_t
+fb_srange_longest(fb_group_t *fb, size_t nbits) {
+ return fb_range_longest_impl(fb, nbits, /* val */ true);
+}
+
+static inline size_t
+fb_urange_longest(fb_group_t *fb, size_t nbits) {
+ return fb_range_longest_impl(fb, nbits, /* val */ false);
+}
+
+/*
+ * Initializes each bit of dst with the bitwise-AND of the corresponding bits of
+ * src1 and src2. All bitmaps must be the same size.
+ */
+static inline void
+fb_bit_and(fb_group_t *dst, fb_group_t *src1, fb_group_t *src2, size_t nbits) {
+ size_t ngroups = FB_NGROUPS(nbits);
+ for (size_t i = 0; i < ngroups; i++) {
+ dst[i] = src1[i] & src2[i];
+ }
+}
+
+/* Like fb_bit_and, but with bitwise-OR. */
+static inline void
+fb_bit_or(fb_group_t *dst, fb_group_t *src1, fb_group_t *src2, size_t nbits) {
+ size_t ngroups = FB_NGROUPS(nbits);
+ for (size_t i = 0; i < ngroups; i++) {
+ dst[i] = src1[i] | src2[i];
+ }
+}
+
+/* Initializes dst bit i to the negation of source bit i. */
+static inline void
+fb_bit_not(fb_group_t *dst, fb_group_t *src, size_t nbits) {
+ size_t ngroups = FB_NGROUPS(nbits);
+ for (size_t i = 0; i < ngroups; i++) {
+ dst[i] = ~src[i];
+ }
+}
+
+#endif /* JEMALLOC_INTERNAL_FB_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/fxp.h b/contrib/jemalloc/include/jemalloc/internal/fxp.h
new file mode 100644
index 000000000000..415a982890a1
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/internal/fxp.h
@@ -0,0 +1,126 @@
+#ifndef JEMALLOC_INTERNAL_FXP_H
+#define JEMALLOC_INTERNAL_FXP_H
+
+/*
+ * A simple fixed-point math implementation, supporting only unsigned values
+ * (with overflow being an error).
+ *
+ * It's not in general safe to use floating point in core code, because various
+ * libc implementations we get linked against can assume that malloc won't touch
+ * floating point state and call it with an unusual calling convention.
+ */
+
+/*
+ * High 16 bits are the integer part, low 16 are the fractional part. Or
+ * equivalently, repr == 2**16 * val, where we use "val" to refer to the
+ * (imaginary) fractional representation of the true value.
+ *
+ * We pick a uint32_t here since it's convenient in some places to
+ * double the representation size (i.e. multiplication and division use
+ * 64-bit integer types), and a uint64_t is the largest type we're
+ * certain is available.
+ */
+typedef uint32_t fxp_t;
+#define FXP_INIT_INT(x) ((x) << 16)
+#define FXP_INIT_PERCENT(pct) (((pct) << 16) / 100)
+
+/*
+ * Amount of precision used in parsing and printing numbers. The integer bound
+ * is simply because the integer part of the number gets 16 bits, and so is
+ * bounded by 65536.
+ *
+ * We use a lot of precision for the fractional part, even though most of it
+ * gets rounded off; this lets us get exact values for the important special
+ * case where the denominator is a small power of 2 (for instance,
+ * 1/512 == 0.001953125 is exactly representable even with only 16 bits of
+ * fractional precision). We need to left-shift by 16 before dividing by
+ * 10**precision, so we pick precision to be floor(log(2**48)) = 14.
+ */
+#define FXP_INTEGER_PART_DIGITS 5
+#define FXP_FRACTIONAL_PART_DIGITS 14
+
+/*
+ * In addition to the integer and fractional parts of the number, we need to
+ * include a null character and (possibly) a decimal point.
+ */
+#define FXP_BUF_SIZE (FXP_INTEGER_PART_DIGITS + FXP_FRACTIONAL_PART_DIGITS + 2)
+
+static inline fxp_t
+fxp_add(fxp_t a, fxp_t b) {
+ return a + b;
+}
+
+static inline fxp_t
+fxp_sub(fxp_t a, fxp_t b) {
+ assert(a >= b);
+ return a - b;
+}
+
+static inline fxp_t
+fxp_mul(fxp_t a, fxp_t b) {
+ uint64_t unshifted = (uint64_t)a * (uint64_t)b;
+ /*
+ * Unshifted is (a.val * 2**16) * (b.val * 2**16)
+ * == (a.val * b.val) * 2**32, but we want
+ * (a.val * b.val) * 2 ** 16.
+ */
+ return (uint32_t)(unshifted >> 16);
+}
+
+static inline fxp_t
+fxp_div(fxp_t a, fxp_t b) {
+ assert(b != 0);
+ uint64_t unshifted = ((uint64_t)a << 32) / (uint64_t)b;
+ /*
+ * Unshifted is (a.val * 2**16) * (2**32) / (b.val * 2**16)
+ * == (a.val / b.val) * (2 ** 32), which again corresponds to a right
+ * shift of 16.
+ */
+ return (uint32_t)(unshifted >> 16);
+}
+
+static inline uint32_t
+fxp_round_down(fxp_t a) {
+ return a >> 16;
+}
+
+static inline uint32_t
+fxp_round_nearest(fxp_t a) {
+ uint32_t fractional_part = (a & ((1U << 16) - 1));
+ uint32_t increment = (uint32_t)(fractional_part >= (1U << 15));
+ return (a >> 16) + increment;
+}
+
+/*
+ * Approximately computes x * frac, without the size limitations that would be
+ * imposed by converting u to an fxp_t.
+ */
+static inline size_t
+fxp_mul_frac(size_t x_orig, fxp_t frac) {
+ assert(frac <= (1U << 16));
+ /*
+ * Work around an over-enthusiastic warning about type limits below (on
+ * 32-bit platforms, a size_t is always less than 1ULL << 48).
+ */
+ uint64_t x = (uint64_t)x_orig;
+ /*
+ * If we can guarantee no overflow, multiply first before shifting, to
+ * preserve some precision. Otherwise, shift first and then multiply.
+ * In the latter case, we only lose the low 16 bits of a 48-bit number,
+ * so we're still accurate to within 1/2**32.
+ */
+ if (x < (1ULL << 48)) {
+ return (size_t)((x * frac) >> 16);
+ } else {
+ return (size_t)((x >> 16) * (uint64_t)frac);
+ }
+}
+
+/*
+ * Returns true on error. Otherwise, returns false and updates *ptr to point to
+ * the first character not parsed (because it wasn't a digit).
+ */
+bool fxp_parse(fxp_t *a, const char *ptr, char **end);
+void fxp_print(fxp_t a, char buf[FXP_BUF_SIZE]);
+
+#endif /* JEMALLOC_INTERNAL_FXP_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/hash.h b/contrib/jemalloc/include/jemalloc/internal/hash.h
index 935ddcfc95a1..7f945679efbc 100644
--- a/contrib/jemalloc/include/jemalloc/internal/hash.h
+++ b/contrib/jemalloc/include/jemalloc/internal/hash.h
@@ -104,8 +104,8 @@ hash_x86_32(const void *key, int len, uint32_t seed) {
uint32_t k1 = 0;
switch (len & 3) {
- case 3: k1 ^= tail[2] << 16; JEMALLOC_FALLTHROUGH
- case 2: k1 ^= tail[1] << 8; JEMALLOC_FALLTHROUGH
+ case 3: k1 ^= tail[2] << 16; JEMALLOC_FALLTHROUGH;
+ case 2: k1 ^= tail[1] << 8; JEMALLOC_FALLTHROUGH;
case 1: k1 ^= tail[0]; k1 *= c1; k1 = hash_rotl_32(k1, 15);
k1 *= c2; h1 ^= k1;
}
@@ -177,26 +177,26 @@ hash_x86_128(const void *key, const int len, uint32_t seed,
uint32_t k4 = 0;
switch (len & 15) {
- case 15: k4 ^= tail[14] << 16; JEMALLOC_FALLTHROUGH
- case 14: k4 ^= tail[13] << 8; JEMALLOC_FALLTHROUGH
+ case 15: k4 ^= tail[14] << 16; JEMALLOC_FALLTHROUGH;
+ case 14: k4 ^= tail[13] << 8; JEMALLOC_FALLTHROUGH;
case 13: k4 ^= tail[12] << 0;
k4 *= c4; k4 = hash_rotl_32(k4, 18); k4 *= c1; h4 ^= k4;
- JEMALLOC_FALLTHROUGH
- case 12: k3 ^= tail[11] << 24; JEMALLOC_FALLTHROUGH
- case 11: k3 ^= tail[10] << 16; JEMALLOC_FALLTHROUGH
- case 10: k3 ^= tail[ 9] << 8; JEMALLOC_FALLTHROUGH
+ JEMALLOC_FALLTHROUGH;
+ case 12: k3 ^= (uint32_t) tail[11] << 24; JEMALLOC_FALLTHROUGH;
+ case 11: k3 ^= tail[10] << 16; JEMALLOC_FALLTHROUGH;
+ case 10: k3 ^= tail[ 9] << 8; JEMALLOC_FALLTHROUGH;
case 9: k3 ^= tail[ 8] << 0;
- k3 *= c3; k3 = hash_rotl_32(k3, 17); k3 *= c4; h3 ^= k3;
- JEMALLOC_FALLTHROUGH
- case 8: k2 ^= tail[ 7] << 24; JEMALLOC_FALLTHROUGH
- case 7: k2 ^= tail[ 6] << 16; JEMALLOC_FALLTHROUGH
- case 6: k2 ^= tail[ 5] << 8; JEMALLOC_FALLTHROUGH
+ k3 *= c3; k3 = hash_rotl_32(k3, 17); k3 *= c4; h3 ^= k3;
+ JEMALLOC_FALLTHROUGH;
+ case 8: k2 ^= (uint32_t) tail[ 7] << 24; JEMALLOC_FALLTHROUGH;
+ case 7: k2 ^= tail[ 6] << 16; JEMALLOC_FALLTHROUGH;
+ case 6: k2 ^= tail[ 5] << 8; JEMALLOC_FALLTHROUGH;
case 5: k2 ^= tail[ 4] << 0;
k2 *= c2; k2 = hash_rotl_32(k2, 16); k2 *= c3; h2 ^= k2;
- JEMALLOC_FALLTHROUGH
- case 4: k1 ^= tail[ 3] << 24; JEMALLOC_FALLTHROUGH
- case 3: k1 ^= tail[ 2] << 16; JEMALLOC_FALLTHROUGH
- case 2: k1 ^= tail[ 1] << 8; JEMALLOC_FALLTHROUGH
+ JEMALLOC_FALLTHROUGH;
+ case 4: k1 ^= (uint32_t) tail[ 3] << 24; JEMALLOC_FALLTHROUGH;
+ case 3: k1 ^= tail[ 2] << 16; JEMALLOC_FALLTHROUGH;
+ case 2: k1 ^= tail[ 1] << 8; JEMALLOC_FALLTHROUGH;
case 1: k1 ^= tail[ 0] << 0;
k1 *= c1; k1 = hash_rotl_32(k1, 15); k1 *= c2; h1 ^= k1;
break;
@@ -261,24 +261,25 @@ hash_x64_128(const void *key, const int len, const uint32_t seed,
uint64_t k2 = 0;
switch (len & 15) {
- case 15: k2 ^= ((uint64_t)(tail[14])) << 48; JEMALLOC_FALLTHROUGH
- case 14: k2 ^= ((uint64_t)(tail[13])) << 40; JEMALLOC_FALLTHROUGH
- case 13: k2 ^= ((uint64_t)(tail[12])) << 32; JEMALLOC_FALLTHROUGH
- case 12: k2 ^= ((uint64_t)(tail[11])) << 24; JEMALLOC_FALLTHROUGH
- case 11: k2 ^= ((uint64_t)(tail[10])) << 16; JEMALLOC_FALLTHROUGH
- case 10: k2 ^= ((uint64_t)(tail[ 9])) << 8; JEMALLOC_FALLTHROUGH
+ case 15: k2 ^= ((uint64_t)(tail[14])) << 48; JEMALLOC_FALLTHROUGH;
+ case 14: k2 ^= ((uint64_t)(tail[13])) << 40; JEMALLOC_FALLTHROUGH;
+ case 13: k2 ^= ((uint64_t)(tail[12])) << 32; JEMALLOC_FALLTHROUGH;
+ case 12: k2 ^= ((uint64_t)(tail[11])) << 24; JEMALLOC_FALLTHROUGH;
+ case 11: k2 ^= ((uint64_t)(tail[10])) << 16; JEMALLOC_FALLTHROUGH;
+ case 10: k2 ^= ((uint64_t)(tail[ 9])) << 8; JEMALLOC_FALLTHROUGH;
case 9: k2 ^= ((uint64_t)(tail[ 8])) << 0;
k2 *= c2; k2 = hash_rotl_64(k2, 33); k2 *= c1; h2 ^= k2;
- JEMALLOC_FALLTHROUGH
- case 8: k1 ^= ((uint64_t)(tail[ 7])) << 56; JEMALLOC_FALLTHROUGH
- case 7: k1 ^= ((uint64_t)(tail[ 6])) << 48; JEMALLOC_FALLTHROUGH
- case 6: k1 ^= ((uint64_t)(tail[ 5])) << 40; JEMALLOC_FALLTHROUGH
- case 5: k1 ^= ((uint64_t)(tail[ 4])) << 32; JEMALLOC_FALLTHROUGH
- case 4: k1 ^= ((uint64_t)(tail[ 3])) << 24; JEMALLOC_FALLTHROUGH
- case 3: k1 ^= ((uint64_t)(tail[ 2])) << 16; JEMALLOC_FALLTHROUGH
- case 2: k1 ^= ((uint64_t)(tail[ 1])) << 8; JEMALLOC_FALLTHROUGH
+ JEMALLOC_FALLTHROUGH;
+ case 8: k1 ^= ((uint64_t)(tail[ 7])) << 56; JEMALLOC_FALLTHROUGH;
+ case 7: k1 ^= ((uint64_t)(tail[ 6])) << 48; JEMALLOC_FALLTHROUGH;
+ case 6: k1 ^= ((uint64_t)(tail[ 5])) << 40; JEMALLOC_FALLTHROUGH;
+ case 5: k1 ^= ((uint64_t)(tail[ 4])) << 32; JEMALLOC_FALLTHROUGH;
+ case 4: k1 ^= ((uint64_t)(tail[ 3])) << 24; JEMALLOC_FALLTHROUGH;
+ case 3: k1 ^= ((uint64_t)(tail[ 2])) << 16; JEMALLOC_FALLTHROUGH;
+ case 2: k1 ^= ((uint64_t)(tail[ 1])) << 8; JEMALLOC_FALLTHROUGH;
case 1: k1 ^= ((uint64_t)(tail[ 0])) << 0;
k1 *= c1; k1 = hash_rotl_64(k1, 31); k1 *= c2; h1 ^= k1;
+ break;
}
}
diff --git a/contrib/jemalloc/include/jemalloc/internal/hpa.h b/contrib/jemalloc/include/jemalloc/internal/hpa.h
new file mode 100644
index 000000000000..f3562853e802
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/internal/hpa.h
@@ -0,0 +1,182 @@
+#ifndef JEMALLOC_INTERNAL_HPA_H
+#define JEMALLOC_INTERNAL_HPA_H
+
+#include "jemalloc/internal/exp_grow.h"
+#include "jemalloc/internal/hpa_hooks.h"
+#include "jemalloc/internal/hpa_opts.h"
+#include "jemalloc/internal/pai.h"
+#include "jemalloc/internal/psset.h"
+
+typedef struct hpa_central_s hpa_central_t;
+struct hpa_central_s {
+ /*
+ * The mutex guarding most of the operations on the central data
+ * structure.
+ */
+ malloc_mutex_t mtx;
+ /*
+ * Guards expansion of eden. We separate this from the regular mutex so
+ * that cheaper operations can still continue while we're doing the OS
+ * call.
+ */
+ malloc_mutex_t grow_mtx;
+ /*
+ * Either NULL (if empty), or some integer multiple of a
+ * hugepage-aligned number of hugepages. We carve them off one at a
+ * time to satisfy new pageslab requests.
+ *
+ * Guarded by grow_mtx.
+ */
+ void *eden;
+ size_t eden_len;
+ /* Source for metadata. */
+ base_t *base;
+ /* Number of grow operations done on this hpa_central_t. */
+ uint64_t age_counter;
+
+ /* The HPA hooks. */
+ hpa_hooks_t hooks;
+};
+
+typedef struct hpa_shard_nonderived_stats_s hpa_shard_nonderived_stats_t;
+struct hpa_shard_nonderived_stats_s {
+ /*
+ * The number of times we've purged within a hugepage.
+ *
+ * Guarded by mtx.
+ */
+ uint64_t npurge_passes;
+ /*
+ * The number of individual purge calls we perform (which should always
+ * be bigger than npurge_passes, since each pass purges at least one
+ * extent within a hugepage.
+ *
+ * Guarded by mtx.
+ */
+ uint64_t npurges;
+
+ /*
+ * The number of times we've hugified a pageslab.
+ *
+ * Guarded by mtx.
+ */
+ uint64_t nhugifies;
+ /*
+ * The number of times we've dehugified a pageslab.
+ *
+ * Guarded by mtx.
+ */
+ uint64_t ndehugifies;
+};
+
+/* Completely derived; only used by CTL. */
+typedef struct hpa_shard_stats_s hpa_shard_stats_t;
+struct hpa_shard_stats_s {
+ psset_stats_t psset_stats;
+ hpa_shard_nonderived_stats_t nonderived_stats;
+};
+
+typedef struct hpa_shard_s hpa_shard_t;
+struct hpa_shard_s {
+ /*
+ * pai must be the first member; we cast from a pointer to it to a
+ * pointer to the hpa_shard_t.
+ */
+ pai_t pai;
+
+ /* The central allocator we get our hugepages from. */
+ hpa_central_t *central;
+ /* Protects most of this shard's state. */
+ malloc_mutex_t mtx;
+ /*
+ * Guards the shard's access to the central allocator (preventing
+ * multiple threads operating on this shard from accessing the central
+ * allocator).
+ */
+ malloc_mutex_t grow_mtx;
+ /* The base metadata allocator. */
+ base_t *base;
+
+ /*
+ * This edata cache is the one we use when allocating a small extent
+ * from a pageslab. The pageslab itself comes from the centralized
+ * allocator, and so will use its edata_cache.
+ */
+ edata_cache_fast_t ecf;
+
+ psset_t psset;
+
+ /*
+ * How many grow operations have occurred.
+ *
+ * Guarded by grow_mtx.
+ */
+ uint64_t age_counter;
+
+ /* The arena ind we're associated with. */
+ unsigned ind;
+
+ /*
+ * Our emap. This is just a cache of the emap pointer in the associated
+ * hpa_central.
+ */
+ emap_t *emap;
+
+ /* The configuration choices for this hpa shard. */
+ hpa_shard_opts_t opts;
+
+ /*
+ * How many pages have we started but not yet finished purging in this
+ * hpa shard.
+ */
+ size_t npending_purge;
+
+ /*
+ * Those stats which are copied directly into the CTL-centric hpa shard
+ * stats.
+ */
+ hpa_shard_nonderived_stats_t stats;
+
+ /*
+ * Last time we performed purge on this shard.
+ */
+ nstime_t last_purge;
+};
+
+/*
+ * Whether or not the HPA can be used given the current configuration. This is
+ * is not necessarily a guarantee that it backs its allocations by hugepages,
+ * just that it can function properly given the system it's running on.
+ */
+bool hpa_supported();
+bool hpa_central_init(hpa_central_t *central, base_t *base, const hpa_hooks_t *hooks);
+bool hpa_shard_init(hpa_shard_t *shard, hpa_central_t *central, emap_t *emap,
+ base_t *base, edata_cache_t *edata_cache, unsigned ind,
+ const hpa_shard_opts_t *opts);
+
+void hpa_shard_stats_accum(hpa_shard_stats_t *dst, hpa_shard_stats_t *src);
+void hpa_shard_stats_merge(tsdn_t *tsdn, hpa_shard_t *shard,
+ hpa_shard_stats_t *dst);
+
+/*
+ * Notify the shard that we won't use it for allocations much longer. Due to
+ * the possibility of races, we don't actually prevent allocations; just flush
+ * and disable the embedded edata_cache_small.
+ */
+void hpa_shard_disable(tsdn_t *tsdn, hpa_shard_t *shard);
+void hpa_shard_destroy(tsdn_t *tsdn, hpa_shard_t *shard);
+
+void hpa_shard_set_deferral_allowed(tsdn_t *tsdn, hpa_shard_t *shard,
+ bool deferral_allowed);
+void hpa_shard_do_deferred_work(tsdn_t *tsdn, hpa_shard_t *shard);
+
+/*
+ * We share the fork ordering with the PA and arena prefork handling; that's why
+ * these are 3 and 4 rather than 0 and 1.
+ */
+void hpa_shard_prefork3(tsdn_t *tsdn, hpa_shard_t *shard);
+void hpa_shard_prefork4(tsdn_t *tsdn, hpa_shard_t *shard);
+void hpa_shard_postfork_parent(tsdn_t *tsdn, hpa_shard_t *shard);
+void hpa_shard_postfork_child(tsdn_t *tsdn, hpa_shard_t *shard);
+
+#endif /* JEMALLOC_INTERNAL_HPA_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/hpa_hooks.h b/contrib/jemalloc/include/jemalloc/internal/hpa_hooks.h
new file mode 100644
index 000000000000..4ea221cb0b42
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/internal/hpa_hooks.h
@@ -0,0 +1,17 @@
+#ifndef JEMALLOC_INTERNAL_HPA_HOOKS_H
+#define JEMALLOC_INTERNAL_HPA_HOOKS_H
+
+typedef struct hpa_hooks_s hpa_hooks_t;
+struct hpa_hooks_s {
+ void *(*map)(size_t size);
+ void (*unmap)(void *ptr, size_t size);
+ void (*purge)(void *ptr, size_t size);
+ void (*hugify)(void *ptr, size_t size);
+ void (*dehugify)(void *ptr, size_t size);
+ void (*curtime)(nstime_t *r_time, bool first_reading);
+ uint64_t (*ms_since)(nstime_t *r_time);
+};
+
+extern hpa_hooks_t hpa_hooks_default;
+
+#endif /* JEMALLOC_INTERNAL_HPA_HOOKS_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/hpa_opts.h b/contrib/jemalloc/include/jemalloc/internal/hpa_opts.h
new file mode 100644
index 000000000000..ee84fea13757
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/internal/hpa_opts.h
@@ -0,0 +1,74 @@
+#ifndef JEMALLOC_INTERNAL_HPA_OPTS_H
+#define JEMALLOC_INTERNAL_HPA_OPTS_H
+
+#include "jemalloc/internal/fxp.h"
+
+/*
+ * This file is morally part of hpa.h, but is split out for header-ordering
+ * reasons.
+ */
+
+typedef struct hpa_shard_opts_s hpa_shard_opts_t;
+struct hpa_shard_opts_s {
+ /*
+ * The largest size we'll allocate out of the shard. For those
+ * allocations refused, the caller (in practice, the PA module) will
+ * fall back to the more general (for now) PAC, which can always handle
+ * any allocation request.
+ */
+ size_t slab_max_alloc;
+
+ /*
+ * When the number of active bytes in a hugepage is >=
+ * hugification_threshold, we force hugify it.
+ */
+ size_t hugification_threshold;
+
+ /*
+ * The HPA purges whenever the number of pages exceeds dirty_mult *
+ * active_pages. This may be set to (fxp_t)-1 to disable purging.
+ */
+ fxp_t dirty_mult;
+
+ /*
+ * Whether or not the PAI methods are allowed to defer work to a
+ * subsequent hpa_shard_do_deferred_work() call. Practically, this
+ * corresponds to background threads being enabled. We track this
+ * ourselves for encapsulation purposes.
+ */
+ bool deferral_allowed;
+
+ /*
+ * How long a hugepage has to be a hugification candidate before it will
+ * actually get hugified.
+ */
+ uint64_t hugify_delay_ms;
+
+ /*
+ * Minimum amount of time between purges.
+ */
+ uint64_t min_purge_interval_ms;
+};
+
+#define HPA_SHARD_OPTS_DEFAULT { \
+ /* slab_max_alloc */ \
+ 64 * 1024, \
+ /* hugification_threshold */ \
+ HUGEPAGE * 95 / 100, \
+ /* dirty_mult */ \
+ FXP_INIT_PERCENT(25), \
+ /* \
+ * deferral_allowed \
+ * \
+ * Really, this is always set by the arena during creation \
+ * or by an hpa_shard_set_deferral_allowed call, so the value \
+ * we put here doesn't matter. \
+ */ \
+ false, \
+ /* hugify_delay_ms */ \
+ 10 * 1000, \
+ /* min_purge_interval_ms */ \
+ 5 * 1000 \
+}
+
+#endif /* JEMALLOC_INTERNAL_HPA_OPTS_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/hpdata.h b/contrib/jemalloc/include/jemalloc/internal/hpdata.h
new file mode 100644
index 000000000000..1fb534db016a
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/internal/hpdata.h
@@ -0,0 +1,413 @@
+#ifndef JEMALLOC_INTERNAL_HPDATA_H
+#define JEMALLOC_INTERNAL_HPDATA_H
+
+#include "jemalloc/internal/fb.h"
+#include "jemalloc/internal/ph.h"
+#include "jemalloc/internal/ql.h"
+#include "jemalloc/internal/typed_list.h"
+
+/*
+ * The metadata representation we use for extents in hugepages. While the PAC
+ * uses the edata_t to represent both active and inactive extents, the HP only
+ * uses the edata_t for active ones; instead, inactive extent state is tracked
+ * within hpdata associated with the enclosing hugepage-sized, hugepage-aligned
+ * region of virtual address space.
+ *
+ * An hpdata need not be "truly" backed by a hugepage (which is not necessarily
+ * an observable property of any given region of address space). It's just
+ * hugepage-sized and hugepage-aligned; it's *potentially* huge.
+ */
+typedef struct hpdata_s hpdata_t;
+ph_structs(hpdata_age_heap, hpdata_t);
+struct hpdata_s {
+ /*
+ * We likewise follow the edata convention of mangling names and forcing
+ * the use of accessors -- this lets us add some consistency checks on
+ * access.
+ */
+
+ /*
+ * The address of the hugepage in question. This can't be named h_addr,
+ * since that conflicts with a macro defined in Windows headers.
+ */
+ void *h_address;
+ /* Its age (measured in psset operations). */
+ uint64_t h_age;
+ /* Whether or not we think the hugepage is mapped that way by the OS. */
+ bool h_huge;
+
+ /*
+ * For some properties, we keep parallel sets of bools; h_foo_allowed
+ * and h_in_psset_foo_container. This is a decoupling mechanism to
+ * avoid bothering the hpa (which manages policies) from the psset
+ * (which is the mechanism used to enforce those policies). This allows
+ * all the container management logic to live in one place, without the
+ * HPA needing to know or care how that happens.
+ */
+
+ /*
+ * Whether or not the hpdata is allowed to be used to serve allocations,
+ * and whether or not the psset is currently tracking it as such.
+ */
+ bool h_alloc_allowed;
+ bool h_in_psset_alloc_container;
+
+ /*
+ * The same, but with purging. There's no corresponding
+ * h_in_psset_purge_container, because the psset (currently) always
+ * removes hpdatas from their containers during updates (to implement
+ * LRU for purging).
+ */
+ bool h_purge_allowed;
+
+ /* And with hugifying. */
+ bool h_hugify_allowed;
+ /* When we became a hugification candidate. */
+ nstime_t h_time_hugify_allowed;
+ bool h_in_psset_hugify_container;
+
+ /* Whether or not a purge or hugify is currently happening. */
+ bool h_mid_purge;
+ bool h_mid_hugify;
+
+ /*
+ * Whether or not the hpdata is being updated in the psset (i.e. if
+ * there has been a psset_update_begin call issued without a matching
+ * psset_update_end call). Eventually this will expand to other types
+ * of updates.
+ */
+ bool h_updating;
+
+ /* Whether or not the hpdata is in a psset. */
+ bool h_in_psset;
+
+ union {
+ /* When nonempty (and also nonfull), used by the psset bins. */
+ hpdata_age_heap_link_t age_link;
+ /*
+ * When empty (or not corresponding to any hugepage), list
+ * linkage.
+ */
+ ql_elm(hpdata_t) ql_link_empty;
+ };
+
+ /*
+ * Linkage for the psset to track candidates for purging and hugifying.
+ */
+ ql_elm(hpdata_t) ql_link_purge;
+ ql_elm(hpdata_t) ql_link_hugify;
+
+ /* The length of the largest contiguous sequence of inactive pages. */
+ size_t h_longest_free_range;
+
+ /* Number of active pages. */
+ size_t h_nactive;
+
+ /* A bitmap with bits set in the active pages. */
+ fb_group_t active_pages[FB_NGROUPS(HUGEPAGE_PAGES)];
+
+ /*
+ * Number of dirty or active pages, and a bitmap tracking them. One
+ * way to think of this is as which pages are dirty from the OS's
+ * perspective.
+ */
+ size_t h_ntouched;
+
+ /* The touched pages (using the same definition as above). */
+ fb_group_t touched_pages[FB_NGROUPS(HUGEPAGE_PAGES)];
+};
+
+TYPED_LIST(hpdata_empty_list, hpdata_t, ql_link_empty)
+TYPED_LIST(hpdata_purge_list, hpdata_t, ql_link_purge)
+TYPED_LIST(hpdata_hugify_list, hpdata_t, ql_link_hugify)
+
+ph_proto(, hpdata_age_heap, hpdata_t);
+
+static inline void *
+hpdata_addr_get(const hpdata_t *hpdata) {
+ return hpdata->h_address;
+}
+
+static inline void
+hpdata_addr_set(hpdata_t *hpdata, void *addr) {
+ assert(HUGEPAGE_ADDR2BASE(addr) == addr);
+ hpdata->h_address = addr;
+}
+
+static inline uint64_t
+hpdata_age_get(const hpdata_t *hpdata) {
+ return hpdata->h_age;
+}
+
+static inline void
+hpdata_age_set(hpdata_t *hpdata, uint64_t age) {
+ hpdata->h_age = age;
+}
+
+static inline bool
+hpdata_huge_get(const hpdata_t *hpdata) {
+ return hpdata->h_huge;
+}
+
+static inline bool
+hpdata_alloc_allowed_get(const hpdata_t *hpdata) {
+ return hpdata->h_alloc_allowed;
+}
+
+static inline void
+hpdata_alloc_allowed_set(hpdata_t *hpdata, bool alloc_allowed) {
+ hpdata->h_alloc_allowed = alloc_allowed;
+}
+
+static inline bool
+hpdata_in_psset_alloc_container_get(const hpdata_t *hpdata) {
+ return hpdata->h_in_psset_alloc_container;
+}
+
+static inline void
+hpdata_in_psset_alloc_container_set(hpdata_t *hpdata, bool in_container) {
+ assert(in_container != hpdata->h_in_psset_alloc_container);
+ hpdata->h_in_psset_alloc_container = in_container;
+}
+
+static inline bool
+hpdata_purge_allowed_get(const hpdata_t *hpdata) {
+ return hpdata->h_purge_allowed;
+}
+
+static inline void
+hpdata_purge_allowed_set(hpdata_t *hpdata, bool purge_allowed) {
+ assert(purge_allowed == false || !hpdata->h_mid_purge);
+ hpdata->h_purge_allowed = purge_allowed;
+}
+
+static inline bool
+hpdata_hugify_allowed_get(const hpdata_t *hpdata) {
+ return hpdata->h_hugify_allowed;
+}
+
+static inline void
+hpdata_allow_hugify(hpdata_t *hpdata, nstime_t now) {
+ assert(!hpdata->h_mid_hugify);
+ hpdata->h_hugify_allowed = true;
+ hpdata->h_time_hugify_allowed = now;
+}
+
+static inline nstime_t
+hpdata_time_hugify_allowed(hpdata_t *hpdata) {
+ return hpdata->h_time_hugify_allowed;
+}
+
+static inline void
+hpdata_disallow_hugify(hpdata_t *hpdata) {
+ hpdata->h_hugify_allowed = false;
+}
+
+static inline bool
+hpdata_in_psset_hugify_container_get(const hpdata_t *hpdata) {
+ return hpdata->h_in_psset_hugify_container;
+}
+
+static inline void
+hpdata_in_psset_hugify_container_set(hpdata_t *hpdata, bool in_container) {
+ assert(in_container != hpdata->h_in_psset_hugify_container);
+ hpdata->h_in_psset_hugify_container = in_container;
+}
+
+static inline bool
+hpdata_mid_purge_get(const hpdata_t *hpdata) {
+ return hpdata->h_mid_purge;
+}
+
+static inline void
+hpdata_mid_purge_set(hpdata_t *hpdata, bool mid_purge) {
+ assert(mid_purge != hpdata->h_mid_purge);
+ hpdata->h_mid_purge = mid_purge;
+}
+
+static inline bool
+hpdata_mid_hugify_get(const hpdata_t *hpdata) {
+ return hpdata->h_mid_hugify;
+}
+
+static inline void
+hpdata_mid_hugify_set(hpdata_t *hpdata, bool mid_hugify) {
+ assert(mid_hugify != hpdata->h_mid_hugify);
+ hpdata->h_mid_hugify = mid_hugify;
+}
+
+static inline bool
+hpdata_changing_state_get(const hpdata_t *hpdata) {
+ return hpdata->h_mid_purge || hpdata->h_mid_hugify;
+}
+
+
+static inline bool
+hpdata_updating_get(const hpdata_t *hpdata) {
+ return hpdata->h_updating;
+}
+
+static inline void
+hpdata_updating_set(hpdata_t *hpdata, bool updating) {
+ assert(updating != hpdata->h_updating);
+ hpdata->h_updating = updating;
+}
+
+static inline bool
+hpdata_in_psset_get(const hpdata_t *hpdata) {
+ return hpdata->h_in_psset;
+}
+
+static inline void
+hpdata_in_psset_set(hpdata_t *hpdata, bool in_psset) {
+ assert(in_psset != hpdata->h_in_psset);
+ hpdata->h_in_psset = in_psset;
+}
+
+static inline size_t
+hpdata_longest_free_range_get(const hpdata_t *hpdata) {
+ return hpdata->h_longest_free_range;
+}
+
+static inline void
+hpdata_longest_free_range_set(hpdata_t *hpdata, size_t longest_free_range) {
+ assert(longest_free_range <= HUGEPAGE_PAGES);
+ hpdata->h_longest_free_range = longest_free_range;
+}
+
+static inline size_t
+hpdata_nactive_get(hpdata_t *hpdata) {
+ return hpdata->h_nactive;
+}
+
+static inline size_t
+hpdata_ntouched_get(hpdata_t *hpdata) {
+ return hpdata->h_ntouched;
+}
+
+static inline size_t
+hpdata_ndirty_get(hpdata_t *hpdata) {
+ return hpdata->h_ntouched - hpdata->h_nactive;
+}
+
+static inline size_t
+hpdata_nretained_get(hpdata_t *hpdata) {
+ return HUGEPAGE_PAGES - hpdata->h_ntouched;
+}
+
+static inline void
+hpdata_assert_empty(hpdata_t *hpdata) {
+ assert(fb_empty(hpdata->active_pages, HUGEPAGE_PAGES));
+ assert(hpdata->h_nactive == 0);
+}
+
+/*
+ * Only used in tests, and in hpdata_assert_consistent, below. Verifies some
+ * consistency properties of the hpdata (e.g. that cached counts of page stats
+ * match computed ones).
+ */
+static inline bool
+hpdata_consistent(hpdata_t *hpdata) {
+ if(fb_urange_longest(hpdata->active_pages, HUGEPAGE_PAGES)
+ != hpdata_longest_free_range_get(hpdata)) {
+ return false;
+ }
+ if (fb_scount(hpdata->active_pages, HUGEPAGE_PAGES, 0, HUGEPAGE_PAGES)
+ != hpdata->h_nactive) {
+ return false;
+ }
+ if (fb_scount(hpdata->touched_pages, HUGEPAGE_PAGES, 0, HUGEPAGE_PAGES)
+ != hpdata->h_ntouched) {
+ return false;
+ }
+ if (hpdata->h_ntouched < hpdata->h_nactive) {
+ return false;
+ }
+ if (hpdata->h_huge && hpdata->h_ntouched != HUGEPAGE_PAGES) {
+ return false;
+ }
+ if (hpdata_changing_state_get(hpdata)
+ && ((hpdata->h_purge_allowed) || hpdata->h_hugify_allowed)) {
+ return false;
+ }
+ if (hpdata_hugify_allowed_get(hpdata)
+ != hpdata_in_psset_hugify_container_get(hpdata)) {
+ return false;
+ }
+ return true;
+}
+
+static inline void
+hpdata_assert_consistent(hpdata_t *hpdata) {
+ assert(hpdata_consistent(hpdata));
+}
+
+static inline bool
+hpdata_empty(hpdata_t *hpdata) {
+ return hpdata->h_nactive == 0;
+}
+
+static inline bool
+hpdata_full(hpdata_t *hpdata) {
+ return hpdata->h_nactive == HUGEPAGE_PAGES;
+}
+
+void hpdata_init(hpdata_t *hpdata, void *addr, uint64_t age);
+
+/*
+ * Given an hpdata which can serve an allocation request, pick and reserve an
+ * offset within that allocation.
+ */
+void *hpdata_reserve_alloc(hpdata_t *hpdata, size_t sz);
+void hpdata_unreserve(hpdata_t *hpdata, void *begin, size_t sz);
+
+/*
+ * The hpdata_purge_prepare_t allows grabbing the metadata required to purge
+ * subranges of a hugepage while holding a lock, drop the lock during the actual
+ * purging of them, and reacquire it to update the metadata again.
+ */
+typedef struct hpdata_purge_state_s hpdata_purge_state_t;
+struct hpdata_purge_state_s {
+ size_t npurged;
+ size_t ndirty_to_purge;
+ fb_group_t to_purge[FB_NGROUPS(HUGEPAGE_PAGES)];
+ size_t next_purge_search_begin;
+};
+
+/*
+ * Initializes purge state. The access to hpdata must be externally
+ * synchronized with other hpdata_* calls.
+ *
+ * You can tell whether or not a thread is purging or hugifying a given hpdata
+ * via hpdata_changing_state_get(hpdata). Racing hugification or purging
+ * operations aren't allowed.
+ *
+ * Once you begin purging, you have to follow through and call hpdata_purge_next
+ * until you're done, and then end. Allocating out of an hpdata undergoing
+ * purging is not allowed.
+ *
+ * Returns the number of dirty pages that will be purged.
+ */
+size_t hpdata_purge_begin(hpdata_t *hpdata, hpdata_purge_state_t *purge_state);
+
+/*
+ * If there are more extents to purge, sets *r_purge_addr and *r_purge_size to
+ * true, and returns true. Otherwise, returns false to indicate that we're
+ * done.
+ *
+ * This requires exclusive access to the purge state, but *not* to the hpdata.
+ * In particular, unreserve calls are allowed while purging (i.e. you can dalloc
+ * into one part of the hpdata while purging a different part).
+ */
+bool hpdata_purge_next(hpdata_t *hpdata, hpdata_purge_state_t *purge_state,
+ void **r_purge_addr, size_t *r_purge_size);
+/*
+ * Updates the hpdata metadata after all purging is done. Needs external
+ * synchronization.
+ */
+void hpdata_purge_end(hpdata_t *hpdata, hpdata_purge_state_t *purge_state);
+
+void hpdata_hugify(hpdata_t *hpdata);
+void hpdata_dehugify(hpdata_t *hpdata);
+
+#endif /* JEMALLOC_INTERNAL_HPDATA_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/inspect.h b/contrib/jemalloc/include/jemalloc/internal/inspect.h
new file mode 100644
index 000000000000..65fef51dfa81
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/internal/inspect.h
@@ -0,0 +1,40 @@
+#ifndef JEMALLOC_INTERNAL_INSPECT_H
+#define JEMALLOC_INTERNAL_INSPECT_H
+
+/*
+ * This module contains the heap introspection capabilities. For now they are
+ * exposed purely through mallctl APIs in the experimental namespace, but this
+ * may change over time.
+ */
+
+/*
+ * The following two structs are for experimental purposes. See
+ * experimental_utilization_query_ctl and
+ * experimental_utilization_batch_query_ctl in src/ctl.c.
+ */
+typedef struct inspect_extent_util_stats_s inspect_extent_util_stats_t;
+struct inspect_extent_util_stats_s {
+ size_t nfree;
+ size_t nregs;
+ size_t size;
+};
+
+typedef struct inspect_extent_util_stats_verbose_s
+ inspect_extent_util_stats_verbose_t;
+
+struct inspect_extent_util_stats_verbose_s {
+ void *slabcur_addr;
+ size_t nfree;
+ size_t nregs;
+ size_t size;
+ size_t bin_nfree;
+ size_t bin_nregs;
+};
+
+void inspect_extent_util_stats_get(tsdn_t *tsdn, const void *ptr,
+ size_t *nfree, size_t *nregs, size_t *size);
+void inspect_extent_util_stats_verbose_get(tsdn_t *tsdn, const void *ptr,
+ size_t *nfree, size_t *nregs, size_t *size,
+ size_t *bin_nfree, size_t *bin_nregs, void **slabcur_addr);
+
+#endif /* JEMALLOC_INTERNAL_INSPECT_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/jemalloc_internal_decls.h b/contrib/jemalloc/include/jemalloc/internal/jemalloc_internal_decls.h
index a0e4f5af0124..d7790be0ad16 100644
--- a/contrib/jemalloc/include/jemalloc/internal/jemalloc_internal_decls.h
+++ b/contrib/jemalloc/include/jemalloc/internal/jemalloc_internal_decls.h
@@ -1,13 +1,16 @@
#ifndef JEMALLOC_INTERNAL_DECLS_H
#define JEMALLOC_INTERNAL_DECLS_H
+#ifndef JEMALLOC_NO_PRIVATE_NAMESPACE
#include "libc_private.h"
#include "namespace.h"
+#endif
#include <math.h>
#ifdef _WIN32
# include <windows.h>
# include "msvc_compat/windows_extra.h"
+# include "msvc_compat/strings.h"
# ifdef _WIN64
# if LG_VADDR <= 32
# error Generate the headers using x64 vcargs
@@ -34,8 +37,12 @@
# include <sys/uio.h>
# endif
# include <pthread.h>
-# ifdef __FreeBSD__
+# if defined(__FreeBSD__) || defined(__DragonFly__)
# include <pthread_np.h>
+# include <sched.h>
+# if defined(__FreeBSD__)
+# define cpu_set_t cpuset_t
+# endif
# endif
# include <signal.h>
# ifdef JEMALLOC_OS_UNFAIR_LOCK
@@ -94,4 +101,13 @@ isblank(int c) {
#endif
#include <fcntl.h>
+/*
+ * The Win32 midl compiler has #define small char; we don't use midl, but
+ * "small" is a nice identifier to have available when talking about size
+ * classes.
+ */
+#ifdef small
+# undef small
+#endif
+
#endif /* JEMALLOC_INTERNAL_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/jemalloc_internal_defs.h.in b/contrib/jemalloc/include/jemalloc/internal/jemalloc_internal_defs.h.in
new file mode 100644
index 000000000000..3588072f178c
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/internal/jemalloc_internal_defs.h.in
@@ -0,0 +1,427 @@
+#ifndef JEMALLOC_INTERNAL_DEFS_H_
+#define JEMALLOC_INTERNAL_DEFS_H_
+/*
+ * If JEMALLOC_PREFIX is defined via --with-jemalloc-prefix, it will cause all
+ * public APIs to be prefixed. This makes it possible, with some care, to use
+ * multiple allocators simultaneously.
+ */
+#undef JEMALLOC_PREFIX
+#undef JEMALLOC_CPREFIX
+
+/*
+ * Define overrides for non-standard allocator-related functions if they are
+ * present on the system.
+ */
+#undef JEMALLOC_OVERRIDE___LIBC_CALLOC
+#undef JEMALLOC_OVERRIDE___LIBC_FREE
+#undef JEMALLOC_OVERRIDE___LIBC_MALLOC
+#undef JEMALLOC_OVERRIDE___LIBC_MEMALIGN
+#undef JEMALLOC_OVERRIDE___LIBC_REALLOC
+#undef JEMALLOC_OVERRIDE___LIBC_VALLOC
+#undef JEMALLOC_OVERRIDE___POSIX_MEMALIGN
+
+/*
+ * JEMALLOC_PRIVATE_NAMESPACE is used as a prefix for all library-private APIs.
+ * For shared libraries, symbol visibility mechanisms prevent these symbols
+ * from being exported, but for static libraries, naming collisions are a real
+ * possibility.
+ */
+#undef JEMALLOC_PRIVATE_NAMESPACE
+
+/*
+ * Hyper-threaded CPUs may need a special instruction inside spin loops in
+ * order to yield to another virtual CPU.
+ */
+#undef CPU_SPINWAIT
+/* 1 if CPU_SPINWAIT is defined, 0 otherwise. */
+#undef HAVE_CPU_SPINWAIT
+
+/*
+ * Number of significant bits in virtual addresses. This may be less than the
+ * total number of bits in a pointer, e.g. on x64, for which the uppermost 16
+ * bits are the same as bit 47.
+ */
+#undef LG_VADDR
+
+/* Defined if C11 atomics are available. */
+#undef JEMALLOC_C11_ATOMICS
+
+/* Defined if GCC __atomic atomics are available. */
+#undef JEMALLOC_GCC_ATOMIC_ATOMICS
+/* and the 8-bit variant support. */
+#undef JEMALLOC_GCC_U8_ATOMIC_ATOMICS
+
+/* Defined if GCC __sync atomics are available. */
+#undef JEMALLOC_GCC_SYNC_ATOMICS
+/* and the 8-bit variant support. */
+#undef JEMALLOC_GCC_U8_SYNC_ATOMICS
+
+/*
+ * Defined if __builtin_clz() and __builtin_clzl() are available.
+ */
+#undef JEMALLOC_HAVE_BUILTIN_CLZ
+
+/*
+ * Defined if os_unfair_lock_*() functions are available, as provided by Darwin.
+ */
+#undef JEMALLOC_OS_UNFAIR_LOCK
+
+/* Defined if syscall(2) is usable. */
+#undef JEMALLOC_USE_SYSCALL
+
+/*
+ * Defined if secure_getenv(3) is available.
+ */
+#undef JEMALLOC_HAVE_SECURE_GETENV
+
+/*
+ * Defined if issetugid(2) is available.
+ */
+#undef JEMALLOC_HAVE_ISSETUGID
+
+/* Defined if pthread_atfork(3) is available. */
+#undef JEMALLOC_HAVE_PTHREAD_ATFORK
+
+/* Defined if pthread_setname_np(3) is available. */
+#undef JEMALLOC_HAVE_PTHREAD_SETNAME_NP
+
+/* Defined if pthread_getname_np(3) is available. */
+#undef JEMALLOC_HAVE_PTHREAD_GETNAME_NP
+
+/* Defined if pthread_get_name_np(3) is available. */
+#undef JEMALLOC_HAVE_PTHREAD_GET_NAME_NP
+
+/*
+ * Defined if clock_gettime(CLOCK_MONOTONIC_COARSE, ...) is available.
+ */
+#undef JEMALLOC_HAVE_CLOCK_MONOTONIC_COARSE
+
+/*
+ * Defined if clock_gettime(CLOCK_MONOTONIC, ...) is available.
+ */
+#undef JEMALLOC_HAVE_CLOCK_MONOTONIC
+
+/*
+ * Defined if mach_absolute_time() is available.
+ */
+#undef JEMALLOC_HAVE_MACH_ABSOLUTE_TIME
+
+/*
+ * Defined if clock_gettime(CLOCK_REALTIME, ...) is available.
+ */
+#undef JEMALLOC_HAVE_CLOCK_REALTIME
+
+/*
+ * Defined if _malloc_thread_cleanup() exists. At least in the case of
+ * FreeBSD, pthread_key_create() allocates, which if used during malloc
+ * bootstrapping will cause recursion into the pthreads library. Therefore, if
+ * _malloc_thread_cleanup() exists, use it as the basis for thread cleanup in
+ * malloc_tsd.
+ */
+#undef JEMALLOC_MALLOC_THREAD_CLEANUP
+
+/*
+ * Defined if threaded initialization is known to be safe on this platform.
+ * Among other things, it must be possible to initialize a mutex without
+ * triggering allocation in order for threaded allocation to be safe.
+ */
+#undef JEMALLOC_THREADED_INIT
+
+/*
+ * Defined if the pthreads implementation defines
+ * _pthread_mutex_init_calloc_cb(), in which case the function is used in order
+ * to avoid recursive allocation during mutex initialization.
+ */
+#undef JEMALLOC_MUTEX_INIT_CB
+
+/* Non-empty if the tls_model attribute is supported. */
+#undef JEMALLOC_TLS_MODEL
+
+/*
+ * JEMALLOC_DEBUG enables assertions and other sanity checks, and disables
+ * inline functions.
+ */
+#undef JEMALLOC_DEBUG
+
+/* JEMALLOC_STATS enables statistics calculation. */
+#undef JEMALLOC_STATS
+
+/* JEMALLOC_EXPERIMENTAL_SMALLOCX_API enables experimental smallocx API. */
+#undef JEMALLOC_EXPERIMENTAL_SMALLOCX_API
+
+/* JEMALLOC_PROF enables allocation profiling. */
+#undef JEMALLOC_PROF
+
+/* Use libunwind for profile backtracing if defined. */
+#undef JEMALLOC_PROF_LIBUNWIND
+
+/* Use libgcc for profile backtracing if defined. */
+#undef JEMALLOC_PROF_LIBGCC
+
+/* Use gcc intrinsics for profile backtracing if defined. */
+#undef JEMALLOC_PROF_GCC
+
+/*
+ * JEMALLOC_DSS enables use of sbrk(2) to allocate extents from the data storage
+ * segment (DSS).
+ */
+#undef JEMALLOC_DSS
+
+/* Support memory filling (junk/zero). */
+#undef JEMALLOC_FILL
+
+/* Support utrace(2)-based tracing. */
+#undef JEMALLOC_UTRACE
+
+/* Support utrace(2)-based tracing (label based signature). */
+#undef JEMALLOC_UTRACE_LABEL
+
+/* Support optional abort() on OOM. */
+#undef JEMALLOC_XMALLOC
+
+/* Support lazy locking (avoid locking unless a second thread is launched). */
+#undef JEMALLOC_LAZY_LOCK
+
+/*
+ * Minimum allocation alignment is 2^LG_QUANTUM bytes (ignoring tiny size
+ * classes).
+ */
+#undef LG_QUANTUM
+
+/* One page is 2^LG_PAGE bytes. */
+#undef LG_PAGE
+
+/* Maximum number of regions in a slab. */
+#undef CONFIG_LG_SLAB_MAXREGS
+
+/*
+ * One huge page is 2^LG_HUGEPAGE bytes. Note that this is defined even if the
+ * system does not explicitly support huge pages; system calls that require
+ * explicit huge page support are separately configured.
+ */
+#undef LG_HUGEPAGE
+
+/*
+ * If defined, adjacent virtual memory mappings with identical attributes
+ * automatically coalesce, and they fragment when changes are made to subranges.
+ * This is the normal order of things for mmap()/munmap(), but on Windows
+ * VirtualAlloc()/VirtualFree() operations must be precisely matched, i.e.
+ * mappings do *not* coalesce/fragment.
+ */
+#undef JEMALLOC_MAPS_COALESCE
+
+/*
+ * If defined, retain memory for later reuse by default rather than using e.g.
+ * munmap() to unmap freed extents. This is enabled on 64-bit Linux because
+ * common sequences of mmap()/munmap() calls will cause virtual memory map
+ * holes.
+ */
+#undef JEMALLOC_RETAIN
+
+/* TLS is used to map arenas and magazine caches to threads. */
+#undef JEMALLOC_TLS
+
+/*
+ * Used to mark unreachable code to quiet "end of non-void" compiler warnings.
+ * Don't use this directly; instead use unreachable() from util.h
+ */
+#undef JEMALLOC_INTERNAL_UNREACHABLE
+
+/*
+ * ffs*() functions to use for bitmapping. Don't use these directly; instead,
+ * use ffs_*() from util.h.
+ */
+#undef JEMALLOC_INTERNAL_FFSLL
+#undef JEMALLOC_INTERNAL_FFSL
+#undef JEMALLOC_INTERNAL_FFS
+
+/*
+ * popcount*() functions to use for bitmapping.
+ */
+#undef JEMALLOC_INTERNAL_POPCOUNTL
+#undef JEMALLOC_INTERNAL_POPCOUNT
+
+/*
+ * If defined, explicitly attempt to more uniformly distribute large allocation
+ * pointer alignments across all cache indices.
+ */
+#undef JEMALLOC_CACHE_OBLIVIOUS
+
+/*
+ * If defined, enable logging facilities. We make this a configure option to
+ * avoid taking extra branches everywhere.
+ */
+#undef JEMALLOC_LOG
+
+/*
+ * If defined, use readlinkat() (instead of readlink()) to follow
+ * /etc/malloc_conf.
+ */
+#undef JEMALLOC_READLINKAT
+
+/*
+ * Darwin (OS X) uses zones to work around Mach-O symbol override shortcomings.
+ */
+#undef JEMALLOC_ZONE
+
+/*
+ * Methods for determining whether the OS overcommits.
+ * JEMALLOC_PROC_SYS_VM_OVERCOMMIT_MEMORY: Linux's
+ * /proc/sys/vm.overcommit_memory file.
+ * JEMALLOC_SYSCTL_VM_OVERCOMMIT: FreeBSD's vm.overcommit sysctl.
+ */
+#undef JEMALLOC_SYSCTL_VM_OVERCOMMIT
+#undef JEMALLOC_PROC_SYS_VM_OVERCOMMIT_MEMORY
+
+/* Defined if madvise(2) is available. */
+#undef JEMALLOC_HAVE_MADVISE
+
+/*
+ * Defined if transparent huge pages are supported via the MADV_[NO]HUGEPAGE
+ * arguments to madvise(2).
+ */
+#undef JEMALLOC_HAVE_MADVISE_HUGE
+
+/*
+ * Methods for purging unused pages differ between operating systems.
+ *
+ * madvise(..., MADV_FREE) : This marks pages as being unused, such that they
+ * will be discarded rather than swapped out.
+ * madvise(..., MADV_DONTNEED) : If JEMALLOC_PURGE_MADVISE_DONTNEED_ZEROS is
+ * defined, this immediately discards pages,
+ * such that new pages will be demand-zeroed if
+ * the address region is later touched;
+ * otherwise this behaves similarly to
+ * MADV_FREE, though typically with higher
+ * system overhead.
+ */
+#undef JEMALLOC_PURGE_MADVISE_FREE
+#undef JEMALLOC_PURGE_MADVISE_DONTNEED
+#undef JEMALLOC_PURGE_MADVISE_DONTNEED_ZEROS
+
+/* Defined if madvise(2) is available but MADV_FREE is not (x86 Linux only). */
+#undef JEMALLOC_DEFINE_MADVISE_FREE
+
+/*
+ * Defined if MADV_DO[NT]DUMP is supported as an argument to madvise.
+ */
+#undef JEMALLOC_MADVISE_DONTDUMP
+
+/*
+ * Defined if MADV_[NO]CORE is supported as an argument to madvise.
+ */
+#undef JEMALLOC_MADVISE_NOCORE
+
+/* Defined if mprotect(2) is available. */
+#undef JEMALLOC_HAVE_MPROTECT
+
+/*
+ * Defined if transparent huge pages (THPs) are supported via the
+ * MADV_[NO]HUGEPAGE arguments to madvise(2), and THP support is enabled.
+ */
+#undef JEMALLOC_THP
+
+/* Defined if posix_madvise is available. */
+#undef JEMALLOC_HAVE_POSIX_MADVISE
+
+/*
+ * Method for purging unused pages using posix_madvise.
+ *
+ * posix_madvise(..., POSIX_MADV_DONTNEED)
+ */
+#undef JEMALLOC_PURGE_POSIX_MADVISE_DONTNEED
+#undef JEMALLOC_PURGE_POSIX_MADVISE_DONTNEED_ZEROS
+
+/*
+ * Defined if memcntl page admin call is supported
+ */
+#undef JEMALLOC_HAVE_MEMCNTL
+
+/*
+ * Defined if malloc_size is supported
+ */
+#undef JEMALLOC_HAVE_MALLOC_SIZE
+
+/* Define if operating system has alloca.h header. */
+#undef JEMALLOC_HAS_ALLOCA_H
+
+/* C99 restrict keyword supported. */
+#undef JEMALLOC_HAS_RESTRICT
+
+/* For use by hash code. */
+#undef JEMALLOC_BIG_ENDIAN
+
+/* sizeof(int) == 2^LG_SIZEOF_INT. */
+#undef LG_SIZEOF_INT
+
+/* sizeof(long) == 2^LG_SIZEOF_LONG. */
+#undef LG_SIZEOF_LONG
+
+/* sizeof(long long) == 2^LG_SIZEOF_LONG_LONG. */
+#undef LG_SIZEOF_LONG_LONG
+
+/* sizeof(intmax_t) == 2^LG_SIZEOF_INTMAX_T. */
+#undef LG_SIZEOF_INTMAX_T
+
+/* glibc malloc hooks (__malloc_hook, __realloc_hook, __free_hook). */
+#undef JEMALLOC_GLIBC_MALLOC_HOOK
+
+/* glibc memalign hook. */
+#undef JEMALLOC_GLIBC_MEMALIGN_HOOK
+
+/* pthread support */
+#undef JEMALLOC_HAVE_PTHREAD
+
+/* dlsym() support */
+#undef JEMALLOC_HAVE_DLSYM
+
+/* Adaptive mutex support in pthreads. */
+#undef JEMALLOC_HAVE_PTHREAD_MUTEX_ADAPTIVE_NP
+
+/* GNU specific sched_getcpu support */
+#undef JEMALLOC_HAVE_SCHED_GETCPU
+
+/* GNU specific sched_setaffinity support */
+#undef JEMALLOC_HAVE_SCHED_SETAFFINITY
+
+/*
+ * If defined, all the features necessary for background threads are present.
+ */
+#undef JEMALLOC_BACKGROUND_THREAD
+
+/*
+ * If defined, jemalloc symbols are not exported (doesn't work when
+ * JEMALLOC_PREFIX is not defined).
+ */
+#undef JEMALLOC_EXPORT
+
+/* config.malloc_conf options string. */
+#undef JEMALLOC_CONFIG_MALLOC_CONF
+
+/* If defined, jemalloc takes the malloc/free/etc. symbol names. */
+#undef JEMALLOC_IS_MALLOC
+
+/*
+ * Defined if strerror_r returns char * if _GNU_SOURCE is defined.
+ */
+#undef JEMALLOC_STRERROR_R_RETURNS_CHAR_WITH_GNU_SOURCE
+
+/* Performs additional safety checks when defined. */
+#undef JEMALLOC_OPT_SAFETY_CHECKS
+
+/* Is C++ support being built? */
+#undef JEMALLOC_ENABLE_CXX
+
+/* Performs additional size checks when defined. */
+#undef JEMALLOC_OPT_SIZE_CHECKS
+
+/* Allows sampled junk and stash for checking use-after-free when defined. */
+#undef JEMALLOC_UAF_DETECTION
+
+/* Darwin VM_MAKE_TAG support */
+#undef JEMALLOC_HAVE_VM_MAKE_TAG
+
+/* If defined, realloc(ptr, 0) defaults to "free" instead of "alloc". */
+#undef JEMALLOC_ZERO_REALLOC_DEFAULT_FREE
+
+#endif /* JEMALLOC_INTERNAL_DEFS_H_ */
diff --git a/contrib/jemalloc/include/jemalloc/internal/jemalloc_internal_externs.h b/contrib/jemalloc/include/jemalloc/internal/jemalloc_internal_externs.h
index d291170beefa..fc834c67373d 100644
--- a/contrib/jemalloc/include/jemalloc/internal/jemalloc_internal_externs.h
+++ b/contrib/jemalloc/include/jemalloc/internal/jemalloc_internal_externs.h
@@ -2,7 +2,10 @@
#define JEMALLOC_INTERNAL_EXTERNS_H
#include "jemalloc/internal/atomic.h"
+#include "jemalloc/internal/hpa_opts.h"
+#include "jemalloc/internal/sec_opts.h"
#include "jemalloc/internal/tsd_types.h"
+#include "jemalloc/internal/nstime.h"
/* TSD checks this to set thread local slow state accordingly. */
extern bool malloc_slow;
@@ -10,14 +13,30 @@ extern bool malloc_slow;
/* Run-time options. */
extern bool opt_abort;
extern bool opt_abort_conf;
+extern bool opt_trust_madvise;
extern bool opt_confirm_conf;
+extern bool opt_hpa;
+extern hpa_shard_opts_t opt_hpa_opts;
+extern sec_opts_t opt_hpa_sec_opts;
+
extern const char *opt_junk;
extern bool opt_junk_alloc;
extern bool opt_junk_free;
+extern void (*junk_free_callback)(void *ptr, size_t size);
+extern void (*junk_alloc_callback)(void *ptr, size_t size);
extern bool opt_utrace;
extern bool opt_xmalloc;
+extern bool opt_experimental_infallible_new;
extern bool opt_zero;
extern unsigned opt_narenas;
+extern zero_realloc_action_t opt_zero_realloc_action;
+extern malloc_init_t malloc_init_state;
+extern const char *zero_realloc_mode_names[];
+extern atomic_zu_t zero_realloc_count;
+extern bool opt_cache_oblivious;
+
+/* Escape free-fastpath when ptr & mask == 0 (for sanitization purpose). */
+extern uintptr_t san_cache_bin_nonfast_mask;
/* Number of CPUs. */
extern unsigned ncpus;
@@ -41,17 +60,16 @@ void *bootstrap_calloc(size_t num, size_t size);
void bootstrap_free(void *ptr);
void arena_set(unsigned ind, arena_t *arena);
unsigned narenas_total_get(void);
-arena_t *arena_init(tsdn_t *tsdn, unsigned ind, extent_hooks_t *extent_hooks);
-arena_tdata_t *arena_tdata_get_hard(tsd_t *tsd, unsigned ind);
+arena_t *arena_init(tsdn_t *tsdn, unsigned ind, const arena_config_t *config);
arena_t *arena_choose_hard(tsd_t *tsd, bool internal);
-void arena_migrate(tsd_t *tsd, unsigned oldind, unsigned newind);
+void arena_migrate(tsd_t *tsd, arena_t *oldarena, arena_t *newarena);
void iarena_cleanup(tsd_t *tsd);
void arena_cleanup(tsd_t *tsd);
-void arenas_tdata_cleanup(tsd_t *tsd);
+size_t batch_alloc(void **ptrs, size_t num, size_t size, int flags);
void jemalloc_prefork(void);
void jemalloc_postfork_parent(void);
void jemalloc_postfork_child(void);
-bool malloc_initialized(void);
void je_sdallocx_noflags(void *ptr, size_t size);
+void *malloc_default(size_t size);
#endif /* JEMALLOC_INTERNAL_EXTERNS_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/jemalloc_internal_includes.h b/contrib/jemalloc/include/jemalloc/internal/jemalloc_internal_includes.h
index 437eaa407939..751c112ff4c0 100644
--- a/contrib/jemalloc/include/jemalloc/internal/jemalloc_internal_includes.h
+++ b/contrib/jemalloc/include/jemalloc/internal/jemalloc_internal_includes.h
@@ -10,7 +10,7 @@
* structs, externs, and inlines), and included each header file multiple times
* in this file, picking out the portion we want on each pass using the
* following #defines:
- * JEMALLOC_H_TYPES : Preprocessor-defined constants and psuedo-opaque data
+ * JEMALLOC_H_TYPES : Preprocessor-defined constants and pseudo-opaque data
* types.
* JEMALLOC_H_STRUCTS : Data structures.
* JEMALLOC_H_EXTERNS : Extern data declarations and function prototypes.
@@ -40,8 +40,6 @@
/* TYPES */
/******************************************************************************/
-#include "jemalloc/internal/extent_types.h"
-#include "jemalloc/internal/base_types.h"
#include "jemalloc/internal/arena_types.h"
#include "jemalloc/internal/tcache_types.h"
#include "jemalloc/internal/prof_types.h"
@@ -50,11 +48,8 @@
/* STRUCTS */
/******************************************************************************/
-#include "jemalloc/internal/arena_structs_a.h"
-#include "jemalloc/internal/extent_structs.h"
-#include "jemalloc/internal/base_structs.h"
#include "jemalloc/internal/prof_structs.h"
-#include "jemalloc/internal/arena_structs_b.h"
+#include "jemalloc/internal/arena_structs.h"
#include "jemalloc/internal/tcache_structs.h"
#include "jemalloc/internal/background_thread_structs.h"
@@ -63,8 +58,6 @@
/******************************************************************************/
#include "jemalloc/internal/jemalloc_internal_externs.h"
-#include "jemalloc/internal/extent_externs.h"
-#include "jemalloc/internal/base_externs.h"
#include "jemalloc/internal/arena_externs.h"
#include "jemalloc/internal/large_externs.h"
#include "jemalloc/internal/tcache_externs.h"
@@ -76,19 +69,16 @@
/******************************************************************************/
#include "jemalloc/internal/jemalloc_internal_inlines_a.h"
-#include "jemalloc/internal/base_inlines.h"
/*
* Include portions of arena code interleaved with tcache code in order to
* resolve circular dependencies.
*/
-#include "jemalloc/internal/prof_inlines_a.h"
#include "jemalloc/internal/arena_inlines_a.h"
-#include "jemalloc/internal/extent_inlines.h"
#include "jemalloc/internal/jemalloc_internal_inlines_b.h"
#include "jemalloc/internal/tcache_inlines.h"
#include "jemalloc/internal/arena_inlines_b.h"
#include "jemalloc/internal/jemalloc_internal_inlines_c.h"
-#include "jemalloc/internal/prof_inlines_b.h"
+#include "jemalloc/internal/prof_inlines.h"
#include "jemalloc/internal/background_thread_inlines.h"
#endif /* JEMALLOC_INTERNAL_INCLUDES_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/jemalloc_internal_inlines_a.h b/contrib/jemalloc/include/jemalloc/internal/jemalloc_internal_inlines_a.h
index ddde9b4e63e6..9e27cc3012fd 100644
--- a/contrib/jemalloc/include/jemalloc/internal/jemalloc_internal_inlines_a.h
+++ b/contrib/jemalloc/include/jemalloc/internal/jemalloc_internal_inlines_a.h
@@ -56,31 +56,6 @@ percpu_arena_ind_limit(percpu_arena_mode_t mode) {
}
}
-static inline arena_tdata_t *
-arena_tdata_get(tsd_t *tsd, unsigned ind, bool refresh_if_missing) {
- arena_tdata_t *tdata;
- arena_tdata_t *arenas_tdata = tsd_arenas_tdata_get(tsd);
-
- if (unlikely(arenas_tdata == NULL)) {
- /* arenas_tdata hasn't been initialized yet. */
- return arena_tdata_get_hard(tsd, ind);
- }
- if (unlikely(ind >= tsd_narenas_tdata_get(tsd))) {
- /*
- * ind is invalid, cache is old (too small), or tdata to be
- * initialized.
- */
- return (refresh_if_missing ? arena_tdata_get_hard(tsd, ind) :
- NULL);
- }
-
- tdata = &arenas_tdata[ind];
- if (likely(tdata != NULL) || !refresh_if_missing) {
- return tdata;
- }
- return arena_tdata_get_hard(tsd, ind);
-}
-
static inline arena_t *
arena_get(tsdn_t *tsdn, unsigned ind, bool init_if_missing) {
arena_t *ret;
@@ -90,36 +65,12 @@ arena_get(tsdn_t *tsdn, unsigned ind, bool init_if_missing) {
ret = (arena_t *)atomic_load_p(&arenas[ind], ATOMIC_ACQUIRE);
if (unlikely(ret == NULL)) {
if (init_if_missing) {
- ret = arena_init(tsdn, ind,
- (extent_hooks_t *)&extent_hooks_default);
+ ret = arena_init(tsdn, ind, &arena_config_default);
}
}
return ret;
}
-static inline ticker_t *
-decay_ticker_get(tsd_t *tsd, unsigned ind) {
- arena_tdata_t *tdata;
-
- tdata = arena_tdata_get(tsd, ind, true);
- if (unlikely(tdata == NULL)) {
- return NULL;
- }
- return &tdata->decay_ticker;
-}
-
-JEMALLOC_ALWAYS_INLINE cache_bin_t *
-tcache_small_bin_get(tcache_t *tcache, szind_t binind) {
- assert(binind < SC_NBINS);
- return &tcache->bins_small[binind];
-}
-
-JEMALLOC_ALWAYS_INLINE cache_bin_t *
-tcache_large_bin_get(tcache_t *tcache, szind_t binind) {
- assert(binind >= SC_NBINS &&binind < nhbins);
- return &tcache->bins_large[binind - SC_NBINS];
-}
-
JEMALLOC_ALWAYS_INLINE bool
tcache_available(tsd_t *tsd) {
/*
@@ -129,9 +80,9 @@ tcache_available(tsd_t *tsd) {
*/
if (likely(tsd_tcache_enabled_get(tsd))) {
/* Associated arena == NULL implies tcache init in progress. */
- assert(tsd_tcachep_get(tsd)->arena == NULL ||
- tcache_small_bin_get(tsd_tcachep_get(tsd), 0)->avail !=
- NULL);
+ if (config_debug && tsd_tcache_slowp_get(tsd)->arena != NULL) {
+ tcache_assert_initialized(tsd_tcachep_get(tsd));
+ }
return true;
}
@@ -147,28 +98,25 @@ tcache_get(tsd_t *tsd) {
return tsd_tcachep_get(tsd);
}
+JEMALLOC_ALWAYS_INLINE tcache_slow_t *
+tcache_slow_get(tsd_t *tsd) {
+ if (!tcache_available(tsd)) {
+ return NULL;
+ }
+
+ return tsd_tcache_slowp_get(tsd);
+}
+
static inline void
pre_reentrancy(tsd_t *tsd, arena_t *arena) {
/* arena is the current context. Reentry from a0 is not allowed. */
assert(arena != arena_get(tsd_tsdn(tsd), 0, false));
-
- bool fast = tsd_fast(tsd);
- assert(tsd_reentrancy_level_get(tsd) < INT8_MAX);
- ++*tsd_reentrancy_levelp_get(tsd);
- if (fast) {
- /* Prepare slow path for reentrancy. */
- tsd_slow_update(tsd);
- assert(tsd_state_get(tsd) == tsd_state_nominal_slow);
- }
+ tsd_pre_reentrancy_raw(tsd);
}
static inline void
post_reentrancy(tsd_t *tsd) {
- int8_t *reentrancy_level = tsd_reentrancy_levelp_get(tsd);
- assert(*reentrancy_level > 0);
- if (--*reentrancy_level == 0) {
- tsd_slow_update(tsd);
- }
+ tsd_post_reentrancy_raw(tsd);
}
#endif /* JEMALLOC_INTERNAL_INLINES_A_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/jemalloc_internal_inlines_b.h b/contrib/jemalloc/include/jemalloc/internal/jemalloc_internal_inlines_b.h
index 70d6e5788570..152f8a039569 100644
--- a/contrib/jemalloc/include/jemalloc/internal/jemalloc_internal_inlines_b.h
+++ b/contrib/jemalloc/include/jemalloc/internal/jemalloc_internal_inlines_b.h
@@ -1,7 +1,31 @@
#ifndef JEMALLOC_INTERNAL_INLINES_B_H
#define JEMALLOC_INTERNAL_INLINES_B_H
-#include "jemalloc/internal/rtree.h"
+#include "jemalloc/internal/extent.h"
+
+static inline void
+percpu_arena_update(tsd_t *tsd, unsigned cpu) {
+ assert(have_percpu_arena);
+ arena_t *oldarena = tsd_arena_get(tsd);
+ assert(oldarena != NULL);
+ unsigned oldind = arena_ind_get(oldarena);
+
+ if (oldind != cpu) {
+ unsigned newind = cpu;
+ arena_t *newarena = arena_get(tsd_tsdn(tsd), newind, true);
+ assert(newarena != NULL);
+
+ /* Set new arena/tcache associations. */
+ arena_migrate(tsd, oldarena, newarena);
+ tcache_t *tcache = tcache_get(tsd);
+ if (tcache != NULL) {
+ tcache_slow_t *tcache_slow = tsd_tcache_slowp_get(tsd);
+ tcache_arena_reassociate(tsd_tsdn(tsd), tcache_slow,
+ tcache, newarena);
+ }
+ }
+}
+
/* Choose an arena based on a per-thread value. */
static inline arena_t *
@@ -22,18 +46,19 @@ arena_choose_impl(tsd_t *tsd, arena_t *arena, bool internal) {
ret = arena_choose_hard(tsd, internal);
assert(ret);
if (tcache_available(tsd)) {
- tcache_t *tcache = tcache_get(tsd);
- if (tcache->arena != NULL) {
- /* See comments in tcache_data_init().*/
- assert(tcache->arena ==
+ tcache_slow_t *tcache_slow = tsd_tcache_slowp_get(tsd);
+ tcache_t *tcache = tsd_tcachep_get(tsd);
+ if (tcache_slow->arena != NULL) {
+ /* See comments in tsd_tcache_data_init().*/
+ assert(tcache_slow->arena ==
arena_get(tsd_tsdn(tsd), 0, false));
- if (tcache->arena != ret) {
+ if (tcache_slow->arena != ret) {
tcache_arena_reassociate(tsd_tsdn(tsd),
- tcache, ret);
+ tcache_slow, tcache, ret);
}
} else {
- tcache_arena_associate(tsd_tsdn(tsd), tcache,
- ret);
+ tcache_arena_associate(tsd_tsdn(tsd),
+ tcache_slow, tcache, ret);
}
}
}
@@ -75,13 +100,4 @@ arena_is_auto(arena_t *arena) {
return (arena_ind_get(arena) < manual_arena_base);
}
-JEMALLOC_ALWAYS_INLINE extent_t *
-iealloc(tsdn_t *tsdn, const void *ptr) {
- rtree_ctx_t rtree_ctx_fallback;
- rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);
-
- return rtree_extent_read(tsdn, &extents_rtree, rtree_ctx,
- (uintptr_t)ptr, true);
-}
-
#endif /* JEMALLOC_INTERNAL_INLINES_B_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/jemalloc_internal_inlines_c.h b/contrib/jemalloc/include/jemalloc/internal/jemalloc_internal_inlines_c.h
index cdb10eb21f73..b0868b7d616b 100644
--- a/contrib/jemalloc/include/jemalloc/internal/jemalloc_internal_inlines_c.h
+++ b/contrib/jemalloc/include/jemalloc/internal/jemalloc_internal_inlines_c.h
@@ -3,7 +3,9 @@
#include "jemalloc/internal/hook.h"
#include "jemalloc/internal/jemalloc_internal_types.h"
+#include "jemalloc/internal/log.h"
#include "jemalloc/internal/sz.h"
+#include "jemalloc/internal/thread_event.h"
#include "jemalloc/internal/witness.h"
/*
@@ -101,8 +103,8 @@ ivsalloc(tsdn_t *tsdn, const void *ptr) {
}
JEMALLOC_ALWAYS_INLINE void
-idalloctm(tsdn_t *tsdn, void *ptr, tcache_t *tcache, alloc_ctx_t *alloc_ctx,
- bool is_internal, bool slow_path) {
+idalloctm(tsdn_t *tsdn, void *ptr, tcache_t *tcache,
+ emap_alloc_ctx_t *alloc_ctx, bool is_internal, bool slow_path) {
assert(ptr != NULL);
assert(!is_internal || tcache == NULL);
assert(!is_internal || arena_is_auto(iaalloc(tsdn, ptr)));
@@ -125,7 +127,7 @@ idalloc(tsd_t *tsd, void *ptr) {
JEMALLOC_ALWAYS_INLINE void
isdalloct(tsdn_t *tsdn, void *ptr, size_t size, tcache_t *tcache,
- alloc_ctx_t *alloc_ctx, bool slow_path) {
+ emap_alloc_ctx_t *alloc_ctx, bool slow_path) {
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
WITNESS_RANK_CORE, 0);
arena_sdalloc(tsdn, ptr, size, tcache, alloc_ctx, slow_path);
@@ -219,4 +221,120 @@ ixalloc(tsdn_t *tsdn, void *ptr, size_t oldsize, size_t size, size_t extra,
newsize);
}
+JEMALLOC_ALWAYS_INLINE void
+fastpath_success_finish(tsd_t *tsd, uint64_t allocated_after,
+ cache_bin_t *bin, void *ret) {
+ thread_allocated_set(tsd, allocated_after);
+ if (config_stats) {
+ bin->tstats.nrequests++;
+ }
+
+ LOG("core.malloc.exit", "result: %p", ret);
+}
+
+JEMALLOC_ALWAYS_INLINE bool
+malloc_initialized(void) {
+ return (malloc_init_state == malloc_init_initialized);
+}
+
+/*
+ * malloc() fastpath. Included here so that we can inline it into operator new;
+ * function call overhead there is non-negligible as a fraction of total CPU in
+ * allocation-heavy C++ programs. We take the fallback alloc to allow malloc
+ * (which can return NULL) to differ in its behavior from operator new (which
+ * can't). It matches the signature of malloc / operator new so that we can
+ * tail-call the fallback allocator, allowing us to avoid setting up the call
+ * frame in the common case.
+ *
+ * Fastpath assumes size <= SC_LOOKUP_MAXCLASS, and that we hit
+ * tcache. If either of these is false, we tail-call to the slowpath,
+ * malloc_default(). Tail-calling is used to avoid any caller-saved
+ * registers.
+ *
+ * fastpath supports ticker and profiling, both of which will also
+ * tail-call to the slowpath if they fire.
+ */
+JEMALLOC_ALWAYS_INLINE void *
+imalloc_fastpath(size_t size, void *(fallback_alloc)(size_t)) {
+ LOG("core.malloc.entry", "size: %zu", size);
+ if (tsd_get_allocates() && unlikely(!malloc_initialized())) {
+ return fallback_alloc(size);
+ }
+
+ tsd_t *tsd = tsd_get(false);
+ if (unlikely((size > SC_LOOKUP_MAXCLASS) || tsd == NULL)) {
+ return fallback_alloc(size);
+ }
+ /*
+ * The code below till the branch checking the next_event threshold may
+ * execute before malloc_init(), in which case the threshold is 0 to
+ * trigger slow path and initialization.
+ *
+ * Note that when uninitialized, only the fast-path variants of the sz /
+ * tsd facilities may be called.
+ */
+ szind_t ind;
+ /*
+ * The thread_allocated counter in tsd serves as a general purpose
+ * accumulator for bytes of allocation to trigger different types of
+ * events. usize is always needed to advance thread_allocated, though
+ * it's not always needed in the core allocation logic.
+ */
+ size_t usize;
+ sz_size2index_usize_fastpath(size, &ind, &usize);
+ /* Fast path relies on size being a bin. */
+ assert(ind < SC_NBINS);
+ assert((SC_LOOKUP_MAXCLASS < SC_SMALL_MAXCLASS) &&
+ (size <= SC_SMALL_MAXCLASS));
+
+ uint64_t allocated, threshold;
+ te_malloc_fastpath_ctx(tsd, &allocated, &threshold);
+ uint64_t allocated_after = allocated + usize;
+ /*
+ * The ind and usize might be uninitialized (or partially) before
+ * malloc_init(). The assertions check for: 1) full correctness (usize
+ * & ind) when initialized; and 2) guaranteed slow-path (threshold == 0)
+ * when !initialized.
+ */
+ if (!malloc_initialized()) {
+ assert(threshold == 0);
+ } else {
+ assert(ind == sz_size2index(size));
+ assert(usize > 0 && usize == sz_index2size(ind));
+ }
+ /*
+ * Check for events and tsd non-nominal (fast_threshold will be set to
+ * 0) in a single branch.
+ */
+ if (unlikely(allocated_after >= threshold)) {
+ return fallback_alloc(size);
+ }
+ assert(tsd_fast(tsd));
+
+ tcache_t *tcache = tsd_tcachep_get(tsd);
+ assert(tcache == tcache_get(tsd));
+ cache_bin_t *bin = &tcache->bins[ind];
+ bool tcache_success;
+ void *ret;
+
+ /*
+ * We split up the code this way so that redundant low-water
+ * computation doesn't happen on the (more common) case in which we
+ * don't touch the low water mark. The compiler won't do this
+ * duplication on its own.
+ */
+ ret = cache_bin_alloc_easy(bin, &tcache_success);
+ if (tcache_success) {
+ fastpath_success_finish(tsd, allocated_after, bin, ret);
+ return ret;
+ }
+ ret = cache_bin_alloc(bin, &tcache_success);
+ if (tcache_success) {
+ fastpath_success_finish(tsd, allocated_after, bin, ret);
+ return ret;
+ }
+
+ return fallback_alloc(size);
+}
+
#endif /* JEMALLOC_INTERNAL_INLINES_C_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/jemalloc_internal_macros.h b/contrib/jemalloc/include/jemalloc/internal/jemalloc_internal_macros.h
index d8ea06f6d069..e97b5f90730c 100644
--- a/contrib/jemalloc/include/jemalloc/internal/jemalloc_internal_macros.h
+++ b/contrib/jemalloc/include/jemalloc/internal/jemalloc_internal_macros.h
@@ -4,7 +4,11 @@
#ifdef JEMALLOC_DEBUG
# define JEMALLOC_ALWAYS_INLINE static inline
#else
-# define JEMALLOC_ALWAYS_INLINE JEMALLOC_ATTR(always_inline) static inline
+# ifdef _MSC_VER
+# define JEMALLOC_ALWAYS_INLINE static __forceinline
+# else
+# define JEMALLOC_ALWAYS_INLINE JEMALLOC_ATTR(always_inline) static inline
+# endif
#endif
#ifdef _MSC_VER
# define inline _inline
@@ -40,13 +44,6 @@
#define JEMALLOC_VA_ARGS_HEAD(head, ...) head
#define JEMALLOC_VA_ARGS_TAIL(head, ...) __VA_ARGS__
-#if (defined(__GNUC__) || defined(__GNUG__)) && !defined(__clang__) \
- && defined(JEMALLOC_HAVE_ATTR) && (__GNUC__ >= 7)
-#define JEMALLOC_FALLTHROUGH JEMALLOC_ATTR(fallthrough);
-#else
-#define JEMALLOC_FALLTHROUGH /* falls through */
-#endif
-
/* Diagnostic suppression macros */
#if defined(_MSC_VER) && !defined(__clang__)
# define JEMALLOC_DIAGNOSTIC_PUSH __pragma(warning(push))
diff --git a/contrib/jemalloc/include/jemalloc/internal/jemalloc_internal_types.h b/contrib/jemalloc/include/jemalloc/internal/jemalloc_internal_types.h
index e296c5a7e847..62c2b59c71c2 100644
--- a/contrib/jemalloc/include/jemalloc/internal/jemalloc_internal_types.h
+++ b/contrib/jemalloc/include/jemalloc/internal/jemalloc_internal_types.h
@@ -3,15 +3,31 @@
#include "jemalloc/internal/quantum.h"
-/* Page size index type. */
-typedef unsigned pszind_t;
-
-/* Size class index type. */
-typedef unsigned szind_t;
-
/* Processor / core id type. */
typedef int malloc_cpuid_t;
+/* When realloc(non-null-ptr, 0) is called, what happens? */
+enum zero_realloc_action_e {
+ /* Realloc(ptr, 0) is free(ptr); return malloc(0); */
+ zero_realloc_action_alloc = 0,
+ /* Realloc(ptr, 0) is free(ptr); */
+ zero_realloc_action_free = 1,
+ /* Realloc(ptr, 0) aborts. */
+ zero_realloc_action_abort = 2
+};
+typedef enum zero_realloc_action_e zero_realloc_action_t;
+
+/* Signature of write callback. */
+typedef void (write_cb_t)(void *, const char *);
+
+enum malloc_init_e {
+ malloc_init_uninitialized = 3,
+ malloc_init_a0_initialized = 2,
+ malloc_init_recursible = 1,
+ malloc_init_initialized = 0 /* Common case --> jnz. */
+};
+typedef enum malloc_init_e malloc_init_t;
+
/*
* Flags bits:
*
diff --git a/contrib/jemalloc/include/jemalloc/internal/jemalloc_preamble.h.in b/contrib/jemalloc/include/jemalloc/internal/jemalloc_preamble.h.in
new file mode 100644
index 000000000000..e4932f1e8c18
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/internal/jemalloc_preamble.h.in
@@ -0,0 +1,262 @@
+#ifndef JEMALLOC_PREAMBLE_H
+#define JEMALLOC_PREAMBLE_H
+
+#include "jemalloc_internal_defs.h"
+#include "jemalloc/internal/jemalloc_internal_decls.h"
+
+#if defined(JEMALLOC_UTRACE) || defined(JEMALLOC_UTRACE_LABEL)
+#include <sys/ktrace.h>
+# if defined(JEMALLOC_UTRACE)
+# define UTRACE_CALL(p, l) utrace(p, l)
+# else
+# define UTRACE_CALL(p, l) utrace("jemalloc_process", p, l)
+# define JEMALLOC_UTRACE
+# endif
+#endif
+
+#ifndef JEMALLOC_PRIVATE_NAMESPACE
+#include "un-namespace.h"
+#include "libc_private.h"
+#endif
+
+#define JEMALLOC_NO_DEMANGLE
+#ifdef JEMALLOC_JET
+# undef JEMALLOC_IS_MALLOC
+# define JEMALLOC_N(n) jet_##n
+# include "jemalloc/internal/public_namespace.h"
+# define JEMALLOC_NO_RENAME
+# include "../jemalloc@install_suffix@.h"
+# undef JEMALLOC_NO_RENAME
+#else
+# define JEMALLOC_N(n) @private_namespace@##n
+# include "../jemalloc@install_suffix@.h"
+#endif
+
+#if defined(JEMALLOC_OSATOMIC)
+#include <libkern/OSAtomic.h>
+#endif
+
+#ifdef JEMALLOC_ZONE
+#include <mach/mach_error.h>
+#include <mach/mach_init.h>
+#include <mach/vm_map.h>
+#endif
+
+#include "jemalloc/internal/jemalloc_internal_macros.h"
+
+/*
+ * Note that the ordering matters here; the hook itself is name-mangled. We
+ * want the inclusion of hooks to happen early, so that we hook as much as
+ * possible.
+ */
+#ifndef JEMALLOC_NO_PRIVATE_NAMESPACE
+# ifndef JEMALLOC_JET
+# include "jemalloc/internal/private_namespace.h"
+# else
+# include "jemalloc/internal/private_namespace_jet.h"
+# endif
+#endif
+#include "jemalloc/internal/test_hooks.h"
+
+#ifdef JEMALLOC_DEFINE_MADVISE_FREE
+# define JEMALLOC_MADV_FREE 8
+#endif
+
+static const bool config_debug =
+#ifdef JEMALLOC_DEBUG
+ true
+#else
+ false
+#endif
+ ;
+static const bool have_dss =
+#ifdef JEMALLOC_DSS
+ true
+#else
+ false
+#endif
+ ;
+static const bool have_madvise_huge =
+#ifdef JEMALLOC_HAVE_MADVISE_HUGE
+ true
+#else
+ false
+#endif
+ ;
+static const bool config_fill =
+#ifdef JEMALLOC_FILL
+ true
+#else
+ false
+#endif
+ ;
+static const bool config_lazy_lock = true;
+static const char * const config_malloc_conf = JEMALLOC_CONFIG_MALLOC_CONF;
+static const bool config_prof =
+#ifdef JEMALLOC_PROF
+ true
+#else
+ false
+#endif
+ ;
+static const bool config_prof_libgcc =
+#ifdef JEMALLOC_PROF_LIBGCC
+ true
+#else
+ false
+#endif
+ ;
+static const bool config_prof_libunwind =
+#ifdef JEMALLOC_PROF_LIBUNWIND
+ true
+#else
+ false
+#endif
+ ;
+static const bool maps_coalesce =
+#ifdef JEMALLOC_MAPS_COALESCE
+ true
+#else
+ false
+#endif
+ ;
+static const bool config_stats =
+#ifdef JEMALLOC_STATS
+ true
+#else
+ false
+#endif
+ ;
+static const bool config_tls =
+#ifdef JEMALLOC_TLS
+ true
+#else
+ false
+#endif
+ ;
+static const bool config_utrace =
+#ifdef JEMALLOC_UTRACE
+ true
+#else
+ false
+#endif
+ ;
+static const bool config_xmalloc =
+#ifdef JEMALLOC_XMALLOC
+ true
+#else
+ false
+#endif
+ ;
+static const bool config_cache_oblivious =
+#ifdef JEMALLOC_CACHE_OBLIVIOUS
+ true
+#else
+ false
+#endif
+ ;
+/*
+ * Undocumented, for jemalloc development use only at the moment. See the note
+ * in jemalloc/internal/log.h.
+ */
+static const bool config_log =
+#ifdef JEMALLOC_LOG
+ true
+#else
+ false
+#endif
+ ;
+/*
+ * Are extra safety checks enabled; things like checking the size of sized
+ * deallocations, double-frees, etc.
+ */
+static const bool config_opt_safety_checks =
+#ifdef JEMALLOC_OPT_SAFETY_CHECKS
+ true
+#elif defined(JEMALLOC_DEBUG)
+ /*
+ * This lets us only guard safety checks by one flag instead of two; fast
+ * checks can guard solely by config_opt_safety_checks and run in debug mode
+ * too.
+ */
+ true
+#else
+ false
+#endif
+ ;
+
+/*
+ * Extra debugging of sized deallocations too onerous to be included in the
+ * general safety checks.
+ */
+static const bool config_opt_size_checks =
+#if defined(JEMALLOC_OPT_SIZE_CHECKS) || defined(JEMALLOC_DEBUG)
+ true
+#else
+ false
+#endif
+ ;
+
+static const bool config_uaf_detection =
+#if defined(JEMALLOC_UAF_DETECTION) || defined(JEMALLOC_DEBUG)
+ true
+#else
+ false
+#endif
+ ;
+
+/* Whether or not the C++ extensions are enabled. */
+static const bool config_enable_cxx =
+#ifdef JEMALLOC_ENABLE_CXX
+ true
+#else
+ false
+#endif
+;
+
+#if defined(_WIN32) || defined(JEMALLOC_HAVE_SCHED_GETCPU)
+/* Currently percpu_arena depends on sched_getcpu. */
+#define JEMALLOC_PERCPU_ARENA
+#endif
+static const bool have_percpu_arena =
+#ifdef JEMALLOC_PERCPU_ARENA
+ true
+#else
+ false
+#endif
+ ;
+/*
+ * Undocumented, and not recommended; the application should take full
+ * responsibility for tracking provenance.
+ */
+static const bool force_ivsalloc =
+#ifdef JEMALLOC_FORCE_IVSALLOC
+ true
+#else
+ false
+#endif
+ ;
+static const bool have_background_thread =
+#ifdef JEMALLOC_BACKGROUND_THREAD
+ true
+#else
+ false
+#endif
+ ;
+static const bool config_high_res_timer =
+#ifdef JEMALLOC_HAVE_CLOCK_REALTIME
+ true
+#else
+ false
+#endif
+ ;
+
+static const bool have_memcntl =
+#ifdef JEMALLOC_HAVE_MEMCNTL
+ true
+#else
+ false
+#endif
+ ;
+
+#endif /* JEMALLOC_PREAMBLE_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/large_externs.h b/contrib/jemalloc/include/jemalloc/internal/large_externs.h
index a05019e8a542..8e09122dfb7b 100644
--- a/contrib/jemalloc/include/jemalloc/internal/large_externs.h
+++ b/contrib/jemalloc/include/jemalloc/internal/large_externs.h
@@ -6,27 +6,19 @@
void *large_malloc(tsdn_t *tsdn, arena_t *arena, size_t usize, bool zero);
void *large_palloc(tsdn_t *tsdn, arena_t *arena, size_t usize, size_t alignment,
bool zero);
-bool large_ralloc_no_move(tsdn_t *tsdn, extent_t *extent, size_t usize_min,
+bool large_ralloc_no_move(tsdn_t *tsdn, edata_t *edata, size_t usize_min,
size_t usize_max, bool zero);
void *large_ralloc(tsdn_t *tsdn, arena_t *arena, void *ptr, size_t usize,
size_t alignment, bool zero, tcache_t *tcache,
hook_ralloc_args_t *hook_args);
-typedef void (large_dalloc_junk_t)(void *, size_t);
-extern large_dalloc_junk_t *JET_MUTABLE large_dalloc_junk;
-
-typedef void (large_dalloc_maybe_junk_t)(void *, size_t);
-extern large_dalloc_maybe_junk_t *JET_MUTABLE large_dalloc_maybe_junk;
-
-void large_dalloc_prep_junked_locked(tsdn_t *tsdn, extent_t *extent);
-void large_dalloc_finish(tsdn_t *tsdn, extent_t *extent);
-void large_dalloc(tsdn_t *tsdn, extent_t *extent);
-size_t large_salloc(tsdn_t *tsdn, const extent_t *extent);
-prof_tctx_t *large_prof_tctx_get(tsdn_t *tsdn, const extent_t *extent);
-void large_prof_tctx_set(tsdn_t *tsdn, extent_t *extent, prof_tctx_t *tctx);
-void large_prof_tctx_reset(tsdn_t *tsdn, extent_t *extent);
-
-nstime_t large_prof_alloc_time_get(const extent_t *extent);
-void large_prof_alloc_time_set(extent_t *extent, nstime_t time);
+void large_dalloc_prep_locked(tsdn_t *tsdn, edata_t *edata);
+void large_dalloc_finish(tsdn_t *tsdn, edata_t *edata);
+void large_dalloc(tsdn_t *tsdn, edata_t *edata);
+size_t large_salloc(tsdn_t *tsdn, const edata_t *edata);
+void large_prof_info_get(tsd_t *tsd, edata_t *edata, prof_info_t *prof_info,
+ bool reset_recent);
+void large_prof_tctx_reset(edata_t *edata);
+void large_prof_info_set(edata_t *edata, prof_tctx_t *tctx, size_t size);
#endif /* JEMALLOC_INTERNAL_LARGE_EXTERNS_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/lockedint.h b/contrib/jemalloc/include/jemalloc/internal/lockedint.h
new file mode 100644
index 000000000000..d020ebec1c4b
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/internal/lockedint.h
@@ -0,0 +1,204 @@
+#ifndef JEMALLOC_INTERNAL_LOCKEDINT_H
+#define JEMALLOC_INTERNAL_LOCKEDINT_H
+
+/*
+ * In those architectures that support 64-bit atomics, we use atomic updates for
+ * our 64-bit values. Otherwise, we use a plain uint64_t and synchronize
+ * externally.
+ */
+
+typedef struct locked_u64_s locked_u64_t;
+#ifdef JEMALLOC_ATOMIC_U64
+struct locked_u64_s {
+ atomic_u64_t val;
+};
+#else
+/* Must hold the associated mutex. */
+struct locked_u64_s {
+ uint64_t val;
+};
+#endif
+
+typedef struct locked_zu_s locked_zu_t;
+struct locked_zu_s {
+ atomic_zu_t val;
+};
+
+#ifndef JEMALLOC_ATOMIC_U64
+# define LOCKEDINT_MTX_DECLARE(name) malloc_mutex_t name;
+# define LOCKEDINT_MTX_INIT(mu, name, rank, rank_mode) \
+ malloc_mutex_init(&(mu), name, rank, rank_mode)
+# define LOCKEDINT_MTX(mtx) (&(mtx))
+# define LOCKEDINT_MTX_LOCK(tsdn, mu) malloc_mutex_lock(tsdn, &(mu))
+# define LOCKEDINT_MTX_UNLOCK(tsdn, mu) malloc_mutex_unlock(tsdn, &(mu))
+# define LOCKEDINT_MTX_PREFORK(tsdn, mu) malloc_mutex_prefork(tsdn, &(mu))
+# define LOCKEDINT_MTX_POSTFORK_PARENT(tsdn, mu) \
+ malloc_mutex_postfork_parent(tsdn, &(mu))
+# define LOCKEDINT_MTX_POSTFORK_CHILD(tsdn, mu) \
+ malloc_mutex_postfork_child(tsdn, &(mu))
+#else
+# define LOCKEDINT_MTX_DECLARE(name)
+# define LOCKEDINT_MTX(mtx) NULL
+# define LOCKEDINT_MTX_INIT(mu, name, rank, rank_mode) false
+# define LOCKEDINT_MTX_LOCK(tsdn, mu)
+# define LOCKEDINT_MTX_UNLOCK(tsdn, mu)
+# define LOCKEDINT_MTX_PREFORK(tsdn, mu)
+# define LOCKEDINT_MTX_POSTFORK_PARENT(tsdn, mu)
+# define LOCKEDINT_MTX_POSTFORK_CHILD(tsdn, mu)
+#endif
+
+#ifdef JEMALLOC_ATOMIC_U64
+# define LOCKEDINT_MTX_ASSERT_INTERNAL(tsdn, mtx) assert((mtx) == NULL)
+#else
+# define LOCKEDINT_MTX_ASSERT_INTERNAL(tsdn, mtx) \
+ malloc_mutex_assert_owner(tsdn, (mtx))
+#endif
+
+static inline uint64_t
+locked_read_u64(tsdn_t *tsdn, malloc_mutex_t *mtx, locked_u64_t *p) {
+ LOCKEDINT_MTX_ASSERT_INTERNAL(tsdn, mtx);
+#ifdef JEMALLOC_ATOMIC_U64
+ return atomic_load_u64(&p->val, ATOMIC_RELAXED);
+#else
+ return p->val;
+#endif
+}
+
+static inline void
+locked_inc_u64(tsdn_t *tsdn, malloc_mutex_t *mtx, locked_u64_t *p,
+ uint64_t x) {
+ LOCKEDINT_MTX_ASSERT_INTERNAL(tsdn, mtx);
+#ifdef JEMALLOC_ATOMIC_U64
+ atomic_fetch_add_u64(&p->val, x, ATOMIC_RELAXED);
+#else
+ p->val += x;
+#endif
+}
+
+static inline void
+locked_dec_u64(tsdn_t *tsdn, malloc_mutex_t *mtx, locked_u64_t *p,
+ uint64_t x) {
+ LOCKEDINT_MTX_ASSERT_INTERNAL(tsdn, mtx);
+#ifdef JEMALLOC_ATOMIC_U64
+ uint64_t r = atomic_fetch_sub_u64(&p->val, x, ATOMIC_RELAXED);
+ assert(r - x <= r);
+#else
+ p->val -= x;
+ assert(p->val + x >= p->val);
+#endif
+}
+
+/* Increment and take modulus. Returns whether the modulo made any change. */
+static inline bool
+locked_inc_mod_u64(tsdn_t *tsdn, malloc_mutex_t *mtx, locked_u64_t *p,
+ const uint64_t x, const uint64_t modulus) {
+ LOCKEDINT_MTX_ASSERT_INTERNAL(tsdn, mtx);
+ uint64_t before, after;
+ bool overflow;
+#ifdef JEMALLOC_ATOMIC_U64
+ before = atomic_load_u64(&p->val, ATOMIC_RELAXED);
+ do {
+ after = before + x;
+ assert(after >= before);
+ overflow = (after >= modulus);
+ if (overflow) {
+ after %= modulus;
+ }
+ } while (!atomic_compare_exchange_weak_u64(&p->val, &before, after,
+ ATOMIC_RELAXED, ATOMIC_RELAXED));
+#else
+ before = p->val;
+ after = before + x;
+ overflow = (after >= modulus);
+ if (overflow) {
+ after %= modulus;
+ }
+ p->val = after;
+#endif
+ return overflow;
+}
+
+/*
+ * Non-atomically sets *dst += src. *dst needs external synchronization.
+ * This lets us avoid the cost of a fetch_add when its unnecessary (note that
+ * the types here are atomic).
+ */
+static inline void
+locked_inc_u64_unsynchronized(locked_u64_t *dst, uint64_t src) {
+#ifdef JEMALLOC_ATOMIC_U64
+ uint64_t cur_dst = atomic_load_u64(&dst->val, ATOMIC_RELAXED);
+ atomic_store_u64(&dst->val, src + cur_dst, ATOMIC_RELAXED);
+#else
+ dst->val += src;
+#endif
+}
+
+static inline uint64_t
+locked_read_u64_unsynchronized(locked_u64_t *p) {
+#ifdef JEMALLOC_ATOMIC_U64
+ return atomic_load_u64(&p->val, ATOMIC_RELAXED);
+#else
+ return p->val;
+#endif
+}
+
+static inline void
+locked_init_u64_unsynchronized(locked_u64_t *p, uint64_t x) {
+#ifdef JEMALLOC_ATOMIC_U64
+ atomic_store_u64(&p->val, x, ATOMIC_RELAXED);
+#else
+ p->val = x;
+#endif
+}
+
+static inline size_t
+locked_read_zu(tsdn_t *tsdn, malloc_mutex_t *mtx, locked_zu_t *p) {
+ LOCKEDINT_MTX_ASSERT_INTERNAL(tsdn, mtx);
+#ifdef JEMALLOC_ATOMIC_U64
+ return atomic_load_zu(&p->val, ATOMIC_RELAXED);
+#else
+ return atomic_load_zu(&p->val, ATOMIC_RELAXED);
+#endif
+}
+
+static inline void
+locked_inc_zu(tsdn_t *tsdn, malloc_mutex_t *mtx, locked_zu_t *p,
+ size_t x) {
+ LOCKEDINT_MTX_ASSERT_INTERNAL(tsdn, mtx);
+#ifdef JEMALLOC_ATOMIC_U64
+ atomic_fetch_add_zu(&p->val, x, ATOMIC_RELAXED);
+#else
+ size_t cur = atomic_load_zu(&p->val, ATOMIC_RELAXED);
+ atomic_store_zu(&p->val, cur + x, ATOMIC_RELAXED);
+#endif
+}
+
+static inline void
+locked_dec_zu(tsdn_t *tsdn, malloc_mutex_t *mtx, locked_zu_t *p,
+ size_t x) {
+ LOCKEDINT_MTX_ASSERT_INTERNAL(tsdn, mtx);
+#ifdef JEMALLOC_ATOMIC_U64
+ size_t r = atomic_fetch_sub_zu(&p->val, x, ATOMIC_RELAXED);
+ assert(r - x <= r);
+#else
+ size_t cur = atomic_load_zu(&p->val, ATOMIC_RELAXED);
+ atomic_store_zu(&p->val, cur - x, ATOMIC_RELAXED);
+#endif
+}
+
+/* Like the _u64 variant, needs an externally synchronized *dst. */
+static inline void
+locked_inc_zu_unsynchronized(locked_zu_t *dst, size_t src) {
+ size_t cur_dst = atomic_load_zu(&dst->val, ATOMIC_RELAXED);
+ atomic_store_zu(&dst->val, src + cur_dst, ATOMIC_RELAXED);
+}
+
+/*
+ * Unlike the _u64 variant, this is safe to call unconditionally.
+ */
+static inline size_t
+locked_read_atomic_zu(locked_zu_t *p) {
+ return atomic_load_zu(&p->val, ATOMIC_RELAXED);
+}
+
+#endif /* JEMALLOC_INTERNAL_LOCKEDINT_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/malloc_io.h b/contrib/jemalloc/include/jemalloc/internal/malloc_io.h
index 1d1a414e0f0b..a375bdae084a 100644
--- a/contrib/jemalloc/include/jemalloc/internal/malloc_io.h
+++ b/contrib/jemalloc/include/jemalloc/internal/malloc_io.h
@@ -1,6 +1,8 @@
#ifndef JEMALLOC_INTERNAL_MALLOC_IO_H
#define JEMALLOC_INTERNAL_MALLOC_IO_H
+#include "jemalloc/internal/jemalloc_internal_types.h"
+
#ifdef _WIN32
# ifdef _WIN64
# define FMT64_PREFIX "ll"
@@ -40,6 +42,7 @@
*/
#define MALLOC_PRINTF_BUFSIZE 4096
+write_cb_t wrtmessage;
int buferror(int err, char *buf, size_t buflen);
uintmax_t malloc_strtoumax(const char *restrict nptr, char **restrict endptr,
int base);
@@ -57,10 +60,10 @@ size_t malloc_snprintf(char *str, size_t size, const char *format, ...)
* The caller can set write_cb to null to choose to print with the
* je_malloc_message hook.
*/
-void malloc_vcprintf(void (*write_cb)(void *, const char *), void *cbopaque,
- const char *format, va_list ap);
-void malloc_cprintf(void (*write_cb)(void *, const char *), void *cbopaque,
- const char *format, ...) JEMALLOC_FORMAT_PRINTF(3, 4);
+void malloc_vcprintf(write_cb_t *write_cb, void *cbopaque, const char *format,
+ va_list ap);
+void malloc_cprintf(write_cb_t *write_cb, void *cbopaque, const char *format,
+ ...) JEMALLOC_FORMAT_PRINTF(3, 4);
void malloc_printf(const char *format, ...) JEMALLOC_FORMAT_PRINTF(1, 2);
static inline ssize_t
diff --git a/contrib/jemalloc/include/jemalloc/internal/mpsc_queue.h b/contrib/jemalloc/include/jemalloc/internal/mpsc_queue.h
new file mode 100644
index 000000000000..316ea9b16edd
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/internal/mpsc_queue.h
@@ -0,0 +1,134 @@
+#ifndef JEMALLOC_INTERNAL_MPSC_QUEUE_H
+#define JEMALLOC_INTERNAL_MPSC_QUEUE_H
+
+#include "jemalloc/internal/atomic.h"
+
+/*
+ * A concurrent implementation of a multi-producer, single-consumer queue. It
+ * supports three concurrent operations:
+ * - Push
+ * - Push batch
+ * - Pop batch
+ *
+ * These operations are all lock-free.
+ *
+ * The implementation is the simple two-stack queue built on a Treiber stack.
+ * It's not terribly efficient, but this isn't expected to go into anywhere with
+ * hot code. In fact, we don't really even need queue semantics in any
+ * anticipated use cases; we could get away with just the stack. But this way
+ * lets us frame the API in terms of the existing list types, which is a nice
+ * convenience. We can save on cache misses by introducing our own (parallel)
+ * single-linked list type here, and dropping FIFO semantics, if we need this to
+ * get faster. Since we're currently providing queue semantics though, we use
+ * the prev field in the link rather than the next field for Treiber-stack
+ * linkage, so that we can preserve order for bash-pushed lists (recall that the
+ * two-stack tricks reverses orders in the lock-free first stack).
+ */
+
+#define mpsc_queue(a_type) \
+struct { \
+ atomic_p_t tail; \
+}
+
+#define mpsc_queue_proto(a_attr, a_prefix, a_queue_type, a_type, \
+ a_list_type) \
+/* Initialize a queue. */ \
+a_attr void \
+a_prefix##new(a_queue_type *queue); \
+/* Insert all items in src into the queue, clearing src. */ \
+a_attr void \
+a_prefix##push_batch(a_queue_type *queue, a_list_type *src); \
+/* Insert node into the queue. */ \
+a_attr void \
+a_prefix##push(a_queue_type *queue, a_type *node); \
+/* \
+ * Pop all items in the queue into the list at dst. dst should already \
+ * be initialized (and may contain existing items, which then remain \
+ * in dst). \
+ */ \
+a_attr void \
+a_prefix##pop_batch(a_queue_type *queue, a_list_type *dst);
+
+#define mpsc_queue_gen(a_attr, a_prefix, a_queue_type, a_type, \
+ a_list_type, a_link) \
+a_attr void \
+a_prefix##new(a_queue_type *queue) { \
+ atomic_store_p(&queue->tail, NULL, ATOMIC_RELAXED); \
+} \
+a_attr void \
+a_prefix##push_batch(a_queue_type *queue, a_list_type *src) { \
+ /* \
+ * Reuse the ql list next field as the Treiber stack next \
+ * field. \
+ */ \
+ a_type *first = ql_first(src); \
+ a_type *last = ql_last(src, a_link); \
+ void* cur_tail = atomic_load_p(&queue->tail, ATOMIC_RELAXED); \
+ do { \
+ /* \
+ * Note that this breaks the queue ring structure; \
+ * it's not a ring any more! \
+ */ \
+ first->a_link.qre_prev = cur_tail; \
+ /* \
+ * Note: the upcoming CAS doesn't need an atomic; every \
+ * push only needs to synchronize with the next pop, \
+ * which we get from the release sequence rules. \
+ */ \
+ } while (!atomic_compare_exchange_weak_p(&queue->tail, \
+ &cur_tail, last, ATOMIC_RELEASE, ATOMIC_RELAXED)); \
+ ql_new(src); \
+} \
+a_attr void \
+a_prefix##push(a_queue_type *queue, a_type *node) { \
+ ql_elm_new(node, a_link); \
+ a_list_type list; \
+ ql_new(&list); \
+ ql_head_insert(&list, node, a_link); \
+ a_prefix##push_batch(queue, &list); \
+} \
+a_attr void \
+a_prefix##pop_batch(a_queue_type *queue, a_list_type *dst) { \
+ a_type *tail = atomic_load_p(&queue->tail, ATOMIC_RELAXED); \
+ if (tail == NULL) { \
+ /* \
+ * In the common special case where there are no \
+ * pending elements, bail early without a costly RMW. \
+ */ \
+ return; \
+ } \
+ tail = atomic_exchange_p(&queue->tail, NULL, ATOMIC_ACQUIRE); \
+ /* \
+ * It's a single-consumer queue, so if cur started non-NULL, \
+ * it'd better stay non-NULL. \
+ */ \
+ assert(tail != NULL); \
+ /* \
+ * We iterate through the stack and both fix up the link \
+ * structure (stack insertion broke the list requirement that \
+ * the list be circularly linked). It's just as efficient at \
+ * this point to make the queue a "real" queue, so do that as \
+ * well. \
+ * If this ever gets to be a hot spot, we can omit this fixup \
+ * and make the queue a bag (i.e. not necessarily ordered), but \
+ * that would mean jettisoning the existing list API as the \
+ * batch pushing/popping interface. \
+ */ \
+ a_list_type reversed; \
+ ql_new(&reversed); \
+ while (tail != NULL) { \
+ /* \
+ * Pop an item off the stack, prepend it onto the list \
+ * (reversing the order). Recall that we use the \
+ * list prev field as the Treiber stack next field to \
+ * preserve order of batch-pushed items when reversed. \
+ */ \
+ a_type *next = tail->a_link.qre_prev; \
+ ql_elm_new(tail, a_link); \
+ ql_head_insert(&reversed, tail, a_link); \
+ tail = next; \
+ } \
+ ql_concat(dst, &reversed, a_link); \
+}
+
+#endif /* JEMALLOC_INTERNAL_MPSC_QUEUE_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/mutex.h b/contrib/jemalloc/include/jemalloc/internal/mutex.h
index 94af16183649..8468e7bcb864 100644
--- a/contrib/jemalloc/include/jemalloc/internal/mutex.h
+++ b/contrib/jemalloc/include/jemalloc/internal/mutex.h
@@ -6,6 +6,8 @@
#include "jemalloc/internal/tsd.h"
#include "jemalloc/internal/witness.h"
+extern int64_t opt_mutex_max_spin;
+
typedef enum {
/* Can only acquire one mutex of a given witness rank at a time. */
malloc_mutex_rank_exclusive,
@@ -43,7 +45,7 @@ struct malloc_mutex_s {
#else
pthread_mutex_t lock;
#endif
- /*
+ /*
* Hint flag to avoid exclusive cache line contention
* during spin waiting
*/
@@ -67,12 +69,6 @@ struct malloc_mutex_s {
#endif
};
-/*
- * Based on benchmark results, a fixed spin with this amount of retries works
- * well for our critical sections.
- */
-#define MALLOC_MUTEX_MAX_SPIN 250
-
#ifdef _WIN32
# if _WIN32_WINNT >= 0x0600
# define MALLOC_MUTEX_LOCK(m) AcquireSRWLockExclusive(&(m)->lock)
@@ -243,22 +239,25 @@ malloc_mutex_assert_not_owner(tsdn_t *tsdn, malloc_mutex_t *mutex) {
witness_assert_not_owner(tsdn_witness_tsdp_get(tsdn), &mutex->witness);
}
-/* Copy the prof data from mutex for processing. */
static inline void
-malloc_mutex_prof_read(tsdn_t *tsdn, mutex_prof_data_t *data,
- malloc_mutex_t *mutex) {
- mutex_prof_data_t *source = &mutex->prof_data;
- /* Can only read holding the mutex. */
- malloc_mutex_assert_owner(tsdn, mutex);
-
+malloc_mutex_prof_copy(mutex_prof_data_t *dst, mutex_prof_data_t *source) {
/*
* Not *really* allowed (we shouldn't be doing non-atomic loads of
* atomic data), but the mutex protection makes this safe, and writing
* a member-for-member copy is tedious for this situation.
*/
- *data = *source;
+ *dst = *source;
/* n_wait_thds is not reported (modified w/o locking). */
- atomic_store_u32(&data->n_waiting_thds, 0, ATOMIC_RELAXED);
+ atomic_store_u32(&dst->n_waiting_thds, 0, ATOMIC_RELAXED);
+}
+
+/* Copy the prof data from mutex for processing. */
+static inline void
+malloc_mutex_prof_read(tsdn_t *tsdn, mutex_prof_data_t *data,
+ malloc_mutex_t *mutex) {
+ /* Can only read holding the mutex. */
+ malloc_mutex_assert_owner(tsdn, mutex);
+ malloc_mutex_prof_copy(data, &mutex->prof_data);
}
static inline void
@@ -283,4 +282,36 @@ malloc_mutex_prof_accum(tsdn_t *tsdn, mutex_prof_data_t *data,
data->n_lock_ops += source->n_lock_ops;
}
+/* Compare the prof data and update to the maximum. */
+static inline void
+malloc_mutex_prof_max_update(tsdn_t *tsdn, mutex_prof_data_t *data,
+ malloc_mutex_t *mutex) {
+ mutex_prof_data_t *source = &mutex->prof_data;
+ /* Can only read holding the mutex. */
+ malloc_mutex_assert_owner(tsdn, mutex);
+
+ if (nstime_compare(&source->tot_wait_time, &data->tot_wait_time) > 0) {
+ nstime_copy(&data->tot_wait_time, &source->tot_wait_time);
+ }
+ if (nstime_compare(&source->max_wait_time, &data->max_wait_time) > 0) {
+ nstime_copy(&data->max_wait_time, &source->max_wait_time);
+ }
+ if (source->n_wait_times > data->n_wait_times) {
+ data->n_wait_times = source->n_wait_times;
+ }
+ if (source->n_spin_acquired > data->n_spin_acquired) {
+ data->n_spin_acquired = source->n_spin_acquired;
+ }
+ if (source->max_n_thds > data->max_n_thds) {
+ data->max_n_thds = source->max_n_thds;
+ }
+ if (source->n_owner_switches > data->n_owner_switches) {
+ data->n_owner_switches = source->n_owner_switches;
+ }
+ if (source->n_lock_ops > data->n_lock_ops) {
+ data->n_lock_ops = source->n_lock_ops;
+ }
+ /* n_wait_thds is not reported. */
+}
+
#endif /* JEMALLOC_INTERNAL_MUTEX_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/mutex_pool.h b/contrib/jemalloc/include/jemalloc/internal/mutex_pool.h
deleted file mode 100644
index 726cece90bc7..000000000000
--- a/contrib/jemalloc/include/jemalloc/internal/mutex_pool.h
+++ /dev/null
@@ -1,94 +0,0 @@
-#ifndef JEMALLOC_INTERNAL_MUTEX_POOL_H
-#define JEMALLOC_INTERNAL_MUTEX_POOL_H
-
-#include "jemalloc/internal/hash.h"
-#include "jemalloc/internal/mutex.h"
-#include "jemalloc/internal/witness.h"
-
-/* We do mod reductions by this value, so it should be kept a power of 2. */
-#define MUTEX_POOL_SIZE 256
-
-typedef struct mutex_pool_s mutex_pool_t;
-struct mutex_pool_s {
- malloc_mutex_t mutexes[MUTEX_POOL_SIZE];
-};
-
-bool mutex_pool_init(mutex_pool_t *pool, const char *name, witness_rank_t rank);
-
-/* Internal helper - not meant to be called outside this module. */
-static inline malloc_mutex_t *
-mutex_pool_mutex(mutex_pool_t *pool, uintptr_t key) {
- size_t hash_result[2];
- hash(&key, sizeof(key), 0xd50dcc1b, hash_result);
- return &pool->mutexes[hash_result[0] % MUTEX_POOL_SIZE];
-}
-
-static inline void
-mutex_pool_assert_not_held(tsdn_t *tsdn, mutex_pool_t *pool) {
- for (int i = 0; i < MUTEX_POOL_SIZE; i++) {
- malloc_mutex_assert_not_owner(tsdn, &pool->mutexes[i]);
- }
-}
-
-/*
- * Note that a mutex pool doesn't work exactly the way an embdedded mutex would.
- * You're not allowed to acquire mutexes in the pool one at a time. You have to
- * acquire all the mutexes you'll need in a single function call, and then
- * release them all in a single function call.
- */
-
-static inline void
-mutex_pool_lock(tsdn_t *tsdn, mutex_pool_t *pool, uintptr_t key) {
- mutex_pool_assert_not_held(tsdn, pool);
-
- malloc_mutex_t *mutex = mutex_pool_mutex(pool, key);
- malloc_mutex_lock(tsdn, mutex);
-}
-
-static inline void
-mutex_pool_unlock(tsdn_t *tsdn, mutex_pool_t *pool, uintptr_t key) {
- malloc_mutex_t *mutex = mutex_pool_mutex(pool, key);
- malloc_mutex_unlock(tsdn, mutex);
-
- mutex_pool_assert_not_held(tsdn, pool);
-}
-
-static inline void
-mutex_pool_lock2(tsdn_t *tsdn, mutex_pool_t *pool, uintptr_t key1,
- uintptr_t key2) {
- mutex_pool_assert_not_held(tsdn, pool);
-
- malloc_mutex_t *mutex1 = mutex_pool_mutex(pool, key1);
- malloc_mutex_t *mutex2 = mutex_pool_mutex(pool, key2);
- if ((uintptr_t)mutex1 < (uintptr_t)mutex2) {
- malloc_mutex_lock(tsdn, mutex1);
- malloc_mutex_lock(tsdn, mutex2);
- } else if ((uintptr_t)mutex1 == (uintptr_t)mutex2) {
- malloc_mutex_lock(tsdn, mutex1);
- } else {
- malloc_mutex_lock(tsdn, mutex2);
- malloc_mutex_lock(tsdn, mutex1);
- }
-}
-
-static inline void
-mutex_pool_unlock2(tsdn_t *tsdn, mutex_pool_t *pool, uintptr_t key1,
- uintptr_t key2) {
- malloc_mutex_t *mutex1 = mutex_pool_mutex(pool, key1);
- malloc_mutex_t *mutex2 = mutex_pool_mutex(pool, key2);
- if (mutex1 == mutex2) {
- malloc_mutex_unlock(tsdn, mutex1);
- } else {
- malloc_mutex_unlock(tsdn, mutex1);
- malloc_mutex_unlock(tsdn, mutex2);
- }
-
- mutex_pool_assert_not_held(tsdn, pool);
-}
-
-static inline void
-mutex_pool_assert_owner(tsdn_t *tsdn, mutex_pool_t *pool, uintptr_t key) {
- malloc_mutex_assert_owner(tsdn, mutex_pool_mutex(pool, key));
-}
-
-#endif /* JEMALLOC_INTERNAL_MUTEX_POOL_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/mutex_prof.h b/contrib/jemalloc/include/jemalloc/internal/mutex_prof.h
index 2cb8fb0cbf7b..4a526a5aeb86 100644
--- a/contrib/jemalloc/include/jemalloc/internal/mutex_prof.h
+++ b/contrib/jemalloc/include/jemalloc/internal/mutex_prof.h
@@ -7,8 +7,14 @@
#define MUTEX_PROF_GLOBAL_MUTEXES \
OP(background_thread) \
+ OP(max_per_bg_thd) \
OP(ctl) \
- OP(prof)
+ OP(prof) \
+ OP(prof_thds_data) \
+ OP(prof_dump) \
+ OP(prof_recent_alloc) \
+ OP(prof_recent_dump) \
+ OP(prof_stats)
typedef enum {
#define OP(mtx) global_prof_mutex_##mtx,
@@ -26,7 +32,10 @@ typedef enum {
OP(decay_dirty) \
OP(decay_muzzy) \
OP(base) \
- OP(tcache_list)
+ OP(tcache_list) \
+ OP(hpa_shard) \
+ OP(hpa_shard_grow) \
+ OP(hpa_sec)
typedef enum {
#define OP(mtx) arena_prof_mutex_##mtx,
diff --git a/contrib/jemalloc/include/jemalloc/internal/nstime.h b/contrib/jemalloc/include/jemalloc/internal/nstime.h
index 17c177c7f4b3..486e5ccacc73 100644
--- a/contrib/jemalloc/include/jemalloc/internal/nstime.h
+++ b/contrib/jemalloc/include/jemalloc/internal/nstime.h
@@ -3,12 +3,23 @@
/* Maximum supported number of seconds (~584 years). */
#define NSTIME_SEC_MAX KQU(18446744072)
-#define NSTIME_ZERO_INITIALIZER {0}
+
+#define NSTIME_MAGIC ((uint32_t)0xb8a9ce37)
+#ifdef JEMALLOC_DEBUG
+# define NSTIME_ZERO_INITIALIZER {0, NSTIME_MAGIC}
+#else
+# define NSTIME_ZERO_INITIALIZER {0}
+#endif
typedef struct {
uint64_t ns;
+#ifdef JEMALLOC_DEBUG
+ uint32_t magic; /* Tracks if initialized. */
+#endif
} nstime_t;
+static const nstime_t nstime_zero = NSTIME_ZERO_INITIALIZER;
+
void nstime_init(nstime_t *time, uint64_t ns);
void nstime_init2(nstime_t *time, uint64_t sec, uint64_t nsec);
uint64_t nstime_ns(const nstime_t *time);
@@ -24,11 +35,39 @@ void nstime_isubtract(nstime_t *time, uint64_t subtrahend);
void nstime_imultiply(nstime_t *time, uint64_t multiplier);
void nstime_idivide(nstime_t *time, uint64_t divisor);
uint64_t nstime_divide(const nstime_t *time, const nstime_t *divisor);
+uint64_t nstime_ns_since(const nstime_t *past);
typedef bool (nstime_monotonic_t)(void);
extern nstime_monotonic_t *JET_MUTABLE nstime_monotonic;
-typedef bool (nstime_update_t)(nstime_t *);
+typedef void (nstime_update_t)(nstime_t *);
extern nstime_update_t *JET_MUTABLE nstime_update;
+typedef void (nstime_prof_update_t)(nstime_t *);
+extern nstime_prof_update_t *JET_MUTABLE nstime_prof_update;
+
+void nstime_init_update(nstime_t *time);
+void nstime_prof_init_update(nstime_t *time);
+
+enum prof_time_res_e {
+ prof_time_res_default = 0,
+ prof_time_res_high = 1
+};
+typedef enum prof_time_res_e prof_time_res_t;
+
+extern prof_time_res_t opt_prof_time_res;
+extern const char *prof_time_res_mode_names[];
+
+JEMALLOC_ALWAYS_INLINE void
+nstime_init_zero(nstime_t *time) {
+ nstime_copy(time, &nstime_zero);
+}
+
+JEMALLOC_ALWAYS_INLINE bool
+nstime_equals_zero(nstime_t *time) {
+ int diff = nstime_compare(time, &nstime_zero);
+ assert(diff >= 0);
+ return diff == 0;
+}
+
#endif /* JEMALLOC_INTERNAL_NSTIME_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/pa.h b/contrib/jemalloc/include/jemalloc/internal/pa.h
new file mode 100644
index 000000000000..4748a05b691e
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/internal/pa.h
@@ -0,0 +1,243 @@
+#ifndef JEMALLOC_INTERNAL_PA_H
+#define JEMALLOC_INTERNAL_PA_H
+
+#include "jemalloc/internal/base.h"
+#include "jemalloc/internal/decay.h"
+#include "jemalloc/internal/ecache.h"
+#include "jemalloc/internal/edata_cache.h"
+#include "jemalloc/internal/emap.h"
+#include "jemalloc/internal/hpa.h"
+#include "jemalloc/internal/lockedint.h"
+#include "jemalloc/internal/pac.h"
+#include "jemalloc/internal/pai.h"
+#include "jemalloc/internal/sec.h"
+
+/*
+ * The page allocator; responsible for acquiring pages of memory for
+ * allocations. It picks the implementation of the page allocator interface
+ * (i.e. a pai_t) to handle a given page-level allocation request. For now, the
+ * only such implementation is the PAC code ("page allocator classic"), but
+ * others will be coming soon.
+ */
+
+typedef struct pa_central_s pa_central_t;
+struct pa_central_s {
+ hpa_central_t hpa;
+};
+
+/*
+ * The stats for a particular pa_shard. Because of the way the ctl module
+ * handles stats epoch data collection (it has its own arena_stats, and merges
+ * the stats from each arena into it), this needs to live in the arena_stats_t;
+ * hence we define it here and let the pa_shard have a pointer (rather than the
+ * more natural approach of just embedding it in the pa_shard itself).
+ *
+ * We follow the arena_stats_t approach of marking the derived fields. These
+ * are the ones that are not maintained on their own; instead, their values are
+ * derived during those stats merges.
+ */
+typedef struct pa_shard_stats_s pa_shard_stats_t;
+struct pa_shard_stats_s {
+ /* Number of edata_t structs allocated by base, but not being used. */
+ size_t edata_avail; /* Derived. */
+ /*
+ * Stats specific to the PAC. For now, these are the only stats that
+ * exist, but there will eventually be other page allocators. Things
+ * like edata_avail make sense in a cross-PA sense, but things like
+ * npurges don't.
+ */
+ pac_stats_t pac_stats;
+};
+
+/*
+ * The local allocator handle. Keeps the state necessary to satisfy page-sized
+ * allocations.
+ *
+ * The contents are mostly internal to the PA module. The key exception is that
+ * arena decay code is allowed to grab pointers to the dirty and muzzy ecaches
+ * decay_ts, for a couple of queries, passing them back to a PA function, or
+ * acquiring decay.mtx and looking at decay.purging. The reasoning is that,
+ * while PA decides what and how to purge, the arena code decides when and where
+ * (e.g. on what thread). It's allowed to use the presence of another purger to
+ * decide.
+ * (The background thread code also touches some other decay internals, but
+ * that's not fundamental; its' just an artifact of a partial refactoring, and
+ * its accesses could be straightforwardly moved inside the decay module).
+ */
+typedef struct pa_shard_s pa_shard_t;
+struct pa_shard_s {
+ /* The central PA this shard is associated with. */
+ pa_central_t *central;
+
+ /*
+ * Number of pages in active extents.
+ *
+ * Synchronization: atomic.
+ */
+ atomic_zu_t nactive;
+
+ /*
+ * Whether or not we should prefer the hugepage allocator. Atomic since
+ * it may be concurrently modified by a thread setting extent hooks.
+ * Note that we still may do HPA operations in this arena; if use_hpa is
+ * changed from true to false, we'll free back to the hugepage allocator
+ * for those allocations.
+ */
+ atomic_b_t use_hpa;
+
+ /*
+ * If we never used the HPA to begin with, it wasn't initialized, and so
+ * we shouldn't try to e.g. acquire its mutexes during fork. This
+ * tracks that knowledge.
+ */
+ bool ever_used_hpa;
+
+ /* Allocates from a PAC. */
+ pac_t pac;
+
+ /*
+ * We place a small extent cache in front of the HPA, since we intend
+ * these configurations to use many fewer arenas, and therefore have a
+ * higher risk of hot locks.
+ */
+ sec_t hpa_sec;
+ hpa_shard_t hpa_shard;
+
+ /* The source of edata_t objects. */
+ edata_cache_t edata_cache;
+
+ unsigned ind;
+
+ malloc_mutex_t *stats_mtx;
+ pa_shard_stats_t *stats;
+
+ /* The emap this shard is tied to. */
+ emap_t *emap;
+
+ /* The base from which we get the ehooks and allocate metadat. */
+ base_t *base;
+};
+
+static inline bool
+pa_shard_dont_decay_muzzy(pa_shard_t *shard) {
+ return ecache_npages_get(&shard->pac.ecache_muzzy) == 0 &&
+ pac_decay_ms_get(&shard->pac, extent_state_muzzy) <= 0;
+}
+
+static inline ehooks_t *
+pa_shard_ehooks_get(pa_shard_t *shard) {
+ return base_ehooks_get(shard->base);
+}
+
+/* Returns true on error. */
+bool pa_central_init(pa_central_t *central, base_t *base, bool hpa,
+ hpa_hooks_t *hpa_hooks);
+
+/* Returns true on error. */
+bool pa_shard_init(tsdn_t *tsdn, pa_shard_t *shard, pa_central_t *central,
+ emap_t *emap, base_t *base, unsigned ind, pa_shard_stats_t *stats,
+ malloc_mutex_t *stats_mtx, nstime_t *cur_time, size_t oversize_threshold,
+ ssize_t dirty_decay_ms, ssize_t muzzy_decay_ms);
+
+/*
+ * This isn't exposed to users; we allow late enablement of the HPA shard so
+ * that we can boot without worrying about the HPA, then turn it on in a0.
+ */
+bool pa_shard_enable_hpa(tsdn_t *tsdn, pa_shard_t *shard,
+ const hpa_shard_opts_t *hpa_opts, const sec_opts_t *hpa_sec_opts);
+
+/*
+ * We stop using the HPA when custom extent hooks are installed, but still
+ * redirect deallocations to it.
+ */
+void pa_shard_disable_hpa(tsdn_t *tsdn, pa_shard_t *shard);
+
+/*
+ * This does the PA-specific parts of arena reset (i.e. freeing all active
+ * allocations).
+ */
+void pa_shard_reset(tsdn_t *tsdn, pa_shard_t *shard);
+
+/*
+ * Destroy all the remaining retained extents. Should only be called after
+ * decaying all active, dirty, and muzzy extents to the retained state, as the
+ * last step in destroying the shard.
+ */
+void pa_shard_destroy(tsdn_t *tsdn, pa_shard_t *shard);
+
+/* Gets an edata for the given allocation. */
+edata_t *pa_alloc(tsdn_t *tsdn, pa_shard_t *shard, size_t size,
+ size_t alignment, bool slab, szind_t szind, bool zero, bool guarded,
+ bool *deferred_work_generated);
+/* Returns true on error, in which case nothing changed. */
+bool pa_expand(tsdn_t *tsdn, pa_shard_t *shard, edata_t *edata, size_t old_size,
+ size_t new_size, szind_t szind, bool zero, bool *deferred_work_generated);
+/*
+ * The same. Sets *generated_dirty to true if we produced new dirty pages, and
+ * false otherwise.
+ */
+bool pa_shrink(tsdn_t *tsdn, pa_shard_t *shard, edata_t *edata, size_t old_size,
+ size_t new_size, szind_t szind, bool *deferred_work_generated);
+/*
+ * Frees the given edata back to the pa. Sets *generated_dirty if we produced
+ * new dirty pages (well, we always set it for now; but this need not be the
+ * case).
+ * (We could make generated_dirty the return value of course, but this is more
+ * consistent with the shrink pathway and our error codes here).
+ */
+void pa_dalloc(tsdn_t *tsdn, pa_shard_t *shard, edata_t *edata,
+ bool *deferred_work_generated);
+bool pa_decay_ms_set(tsdn_t *tsdn, pa_shard_t *shard, extent_state_t state,
+ ssize_t decay_ms, pac_purge_eagerness_t eagerness);
+ssize_t pa_decay_ms_get(pa_shard_t *shard, extent_state_t state);
+
+/*
+ * Do deferred work on this PA shard.
+ *
+ * Morally, this should do both PAC decay and the HPA deferred work. For now,
+ * though, the arena, background thread, and PAC modules are tightly interwoven
+ * in a way that's tricky to extricate, so we only do the HPA-specific parts.
+ */
+void pa_shard_set_deferral_allowed(tsdn_t *tsdn, pa_shard_t *shard,
+ bool deferral_allowed);
+void pa_shard_do_deferred_work(tsdn_t *tsdn, pa_shard_t *shard);
+void pa_shard_try_deferred_work(tsdn_t *tsdn, pa_shard_t *shard);
+uint64_t pa_shard_time_until_deferred_work(tsdn_t *tsdn, pa_shard_t *shard);
+
+/******************************************************************************/
+/*
+ * Various bits of "boring" functionality that are still part of this module,
+ * but that we relegate to pa_extra.c, to keep the core logic in pa.c as
+ * readable as possible.
+ */
+
+/*
+ * These fork phases are synchronized with the arena fork phase numbering to
+ * make it easy to keep straight. That's why there's no prefork1.
+ */
+void pa_shard_prefork0(tsdn_t *tsdn, pa_shard_t *shard);
+void pa_shard_prefork2(tsdn_t *tsdn, pa_shard_t *shard);
+void pa_shard_prefork3(tsdn_t *tsdn, pa_shard_t *shard);
+void pa_shard_prefork4(tsdn_t *tsdn, pa_shard_t *shard);
+void pa_shard_prefork5(tsdn_t *tsdn, pa_shard_t *shard);
+void pa_shard_postfork_parent(tsdn_t *tsdn, pa_shard_t *shard);
+void pa_shard_postfork_child(tsdn_t *tsdn, pa_shard_t *shard);
+
+void pa_shard_basic_stats_merge(pa_shard_t *shard, size_t *nactive,
+ size_t *ndirty, size_t *nmuzzy);
+
+void pa_shard_stats_merge(tsdn_t *tsdn, pa_shard_t *shard,
+ pa_shard_stats_t *pa_shard_stats_out, pac_estats_t *estats_out,
+ hpa_shard_stats_t *hpa_stats_out, sec_stats_t *sec_stats_out,
+ size_t *resident);
+
+/*
+ * Reads the PA-owned mutex stats into the output stats array, at the
+ * appropriate positions. Morally, these stats should really live in
+ * pa_shard_stats_t, but the indices are sort of baked into the various mutex
+ * prof macros. This would be a good thing to do at some point.
+ */
+void pa_shard_mtx_stats_read(tsdn_t *tsdn, pa_shard_t *shard,
+ mutex_prof_data_t mutex_prof_data[mutex_prof_num_arena_mutexes]);
+
+#endif /* JEMALLOC_INTERNAL_PA_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/pac.h b/contrib/jemalloc/include/jemalloc/internal/pac.h
new file mode 100644
index 000000000000..01c4e6afabb0
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/internal/pac.h
@@ -0,0 +1,179 @@
+#ifndef JEMALLOC_INTERNAL_PAC_H
+#define JEMALLOC_INTERNAL_PAC_H
+
+#include "jemalloc/internal/exp_grow.h"
+#include "jemalloc/internal/pai.h"
+#include "san_bump.h"
+
+
+/*
+ * Page allocator classic; an implementation of the PAI interface that:
+ * - Can be used for arenas with custom extent hooks.
+ * - Can always satisfy any allocation request (including highly-fragmentary
+ * ones).
+ * - Can use efficient OS-level zeroing primitives for demand-filled pages.
+ */
+
+/* How "eager" decay/purging should be. */
+enum pac_purge_eagerness_e {
+ PAC_PURGE_ALWAYS,
+ PAC_PURGE_NEVER,
+ PAC_PURGE_ON_EPOCH_ADVANCE
+};
+typedef enum pac_purge_eagerness_e pac_purge_eagerness_t;
+
+typedef struct pac_decay_stats_s pac_decay_stats_t;
+struct pac_decay_stats_s {
+ /* Total number of purge sweeps. */
+ locked_u64_t npurge;
+ /* Total number of madvise calls made. */
+ locked_u64_t nmadvise;
+ /* Total number of pages purged. */
+ locked_u64_t purged;
+};
+
+typedef struct pac_estats_s pac_estats_t;
+struct pac_estats_s {
+ /*
+ * Stats for a given index in the range [0, SC_NPSIZES] in the various
+ * ecache_ts.
+ * We track both bytes and # of extents: two extents in the same bucket
+ * may have different sizes if adjacent size classes differ by more than
+ * a page, so bytes cannot always be derived from # of extents.
+ */
+ size_t ndirty;
+ size_t dirty_bytes;
+ size_t nmuzzy;
+ size_t muzzy_bytes;
+ size_t nretained;
+ size_t retained_bytes;
+};
+
+typedef struct pac_stats_s pac_stats_t;
+struct pac_stats_s {
+ pac_decay_stats_t decay_dirty;
+ pac_decay_stats_t decay_muzzy;
+
+ /*
+ * Number of unused virtual memory bytes currently retained. Retained
+ * bytes are technically mapped (though always decommitted or purged),
+ * but they are excluded from the mapped statistic (above).
+ */
+ size_t retained; /* Derived. */
+
+ /*
+ * Number of bytes currently mapped, excluding retained memory (and any
+ * base-allocated memory, which is tracked by the arena stats).
+ *
+ * We name this "pac_mapped" to avoid confusion with the arena_stats
+ * "mapped".
+ */
+ atomic_zu_t pac_mapped;
+
+ /* VM space had to be leaked (undocumented). Normally 0. */
+ atomic_zu_t abandoned_vm;
+};
+
+typedef struct pac_s pac_t;
+struct pac_s {
+ /*
+ * Must be the first member (we convert it to a PAC given only a
+ * pointer). The handle to the allocation interface.
+ */
+ pai_t pai;
+ /*
+ * Collections of extents that were previously allocated. These are
+ * used when allocating extents, in an attempt to re-use address space.
+ *
+ * Synchronization: internal.
+ */
+ ecache_t ecache_dirty;
+ ecache_t ecache_muzzy;
+ ecache_t ecache_retained;
+
+ base_t *base;
+ emap_t *emap;
+ edata_cache_t *edata_cache;
+
+ /* The grow info for the retained ecache. */
+ exp_grow_t exp_grow;
+ malloc_mutex_t grow_mtx;
+
+ /* Special allocator for guarded frequently reused extents. */
+ san_bump_alloc_t sba;
+
+ /* How large extents should be before getting auto-purged. */
+ atomic_zu_t oversize_threshold;
+
+ /*
+ * Decay-based purging state, responsible for scheduling extent state
+ * transitions.
+ *
+ * Synchronization: via the internal mutex.
+ */
+ decay_t decay_dirty; /* dirty --> muzzy */
+ decay_t decay_muzzy; /* muzzy --> retained */
+
+ malloc_mutex_t *stats_mtx;
+ pac_stats_t *stats;
+
+ /* Extent serial number generator state. */
+ atomic_zu_t extent_sn_next;
+};
+
+bool pac_init(tsdn_t *tsdn, pac_t *pac, base_t *base, emap_t *emap,
+ edata_cache_t *edata_cache, nstime_t *cur_time, size_t oversize_threshold,
+ ssize_t dirty_decay_ms, ssize_t muzzy_decay_ms, pac_stats_t *pac_stats,
+ malloc_mutex_t *stats_mtx);
+
+static inline size_t
+pac_mapped(pac_t *pac) {
+ return atomic_load_zu(&pac->stats->pac_mapped, ATOMIC_RELAXED);
+}
+
+static inline ehooks_t *
+pac_ehooks_get(pac_t *pac) {
+ return base_ehooks_get(pac->base);
+}
+
+/*
+ * All purging functions require holding decay->mtx. This is one of the few
+ * places external modules are allowed to peek inside pa_shard_t internals.
+ */
+
+/*
+ * Decays the number of pages currently in the ecache. This might not leave the
+ * ecache empty if other threads are inserting dirty objects into it
+ * concurrently with the call.
+ */
+void pac_decay_all(tsdn_t *tsdn, pac_t *pac, decay_t *decay,
+ pac_decay_stats_t *decay_stats, ecache_t *ecache, bool fully_decay);
+/*
+ * Updates decay settings for the current time, and conditionally purges in
+ * response (depending on decay_purge_setting). Returns whether or not the
+ * epoch advanced.
+ */
+bool pac_maybe_decay_purge(tsdn_t *tsdn, pac_t *pac, decay_t *decay,
+ pac_decay_stats_t *decay_stats, ecache_t *ecache,
+ pac_purge_eagerness_t eagerness);
+
+/*
+ * Gets / sets the maximum amount that we'll grow an arena down the
+ * grow-retained pathways (unless forced to by an allocaction request).
+ *
+ * Set new_limit to NULL if it's just a query, or old_limit to NULL if you don't
+ * care about the previous value.
+ *
+ * Returns true on error (if the new limit is not valid).
+ */
+bool pac_retain_grow_limit_get_set(tsdn_t *tsdn, pac_t *pac, size_t *old_limit,
+ size_t *new_limit);
+
+bool pac_decay_ms_set(tsdn_t *tsdn, pac_t *pac, extent_state_t state,
+ ssize_t decay_ms, pac_purge_eagerness_t eagerness);
+ssize_t pac_decay_ms_get(pac_t *pac, extent_state_t state);
+
+void pac_reset(tsdn_t *tsdn, pac_t *pac);
+void pac_destroy(tsdn_t *tsdn, pac_t *pac);
+
+#endif /* JEMALLOC_INTERNAL_PAC_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/pages.h b/contrib/jemalloc/include/jemalloc/internal/pages.h
index 7dae633afe58..ad1f606a8a05 100644
--- a/contrib/jemalloc/include/jemalloc/internal/pages.h
+++ b/contrib/jemalloc/include/jemalloc/internal/pages.h
@@ -13,10 +13,27 @@
/* Return the smallest pagesize multiple that is >= s. */
#define PAGE_CEILING(s) \
(((s) + PAGE_MASK) & ~PAGE_MASK)
+/* Return the largest pagesize multiple that is <=s. */
+#define PAGE_FLOOR(s) \
+ ((s) & ~PAGE_MASK)
/* Huge page size. LG_HUGEPAGE is determined by the configure script. */
#define HUGEPAGE ((size_t)(1U << LG_HUGEPAGE))
#define HUGEPAGE_MASK ((size_t)(HUGEPAGE - 1))
+
+#if LG_HUGEPAGE != 0
+# define HUGEPAGE_PAGES (HUGEPAGE / PAGE)
+#else
+/*
+ * It's convenient to define arrays (or bitmaps) of HUGEPAGE_PAGES lengths. If
+ * we can't autodetect the hugepage size, it gets treated as 0, in which case
+ * we'll trigger a compiler error in those arrays. Avoid this case by ensuring
+ * that this value is at least 1. (We won't ever run in this degraded state;
+ * hpa_supported() returns false in this case.
+ */
+# define HUGEPAGE_PAGES 1
+#endif
+
/* Return the huge page base address for the huge page containing address a. */
#define HUGEPAGE_ADDR2BASE(a) \
((void *)((uintptr_t)(a) & ~HUGEPAGE_MASK))
@@ -58,6 +75,18 @@ static const bool pages_can_purge_forced =
#endif
;
+#if defined(JEMALLOC_HAVE_MADVISE_HUGE) || defined(JEMALLOC_HAVE_MEMCNTL)
+# define PAGES_CAN_HUGIFY
+#endif
+
+static const bool pages_can_hugify =
+#ifdef PAGES_CAN_HUGIFY
+ true
+#else
+ false
+#endif
+ ;
+
typedef enum {
thp_mode_default = 0, /* Do not change hugepage settings. */
thp_mode_always = 1, /* Always set MADV_HUGEPAGE. */
@@ -84,5 +113,7 @@ bool pages_dontdump(void *addr, size_t size);
bool pages_dodump(void *addr, size_t size);
bool pages_boot(void);
void pages_set_thp_state (void *ptr, size_t size);
+void pages_mark_guards(void *head, void *tail);
+void pages_unmark_guards(void *head, void *tail);
#endif /* JEMALLOC_INTERNAL_PAGES_EXTERNS_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/pai.h b/contrib/jemalloc/include/jemalloc/internal/pai.h
new file mode 100644
index 000000000000..d978cd7d25ec
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/internal/pai.h
@@ -0,0 +1,95 @@
+#ifndef JEMALLOC_INTERNAL_PAI_H
+#define JEMALLOC_INTERNAL_PAI_H
+
+/* An interface for page allocation. */
+
+typedef struct pai_s pai_t;
+struct pai_s {
+ /* Returns NULL on failure. */
+ edata_t *(*alloc)(tsdn_t *tsdn, pai_t *self, size_t size,
+ size_t alignment, bool zero, bool guarded, bool frequent_reuse,
+ bool *deferred_work_generated);
+ /*
+ * Returns the number of extents added to the list (which may be fewer
+ * than requested, in case of OOM). The list should already be
+ * initialized. The only alignment guarantee is page-alignment, and
+ * the results are not necessarily zeroed.
+ */
+ size_t (*alloc_batch)(tsdn_t *tsdn, pai_t *self, size_t size,
+ size_t nallocs, edata_list_active_t *results,
+ bool *deferred_work_generated);
+ bool (*expand)(tsdn_t *tsdn, pai_t *self, edata_t *edata,
+ size_t old_size, size_t new_size, bool zero,
+ bool *deferred_work_generated);
+ bool (*shrink)(tsdn_t *tsdn, pai_t *self, edata_t *edata,
+ size_t old_size, size_t new_size, bool *deferred_work_generated);
+ void (*dalloc)(tsdn_t *tsdn, pai_t *self, edata_t *edata,
+ bool *deferred_work_generated);
+ /* This function empties out list as a side-effect of being called. */
+ void (*dalloc_batch)(tsdn_t *tsdn, pai_t *self,
+ edata_list_active_t *list, bool *deferred_work_generated);
+ uint64_t (*time_until_deferred_work)(tsdn_t *tsdn, pai_t *self);
+};
+
+/*
+ * These are just simple convenience functions to avoid having to reference the
+ * same pai_t twice on every invocation.
+ */
+
+static inline edata_t *
+pai_alloc(tsdn_t *tsdn, pai_t *self, size_t size, size_t alignment,
+ bool zero, bool guarded, bool frequent_reuse,
+ bool *deferred_work_generated) {
+ return self->alloc(tsdn, self, size, alignment, zero, guarded,
+ frequent_reuse, deferred_work_generated);
+}
+
+static inline size_t
+pai_alloc_batch(tsdn_t *tsdn, pai_t *self, size_t size, size_t nallocs,
+ edata_list_active_t *results, bool *deferred_work_generated) {
+ return self->alloc_batch(tsdn, self, size, nallocs, results,
+ deferred_work_generated);
+}
+
+static inline bool
+pai_expand(tsdn_t *tsdn, pai_t *self, edata_t *edata, size_t old_size,
+ size_t new_size, bool zero, bool *deferred_work_generated) {
+ return self->expand(tsdn, self, edata, old_size, new_size, zero,
+ deferred_work_generated);
+}
+
+static inline bool
+pai_shrink(tsdn_t *tsdn, pai_t *self, edata_t *edata, size_t old_size,
+ size_t new_size, bool *deferred_work_generated) {
+ return self->shrink(tsdn, self, edata, old_size, new_size,
+ deferred_work_generated);
+}
+
+static inline void
+pai_dalloc(tsdn_t *tsdn, pai_t *self, edata_t *edata,
+ bool *deferred_work_generated) {
+ self->dalloc(tsdn, self, edata, deferred_work_generated);
+}
+
+static inline void
+pai_dalloc_batch(tsdn_t *tsdn, pai_t *self, edata_list_active_t *list,
+ bool *deferred_work_generated) {
+ self->dalloc_batch(tsdn, self, list, deferred_work_generated);
+}
+
+static inline uint64_t
+pai_time_until_deferred_work(tsdn_t *tsdn, pai_t *self) {
+ return self->time_until_deferred_work(tsdn, self);
+}
+
+/*
+ * An implementation of batch allocation that simply calls alloc once for
+ * each item in the list.
+ */
+size_t pai_alloc_batch_default(tsdn_t *tsdn, pai_t *self, size_t size,
+ size_t nallocs, edata_list_active_t *results, bool *deferred_work_generated);
+/* Ditto, for dalloc. */
+void pai_dalloc_batch_default(tsdn_t *tsdn, pai_t *self,
+ edata_list_active_t *list, bool *deferred_work_generated);
+
+#endif /* JEMALLOC_INTERNAL_PAI_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/peak.h b/contrib/jemalloc/include/jemalloc/internal/peak.h
new file mode 100644
index 000000000000..59da3e41b6b7
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/internal/peak.h
@@ -0,0 +1,37 @@
+#ifndef JEMALLOC_INTERNAL_PEAK_H
+#define JEMALLOC_INTERNAL_PEAK_H
+
+typedef struct peak_s peak_t;
+struct peak_s {
+ /* The highest recorded peak value, after adjustment (see below). */
+ uint64_t cur_max;
+ /*
+ * The difference between alloc and dalloc at the last set_zero call;
+ * this lets us cancel out the appropriate amount of excess.
+ */
+ uint64_t adjustment;
+};
+
+#define PEAK_INITIALIZER {0, 0}
+
+static inline uint64_t
+peak_max(peak_t *peak) {
+ return peak->cur_max;
+}
+
+static inline void
+peak_update(peak_t *peak, uint64_t alloc, uint64_t dalloc) {
+ int64_t candidate_max = (int64_t)(alloc - dalloc - peak->adjustment);
+ if (candidate_max > (int64_t)peak->cur_max) {
+ peak->cur_max = candidate_max;
+ }
+}
+
+/* Resets the counter to zero; all peaks are now relative to this point. */
+static inline void
+peak_set_zero(peak_t *peak, uint64_t alloc, uint64_t dalloc) {
+ peak->cur_max = 0;
+ peak->adjustment = alloc - dalloc;
+}
+
+#endif /* JEMALLOC_INTERNAL_PEAK_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/peak_event.h b/contrib/jemalloc/include/jemalloc/internal/peak_event.h
new file mode 100644
index 000000000000..b808ce043299
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/internal/peak_event.h
@@ -0,0 +1,24 @@
+#ifndef JEMALLOC_INTERNAL_PEAK_EVENT_H
+#define JEMALLOC_INTERNAL_PEAK_EVENT_H
+
+/*
+ * While peak.h contains the simple helper struct that tracks state, this
+ * contains the allocator tie-ins (and knows about tsd, the event module, etc.).
+ */
+
+/* Update the peak with current tsd state. */
+void peak_event_update(tsd_t *tsd);
+/* Set current state to zero. */
+void peak_event_zero(tsd_t *tsd);
+uint64_t peak_event_max(tsd_t *tsd);
+
+/* Manual hooks. */
+/* The activity-triggered hooks. */
+uint64_t peak_alloc_new_event_wait(tsd_t *tsd);
+uint64_t peak_alloc_postponed_event_wait(tsd_t *tsd);
+void peak_alloc_event_handler(tsd_t *tsd, uint64_t elapsed);
+uint64_t peak_dalloc_new_event_wait(tsd_t *tsd);
+uint64_t peak_dalloc_postponed_event_wait(tsd_t *tsd);
+void peak_dalloc_event_handler(tsd_t *tsd, uint64_t elapsed);
+
+#endif /* JEMALLOC_INTERNAL_PEAK_EVENT_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/ph.h b/contrib/jemalloc/include/jemalloc/internal/ph.h
index 84d6778a906e..5f091c5fbb0c 100644
--- a/contrib/jemalloc/include/jemalloc/internal/ph.h
+++ b/contrib/jemalloc/include/jemalloc/internal/ph.h
@@ -1,3 +1,6 @@
+#ifndef JEMALLOC_INTERNAL_PH_H
+#define JEMALLOC_INTERNAL_PH_H
+
/*
* A Pairing Heap implementation.
*
@@ -10,382 +13,508 @@
* http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.106.2988&rep=rep1&type=pdf
*
*******************************************************************************
+ *
+ * We include a non-obvious optimization:
+ * - First, we introduce a new pop-and-link operation; pop the two most
+ * recently-inserted items off the aux-list, link them, and push the resulting
+ * heap.
+ * - We maintain a count of the number of insertions since the last time we
+ * merged the aux-list (i.e. via first() or remove_first()). After N inserts,
+ * we do ffs(N) pop-and-link operations.
+ *
+ * One way to think of this is that we're progressively building up a tree in
+ * the aux-list, rather than a linked-list (think of the series of merges that
+ * will be performed as the aux-count grows).
+ *
+ * There's a couple reasons we benefit from this:
+ * - Ordinarily, after N insertions, the aux-list is of size N. With our
+ * strategy, it's of size O(log(N)). So we decrease the worst-case time of
+ * first() calls, and reduce the average cost of remove_min calls. Since
+ * these almost always occur while holding a lock, we practically reduce the
+ * frequency of unusually long hold times.
+ * - This moves the bulk of the work of merging the aux-list onto the threads
+ * that are inserting into the heap. In some common scenarios, insertions
+ * happen in bulk, from a single thread (think tcache flushing; we potentially
+ * move many slabs from slabs_full to slabs_nonfull). All the nodes in this
+ * case are in the inserting threads cache, and linking them is very cheap
+ * (cache misses dominate linking cost). Without this optimization, linking
+ * happens on the next call to remove_first. Since that remove_first call
+ * likely happens on a different thread (or at least, after the cache has
+ * gotten cold if done on the same thread), deferring linking trades cheap
+ * link operations now for expensive ones later.
+ *
+ * The ffs trick keeps amortized insert cost at constant time. Similar
+ * strategies based on periodically sorting the list after a batch of operations
+ * perform worse than this in practice, even with various fancy tricks; they
+ * all took amortized complexity of an insert from O(1) to O(log(n)).
*/
-#ifndef PH_H_
-#define PH_H_
+typedef int (*ph_cmp_t)(void *, void *);
/* Node structure. */
-#define phn(a_type) \
-struct { \
- a_type *phn_prev; \
- a_type *phn_next; \
- a_type *phn_lchild; \
+typedef struct phn_link_s phn_link_t;
+struct phn_link_s {
+ void *prev;
+ void *next;
+ void *lchild;
+};
+
+typedef struct ph_s ph_t;
+struct ph_s {
+ void *root;
+ /*
+ * Inserts done since the last aux-list merge. This is not necessarily
+ * the size of the aux-list, since it's possible that removals have
+ * happened since, and we don't track whether or not those removals are
+ * from the aux list.
+ */
+ size_t auxcount;
+};
+
+JEMALLOC_ALWAYS_INLINE phn_link_t *
+phn_link_get(void *phn, size_t offset) {
+ return (phn_link_t *)(((uintptr_t)phn) + offset);
}
-/* Root structure. */
-#define ph(a_type) \
-struct { \
- a_type *ph_root; \
+JEMALLOC_ALWAYS_INLINE void
+phn_link_init(void *phn, size_t offset) {
+ phn_link_get(phn, offset)->prev = NULL;
+ phn_link_get(phn, offset)->next = NULL;
+ phn_link_get(phn, offset)->lchild = NULL;
}
-/* Internal utility macros. */
-#define phn_lchild_get(a_type, a_field, a_phn) \
- (a_phn->a_field.phn_lchild)
-#define phn_lchild_set(a_type, a_field, a_phn, a_lchild) do { \
- a_phn->a_field.phn_lchild = a_lchild; \
-} while (0)
-
-#define phn_next_get(a_type, a_field, a_phn) \
- (a_phn->a_field.phn_next)
-#define phn_prev_set(a_type, a_field, a_phn, a_prev) do { \
- a_phn->a_field.phn_prev = a_prev; \
-} while (0)
-
-#define phn_prev_get(a_type, a_field, a_phn) \
- (a_phn->a_field.phn_prev)
-#define phn_next_set(a_type, a_field, a_phn, a_next) do { \
- a_phn->a_field.phn_next = a_next; \
-} while (0)
-
-#define phn_merge_ordered(a_type, a_field, a_phn0, a_phn1, a_cmp) do { \
- a_type *phn0child; \
- \
- assert(a_phn0 != NULL); \
- assert(a_phn1 != NULL); \
- assert(a_cmp(a_phn0, a_phn1) <= 0); \
- \
- phn_prev_set(a_type, a_field, a_phn1, a_phn0); \
- phn0child = phn_lchild_get(a_type, a_field, a_phn0); \
- phn_next_set(a_type, a_field, a_phn1, phn0child); \
- if (phn0child != NULL) { \
- phn_prev_set(a_type, a_field, phn0child, a_phn1); \
- } \
- phn_lchild_set(a_type, a_field, a_phn0, a_phn1); \
-} while (0)
-
-#define phn_merge(a_type, a_field, a_phn0, a_phn1, a_cmp, r_phn) do { \
- if (a_phn0 == NULL) { \
- r_phn = a_phn1; \
- } else if (a_phn1 == NULL) { \
- r_phn = a_phn0; \
- } else if (a_cmp(a_phn0, a_phn1) < 0) { \
- phn_merge_ordered(a_type, a_field, a_phn0, a_phn1, \
- a_cmp); \
- r_phn = a_phn0; \
- } else { \
- phn_merge_ordered(a_type, a_field, a_phn1, a_phn0, \
- a_cmp); \
- r_phn = a_phn1; \
- } \
-} while (0)
+/* Internal utility helpers. */
+JEMALLOC_ALWAYS_INLINE void *
+phn_lchild_get(void *phn, size_t offset) {
+ return phn_link_get(phn, offset)->lchild;
+}
+
+JEMALLOC_ALWAYS_INLINE void
+phn_lchild_set(void *phn, void *lchild, size_t offset) {
+ phn_link_get(phn, offset)->lchild = lchild;
+}
+
+JEMALLOC_ALWAYS_INLINE void *
+phn_next_get(void *phn, size_t offset) {
+ return phn_link_get(phn, offset)->next;
+}
+
+JEMALLOC_ALWAYS_INLINE void
+phn_next_set(void *phn, void *next, size_t offset) {
+ phn_link_get(phn, offset)->next = next;
+}
+
+JEMALLOC_ALWAYS_INLINE void *
+phn_prev_get(void *phn, size_t offset) {
+ return phn_link_get(phn, offset)->prev;
+}
+
+JEMALLOC_ALWAYS_INLINE void
+phn_prev_set(void *phn, void *prev, size_t offset) {
+ phn_link_get(phn, offset)->prev = prev;
+}
+
+JEMALLOC_ALWAYS_INLINE void
+phn_merge_ordered(void *phn0, void *phn1, size_t offset,
+ ph_cmp_t cmp) {
+ void *phn0child;
+
+ assert(phn0 != NULL);
+ assert(phn1 != NULL);
+ assert(cmp(phn0, phn1) <= 0);
+
+ phn_prev_set(phn1, phn0, offset);
+ phn0child = phn_lchild_get(phn0, offset);
+ phn_next_set(phn1, phn0child, offset);
+ if (phn0child != NULL) {
+ phn_prev_set(phn0child, phn1, offset);
+ }
+ phn_lchild_set(phn0, phn1, offset);
+}
+
+JEMALLOC_ALWAYS_INLINE void *
+phn_merge(void *phn0, void *phn1, size_t offset, ph_cmp_t cmp) {
+ void *result;
+ if (phn0 == NULL) {
+ result = phn1;
+ } else if (phn1 == NULL) {
+ result = phn0;
+ } else if (cmp(phn0, phn1) < 0) {
+ phn_merge_ordered(phn0, phn1, offset, cmp);
+ result = phn0;
+ } else {
+ phn_merge_ordered(phn1, phn0, offset, cmp);
+ result = phn1;
+ }
+ return result;
+}
+
+JEMALLOC_ALWAYS_INLINE void *
+phn_merge_siblings(void *phn, size_t offset, ph_cmp_t cmp) {
+ void *head = NULL;
+ void *tail = NULL;
+ void *phn0 = phn;
+ void *phn1 = phn_next_get(phn0, offset);
+
+ /*
+ * Multipass merge, wherein the first two elements of a FIFO
+ * are repeatedly merged, and each result is appended to the
+ * singly linked FIFO, until the FIFO contains only a single
+ * element. We start with a sibling list but no reference to
+ * its tail, so we do a single pass over the sibling list to
+ * populate the FIFO.
+ */
+ if (phn1 != NULL) {
+ void *phnrest = phn_next_get(phn1, offset);
+ if (phnrest != NULL) {
+ phn_prev_set(phnrest, NULL, offset);
+ }
+ phn_prev_set(phn0, NULL, offset);
+ phn_next_set(phn0, NULL, offset);
+ phn_prev_set(phn1, NULL, offset);
+ phn_next_set(phn1, NULL, offset);
+ phn0 = phn_merge(phn0, phn1, offset, cmp);
+ head = tail = phn0;
+ phn0 = phnrest;
+ while (phn0 != NULL) {
+ phn1 = phn_next_get(phn0, offset);
+ if (phn1 != NULL) {
+ phnrest = phn_next_get(phn1, offset);
+ if (phnrest != NULL) {
+ phn_prev_set(phnrest, NULL, offset);
+ }
+ phn_prev_set(phn0, NULL, offset);
+ phn_next_set(phn0, NULL, offset);
+ phn_prev_set(phn1, NULL, offset);
+ phn_next_set(phn1, NULL, offset);
+ phn0 = phn_merge(phn0, phn1, offset, cmp);
+ phn_next_set(tail, phn0, offset);
+ tail = phn0;
+ phn0 = phnrest;
+ } else {
+ phn_next_set(tail, phn0, offset);
+ tail = phn0;
+ phn0 = NULL;
+ }
+ }
+ phn0 = head;
+ phn1 = phn_next_get(phn0, offset);
+ if (phn1 != NULL) {
+ while (true) {
+ head = phn_next_get(phn1, offset);
+ assert(phn_prev_get(phn0, offset) == NULL);
+ phn_next_set(phn0, NULL, offset);
+ assert(phn_prev_get(phn1, offset) == NULL);
+ phn_next_set(phn1, NULL, offset);
+ phn0 = phn_merge(phn0, phn1, offset, cmp);
+ if (head == NULL) {
+ break;
+ }
+ phn_next_set(tail, phn0, offset);
+ tail = phn0;
+ phn0 = head;
+ phn1 = phn_next_get(phn0, offset);
+ }
+ }
+ }
+ return phn0;
+}
+
+JEMALLOC_ALWAYS_INLINE void
+ph_merge_aux(ph_t *ph, size_t offset, ph_cmp_t cmp) {
+ ph->auxcount = 0;
+ void *phn = phn_next_get(ph->root, offset);
+ if (phn != NULL) {
+ phn_prev_set(ph->root, NULL, offset);
+ phn_next_set(ph->root, NULL, offset);
+ phn_prev_set(phn, NULL, offset);
+ phn = phn_merge_siblings(phn, offset, cmp);
+ assert(phn_next_get(phn, offset) == NULL);
+ ph->root = phn_merge(ph->root, phn, offset, cmp);
+ }
+}
+
+JEMALLOC_ALWAYS_INLINE void *
+ph_merge_children(void *phn, size_t offset, ph_cmp_t cmp) {
+ void *result;
+ void *lchild = phn_lchild_get(phn, offset);
+ if (lchild == NULL) {
+ result = NULL;
+ } else {
+ result = phn_merge_siblings(lchild, offset, cmp);
+ }
+ return result;
+}
+
+JEMALLOC_ALWAYS_INLINE void
+ph_new(ph_t *ph) {
+ ph->root = NULL;
+ ph->auxcount = 0;
+}
+
+JEMALLOC_ALWAYS_INLINE bool
+ph_empty(ph_t *ph) {
+ return ph->root == NULL;
+}
+
+JEMALLOC_ALWAYS_INLINE void *
+ph_first(ph_t *ph, size_t offset, ph_cmp_t cmp) {
+ if (ph->root == NULL) {
+ return NULL;
+ }
+ ph_merge_aux(ph, offset, cmp);
+ return ph->root;
+}
+
+JEMALLOC_ALWAYS_INLINE void *
+ph_any(ph_t *ph, size_t offset) {
+ if (ph->root == NULL) {
+ return NULL;
+ }
+ void *aux = phn_next_get(ph->root, offset);
+ if (aux != NULL) {
+ return aux;
+ }
+ return ph->root;
+}
+
+/* Returns true if we should stop trying to merge. */
+JEMALLOC_ALWAYS_INLINE bool
+ph_try_aux_merge_pair(ph_t *ph, size_t offset, ph_cmp_t cmp) {
+ assert(ph->root != NULL);
+ void *phn0 = phn_next_get(ph->root, offset);
+ if (phn0 == NULL) {
+ return true;
+ }
+ void *phn1 = phn_next_get(phn0, offset);
+ if (phn1 == NULL) {
+ return true;
+ }
+ void *next_phn1 = phn_next_get(phn1, offset);
+ phn_next_set(phn0, NULL, offset);
+ phn_prev_set(phn0, NULL, offset);
+ phn_next_set(phn1, NULL, offset);
+ phn_prev_set(phn1, NULL, offset);
+ phn0 = phn_merge(phn0, phn1, offset, cmp);
+ phn_next_set(phn0, next_phn1, offset);
+ if (next_phn1 != NULL) {
+ phn_prev_set(next_phn1, phn0, offset);
+ }
+ phn_next_set(ph->root, phn0, offset);
+ phn_prev_set(phn0, ph->root, offset);
+ return next_phn1 == NULL;
+}
+
+JEMALLOC_ALWAYS_INLINE void
+ph_insert(ph_t *ph, void *phn, size_t offset, ph_cmp_t cmp) {
+ phn_link_init(phn, offset);
-#define ph_merge_siblings(a_type, a_field, a_phn, a_cmp, r_phn) do { \
- a_type *head = NULL; \
- a_type *tail = NULL; \
- a_type *phn0 = a_phn; \
- a_type *phn1 = phn_next_get(a_type, a_field, phn0); \
+ /*
+ * Treat the root as an aux list during insertion, and lazily merge
+ * during a_prefix##remove_first(). For elements that are inserted,
+ * then removed via a_prefix##remove() before the aux list is ever
+ * processed, this makes insert/remove constant-time, whereas eager
+ * merging would make insert O(log n).
+ */
+ if (ph->root == NULL) {
+ ph->root = phn;
+ } else {
+ /*
+ * As a special case, check to see if we can replace the root.
+ * This is practically common in some important cases, and lets
+ * us defer some insertions (hopefully, until the point where
+ * some of the items in the aux list have been removed, savings
+ * us from linking them at all).
+ */
+ if (cmp(phn, ph->root) < 0) {
+ phn_lchild_set(phn, ph->root, offset);
+ phn_prev_set(ph->root, phn, offset);
+ ph->root = phn;
+ ph->auxcount = 0;
+ return;
+ }
+ ph->auxcount++;
+ phn_next_set(phn, phn_next_get(ph->root, offset), offset);
+ if (phn_next_get(ph->root, offset) != NULL) {
+ phn_prev_set(phn_next_get(ph->root, offset), phn,
+ offset);
+ }
+ phn_prev_set(phn, ph->root, offset);
+ phn_next_set(ph->root, phn, offset);
+ }
+ if (ph->auxcount > 1) {
+ unsigned nmerges = ffs_zu(ph->auxcount - 1);
+ bool done = false;
+ for (unsigned i = 0; i < nmerges && !done; i++) {
+ done = ph_try_aux_merge_pair(ph, offset, cmp);
+ }
+ }
+}
+
+JEMALLOC_ALWAYS_INLINE void *
+ph_remove_first(ph_t *ph, size_t offset, ph_cmp_t cmp) {
+ void *ret;
+
+ if (ph->root == NULL) {
+ return NULL;
+ }
+ ph_merge_aux(ph, offset, cmp);
+ ret = ph->root;
+ ph->root = ph_merge_children(ph->root, offset, cmp);
+
+ return ret;
+
+}
+
+JEMALLOC_ALWAYS_INLINE void
+ph_remove(ph_t *ph, void *phn, size_t offset, ph_cmp_t cmp) {
+ void *replace;
+ void *parent;
+
+ if (ph->root == phn) {
+ /*
+ * We can delete from aux list without merging it, but we need
+ * to merge if we are dealing with the root node and it has
+ * children.
+ */
+ if (phn_lchild_get(phn, offset) == NULL) {
+ ph->root = phn_next_get(phn, offset);
+ if (ph->root != NULL) {
+ phn_prev_set(ph->root, NULL, offset);
+ }
+ return;
+ }
+ ph_merge_aux(ph, offset, cmp);
+ if (ph->root == phn) {
+ ph->root = ph_merge_children(ph->root, offset, cmp);
+ return;
+ }
+ }
+
+ /* Get parent (if phn is leftmost child) before mutating. */
+ if ((parent = phn_prev_get(phn, offset)) != NULL) {
+ if (phn_lchild_get(parent, offset) != phn) {
+ parent = NULL;
+ }
+ }
+ /* Find a possible replacement node, and link to parent. */
+ replace = ph_merge_children(phn, offset, cmp);
+ /* Set next/prev for sibling linked list. */
+ if (replace != NULL) {
+ if (parent != NULL) {
+ phn_prev_set(replace, parent, offset);
+ phn_lchild_set(parent, replace, offset);
+ } else {
+ phn_prev_set(replace, phn_prev_get(phn, offset),
+ offset);
+ if (phn_prev_get(phn, offset) != NULL) {
+ phn_next_set(phn_prev_get(phn, offset), replace,
+ offset);
+ }
+ }
+ phn_next_set(replace, phn_next_get(phn, offset), offset);
+ if (phn_next_get(phn, offset) != NULL) {
+ phn_prev_set(phn_next_get(phn, offset), replace,
+ offset);
+ }
+ } else {
+ if (parent != NULL) {
+ void *next = phn_next_get(phn, offset);
+ phn_lchild_set(parent, next, offset);
+ if (next != NULL) {
+ phn_prev_set(next, parent, offset);
+ }
+ } else {
+ assert(phn_prev_get(phn, offset) != NULL);
+ phn_next_set(
+ phn_prev_get(phn, offset),
+ phn_next_get(phn, offset), offset);
+ }
+ if (phn_next_get(phn, offset) != NULL) {
+ phn_prev_set(
+ phn_next_get(phn, offset),
+ phn_prev_get(phn, offset), offset);
+ }
+ }
+}
+
+#define ph_structs(a_prefix, a_type) \
+typedef struct { \
+ phn_link_t link; \
+} a_prefix##_link_t; \
\
- /* \
- * Multipass merge, wherein the first two elements of a FIFO \
- * are repeatedly merged, and each result is appended to the \
- * singly linked FIFO, until the FIFO contains only a single \
- * element. We start with a sibling list but no reference to \
- * its tail, so we do a single pass over the sibling list to \
- * populate the FIFO. \
- */ \
- if (phn1 != NULL) { \
- a_type *phnrest = phn_next_get(a_type, a_field, phn1); \
- if (phnrest != NULL) { \
- phn_prev_set(a_type, a_field, phnrest, NULL); \
- } \
- phn_prev_set(a_type, a_field, phn0, NULL); \
- phn_next_set(a_type, a_field, phn0, NULL); \
- phn_prev_set(a_type, a_field, phn1, NULL); \
- phn_next_set(a_type, a_field, phn1, NULL); \
- phn_merge(a_type, a_field, phn0, phn1, a_cmp, phn0); \
- head = tail = phn0; \
- phn0 = phnrest; \
- while (phn0 != NULL) { \
- phn1 = phn_next_get(a_type, a_field, phn0); \
- if (phn1 != NULL) { \
- phnrest = phn_next_get(a_type, a_field, \
- phn1); \
- if (phnrest != NULL) { \
- phn_prev_set(a_type, a_field, \
- phnrest, NULL); \
- } \
- phn_prev_set(a_type, a_field, phn0, \
- NULL); \
- phn_next_set(a_type, a_field, phn0, \
- NULL); \
- phn_prev_set(a_type, a_field, phn1, \
- NULL); \
- phn_next_set(a_type, a_field, phn1, \
- NULL); \
- phn_merge(a_type, a_field, phn0, phn1, \
- a_cmp, phn0); \
- phn_next_set(a_type, a_field, tail, \
- phn0); \
- tail = phn0; \
- phn0 = phnrest; \
- } else { \
- phn_next_set(a_type, a_field, tail, \
- phn0); \
- tail = phn0; \
- phn0 = NULL; \
- } \
- } \
- phn0 = head; \
- phn1 = phn_next_get(a_type, a_field, phn0); \
- if (phn1 != NULL) { \
- while (true) { \
- head = phn_next_get(a_type, a_field, \
- phn1); \
- assert(phn_prev_get(a_type, a_field, \
- phn0) == NULL); \
- phn_next_set(a_type, a_field, phn0, \
- NULL); \
- assert(phn_prev_get(a_type, a_field, \
- phn1) == NULL); \
- phn_next_set(a_type, a_field, phn1, \
- NULL); \
- phn_merge(a_type, a_field, phn0, phn1, \
- a_cmp, phn0); \
- if (head == NULL) { \
- break; \
- } \
- phn_next_set(a_type, a_field, tail, \
- phn0); \
- tail = phn0; \
- phn0 = head; \
- phn1 = phn_next_get(a_type, a_field, \
- phn0); \
- } \
- } \
- } \
- r_phn = phn0; \
-} while (0)
-
-#define ph_merge_aux(a_type, a_field, a_ph, a_cmp) do { \
- a_type *phn = phn_next_get(a_type, a_field, a_ph->ph_root); \
- if (phn != NULL) { \
- phn_prev_set(a_type, a_field, a_ph->ph_root, NULL); \
- phn_next_set(a_type, a_field, a_ph->ph_root, NULL); \
- phn_prev_set(a_type, a_field, phn, NULL); \
- ph_merge_siblings(a_type, a_field, phn, a_cmp, phn); \
- assert(phn_next_get(a_type, a_field, phn) == NULL); \
- phn_merge(a_type, a_field, a_ph->ph_root, phn, a_cmp, \
- a_ph->ph_root); \
- } \
-} while (0)
-
-#define ph_merge_children(a_type, a_field, a_phn, a_cmp, r_phn) do { \
- a_type *lchild = phn_lchild_get(a_type, a_field, a_phn); \
- if (lchild == NULL) { \
- r_phn = NULL; \
- } else { \
- ph_merge_siblings(a_type, a_field, lchild, a_cmp, \
- r_phn); \
- } \
-} while (0)
+typedef struct { \
+ ph_t ph; \
+} a_prefix##_t;
/*
* The ph_proto() macro generates function prototypes that correspond to the
* functions generated by an equivalently parameterized call to ph_gen().
*/
-#define ph_proto(a_attr, a_prefix, a_ph_type, a_type) \
-a_attr void a_prefix##new(a_ph_type *ph); \
-a_attr bool a_prefix##empty(a_ph_type *ph); \
-a_attr a_type *a_prefix##first(a_ph_type *ph); \
-a_attr a_type *a_prefix##any(a_ph_type *ph); \
-a_attr void a_prefix##insert(a_ph_type *ph, a_type *phn); \
-a_attr a_type *a_prefix##remove_first(a_ph_type *ph); \
-a_attr a_type *a_prefix##remove_any(a_ph_type *ph); \
-a_attr void a_prefix##remove(a_ph_type *ph, a_type *phn);
+#define ph_proto(a_attr, a_prefix, a_type) \
+ \
+a_attr void a_prefix##_new(a_prefix##_t *ph); \
+a_attr bool a_prefix##_empty(a_prefix##_t *ph); \
+a_attr a_type *a_prefix##_first(a_prefix##_t *ph); \
+a_attr a_type *a_prefix##_any(a_prefix##_t *ph); \
+a_attr void a_prefix##_insert(a_prefix##_t *ph, a_type *phn); \
+a_attr a_type *a_prefix##_remove_first(a_prefix##_t *ph); \
+a_attr void a_prefix##_remove(a_prefix##_t *ph, a_type *phn); \
+a_attr a_type *a_prefix##_remove_any(a_prefix##_t *ph);
-/*
- * The ph_gen() macro generates a type-specific pairing heap implementation,
- * based on the above cpp macros.
- */
-#define ph_gen(a_attr, a_prefix, a_ph_type, a_type, a_field, a_cmp) \
+/* The ph_gen() macro generates a type-specific pairing heap implementation. */
+#define ph_gen(a_attr, a_prefix, a_type, a_field, a_cmp) \
+JEMALLOC_ALWAYS_INLINE int \
+a_prefix##_ph_cmp(void *a, void *b) { \
+ return a_cmp((a_type *)a, (a_type *)b); \
+} \
+ \
a_attr void \
-a_prefix##new(a_ph_type *ph) { \
- memset(ph, 0, sizeof(ph(a_type))); \
+a_prefix##_new(a_prefix##_t *ph) { \
+ ph_new(&ph->ph); \
} \
+ \
a_attr bool \
-a_prefix##empty(a_ph_type *ph) { \
- return (ph->ph_root == NULL); \
+a_prefix##_empty(a_prefix##_t *ph) { \
+ return ph_empty(&ph->ph); \
} \
+ \
a_attr a_type * \
-a_prefix##first(a_ph_type *ph) { \
- if (ph->ph_root == NULL) { \
- return NULL; \
- } \
- ph_merge_aux(a_type, a_field, ph, a_cmp); \
- return ph->ph_root; \
+a_prefix##_first(a_prefix##_t *ph) { \
+ return ph_first(&ph->ph, offsetof(a_type, a_field), \
+ &a_prefix##_ph_cmp); \
} \
+ \
a_attr a_type * \
-a_prefix##any(a_ph_type *ph) { \
- if (ph->ph_root == NULL) { \
- return NULL; \
- } \
- a_type *aux = phn_next_get(a_type, a_field, ph->ph_root); \
- if (aux != NULL) { \
- return aux; \
- } \
- return ph->ph_root; \
+a_prefix##_any(a_prefix##_t *ph) { \
+ return ph_any(&ph->ph, offsetof(a_type, a_field)); \
} \
-a_attr void \
-a_prefix##insert(a_ph_type *ph, a_type *phn) { \
- memset(&phn->a_field, 0, sizeof(phn(a_type))); \
\
- /* \
- * Treat the root as an aux list during insertion, and lazily \
- * merge during a_prefix##remove_first(). For elements that \
- * are inserted, then removed via a_prefix##remove() before the \
- * aux list is ever processed, this makes insert/remove \
- * constant-time, whereas eager merging would make insert \
- * O(log n). \
- */ \
- if (ph->ph_root == NULL) { \
- ph->ph_root = phn; \
- } else { \
- phn_next_set(a_type, a_field, phn, phn_next_get(a_type, \
- a_field, ph->ph_root)); \
- if (phn_next_get(a_type, a_field, ph->ph_root) != \
- NULL) { \
- phn_prev_set(a_type, a_field, \
- phn_next_get(a_type, a_field, ph->ph_root), \
- phn); \
- } \
- phn_prev_set(a_type, a_field, phn, ph->ph_root); \
- phn_next_set(a_type, a_field, ph->ph_root, phn); \
- } \
+a_attr void \
+a_prefix##_insert(a_prefix##_t *ph, a_type *phn) { \
+ ph_insert(&ph->ph, phn, offsetof(a_type, a_field), \
+ a_prefix##_ph_cmp); \
} \
-a_attr a_type * \
-a_prefix##remove_first(a_ph_type *ph) { \
- a_type *ret; \
\
- if (ph->ph_root == NULL) { \
- return NULL; \
- } \
- ph_merge_aux(a_type, a_field, ph, a_cmp); \
- \
- ret = ph->ph_root; \
- \
- ph_merge_children(a_type, a_field, ph->ph_root, a_cmp, \
- ph->ph_root); \
+a_attr a_type * \
+a_prefix##_remove_first(a_prefix##_t *ph) { \
+ return ph_remove_first(&ph->ph, offsetof(a_type, a_field), \
+ a_prefix##_ph_cmp); \
+} \
\
- return ret; \
+a_attr void \
+a_prefix##_remove(a_prefix##_t *ph, a_type *phn) { \
+ ph_remove(&ph->ph, phn, offsetof(a_type, a_field), \
+ a_prefix##_ph_cmp); \
} \
+ \
a_attr a_type * \
-a_prefix##remove_any(a_ph_type *ph) { \
- /* \
- * Remove the most recently inserted aux list element, or the \
- * root if the aux list is empty. This has the effect of \
- * behaving as a LIFO (and insertion/removal is therefore \
- * constant-time) if a_prefix##[remove_]first() are never \
- * called. \
- */ \
- if (ph->ph_root == NULL) { \
- return NULL; \
- } \
- a_type *ret = phn_next_get(a_type, a_field, ph->ph_root); \
+a_prefix##_remove_any(a_prefix##_t *ph) { \
+ a_type *ret = a_prefix##_any(ph); \
if (ret != NULL) { \
- a_type *aux = phn_next_get(a_type, a_field, ret); \
- phn_next_set(a_type, a_field, ph->ph_root, aux); \
- if (aux != NULL) { \
- phn_prev_set(a_type, a_field, aux, \
- ph->ph_root); \
- } \
- return ret; \
+ a_prefix##_remove(ph, ret); \
} \
- ret = ph->ph_root; \
- ph_merge_children(a_type, a_field, ph->ph_root, a_cmp, \
- ph->ph_root); \
return ret; \
-} \
-a_attr void \
-a_prefix##remove(a_ph_type *ph, a_type *phn) { \
- a_type *replace, *parent; \
- \
- if (ph->ph_root == phn) { \
- /* \
- * We can delete from aux list without merging it, but \
- * we need to merge if we are dealing with the root \
- * node and it has children. \
- */ \
- if (phn_lchild_get(a_type, a_field, phn) == NULL) { \
- ph->ph_root = phn_next_get(a_type, a_field, \
- phn); \
- if (ph->ph_root != NULL) { \
- phn_prev_set(a_type, a_field, \
- ph->ph_root, NULL); \
- } \
- return; \
- } \
- ph_merge_aux(a_type, a_field, ph, a_cmp); \
- if (ph->ph_root == phn) { \
- ph_merge_children(a_type, a_field, ph->ph_root, \
- a_cmp, ph->ph_root); \
- return; \
- } \
- } \
- \
- /* Get parent (if phn is leftmost child) before mutating. */ \
- if ((parent = phn_prev_get(a_type, a_field, phn)) != NULL) { \
- if (phn_lchild_get(a_type, a_field, parent) != phn) { \
- parent = NULL; \
- } \
- } \
- /* Find a possible replacement node, and link to parent. */ \
- ph_merge_children(a_type, a_field, phn, a_cmp, replace); \
- /* Set next/prev for sibling linked list. */ \
- if (replace != NULL) { \
- if (parent != NULL) { \
- phn_prev_set(a_type, a_field, replace, parent); \
- phn_lchild_set(a_type, a_field, parent, \
- replace); \
- } else { \
- phn_prev_set(a_type, a_field, replace, \
- phn_prev_get(a_type, a_field, phn)); \
- if (phn_prev_get(a_type, a_field, phn) != \
- NULL) { \
- phn_next_set(a_type, a_field, \
- phn_prev_get(a_type, a_field, phn), \
- replace); \
- } \
- } \
- phn_next_set(a_type, a_field, replace, \
- phn_next_get(a_type, a_field, phn)); \
- if (phn_next_get(a_type, a_field, phn) != NULL) { \
- phn_prev_set(a_type, a_field, \
- phn_next_get(a_type, a_field, phn), \
- replace); \
- } \
- } else { \
- if (parent != NULL) { \
- a_type *next = phn_next_get(a_type, a_field, \
- phn); \
- phn_lchild_set(a_type, a_field, parent, next); \
- if (next != NULL) { \
- phn_prev_set(a_type, a_field, next, \
- parent); \
- } \
- } else { \
- assert(phn_prev_get(a_type, a_field, phn) != \
- NULL); \
- phn_next_set(a_type, a_field, \
- phn_prev_get(a_type, a_field, phn), \
- phn_next_get(a_type, a_field, phn)); \
- } \
- if (phn_next_get(a_type, a_field, phn) != NULL) { \
- phn_prev_set(a_type, a_field, \
- phn_next_get(a_type, a_field, phn), \
- phn_prev_get(a_type, a_field, phn)); \
- } \
- } \
}
-#endif /* PH_H_ */
+#endif /* JEMALLOC_INTERNAL_PH_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/private_namespace.sh b/contrib/jemalloc/include/jemalloc/internal/private_namespace.sh
new file mode 100755
index 000000000000..6ef1346a3c1a
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/internal/private_namespace.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+for symbol in `cat "$@"` ; do
+ echo "#define ${symbol} JEMALLOC_N(${symbol})"
+done
diff --git a/contrib/jemalloc/include/jemalloc/internal/private_symbols.sh b/contrib/jemalloc/include/jemalloc/internal/private_symbols.sh
new file mode 100755
index 000000000000..442a259fdc89
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/internal/private_symbols.sh
@@ -0,0 +1,51 @@
+#!/bin/sh
+#
+# Generate private_symbols[_jet].awk.
+#
+# Usage: private_symbols.sh <sym_prefix> <sym>*
+#
+# <sym_prefix> is typically "" or "_".
+
+sym_prefix=$1
+shift
+
+cat <<EOF
+#!/usr/bin/env awk -f
+
+BEGIN {
+ sym_prefix = "${sym_prefix}"
+ split("\\
+EOF
+
+for public_sym in "$@" ; do
+ cat <<EOF
+ ${sym_prefix}${public_sym} \\
+EOF
+done
+
+cat <<"EOF"
+ ", exported_symbol_names)
+ # Store exported symbol names as keys in exported_symbols.
+ for (i in exported_symbol_names) {
+ exported_symbols[exported_symbol_names[i]] = 1
+ }
+}
+
+# Process 'nm -a <c_source.o>' output.
+#
+# Handle lines like:
+# 0000000000000008 D opt_junk
+# 0000000000007574 T malloc_initialized
+(NF == 3 && $2 ~ /^[ABCDGRSTVW]$/ && !($3 in exported_symbols) && $3 ~ /^[A-Za-z0-9_]+$/) {
+ print substr($3, 1+length(sym_prefix), length($3)-length(sym_prefix))
+}
+
+# Process 'dumpbin /SYMBOLS <c_source.obj>' output.
+#
+# Handle lines like:
+# 353 00008098 SECT4 notype External | opt_junk
+# 3F1 00000000 SECT7 notype () External | malloc_initialized
+($3 ~ /^SECT[0-9]+/ && $(NF-2) == "External" && !($NF in exported_symbols)) {
+ print $NF
+}
+EOF
diff --git a/contrib/jemalloc/include/jemalloc/internal/prng.h b/contrib/jemalloc/include/jemalloc/internal/prng.h
index 15cc2d18fa4d..14542aa12d46 100644
--- a/contrib/jemalloc/include/jemalloc/internal/prng.h
+++ b/contrib/jemalloc/include/jemalloc/internal/prng.h
@@ -1,7 +1,6 @@
#ifndef JEMALLOC_INTERNAL_PRNG_H
#define JEMALLOC_INTERNAL_PRNG_H
-#include "jemalloc/internal/atomic.h"
#include "jemalloc/internal/bit_util.h"
/*
@@ -59,66 +58,38 @@ prng_state_next_zu(size_t state) {
/*
* The prng_lg_range functions give a uniform int in the half-open range [0,
- * 2**lg_range). If atomic is true, they do so safely from multiple threads.
- * Multithreaded 64-bit prngs aren't supported.
+ * 2**lg_range).
*/
JEMALLOC_ALWAYS_INLINE uint32_t
-prng_lg_range_u32(atomic_u32_t *state, unsigned lg_range, bool atomic) {
- uint32_t ret, state0, state1;
-
+prng_lg_range_u32(uint32_t *state, unsigned lg_range) {
assert(lg_range > 0);
assert(lg_range <= 32);
- state0 = atomic_load_u32(state, ATOMIC_RELAXED);
-
- if (atomic) {
- do {
- state1 = prng_state_next_u32(state0);
- } while (!atomic_compare_exchange_weak_u32(state, &state0,
- state1, ATOMIC_RELAXED, ATOMIC_RELAXED));
- } else {
- state1 = prng_state_next_u32(state0);
- atomic_store_u32(state, state1, ATOMIC_RELAXED);
- }
- ret = state1 >> (32 - lg_range);
+ *state = prng_state_next_u32(*state);
+ uint32_t ret = *state >> (32 - lg_range);
return ret;
}
JEMALLOC_ALWAYS_INLINE uint64_t
prng_lg_range_u64(uint64_t *state, unsigned lg_range) {
- uint64_t ret, state1;
-
assert(lg_range > 0);
assert(lg_range <= 64);
- state1 = prng_state_next_u64(*state);
- *state = state1;
- ret = state1 >> (64 - lg_range);
+ *state = prng_state_next_u64(*state);
+ uint64_t ret = *state >> (64 - lg_range);
return ret;
}
JEMALLOC_ALWAYS_INLINE size_t
-prng_lg_range_zu(atomic_zu_t *state, unsigned lg_range, bool atomic) {
- size_t ret, state0, state1;
-
+prng_lg_range_zu(size_t *state, unsigned lg_range) {
assert(lg_range > 0);
assert(lg_range <= ZU(1) << (3 + LG_SIZEOF_PTR));
- state0 = atomic_load_zu(state, ATOMIC_RELAXED);
-
- if (atomic) {
- do {
- state1 = prng_state_next_zu(state0);
- } while (atomic_compare_exchange_weak_zu(state, &state0,
- state1, ATOMIC_RELAXED, ATOMIC_RELAXED));
- } else {
- state1 = prng_state_next_zu(state0);
- atomic_store_zu(state, state1, ATOMIC_RELAXED);
- }
- ret = state1 >> ((ZU(1) << (3 + LG_SIZEOF_PTR)) - lg_range);
+ *state = prng_state_next_zu(*state);
+ size_t ret = *state >> ((ZU(1) << (3 + LG_SIZEOF_PTR)) - lg_range);
return ret;
}
@@ -129,18 +100,24 @@ prng_lg_range_zu(atomic_zu_t *state, unsigned lg_range, bool atomic) {
*/
JEMALLOC_ALWAYS_INLINE uint32_t
-prng_range_u32(atomic_u32_t *state, uint32_t range, bool atomic) {
- uint32_t ret;
- unsigned lg_range;
-
- assert(range > 1);
+prng_range_u32(uint32_t *state, uint32_t range) {
+ assert(range != 0);
+ /*
+ * If range were 1, lg_range would be 0, so the shift in
+ * prng_lg_range_u32 would be a shift of a 32-bit variable by 32 bits,
+ * which is UB. Just handle this case as a one-off.
+ */
+ if (range == 1) {
+ return 0;
+ }
/* Compute the ceiling of lg(range). */
- lg_range = ffs_u32(pow2_ceil_u32(range)) - 1;
+ unsigned lg_range = ffs_u32(pow2_ceil_u32(range));
/* Generate a result in [0..range) via repeated trial. */
+ uint32_t ret;
do {
- ret = prng_lg_range_u32(state, lg_range, atomic);
+ ret = prng_lg_range_u32(state, lg_range);
} while (ret >= range);
return ret;
@@ -148,15 +125,18 @@ prng_range_u32(atomic_u32_t *state, uint32_t range, bool atomic) {
JEMALLOC_ALWAYS_INLINE uint64_t
prng_range_u64(uint64_t *state, uint64_t range) {
- uint64_t ret;
- unsigned lg_range;
+ assert(range != 0);
- assert(range > 1);
+ /* See the note in prng_range_u32. */
+ if (range == 1) {
+ return 0;
+ }
/* Compute the ceiling of lg(range). */
- lg_range = ffs_u64(pow2_ceil_u64(range)) - 1;
+ unsigned lg_range = ffs_u64(pow2_ceil_u64(range));
/* Generate a result in [0..range) via repeated trial. */
+ uint64_t ret;
do {
ret = prng_lg_range_u64(state, lg_range);
} while (ret >= range);
@@ -165,18 +145,21 @@ prng_range_u64(uint64_t *state, uint64_t range) {
}
JEMALLOC_ALWAYS_INLINE size_t
-prng_range_zu(atomic_zu_t *state, size_t range, bool atomic) {
- size_t ret;
- unsigned lg_range;
+prng_range_zu(size_t *state, size_t range) {
+ assert(range != 0);
- assert(range > 1);
+ /* See the note in prng_range_u32. */
+ if (range == 1) {
+ return 0;
+ }
/* Compute the ceiling of lg(range). */
- lg_range = ffs_u64(pow2_ceil_u64(range)) - 1;
+ unsigned lg_range = ffs_u64(pow2_ceil_u64(range));
/* Generate a result in [0..range) via repeated trial. */
+ size_t ret;
do {
- ret = prng_lg_range_zu(state, lg_range, atomic);
+ ret = prng_lg_range_zu(state, lg_range);
} while (ret >= range);
return ret;
diff --git a/contrib/jemalloc/include/jemalloc/internal/prof_data.h b/contrib/jemalloc/include/jemalloc/internal/prof_data.h
new file mode 100644
index 000000000000..4c8e22c76f20
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/internal/prof_data.h
@@ -0,0 +1,37 @@
+#ifndef JEMALLOC_INTERNAL_PROF_DATA_H
+#define JEMALLOC_INTERNAL_PROF_DATA_H
+
+#include "jemalloc/internal/mutex.h"
+
+extern malloc_mutex_t bt2gctx_mtx;
+extern malloc_mutex_t tdatas_mtx;
+extern malloc_mutex_t prof_dump_mtx;
+
+extern malloc_mutex_t *gctx_locks;
+extern malloc_mutex_t *tdata_locks;
+
+extern size_t prof_unbiased_sz[PROF_SC_NSIZES];
+extern size_t prof_shifted_unbiased_cnt[PROF_SC_NSIZES];
+
+void prof_bt_hash(const void *key, size_t r_hash[2]);
+bool prof_bt_keycomp(const void *k1, const void *k2);
+
+bool prof_data_init(tsd_t *tsd);
+prof_tctx_t *prof_lookup(tsd_t *tsd, prof_bt_t *bt);
+char *prof_thread_name_alloc(tsd_t *tsd, const char *thread_name);
+int prof_thread_name_set_impl(tsd_t *tsd, const char *thread_name);
+void prof_unbias_map_init();
+void prof_dump_impl(tsd_t *tsd, write_cb_t *prof_dump_write, void *cbopaque,
+ prof_tdata_t *tdata, bool leakcheck);
+prof_tdata_t * prof_tdata_init_impl(tsd_t *tsd, uint64_t thr_uid,
+ uint64_t thr_discrim, char *thread_name, bool active);
+void prof_tdata_detach(tsd_t *tsd, prof_tdata_t *tdata);
+void prof_reset(tsd_t *tsd, size_t lg_sample);
+void prof_tctx_try_destroy(tsd_t *tsd, prof_tctx_t *tctx);
+
+/* Used in unit tests. */
+size_t prof_tdata_count(void);
+size_t prof_bt_count(void);
+void prof_cnt_all(prof_cnt_t *cnt_all);
+
+#endif /* JEMALLOC_INTERNAL_PROF_DATA_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/prof_externs.h b/contrib/jemalloc/include/jemalloc/internal/prof_externs.h
index 094f3e170ae7..bdff1349aeaf 100644
--- a/contrib/jemalloc/include/jemalloc/internal/prof_externs.h
+++ b/contrib/jemalloc/include/jemalloc/internal/prof_externs.h
@@ -2,75 +2,72 @@
#define JEMALLOC_INTERNAL_PROF_EXTERNS_H
#include "jemalloc/internal/mutex.h"
+#include "jemalloc/internal/prof_hook.h"
-extern malloc_mutex_t bt2gctx_mtx;
-
-extern bool opt_prof;
-extern bool opt_prof_active;
-extern bool opt_prof_thread_active_init;
-extern size_t opt_lg_prof_sample; /* Mean bytes between samples. */
-extern ssize_t opt_lg_prof_interval; /* lg(prof_interval). */
-extern bool opt_prof_gdump; /* High-water memory dumping. */
-extern bool opt_prof_final; /* Final profile dumping. */
-extern bool opt_prof_leak; /* Dump leak summary at exit. */
-extern bool opt_prof_accum; /* Report cumulative bytes. */
-extern bool opt_prof_log; /* Turn logging on at boot. */
-extern char opt_prof_prefix[
+extern bool opt_prof;
+extern bool opt_prof_active;
+extern bool opt_prof_thread_active_init;
+extern size_t opt_lg_prof_sample; /* Mean bytes between samples. */
+extern ssize_t opt_lg_prof_interval; /* lg(prof_interval). */
+extern bool opt_prof_gdump; /* High-water memory dumping. */
+extern bool opt_prof_final; /* Final profile dumping. */
+extern bool opt_prof_leak; /* Dump leak summary at exit. */
+extern bool opt_prof_leak_error; /* Exit with error code if memory leaked */
+extern bool opt_prof_accum; /* Report cumulative bytes. */
+extern bool opt_prof_log; /* Turn logging on at boot. */
+extern char opt_prof_prefix[
/* Minimize memory bloat for non-prof builds. */
#ifdef JEMALLOC_PROF
PATH_MAX +
#endif
1];
+extern bool opt_prof_unbias;
+
+/* For recording recent allocations */
+extern ssize_t opt_prof_recent_alloc_max;
+
+/* Whether to use thread name provided by the system or by mallctl. */
+extern bool opt_prof_sys_thread_name;
+
+/* Whether to record per size class counts and request size totals. */
+extern bool opt_prof_stats;
/* Accessed via prof_active_[gs]et{_unlocked,}(). */
-extern bool prof_active;
+extern bool prof_active_state;
/* Accessed via prof_gdump_[gs]et{_unlocked,}(). */
-extern bool prof_gdump_val;
+extern bool prof_gdump_val;
-/*
- * Profile dump interval, measured in bytes allocated. Each arena triggers a
- * profile dump when it reaches this threshold. The effect is that the
- * interval between profile dumps averages prof_interval, though the actual
- * interval between dumps will tend to be sporadic, and the interval will be a
- * maximum of approximately (prof_interval * narenas).
- */
-extern uint64_t prof_interval;
+/* Profile dump interval, measured in bytes allocated. */
+extern uint64_t prof_interval;
/*
* Initialized as opt_lg_prof_sample, and potentially modified during profiling
* resets.
*/
-extern size_t lg_prof_sample;
-
-void prof_alloc_rollback(tsd_t *tsd, prof_tctx_t *tctx, bool updated);
-void prof_malloc_sample_object(tsdn_t *tsdn, const void *ptr, size_t usize,
- prof_tctx_t *tctx);
-void prof_free_sampled_object(tsd_t *tsd, const void *ptr, size_t usize,
- prof_tctx_t *tctx);
-void bt_init(prof_bt_t *bt, void **vec);
-void prof_backtrace(prof_bt_t *bt);
-prof_tctx_t *prof_lookup(tsd_t *tsd, prof_bt_t *bt);
-#ifdef JEMALLOC_JET
-size_t prof_tdata_count(void);
-size_t prof_bt_count(void);
-#endif
-typedef int (prof_dump_open_t)(bool, const char *);
-extern prof_dump_open_t *JET_MUTABLE prof_dump_open;
-
-typedef bool (prof_dump_header_t)(tsdn_t *, bool, const prof_cnt_t *);
-extern prof_dump_header_t *JET_MUTABLE prof_dump_header;
-#ifdef JEMALLOC_JET
-void prof_cnt_all(uint64_t *curobjs, uint64_t *curbytes, uint64_t *accumobjs,
- uint64_t *accumbytes);
-#endif
-bool prof_accum_init(tsdn_t *tsdn, prof_accum_t *prof_accum);
+extern size_t lg_prof_sample;
+
+extern bool prof_booted;
+
+void prof_backtrace_hook_set(prof_backtrace_hook_t hook);
+prof_backtrace_hook_t prof_backtrace_hook_get();
+
+void prof_dump_hook_set(prof_dump_hook_t hook);
+prof_dump_hook_t prof_dump_hook_get();
+
+/* Functions only accessed in prof_inlines.h */
+prof_tdata_t *prof_tdata_init(tsd_t *tsd);
+prof_tdata_t *prof_tdata_reinit(tsd_t *tsd, prof_tdata_t *tdata);
+
+void prof_alloc_rollback(tsd_t *tsd, prof_tctx_t *tctx);
+void prof_malloc_sample_object(tsd_t *tsd, const void *ptr, size_t size,
+ size_t usize, prof_tctx_t *tctx);
+void prof_free_sampled_object(tsd_t *tsd, size_t usize, prof_info_t *prof_info);
+prof_tctx_t *prof_tctx_create(tsd_t *tsd);
void prof_idump(tsdn_t *tsdn);
bool prof_mdump(tsd_t *tsd, const char *filename);
void prof_gdump(tsdn_t *tsdn);
-prof_tdata_t *prof_tdata_init(tsd_t *tsd);
-prof_tdata_t *prof_tdata_reinit(tsd_t *tsd, prof_tdata_t *tdata);
-void prof_reset(tsd_t *tsd, size_t lg_sample);
+
void prof_tdata_cleanup(tsd_t *tsd);
bool prof_active_get(tsdn_t *tsdn);
bool prof_active_set(tsdn_t *tsdn, bool active);
@@ -84,22 +81,15 @@ bool prof_gdump_get(tsdn_t *tsdn);
bool prof_gdump_set(tsdn_t *tsdn, bool active);
void prof_boot0(void);
void prof_boot1(void);
-bool prof_boot2(tsd_t *tsd);
+bool prof_boot2(tsd_t *tsd, base_t *base);
void prof_prefork0(tsdn_t *tsdn);
void prof_prefork1(tsdn_t *tsdn);
void prof_postfork_parent(tsdn_t *tsdn);
void prof_postfork_child(tsdn_t *tsdn);
-void prof_sample_threshold_update(prof_tdata_t *tdata);
-
-bool prof_log_start(tsdn_t *tsdn, const char *filename);
-bool prof_log_stop(tsdn_t *tsdn);
-#ifdef JEMALLOC_JET
-size_t prof_log_bt_count(void);
-size_t prof_log_alloc_count(void);
-size_t prof_log_thr_count(void);
-bool prof_log_is_logging(void);
-bool prof_log_rep_check(void);
-void prof_log_dummy_set(bool new_value);
-#endif
+
+/* Only accessed by thread event. */
+uint64_t prof_sample_new_event_wait(tsd_t *tsd);
+uint64_t prof_sample_postponed_event_wait(tsd_t *tsd);
+void prof_sample_event_handler(tsd_t *tsd, uint64_t elapsed);
#endif /* JEMALLOC_INTERNAL_PROF_EXTERNS_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/prof_hook.h b/contrib/jemalloc/include/jemalloc/internal/prof_hook.h
new file mode 100644
index 000000000000..150d19d3d61c
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/internal/prof_hook.h
@@ -0,0 +1,21 @@
+#ifndef JEMALLOC_INTERNAL_PROF_HOOK_H
+#define JEMALLOC_INTERNAL_PROF_HOOK_H
+
+/*
+ * The hooks types of which are declared in this file are experimental and
+ * undocumented, thus the typedefs are located in an 'internal' header.
+ */
+
+/*
+ * A hook to mock out backtrace functionality. This can be handy, since it's
+ * otherwise difficult to guarantee that two allocations are reported as coming
+ * from the exact same stack trace in the presence of an optimizing compiler.
+ */
+typedef void (*prof_backtrace_hook_t)(void **, unsigned *, unsigned);
+
+/*
+ * A callback hook that notifies about recently dumped heap profile.
+ */
+typedef void (*prof_dump_hook_t)(const char *filename);
+
+#endif /* JEMALLOC_INTERNAL_PROF_HOOK_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/prof_inlines.h b/contrib/jemalloc/include/jemalloc/internal/prof_inlines.h
new file mode 100644
index 000000000000..a8e7e7fb663e
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/internal/prof_inlines.h
@@ -0,0 +1,261 @@
+#ifndef JEMALLOC_INTERNAL_PROF_INLINES_H
+#define JEMALLOC_INTERNAL_PROF_INLINES_H
+
+#include "jemalloc/internal/safety_check.h"
+#include "jemalloc/internal/sz.h"
+#include "jemalloc/internal/thread_event.h"
+
+JEMALLOC_ALWAYS_INLINE void
+prof_active_assert() {
+ cassert(config_prof);
+ /*
+ * If opt_prof is off, then prof_active must always be off, regardless
+ * of whether prof_active_mtx is in effect or not.
+ */
+ assert(opt_prof || !prof_active_state);
+}
+
+JEMALLOC_ALWAYS_INLINE bool
+prof_active_get_unlocked(void) {
+ prof_active_assert();
+ /*
+ * Even if opt_prof is true, sampling can be temporarily disabled by
+ * setting prof_active to false. No locking is used when reading
+ * prof_active in the fast path, so there are no guarantees regarding
+ * how long it will take for all threads to notice state changes.
+ */
+ return prof_active_state;
+}
+
+JEMALLOC_ALWAYS_INLINE bool
+prof_gdump_get_unlocked(void) {
+ /*
+ * No locking is used when reading prof_gdump_val in the fast path, so
+ * there are no guarantees regarding how long it will take for all
+ * threads to notice state changes.
+ */
+ return prof_gdump_val;
+}
+
+JEMALLOC_ALWAYS_INLINE prof_tdata_t *
+prof_tdata_get(tsd_t *tsd, bool create) {
+ prof_tdata_t *tdata;
+
+ cassert(config_prof);
+
+ tdata = tsd_prof_tdata_get(tsd);
+ if (create) {
+ assert(tsd_reentrancy_level_get(tsd) == 0);
+ if (unlikely(tdata == NULL)) {
+ if (tsd_nominal(tsd)) {
+ tdata = prof_tdata_init(tsd);
+ tsd_prof_tdata_set(tsd, tdata);
+ }
+ } else if (unlikely(tdata->expired)) {
+ tdata = prof_tdata_reinit(tsd, tdata);
+ tsd_prof_tdata_set(tsd, tdata);
+ }
+ assert(tdata == NULL || tdata->attached);
+ }
+
+ return tdata;
+}
+
+JEMALLOC_ALWAYS_INLINE void
+prof_info_get(tsd_t *tsd, const void *ptr, emap_alloc_ctx_t *alloc_ctx,
+ prof_info_t *prof_info) {
+ cassert(config_prof);
+ assert(ptr != NULL);
+ assert(prof_info != NULL);
+
+ arena_prof_info_get(tsd, ptr, alloc_ctx, prof_info, false);
+}
+
+JEMALLOC_ALWAYS_INLINE void
+prof_info_get_and_reset_recent(tsd_t *tsd, const void *ptr,
+ emap_alloc_ctx_t *alloc_ctx, prof_info_t *prof_info) {
+ cassert(config_prof);
+ assert(ptr != NULL);
+ assert(prof_info != NULL);
+
+ arena_prof_info_get(tsd, ptr, alloc_ctx, prof_info, true);
+}
+
+JEMALLOC_ALWAYS_INLINE void
+prof_tctx_reset(tsd_t *tsd, const void *ptr, emap_alloc_ctx_t *alloc_ctx) {
+ cassert(config_prof);
+ assert(ptr != NULL);
+
+ arena_prof_tctx_reset(tsd, ptr, alloc_ctx);
+}
+
+JEMALLOC_ALWAYS_INLINE void
+prof_tctx_reset_sampled(tsd_t *tsd, const void *ptr) {
+ cassert(config_prof);
+ assert(ptr != NULL);
+
+ arena_prof_tctx_reset_sampled(tsd, ptr);
+}
+
+JEMALLOC_ALWAYS_INLINE void
+prof_info_set(tsd_t *tsd, edata_t *edata, prof_tctx_t *tctx, size_t size) {
+ cassert(config_prof);
+ assert(edata != NULL);
+ assert((uintptr_t)tctx > (uintptr_t)1U);
+
+ arena_prof_info_set(tsd, edata, tctx, size);
+}
+
+JEMALLOC_ALWAYS_INLINE bool
+prof_sample_should_skip(tsd_t *tsd, bool sample_event) {
+ cassert(config_prof);
+
+ /* Fastpath: no need to load tdata */
+ if (likely(!sample_event)) {
+ return true;
+ }
+
+ /*
+ * sample_event is always obtained from the thread event module, and
+ * whenever it's true, it means that the thread event module has
+ * already checked the reentrancy level.
+ */
+ assert(tsd_reentrancy_level_get(tsd) == 0);
+
+ prof_tdata_t *tdata = prof_tdata_get(tsd, true);
+ if (unlikely(tdata == NULL)) {
+ return true;
+ }
+
+ return !tdata->active;
+}
+
+JEMALLOC_ALWAYS_INLINE prof_tctx_t *
+prof_alloc_prep(tsd_t *tsd, bool prof_active, bool sample_event) {
+ prof_tctx_t *ret;
+
+ if (!prof_active ||
+ likely(prof_sample_should_skip(tsd, sample_event))) {
+ ret = (prof_tctx_t *)(uintptr_t)1U;
+ } else {
+ ret = prof_tctx_create(tsd);
+ }
+
+ return ret;
+}
+
+JEMALLOC_ALWAYS_INLINE void
+prof_malloc(tsd_t *tsd, const void *ptr, size_t size, size_t usize,
+ emap_alloc_ctx_t *alloc_ctx, prof_tctx_t *tctx) {
+ cassert(config_prof);
+ assert(ptr != NULL);
+ assert(usize == isalloc(tsd_tsdn(tsd), ptr));
+
+ if (unlikely((uintptr_t)tctx > (uintptr_t)1U)) {
+ prof_malloc_sample_object(tsd, ptr, size, usize, tctx);
+ } else {
+ prof_tctx_reset(tsd, ptr, alloc_ctx);
+ }
+}
+
+JEMALLOC_ALWAYS_INLINE void
+prof_realloc(tsd_t *tsd, const void *ptr, size_t size, size_t usize,
+ prof_tctx_t *tctx, bool prof_active, const void *old_ptr, size_t old_usize,
+ prof_info_t *old_prof_info, bool sample_event) {
+ bool sampled, old_sampled, moved;
+
+ cassert(config_prof);
+ assert(ptr != NULL || (uintptr_t)tctx <= (uintptr_t)1U);
+
+ if (prof_active && ptr != NULL) {
+ assert(usize == isalloc(tsd_tsdn(tsd), ptr));
+ if (prof_sample_should_skip(tsd, sample_event)) {
+ /*
+ * Don't sample. The usize passed to prof_alloc_prep()
+ * was larger than what actually got allocated, so a
+ * backtrace was captured for this allocation, even
+ * though its actual usize was insufficient to cross the
+ * sample threshold.
+ */
+ prof_alloc_rollback(tsd, tctx);
+ tctx = (prof_tctx_t *)(uintptr_t)1U;
+ }
+ }
+
+ sampled = ((uintptr_t)tctx > (uintptr_t)1U);
+ old_sampled = ((uintptr_t)old_prof_info->alloc_tctx > (uintptr_t)1U);
+ moved = (ptr != old_ptr);
+
+ if (unlikely(sampled)) {
+ prof_malloc_sample_object(tsd, ptr, size, usize, tctx);
+ } else if (moved) {
+ prof_tctx_reset(tsd, ptr, NULL);
+ } else if (unlikely(old_sampled)) {
+ /*
+ * prof_tctx_reset() would work for the !moved case as well,
+ * but prof_tctx_reset_sampled() is slightly cheaper, and the
+ * proper thing to do here in the presence of explicit
+ * knowledge re: moved state.
+ */
+ prof_tctx_reset_sampled(tsd, ptr);
+ } else {
+ prof_info_t prof_info;
+ prof_info_get(tsd, ptr, NULL, &prof_info);
+ assert((uintptr_t)prof_info.alloc_tctx == (uintptr_t)1U);
+ }
+
+ /*
+ * The prof_free_sampled_object() call must come after the
+ * prof_malloc_sample_object() call, because tctx and old_tctx may be
+ * the same, in which case reversing the call order could cause the tctx
+ * to be prematurely destroyed as a side effect of momentarily zeroed
+ * counters.
+ */
+ if (unlikely(old_sampled)) {
+ prof_free_sampled_object(tsd, old_usize, old_prof_info);
+ }
+}
+
+JEMALLOC_ALWAYS_INLINE size_t
+prof_sample_align(size_t orig_align) {
+ /*
+ * Enforce page alignment, so that sampled allocations can be identified
+ * w/o metadata lookup.
+ */
+ assert(opt_prof);
+ return (opt_cache_oblivious && orig_align < PAGE) ? PAGE :
+ orig_align;
+}
+
+JEMALLOC_ALWAYS_INLINE bool
+prof_sample_aligned(const void *ptr) {
+ return ((uintptr_t)ptr & PAGE_MASK) == 0;
+}
+
+JEMALLOC_ALWAYS_INLINE bool
+prof_sampled(tsd_t *tsd, const void *ptr) {
+ prof_info_t prof_info;
+ prof_info_get(tsd, ptr, NULL, &prof_info);
+ bool sampled = (uintptr_t)prof_info.alloc_tctx > (uintptr_t)1U;
+ if (sampled) {
+ assert(prof_sample_aligned(ptr));
+ }
+ return sampled;
+}
+
+JEMALLOC_ALWAYS_INLINE void
+prof_free(tsd_t *tsd, const void *ptr, size_t usize,
+ emap_alloc_ctx_t *alloc_ctx) {
+ prof_info_t prof_info;
+ prof_info_get_and_reset_recent(tsd, ptr, alloc_ctx, &prof_info);
+
+ cassert(config_prof);
+ assert(usize == isalloc(tsd_tsdn(tsd), ptr));
+
+ if (unlikely((uintptr_t)prof_info.alloc_tctx > (uintptr_t)1U)) {
+ assert(prof_sample_aligned(ptr));
+ prof_free_sampled_object(tsd, usize, &prof_info);
+ }
+}
+
+#endif /* JEMALLOC_INTERNAL_PROF_INLINES_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/prof_inlines_a.h b/contrib/jemalloc/include/jemalloc/internal/prof_inlines_a.h
deleted file mode 100644
index 471d9853cf87..000000000000
--- a/contrib/jemalloc/include/jemalloc/internal/prof_inlines_a.h
+++ /dev/null
@@ -1,85 +0,0 @@
-#ifndef JEMALLOC_INTERNAL_PROF_INLINES_A_H
-#define JEMALLOC_INTERNAL_PROF_INLINES_A_H
-
-#include "jemalloc/internal/mutex.h"
-
-static inline bool
-prof_accum_add(tsdn_t *tsdn, prof_accum_t *prof_accum,
- uint64_t accumbytes) {
- cassert(config_prof);
-
- bool overflow;
- uint64_t a0, a1;
-
- /*
- * If the application allocates fast enough (and/or if idump is slow
- * enough), extreme overflow here (a1 >= prof_interval * 2) can cause
- * idump trigger coalescing. This is an intentional mechanism that
- * avoids rate-limiting allocation.
- */
-#ifdef JEMALLOC_ATOMIC_U64
- a0 = atomic_load_u64(&prof_accum->accumbytes, ATOMIC_RELAXED);
- do {
- a1 = a0 + accumbytes;
- assert(a1 >= a0);
- overflow = (a1 >= prof_interval);
- if (overflow) {
- a1 %= prof_interval;
- }
- } while (!atomic_compare_exchange_weak_u64(&prof_accum->accumbytes, &a0,
- a1, ATOMIC_RELAXED, ATOMIC_RELAXED));
-#else
- malloc_mutex_lock(tsdn, &prof_accum->mtx);
- a0 = prof_accum->accumbytes;
- a1 = a0 + accumbytes;
- overflow = (a1 >= prof_interval);
- if (overflow) {
- a1 %= prof_interval;
- }
- prof_accum->accumbytes = a1;
- malloc_mutex_unlock(tsdn, &prof_accum->mtx);
-#endif
- return overflow;
-}
-
-static inline void
-prof_accum_cancel(tsdn_t *tsdn, prof_accum_t *prof_accum,
- size_t usize) {
- cassert(config_prof);
-
- /*
- * Cancel out as much of the excessive prof_accumbytes increase as
- * possible without underflowing. Interval-triggered dumps occur
- * slightly more often than intended as a result of incomplete
- * canceling.
- */
- uint64_t a0, a1;
-#ifdef JEMALLOC_ATOMIC_U64
- a0 = atomic_load_u64(&prof_accum->accumbytes, ATOMIC_RELAXED);
- do {
- a1 = (a0 >= SC_LARGE_MINCLASS - usize)
- ? a0 - (SC_LARGE_MINCLASS - usize) : 0;
- } while (!atomic_compare_exchange_weak_u64(&prof_accum->accumbytes, &a0,
- a1, ATOMIC_RELAXED, ATOMIC_RELAXED));
-#else
- malloc_mutex_lock(tsdn, &prof_accum->mtx);
- a0 = prof_accum->accumbytes;
- a1 = (a0 >= SC_LARGE_MINCLASS - usize)
- ? a0 - (SC_LARGE_MINCLASS - usize) : 0;
- prof_accum->accumbytes = a1;
- malloc_mutex_unlock(tsdn, &prof_accum->mtx);
-#endif
-}
-
-JEMALLOC_ALWAYS_INLINE bool
-prof_active_get_unlocked(void) {
- /*
- * Even if opt_prof is true, sampling can be temporarily disabled by
- * setting prof_active to false. No locking is used when reading
- * prof_active in the fast path, so there are no guarantees regarding
- * how long it will take for all threads to notice state changes.
- */
- return prof_active;
-}
-
-#endif /* JEMALLOC_INTERNAL_PROF_INLINES_A_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/prof_inlines_b.h b/contrib/jemalloc/include/jemalloc/internal/prof_inlines_b.h
deleted file mode 100644
index 8ba8a1e1ffe7..000000000000
--- a/contrib/jemalloc/include/jemalloc/internal/prof_inlines_b.h
+++ /dev/null
@@ -1,250 +0,0 @@
-#ifndef JEMALLOC_INTERNAL_PROF_INLINES_B_H
-#define JEMALLOC_INTERNAL_PROF_INLINES_B_H
-
-#include "jemalloc/internal/safety_check.h"
-#include "jemalloc/internal/sz.h"
-
-JEMALLOC_ALWAYS_INLINE bool
-prof_gdump_get_unlocked(void) {
- /*
- * No locking is used when reading prof_gdump_val in the fast path, so
- * there are no guarantees regarding how long it will take for all
- * threads to notice state changes.
- */
- return prof_gdump_val;
-}
-
-JEMALLOC_ALWAYS_INLINE prof_tdata_t *
-prof_tdata_get(tsd_t *tsd, bool create) {
- prof_tdata_t *tdata;
-
- cassert(config_prof);
-
- tdata = tsd_prof_tdata_get(tsd);
- if (create) {
- if (unlikely(tdata == NULL)) {
- if (tsd_nominal(tsd)) {
- tdata = prof_tdata_init(tsd);
- tsd_prof_tdata_set(tsd, tdata);
- }
- } else if (unlikely(tdata->expired)) {
- tdata = prof_tdata_reinit(tsd, tdata);
- tsd_prof_tdata_set(tsd, tdata);
- }
- assert(tdata == NULL || tdata->attached);
- }
-
- return tdata;
-}
-
-JEMALLOC_ALWAYS_INLINE prof_tctx_t *
-prof_tctx_get(tsdn_t *tsdn, const void *ptr, alloc_ctx_t *alloc_ctx) {
- cassert(config_prof);
- assert(ptr != NULL);
-
- return arena_prof_tctx_get(tsdn, ptr, alloc_ctx);
-}
-
-JEMALLOC_ALWAYS_INLINE void
-prof_tctx_set(tsdn_t *tsdn, const void *ptr, size_t usize,
- alloc_ctx_t *alloc_ctx, prof_tctx_t *tctx) {
- cassert(config_prof);
- assert(ptr != NULL);
-
- arena_prof_tctx_set(tsdn, ptr, usize, alloc_ctx, tctx);
-}
-
-JEMALLOC_ALWAYS_INLINE void
-prof_tctx_reset(tsdn_t *tsdn, const void *ptr, prof_tctx_t *tctx) {
- cassert(config_prof);
- assert(ptr != NULL);
-
- arena_prof_tctx_reset(tsdn, ptr, tctx);
-}
-
-JEMALLOC_ALWAYS_INLINE nstime_t
-prof_alloc_time_get(tsdn_t *tsdn, const void *ptr, alloc_ctx_t *alloc_ctx) {
- cassert(config_prof);
- assert(ptr != NULL);
-
- return arena_prof_alloc_time_get(tsdn, ptr, alloc_ctx);
-}
-
-JEMALLOC_ALWAYS_INLINE void
-prof_alloc_time_set(tsdn_t *tsdn, const void *ptr, alloc_ctx_t *alloc_ctx,
- nstime_t t) {
- cassert(config_prof);
- assert(ptr != NULL);
-
- arena_prof_alloc_time_set(tsdn, ptr, alloc_ctx, t);
-}
-
-JEMALLOC_ALWAYS_INLINE bool
-prof_sample_check(tsd_t *tsd, size_t usize, bool update) {
- ssize_t check = update ? 0 : usize;
-
- int64_t bytes_until_sample = tsd_bytes_until_sample_get(tsd);
- if (update) {
- bytes_until_sample -= usize;
- if (tsd_nominal(tsd)) {
- tsd_bytes_until_sample_set(tsd, bytes_until_sample);
- }
- }
- if (likely(bytes_until_sample >= check)) {
- return true;
- }
-
- return false;
-}
-
-JEMALLOC_ALWAYS_INLINE bool
-prof_sample_accum_update(tsd_t *tsd, size_t usize, bool update,
- prof_tdata_t **tdata_out) {
- prof_tdata_t *tdata;
-
- cassert(config_prof);
-
- /* Fastpath: no need to load tdata */
- if (likely(prof_sample_check(tsd, usize, update))) {
- return true;
- }
-
- bool booted = tsd_prof_tdata_get(tsd);
- tdata = prof_tdata_get(tsd, true);
- if (unlikely((uintptr_t)tdata <= (uintptr_t)PROF_TDATA_STATE_MAX)) {
- tdata = NULL;
- }
-
- if (tdata_out != NULL) {
- *tdata_out = tdata;
- }
-
- if (unlikely(tdata == NULL)) {
- return true;
- }
-
- /*
- * If this was the first creation of tdata, then
- * prof_tdata_get() reset bytes_until_sample, so decrement and
- * check it again
- */
- if (!booted && prof_sample_check(tsd, usize, update)) {
- return true;
- }
-
- if (tsd_reentrancy_level_get(tsd) > 0) {
- return true;
- }
- /* Compute new sample threshold. */
- if (update) {
- prof_sample_threshold_update(tdata);
- }
- return !tdata->active;
-}
-
-JEMALLOC_ALWAYS_INLINE prof_tctx_t *
-prof_alloc_prep(tsd_t *tsd, size_t usize, bool prof_active, bool update) {
- prof_tctx_t *ret;
- prof_tdata_t *tdata;
- prof_bt_t bt;
-
- assert(usize == sz_s2u(usize));
-
- if (!prof_active || likely(prof_sample_accum_update(tsd, usize, update,
- &tdata))) {
- ret = (prof_tctx_t *)(uintptr_t)1U;
- } else {
- bt_init(&bt, tdata->vec);
- prof_backtrace(&bt);
- ret = prof_lookup(tsd, &bt);
- }
-
- return ret;
-}
-
-JEMALLOC_ALWAYS_INLINE void
-prof_malloc(tsdn_t *tsdn, const void *ptr, size_t usize, alloc_ctx_t *alloc_ctx,
- prof_tctx_t *tctx) {
- cassert(config_prof);
- assert(ptr != NULL);
- assert(usize == isalloc(tsdn, ptr));
-
- if (unlikely((uintptr_t)tctx > (uintptr_t)1U)) {
- prof_malloc_sample_object(tsdn, ptr, usize, tctx);
- } else {
- prof_tctx_set(tsdn, ptr, usize, alloc_ctx,
- (prof_tctx_t *)(uintptr_t)1U);
- }
-}
-
-JEMALLOC_ALWAYS_INLINE void
-prof_realloc(tsd_t *tsd, const void *ptr, size_t usize, prof_tctx_t *tctx,
- bool prof_active, bool updated, const void *old_ptr, size_t old_usize,
- prof_tctx_t *old_tctx) {
- bool sampled, old_sampled, moved;
-
- cassert(config_prof);
- assert(ptr != NULL || (uintptr_t)tctx <= (uintptr_t)1U);
-
- if (prof_active && !updated && ptr != NULL) {
- assert(usize == isalloc(tsd_tsdn(tsd), ptr));
- if (prof_sample_accum_update(tsd, usize, true, NULL)) {
- /*
- * Don't sample. The usize passed to prof_alloc_prep()
- * was larger than what actually got allocated, so a
- * backtrace was captured for this allocation, even
- * though its actual usize was insufficient to cross the
- * sample threshold.
- */
- prof_alloc_rollback(tsd, tctx, true);
- tctx = (prof_tctx_t *)(uintptr_t)1U;
- }
- }
-
- sampled = ((uintptr_t)tctx > (uintptr_t)1U);
- old_sampled = ((uintptr_t)old_tctx > (uintptr_t)1U);
- moved = (ptr != old_ptr);
-
- if (unlikely(sampled)) {
- prof_malloc_sample_object(tsd_tsdn(tsd), ptr, usize, tctx);
- } else if (moved) {
- prof_tctx_set(tsd_tsdn(tsd), ptr, usize, NULL,
- (prof_tctx_t *)(uintptr_t)1U);
- } else if (unlikely(old_sampled)) {
- /*
- * prof_tctx_set() would work for the !moved case as well, but
- * prof_tctx_reset() is slightly cheaper, and the proper thing
- * to do here in the presence of explicit knowledge re: moved
- * state.
- */
- prof_tctx_reset(tsd_tsdn(tsd), ptr, tctx);
- } else {
- assert((uintptr_t)prof_tctx_get(tsd_tsdn(tsd), ptr, NULL) ==
- (uintptr_t)1U);
- }
-
- /*
- * The prof_free_sampled_object() call must come after the
- * prof_malloc_sample_object() call, because tctx and old_tctx may be
- * the same, in which case reversing the call order could cause the tctx
- * to be prematurely destroyed as a side effect of momentarily zeroed
- * counters.
- */
- if (unlikely(old_sampled)) {
- prof_free_sampled_object(tsd, ptr, old_usize, old_tctx);
- }
-}
-
-JEMALLOC_ALWAYS_INLINE void
-prof_free(tsd_t *tsd, const void *ptr, size_t usize, alloc_ctx_t *alloc_ctx) {
- prof_tctx_t *tctx = prof_tctx_get(tsd_tsdn(tsd), ptr, alloc_ctx);
-
- cassert(config_prof);
- assert(usize == isalloc(tsd_tsdn(tsd), ptr));
-
- if (unlikely((uintptr_t)tctx > (uintptr_t)1U)) {
- prof_free_sampled_object(tsd, ptr, usize, tctx);
- }
-}
-
-#endif /* JEMALLOC_INTERNAL_PROF_INLINES_B_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/prof_log.h b/contrib/jemalloc/include/jemalloc/internal/prof_log.h
new file mode 100644
index 000000000000..ccb557dde69f
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/internal/prof_log.h
@@ -0,0 +1,22 @@
+#ifndef JEMALLOC_INTERNAL_PROF_LOG_H
+#define JEMALLOC_INTERNAL_PROF_LOG_H
+
+#include "jemalloc/internal/mutex.h"
+
+extern malloc_mutex_t log_mtx;
+
+void prof_try_log(tsd_t *tsd, size_t usize, prof_info_t *prof_info);
+bool prof_log_init(tsd_t *tsdn);
+
+/* Used in unit tests. */
+size_t prof_log_bt_count(void);
+size_t prof_log_alloc_count(void);
+size_t prof_log_thr_count(void);
+bool prof_log_is_logging(void);
+bool prof_log_rep_check(void);
+void prof_log_dummy_set(bool new_value);
+
+bool prof_log_start(tsdn_t *tsdn, const char *filename);
+bool prof_log_stop(tsdn_t *tsdn);
+
+#endif /* JEMALLOC_INTERNAL_PROF_LOG_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/prof_recent.h b/contrib/jemalloc/include/jemalloc/internal/prof_recent.h
new file mode 100644
index 000000000000..df4102362699
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/internal/prof_recent.h
@@ -0,0 +1,23 @@
+#ifndef JEMALLOC_INTERNAL_PROF_RECENT_H
+#define JEMALLOC_INTERNAL_PROF_RECENT_H
+
+extern malloc_mutex_t prof_recent_alloc_mtx;
+extern malloc_mutex_t prof_recent_dump_mtx;
+
+bool prof_recent_alloc_prepare(tsd_t *tsd, prof_tctx_t *tctx);
+void prof_recent_alloc(tsd_t *tsd, edata_t *edata, size_t size, size_t usize);
+void prof_recent_alloc_reset(tsd_t *tsd, edata_t *edata);
+bool prof_recent_init();
+void edata_prof_recent_alloc_init(edata_t *edata);
+
+/* Used in unit tests. */
+typedef ql_head(prof_recent_t) prof_recent_list_t;
+extern prof_recent_list_t prof_recent_alloc_list;
+edata_t *prof_recent_alloc_edata_get_no_lock_test(const prof_recent_t *node);
+prof_recent_t *edata_prof_recent_alloc_get_no_lock_test(const edata_t *edata);
+
+ssize_t prof_recent_alloc_max_ctl_read();
+ssize_t prof_recent_alloc_max_ctl_write(tsd_t *tsd, ssize_t max);
+void prof_recent_alloc_dump(tsd_t *tsd, write_cb_t *write_cb, void *cbopaque);
+
+#endif /* JEMALLOC_INTERNAL_PROF_RECENT_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/prof_stats.h b/contrib/jemalloc/include/jemalloc/internal/prof_stats.h
new file mode 100644
index 000000000000..7954e82de796
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/internal/prof_stats.h
@@ -0,0 +1,17 @@
+#ifndef JEMALLOC_INTERNAL_PROF_STATS_H
+#define JEMALLOC_INTERNAL_PROF_STATS_H
+
+typedef struct prof_stats_s prof_stats_t;
+struct prof_stats_s {
+ uint64_t req_sum;
+ uint64_t count;
+};
+
+extern malloc_mutex_t prof_stats_mtx;
+
+void prof_stats_inc(tsd_t *tsd, szind_t ind, size_t size);
+void prof_stats_dec(tsd_t *tsd, szind_t ind, size_t size);
+void prof_stats_get_live(tsd_t *tsd, szind_t ind, prof_stats_t *stats);
+void prof_stats_get_accum(tsd_t *tsd, szind_t ind, prof_stats_t *stats);
+
+#endif /* JEMALLOC_INTERNAL_PROF_STATS_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/prof_structs.h b/contrib/jemalloc/include/jemalloc/internal/prof_structs.h
index 34ed4822b672..dd22115f6222 100644
--- a/contrib/jemalloc/include/jemalloc/internal/prof_structs.h
+++ b/contrib/jemalloc/include/jemalloc/internal/prof_structs.h
@@ -2,6 +2,7 @@
#define JEMALLOC_INTERNAL_PROF_STRUCTS_H
#include "jemalloc/internal/ckh.h"
+#include "jemalloc/internal/edata.h"
#include "jemalloc/internal/mutex.h"
#include "jemalloc/internal/prng.h"
#include "jemalloc/internal/rb.h"
@@ -15,26 +16,22 @@ struct prof_bt_s {
#ifdef JEMALLOC_PROF_LIBGCC
/* Data structure passed to libgcc _Unwind_Backtrace() callback functions. */
typedef struct {
- prof_bt_t *bt;
+ void **vec;
+ unsigned *len;
unsigned max;
} prof_unwind_data_t;
#endif
-struct prof_accum_s {
-#ifndef JEMALLOC_ATOMIC_U64
- malloc_mutex_t mtx;
- uint64_t accumbytes;
-#else
- atomic_u64_t accumbytes;
-#endif
-};
-
struct prof_cnt_s {
/* Profiling counters. */
uint64_t curobjs;
+ uint64_t curobjs_shifted_unbiased;
uint64_t curbytes;
+ uint64_t curbytes_unbiased;
uint64_t accumobjs;
+ uint64_t accumobjs_shifted_unbiased;
uint64_t accumbytes;
+ uint64_t accumbytes_unbiased;
};
typedef enum {
@@ -55,6 +52,12 @@ struct prof_tctx_s {
uint64_t thr_uid;
uint64_t thr_discrim;
+ /*
+ * Reference count of how many times this tctx object is referenced in
+ * recent allocation / deallocation records, protected by tdata->lock.
+ */
+ uint64_t recent_count;
+
/* Profiling counters, protected by tdata->lock. */
prof_cnt_t cnts;
@@ -96,6 +99,15 @@ struct prof_tctx_s {
};
typedef rb_tree(prof_tctx_t) prof_tctx_tree_t;
+struct prof_info_s {
+ /* Time when the allocation was made. */
+ nstime_t alloc_time;
+ /* Points to the prof_tctx_t corresponding to the allocation. */
+ prof_tctx_t *alloc_tctx;
+ /* Allocation request size. */
+ size_t alloc_size;
+};
+
struct prof_gctx_s {
/* Protects nlimbo, cnt_summed, and tctxs. */
malloc_mutex_t *lock;
@@ -167,9 +179,6 @@ struct prof_tdata_s {
*/
ckh_t bt2tctx;
- /* Sampling state. */
- uint64_t prng_state;
-
/* State used to avoid dumping while operating on prof internals. */
bool enq;
bool enq_idump;
@@ -197,4 +206,16 @@ struct prof_tdata_s {
};
typedef rb_tree(prof_tdata_t) prof_tdata_tree_t;
+struct prof_recent_s {
+ nstime_t alloc_time;
+ nstime_t dalloc_time;
+
+ ql_elm(prof_recent_t) link;
+ size_t size;
+ size_t usize;
+ atomic_p_t alloc_edata; /* NULL means allocation has been freed. */
+ prof_tctx_t *alloc_tctx;
+ prof_tctx_t *dalloc_tctx;
+};
+
#endif /* JEMALLOC_INTERNAL_PROF_STRUCTS_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/prof_sys.h b/contrib/jemalloc/include/jemalloc/internal/prof_sys.h
new file mode 100644
index 000000000000..3d25a4295e28
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/internal/prof_sys.h
@@ -0,0 +1,30 @@
+#ifndef JEMALLOC_INTERNAL_PROF_SYS_H
+#define JEMALLOC_INTERNAL_PROF_SYS_H
+
+extern malloc_mutex_t prof_dump_filename_mtx;
+extern base_t *prof_base;
+
+void bt_init(prof_bt_t *bt, void **vec);
+void prof_backtrace(tsd_t *tsd, prof_bt_t *bt);
+void prof_hooks_init();
+void prof_unwind_init();
+void prof_sys_thread_name_fetch(tsd_t *tsd);
+int prof_getpid(void);
+void prof_get_default_filename(tsdn_t *tsdn, char *filename, uint64_t ind);
+bool prof_prefix_set(tsdn_t *tsdn, const char *prefix);
+void prof_fdump_impl(tsd_t *tsd);
+void prof_idump_impl(tsd_t *tsd);
+bool prof_mdump_impl(tsd_t *tsd, const char *filename);
+void prof_gdump_impl(tsd_t *tsd);
+
+/* Used in unit tests. */
+typedef int (prof_sys_thread_name_read_t)(char *buf, size_t limit);
+extern prof_sys_thread_name_read_t *JET_MUTABLE prof_sys_thread_name_read;
+typedef int (prof_dump_open_file_t)(const char *, int);
+extern prof_dump_open_file_t *JET_MUTABLE prof_dump_open_file;
+typedef ssize_t (prof_dump_write_file_t)(int, const void *, size_t);
+extern prof_dump_write_file_t *JET_MUTABLE prof_dump_write_file;
+typedef int (prof_dump_open_maps_t)();
+extern prof_dump_open_maps_t *JET_MUTABLE prof_dump_open_maps;
+
+#endif /* JEMALLOC_INTERNAL_PROF_SYS_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/prof_types.h b/contrib/jemalloc/include/jemalloc/internal/prof_types.h
index 1eff995ecf0f..ba6286548e04 100644
--- a/contrib/jemalloc/include/jemalloc/internal/prof_types.h
+++ b/contrib/jemalloc/include/jemalloc/internal/prof_types.h
@@ -2,11 +2,12 @@
#define JEMALLOC_INTERNAL_PROF_TYPES_H
typedef struct prof_bt_s prof_bt_t;
-typedef struct prof_accum_s prof_accum_t;
typedef struct prof_cnt_s prof_cnt_t;
typedef struct prof_tctx_s prof_tctx_t;
+typedef struct prof_info_s prof_info_t;
typedef struct prof_gctx_s prof_gctx_t;
typedef struct prof_tdata_s prof_tdata_t;
+typedef struct prof_recent_s prof_recent_t;
/* Option defaults. */
#ifdef JEMALLOC_PROF
@@ -28,7 +29,23 @@ typedef struct prof_tdata_s prof_tdata_t;
#define PROF_CKH_MINITEMS 64
/* Size of memory buffer to use when writing dump files. */
-#define PROF_DUMP_BUFSIZE 65536
+#ifndef JEMALLOC_PROF
+/* Minimize memory bloat for non-prof builds. */
+# define PROF_DUMP_BUFSIZE 1
+#elif defined(JEMALLOC_DEBUG)
+/* Use a small buffer size in debug build, mainly to facilitate testing. */
+# define PROF_DUMP_BUFSIZE 16
+#else
+# define PROF_DUMP_BUFSIZE 65536
+#endif
+
+/* Size of size class related tables */
+#ifdef JEMALLOC_PROF
+# define PROF_SC_NSIZES SC_NSIZES
+#else
+/* Minimize memory bloat for non-prof builds. */
+# define PROF_SC_NSIZES 1
+#endif
/* Size of stack-allocated buffer used by prof_printf(). */
#define PROF_PRINTF_BUFSIZE 128
@@ -45,12 +62,14 @@ typedef struct prof_tdata_s prof_tdata_t;
*/
#define PROF_NTDATA_LOCKS 256
-/*
- * prof_tdata pointers close to NULL are used to encode state information that
- * is used for cleaning up during thread shutdown.
- */
-#define PROF_TDATA_STATE_REINCARNATED ((prof_tdata_t *)(uintptr_t)1)
-#define PROF_TDATA_STATE_PURGATORY ((prof_tdata_t *)(uintptr_t)2)
-#define PROF_TDATA_STATE_MAX PROF_TDATA_STATE_PURGATORY
+/* Minimize memory bloat for non-prof builds. */
+#ifdef JEMALLOC_PROF
+#define PROF_DUMP_FILENAME_LEN (PATH_MAX + 1)
+#else
+#define PROF_DUMP_FILENAME_LEN 1
+#endif
+
+/* Default number of recent allocations to record. */
+#define PROF_RECENT_ALLOC_MAX_DEFAULT 0
#endif /* JEMALLOC_INTERNAL_PROF_TYPES_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/psset.h b/contrib/jemalloc/include/jemalloc/internal/psset.h
new file mode 100644
index 000000000000..e1d64970ee14
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/internal/psset.h
@@ -0,0 +1,131 @@
+#ifndef JEMALLOC_INTERNAL_PSSET_H
+#define JEMALLOC_INTERNAL_PSSET_H
+
+#include "jemalloc/internal/hpdata.h"
+
+/*
+ * A page-slab set. What the eset is to PAC, the psset is to HPA. It maintains
+ * a collection of page-slabs (the intent being that they are backed by
+ * hugepages, or at least could be), and handles allocation and deallocation
+ * requests.
+ */
+
+/*
+ * One more than the maximum pszind_t we will serve out of the HPA.
+ * Practically, we expect only the first few to be actually used. This
+ * corresponds to a maximum size of of 512MB on systems with 4k pages and
+ * SC_NGROUP == 4, which is already an unreasonably large maximum. Morally, you
+ * can think of this as being SC_NPSIZES, but there's no sense in wasting that
+ * much space in the arena, making bitmaps that much larger, etc.
+ */
+#define PSSET_NPSIZES 64
+
+/*
+ * We keep two purge lists per page size class; one for hugified hpdatas (at
+ * index 2*pszind), and one for the non-hugified hpdatas (at index 2*pszind +
+ * 1). This lets us implement a preference for purging non-hugified hpdatas
+ * among similarly-dirty ones.
+ * We reserve the last two indices for empty slabs, in that case purging
+ * hugified ones (which are definitionally all waste) before non-hugified ones
+ * (i.e. reversing the order).
+ */
+#define PSSET_NPURGE_LISTS (2 * PSSET_NPSIZES)
+
+typedef struct psset_bin_stats_s psset_bin_stats_t;
+struct psset_bin_stats_s {
+ /* How many pageslabs are in this bin? */
+ size_t npageslabs;
+ /* Of them, how many pages are active? */
+ size_t nactive;
+ /* And how many are dirty? */
+ size_t ndirty;
+};
+
+typedef struct psset_stats_s psset_stats_t;
+struct psset_stats_s {
+ /*
+ * The second index is huge stats; nonfull_slabs[pszind][0] contains
+ * stats for the non-huge slabs in bucket pszind, while
+ * nonfull_slabs[pszind][1] contains stats for the huge slabs.
+ */
+ psset_bin_stats_t nonfull_slabs[PSSET_NPSIZES][2];
+
+ /*
+ * Full slabs don't live in any edata heap, but we still track their
+ * stats.
+ */
+ psset_bin_stats_t full_slabs[2];
+
+ /* Empty slabs are similar. */
+ psset_bin_stats_t empty_slabs[2];
+};
+
+typedef struct psset_s psset_t;
+struct psset_s {
+ /*
+ * The pageslabs, quantized by the size class of the largest contiguous
+ * free run of pages in a pageslab.
+ */
+ hpdata_age_heap_t pageslabs[PSSET_NPSIZES];
+ /* Bitmap for which set bits correspond to non-empty heaps. */
+ fb_group_t pageslab_bitmap[FB_NGROUPS(PSSET_NPSIZES)];
+ /*
+ * The sum of all bin stats in stats. This lets us quickly answer
+ * queries for the number of dirty, active, and retained pages in the
+ * entire set.
+ */
+ psset_bin_stats_t merged_stats;
+ psset_stats_t stats;
+ /*
+ * Slabs with no active allocations, but which are allowed to serve new
+ * allocations.
+ */
+ hpdata_empty_list_t empty;
+ /*
+ * Slabs which are available to be purged, ordered by how much we want
+ * to purge them (with later indices indicating slabs we want to purge
+ * more).
+ */
+ hpdata_purge_list_t to_purge[PSSET_NPURGE_LISTS];
+ /* Bitmap for which set bits correspond to non-empty purge lists. */
+ fb_group_t purge_bitmap[FB_NGROUPS(PSSET_NPURGE_LISTS)];
+ /* Slabs which are available to be hugified. */
+ hpdata_hugify_list_t to_hugify;
+};
+
+void psset_init(psset_t *psset);
+void psset_stats_accum(psset_stats_t *dst, psset_stats_t *src);
+
+/*
+ * Begin or end updating the given pageslab's metadata. While the pageslab is
+ * being updated, it won't be returned from psset_fit calls.
+ */
+void psset_update_begin(psset_t *psset, hpdata_t *ps);
+void psset_update_end(psset_t *psset, hpdata_t *ps);
+
+/* Analogous to the eset_fit; pick a hpdata to serve the request. */
+hpdata_t *psset_pick_alloc(psset_t *psset, size_t size);
+/* Pick one to purge. */
+hpdata_t *psset_pick_purge(psset_t *psset);
+/* Pick one to hugify. */
+hpdata_t *psset_pick_hugify(psset_t *psset);
+
+void psset_insert(psset_t *psset, hpdata_t *ps);
+void psset_remove(psset_t *psset, hpdata_t *ps);
+
+static inline size_t
+psset_npageslabs(psset_t *psset) {
+ return psset->merged_stats.npageslabs;
+}
+
+static inline size_t
+psset_nactive(psset_t *psset) {
+ return psset->merged_stats.nactive;
+}
+
+static inline size_t
+psset_ndirty(psset_t *psset) {
+ return psset->merged_stats.ndirty;
+}
+
+#endif /* JEMALLOC_INTERNAL_PSSET_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/public_namespace.sh b/contrib/jemalloc/include/jemalloc/internal/public_namespace.sh
new file mode 100755
index 000000000000..4d415ba01fa8
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/internal/public_namespace.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+for nm in `cat $1` ; do
+ n=`echo ${nm} |tr ':' ' ' |awk '{print $1}'`
+ echo "#define je_${n} JEMALLOC_N(${n})"
+done
diff --git a/contrib/jemalloc/include/jemalloc/internal/public_unnamespace.sh b/contrib/jemalloc/include/jemalloc/internal/public_unnamespace.sh
new file mode 100755
index 000000000000..4239d17754ca
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/internal/public_unnamespace.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+for nm in `cat $1` ; do
+ n=`echo ${nm} |tr ':' ' ' |awk '{print $1}'`
+ echo "#undef je_${n}"
+done
diff --git a/contrib/jemalloc/include/jemalloc/internal/ql.h b/contrib/jemalloc/include/jemalloc/internal/ql.h
index 802904077161..c7f52f862219 100644
--- a/contrib/jemalloc/include/jemalloc/internal/ql.h
+++ b/contrib/jemalloc/include/jemalloc/internal/ql.h
@@ -3,37 +3,85 @@
#include "jemalloc/internal/qr.h"
+/*
+ * A linked-list implementation.
+ *
+ * This is built on top of the ring implementation, but that can be viewed as an
+ * implementation detail (i.e. trying to advance past the tail of the list
+ * doesn't wrap around).
+ *
+ * You define a struct like so:
+ * typedef strucy my_s my_t;
+ * struct my_s {
+ * int data;
+ * ql_elm(my_t) my_link;
+ * };
+ *
+ * // We wobble between "list" and "head" for this type; we're now mostly
+ * // heading towards "list".
+ * typedef ql_head(my_t) my_list_t;
+ *
+ * You then pass a my_list_t * for a_head arguments, a my_t * for a_elm
+ * arguments, the token "my_link" for a_field arguments, and the token "my_t"
+ * for a_type arguments.
+ */
+
/* List definitions. */
#define ql_head(a_type) \
struct { \
a_type *qlh_first; \
}
+/* Static initializer for an empty list. */
#define ql_head_initializer(a_head) {NULL}
+/* The field definition. */
#define ql_elm(a_type) qr(a_type)
-/* List functions. */
+/* A pointer to the first element in the list, or NULL if the list is empty. */
+#define ql_first(a_head) ((a_head)->qlh_first)
+
+/* Dynamically initializes a list. */
#define ql_new(a_head) do { \
- (a_head)->qlh_first = NULL; \
+ ql_first(a_head) = NULL; \
} while (0)
-#define ql_elm_new(a_elm, a_field) qr_new((a_elm), a_field)
+/*
+ * Sets dest to be the contents of src (overwriting any elements there), leaving
+ * src empty.
+ */
+#define ql_move(a_head_dest, a_head_src) do { \
+ ql_first(a_head_dest) = ql_first(a_head_src); \
+ ql_new(a_head_src); \
+} while (0)
-#define ql_first(a_head) ((a_head)->qlh_first)
+/* True if the list is empty, otherwise false. */
+#define ql_empty(a_head) (ql_first(a_head) == NULL)
+
+/*
+ * Initializes a ql_elm. Must be called even if the field is about to be
+ * overwritten.
+ */
+#define ql_elm_new(a_elm, a_field) qr_new((a_elm), a_field)
+/*
+ * Obtains the last item in the list.
+ */
#define ql_last(a_head, a_field) \
- ((ql_first(a_head) != NULL) \
- ? qr_prev(ql_first(a_head), a_field) : NULL)
+ (ql_empty(a_head) ? NULL : qr_prev(ql_first(a_head), a_field))
+/*
+ * Gets a pointer to the next/prev element in the list. Trying to advance past
+ * the end or retreat before the beginning of the list returns NULL.
+ */
#define ql_next(a_head, a_elm, a_field) \
((ql_last(a_head, a_field) != (a_elm)) \
? qr_next((a_elm), a_field) : NULL)
-
#define ql_prev(a_head, a_elm, a_field) \
((ql_first(a_head) != (a_elm)) ? qr_prev((a_elm), a_field) \
: NULL)
+/* Inserts a_elm before a_qlelm in the list. */
#define ql_before_insert(a_head, a_qlelm, a_elm, a_field) do { \
qr_before_insert((a_qlelm), (a_elm), a_field); \
if (ql_first(a_head) == (a_qlelm)) { \
@@ -41,23 +89,41 @@ struct { \
} \
} while (0)
+/* Inserts a_elm after a_qlelm in the list. */
#define ql_after_insert(a_qlelm, a_elm, a_field) \
qr_after_insert((a_qlelm), (a_elm), a_field)
+/* Inserts a_elm as the first item in the list. */
#define ql_head_insert(a_head, a_elm, a_field) do { \
- if (ql_first(a_head) != NULL) { \
+ if (!ql_empty(a_head)) { \
qr_before_insert(ql_first(a_head), (a_elm), a_field); \
} \
ql_first(a_head) = (a_elm); \
} while (0)
+/* Inserts a_elm as the last item in the list. */
#define ql_tail_insert(a_head, a_elm, a_field) do { \
- if (ql_first(a_head) != NULL) { \
+ if (!ql_empty(a_head)) { \
qr_before_insert(ql_first(a_head), (a_elm), a_field); \
} \
ql_first(a_head) = qr_next((a_elm), a_field); \
} while (0)
+/*
+ * Given lists a = [a_1, ..., a_n] and [b_1, ..., b_n], results in:
+ * a = [a1, ..., a_n, b_1, ..., b_n] and b = [].
+ */
+#define ql_concat(a_head_a, a_head_b, a_field) do { \
+ if (ql_empty(a_head_a)) { \
+ ql_move(a_head_a, a_head_b); \
+ } else if (!ql_empty(a_head_b)) { \
+ qr_meld(ql_first(a_head_a), ql_first(a_head_b), \
+ a_field); \
+ ql_new(a_head_b); \
+ } \
+} while (0)
+
+/* Removes a_elm from the list. */
#define ql_remove(a_head, a_elm, a_field) do { \
if (ql_first(a_head) == (a_elm)) { \
ql_first(a_head) = qr_next(ql_first(a_head), a_field); \
@@ -65,20 +131,63 @@ struct { \
if (ql_first(a_head) != (a_elm)) { \
qr_remove((a_elm), a_field); \
} else { \
- ql_first(a_head) = NULL; \
+ ql_new(a_head); \
} \
} while (0)
+/* Removes the first item in the list. */
#define ql_head_remove(a_head, a_type, a_field) do { \
a_type *t = ql_first(a_head); \
ql_remove((a_head), t, a_field); \
} while (0)
+/* Removes the last item in the list. */
#define ql_tail_remove(a_head, a_type, a_field) do { \
a_type *t = ql_last(a_head, a_field); \
ql_remove((a_head), t, a_field); \
} while (0)
+/*
+ * Given a = [a_1, a_2, ..., a_n-1, a_n, a_n+1, ...],
+ * ql_split(a, a_n, b, some_field) results in
+ * a = [a_1, a_2, ..., a_n-1]
+ * and replaces b's contents with:
+ * b = [a_n, a_n+1, ...]
+ */
+#define ql_split(a_head_a, a_elm, a_head_b, a_field) do { \
+ if (ql_first(a_head_a) == (a_elm)) { \
+ ql_move(a_head_b, a_head_a); \
+ } else { \
+ qr_split(ql_first(a_head_a), (a_elm), a_field); \
+ ql_first(a_head_b) = (a_elm); \
+ } \
+} while (0)
+
+/*
+ * An optimized version of:
+ * a_type *t = ql_first(a_head);
+ * ql_remove((a_head), t, a_field);
+ * ql_tail_insert((a_head), t, a_field);
+ */
+#define ql_rotate(a_head, a_field) do { \
+ ql_first(a_head) = qr_next(ql_first(a_head), a_field); \
+} while (0)
+
+/*
+ * Helper macro to iterate over each element in a list in order, starting from
+ * the head (or in reverse order, starting from the tail). The usage is
+ * (assuming my_t and my_list_t defined as above).
+ *
+ * int sum(my_list_t *list) {
+ * int sum = 0;
+ * my_t *iter;
+ * ql_foreach(iter, list, link) {
+ * sum += iter->data;
+ * }
+ * return sum;
+ * }
+ */
+
#define ql_foreach(a_var, a_head, a_field) \
qr_foreach((a_var), ql_first(a_head), a_field)
diff --git a/contrib/jemalloc/include/jemalloc/internal/qr.h b/contrib/jemalloc/include/jemalloc/internal/qr.h
index 1e1056b38685..ece4f5568600 100644
--- a/contrib/jemalloc/include/jemalloc/internal/qr.h
+++ b/contrib/jemalloc/include/jemalloc/internal/qr.h
@@ -1,6 +1,21 @@
#ifndef JEMALLOC_INTERNAL_QR_H
#define JEMALLOC_INTERNAL_QR_H
+/*
+ * A ring implementation based on an embedded circular doubly-linked list.
+ *
+ * You define your struct like so:
+ *
+ * typedef struct my_s my_t;
+ * struct my_s {
+ * int data;
+ * qr(my_t) my_link;
+ * };
+ *
+ * And then pass a my_t * into macros for a_qr arguments, and the token
+ * "my_link" into a_field fields.
+ */
+
/* Ring definitions. */
#define qr(a_type) \
struct { \
@@ -8,61 +23,114 @@ struct { \
a_type *qre_prev; \
}
-/* Ring functions. */
+/*
+ * Initialize a qr link. Every link must be initialized before being used, even
+ * if that initialization is going to be immediately overwritten (say, by being
+ * passed into an insertion macro).
+ */
#define qr_new(a_qr, a_field) do { \
(a_qr)->a_field.qre_next = (a_qr); \
(a_qr)->a_field.qre_prev = (a_qr); \
} while (0)
+/*
+ * Go forwards or backwards in the ring. Note that (the ring being circular), this
+ * always succeeds -- you just keep looping around and around the ring if you
+ * chase pointers without end.
+ */
#define qr_next(a_qr, a_field) ((a_qr)->a_field.qre_next)
-
#define qr_prev(a_qr, a_field) ((a_qr)->a_field.qre_prev)
-#define qr_before_insert(a_qrelm, a_qr, a_field) do { \
- (a_qr)->a_field.qre_prev = (a_qrelm)->a_field.qre_prev; \
- (a_qr)->a_field.qre_next = (a_qrelm); \
- (a_qr)->a_field.qre_prev->a_field.qre_next = (a_qr); \
- (a_qrelm)->a_field.qre_prev = (a_qr); \
+/*
+ * Given two rings:
+ * a -> a_1 -> ... -> a_n --
+ * ^ |
+ * |------------------------
+ *
+ * b -> b_1 -> ... -> b_n --
+ * ^ |
+ * |------------------------
+ *
+ * Results in the ring:
+ * a -> a_1 -> ... -> a_n -> b -> b_1 -> ... -> b_n --
+ * ^ |
+ * |-------------------------------------------------|
+ *
+ * a_qr_a can directly be a qr_next() macro, but a_qr_b cannot.
+ */
+#define qr_meld(a_qr_a, a_qr_b, a_field) do { \
+ (a_qr_b)->a_field.qre_prev->a_field.qre_next = \
+ (a_qr_a)->a_field.qre_prev; \
+ (a_qr_a)->a_field.qre_prev = (a_qr_b)->a_field.qre_prev; \
+ (a_qr_b)->a_field.qre_prev = \
+ (a_qr_b)->a_field.qre_prev->a_field.qre_next; \
+ (a_qr_a)->a_field.qre_prev->a_field.qre_next = (a_qr_a); \
+ (a_qr_b)->a_field.qre_prev->a_field.qre_next = (a_qr_b); \
} while (0)
-#define qr_after_insert(a_qrelm, a_qr, a_field) do { \
- (a_qr)->a_field.qre_next = (a_qrelm)->a_field.qre_next; \
- (a_qr)->a_field.qre_prev = (a_qrelm); \
- (a_qr)->a_field.qre_next->a_field.qre_prev = (a_qr); \
- (a_qrelm)->a_field.qre_next = (a_qr); \
-} while (0)
+/*
+ * Logically, this is just a meld. The intent, though, is that a_qrelm is a
+ * single-element ring, so that "before" has a more obvious interpretation than
+ * meld.
+ */
+#define qr_before_insert(a_qrelm, a_qr, a_field) \
+ qr_meld((a_qrelm), (a_qr), a_field)
-#define qr_meld(a_qr_a, a_qr_b, a_type, a_field) do { \
- a_type *t; \
- (a_qr_a)->a_field.qre_prev->a_field.qre_next = (a_qr_b); \
- (a_qr_b)->a_field.qre_prev->a_field.qre_next = (a_qr_a); \
- t = (a_qr_a)->a_field.qre_prev; \
- (a_qr_a)->a_field.qre_prev = (a_qr_b)->a_field.qre_prev; \
- (a_qr_b)->a_field.qre_prev = t; \
-} while (0)
+/* Ditto, but inserting after rather than before. */
+#define qr_after_insert(a_qrelm, a_qr, a_field) \
+ qr_before_insert(qr_next(a_qrelm, a_field), (a_qr), a_field)
/*
+ * Inverts meld; given the ring:
+ * a -> a_1 -> ... -> a_n -> b -> b_1 -> ... -> b_n --
+ * ^ |
+ * |-------------------------------------------------|
+ *
+ * Results in two rings:
+ * a -> a_1 -> ... -> a_n --
+ * ^ |
+ * |------------------------
+ *
+ * b -> b_1 -> ... -> b_n --
+ * ^ |
+ * |------------------------
+ *
* qr_meld() and qr_split() are functionally equivalent, so there's no need to
* have two copies of the code.
*/
-#define qr_split(a_qr_a, a_qr_b, a_type, a_field) \
- qr_meld((a_qr_a), (a_qr_b), a_type, a_field)
+#define qr_split(a_qr_a, a_qr_b, a_field) \
+ qr_meld((a_qr_a), (a_qr_b), a_field)
-#define qr_remove(a_qr, a_field) do { \
- (a_qr)->a_field.qre_prev->a_field.qre_next \
- = (a_qr)->a_field.qre_next; \
- (a_qr)->a_field.qre_next->a_field.qre_prev \
- = (a_qr)->a_field.qre_prev; \
- (a_qr)->a_field.qre_next = (a_qr); \
- (a_qr)->a_field.qre_prev = (a_qr); \
-} while (0)
+/*
+ * Splits off a_qr from the rest of its ring, so that it becomes a
+ * single-element ring.
+ */
+#define qr_remove(a_qr, a_field) \
+ qr_split(qr_next(a_qr, a_field), (a_qr), a_field)
+/*
+ * Helper macro to iterate over each element in a ring exactly once, starting
+ * with a_qr. The usage is (assuming my_t defined as above):
+ *
+ * int sum(my_t *item) {
+ * int sum = 0;
+ * my_t *iter;
+ * qr_foreach(iter, item, link) {
+ * sum += iter->data;
+ * }
+ * return sum;
+ * }
+ */
#define qr_foreach(var, a_qr, a_field) \
for ((var) = (a_qr); \
(var) != NULL; \
(var) = (((var)->a_field.qre_next != (a_qr)) \
? (var)->a_field.qre_next : NULL))
+/*
+ * The same (and with the same usage) as qr_foreach, but in the opposite order,
+ * ending with a_qr.
+ */
#define qr_reverse_foreach(var, a_qr, a_field) \
for ((var) = ((a_qr) != NULL) ? qr_prev(a_qr, a_field) : NULL; \
(var) != NULL; \
diff --git a/contrib/jemalloc/include/jemalloc/internal/quantum.h b/contrib/jemalloc/include/jemalloc/internal/quantum.h
index 821086e992cd..c22d753aa79f 100644
--- a/contrib/jemalloc/include/jemalloc/internal/quantum.h
+++ b/contrib/jemalloc/include/jemalloc/internal/quantum.h
@@ -30,11 +30,18 @@
# ifdef __hppa__
# define LG_QUANTUM 4
# endif
+# ifdef __loongarch__
+# define LG_QUANTUM 4
+# endif
# ifdef __m68k__
# define LG_QUANTUM 3
# endif
# ifdef __mips__
-# define LG_QUANTUM 3
+# if defined(__mips_n32) || defined(__mips_n64)
+# define LG_QUANTUM 4
+# else
+# define LG_QUANTUM 3
+# endif
# endif
# ifdef __nios2__
# define LG_QUANTUM 3
@@ -61,6 +68,9 @@
# ifdef __le32__
# define LG_QUANTUM 4
# endif
+# ifdef __arc__
+# define LG_QUANTUM 3
+# endif
# ifndef LG_QUANTUM
# error "Unknown minimum alignment for architecture; specify via "
"--with-lg-quantum"
diff --git a/contrib/jemalloc/include/jemalloc/internal/rb.h b/contrib/jemalloc/include/jemalloc/internal/rb.h
index 47fa5ca99bbe..a9a51cb68604 100644
--- a/contrib/jemalloc/include/jemalloc/internal/rb.h
+++ b/contrib/jemalloc/include/jemalloc/internal/rb.h
@@ -1,3 +1,6 @@
+#ifndef JEMALLOC_INTERNAL_RB_H
+#define JEMALLOC_INTERNAL_RB_H
+
/*-
*******************************************************************************
*
@@ -19,13 +22,19 @@
*******************************************************************************
*/
-#ifndef RB_H_
-#define RB_H_
-
#ifndef __PGI
#define RB_COMPACT
#endif
+/*
+ * Each node in the RB tree consumes at least 1 byte of space (for the linkage
+ * if nothing else, so there are a maximum of sizeof(void *) << 3 rb tree nodes
+ * in any process (and thus, at most sizeof(void *) << 3 nodes in any rb tree).
+ * The choice of algorithm bounds the depth of a tree to twice the binary log of
+ * the number of elements in the tree; the following bound follows.
+ */
+#define RB_MAX_DEPTH (sizeof(void *) << 4)
+
#ifdef RB_COMPACT
/* Node structure. */
#define rb_node(a_type) \
@@ -159,12 +168,22 @@ struct { \
rbtn_right_set(a_type, a_field, (r_node), (a_node)); \
} while (0)
+#define rb_summarized_only_false(...)
+#define rb_summarized_only_true(...) __VA_ARGS__
+#define rb_empty_summarize(a_node, a_lchild, a_rchild) false
+
/*
- * The rb_proto() macro generates function prototypes that correspond to the
- * functions generated by an equivalently parameterized call to rb_gen().
+ * The rb_proto() and rb_summarized_proto() macros generate function prototypes
+ * that correspond to the functions generated by an equivalently parameterized
+ * call to rb_gen() or rb_summarized_gen(), respectively.
*/
#define rb_proto(a_attr, a_prefix, a_rbt_type, a_type) \
+ rb_proto_impl(a_attr, a_prefix, a_rbt_type, a_type, false)
+#define rb_summarized_proto(a_attr, a_prefix, a_rbt_type, a_type) \
+ rb_proto_impl(a_attr, a_prefix, a_rbt_type, a_type, true)
+#define rb_proto_impl(a_attr, a_prefix, a_rbt_type, a_type, \
+ a_is_summarized) \
a_attr void \
a_prefix##new(a_rbt_type *rbtree); \
a_attr bool \
@@ -195,31 +214,94 @@ a_prefix##reverse_iter(a_rbt_type *rbtree, a_type *start, \
a_type *(*cb)(a_rbt_type *, a_type *, void *), void *arg); \
a_attr void \
a_prefix##destroy(a_rbt_type *rbtree, void (*cb)(a_type *, void *), \
- void *arg);
+ void *arg); \
+/* Extended API */ \
+rb_summarized_only_##a_is_summarized( \
+a_attr void \
+a_prefix##update_summaries(a_rbt_type *rbtree, a_type *node); \
+a_attr bool \
+a_prefix##empty_filtered(a_rbt_type *rbtree, \
+ bool (*filter_node)(void *, a_type *), \
+ bool (*filter_subtree)(void *, a_type *), \
+ void *filter_ctx); \
+a_attr a_type * \
+a_prefix##first_filtered(a_rbt_type *rbtree, \
+ bool (*filter_node)(void *, a_type *), \
+ bool (*filter_subtree)(void *, a_type *), \
+ void *filter_ctx); \
+a_attr a_type * \
+a_prefix##last_filtered(a_rbt_type *rbtree, \
+ bool (*filter_node)(void *, a_type *), \
+ bool (*filter_subtree)(void *, a_type *), \
+ void *filter_ctx); \
+a_attr a_type * \
+a_prefix##next_filtered(a_rbt_type *rbtree, a_type *node, \
+ bool (*filter_node)(void *, a_type *), \
+ bool (*filter_subtree)(void *, a_type *), \
+ void *filter_ctx); \
+a_attr a_type * \
+a_prefix##prev_filtered(a_rbt_type *rbtree, a_type *node, \
+ bool (*filter_node)(void *, a_type *), \
+ bool (*filter_subtree)(void *, a_type *), \
+ void *filter_ctx); \
+a_attr a_type * \
+a_prefix##search_filtered(a_rbt_type *rbtree, const a_type *key, \
+ bool (*filter_node)(void *, a_type *), \
+ bool (*filter_subtree)(void *, a_type *), \
+ void *filter_ctx); \
+a_attr a_type * \
+a_prefix##nsearch_filtered(a_rbt_type *rbtree, const a_type *key, \
+ bool (*filter_node)(void *, a_type *), \
+ bool (*filter_subtree)(void *, a_type *), \
+ void *filter_ctx); \
+a_attr a_type * \
+a_prefix##psearch_filtered(a_rbt_type *rbtree, const a_type *key, \
+ bool (*filter_node)(void *, a_type *), \
+ bool (*filter_subtree)(void *, a_type *), \
+ void *filter_ctx); \
+a_attr a_type * \
+a_prefix##iter_filtered(a_rbt_type *rbtree, a_type *start, \
+ a_type *(*cb)(a_rbt_type *, a_type *, void *), void *arg, \
+ bool (*filter_node)(void *, a_type *), \
+ bool (*filter_subtree)(void *, a_type *), \
+ void *filter_ctx); \
+a_attr a_type * \
+a_prefix##reverse_iter_filtered(a_rbt_type *rbtree, a_type *start, \
+ a_type *(*cb)(a_rbt_type *, a_type *, void *), void *arg, \
+ bool (*filter_node)(void *, a_type *), \
+ bool (*filter_subtree)(void *, a_type *), \
+ void *filter_ctx); \
+)
/*
* The rb_gen() macro generates a type-specific red-black tree implementation,
* based on the above cpp macros.
- *
* Arguments:
*
- * a_attr : Function attribute for generated functions (ex: static).
- * a_prefix : Prefix for generated functions (ex: ex_).
- * a_rb_type : Type for red-black tree data structure (ex: ex_t).
- * a_type : Type for red-black tree node data structure (ex: ex_node_t).
- * a_field : Name of red-black tree node linkage (ex: ex_link).
- * a_cmp : Node comparison function name, with the following prototype:
- * int (a_cmp *)(a_type *a_node, a_type *a_other);
- * ^^^^^^
- * or a_key
- * Interpretation of comparison function return values:
- * -1 : a_node < a_other
- * 0 : a_node == a_other
- * 1 : a_node > a_other
- * In all cases, the a_node or a_key macro argument is the first
- * argument to the comparison function, which makes it possible
- * to write comparison functions that treat the first argument
- * specially.
+ * a_attr:
+ * Function attribute for generated functions (ex: static).
+ * a_prefix:
+ * Prefix for generated functions (ex: ex_).
+ * a_rb_type:
+ * Type for red-black tree data structure (ex: ex_t).
+ * a_type:
+ * Type for red-black tree node data structure (ex: ex_node_t).
+ * a_field:
+ * Name of red-black tree node linkage (ex: ex_link).
+ * a_cmp:
+ * Node comparison function name, with the following prototype:
+ *
+ * int a_cmp(a_type *a_node, a_type *a_other);
+ * ^^^^^^
+ * or a_key
+ * Interpretation of comparison function return values:
+ * -1 : a_node < a_other
+ * 0 : a_node == a_other
+ * 1 : a_node > a_other
+ * In all cases, the a_node or a_key macro argument is the first argument to
+ * the comparison function, which makes it possible to write comparison
+ * functions that treat the first argument specially. a_cmp must be a total
+ * order on values inserted into the tree -- duplicates are not allowed.
*
* Assuming the following setup:
*
@@ -338,8 +420,193 @@ a_prefix##destroy(a_rbt_type *rbtree, void (*cb)(a_type *, void *), \
* during iteration. There is no way to stop iteration once it
* has begun.
* arg : Opaque pointer passed to cb().
+ *
+ * The rb_summarized_gen() macro generates all the functions above, but has an
+ * expanded interface. In introduces the notion of summarizing subtrees, and of
+ * filtering searches in the tree according to the information contained in
+ * those summaries.
+ * The extra macro argument is:
+ * a_summarize:
+ * Tree summarization function name, with the following prototype:
+ *
+ * bool a_summarize(a_type *a_node, const a_type *a_left_child,
+ * const a_type *a_right_child);
+ *
+ * This function should update a_node with the summary of the subtree rooted
+ * there, using the data contained in it and the summaries in a_left_child
+ * and a_right_child. One or both of them may be NULL. When the tree
+ * changes due to an insertion or removal, it updates the summaries of all
+ * nodes whose subtrees have changed (always updating the summaries of
+ * children before their parents). If the user alters a node in the tree in
+ * a way that may change its summary, they can call the generated
+ * update_summaries function to bubble up the summary changes to the root.
+ * It should return true if the summary changed (or may have changed), and
+ * false if it didn't (which will allow the implementation to terminate
+ * "bubbling up" the summaries early).
+ * As the parameter names indicate, the children are ordered as they are in
+ * the tree, a_left_child, if it is not NULL, compares less than a_node,
+ * which in turn compares less than a_right_child (if a_right_child is not
+ * NULL).
+ *
+ * Using the same setup as above but replacing the macro with
+ * rb_summarized_gen(static, ex_, ex_t, ex_node_t, ex_link, ex_cmp,
+ * ex_summarize)
+ *
+ * Generates all the previous functions, but adds some more:
+ *
+ * static void
+ * ex_update_summaries(ex_t *tree, ex_node_t *node);
+ * Description: Recompute all summaries of ancestors of node.
+ * Args:
+ * tree: Pointer to an initialized red-black tree object.
+ * node: The element of the tree whose summary may have changed.
+ *
+ * For each of ex_empty, ex_first, ex_last, ex_next, ex_prev, ex_search,
+ * ex_nsearch, ex_psearch, ex_iter, and ex_reverse_iter, an additional function
+ * is generated as well, with the suffix _filtered (e.g. ex_empty_filtered,
+ * ex_first_filtered, etc.). These use the concept of a "filter"; a binary
+ * property some node either satisfies or does not satisfy. Clever use of the
+ * a_summary argument to rb_summarized_gen can allow efficient computation of
+ * these predicates across whole subtrees of the tree.
+ * The extended API functions accept three additional arguments after the
+ * arguments to the corresponding non-extended equivalent.
+ *
+ * ex_fn(..., bool (*filter_node)(void *, ex_node_t *),
+ * bool (*filter_subtree)(void *, ex_node_t *), void *filter_ctx);
+ * filter_node : Returns true if the node passes the filter.
+ * filter_subtree : Returns true if some node in the subtree rooted at
+ * node passes the filter.
+ * filter_ctx : A context argument passed to the filters.
+ *
+ * For a more concrete example of summarizing and filtering, suppose we're using
+ * the red-black tree to track a set of integers:
+ *
+ * struct ex_node_s {
+ * rb_node(ex_node_t) ex_link;
+ * unsigned data;
+ * };
+ *
+ * Suppose, for some application-specific reason, we want to be able to quickly
+ * find numbers in the set which are divisible by large powers of 2 (say, for
+ * aligned allocation purposes). We augment the node with a summary field:
+ *
+ * struct ex_node_s {
+ * rb_node(ex_node_t) ex_link;
+ * unsigned data;
+ * unsigned max_subtree_ffs;
+ * }
+ *
+ * and define our summarization function as follows:
+ *
+ * bool
+ * ex_summarize(ex_node_t *node, const ex_node_t *lchild,
+ * const ex_node_t *rchild) {
+ * unsigned new_max_subtree_ffs = ffs(node->data);
+ * if (lchild != NULL && lchild->max_subtree_ffs > new_max_subtree_ffs) {
+ * new_max_subtree_ffs = lchild->max_subtree_ffs;
+ * }
+ * if (rchild != NULL && rchild->max_subtree_ffs > new_max_subtree_ffs) {
+ * new_max_subtree_ffs = rchild->max_subtree_ffs;
+ * }
+ * bool changed = (node->max_subtree_ffs != new_max_subtree_ffs)
+ * node->max_subtree_ffs = new_max_subtree_ffs;
+ * // This could be "return true" without any correctness or big-O
+ * // performance changes; but practically, precisely reporting summary
+ * // changes reduces the amount of work that has to be done when "bubbling
+ * // up" summary changes.
+ * return changed;
+ * }
+ *
+ * We can now implement our filter functions as follows:
+ * bool
+ * ex_filter_node(void *filter_ctx, ex_node_t *node) {
+ * unsigned required_ffs = *(unsigned *)filter_ctx;
+ * return ffs(node->data) >= required_ffs;
+ * }
+ * bool
+ * ex_filter_subtree(void *filter_ctx, ex_node_t *node) {
+ * unsigned required_ffs = *(unsigned *)filter_ctx;
+ * return node->max_subtree_ffs >= required_ffs;
+ * }
+ *
+ * We can now easily search for, e.g., the smallest integer in the set that's
+ * divisible by 128:
+ * ex_node_t *
+ * find_div_128(ex_tree_t *tree) {
+ * unsigned min_ffs = 7;
+ * return ex_first_filtered(tree, &ex_filter_node, &ex_filter_subtree,
+ * &min_ffs);
+ * }
+ *
+ * We could with similar ease:
+ * - Fnd the next multiple of 128 in the set that's larger than 12345 (with
+ * ex_nsearch_filtered)
+ * - Iterate over just those multiples of 64 that are in the set (with
+ * ex_iter_filtered)
+ * - Determine if the set contains any multiples of 1024 (with
+ * ex_empty_filtered).
+ *
+ * Some possibly subtle API notes:
+ * - The node argument to ex_next_filtered and ex_prev_filtered need not pass
+ * the filter; it will find the next/prev node that passes the filter.
+ * - ex_search_filtered will fail even for a node in the tree, if that node does
+ * not pass the filter. ex_psearch_filtered and ex_nsearch_filtered behave
+ * similarly; they may return a node larger/smaller than the key, even if a
+ * node equivalent to the key is in the tree (but does not pass the filter).
+ * - Similarly, if the start argument to a filtered iteration function does not
+ * pass the filter, the callback won't be invoked on it.
+ *
+ * These should make sense after a moment's reflection; each post-condition is
+ * the same as with the unfiltered version, with the added constraint that the
+ * returned node must pass the filter.
*/
#define rb_gen(a_attr, a_prefix, a_rbt_type, a_type, a_field, a_cmp) \
+ rb_gen_impl(a_attr, a_prefix, a_rbt_type, a_type, a_field, a_cmp, \
+ rb_empty_summarize, false)
+#define rb_summarized_gen(a_attr, a_prefix, a_rbt_type, a_type, \
+ a_field, a_cmp, a_summarize) \
+ rb_gen_impl(a_attr, a_prefix, a_rbt_type, a_type, a_field, a_cmp, \
+ a_summarize, true)
+
+#define rb_gen_impl(a_attr, a_prefix, a_rbt_type, a_type, \
+ a_field, a_cmp, a_summarize, a_is_summarized) \
+typedef struct { \
+ a_type *node; \
+ int cmp; \
+} a_prefix##path_entry_t; \
+static inline void \
+a_prefix##summarize_range(a_prefix##path_entry_t *rfirst, \
+ a_prefix##path_entry_t *rlast) { \
+ while ((uintptr_t)rlast >= (uintptr_t)rfirst) { \
+ a_type *node = rlast->node; \
+ /* Avoid a warning when a_summarize is rb_empty_summarize. */ \
+ (void)node; \
+ bool changed = a_summarize(node, rbtn_left_get(a_type, a_field, \
+ node), rbtn_right_get(a_type, a_field, node)); \
+ if (!changed) { \
+ break; \
+ } \
+ rlast--; \
+ } \
+} \
+/* On the remove pathways, we sometimes swap the node being removed */\
+/* and its first successor; in such cases we need to do two range */\
+/* updates; one from the node to its (former) swapped successor, the */\
+/* next from that successor to the root (with either allowed to */\
+/* bail out early if appropriate. */\
+static inline void \
+a_prefix##summarize_swapped_range(a_prefix##path_entry_t *rfirst, \
+ a_prefix##path_entry_t *rlast, a_prefix##path_entry_t *swap_loc) { \
+ if (swap_loc == NULL || rlast <= swap_loc) { \
+ a_prefix##summarize_range(rfirst, rlast); \
+ } else { \
+ a_prefix##summarize_range(swap_loc + 1, rlast); \
+ (void)a_summarize(swap_loc->node, \
+ rbtn_left_get(a_type, a_field, swap_loc->node), \
+ rbtn_right_get(a_type, a_field, swap_loc->node)); \
+ a_prefix##summarize_range(rfirst, swap_loc - 1); \
+ } \
+} \
a_attr void \
a_prefix##new(a_rbt_type *rbtree) { \
rb_new(a_type, a_field, rbtree); \
@@ -465,10 +732,8 @@ a_prefix##psearch(a_rbt_type *rbtree, const a_type *key) { \
} \
a_attr void \
a_prefix##insert(a_rbt_type *rbtree, a_type *node) { \
- struct { \
- a_type *node; \
- int cmp; \
- } path[sizeof(void *) << 4], *pathp; \
+ a_prefix##path_entry_t path[RB_MAX_DEPTH]; \
+ a_prefix##path_entry_t *pathp; \
rbt_node_new(a_type, a_field, rbtree, node); \
/* Wind. */ \
path->node = rbtree->rbt_root; \
@@ -484,6 +749,13 @@ a_prefix##insert(a_rbt_type *rbtree, a_type *node) { \
} \
} \
pathp->node = node; \
+ /* A loop invariant we maintain is that all nodes with */\
+ /* out-of-date summaries live in path[0], path[1], ..., *pathp. */\
+ /* To maintain this, we have to summarize node, since we */\
+ /* decrement pathp before the first iteration. */\
+ assert(rbtn_left_get(a_type, a_field, node) == NULL); \
+ assert(rbtn_right_get(a_type, a_field, node) == NULL); \
+ (void)a_summarize(node, NULL, NULL); \
/* Unwind. */ \
for (pathp--; (uintptr_t)pathp >= (uintptr_t)path; pathp--) { \
a_type *cnode = pathp->node; \
@@ -498,9 +770,13 @@ a_prefix##insert(a_rbt_type *rbtree, a_type *node) { \
a_type *tnode; \
rbtn_black_set(a_type, a_field, leftleft); \
rbtn_rotate_right(a_type, a_field, cnode, tnode); \
+ (void)a_summarize(cnode, \
+ rbtn_left_get(a_type, a_field, cnode), \
+ rbtn_right_get(a_type, a_field, cnode)); \
cnode = tnode; \
} \
} else { \
+ a_prefix##summarize_range(path, pathp); \
return; \
} \
} else { \
@@ -521,13 +797,20 @@ a_prefix##insert(a_rbt_type *rbtree, a_type *node) { \
rbtn_rotate_left(a_type, a_field, cnode, tnode); \
rbtn_color_set(a_type, a_field, tnode, tred); \
rbtn_red_set(a_type, a_field, cnode); \
+ (void)a_summarize(cnode, \
+ rbtn_left_get(a_type, a_field, cnode), \
+ rbtn_right_get(a_type, a_field, cnode)); \
cnode = tnode; \
} \
} else { \
+ a_prefix##summarize_range(path, pathp); \
return; \
} \
} \
pathp->node = cnode; \
+ (void)a_summarize(cnode, \
+ rbtn_left_get(a_type, a_field, cnode), \
+ rbtn_right_get(a_type, a_field, cnode)); \
} \
/* Set root, and make it black. */ \
rbtree->rbt_root = path->node; \
@@ -535,12 +818,18 @@ a_prefix##insert(a_rbt_type *rbtree, a_type *node) { \
} \
a_attr void \
a_prefix##remove(a_rbt_type *rbtree, a_type *node) { \
- struct { \
- a_type *node; \
- int cmp; \
- } *pathp, *nodep, path[sizeof(void *) << 4]; \
+ a_prefix##path_entry_t path[RB_MAX_DEPTH]; \
+ a_prefix##path_entry_t *pathp; \
+ a_prefix##path_entry_t *nodep; \
+ a_prefix##path_entry_t *swap_loc; \
+ /* This is a "real" sentinel -- NULL means we didn't swap the */\
+ /* node to be pruned with one of its successors, and so */\
+ /* summarization can terminate early whenever some summary */\
+ /* doesn't change. */\
+ swap_loc = NULL; \
+ /* This is just to silence a compiler warning. */ \
+ nodep = NULL; \
/* Wind. */ \
- nodep = NULL; /* Silence compiler warning. */ \
path->node = rbtree->rbt_root; \
for (pathp = path; pathp->node != NULL; pathp++) { \
int cmp = pathp->cmp = a_cmp(node, pathp->node); \
@@ -567,6 +856,7 @@ a_prefix##remove(a_rbt_type *rbtree, a_type *node) { \
pathp--; \
if (pathp->node != node) { \
/* Swap node with its successor. */ \
+ swap_loc = nodep; \
bool tred = rbtn_red_get(a_type, a_field, pathp->node); \
rbtn_color_set(a_type, a_field, pathp->node, \
rbtn_red_get(a_type, a_field, node)); \
@@ -604,6 +894,9 @@ a_prefix##remove(a_rbt_type *rbtree, a_type *node) { \
rbtn_black_set(a_type, a_field, left); \
if (pathp == path) { \
rbtree->rbt_root = left; \
+ /* Nothing to summarize -- the subtree rooted at the */\
+ /* node's left child hasn't changed, and it's now the */\
+ /* root. */\
} else { \
if (pathp[-1].cmp < 0) { \
rbtn_left_set(a_type, a_field, pathp[-1].node, \
@@ -612,6 +905,8 @@ a_prefix##remove(a_rbt_type *rbtree, a_type *node) { \
rbtn_right_set(a_type, a_field, pathp[-1].node, \
left); \
} \
+ a_prefix##summarize_swapped_range(path, &pathp[-1], \
+ swap_loc); \
} \
return; \
} else if (pathp == path) { \
@@ -620,10 +915,15 @@ a_prefix##remove(a_rbt_type *rbtree, a_type *node) { \
return; \
} \
} \
+ /* We've now established the invariant that the node has no right */\
+ /* child (well, morally; we didn't bother nulling it out if we */\
+ /* swapped it with its successor), and that the only nodes with */\
+ /* out-of-date summaries live in path[0], path[1], ..., pathp[-1].*/\
if (rbtn_red_get(a_type, a_field, pathp->node)) { \
/* Prune red node, which requires no fixup. */ \
assert(pathp[-1].cmp < 0); \
rbtn_left_set(a_type, a_field, pathp[-1].node, NULL); \
+ a_prefix##summarize_swapped_range(path, &pathp[-1], swap_loc); \
return; \
} \
/* The node to be pruned is black, so unwind until balance is */\
@@ -657,6 +957,12 @@ a_prefix##remove(a_rbt_type *rbtree, a_type *node) { \
rbtn_right_set(a_type, a_field, pathp->node, tnode);\
rbtn_rotate_left(a_type, a_field, pathp->node, \
tnode); \
+ (void)a_summarize(pathp->node, \
+ rbtn_left_get(a_type, a_field, pathp->node), \
+ rbtn_right_get(a_type, a_field, pathp->node)); \
+ (void)a_summarize(right, \
+ rbtn_left_get(a_type, a_field, right), \
+ rbtn_right_get(a_type, a_field, right)); \
} else { \
/* || */\
/* pathp(r) */\
@@ -667,7 +973,12 @@ a_prefix##remove(a_rbt_type *rbtree, a_type *node) { \
/* */\
rbtn_rotate_left(a_type, a_field, pathp->node, \
tnode); \
+ (void)a_summarize(pathp->node, \
+ rbtn_left_get(a_type, a_field, pathp->node), \
+ rbtn_right_get(a_type, a_field, pathp->node)); \
} \
+ (void)a_summarize(tnode, rbtn_left_get(a_type, a_field, \
+ tnode), rbtn_right_get(a_type, a_field, tnode)); \
/* Balance restored, but rotation modified subtree */\
/* root. */\
assert((uintptr_t)pathp > (uintptr_t)path); \
@@ -678,6 +989,8 @@ a_prefix##remove(a_rbt_type *rbtree, a_type *node) { \
rbtn_right_set(a_type, a_field, pathp[-1].node, \
tnode); \
} \
+ a_prefix##summarize_swapped_range(path, &pathp[-1], \
+ swap_loc); \
return; \
} else { \
a_type *right = rbtn_right_get(a_type, a_field, \
@@ -698,6 +1011,15 @@ a_prefix##remove(a_rbt_type *rbtree, a_type *node) { \
rbtn_right_set(a_type, a_field, pathp->node, tnode);\
rbtn_rotate_left(a_type, a_field, pathp->node, \
tnode); \
+ (void)a_summarize(pathp->node, \
+ rbtn_left_get(a_type, a_field, pathp->node), \
+ rbtn_right_get(a_type, a_field, pathp->node)); \
+ (void)a_summarize(right, \
+ rbtn_left_get(a_type, a_field, right), \
+ rbtn_right_get(a_type, a_field, right)); \
+ (void)a_summarize(tnode, \
+ rbtn_left_get(a_type, a_field, tnode), \
+ rbtn_right_get(a_type, a_field, tnode)); \
/* Balance restored, but rotation modified */\
/* subtree root, which may actually be the tree */\
/* root. */\
@@ -712,6 +1034,8 @@ a_prefix##remove(a_rbt_type *rbtree, a_type *node) { \
rbtn_right_set(a_type, a_field, \
pathp[-1].node, tnode); \
} \
+ a_prefix##summarize_swapped_range(path, \
+ &pathp[-1], swap_loc); \
} \
return; \
} else { \
@@ -725,6 +1049,12 @@ a_prefix##remove(a_rbt_type *rbtree, a_type *node) { \
rbtn_red_set(a_type, a_field, pathp->node); \
rbtn_rotate_left(a_type, a_field, pathp->node, \
tnode); \
+ (void)a_summarize(pathp->node, \
+ rbtn_left_get(a_type, a_field, pathp->node), \
+ rbtn_right_get(a_type, a_field, pathp->node)); \
+ (void)a_summarize(tnode, \
+ rbtn_left_get(a_type, a_field, tnode), \
+ rbtn_right_get(a_type, a_field, tnode)); \
pathp->node = tnode; \
} \
} \
@@ -757,6 +1087,12 @@ a_prefix##remove(a_rbt_type *rbtree, a_type *node) { \
tnode); \
rbtn_right_set(a_type, a_field, unode, tnode); \
rbtn_rotate_left(a_type, a_field, unode, tnode); \
+ (void)a_summarize(pathp->node, \
+ rbtn_left_get(a_type, a_field, pathp->node), \
+ rbtn_right_get(a_type, a_field, pathp->node)); \
+ (void)a_summarize(unode, \
+ rbtn_left_get(a_type, a_field, unode), \
+ rbtn_right_get(a_type, a_field, unode)); \
} else { \
/* || */\
/* pathp(b) */\
@@ -771,7 +1107,13 @@ a_prefix##remove(a_rbt_type *rbtree, a_type *node) { \
rbtn_rotate_right(a_type, a_field, pathp->node, \
tnode); \
rbtn_black_set(a_type, a_field, tnode); \
+ (void)a_summarize(pathp->node, \
+ rbtn_left_get(a_type, a_field, pathp->node), \
+ rbtn_right_get(a_type, a_field, pathp->node)); \
} \
+ (void)a_summarize(tnode, \
+ rbtn_left_get(a_type, a_field, tnode), \
+ rbtn_right_get(a_type, a_field, tnode)); \
/* Balance restored, but rotation modified subtree */\
/* root, which may actually be the tree root. */\
if (pathp == path) { \
@@ -785,6 +1127,8 @@ a_prefix##remove(a_rbt_type *rbtree, a_type *node) { \
rbtn_right_set(a_type, a_field, pathp[-1].node, \
tnode); \
} \
+ a_prefix##summarize_swapped_range(path, &pathp[-1], \
+ swap_loc); \
} \
return; \
} else if (rbtn_red_get(a_type, a_field, pathp->node)) { \
@@ -803,6 +1147,12 @@ a_prefix##remove(a_rbt_type *rbtree, a_type *node) { \
rbtn_black_set(a_type, a_field, leftleft); \
rbtn_rotate_right(a_type, a_field, pathp->node, \
tnode); \
+ (void)a_summarize(pathp->node, \
+ rbtn_left_get(a_type, a_field, pathp->node), \
+ rbtn_right_get(a_type, a_field, pathp->node)); \
+ (void)a_summarize(tnode, \
+ rbtn_left_get(a_type, a_field, tnode), \
+ rbtn_right_get(a_type, a_field, tnode)); \
/* Balance restored, but rotation modified */\
/* subtree root. */\
assert((uintptr_t)pathp > (uintptr_t)path); \
@@ -813,6 +1163,8 @@ a_prefix##remove(a_rbt_type *rbtree, a_type *node) { \
rbtn_right_set(a_type, a_field, pathp[-1].node, \
tnode); \
} \
+ a_prefix##summarize_swapped_range(path, &pathp[-1], \
+ swap_loc); \
return; \
} else { \
/* || */\
@@ -824,6 +1176,8 @@ a_prefix##remove(a_rbt_type *rbtree, a_type *node) { \
rbtn_red_set(a_type, a_field, left); \
rbtn_black_set(a_type, a_field, pathp->node); \
/* Balance restored. */ \
+ a_prefix##summarize_swapped_range(path, pathp, \
+ swap_loc); \
return; \
} \
} else { \
@@ -840,6 +1194,12 @@ a_prefix##remove(a_rbt_type *rbtree, a_type *node) { \
rbtn_black_set(a_type, a_field, leftleft); \
rbtn_rotate_right(a_type, a_field, pathp->node, \
tnode); \
+ (void)a_summarize(pathp->node, \
+ rbtn_left_get(a_type, a_field, pathp->node), \
+ rbtn_right_get(a_type, a_field, pathp->node)); \
+ (void)a_summarize(tnode, \
+ rbtn_left_get(a_type, a_field, tnode), \
+ rbtn_right_get(a_type, a_field, tnode)); \
/* Balance restored, but rotation modified */\
/* subtree root, which may actually be the tree */\
/* root. */\
@@ -854,6 +1214,8 @@ a_prefix##remove(a_rbt_type *rbtree, a_type *node) { \
rbtn_right_set(a_type, a_field, \
pathp[-1].node, tnode); \
} \
+ a_prefix##summarize_swapped_range(path, \
+ &pathp[-1], swap_loc); \
} \
return; \
} else { \
@@ -864,6 +1226,9 @@ a_prefix##remove(a_rbt_type *rbtree, a_type *node) { \
/* / */\
/* (b) */\
rbtn_red_set(a_type, a_field, left); \
+ (void)a_summarize(pathp->node, \
+ rbtn_left_get(a_type, a_field, pathp->node), \
+ rbtn_right_get(a_type, a_field, pathp->node)); \
} \
} \
} \
@@ -1001,6 +1366,491 @@ a_prefix##destroy(a_rbt_type *rbtree, void (*cb)(a_type *, void *), \
void *arg) { \
a_prefix##destroy_recurse(rbtree, rbtree->rbt_root, cb, arg); \
rbtree->rbt_root = NULL; \
-}
+} \
+/* BEGIN SUMMARIZED-ONLY IMPLEMENTATION */ \
+rb_summarized_only_##a_is_summarized( \
+static inline a_prefix##path_entry_t * \
+a_prefix##wind(a_rbt_type *rbtree, \
+ a_prefix##path_entry_t path[RB_MAX_DEPTH], a_type *node) { \
+ a_prefix##path_entry_t *pathp; \
+ path->node = rbtree->rbt_root; \
+ for (pathp = path; ; pathp++) { \
+ assert((size_t)(pathp - path) < RB_MAX_DEPTH); \
+ pathp->cmp = a_cmp(node, pathp->node); \
+ if (pathp->cmp < 0) { \
+ pathp[1].node = rbtn_left_get(a_type, a_field, \
+ pathp->node); \
+ } else if (pathp->cmp == 0) { \
+ return pathp; \
+ } else { \
+ pathp[1].node = rbtn_right_get(a_type, a_field, \
+ pathp->node); \
+ } \
+ } \
+ unreachable(); \
+} \
+a_attr void \
+a_prefix##update_summaries(a_rbt_type *rbtree, a_type *node) { \
+ a_prefix##path_entry_t path[RB_MAX_DEPTH]; \
+ a_prefix##path_entry_t *pathp = a_prefix##wind(rbtree, path, node); \
+ a_prefix##summarize_range(path, pathp); \
+} \
+a_attr bool \
+a_prefix##empty_filtered(a_rbt_type *rbtree, \
+ bool (*filter_node)(void *, a_type *), \
+ bool (*filter_subtree)(void *, a_type *), \
+ void *filter_ctx) { \
+ a_type *node = rbtree->rbt_root; \
+ return node == NULL || !filter_subtree(filter_ctx, node); \
+} \
+static inline a_type * \
+a_prefix##first_filtered_from_node(a_type *node, \
+ bool (*filter_node)(void *, a_type *), \
+ bool (*filter_subtree)(void *, a_type *), \
+ void *filter_ctx) { \
+ assert(node != NULL && filter_subtree(filter_ctx, node)); \
+ while (true) { \
+ a_type *left = rbtn_left_get(a_type, a_field, node); \
+ a_type *right = rbtn_right_get(a_type, a_field, node); \
+ if (left != NULL && filter_subtree(filter_ctx, left)) { \
+ node = left; \
+ } else if (filter_node(filter_ctx, node)) { \
+ return node; \
+ } else { \
+ assert(right != NULL \
+ && filter_subtree(filter_ctx, right)); \
+ node = right; \
+ } \
+ } \
+ unreachable(); \
+} \
+a_attr a_type * \
+a_prefix##first_filtered(a_rbt_type *rbtree, \
+ bool (*filter_node)(void *, a_type *), \
+ bool (*filter_subtree)(void *, a_type *), \
+ void *filter_ctx) { \
+ a_type *node = rbtree->rbt_root; \
+ if (node == NULL || !filter_subtree(filter_ctx, node)) { \
+ return NULL; \
+ } \
+ return a_prefix##first_filtered_from_node(node, filter_node, \
+ filter_subtree, filter_ctx); \
+} \
+static inline a_type * \
+a_prefix##last_filtered_from_node(a_type *node, \
+ bool (*filter_node)(void *, a_type *), \
+ bool (*filter_subtree)(void *, a_type *), \
+ void *filter_ctx) { \
+ assert(node != NULL && filter_subtree(filter_ctx, node)); \
+ while (true) { \
+ a_type *left = rbtn_left_get(a_type, a_field, node); \
+ a_type *right = rbtn_right_get(a_type, a_field, node); \
+ if (right != NULL && filter_subtree(filter_ctx, right)) { \
+ node = right; \
+ } else if (filter_node(filter_ctx, node)) { \
+ return node; \
+ } else { \
+ assert(left != NULL \
+ && filter_subtree(filter_ctx, left)); \
+ node = left; \
+ } \
+ } \
+ unreachable(); \
+} \
+a_attr a_type * \
+a_prefix##last_filtered(a_rbt_type *rbtree, \
+ bool (*filter_node)(void *, a_type *), \
+ bool (*filter_subtree)(void *, a_type *), \
+ void *filter_ctx) { \
+ a_type *node = rbtree->rbt_root; \
+ if (node == NULL || !filter_subtree(filter_ctx, node)) { \
+ return NULL; \
+ } \
+ return a_prefix##last_filtered_from_node(node, filter_node, \
+ filter_subtree, filter_ctx); \
+} \
+/* Internal implementation function. Search for a node comparing */\
+/* equal to key matching the filter. If such a node is in the tree, */\
+/* return it. Additionally, the caller has the option to ask for */\
+/* bounds on the next / prev node in the tree passing the filter. */\
+/* If nextbound is true, then this function will do one of the */\
+/* following: */\
+/* - Fill in *nextbound_node with the smallest node in the tree */\
+/* greater than key passing the filter, and NULL-out */\
+/* *nextbound_subtree. */\
+/* - Fill in *nextbound_subtree with a parent of that node which is */\
+/* not a parent of the searched-for node, and NULL-out */\
+/* *nextbound_node. */\
+/* - NULL-out both *nextbound_node and *nextbound_subtree, in which */\
+/* case no node greater than key but passing the filter is in the */\
+/* tree. */\
+/* The prevbound case is similar. If the caller knows that key is in */\
+/* the tree and that the subtree rooted at key does not contain a */\
+/* node satisfying the bound being searched for, then they can pass */\
+/* false for include_subtree, in which case we won't bother searching */\
+/* there (risking a cache miss). */\
+/* */\
+/* This API is unfortunately complex; but the logic for filtered */\
+/* searches is very subtle, and otherwise we would have to repeat it */\
+/* multiple times for filtered search, nsearch, psearch, next, and */\
+/* prev. */\
+static inline a_type * \
+a_prefix##search_with_filter_bounds(a_rbt_type *rbtree, \
+ const a_type *key, \
+ bool (*filter_node)(void *, a_type *), \
+ bool (*filter_subtree)(void *, a_type *), \
+ void *filter_ctx, \
+ bool include_subtree, \
+ bool nextbound, a_type **nextbound_node, a_type **nextbound_subtree, \
+ bool prevbound, a_type **prevbound_node, a_type **prevbound_subtree) {\
+ if (nextbound) { \
+ *nextbound_node = NULL; \
+ *nextbound_subtree = NULL; \
+ } \
+ if (prevbound) { \
+ *prevbound_node = NULL; \
+ *prevbound_subtree = NULL; \
+ } \
+ a_type *tnode = rbtree->rbt_root; \
+ while (tnode != NULL && filter_subtree(filter_ctx, tnode)) { \
+ int cmp = a_cmp(key, tnode); \
+ a_type *tleft = rbtn_left_get(a_type, a_field, tnode); \
+ a_type *tright = rbtn_right_get(a_type, a_field, tnode); \
+ if (cmp < 0) { \
+ if (nextbound) { \
+ if (filter_node(filter_ctx, tnode)) { \
+ *nextbound_node = tnode; \
+ *nextbound_subtree = NULL; \
+ } else if (tright != NULL && filter_subtree( \
+ filter_ctx, tright)) { \
+ *nextbound_node = NULL; \
+ *nextbound_subtree = tright; \
+ } \
+ } \
+ tnode = tleft; \
+ } else if (cmp > 0) { \
+ if (prevbound) { \
+ if (filter_node(filter_ctx, tnode)) { \
+ *prevbound_node = tnode; \
+ *prevbound_subtree = NULL; \
+ } else if (tleft != NULL && filter_subtree( \
+ filter_ctx, tleft)) { \
+ *prevbound_node = NULL; \
+ *prevbound_subtree = tleft; \
+ } \
+ } \
+ tnode = tright; \
+ } else { \
+ if (filter_node(filter_ctx, tnode)) { \
+ return tnode; \
+ } \
+ if (include_subtree) { \
+ if (prevbound && tleft != NULL && filter_subtree( \
+ filter_ctx, tleft)) { \
+ *prevbound_node = NULL; \
+ *prevbound_subtree = tleft; \
+ } \
+ if (nextbound && tright != NULL && filter_subtree( \
+ filter_ctx, tright)) { \
+ *nextbound_node = NULL; \
+ *nextbound_subtree = tright; \
+ } \
+ } \
+ return NULL; \
+ } \
+ } \
+ return NULL; \
+} \
+a_attr a_type * \
+a_prefix##next_filtered(a_rbt_type *rbtree, a_type *node, \
+ bool (*filter_node)(void *, a_type *), \
+ bool (*filter_subtree)(void *, a_type *), \
+ void *filter_ctx) { \
+ a_type *nright = rbtn_right_get(a_type, a_field, node); \
+ if (nright != NULL && filter_subtree(filter_ctx, nright)) { \
+ return a_prefix##first_filtered_from_node(nright, filter_node, \
+ filter_subtree, filter_ctx); \
+ } \
+ a_type *node_candidate; \
+ a_type *subtree_candidate; \
+ a_type *search_result = a_prefix##search_with_filter_bounds( \
+ rbtree, node, filter_node, filter_subtree, filter_ctx, \
+ /* include_subtree */ false, \
+ /* nextbound */ true, &node_candidate, &subtree_candidate, \
+ /* prevbound */ false, NULL, NULL); \
+ assert(node == search_result \
+ || !filter_node(filter_ctx, node)); \
+ if (node_candidate != NULL) { \
+ return node_candidate; \
+ } \
+ if (subtree_candidate != NULL) { \
+ return a_prefix##first_filtered_from_node( \
+ subtree_candidate, filter_node, filter_subtree, \
+ filter_ctx); \
+ } \
+ return NULL; \
+} \
+a_attr a_type * \
+a_prefix##prev_filtered(a_rbt_type *rbtree, a_type *node, \
+ bool (*filter_node)(void *, a_type *), \
+ bool (*filter_subtree)(void *, a_type *), \
+ void *filter_ctx) { \
+ a_type *nleft = rbtn_left_get(a_type, a_field, node); \
+ if (nleft != NULL && filter_subtree(filter_ctx, nleft)) { \
+ return a_prefix##last_filtered_from_node(nleft, filter_node, \
+ filter_subtree, filter_ctx); \
+ } \
+ a_type *node_candidate; \
+ a_type *subtree_candidate; \
+ a_type *search_result = a_prefix##search_with_filter_bounds( \
+ rbtree, node, filter_node, filter_subtree, filter_ctx, \
+ /* include_subtree */ false, \
+ /* nextbound */ false, NULL, NULL, \
+ /* prevbound */ true, &node_candidate, &subtree_candidate); \
+ assert(node == search_result \
+ || !filter_node(filter_ctx, node)); \
+ if (node_candidate != NULL) { \
+ return node_candidate; \
+ } \
+ if (subtree_candidate != NULL) { \
+ return a_prefix##last_filtered_from_node( \
+ subtree_candidate, filter_node, filter_subtree, \
+ filter_ctx); \
+ } \
+ return NULL; \
+} \
+a_attr a_type * \
+a_prefix##search_filtered(a_rbt_type *rbtree, const a_type *key, \
+ bool (*filter_node)(void *, a_type *), \
+ bool (*filter_subtree)(void *, a_type *), \
+ void *filter_ctx) { \
+ a_type *result = a_prefix##search_with_filter_bounds(rbtree, key, \
+ filter_node, filter_subtree, filter_ctx, \
+ /* include_subtree */ false, \
+ /* nextbound */ false, NULL, NULL, \
+ /* prevbound */ false, NULL, NULL); \
+ return result; \
+} \
+a_attr a_type * \
+a_prefix##nsearch_filtered(a_rbt_type *rbtree, const a_type *key, \
+ bool (*filter_node)(void *, a_type *), \
+ bool (*filter_subtree)(void *, a_type *), \
+ void *filter_ctx) { \
+ a_type *node_candidate; \
+ a_type *subtree_candidate; \
+ a_type *result = a_prefix##search_with_filter_bounds(rbtree, key, \
+ filter_node, filter_subtree, filter_ctx, \
+ /* include_subtree */ true, \
+ /* nextbound */ true, &node_candidate, &subtree_candidate, \
+ /* prevbound */ false, NULL, NULL); \
+ if (result != NULL) { \
+ return result; \
+ } \
+ if (node_candidate != NULL) { \
+ return node_candidate; \
+ } \
+ if (subtree_candidate != NULL) { \
+ return a_prefix##first_filtered_from_node( \
+ subtree_candidate, filter_node, filter_subtree, \
+ filter_ctx); \
+ } \
+ return NULL; \
+} \
+a_attr a_type * \
+a_prefix##psearch_filtered(a_rbt_type *rbtree, const a_type *key, \
+ bool (*filter_node)(void *, a_type *), \
+ bool (*filter_subtree)(void *, a_type *), \
+ void *filter_ctx) { \
+ a_type *node_candidate; \
+ a_type *subtree_candidate; \
+ a_type *result = a_prefix##search_with_filter_bounds(rbtree, key, \
+ filter_node, filter_subtree, filter_ctx, \
+ /* include_subtree */ true, \
+ /* nextbound */ false, NULL, NULL, \
+ /* prevbound */ true, &node_candidate, &subtree_candidate); \
+ if (result != NULL) { \
+ return result; \
+ } \
+ if (node_candidate != NULL) { \
+ return node_candidate; \
+ } \
+ if (subtree_candidate != NULL) { \
+ return a_prefix##last_filtered_from_node( \
+ subtree_candidate, filter_node, filter_subtree, \
+ filter_ctx); \
+ } \
+ return NULL; \
+} \
+a_attr a_type * \
+a_prefix##iter_recurse_filtered(a_rbt_type *rbtree, a_type *node, \
+ a_type *(*cb)(a_rbt_type *, a_type *, void *), void *arg, \
+ bool (*filter_node)(void *, a_type *), \
+ bool (*filter_subtree)(void *, a_type *), \
+ void *filter_ctx) { \
+ if (node == NULL || !filter_subtree(filter_ctx, node)) { \
+ return NULL; \
+ } \
+ a_type *ret; \
+ a_type *left = rbtn_left_get(a_type, a_field, node); \
+ a_type *right = rbtn_right_get(a_type, a_field, node); \
+ ret = a_prefix##iter_recurse_filtered(rbtree, left, cb, arg, \
+ filter_node, filter_subtree, filter_ctx); \
+ if (ret != NULL) { \
+ return ret; \
+ } \
+ if (filter_node(filter_ctx, node)) { \
+ ret = cb(rbtree, node, arg); \
+ } \
+ if (ret != NULL) { \
+ return ret; \
+ } \
+ return a_prefix##iter_recurse_filtered(rbtree, right, cb, arg, \
+ filter_node, filter_subtree, filter_ctx); \
+} \
+a_attr a_type * \
+a_prefix##iter_start_filtered(a_rbt_type *rbtree, a_type *start, \
+ a_type *node, a_type *(*cb)(a_rbt_type *, a_type *, void *), \
+ void *arg, bool (*filter_node)(void *, a_type *), \
+ bool (*filter_subtree)(void *, a_type *), \
+ void *filter_ctx) { \
+ if (!filter_subtree(filter_ctx, node)) { \
+ return NULL; \
+ } \
+ int cmp = a_cmp(start, node); \
+ a_type *ret; \
+ a_type *left = rbtn_left_get(a_type, a_field, node); \
+ a_type *right = rbtn_right_get(a_type, a_field, node); \
+ if (cmp < 0) { \
+ ret = a_prefix##iter_start_filtered(rbtree, start, left, cb, \
+ arg, filter_node, filter_subtree, filter_ctx); \
+ if (ret != NULL) { \
+ return ret; \
+ } \
+ if (filter_node(filter_ctx, node)) { \
+ ret = cb(rbtree, node, arg); \
+ if (ret != NULL) { \
+ return ret; \
+ } \
+ } \
+ return a_prefix##iter_recurse_filtered(rbtree, right, cb, arg, \
+ filter_node, filter_subtree, filter_ctx); \
+ } else if (cmp > 0) { \
+ return a_prefix##iter_start_filtered(rbtree, start, right, \
+ cb, arg, filter_node, filter_subtree, filter_ctx); \
+ } else { \
+ if (filter_node(filter_ctx, node)) { \
+ ret = cb(rbtree, node, arg); \
+ if (ret != NULL) { \
+ return ret; \
+ } \
+ } \
+ return a_prefix##iter_recurse_filtered(rbtree, right, cb, arg, \
+ filter_node, filter_subtree, filter_ctx); \
+ } \
+} \
+a_attr a_type * \
+a_prefix##iter_filtered(a_rbt_type *rbtree, a_type *start, \
+ a_type *(*cb)(a_rbt_type *, a_type *, void *), void *arg, \
+ bool (*filter_node)(void *, a_type *), \
+ bool (*filter_subtree)(void *, a_type *), \
+ void *filter_ctx) { \
+ a_type *ret; \
+ if (start != NULL) { \
+ ret = a_prefix##iter_start_filtered(rbtree, start, \
+ rbtree->rbt_root, cb, arg, filter_node, filter_subtree, \
+ filter_ctx); \
+ } else { \
+ ret = a_prefix##iter_recurse_filtered(rbtree, rbtree->rbt_root, \
+ cb, arg, filter_node, filter_subtree, filter_ctx); \
+ } \
+ return ret; \
+} \
+a_attr a_type * \
+a_prefix##reverse_iter_recurse_filtered(a_rbt_type *rbtree, \
+ a_type *node, a_type *(*cb)(a_rbt_type *, a_type *, void *), \
+ void *arg, \
+ bool (*filter_node)(void *, a_type *), \
+ bool (*filter_subtree)(void *, a_type *), \
+ void *filter_ctx) { \
+ if (node == NULL || !filter_subtree(filter_ctx, node)) { \
+ return NULL; \
+ } \
+ a_type *ret; \
+ a_type *left = rbtn_left_get(a_type, a_field, node); \
+ a_type *right = rbtn_right_get(a_type, a_field, node); \
+ ret = a_prefix##reverse_iter_recurse_filtered(rbtree, right, cb, \
+ arg, filter_node, filter_subtree, filter_ctx); \
+ if (ret != NULL) { \
+ return ret; \
+ } \
+ if (filter_node(filter_ctx, node)) { \
+ ret = cb(rbtree, node, arg); \
+ } \
+ if (ret != NULL) { \
+ return ret; \
+ } \
+ return a_prefix##reverse_iter_recurse_filtered(rbtree, left, cb, \
+ arg, filter_node, filter_subtree, filter_ctx); \
+} \
+a_attr a_type * \
+a_prefix##reverse_iter_start_filtered(a_rbt_type *rbtree, a_type *start,\
+ a_type *node, a_type *(*cb)(a_rbt_type *, a_type *, void *), \
+ void *arg, bool (*filter_node)(void *, a_type *), \
+ bool (*filter_subtree)(void *, a_type *), \
+ void *filter_ctx) { \
+ if (!filter_subtree(filter_ctx, node)) { \
+ return NULL; \
+ } \
+ int cmp = a_cmp(start, node); \
+ a_type *ret; \
+ a_type *left = rbtn_left_get(a_type, a_field, node); \
+ a_type *right = rbtn_right_get(a_type, a_field, node); \
+ if (cmp > 0) { \
+ ret = a_prefix##reverse_iter_start_filtered(rbtree, start, \
+ right, cb, arg, filter_node, filter_subtree, filter_ctx); \
+ if (ret != NULL) { \
+ return ret; \
+ } \
+ if (filter_node(filter_ctx, node)) { \
+ ret = cb(rbtree, node, arg); \
+ if (ret != NULL) { \
+ return ret; \
+ } \
+ } \
+ return a_prefix##reverse_iter_recurse_filtered(rbtree, left, cb,\
+ arg, filter_node, filter_subtree, filter_ctx); \
+ } else if (cmp < 0) { \
+ return a_prefix##reverse_iter_start_filtered(rbtree, start, \
+ left, cb, arg, filter_node, filter_subtree, filter_ctx); \
+ } else { \
+ if (filter_node(filter_ctx, node)) { \
+ ret = cb(rbtree, node, arg); \
+ if (ret != NULL) { \
+ return ret; \
+ } \
+ } \
+ return a_prefix##reverse_iter_recurse_filtered(rbtree, left, cb,\
+ arg, filter_node, filter_subtree, filter_ctx); \
+ } \
+} \
+a_attr a_type * \
+a_prefix##reverse_iter_filtered(a_rbt_type *rbtree, a_type *start, \
+ a_type *(*cb)(a_rbt_type *, a_type *, void *), void *arg, \
+ bool (*filter_node)(void *, a_type *), \
+ bool (*filter_subtree)(void *, a_type *), \
+ void *filter_ctx) { \
+ a_type *ret; \
+ if (start != NULL) { \
+ ret = a_prefix##reverse_iter_start_filtered(rbtree, start, \
+ rbtree->rbt_root, cb, arg, filter_node, filter_subtree, \
+ filter_ctx); \
+ } else { \
+ ret = a_prefix##reverse_iter_recurse_filtered(rbtree, \
+ rbtree->rbt_root, cb, arg, filter_node, filter_subtree, \
+ filter_ctx); \
+ } \
+ return ret; \
+} \
+) /* end rb_summarized_only */
-#endif /* RB_H_ */
+#endif /* JEMALLOC_INTERNAL_RB_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/rtree.h b/contrib/jemalloc/include/jemalloc/internal/rtree.h
index 16ccbebee7f0..a00adb2982f3 100644
--- a/contrib/jemalloc/include/jemalloc/internal/rtree.h
+++ b/contrib/jemalloc/include/jemalloc/internal/rtree.h
@@ -35,33 +35,52 @@
# define RTREE_LEAF_COMPACT
#endif
-/* Needed for initialization only. */
-#define RTREE_LEAFKEY_INVALID ((uintptr_t)1)
-
typedef struct rtree_node_elm_s rtree_node_elm_t;
struct rtree_node_elm_s {
atomic_p_t child; /* (rtree_{node,leaf}_elm_t *) */
};
+typedef struct rtree_metadata_s rtree_metadata_t;
+struct rtree_metadata_s {
+ szind_t szind;
+ extent_state_t state; /* Mirrors edata->state. */
+ bool is_head; /* Mirrors edata->is_head. */
+ bool slab;
+};
+
+typedef struct rtree_contents_s rtree_contents_t;
+struct rtree_contents_s {
+ edata_t *edata;
+ rtree_metadata_t metadata;
+};
+
+#define RTREE_LEAF_STATE_WIDTH EDATA_BITS_STATE_WIDTH
+#define RTREE_LEAF_STATE_SHIFT 2
+#define RTREE_LEAF_STATE_MASK MASK(RTREE_LEAF_STATE_WIDTH, RTREE_LEAF_STATE_SHIFT)
+
struct rtree_leaf_elm_s {
#ifdef RTREE_LEAF_COMPACT
/*
* Single pointer-width field containing all three leaf element fields.
* For example, on a 64-bit x64 system with 48 significant virtual
- * memory address bits, the index, extent, and slab fields are packed as
+ * memory address bits, the index, edata, and slab fields are packed as
* such:
*
* x: index
- * e: extent
+ * e: edata
+ * s: state
+ * h: is_head
* b: slab
*
- * 00000000 xxxxxxxx eeeeeeee [...] eeeeeeee eeee000b
+ * 00000000 xxxxxxxx eeeeeeee [...] eeeeeeee e00ssshb
*/
atomic_p_t le_bits;
#else
- atomic_p_t le_extent; /* (extent_t *) */
- atomic_u_t le_szind; /* (szind_t) */
- atomic_b_t le_slab; /* (bool) */
+ atomic_p_t le_edata; /* (edata_t *) */
+ /*
+ * From high to low bits: szind (8 bits), state (4 bits), is_head, slab
+ */
+ atomic_u_t le_metadata;
#endif
};
@@ -78,6 +97,7 @@ struct rtree_level_s {
typedef struct rtree_s rtree_t;
struct rtree_s {
+ base_t *base;
malloc_mutex_t init_lock;
/* Number of elements based on rtree_levels[0].bits. */
#if RTREE_HEIGHT > 1
@@ -109,42 +129,29 @@ static const rtree_level_t rtree_levels[] = {
#endif
};
-bool rtree_new(rtree_t *rtree, bool zeroed);
-
-typedef rtree_node_elm_t *(rtree_node_alloc_t)(tsdn_t *, rtree_t *, size_t);
-extern rtree_node_alloc_t *JET_MUTABLE rtree_node_alloc;
+bool rtree_new(rtree_t *rtree, base_t *base, bool zeroed);
-typedef rtree_leaf_elm_t *(rtree_leaf_alloc_t)(tsdn_t *, rtree_t *, size_t);
-extern rtree_leaf_alloc_t *JET_MUTABLE rtree_leaf_alloc;
-
-typedef void (rtree_node_dalloc_t)(tsdn_t *, rtree_t *, rtree_node_elm_t *);
-extern rtree_node_dalloc_t *JET_MUTABLE rtree_node_dalloc;
-
-typedef void (rtree_leaf_dalloc_t)(tsdn_t *, rtree_t *, rtree_leaf_elm_t *);
-extern rtree_leaf_dalloc_t *JET_MUTABLE rtree_leaf_dalloc;
-#ifdef JEMALLOC_JET
-void rtree_delete(tsdn_t *tsdn, rtree_t *rtree);
-#endif
rtree_leaf_elm_t *rtree_leaf_elm_lookup_hard(tsdn_t *tsdn, rtree_t *rtree,
rtree_ctx_t *rtree_ctx, uintptr_t key, bool dependent, bool init_missing);
-JEMALLOC_ALWAYS_INLINE uintptr_t
-rtree_leafkey(uintptr_t key) {
+JEMALLOC_ALWAYS_INLINE unsigned
+rtree_leaf_maskbits(void) {
unsigned ptrbits = ZU(1) << (LG_SIZEOF_PTR+3);
unsigned cumbits = (rtree_levels[RTREE_HEIGHT-1].cumbits -
rtree_levels[RTREE_HEIGHT-1].bits);
- unsigned maskbits = ptrbits - cumbits;
- uintptr_t mask = ~((ZU(1) << maskbits) - 1);
+ return ptrbits - cumbits;
+}
+
+JEMALLOC_ALWAYS_INLINE uintptr_t
+rtree_leafkey(uintptr_t key) {
+ uintptr_t mask = ~((ZU(1) << rtree_leaf_maskbits()) - 1);
return (key & mask);
}
JEMALLOC_ALWAYS_INLINE size_t
rtree_cache_direct_map(uintptr_t key) {
- unsigned ptrbits = ZU(1) << (LG_SIZEOF_PTR+3);
- unsigned cumbits = (rtree_levels[RTREE_HEIGHT-1].cumbits -
- rtree_levels[RTREE_HEIGHT-1].bits);
- unsigned maskbits = ptrbits - cumbits;
- return (size_t)((key >> maskbits) & (RTREE_CTX_NCACHE - 1));
+ return (size_t)((key >> rtree_leaf_maskbits()) &
+ (RTREE_CTX_NCACHE - 1));
}
JEMALLOC_ALWAYS_INLINE uintptr_t
@@ -176,151 +183,174 @@ rtree_leaf_elm_bits_read(tsdn_t *tsdn, rtree_t *rtree,
? ATOMIC_RELAXED : ATOMIC_ACQUIRE);
}
-JEMALLOC_ALWAYS_INLINE extent_t *
-rtree_leaf_elm_bits_extent_get(uintptr_t bits) {
+JEMALLOC_ALWAYS_INLINE uintptr_t
+rtree_leaf_elm_bits_encode(rtree_contents_t contents) {
+ assert((uintptr_t)contents.edata % (uintptr_t)EDATA_ALIGNMENT == 0);
+ uintptr_t edata_bits = (uintptr_t)contents.edata
+ & (((uintptr_t)1 << LG_VADDR) - 1);
+
+ uintptr_t szind_bits = (uintptr_t)contents.metadata.szind << LG_VADDR;
+ uintptr_t slab_bits = (uintptr_t)contents.metadata.slab;
+ uintptr_t is_head_bits = (uintptr_t)contents.metadata.is_head << 1;
+ uintptr_t state_bits = (uintptr_t)contents.metadata.state <<
+ RTREE_LEAF_STATE_SHIFT;
+ uintptr_t metadata_bits = szind_bits | state_bits | is_head_bits |
+ slab_bits;
+ assert((edata_bits & metadata_bits) == 0);
+
+ return edata_bits | metadata_bits;
+}
+
+JEMALLOC_ALWAYS_INLINE rtree_contents_t
+rtree_leaf_elm_bits_decode(uintptr_t bits) {
+ rtree_contents_t contents;
+ /* Do the easy things first. */
+ contents.metadata.szind = bits >> LG_VADDR;
+ contents.metadata.slab = (bool)(bits & 1);
+ contents.metadata.is_head = (bool)(bits & (1 << 1));
+
+ uintptr_t state_bits = (bits & RTREE_LEAF_STATE_MASK) >>
+ RTREE_LEAF_STATE_SHIFT;
+ assert(state_bits <= extent_state_max);
+ contents.metadata.state = (extent_state_t)state_bits;
+
+ uintptr_t low_bit_mask = ~((uintptr_t)EDATA_ALIGNMENT - 1);
# ifdef __aarch64__
/*
* aarch64 doesn't sign extend the highest virtual address bit to set
- * the higher ones. Instead, the high bits gets zeroed.
+ * the higher ones. Instead, the high bits get zeroed.
*/
uintptr_t high_bit_mask = ((uintptr_t)1 << LG_VADDR) - 1;
- /* Mask off the slab bit. */
- uintptr_t low_bit_mask = ~(uintptr_t)1;
+ /* Mask off metadata. */
uintptr_t mask = high_bit_mask & low_bit_mask;
- return (extent_t *)(bits & mask);
+ contents.edata = (edata_t *)(bits & mask);
# else
- /* Restore sign-extended high bits, mask slab bit. */
- return (extent_t *)((uintptr_t)((intptr_t)(bits << RTREE_NHIB) >>
- RTREE_NHIB) & ~((uintptr_t)0x1));
+ /* Restore sign-extended high bits, mask metadata bits. */
+ contents.edata = (edata_t *)((uintptr_t)((intptr_t)(bits << RTREE_NHIB)
+ >> RTREE_NHIB) & low_bit_mask);
# endif
+ assert((uintptr_t)contents.edata % (uintptr_t)EDATA_ALIGNMENT == 0);
+ return contents;
}
-JEMALLOC_ALWAYS_INLINE szind_t
-rtree_leaf_elm_bits_szind_get(uintptr_t bits) {
- return (szind_t)(bits >> LG_VADDR);
-}
-
-JEMALLOC_ALWAYS_INLINE bool
-rtree_leaf_elm_bits_slab_get(uintptr_t bits) {
- return (bool)(bits & (uintptr_t)0x1);
-}
+# endif /* RTREE_LEAF_COMPACT */
-# endif
-
-JEMALLOC_ALWAYS_INLINE extent_t *
-rtree_leaf_elm_extent_read(tsdn_t *tsdn, rtree_t *rtree,
- rtree_leaf_elm_t *elm, bool dependent) {
+JEMALLOC_ALWAYS_INLINE rtree_contents_t
+rtree_leaf_elm_read(tsdn_t *tsdn, rtree_t *rtree, rtree_leaf_elm_t *elm,
+ bool dependent) {
#ifdef RTREE_LEAF_COMPACT
uintptr_t bits = rtree_leaf_elm_bits_read(tsdn, rtree, elm, dependent);
- return rtree_leaf_elm_bits_extent_get(bits);
+ rtree_contents_t contents = rtree_leaf_elm_bits_decode(bits);
+ return contents;
#else
- extent_t *extent = (extent_t *)atomic_load_p(&elm->le_extent, dependent
+ rtree_contents_t contents;
+ unsigned metadata_bits = atomic_load_u(&elm->le_metadata, dependent
? ATOMIC_RELAXED : ATOMIC_ACQUIRE);
- return extent;
-#endif
-}
+ contents.metadata.slab = (bool)(metadata_bits & 1);
+ contents.metadata.is_head = (bool)(metadata_bits & (1 << 1));
-JEMALLOC_ALWAYS_INLINE szind_t
-rtree_leaf_elm_szind_read(tsdn_t *tsdn, rtree_t *rtree,
- rtree_leaf_elm_t *elm, bool dependent) {
-#ifdef RTREE_LEAF_COMPACT
- uintptr_t bits = rtree_leaf_elm_bits_read(tsdn, rtree, elm, dependent);
- return rtree_leaf_elm_bits_szind_get(bits);
-#else
- return (szind_t)atomic_load_u(&elm->le_szind, dependent ? ATOMIC_RELAXED
- : ATOMIC_ACQUIRE);
+ uintptr_t state_bits = (metadata_bits & RTREE_LEAF_STATE_MASK) >>
+ RTREE_LEAF_STATE_SHIFT;
+ assert(state_bits <= extent_state_max);
+ contents.metadata.state = (extent_state_t)state_bits;
+ contents.metadata.szind = metadata_bits >> (RTREE_LEAF_STATE_SHIFT +
+ RTREE_LEAF_STATE_WIDTH);
+
+ contents.edata = (edata_t *)atomic_load_p(&elm->le_edata, dependent
+ ? ATOMIC_RELAXED : ATOMIC_ACQUIRE);
+
+ return contents;
#endif
}
-JEMALLOC_ALWAYS_INLINE bool
-rtree_leaf_elm_slab_read(tsdn_t *tsdn, rtree_t *rtree,
- rtree_leaf_elm_t *elm, bool dependent) {
+JEMALLOC_ALWAYS_INLINE void
+rtree_contents_encode(rtree_contents_t contents, void **bits,
+ unsigned *additional) {
#ifdef RTREE_LEAF_COMPACT
- uintptr_t bits = rtree_leaf_elm_bits_read(tsdn, rtree, elm, dependent);
- return rtree_leaf_elm_bits_slab_get(bits);
+ *bits = (void *)rtree_leaf_elm_bits_encode(contents);
#else
- return atomic_load_b(&elm->le_slab, dependent ? ATOMIC_RELAXED :
- ATOMIC_ACQUIRE);
+ *additional = (unsigned)contents.metadata.slab
+ | ((unsigned)contents.metadata.is_head << 1)
+ | ((unsigned)contents.metadata.state << RTREE_LEAF_STATE_SHIFT)
+ | ((unsigned)contents.metadata.szind << (RTREE_LEAF_STATE_SHIFT +
+ RTREE_LEAF_STATE_WIDTH));
+ *bits = contents.edata;
#endif
}
-static inline void
-rtree_leaf_elm_extent_write(tsdn_t *tsdn, rtree_t *rtree,
- rtree_leaf_elm_t *elm, extent_t *extent) {
+JEMALLOC_ALWAYS_INLINE void
+rtree_leaf_elm_write_commit(tsdn_t *tsdn, rtree_t *rtree,
+ rtree_leaf_elm_t *elm, void *bits, unsigned additional) {
#ifdef RTREE_LEAF_COMPACT
- uintptr_t old_bits = rtree_leaf_elm_bits_read(tsdn, rtree, elm, true);
- uintptr_t bits = ((uintptr_t)rtree_leaf_elm_bits_szind_get(old_bits) <<
- LG_VADDR) | ((uintptr_t)extent & (((uintptr_t)0x1 << LG_VADDR) - 1))
- | ((uintptr_t)rtree_leaf_elm_bits_slab_get(old_bits));
- atomic_store_p(&elm->le_bits, (void *)bits, ATOMIC_RELEASE);
+ atomic_store_p(&elm->le_bits, bits, ATOMIC_RELEASE);
#else
- atomic_store_p(&elm->le_extent, extent, ATOMIC_RELEASE);
+ atomic_store_u(&elm->le_metadata, additional, ATOMIC_RELEASE);
+ /*
+ * Write edata last, since the element is atomically considered valid
+ * as soon as the edata field is non-NULL.
+ */
+ atomic_store_p(&elm->le_edata, bits, ATOMIC_RELEASE);
#endif
}
-static inline void
-rtree_leaf_elm_szind_write(tsdn_t *tsdn, rtree_t *rtree,
- rtree_leaf_elm_t *elm, szind_t szind) {
- assert(szind <= SC_NSIZES);
+JEMALLOC_ALWAYS_INLINE void
+rtree_leaf_elm_write(tsdn_t *tsdn, rtree_t *rtree,
+ rtree_leaf_elm_t *elm, rtree_contents_t contents) {
+ assert((uintptr_t)contents.edata % EDATA_ALIGNMENT == 0);
+ void *bits;
+ unsigned additional;
-#ifdef RTREE_LEAF_COMPACT
- uintptr_t old_bits = rtree_leaf_elm_bits_read(tsdn, rtree, elm,
- true);
- uintptr_t bits = ((uintptr_t)szind << LG_VADDR) |
- ((uintptr_t)rtree_leaf_elm_bits_extent_get(old_bits) &
- (((uintptr_t)0x1 << LG_VADDR) - 1)) |
- ((uintptr_t)rtree_leaf_elm_bits_slab_get(old_bits));
- atomic_store_p(&elm->le_bits, (void *)bits, ATOMIC_RELEASE);
-#else
- atomic_store_u(&elm->le_szind, szind, ATOMIC_RELEASE);
-#endif
+ rtree_contents_encode(contents, &bits, &additional);
+ rtree_leaf_elm_write_commit(tsdn, rtree, elm, bits, additional);
}
-static inline void
-rtree_leaf_elm_slab_write(tsdn_t *tsdn, rtree_t *rtree,
- rtree_leaf_elm_t *elm, bool slab) {
+/* The state field can be updated independently (and more frequently). */
+JEMALLOC_ALWAYS_INLINE void
+rtree_leaf_elm_state_update(tsdn_t *tsdn, rtree_t *rtree,
+ rtree_leaf_elm_t *elm1, rtree_leaf_elm_t *elm2, extent_state_t state) {
+ assert(elm1 != NULL);
#ifdef RTREE_LEAF_COMPACT
- uintptr_t old_bits = rtree_leaf_elm_bits_read(tsdn, rtree, elm,
- true);
- uintptr_t bits = ((uintptr_t)rtree_leaf_elm_bits_szind_get(old_bits) <<
- LG_VADDR) | ((uintptr_t)rtree_leaf_elm_bits_extent_get(old_bits) &
- (((uintptr_t)0x1 << LG_VADDR) - 1)) | ((uintptr_t)slab);
- atomic_store_p(&elm->le_bits, (void *)bits, ATOMIC_RELEASE);
+ uintptr_t bits = rtree_leaf_elm_bits_read(tsdn, rtree, elm1,
+ /* dependent */ true);
+ bits &= ~RTREE_LEAF_STATE_MASK;
+ bits |= state << RTREE_LEAF_STATE_SHIFT;
+ atomic_store_p(&elm1->le_bits, (void *)bits, ATOMIC_RELEASE);
+ if (elm2 != NULL) {
+ atomic_store_p(&elm2->le_bits, (void *)bits, ATOMIC_RELEASE);
+ }
#else
- atomic_store_b(&elm->le_slab, slab, ATOMIC_RELEASE);
+ unsigned bits = atomic_load_u(&elm1->le_metadata, ATOMIC_RELAXED);
+ bits &= ~RTREE_LEAF_STATE_MASK;
+ bits |= state << RTREE_LEAF_STATE_SHIFT;
+ atomic_store_u(&elm1->le_metadata, bits, ATOMIC_RELEASE);
+ if (elm2 != NULL) {
+ atomic_store_u(&elm2->le_metadata, bits, ATOMIC_RELEASE);
+ }
#endif
}
-static inline void
-rtree_leaf_elm_write(tsdn_t *tsdn, rtree_t *rtree,
- rtree_leaf_elm_t *elm, extent_t *extent, szind_t szind, bool slab) {
-#ifdef RTREE_LEAF_COMPACT
- uintptr_t bits = ((uintptr_t)szind << LG_VADDR) |
- ((uintptr_t)extent & (((uintptr_t)0x1 << LG_VADDR) - 1)) |
- ((uintptr_t)slab);
- atomic_store_p(&elm->le_bits, (void *)bits, ATOMIC_RELEASE);
-#else
- rtree_leaf_elm_slab_write(tsdn, rtree, elm, slab);
- rtree_leaf_elm_szind_write(tsdn, rtree, elm, szind);
- /*
- * Write extent last, since the element is atomically considered valid
- * as soon as the extent field is non-NULL.
- */
- rtree_leaf_elm_extent_write(tsdn, rtree, elm, extent);
-#endif
-}
+/*
+ * Tries to look up the key in the L1 cache, returning false if there's a hit, or
+ * true if there's a miss.
+ * Key is allowed to be NULL; returns true in this case.
+ */
+JEMALLOC_ALWAYS_INLINE bool
+rtree_leaf_elm_lookup_fast(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx,
+ uintptr_t key, rtree_leaf_elm_t **elm) {
+ size_t slot = rtree_cache_direct_map(key);
+ uintptr_t leafkey = rtree_leafkey(key);
+ assert(leafkey != RTREE_LEAFKEY_INVALID);
-static inline void
-rtree_leaf_elm_szind_slab_update(tsdn_t *tsdn, rtree_t *rtree,
- rtree_leaf_elm_t *elm, szind_t szind, bool slab) {
- assert(!slab || szind < SC_NBINS);
+ if (unlikely(rtree_ctx->cache[slot].leafkey != leafkey)) {
+ return true;
+ }
- /*
- * The caller implicitly assures that it is the only writer to the szind
- * and slab fields, and that the extent field cannot currently change.
- */
- rtree_leaf_elm_slab_write(tsdn, rtree, elm, slab);
- rtree_leaf_elm_szind_write(tsdn, rtree, elm, szind);
+ rtree_leaf_elm_t *leaf = rtree_ctx->cache[slot].leaf;
+ assert(leaf != NULL);
+ uintptr_t subkey = rtree_subkey(key, RTREE_HEIGHT-1);
+ *elm = &leaf[subkey];
+
+ return false;
}
JEMALLOC_ALWAYS_INLINE rtree_leaf_elm_t *
@@ -382,147 +412,143 @@ rtree_leaf_elm_lookup(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx,
dependent, init_missing);
}
+/*
+ * Returns true on lookup failure.
+ */
static inline bool
-rtree_write(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx, uintptr_t key,
- extent_t *extent, szind_t szind, bool slab) {
- /* Use rtree_clear() to set the extent to NULL. */
- assert(extent != NULL);
-
+rtree_read_independent(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx,
+ uintptr_t key, rtree_contents_t *r_contents) {
rtree_leaf_elm_t *elm = rtree_leaf_elm_lookup(tsdn, rtree, rtree_ctx,
- key, false, true);
+ key, /* dependent */ false, /* init_missing */ false);
if (elm == NULL) {
return true;
}
-
- assert(rtree_leaf_elm_extent_read(tsdn, rtree, elm, false) == NULL);
- rtree_leaf_elm_write(tsdn, rtree, elm, extent, szind, slab);
-
+ *r_contents = rtree_leaf_elm_read(tsdn, rtree, elm,
+ /* dependent */ false);
return false;
}
-JEMALLOC_ALWAYS_INLINE rtree_leaf_elm_t *
-rtree_read(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx, uintptr_t key,
- bool dependent) {
+static inline rtree_contents_t
+rtree_read(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx,
+ uintptr_t key) {
rtree_leaf_elm_t *elm = rtree_leaf_elm_lookup(tsdn, rtree, rtree_ctx,
- key, dependent, false);
- if (!dependent && elm == NULL) {
- return NULL;
- }
+ key, /* dependent */ true, /* init_missing */ false);
assert(elm != NULL);
- return elm;
+ return rtree_leaf_elm_read(tsdn, rtree, elm, /* dependent */ true);
}
-JEMALLOC_ALWAYS_INLINE extent_t *
-rtree_extent_read(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx,
- uintptr_t key, bool dependent) {
- rtree_leaf_elm_t *elm = rtree_read(tsdn, rtree, rtree_ctx, key,
- dependent);
- if (!dependent && elm == NULL) {
- return NULL;
- }
- return rtree_leaf_elm_extent_read(tsdn, rtree, elm, dependent);
-}
-
-JEMALLOC_ALWAYS_INLINE szind_t
-rtree_szind_read(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx,
- uintptr_t key, bool dependent) {
- rtree_leaf_elm_t *elm = rtree_read(tsdn, rtree, rtree_ctx, key,
- dependent);
- if (!dependent && elm == NULL) {
- return SC_NSIZES;
- }
- return rtree_leaf_elm_szind_read(tsdn, rtree, elm, dependent);
+static inline rtree_metadata_t
+rtree_metadata_read(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx,
+ uintptr_t key) {
+ rtree_leaf_elm_t *elm = rtree_leaf_elm_lookup(tsdn, rtree, rtree_ctx,
+ key, /* dependent */ true, /* init_missing */ false);
+ assert(elm != NULL);
+ return rtree_leaf_elm_read(tsdn, rtree, elm,
+ /* dependent */ true).metadata;
}
/*
- * rtree_slab_read() is intentionally omitted because slab is always read in
- * conjunction with szind, which makes rtree_szind_slab_read() a better choice.
+ * Returns true when the request cannot be fulfilled by fastpath.
*/
-
-JEMALLOC_ALWAYS_INLINE bool
-rtree_extent_szind_read(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx,
- uintptr_t key, bool dependent, extent_t **r_extent, szind_t *r_szind) {
- rtree_leaf_elm_t *elm = rtree_read(tsdn, rtree, rtree_ctx, key,
- dependent);
- if (!dependent && elm == NULL) {
+static inline bool
+rtree_metadata_try_read_fast(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx,
+ uintptr_t key, rtree_metadata_t *r_rtree_metadata) {
+ rtree_leaf_elm_t *elm;
+ /*
+ * Should check the bool return value (lookup success or not) instead of
+ * elm == NULL (which will result in an extra branch). This is because
+ * when the cache lookup succeeds, there will never be a NULL pointer
+ * returned (which is unknown to the compiler).
+ */
+ if (rtree_leaf_elm_lookup_fast(tsdn, rtree, rtree_ctx, key, &elm)) {
return true;
}
- *r_extent = rtree_leaf_elm_extent_read(tsdn, rtree, elm, dependent);
- *r_szind = rtree_leaf_elm_szind_read(tsdn, rtree, elm, dependent);
+ assert(elm != NULL);
+ *r_rtree_metadata = rtree_leaf_elm_read(tsdn, rtree, elm,
+ /* dependent */ true).metadata;
return false;
}
-/*
- * Try to read szind_slab from the L1 cache. Returns true on a hit,
- * and fills in r_szind and r_slab. Otherwise returns false.
- *
- * Key is allowed to be NULL in order to save an extra branch on the
- * fastpath. returns false in this case.
- */
-JEMALLOC_ALWAYS_INLINE bool
-rtree_szind_slab_read_fast(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx,
- uintptr_t key, szind_t *r_szind, bool *r_slab) {
- rtree_leaf_elm_t *elm;
-
- size_t slot = rtree_cache_direct_map(key);
- uintptr_t leafkey = rtree_leafkey(key);
- assert(leafkey != RTREE_LEAFKEY_INVALID);
-
- if (likely(rtree_ctx->cache[slot].leafkey == leafkey)) {
- rtree_leaf_elm_t *leaf = rtree_ctx->cache[slot].leaf;
- assert(leaf != NULL);
- uintptr_t subkey = rtree_subkey(key, RTREE_HEIGHT-1);
- elm = &leaf[subkey];
-
-#ifdef RTREE_LEAF_COMPACT
- uintptr_t bits = rtree_leaf_elm_bits_read(tsdn, rtree,
- elm, true);
- *r_szind = rtree_leaf_elm_bits_szind_get(bits);
- *r_slab = rtree_leaf_elm_bits_slab_get(bits);
-#else
- *r_szind = rtree_leaf_elm_szind_read(tsdn, rtree, elm, true);
- *r_slab = rtree_leaf_elm_slab_read(tsdn, rtree, elm, true);
-#endif
- return true;
- } else {
- return false;
+JEMALLOC_ALWAYS_INLINE void
+rtree_write_range_impl(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx,
+ uintptr_t base, uintptr_t end, rtree_contents_t contents, bool clearing) {
+ assert((base & PAGE_MASK) == 0 && (end & PAGE_MASK) == 0);
+ /*
+ * Only used for emap_(de)register_interior, which implies the
+ * boundaries have been registered already. Therefore all the lookups
+ * are dependent w/o init_missing, assuming the range spans across at
+ * most 2 rtree leaf nodes (each covers 1 GiB of vaddr).
+ */
+ void *bits;
+ unsigned additional;
+ rtree_contents_encode(contents, &bits, &additional);
+
+ rtree_leaf_elm_t *elm = NULL; /* Dead store. */
+ for (uintptr_t addr = base; addr <= end; addr += PAGE) {
+ if (addr == base ||
+ (addr & ((ZU(1) << rtree_leaf_maskbits()) - 1)) == 0) {
+ elm = rtree_leaf_elm_lookup(tsdn, rtree, rtree_ctx, addr,
+ /* dependent */ true, /* init_missing */ false);
+ assert(elm != NULL);
+ }
+ assert(elm == rtree_leaf_elm_lookup(tsdn, rtree, rtree_ctx, addr,
+ /* dependent */ true, /* init_missing */ false));
+ assert(!clearing || rtree_leaf_elm_read(tsdn, rtree, elm,
+ /* dependent */ true).edata != NULL);
+ rtree_leaf_elm_write_commit(tsdn, rtree, elm, bits, additional);
+ elm++;
}
}
+
+JEMALLOC_ALWAYS_INLINE void
+rtree_write_range(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx,
+ uintptr_t base, uintptr_t end, rtree_contents_t contents) {
+ rtree_write_range_impl(tsdn, rtree, rtree_ctx, base, end, contents,
+ /* clearing */ false);
+}
+
JEMALLOC_ALWAYS_INLINE bool
-rtree_szind_slab_read(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx,
- uintptr_t key, bool dependent, szind_t *r_szind, bool *r_slab) {
- rtree_leaf_elm_t *elm = rtree_read(tsdn, rtree, rtree_ctx, key,
- dependent);
- if (!dependent && elm == NULL) {
+rtree_write(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx, uintptr_t key,
+ rtree_contents_t contents) {
+ rtree_leaf_elm_t *elm = rtree_leaf_elm_lookup(tsdn, rtree, rtree_ctx,
+ key, /* dependent */ false, /* init_missing */ true);
+ if (elm == NULL) {
return true;
}
-#ifdef RTREE_LEAF_COMPACT
- uintptr_t bits = rtree_leaf_elm_bits_read(tsdn, rtree, elm, dependent);
- *r_szind = rtree_leaf_elm_bits_szind_get(bits);
- *r_slab = rtree_leaf_elm_bits_slab_get(bits);
-#else
- *r_szind = rtree_leaf_elm_szind_read(tsdn, rtree, elm, dependent);
- *r_slab = rtree_leaf_elm_slab_read(tsdn, rtree, elm, dependent);
-#endif
- return false;
-}
-static inline void
-rtree_szind_slab_update(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx,
- uintptr_t key, szind_t szind, bool slab) {
- assert(!slab || szind < SC_NBINS);
+ rtree_leaf_elm_write(tsdn, rtree, elm, contents);
- rtree_leaf_elm_t *elm = rtree_read(tsdn, rtree, rtree_ctx, key, true);
- rtree_leaf_elm_szind_slab_update(tsdn, rtree, elm, szind, slab);
+ return false;
}
static inline void
rtree_clear(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx,
uintptr_t key) {
- rtree_leaf_elm_t *elm = rtree_read(tsdn, rtree, rtree_ctx, key, true);
- assert(rtree_leaf_elm_extent_read(tsdn, rtree, elm, false) !=
- NULL);
- rtree_leaf_elm_write(tsdn, rtree, elm, NULL, SC_NSIZES, false);
+ rtree_leaf_elm_t *elm = rtree_leaf_elm_lookup(tsdn, rtree, rtree_ctx,
+ key, /* dependent */ true, /* init_missing */ false);
+ assert(elm != NULL);
+ assert(rtree_leaf_elm_read(tsdn, rtree, elm,
+ /* dependent */ true).edata != NULL);
+ rtree_contents_t contents;
+ contents.edata = NULL;
+ contents.metadata.szind = SC_NSIZES;
+ contents.metadata.slab = false;
+ contents.metadata.is_head = false;
+ contents.metadata.state = (extent_state_t)0;
+ rtree_leaf_elm_write(tsdn, rtree, elm, contents);
+}
+
+static inline void
+rtree_clear_range(tsdn_t *tsdn, rtree_t *rtree, rtree_ctx_t *rtree_ctx,
+ uintptr_t base, uintptr_t end) {
+ rtree_contents_t contents;
+ contents.edata = NULL;
+ contents.metadata.szind = SC_NSIZES;
+ contents.metadata.slab = false;
+ contents.metadata.is_head = false;
+ contents.metadata.state = (extent_state_t)0;
+ rtree_write_range_impl(tsdn, rtree, rtree_ctx, base, end, contents,
+ /* clearing */ true);
}
#endif /* JEMALLOC_INTERNAL_RTREE_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/rtree_tsd.h b/contrib/jemalloc/include/jemalloc/internal/rtree_tsd.h
index 562e29297a76..e45525c5e6a3 100644
--- a/contrib/jemalloc/include/jemalloc/internal/rtree_tsd.h
+++ b/contrib/jemalloc/include/jemalloc/internal/rtree_tsd.h
@@ -18,16 +18,28 @@
* cache misses if made overly large, plus the cost of linear search in the LRU
* cache.
*/
-#define RTREE_CTX_LG_NCACHE 4
-#define RTREE_CTX_NCACHE (1 << RTREE_CTX_LG_NCACHE)
+#define RTREE_CTX_NCACHE 16
#define RTREE_CTX_NCACHE_L2 8
+/* Needed for initialization only. */
+#define RTREE_LEAFKEY_INVALID ((uintptr_t)1)
+#define RTREE_CTX_CACHE_ELM_INVALID {RTREE_LEAFKEY_INVALID, NULL}
+
+#define RTREE_CTX_INIT_ELM_1 RTREE_CTX_CACHE_ELM_INVALID
+#define RTREE_CTX_INIT_ELM_2 RTREE_CTX_INIT_ELM_1, RTREE_CTX_INIT_ELM_1
+#define RTREE_CTX_INIT_ELM_4 RTREE_CTX_INIT_ELM_2, RTREE_CTX_INIT_ELM_2
+#define RTREE_CTX_INIT_ELM_8 RTREE_CTX_INIT_ELM_4, RTREE_CTX_INIT_ELM_4
+#define RTREE_CTX_INIT_ELM_16 RTREE_CTX_INIT_ELM_8, RTREE_CTX_INIT_ELM_8
+
+#define _RTREE_CTX_INIT_ELM_DATA(n) RTREE_CTX_INIT_ELM_##n
+#define RTREE_CTX_INIT_ELM_DATA(n) _RTREE_CTX_INIT_ELM_DATA(n)
+
/*
- * Zero initializer required for tsd initialization only. Proper initialization
- * done via rtree_ctx_data_init().
+ * Static initializer (to invalidate the cache entries) is required because the
+ * free fastpath may access the rtree cache before a full tsd initialization.
*/
-#define RTREE_CTX_ZERO_INITIALIZER {{{0, 0}}, {{0, 0}}}
-
+#define RTREE_CTX_INITIALIZER {{RTREE_CTX_INIT_ELM_DATA(RTREE_CTX_NCACHE)}, \
+ {RTREE_CTX_INIT_ELM_DATA(RTREE_CTX_NCACHE_L2)}}
typedef struct rtree_leaf_elm_s rtree_leaf_elm_t;
diff --git a/contrib/jemalloc/include/jemalloc/internal/safety_check.h b/contrib/jemalloc/include/jemalloc/internal/safety_check.h
index 53339ac12f2d..f1a74f174dee 100644
--- a/contrib/jemalloc/include/jemalloc/internal/safety_check.h
+++ b/contrib/jemalloc/include/jemalloc/internal/safety_check.h
@@ -1,9 +1,14 @@
#ifndef JEMALLOC_INTERNAL_SAFETY_CHECK_H
#define JEMALLOC_INTERNAL_SAFETY_CHECK_H
+void safety_check_fail_sized_dealloc(bool current_dealloc, const void *ptr,
+ size_t true_size, size_t input_size);
void safety_check_fail(const char *format, ...);
+
+typedef void (*safety_check_abort_hook_t)(const char *message);
+
/* Can set to NULL for a default. */
-void safety_check_set_abort(void (*abort_fn)());
+void safety_check_set_abort(safety_check_abort_hook_t abort_fn);
JEMALLOC_ALWAYS_INLINE void
safety_check_set_redzone(void *ptr, size_t usize, size_t bumped_usize) {
diff --git a/contrib/jemalloc/include/jemalloc/internal/san.h b/contrib/jemalloc/include/jemalloc/internal/san.h
new file mode 100644
index 000000000000..8813d6bbe7de
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/internal/san.h
@@ -0,0 +1,191 @@
+#ifndef JEMALLOC_INTERNAL_GUARD_H
+#define JEMALLOC_INTERNAL_GUARD_H
+
+#include "jemalloc/internal/ehooks.h"
+#include "jemalloc/internal/emap.h"
+
+#define SAN_PAGE_GUARD PAGE
+#define SAN_PAGE_GUARDS_SIZE (SAN_PAGE_GUARD * 2)
+
+#define SAN_GUARD_LARGE_EVERY_N_EXTENTS_DEFAULT 0
+#define SAN_GUARD_SMALL_EVERY_N_EXTENTS_DEFAULT 0
+
+#define SAN_LG_UAF_ALIGN_DEFAULT (-1)
+#define SAN_CACHE_BIN_NONFAST_MASK_DEFAULT (uintptr_t)(-1)
+
+static const uintptr_t uaf_detect_junk = (uintptr_t)0x5b5b5b5b5b5b5b5bULL;
+
+/* 0 means disabled, i.e. never guarded. */
+extern size_t opt_san_guard_large;
+extern size_t opt_san_guard_small;
+/* -1 means disabled, i.e. never check for use-after-free. */
+extern ssize_t opt_lg_san_uaf_align;
+
+void san_guard_pages(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata,
+ emap_t *emap, bool left, bool right, bool remap);
+void san_unguard_pages(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata,
+ emap_t *emap, bool left, bool right);
+/*
+ * Unguard the extent, but don't modify emap boundaries. Must be called on an
+ * extent that has been erased from emap and shouldn't be placed back.
+ */
+void san_unguard_pages_pre_destroy(tsdn_t *tsdn, ehooks_t *ehooks,
+ edata_t *edata, emap_t *emap);
+void san_check_stashed_ptrs(void **ptrs, size_t nstashed, size_t usize);
+
+void tsd_san_init(tsd_t *tsd);
+void san_init(ssize_t lg_san_uaf_align);
+
+static inline void
+san_guard_pages_two_sided(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata,
+ emap_t *emap, bool remap) {
+ san_guard_pages(tsdn, ehooks, edata, emap, true, true, remap);
+}
+
+static inline void
+san_unguard_pages_two_sided(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata,
+ emap_t *emap) {
+ san_unguard_pages(tsdn, ehooks, edata, emap, true, true);
+}
+
+static inline size_t
+san_two_side_unguarded_sz(size_t size) {
+ assert(size % PAGE == 0);
+ assert(size >= SAN_PAGE_GUARDS_SIZE);
+ return size - SAN_PAGE_GUARDS_SIZE;
+}
+
+static inline size_t
+san_two_side_guarded_sz(size_t size) {
+ assert(size % PAGE == 0);
+ return size + SAN_PAGE_GUARDS_SIZE;
+}
+
+static inline size_t
+san_one_side_unguarded_sz(size_t size) {
+ assert(size % PAGE == 0);
+ assert(size >= SAN_PAGE_GUARD);
+ return size - SAN_PAGE_GUARD;
+}
+
+static inline size_t
+san_one_side_guarded_sz(size_t size) {
+ assert(size % PAGE == 0);
+ return size + SAN_PAGE_GUARD;
+}
+
+static inline bool
+san_guard_enabled(void) {
+ return (opt_san_guard_large != 0 || opt_san_guard_small != 0);
+}
+
+static inline bool
+san_large_extent_decide_guard(tsdn_t *tsdn, ehooks_t *ehooks, size_t size,
+ size_t alignment) {
+ if (opt_san_guard_large == 0 || ehooks_guard_will_fail(ehooks) ||
+ tsdn_null(tsdn)) {
+ return false;
+ }
+
+ tsd_t *tsd = tsdn_tsd(tsdn);
+ uint64_t n = tsd_san_extents_until_guard_large_get(tsd);
+ assert(n >= 1);
+ if (n > 1) {
+ /*
+ * Subtract conditionally because the guard may not happen due
+ * to alignment or size restriction below.
+ */
+ *tsd_san_extents_until_guard_largep_get(tsd) = n - 1;
+ }
+
+ if (n == 1 && (alignment <= PAGE) &&
+ (san_two_side_guarded_sz(size) <= SC_LARGE_MAXCLASS)) {
+ *tsd_san_extents_until_guard_largep_get(tsd) =
+ opt_san_guard_large;
+ return true;
+ } else {
+ assert(tsd_san_extents_until_guard_large_get(tsd) >= 1);
+ return false;
+ }
+}
+
+static inline bool
+san_slab_extent_decide_guard(tsdn_t *tsdn, ehooks_t *ehooks) {
+ if (opt_san_guard_small == 0 || ehooks_guard_will_fail(ehooks) ||
+ tsdn_null(tsdn)) {
+ return false;
+ }
+
+ tsd_t *tsd = tsdn_tsd(tsdn);
+ uint64_t n = tsd_san_extents_until_guard_small_get(tsd);
+ assert(n >= 1);
+ if (n == 1) {
+ *tsd_san_extents_until_guard_smallp_get(tsd) =
+ opt_san_guard_small;
+ return true;
+ } else {
+ *tsd_san_extents_until_guard_smallp_get(tsd) = n - 1;
+ assert(tsd_san_extents_until_guard_small_get(tsd) >= 1);
+ return false;
+ }
+}
+
+static inline void
+san_junk_ptr_locations(void *ptr, size_t usize, void **first, void **mid,
+ void **last) {
+ size_t ptr_sz = sizeof(void *);
+
+ *first = ptr;
+
+ *mid = (void *)((uintptr_t)ptr + ((usize >> 1) & ~(ptr_sz - 1)));
+ assert(*first != *mid || usize == ptr_sz);
+ assert((uintptr_t)*first <= (uintptr_t)*mid);
+
+ /*
+ * When usize > 32K, the gap between requested_size and usize might be
+ * greater than 4K -- this means the last write may access an
+ * likely-untouched page (default settings w/ 4K pages). However by
+ * default the tcache only goes up to the 32K size class, and is usually
+ * tuned lower instead of higher, which makes it less of a concern.
+ */
+ *last = (void *)((uintptr_t)ptr + usize - sizeof(uaf_detect_junk));
+ assert(*first != *last || usize == ptr_sz);
+ assert(*mid != *last || usize <= ptr_sz * 2);
+ assert((uintptr_t)*mid <= (uintptr_t)*last);
+}
+
+static inline bool
+san_junk_ptr_should_slow(void) {
+ /*
+ * The latter condition (pointer size greater than the min size class)
+ * is not expected -- fall back to the slow path for simplicity.
+ */
+ return config_debug || (LG_SIZEOF_PTR > SC_LG_TINY_MIN);
+}
+
+static inline void
+san_junk_ptr(void *ptr, size_t usize) {
+ if (san_junk_ptr_should_slow()) {
+ memset(ptr, (char)uaf_detect_junk, usize);
+ return;
+ }
+
+ void *first, *mid, *last;
+ san_junk_ptr_locations(ptr, usize, &first, &mid, &last);
+ *(uintptr_t *)first = uaf_detect_junk;
+ *(uintptr_t *)mid = uaf_detect_junk;
+ *(uintptr_t *)last = uaf_detect_junk;
+}
+
+static inline bool
+san_uaf_detection_enabled(void) {
+ bool ret = config_uaf_detection && (opt_lg_san_uaf_align != -1);
+ if (config_uaf_detection && ret) {
+ assert(san_cache_bin_nonfast_mask == ((uintptr_t)1 <<
+ opt_lg_san_uaf_align) - 1);
+ }
+
+ return ret;
+}
+
+#endif /* JEMALLOC_INTERNAL_GUARD_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/san_bump.h b/contrib/jemalloc/include/jemalloc/internal/san_bump.h
new file mode 100644
index 000000000000..8ec4a710d6f4
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/internal/san_bump.h
@@ -0,0 +1,52 @@
+#ifndef JEMALLOC_INTERNAL_SAN_BUMP_H
+#define JEMALLOC_INTERNAL_SAN_BUMP_H
+
+#include "jemalloc/internal/edata.h"
+#include "jemalloc/internal/exp_grow.h"
+#include "jemalloc/internal/mutex.h"
+
+#define SBA_RETAINED_ALLOC_SIZE ((size_t)4 << 20)
+
+extern bool opt_retain;
+
+typedef struct ehooks_s ehooks_t;
+typedef struct pac_s pac_t;
+
+typedef struct san_bump_alloc_s san_bump_alloc_t;
+struct san_bump_alloc_s {
+ malloc_mutex_t mtx;
+
+ edata_t *curr_reg;
+};
+
+static inline bool
+san_bump_enabled() {
+ /*
+ * We enable san_bump allocator only when it's possible to break up a
+ * mapping and unmap a part of it (maps_coalesce). This is needed to
+ * ensure the arena destruction process can destroy all retained guarded
+ * extents one by one and to unmap a trailing part of a retained guarded
+ * region when it's too small to fit a pending allocation.
+ * opt_retain is required, because this allocator retains a large
+ * virtual memory mapping and returns smaller parts of it.
+ */
+ return maps_coalesce && opt_retain;
+}
+
+static inline bool
+san_bump_alloc_init(san_bump_alloc_t* sba) {
+ bool err = malloc_mutex_init(&sba->mtx, "sanitizer_bump_allocator",
+ WITNESS_RANK_SAN_BUMP_ALLOC, malloc_mutex_rank_exclusive);
+ if (err) {
+ return true;
+ }
+ sba->curr_reg = NULL;
+
+ return false;
+}
+
+edata_t *
+san_bump_alloc(tsdn_t *tsdn, san_bump_alloc_t* sba, pac_t *pac, ehooks_t *ehooks,
+ size_t size, bool zero);
+
+#endif /* JEMALLOC_INTERNAL_SAN_BUMP_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/sc.h b/contrib/jemalloc/include/jemalloc/internal/sc.h
index 9a099d8b6457..9bab347beeed 100644
--- a/contrib/jemalloc/include/jemalloc/internal/sc.h
+++ b/contrib/jemalloc/include/jemalloc/internal/sc.h
@@ -197,30 +197,34 @@
(SC_LG_BASE_MAX - SC_LG_FIRST_REGULAR_BASE + 1) - 1)
#define SC_NSIZES (SC_NTINY + SC_NPSEUDO + SC_NREGULAR)
-/* The number of size classes that are a multiple of the page size. */
-#define SC_NPSIZES ( \
- /* Start with all the size classes. */ \
- SC_NSIZES \
- /* Subtract out those groups with too small a base. */ \
- - (LG_PAGE - 1 - SC_LG_FIRST_REGULAR_BASE) * SC_NGROUP \
- /* And the pseudo-group. */ \
- - SC_NPSEUDO \
- /* And the tiny group. */ \
- - SC_NTINY \
- /* Sizes where ndelta*delta is not a multiple of the page size. */ \
- - (SC_LG_NGROUP * SC_NGROUP))
/*
- * Note that the last line is computed as the sum of the second column in the
- * following table:
- * lg(base) | count of sizes to exclude
- * ------------------------------|-----------------------------
- * LG_PAGE - 1 | SC_NGROUP - 1
- * LG_PAGE | SC_NGROUP - 1
- * LG_PAGE + 1 | SC_NGROUP - 2
- * LG_PAGE + 2 | SC_NGROUP - 4
- * ... | ...
- * LG_PAGE + (SC_LG_NGROUP - 1) | SC_NGROUP - (SC_NGROUP / 2)
+ * The number of size classes that are a multiple of the page size.
+ *
+ * Here are the first few bases that have a page-sized SC.
+ *
+ * lg(base) | base | highest SC | page-multiple SCs
+ * --------------|------------------------------------------
+ * LG_PAGE - 1 | PAGE / 2 | PAGE | 1
+ * LG_PAGE | PAGE | 2 * PAGE | 1
+ * LG_PAGE + 1 | 2 * PAGE | 4 * PAGE | 2
+ * LG_PAGE + 2 | 4 * PAGE | 8 * PAGE | 4
+ *
+ * The number of page-multiple SCs continues to grow in powers of two, up until
+ * lg_delta == lg_page, which corresponds to setting lg_base to lg_page +
+ * SC_LG_NGROUP. So, then, the number of size classes that are multiples of the
+ * page size whose lg_delta is less than the page size are
+ * is 1 + (2**0 + 2**1 + ... + 2**(lg_ngroup - 1) == 2**lg_ngroup.
+ *
+ * For each base with lg_base in [lg_page + lg_ngroup, lg_base_max), there are
+ * NGROUP page-sized size classes, and when lg_base == lg_base_max, there are
+ * NGROUP - 1.
+ *
+ * This gives us the quantity we seek.
*/
+#define SC_NPSIZES ( \
+ SC_NGROUP \
+ + (SC_LG_BASE_MAX - (LG_PAGE + SC_LG_NGROUP)) * SC_NGROUP \
+ + SC_NGROUP - 1)
/*
* We declare a size class is binnable if size < page size * group. Or, in other
@@ -242,17 +246,23 @@
# error "Too many small size classes"
#endif
-/* The largest size class in the lookup table. */
-#define SC_LOOKUP_MAXCLASS ((size_t)1 << 12)
+/* The largest size class in the lookup table, and its binary log. */
+#define SC_LG_MAX_LOOKUP 12
+#define SC_LOOKUP_MAXCLASS (1 << SC_LG_MAX_LOOKUP)
/* Internal, only used for the definition of SC_SMALL_MAXCLASS. */
-#define SC_SMALL_MAX_BASE ((size_t)1 << (LG_PAGE + SC_LG_NGROUP - 1))
-#define SC_SMALL_MAX_DELTA ((size_t)1 << (LG_PAGE - 1))
+#define SC_SMALL_MAX_BASE (1 << (LG_PAGE + SC_LG_NGROUP - 1))
+#define SC_SMALL_MAX_DELTA (1 << (LG_PAGE - 1))
/* The largest size class allocated out of a slab. */
#define SC_SMALL_MAXCLASS (SC_SMALL_MAX_BASE \
+ (SC_NGROUP - 1) * SC_SMALL_MAX_DELTA)
+/* The fastpath assumes all lookup-able sizes are small. */
+#if (SC_SMALL_MAXCLASS < SC_LOOKUP_MAXCLASS)
+# error "Lookup table sizes must be small"
+#endif
+
/* The smallest size class not allocated out of a slab. */
#define SC_LARGE_MINCLASS ((size_t)1ULL << (LG_PAGE + SC_LG_NGROUP))
#define SC_LG_LARGE_MINCLASS (LG_PAGE + SC_LG_NGROUP)
@@ -264,6 +274,19 @@
/* The largest size class supported. */
#define SC_LARGE_MAXCLASS (SC_MAX_BASE + (SC_NGROUP - 1) * SC_MAX_DELTA)
+/* Maximum number of regions in one slab. */
+#ifndef CONFIG_LG_SLAB_MAXREGS
+# define SC_LG_SLAB_MAXREGS (LG_PAGE - SC_LG_TINY_MIN)
+#else
+# if CONFIG_LG_SLAB_MAXREGS < (LG_PAGE - SC_LG_TINY_MIN)
+# error "Unsupported SC_LG_SLAB_MAXREGS"
+# else
+# define SC_LG_SLAB_MAXREGS CONFIG_LG_SLAB_MAXREGS
+# endif
+#endif
+
+#define SC_SLAB_MAXREGS (1U << SC_LG_SLAB_MAXREGS)
+
typedef struct sc_s sc_t;
struct sc_s {
/* Size class index, or -1 if not a valid size class. */
@@ -321,10 +344,11 @@ struct sc_data_s {
sc_t sc[SC_NSIZES];
};
+size_t reg_size_compute(int lg_base, int lg_delta, int ndelta);
void sc_data_init(sc_data_t *data);
/*
* Updates slab sizes in [begin, end] to be pgs pages in length, if possible.
- * Otherwise, does its best to accomodate the request.
+ * Otherwise, does its best to accommodate the request.
*/
void sc_data_update_slab_size(sc_data_t *data, size_t begin, size_t end,
int pgs);
diff --git a/contrib/jemalloc/include/jemalloc/internal/sec.h b/contrib/jemalloc/include/jemalloc/internal/sec.h
new file mode 100644
index 000000000000..fa863382db97
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/internal/sec.h
@@ -0,0 +1,120 @@
+#ifndef JEMALLOC_INTERNAL_SEC_H
+#define JEMALLOC_INTERNAL_SEC_H
+
+#include "jemalloc/internal/atomic.h"
+#include "jemalloc/internal/pai.h"
+
+/*
+ * Small extent cache.
+ *
+ * This includes some utilities to cache small extents. We have a per-pszind
+ * bin with its own list of extents of that size. We don't try to do any
+ * coalescing of extents (since it would in general require cross-shard locks or
+ * knowledge of the underlying PAI implementation).
+ */
+
+/*
+ * For now, this is just one field; eventually, we'll probably want to get more
+ * fine-grained data out (like per-size class statistics).
+ */
+typedef struct sec_stats_s sec_stats_t;
+struct sec_stats_s {
+ /* Sum of bytes_cur across all shards. */
+ size_t bytes;
+};
+
+static inline void
+sec_stats_accum(sec_stats_t *dst, sec_stats_t *src) {
+ dst->bytes += src->bytes;
+}
+
+/* A collections of free extents, all of the same size. */
+typedef struct sec_bin_s sec_bin_t;
+struct sec_bin_s {
+ /*
+ * When we fail to fulfill an allocation, we do a batch-alloc on the
+ * underlying allocator to fill extra items, as well. We drop the SEC
+ * lock while doing so, to allow operations on other bins to succeed.
+ * That introduces the possibility of other threads also trying to
+ * allocate out of this bin, failing, and also going to the backing
+ * allocator. To avoid a thundering herd problem in which lots of
+ * threads do batch allocs and overfill this bin as a result, we only
+ * allow one batch allocation at a time for a bin. This bool tracks
+ * whether or not some thread is already batch allocating.
+ *
+ * Eventually, the right answer may be a smarter sharding policy for the
+ * bins (e.g. a mutex per bin, which would also be more scalable
+ * generally; the batch-allocating thread could hold it while
+ * batch-allocating).
+ */
+ bool being_batch_filled;
+
+ /*
+ * Number of bytes in this particular bin (as opposed to the
+ * sec_shard_t's bytes_cur. This isn't user visible or reported in
+ * stats; rather, it allows us to quickly determine the change in the
+ * centralized counter when flushing.
+ */
+ size_t bytes_cur;
+ edata_list_active_t freelist;
+};
+
+typedef struct sec_shard_s sec_shard_t;
+struct sec_shard_s {
+ /*
+ * We don't keep per-bin mutexes, even though that would allow more
+ * sharding; this allows global cache-eviction, which in turn allows for
+ * better balancing across free lists.
+ */
+ malloc_mutex_t mtx;
+ /*
+ * A SEC may need to be shut down (i.e. flushed of its contents and
+ * prevented from further caching). To avoid tricky synchronization
+ * issues, we just track enabled-status in each shard, guarded by a
+ * mutex. In practice, this is only ever checked during brief races,
+ * since the arena-level atomic boolean tracking HPA enabled-ness means
+ * that we won't go down these pathways very often after custom extent
+ * hooks are installed.
+ */
+ bool enabled;
+ sec_bin_t *bins;
+ /* Number of bytes in all bins in the shard. */
+ size_t bytes_cur;
+ /* The next pszind to flush in the flush-some pathways. */
+ pszind_t to_flush_next;
+};
+
+typedef struct sec_s sec_t;
+struct sec_s {
+ pai_t pai;
+ pai_t *fallback;
+
+ sec_opts_t opts;
+ sec_shard_t *shards;
+ pszind_t npsizes;
+};
+
+bool sec_init(tsdn_t *tsdn, sec_t *sec, base_t *base, pai_t *fallback,
+ const sec_opts_t *opts);
+void sec_flush(tsdn_t *tsdn, sec_t *sec);
+void sec_disable(tsdn_t *tsdn, sec_t *sec);
+
+/*
+ * Morally, these two stats methods probably ought to be a single one (and the
+ * mutex_prof_data ought to live in the sec_stats_t. But splitting them apart
+ * lets them fit easily into the pa_shard stats framework (which also has this
+ * split), which simplifies the stats management.
+ */
+void sec_stats_merge(tsdn_t *tsdn, sec_t *sec, sec_stats_t *stats);
+void sec_mutex_stats_read(tsdn_t *tsdn, sec_t *sec,
+ mutex_prof_data_t *mutex_prof_data);
+
+/*
+ * We use the arena lock ordering; these are acquired in phase 2 of forking, but
+ * should be acquired before the underlying allocator mutexes.
+ */
+void sec_prefork2(tsdn_t *tsdn, sec_t *sec);
+void sec_postfork_parent(tsdn_t *tsdn, sec_t *sec);
+void sec_postfork_child(tsdn_t *tsdn, sec_t *sec);
+
+#endif /* JEMALLOC_INTERNAL_SEC_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/sec_opts.h b/contrib/jemalloc/include/jemalloc/internal/sec_opts.h
new file mode 100644
index 000000000000..a3ad72fbece5
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/internal/sec_opts.h
@@ -0,0 +1,59 @@
+#ifndef JEMALLOC_INTERNAL_SEC_OPTS_H
+#define JEMALLOC_INTERNAL_SEC_OPTS_H
+
+/*
+ * The configuration settings used by an sec_t. Morally, this is part of the
+ * SEC interface, but we put it here for header-ordering reasons.
+ */
+
+typedef struct sec_opts_s sec_opts_t;
+struct sec_opts_s {
+ /*
+ * We don't necessarily always use all the shards; requests are
+ * distributed across shards [0, nshards - 1).
+ */
+ size_t nshards;
+ /*
+ * We'll automatically refuse to cache any objects in this sec if
+ * they're larger than max_alloc bytes, instead forwarding such objects
+ * directly to the fallback.
+ */
+ size_t max_alloc;
+ /*
+ * Exceeding this amount of cached extents in a shard causes us to start
+ * flushing bins in that shard until we fall below bytes_after_flush.
+ */
+ size_t max_bytes;
+ /*
+ * The number of bytes (in all bins) we flush down to when we exceed
+ * bytes_cur. We want this to be less than bytes_cur, because
+ * otherwise we could get into situations where a shard undergoing
+ * net-deallocation keeps bytes_cur very near to max_bytes, so that
+ * most deallocations get immediately forwarded to the underlying PAI
+ * implementation, defeating the point of the SEC.
+ */
+ size_t bytes_after_flush;
+ /*
+ * When we can't satisfy an allocation out of the SEC because there are
+ * no available ones cached, we allocate multiple of that size out of
+ * the fallback allocator. Eventually we might want to do something
+ * cleverer, but for now we just grab a fixed number.
+ */
+ size_t batch_fill_extra;
+};
+
+#define SEC_OPTS_DEFAULT { \
+ /* nshards */ \
+ 4, \
+ /* max_alloc */ \
+ (32 * 1024) < PAGE ? PAGE : (32 * 1024), \
+ /* max_bytes */ \
+ 256 * 1024, \
+ /* bytes_after_flush */ \
+ 128 * 1024, \
+ /* batch_fill_extra */ \
+ 0 \
+}
+
+
+#endif /* JEMALLOC_INTERNAL_SEC_OPTS_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/slab_data.h b/contrib/jemalloc/include/jemalloc/internal/slab_data.h
new file mode 100644
index 000000000000..e821863d8d1c
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/internal/slab_data.h
@@ -0,0 +1,12 @@
+#ifndef JEMALLOC_INTERNAL_SLAB_DATA_H
+#define JEMALLOC_INTERNAL_SLAB_DATA_H
+
+#include "jemalloc/internal/bitmap.h"
+
+typedef struct slab_data_s slab_data_t;
+struct slab_data_s {
+ /* Per region allocated/deallocated bitmap. */
+ bitmap_t bitmap[BITMAP_GROUPS_MAX];
+};
+
+#endif /* JEMALLOC_INTERNAL_SLAB_DATA_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/smoothstep.sh b/contrib/jemalloc/include/jemalloc/internal/smoothstep.sh
new file mode 100755
index 000000000000..65de97bf46a8
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/internal/smoothstep.sh
@@ -0,0 +1,101 @@
+#!/bin/sh
+#
+# Generate a discrete lookup table for a sigmoid function in the smoothstep
+# family (https://en.wikipedia.org/wiki/Smoothstep), where the lookup table
+# entries correspond to x in [1/nsteps, 2/nsteps, ..., nsteps/nsteps]. Encode
+# the entries using a binary fixed point representation.
+#
+# Usage: smoothstep.sh <variant> <nsteps> <bfp> <xprec> <yprec>
+#
+# <variant> is in {smooth, smoother, smoothest}.
+# <nsteps> must be greater than zero.
+# <bfp> must be in [0..62]; reasonable values are roughly [10..30].
+# <xprec> is x decimal precision.
+# <yprec> is y decimal precision.
+
+#set -x
+
+cmd="sh smoothstep.sh $*"
+variant=$1
+nsteps=$2
+bfp=$3
+xprec=$4
+yprec=$5
+
+case "${variant}" in
+ smooth)
+ ;;
+ smoother)
+ ;;
+ smoothest)
+ ;;
+ *)
+ echo "Unsupported variant"
+ exit 1
+ ;;
+esac
+
+smooth() {
+ step=$1
+ y=`echo ${yprec} k ${step} ${nsteps} / sx _2 lx 3 ^ '*' 3 lx 2 ^ '*' + p | dc | tr -d '\\\\\n' | sed -e 's#^\.#0.#g'`
+ h=`echo ${yprec} k 2 ${bfp} ^ ${y} '*' p | dc | tr -d '\\\\\n' | sed -e 's#^\.#0.#g' | tr '.' ' ' | awk '{print $1}' `
+}
+
+smoother() {
+ step=$1
+ y=`echo ${yprec} k ${step} ${nsteps} / sx 6 lx 5 ^ '*' _15 lx 4 ^ '*' + 10 lx 3 ^ '*' + p | dc | tr -d '\\\\\n' | sed -e 's#^\.#0.#g'`
+ h=`echo ${yprec} k 2 ${bfp} ^ ${y} '*' p | dc | tr -d '\\\\\n' | sed -e 's#^\.#0.#g' | tr '.' ' ' | awk '{print $1}' `
+}
+
+smoothest() {
+ step=$1
+ y=`echo ${yprec} k ${step} ${nsteps} / sx _20 lx 7 ^ '*' 70 lx 6 ^ '*' + _84 lx 5 ^ '*' + 35 lx 4 ^ '*' + p | dc | tr -d '\\\\\n' | sed -e 's#^\.#0.#g'`
+ h=`echo ${yprec} k 2 ${bfp} ^ ${y} '*' p | dc | tr -d '\\\\\n' | sed -e 's#^\.#0.#g' | tr '.' ' ' | awk '{print $1}' `
+}
+
+cat <<EOF
+#ifndef JEMALLOC_INTERNAL_SMOOTHSTEP_H
+#define JEMALLOC_INTERNAL_SMOOTHSTEP_H
+
+/*
+ * This file was generated by the following command:
+ * $cmd
+ */
+/******************************************************************************/
+
+/*
+ * This header defines a precomputed table based on the smoothstep family of
+ * sigmoidal curves (https://en.wikipedia.org/wiki/Smoothstep) that grow from 0
+ * to 1 in 0 <= x <= 1. The table is stored as integer fixed point values so
+ * that floating point math can be avoided.
+ *
+ * 3 2
+ * smoothstep(x) = -2x + 3x
+ *
+ * 5 4 3
+ * smootherstep(x) = 6x - 15x + 10x
+ *
+ * 7 6 5 4
+ * smootheststep(x) = -20x + 70x - 84x + 35x
+ */
+
+#define SMOOTHSTEP_VARIANT "${variant}"
+#define SMOOTHSTEP_NSTEPS ${nsteps}
+#define SMOOTHSTEP_BFP ${bfp}
+#define SMOOTHSTEP \\
+ /* STEP(step, h, x, y) */ \\
+EOF
+
+s=1
+while [ $s -le $nsteps ] ; do
+ $variant ${s}
+ x=`echo ${xprec} k ${s} ${nsteps} / p | dc | tr -d '\\\\\n' | sed -e 's#^\.#0.#g'`
+ printf ' STEP(%4d, UINT64_C(0x%016x), %s, %s) \\\n' ${s} ${h} ${x} ${y}
+
+ s=$((s+1))
+done
+echo
+
+cat <<EOF
+#endif /* JEMALLOC_INTERNAL_SMOOTHSTEP_H */
+EOF
diff --git a/contrib/jemalloc/include/jemalloc/internal/stats.h b/contrib/jemalloc/include/jemalloc/internal/stats.h
index 3b9e0eac12b8..727f7dcbd718 100644
--- a/contrib/jemalloc/include/jemalloc/internal/stats.h
+++ b/contrib/jemalloc/include/jemalloc/internal/stats.h
@@ -11,7 +11,8 @@
OPTION('b', bins, true, false) \
OPTION('l', large, true, false) \
OPTION('x', mutex, true, false) \
- OPTION('e', extents, true, false)
+ OPTION('e', extents, true, false) \
+ OPTION('h', hpa, config_stats, false)
enum {
#define OPTION(o, v, d, s) stats_print_option_num_##v,
@@ -24,8 +25,30 @@ enum {
extern bool opt_stats_print;
extern char opt_stats_print_opts[stats_print_tot_num_options+1];
+/* Utilities for stats_interval. */
+extern int64_t opt_stats_interval;
+extern char opt_stats_interval_opts[stats_print_tot_num_options+1];
+
+#define STATS_INTERVAL_DEFAULT -1
+/*
+ * Batch-increment the counter to reduce synchronization overhead. Each thread
+ * merges after (interval >> LG_BATCH_SIZE) bytes of allocations; also limit the
+ * BATCH_MAX for accuracy when the interval is huge (which is expected).
+ */
+#define STATS_INTERVAL_ACCUM_LG_BATCH_SIZE 6
+#define STATS_INTERVAL_ACCUM_BATCH_MAX (4 << 20)
+
+/* Only accessed by thread event. */
+uint64_t stats_interval_new_event_wait(tsd_t *tsd);
+uint64_t stats_interval_postponed_event_wait(tsd_t *tsd);
+void stats_interval_event_handler(tsd_t *tsd, uint64_t elapsed);
+
/* Implements je_malloc_stats_print. */
-void stats_print(void (*write_cb)(void *, const char *), void *cbopaque,
- const char *opts);
+void stats_print(write_cb_t *write_cb, void *cbopaque, const char *opts);
+
+bool stats_boot(void);
+void stats_prefork(tsdn_t *tsdn);
+void stats_postfork_parent(tsdn_t *tsdn);
+void stats_postfork_child(tsdn_t *tsdn);
#endif /* JEMALLOC_INTERNAL_STATS_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/sz.h b/contrib/jemalloc/include/jemalloc/internal/sz.h
index 68e558abfea3..3c0fc1da33a6 100644
--- a/contrib/jemalloc/include/jemalloc/internal/sz.h
+++ b/contrib/jemalloc/include/jemalloc/internal/sz.h
@@ -22,6 +22,12 @@
* size that would result from such an allocation.
*/
+/* Page size index type. */
+typedef unsigned pszind_t;
+
+/* Size class index type. */
+typedef unsigned szind_t;
+
/*
* sz_pind2sz_tab encodes the same information as could be computed by
* sz_pind2sz_compute().
@@ -39,34 +45,62 @@ extern size_t sz_index2size_tab[SC_NSIZES];
*/
extern uint8_t sz_size2index_tab[];
-static const size_t sz_large_pad =
-#ifdef JEMALLOC_CACHE_OBLIVIOUS
- PAGE
-#else
- 0
-#endif
- ;
+/*
+ * Padding for large allocations: PAGE when opt_cache_oblivious == true (to
+ * enable cache index randomization); 0 otherwise.
+ */
+extern size_t sz_large_pad;
-extern void sz_boot(const sc_data_t *sc_data);
+extern void sz_boot(const sc_data_t *sc_data, bool cache_oblivious);
JEMALLOC_ALWAYS_INLINE pszind_t
sz_psz2ind(size_t psz) {
+ assert(psz > 0);
if (unlikely(psz > SC_LARGE_MAXCLASS)) {
return SC_NPSIZES;
}
- pszind_t x = lg_floor((psz<<1)-1);
- pszind_t shift = (x < SC_LG_NGROUP + LG_PAGE) ?
+ /* x is the lg of the first base >= psz. */
+ pszind_t x = lg_ceil(psz);
+ /*
+ * sc.h introduces a lot of size classes. These size classes are divided
+ * into different size class groups. There is a very special size class
+ * group, each size class in or after it is an integer multiple of PAGE.
+ * We call it first_ps_rg. It means first page size regular group. The
+ * range of first_ps_rg is (base, base * 2], and base == PAGE *
+ * SC_NGROUP. off_to_first_ps_rg begins from 1, instead of 0. e.g.
+ * off_to_first_ps_rg is 1 when psz is (PAGE * SC_NGROUP + 1).
+ */
+ pszind_t off_to_first_ps_rg = (x < SC_LG_NGROUP + LG_PAGE) ?
0 : x - (SC_LG_NGROUP + LG_PAGE);
- pszind_t grp = shift << SC_LG_NGROUP;
- pszind_t lg_delta = (x < SC_LG_NGROUP + LG_PAGE + 1) ?
- LG_PAGE : x - SC_LG_NGROUP - 1;
+ /*
+ * Same as sc_s::lg_delta.
+ * Delta for off_to_first_ps_rg == 1 is PAGE,
+ * for each increase in offset, it's multiplied by two.
+ * Therefore, lg_delta = LG_PAGE + (off_to_first_ps_rg - 1).
+ */
+ pszind_t lg_delta = (off_to_first_ps_rg == 0) ?
+ LG_PAGE : LG_PAGE + (off_to_first_ps_rg - 1);
- size_t delta_inverse_mask = ZU(-1) << lg_delta;
- pszind_t mod = ((((psz-1) & delta_inverse_mask) >> lg_delta)) &
- ((ZU(1) << SC_LG_NGROUP) - 1);
+ /*
+ * Let's write psz in binary, e.g. 0011 for 0x3, 0111 for 0x7.
+ * The leftmost bits whose len is lg_base decide the base of psz.
+ * The rightmost bits whose len is lg_delta decide (pgz % PAGE).
+ * The middle bits whose len is SC_LG_NGROUP decide ndelta.
+ * ndelta is offset to the first size class in the size class group,
+ * starts from 1.
+ * If you don't know lg_base, ndelta or lg_delta, see sc.h.
+ * |xxxxxxxxxxxxxxxxxxxx|------------------------|yyyyyyyyyyyyyyyyyyyyy|
+ * |<-- len: lg_base -->|<-- len: SC_LG_NGROUP-->|<-- len: lg_delta -->|
+ * |<-- ndelta -->|
+ * rg_inner_off = ndelta - 1
+ * Why use (psz - 1)?
+ * To handle case: psz % (1 << lg_delta) == 0.
+ */
+ pszind_t rg_inner_off = (((psz - 1)) >> lg_delta) & (SC_NGROUP - 1);
- pszind_t ind = grp + mod;
+ pszind_t base_ind = off_to_first_ps_rg << SC_LG_NGROUP;
+ pszind_t ind = base_ind + rg_inner_off;
return ind;
}
@@ -152,10 +186,15 @@ sz_size2index_compute(size_t size) {
}
JEMALLOC_ALWAYS_INLINE szind_t
-sz_size2index_lookup(size_t size) {
+sz_size2index_lookup_impl(size_t size) {
assert(size <= SC_LOOKUP_MAXCLASS);
- szind_t ret = (sz_size2index_tab[(size + (ZU(1) << SC_LG_TINY_MIN) - 1)
- >> SC_LG_TINY_MIN]);
+ return sz_size2index_tab[(size + (ZU(1) << SC_LG_TINY_MIN) - 1)
+ >> SC_LG_TINY_MIN];
+}
+
+JEMALLOC_ALWAYS_INLINE szind_t
+sz_size2index_lookup(size_t size) {
+ szind_t ret = sz_size2index_lookup_impl(size);
assert(ret == sz_size2index_compute(size));
return ret;
}
@@ -195,8 +234,13 @@ sz_index2size_compute(szind_t index) {
}
JEMALLOC_ALWAYS_INLINE size_t
+sz_index2size_lookup_impl(szind_t index) {
+ return sz_index2size_tab[index];
+}
+
+JEMALLOC_ALWAYS_INLINE size_t
sz_index2size_lookup(szind_t index) {
- size_t ret = (size_t)sz_index2size_tab[index];
+ size_t ret = sz_index2size_lookup_impl(index);
assert(ret == sz_index2size_compute(index));
return ret;
}
@@ -207,6 +251,12 @@ sz_index2size(szind_t index) {
return sz_index2size_lookup(index);
}
+JEMALLOC_ALWAYS_INLINE void
+sz_size2index_usize_fastpath(size_t size, szind_t *ind, size_t *usize) {
+ *ind = sz_size2index_lookup_impl(size);
+ *usize = sz_index2size_lookup_impl(*ind);
+}
+
JEMALLOC_ALWAYS_INLINE size_t
sz_s2u_compute(size_t size) {
if (unlikely(size > SC_LARGE_MAXCLASS)) {
@@ -266,7 +316,7 @@ sz_sa2u(size_t size, size_t alignment) {
assert(alignment != 0 && ((alignment - 1) & alignment) == 0);
/* Try for a small size class. */
- if (size <= SC_SMALL_MAXCLASS && alignment < PAGE) {
+ if (size <= SC_SMALL_MAXCLASS && alignment <= PAGE) {
/*
* Round size up to the nearest multiple of alignment.
*
@@ -315,4 +365,7 @@ sz_sa2u(size_t size, size_t alignment) {
return usize;
}
+size_t sz_psz_quantize_floor(size_t size);
+size_t sz_psz_quantize_ceil(size_t size);
+
#endif /* JEMALLOC_INTERNAL_SIZE_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/tcache_externs.h b/contrib/jemalloc/include/jemalloc/internal/tcache_externs.h
index d63eafde8ce6..a2ab7101b065 100644
--- a/contrib/jemalloc/include/jemalloc/internal/tcache_externs.h
+++ b/contrib/jemalloc/include/jemalloc/internal/tcache_externs.h
@@ -1,10 +1,17 @@
#ifndef JEMALLOC_INTERNAL_TCACHE_EXTERNS_H
#define JEMALLOC_INTERNAL_TCACHE_EXTERNS_H
-extern bool opt_tcache;
-extern ssize_t opt_lg_tcache_max;
-
-extern cache_bin_info_t *tcache_bin_info;
+extern bool opt_tcache;
+extern size_t opt_tcache_max;
+extern ssize_t opt_lg_tcache_nslots_mul;
+extern unsigned opt_tcache_nslots_small_min;
+extern unsigned opt_tcache_nslots_small_max;
+extern unsigned opt_tcache_nslots_large;
+extern ssize_t opt_lg_tcache_shift;
+extern size_t opt_tcache_gc_incr_bytes;
+extern size_t opt_tcache_gc_delay_bytes;
+extern unsigned opt_lg_tcache_flush_small_div;
+extern unsigned opt_lg_tcache_flush_large_div;
/*
* Number of tcache bins. There are SC_NBINS small-object bins, plus 0 or more
@@ -15,6 +22,8 @@ extern unsigned nhbins;
/* Maximum cached size class. */
extern size_t tcache_maxclass;
+extern cache_bin_info_t *tcache_bin_info;
+
/*
* Explicit tcaches, managed via the tcache.{create,flush,destroy} mallctls and
* usable via the MALLOCX_TCACHE() flag. The automatic per thread tcaches are
@@ -25,24 +34,27 @@ extern size_t tcache_maxclass;
*/
extern tcaches_t *tcaches;
-size_t tcache_salloc(tsdn_t *tsdn, const void *ptr);
-void tcache_event_hard(tsd_t *tsd, tcache_t *tcache);
-void *tcache_alloc_small_hard(tsdn_t *tsdn, arena_t *arena, tcache_t *tcache,
+size_t tcache_salloc(tsdn_t *tsdn, const void *ptr);
+void *tcache_alloc_small_hard(tsdn_t *tsdn, arena_t *arena, tcache_t *tcache,
cache_bin_t *tbin, szind_t binind, bool *tcache_success);
-void tcache_bin_flush_small(tsd_t *tsd, tcache_t *tcache, cache_bin_t *tbin,
+
+void tcache_bin_flush_small(tsd_t *tsd, tcache_t *tcache, cache_bin_t *tbin,
szind_t binind, unsigned rem);
-void tcache_bin_flush_large(tsd_t *tsd, cache_bin_t *tbin, szind_t binind,
- unsigned rem, tcache_t *tcache);
-void tcache_arena_reassociate(tsdn_t *tsdn, tcache_t *tcache,
- arena_t *arena);
+void tcache_bin_flush_large(tsd_t *tsd, tcache_t *tcache, cache_bin_t *tbin,
+ szind_t binind, unsigned rem);
+void tcache_bin_flush_stashed(tsd_t *tsd, tcache_t *tcache, cache_bin_t *bin,
+ szind_t binind, bool is_small);
+void tcache_arena_reassociate(tsdn_t *tsdn, tcache_slow_t *tcache_slow,
+ tcache_t *tcache, arena_t *arena);
tcache_t *tcache_create_explicit(tsd_t *tsd);
-void tcache_cleanup(tsd_t *tsd);
-void tcache_stats_merge(tsdn_t *tsdn, tcache_t *tcache, arena_t *arena);
-bool tcaches_create(tsd_t *tsd, unsigned *r_ind);
-void tcaches_flush(tsd_t *tsd, unsigned ind);
-void tcaches_destroy(tsd_t *tsd, unsigned ind);
-bool tcache_boot(tsdn_t *tsdn);
-void tcache_arena_associate(tsdn_t *tsdn, tcache_t *tcache, arena_t *arena);
+void tcache_cleanup(tsd_t *tsd);
+void tcache_stats_merge(tsdn_t *tsdn, tcache_t *tcache, arena_t *arena);
+bool tcaches_create(tsd_t *tsd, base_t *base, unsigned *r_ind);
+void tcaches_flush(tsd_t *tsd, unsigned ind);
+void tcaches_destroy(tsd_t *tsd, unsigned ind);
+bool tcache_boot(tsdn_t *tsdn, base_t *base);
+void tcache_arena_associate(tsdn_t *tsdn, tcache_slow_t *tcache_slow,
+ tcache_t *tcache, arena_t *arena);
void tcache_prefork(tsdn_t *tsdn);
void tcache_postfork_parent(tsdn_t *tsdn);
void tcache_postfork_child(tsdn_t *tsdn);
@@ -50,4 +62,14 @@ void tcache_flush(tsd_t *tsd);
bool tsd_tcache_data_init(tsd_t *tsd);
bool tsd_tcache_enabled_data_init(tsd_t *tsd);
+void tcache_assert_initialized(tcache_t *tcache);
+
+/* Only accessed by thread event. */
+uint64_t tcache_gc_new_event_wait(tsd_t *tsd);
+uint64_t tcache_gc_postponed_event_wait(tsd_t *tsd);
+void tcache_gc_event_handler(tsd_t *tsd, uint64_t elapsed);
+uint64_t tcache_gc_dalloc_new_event_wait(tsd_t *tsd);
+uint64_t tcache_gc_dalloc_postponed_event_wait(tsd_t *tsd);
+void tcache_gc_dalloc_event_handler(tsd_t *tsd, uint64_t elapsed);
+
#endif /* JEMALLOC_INTERNAL_TCACHE_EXTERNS_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/tcache_inlines.h b/contrib/jemalloc/include/jemalloc/internal/tcache_inlines.h
index 5eca20e893b4..2634f145dc39 100644
--- a/contrib/jemalloc/include/jemalloc/internal/tcache_inlines.h
+++ b/contrib/jemalloc/include/jemalloc/internal/tcache_inlines.h
@@ -3,9 +3,9 @@
#include "jemalloc/internal/bin.h"
#include "jemalloc/internal/jemalloc_internal_types.h"
+#include "jemalloc/internal/san.h"
#include "jemalloc/internal/sc.h"
#include "jemalloc/internal/sz.h"
-#include "jemalloc/internal/ticker.h"
#include "jemalloc/internal/util.h"
static inline bool
@@ -27,28 +27,29 @@ tcache_enabled_set(tsd_t *tsd, bool enabled) {
tsd_slow_update(tsd);
}
-JEMALLOC_ALWAYS_INLINE void
-tcache_event(tsd_t *tsd, tcache_t *tcache) {
- if (TCACHE_GC_INCR == 0) {
- return;
+JEMALLOC_ALWAYS_INLINE bool
+tcache_small_bin_disabled(szind_t ind, cache_bin_t *bin) {
+ assert(ind < SC_NBINS);
+ bool ret = (cache_bin_info_ncached_max(&tcache_bin_info[ind]) == 0);
+ if (ret && bin != NULL) {
+ /* small size class but cache bin disabled. */
+ assert(ind >= nhbins);
+ assert((uintptr_t)(*bin->stack_head) ==
+ cache_bin_preceding_junk);
}
- if (unlikely(ticker_tick(&tcache->gc_ticker))) {
- tcache_event_hard(tsd, tcache);
- }
+ return ret;
}
JEMALLOC_ALWAYS_INLINE void *
tcache_alloc_small(tsd_t *tsd, arena_t *arena, tcache_t *tcache,
size_t size, szind_t binind, bool zero, bool slow_path) {
void *ret;
- cache_bin_t *bin;
bool tcache_success;
- size_t usize JEMALLOC_CC_SILENCE_INIT(0);
assert(binind < SC_NBINS);
- bin = tcache_small_bin_get(tcache, binind);
- ret = cache_bin_alloc_easy(bin, &tcache_success);
+ cache_bin_t *bin = &tcache->bins[binind];
+ ret = cache_bin_alloc(bin, &tcache_success);
assert(tcache_success == (ret != NULL));
if (unlikely(!tcache_success)) {
bool tcache_hard_success;
@@ -56,6 +57,13 @@ tcache_alloc_small(tsd_t *tsd, arena_t *arena, tcache_t *tcache,
if (unlikely(arena == NULL)) {
return NULL;
}
+ if (unlikely(tcache_small_bin_disabled(binind, bin))) {
+ /* stats and zero are handled directly by the arena. */
+ return arena_malloc_hard(tsd_tsdn(tsd), arena, size,
+ binind, zero);
+ }
+ tcache_bin_flush_stashed(tsd, tcache, bin, binind,
+ /* is_small */ true);
ret = tcache_alloc_small_hard(tsd_tsdn(tsd), arena, tcache,
bin, binind, &tcache_hard_success);
@@ -65,38 +73,14 @@ tcache_alloc_small(tsd_t *tsd, arena_t *arena, tcache_t *tcache,
}
assert(ret);
- /*
- * Only compute usize if required. The checks in the following if
- * statement are all static.
- */
- if (config_prof || (slow_path && config_fill) || unlikely(zero)) {
- usize = sz_index2size(binind);
+ if (unlikely(zero)) {
+ size_t usize = sz_index2size(binind);
assert(tcache_salloc(tsd_tsdn(tsd), ret) == usize);
- }
-
- if (likely(!zero)) {
- if (slow_path && config_fill) {
- if (unlikely(opt_junk_alloc)) {
- arena_alloc_junk_small(ret, &bin_infos[binind],
- false);
- } else if (unlikely(opt_zero)) {
- memset(ret, 0, usize);
- }
- }
- } else {
- if (slow_path && config_fill && unlikely(opt_junk_alloc)) {
- arena_alloc_junk_small(ret, &bin_infos[binind], true);
- }
memset(ret, 0, usize);
}
-
if (config_stats) {
bin->tstats.nrequests++;
}
- if (config_prof) {
- tcache->prof_accumbytes += usize;
- }
- tcache_event(tsd, tcache);
return ret;
}
@@ -104,12 +88,11 @@ JEMALLOC_ALWAYS_INLINE void *
tcache_alloc_large(tsd_t *tsd, arena_t *arena, tcache_t *tcache, size_t size,
szind_t binind, bool zero, bool slow_path) {
void *ret;
- cache_bin_t *bin;
bool tcache_success;
- assert(binind >= SC_NBINS &&binind < nhbins);
- bin = tcache_large_bin_get(tcache, binind);
- ret = cache_bin_alloc_easy(bin, &tcache_success);
+ assert(binind >= SC_NBINS && binind < nhbins);
+ cache_bin_t *bin = &tcache->bins[binind];
+ ret = cache_bin_alloc(bin, &tcache_success);
assert(tcache_success == (ret != NULL));
if (unlikely(!tcache_success)) {
/*
@@ -120,96 +103,79 @@ tcache_alloc_large(tsd_t *tsd, arena_t *arena, tcache_t *tcache, size_t size,
if (unlikely(arena == NULL)) {
return NULL;
}
+ tcache_bin_flush_stashed(tsd, tcache, bin, binind,
+ /* is_small */ false);
ret = large_malloc(tsd_tsdn(tsd), arena, sz_s2u(size), zero);
if (ret == NULL) {
return NULL;
}
} else {
- size_t usize JEMALLOC_CC_SILENCE_INIT(0);
-
- /* Only compute usize on demand */
- if (config_prof || (slow_path && config_fill) ||
- unlikely(zero)) {
- usize = sz_index2size(binind);
+ if (unlikely(zero)) {
+ size_t usize = sz_index2size(binind);
assert(usize <= tcache_maxclass);
- }
-
- if (likely(!zero)) {
- if (slow_path && config_fill) {
- if (unlikely(opt_junk_alloc)) {
- memset(ret, JEMALLOC_ALLOC_JUNK,
- usize);
- } else if (unlikely(opt_zero)) {
- memset(ret, 0, usize);
- }
- }
- } else {
memset(ret, 0, usize);
}
if (config_stats) {
bin->tstats.nrequests++;
}
- if (config_prof) {
- tcache->prof_accumbytes += usize;
- }
}
- tcache_event(tsd, tcache);
return ret;
}
JEMALLOC_ALWAYS_INLINE void
tcache_dalloc_small(tsd_t *tsd, tcache_t *tcache, void *ptr, szind_t binind,
bool slow_path) {
- cache_bin_t *bin;
- cache_bin_info_t *bin_info;
+ assert(tcache_salloc(tsd_tsdn(tsd), ptr) <= SC_SMALL_MAXCLASS);
- assert(tcache_salloc(tsd_tsdn(tsd), ptr)
- <= SC_SMALL_MAXCLASS);
-
- if (slow_path && config_fill && unlikely(opt_junk_free)) {
- arena_dalloc_junk_small(ptr, &bin_infos[binind]);
+ cache_bin_t *bin = &tcache->bins[binind];
+ /*
+ * Not marking the branch unlikely because this is past free_fastpath()
+ * (which handles the most common cases), i.e. at this point it's often
+ * uncommon cases.
+ */
+ if (cache_bin_nonfast_aligned(ptr)) {
+ /* Junk unconditionally, even if bin is full. */
+ san_junk_ptr(ptr, sz_index2size(binind));
+ if (cache_bin_stash(bin, ptr)) {
+ return;
+ }
+ assert(cache_bin_full(bin));
+ /* Bin full; fall through into the flush branch. */
}
- bin = tcache_small_bin_get(tcache, binind);
- bin_info = &tcache_bin_info[binind];
- if (unlikely(!cache_bin_dalloc_easy(bin, bin_info, ptr))) {
- tcache_bin_flush_small(tsd, tcache, bin, binind,
- (bin_info->ncached_max >> 1));
- bool ret = cache_bin_dalloc_easy(bin, bin_info, ptr);
+ if (unlikely(!cache_bin_dalloc_easy(bin, ptr))) {
+ if (unlikely(tcache_small_bin_disabled(binind, bin))) {
+ arena_dalloc_small(tsd_tsdn(tsd), ptr);
+ return;
+ }
+ cache_bin_sz_t max = cache_bin_info_ncached_max(
+ &tcache_bin_info[binind]);
+ unsigned remain = max >> opt_lg_tcache_flush_small_div;
+ tcache_bin_flush_small(tsd, tcache, bin, binind, remain);
+ bool ret = cache_bin_dalloc_easy(bin, ptr);
assert(ret);
}
-
- tcache_event(tsd, tcache);
}
JEMALLOC_ALWAYS_INLINE void
tcache_dalloc_large(tsd_t *tsd, tcache_t *tcache, void *ptr, szind_t binind,
bool slow_path) {
- cache_bin_t *bin;
- cache_bin_info_t *bin_info;
assert(tcache_salloc(tsd_tsdn(tsd), ptr)
> SC_SMALL_MAXCLASS);
assert(tcache_salloc(tsd_tsdn(tsd), ptr) <= tcache_maxclass);
- if (slow_path && config_fill && unlikely(opt_junk_free)) {
- large_dalloc_junk(ptr, sz_index2size(binind));
- }
-
- bin = tcache_large_bin_get(tcache, binind);
- bin_info = &tcache_bin_info[binind];
- if (unlikely(bin->ncached == bin_info->ncached_max)) {
- tcache_bin_flush_large(tsd, bin, binind,
- (bin_info->ncached_max >> 1), tcache);
+ cache_bin_t *bin = &tcache->bins[binind];
+ if (unlikely(!cache_bin_dalloc_easy(bin, ptr))) {
+ unsigned remain = cache_bin_info_ncached_max(
+ &tcache_bin_info[binind]) >> opt_lg_tcache_flush_large_div;
+ tcache_bin_flush_large(tsd, tcache, bin, binind, remain);
+ bool ret = cache_bin_dalloc_easy(bin, ptr);
+ assert(ret);
}
- assert(bin->ncached < bin_info->ncached_max);
- bin->ncached++;
- *(bin->avail - bin->ncached) = ptr;
-
- tcache_event(tsd, tcache);
}
JEMALLOC_ALWAYS_INLINE tcache_t *
diff --git a/contrib/jemalloc/include/jemalloc/internal/tcache_structs.h b/contrib/jemalloc/include/jemalloc/internal/tcache_structs.h
index 172ef9040c04..176d73de95b7 100644
--- a/contrib/jemalloc/include/jemalloc/internal/tcache_structs.h
+++ b/contrib/jemalloc/include/jemalloc/internal/tcache_structs.h
@@ -7,36 +7,19 @@
#include "jemalloc/internal/ticker.h"
#include "jemalloc/internal/tsd_types.h"
-/* Various uses of this struct need it to be a named type. */
-typedef ql_elm(tsd_t) tsd_link_t;
+/*
+ * The tcache state is split into the slow and hot path data. Each has a
+ * pointer to the other, and the data always comes in pairs. The layout of each
+ * of them varies in practice; tcache_slow lives in the TSD for the automatic
+ * tcache, and as part of a dynamic allocation for manual allocations. Keeping
+ * a pointer to tcache_slow lets us treat these cases uniformly, rather than
+ * splitting up the tcache [de]allocation code into those paths called with the
+ * TSD tcache and those called with a manual tcache.
+ */
-struct tcache_s {
- /*
- * To minimize our cache-footprint, we put the frequently accessed data
- * together at the start of this struct.
- */
-
- /* Cleared after arena_prof_accum(). */
- uint64_t prof_accumbytes;
- /* Drives incremental GC. */
- ticker_t gc_ticker;
- /*
- * The pointer stacks associated with bins follow as a contiguous array.
- * During tcache initialization, the avail pointer in each element of
- * tbins is initialized to point to the proper offset within this array.
- */
- cache_bin_t bins_small[SC_NBINS];
-
- /*
- * This data is less hot; we can be a little less careful with our
- * footprint here.
- */
+struct tcache_slow_s {
/* Lets us track all the tcaches in an arena. */
- ql_elm(tcache_t) link;
-
- /* Logically scoped to tsd, but put here for cache layout reasons. */
- ql_elm(tsd_t) tsd_link;
- bool in_hook;
+ ql_elm(tcache_slow_t) link;
/*
* The descriptor lets the arena find our cache bins without seeing the
@@ -51,12 +34,27 @@ struct tcache_s {
szind_t next_gc_bin;
/* For small bins, fill (ncached_max >> lg_fill_div). */
uint8_t lg_fill_div[SC_NBINS];
+ /* For small bins, whether has been refilled since last GC. */
+ bool bin_refilled[SC_NBINS];
+ /*
+ * For small bins, the number of items we can pretend to flush before
+ * actually flushing.
+ */
+ uint8_t bin_flush_delay_items[SC_NBINS];
/*
- * We put the cache bins for large size classes at the end of the
- * struct, since some of them might not get used. This might end up
- * letting us avoid touching an extra page if we don't have to.
+ * The start of the allocation containing the dynamic allocation for
+ * either the cache bins alone, or the cache bin memory as well as this
+ * tcache_slow_t and its associated tcache_t.
*/
- cache_bin_t bins_large[SC_NSIZES-SC_NBINS];
+ void *dyn_alloc;
+
+ /* The associated bins. */
+ tcache_t *tcache;
+};
+
+struct tcache_s {
+ tcache_slow_t *tcache_slow;
+ cache_bin_t bins[TCACHE_NBINS_MAX];
};
/* Linkage for list of available (previously used) explicit tcache IDs. */
diff --git a/contrib/jemalloc/include/jemalloc/internal/tcache_types.h b/contrib/jemalloc/include/jemalloc/internal/tcache_types.h
index dce69382ebb0..583677ea2d35 100644
--- a/contrib/jemalloc/include/jemalloc/internal/tcache_types.h
+++ b/contrib/jemalloc/include/jemalloc/internal/tcache_types.h
@@ -3,6 +3,7 @@
#include "jemalloc/internal/sc.h"
+typedef struct tcache_slow_s tcache_slow_t;
typedef struct tcache_s tcache_t;
typedef struct tcaches_s tcaches_t;
@@ -16,39 +17,9 @@ typedef struct tcaches_s tcaches_t;
#define TCACHE_STATE_PURGATORY ((tcache_t *)(uintptr_t)3)
#define TCACHE_STATE_MAX TCACHE_STATE_PURGATORY
-/*
- * Absolute minimum number of cache slots for each small bin.
- */
-#define TCACHE_NSLOTS_SMALL_MIN 20
-
-/*
- * Absolute maximum number of cache slots for each small bin in the thread
- * cache. This is an additional constraint beyond that imposed as: twice the
- * number of regions per slab for this size class.
- *
- * This constant must be an even number.
- */
-#define TCACHE_NSLOTS_SMALL_MAX 200
-
-/* Number of cache slots for large size classes. */
-#define TCACHE_NSLOTS_LARGE 20
-
-/* (1U << opt_lg_tcache_max) is used to compute tcache_maxclass. */
-#define LG_TCACHE_MAXCLASS_DEFAULT 15
-
-/*
- * TCACHE_GC_SWEEP is the approximate number of allocation events between
- * full GC sweeps. Integer rounding may cause the actual number to be
- * slightly higher, since GC is performed incrementally.
- */
-#define TCACHE_GC_SWEEP 8192
-
-/* Number of tcache allocation/deallocation events between incremental GCs. */
-#define TCACHE_GC_INCR \
- ((TCACHE_GC_SWEEP / SC_NBINS) + ((TCACHE_GC_SWEEP / SC_NBINS == 0) ? 0 : 1))
-
-/* Used in TSD static initializer only. Real init in tcache_data_init(). */
+/* Used in TSD static initializer only. Real init in tsd_tcache_data_init(). */
#define TCACHE_ZERO_INITIALIZER {0}
+#define TCACHE_SLOW_ZERO_INITIALIZER {0}
/* Used in TSD static initializer only. Will be initialized to opt_tcache. */
#define TCACHE_ENABLED_ZERO_INITIALIZER false
@@ -56,4 +27,9 @@ typedef struct tcaches_s tcaches_t;
/* Used for explicit tcache only. Means flushed but not destroyed. */
#define TCACHES_ELM_NEED_REINIT ((tcache_t *)(uintptr_t)1)
+#define TCACHE_LG_MAXCLASS_LIMIT 23 /* tcache_maxclass = 8M */
+#define TCACHE_MAXCLASS_LIMIT ((size_t)1 << TCACHE_LG_MAXCLASS_LIMIT)
+#define TCACHE_NBINS_MAX (SC_NBINS + SC_NGROUP * \
+ (TCACHE_LG_MAXCLASS_LIMIT - SC_LG_LARGE_MINCLASS) + 1)
+
#endif /* JEMALLOC_INTERNAL_TCACHE_TYPES_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/test_hooks.h b/contrib/jemalloc/include/jemalloc/internal/test_hooks.h
index 0780c52fa270..3d530b5c5756 100644
--- a/contrib/jemalloc/include/jemalloc/internal/test_hooks.h
+++ b/contrib/jemalloc/include/jemalloc/internal/test_hooks.h
@@ -4,9 +4,21 @@
extern JEMALLOC_EXPORT void (*test_hooks_arena_new_hook)();
extern JEMALLOC_EXPORT void (*test_hooks_libc_hook)();
-#define JEMALLOC_HOOK(fn, hook) ((void)(hook != NULL && (hook(), 0)), fn)
+#if defined(JEMALLOC_JET) || defined(JEMALLOC_UNIT_TEST)
+# define JEMALLOC_TEST_HOOK(fn, hook) ((void)(hook != NULL && (hook(), 0)), fn)
+# define open JEMALLOC_TEST_HOOK(open, test_hooks_libc_hook)
+# define read JEMALLOC_TEST_HOOK(read, test_hooks_libc_hook)
+# define write JEMALLOC_TEST_HOOK(write, test_hooks_libc_hook)
+# define readlink JEMALLOC_TEST_HOOK(readlink, test_hooks_libc_hook)
+# define close JEMALLOC_TEST_HOOK(close, test_hooks_libc_hook)
+# define creat JEMALLOC_TEST_HOOK(creat, test_hooks_libc_hook)
+# define secure_getenv JEMALLOC_TEST_HOOK(secure_getenv, test_hooks_libc_hook)
/* Note that this is undef'd and re-define'd in src/prof.c. */
-#define _Unwind_Backtrace JEMALLOC_HOOK(_Unwind_Backtrace, test_hooks_libc_hook)
+# define _Unwind_Backtrace JEMALLOC_TEST_HOOK(_Unwind_Backtrace, test_hooks_libc_hook)
+#else
+# define JEMALLOC_TEST_HOOK(fn, hook) fn
+#endif
+
#endif /* JEMALLOC_INTERNAL_TEST_HOOKS_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/thread_event.h b/contrib/jemalloc/include/jemalloc/internal/thread_event.h
new file mode 100644
index 000000000000..2f4e1b39c7bc
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/internal/thread_event.h
@@ -0,0 +1,301 @@
+#ifndef JEMALLOC_INTERNAL_THREAD_EVENT_H
+#define JEMALLOC_INTERNAL_THREAD_EVENT_H
+
+#include "jemalloc/internal/tsd.h"
+
+/* "te" is short for "thread_event" */
+
+/*
+ * TE_MIN_START_WAIT should not exceed the minimal allocation usize.
+ */
+#define TE_MIN_START_WAIT ((uint64_t)1U)
+#define TE_MAX_START_WAIT UINT64_MAX
+
+/*
+ * Maximum threshold on thread_(de)allocated_next_event_fast, so that there is
+ * no need to check overflow in malloc fast path. (The allocation size in malloc
+ * fast path never exceeds SC_LOOKUP_MAXCLASS.)
+ */
+#define TE_NEXT_EVENT_FAST_MAX (UINT64_MAX - SC_LOOKUP_MAXCLASS + 1U)
+
+/*
+ * The max interval helps make sure that malloc stays on the fast path in the
+ * common case, i.e. thread_allocated < thread_allocated_next_event_fast. When
+ * thread_allocated is within an event's distance to TE_NEXT_EVENT_FAST_MAX
+ * above, thread_allocated_next_event_fast is wrapped around and we fall back to
+ * the medium-fast path. The max interval makes sure that we're not staying on
+ * the fallback case for too long, even if there's no active event or if all
+ * active events have long wait times.
+ */
+#define TE_MAX_INTERVAL ((uint64_t)(4U << 20))
+
+/*
+ * Invalid elapsed time, for situations where elapsed time is not needed. See
+ * comments in thread_event.c for more info.
+ */
+#define TE_INVALID_ELAPSED UINT64_MAX
+
+typedef struct te_ctx_s {
+ bool is_alloc;
+ uint64_t *current;
+ uint64_t *last_event;
+ uint64_t *next_event;
+ uint64_t *next_event_fast;
+} te_ctx_t;
+
+void te_assert_invariants_debug(tsd_t *tsd);
+void te_event_trigger(tsd_t *tsd, te_ctx_t *ctx);
+void te_recompute_fast_threshold(tsd_t *tsd);
+void tsd_te_init(tsd_t *tsd);
+
+/*
+ * List of all events, in the following format:
+ * E(event, (condition), is_alloc_event)
+ */
+#define ITERATE_OVER_ALL_EVENTS \
+ E(tcache_gc, (opt_tcache_gc_incr_bytes > 0), true) \
+ E(prof_sample, (config_prof && opt_prof), true) \
+ E(stats_interval, (opt_stats_interval >= 0), true) \
+ E(tcache_gc_dalloc, (opt_tcache_gc_incr_bytes > 0), false) \
+ E(peak_alloc, config_stats, true) \
+ E(peak_dalloc, config_stats, false)
+
+#define E(event, condition_unused, is_alloc_event_unused) \
+ C(event##_event_wait)
+
+/* List of all thread event counters. */
+#define ITERATE_OVER_ALL_COUNTERS \
+ C(thread_allocated) \
+ C(thread_allocated_last_event) \
+ ITERATE_OVER_ALL_EVENTS \
+ C(prof_sample_last_event) \
+ C(stats_interval_last_event)
+
+/* Getters directly wrap TSD getters. */
+#define C(counter) \
+JEMALLOC_ALWAYS_INLINE uint64_t \
+counter##_get(tsd_t *tsd) { \
+ return tsd_##counter##_get(tsd); \
+}
+
+ITERATE_OVER_ALL_COUNTERS
+#undef C
+
+/*
+ * Setters call the TSD pointer getters rather than the TSD setters, so that
+ * the counters can be modified even when TSD state is reincarnated or
+ * minimal_initialized: if an event is triggered in such cases, we will
+ * temporarily delay the event and let it be immediately triggered at the next
+ * allocation call.
+ */
+#define C(counter) \
+JEMALLOC_ALWAYS_INLINE void \
+counter##_set(tsd_t *tsd, uint64_t v) { \
+ *tsd_##counter##p_get(tsd) = v; \
+}
+
+ITERATE_OVER_ALL_COUNTERS
+#undef C
+
+/*
+ * For generating _event_wait getter / setter functions for each individual
+ * event.
+ */
+#undef E
+
+/*
+ * The malloc and free fastpath getters -- use the unsafe getters since tsd may
+ * be non-nominal, in which case the fast_threshold will be set to 0. This
+ * allows checking for events and tsd non-nominal in a single branch.
+ *
+ * Note that these can only be used on the fastpath.
+ */
+JEMALLOC_ALWAYS_INLINE void
+te_malloc_fastpath_ctx(tsd_t *tsd, uint64_t *allocated, uint64_t *threshold) {
+ *allocated = *tsd_thread_allocatedp_get_unsafe(tsd);
+ *threshold = *tsd_thread_allocated_next_event_fastp_get_unsafe(tsd);
+ assert(*threshold <= TE_NEXT_EVENT_FAST_MAX);
+}
+
+JEMALLOC_ALWAYS_INLINE void
+te_free_fastpath_ctx(tsd_t *tsd, uint64_t *deallocated, uint64_t *threshold) {
+ /* Unsafe getters since this may happen before tsd_init. */
+ *deallocated = *tsd_thread_deallocatedp_get_unsafe(tsd);
+ *threshold = *tsd_thread_deallocated_next_event_fastp_get_unsafe(tsd);
+ assert(*threshold <= TE_NEXT_EVENT_FAST_MAX);
+}
+
+JEMALLOC_ALWAYS_INLINE bool
+te_ctx_is_alloc(te_ctx_t *ctx) {
+ return ctx->is_alloc;
+}
+
+JEMALLOC_ALWAYS_INLINE uint64_t
+te_ctx_current_bytes_get(te_ctx_t *ctx) {
+ return *ctx->current;
+}
+
+JEMALLOC_ALWAYS_INLINE void
+te_ctx_current_bytes_set(te_ctx_t *ctx, uint64_t v) {
+ *ctx->current = v;
+}
+
+JEMALLOC_ALWAYS_INLINE uint64_t
+te_ctx_last_event_get(te_ctx_t *ctx) {
+ return *ctx->last_event;
+}
+
+JEMALLOC_ALWAYS_INLINE void
+te_ctx_last_event_set(te_ctx_t *ctx, uint64_t v) {
+ *ctx->last_event = v;
+}
+
+/* Below 3 for next_event_fast. */
+JEMALLOC_ALWAYS_INLINE uint64_t
+te_ctx_next_event_fast_get(te_ctx_t *ctx) {
+ uint64_t v = *ctx->next_event_fast;
+ assert(v <= TE_NEXT_EVENT_FAST_MAX);
+ return v;
+}
+
+JEMALLOC_ALWAYS_INLINE void
+te_ctx_next_event_fast_set(te_ctx_t *ctx, uint64_t v) {
+ assert(v <= TE_NEXT_EVENT_FAST_MAX);
+ *ctx->next_event_fast = v;
+}
+
+JEMALLOC_ALWAYS_INLINE void
+te_next_event_fast_set_non_nominal(tsd_t *tsd) {
+ /*
+ * Set the fast thresholds to zero when tsd is non-nominal. Use the
+ * unsafe getter as this may get called during tsd init and clean up.
+ */
+ *tsd_thread_allocated_next_event_fastp_get_unsafe(tsd) = 0;
+ *tsd_thread_deallocated_next_event_fastp_get_unsafe(tsd) = 0;
+}
+
+/* For next_event. Setter also updates the fast threshold. */
+JEMALLOC_ALWAYS_INLINE uint64_t
+te_ctx_next_event_get(te_ctx_t *ctx) {
+ return *ctx->next_event;
+}
+
+JEMALLOC_ALWAYS_INLINE void
+te_ctx_next_event_set(tsd_t *tsd, te_ctx_t *ctx, uint64_t v) {
+ *ctx->next_event = v;
+ te_recompute_fast_threshold(tsd);
+}
+
+/*
+ * The function checks in debug mode whether the thread event counters are in
+ * a consistent state, which forms the invariants before and after each round
+ * of thread event handling that we can rely on and need to promise.
+ * The invariants are only temporarily violated in the middle of
+ * te_event_advance() if an event is triggered (the te_event_trigger() call at
+ * the end will restore the invariants).
+ */
+JEMALLOC_ALWAYS_INLINE void
+te_assert_invariants(tsd_t *tsd) {
+ if (config_debug) {
+ te_assert_invariants_debug(tsd);
+ }
+}
+
+JEMALLOC_ALWAYS_INLINE void
+te_ctx_get(tsd_t *tsd, te_ctx_t *ctx, bool is_alloc) {
+ ctx->is_alloc = is_alloc;
+ if (is_alloc) {
+ ctx->current = tsd_thread_allocatedp_get(tsd);
+ ctx->last_event = tsd_thread_allocated_last_eventp_get(tsd);
+ ctx->next_event = tsd_thread_allocated_next_eventp_get(tsd);
+ ctx->next_event_fast =
+ tsd_thread_allocated_next_event_fastp_get(tsd);
+ } else {
+ ctx->current = tsd_thread_deallocatedp_get(tsd);
+ ctx->last_event = tsd_thread_deallocated_last_eventp_get(tsd);
+ ctx->next_event = tsd_thread_deallocated_next_eventp_get(tsd);
+ ctx->next_event_fast =
+ tsd_thread_deallocated_next_event_fastp_get(tsd);
+ }
+}
+
+/*
+ * The lookahead functionality facilitates events to be able to lookahead, i.e.
+ * without touching the event counters, to determine whether an event would be
+ * triggered. The event counters are not advanced until the end of the
+ * allocation / deallocation calls, so the lookahead can be useful if some
+ * preparation work for some event must be done early in the allocation /
+ * deallocation calls.
+ *
+ * Currently only the profiling sampling event needs the lookahead
+ * functionality, so we don't yet define general purpose lookahead functions.
+ *
+ * Surplus is a terminology referring to the amount of bytes beyond what's
+ * needed for triggering an event, which can be a useful quantity to have in
+ * general when lookahead is being called.
+ */
+
+JEMALLOC_ALWAYS_INLINE bool
+te_prof_sample_event_lookahead_surplus(tsd_t *tsd, size_t usize,
+ size_t *surplus) {
+ if (surplus != NULL) {
+ /*
+ * This is a dead store: the surplus will be overwritten before
+ * any read. The initialization suppresses compiler warnings.
+ * Meanwhile, using SIZE_MAX to initialize is good for
+ * debugging purpose, because a valid surplus value is strictly
+ * less than usize, which is at most SIZE_MAX.
+ */
+ *surplus = SIZE_MAX;
+ }
+ if (unlikely(!tsd_nominal(tsd) || tsd_reentrancy_level_get(tsd) > 0)) {
+ return false;
+ }
+ /* The subtraction is intentionally susceptible to underflow. */
+ uint64_t accumbytes = tsd_thread_allocated_get(tsd) + usize -
+ tsd_thread_allocated_last_event_get(tsd);
+ uint64_t sample_wait = tsd_prof_sample_event_wait_get(tsd);
+ if (accumbytes < sample_wait) {
+ return false;
+ }
+ assert(accumbytes - sample_wait < (uint64_t)usize);
+ if (surplus != NULL) {
+ *surplus = (size_t)(accumbytes - sample_wait);
+ }
+ return true;
+}
+
+JEMALLOC_ALWAYS_INLINE bool
+te_prof_sample_event_lookahead(tsd_t *tsd, size_t usize) {
+ return te_prof_sample_event_lookahead_surplus(tsd, usize, NULL);
+}
+
+JEMALLOC_ALWAYS_INLINE void
+te_event_advance(tsd_t *tsd, size_t usize, bool is_alloc) {
+ te_assert_invariants(tsd);
+
+ te_ctx_t ctx;
+ te_ctx_get(tsd, &ctx, is_alloc);
+
+ uint64_t bytes_before = te_ctx_current_bytes_get(&ctx);
+ te_ctx_current_bytes_set(&ctx, bytes_before + usize);
+
+ /* The subtraction is intentionally susceptible to underflow. */
+ if (likely(usize < te_ctx_next_event_get(&ctx) - bytes_before)) {
+ te_assert_invariants(tsd);
+ } else {
+ te_event_trigger(tsd, &ctx);
+ }
+}
+
+JEMALLOC_ALWAYS_INLINE void
+thread_dalloc_event(tsd_t *tsd, size_t usize) {
+ te_event_advance(tsd, usize, false);
+}
+
+JEMALLOC_ALWAYS_INLINE void
+thread_alloc_event(tsd_t *tsd, size_t usize) {
+ te_event_advance(tsd, usize, true);
+}
+
+#endif /* JEMALLOC_INTERNAL_THREAD_EVENT_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/ticker.h b/contrib/jemalloc/include/jemalloc/internal/ticker.h
index 52d0db4c89c6..6b51ddec43bf 100644
--- a/contrib/jemalloc/include/jemalloc/internal/ticker.h
+++ b/contrib/jemalloc/include/jemalloc/internal/ticker.h
@@ -1,6 +1,7 @@
#ifndef JEMALLOC_INTERNAL_TICKER_H
#define JEMALLOC_INTERNAL_TICKER_H
+#include "jemalloc/internal/prng.h"
#include "jemalloc/internal/util.h"
/**
@@ -10,11 +11,11 @@
* have occurred with a call to ticker_ticks), which will return true (and reset
* the counter) if the countdown hit zero.
*/
-
-typedef struct {
+typedef struct ticker_s ticker_t;
+struct ticker_s {
int32_t tick;
int32_t nticks;
-} ticker_t;
+};
static inline void
ticker_init(ticker_t *ticker, int32_t nticks) {
@@ -75,7 +76,7 @@ ticker_tick(ticker_t *ticker) {
return ticker_ticks(ticker, 1);
}
-/*
+/*
* Try to tick. If ticker would fire, return true, but rely on
* slowpath to reset ticker.
*/
@@ -88,4 +89,87 @@ ticker_trytick(ticker_t *ticker) {
return false;
}
+/*
+ * The ticker_geom_t is much like the ticker_t, except that instead of ticker
+ * having a constant countdown, it has an approximate one; each tick has
+ * approximately a 1/nticks chance of triggering the count.
+ *
+ * The motivation is in triggering arena decay. With a naive strategy, each
+ * thread would maintain a ticker per arena, and check if decay is necessary
+ * each time that the arena's ticker fires. This has two costs:
+ * - Since under reasonable assumptions both threads and arenas can scale
+ * linearly with the number of CPUs, maintaining per-arena data in each thread
+ * scales quadratically with the number of CPUs.
+ * - These tickers are often a cache miss down tcache flush pathways.
+ *
+ * By giving each tick a 1/nticks chance of firing, we still maintain the same
+ * average number of ticks-until-firing per arena, with only a single ticker's
+ * worth of metadata.
+ */
+
+/* See ticker.c for an explanation of these constants. */
+#define TICKER_GEOM_NBITS 6
+#define TICKER_GEOM_MUL 61
+extern const uint8_t ticker_geom_table[1 << TICKER_GEOM_NBITS];
+
+/* Not actually any different from ticker_t; just for type safety. */
+typedef struct ticker_geom_s ticker_geom_t;
+struct ticker_geom_s {
+ int32_t tick;
+ int32_t nticks;
+};
+
+/*
+ * Just pick the average delay for the first counter. We're more concerned with
+ * the behavior over long periods of time rather than the exact timing of the
+ * initial ticks.
+ */
+#define TICKER_GEOM_INIT(nticks) {nticks, nticks}
+
+static inline void
+ticker_geom_init(ticker_geom_t *ticker, int32_t nticks) {
+ /*
+ * Make sure there's no overflow possible. This shouldn't really be a
+ * problem for reasonable nticks choices, which are all static and
+ * relatively small.
+ */
+ assert((uint64_t)nticks * (uint64_t)255 / (uint64_t)TICKER_GEOM_MUL
+ <= (uint64_t)INT32_MAX);
+ ticker->tick = nticks;
+ ticker->nticks = nticks;
+}
+
+static inline int32_t
+ticker_geom_read(const ticker_geom_t *ticker) {
+ return ticker->tick;
+}
+
+/* Same deal as above. */
+#if defined(__GNUC__) && !defined(__clang__) \
+ && (defined(__x86_64__) || defined(__i386__))
+JEMALLOC_NOINLINE
+#endif
+static bool
+ticker_geom_fixup(ticker_geom_t *ticker, uint64_t *prng_state) {
+ uint64_t idx = prng_lg_range_u64(prng_state, TICKER_GEOM_NBITS);
+ ticker->tick = (uint32_t)(
+ (uint64_t)ticker->nticks * (uint64_t)ticker_geom_table[idx]
+ / (uint64_t)TICKER_GEOM_MUL);
+ return true;
+}
+
+static inline bool
+ticker_geom_ticks(ticker_geom_t *ticker, uint64_t *prng_state, int32_t nticks) {
+ ticker->tick -= nticks;
+ if (unlikely(ticker->tick < 0)) {
+ return ticker_geom_fixup(ticker, prng_state);
+ }
+ return false;
+}
+
+static inline bool
+ticker_geom_tick(ticker_geom_t *ticker, uint64_t *prng_state) {
+ return ticker_geom_ticks(ticker, prng_state, 1);
+}
+
#endif /* JEMALLOC_INTERNAL_TICKER_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/tsd.h b/contrib/jemalloc/include/jemalloc/internal/tsd.h
index ecfda5d60df1..6cd52aeee962 100644
--- a/contrib/jemalloc/include/jemalloc/internal/tsd.h
+++ b/contrib/jemalloc/include/jemalloc/internal/tsd.h
@@ -1,10 +1,12 @@
#ifndef JEMALLOC_INTERNAL_TSD_H
#define JEMALLOC_INTERNAL_TSD_H
+#include "jemalloc/internal/activity_callback.h"
#include "jemalloc/internal/arena_types.h"
#include "jemalloc/internal/assert.h"
#include "jemalloc/internal/bin_types.h"
#include "jemalloc/internal/jemalloc_internal_externs.h"
+#include "jemalloc/internal/peak.h"
#include "jemalloc/internal/prof_types.h"
#include "jemalloc/internal/ql.h"
#include "jemalloc/internal/rtree_tsd.h"
@@ -15,39 +17,30 @@
/*
* Thread-Specific-Data layout
- * --- data accessed on tcache fast path: state, rtree_ctx, stats, prof ---
- * s: state
- * e: tcache_enabled
- * m: thread_allocated (config_stats)
- * f: thread_deallocated (config_stats)
- * p: prof_tdata (config_prof)
- * c: rtree_ctx (rtree cache accessed on deallocation)
- * t: tcache
- * --- data not accessed on tcache fast path: arena-related fields ---
- * d: arenas_tdata_bypass
- * r: reentrancy_level
- * x: narenas_tdata
- * i: iarena
- * a: arena
- * o: arenas_tdata
- * Loading TSD data is on the critical path of basically all malloc operations.
- * In particular, tcache and rtree_ctx rely on hot CPU cache to be effective.
- * Use a compact layout to reduce cache footprint.
- * +--- 64-bit and 64B cacheline; 1B each letter; First byte on the left. ---+
- * |---------------------------- 1st cacheline ----------------------------|
- * | sedrxxxx mmmmmmmm ffffffff pppppppp [c * 32 ........ ........ .......] |
- * |---------------------------- 2nd cacheline ----------------------------|
- * | [c * 64 ........ ........ ........ ........ ........ ........ .......] |
- * |---------------------------- 3nd cacheline ----------------------------|
- * | [c * 32 ........ ........ .......] iiiiiiii aaaaaaaa oooooooo [t...... |
- * +-------------------------------------------------------------------------+
- * Note: the entire tcache is embedded into TSD and spans multiple cachelines.
*
- * The last 3 members (i, a and o) before tcache isn't really needed on tcache
- * fast path. However we have a number of unused tcache bins and witnesses
- * (never touched unless config_debug) at the end of tcache, so we place them
- * there to avoid breaking the cachelines and possibly paging in an extra page.
+ * At least some thread-local data gets touched on the fast-path of almost all
+ * malloc operations. But much of it is only necessary down slow-paths, or
+ * testing. We want to colocate the fast-path data so that it can live on the
+ * same cacheline if possible. So we define three tiers of hotness:
+ * TSD_DATA_FAST: Touched on the alloc/dalloc fast paths.
+ * TSD_DATA_SLOW: Touched down slow paths. "Slow" here is sort of general;
+ * there are "semi-slow" paths like "not a sized deallocation, but can still
+ * live in the tcache". We'll want to keep these closer to the fast-path
+ * data.
+ * TSD_DATA_SLOWER: Only touched in test or debug modes, or not touched at all.
+ *
+ * An additional concern is that the larger tcache bins won't be used (we have a
+ * bin per size class, but by default only cache relatively small objects). So
+ * the earlier bins are in the TSD_DATA_FAST tier, but the later ones are in the
+ * TSD_DATA_SLOWER tier.
+ *
+ * As a result of all this, we put the slow data first, then the fast data, then
+ * the slower data, while keeping the tcache as the last element of the fast
+ * data (so that the fast -> slower transition happens midway through the
+ * tcache). While we don't yet play alignment tricks to guarantee it, this
+ * increases our odds of getting some cache/page locality on fast paths.
*/
+
#ifdef JEMALLOC_JET
typedef void (*test_callback_t)(int *);
# define MALLOC_TSD_TEST_DATA_INIT 0x72b65c10
@@ -60,50 +53,112 @@ typedef void (*test_callback_t)(int *);
# define MALLOC_TEST_TSD_INITIALIZER
#endif
-/* O(name, type, nullable type */
-#define MALLOC_TSD \
+typedef ql_elm(tsd_t) tsd_link_t;
+
+/* O(name, type, nullable type) */
+#define TSD_DATA_SLOW \
O(tcache_enabled, bool, bool) \
- O(arenas_tdata_bypass, bool, bool) \
O(reentrancy_level, int8_t, int8_t) \
- O(narenas_tdata, uint32_t, uint32_t) \
- O(offset_state, uint64_t, uint64_t) \
- O(thread_allocated, uint64_t, uint64_t) \
- O(thread_deallocated, uint64_t, uint64_t) \
- O(bytes_until_sample, int64_t, int64_t) \
+ O(thread_allocated_last_event, uint64_t, uint64_t) \
+ O(thread_allocated_next_event, uint64_t, uint64_t) \
+ O(thread_deallocated_last_event, uint64_t, uint64_t) \
+ O(thread_deallocated_next_event, uint64_t, uint64_t) \
+ O(tcache_gc_event_wait, uint64_t, uint64_t) \
+ O(tcache_gc_dalloc_event_wait, uint64_t, uint64_t) \
+ O(prof_sample_event_wait, uint64_t, uint64_t) \
+ O(prof_sample_last_event, uint64_t, uint64_t) \
+ O(stats_interval_event_wait, uint64_t, uint64_t) \
+ O(stats_interval_last_event, uint64_t, uint64_t) \
+ O(peak_alloc_event_wait, uint64_t, uint64_t) \
+ O(peak_dalloc_event_wait, uint64_t, uint64_t) \
O(prof_tdata, prof_tdata_t *, prof_tdata_t *) \
- O(rtree_ctx, rtree_ctx_t, rtree_ctx_t) \
+ O(prng_state, uint64_t, uint64_t) \
+ O(san_extents_until_guard_small, uint64_t, uint64_t) \
+ O(san_extents_until_guard_large, uint64_t, uint64_t) \
O(iarena, arena_t *, arena_t *) \
O(arena, arena_t *, arena_t *) \
- O(arenas_tdata, arena_tdata_t *, arena_tdata_t *)\
+ O(arena_decay_ticker, ticker_geom_t, ticker_geom_t) \
+ O(sec_shard, uint8_t, uint8_t) \
O(binshards, tsd_binshards_t, tsd_binshards_t)\
- O(tcache, tcache_t, tcache_t) \
+ O(tsd_link, tsd_link_t, tsd_link_t) \
+ O(in_hook, bool, bool) \
+ O(peak, peak_t, peak_t) \
+ O(activity_callback_thunk, activity_callback_thunk_t, \
+ activity_callback_thunk_t) \
+ O(tcache_slow, tcache_slow_t, tcache_slow_t) \
+ O(rtree_ctx, rtree_ctx_t, rtree_ctx_t)
+
+#define TSD_DATA_SLOW_INITIALIZER \
+ /* tcache_enabled */ TCACHE_ENABLED_ZERO_INITIALIZER, \
+ /* reentrancy_level */ 0, \
+ /* thread_allocated_last_event */ 0, \
+ /* thread_allocated_next_event */ 0, \
+ /* thread_deallocated_last_event */ 0, \
+ /* thread_deallocated_next_event */ 0, \
+ /* tcache_gc_event_wait */ 0, \
+ /* tcache_gc_dalloc_event_wait */ 0, \
+ /* prof_sample_event_wait */ 0, \
+ /* prof_sample_last_event */ 0, \
+ /* stats_interval_event_wait */ 0, \
+ /* stats_interval_last_event */ 0, \
+ /* peak_alloc_event_wait */ 0, \
+ /* peak_dalloc_event_wait */ 0, \
+ /* prof_tdata */ NULL, \
+ /* prng_state */ 0, \
+ /* san_extents_until_guard_small */ 0, \
+ /* san_extents_until_guard_large */ 0, \
+ /* iarena */ NULL, \
+ /* arena */ NULL, \
+ /* arena_decay_ticker */ \
+ TICKER_GEOM_INIT(ARENA_DECAY_NTICKS_PER_UPDATE), \
+ /* sec_shard */ (uint8_t)-1, \
+ /* binshards */ TSD_BINSHARDS_ZERO_INITIALIZER, \
+ /* tsd_link */ {NULL}, \
+ /* in_hook */ false, \
+ /* peak */ PEAK_INITIALIZER, \
+ /* activity_callback_thunk */ \
+ ACTIVITY_CALLBACK_THUNK_INITIALIZER, \
+ /* tcache_slow */ TCACHE_SLOW_ZERO_INITIALIZER, \
+ /* rtree_ctx */ RTREE_CTX_INITIALIZER,
+
+/* O(name, type, nullable type) */
+#define TSD_DATA_FAST \
+ O(thread_allocated, uint64_t, uint64_t) \
+ O(thread_allocated_next_event_fast, uint64_t, uint64_t) \
+ O(thread_deallocated, uint64_t, uint64_t) \
+ O(thread_deallocated_next_event_fast, uint64_t, uint64_t) \
+ O(tcache, tcache_t, tcache_t)
+
+#define TSD_DATA_FAST_INITIALIZER \
+ /* thread_allocated */ 0, \
+ /* thread_allocated_next_event_fast */ 0, \
+ /* thread_deallocated */ 0, \
+ /* thread_deallocated_next_event_fast */ 0, \
+ /* tcache */ TCACHE_ZERO_INITIALIZER,
+
+/* O(name, type, nullable type) */
+#define TSD_DATA_SLOWER \
O(witness_tsd, witness_tsd_t, witness_tsdn_t) \
MALLOC_TEST_TSD
+#define TSD_DATA_SLOWER_INITIALIZER \
+ /* witness */ WITNESS_TSD_INITIALIZER \
+ /* test data */ MALLOC_TEST_TSD_INITIALIZER
+
+
#define TSD_INITIALIZER { \
- ATOMIC_INIT(tsd_state_uninitialized), \
- TCACHE_ENABLED_ZERO_INITIALIZER, \
- false, \
- 0, \
- 0, \
- 0, \
- 0, \
- 0, \
- 0, \
- NULL, \
- RTREE_CTX_ZERO_INITIALIZER, \
- NULL, \
- NULL, \
- NULL, \
- TSD_BINSHARDS_ZERO_INITIALIZER, \
- TCACHE_ZERO_INITIALIZER, \
- WITNESS_TSD_INITIALIZER \
- MALLOC_TEST_TSD_INITIALIZER \
+ TSD_DATA_SLOW_INITIALIZER \
+ /* state */ ATOMIC_INIT(tsd_state_uninitialized), \
+ TSD_DATA_FAST_INITIALIZER \
+ TSD_DATA_SLOWER_INITIALIZER \
}
+#if defined(JEMALLOC_MALLOC_THREAD_CLEANUP) || defined(_WIN32)
+void _malloc_tsd_cleanup_register(bool (*f)(void));
+#endif
+
void *malloc_tsd_malloc(size_t size);
void malloc_tsd_dalloc(void *wrapper);
-void malloc_tsd_cleanup_register(bool (*f)(void));
tsd_t *malloc_tsd_boot0(void);
void malloc_tsd_boot1(void);
void tsd_cleanup(void *arg);
@@ -189,14 +244,17 @@ struct tsd_s {
* setters below.
*/
+#define O(n, t, nt) \
+ t TSD_MANGLE(n);
+
+ TSD_DATA_SLOW
/*
* We manually limit the state to just a single byte. Unless the 8-bit
* atomics are unavailable (which is rare).
*/
tsd_state_t state;
-#define O(n, t, nt) \
- t TSD_MANGLE(n);
-MALLOC_TSD
+ TSD_DATA_FAST
+ TSD_DATA_SLOWER
#undef O
/* AddressSanitizer requires TLS data to be aligned to at least 8 bytes. */
} JEMALLOC_ALIGNED(16);
@@ -263,7 +321,9 @@ JEMALLOC_ALWAYS_INLINE t * \
tsd_##n##p_get_unsafe(tsd_t *tsd) { \
return &tsd->TSD_MANGLE(n); \
}
-MALLOC_TSD
+TSD_DATA_SLOW
+TSD_DATA_FAST
+TSD_DATA_SLOWER
#undef O
/* tsd_foop_get(tsd) returns a pointer to the thread-local instance of foo. */
@@ -282,7 +342,9 @@ tsd_##n##p_get(tsd_t *tsd) { \
state == tsd_state_minimal_initialized); \
return tsd_##n##p_get_unsafe(tsd); \
}
-MALLOC_TSD
+TSD_DATA_SLOW
+TSD_DATA_FAST
+TSD_DATA_SLOWER
#undef O
/*
@@ -298,7 +360,9 @@ tsdn_##n##p_get(tsdn_t *tsdn) { \
tsd_t *tsd = tsdn_tsd(tsdn); \
return (nt *)tsd_##n##p_get(tsd); \
}
-MALLOC_TSD
+TSD_DATA_SLOW
+TSD_DATA_FAST
+TSD_DATA_SLOWER
#undef O
/* tsd_foo_get(tsd) returns the value of the thread-local instance of foo. */
@@ -307,7 +371,9 @@ JEMALLOC_ALWAYS_INLINE t \
tsd_##n##_get(tsd_t *tsd) { \
return *tsd_##n##p_get(tsd); \
}
-MALLOC_TSD
+TSD_DATA_SLOW
+TSD_DATA_FAST
+TSD_DATA_SLOWER
#undef O
/* tsd_foo_set(tsd, val) updates the thread-local instance of foo to be val. */
@@ -318,7 +384,9 @@ tsd_##n##_set(tsd_t *tsd, t val) { \
tsd_state_get(tsd) != tsd_state_minimal_initialized); \
*tsd_##n##p_get(tsd) = val; \
}
-MALLOC_TSD
+TSD_DATA_SLOW
+TSD_DATA_FAST
+TSD_DATA_SLOWER
#undef O
JEMALLOC_ALWAYS_INLINE void
@@ -383,7 +451,10 @@ tsd_fetch(void) {
static inline bool
tsd_nominal(tsd_t *tsd) {
- return (tsd_state_get(tsd) <= tsd_state_nominal_max);
+ bool nominal = tsd_state_get(tsd) <= tsd_state_nominal_max;
+ assert(nominal || tsd_reentrancy_level_get(tsd) > 0);
+
+ return nominal;
}
JEMALLOC_ALWAYS_INLINE tsdn_t *
@@ -413,4 +484,36 @@ tsdn_rtree_ctx(tsdn_t *tsdn, rtree_ctx_t *fallback) {
return tsd_rtree_ctx(tsdn_tsd(tsdn));
}
+static inline bool
+tsd_state_nocleanup(tsd_t *tsd) {
+ return tsd_state_get(tsd) == tsd_state_reincarnated ||
+ tsd_state_get(tsd) == tsd_state_minimal_initialized;
+}
+
+/*
+ * These "raw" tsd reentrancy functions don't have any debug checking to make
+ * sure that we're not touching arena 0. Better is to call pre_reentrancy and
+ * post_reentrancy if this is possible.
+ */
+static inline void
+tsd_pre_reentrancy_raw(tsd_t *tsd) {
+ bool fast = tsd_fast(tsd);
+ assert(tsd_reentrancy_level_get(tsd) < INT8_MAX);
+ ++*tsd_reentrancy_levelp_get(tsd);
+ if (fast) {
+ /* Prepare slow path for reentrancy. */
+ tsd_slow_update(tsd);
+ assert(tsd_state_get(tsd) == tsd_state_nominal_slow);
+ }
+}
+
+static inline void
+tsd_post_reentrancy_raw(tsd_t *tsd) {
+ int8_t *reentrancy_level = tsd_reentrancy_levelp_get(tsd);
+ assert(*reentrancy_level > 0);
+ if (--*reentrancy_level == 0) {
+ tsd_slow_update(tsd);
+ }
+}
+
#endif /* JEMALLOC_INTERNAL_TSD_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/tsd_generic.h b/contrib/jemalloc/include/jemalloc/internal/tsd_generic.h
index cf73c0c71553..a718472f32ef 100644
--- a/contrib/jemalloc/include/jemalloc/internal/tsd_generic.h
+++ b/contrib/jemalloc/include/jemalloc/internal/tsd_generic.h
@@ -52,6 +52,9 @@ tsd_cleanup_wrapper(void *arg) {
JEMALLOC_ALWAYS_INLINE void
tsd_wrapper_set(tsd_wrapper_t *wrapper) {
+ if (unlikely(!tsd_booted)) {
+ return;
+ }
if (pthread_setspecific(tsd_tsd, (void *)wrapper) != 0) {
malloc_write("<jemalloc>: Error setting TSD\n");
abort();
@@ -60,7 +63,13 @@ tsd_wrapper_set(tsd_wrapper_t *wrapper) {
JEMALLOC_ALWAYS_INLINE tsd_wrapper_t *
tsd_wrapper_get(bool init) {
- tsd_wrapper_t *wrapper = (tsd_wrapper_t *)pthread_getspecific(tsd_tsd);
+ tsd_wrapper_t *wrapper;
+
+ if (unlikely(!tsd_booted)) {
+ return &tsd_boot_wrapper;
+ }
+
+ wrapper = (tsd_wrapper_t *)pthread_getspecific(tsd_tsd);
if (init && unlikely(wrapper == NULL)) {
tsd_init_block_t block;
@@ -91,11 +100,21 @@ tsd_wrapper_get(bool init) {
JEMALLOC_ALWAYS_INLINE bool
tsd_boot0(void) {
+ tsd_wrapper_t *wrapper;
+ tsd_init_block_t block;
+
+ wrapper = (tsd_wrapper_t *)
+ tsd_init_check_recursion(&tsd_init_head, &block);
+ if (wrapper) {
+ return false;
+ }
+ block.data = &tsd_boot_wrapper;
if (pthread_key_create(&tsd_tsd, tsd_cleanup_wrapper) != 0) {
return true;
}
- tsd_wrapper_set(&tsd_boot_wrapper);
tsd_booted = true;
+ tsd_wrapper_set(&tsd_boot_wrapper);
+ tsd_init_finish(&tsd_init_head, &block);
return false;
}
diff --git a/contrib/jemalloc/include/jemalloc/internal/tsd_malloc_thread_cleanup.h b/contrib/jemalloc/include/jemalloc/internal/tsd_malloc_thread_cleanup.h
index 65852d5c1492..d8f3ef13c00f 100644
--- a/contrib/jemalloc/include/jemalloc/internal/tsd_malloc_thread_cleanup.h
+++ b/contrib/jemalloc/include/jemalloc/internal/tsd_malloc_thread_cleanup.h
@@ -21,7 +21,7 @@ tsd_cleanup_wrapper(void) {
JEMALLOC_ALWAYS_INLINE bool
tsd_boot0(void) {
- malloc_tsd_cleanup_register(&tsd_cleanup_wrapper);
+ _malloc_tsd_cleanup_register(&tsd_cleanup_wrapper);
tsd_booted = true;
return false;
}
diff --git a/contrib/jemalloc/include/jemalloc/internal/tsd_types.h b/contrib/jemalloc/include/jemalloc/internal/tsd_types.h
index 6200af61f3dc..a6ae37da5a21 100644
--- a/contrib/jemalloc/include/jemalloc/internal/tsd_types.h
+++ b/contrib/jemalloc/include/jemalloc/internal/tsd_types.h
@@ -1,7 +1,7 @@
#ifndef JEMALLOC_INTERNAL_TSD_TYPES_H
#define JEMALLOC_INTERNAL_TSD_TYPES_H
-#define MALLOC_TSD_CLEANUPS_MAX 2
+#define MALLOC_TSD_CLEANUPS_MAX 4
typedef struct tsd_s tsd_t;
typedef struct tsdn_s tsdn_t;
diff --git a/contrib/jemalloc/include/jemalloc/internal/tsd_win.h b/contrib/jemalloc/include/jemalloc/internal/tsd_win.h
new file mode 100644
index 000000000000..a91dac88e06a
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/internal/tsd_win.h
@@ -0,0 +1,139 @@
+#ifdef JEMALLOC_INTERNAL_TSD_WIN_H
+#error This file should be included only once, by tsd.h.
+#endif
+#define JEMALLOC_INTERNAL_TSD_WIN_H
+
+typedef struct {
+ bool initialized;
+ tsd_t val;
+} tsd_wrapper_t;
+
+extern DWORD tsd_tsd;
+extern tsd_wrapper_t tsd_boot_wrapper;
+extern bool tsd_booted;
+
+/* Initialization/cleanup. */
+JEMALLOC_ALWAYS_INLINE bool
+tsd_cleanup_wrapper(void) {
+ DWORD error = GetLastError();
+ tsd_wrapper_t *wrapper = (tsd_wrapper_t *)TlsGetValue(tsd_tsd);
+ SetLastError(error);
+
+ if (wrapper == NULL) {
+ return false;
+ }
+
+ if (wrapper->initialized) {
+ wrapper->initialized = false;
+ tsd_cleanup(&wrapper->val);
+ if (wrapper->initialized) {
+ /* Trigger another cleanup round. */
+ return true;
+ }
+ }
+ malloc_tsd_dalloc(wrapper);
+ return false;
+}
+
+JEMALLOC_ALWAYS_INLINE void
+tsd_wrapper_set(tsd_wrapper_t *wrapper) {
+ if (!TlsSetValue(tsd_tsd, (void *)wrapper)) {
+ malloc_write("<jemalloc>: Error setting TSD\n");
+ abort();
+ }
+}
+
+JEMALLOC_ALWAYS_INLINE tsd_wrapper_t *
+tsd_wrapper_get(bool init) {
+ DWORD error = GetLastError();
+ tsd_wrapper_t *wrapper = (tsd_wrapper_t *) TlsGetValue(tsd_tsd);
+ SetLastError(error);
+
+ if (init && unlikely(wrapper == NULL)) {
+ wrapper = (tsd_wrapper_t *)
+ malloc_tsd_malloc(sizeof(tsd_wrapper_t));
+ if (wrapper == NULL) {
+ malloc_write("<jemalloc>: Error allocating TSD\n");
+ abort();
+ } else {
+ wrapper->initialized = false;
+ /* MSVC is finicky about aggregate initialization. */
+ tsd_t tsd_initializer = TSD_INITIALIZER;
+ wrapper->val = tsd_initializer;
+ }
+ tsd_wrapper_set(wrapper);
+ }
+ return wrapper;
+}
+
+JEMALLOC_ALWAYS_INLINE bool
+tsd_boot0(void) {
+ tsd_tsd = TlsAlloc();
+ if (tsd_tsd == TLS_OUT_OF_INDEXES) {
+ return true;
+ }
+ _malloc_tsd_cleanup_register(&tsd_cleanup_wrapper);
+ tsd_wrapper_set(&tsd_boot_wrapper);
+ tsd_booted = true;
+ return false;
+}
+
+JEMALLOC_ALWAYS_INLINE void
+tsd_boot1(void) {
+ tsd_wrapper_t *wrapper;
+ wrapper = (tsd_wrapper_t *)
+ malloc_tsd_malloc(sizeof(tsd_wrapper_t));
+ if (wrapper == NULL) {
+ malloc_write("<jemalloc>: Error allocating TSD\n");
+ abort();
+ }
+ tsd_boot_wrapper.initialized = false;
+ tsd_cleanup(&tsd_boot_wrapper.val);
+ wrapper->initialized = false;
+ tsd_t initializer = TSD_INITIALIZER;
+ wrapper->val = initializer;
+ tsd_wrapper_set(wrapper);
+}
+JEMALLOC_ALWAYS_INLINE bool
+tsd_boot(void) {
+ if (tsd_boot0()) {
+ return true;
+ }
+ tsd_boot1();
+ return false;
+}
+
+JEMALLOC_ALWAYS_INLINE bool
+tsd_booted_get(void) {
+ return tsd_booted;
+}
+
+JEMALLOC_ALWAYS_INLINE bool
+tsd_get_allocates(void) {
+ return true;
+}
+
+/* Get/set. */
+JEMALLOC_ALWAYS_INLINE tsd_t *
+tsd_get(bool init) {
+ tsd_wrapper_t *wrapper;
+
+ assert(tsd_booted);
+ wrapper = tsd_wrapper_get(init);
+ if (tsd_get_allocates() && !init && wrapper == NULL) {
+ return NULL;
+ }
+ return &wrapper->val;
+}
+
+JEMALLOC_ALWAYS_INLINE void
+tsd_set(tsd_t *val) {
+ tsd_wrapper_t *wrapper;
+
+ assert(tsd_booted);
+ wrapper = tsd_wrapper_get(true);
+ if (likely(&wrapper->val != val)) {
+ wrapper->val = *(val);
+ }
+ wrapper->initialized = true;
+}
diff --git a/contrib/jemalloc/include/jemalloc/internal/typed_list.h b/contrib/jemalloc/include/jemalloc/internal/typed_list.h
new file mode 100644
index 000000000000..6535055a1eca
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/internal/typed_list.h
@@ -0,0 +1,55 @@
+#ifndef JEMALLOC_INTERNAL_TYPED_LIST_H
+#define JEMALLOC_INTERNAL_TYPED_LIST_H
+
+/*
+ * This wraps the ql module to implement a list class in a way that's a little
+ * bit easier to use; it handles ql_elm_new calls and provides type safety.
+ */
+
+#define TYPED_LIST(list_type, el_type, linkage) \
+typedef struct { \
+ ql_head(el_type) head; \
+} list_type##_t; \
+static inline void \
+list_type##_init(list_type##_t *list) { \
+ ql_new(&list->head); \
+} \
+static inline el_type * \
+list_type##_first(const list_type##_t *list) { \
+ return ql_first(&list->head); \
+} \
+static inline el_type * \
+list_type##_last(const list_type##_t *list) { \
+ return ql_last(&list->head, linkage); \
+} \
+static inline void \
+list_type##_append(list_type##_t *list, el_type *item) { \
+ ql_elm_new(item, linkage); \
+ ql_tail_insert(&list->head, item, linkage); \
+} \
+static inline void \
+list_type##_prepend(list_type##_t *list, el_type *item) { \
+ ql_elm_new(item, linkage); \
+ ql_head_insert(&list->head, item, linkage); \
+} \
+static inline void \
+list_type##_replace(list_type##_t *list, el_type *to_remove, \
+ el_type *to_insert) { \
+ ql_elm_new(to_insert, linkage); \
+ ql_after_insert(to_remove, to_insert, linkage); \
+ ql_remove(&list->head, to_remove, linkage); \
+} \
+static inline void \
+list_type##_remove(list_type##_t *list, el_type *item) { \
+ ql_remove(&list->head, item, linkage); \
+} \
+static inline bool \
+list_type##_empty(list_type##_t *list) { \
+ return ql_empty(&list->head); \
+} \
+static inline void \
+list_type##_concat(list_type##_t *list_a, list_type##_t *list_b) { \
+ ql_concat(&list_a->head, &list_b->head, linkage); \
+}
+
+#endif /* JEMALLOC_INTERNAL_TYPED_LIST_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/util.h b/contrib/jemalloc/include/jemalloc/internal/util.h
index 304cb545afcb..dcb1c0a5d62b 100644
--- a/contrib/jemalloc/include/jemalloc/internal/util.h
+++ b/contrib/jemalloc/include/jemalloc/internal/util.h
@@ -62,6 +62,62 @@ get_errno(void) {
#endif
}
+JEMALLOC_ALWAYS_INLINE void
+util_assume(bool b) {
+ if (!b) {
+ unreachable();
+ }
+}
+
+/* ptr should be valid. */
+JEMALLOC_ALWAYS_INLINE void
+util_prefetch_read(void *ptr) {
+ /*
+ * This should arguably be a config check; but any version of GCC so old
+ * that it doesn't support __builtin_prefetch is also too old to build
+ * jemalloc.
+ */
+#ifdef __GNUC__
+ if (config_debug) {
+ /* Enforce the "valid ptr" requirement. */
+ *(volatile char *)ptr;
+ }
+ __builtin_prefetch(ptr, /* read or write */ 0, /* locality hint */ 3);
+#else
+ *(volatile char *)ptr;
+#endif
+}
+
+JEMALLOC_ALWAYS_INLINE void
+util_prefetch_write(void *ptr) {
+#ifdef __GNUC__
+ if (config_debug) {
+ *(volatile char *)ptr;
+ }
+ /*
+ * The only difference from the read variant is that this has a 1 as the
+ * second argument (the write hint).
+ */
+ __builtin_prefetch(ptr, 1, 3);
+#else
+ *(volatile char *)ptr;
+#endif
+}
+
+JEMALLOC_ALWAYS_INLINE void
+util_prefetch_read_range(void *ptr, size_t sz) {
+ for (size_t i = 0; i < sz; i += CACHELINE) {
+ util_prefetch_read((void *)((uintptr_t)ptr + i));
+ }
+}
+
+JEMALLOC_ALWAYS_INLINE void
+util_prefetch_write_range(void *ptr, size_t sz) {
+ for (size_t i = 0; i < sz; i += CACHELINE) {
+ util_prefetch_write((void *)((uintptr_t)ptr + i));
+ }
+}
+
#undef UTIL_INLINE
#endif /* JEMALLOC_INTERNAL_UTIL_H */
diff --git a/contrib/jemalloc/include/jemalloc/internal/witness.h b/contrib/jemalloc/include/jemalloc/internal/witness.h
index fff9e98cb64f..e81b9a0069c0 100644
--- a/contrib/jemalloc/include/jemalloc/internal/witness.h
+++ b/contrib/jemalloc/include/jemalloc/internal/witness.h
@@ -7,60 +7,76 @@
/* LOCK RANKS */
/******************************************************************************/
-/*
- * Witnesses with rank WITNESS_RANK_OMIT are completely ignored by the witness
- * machinery.
- */
-
-#define WITNESS_RANK_OMIT 0U
-
-#define WITNESS_RANK_MIN 1U
-
-#define WITNESS_RANK_INIT 1U
-#define WITNESS_RANK_CTL 1U
-#define WITNESS_RANK_TCACHES 2U
-#define WITNESS_RANK_ARENAS 3U
-
-#define WITNESS_RANK_BACKGROUND_THREAD_GLOBAL 4U
-
-#define WITNESS_RANK_PROF_DUMP 5U
-#define WITNESS_RANK_PROF_BT2GCTX 6U
-#define WITNESS_RANK_PROF_TDATAS 7U
-#define WITNESS_RANK_PROF_TDATA 8U
-#define WITNESS_RANK_PROF_LOG 9U
-#define WITNESS_RANK_PROF_GCTX 10U
-#define WITNESS_RANK_BACKGROUND_THREAD 11U
-
-/*
- * Used as an argument to witness_assert_depth_to_rank() in order to validate
- * depth excluding non-core locks with lower ranks. Since the rank argument to
- * witness_assert_depth_to_rank() is inclusive rather than exclusive, this
- * definition can have the same value as the minimally ranked core lock.
- */
-#define WITNESS_RANK_CORE 12U
-
-#define WITNESS_RANK_DECAY 12U
-#define WITNESS_RANK_TCACHE_QL 13U
-#define WITNESS_RANK_EXTENT_GROW 14U
-#define WITNESS_RANK_EXTENTS 15U
-#define WITNESS_RANK_EXTENT_AVAIL 16U
-
-#define WITNESS_RANK_EXTENT_POOL 17U
-#define WITNESS_RANK_RTREE 18U
-#define WITNESS_RANK_BASE 19U
-#define WITNESS_RANK_ARENA_LARGE 20U
-#define WITNESS_RANK_HOOK 21U
-
-#define WITNESS_RANK_LEAF 0xffffffffU
-#define WITNESS_RANK_BIN WITNESS_RANK_LEAF
-#define WITNESS_RANK_ARENA_STATS WITNESS_RANK_LEAF
-#define WITNESS_RANK_DSS WITNESS_RANK_LEAF
-#define WITNESS_RANK_PROF_ACTIVE WITNESS_RANK_LEAF
-#define WITNESS_RANK_PROF_ACCUM WITNESS_RANK_LEAF
-#define WITNESS_RANK_PROF_DUMP_SEQ WITNESS_RANK_LEAF
-#define WITNESS_RANK_PROF_GDUMP WITNESS_RANK_LEAF
-#define WITNESS_RANK_PROF_NEXT_THR_UID WITNESS_RANK_LEAF
-#define WITNESS_RANK_PROF_THREAD_ACTIVE_INIT WITNESS_RANK_LEAF
+enum witness_rank_e {
+ /*
+ * Order matters within this enum listing -- higher valued locks can
+ * only be acquired after lower-valued ones. We use the
+ * auto-incrementing-ness of enum values to enforce this.
+ */
+
+ /*
+ * Witnesses with rank WITNESS_RANK_OMIT are completely ignored by the
+ * witness machinery.
+ */
+ WITNESS_RANK_OMIT,
+ WITNESS_RANK_MIN,
+ WITNESS_RANK_INIT = WITNESS_RANK_MIN,
+ WITNESS_RANK_CTL,
+ WITNESS_RANK_TCACHES,
+ WITNESS_RANK_ARENAS,
+ WITNESS_RANK_BACKGROUND_THREAD_GLOBAL,
+ WITNESS_RANK_PROF_DUMP,
+ WITNESS_RANK_PROF_BT2GCTX,
+ WITNESS_RANK_PROF_TDATAS,
+ WITNESS_RANK_PROF_TDATA,
+ WITNESS_RANK_PROF_LOG,
+ WITNESS_RANK_PROF_GCTX,
+ WITNESS_RANK_PROF_RECENT_DUMP,
+ WITNESS_RANK_BACKGROUND_THREAD,
+ /*
+ * Used as an argument to witness_assert_depth_to_rank() in order to
+ * validate depth excluding non-core locks with lower ranks. Since the
+ * rank argument to witness_assert_depth_to_rank() is inclusive rather
+ * than exclusive, this definition can have the same value as the
+ * minimally ranked core lock.
+ */
+ WITNESS_RANK_CORE,
+ WITNESS_RANK_DECAY = WITNESS_RANK_CORE,
+ WITNESS_RANK_TCACHE_QL,
+
+ WITNESS_RANK_SEC_SHARD,
+
+ WITNESS_RANK_EXTENT_GROW,
+ WITNESS_RANK_HPA_SHARD_GROW = WITNESS_RANK_EXTENT_GROW,
+ WITNESS_RANK_SAN_BUMP_ALLOC = WITNESS_RANK_EXTENT_GROW,
+
+ WITNESS_RANK_EXTENTS,
+ WITNESS_RANK_HPA_SHARD = WITNESS_RANK_EXTENTS,
+
+ WITNESS_RANK_HPA_CENTRAL_GROW,
+ WITNESS_RANK_HPA_CENTRAL,
+
+ WITNESS_RANK_EDATA_CACHE,
+
+ WITNESS_RANK_RTREE,
+ WITNESS_RANK_BASE,
+ WITNESS_RANK_ARENA_LARGE,
+ WITNESS_RANK_HOOK,
+
+ WITNESS_RANK_LEAF=0x1000,
+ WITNESS_RANK_BIN = WITNESS_RANK_LEAF,
+ WITNESS_RANK_ARENA_STATS = WITNESS_RANK_LEAF,
+ WITNESS_RANK_COUNTER_ACCUM = WITNESS_RANK_LEAF,
+ WITNESS_RANK_DSS = WITNESS_RANK_LEAF,
+ WITNESS_RANK_PROF_ACTIVE = WITNESS_RANK_LEAF,
+ WITNESS_RANK_PROF_DUMP_FILENAME = WITNESS_RANK_LEAF,
+ WITNESS_RANK_PROF_GDUMP = WITNESS_RANK_LEAF,
+ WITNESS_RANK_PROF_NEXT_THR_UID = WITNESS_RANK_LEAF,
+ WITNESS_RANK_PROF_RECENT_ALLOC = WITNESS_RANK_LEAF,
+ WITNESS_RANK_PROF_STATS = WITNESS_RANK_LEAF,
+ WITNESS_RANK_PROF_THREAD_ACTIVE_INIT = WITNESS_RANK_LEAF,
+};
+typedef enum witness_rank_e witness_rank_t;
/******************************************************************************/
/* PER-WITNESS DATA */
@@ -72,7 +88,6 @@
#endif
typedef struct witness_s witness_t;
-typedef unsigned witness_rank_t;
typedef ql_head(witness_t) witness_list_t;
typedef int witness_comp_t (const witness_t *, void *, const witness_t *,
void *);
@@ -82,8 +97,8 @@ struct witness_s {
const char *name;
/*
- * Witness rank, where 0 is lowest and UINT_MAX is highest. Witnesses
- * must be acquired in order of increasing rank.
+ * Witness rank, where 0 is lowest and WITNESS_RANK_LEAF is highest.
+ * Witnesses must be acquired in order of increasing rank.
*/
witness_rank_t rank;
@@ -228,26 +243,13 @@ witness_assert_not_owner(witness_tsdn_t *witness_tsdn,
}
}
-static inline void
-witness_assert_depth_to_rank(witness_tsdn_t *witness_tsdn,
- witness_rank_t rank_inclusive, unsigned depth) {
- witness_tsd_t *witness_tsd;
- unsigned d;
- witness_list_t *witnesses;
- witness_t *w;
-
- if (!config_debug) {
- return;
- }
+/* Returns depth. Not intended for direct use. */
+static inline unsigned
+witness_depth_to_rank(witness_list_t *witnesses, witness_rank_t rank_inclusive)
+{
+ unsigned d = 0;
+ witness_t *w = ql_last(witnesses, link);
- if (witness_tsdn_null(witness_tsdn)) {
- return;
- }
- witness_tsd = witness_tsdn_tsd(witness_tsdn);
-
- d = 0;
- witnesses = &witness_tsd->witnesses;
- w = ql_last(witnesses, link);
if (w != NULL) {
ql_reverse_foreach(w, witnesses, link) {
if (w->rank < rank_inclusive) {
@@ -256,6 +258,20 @@ witness_assert_depth_to_rank(witness_tsdn_t *witness_tsdn,
d++;
}
}
+
+ return d;
+}
+
+static inline void
+witness_assert_depth_to_rank(witness_tsdn_t *witness_tsdn,
+ witness_rank_t rank_inclusive, unsigned depth) {
+ if (!config_debug || witness_tsdn_null(witness_tsdn)) {
+ return;
+ }
+
+ witness_list_t *witnesses = &witness_tsdn_tsd(witness_tsdn)->witnesses;
+ unsigned d = witness_depth_to_rank(witnesses, rank_inclusive);
+
if (d != depth) {
witness_depth_error(witnesses, rank_inclusive, depth);
}
@@ -272,6 +288,21 @@ witness_assert_lockless(witness_tsdn_t *witness_tsdn) {
}
static inline void
+witness_assert_positive_depth_to_rank(witness_tsdn_t *witness_tsdn,
+ witness_rank_t rank_inclusive) {
+ if (!config_debug || witness_tsdn_null(witness_tsdn)) {
+ return;
+ }
+
+ witness_list_t *witnesses = &witness_tsdn_tsd(witness_tsdn)->witnesses;
+ unsigned d = witness_depth_to_rank(witnesses, rank_inclusive);
+
+ if (d == 0) {
+ witness_depth_error(witnesses, rank_inclusive, 1);
+ }
+}
+
+static inline void
witness_lock(witness_tsdn_t *witness_tsdn, witness_t *witness) {
witness_tsd_t *witness_tsd;
witness_list_t *witnesses;
diff --git a/contrib/jemalloc/include/jemalloc/jemalloc.sh b/contrib/jemalloc/include/jemalloc/jemalloc.sh
new file mode 100755
index 000000000000..b19b1548b3d7
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/jemalloc.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+objroot=$1
+
+cat <<EOF
+#ifndef JEMALLOC_H_
+#define JEMALLOC_H_
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+EOF
+
+for hdr in jemalloc_defs.h jemalloc_rename.h jemalloc_macros.h \
+ jemalloc_protos.h jemalloc_typedefs.h jemalloc_mangle.h ; do
+ cat "${objroot}include/jemalloc/${hdr}" \
+ | grep -v 'Generated from .* by configure\.' \
+ | sed -e 's/ $//g'
+ echo
+done
+
+cat <<EOF
+#ifdef __cplusplus
+}
+#endif
+#endif /* JEMALLOC_H_ */
+EOF
diff --git a/contrib/jemalloc/include/jemalloc/jemalloc_defs.h.in b/contrib/jemalloc/include/jemalloc/jemalloc_defs.h.in
new file mode 100644
index 000000000000..cbe2fca6ba53
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/jemalloc_defs.h.in
@@ -0,0 +1,54 @@
+/* Defined if __attribute__((...)) syntax is supported. */
+#undef JEMALLOC_HAVE_ATTR
+
+/* Defined if alloc_size attribute is supported. */
+#undef JEMALLOC_HAVE_ATTR_ALLOC_SIZE
+
+/* Defined if format_arg(...) attribute is supported. */
+#undef JEMALLOC_HAVE_ATTR_FORMAT_ARG
+
+/* Defined if format(gnu_printf, ...) attribute is supported. */
+#undef JEMALLOC_HAVE_ATTR_FORMAT_GNU_PRINTF
+
+/* Defined if format(printf, ...) attribute is supported. */
+#undef JEMALLOC_HAVE_ATTR_FORMAT_PRINTF
+
+/* Defined if fallthrough attribute is supported. */
+#undef JEMALLOC_HAVE_ATTR_FALLTHROUGH
+
+/* Defined if cold attribute is supported. */
+#undef JEMALLOC_HAVE_ATTR_COLD
+
+/*
+ * Define overrides for non-standard allocator-related functions if they are
+ * present on the system.
+ */
+#undef JEMALLOC_OVERRIDE_MEMALIGN
+#undef JEMALLOC_OVERRIDE_VALLOC
+
+/*
+ * At least Linux omits the "const" in:
+ *
+ * size_t malloc_usable_size(const void *ptr);
+ *
+ * Match the operating system's prototype.
+ */
+#undef JEMALLOC_USABLE_SIZE_CONST
+
+/*
+ * If defined, specify throw() for the public function prototypes when compiling
+ * with C++. The only justification for this is to match the prototypes that
+ * glibc defines.
+ */
+#undef JEMALLOC_USE_CXX_THROW
+
+#ifdef _MSC_VER
+# ifdef _WIN64
+# define LG_SIZEOF_PTR_WIN 3
+# else
+# define LG_SIZEOF_PTR_WIN 2
+# endif
+#endif
+
+/* sizeof(void *) == 2^LG_SIZEOF_PTR. */
+#undef LG_SIZEOF_PTR
diff --git a/contrib/jemalloc/include/jemalloc/jemalloc_macros.h.in b/contrib/jemalloc/include/jemalloc/jemalloc_macros.h.in
new file mode 100644
index 000000000000..ebb3137e6f7e
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/jemalloc_macros.h.in
@@ -0,0 +1,149 @@
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <limits.h>
+#include <strings.h>
+
+#define JEMALLOC_VERSION "@jemalloc_version@"
+#define JEMALLOC_VERSION_MAJOR @jemalloc_version_major@
+#define JEMALLOC_VERSION_MINOR @jemalloc_version_minor@
+#define JEMALLOC_VERSION_BUGFIX @jemalloc_version_bugfix@
+#define JEMALLOC_VERSION_NREV @jemalloc_version_nrev@
+#define JEMALLOC_VERSION_GID "@jemalloc_version_gid@"
+#define JEMALLOC_VERSION_GID_IDENT @jemalloc_version_gid@
+
+#define MALLOCX_LG_ALIGN(la) ((int)(la))
+#if LG_SIZEOF_PTR == 2
+# define MALLOCX_ALIGN(a) ((int)(ffs((int)(a))-1))
+#else
+# define MALLOCX_ALIGN(a) \
+ ((int)(((size_t)(a) < (size_t)INT_MAX) ? ffs((int)(a))-1 : \
+ ffs((int)(((size_t)(a))>>32))+31))
+#endif
+#define MALLOCX_ZERO ((int)0x40)
+/*
+ * Bias tcache index bits so that 0 encodes "automatic tcache management", and 1
+ * encodes MALLOCX_TCACHE_NONE.
+ */
+#define MALLOCX_TCACHE(tc) ((int)(((tc)+2) << 8))
+#define MALLOCX_TCACHE_NONE MALLOCX_TCACHE(-1)
+/*
+ * Bias arena index bits so that 0 encodes "use an automatically chosen arena".
+ */
+#define MALLOCX_ARENA(a) ((((int)(a))+1) << 20)
+
+/*
+ * Use as arena index in "arena.<i>.{purge,decay,dss}" and
+ * "stats.arenas.<i>.*" mallctl interfaces to select all arenas. This
+ * definition is intentionally specified in raw decimal format to support
+ * cpp-based string concatenation, e.g.
+ *
+ * #define STRINGIFY_HELPER(x) #x
+ * #define STRINGIFY(x) STRINGIFY_HELPER(x)
+ *
+ * mallctl("arena." STRINGIFY(MALLCTL_ARENAS_ALL) ".purge", NULL, NULL, NULL,
+ * 0);
+ */
+#define MALLCTL_ARENAS_ALL 4096
+/*
+ * Use as arena index in "stats.arenas.<i>.*" mallctl interfaces to select
+ * destroyed arenas.
+ */
+#define MALLCTL_ARENAS_DESTROYED 4097
+
+#if defined(__cplusplus) && defined(JEMALLOC_USE_CXX_THROW)
+# define JEMALLOC_CXX_THROW throw()
+#else
+# define JEMALLOC_CXX_THROW
+#endif
+
+#if defined(_MSC_VER)
+# define JEMALLOC_ATTR(s)
+# define JEMALLOC_ALIGNED(s) __declspec(align(s))
+# define JEMALLOC_ALLOC_SIZE(s)
+# define JEMALLOC_ALLOC_SIZE2(s1, s2)
+# ifndef JEMALLOC_EXPORT
+# ifdef DLLEXPORT
+# define JEMALLOC_EXPORT __declspec(dllexport)
+# else
+# define JEMALLOC_EXPORT __declspec(dllimport)
+# endif
+# endif
+# define JEMALLOC_FORMAT_ARG(i)
+# define JEMALLOC_FORMAT_PRINTF(s, i)
+# define JEMALLOC_FALLTHROUGH
+# define JEMALLOC_NOINLINE __declspec(noinline)
+# ifdef __cplusplus
+# define JEMALLOC_NOTHROW __declspec(nothrow)
+# else
+# define JEMALLOC_NOTHROW
+# endif
+# define JEMALLOC_SECTION(s) __declspec(allocate(s))
+# define JEMALLOC_RESTRICT_RETURN __declspec(restrict)
+# if _MSC_VER >= 1900 && !defined(__EDG__)
+# define JEMALLOC_ALLOCATOR __declspec(allocator)
+# else
+# define JEMALLOC_ALLOCATOR
+# endif
+# define JEMALLOC_COLD
+#elif defined(JEMALLOC_HAVE_ATTR)
+# define JEMALLOC_ATTR(s) __attribute__((s))
+# define JEMALLOC_ALIGNED(s) JEMALLOC_ATTR(aligned(s))
+# ifdef JEMALLOC_HAVE_ATTR_ALLOC_SIZE
+# define JEMALLOC_ALLOC_SIZE(s) JEMALLOC_ATTR(alloc_size(s))
+# define JEMALLOC_ALLOC_SIZE2(s1, s2) JEMALLOC_ATTR(alloc_size(s1, s2))
+# else
+# define JEMALLOC_ALLOC_SIZE(s)
+# define JEMALLOC_ALLOC_SIZE2(s1, s2)
+# endif
+# ifndef JEMALLOC_EXPORT
+# define JEMALLOC_EXPORT JEMALLOC_ATTR(visibility("default"))
+# endif
+# ifdef JEMALLOC_HAVE_ATTR_FORMAT_ARG
+# define JEMALLOC_FORMAT_ARG(i) JEMALLOC_ATTR(__format_arg__(3))
+# else
+# define JEMALLOC_FORMAT_ARG(i)
+# endif
+# ifdef JEMALLOC_HAVE_ATTR_FORMAT_GNU_PRINTF
+# define JEMALLOC_FORMAT_PRINTF(s, i) JEMALLOC_ATTR(format(gnu_printf, s, i))
+# elif defined(JEMALLOC_HAVE_ATTR_FORMAT_PRINTF)
+# define JEMALLOC_FORMAT_PRINTF(s, i) JEMALLOC_ATTR(format(printf, s, i))
+# else
+# define JEMALLOC_FORMAT_PRINTF(s, i)
+# endif
+# ifdef JEMALLOC_HAVE_ATTR_FALLTHROUGH
+# define JEMALLOC_FALLTHROUGH JEMALLOC_ATTR(fallthrough)
+# else
+# define JEMALLOC_FALLTHROUGH
+# endif
+# define JEMALLOC_NOINLINE JEMALLOC_ATTR(noinline)
+# define JEMALLOC_NOTHROW JEMALLOC_ATTR(nothrow)
+# define JEMALLOC_SECTION(s) JEMALLOC_ATTR(section(s))
+# define JEMALLOC_RESTRICT_RETURN
+# define JEMALLOC_ALLOCATOR
+# ifdef JEMALLOC_HAVE_ATTR_COLD
+# define JEMALLOC_COLD JEMALLOC_ATTR(__cold__)
+# else
+# define JEMALLOC_COLD
+# endif
+#else
+# define JEMALLOC_ATTR(s)
+# define JEMALLOC_ALIGNED(s)
+# define JEMALLOC_ALLOC_SIZE(s)
+# define JEMALLOC_ALLOC_SIZE2(s1, s2)
+# define JEMALLOC_EXPORT
+# define JEMALLOC_FORMAT_PRINTF(s, i)
+# define JEMALLOC_FALLTHROUGH
+# define JEMALLOC_NOINLINE
+# define JEMALLOC_NOTHROW
+# define JEMALLOC_SECTION(s)
+# define JEMALLOC_RESTRICT_RETURN
+# define JEMALLOC_ALLOCATOR
+# define JEMALLOC_COLD
+#endif
+
+#if (defined(__APPLE__) || defined(__FreeBSD__)) && !defined(JEMALLOC_NO_RENAME)
+# define JEMALLOC_SYS_NOTHROW
+#else
+# define JEMALLOC_SYS_NOTHROW JEMALLOC_NOTHROW
+#endif
diff --git a/contrib/jemalloc/include/jemalloc/jemalloc_mangle.sh b/contrib/jemalloc/include/jemalloc/jemalloc_mangle.sh
new file mode 100755
index 000000000000..c675bb469f1c
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/jemalloc_mangle.sh
@@ -0,0 +1,45 @@
+#!/bin/sh -eu
+
+public_symbols_txt=$1
+symbol_prefix=$2
+
+cat <<EOF
+/*
+ * By default application code must explicitly refer to mangled symbol names,
+ * so that it is possible to use jemalloc in conjunction with another allocator
+ * in the same application. Define JEMALLOC_MANGLE in order to cause automatic
+ * name mangling that matches the API prefixing that happened as a result of
+ * --with-mangling and/or --with-jemalloc-prefix configuration settings.
+ */
+#ifdef JEMALLOC_MANGLE
+# ifndef JEMALLOC_NO_DEMANGLE
+# define JEMALLOC_NO_DEMANGLE
+# endif
+EOF
+
+for nm in `cat ${public_symbols_txt}` ; do
+ n=`echo ${nm} |tr ':' ' ' |awk '{print $1}'`
+ echo "# define ${n} ${symbol_prefix}${n}"
+done
+
+cat <<EOF
+#endif
+
+/*
+ * The ${symbol_prefix}* macros can be used as stable alternative names for the
+ * public jemalloc API if JEMALLOC_NO_DEMANGLE is defined. This is primarily
+ * meant for use in jemalloc itself, but it can be used by application code to
+ * provide isolation from the name mangling specified via --with-mangling
+ * and/or --with-jemalloc-prefix.
+ */
+#ifndef JEMALLOC_NO_DEMANGLE
+EOF
+
+for nm in `cat ${public_symbols_txt}` ; do
+ n=`echo ${nm} |tr ':' ' ' |awk '{print $1}'`
+ echo "# undef ${symbol_prefix}${n}"
+done
+
+cat <<EOF
+#endif
+EOF
diff --git a/contrib/jemalloc/include/jemalloc/jemalloc_protos.h.in b/contrib/jemalloc/include/jemalloc/jemalloc_protos.h.in
new file mode 100644
index 000000000000..356221cc8d52
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/jemalloc_protos.h.in
@@ -0,0 +1,71 @@
+/*
+ * The @je_@ prefix on the following public symbol declarations is an artifact
+ * of namespace management, and should be omitted in application code unless
+ * JEMALLOC_NO_DEMANGLE is defined (see jemalloc_mangle@install_suffix@.h).
+ */
+extern JEMALLOC_EXPORT const char *@je_@malloc_conf;
+extern JEMALLOC_EXPORT void (*@je_@malloc_message)(void *cbopaque,
+ const char *s);
+
+JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
+ void JEMALLOC_SYS_NOTHROW *@je_@malloc(size_t size)
+ JEMALLOC_CXX_THROW JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE(1);
+JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
+ void JEMALLOC_SYS_NOTHROW *@je_@calloc(size_t num, size_t size)
+ JEMALLOC_CXX_THROW JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE2(1, 2);
+JEMALLOC_EXPORT int JEMALLOC_SYS_NOTHROW @je_@posix_memalign(
+ void **memptr, size_t alignment, size_t size) JEMALLOC_CXX_THROW
+ JEMALLOC_ATTR(nonnull(1));
+JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
+ void JEMALLOC_SYS_NOTHROW *@je_@aligned_alloc(size_t alignment,
+ size_t size) JEMALLOC_CXX_THROW JEMALLOC_ATTR(malloc)
+ JEMALLOC_ALLOC_SIZE(2);
+JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
+ void JEMALLOC_SYS_NOTHROW *@je_@realloc(void *ptr, size_t size)
+ JEMALLOC_CXX_THROW JEMALLOC_ALLOC_SIZE(2);
+JEMALLOC_EXPORT void JEMALLOC_SYS_NOTHROW @je_@free(void *ptr)
+ JEMALLOC_CXX_THROW;
+
+JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
+ void JEMALLOC_NOTHROW *@je_@mallocx(size_t size, int flags)
+ JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE(1);
+JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
+ void JEMALLOC_NOTHROW *@je_@rallocx(void *ptr, size_t size,
+ int flags) JEMALLOC_ALLOC_SIZE(2);
+JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW @je_@xallocx(void *ptr, size_t size,
+ size_t extra, int flags);
+JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW @je_@sallocx(const void *ptr,
+ int flags) JEMALLOC_ATTR(pure);
+JEMALLOC_EXPORT void JEMALLOC_NOTHROW @je_@dallocx(void *ptr, int flags);
+JEMALLOC_EXPORT void JEMALLOC_NOTHROW @je_@sdallocx(void *ptr, size_t size,
+ int flags);
+JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW @je_@nallocx(size_t size, int flags)
+ JEMALLOC_ATTR(pure);
+
+JEMALLOC_EXPORT int JEMALLOC_NOTHROW @je_@mallctl(const char *name,
+ void *oldp, size_t *oldlenp, void *newp, size_t newlen);
+JEMALLOC_EXPORT int JEMALLOC_NOTHROW @je_@mallctlnametomib(const char *name,
+ size_t *mibp, size_t *miblenp);
+JEMALLOC_EXPORT int JEMALLOC_NOTHROW @je_@mallctlbymib(const size_t *mib,
+ size_t miblen, void *oldp, size_t *oldlenp, void *newp, size_t newlen);
+JEMALLOC_EXPORT void JEMALLOC_NOTHROW @je_@malloc_stats_print(
+ void (*write_cb)(void *, const char *), void *@je_@cbopaque,
+ const char *opts);
+JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW @je_@malloc_usable_size(
+ JEMALLOC_USABLE_SIZE_CONST void *ptr) JEMALLOC_CXX_THROW;
+#ifdef JEMALLOC_HAVE_MALLOC_SIZE
+JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW @je_@malloc_size(
+ const void *ptr);
+#endif
+
+#ifdef JEMALLOC_OVERRIDE_MEMALIGN
+JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
+ void JEMALLOC_SYS_NOTHROW *@je_@memalign(size_t alignment, size_t size)
+ JEMALLOC_CXX_THROW JEMALLOC_ATTR(malloc);
+#endif
+
+#ifdef JEMALLOC_OVERRIDE_VALLOC
+JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
+ void JEMALLOC_SYS_NOTHROW *@je_@valloc(size_t size) JEMALLOC_CXX_THROW
+ JEMALLOC_ATTR(malloc);
+#endif
diff --git a/contrib/jemalloc/include/jemalloc/jemalloc_rename.sh b/contrib/jemalloc/include/jemalloc/jemalloc_rename.sh
new file mode 100755
index 000000000000..f94389120e5e
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/jemalloc_rename.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+public_symbols_txt=$1
+
+cat <<EOF
+/*
+ * Name mangling for public symbols is controlled by --with-mangling and
+ * --with-jemalloc-prefix. With default settings the je_ prefix is stripped by
+ * these macro definitions.
+ */
+#ifndef JEMALLOC_NO_RENAME
+EOF
+
+for nm in `cat ${public_symbols_txt}` ; do
+ n=`echo ${nm} |tr ':' ' ' |awk '{print $1}'`
+ m=`echo ${nm} |tr ':' ' ' |awk '{print $2}'`
+ echo "# define je_${n} ${m}"
+done
+
+cat <<EOF
+#endif
+EOF
diff --git a/contrib/jemalloc/include/jemalloc/jemalloc_typedefs.h.in b/contrib/jemalloc/include/jemalloc/jemalloc_typedefs.h.in
new file mode 100644
index 000000000000..1a58874306eb
--- /dev/null
+++ b/contrib/jemalloc/include/jemalloc/jemalloc_typedefs.h.in
@@ -0,0 +1,77 @@
+typedef struct extent_hooks_s extent_hooks_t;
+
+/*
+ * void *
+ * extent_alloc(extent_hooks_t *extent_hooks, void *new_addr, size_t size,
+ * size_t alignment, bool *zero, bool *commit, unsigned arena_ind);
+ */
+typedef void *(extent_alloc_t)(extent_hooks_t *, void *, size_t, size_t, bool *,
+ bool *, unsigned);
+
+/*
+ * bool
+ * extent_dalloc(extent_hooks_t *extent_hooks, void *addr, size_t size,
+ * bool committed, unsigned arena_ind);
+ */
+typedef bool (extent_dalloc_t)(extent_hooks_t *, void *, size_t, bool,
+ unsigned);
+
+/*
+ * void
+ * extent_destroy(extent_hooks_t *extent_hooks, void *addr, size_t size,
+ * bool committed, unsigned arena_ind);
+ */
+typedef void (extent_destroy_t)(extent_hooks_t *, void *, size_t, bool,
+ unsigned);
+
+/*
+ * bool
+ * extent_commit(extent_hooks_t *extent_hooks, void *addr, size_t size,
+ * size_t offset, size_t length, unsigned arena_ind);
+ */
+typedef bool (extent_commit_t)(extent_hooks_t *, void *, size_t, size_t, size_t,
+ unsigned);
+
+/*
+ * bool
+ * extent_decommit(extent_hooks_t *extent_hooks, void *addr, size_t size,
+ * size_t offset, size_t length, unsigned arena_ind);
+ */
+typedef bool (extent_decommit_t)(extent_hooks_t *, void *, size_t, size_t,
+ size_t, unsigned);
+
+/*
+ * bool
+ * extent_purge(extent_hooks_t *extent_hooks, void *addr, size_t size,
+ * size_t offset, size_t length, unsigned arena_ind);
+ */
+typedef bool (extent_purge_t)(extent_hooks_t *, void *, size_t, size_t, size_t,
+ unsigned);
+
+/*
+ * bool
+ * extent_split(extent_hooks_t *extent_hooks, void *addr, size_t size,
+ * size_t size_a, size_t size_b, bool committed, unsigned arena_ind);
+ */
+typedef bool (extent_split_t)(extent_hooks_t *, void *, size_t, size_t, size_t,
+ bool, unsigned);
+
+/*
+ * bool
+ * extent_merge(extent_hooks_t *extent_hooks, void *addr_a, size_t size_a,
+ * void *addr_b, size_t size_b, bool committed, unsigned arena_ind);
+ */
+typedef bool (extent_merge_t)(extent_hooks_t *, void *, size_t, void *, size_t,
+ bool, unsigned);
+
+struct extent_hooks_s {
+ extent_alloc_t *alloc;
+ extent_dalloc_t *dalloc;
+ extent_destroy_t *destroy;
+ extent_commit_t *commit;
+ extent_decommit_t *decommit;
+ extent_purge_t *purge_lazy;
+ extent_purge_t *purge_forced;
+ extent_split_t *split;
+ extent_merge_t *merge;
+};
diff --git a/contrib/jemalloc/include/msvc_compat/C99/stdbool.h b/contrib/jemalloc/include/msvc_compat/C99/stdbool.h
new file mode 100644
index 000000000000..d92160ebc752
--- /dev/null
+++ b/contrib/jemalloc/include/msvc_compat/C99/stdbool.h
@@ -0,0 +1,20 @@
+#ifndef stdbool_h
+#define stdbool_h
+
+#include <wtypes.h>
+
+/* MSVC doesn't define _Bool or bool in C, but does have BOOL */
+/* Note this doesn't pass autoconf's test because (bool) 0.5 != true */
+/* Clang-cl uses MSVC headers, so needs msvc_compat, but has _Bool as
+ * a built-in type. */
+#ifndef __clang__
+typedef BOOL _Bool;
+#endif
+
+#define bool _Bool
+#define true 1
+#define false 0
+
+#define __bool_true_false_are_defined 1
+
+#endif /* stdbool_h */
diff --git a/contrib/jemalloc/include/msvc_compat/C99/stdint.h b/contrib/jemalloc/include/msvc_compat/C99/stdint.h
new file mode 100644
index 000000000000..d02608a59726
--- /dev/null
+++ b/contrib/jemalloc/include/msvc_compat/C99/stdint.h
@@ -0,0 +1,247 @@
+// ISO C9x compliant stdint.h for Microsoft Visual Studio
+// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124
+//
+// Copyright (c) 2006-2008 Alexander Chemeris
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// 3. The name of the author may be used to endorse or promote products
+// derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef _MSC_VER // [
+#error "Use this header only with Microsoft Visual C++ compilers!"
+#endif // _MSC_VER ]
+
+#ifndef _MSC_STDINT_H_ // [
+#define _MSC_STDINT_H_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif
+
+#include <limits.h>
+
+// For Visual Studio 6 in C++ mode and for many Visual Studio versions when
+// compiling for ARM we should wrap <wchar.h> include with 'extern "C++" {}'
+// or compiler give many errors like this:
+// error C2733: second C linkage of overloaded function 'wmemchr' not allowed
+#ifdef __cplusplus
+extern "C" {
+#endif
+# include <wchar.h>
+#ifdef __cplusplus
+}
+#endif
+
+// Define _W64 macros to mark types changing their size, like intptr_t.
+#ifndef _W64
+# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300
+# define _W64 __w64
+# else
+# define _W64
+# endif
+#endif
+
+
+// 7.18.1 Integer types
+
+// 7.18.1.1 Exact-width integer types
+
+// Visual Studio 6 and Embedded Visual C++ 4 doesn't
+// realize that, e.g. char has the same size as __int8
+// so we give up on __intX for them.
+#if (_MSC_VER < 1300)
+ typedef signed char int8_t;
+ typedef signed short int16_t;
+ typedef signed int int32_t;
+ typedef unsigned char uint8_t;
+ typedef unsigned short uint16_t;
+ typedef unsigned int uint32_t;
+#else
+ typedef signed __int8 int8_t;
+ typedef signed __int16 int16_t;
+ typedef signed __int32 int32_t;
+ typedef unsigned __int8 uint8_t;
+ typedef unsigned __int16 uint16_t;
+ typedef unsigned __int32 uint32_t;
+#endif
+typedef signed __int64 int64_t;
+typedef unsigned __int64 uint64_t;
+
+
+// 7.18.1.2 Minimum-width integer types
+typedef int8_t int_least8_t;
+typedef int16_t int_least16_t;
+typedef int32_t int_least32_t;
+typedef int64_t int_least64_t;
+typedef uint8_t uint_least8_t;
+typedef uint16_t uint_least16_t;
+typedef uint32_t uint_least32_t;
+typedef uint64_t uint_least64_t;
+
+// 7.18.1.3 Fastest minimum-width integer types
+typedef int8_t int_fast8_t;
+typedef int16_t int_fast16_t;
+typedef int32_t int_fast32_t;
+typedef int64_t int_fast64_t;
+typedef uint8_t uint_fast8_t;
+typedef uint16_t uint_fast16_t;
+typedef uint32_t uint_fast32_t;
+typedef uint64_t uint_fast64_t;
+
+// 7.18.1.4 Integer types capable of holding object pointers
+#ifdef _WIN64 // [
+ typedef signed __int64 intptr_t;
+ typedef unsigned __int64 uintptr_t;
+#else // _WIN64 ][
+ typedef _W64 signed int intptr_t;
+ typedef _W64 unsigned int uintptr_t;
+#endif // _WIN64 ]
+
+// 7.18.1.5 Greatest-width integer types
+typedef int64_t intmax_t;
+typedef uint64_t uintmax_t;
+
+
+// 7.18.2 Limits of specified-width integer types
+
+#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259
+
+// 7.18.2.1 Limits of exact-width integer types
+#define INT8_MIN ((int8_t)_I8_MIN)
+#define INT8_MAX _I8_MAX
+#define INT16_MIN ((int16_t)_I16_MIN)
+#define INT16_MAX _I16_MAX
+#define INT32_MIN ((int32_t)_I32_MIN)
+#define INT32_MAX _I32_MAX
+#define INT64_MIN ((int64_t)_I64_MIN)
+#define INT64_MAX _I64_MAX
+#define UINT8_MAX _UI8_MAX
+#define UINT16_MAX _UI16_MAX
+#define UINT32_MAX _UI32_MAX
+#define UINT64_MAX _UI64_MAX
+
+// 7.18.2.2 Limits of minimum-width integer types
+#define INT_LEAST8_MIN INT8_MIN
+#define INT_LEAST8_MAX INT8_MAX
+#define INT_LEAST16_MIN INT16_MIN
+#define INT_LEAST16_MAX INT16_MAX
+#define INT_LEAST32_MIN INT32_MIN
+#define INT_LEAST32_MAX INT32_MAX
+#define INT_LEAST64_MIN INT64_MIN
+#define INT_LEAST64_MAX INT64_MAX
+#define UINT_LEAST8_MAX UINT8_MAX
+#define UINT_LEAST16_MAX UINT16_MAX
+#define UINT_LEAST32_MAX UINT32_MAX
+#define UINT_LEAST64_MAX UINT64_MAX
+
+// 7.18.2.3 Limits of fastest minimum-width integer types
+#define INT_FAST8_MIN INT8_MIN
+#define INT_FAST8_MAX INT8_MAX
+#define INT_FAST16_MIN INT16_MIN
+#define INT_FAST16_MAX INT16_MAX
+#define INT_FAST32_MIN INT32_MIN
+#define INT_FAST32_MAX INT32_MAX
+#define INT_FAST64_MIN INT64_MIN
+#define INT_FAST64_MAX INT64_MAX
+#define UINT_FAST8_MAX UINT8_MAX
+#define UINT_FAST16_MAX UINT16_MAX
+#define UINT_FAST32_MAX UINT32_MAX
+#define UINT_FAST64_MAX UINT64_MAX
+
+// 7.18.2.4 Limits of integer types capable of holding object pointers
+#ifdef _WIN64 // [
+# define INTPTR_MIN INT64_MIN
+# define INTPTR_MAX INT64_MAX
+# define UINTPTR_MAX UINT64_MAX
+#else // _WIN64 ][
+# define INTPTR_MIN INT32_MIN
+# define INTPTR_MAX INT32_MAX
+# define UINTPTR_MAX UINT32_MAX
+#endif // _WIN64 ]
+
+// 7.18.2.5 Limits of greatest-width integer types
+#define INTMAX_MIN INT64_MIN
+#define INTMAX_MAX INT64_MAX
+#define UINTMAX_MAX UINT64_MAX
+
+// 7.18.3 Limits of other integer types
+
+#ifdef _WIN64 // [
+# define PTRDIFF_MIN _I64_MIN
+# define PTRDIFF_MAX _I64_MAX
+#else // _WIN64 ][
+# define PTRDIFF_MIN _I32_MIN
+# define PTRDIFF_MAX _I32_MAX
+#endif // _WIN64 ]
+
+#define SIG_ATOMIC_MIN INT_MIN
+#define SIG_ATOMIC_MAX INT_MAX
+
+#ifndef SIZE_MAX // [
+# ifdef _WIN64 // [
+# define SIZE_MAX _UI64_MAX
+# else // _WIN64 ][
+# define SIZE_MAX _UI32_MAX
+# endif // _WIN64 ]
+#endif // SIZE_MAX ]
+
+// WCHAR_MIN and WCHAR_MAX are also defined in <wchar.h>
+#ifndef WCHAR_MIN // [
+# define WCHAR_MIN 0
+#endif // WCHAR_MIN ]
+#ifndef WCHAR_MAX // [
+# define WCHAR_MAX _UI16_MAX
+#endif // WCHAR_MAX ]
+
+#define WINT_MIN 0
+#define WINT_MAX _UI16_MAX
+
+#endif // __STDC_LIMIT_MACROS ]
+
+
+// 7.18.4 Limits of other integer types
+
+#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260
+
+// 7.18.4.1 Macros for minimum-width integer constants
+
+#define INT8_C(val) val##i8
+#define INT16_C(val) val##i16
+#define INT32_C(val) val##i32
+#define INT64_C(val) val##i64
+
+#define UINT8_C(val) val##ui8
+#define UINT16_C(val) val##ui16
+#define UINT32_C(val) val##ui32
+#define UINT64_C(val) val##ui64
+
+// 7.18.4.2 Macros for greatest-width integer constants
+#define INTMAX_C INT64_C
+#define UINTMAX_C UINT64_C
+
+#endif // __STDC_CONSTANT_MACROS ]
+
+
+#endif // _MSC_STDINT_H_ ]
diff --git a/contrib/jemalloc/include/msvc_compat/strings.h b/contrib/jemalloc/include/msvc_compat/strings.h
new file mode 100644
index 000000000000..996f256ce84e
--- /dev/null
+++ b/contrib/jemalloc/include/msvc_compat/strings.h
@@ -0,0 +1,58 @@
+#ifndef strings_h
+#define strings_h
+
+/* MSVC doesn't define ffs/ffsl. This dummy strings.h header is provided
+ * for both */
+#ifdef _MSC_VER
+# include <intrin.h>
+# pragma intrinsic(_BitScanForward)
+static __forceinline int ffsl(long x) {
+ unsigned long i;
+
+ if (_BitScanForward(&i, x)) {
+ return i + 1;
+ }
+ return 0;
+}
+
+static __forceinline int ffs(int x) {
+ return ffsl(x);
+}
+
+# ifdef _M_X64
+# pragma intrinsic(_BitScanForward64)
+# endif
+
+static __forceinline int ffsll(unsigned __int64 x) {
+ unsigned long i;
+#ifdef _M_X64
+ if (_BitScanForward64(&i, x)) {
+ return i + 1;
+ }
+ return 0;
+#else
+// Fallback for 32-bit build where 64-bit version not available
+// assuming little endian
+ union {
+ unsigned __int64 ll;
+ unsigned long l[2];
+ } s;
+
+ s.ll = x;
+
+ if (_BitScanForward(&i, s.l[0])) {
+ return i + 1;
+ } else if(_BitScanForward(&i, s.l[1])) {
+ return i + 33;
+ }
+ return 0;
+#endif
+}
+
+#else
+# define ffsll(x) __builtin_ffsll(x)
+# define ffsl(x) __builtin_ffsl(x)
+# define ffs(x) __builtin_ffs(x)
+#endif
+
+#endif /* strings_h */
diff --git a/contrib/jemalloc/include/msvc_compat/windows_extra.h b/contrib/jemalloc/include/msvc_compat/windows_extra.h
new file mode 100644
index 000000000000..a6ebb9306f24
--- /dev/null
+++ b/contrib/jemalloc/include/msvc_compat/windows_extra.h
@@ -0,0 +1,6 @@
+#ifndef MSVC_COMPAT_WINDOWS_EXTRA_H
+#define MSVC_COMPAT_WINDOWS_EXTRA_H
+
+#include <errno.h>
+
+#endif /* MSVC_COMPAT_WINDOWS_EXTRA_H */
diff --git a/contrib/jemalloc/jemalloc.pc.in b/contrib/jemalloc/jemalloc.pc.in
new file mode 100644
index 000000000000..c428a86dc39a
--- /dev/null
+++ b/contrib/jemalloc/jemalloc.pc.in
@@ -0,0 +1,12 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+install_suffix=@install_suffix@
+
+Name: jemalloc
+Description: A general purpose malloc(3) implementation that emphasizes fragmentation avoidance and scalable concurrency support.
+URL: http://jemalloc.net/
+Version: @jemalloc_version_major@.@jemalloc_version_minor@.@jemalloc_version_bugfix@_@jemalloc_version_nrev@
+Cflags: -I${includedir}
+Libs: -L${libdir} -ljemalloc${install_suffix}
diff --git a/contrib/jemalloc/m4/ax_cxx_compile_stdcxx.m4 b/contrib/jemalloc/m4/ax_cxx_compile_stdcxx.m4
new file mode 100644
index 000000000000..43087b2e6889
--- /dev/null
+++ b/contrib/jemalloc/m4/ax_cxx_compile_stdcxx.m4
@@ -0,0 +1,951 @@
+# ===========================================================================
+# https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html
+# ===========================================================================
+#
+# SYNOPSIS
+#
+# AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional])
+#
+# DESCRIPTION
+#
+# Check for baseline language coverage in the compiler for the specified
+# version of the C++ standard. If necessary, add switches to CXX and
+# CXXCPP to enable support. VERSION may be '11' (for the C++11 standard)
+# or '14' (for the C++14 standard).
+#
+# The second argument, if specified, indicates whether you insist on an
+# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g.
+# -std=c++11). If neither is specified, you get whatever works, with
+# preference for an extended mode.
+#
+# The third argument, if specified 'mandatory' or if left unspecified,
+# indicates that baseline support for the specified C++ standard is
+# required and that the macro should error out if no mode with that
+# support is found. If specified 'optional', then configuration proceeds
+# regardless, after defining HAVE_CXX${VERSION} if and only if a
+# supporting mode is found.
+#
+# LICENSE
+#
+# Copyright (c) 2008 Benjamin Kosnik <bkoz@redhat.com>
+# Copyright (c) 2012 Zack Weinberg <zackw@panix.com>
+# Copyright (c) 2013 Roy Stogner <roystgnr@ices.utexas.edu>
+# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov <sokolov@google.com>
+# Copyright (c) 2015 Paul Norman <penorman@mac.com>
+# Copyright (c) 2015 Moritz Klammler <moritz@klammler.eu>
+# Copyright (c) 2016, 2018 Krzesimir Nowak <qdlacz@gmail.com>
+# Copyright (c) 2019 Enji Cooper <yaneurabeya@gmail.com>
+#
+# Copying and distribution of this file, with or without modification, are
+# permitted in any medium without royalty provided the copyright notice
+# and this notice are preserved. This file is offered as-is, without any
+# warranty.
+
+#serial 11
+
+dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro
+dnl (serial version number 13).
+
+AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl
+ m4_if([$1], [11], [ax_cxx_compile_alternatives="11 0x"],
+ [$1], [14], [ax_cxx_compile_alternatives="14 1y"],
+ [$1], [17], [ax_cxx_compile_alternatives="17 1z"],
+ [m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl
+ m4_if([$2], [], [],
+ [$2], [ext], [],
+ [$2], [noext], [],
+ [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl
+ m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true],
+ [$3], [mandatory], [ax_cxx_compile_cxx$1_required=true],
+ [$3], [optional], [ax_cxx_compile_cxx$1_required=false],
+ [m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])])
+ AC_LANG_PUSH([C++])dnl
+ ac_success=no
+
+ m4_if([$2], [noext], [], [dnl
+ if test x$ac_success = xno; then
+ for alternative in ${ax_cxx_compile_alternatives}; do
+ switch="-std=gnu++${alternative}"
+ cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
+ AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
+ $cachevar,
+ [ac_save_CXX="$CXX"
+ CXX="$CXX $switch"
+ AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
+ [eval $cachevar=yes],
+ [eval $cachevar=no])
+ CXX="$ac_save_CXX"])
+ if eval test x\$$cachevar = xyes; then
+ CXX="$CXX $switch"
+ if test -n "$CXXCPP" ; then
+ CXXCPP="$CXXCPP $switch"
+ fi
+ ac_success=yes
+ break
+ fi
+ done
+ fi])
+
+ m4_if([$2], [ext], [], [dnl
+ if test x$ac_success = xno; then
+ dnl HP's aCC needs +std=c++11 according to:
+ dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf
+ dnl Cray's crayCC needs "-h std=c++11"
+ for alternative in ${ax_cxx_compile_alternatives}; do
+ for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do
+ cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch])
+ AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch,
+ $cachevar,
+ [ac_save_CXX="$CXX"
+ CXX="$CXX $switch"
+ AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
+ [eval $cachevar=yes],
+ [eval $cachevar=no])
+ CXX="$ac_save_CXX"])
+ if eval test x\$$cachevar = xyes; then
+ CXX="$CXX $switch"
+ if test -n "$CXXCPP" ; then
+ CXXCPP="$CXXCPP $switch"
+ fi
+ ac_success=yes
+ break
+ fi
+ done
+ if test x$ac_success = xyes; then
+ break
+ fi
+ done
+ fi])
+ AC_LANG_POP([C++])
+ if test x$ax_cxx_compile_cxx$1_required = xtrue; then
+ if test x$ac_success = xno; then
+ AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.])
+ fi
+ fi
+ if test x$ac_success = xno; then
+ HAVE_CXX$1=0
+ AC_MSG_NOTICE([No compiler with C++$1 support was found])
+ else
+ HAVE_CXX$1=1
+ AC_DEFINE(HAVE_CXX$1,1,
+ [define if the compiler supports basic C++$1 syntax])
+ fi
+ AC_SUBST(HAVE_CXX$1)
+])
+
+
+dnl Test body for checking C++11 support
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11],
+ _AX_CXX_COMPILE_STDCXX_testbody_new_in_11
+)
+
+
+dnl Test body for checking C++14 support
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14],
+ _AX_CXX_COMPILE_STDCXX_testbody_new_in_11
+ _AX_CXX_COMPILE_STDCXX_testbody_new_in_14
+)
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_17],
+ _AX_CXX_COMPILE_STDCXX_testbody_new_in_11
+ _AX_CXX_COMPILE_STDCXX_testbody_new_in_14
+ _AX_CXX_COMPILE_STDCXX_testbody_new_in_17
+)
+
+dnl Tests for new features in C++11
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[
+
+// If the compiler admits that it is not ready for C++11, why torture it?
+// Hopefully, this will speed up the test.
+
+#ifndef __cplusplus
+
+#error "This is not a C++ compiler"
+
+#elif __cplusplus < 201103L
+
+#error "This is not a C++11 compiler"
+
+#else
+
+namespace cxx11
+{
+
+ namespace test_static_assert
+ {
+
+ template <typename T>
+ struct check
+ {
+ static_assert(sizeof(int) <= sizeof(T), "not big enough");
+ };
+
+ }
+
+ namespace test_final_override
+ {
+
+ struct Base
+ {
+ virtual ~Base() {}
+ virtual void f() {}
+ };
+
+ struct Derived : public Base
+ {
+ virtual ~Derived() override {}
+ virtual void f() override {}
+ };
+
+ }
+
+ namespace test_double_right_angle_brackets
+ {
+
+ template < typename T >
+ struct check {};
+
+ typedef check<void> single_type;
+ typedef check<check<void>> double_type;
+ typedef check<check<check<void>>> triple_type;
+ typedef check<check<check<check<void>>>> quadruple_type;
+
+ }
+
+ namespace test_decltype
+ {
+
+ int
+ f()
+ {
+ int a = 1;
+ decltype(a) b = 2;
+ return a + b;
+ }
+
+ }
+
+ namespace test_type_deduction
+ {
+
+ template < typename T1, typename T2 >
+ struct is_same
+ {
+ static const bool value = false;
+ };
+
+ template < typename T >
+ struct is_same<T, T>
+ {
+ static const bool value = true;
+ };
+
+ template < typename T1, typename T2 >
+ auto
+ add(T1 a1, T2 a2) -> decltype(a1 + a2)
+ {
+ return a1 + a2;
+ }
+
+ int
+ test(const int c, volatile int v)
+ {
+ static_assert(is_same<int, decltype(0)>::value == true, "");
+ static_assert(is_same<int, decltype(c)>::value == false, "");
+ static_assert(is_same<int, decltype(v)>::value == false, "");
+ auto ac = c;
+ auto av = v;
+ auto sumi = ac + av + 'x';
+ auto sumf = ac + av + 1.0;
+ static_assert(is_same<int, decltype(ac)>::value == true, "");
+ static_assert(is_same<int, decltype(av)>::value == true, "");
+ static_assert(is_same<int, decltype(sumi)>::value == true, "");
+ static_assert(is_same<int, decltype(sumf)>::value == false, "");
+ static_assert(is_same<int, decltype(add(c, v))>::value == true, "");
+ return (sumf > 0.0) ? sumi : add(c, v);
+ }
+
+ }
+
+ namespace test_noexcept
+ {
+
+ int f() { return 0; }
+ int g() noexcept { return 0; }
+
+ static_assert(noexcept(f()) == false, "");
+ static_assert(noexcept(g()) == true, "");
+
+ }
+
+ namespace test_constexpr
+ {
+
+ template < typename CharT >
+ unsigned long constexpr
+ strlen_c_r(const CharT *const s, const unsigned long acc) noexcept
+ {
+ return *s ? strlen_c_r(s + 1, acc + 1) : acc;
+ }
+
+ template < typename CharT >
+ unsigned long constexpr
+ strlen_c(const CharT *const s) noexcept
+ {
+ return strlen_c_r(s, 0UL);
+ }
+
+ static_assert(strlen_c("") == 0UL, "");
+ static_assert(strlen_c("1") == 1UL, "");
+ static_assert(strlen_c("example") == 7UL, "");
+ static_assert(strlen_c("another\0example") == 7UL, "");
+
+ }
+
+ namespace test_rvalue_references
+ {
+
+ template < int N >
+ struct answer
+ {
+ static constexpr int value = N;
+ };
+
+ answer<1> f(int&) { return answer<1>(); }
+ answer<2> f(const int&) { return answer<2>(); }
+ answer<3> f(int&&) { return answer<3>(); }
+
+ void
+ test()
+ {
+ int i = 0;
+ const int c = 0;
+ static_assert(decltype(f(i))::value == 1, "");
+ static_assert(decltype(f(c))::value == 2, "");
+ static_assert(decltype(f(0))::value == 3, "");
+ }
+
+ }
+
+ namespace test_uniform_initialization
+ {
+
+ struct test
+ {
+ static const int zero {};
+ static const int one {1};
+ };
+
+ static_assert(test::zero == 0, "");
+ static_assert(test::one == 1, "");
+
+ }
+
+ namespace test_lambdas
+ {
+
+ void
+ test1()
+ {
+ auto lambda1 = [](){};
+ auto lambda2 = lambda1;
+ lambda1();
+ lambda2();
+ }
+
+ int
+ test2()
+ {
+ auto a = [](int i, int j){ return i + j; }(1, 2);
+ auto b = []() -> int { return '0'; }();
+ auto c = [=](){ return a + b; }();
+ auto d = [&](){ return c; }();
+ auto e = [a, &b](int x) mutable {
+ const auto identity = [](int y){ return y; };
+ for (auto i = 0; i < a; ++i)
+ a += b--;
+ return x + identity(a + b);
+ }(0);
+ return a + b + c + d + e;
+ }
+
+ int
+ test3()
+ {
+ const auto nullary = [](){ return 0; };
+ const auto unary = [](int x){ return x; };
+ using nullary_t = decltype(nullary);
+ using unary_t = decltype(unary);
+ const auto higher1st = [](nullary_t f){ return f(); };
+ const auto higher2nd = [unary](nullary_t f1){
+ return [unary, f1](unary_t f2){ return f2(unary(f1())); };
+ };
+ return higher1st(nullary) + higher2nd(nullary)(unary);
+ }
+
+ }
+
+ namespace test_variadic_templates
+ {
+
+ template <int...>
+ struct sum;
+
+ template <int N0, int... N1toN>
+ struct sum<N0, N1toN...>
+ {
+ static constexpr auto value = N0 + sum<N1toN...>::value;
+ };
+
+ template <>
+ struct sum<>
+ {
+ static constexpr auto value = 0;
+ };
+
+ static_assert(sum<>::value == 0, "");
+ static_assert(sum<1>::value == 1, "");
+ static_assert(sum<23>::value == 23, "");
+ static_assert(sum<1, 2>::value == 3, "");
+ static_assert(sum<5, 5, 11>::value == 21, "");
+ static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, "");
+
+ }
+
+ // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae
+ // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function
+ // because of this.
+ namespace test_template_alias_sfinae
+ {
+
+ struct foo {};
+
+ template<typename T>
+ using member = typename T::member_type;
+
+ template<typename T>
+ void func(...) {}
+
+ template<typename T>
+ void func(member<T>*) {}
+
+ void test();
+
+ void test() { func<foo>(0); }
+
+ }
+
+} // namespace cxx11
+
+#endif // __cplusplus >= 201103L
+
+]])
+
+
+dnl Tests for new features in C++14
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[
+
+// If the compiler admits that it is not ready for C++14, why torture it?
+// Hopefully, this will speed up the test.
+
+#ifndef __cplusplus
+
+#error "This is not a C++ compiler"
+
+#elif __cplusplus < 201402L
+
+#error "This is not a C++14 compiler"
+
+#else
+
+namespace cxx14
+{
+
+ namespace test_polymorphic_lambdas
+ {
+
+ int
+ test()
+ {
+ const auto lambda = [](auto&&... args){
+ const auto istiny = [](auto x){
+ return (sizeof(x) == 1UL) ? 1 : 0;
+ };
+ const int aretiny[] = { istiny(args)... };
+ return aretiny[0];
+ };
+ return lambda(1, 1L, 1.0f, '1');
+ }
+
+ }
+
+ namespace test_binary_literals
+ {
+
+ constexpr auto ivii = 0b0000000000101010;
+ static_assert(ivii == 42, "wrong value");
+
+ }
+
+ namespace test_generalized_constexpr
+ {
+
+ template < typename CharT >
+ constexpr unsigned long
+ strlen_c(const CharT *const s) noexcept
+ {
+ auto length = 0UL;
+ for (auto p = s; *p; ++p)
+ ++length;
+ return length;
+ }
+
+ static_assert(strlen_c("") == 0UL, "");
+ static_assert(strlen_c("x") == 1UL, "");
+ static_assert(strlen_c("test") == 4UL, "");
+ static_assert(strlen_c("another\0test") == 7UL, "");
+
+ }
+
+ namespace test_lambda_init_capture
+ {
+
+ int
+ test()
+ {
+ auto x = 0;
+ const auto lambda1 = [a = x](int b){ return a + b; };
+ const auto lambda2 = [a = lambda1(x)](){ return a; };
+ return lambda2();
+ }
+
+ }
+
+ namespace test_digit_separators
+ {
+
+ constexpr auto ten_million = 100'000'000;
+ static_assert(ten_million == 100000000, "");
+
+ }
+
+ namespace test_return_type_deduction
+ {
+
+ auto f(int& x) { return x; }
+ decltype(auto) g(int& x) { return x; }
+
+ template < typename T1, typename T2 >
+ struct is_same
+ {
+ static constexpr auto value = false;
+ };
+
+ template < typename T >
+ struct is_same<T, T>
+ {
+ static constexpr auto value = true;
+ };
+
+ int
+ test()
+ {
+ auto x = 0;
+ static_assert(is_same<int, decltype(f(x))>::value, "");
+ static_assert(is_same<int&, decltype(g(x))>::value, "");
+ return x;
+ }
+
+ }
+
+} // namespace cxx14
+
+#endif // __cplusplus >= 201402L
+
+]])
+
+
+dnl Tests for new features in C++17
+
+m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_17], [[
+
+// If the compiler admits that it is not ready for C++17, why torture it?
+// Hopefully, this will speed up the test.
+
+#ifndef __cplusplus
+
+#error "This is not a C++ compiler"
+
+#elif __cplusplus < 201703L
+
+#error "This is not a C++17 compiler"
+
+#else
+
+#include <initializer_list>
+#include <utility>
+#include <type_traits>
+
+namespace cxx17
+{
+
+ namespace test_constexpr_lambdas
+ {
+
+ constexpr int foo = [](){return 42;}();
+
+ }
+
+ namespace test::nested_namespace::definitions
+ {
+
+ }
+
+ namespace test_fold_expression
+ {
+
+ template<typename... Args>
+ int multiply(Args... args)
+ {
+ return (args * ... * 1);
+ }
+
+ template<typename... Args>
+ bool all(Args... args)
+ {
+ return (args && ...);
+ }
+
+ }
+
+ namespace test_extended_static_assert
+ {
+
+ static_assert (true);
+
+ }
+
+ namespace test_auto_brace_init_list
+ {
+
+ auto foo = {5};
+ auto bar {5};
+
+ static_assert(std::is_same<std::initializer_list<int>, decltype(foo)>::value);
+ static_assert(std::is_same<int, decltype(bar)>::value);
+ }
+
+ namespace test_typename_in_template_template_parameter
+ {
+
+ template<template<typename> typename X> struct D;
+
+ }
+
+ namespace test_fallthrough_nodiscard_maybe_unused_attributes
+ {
+
+ int f1()
+ {
+ return 42;
+ }
+
+ [[nodiscard]] int f2()
+ {
+ [[maybe_unused]] auto unused = f1();
+
+ switch (f1())
+ {
+ case 17:
+ f1();
+ [[fallthrough]];
+ case 42:
+ f1();
+ }
+ return f1();
+ }
+
+ }
+
+ namespace test_extended_aggregate_initialization
+ {
+
+ struct base1
+ {
+ int b1, b2 = 42;
+ };
+
+ struct base2
+ {
+ base2() {
+ b3 = 42;
+ }
+ int b3;
+ };
+
+ struct derived : base1, base2
+ {
+ int d;
+ };
+
+ derived d1 {{1, 2}, {}, 4}; // full initialization
+ derived d2 {{}, {}, 4}; // value-initialized bases
+
+ }
+
+ namespace test_general_range_based_for_loop
+ {
+
+ struct iter
+ {
+ int i;
+
+ int& operator* ()
+ {
+ return i;
+ }
+
+ const int& operator* () const
+ {
+ return i;
+ }
+
+ iter& operator++()
+ {
+ ++i;
+ return *this;
+ }
+ };
+
+ struct sentinel
+ {
+ int i;
+ };
+
+ bool operator== (const iter& i, const sentinel& s)
+ {
+ return i.i == s.i;
+ }
+
+ bool operator!= (const iter& i, const sentinel& s)
+ {
+ return !(i == s);
+ }
+
+ struct range
+ {
+ iter begin() const
+ {
+ return {0};
+ }
+
+ sentinel end() const
+ {
+ return {5};
+ }
+ };
+
+ void f()
+ {
+ range r {};
+
+ for (auto i : r)
+ {
+ [[maybe_unused]] auto v = i;
+ }
+ }
+
+ }
+
+ namespace test_lambda_capture_asterisk_this_by_value
+ {
+
+ struct t
+ {
+ int i;
+ int foo()
+ {
+ return [*this]()
+ {
+ return i;
+ }();
+ }
+ };
+
+ }
+
+ namespace test_enum_class_construction
+ {
+
+ enum class byte : unsigned char
+ {};
+
+ byte foo {42};
+
+ }
+
+ namespace test_constexpr_if
+ {
+
+ template <bool cond>
+ int f ()
+ {
+ if constexpr(cond)
+ {
+ return 13;
+ }
+ else
+ {
+ return 42;
+ }
+ }
+
+ }
+
+ namespace test_selection_statement_with_initializer
+ {
+
+ int f()
+ {
+ return 13;
+ }
+
+ int f2()
+ {
+ if (auto i = f(); i > 0)
+ {
+ return 3;
+ }
+
+ switch (auto i = f(); i + 4)
+ {
+ case 17:
+ return 2;
+
+ default:
+ return 1;
+ }
+ }
+
+ }
+
+ namespace test_template_argument_deduction_for_class_templates
+ {
+
+ template <typename T1, typename T2>
+ struct pair
+ {
+ pair (T1 p1, T2 p2)
+ : m1 {p1},
+ m2 {p2}
+ {}
+
+ T1 m1;
+ T2 m2;
+ };
+
+ void f()
+ {
+ [[maybe_unused]] auto p = pair{13, 42u};
+ }
+
+ }
+
+ namespace test_non_type_auto_template_parameters
+ {
+
+ template <auto n>
+ struct B
+ {};
+
+ B<5> b1;
+ B<'a'> b2;
+
+ }
+
+ namespace test_structured_bindings
+ {
+
+ int arr[2] = { 1, 2 };
+ std::pair<int, int> pr = { 1, 2 };
+
+ auto f1() -> int(&)[2]
+ {
+ return arr;
+ }
+
+ auto f2() -> std::pair<int, int>&
+ {
+ return pr;
+ }
+
+ struct S
+ {
+ int x1 : 2;
+ volatile double y1;
+ };
+
+ S f3()
+ {
+ return {};
+ }
+
+ auto [ x1, y1 ] = f1();
+ auto& [ xr1, yr1 ] = f1();
+ auto [ x2, y2 ] = f2();
+ auto& [ xr2, yr2 ] = f2();
+ const auto [ x3, y3 ] = f3();
+
+ }
+
+ namespace test_exception_spec_type_system
+ {
+
+ struct Good {};
+ struct Bad {};
+
+ void g1() noexcept;
+ void g2();
+
+ template<typename T>
+ Bad
+ f(T*, T*);
+
+ template<typename T1, typename T2>
+ Good
+ f(T1*, T2*);
+
+ static_assert (std::is_same_v<Good, decltype(f(g1, g2))>);
+
+ }
+
+ namespace test_inline_variables
+ {
+
+ template<class T> void f(T)
+ {}
+
+ template<class T> inline T g(T)
+ {
+ return T{};
+ }
+
+ template<> inline void f<>(int)
+ {}
+
+ template<> int g<>(int)
+ {
+ return 5;
+ }
+
+ }
+
+} // namespace cxx17
+
+#endif // __cplusplus < 201703L
+
+]])
diff --git a/contrib/jemalloc/run_tests.sh b/contrib/jemalloc/run_tests.sh
new file mode 100755
index 000000000000..b434f15b335e
--- /dev/null
+++ b/contrib/jemalloc/run_tests.sh
@@ -0,0 +1 @@
+$(dirname "$)")/scripts/gen_run_tests.py | bash
diff --git a/contrib/jemalloc/scripts/check-formatting.sh b/contrib/jemalloc/scripts/check-formatting.sh
new file mode 100755
index 000000000000..68cafd8e546e
--- /dev/null
+++ b/contrib/jemalloc/scripts/check-formatting.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+
+# The files that need to be properly formatted. We'll grow this incrementally
+# until it includes all the jemalloc source files (as we convert things over),
+# and then just replace it with
+# find -name '*.c' -o -name '*.h' -o -name '*.cpp
+FILES=(
+)
+
+if command -v clang-format &> /dev/null; then
+ CLANG_FORMAT="clang-format"
+elif command -v clang-format-8 &> /dev/null; then
+ CLANG_FORMAT="clang-format-8"
+else
+ echo "Couldn't find clang-format."
+fi
+
+if ! $CLANG_FORMAT -version | grep "version 8\." &> /dev/null; then
+ echo "clang-format is the wrong version."
+ exit 1
+fi
+
+for file in ${FILES[@]}; do
+ if ! cmp --silent $file <($CLANG_FORMAT $file) &> /dev/null; then
+ echo "Error: $file is not clang-formatted"
+ exit 1
+ fi
+done
diff --git a/contrib/jemalloc/scripts/freebsd/before_install.sh b/contrib/jemalloc/scripts/freebsd/before_install.sh
new file mode 100644
index 000000000000..f2bee321f73f
--- /dev/null
+++ b/contrib/jemalloc/scripts/freebsd/before_install.sh
@@ -0,0 +1,3 @@
+#!/bin/tcsh
+
+su -m root -c 'pkg install -y git'
diff --git a/contrib/jemalloc/scripts/freebsd/before_script.sh b/contrib/jemalloc/scripts/freebsd/before_script.sh
new file mode 100644
index 000000000000..29406f6fbbdf
--- /dev/null
+++ b/contrib/jemalloc/scripts/freebsd/before_script.sh
@@ -0,0 +1,10 @@
+#!/bin/tcsh
+
+autoconf
+# We don't perfectly track freebsd stdlib.h definitions. This is fine when
+# we count as a system header, but breaks otherwise, like during these
+# tests.
+./configure --with-jemalloc-prefix=ci_ ${COMPILER_FLAGS:+ CC="$CC $COMPILER_FLAGS" CXX="$CXX $COMPILER_FLAGS"} $CONFIGURE_FLAGS
+JE_NCPUS=`sysctl -n kern.smp.cpus`
+gmake -j${JE_NCPUS}
+gmake -j${JE_NCPUS} tests
diff --git a/contrib/jemalloc/scripts/freebsd/script.sh b/contrib/jemalloc/scripts/freebsd/script.sh
new file mode 100644
index 000000000000..d9c53a201f5e
--- /dev/null
+++ b/contrib/jemalloc/scripts/freebsd/script.sh
@@ -0,0 +1,3 @@
+#!/bin/tcsh
+
+gmake check
diff --git a/contrib/jemalloc/scripts/gen_run_tests.py b/contrib/jemalloc/scripts/gen_run_tests.py
new file mode 100755
index 000000000000..7c3075f9f6a6
--- /dev/null
+++ b/contrib/jemalloc/scripts/gen_run_tests.py
@@ -0,0 +1,130 @@
+#!/usr/bin/env python3
+
+import sys
+from itertools import combinations
+from os import uname
+from multiprocessing import cpu_count
+from subprocess import call
+
+# Later, we want to test extended vaddr support. Apparently, the "real" way of
+# checking this is flaky on OS X.
+bits_64 = sys.maxsize > 2**32
+
+nparallel = cpu_count() * 2
+
+uname = uname()[0]
+
+if call("command -v gmake", shell=True) == 0:
+ make_cmd = 'gmake'
+else:
+ make_cmd = 'make'
+
+def powerset(items):
+ result = []
+ for i in range(len(items) + 1):
+ result += combinations(items, i)
+ return result
+
+possible_compilers = []
+for cc, cxx in (['gcc', 'g++'], ['clang', 'clang++']):
+ try:
+ cmd_ret = call([cc, "-v"])
+ if cmd_ret == 0:
+ possible_compilers.append((cc, cxx))
+ except:
+ pass
+possible_compiler_opts = [
+ '-m32',
+]
+possible_config_opts = [
+ '--enable-debug',
+ '--enable-prof',
+ '--disable-stats',
+ '--enable-opt-safety-checks',
+ '--with-lg-page=16',
+]
+if bits_64:
+ possible_config_opts.append('--with-lg-vaddr=56')
+
+possible_malloc_conf_opts = [
+ 'tcache:false',
+ 'dss:primary',
+ 'percpu_arena:percpu',
+ 'background_thread:true',
+]
+
+print('set -e')
+print('if [ -f Makefile ] ; then %(make_cmd)s relclean ; fi' % {'make_cmd':
+ make_cmd})
+print('autoconf')
+print('rm -rf run_tests.out')
+print('mkdir run_tests.out')
+print('cd run_tests.out')
+
+ind = 0
+for cc, cxx in possible_compilers:
+ for compiler_opts in powerset(possible_compiler_opts):
+ for config_opts in powerset(possible_config_opts):
+ for malloc_conf_opts in powerset(possible_malloc_conf_opts):
+ if cc == 'clang' \
+ and '-m32' in possible_compiler_opts \
+ and '--enable-prof' in config_opts:
+ continue
+ config_line = (
+ 'EXTRA_CFLAGS=-Werror EXTRA_CXXFLAGS=-Werror '
+ + 'CC="{} {}" '.format(cc, " ".join(compiler_opts))
+ + 'CXX="{} {}" '.format(cxx, " ".join(compiler_opts))
+ + '../../configure '
+ + " ".join(config_opts) + (' --with-malloc-conf=' +
+ ",".join(malloc_conf_opts) if len(malloc_conf_opts) > 0
+ else '')
+ )
+
+ # We don't want to test large vaddr spaces in 32-bit mode.
+ if ('-m32' in compiler_opts and '--with-lg-vaddr=56' in
+ config_opts):
+ continue
+
+ # Per CPU arenas are only supported on Linux.
+ linux_supported = ('percpu_arena:percpu' in malloc_conf_opts \
+ or 'background_thread:true' in malloc_conf_opts)
+ # Heap profiling and dss are not supported on OS X.
+ darwin_unsupported = ('--enable-prof' in config_opts or \
+ 'dss:primary' in malloc_conf_opts)
+ if (uname == 'Linux' and linux_supported) \
+ or (not linux_supported and (uname != 'Darwin' or \
+ not darwin_unsupported)):
+ print("""cat <<EOF > run_test_%(ind)d.sh
+#!/bin/sh
+
+set -e
+
+abort() {
+ echo "==> Error" >> run_test.log
+ echo "Error; see run_tests.out/run_test_%(ind)d.out/run_test.log"
+ exit 255 # Special exit code tells xargs to terminate.
+}
+
+# Environment variables are not supported.
+run_cmd() {
+ echo "==> \$@" >> run_test.log
+ \$@ >> run_test.log 2>&1 || abort
+}
+
+echo "=> run_test_%(ind)d: %(config_line)s"
+mkdir run_test_%(ind)d.out
+cd run_test_%(ind)d.out
+
+echo "==> %(config_line)s" >> run_test.log
+%(config_line)s >> run_test.log 2>&1 || abort
+
+run_cmd %(make_cmd)s all tests
+run_cmd %(make_cmd)s check
+run_cmd %(make_cmd)s distclean
+EOF
+chmod 755 run_test_%(ind)d.sh""" % {'ind': ind, 'config_line': config_line,
+ 'make_cmd': make_cmd})
+ ind += 1
+
+print('for i in `seq 0 %(last_ind)d` ; do echo run_test_${i}.sh ; done | xargs'
+ ' -P %(nparallel)d -n 1 sh' % {'last_ind': ind-1, 'nparallel': nparallel})
diff --git a/contrib/jemalloc/scripts/gen_travis.py b/contrib/jemalloc/scripts/gen_travis.py
new file mode 100755
index 000000000000..4366a066eeab
--- /dev/null
+++ b/contrib/jemalloc/scripts/gen_travis.py
@@ -0,0 +1,327 @@
+#!/usr/bin/env python3
+
+from itertools import combinations, chain
+from enum import Enum, auto
+
+
+LINUX = 'linux'
+OSX = 'osx'
+WINDOWS = 'windows'
+FREEBSD = 'freebsd'
+
+
+AMD64 = 'amd64'
+ARM64 = 'arm64'
+PPC64LE = 'ppc64le'
+
+
+TRAVIS_TEMPLATE = """\
+# This config file is generated by ./scripts/gen_travis.py.
+# Do not edit by hand.
+
+# We use 'minimal', because 'generic' makes Windows VMs hang at startup. Also
+# the software provided by 'generic' is simply not needed for our tests.
+# Differences are explained here:
+# https://docs.travis-ci.com/user/languages/minimal-and-generic/
+language: minimal
+dist: focal
+
+jobs:
+ include:
+{jobs}
+
+before_install:
+ - |-
+ if test -f "./scripts/$TRAVIS_OS_NAME/before_install.sh"; then
+ source ./scripts/$TRAVIS_OS_NAME/before_install.sh
+ fi
+
+before_script:
+ - |-
+ if test -f "./scripts/$TRAVIS_OS_NAME/before_script.sh"; then
+ source ./scripts/$TRAVIS_OS_NAME/before_script.sh
+ else
+ scripts/gen_travis.py > travis_script && diff .travis.yml travis_script
+ autoconf
+ # If COMPILER_FLAGS are not empty, add them to CC and CXX
+ ./configure ${{COMPILER_FLAGS:+ CC="$CC $COMPILER_FLAGS" \
+CXX="$CXX $COMPILER_FLAGS"}} $CONFIGURE_FLAGS
+ make -j3
+ make -j3 tests
+ fi
+
+script:
+ - |-
+ if test -f "./scripts/$TRAVIS_OS_NAME/script.sh"; then
+ source ./scripts/$TRAVIS_OS_NAME/script.sh
+ else
+ make check
+ fi
+"""
+
+
+class Option(object):
+ class Type:
+ COMPILER = auto()
+ COMPILER_FLAG = auto()
+ CONFIGURE_FLAG = auto()
+ MALLOC_CONF = auto()
+ FEATURE = auto()
+
+ def __init__(self, type, value):
+ self.type = type
+ self.value = value
+
+ @staticmethod
+ def as_compiler(value):
+ return Option(Option.Type.COMPILER, value)
+
+ @staticmethod
+ def as_compiler_flag(value):
+ return Option(Option.Type.COMPILER_FLAG, value)
+
+ @staticmethod
+ def as_configure_flag(value):
+ return Option(Option.Type.CONFIGURE_FLAG, value)
+
+ @staticmethod
+ def as_malloc_conf(value):
+ return Option(Option.Type.MALLOC_CONF, value)
+
+ @staticmethod
+ def as_feature(value):
+ return Option(Option.Type.FEATURE, value)
+
+ def __eq__(self, obj):
+ return (isinstance(obj, Option) and obj.type == self.type
+ and obj.value == self.value)
+
+
+# The 'default' configuration is gcc, on linux, with no compiler or configure
+# flags. We also test with clang, -m32, --enable-debug, --enable-prof,
+# --disable-stats, and --with-malloc-conf=tcache:false. To avoid abusing
+# travis though, we don't test all 2**7 = 128 possible combinations of these;
+# instead, we only test combinations of up to 2 'unusual' settings, under the
+# hope that bugs involving interactions of such settings are rare.
+MAX_UNUSUAL_OPTIONS = 2
+
+
+GCC = Option.as_compiler('CC=gcc CXX=g++')
+CLANG = Option.as_compiler('CC=clang CXX=clang++')
+CL = Option.as_compiler('CC=cl.exe CXX=cl.exe')
+
+
+compilers_unusual = [CLANG,]
+
+
+CROSS_COMPILE_32BIT = Option.as_feature('CROSS_COMPILE_32BIT')
+feature_unusuals = [CROSS_COMPILE_32BIT]
+
+
+configure_flag_unusuals = [Option.as_configure_flag(opt) for opt in (
+ '--enable-debug',
+ '--enable-prof',
+ '--disable-stats',
+ '--disable-libdl',
+ '--enable-opt-safety-checks',
+ '--with-lg-page=16',
+)]
+
+
+malloc_conf_unusuals = [Option.as_malloc_conf(opt) for opt in (
+ 'tcache:false',
+ 'dss:primary',
+ 'percpu_arena:percpu',
+ 'background_thread:true',
+)]
+
+
+all_unusuals = (compilers_unusual + feature_unusuals
+ + configure_flag_unusuals + malloc_conf_unusuals)
+
+
+def get_extra_cflags(os, compiler):
+ if os == FREEBSD:
+ return []
+
+ if os == WINDOWS:
+ # For non-CL compilers under Windows (for now it's only MinGW-GCC),
+ # -fcommon needs to be specified to correctly handle multiple
+ # 'malloc_conf' symbols and such, which are declared weak under Linux.
+ # Weak symbols don't work with MinGW-GCC.
+ if compiler != CL.value:
+ return ['-fcommon']
+ else:
+ return []
+
+ # We get some spurious errors when -Warray-bounds is enabled.
+ extra_cflags = ['-Werror', '-Wno-array-bounds']
+ if compiler == CLANG.value or os == OSX:
+ extra_cflags += [
+ '-Wno-unknown-warning-option',
+ '-Wno-ignored-attributes'
+ ]
+ if os == OSX:
+ extra_cflags += [
+ '-Wno-deprecated-declarations',
+ ]
+ return extra_cflags
+
+
+# Formats a job from a combination of flags
+def format_job(os, arch, combination):
+ compilers = [x.value for x in combination if x.type == Option.Type.COMPILER]
+ assert(len(compilers) <= 1)
+ compiler_flags = [x.value for x in combination if x.type == Option.Type.COMPILER_FLAG]
+ configure_flags = [x.value for x in combination if x.type == Option.Type.CONFIGURE_FLAG]
+ malloc_conf = [x.value for x in combination if x.type == Option.Type.MALLOC_CONF]
+ features = [x.value for x in combination if x.type == Option.Type.FEATURE]
+
+ if len(malloc_conf) > 0:
+ configure_flags.append('--with-malloc-conf=' + ','.join(malloc_conf))
+
+ if not compilers:
+ compiler = GCC.value
+ else:
+ compiler = compilers[0]
+
+ extra_environment_vars = ''
+ cross_compile = CROSS_COMPILE_32BIT.value in features
+ if os == LINUX and cross_compile:
+ compiler_flags.append('-m32')
+
+ features_str = ' '.join([' {}=yes'.format(feature) for feature in features])
+
+ stringify = lambda arr, name: ' {}="{}"'.format(name, ' '.join(arr)) if arr else ''
+ env_string = '{}{}{}{}{}{}'.format(
+ compiler,
+ features_str,
+ stringify(compiler_flags, 'COMPILER_FLAGS'),
+ stringify(configure_flags, 'CONFIGURE_FLAGS'),
+ stringify(get_extra_cflags(os, compiler), 'EXTRA_CFLAGS'),
+ extra_environment_vars)
+
+ job = ' - os: {}\n'.format(os)
+ job += ' arch: {}\n'.format(arch)
+ job += ' env: {}'.format(env_string)
+ return job
+
+
+def generate_unusual_combinations(unusuals, max_unusual_opts):
+ """
+ Generates different combinations of non-standard compilers, compiler flags,
+ configure flags and malloc_conf settings.
+
+ @param max_unusual_opts: Limit of unusual options per combination.
+ """
+ return chain.from_iterable(
+ [combinations(unusuals, i) for i in range(max_unusual_opts + 1)])
+
+
+def included(combination, exclude):
+ """
+ Checks if the combination of options should be included in the Travis
+ testing matrix.
+
+ @param exclude: A list of options to be avoided.
+ """
+ return not any(excluded in combination for excluded in exclude)
+
+
+def generate_jobs(os, arch, exclude, max_unusual_opts, unusuals=all_unusuals):
+ jobs = []
+ for combination in generate_unusual_combinations(unusuals, max_unusual_opts):
+ if included(combination, exclude):
+ jobs.append(format_job(os, arch, combination))
+ return '\n'.join(jobs)
+
+
+def generate_linux(arch):
+ os = LINUX
+
+ # Only generate 2 unusual options for AMD64 to reduce matrix size
+ max_unusual_opts = MAX_UNUSUAL_OPTIONS if arch == AMD64 else 1
+
+ exclude = []
+ if arch == PPC64LE:
+ # Avoid 32 bit builds and clang on PowerPC
+ exclude = (CROSS_COMPILE_32BIT, CLANG,)
+
+ return generate_jobs(os, arch, exclude, max_unusual_opts)
+
+
+def generate_macos(arch):
+ os = OSX
+
+ max_unusual_opts = 1
+
+ exclude = ([Option.as_malloc_conf(opt) for opt in (
+ 'dss:primary',
+ 'percpu_arena:percpu',
+ 'background_thread:true')] +
+ [Option.as_configure_flag('--enable-prof')] +
+ [CLANG,])
+
+ return generate_jobs(os, arch, exclude, max_unusual_opts)
+
+
+def generate_windows(arch):
+ os = WINDOWS
+
+ max_unusual_opts = 3
+ unusuals = (
+ Option.as_configure_flag('--enable-debug'),
+ CL,
+ CROSS_COMPILE_32BIT,
+ )
+ return generate_jobs(os, arch, (), max_unusual_opts, unusuals)
+
+
+def generate_freebsd(arch):
+ os = FREEBSD
+
+ max_unusual_opts = 4
+ unusuals = (
+ Option.as_configure_flag('--enable-debug'),
+ Option.as_configure_flag('--enable-prof --enable-prof-libunwind'),
+ Option.as_configure_flag('--with-lg-page=16 --with-malloc-conf=tcache:false'),
+ CROSS_COMPILE_32BIT,
+ )
+ return generate_jobs(os, arch, (), max_unusual_opts, unusuals)
+
+
+
+def get_manual_jobs():
+ return """\
+ # Development build
+ - os: linux
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-debug \
+--disable-cache-oblivious --enable-stats --enable-log --enable-prof" \
+EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ # --enable-expermental-smallocx:
+ - os: linux
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-debug \
+--enable-experimental-smallocx --enable-stats --enable-prof" \
+EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+"""
+
+
+def main():
+ jobs = '\n'.join((
+ generate_windows(AMD64),
+
+ generate_freebsd(AMD64),
+
+ generate_linux(AMD64),
+ generate_linux(PPC64LE),
+
+ generate_macos(AMD64),
+
+ get_manual_jobs(),
+ ))
+
+ print(TRAVIS_TEMPLATE.format(jobs=jobs))
+
+
+if __name__ == '__main__':
+ main()
diff --git a/contrib/jemalloc/scripts/linux/before_install.sh b/contrib/jemalloc/scripts/linux/before_install.sh
new file mode 100644
index 000000000000..674174639ea9
--- /dev/null
+++ b/contrib/jemalloc/scripts/linux/before_install.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+
+set -ev
+
+if [[ "$TRAVIS_OS_NAME" != "linux" ]]; then
+ echo "Incorrect \$TRAVIS_OS_NAME: expected linux, got $TRAVIS_OS_NAME"
+ exit 1
+fi
+
+if [[ "$CROSS_COMPILE_32BIT" == "yes" ]]; then
+ sudo apt-get update
+ sudo apt-get -y install gcc-multilib g++-multilib
+fi
diff --git a/contrib/jemalloc/scripts/windows/before_install.sh b/contrib/jemalloc/scripts/windows/before_install.sh
new file mode 100644
index 000000000000..2740c4588d61
--- /dev/null
+++ b/contrib/jemalloc/scripts/windows/before_install.sh
@@ -0,0 +1,83 @@
+#!/bin/bash
+
+set -e
+
+# The purpose of this script is to install build dependencies and set
+# $build_env to a function that sets appropriate environment variables,
+# to enable (mingw32|mingw64) environment if we want to compile with gcc, or
+# (mingw32|mingw64) + vcvarsall.bat if we want to compile with cl.exe
+
+if [[ "$TRAVIS_OS_NAME" != "windows" ]]; then
+ echo "Incorrect \$TRAVIS_OS_NAME: expected windows, got $TRAVIS_OS_NAME"
+ exit 1
+fi
+
+[[ ! -f C:/tools/msys64/msys2_shell.cmd ]] && rm -rf C:/tools/msys64
+choco uninstall -y mingw
+choco upgrade --no-progress -y msys2
+
+msys_shell_cmd="cmd //C RefreshEnv.cmd && set MSYS=winsymlinks:nativestrict && C:\\tools\\msys64\\msys2_shell.cmd"
+
+msys2() { $msys_shell_cmd -defterm -no-start -msys2 -c "$*"; }
+mingw32() { $msys_shell_cmd -defterm -no-start -mingw32 -c "$*"; }
+mingw64() { $msys_shell_cmd -defterm -no-start -mingw64 -c "$*"; }
+
+if [[ "$CROSS_COMPILE_32BIT" == "yes" ]]; then
+ mingw=mingw32
+ mingw_gcc_package_arch=i686
+else
+ mingw=mingw64
+ mingw_gcc_package_arch=x86_64
+fi
+
+if [[ "$CC" == *"gcc"* ]]; then
+ $mingw pacman -S --noconfirm --needed \
+ autotools \
+ git \
+ mingw-w64-${mingw_gcc_package_arch}-make \
+ mingw-w64-${mingw_gcc_package_arch}-gcc \
+ mingw-w64-${mingw_gcc_package_arch}-binutils
+ build_env=$mingw
+elif [[ "$CC" == *"cl"* ]]; then
+ $mingw pacman -S --noconfirm --needed \
+ autotools \
+ git \
+ mingw-w64-${mingw_gcc_package_arch}-make \
+ mingw-w64-${mingw_gcc_package_arch}-binutils
+
+ # In order to use MSVC compiler (cl.exe), we need to correctly set some environment
+ # variables, namely PATH, INCLUDE, LIB and LIBPATH. The correct values of these
+ # variables are set by a batch script "vcvarsall.bat". The code below generates
+ # a batch script that calls "vcvarsall.bat" and prints the environment variables.
+ #
+ # Then, those environment variables are transformed from cmd to bash format and put
+ # into a script $apply_vsenv. If cl.exe needs to be used from bash, one can
+ # 'source $apply_vsenv' and it will apply the environment variables needed for cl.exe
+ # to be located and function correctly.
+ #
+ # At last, a function "mingw_with_msvc_vars" is generated which forwards user input
+ # into a correct mingw (32 or 64) subshell that automatically performs 'source $apply_vsenv',
+ # making it possible for autotools to discover and use cl.exe.
+ vcvarsall="vcvarsall.tmp.bat"
+ echo "@echo off" > $vcvarsall
+ echo "call \"c:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\\\vcvarsall.bat\" $USE_MSVC" >> $vcvarsall
+ echo "set" >> $vcvarsall
+
+ apply_vsenv="./apply_vsenv.sh"
+ cmd //C $vcvarsall | grep -E "^PATH=" | sed -n -e 's/\(.*\)=\(.*\)/export \1=$PATH:"\2"/g' \
+ -e 's/\([a-zA-Z]\):[\\\/]/\/\1\//g' \
+ -e 's/\\/\//g' \
+ -e 's/;\//:\//gp' > $apply_vsenv
+ cmd //C $vcvarsall | grep -E "^(INCLUDE|LIB|LIBPATH)=" | sed -n -e 's/\(.*\)=\(.*\)/export \1="\2"/gp' >> $apply_vsenv
+
+ cat $apply_vsenv
+ mingw_with_msvc_vars() { $msys_shell_cmd -defterm -no-start -$mingw -c "source $apply_vsenv && ""$*"; }
+ build_env=mingw_with_msvc_vars
+
+ rm -f $vcvarsall
+else
+ echo "Unknown C compiler: $CC"
+ exit 1
+fi
+
+echo "Build environment function: $build_env"
diff --git a/contrib/jemalloc/scripts/windows/before_script.sh b/contrib/jemalloc/scripts/windows/before_script.sh
new file mode 100644
index 000000000000..9d30ababd933
--- /dev/null
+++ b/contrib/jemalloc/scripts/windows/before_script.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+set -e
+
+if [[ "$TRAVIS_OS_NAME" != "windows" ]]; then
+ echo "Incorrect \$TRAVIS_OS_NAME: expected windows, got $TRAVIS_OS_NAME"
+ exit 1
+fi
+
+$build_env autoconf
+$build_env ./configure $CONFIGURE_FLAGS
+# mingw32-make simply means "make", unrelated to mingw32 vs mingw64.
+# Simply disregard the prefix and treat is as "make".
+$build_env mingw32-make -j3
+# At the moment, it's impossible to make tests in parallel,
+# seemingly due to concurrent writes to '.pdb' file. I don't know why
+# that happens, because we explicitly supply '/Fs' to the compiler.
+# Until we figure out how to fix it, we should build tests sequentially
+# on Windows.
+$build_env mingw32-make tests
diff --git a/contrib/jemalloc/scripts/windows/script.sh b/contrib/jemalloc/scripts/windows/script.sh
new file mode 100644
index 000000000000..3a27f70aa517
--- /dev/null
+++ b/contrib/jemalloc/scripts/windows/script.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+set -e
+
+if [[ "$TRAVIS_OS_NAME" != "windows" ]]; then
+ echo "Incorrect \$TRAVIS_OS_NAME: expected windows, got $TRAVIS_OS_NAME"
+ exit 1
+fi
+
+$build_env mingw32-make -k check
diff --git a/contrib/jemalloc/src/arena.c b/contrib/jemalloc/src/arena.c
index ba50e41033ff..857b27c52fb0 100644
--- a/contrib/jemalloc/src/arena.c
+++ b/contrib/jemalloc/src/arena.c
@@ -1,11 +1,12 @@
-#define JEMALLOC_ARENA_C_
#include "jemalloc/internal/jemalloc_preamble.h"
#include "jemalloc/internal/jemalloc_internal_includes.h"
#include "jemalloc/internal/assert.h"
-#include "jemalloc/internal/div.h"
+#include "jemalloc/internal/decay.h"
+#include "jemalloc/internal/ehooks.h"
#include "jemalloc/internal/extent_dss.h"
#include "jemalloc/internal/extent_mmap.h"
+#include "jemalloc/internal/san.h"
#include "jemalloc/internal/mutex.h"
#include "jemalloc/internal/rtree.h"
#include "jemalloc/internal/safety_check.h"
@@ -35,34 +36,37 @@ ssize_t opt_muzzy_decay_ms = MUZZY_DECAY_MS_DEFAULT;
static atomic_zd_t dirty_decay_ms_default;
static atomic_zd_t muzzy_decay_ms_default;
-const uint64_t h_steps[SMOOTHSTEP_NSTEPS] = {
-#define STEP(step, h, x, y) \
- h,
- SMOOTHSTEP
-#undef STEP
-};
+emap_t arena_emap_global;
+pa_central_t arena_pa_central_global;
-static div_info_t arena_binind_div_info[SC_NBINS];
+div_info_t arena_binind_div_info[SC_NBINS];
size_t opt_oversize_threshold = OVERSIZE_THRESHOLD_DEFAULT;
size_t oversize_threshold = OVERSIZE_THRESHOLD_DEFAULT;
+
+uint32_t arena_bin_offsets[SC_NBINS];
+static unsigned nbins_total;
+
static unsigned huge_arena_ind;
+const arena_config_t arena_config_default = {
+ /* .extent_hooks = */ (extent_hooks_t *)&ehooks_default_extent_hooks,
+ /* .metadata_use_hooks = */ true,
+};
+
/******************************************************************************/
/*
* Function prototypes for static functions that are referenced prior to
* definition.
*/
-static void arena_decay_to_limit(tsdn_t *tsdn, arena_t *arena,
- arena_decay_t *decay, extents_t *extents, bool all, size_t npages_limit,
- size_t npages_decay_max, bool is_background_thread);
static bool arena_decay_dirty(tsdn_t *tsdn, arena_t *arena,
bool is_background_thread, bool all);
-static void arena_dalloc_bin_slab(tsdn_t *tsdn, arena_t *arena, extent_t *slab,
- bin_t *bin);
-static void arena_bin_lower_slab(tsdn_t *tsdn, arena_t *arena, extent_t *slab,
+static void arena_bin_lower_slab(tsdn_t *tsdn, arena_t *arena, edata_t *slab,
bin_t *bin);
+static void
+arena_maybe_do_deferred_work(tsdn_t *tsdn, arena_t *arena, decay_t *decay,
+ size_t npages_new);
/******************************************************************************/
@@ -72,19 +76,17 @@ arena_basic_stats_merge(tsdn_t *tsdn, arena_t *arena, unsigned *nthreads,
size_t *nactive, size_t *ndirty, size_t *nmuzzy) {
*nthreads += arena_nthreads_get(arena, false);
*dss = dss_prec_names[arena_dss_prec_get(arena)];
- *dirty_decay_ms = arena_dirty_decay_ms_get(arena);
- *muzzy_decay_ms = arena_muzzy_decay_ms_get(arena);
- *nactive += atomic_load_zu(&arena->nactive, ATOMIC_RELAXED);
- *ndirty += extents_npages_get(&arena->extents_dirty);
- *nmuzzy += extents_npages_get(&arena->extents_muzzy);
+ *dirty_decay_ms = arena_decay_ms_get(arena, extent_state_dirty);
+ *muzzy_decay_ms = arena_decay_ms_get(arena, extent_state_muzzy);
+ pa_shard_basic_stats_merge(&arena->pa_shard, nactive, ndirty, nmuzzy);
}
void
arena_stats_merge(tsdn_t *tsdn, arena_t *arena, unsigned *nthreads,
const char **dss, ssize_t *dirty_decay_ms, ssize_t *muzzy_decay_ms,
size_t *nactive, size_t *ndirty, size_t *nmuzzy, arena_stats_t *astats,
- bin_stats_t *bstats, arena_stats_large_t *lstats,
- arena_stats_extents_t *estats) {
+ bin_stats_data_t *bstats, arena_stats_large_t *lstats,
+ pac_estats_t *estats, hpa_shard_stats_t *hpastats, sec_stats_t *secstats) {
cassert(config_stats);
arena_basic_stats_merge(tsdn, arena, nthreads, dss, dirty_decay_ms,
@@ -93,122 +95,74 @@ arena_stats_merge(tsdn_t *tsdn, arena_t *arena, unsigned *nthreads,
size_t base_allocated, base_resident, base_mapped, metadata_thp;
base_stats_get(tsdn, arena->base, &base_allocated, &base_resident,
&base_mapped, &metadata_thp);
+ size_t pac_mapped_sz = pac_mapped(&arena->pa_shard.pac);
+ astats->mapped += base_mapped + pac_mapped_sz;
+ astats->resident += base_resident;
- arena_stats_lock(tsdn, &arena->stats);
+ LOCKEDINT_MTX_LOCK(tsdn, arena->stats.mtx);
- arena_stats_accum_zu(&astats->mapped, base_mapped
- + arena_stats_read_zu(tsdn, &arena->stats, &arena->stats.mapped));
- arena_stats_accum_zu(&astats->retained,
- extents_npages_get(&arena->extents_retained) << LG_PAGE);
-
- atomic_store_zu(&astats->extent_avail,
- atomic_load_zu(&arena->extent_avail_cnt, ATOMIC_RELAXED),
- ATOMIC_RELAXED);
-
- arena_stats_accum_u64(&astats->decay_dirty.npurge,
- arena_stats_read_u64(tsdn, &arena->stats,
- &arena->stats.decay_dirty.npurge));
- arena_stats_accum_u64(&astats->decay_dirty.nmadvise,
- arena_stats_read_u64(tsdn, &arena->stats,
- &arena->stats.decay_dirty.nmadvise));
- arena_stats_accum_u64(&astats->decay_dirty.purged,
- arena_stats_read_u64(tsdn, &arena->stats,
- &arena->stats.decay_dirty.purged));
-
- arena_stats_accum_u64(&astats->decay_muzzy.npurge,
- arena_stats_read_u64(tsdn, &arena->stats,
- &arena->stats.decay_muzzy.npurge));
- arena_stats_accum_u64(&astats->decay_muzzy.nmadvise,
- arena_stats_read_u64(tsdn, &arena->stats,
- &arena->stats.decay_muzzy.nmadvise));
- arena_stats_accum_u64(&astats->decay_muzzy.purged,
- arena_stats_read_u64(tsdn, &arena->stats,
- &arena->stats.decay_muzzy.purged));
-
- arena_stats_accum_zu(&astats->base, base_allocated);
- arena_stats_accum_zu(&astats->internal, arena_internal_get(arena));
- arena_stats_accum_zu(&astats->metadata_thp, metadata_thp);
- arena_stats_accum_zu(&astats->resident, base_resident +
- (((atomic_load_zu(&arena->nactive, ATOMIC_RELAXED) +
- extents_npages_get(&arena->extents_dirty) +
- extents_npages_get(&arena->extents_muzzy)) << LG_PAGE)));
- arena_stats_accum_zu(&astats->abandoned_vm, atomic_load_zu(
- &arena->stats.abandoned_vm, ATOMIC_RELAXED));
+ astats->base += base_allocated;
+ atomic_load_add_store_zu(&astats->internal, arena_internal_get(arena));
+ astats->metadata_thp += metadata_thp;
for (szind_t i = 0; i < SC_NSIZES - SC_NBINS; i++) {
- uint64_t nmalloc = arena_stats_read_u64(tsdn, &arena->stats,
+ uint64_t nmalloc = locked_read_u64(tsdn,
+ LOCKEDINT_MTX(arena->stats.mtx),
&arena->stats.lstats[i].nmalloc);
- arena_stats_accum_u64(&lstats[i].nmalloc, nmalloc);
- arena_stats_accum_u64(&astats->nmalloc_large, nmalloc);
+ locked_inc_u64_unsynchronized(&lstats[i].nmalloc, nmalloc);
+ astats->nmalloc_large += nmalloc;
- uint64_t ndalloc = arena_stats_read_u64(tsdn, &arena->stats,
+ uint64_t ndalloc = locked_read_u64(tsdn,
+ LOCKEDINT_MTX(arena->stats.mtx),
&arena->stats.lstats[i].ndalloc);
- arena_stats_accum_u64(&lstats[i].ndalloc, ndalloc);
- arena_stats_accum_u64(&astats->ndalloc_large, ndalloc);
+ locked_inc_u64_unsynchronized(&lstats[i].ndalloc, ndalloc);
+ astats->ndalloc_large += ndalloc;
- uint64_t nrequests = arena_stats_read_u64(tsdn, &arena->stats,
+ uint64_t nrequests = locked_read_u64(tsdn,
+ LOCKEDINT_MTX(arena->stats.mtx),
&arena->stats.lstats[i].nrequests);
- arena_stats_accum_u64(&lstats[i].nrequests,
- nmalloc + nrequests);
- arena_stats_accum_u64(&astats->nrequests_large,
+ locked_inc_u64_unsynchronized(&lstats[i].nrequests,
nmalloc + nrequests);
+ astats->nrequests_large += nmalloc + nrequests;
/* nfill == nmalloc for large currently. */
- arena_stats_accum_u64(&lstats[i].nfills, nmalloc);
- arena_stats_accum_u64(&astats->nfills_large, nmalloc);
+ locked_inc_u64_unsynchronized(&lstats[i].nfills, nmalloc);
+ astats->nfills_large += nmalloc;
- uint64_t nflush = arena_stats_read_u64(tsdn, &arena->stats,
+ uint64_t nflush = locked_read_u64(tsdn,
+ LOCKEDINT_MTX(arena->stats.mtx),
&arena->stats.lstats[i].nflushes);
- arena_stats_accum_u64(&lstats[i].nflushes, nflush);
- arena_stats_accum_u64(&astats->nflushes_large, nflush);
+ locked_inc_u64_unsynchronized(&lstats[i].nflushes, nflush);
+ astats->nflushes_large += nflush;
assert(nmalloc >= ndalloc);
assert(nmalloc - ndalloc <= SIZE_T_MAX);
size_t curlextents = (size_t)(nmalloc - ndalloc);
lstats[i].curlextents += curlextents;
- arena_stats_accum_zu(&astats->allocated_large,
- curlextents * sz_index2size(SC_NBINS + i));
- }
-
- for (pszind_t i = 0; i < SC_NPSIZES; i++) {
- size_t dirty, muzzy, retained, dirty_bytes, muzzy_bytes,
- retained_bytes;
- dirty = extents_nextents_get(&arena->extents_dirty, i);
- muzzy = extents_nextents_get(&arena->extents_muzzy, i);
- retained = extents_nextents_get(&arena->extents_retained, i);
- dirty_bytes = extents_nbytes_get(&arena->extents_dirty, i);
- muzzy_bytes = extents_nbytes_get(&arena->extents_muzzy, i);
- retained_bytes =
- extents_nbytes_get(&arena->extents_retained, i);
-
- atomic_store_zu(&estats[i].ndirty, dirty, ATOMIC_RELAXED);
- atomic_store_zu(&estats[i].nmuzzy, muzzy, ATOMIC_RELAXED);
- atomic_store_zu(&estats[i].nretained, retained, ATOMIC_RELAXED);
- atomic_store_zu(&estats[i].dirty_bytes, dirty_bytes,
- ATOMIC_RELAXED);
- atomic_store_zu(&estats[i].muzzy_bytes, muzzy_bytes,
- ATOMIC_RELAXED);
- atomic_store_zu(&estats[i].retained_bytes, retained_bytes,
- ATOMIC_RELAXED);
- }
-
- arena_stats_unlock(tsdn, &arena->stats);
-
- /* tcache_bytes counts currently cached bytes. */
- atomic_store_zu(&astats->tcache_bytes, 0, ATOMIC_RELAXED);
+ astats->allocated_large +=
+ curlextents * sz_index2size(SC_NBINS + i);
+ }
+
+ pa_shard_stats_merge(tsdn, &arena->pa_shard, &astats->pa_shard_stats,
+ estats, hpastats, secstats, &astats->resident);
+
+ LOCKEDINT_MTX_UNLOCK(tsdn, arena->stats.mtx);
+
+ /* Currently cached bytes and sanitizer-stashed bytes in tcache. */
+ astats->tcache_bytes = 0;
+ astats->tcache_stashed_bytes = 0;
malloc_mutex_lock(tsdn, &arena->tcache_ql_mtx);
cache_bin_array_descriptor_t *descriptor;
ql_foreach(descriptor, &arena->cache_bin_array_descriptor_ql, link) {
- szind_t i = 0;
- for (; i < SC_NBINS; i++) {
- cache_bin_t *tbin = &descriptor->bins_small[i];
- arena_stats_accum_zu(&astats->tcache_bytes,
- tbin->ncached * sz_index2size(i));
- }
- for (; i < nhbins; i++) {
- cache_bin_t *tbin = &descriptor->bins_large[i];
- arena_stats_accum_zu(&astats->tcache_bytes,
- tbin->ncached * sz_index2size(i));
+ for (szind_t i = 0; i < nhbins; i++) {
+ cache_bin_t *cache_bin = &descriptor->bins[i];
+ cache_bin_sz_t ncached, nstashed;
+ cache_bin_nitems_get_remote(cache_bin,
+ &tcache_bin_info[i], &ncached, &nstashed);
+
+ astats->tcache_bytes += ncached * sz_index2size(i);
+ astats->tcache_stashed_bytes += nstashed *
+ sz_index2size(i);
}
}
malloc_mutex_prof_read(tsdn,
@@ -224,21 +178,11 @@ arena_stats_merge(tsdn_t *tsdn, arena_t *arena, unsigned *nthreads,
/* Gather per arena mutex profiling data. */
READ_ARENA_MUTEX_PROF_DATA(large_mtx, arena_prof_mutex_large);
- READ_ARENA_MUTEX_PROF_DATA(extent_avail_mtx,
- arena_prof_mutex_extent_avail)
- READ_ARENA_MUTEX_PROF_DATA(extents_dirty.mtx,
- arena_prof_mutex_extents_dirty)
- READ_ARENA_MUTEX_PROF_DATA(extents_muzzy.mtx,
- arena_prof_mutex_extents_muzzy)
- READ_ARENA_MUTEX_PROF_DATA(extents_retained.mtx,
- arena_prof_mutex_extents_retained)
- READ_ARENA_MUTEX_PROF_DATA(decay_dirty.mtx,
- arena_prof_mutex_decay_dirty)
- READ_ARENA_MUTEX_PROF_DATA(decay_muzzy.mtx,
- arena_prof_mutex_decay_muzzy)
READ_ARENA_MUTEX_PROF_DATA(base->mtx,
- arena_prof_mutex_base)
+ arena_prof_mutex_base);
#undef READ_ARENA_MUTEX_PROF_DATA
+ pa_shard_mtx_stats_read(tsdn, &arena->pa_shard,
+ astats->mutex_prof_data);
nstime_copy(&astats->uptime, &arena->create_time);
nstime_update(&astats->uptime);
@@ -247,55 +191,67 @@ arena_stats_merge(tsdn_t *tsdn, arena_t *arena, unsigned *nthreads,
for (szind_t i = 0; i < SC_NBINS; i++) {
for (unsigned j = 0; j < bin_infos[i].n_shards; j++) {
bin_stats_merge(tsdn, &bstats[i],
- &arena->bins[i].bin_shards[j]);
+ arena_get_bin(arena, i, j));
}
}
}
-void
-arena_extents_dirty_dalloc(tsdn_t *tsdn, arena_t *arena,
- extent_hooks_t **r_extent_hooks, extent_t *extent) {
+static void
+arena_background_thread_inactivity_check(tsdn_t *tsdn, arena_t *arena,
+ bool is_background_thread) {
+ if (!background_thread_enabled() || is_background_thread) {
+ return;
+ }
+ background_thread_info_t *info =
+ arena_background_thread_info_get(arena);
+ if (background_thread_indefinite_sleep(info)) {
+ arena_maybe_do_deferred_work(tsdn, arena,
+ &arena->pa_shard.pac.decay_dirty, 0);
+ }
+}
+
+/*
+ * React to deferred work generated by a PAI function.
+ */
+void arena_handle_deferred_work(tsdn_t *tsdn, arena_t *arena) {
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
WITNESS_RANK_CORE, 0);
- extents_dalloc(tsdn, arena, r_extent_hooks, &arena->extents_dirty,
- extent);
- if (arena_dirty_decay_ms_get(arena) == 0) {
+ if (decay_immediately(&arena->pa_shard.pac.decay_dirty)) {
arena_decay_dirty(tsdn, arena, false, true);
- } else {
- arena_background_thread_inactivity_check(tsdn, arena, false);
}
+ arena_background_thread_inactivity_check(tsdn, arena, false);
}
static void *
-arena_slab_reg_alloc(extent_t *slab, const bin_info_t *bin_info) {
+arena_slab_reg_alloc(edata_t *slab, const bin_info_t *bin_info) {
void *ret;
- arena_slab_data_t *slab_data = extent_slab_data_get(slab);
+ slab_data_t *slab_data = edata_slab_data_get(slab);
size_t regind;
- assert(extent_nfree_get(slab) > 0);
+ assert(edata_nfree_get(slab) > 0);
assert(!bitmap_full(slab_data->bitmap, &bin_info->bitmap_info));
regind = bitmap_sfu(slab_data->bitmap, &bin_info->bitmap_info);
- ret = (void *)((uintptr_t)extent_addr_get(slab) +
+ ret = (void *)((uintptr_t)edata_addr_get(slab) +
(uintptr_t)(bin_info->reg_size * regind));
- extent_nfree_dec(slab);
+ edata_nfree_dec(slab);
return ret;
}
static void
-arena_slab_reg_alloc_batch(extent_t *slab, const bin_info_t *bin_info,
+arena_slab_reg_alloc_batch(edata_t *slab, const bin_info_t *bin_info,
unsigned cnt, void** ptrs) {
- arena_slab_data_t *slab_data = extent_slab_data_get(slab);
+ slab_data_t *slab_data = edata_slab_data_get(slab);
- assert(extent_nfree_get(slab) >= cnt);
+ assert(edata_nfree_get(slab) >= cnt);
assert(!bitmap_full(slab_data->bitmap, &bin_info->bitmap_info));
#if (! defined JEMALLOC_INTERNAL_POPCOUNTL) || (defined BITMAP_USE_TREE)
for (unsigned i = 0; i < cnt; i++) {
size_t regind = bitmap_sfu(slab_data->bitmap,
&bin_info->bitmap_info);
- *(ptrs + i) = (void *)((uintptr_t)extent_addr_get(slab) +
+ *(ptrs + i) = (void *)((uintptr_t)edata_addr_get(slab) +
(uintptr_t)(bin_info->reg_size * regind));
}
#else
@@ -316,7 +272,7 @@ arena_slab_reg_alloc_batch(extent_t *slab, const bin_info_t *bin_info,
* Load from memory locations only once, outside the
* hot loop below.
*/
- uintptr_t base = (uintptr_t)extent_addr_get(slab);
+ uintptr_t base = (uintptr_t)edata_addr_get(slab);
uintptr_t regsize = (uintptr_t)bin_info->reg_size;
while (pop--) {
size_t bit = cfs_lu(&g);
@@ -328,56 +284,7 @@ arena_slab_reg_alloc_batch(extent_t *slab, const bin_info_t *bin_info,
slab_data->bitmap[group] = g;
}
#endif
- extent_nfree_sub(slab, cnt);
-}
-
-#ifndef JEMALLOC_JET
-static
-#endif
-size_t
-arena_slab_regind(extent_t *slab, szind_t binind, const void *ptr) {
- size_t diff, regind;
-
- /* Freeing a pointer outside the slab can cause assertion failure. */
- assert((uintptr_t)ptr >= (uintptr_t)extent_addr_get(slab));
- assert((uintptr_t)ptr < (uintptr_t)extent_past_get(slab));
- /* Freeing an interior pointer can cause assertion failure. */
- assert(((uintptr_t)ptr - (uintptr_t)extent_addr_get(slab)) %
- (uintptr_t)bin_infos[binind].reg_size == 0);
-
- diff = (size_t)((uintptr_t)ptr - (uintptr_t)extent_addr_get(slab));
-
- /* Avoid doing division with a variable divisor. */
- regind = div_compute(&arena_binind_div_info[binind], diff);
-
- assert(regind < bin_infos[binind].nregs);
-
- return regind;
-}
-
-static void
-arena_slab_reg_dalloc(extent_t *slab, arena_slab_data_t *slab_data, void *ptr) {
- szind_t binind = extent_szind_get(slab);
- const bin_info_t *bin_info = &bin_infos[binind];
- size_t regind = arena_slab_regind(slab, binind, ptr);
-
- assert(extent_nfree_get(slab) < bin_info->nregs);
- /* Freeing an unallocated pointer can cause assertion failure. */
- assert(bitmap_get(slab_data->bitmap, &bin_info->bitmap_info, regind));
-
- bitmap_unset(slab_data->bitmap, &bin_info->bitmap_info, regind);
- extent_nfree_inc(slab);
-}
-
-static void
-arena_nactive_add(arena_t *arena, size_t add_pages) {
- atomic_fetch_add_zu(&arena->nactive, add_pages, ATOMIC_RELAXED);
-}
-
-static void
-arena_nactive_sub(arena_t *arena, size_t sub_pages) {
- assert(atomic_load_zu(&arena->nactive, ATOMIC_RELAXED) >= sub_pages);
- atomic_fetch_sub_zu(&arena->nactive, sub_pages, ATOMIC_RELAXED);
+ edata_nfree_sub(slab, cnt);
}
static void
@@ -392,7 +299,7 @@ arena_large_malloc_stats_update(tsdn_t *tsdn, arena_t *arena, size_t usize) {
index = sz_size2index(usize);
hindex = (index >= SC_NBINS) ? index - SC_NBINS : 0;
- arena_stats_add_u64(tsdn, &arena->stats,
+ locked_inc_u64(tsdn, LOCKEDINT_MTX(arena->stats.mtx),
&arena->stats.lstats[hindex].nmalloc, 1);
}
@@ -408,551 +315,118 @@ arena_large_dalloc_stats_update(tsdn_t *tsdn, arena_t *arena, size_t usize) {
index = sz_size2index(usize);
hindex = (index >= SC_NBINS) ? index - SC_NBINS : 0;
- arena_stats_add_u64(tsdn, &arena->stats,
+ locked_inc_u64(tsdn, LOCKEDINT_MTX(arena->stats.mtx),
&arena->stats.lstats[hindex].ndalloc, 1);
}
static void
arena_large_ralloc_stats_update(tsdn_t *tsdn, arena_t *arena, size_t oldusize,
size_t usize) {
- arena_large_dalloc_stats_update(tsdn, arena, oldusize);
arena_large_malloc_stats_update(tsdn, arena, usize);
+ arena_large_dalloc_stats_update(tsdn, arena, oldusize);
}
-static bool
-arena_may_have_muzzy(arena_t *arena) {
- return (pages_can_purge_lazy && (arena_muzzy_decay_ms_get(arena) != 0));
-}
-
-extent_t *
+edata_t *
arena_extent_alloc_large(tsdn_t *tsdn, arena_t *arena, size_t usize,
- size_t alignment, bool *zero) {
- extent_hooks_t *extent_hooks = EXTENT_HOOKS_INITIALIZER;
+ size_t alignment, bool zero) {
+ bool deferred_work_generated = false;
+ szind_t szind = sz_size2index(usize);
+ size_t esize = usize + sz_large_pad;
- witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
- WITNESS_RANK_CORE, 0);
+ bool guarded = san_large_extent_decide_guard(tsdn,
+ arena_get_ehooks(arena), esize, alignment);
+ edata_t *edata = pa_alloc(tsdn, &arena->pa_shard, esize, alignment,
+ /* slab */ false, szind, zero, guarded, &deferred_work_generated);
+ assert(deferred_work_generated == false);
- szind_t szind = sz_size2index(usize);
- size_t mapped_add;
- bool commit = true;
- extent_t *extent = extents_alloc(tsdn, arena, &extent_hooks,
- &arena->extents_dirty, NULL, usize, sz_large_pad, alignment, false,
- szind, zero, &commit);
- if (extent == NULL && arena_may_have_muzzy(arena)) {
- extent = extents_alloc(tsdn, arena, &extent_hooks,
- &arena->extents_muzzy, NULL, usize, sz_large_pad, alignment,
- false, szind, zero, &commit);
- }
- size_t size = usize + sz_large_pad;
- if (extent == NULL) {
- extent = extent_alloc_wrapper(tsdn, arena, &extent_hooks, NULL,
- usize, sz_large_pad, alignment, false, szind, zero,
- &commit);
+ if (edata != NULL) {
if (config_stats) {
- /*
- * extent may be NULL on OOM, but in that case
- * mapped_add isn't used below, so there's no need to
- * conditionlly set it to 0 here.
- */
- mapped_add = size;
+ LOCKEDINT_MTX_LOCK(tsdn, arena->stats.mtx);
+ arena_large_malloc_stats_update(tsdn, arena, usize);
+ LOCKEDINT_MTX_UNLOCK(tsdn, arena->stats.mtx);
}
- } else if (config_stats) {
- mapped_add = 0;
}
- if (extent != NULL) {
- if (config_stats) {
- arena_stats_lock(tsdn, &arena->stats);
- arena_large_malloc_stats_update(tsdn, arena, usize);
- if (mapped_add != 0) {
- arena_stats_add_zu(tsdn, &arena->stats,
- &arena->stats.mapped, mapped_add);
- }
- arena_stats_unlock(tsdn, &arena->stats);
- }
- arena_nactive_add(arena, size >> LG_PAGE);
+ if (edata != NULL && sz_large_pad != 0) {
+ arena_cache_oblivious_randomize(tsdn, arena, edata, alignment);
}
- return extent;
+ return edata;
}
void
-arena_extent_dalloc_large_prep(tsdn_t *tsdn, arena_t *arena, extent_t *extent) {
+arena_extent_dalloc_large_prep(tsdn_t *tsdn, arena_t *arena, edata_t *edata) {
if (config_stats) {
- arena_stats_lock(tsdn, &arena->stats);
+ LOCKEDINT_MTX_LOCK(tsdn, arena->stats.mtx);
arena_large_dalloc_stats_update(tsdn, arena,
- extent_usize_get(extent));
- arena_stats_unlock(tsdn, &arena->stats);
+ edata_usize_get(edata));
+ LOCKEDINT_MTX_UNLOCK(tsdn, arena->stats.mtx);
}
- arena_nactive_sub(arena, extent_size_get(extent) >> LG_PAGE);
}
void
-arena_extent_ralloc_large_shrink(tsdn_t *tsdn, arena_t *arena, extent_t *extent,
+arena_extent_ralloc_large_shrink(tsdn_t *tsdn, arena_t *arena, edata_t *edata,
size_t oldusize) {
- size_t usize = extent_usize_get(extent);
- size_t udiff = oldusize - usize;
+ size_t usize = edata_usize_get(edata);
if (config_stats) {
- arena_stats_lock(tsdn, &arena->stats);
+ LOCKEDINT_MTX_LOCK(tsdn, arena->stats.mtx);
arena_large_ralloc_stats_update(tsdn, arena, oldusize, usize);
- arena_stats_unlock(tsdn, &arena->stats);
+ LOCKEDINT_MTX_UNLOCK(tsdn, arena->stats.mtx);
}
- arena_nactive_sub(arena, udiff >> LG_PAGE);
}
void
-arena_extent_ralloc_large_expand(tsdn_t *tsdn, arena_t *arena, extent_t *extent,
+arena_extent_ralloc_large_expand(tsdn_t *tsdn, arena_t *arena, edata_t *edata,
size_t oldusize) {
- size_t usize = extent_usize_get(extent);
- size_t udiff = usize - oldusize;
+ size_t usize = edata_usize_get(edata);
if (config_stats) {
- arena_stats_lock(tsdn, &arena->stats);
+ LOCKEDINT_MTX_LOCK(tsdn, arena->stats.mtx);
arena_large_ralloc_stats_update(tsdn, arena, oldusize, usize);
- arena_stats_unlock(tsdn, &arena->stats);
- }
- arena_nactive_add(arena, udiff >> LG_PAGE);
-}
-
-static ssize_t
-arena_decay_ms_read(arena_decay_t *decay) {
- return atomic_load_zd(&decay->time_ms, ATOMIC_RELAXED);
-}
-
-static void
-arena_decay_ms_write(arena_decay_t *decay, ssize_t decay_ms) {
- atomic_store_zd(&decay->time_ms, decay_ms, ATOMIC_RELAXED);
-}
-
-static void
-arena_decay_deadline_init(arena_decay_t *decay) {
- /*
- * Generate a new deadline that is uniformly random within the next
- * epoch after the current one.
- */
- nstime_copy(&decay->deadline, &decay->epoch);
- nstime_add(&decay->deadline, &decay->interval);
- if (arena_decay_ms_read(decay) > 0) {
- nstime_t jitter;
-
- nstime_init(&jitter, prng_range_u64(&decay->jitter_state,
- nstime_ns(&decay->interval)));
- nstime_add(&decay->deadline, &jitter);
- }
-}
-
-static bool
-arena_decay_deadline_reached(const arena_decay_t *decay, const nstime_t *time) {
- return (nstime_compare(&decay->deadline, time) <= 0);
-}
-
-static size_t
-arena_decay_backlog_npages_limit(const arena_decay_t *decay) {
- uint64_t sum;
- size_t npages_limit_backlog;
- unsigned i;
-
- /*
- * For each element of decay_backlog, multiply by the corresponding
- * fixed-point smoothstep decay factor. Sum the products, then divide
- * to round down to the nearest whole number of pages.
- */
- sum = 0;
- for (i = 0; i < SMOOTHSTEP_NSTEPS; i++) {
- sum += decay->backlog[i] * h_steps[i];
- }
- npages_limit_backlog = (size_t)(sum >> SMOOTHSTEP_BFP);
-
- return npages_limit_backlog;
-}
-
-static void
-arena_decay_backlog_update_last(arena_decay_t *decay, size_t current_npages) {
- size_t npages_delta = (current_npages > decay->nunpurged) ?
- current_npages - decay->nunpurged : 0;
- decay->backlog[SMOOTHSTEP_NSTEPS-1] = npages_delta;
-
- if (config_debug) {
- if (current_npages > decay->ceil_npages) {
- decay->ceil_npages = current_npages;
- }
- size_t npages_limit = arena_decay_backlog_npages_limit(decay);
- assert(decay->ceil_npages >= npages_limit);
- if (decay->ceil_npages > npages_limit) {
- decay->ceil_npages = npages_limit;
- }
+ LOCKEDINT_MTX_UNLOCK(tsdn, arena->stats.mtx);
}
}
-static void
-arena_decay_backlog_update(arena_decay_t *decay, uint64_t nadvance_u64,
- size_t current_npages) {
- if (nadvance_u64 >= SMOOTHSTEP_NSTEPS) {
- memset(decay->backlog, 0, (SMOOTHSTEP_NSTEPS-1) *
- sizeof(size_t));
- } else {
- size_t nadvance_z = (size_t)nadvance_u64;
-
- assert((uint64_t)nadvance_z == nadvance_u64);
-
- memmove(decay->backlog, &decay->backlog[nadvance_z],
- (SMOOTHSTEP_NSTEPS - nadvance_z) * sizeof(size_t));
- if (nadvance_z > 1) {
- memset(&decay->backlog[SMOOTHSTEP_NSTEPS -
- nadvance_z], 0, (nadvance_z-1) * sizeof(size_t));
- }
- }
-
- arena_decay_backlog_update_last(decay, current_npages);
-}
-
-static void
-arena_decay_try_purge(tsdn_t *tsdn, arena_t *arena, arena_decay_t *decay,
- extents_t *extents, size_t current_npages, size_t npages_limit,
- bool is_background_thread) {
- if (current_npages > npages_limit) {
- arena_decay_to_limit(tsdn, arena, decay, extents, false,
- npages_limit, current_npages - npages_limit,
- is_background_thread);
- }
-}
-
-static void
-arena_decay_epoch_advance_helper(arena_decay_t *decay, const nstime_t *time,
- size_t current_npages) {
- assert(arena_decay_deadline_reached(decay, time));
-
- nstime_t delta;
- nstime_copy(&delta, time);
- nstime_subtract(&delta, &decay->epoch);
-
- uint64_t nadvance_u64 = nstime_divide(&delta, &decay->interval);
- assert(nadvance_u64 > 0);
-
- /* Add nadvance_u64 decay intervals to epoch. */
- nstime_copy(&delta, &decay->interval);
- nstime_imultiply(&delta, nadvance_u64);
- nstime_add(&decay->epoch, &delta);
-
- /* Set a new deadline. */
- arena_decay_deadline_init(decay);
-
- /* Update the backlog. */
- arena_decay_backlog_update(decay, nadvance_u64, current_npages);
-}
-
-static void
-arena_decay_epoch_advance(tsdn_t *tsdn, arena_t *arena, arena_decay_t *decay,
- extents_t *extents, const nstime_t *time, bool is_background_thread) {
- size_t current_npages = extents_npages_get(extents);
- arena_decay_epoch_advance_helper(decay, time, current_npages);
-
- size_t npages_limit = arena_decay_backlog_npages_limit(decay);
- /* We may unlock decay->mtx when try_purge(). Finish logging first. */
- decay->nunpurged = (npages_limit > current_npages) ? npages_limit :
- current_npages;
-
- if (!background_thread_enabled() || is_background_thread) {
- arena_decay_try_purge(tsdn, arena, decay, extents,
- current_npages, npages_limit, is_background_thread);
- }
-}
-
-static void
-arena_decay_reinit(arena_decay_t *decay, ssize_t decay_ms) {
- arena_decay_ms_write(decay, decay_ms);
- if (decay_ms > 0) {
- nstime_init(&decay->interval, (uint64_t)decay_ms *
- KQU(1000000));
- nstime_idivide(&decay->interval, SMOOTHSTEP_NSTEPS);
- }
-
- nstime_init(&decay->epoch, 0);
- nstime_update(&decay->epoch);
- decay->jitter_state = (uint64_t)(uintptr_t)decay;
- arena_decay_deadline_init(decay);
- decay->nunpurged = 0;
- memset(decay->backlog, 0, SMOOTHSTEP_NSTEPS * sizeof(size_t));
-}
-
-static bool
-arena_decay_init(arena_decay_t *decay, ssize_t decay_ms,
- arena_stats_decay_t *stats) {
- if (config_debug) {
- for (size_t i = 0; i < sizeof(arena_decay_t); i++) {
- assert(((char *)decay)[i] == 0);
- }
- decay->ceil_npages = 0;
- }
- if (malloc_mutex_init(&decay->mtx, "decay", WITNESS_RANK_DECAY,
- malloc_mutex_rank_exclusive)) {
- return true;
- }
- decay->purging = false;
- arena_decay_reinit(decay, decay_ms);
- /* Memory is zeroed, so there is no need to clear stats. */
- if (config_stats) {
- decay->stats = stats;
- }
- return false;
-}
-
-static bool
-arena_decay_ms_valid(ssize_t decay_ms) {
- if (decay_ms < -1) {
- return false;
- }
- if (decay_ms == -1 || (uint64_t)decay_ms <= NSTIME_SEC_MAX *
- KQU(1000)) {
- return true;
- }
- return false;
-}
-
-static bool
-arena_maybe_decay(tsdn_t *tsdn, arena_t *arena, arena_decay_t *decay,
- extents_t *extents, bool is_background_thread) {
- malloc_mutex_assert_owner(tsdn, &decay->mtx);
-
- /* Purge all or nothing if the option is disabled. */
- ssize_t decay_ms = arena_decay_ms_read(decay);
- if (decay_ms <= 0) {
- if (decay_ms == 0) {
- arena_decay_to_limit(tsdn, arena, decay, extents, false,
- 0, extents_npages_get(extents),
- is_background_thread);
- }
- return false;
- }
-
- nstime_t time;
- nstime_init(&time, 0);
- nstime_update(&time);
- if (unlikely(!nstime_monotonic() && nstime_compare(&decay->epoch, &time)
- > 0)) {
- /*
- * Time went backwards. Move the epoch back in time and
- * generate a new deadline, with the expectation that time
- * typically flows forward for long enough periods of time that
- * epochs complete. Unfortunately, this strategy is susceptible
- * to clock jitter triggering premature epoch advances, but
- * clock jitter estimation and compensation isn't feasible here
- * because calls into this code are event-driven.
- */
- nstime_copy(&decay->epoch, &time);
- arena_decay_deadline_init(decay);
+/*
+ * In situations where we're not forcing a decay (i.e. because the user
+ * specifically requested it), should we purge ourselves, or wait for the
+ * background thread to get to it.
+ */
+static pac_purge_eagerness_t
+arena_decide_unforced_purge_eagerness(bool is_background_thread) {
+ if (is_background_thread) {
+ return PAC_PURGE_ALWAYS;
+ } else if (!is_background_thread && background_thread_enabled()) {
+ return PAC_PURGE_NEVER;
} else {
- /* Verify that time does not go backwards. */
- assert(nstime_compare(&decay->epoch, &time) <= 0);
+ return PAC_PURGE_ON_EPOCH_ADVANCE;
}
-
- /*
- * If the deadline has been reached, advance to the current epoch and
- * purge to the new limit if necessary. Note that dirty pages created
- * during the current epoch are not subject to purge until a future
- * epoch, so as a result purging only happens during epoch advances, or
- * being triggered by background threads (scheduled event).
- */
- bool advance_epoch = arena_decay_deadline_reached(decay, &time);
- if (advance_epoch) {
- arena_decay_epoch_advance(tsdn, arena, decay, extents, &time,
- is_background_thread);
- } else if (is_background_thread) {
- arena_decay_try_purge(tsdn, arena, decay, extents,
- extents_npages_get(extents),
- arena_decay_backlog_npages_limit(decay),
- is_background_thread);
- }
-
- return advance_epoch;
-}
-
-static ssize_t
-arena_decay_ms_get(arena_decay_t *decay) {
- return arena_decay_ms_read(decay);
-}
-
-ssize_t
-arena_dirty_decay_ms_get(arena_t *arena) {
- return arena_decay_ms_get(&arena->decay_dirty);
-}
-
-ssize_t
-arena_muzzy_decay_ms_get(arena_t *arena) {
- return arena_decay_ms_get(&arena->decay_muzzy);
-}
-
-static bool
-arena_decay_ms_set(tsdn_t *tsdn, arena_t *arena, arena_decay_t *decay,
- extents_t *extents, ssize_t decay_ms) {
- if (!arena_decay_ms_valid(decay_ms)) {
- return true;
- }
-
- malloc_mutex_lock(tsdn, &decay->mtx);
- /*
- * Restart decay backlog from scratch, which may cause many dirty pages
- * to be immediately purged. It would conceptually be possible to map
- * the old backlog onto the new backlog, but there is no justification
- * for such complexity since decay_ms changes are intended to be
- * infrequent, either between the {-1, 0, >0} states, or a one-time
- * arbitrary change during initial arena configuration.
- */
- arena_decay_reinit(decay, decay_ms);
- arena_maybe_decay(tsdn, arena, decay, extents, false);
- malloc_mutex_unlock(tsdn, &decay->mtx);
-
- return false;
-}
-
-bool
-arena_dirty_decay_ms_set(tsdn_t *tsdn, arena_t *arena,
- ssize_t decay_ms) {
- return arena_decay_ms_set(tsdn, arena, &arena->decay_dirty,
- &arena->extents_dirty, decay_ms);
}
bool
-arena_muzzy_decay_ms_set(tsdn_t *tsdn, arena_t *arena,
+arena_decay_ms_set(tsdn_t *tsdn, arena_t *arena, extent_state_t state,
ssize_t decay_ms) {
- return arena_decay_ms_set(tsdn, arena, &arena->decay_muzzy,
- &arena->extents_muzzy, decay_ms);
+ pac_purge_eagerness_t eagerness = arena_decide_unforced_purge_eagerness(
+ /* is_background_thread */ false);
+ return pa_decay_ms_set(tsdn, &arena->pa_shard, state, decay_ms,
+ eagerness);
}
-static size_t
-arena_stash_decayed(tsdn_t *tsdn, arena_t *arena,
- extent_hooks_t **r_extent_hooks, extents_t *extents, size_t npages_limit,
- size_t npages_decay_max, extent_list_t *decay_extents) {
- witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
- WITNESS_RANK_CORE, 0);
-
- /* Stash extents according to npages_limit. */
- size_t nstashed = 0;
- extent_t *extent;
- while (nstashed < npages_decay_max &&
- (extent = extents_evict(tsdn, arena, r_extent_hooks, extents,
- npages_limit)) != NULL) {
- extent_list_append(decay_extents, extent);
- nstashed += extent_size_get(extent) >> LG_PAGE;
- }
- return nstashed;
-}
-
-static size_t
-arena_decay_stashed(tsdn_t *tsdn, arena_t *arena,
- extent_hooks_t **r_extent_hooks, arena_decay_t *decay, extents_t *extents,
- bool all, extent_list_t *decay_extents, bool is_background_thread) {
- size_t nmadvise, nunmapped;
- size_t npurged;
-
- if (config_stats) {
- nmadvise = 0;
- nunmapped = 0;
- }
- npurged = 0;
-
- ssize_t muzzy_decay_ms = arena_muzzy_decay_ms_get(arena);
- for (extent_t *extent = extent_list_first(decay_extents); extent !=
- NULL; extent = extent_list_first(decay_extents)) {
- if (config_stats) {
- nmadvise++;
- }
- size_t npages = extent_size_get(extent) >> LG_PAGE;
- npurged += npages;
- extent_list_remove(decay_extents, extent);
- switch (extents_state_get(extents)) {
- case extent_state_active:
- not_reached();
- case extent_state_dirty:
- if (!all && muzzy_decay_ms != 0 &&
- !extent_purge_lazy_wrapper(tsdn, arena,
- r_extent_hooks, extent, 0,
- extent_size_get(extent))) {
- extents_dalloc(tsdn, arena, r_extent_hooks,
- &arena->extents_muzzy, extent);
- arena_background_thread_inactivity_check(tsdn,
- arena, is_background_thread);
- break;
- }
- /* Fall through. */
- case extent_state_muzzy:
- extent_dalloc_wrapper(tsdn, arena, r_extent_hooks,
- extent);
- if (config_stats) {
- nunmapped += npages;
- }
- break;
- case extent_state_retained:
- default:
- not_reached();
- }
- }
-
- if (config_stats) {
- arena_stats_lock(tsdn, &arena->stats);
- arena_stats_add_u64(tsdn, &arena->stats, &decay->stats->npurge,
- 1);
- arena_stats_add_u64(tsdn, &arena->stats,
- &decay->stats->nmadvise, nmadvise);
- arena_stats_add_u64(tsdn, &arena->stats, &decay->stats->purged,
- npurged);
- arena_stats_sub_zu(tsdn, &arena->stats, &arena->stats.mapped,
- nunmapped << LG_PAGE);
- arena_stats_unlock(tsdn, &arena->stats);
- }
-
- return npurged;
-}
-
-/*
- * npages_limit: Decay at most npages_decay_max pages without violating the
- * invariant: (extents_npages_get(extents) >= npages_limit). We need an upper
- * bound on number of pages in order to prevent unbounded growth (namely in
- * stashed), otherwise unbounded new pages could be added to extents during the
- * current decay run, so that the purging thread never finishes.
- */
-static void
-arena_decay_to_limit(tsdn_t *tsdn, arena_t *arena, arena_decay_t *decay,
- extents_t *extents, bool all, size_t npages_limit, size_t npages_decay_max,
- bool is_background_thread) {
- witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
- WITNESS_RANK_CORE, 1);
- malloc_mutex_assert_owner(tsdn, &decay->mtx);
-
- if (decay->purging) {
- return;
- }
- decay->purging = true;
- malloc_mutex_unlock(tsdn, &decay->mtx);
-
- extent_hooks_t *extent_hooks = extent_hooks_get(arena);
-
- extent_list_t decay_extents;
- extent_list_init(&decay_extents);
-
- size_t npurge = arena_stash_decayed(tsdn, arena, &extent_hooks, extents,
- npages_limit, npages_decay_max, &decay_extents);
- if (npurge != 0) {
- size_t npurged = arena_decay_stashed(tsdn, arena,
- &extent_hooks, decay, extents, all, &decay_extents,
- is_background_thread);
- assert(npurged == npurge);
- }
-
- malloc_mutex_lock(tsdn, &decay->mtx);
- decay->purging = false;
+ssize_t
+arena_decay_ms_get(arena_t *arena, extent_state_t state) {
+ return pa_decay_ms_get(&arena->pa_shard, state);
}
static bool
-arena_decay_impl(tsdn_t *tsdn, arena_t *arena, arena_decay_t *decay,
- extents_t *extents, bool is_background_thread, bool all) {
+arena_decay_impl(tsdn_t *tsdn, arena_t *arena, decay_t *decay,
+ pac_decay_stats_t *decay_stats, ecache_t *ecache,
+ bool is_background_thread, bool all) {
if (all) {
malloc_mutex_lock(tsdn, &decay->mtx);
- arena_decay_to_limit(tsdn, arena, decay, extents, all, 0,
- extents_npages_get(extents), is_background_thread);
+ pac_decay_all(tsdn, &arena->pa_shard.pac, decay, decay_stats,
+ ecache, /* fully_decay */ all);
malloc_mutex_unlock(tsdn, &decay->mtx);
-
return false;
}
@@ -960,20 +434,20 @@ arena_decay_impl(tsdn_t *tsdn, arena_t *arena, arena_decay_t *decay,
/* No need to wait if another thread is in progress. */
return true;
}
-
- bool epoch_advanced = arena_maybe_decay(tsdn, arena, decay, extents,
- is_background_thread);
+ pac_purge_eagerness_t eagerness =
+ arena_decide_unforced_purge_eagerness(is_background_thread);
+ bool epoch_advanced = pac_maybe_decay_purge(tsdn, &arena->pa_shard.pac,
+ decay, decay_stats, ecache, eagerness);
size_t npages_new;
if (epoch_advanced) {
/* Backlog is updated on epoch advance. */
- npages_new = decay->backlog[SMOOTHSTEP_NSTEPS-1];
+ npages_new = decay_epoch_npages_delta(decay);
}
malloc_mutex_unlock(tsdn, &decay->mtx);
if (have_background_thread && background_thread_enabled() &&
epoch_advanced && !is_background_thread) {
- background_thread_interval_check(tsdn, arena, decay,
- npages_new);
+ arena_maybe_do_deferred_work(tsdn, arena, decay, npages_new);
}
return false;
@@ -982,53 +456,143 @@ arena_decay_impl(tsdn_t *tsdn, arena_t *arena, arena_decay_t *decay,
static bool
arena_decay_dirty(tsdn_t *tsdn, arena_t *arena, bool is_background_thread,
bool all) {
- return arena_decay_impl(tsdn, arena, &arena->decay_dirty,
- &arena->extents_dirty, is_background_thread, all);
+ return arena_decay_impl(tsdn, arena, &arena->pa_shard.pac.decay_dirty,
+ &arena->pa_shard.pac.stats->decay_dirty,
+ &arena->pa_shard.pac.ecache_dirty, is_background_thread, all);
}
static bool
arena_decay_muzzy(tsdn_t *tsdn, arena_t *arena, bool is_background_thread,
bool all) {
- return arena_decay_impl(tsdn, arena, &arena->decay_muzzy,
- &arena->extents_muzzy, is_background_thread, all);
+ if (pa_shard_dont_decay_muzzy(&arena->pa_shard)) {
+ return false;
+ }
+ return arena_decay_impl(tsdn, arena, &arena->pa_shard.pac.decay_muzzy,
+ &arena->pa_shard.pac.stats->decay_muzzy,
+ &arena->pa_shard.pac.ecache_muzzy, is_background_thread, all);
}
void
arena_decay(tsdn_t *tsdn, arena_t *arena, bool is_background_thread, bool all) {
+ if (all) {
+ /*
+ * We should take a purge of "all" to mean "save as much memory
+ * as possible", including flushing any caches (for situations
+ * like thread death, or manual purge calls).
+ */
+ sec_flush(tsdn, &arena->pa_shard.hpa_sec);
+ }
if (arena_decay_dirty(tsdn, arena, is_background_thread, all)) {
return;
}
arena_decay_muzzy(tsdn, arena, is_background_thread, all);
}
+static bool
+arena_should_decay_early(tsdn_t *tsdn, arena_t *arena, decay_t *decay,
+ background_thread_info_t *info, nstime_t *remaining_sleep,
+ size_t npages_new) {
+ malloc_mutex_assert_owner(tsdn, &info->mtx);
+
+ if (malloc_mutex_trylock(tsdn, &decay->mtx)) {
+ return false;
+ }
+
+ if (!decay_gradually(decay)) {
+ malloc_mutex_unlock(tsdn, &decay->mtx);
+ return false;
+ }
+
+ nstime_init(remaining_sleep, background_thread_wakeup_time_get(info));
+ if (nstime_compare(remaining_sleep, &decay->epoch) <= 0) {
+ malloc_mutex_unlock(tsdn, &decay->mtx);
+ return false;
+ }
+ nstime_subtract(remaining_sleep, &decay->epoch);
+ if (npages_new > 0) {
+ uint64_t npurge_new = decay_npages_purge_in(decay,
+ remaining_sleep, npages_new);
+ info->npages_to_purge_new += npurge_new;
+ }
+ malloc_mutex_unlock(tsdn, &decay->mtx);
+ return info->npages_to_purge_new >
+ ARENA_DEFERRED_PURGE_NPAGES_THRESHOLD;
+}
+
+/*
+ * Check if deferred work needs to be done sooner than planned.
+ * For decay we might want to wake up earlier because of an influx of dirty
+ * pages. Rather than waiting for previously estimated time, we proactively
+ * purge those pages.
+ * If background thread sleeps indefinitely, always wake up because some
+ * deferred work has been generated.
+ */
static void
-arena_slab_dalloc(tsdn_t *tsdn, arena_t *arena, extent_t *slab) {
- arena_nactive_sub(arena, extent_size_get(slab) >> LG_PAGE);
+arena_maybe_do_deferred_work(tsdn_t *tsdn, arena_t *arena, decay_t *decay,
+ size_t npages_new) {
+ background_thread_info_t *info = arena_background_thread_info_get(
+ arena);
+ if (malloc_mutex_trylock(tsdn, &info->mtx)) {
+ /*
+ * Background thread may hold the mutex for a long period of
+ * time. We'd like to avoid the variance on application
+ * threads. So keep this non-blocking, and leave the work to a
+ * future epoch.
+ */
+ return;
+ }
+ if (!background_thread_is_started(info)) {
+ goto label_done;
+ }
+
+ nstime_t remaining_sleep;
+ if (background_thread_indefinite_sleep(info)) {
+ background_thread_wakeup_early(info, NULL);
+ } else if (arena_should_decay_early(tsdn, arena, decay, info,
+ &remaining_sleep, npages_new)) {
+ info->npages_to_purge_new = 0;
+ background_thread_wakeup_early(info, &remaining_sleep);
+ }
+label_done:
+ malloc_mutex_unlock(tsdn, &info->mtx);
+}
+
+/* Called from background threads. */
+void
+arena_do_deferred_work(tsdn_t *tsdn, arena_t *arena) {
+ arena_decay(tsdn, arena, true, false);
+ pa_shard_do_deferred_work(tsdn, &arena->pa_shard);
+}
- extent_hooks_t *extent_hooks = EXTENT_HOOKS_INITIALIZER;
- arena_extents_dirty_dalloc(tsdn, arena, &extent_hooks, slab);
+void
+arena_slab_dalloc(tsdn_t *tsdn, arena_t *arena, edata_t *slab) {
+ bool deferred_work_generated = false;
+ pa_dalloc(tsdn, &arena->pa_shard, slab, &deferred_work_generated);
+ if (deferred_work_generated) {
+ arena_handle_deferred_work(tsdn, arena);
+ }
}
static void
-arena_bin_slabs_nonfull_insert(bin_t *bin, extent_t *slab) {
- assert(extent_nfree_get(slab) > 0);
- extent_heap_insert(&bin->slabs_nonfull, slab);
+arena_bin_slabs_nonfull_insert(bin_t *bin, edata_t *slab) {
+ assert(edata_nfree_get(slab) > 0);
+ edata_heap_insert(&bin->slabs_nonfull, slab);
if (config_stats) {
bin->stats.nonfull_slabs++;
}
}
static void
-arena_bin_slabs_nonfull_remove(bin_t *bin, extent_t *slab) {
- extent_heap_remove(&bin->slabs_nonfull, slab);
+arena_bin_slabs_nonfull_remove(bin_t *bin, edata_t *slab) {
+ edata_heap_remove(&bin->slabs_nonfull, slab);
if (config_stats) {
bin->stats.nonfull_slabs--;
}
}
-static extent_t *
+static edata_t *
arena_bin_slabs_nonfull_tryget(bin_t *bin) {
- extent_t *slab = extent_heap_remove_first(&bin->slabs_nonfull);
+ edata_t *slab = edata_heap_remove_first(&bin->slabs_nonfull);
if (slab == NULL) {
return NULL;
}
@@ -1040,30 +604,30 @@ arena_bin_slabs_nonfull_tryget(bin_t *bin) {
}
static void
-arena_bin_slabs_full_insert(arena_t *arena, bin_t *bin, extent_t *slab) {
- assert(extent_nfree_get(slab) == 0);
+arena_bin_slabs_full_insert(arena_t *arena, bin_t *bin, edata_t *slab) {
+ assert(edata_nfree_get(slab) == 0);
/*
* Tracking extents is required by arena_reset, which is not allowed
- * for auto arenas. Bypass this step to avoid touching the extent
+ * for auto arenas. Bypass this step to avoid touching the edata
* linkage (often results in cache misses) for auto arenas.
*/
if (arena_is_auto(arena)) {
return;
}
- extent_list_append(&bin->slabs_full, slab);
+ edata_list_active_append(&bin->slabs_full, slab);
}
static void
-arena_bin_slabs_full_remove(arena_t *arena, bin_t *bin, extent_t *slab) {
+arena_bin_slabs_full_remove(arena_t *arena, bin_t *bin, edata_t *slab) {
if (arena_is_auto(arena)) {
return;
}
- extent_list_remove(&bin->slabs_full, slab);
+ edata_list_active_remove(&bin->slabs_full, slab);
}
static void
arena_bin_reset(tsd_t *tsd, arena_t *arena, bin_t *bin) {
- extent_t *slab;
+ edata_t *slab;
malloc_mutex_lock(tsd_tsdn(tsd), &bin->lock);
if (bin->slabcur != NULL) {
@@ -1073,13 +637,13 @@ arena_bin_reset(tsd_t *tsd, arena_t *arena, bin_t *bin) {
arena_slab_dalloc(tsd_tsdn(tsd), arena, slab);
malloc_mutex_lock(tsd_tsdn(tsd), &bin->lock);
}
- while ((slab = extent_heap_remove_first(&bin->slabs_nonfull)) != NULL) {
+ while ((slab = edata_heap_remove_first(&bin->slabs_nonfull)) != NULL) {
malloc_mutex_unlock(tsd_tsdn(tsd), &bin->lock);
arena_slab_dalloc(tsd_tsdn(tsd), arena, slab);
malloc_mutex_lock(tsd_tsdn(tsd), &bin->lock);
}
- for (slab = extent_list_first(&bin->slabs_full); slab != NULL;
- slab = extent_list_first(&bin->slabs_full)) {
+ for (slab = edata_list_active_first(&bin->slabs_full); slab != NULL;
+ slab = edata_list_active_first(&bin->slabs_full)) {
arena_bin_slabs_full_remove(arena, bin, slab);
malloc_mutex_unlock(tsd_tsdn(tsd), &bin->lock);
arena_slab_dalloc(tsd_tsdn(tsd), arena, slab);
@@ -1111,16 +675,15 @@ arena_reset(tsd_t *tsd, arena_t *arena) {
/* Large allocations. */
malloc_mutex_lock(tsd_tsdn(tsd), &arena->large_mtx);
- for (extent_t *extent = extent_list_first(&arena->large); extent !=
- NULL; extent = extent_list_first(&arena->large)) {
- void *ptr = extent_base_get(extent);
+ for (edata_t *edata = edata_list_active_first(&arena->large);
+ edata != NULL; edata = edata_list_active_first(&arena->large)) {
+ void *ptr = edata_base_get(edata);
size_t usize;
malloc_mutex_unlock(tsd_tsdn(tsd), &arena->large_mtx);
- alloc_ctx_t alloc_ctx;
- rtree_ctx_t *rtree_ctx = tsd_rtree_ctx(tsd);
- rtree_szind_slab_read(tsd_tsdn(tsd), &extents_rtree, rtree_ctx,
- (uintptr_t)ptr, true, &alloc_ctx.szind, &alloc_ctx.slab);
+ emap_alloc_ctx_t alloc_ctx;
+ emap_alloc_ctx_lookup(tsd_tsdn(tsd), &arena_emap_global, ptr,
+ &alloc_ctx);
assert(alloc_ctx.szind != SC_NSIZES);
if (config_stats || (config_prof && opt_prof)) {
@@ -1131,7 +694,7 @@ arena_reset(tsd_t *tsd, arena_t *arena) {
if (config_prof && opt_prof) {
prof_free(tsd, ptr, usize, &alloc_ctx);
}
- large_dalloc(tsd_tsdn(tsd), extent);
+ large_dalloc(tsd_tsdn(tsd), edata);
malloc_mutex_lock(tsd_tsdn(tsd), &arena->large_mtx);
}
malloc_mutex_unlock(tsd_tsdn(tsd), &arena->large_mtx);
@@ -1139,32 +702,95 @@ arena_reset(tsd_t *tsd, arena_t *arena) {
/* Bins. */
for (unsigned i = 0; i < SC_NBINS; i++) {
for (unsigned j = 0; j < bin_infos[i].n_shards; j++) {
- arena_bin_reset(tsd, arena,
- &arena->bins[i].bin_shards[j]);
+ arena_bin_reset(tsd, arena, arena_get_bin(arena, i, j));
}
}
+ pa_shard_reset(tsd_tsdn(tsd), &arena->pa_shard);
+}
+
+static void
+arena_prepare_base_deletion_sync_finish(tsd_t *tsd, malloc_mutex_t **mutexes,
+ unsigned n_mtx) {
+ for (unsigned i = 0; i < n_mtx; i++) {
+ malloc_mutex_lock(tsd_tsdn(tsd), mutexes[i]);
+ malloc_mutex_unlock(tsd_tsdn(tsd), mutexes[i]);
+ }
+}
+
+#define ARENA_DESTROY_MAX_DELAYED_MTX 32
+static void
+arena_prepare_base_deletion_sync(tsd_t *tsd, malloc_mutex_t *mtx,
+ malloc_mutex_t **delayed_mtx, unsigned *n_delayed) {
+ if (!malloc_mutex_trylock(tsd_tsdn(tsd), mtx)) {
+ /* No contention. */
+ malloc_mutex_unlock(tsd_tsdn(tsd), mtx);
+ return;
+ }
+ unsigned n = *n_delayed;
+ assert(n < ARENA_DESTROY_MAX_DELAYED_MTX);
+ /* Add another to the batch. */
+ delayed_mtx[n++] = mtx;
- atomic_store_zu(&arena->nactive, 0, ATOMIC_RELAXED);
+ if (n == ARENA_DESTROY_MAX_DELAYED_MTX) {
+ arena_prepare_base_deletion_sync_finish(tsd, delayed_mtx, n);
+ n = 0;
+ }
+ *n_delayed = n;
}
static void
-arena_destroy_retained(tsdn_t *tsdn, arena_t *arena) {
+arena_prepare_base_deletion(tsd_t *tsd, base_t *base_to_destroy) {
/*
- * Iterate over the retained extents and destroy them. This gives the
- * extent allocator underlying the extent hooks an opportunity to unmap
- * all retained memory without having to keep its own metadata
- * structures. In practice, virtual memory for dss-allocated extents is
- * leaked here, so best practice is to avoid dss for arenas to be
- * destroyed, or provide custom extent hooks that track retained
- * dss-based extents for later reuse.
+ * In order to coalesce, emap_try_acquire_edata_neighbor will attempt to
+ * check neighbor edata's state to determine eligibility. This means
+ * under certain conditions, the metadata from an arena can be accessed
+ * w/o holding any locks from that arena. In order to guarantee safe
+ * memory access, the metadata and the underlying base allocator needs
+ * to be kept alive, until all pending accesses are done.
+ *
+ * 1) with opt_retain, the arena boundary implies the is_head state
+ * (tracked in the rtree leaf), and the coalesce flow will stop at the
+ * head state branch. Therefore no cross arena metadata access
+ * possible.
+ *
+ * 2) w/o opt_retain, the arena id needs to be read from the edata_t,
+ * meaning read only cross-arena metadata access is possible. The
+ * coalesce attempt will stop at the arena_id mismatch, and is always
+ * under one of the ecache locks. To allow safe passthrough of such
+ * metadata accesses, the loop below will iterate through all manual
+ * arenas' ecache locks. As all the metadata from this base allocator
+ * have been unlinked from the rtree, after going through all the
+ * relevant ecache locks, it's safe to say that a) pending accesses are
+ * all finished, and b) no new access will be generated.
*/
- extent_hooks_t *extent_hooks = extent_hooks_get(arena);
- extent_t *extent;
- while ((extent = extents_evict(tsdn, arena, &extent_hooks,
- &arena->extents_retained, 0)) != NULL) {
- extent_destroy_wrapper(tsdn, arena, &extent_hooks, extent);
+ if (opt_retain) {
+ return;
}
+ unsigned destroy_ind = base_ind_get(base_to_destroy);
+ assert(destroy_ind >= manual_arena_base);
+
+ tsdn_t *tsdn = tsd_tsdn(tsd);
+ malloc_mutex_t *delayed_mtx[ARENA_DESTROY_MAX_DELAYED_MTX];
+ unsigned n_delayed = 0, total = narenas_total_get();
+ for (unsigned i = 0; i < total; i++) {
+ if (i == destroy_ind) {
+ continue;
+ }
+ arena_t *arena = arena_get(tsdn, i, false);
+ if (arena == NULL) {
+ continue;
+ }
+ pac_t *pac = &arena->pa_shard.pac;
+ arena_prepare_base_deletion_sync(tsd, &pac->ecache_dirty.mtx,
+ delayed_mtx, &n_delayed);
+ arena_prepare_base_deletion_sync(tsd, &pac->ecache_muzzy.mtx,
+ delayed_mtx, &n_delayed);
+ arena_prepare_base_deletion_sync(tsd, &pac->ecache_retained.mtx,
+ delayed_mtx, &n_delayed);
+ }
+ arena_prepare_base_deletion_sync_finish(tsd, delayed_mtx, n_delayed);
}
+#undef ARENA_DESTROY_MAX_DELAYED_MTX
void
arena_destroy(tsd_t *tsd, arena_t *arena) {
@@ -1175,13 +801,10 @@ arena_destroy(tsd_t *tsd, arena_t *arena) {
/*
* No allocations have occurred since arena_reset() was called.
* Furthermore, the caller (arena_i_destroy_ctl()) purged all cached
- * extents, so only retained extents may remain.
+ * extents, so only retained extents may remain and it's safe to call
+ * pa_shard_destroy_retained.
*/
- assert(extents_npages_get(&arena->extents_dirty) == 0);
- assert(extents_npages_get(&arena->extents_muzzy) == 0);
-
- /* Deallocate retained memory. */
- arena_destroy_retained(tsd_tsdn(tsd), arena);
+ pa_shard_destroy(tsd_tsdn(tsd), &arena->pa_shard);
/*
* Remove the arena pointer from the arenas array. We rely on the fact
@@ -1197,316 +820,370 @@ arena_destroy(tsd_t *tsd, arena_t *arena) {
/*
* Destroy the base allocator, which manages all metadata ever mapped by
- * this arena.
+ * this arena. The prepare function will make sure no pending access to
+ * the metadata in this base anymore.
*/
+ arena_prepare_base_deletion(tsd, arena->base);
base_delete(tsd_tsdn(tsd), arena->base);
}
-static extent_t *
-arena_slab_alloc_hard(tsdn_t *tsdn, arena_t *arena,
- extent_hooks_t **r_extent_hooks, const bin_info_t *bin_info,
- szind_t szind) {
- extent_t *slab;
- bool zero, commit;
-
- witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
- WITNESS_RANK_CORE, 0);
-
- zero = false;
- commit = true;
- slab = extent_alloc_wrapper(tsdn, arena, r_extent_hooks, NULL,
- bin_info->slab_size, 0, PAGE, true, szind, &zero, &commit);
-
- if (config_stats && slab != NULL) {
- arena_stats_mapped_add(tsdn, &arena->stats,
- bin_info->slab_size);
- }
-
- return slab;
-}
-
-static extent_t *
+static edata_t *
arena_slab_alloc(tsdn_t *tsdn, arena_t *arena, szind_t binind, unsigned binshard,
const bin_info_t *bin_info) {
+ bool deferred_work_generated = false;
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
WITNESS_RANK_CORE, 0);
- extent_hooks_t *extent_hooks = EXTENT_HOOKS_INITIALIZER;
- szind_t szind = sz_size2index(bin_info->reg_size);
- bool zero = false;
- bool commit = true;
- extent_t *slab = extents_alloc(tsdn, arena, &extent_hooks,
- &arena->extents_dirty, NULL, bin_info->slab_size, 0, PAGE, true,
- binind, &zero, &commit);
- if (slab == NULL && arena_may_have_muzzy(arena)) {
- slab = extents_alloc(tsdn, arena, &extent_hooks,
- &arena->extents_muzzy, NULL, bin_info->slab_size, 0, PAGE,
- true, binind, &zero, &commit);
+ bool guarded = san_slab_extent_decide_guard(tsdn,
+ arena_get_ehooks(arena));
+ edata_t *slab = pa_alloc(tsdn, &arena->pa_shard, bin_info->slab_size,
+ /* alignment */ PAGE, /* slab */ true, /* szind */ binind,
+ /* zero */ false, guarded, &deferred_work_generated);
+
+ if (deferred_work_generated) {
+ arena_handle_deferred_work(tsdn, arena);
}
+
if (slab == NULL) {
- slab = arena_slab_alloc_hard(tsdn, arena, &extent_hooks,
- bin_info, szind);
- if (slab == NULL) {
- return NULL;
- }
+ return NULL;
}
- assert(extent_slab_get(slab));
+ assert(edata_slab_get(slab));
/* Initialize slab internals. */
- arena_slab_data_t *slab_data = extent_slab_data_get(slab);
- extent_nfree_binshard_set(slab, bin_info->nregs, binshard);
+ slab_data_t *slab_data = edata_slab_data_get(slab);
+ edata_nfree_binshard_set(slab, bin_info->nregs, binshard);
bitmap_init(slab_data->bitmap, &bin_info->bitmap_info, false);
- arena_nactive_add(arena, extent_size_get(slab) >> LG_PAGE);
-
return slab;
}
-static extent_t *
-arena_bin_nonfull_slab_get(tsdn_t *tsdn, arena_t *arena, bin_t *bin,
- szind_t binind, unsigned binshard) {
- extent_t *slab;
- const bin_info_t *bin_info;
-
- /* Look for a usable slab. */
- slab = arena_bin_slabs_nonfull_tryget(bin);
- if (slab != NULL) {
- return slab;
- }
- /* No existing slabs have any space available. */
-
- bin_info = &bin_infos[binind];
-
- /* Allocate a new slab. */
- malloc_mutex_unlock(tsdn, &bin->lock);
- /******************************/
- slab = arena_slab_alloc(tsdn, arena, binind, binshard, bin_info);
- /********************************/
- malloc_mutex_lock(tsdn, &bin->lock);
- if (slab != NULL) {
- if (config_stats) {
- bin->stats.nslabs++;
- bin->stats.curslabs++;
- }
- return slab;
+/*
+ * Before attempting the _with_fresh_slab approaches below, the _no_fresh_slab
+ * variants (i.e. through slabcur and nonfull) must be tried first.
+ */
+static void
+arena_bin_refill_slabcur_with_fresh_slab(tsdn_t *tsdn, arena_t *arena,
+ bin_t *bin, szind_t binind, edata_t *fresh_slab) {
+ malloc_mutex_assert_owner(tsdn, &bin->lock);
+ /* Only called after slabcur and nonfull both failed. */
+ assert(bin->slabcur == NULL);
+ assert(edata_heap_first(&bin->slabs_nonfull) == NULL);
+ assert(fresh_slab != NULL);
+
+ /* A new slab from arena_slab_alloc() */
+ assert(edata_nfree_get(fresh_slab) == bin_infos[binind].nregs);
+ if (config_stats) {
+ bin->stats.nslabs++;
+ bin->stats.curslabs++;
}
+ bin->slabcur = fresh_slab;
+}
- /*
- * arena_slab_alloc() failed, but another thread may have made
- * sufficient memory available while this one dropped bin->lock above,
- * so search one more time.
- */
- slab = arena_bin_slabs_nonfull_tryget(bin);
- if (slab != NULL) {
- return slab;
- }
+/* Refill slabcur and then alloc using the fresh slab */
+static void *
+arena_bin_malloc_with_fresh_slab(tsdn_t *tsdn, arena_t *arena, bin_t *bin,
+ szind_t binind, edata_t *fresh_slab) {
+ malloc_mutex_assert_owner(tsdn, &bin->lock);
+ arena_bin_refill_slabcur_with_fresh_slab(tsdn, arena, bin, binind,
+ fresh_slab);
- return NULL;
+ return arena_slab_reg_alloc(bin->slabcur, &bin_infos[binind]);
}
-/* Re-fill bin->slabcur, then call arena_slab_reg_alloc(). */
-static void *
-arena_bin_malloc_hard(tsdn_t *tsdn, arena_t *arena, bin_t *bin,
- szind_t binind, unsigned binshard) {
- const bin_info_t *bin_info;
- extent_t *slab;
+static bool
+arena_bin_refill_slabcur_no_fresh_slab(tsdn_t *tsdn, arena_t *arena,
+ bin_t *bin) {
+ malloc_mutex_assert_owner(tsdn, &bin->lock);
+ /* Only called after arena_slab_reg_alloc[_batch] failed. */
+ assert(bin->slabcur == NULL || edata_nfree_get(bin->slabcur) == 0);
- bin_info = &bin_infos[binind];
- if (!arena_is_auto(arena) && bin->slabcur != NULL) {
- arena_bin_slabs_full_insert(arena, bin, bin->slabcur);
- bin->slabcur = NULL;
- }
- slab = arena_bin_nonfull_slab_get(tsdn, arena, bin, binind, binshard);
if (bin->slabcur != NULL) {
- /*
- * Another thread updated slabcur while this one ran without the
- * bin lock in arena_bin_nonfull_slab_get().
- */
- if (extent_nfree_get(bin->slabcur) > 0) {
- void *ret = arena_slab_reg_alloc(bin->slabcur,
- bin_info);
- if (slab != NULL) {
- /*
- * arena_slab_alloc() may have allocated slab,
- * or it may have been pulled from
- * slabs_nonfull. Therefore it is unsafe to
- * make any assumptions about how slab has
- * previously been used, and
- * arena_bin_lower_slab() must be called, as if
- * a region were just deallocated from the slab.
- */
- if (extent_nfree_get(slab) == bin_info->nregs) {
- arena_dalloc_bin_slab(tsdn, arena, slab,
- bin);
- } else {
- arena_bin_lower_slab(tsdn, arena, slab,
- bin);
- }
- }
- return ret;
- }
-
arena_bin_slabs_full_insert(arena, bin, bin->slabcur);
- bin->slabcur = NULL;
- }
-
- if (slab == NULL) {
- return NULL;
}
- bin->slabcur = slab;
- assert(extent_nfree_get(bin->slabcur) > 0);
+ /* Look for a usable slab. */
+ bin->slabcur = arena_bin_slabs_nonfull_tryget(bin);
+ assert(bin->slabcur == NULL || edata_nfree_get(bin->slabcur) > 0);
- return arena_slab_reg_alloc(slab, bin_info);
+ return (bin->slabcur == NULL);
}
-/* Choose a bin shard and return the locked bin. */
bin_t *
-arena_bin_choose_lock(tsdn_t *tsdn, arena_t *arena, szind_t binind,
- unsigned *binshard) {
- bin_t *bin;
+arena_bin_choose(tsdn_t *tsdn, arena_t *arena, szind_t binind,
+ unsigned *binshard_p) {
+ unsigned binshard;
if (tsdn_null(tsdn) || tsd_arena_get(tsdn_tsd(tsdn)) == NULL) {
- *binshard = 0;
+ binshard = 0;
} else {
- *binshard = tsd_binshardsp_get(tsdn_tsd(tsdn))->binshard[binind];
+ binshard = tsd_binshardsp_get(tsdn_tsd(tsdn))->binshard[binind];
}
- assert(*binshard < bin_infos[binind].n_shards);
- bin = &arena->bins[binind].bin_shards[*binshard];
- malloc_mutex_lock(tsdn, &bin->lock);
-
- return bin;
+ assert(binshard < bin_infos[binind].n_shards);
+ if (binshard_p != NULL) {
+ *binshard_p = binshard;
+ }
+ return arena_get_bin(arena, binind, binshard);
}
void
-arena_tcache_fill_small(tsdn_t *tsdn, arena_t *arena, tcache_t *tcache,
- cache_bin_t *tbin, szind_t binind, uint64_t prof_accumbytes) {
- unsigned i, nfill, cnt;
+arena_cache_bin_fill_small(tsdn_t *tsdn, arena_t *arena,
+ cache_bin_t *cache_bin, cache_bin_info_t *cache_bin_info, szind_t binind,
+ const unsigned nfill) {
+ assert(cache_bin_ncached_get_local(cache_bin, cache_bin_info) == 0);
+
+ const bin_info_t *bin_info = &bin_infos[binind];
+
+ CACHE_BIN_PTR_ARRAY_DECLARE(ptrs, nfill);
+ cache_bin_init_ptr_array_for_fill(cache_bin, cache_bin_info, &ptrs,
+ nfill);
+ /*
+ * Bin-local resources are used first: 1) bin->slabcur, and 2) nonfull
+ * slabs. After both are exhausted, new slabs will be allocated through
+ * arena_slab_alloc().
+ *
+ * Bin lock is only taken / released right before / after the while(...)
+ * refill loop, with new slab allocation (which has its own locking)
+ * kept outside of the loop. This setup facilitates flat combining, at
+ * the cost of the nested loop (through goto label_refill).
+ *
+ * To optimize for cases with contention and limited resources
+ * (e.g. hugepage-backed or non-overcommit arenas), each fill-iteration
+ * gets one chance of slab_alloc, and a retry of bin local resources
+ * after the slab allocation (regardless if slab_alloc failed, because
+ * the bin lock is dropped during the slab allocation).
+ *
+ * In other words, new slab allocation is allowed, as long as there was
+ * progress since the previous slab_alloc. This is tracked with
+ * made_progress below, initialized to true to jump start the first
+ * iteration.
+ *
+ * In other words (again), the loop will only terminate early (i.e. stop
+ * with filled < nfill) after going through the three steps: a) bin
+ * local exhausted, b) unlock and slab_alloc returns null, c) re-lock
+ * and bin local fails again.
+ */
+ bool made_progress = true;
+ edata_t *fresh_slab = NULL;
+ bool alloc_and_retry = false;
+ unsigned filled = 0;
+ unsigned binshard;
+ bin_t *bin = arena_bin_choose(tsdn, arena, binind, &binshard);
+
+label_refill:
+ malloc_mutex_lock(tsdn, &bin->lock);
- assert(tbin->ncached == 0);
+ while (filled < nfill) {
+ /* Try batch-fill from slabcur first. */
+ edata_t *slabcur = bin->slabcur;
+ if (slabcur != NULL && edata_nfree_get(slabcur) > 0) {
+ unsigned tofill = nfill - filled;
+ unsigned nfree = edata_nfree_get(slabcur);
+ unsigned cnt = tofill < nfree ? tofill : nfree;
+
+ arena_slab_reg_alloc_batch(slabcur, bin_info, cnt,
+ &ptrs.ptr[filled]);
+ made_progress = true;
+ filled += cnt;
+ continue;
+ }
+ /* Next try refilling slabcur from nonfull slabs. */
+ if (!arena_bin_refill_slabcur_no_fresh_slab(tsdn, arena, bin)) {
+ assert(bin->slabcur != NULL);
+ continue;
+ }
+
+ /* Then see if a new slab was reserved already. */
+ if (fresh_slab != NULL) {
+ arena_bin_refill_slabcur_with_fresh_slab(tsdn, arena,
+ bin, binind, fresh_slab);
+ assert(bin->slabcur != NULL);
+ fresh_slab = NULL;
+ continue;
+ }
+
+ /* Try slab_alloc if made progress (or never did slab_alloc). */
+ if (made_progress) {
+ assert(bin->slabcur == NULL);
+ assert(fresh_slab == NULL);
+ alloc_and_retry = true;
+ /* Alloc a new slab then come back. */
+ break;
+ }
+
+ /* OOM. */
+
+ assert(fresh_slab == NULL);
+ assert(!alloc_and_retry);
+ break;
+ } /* while (filled < nfill) loop. */
- if (config_prof && arena_prof_accum(tsdn, arena, prof_accumbytes)) {
- prof_idump(tsdn);
+ if (config_stats && !alloc_and_retry) {
+ bin->stats.nmalloc += filled;
+ bin->stats.nrequests += cache_bin->tstats.nrequests;
+ bin->stats.curregs += filled;
+ bin->stats.nfills++;
+ cache_bin->tstats.nrequests = 0;
+ }
+
+ malloc_mutex_unlock(tsdn, &bin->lock);
+
+ if (alloc_and_retry) {
+ assert(fresh_slab == NULL);
+ assert(filled < nfill);
+ assert(made_progress);
+
+ fresh_slab = arena_slab_alloc(tsdn, arena, binind, binshard,
+ bin_info);
+ /* fresh_slab NULL case handled in the for loop. */
+
+ alloc_and_retry = false;
+ made_progress = false;
+ goto label_refill;
}
+ assert(filled == nfill || (fresh_slab == NULL && !made_progress));
+ /* Release if allocated but not used. */
+ if (fresh_slab != NULL) {
+ assert(edata_nfree_get(fresh_slab) == bin_info->nregs);
+ arena_slab_dalloc(tsdn, arena, fresh_slab);
+ fresh_slab = NULL;
+ }
+
+ cache_bin_finish_fill(cache_bin, cache_bin_info, &ptrs, filled);
+ arena_decay_tick(tsdn, arena);
+}
+
+size_t
+arena_fill_small_fresh(tsdn_t *tsdn, arena_t *arena, szind_t binind,
+ void **ptrs, size_t nfill, bool zero) {
+ assert(binind < SC_NBINS);
+ const bin_info_t *bin_info = &bin_infos[binind];
+ const size_t nregs = bin_info->nregs;
+ assert(nregs > 0);
+ const size_t usize = bin_info->reg_size;
+
+ const bool manual_arena = !arena_is_auto(arena);
unsigned binshard;
- bin_t *bin = arena_bin_choose_lock(tsdn, arena, binind, &binshard);
-
- for (i = 0, nfill = (tcache_bin_info[binind].ncached_max >>
- tcache->lg_fill_div[binind]); i < nfill; i += cnt) {
- extent_t *slab;
- if ((slab = bin->slabcur) != NULL && extent_nfree_get(slab) >
- 0) {
- unsigned tofill = nfill - i;
- cnt = tofill < extent_nfree_get(slab) ?
- tofill : extent_nfree_get(slab);
- arena_slab_reg_alloc_batch(
- slab, &bin_infos[binind], cnt,
- tbin->avail - nfill + i);
- } else {
- cnt = 1;
- void *ptr = arena_bin_malloc_hard(tsdn, arena, bin,
- binind, binshard);
- /*
- * OOM. tbin->avail isn't yet filled down to its first
- * element, so the successful allocations (if any) must
- * be moved just before tbin->avail before bailing out.
- */
- if (ptr == NULL) {
- if (i > 0) {
- memmove(tbin->avail - i,
- tbin->avail - nfill,
- i * sizeof(void *));
- }
- break;
- }
- /* Insert such that low regions get used first. */
- *(tbin->avail - nfill + i) = ptr;
+ bin_t *bin = arena_bin_choose(tsdn, arena, binind, &binshard);
+
+ size_t nslab = 0;
+ size_t filled = 0;
+ edata_t *slab = NULL;
+ edata_list_active_t fulls;
+ edata_list_active_init(&fulls);
+
+ while (filled < nfill && (slab = arena_slab_alloc(tsdn, arena, binind,
+ binshard, bin_info)) != NULL) {
+ assert((size_t)edata_nfree_get(slab) == nregs);
+ ++nslab;
+ size_t batch = nfill - filled;
+ if (batch > nregs) {
+ batch = nregs;
+ }
+ assert(batch > 0);
+ arena_slab_reg_alloc_batch(slab, bin_info, (unsigned)batch,
+ &ptrs[filled]);
+ assert(edata_addr_get(slab) == ptrs[filled]);
+ if (zero) {
+ memset(ptrs[filled], 0, batch * usize);
}
- if (config_fill && unlikely(opt_junk_alloc)) {
- for (unsigned j = 0; j < cnt; j++) {
- void* ptr = *(tbin->avail - nfill + i + j);
- arena_alloc_junk_small(ptr, &bin_infos[binind],
- true);
+ filled += batch;
+ if (batch == nregs) {
+ if (manual_arena) {
+ edata_list_active_append(&fulls, slab);
}
+ slab = NULL;
}
}
+
+ malloc_mutex_lock(tsdn, &bin->lock);
+ /*
+ * Only the last slab can be non-empty, and the last slab is non-empty
+ * iff slab != NULL.
+ */
+ if (slab != NULL) {
+ arena_bin_lower_slab(tsdn, arena, slab, bin);
+ }
+ if (manual_arena) {
+ edata_list_active_concat(&bin->slabs_full, &fulls);
+ }
+ assert(edata_list_active_empty(&fulls));
if (config_stats) {
- bin->stats.nmalloc += i;
- bin->stats.nrequests += tbin->tstats.nrequests;
- bin->stats.curregs += i;
- bin->stats.nfills++;
- tbin->tstats.nrequests = 0;
+ bin->stats.nslabs += nslab;
+ bin->stats.curslabs += nslab;
+ bin->stats.nmalloc += filled;
+ bin->stats.nrequests += filled;
+ bin->stats.curregs += filled;
}
malloc_mutex_unlock(tsdn, &bin->lock);
- tbin->ncached = i;
+
arena_decay_tick(tsdn, arena);
+ return filled;
}
-void
-arena_alloc_junk_small(void *ptr, const bin_info_t *bin_info, bool zero) {
- if (!zero) {
- memset(ptr, JEMALLOC_ALLOC_JUNK, bin_info->reg_size);
+/*
+ * Without allocating a new slab, try arena_slab_reg_alloc() and re-fill
+ * bin->slabcur if necessary.
+ */
+static void *
+arena_bin_malloc_no_fresh_slab(tsdn_t *tsdn, arena_t *arena, bin_t *bin,
+ szind_t binind) {
+ malloc_mutex_assert_owner(tsdn, &bin->lock);
+ if (bin->slabcur == NULL || edata_nfree_get(bin->slabcur) == 0) {
+ if (arena_bin_refill_slabcur_no_fresh_slab(tsdn, arena, bin)) {
+ return NULL;
+ }
}
-}
-static void
-arena_dalloc_junk_small_impl(void *ptr, const bin_info_t *bin_info) {
- memset(ptr, JEMALLOC_FREE_JUNK, bin_info->reg_size);
+ assert(bin->slabcur != NULL && edata_nfree_get(bin->slabcur) > 0);
+ return arena_slab_reg_alloc(bin->slabcur, &bin_infos[binind]);
}
-arena_dalloc_junk_small_t *JET_MUTABLE arena_dalloc_junk_small =
- arena_dalloc_junk_small_impl;
static void *
arena_malloc_small(tsdn_t *tsdn, arena_t *arena, szind_t binind, bool zero) {
- void *ret;
- bin_t *bin;
- size_t usize;
- extent_t *slab;
-
assert(binind < SC_NBINS);
- usize = sz_index2size(binind);
+ const bin_info_t *bin_info = &bin_infos[binind];
+ size_t usize = sz_index2size(binind);
unsigned binshard;
- bin = arena_bin_choose_lock(tsdn, arena, binind, &binshard);
-
- if ((slab = bin->slabcur) != NULL && extent_nfree_get(slab) > 0) {
- ret = arena_slab_reg_alloc(slab, &bin_infos[binind]);
- } else {
- ret = arena_bin_malloc_hard(tsdn, arena, bin, binind, binshard);
- }
+ bin_t *bin = arena_bin_choose(tsdn, arena, binind, &binshard);
+ malloc_mutex_lock(tsdn, &bin->lock);
+ edata_t *fresh_slab = NULL;
+ void *ret = arena_bin_malloc_no_fresh_slab(tsdn, arena, bin, binind);
if (ret == NULL) {
malloc_mutex_unlock(tsdn, &bin->lock);
- return NULL;
+ /******************************/
+ fresh_slab = arena_slab_alloc(tsdn, arena, binind, binshard,
+ bin_info);
+ /********************************/
+ malloc_mutex_lock(tsdn, &bin->lock);
+ /* Retry since the lock was dropped. */
+ ret = arena_bin_malloc_no_fresh_slab(tsdn, arena, bin, binind);
+ if (ret == NULL) {
+ if (fresh_slab == NULL) {
+ /* OOM */
+ malloc_mutex_unlock(tsdn, &bin->lock);
+ return NULL;
+ }
+ ret = arena_bin_malloc_with_fresh_slab(tsdn, arena, bin,
+ binind, fresh_slab);
+ fresh_slab = NULL;
+ }
}
-
if (config_stats) {
bin->stats.nmalloc++;
bin->stats.nrequests++;
bin->stats.curregs++;
}
malloc_mutex_unlock(tsdn, &bin->lock);
- if (config_prof && arena_prof_accum(tsdn, arena, usize)) {
- prof_idump(tsdn);
- }
- if (!zero) {
- if (config_fill) {
- if (unlikely(opt_junk_alloc)) {
- arena_alloc_junk_small(ret,
- &bin_infos[binind], false);
- } else if (unlikely(opt_zero)) {
- memset(ret, 0, usize);
- }
- }
- } else {
- if (config_fill && unlikely(opt_junk_alloc)) {
- arena_alloc_junk_small(ret, &bin_infos[binind],
- true);
- }
+ if (fresh_slab != NULL) {
+ arena_slab_dalloc(tsdn, arena, fresh_slab);
+ }
+ if (zero) {
memset(ret, 0, usize);
}
-
arena_decay_tick(tsdn, arena);
+
return ret;
}
@@ -1533,10 +1210,17 @@ arena_palloc(tsdn_t *tsdn, arena_t *arena, size_t usize, size_t alignment,
bool zero, tcache_t *tcache) {
void *ret;
- if (usize <= SC_SMALL_MAXCLASS
- && (alignment < PAGE
- || (alignment == PAGE && (usize & PAGE_MASK) == 0))) {
+ if (usize <= SC_SMALL_MAXCLASS) {
/* Small; alignment doesn't require special slab placement. */
+
+ /* usize should be a result of sz_sa2u() */
+ assert((usize & (alignment - 1)) == 0);
+
+ /*
+ * Small usize can't come from an alignment larger than a page.
+ */
+ assert(alignment <= PAGE);
+
ret = arena_malloc(tsdn, arena, usize, sz_size2index(usize),
zero, tcache, true);
} else {
@@ -1560,33 +1244,22 @@ arena_prof_promote(tsdn_t *tsdn, void *ptr, size_t usize) {
safety_check_set_redzone(ptr, usize, SC_LARGE_MINCLASS);
}
- rtree_ctx_t rtree_ctx_fallback;
- rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);
-
- extent_t *extent = rtree_extent_read(tsdn, &extents_rtree, rtree_ctx,
- (uintptr_t)ptr, true);
- arena_t *arena = extent_arena_get(extent);
+ edata_t *edata = emap_edata_lookup(tsdn, &arena_emap_global, ptr);
szind_t szind = sz_size2index(usize);
- extent_szind_set(extent, szind);
- rtree_szind_slab_update(tsdn, &extents_rtree, rtree_ctx, (uintptr_t)ptr,
- szind, false);
-
- prof_accum_cancel(tsdn, &arena->prof_accum, usize);
+ edata_szind_set(edata, szind);
+ emap_remap(tsdn, &arena_emap_global, edata, szind, /* slab */ false);
assert(isalloc(tsdn, ptr) == usize);
}
static size_t
-arena_prof_demote(tsdn_t *tsdn, extent_t *extent, const void *ptr) {
+arena_prof_demote(tsdn_t *tsdn, edata_t *edata, const void *ptr) {
cassert(config_prof);
assert(ptr != NULL);
- extent_szind_set(extent, SC_NBINS);
- rtree_ctx_t rtree_ctx_fallback;
- rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);
- rtree_szind_slab_update(tsdn, &extents_rtree, rtree_ctx, (uintptr_t)ptr,
- SC_NBINS, false);
+ edata_szind_set(edata, SC_NBINS);
+ emap_remap(tsdn, &arena_emap_global, edata, SC_NBINS, /* slab */ false);
assert(isalloc(tsdn, ptr) == SC_LARGE_MINCLASS);
@@ -1599,9 +1272,9 @@ arena_dalloc_promoted(tsdn_t *tsdn, void *ptr, tcache_t *tcache,
cassert(config_prof);
assert(opt_prof);
- extent_t *extent = iealloc(tsdn, ptr);
- size_t usize = extent_usize_get(extent);
- size_t bumped_usize = arena_prof_demote(tsdn, extent, ptr);
+ edata_t *edata = emap_edata_lookup(tsdn, &arena_emap_global, ptr);
+ size_t usize = edata_usize_get(edata);
+ size_t bumped_usize = arena_prof_demote(tsdn, edata, ptr);
if (config_opt_safety_checks && usize < SC_LARGE_MINCLASS) {
/*
* Currently, we only do redzoning for small sampled
@@ -1614,17 +1287,17 @@ arena_dalloc_promoted(tsdn_t *tsdn, void *ptr, tcache_t *tcache,
tcache_dalloc_large(tsdn_tsd(tsdn), tcache, ptr,
sz_size2index(bumped_usize), slow_path);
} else {
- large_dalloc(tsdn, extent);
+ large_dalloc(tsdn, edata);
}
}
static void
-arena_dissociate_bin_slab(arena_t *arena, extent_t *slab, bin_t *bin) {
+arena_dissociate_bin_slab(arena_t *arena, edata_t *slab, bin_t *bin) {
/* Dissociate slab from bin. */
if (slab == bin->slabcur) {
bin->slabcur = NULL;
} else {
- szind_t binind = extent_szind_get(slab);
+ szind_t binind = edata_szind_get(slab);
const bin_info_t *bin_info = &bin_infos[binind];
/*
@@ -1641,24 +1314,9 @@ arena_dissociate_bin_slab(arena_t *arena, extent_t *slab, bin_t *bin) {
}
static void
-arena_dalloc_bin_slab(tsdn_t *tsdn, arena_t *arena, extent_t *slab,
+arena_bin_lower_slab(tsdn_t *tsdn, arena_t *arena, edata_t *slab,
bin_t *bin) {
- assert(slab != bin->slabcur);
-
- malloc_mutex_unlock(tsdn, &bin->lock);
- /******************************/
- arena_slab_dalloc(tsdn, arena, slab);
- /****************************/
- malloc_mutex_lock(tsdn, &bin->lock);
- if (config_stats) {
- bin->stats.curslabs--;
- }
-}
-
-static void
-arena_bin_lower_slab(tsdn_t *tsdn, arena_t *arena, extent_t *slab,
- bin_t *bin) {
- assert(extent_nfree_get(slab) > 0);
+ assert(edata_nfree_get(slab) > 0);
/*
* Make sure that if bin->slabcur is non-NULL, it refers to the
@@ -1666,9 +1324,9 @@ arena_bin_lower_slab(tsdn_t *tsdn, arena_t *arena, extent_t *slab,
* than proactively keeping it pointing at the oldest/lowest non-full
* slab.
*/
- if (bin->slabcur != NULL && extent_snad_comp(bin->slabcur, slab) > 0) {
+ if (bin->slabcur != NULL && edata_snad_comp(bin->slabcur, slab) > 0) {
/* Switch slabcur. */
- if (extent_nfree_get(bin->slabcur) > 0) {
+ if (edata_nfree_get(bin->slabcur) > 0) {
arena_bin_slabs_nonfull_insert(bin, bin->slabcur);
} else {
arena_bin_slabs_full_insert(arena, bin, bin->slabcur);
@@ -1683,56 +1341,54 @@ arena_bin_lower_slab(tsdn_t *tsdn, arena_t *arena, extent_t *slab,
}
static void
-arena_dalloc_bin_locked_impl(tsdn_t *tsdn, arena_t *arena, bin_t *bin,
- szind_t binind, extent_t *slab, void *ptr, bool junked) {
- arena_slab_data_t *slab_data = extent_slab_data_get(slab);
- const bin_info_t *bin_info = &bin_infos[binind];
-
- if (!junked && config_fill && unlikely(opt_junk_free)) {
- arena_dalloc_junk_small(ptr, bin_info);
- }
-
- arena_slab_reg_dalloc(slab, slab_data, ptr);
- unsigned nfree = extent_nfree_get(slab);
- if (nfree == bin_info->nregs) {
- arena_dissociate_bin_slab(arena, slab, bin);
- arena_dalloc_bin_slab(tsdn, arena, slab, bin);
- } else if (nfree == 1 && slab != bin->slabcur) {
- arena_bin_slabs_full_remove(arena, bin, slab);
- arena_bin_lower_slab(tsdn, arena, slab, bin);
- }
+arena_dalloc_bin_slab_prepare(tsdn_t *tsdn, edata_t *slab, bin_t *bin) {
+ malloc_mutex_assert_owner(tsdn, &bin->lock);
+ assert(slab != bin->slabcur);
if (config_stats) {
- bin->stats.ndalloc++;
- bin->stats.curregs--;
+ bin->stats.curslabs--;
}
}
void
-arena_dalloc_bin_junked_locked(tsdn_t *tsdn, arena_t *arena, bin_t *bin,
- szind_t binind, extent_t *extent, void *ptr) {
- arena_dalloc_bin_locked_impl(tsdn, arena, bin, binind, extent, ptr,
- true);
+arena_dalloc_bin_locked_handle_newly_empty(tsdn_t *tsdn, arena_t *arena,
+ edata_t *slab, bin_t *bin) {
+ arena_dissociate_bin_slab(arena, slab, bin);
+ arena_dalloc_bin_slab_prepare(tsdn, slab, bin);
+}
+
+void
+arena_dalloc_bin_locked_handle_newly_nonempty(tsdn_t *tsdn, arena_t *arena,
+ edata_t *slab, bin_t *bin) {
+ arena_bin_slabs_full_remove(arena, bin, slab);
+ arena_bin_lower_slab(tsdn, arena, slab, bin);
}
static void
-arena_dalloc_bin(tsdn_t *tsdn, arena_t *arena, extent_t *extent, void *ptr) {
- szind_t binind = extent_szind_get(extent);
- unsigned binshard = extent_binshard_get(extent);
- bin_t *bin = &arena->bins[binind].bin_shards[binshard];
+arena_dalloc_bin(tsdn_t *tsdn, arena_t *arena, edata_t *edata, void *ptr) {
+ szind_t binind = edata_szind_get(edata);
+ unsigned binshard = edata_binshard_get(edata);
+ bin_t *bin = arena_get_bin(arena, binind, binshard);
malloc_mutex_lock(tsdn, &bin->lock);
- arena_dalloc_bin_locked_impl(tsdn, arena, bin, binind, extent, ptr,
- false);
+ arena_dalloc_bin_locked_info_t info;
+ arena_dalloc_bin_locked_begin(&info, binind);
+ bool ret = arena_dalloc_bin_locked_step(tsdn, arena, bin,
+ &info, binind, edata, ptr);
+ arena_dalloc_bin_locked_finish(tsdn, arena, bin, &info);
malloc_mutex_unlock(tsdn, &bin->lock);
+
+ if (ret) {
+ arena_slab_dalloc(tsdn, arena, edata);
+ }
}
void
arena_dalloc_small(tsdn_t *tsdn, void *ptr) {
- extent_t *extent = iealloc(tsdn, ptr);
- arena_t *arena = extent_arena_get(extent);
+ edata_t *edata = emap_edata_lookup(tsdn, &arena_emap_global, ptr);
+ arena_t *arena = arena_get_from_edata(edata);
- arena_dalloc_bin(tsdn, arena, extent, ptr);
+ arena_dalloc_bin(tsdn, arena, edata, ptr);
arena_decay_tick(tsdn, arena);
}
@@ -1743,7 +1399,7 @@ arena_ralloc_no_move(tsdn_t *tsdn, void *ptr, size_t oldsize, size_t size,
/* Calls with non-zero extra had to clamp extra. */
assert(extra == 0 || size + extra <= SC_LARGE_MAXCLASS);
- extent_t *extent = iealloc(tsdn, ptr);
+ edata_t *edata = emap_edata_lookup(tsdn, &arena_emap_global, ptr);
if (unlikely(size > SC_LARGE_MAXCLASS)) {
ret = true;
goto done;
@@ -1766,18 +1422,19 @@ arena_ralloc_no_move(tsdn_t *tsdn, void *ptr, size_t oldsize, size_t size,
goto done;
}
- arena_decay_tick(tsdn, extent_arena_get(extent));
+ arena_t *arena = arena_get_from_edata(edata);
+ arena_decay_tick(tsdn, arena);
ret = false;
} else if (oldsize >= SC_LARGE_MINCLASS
&& usize_max >= SC_LARGE_MINCLASS) {
- ret = large_ralloc_no_move(tsdn, extent, usize_min, usize_max,
+ ret = large_ralloc_no_move(tsdn, edata, usize_min, usize_max,
zero);
} else {
ret = true;
}
done:
- assert(extent == iealloc(tsdn, ptr));
- *newsize = extent_usize_get(extent);
+ assert(edata == emap_edata_lookup(tsdn, &arena_emap_global, ptr));
+ *newsize = edata_usize_get(edata);
return ret;
}
@@ -1800,7 +1457,7 @@ void *
arena_ralloc(tsdn_t *tsdn, arena_t *arena, void *ptr, size_t oldsize,
size_t size, size_t alignment, bool zero, tcache_t *tcache,
hook_ralloc_args_t *hook_args) {
- size_t usize = sz_s2u(size);
+ size_t usize = alignment == 0 ? sz_s2u(size) : sz_sa2u(size, alignment);
if (unlikely(usize == 0 || size > SC_LARGE_MAXCLASS)) {
return NULL;
}
@@ -1850,6 +1507,29 @@ arena_ralloc(tsdn_t *tsdn, arena_t *arena, void *ptr, size_t oldsize,
return ret;
}
+ehooks_t *
+arena_get_ehooks(arena_t *arena) {
+ return base_ehooks_get(arena->base);
+}
+
+extent_hooks_t *
+arena_set_extent_hooks(tsd_t *tsd, arena_t *arena,
+ extent_hooks_t *extent_hooks) {
+ background_thread_info_t *info;
+ if (have_background_thread) {
+ info = arena_background_thread_info_get(arena);
+ malloc_mutex_lock(tsd_tsdn(tsd), &info->mtx);
+ }
+ /* No using the HPA now that we have the custom hooks. */
+ pa_shard_disable_hpa(tsd_tsdn(tsd), &arena->pa_shard);
+ extent_hooks_t *ret = base_extent_hooks_set(arena->base, extent_hooks);
+ if (have_background_thread) {
+ malloc_mutex_unlock(tsd_tsdn(tsd), &info->mtx);
+ }
+
+ return ret;
+}
+
dss_prec_t
arena_dss_prec_get(arena_t *arena) {
return (dss_prec_t)atomic_load_u(&arena->dss_prec, ATOMIC_ACQUIRE);
@@ -1871,7 +1551,7 @@ arena_dirty_decay_ms_default_get(void) {
bool
arena_dirty_decay_ms_default_set(ssize_t decay_ms) {
- if (!arena_decay_ms_valid(decay_ms)) {
+ if (!decay_ms_valid(decay_ms)) {
return true;
}
atomic_store_zd(&dirty_decay_ms_default, decay_ms, ATOMIC_RELAXED);
@@ -1885,7 +1565,7 @@ arena_muzzy_decay_ms_default_get(void) {
bool
arena_muzzy_decay_ms_default_set(ssize_t decay_ms) {
- if (!arena_decay_ms_valid(decay_ms)) {
+ if (!decay_ms_valid(decay_ms)) {
return true;
}
atomic_store_zd(&muzzy_decay_ms_default, decay_ms, ATOMIC_RELAXED);
@@ -1896,26 +1576,8 @@ bool
arena_retain_grow_limit_get_set(tsd_t *tsd, arena_t *arena, size_t *old_limit,
size_t *new_limit) {
assert(opt_retain);
-
- pszind_t new_ind JEMALLOC_CC_SILENCE_INIT(0);
- if (new_limit != NULL) {
- size_t limit = *new_limit;
- /* Grow no more than the new limit. */
- if ((new_ind = sz_psz2ind(limit + 1) - 1) >= SC_NPSIZES) {
- return true;
- }
- }
-
- malloc_mutex_lock(tsd_tsdn(tsd), &arena->extent_grow_mtx);
- if (old_limit != NULL) {
- *old_limit = sz_pind2sz(arena->retain_grow_limit);
- }
- if (new_limit != NULL) {
- arena->retain_grow_limit = new_ind;
- }
- malloc_mutex_unlock(tsd_tsdn(tsd), &arena->extent_grow_mtx);
-
- return false;
+ return pac_retain_grow_limit_get_set(tsd_tsdn(tsd),
+ &arena->pa_shard.pac, old_limit, new_limit);
}
unsigned
@@ -1933,13 +1595,8 @@ arena_nthreads_dec(arena_t *arena, bool internal) {
atomic_fetch_sub_u(&arena->nthreads[internal], 1, ATOMIC_RELAXED);
}
-size_t
-arena_extent_sn_next(arena_t *arena) {
- return atomic_fetch_add_zu(&arena->extent_sn_next, 1, ATOMIC_RELAXED);
-}
-
arena_t *
-arena_new(tsdn_t *tsdn, unsigned ind, extent_hooks_t *extent_hooks) {
+arena_new(tsdn_t *tsdn, unsigned ind, const arena_config_t *config) {
arena_t *arena;
base_t *base;
unsigned i;
@@ -1947,16 +1604,13 @@ arena_new(tsdn_t *tsdn, unsigned ind, extent_hooks_t *extent_hooks) {
if (ind == 0) {
base = b0get();
} else {
- base = base_new(tsdn, ind, extent_hooks);
+ base = base_new(tsdn, ind, config->extent_hooks,
+ config->metadata_use_hooks);
if (base == NULL) {
return NULL;
}
}
- unsigned nbins_total = 0;
- for (i = 0; i < SC_NBINS; i++) {
- nbins_total += bin_infos[i].n_shards;
- }
size_t arena_size = sizeof(arena_t) + sizeof(bin_t) * nbins_total;
arena = (arena_t *)base_alloc(tsdn, base, arena_size, CACHELINE);
if (arena == NULL) {
@@ -1980,110 +1634,56 @@ arena_new(tsdn_t *tsdn, unsigned ind, extent_hooks_t *extent_hooks) {
}
}
- if (config_prof) {
- if (prof_accum_init(tsdn, &arena->prof_accum)) {
- goto label_error;
- }
- }
-
- if (config_cache_oblivious) {
- /*
- * A nondeterministic seed based on the address of arena reduces
- * the likelihood of lockstep non-uniform cache index
- * utilization among identical concurrent processes, but at the
- * cost of test repeatability. For debug builds, instead use a
- * deterministic seed.
- */
- atomic_store_zu(&arena->offset_state, config_debug ? ind :
- (size_t)(uintptr_t)arena, ATOMIC_RELAXED);
- }
-
- atomic_store_zu(&arena->extent_sn_next, 0, ATOMIC_RELAXED);
-
atomic_store_u(&arena->dss_prec, (unsigned)extent_dss_prec_get(),
ATOMIC_RELAXED);
- atomic_store_zu(&arena->nactive, 0, ATOMIC_RELAXED);
-
- extent_list_init(&arena->large);
+ edata_list_active_init(&arena->large);
if (malloc_mutex_init(&arena->large_mtx, "arena_large",
WITNESS_RANK_ARENA_LARGE, malloc_mutex_rank_exclusive)) {
goto label_error;
}
- /*
- * Delay coalescing for dirty extents despite the disruptive effect on
- * memory layout for best-fit extent allocation, since cached extents
- * are likely to be reused soon after deallocation, and the cost of
- * merging/splitting extents is non-trivial.
- */
- if (extents_init(tsdn, &arena->extents_dirty, extent_state_dirty,
- true)) {
- goto label_error;
- }
- /*
- * Coalesce muzzy extents immediately, because operations on them are in
- * the critical path much less often than for dirty extents.
- */
- if (extents_init(tsdn, &arena->extents_muzzy, extent_state_muzzy,
- false)) {
- goto label_error;
- }
- /*
- * Coalesce retained extents immediately, in part because they will
- * never be evicted (and therefore there's no opportunity for delayed
- * coalescing), but also because operations on retained extents are not
- * in the critical path.
- */
- if (extents_init(tsdn, &arena->extents_retained, extent_state_retained,
- false)) {
- goto label_error;
- }
-
- if (arena_decay_init(&arena->decay_dirty,
- arena_dirty_decay_ms_default_get(), &arena->stats.decay_dirty)) {
- goto label_error;
- }
- if (arena_decay_init(&arena->decay_muzzy,
- arena_muzzy_decay_ms_default_get(), &arena->stats.decay_muzzy)) {
- goto label_error;
- }
-
- arena->extent_grow_next = sz_psz2ind(HUGEPAGE);
- arena->retain_grow_limit = sz_psz2ind(SC_LARGE_MAXCLASS);
- if (malloc_mutex_init(&arena->extent_grow_mtx, "extent_grow",
- WITNESS_RANK_EXTENT_GROW, malloc_mutex_rank_exclusive)) {
- goto label_error;
- }
-
- extent_avail_new(&arena->extent_avail);
- if (malloc_mutex_init(&arena->extent_avail_mtx, "extent_avail",
- WITNESS_RANK_EXTENT_AVAIL, malloc_mutex_rank_exclusive)) {
+ nstime_t cur_time;
+ nstime_init_update(&cur_time);
+ if (pa_shard_init(tsdn, &arena->pa_shard, &arena_pa_central_global,
+ &arena_emap_global, base, ind, &arena->stats.pa_shard_stats,
+ LOCKEDINT_MTX(arena->stats.mtx), &cur_time, oversize_threshold,
+ arena_dirty_decay_ms_default_get(),
+ arena_muzzy_decay_ms_default_get())) {
goto label_error;
}
/* Initialize bins. */
- uintptr_t bin_addr = (uintptr_t)arena + sizeof(arena_t);
atomic_store_u(&arena->binshard_next, 0, ATOMIC_RELEASE);
- for (i = 0; i < SC_NBINS; i++) {
- unsigned nshards = bin_infos[i].n_shards;
- arena->bins[i].bin_shards = (bin_t *)bin_addr;
- bin_addr += nshards * sizeof(bin_t);
- for (unsigned j = 0; j < nshards; j++) {
- bool err = bin_init(&arena->bins[i].bin_shards[j]);
- if (err) {
- goto label_error;
- }
+ for (i = 0; i < nbins_total; i++) {
+ bool err = bin_init(&arena->bins[i]);
+ if (err) {
+ goto label_error;
}
}
- assert(bin_addr == (uintptr_t)arena + arena_size);
arena->base = base;
/* Set arena before creating background threads. */
arena_set(ind, arena);
+ arena->ind = ind;
- nstime_init(&arena->create_time, 0);
- nstime_update(&arena->create_time);
+ nstime_init_update(&arena->create_time);
+
+ /*
+ * We turn on the HPA if set to. There are two exceptions:
+ * - Custom extent hooks (we should only return memory allocated from
+ * them in that case).
+ * - Arena 0 initialization. In this case, we're mid-bootstrapping, and
+ * so arena_hpa_global is not yet initialized.
+ */
+ if (opt_hpa && ehooks_are_default(base_ehooks_get(base)) && ind != 0) {
+ hpa_shard_opts_t hpa_shard_opts = opt_hpa_opts;
+ hpa_shard_opts.deferral_allowed = background_thread_enabled();
+ if (pa_shard_enable_hpa(tsdn, &arena->pa_shard,
+ &hpa_shard_opts, &opt_hpa_sec_opts)) {
+ goto label_error;
+ }
+ }
/* We don't support reentrancy for arena 0 bootstrapping. */
if (ind != 0) {
@@ -2129,10 +1729,12 @@ arena_choose_huge(tsd_t *tsd) {
* expected for huge allocations.
*/
if (arena_dirty_decay_ms_default_get() > 0) {
- arena_dirty_decay_ms_set(tsd_tsdn(tsd), huge_arena, 0);
+ arena_decay_ms_set(tsd_tsdn(tsd), huge_arena,
+ extent_state_dirty, 0);
}
if (arena_muzzy_decay_ms_default_get() > 0) {
- arena_muzzy_decay_ms_set(tsd_tsdn(tsd), huge_arena, 0);
+ arena_decay_ms_set(tsd_tsdn(tsd), huge_arena,
+ extent_state_muzzy, 0);
}
}
@@ -2167,8 +1769,8 @@ arena_is_huge(unsigned arena_ind) {
return (arena_ind == huge_arena_ind);
}
-void
-arena_boot(sc_data_t *sc_data) {
+bool
+arena_boot(sc_data_t *sc_data, base_t *base, bool hpa) {
arena_dirty_decay_ms_default_set(opt_dirty_decay_ms);
arena_muzzy_decay_ms_default_set(opt_muzzy_decay_ms);
for (unsigned i = 0; i < SC_NBINS; i++) {
@@ -2176,12 +1778,20 @@ arena_boot(sc_data_t *sc_data) {
div_init(&arena_binind_div_info[i],
(1U << sc->lg_base) + (sc->ndelta << sc->lg_delta));
}
+
+ uint32_t cur_offset = (uint32_t)offsetof(arena_t, bins);
+ for (szind_t i = 0; i < SC_NBINS; i++) {
+ arena_bin_offsets[i] = cur_offset;
+ nbins_total += bin_infos[i].n_shards;
+ cur_offset += (uint32_t)(bin_infos[i].n_shards * sizeof(bin_t));
+ }
+ return pa_central_init(&arena_pa_central_global, base, hpa,
+ &hpa_hooks_default);
}
void
arena_prefork0(tsdn_t *tsdn, arena_t *arena) {
- malloc_mutex_prefork(tsdn, &arena->decay_dirty.mtx);
- malloc_mutex_prefork(tsdn, &arena->decay_muzzy.mtx);
+ pa_shard_prefork0(tsdn, &arena->pa_shard);
}
void
@@ -2193,59 +1803,50 @@ arena_prefork1(tsdn_t *tsdn, arena_t *arena) {
void
arena_prefork2(tsdn_t *tsdn, arena_t *arena) {
- malloc_mutex_prefork(tsdn, &arena->extent_grow_mtx);
+ pa_shard_prefork2(tsdn, &arena->pa_shard);
}
void
arena_prefork3(tsdn_t *tsdn, arena_t *arena) {
- extents_prefork(tsdn, &arena->extents_dirty);
- extents_prefork(tsdn, &arena->extents_muzzy);
- extents_prefork(tsdn, &arena->extents_retained);
+ pa_shard_prefork3(tsdn, &arena->pa_shard);
}
void
arena_prefork4(tsdn_t *tsdn, arena_t *arena) {
- malloc_mutex_prefork(tsdn, &arena->extent_avail_mtx);
+ pa_shard_prefork4(tsdn, &arena->pa_shard);
}
void
arena_prefork5(tsdn_t *tsdn, arena_t *arena) {
- base_prefork(tsdn, arena->base);
+ pa_shard_prefork5(tsdn, &arena->pa_shard);
}
void
arena_prefork6(tsdn_t *tsdn, arena_t *arena) {
- malloc_mutex_prefork(tsdn, &arena->large_mtx);
+ base_prefork(tsdn, arena->base);
}
void
arena_prefork7(tsdn_t *tsdn, arena_t *arena) {
- for (unsigned i = 0; i < SC_NBINS; i++) {
- for (unsigned j = 0; j < bin_infos[i].n_shards; j++) {
- bin_prefork(tsdn, &arena->bins[i].bin_shards[j]);
- }
+ malloc_mutex_prefork(tsdn, &arena->large_mtx);
+}
+
+void
+arena_prefork8(tsdn_t *tsdn, arena_t *arena) {
+ for (unsigned i = 0; i < nbins_total; i++) {
+ bin_prefork(tsdn, &arena->bins[i]);
}
}
void
arena_postfork_parent(tsdn_t *tsdn, arena_t *arena) {
- unsigned i;
-
- for (i = 0; i < SC_NBINS; i++) {
- for (unsigned j = 0; j < bin_infos[i].n_shards; j++) {
- bin_postfork_parent(tsdn,
- &arena->bins[i].bin_shards[j]);
- }
+ for (unsigned i = 0; i < nbins_total; i++) {
+ bin_postfork_parent(tsdn, &arena->bins[i]);
}
+
malloc_mutex_postfork_parent(tsdn, &arena->large_mtx);
base_postfork_parent(tsdn, arena->base);
- malloc_mutex_postfork_parent(tsdn, &arena->extent_avail_mtx);
- extents_postfork_parent(tsdn, &arena->extents_dirty);
- extents_postfork_parent(tsdn, &arena->extents_muzzy);
- extents_postfork_parent(tsdn, &arena->extents_retained);
- malloc_mutex_postfork_parent(tsdn, &arena->extent_grow_mtx);
- malloc_mutex_postfork_parent(tsdn, &arena->decay_dirty.mtx);
- malloc_mutex_postfork_parent(tsdn, &arena->decay_muzzy.mtx);
+ pa_shard_postfork_parent(tsdn, &arena->pa_shard);
if (config_stats) {
malloc_mutex_postfork_parent(tsdn, &arena->tcache_ql_mtx);
}
@@ -2253,8 +1854,6 @@ arena_postfork_parent(tsdn_t *tsdn, arena_t *arena) {
void
arena_postfork_child(tsdn_t *tsdn, arena_t *arena) {
- unsigned i;
-
atomic_store_u(&arena->nthreads[0], 0, ATOMIC_RELAXED);
atomic_store_u(&arena->nthreads[1], 0, ATOMIC_RELAXED);
if (tsd_arena_get(tsdn_tsd(tsdn)) == arena) {
@@ -2266,32 +1865,26 @@ arena_postfork_child(tsdn_t *tsdn, arena_t *arena) {
if (config_stats) {
ql_new(&arena->tcache_ql);
ql_new(&arena->cache_bin_array_descriptor_ql);
- tcache_t *tcache = tcache_get(tsdn_tsd(tsdn));
- if (tcache != NULL && tcache->arena == arena) {
- ql_elm_new(tcache, link);
- ql_tail_insert(&arena->tcache_ql, tcache, link);
+ tcache_slow_t *tcache_slow = tcache_slow_get(tsdn_tsd(tsdn));
+ if (tcache_slow != NULL && tcache_slow->arena == arena) {
+ tcache_t *tcache = tcache_slow->tcache;
+ ql_elm_new(tcache_slow, link);
+ ql_tail_insert(&arena->tcache_ql, tcache_slow, link);
cache_bin_array_descriptor_init(
- &tcache->cache_bin_array_descriptor,
- tcache->bins_small, tcache->bins_large);
+ &tcache_slow->cache_bin_array_descriptor,
+ tcache->bins);
ql_tail_insert(&arena->cache_bin_array_descriptor_ql,
- &tcache->cache_bin_array_descriptor, link);
+ &tcache_slow->cache_bin_array_descriptor, link);
}
}
- for (i = 0; i < SC_NBINS; i++) {
- for (unsigned j = 0; j < bin_infos[i].n_shards; j++) {
- bin_postfork_child(tsdn, &arena->bins[i].bin_shards[j]);
- }
+ for (unsigned i = 0; i < nbins_total; i++) {
+ bin_postfork_child(tsdn, &arena->bins[i]);
}
+
malloc_mutex_postfork_child(tsdn, &arena->large_mtx);
base_postfork_child(tsdn, arena->base);
- malloc_mutex_postfork_child(tsdn, &arena->extent_avail_mtx);
- extents_postfork_child(tsdn, &arena->extents_dirty);
- extents_postfork_child(tsdn, &arena->extents_muzzy);
- extents_postfork_child(tsdn, &arena->extents_retained);
- malloc_mutex_postfork_child(tsdn, &arena->extent_grow_mtx);
- malloc_mutex_postfork_child(tsdn, &arena->decay_dirty.mtx);
- malloc_mutex_postfork_child(tsdn, &arena->decay_muzzy.mtx);
+ pa_shard_postfork_child(tsdn, &arena->pa_shard);
if (config_stats) {
malloc_mutex_postfork_child(tsdn, &arena->tcache_ql_mtx);
}
diff --git a/contrib/jemalloc/src/background_thread.c b/contrib/jemalloc/src/background_thread.c
index 57b9b256bb9b..3bb8d26cd3d6 100644
--- a/contrib/jemalloc/src/background_thread.c
+++ b/contrib/jemalloc/src/background_thread.c
@@ -1,4 +1,3 @@
-#define JEMALLOC_BACKGROUND_THREAD_C_
#include "jemalloc/internal/jemalloc_preamble.h"
#include "jemalloc/internal/jemalloc_internal_includes.h"
@@ -54,8 +53,9 @@ pthread_create_wrapper(pthread_t *__restrict thread, const pthread_attr_t *attr,
bool background_thread_create(tsd_t *tsd, unsigned arena_ind) NOT_REACHED
bool background_threads_enable(tsd_t *tsd) NOT_REACHED
bool background_threads_disable(tsd_t *tsd) NOT_REACHED
-void background_thread_interval_check(tsdn_t *tsdn, arena_t *arena,
- arena_decay_t *decay, size_t npages_new) NOT_REACHED
+bool background_thread_is_started(background_thread_info_t *info) NOT_REACHED
+void background_thread_wakeup_early(background_thread_info_t *info,
+ nstime_t *remaining_sleep) NOT_REACHED
void background_thread_prefork0(tsdn_t *tsdn) NOT_REACHED
void background_thread_prefork1(tsdn_t *tsdn) NOT_REACHED
void background_thread_postfork_parent(tsdn_t *tsdn) NOT_REACHED
@@ -74,7 +74,7 @@ background_thread_info_init(tsdn_t *tsdn, background_thread_info_t *info) {
info->npages_to_purge_new = 0;
if (config_stats) {
info->tot_n_runs = 0;
- nstime_init(&info->tot_sleep_time, 0);
+ nstime_init_zero(&info->tot_sleep_time);
}
}
@@ -82,136 +82,40 @@ static inline bool
set_current_thread_affinity(int cpu) {
#if defined(JEMALLOC_HAVE_SCHED_SETAFFINITY)
cpu_set_t cpuset;
+#else
+# ifndef __NetBSD__
+ cpuset_t cpuset;
+# else
+ cpuset_t *cpuset;
+# endif
+#endif
+
+#ifndef __NetBSD__
CPU_ZERO(&cpuset);
CPU_SET(cpu, &cpuset);
- int ret = sched_setaffinity(0, sizeof(cpu_set_t), &cpuset);
+#else
+ cpuset = cpuset_create();
+#endif
- return (ret != 0);
+#if defined(JEMALLOC_HAVE_SCHED_SETAFFINITY)
+ return (sched_setaffinity(0, sizeof(cpu_set_t), &cpuset) != 0);
#else
- return false;
+# ifndef __NetBSD__
+ int ret = pthread_setaffinity_np(pthread_self(), sizeof(cpuset_t),
+ &cpuset);
+# else
+ int ret = pthread_setaffinity_np(pthread_self(), cpuset_size(cpuset),
+ cpuset);
+ cpuset_destroy(cpuset);
+# endif
+ return ret != 0;
#endif
}
-/* Threshold for determining when to wake up the background thread. */
-#define BACKGROUND_THREAD_NPAGES_THRESHOLD UINT64_C(1024)
#define BILLION UINT64_C(1000000000)
/* Minimal sleep interval 100 ms. */
#define BACKGROUND_THREAD_MIN_INTERVAL_NS (BILLION / 10)
-static inline size_t
-decay_npurge_after_interval(arena_decay_t *decay, size_t interval) {
- size_t i;
- uint64_t sum = 0;
- for (i = 0; i < interval; i++) {
- sum += decay->backlog[i] * h_steps[i];
- }
- for (; i < SMOOTHSTEP_NSTEPS; i++) {
- sum += decay->backlog[i] * (h_steps[i] - h_steps[i - interval]);
- }
-
- return (size_t)(sum >> SMOOTHSTEP_BFP);
-}
-
-static uint64_t
-arena_decay_compute_purge_interval_impl(tsdn_t *tsdn, arena_decay_t *decay,
- extents_t *extents) {
- if (malloc_mutex_trylock(tsdn, &decay->mtx)) {
- /* Use minimal interval if decay is contended. */
- return BACKGROUND_THREAD_MIN_INTERVAL_NS;
- }
-
- uint64_t interval;
- ssize_t decay_time = atomic_load_zd(&decay->time_ms, ATOMIC_RELAXED);
- if (decay_time <= 0) {
- /* Purging is eagerly done or disabled currently. */
- interval = BACKGROUND_THREAD_INDEFINITE_SLEEP;
- goto label_done;
- }
-
- uint64_t decay_interval_ns = nstime_ns(&decay->interval);
- assert(decay_interval_ns > 0);
- size_t npages = extents_npages_get(extents);
- if (npages == 0) {
- unsigned i;
- for (i = 0; i < SMOOTHSTEP_NSTEPS; i++) {
- if (decay->backlog[i] > 0) {
- break;
- }
- }
- if (i == SMOOTHSTEP_NSTEPS) {
- /* No dirty pages recorded. Sleep indefinitely. */
- interval = BACKGROUND_THREAD_INDEFINITE_SLEEP;
- goto label_done;
- }
- }
- if (npages <= BACKGROUND_THREAD_NPAGES_THRESHOLD) {
- /* Use max interval. */
- interval = decay_interval_ns * SMOOTHSTEP_NSTEPS;
- goto label_done;
- }
-
- size_t lb = BACKGROUND_THREAD_MIN_INTERVAL_NS / decay_interval_ns;
- size_t ub = SMOOTHSTEP_NSTEPS;
- /* Minimal 2 intervals to ensure reaching next epoch deadline. */
- lb = (lb < 2) ? 2 : lb;
- if ((decay_interval_ns * ub <= BACKGROUND_THREAD_MIN_INTERVAL_NS) ||
- (lb + 2 > ub)) {
- interval = BACKGROUND_THREAD_MIN_INTERVAL_NS;
- goto label_done;
- }
-
- assert(lb + 2 <= ub);
- size_t npurge_lb, npurge_ub;
- npurge_lb = decay_npurge_after_interval(decay, lb);
- if (npurge_lb > BACKGROUND_THREAD_NPAGES_THRESHOLD) {
- interval = decay_interval_ns * lb;
- goto label_done;
- }
- npurge_ub = decay_npurge_after_interval(decay, ub);
- if (npurge_ub < BACKGROUND_THREAD_NPAGES_THRESHOLD) {
- interval = decay_interval_ns * ub;
- goto label_done;
- }
-
- unsigned n_search = 0;
- size_t target, npurge;
- while ((npurge_lb + BACKGROUND_THREAD_NPAGES_THRESHOLD < npurge_ub)
- && (lb + 2 < ub)) {
- target = (lb + ub) / 2;
- npurge = decay_npurge_after_interval(decay, target);
- if (npurge > BACKGROUND_THREAD_NPAGES_THRESHOLD) {
- ub = target;
- npurge_ub = npurge;
- } else {
- lb = target;
- npurge_lb = npurge;
- }
- assert(n_search++ < lg_floor(SMOOTHSTEP_NSTEPS) + 1);
- }
- interval = decay_interval_ns * (ub + lb) / 2;
-label_done:
- interval = (interval < BACKGROUND_THREAD_MIN_INTERVAL_NS) ?
- BACKGROUND_THREAD_MIN_INTERVAL_NS : interval;
- malloc_mutex_unlock(tsdn, &decay->mtx);
-
- return interval;
-}
-
-/* Compute purge interval for background threads. */
-static uint64_t
-arena_decay_compute_purge_interval(tsdn_t *tsdn, arena_t *arena) {
- uint64_t i1, i2;
- i1 = arena_decay_compute_purge_interval_impl(tsdn, &arena->decay_dirty,
- &arena->extents_dirty);
- if (i1 == BACKGROUND_THREAD_MIN_INTERVAL_NS) {
- return i1;
- }
- i2 = arena_decay_compute_purge_interval_impl(tsdn, &arena->decay_muzzy,
- &arena->extents_muzzy);
-
- return i1 < i2 ? i1 : i2;
-}
-
static void
background_thread_sleep(tsdn_t *tsdn, background_thread_info_t *info,
uint64_t interval) {
@@ -228,7 +132,8 @@ background_thread_sleep(tsdn_t *tsdn, background_thread_info_t *info,
int ret;
if (interval == BACKGROUND_THREAD_INDEFINITE_SLEEP) {
- assert(background_thread_indefinite_sleep(info));
+ background_thread_wakeup_time_set(tsdn, info,
+ BACKGROUND_THREAD_INDEFINITE_SLEEP);
ret = pthread_cond_wait(&info->cond, &info->mtx.lock);
assert(ret == 0);
} else {
@@ -236,8 +141,7 @@ background_thread_sleep(tsdn_t *tsdn, background_thread_info_t *info,
interval <= BACKGROUND_THREAD_INDEFINITE_SLEEP);
/* We need malloc clock (can be different from tv). */
nstime_t next_wakeup;
- nstime_init(&next_wakeup, 0);
- nstime_update(&next_wakeup);
+ nstime_init_update(&next_wakeup);
nstime_iadd(&next_wakeup, interval);
assert(nstime_ns(&next_wakeup) <
BACKGROUND_THREAD_INDEFINITE_SLEEP);
@@ -254,8 +158,6 @@ background_thread_sleep(tsdn_t *tsdn, background_thread_info_t *info,
assert(!background_thread_indefinite_sleep(info));
ret = pthread_cond_timedwait(&info->cond, &info->mtx.lock, &ts);
assert(ret == ETIMEDOUT || ret == 0);
- background_thread_wakeup_time_set(tsdn, info,
- BACKGROUND_THREAD_INDEFINITE_SLEEP);
}
if (config_stats) {
gettimeofday(&tv, NULL);
@@ -283,28 +185,48 @@ background_thread_pause_check(tsdn_t *tsdn, background_thread_info_t *info) {
}
static inline void
-background_work_sleep_once(tsdn_t *tsdn, background_thread_info_t *info, unsigned ind) {
- uint64_t min_interval = BACKGROUND_THREAD_INDEFINITE_SLEEP;
+background_work_sleep_once(tsdn_t *tsdn, background_thread_info_t *info,
+ unsigned ind) {
+ uint64_t ns_until_deferred = BACKGROUND_THREAD_DEFERRED_MAX;
unsigned narenas = narenas_total_get();
+ bool slept_indefinitely = background_thread_indefinite_sleep(info);
for (unsigned i = ind; i < narenas; i += max_background_threads) {
arena_t *arena = arena_get(tsdn, i, false);
if (!arena) {
continue;
}
- arena_decay(tsdn, arena, true, false);
- if (min_interval == BACKGROUND_THREAD_MIN_INTERVAL_NS) {
+ /*
+ * If thread was woken up from the indefinite sleep, don't
+ * do the work instantly, but rather check when the deferred
+ * work that caused this thread to wake up is scheduled for.
+ */
+ if (!slept_indefinitely) {
+ arena_do_deferred_work(tsdn, arena);
+ }
+ if (ns_until_deferred <= BACKGROUND_THREAD_MIN_INTERVAL_NS) {
/* Min interval will be used. */
continue;
}
- uint64_t interval = arena_decay_compute_purge_interval(tsdn,
- arena);
- assert(interval >= BACKGROUND_THREAD_MIN_INTERVAL_NS);
- if (min_interval > interval) {
- min_interval = interval;
+ uint64_t ns_arena_deferred = pa_shard_time_until_deferred_work(
+ tsdn, &arena->pa_shard);
+ if (ns_arena_deferred < ns_until_deferred) {
+ ns_until_deferred = ns_arena_deferred;
}
}
- background_thread_sleep(tsdn, info, min_interval);
+
+ uint64_t sleep_ns;
+ if (ns_until_deferred == BACKGROUND_THREAD_DEFERRED_MAX) {
+ sleep_ns = BACKGROUND_THREAD_INDEFINITE_SLEEP;
+ } else {
+ sleep_ns =
+ (ns_until_deferred < BACKGROUND_THREAD_MIN_INTERVAL_NS)
+ ? BACKGROUND_THREAD_MIN_INTERVAL_NS
+ : ns_until_deferred;
+
+ }
+
+ background_thread_sleep(tsdn, info, sleep_ns);
}
static bool
@@ -508,7 +430,7 @@ background_thread_entry(void *ind_arg) {
assert(thread_ind < max_background_threads);
#ifdef JEMALLOC_HAVE_PTHREAD_SETNAME_NP
pthread_setname_np(pthread_self(), "jemalloc_bg_thd");
-#elif defined(__FreeBSD__)
+#elif defined(__FreeBSD__) || defined(__DragonFly__)
pthread_set_name_np(pthread_self(), "jemalloc_bg_thd");
#endif
if (opt_percpu_arena != percpu_arena_disabled) {
@@ -608,16 +530,16 @@ background_threads_enable(tsd_t *tsd) {
malloc_mutex_assert_owner(tsd_tsdn(tsd), &background_thread_lock);
VARIABLE_ARRAY(bool, marked, max_background_threads);
- unsigned i, nmarked;
- for (i = 0; i < max_background_threads; i++) {
+ unsigned nmarked;
+ for (unsigned i = 0; i < max_background_threads; i++) {
marked[i] = false;
}
nmarked = 0;
/* Thread 0 is required and created at the end. */
marked[0] = true;
/* Mark the threads we need to create for thread 0. */
- unsigned n = narenas_total_get();
- for (i = 1; i < n; i++) {
+ unsigned narenas = narenas_total_get();
+ for (unsigned i = 1; i < narenas; i++) {
if (marked[i % max_background_threads] ||
arena_get(tsd_tsdn(tsd), i, false) == NULL) {
continue;
@@ -634,7 +556,18 @@ background_threads_enable(tsd_t *tsd) {
}
}
- return background_thread_create_locked(tsd, 0);
+ bool err = background_thread_create_locked(tsd, 0);
+ if (err) {
+ return true;
+ }
+ for (unsigned i = 0; i < narenas; i++) {
+ arena_t *arena = arena_get(tsd_tsdn(tsd), i, false);
+ if (arena != NULL) {
+ pa_shard_set_deferral_allowed(tsd_tsdn(tsd),
+ &arena->pa_shard, true);
+ }
+ }
+ return false;
}
bool
@@ -648,92 +581,36 @@ background_threads_disable(tsd_t *tsd) {
return true;
}
assert(n_background_threads == 0);
+ unsigned narenas = narenas_total_get();
+ for (unsigned i = 0; i < narenas; i++) {
+ arena_t *arena = arena_get(tsd_tsdn(tsd), i, false);
+ if (arena != NULL) {
+ pa_shard_set_deferral_allowed(tsd_tsdn(tsd),
+ &arena->pa_shard, false);
+ }
+ }
return false;
}
-/* Check if we need to signal the background thread early. */
+bool
+background_thread_is_started(background_thread_info_t *info) {
+ return info->state == background_thread_started;
+}
+
void
-background_thread_interval_check(tsdn_t *tsdn, arena_t *arena,
- arena_decay_t *decay, size_t npages_new) {
- background_thread_info_t *info = arena_background_thread_info_get(
- arena);
- if (malloc_mutex_trylock(tsdn, &info->mtx)) {
- /*
- * Background thread may hold the mutex for a long period of
- * time. We'd like to avoid the variance on application
- * threads. So keep this non-blocking, and leave the work to a
- * future epoch.
- */
+background_thread_wakeup_early(background_thread_info_t *info,
+ nstime_t *remaining_sleep) {
+ /*
+ * This is an optimization to increase batching. At this point
+ * we know that background thread wakes up soon, so the time to cache
+ * the just freed memory is bounded and low.
+ */
+ if (remaining_sleep != NULL && nstime_ns(remaining_sleep) <
+ BACKGROUND_THREAD_MIN_INTERVAL_NS) {
return;
}
-
- if (info->state != background_thread_started) {
- goto label_done;
- }
- if (malloc_mutex_trylock(tsdn, &decay->mtx)) {
- goto label_done;
- }
-
- ssize_t decay_time = atomic_load_zd(&decay->time_ms, ATOMIC_RELAXED);
- if (decay_time <= 0) {
- /* Purging is eagerly done or disabled currently. */
- goto label_done_unlock2;
- }
- uint64_t decay_interval_ns = nstime_ns(&decay->interval);
- assert(decay_interval_ns > 0);
-
- nstime_t diff;
- nstime_init(&diff, background_thread_wakeup_time_get(info));
- if (nstime_compare(&diff, &decay->epoch) <= 0) {
- goto label_done_unlock2;
- }
- nstime_subtract(&diff, &decay->epoch);
- if (nstime_ns(&diff) < BACKGROUND_THREAD_MIN_INTERVAL_NS) {
- goto label_done_unlock2;
- }
-
- if (npages_new > 0) {
- size_t n_epoch = (size_t)(nstime_ns(&diff) / decay_interval_ns);
- /*
- * Compute how many new pages we would need to purge by the next
- * wakeup, which is used to determine if we should signal the
- * background thread.
- */
- uint64_t npurge_new;
- if (n_epoch >= SMOOTHSTEP_NSTEPS) {
- npurge_new = npages_new;
- } else {
- uint64_t h_steps_max = h_steps[SMOOTHSTEP_NSTEPS - 1];
- assert(h_steps_max >=
- h_steps[SMOOTHSTEP_NSTEPS - 1 - n_epoch]);
- npurge_new = npages_new * (h_steps_max -
- h_steps[SMOOTHSTEP_NSTEPS - 1 - n_epoch]);
- npurge_new >>= SMOOTHSTEP_BFP;
- }
- info->npages_to_purge_new += npurge_new;
- }
-
- bool should_signal;
- if (info->npages_to_purge_new > BACKGROUND_THREAD_NPAGES_THRESHOLD) {
- should_signal = true;
- } else if (unlikely(background_thread_indefinite_sleep(info)) &&
- (extents_npages_get(&arena->extents_dirty) > 0 ||
- extents_npages_get(&arena->extents_muzzy) > 0 ||
- info->npages_to_purge_new > 0)) {
- should_signal = true;
- } else {
- should_signal = false;
- }
-
- if (should_signal) {
- info->npages_to_purge_new = 0;
- pthread_cond_signal(&info->cond);
- }
-label_done_unlock2:
- malloc_mutex_unlock(tsdn, &decay->mtx);
-label_done:
- malloc_mutex_unlock(tsdn, &info->mtx);
+ pthread_cond_signal(&info->cond);
}
void
@@ -794,9 +671,11 @@ background_thread_stats_read(tsdn_t *tsdn, background_thread_stats_t *stats) {
return true;
}
- stats->num_threads = n_background_threads;
+ nstime_init_zero(&stats->run_interval);
+ memset(&stats->max_counter_per_bg_thd, 0, sizeof(mutex_prof_data_t));
+
uint64_t num_runs = 0;
- nstime_init(&stats->run_interval, 0);
+ stats->num_threads = n_background_threads;
for (unsigned i = 0; i < max_background_threads; i++) {
background_thread_info_t *info = &background_thread_info[i];
if (malloc_mutex_trylock(tsdn, &info->mtx)) {
@@ -809,6 +688,8 @@ background_thread_stats_read(tsdn_t *tsdn, background_thread_stats_t *stats) {
if (info->state != background_thread_stopped) {
num_runs += info->tot_n_runs;
nstime_add(&stats->run_interval, &info->tot_sleep_time);
+ malloc_mutex_prof_max_update(tsdn,
+ &stats->max_counter_per_bg_thd, &info->mtx);
}
malloc_mutex_unlock(tsdn, &info->mtx);
}
@@ -892,7 +773,7 @@ background_thread_boot0(void) {
}
bool
-background_thread_boot1(tsdn_t *tsdn) {
+background_thread_boot1(tsdn_t *tsdn, base_t *base) {
#ifdef JEMALLOC_BACKGROUND_THREAD
assert(have_background_thread);
assert(narenas_total_get() > 0);
@@ -911,7 +792,7 @@ background_thread_boot1(tsdn_t *tsdn) {
}
background_thread_info = (background_thread_info_t *)base_alloc(tsdn,
- b0get(), opt_max_background_threads *
+ base, opt_max_background_threads *
sizeof(background_thread_info_t), CACHELINE);
if (background_thread_info == NULL) {
return true;
diff --git a/contrib/jemalloc/src/base.c b/contrib/jemalloc/src/base.c
index f3c61661a20a..7f4d67564b74 100644
--- a/contrib/jemalloc/src/base.c
+++ b/contrib/jemalloc/src/base.c
@@ -1,4 +1,3 @@
-#define JEMALLOC_BASE_C_
#include "jemalloc/internal/jemalloc_preamble.h"
#include "jemalloc/internal/jemalloc_internal_includes.h"
@@ -7,6 +6,15 @@
#include "jemalloc/internal/mutex.h"
#include "jemalloc/internal/sz.h"
+/*
+ * In auto mode, arenas switch to huge pages for the base allocator on the
+ * second base block. a0 switches to thp on the 5th block (after 20 megabytes
+ * of metadata), since more metadata (e.g. rtree nodes) come from a0's base.
+ */
+
+#define BASE_AUTO_THP_THRESHOLD 2
+#define BASE_AUTO_THP_THRESHOLD_A0 5
+
/******************************************************************************/
/* Data. */
@@ -29,7 +37,7 @@ metadata_thp_madvise(void) {
}
static void *
-base_map(tsdn_t *tsdn, extent_hooks_t *extent_hooks, unsigned ind, size_t size) {
+base_map(tsdn_t *tsdn, ehooks_t *ehooks, unsigned ind, size_t size) {
void *addr;
bool zero = true;
bool commit = true;
@@ -37,22 +45,21 @@ base_map(tsdn_t *tsdn, extent_hooks_t *extent_hooks, unsigned ind, size_t size)
/* Use huge page sizes and alignment regardless of opt_metadata_thp. */
assert(size == HUGEPAGE_CEILING(size));
size_t alignment = HUGEPAGE;
- if (extent_hooks == &extent_hooks_default) {
+ if (ehooks_are_default(ehooks)) {
addr = extent_alloc_mmap(NULL, size, alignment, &zero, &commit);
+ if (have_madvise_huge && addr) {
+ pages_set_thp_state(addr, size);
+ }
} else {
- /* No arena context as we are creating new arenas. */
- tsd_t *tsd = tsdn_null(tsdn) ? tsd_fetch() : tsdn_tsd(tsdn);
- pre_reentrancy(tsd, NULL);
- addr = extent_hooks->alloc(extent_hooks, NULL, size, alignment,
- &zero, &commit, ind);
- post_reentrancy(tsd);
+ addr = ehooks_alloc(tsdn, ehooks, NULL, size, alignment, &zero,
+ &commit);
}
return addr;
}
static void
-base_unmap(tsdn_t *tsdn, extent_hooks_t *extent_hooks, unsigned ind, void *addr,
+base_unmap(tsdn_t *tsdn, ehooks_t *ehooks, unsigned ind, void *addr,
size_t size) {
/*
* Cascade through dalloc, decommit, purge_forced, and purge_lazy,
@@ -64,7 +71,7 @@ base_unmap(tsdn_t *tsdn, extent_hooks_t *extent_hooks, unsigned ind, void *addr,
* may in fact want the end state of all associated virtual memory to be
* in some consistent-but-allocated state.
*/
- if (extent_hooks == &extent_hooks_default) {
+ if (ehooks_are_default(ehooks)) {
if (!extent_dalloc_mmap(addr, size)) {
goto label_done;
}
@@ -80,31 +87,19 @@ base_unmap(tsdn_t *tsdn, extent_hooks_t *extent_hooks, unsigned ind, void *addr,
/* Nothing worked. This should never happen. */
not_reached();
} else {
- tsd_t *tsd = tsdn_null(tsdn) ? tsd_fetch() : tsdn_tsd(tsdn);
- pre_reentrancy(tsd, NULL);
- if (extent_hooks->dalloc != NULL &&
- !extent_hooks->dalloc(extent_hooks, addr, size, true,
- ind)) {
- goto label_post_reentrancy;
+ if (!ehooks_dalloc(tsdn, ehooks, addr, size, true)) {
+ goto label_done;
}
- if (extent_hooks->decommit != NULL &&
- !extent_hooks->decommit(extent_hooks, addr, size, 0, size,
- ind)) {
- goto label_post_reentrancy;
+ if (!ehooks_decommit(tsdn, ehooks, addr, size, 0, size)) {
+ goto label_done;
}
- if (extent_hooks->purge_forced != NULL &&
- !extent_hooks->purge_forced(extent_hooks, addr, size, 0,
- size, ind)) {
- goto label_post_reentrancy;
+ if (!ehooks_purge_forced(tsdn, ehooks, addr, size, 0, size)) {
+ goto label_done;
}
- if (extent_hooks->purge_lazy != NULL &&
- !extent_hooks->purge_lazy(extent_hooks, addr, size, 0, size,
- ind)) {
- goto label_post_reentrancy;
+ if (!ehooks_purge_lazy(tsdn, ehooks, addr, size, 0, size)) {
+ goto label_done;
}
/* Nothing worked. That's the application's problem. */
- label_post_reentrancy:
- post_reentrancy(tsd);
}
label_done:
if (metadata_thp_madvise()) {
@@ -116,14 +111,14 @@ label_done:
}
static void
-base_extent_init(size_t *extent_sn_next, extent_t *extent, void *addr,
+base_edata_init(size_t *extent_sn_next, edata_t *edata, void *addr,
size_t size) {
size_t sn;
sn = *extent_sn_next;
(*extent_sn_next)++;
- extent_binit(extent, addr, size, sn);
+ edata_binit(edata, addr, size, sn);
}
static size_t
@@ -169,7 +164,7 @@ base_auto_thp_switch(tsdn_t *tsdn, base_t *base) {
pages_huge(block, block->size);
if (config_stats) {
base->n_thp += HUGEPAGE_CEILING(block->size -
- extent_bsize_get(&block->extent)) >> LG_HUGEPAGE;
+ edata_bsize_get(&block->edata)) >> LG_HUGEPAGE;
}
block = block->next;
assert(block == NULL || (base_ind_get(base) == 0));
@@ -177,34 +172,34 @@ base_auto_thp_switch(tsdn_t *tsdn, base_t *base) {
}
static void *
-base_extent_bump_alloc_helper(extent_t *extent, size_t *gap_size, size_t size,
+base_extent_bump_alloc_helper(edata_t *edata, size_t *gap_size, size_t size,
size_t alignment) {
void *ret;
assert(alignment == ALIGNMENT_CEILING(alignment, QUANTUM));
assert(size == ALIGNMENT_CEILING(size, alignment));
- *gap_size = ALIGNMENT_CEILING((uintptr_t)extent_addr_get(extent),
- alignment) - (uintptr_t)extent_addr_get(extent);
- ret = (void *)((uintptr_t)extent_addr_get(extent) + *gap_size);
- assert(extent_bsize_get(extent) >= *gap_size + size);
- extent_binit(extent, (void *)((uintptr_t)extent_addr_get(extent) +
- *gap_size + size), extent_bsize_get(extent) - *gap_size - size,
- extent_sn_get(extent));
+ *gap_size = ALIGNMENT_CEILING((uintptr_t)edata_addr_get(edata),
+ alignment) - (uintptr_t)edata_addr_get(edata);
+ ret = (void *)((uintptr_t)edata_addr_get(edata) + *gap_size);
+ assert(edata_bsize_get(edata) >= *gap_size + size);
+ edata_binit(edata, (void *)((uintptr_t)edata_addr_get(edata) +
+ *gap_size + size), edata_bsize_get(edata) - *gap_size - size,
+ edata_sn_get(edata));
return ret;
}
static void
-base_extent_bump_alloc_post(base_t *base, extent_t *extent, size_t gap_size,
+base_extent_bump_alloc_post(base_t *base, edata_t *edata, size_t gap_size,
void *addr, size_t size) {
- if (extent_bsize_get(extent) > 0) {
+ if (edata_bsize_get(edata) > 0) {
/*
* Compute the index for the largest size class that does not
* exceed extent's size.
*/
szind_t index_floor =
- sz_size2index(extent_bsize_get(extent) + 1) - 1;
- extent_heap_insert(&base->avail[index_floor], extent);
+ sz_size2index(edata_bsize_get(edata) + 1) - 1;
+ edata_heap_insert(&base->avail[index_floor], edata);
}
if (config_stats) {
@@ -229,13 +224,13 @@ base_extent_bump_alloc_post(base_t *base, extent_t *extent, size_t gap_size,
}
static void *
-base_extent_bump_alloc(base_t *base, extent_t *extent, size_t size,
+base_extent_bump_alloc(base_t *base, edata_t *edata, size_t size,
size_t alignment) {
void *ret;
size_t gap_size;
- ret = base_extent_bump_alloc_helper(extent, &gap_size, size, alignment);
- base_extent_bump_alloc_post(base, extent, gap_size, ret, size);
+ ret = base_extent_bump_alloc_helper(edata, &gap_size, size, alignment);
+ base_extent_bump_alloc_post(base, edata, gap_size, ret, size);
return ret;
}
@@ -245,8 +240,8 @@ base_extent_bump_alloc(base_t *base, extent_t *extent, size_t size,
* On success a pointer to the initialized base_block_t header is returned.
*/
static base_block_t *
-base_block_alloc(tsdn_t *tsdn, base_t *base, extent_hooks_t *extent_hooks,
- unsigned ind, pszind_t *pind_last, size_t *extent_sn_next, size_t size,
+base_block_alloc(tsdn_t *tsdn, base_t *base, ehooks_t *ehooks, unsigned ind,
+ pszind_t *pind_last, size_t *extent_sn_next, size_t size,
size_t alignment) {
alignment = ALIGNMENT_CEILING(alignment, QUANTUM);
size_t usize = ALIGNMENT_CEILING(size, alignment);
@@ -267,7 +262,7 @@ base_block_alloc(tsdn_t *tsdn, base_t *base, extent_hooks_t *extent_hooks,
size_t next_block_size = HUGEPAGE_CEILING(sz_pind2sz(pind_next));
size_t block_size = (min_block_size > next_block_size) ? min_block_size
: next_block_size;
- base_block_t *block = (base_block_t *)base_map(tsdn, extent_hooks, ind,
+ base_block_t *block = (base_block_t *)base_map(tsdn, ehooks, ind,
block_size);
if (block == NULL) {
return NULL;
@@ -295,7 +290,7 @@ base_block_alloc(tsdn_t *tsdn, base_t *base, extent_hooks_t *extent_hooks,
block->size = block_size;
block->next = NULL;
assert(block_size >= header_size);
- base_extent_init(extent_sn_next, &block->extent,
+ base_edata_init(extent_sn_next, &block->edata,
(void *)((uintptr_t)block + header_size), block_size - header_size);
return block;
}
@@ -304,17 +299,17 @@ base_block_alloc(tsdn_t *tsdn, base_t *base, extent_hooks_t *extent_hooks,
* Allocate an extent that is at least as large as specified size, with
* specified alignment.
*/
-static extent_t *
+static edata_t *
base_extent_alloc(tsdn_t *tsdn, base_t *base, size_t size, size_t alignment) {
malloc_mutex_assert_owner(tsdn, &base->mtx);
- extent_hooks_t *extent_hooks = base_extent_hooks_get(base);
+ ehooks_t *ehooks = base_ehooks_get_for_metadata(base);
/*
* Drop mutex during base_block_alloc(), because an extent hook will be
* called.
*/
malloc_mutex_unlock(tsdn, &base->mtx);
- base_block_t *block = base_block_alloc(tsdn, base, extent_hooks,
+ base_block_t *block = base_block_alloc(tsdn, base, ehooks,
base_ind_get(base), &base->pind_last, &base->extent_sn_next, size,
alignment);
malloc_mutex_lock(tsdn, &base->mtx);
@@ -338,7 +333,7 @@ base_extent_alloc(tsdn_t *tsdn, base_t *base, size_t size, size_t alignment) {
assert(base->resident <= base->mapped);
assert(base->n_thp << LG_HUGEPAGE <= base->mapped);
}
- return &block->extent;
+ return &block->edata;
}
base_t *
@@ -347,10 +342,22 @@ b0get(void) {
}
base_t *
-base_new(tsdn_t *tsdn, unsigned ind, extent_hooks_t *extent_hooks) {
+base_new(tsdn_t *tsdn, unsigned ind, const extent_hooks_t *extent_hooks,
+ bool metadata_use_hooks) {
pszind_t pind_last = 0;
size_t extent_sn_next = 0;
- base_block_t *block = base_block_alloc(tsdn, NULL, extent_hooks, ind,
+
+ /*
+ * The base will contain the ehooks eventually, but it itself is
+ * allocated using them. So we use some stack ehooks to bootstrap its
+ * memory, and then initialize the ehooks within the base_t.
+ */
+ ehooks_t fake_ehooks;
+ ehooks_init(&fake_ehooks, metadata_use_hooks ?
+ (extent_hooks_t *)extent_hooks :
+ (extent_hooks_t *)&ehooks_default_extent_hooks, ind);
+
+ base_block_t *block = base_block_alloc(tsdn, NULL, &fake_ehooks, ind,
&pind_last, &extent_sn_next, sizeof(base_t), QUANTUM);
if (block == NULL) {
return NULL;
@@ -359,13 +366,15 @@ base_new(tsdn_t *tsdn, unsigned ind, extent_hooks_t *extent_hooks) {
size_t gap_size;
size_t base_alignment = CACHELINE;
size_t base_size = ALIGNMENT_CEILING(sizeof(base_t), base_alignment);
- base_t *base = (base_t *)base_extent_bump_alloc_helper(&block->extent,
+ base_t *base = (base_t *)base_extent_bump_alloc_helper(&block->edata,
&gap_size, base_size, base_alignment);
- base->ind = ind;
- atomic_store_p(&base->extent_hooks, extent_hooks, ATOMIC_RELAXED);
+ ehooks_init(&base->ehooks, (extent_hooks_t *)extent_hooks, ind);
+ ehooks_init(&base->ehooks_base, metadata_use_hooks ?
+ (extent_hooks_t *)extent_hooks :
+ (extent_hooks_t *)&ehooks_default_extent_hooks, ind);
if (malloc_mutex_init(&base->mtx, "base", WITNESS_RANK_BASE,
malloc_mutex_rank_exclusive)) {
- base_unmap(tsdn, extent_hooks, ind, block, block->size);
+ base_unmap(tsdn, &fake_ehooks, ind, block, block->size);
return NULL;
}
base->pind_last = pind_last;
@@ -373,7 +382,7 @@ base_new(tsdn_t *tsdn, unsigned ind, extent_hooks_t *extent_hooks) {
base->blocks = block;
base->auto_thp_switched = false;
for (szind_t i = 0; i < SC_NSIZES; i++) {
- extent_heap_new(&base->avail[i]);
+ edata_heap_new(&base->avail[i]);
}
if (config_stats) {
base->allocated = sizeof(base_block_t);
@@ -386,7 +395,7 @@ base_new(tsdn_t *tsdn, unsigned ind, extent_hooks_t *extent_hooks) {
assert(base->resident <= base->mapped);
assert(base->n_thp << LG_HUGEPAGE <= base->mapped);
}
- base_extent_bump_alloc_post(base, &block->extent, gap_size, base,
+ base_extent_bump_alloc_post(base, &block->edata, gap_size, base,
base_size);
return base;
@@ -394,26 +403,31 @@ base_new(tsdn_t *tsdn, unsigned ind, extent_hooks_t *extent_hooks) {
void
base_delete(tsdn_t *tsdn, base_t *base) {
- extent_hooks_t *extent_hooks = base_extent_hooks_get(base);
+ ehooks_t *ehooks = base_ehooks_get_for_metadata(base);
base_block_t *next = base->blocks;
do {
base_block_t *block = next;
next = block->next;
- base_unmap(tsdn, extent_hooks, base_ind_get(base), block,
+ base_unmap(tsdn, ehooks, base_ind_get(base), block,
block->size);
} while (next != NULL);
}
-extent_hooks_t *
-base_extent_hooks_get(base_t *base) {
- return (extent_hooks_t *)atomic_load_p(&base->extent_hooks,
- ATOMIC_ACQUIRE);
+ehooks_t *
+base_ehooks_get(base_t *base) {
+ return &base->ehooks;
+}
+
+ehooks_t *
+base_ehooks_get_for_metadata(base_t *base) {
+ return &base->ehooks_base;
}
extent_hooks_t *
base_extent_hooks_set(base_t *base, extent_hooks_t *extent_hooks) {
- extent_hooks_t *old_extent_hooks = base_extent_hooks_get(base);
- atomic_store_p(&base->extent_hooks, extent_hooks, ATOMIC_RELEASE);
+ extent_hooks_t *old_extent_hooks =
+ ehooks_get_extent_hooks_ptr(&base->ehooks);
+ ehooks_init(&base->ehooks, extent_hooks, ehooks_ind_get(&base->ehooks));
return old_extent_hooks;
}
@@ -424,28 +438,28 @@ base_alloc_impl(tsdn_t *tsdn, base_t *base, size_t size, size_t alignment,
size_t usize = ALIGNMENT_CEILING(size, alignment);
size_t asize = usize + alignment - QUANTUM;
- extent_t *extent = NULL;
+ edata_t *edata = NULL;
malloc_mutex_lock(tsdn, &base->mtx);
for (szind_t i = sz_size2index(asize); i < SC_NSIZES; i++) {
- extent = extent_heap_remove_first(&base->avail[i]);
- if (extent != NULL) {
+ edata = edata_heap_remove_first(&base->avail[i]);
+ if (edata != NULL) {
/* Use existing space. */
break;
}
}
- if (extent == NULL) {
+ if (edata == NULL) {
/* Try to allocate more space. */
- extent = base_extent_alloc(tsdn, base, usize, alignment);
+ edata = base_extent_alloc(tsdn, base, usize, alignment);
}
void *ret;
- if (extent == NULL) {
+ if (edata == NULL) {
ret = NULL;
goto label_return;
}
- ret = base_extent_bump_alloc(base, extent, usize, alignment);
+ ret = base_extent_bump_alloc(base, edata, usize, alignment);
if (esn != NULL) {
- *esn = extent_sn_get(extent);
+ *esn = (size_t)edata_sn_get(edata);
}
label_return:
malloc_mutex_unlock(tsdn, &base->mtx);
@@ -465,16 +479,16 @@ base_alloc(tsdn_t *tsdn, base_t *base, size_t size, size_t alignment) {
return base_alloc_impl(tsdn, base, size, alignment, NULL);
}
-extent_t *
-base_alloc_extent(tsdn_t *tsdn, base_t *base) {
+edata_t *
+base_alloc_edata(tsdn_t *tsdn, base_t *base) {
size_t esn;
- extent_t *extent = base_alloc_impl(tsdn, base, sizeof(extent_t),
- CACHELINE, &esn);
- if (extent == NULL) {
+ edata_t *edata = base_alloc_impl(tsdn, base, sizeof(edata_t),
+ EDATA_ALIGNMENT, &esn);
+ if (edata == NULL) {
return NULL;
}
- extent_esn_set(extent, esn);
- return extent;
+ edata_esn_set(edata, esn);
+ return edata;
}
void
@@ -509,6 +523,7 @@ base_postfork_child(tsdn_t *tsdn, base_t *base) {
bool
base_boot(tsdn_t *tsdn) {
- b0 = base_new(tsdn, 0, (extent_hooks_t *)&extent_hooks_default);
+ b0 = base_new(tsdn, 0, (extent_hooks_t *)&ehooks_default_extent_hooks,
+ /* metadata_use_hooks */ true);
return (b0 == NULL);
}
diff --git a/contrib/jemalloc/src/bin.c b/contrib/jemalloc/src/bin.c
index bca6b12c352b..fa20458705ac 100644
--- a/contrib/jemalloc/src/bin.c
+++ b/contrib/jemalloc/src/bin.c
@@ -6,26 +6,6 @@
#include "jemalloc/internal/sc.h"
#include "jemalloc/internal/witness.h"
-bin_info_t bin_infos[SC_NBINS];
-
-static void
-bin_infos_init(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS],
- bin_info_t bin_infos[SC_NBINS]) {
- for (unsigned i = 0; i < SC_NBINS; i++) {
- bin_info_t *bin_info = &bin_infos[i];
- sc_t *sc = &sc_data->sc[i];
- bin_info->reg_size = ((size_t)1U << sc->lg_base)
- + ((size_t)sc->ndelta << sc->lg_delta);
- bin_info->slab_size = (sc->pgs << LG_PAGE);
- bin_info->nregs =
- (uint32_t)(bin_info->slab_size / bin_info->reg_size);
- bin_info->n_shards = bin_shard_sizes[i];
- bitmap_info_t bitmap_info = BITMAP_INFO_INITIALIZER(
- bin_info->nregs);
- bin_info->bitmap_info = bitmap_info;
- }
-}
-
bool
bin_update_shard_size(unsigned bin_shard_sizes[SC_NBINS], size_t start_size,
size_t end_size, size_t nshards) {
@@ -58,12 +38,6 @@ bin_shard_sizes_boot(unsigned bin_shard_sizes[SC_NBINS]) {
}
}
-void
-bin_boot(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS]) {
- assert(sc_data->initialized);
- bin_infos_init(sc_data, bin_shard_sizes, bin_infos);
-}
-
bool
bin_init(bin_t *bin) {
if (malloc_mutex_init(&bin->lock, "bin", WITNESS_RANK_BIN,
@@ -71,8 +45,8 @@ bin_init(bin_t *bin) {
return true;
}
bin->slabcur = NULL;
- extent_heap_new(&bin->slabs_nonfull);
- extent_list_init(&bin->slabs_full);
+ edata_heap_new(&bin->slabs_nonfull);
+ edata_list_active_init(&bin->slabs_full);
if (config_stats) {
memset(&bin->stats, 0, sizeof(bin_stats_t));
}
diff --git a/contrib/jemalloc/src/bin_info.c b/contrib/jemalloc/src/bin_info.c
new file mode 100644
index 000000000000..8629ef8817df
--- /dev/null
+++ b/contrib/jemalloc/src/bin_info.c
@@ -0,0 +1,30 @@
+#include "jemalloc/internal/jemalloc_preamble.h"
+#include "jemalloc/internal/jemalloc_internal_includes.h"
+
+#include "jemalloc/internal/bin_info.h"
+
+bin_info_t bin_infos[SC_NBINS];
+
+static void
+bin_infos_init(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS],
+ bin_info_t infos[SC_NBINS]) {
+ for (unsigned i = 0; i < SC_NBINS; i++) {
+ bin_info_t *bin_info = &infos[i];
+ sc_t *sc = &sc_data->sc[i];
+ bin_info->reg_size = ((size_t)1U << sc->lg_base)
+ + ((size_t)sc->ndelta << sc->lg_delta);
+ bin_info->slab_size = (sc->pgs << LG_PAGE);
+ bin_info->nregs =
+ (uint32_t)(bin_info->slab_size / bin_info->reg_size);
+ bin_info->n_shards = bin_shard_sizes[i];
+ bitmap_info_t bitmap_info = BITMAP_INFO_INITIALIZER(
+ bin_info->nregs);
+ bin_info->bitmap_info = bitmap_info;
+ }
+}
+
+void
+bin_info_boot(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS]) {
+ assert(sc_data->initialized);
+ bin_infos_init(sc_data, bin_shard_sizes, bin_infos);
+}
diff --git a/contrib/jemalloc/src/bitmap.c b/contrib/jemalloc/src/bitmap.c
index 468b3178ebfa..0ccedc5db5bc 100644
--- a/contrib/jemalloc/src/bitmap.c
+++ b/contrib/jemalloc/src/bitmap.c
@@ -1,4 +1,3 @@
-#define JEMALLOC_BITMAP_C_
#include "jemalloc/internal/jemalloc_preamble.h"
#include "jemalloc/internal/jemalloc_internal_includes.h"
diff --git a/contrib/jemalloc/src/buf_writer.c b/contrib/jemalloc/src/buf_writer.c
new file mode 100644
index 000000000000..7c6f79403c00
--- /dev/null
+++ b/contrib/jemalloc/src/buf_writer.c
@@ -0,0 +1,144 @@
+#include "jemalloc/internal/jemalloc_preamble.h"
+#include "jemalloc/internal/jemalloc_internal_includes.h"
+
+#include "jemalloc/internal/buf_writer.h"
+#include "jemalloc/internal/malloc_io.h"
+
+static void *
+buf_writer_allocate_internal_buf(tsdn_t *tsdn, size_t buf_len) {
+#ifdef JEMALLOC_JET
+ if (buf_len > SC_LARGE_MAXCLASS) {
+ return NULL;
+ }
+#else
+ assert(buf_len <= SC_LARGE_MAXCLASS);
+#endif
+ return iallocztm(tsdn, buf_len, sz_size2index(buf_len), false, NULL,
+ true, arena_get(tsdn, 0, false), true);
+}
+
+static void
+buf_writer_free_internal_buf(tsdn_t *tsdn, void *buf) {
+ if (buf != NULL) {
+ idalloctm(tsdn, buf, NULL, NULL, true, true);
+ }
+}
+
+static void
+buf_writer_assert(buf_writer_t *buf_writer) {
+ assert(buf_writer != NULL);
+ assert(buf_writer->write_cb != NULL);
+ if (buf_writer->buf != NULL) {
+ assert(buf_writer->buf_size > 0);
+ } else {
+ assert(buf_writer->buf_size == 0);
+ assert(buf_writer->internal_buf);
+ }
+ assert(buf_writer->buf_end <= buf_writer->buf_size);
+}
+
+bool
+buf_writer_init(tsdn_t *tsdn, buf_writer_t *buf_writer, write_cb_t *write_cb,
+ void *cbopaque, char *buf, size_t buf_len) {
+ if (write_cb != NULL) {
+ buf_writer->write_cb = write_cb;
+ } else {
+ buf_writer->write_cb = je_malloc_message != NULL ?
+ je_malloc_message : wrtmessage;
+ }
+ buf_writer->cbopaque = cbopaque;
+ assert(buf_len >= 2);
+ if (buf != NULL) {
+ buf_writer->buf = buf;
+ buf_writer->internal_buf = false;
+ } else {
+ buf_writer->buf = buf_writer_allocate_internal_buf(tsdn,
+ buf_len);
+ buf_writer->internal_buf = true;
+ }
+ if (buf_writer->buf != NULL) {
+ buf_writer->buf_size = buf_len - 1; /* Allowing for '\0'. */
+ } else {
+ buf_writer->buf_size = 0;
+ }
+ buf_writer->buf_end = 0;
+ buf_writer_assert(buf_writer);
+ return buf_writer->buf == NULL;
+}
+
+void
+buf_writer_flush(buf_writer_t *buf_writer) {
+ buf_writer_assert(buf_writer);
+ if (buf_writer->buf == NULL) {
+ return;
+ }
+ buf_writer->buf[buf_writer->buf_end] = '\0';
+ buf_writer->write_cb(buf_writer->cbopaque, buf_writer->buf);
+ buf_writer->buf_end = 0;
+ buf_writer_assert(buf_writer);
+}
+
+void
+buf_writer_cb(void *buf_writer_arg, const char *s) {
+ buf_writer_t *buf_writer = (buf_writer_t *)buf_writer_arg;
+ buf_writer_assert(buf_writer);
+ if (buf_writer->buf == NULL) {
+ buf_writer->write_cb(buf_writer->cbopaque, s);
+ return;
+ }
+ size_t i, slen, n;
+ for (i = 0, slen = strlen(s); i < slen; i += n) {
+ if (buf_writer->buf_end == buf_writer->buf_size) {
+ buf_writer_flush(buf_writer);
+ }
+ size_t s_remain = slen - i;
+ size_t buf_remain = buf_writer->buf_size - buf_writer->buf_end;
+ n = s_remain < buf_remain ? s_remain : buf_remain;
+ memcpy(buf_writer->buf + buf_writer->buf_end, s + i, n);
+ buf_writer->buf_end += n;
+ buf_writer_assert(buf_writer);
+ }
+ assert(i == slen);
+}
+
+void
+buf_writer_terminate(tsdn_t *tsdn, buf_writer_t *buf_writer) {
+ buf_writer_assert(buf_writer);
+ buf_writer_flush(buf_writer);
+ if (buf_writer->internal_buf) {
+ buf_writer_free_internal_buf(tsdn, buf_writer->buf);
+ }
+}
+
+void
+buf_writer_pipe(buf_writer_t *buf_writer, read_cb_t *read_cb,
+ void *read_cbopaque) {
+ /*
+ * A tiny local buffer in case the buffered writer failed to allocate
+ * at init.
+ */
+ static char backup_buf[16];
+ static buf_writer_t backup_buf_writer;
+
+ buf_writer_assert(buf_writer);
+ assert(read_cb != NULL);
+ if (buf_writer->buf == NULL) {
+ buf_writer_init(TSDN_NULL, &backup_buf_writer,
+ buf_writer->write_cb, buf_writer->cbopaque, backup_buf,
+ sizeof(backup_buf));
+ buf_writer = &backup_buf_writer;
+ }
+ assert(buf_writer->buf != NULL);
+ ssize_t nread = 0;
+ do {
+ buf_writer->buf_end += nread;
+ buf_writer_assert(buf_writer);
+ if (buf_writer->buf_end == buf_writer->buf_size) {
+ buf_writer_flush(buf_writer);
+ }
+ nread = read_cb(read_cbopaque,
+ buf_writer->buf + buf_writer->buf_end,
+ buf_writer->buf_size - buf_writer->buf_end);
+ } while (nread > 0);
+ buf_writer_flush(buf_writer);
+}
diff --git a/contrib/jemalloc/src/cache_bin.c b/contrib/jemalloc/src/cache_bin.c
new file mode 100644
index 000000000000..9ae072a0ee6e
--- /dev/null
+++ b/contrib/jemalloc/src/cache_bin.c
@@ -0,0 +1,99 @@
+#include "jemalloc/internal/jemalloc_preamble.h"
+#include "jemalloc/internal/jemalloc_internal_includes.h"
+
+#include "jemalloc/internal/bit_util.h"
+#include "jemalloc/internal/cache_bin.h"
+#include "jemalloc/internal/safety_check.h"
+
+void
+cache_bin_info_init(cache_bin_info_t *info,
+ cache_bin_sz_t ncached_max) {
+ assert(ncached_max <= CACHE_BIN_NCACHED_MAX);
+ size_t stack_size = (size_t)ncached_max * sizeof(void *);
+ assert(stack_size < ((size_t)1 << (sizeof(cache_bin_sz_t) * 8)));
+ info->ncached_max = (cache_bin_sz_t)ncached_max;
+}
+
+void
+cache_bin_info_compute_alloc(cache_bin_info_t *infos, szind_t ninfos,
+ size_t *size, size_t *alignment) {
+ /* For the total bin stack region (per tcache), reserve 2 more slots so
+ * that
+ * 1) the empty position can be safely read on the fast path before
+ * checking "is_empty"; and
+ * 2) the cur_ptr can go beyond the empty position by 1 step safely on
+ * the fast path (i.e. no overflow).
+ */
+ *size = sizeof(void *) * 2;
+ for (szind_t i = 0; i < ninfos; i++) {
+ assert(infos[i].ncached_max > 0);
+ *size += infos[i].ncached_max * sizeof(void *);
+ }
+
+ /*
+ * Align to at least PAGE, to minimize the # of TLBs needed by the
+ * smaller sizes; also helps if the larger sizes don't get used at all.
+ */
+ *alignment = PAGE;
+}
+
+void
+cache_bin_preincrement(cache_bin_info_t *infos, szind_t ninfos, void *alloc,
+ size_t *cur_offset) {
+ if (config_debug) {
+ size_t computed_size;
+ size_t computed_alignment;
+
+ /* Pointer should be as aligned as we asked for. */
+ cache_bin_info_compute_alloc(infos, ninfos, &computed_size,
+ &computed_alignment);
+ assert(((uintptr_t)alloc & (computed_alignment - 1)) == 0);
+ }
+
+ *(uintptr_t *)((uintptr_t)alloc + *cur_offset) =
+ cache_bin_preceding_junk;
+ *cur_offset += sizeof(void *);
+}
+
+void
+cache_bin_postincrement(cache_bin_info_t *infos, szind_t ninfos, void *alloc,
+ size_t *cur_offset) {
+ *(uintptr_t *)((uintptr_t)alloc + *cur_offset) =
+ cache_bin_trailing_junk;
+ *cur_offset += sizeof(void *);
+}
+
+void
+cache_bin_init(cache_bin_t *bin, cache_bin_info_t *info, void *alloc,
+ size_t *cur_offset) {
+ /*
+ * The full_position points to the lowest available space. Allocations
+ * will access the slots toward higher addresses (for the benefit of
+ * adjacent prefetch).
+ */
+ void *stack_cur = (void *)((uintptr_t)alloc + *cur_offset);
+ void *full_position = stack_cur;
+ uint16_t bin_stack_size = info->ncached_max * sizeof(void *);
+
+ *cur_offset += bin_stack_size;
+ void *empty_position = (void *)((uintptr_t)alloc + *cur_offset);
+
+ /* Init to the empty position. */
+ bin->stack_head = (void **)empty_position;
+ bin->low_bits_low_water = (uint16_t)(uintptr_t)bin->stack_head;
+ bin->low_bits_full = (uint16_t)(uintptr_t)full_position;
+ bin->low_bits_empty = (uint16_t)(uintptr_t)empty_position;
+ cache_bin_sz_t free_spots = cache_bin_diff(bin,
+ bin->low_bits_full, (uint16_t)(uintptr_t)bin->stack_head,
+ /* racy */ false);
+ assert(free_spots == bin_stack_size);
+ assert(cache_bin_ncached_get_local(bin, info) == 0);
+ assert(cache_bin_empty_position_get(bin) == empty_position);
+
+ assert(bin_stack_size > 0 || empty_position == full_position);
+}
+
+bool
+cache_bin_still_zero_initialized(cache_bin_t *bin) {
+ return bin->stack_head == NULL;
+}
diff --git a/contrib/jemalloc/src/ckh.c b/contrib/jemalloc/src/ckh.c
index 1bf6df5a115e..8db4319c51d6 100644
--- a/contrib/jemalloc/src/ckh.c
+++ b/contrib/jemalloc/src/ckh.c
@@ -34,7 +34,6 @@
* respectively.
*
******************************************************************************/
-#define JEMALLOC_CKH_C_
#include "jemalloc/internal/jemalloc_preamble.h"
#include "jemalloc/internal/ckh.h"
@@ -357,14 +356,14 @@ ckh_shrink(tsd_t *tsd, ckh_t *ckh) {
}
bool
-ckh_new(tsd_t *tsd, ckh_t *ckh, size_t minitems, ckh_hash_t *hash,
+ckh_new(tsd_t *tsd, ckh_t *ckh, size_t minitems, ckh_hash_t *ckh_hash,
ckh_keycomp_t *keycomp) {
bool ret;
size_t mincells, usize;
unsigned lg_mincells;
assert(minitems > 0);
- assert(hash != NULL);
+ assert(ckh_hash != NULL);
assert(keycomp != NULL);
#ifdef CKH_COUNT
@@ -393,7 +392,7 @@ ckh_new(tsd_t *tsd, ckh_t *ckh, size_t minitems, ckh_hash_t *hash,
}
ckh->lg_minbuckets = lg_mincells - LG_CKH_BUCKET_CELLS;
ckh->lg_curbuckets = lg_mincells - LG_CKH_BUCKET_CELLS;
- ckh->hash = hash;
+ ckh->hash = ckh_hash;
ckh->keycomp = keycomp;
usize = sz_sa2u(sizeof(ckhc_t) << lg_mincells, CACHELINE);
diff --git a/contrib/jemalloc/src/counter.c b/contrib/jemalloc/src/counter.c
new file mode 100644
index 000000000000..8f1ae3af45ed
--- /dev/null
+++ b/contrib/jemalloc/src/counter.c
@@ -0,0 +1,30 @@
+#include "jemalloc/internal/jemalloc_preamble.h"
+#include "jemalloc/internal/jemalloc_internal_includes.h"
+
+#include "jemalloc/internal/counter.h"
+
+bool
+counter_accum_init(counter_accum_t *counter, uint64_t interval) {
+ if (LOCKEDINT_MTX_INIT(counter->mtx, "counter_accum",
+ WITNESS_RANK_COUNTER_ACCUM, malloc_mutex_rank_exclusive)) {
+ return true;
+ }
+ locked_init_u64_unsynchronized(&counter->accumbytes, 0);
+ counter->interval = interval;
+ return false;
+}
+
+void
+counter_prefork(tsdn_t *tsdn, counter_accum_t *counter) {
+ LOCKEDINT_MTX_PREFORK(tsdn, counter->mtx);
+}
+
+void
+counter_postfork_parent(tsdn_t *tsdn, counter_accum_t *counter) {
+ LOCKEDINT_MTX_POSTFORK_PARENT(tsdn, counter->mtx);
+}
+
+void
+counter_postfork_child(tsdn_t *tsdn, counter_accum_t *counter) {
+ LOCKEDINT_MTX_POSTFORK_CHILD(tsdn, counter->mtx);
+}
diff --git a/contrib/jemalloc/src/ctl.c b/contrib/jemalloc/src/ctl.c
index 48afaa61f4ee..135271bafae7 100644
--- a/contrib/jemalloc/src/ctl.c
+++ b/contrib/jemalloc/src/ctl.c
@@ -1,4 +1,3 @@
-#define JEMALLOC_CTL_C_
#include "jemalloc/internal/jemalloc_preamble.h"
#include "jemalloc/internal/jemalloc_internal_includes.h"
@@ -6,8 +5,16 @@
#include "jemalloc/internal/ctl.h"
#include "jemalloc/internal/extent_dss.h"
#include "jemalloc/internal/extent_mmap.h"
+#include "jemalloc/internal/inspect.h"
#include "jemalloc/internal/mutex.h"
#include "jemalloc/internal/nstime.h"
+#include "jemalloc/internal/peak_event.h"
+#include "jemalloc/internal/prof_data.h"
+#include "jemalloc/internal/prof_log.h"
+#include "jemalloc/internal/prof_recent.h"
+#include "jemalloc/internal/prof_stats.h"
+#include "jemalloc/internal/prof_sys.h"
+#include "jemalloc/internal/safety_check.h"
#include "jemalloc/internal/sc.h"
#include "jemalloc/internal/util.h"
@@ -60,6 +67,8 @@ CTL_PROTO(background_thread)
CTL_PROTO(max_background_threads)
CTL_PROTO(thread_tcache_enabled)
CTL_PROTO(thread_tcache_flush)
+CTL_PROTO(thread_peak_read)
+CTL_PROTO(thread_peak_reset)
CTL_PROTO(thread_prof_name)
CTL_PROTO(thread_prof_active)
CTL_PROTO(thread_arena)
@@ -67,6 +76,7 @@ CTL_PROTO(thread_allocated)
CTL_PROTO(thread_allocatedp)
CTL_PROTO(thread_deallocated)
CTL_PROTO(thread_deallocatedp)
+CTL_PROTO(thread_idle)
CTL_PROTO(config_cache_oblivious)
CTL_PROTO(config_debug)
CTL_PROTO(config_fill)
@@ -81,7 +91,20 @@ CTL_PROTO(config_utrace)
CTL_PROTO(config_xmalloc)
CTL_PROTO(opt_abort)
CTL_PROTO(opt_abort_conf)
+CTL_PROTO(opt_cache_oblivious)
+CTL_PROTO(opt_trust_madvise)
CTL_PROTO(opt_confirm_conf)
+CTL_PROTO(opt_hpa)
+CTL_PROTO(opt_hpa_slab_max_alloc)
+CTL_PROTO(opt_hpa_hugification_threshold)
+CTL_PROTO(opt_hpa_hugify_delay_ms)
+CTL_PROTO(opt_hpa_min_purge_interval_ms)
+CTL_PROTO(opt_hpa_dirty_mult)
+CTL_PROTO(opt_hpa_sec_nshards)
+CTL_PROTO(opt_hpa_sec_max_alloc)
+CTL_PROTO(opt_hpa_sec_max_bytes)
+CTL_PROTO(opt_hpa_sec_bytes_after_flush)
+CTL_PROTO(opt_hpa_sec_batch_fill_extra)
CTL_PROTO(opt_metadata_thp)
CTL_PROTO(opt_retain)
CTL_PROTO(opt_dss)
@@ -89,19 +112,31 @@ CTL_PROTO(opt_narenas)
CTL_PROTO(opt_percpu_arena)
CTL_PROTO(opt_oversize_threshold)
CTL_PROTO(opt_background_thread)
+CTL_PROTO(opt_mutex_max_spin)
CTL_PROTO(opt_max_background_threads)
CTL_PROTO(opt_dirty_decay_ms)
CTL_PROTO(opt_muzzy_decay_ms)
CTL_PROTO(opt_stats_print)
CTL_PROTO(opt_stats_print_opts)
+CTL_PROTO(opt_stats_interval)
+CTL_PROTO(opt_stats_interval_opts)
CTL_PROTO(opt_junk)
CTL_PROTO(opt_zero)
CTL_PROTO(opt_utrace)
CTL_PROTO(opt_xmalloc)
+CTL_PROTO(opt_experimental_infallible_new)
CTL_PROTO(opt_tcache)
+CTL_PROTO(opt_tcache_max)
+CTL_PROTO(opt_tcache_nslots_small_min)
+CTL_PROTO(opt_tcache_nslots_small_max)
+CTL_PROTO(opt_tcache_nslots_large)
+CTL_PROTO(opt_lg_tcache_nslots_mul)
+CTL_PROTO(opt_tcache_gc_incr_bytes)
+CTL_PROTO(opt_tcache_gc_delay_bytes)
+CTL_PROTO(opt_lg_tcache_flush_small_div)
+CTL_PROTO(opt_lg_tcache_flush_large_div)
CTL_PROTO(opt_thp)
CTL_PROTO(opt_lg_extent_max_active_fit)
-CTL_PROTO(opt_lg_tcache_max)
CTL_PROTO(opt_prof)
CTL_PROTO(opt_prof_prefix)
CTL_PROTO(opt_prof_active)
@@ -111,7 +146,14 @@ CTL_PROTO(opt_lg_prof_interval)
CTL_PROTO(opt_prof_gdump)
CTL_PROTO(opt_prof_final)
CTL_PROTO(opt_prof_leak)
+CTL_PROTO(opt_prof_leak_error)
CTL_PROTO(opt_prof_accum)
+CTL_PROTO(opt_prof_recent_alloc_max)
+CTL_PROTO(opt_prof_stats)
+CTL_PROTO(opt_prof_sys_thread_name)
+CTL_PROTO(opt_prof_time_res)
+CTL_PROTO(opt_lg_san_uaf_align)
+CTL_PROTO(opt_zero_realloc)
CTL_PROTO(tcache_create)
CTL_PROTO(tcache_flush)
CTL_PROTO(tcache_destroy)
@@ -121,6 +163,7 @@ CTL_PROTO(arena_i_purge)
CTL_PROTO(arena_i_reset)
CTL_PROTO(arena_i_destroy)
CTL_PROTO(arena_i_dss)
+CTL_PROTO(arena_i_oversize_threshold)
CTL_PROTO(arena_i_dirty_decay_ms)
CTL_PROTO(arena_i_muzzy_decay_ms)
CTL_PROTO(arena_i_extent_hooks)
@@ -148,11 +191,18 @@ CTL_PROTO(prof_thread_active_init)
CTL_PROTO(prof_active)
CTL_PROTO(prof_dump)
CTL_PROTO(prof_gdump)
+CTL_PROTO(prof_prefix)
CTL_PROTO(prof_reset)
CTL_PROTO(prof_interval)
CTL_PROTO(lg_prof_sample)
CTL_PROTO(prof_log_start)
CTL_PROTO(prof_log_stop)
+CTL_PROTO(prof_stats_bins_i_live)
+CTL_PROTO(prof_stats_bins_i_accum)
+INDEX_PROTO(prof_stats_bins_i)
+CTL_PROTO(prof_stats_lextents_i_live)
+CTL_PROTO(prof_stats_lextents_i_accum)
+INDEX_PROTO(prof_stats_lextents_i)
CTL_PROTO(stats_arenas_i_small_allocated)
CTL_PROTO(stats_arenas_i_small_nmalloc)
CTL_PROTO(stats_arenas_i_small_ndalloc)
@@ -188,6 +238,39 @@ CTL_PROTO(stats_arenas_i_extents_j_dirty_bytes)
CTL_PROTO(stats_arenas_i_extents_j_muzzy_bytes)
CTL_PROTO(stats_arenas_i_extents_j_retained_bytes)
INDEX_PROTO(stats_arenas_i_extents_j)
+CTL_PROTO(stats_arenas_i_hpa_shard_npurge_passes)
+CTL_PROTO(stats_arenas_i_hpa_shard_npurges)
+CTL_PROTO(stats_arenas_i_hpa_shard_nhugifies)
+CTL_PROTO(stats_arenas_i_hpa_shard_ndehugifies)
+
+/* We have a set of stats for full slabs. */
+CTL_PROTO(stats_arenas_i_hpa_shard_full_slabs_npageslabs_nonhuge)
+CTL_PROTO(stats_arenas_i_hpa_shard_full_slabs_npageslabs_huge)
+CTL_PROTO(stats_arenas_i_hpa_shard_full_slabs_nactive_nonhuge)
+CTL_PROTO(stats_arenas_i_hpa_shard_full_slabs_nactive_huge)
+CTL_PROTO(stats_arenas_i_hpa_shard_full_slabs_ndirty_nonhuge)
+CTL_PROTO(stats_arenas_i_hpa_shard_full_slabs_ndirty_huge)
+
+/* A parallel set for the empty slabs. */
+CTL_PROTO(stats_arenas_i_hpa_shard_empty_slabs_npageslabs_nonhuge)
+CTL_PROTO(stats_arenas_i_hpa_shard_empty_slabs_npageslabs_huge)
+CTL_PROTO(stats_arenas_i_hpa_shard_empty_slabs_nactive_nonhuge)
+CTL_PROTO(stats_arenas_i_hpa_shard_empty_slabs_nactive_huge)
+CTL_PROTO(stats_arenas_i_hpa_shard_empty_slabs_ndirty_nonhuge)
+CTL_PROTO(stats_arenas_i_hpa_shard_empty_slabs_ndirty_huge)
+
+/*
+ * And one for the slabs that are neither empty nor full, but indexed by how
+ * full they are.
+ */
+CTL_PROTO(stats_arenas_i_hpa_shard_nonfull_slabs_j_npageslabs_nonhuge)
+CTL_PROTO(stats_arenas_i_hpa_shard_nonfull_slabs_j_npageslabs_huge)
+CTL_PROTO(stats_arenas_i_hpa_shard_nonfull_slabs_j_nactive_nonhuge)
+CTL_PROTO(stats_arenas_i_hpa_shard_nonfull_slabs_j_nactive_huge)
+CTL_PROTO(stats_arenas_i_hpa_shard_nonfull_slabs_j_ndirty_nonhuge)
+CTL_PROTO(stats_arenas_i_hpa_shard_nonfull_slabs_j_ndirty_huge)
+
+INDEX_PROTO(stats_arenas_i_hpa_shard_nonfull_slabs_j)
CTL_PROTO(stats_arenas_i_nthreads)
CTL_PROTO(stats_arenas_i_uptime)
CTL_PROTO(stats_arenas_i_dss)
@@ -209,8 +292,10 @@ CTL_PROTO(stats_arenas_i_base)
CTL_PROTO(stats_arenas_i_internal)
CTL_PROTO(stats_arenas_i_metadata_thp)
CTL_PROTO(stats_arenas_i_tcache_bytes)
+CTL_PROTO(stats_arenas_i_tcache_stashed_bytes)
CTL_PROTO(stats_arenas_i_resident)
CTL_PROTO(stats_arenas_i_abandoned_vm)
+CTL_PROTO(stats_arenas_i_hpa_sec_bytes)
INDEX_PROTO(stats_arenas_i)
CTL_PROTO(stats_allocated)
CTL_PROTO(stats_active)
@@ -222,12 +307,21 @@ CTL_PROTO(stats_metadata_thp)
CTL_PROTO(stats_resident)
CTL_PROTO(stats_mapped)
CTL_PROTO(stats_retained)
+CTL_PROTO(stats_zero_reallocs)
CTL_PROTO(experimental_hooks_install)
CTL_PROTO(experimental_hooks_remove)
+CTL_PROTO(experimental_hooks_prof_backtrace)
+CTL_PROTO(experimental_hooks_prof_dump)
+CTL_PROTO(experimental_hooks_safety_check_abort)
+CTL_PROTO(experimental_thread_activity_callback)
CTL_PROTO(experimental_utilization_query)
CTL_PROTO(experimental_utilization_batch_query)
CTL_PROTO(experimental_arenas_i_pactivep)
INDEX_PROTO(experimental_arenas_i)
+CTL_PROTO(experimental_prof_recent_alloc_max)
+CTL_PROTO(experimental_prof_recent_alloc_dump)
+CTL_PROTO(experimental_batch_alloc)
+CTL_PROTO(experimental_arenas_create_ext)
#define MUTEX_STATS_CTL_PROTO_GEN(n) \
CTL_PROTO(stats_##n##_num_ops) \
@@ -275,6 +369,11 @@ static const ctl_named_node_t thread_tcache_node[] = {
{NAME("flush"), CTL(thread_tcache_flush)}
};
+static const ctl_named_node_t thread_peak_node[] = {
+ {NAME("read"), CTL(thread_peak_read)},
+ {NAME("reset"), CTL(thread_peak_reset)},
+};
+
static const ctl_named_node_t thread_prof_node[] = {
{NAME("name"), CTL(thread_prof_name)},
{NAME("active"), CTL(thread_prof_active)}
@@ -287,7 +386,9 @@ static const ctl_named_node_t thread_node[] = {
{NAME("deallocated"), CTL(thread_deallocated)},
{NAME("deallocatedp"), CTL(thread_deallocatedp)},
{NAME("tcache"), CHILD(named, thread_tcache)},
- {NAME("prof"), CHILD(named, thread_prof)}
+ {NAME("peak"), CHILD(named, thread_peak)},
+ {NAME("prof"), CHILD(named, thread_prof)},
+ {NAME("idle"), CTL(thread_idle)}
};
static const ctl_named_node_t config_node[] = {
@@ -308,27 +409,60 @@ static const ctl_named_node_t config_node[] = {
static const ctl_named_node_t opt_node[] = {
{NAME("abort"), CTL(opt_abort)},
{NAME("abort_conf"), CTL(opt_abort_conf)},
+ {NAME("cache_oblivious"), CTL(opt_cache_oblivious)},
+ {NAME("trust_madvise"), CTL(opt_trust_madvise)},
{NAME("confirm_conf"), CTL(opt_confirm_conf)},
+ {NAME("hpa"), CTL(opt_hpa)},
+ {NAME("hpa_slab_max_alloc"), CTL(opt_hpa_slab_max_alloc)},
+ {NAME("hpa_hugification_threshold"),
+ CTL(opt_hpa_hugification_threshold)},
+ {NAME("hpa_hugify_delay_ms"), CTL(opt_hpa_hugify_delay_ms)},
+ {NAME("hpa_min_purge_interval_ms"), CTL(opt_hpa_min_purge_interval_ms)},
+ {NAME("hpa_dirty_mult"), CTL(opt_hpa_dirty_mult)},
+ {NAME("hpa_sec_nshards"), CTL(opt_hpa_sec_nshards)},
+ {NAME("hpa_sec_max_alloc"), CTL(opt_hpa_sec_max_alloc)},
+ {NAME("hpa_sec_max_bytes"), CTL(opt_hpa_sec_max_bytes)},
+ {NAME("hpa_sec_bytes_after_flush"),
+ CTL(opt_hpa_sec_bytes_after_flush)},
+ {NAME("hpa_sec_batch_fill_extra"),
+ CTL(opt_hpa_sec_batch_fill_extra)},
{NAME("metadata_thp"), CTL(opt_metadata_thp)},
{NAME("retain"), CTL(opt_retain)},
{NAME("dss"), CTL(opt_dss)},
{NAME("narenas"), CTL(opt_narenas)},
{NAME("percpu_arena"), CTL(opt_percpu_arena)},
{NAME("oversize_threshold"), CTL(opt_oversize_threshold)},
+ {NAME("mutex_max_spin"), CTL(opt_mutex_max_spin)},
{NAME("background_thread"), CTL(opt_background_thread)},
{NAME("max_background_threads"), CTL(opt_max_background_threads)},
{NAME("dirty_decay_ms"), CTL(opt_dirty_decay_ms)},
{NAME("muzzy_decay_ms"), CTL(opt_muzzy_decay_ms)},
{NAME("stats_print"), CTL(opt_stats_print)},
{NAME("stats_print_opts"), CTL(opt_stats_print_opts)},
+ {NAME("stats_interval"), CTL(opt_stats_interval)},
+ {NAME("stats_interval_opts"), CTL(opt_stats_interval_opts)},
{NAME("junk"), CTL(opt_junk)},
{NAME("zero"), CTL(opt_zero)},
{NAME("utrace"), CTL(opt_utrace)},
{NAME("xmalloc"), CTL(opt_xmalloc)},
+ {NAME("experimental_infallible_new"),
+ CTL(opt_experimental_infallible_new)},
{NAME("tcache"), CTL(opt_tcache)},
+ {NAME("tcache_max"), CTL(opt_tcache_max)},
+ {NAME("tcache_nslots_small_min"),
+ CTL(opt_tcache_nslots_small_min)},
+ {NAME("tcache_nslots_small_max"),
+ CTL(opt_tcache_nslots_small_max)},
+ {NAME("tcache_nslots_large"), CTL(opt_tcache_nslots_large)},
+ {NAME("lg_tcache_nslots_mul"), CTL(opt_lg_tcache_nslots_mul)},
+ {NAME("tcache_gc_incr_bytes"), CTL(opt_tcache_gc_incr_bytes)},
+ {NAME("tcache_gc_delay_bytes"), CTL(opt_tcache_gc_delay_bytes)},
+ {NAME("lg_tcache_flush_small_div"),
+ CTL(opt_lg_tcache_flush_small_div)},
+ {NAME("lg_tcache_flush_large_div"),
+ CTL(opt_lg_tcache_flush_large_div)},
{NAME("thp"), CTL(opt_thp)},
{NAME("lg_extent_max_active_fit"), CTL(opt_lg_extent_max_active_fit)},
- {NAME("lg_tcache_max"), CTL(opt_lg_tcache_max)},
{NAME("prof"), CTL(opt_prof)},
{NAME("prof_prefix"), CTL(opt_prof_prefix)},
{NAME("prof_active"), CTL(opt_prof_active)},
@@ -338,7 +472,14 @@ static const ctl_named_node_t opt_node[] = {
{NAME("prof_gdump"), CTL(opt_prof_gdump)},
{NAME("prof_final"), CTL(opt_prof_final)},
{NAME("prof_leak"), CTL(opt_prof_leak)},
- {NAME("prof_accum"), CTL(opt_prof_accum)}
+ {NAME("prof_leak_error"), CTL(opt_prof_leak_error)},
+ {NAME("prof_accum"), CTL(opt_prof_accum)},
+ {NAME("prof_recent_alloc_max"), CTL(opt_prof_recent_alloc_max)},
+ {NAME("prof_stats"), CTL(opt_prof_stats)},
+ {NAME("prof_sys_thread_name"), CTL(opt_prof_sys_thread_name)},
+ {NAME("prof_time_resolution"), CTL(opt_prof_time_res)},
+ {NAME("lg_san_uaf_align"), CTL(opt_lg_san_uaf_align)},
+ {NAME("zero_realloc"), CTL(opt_zero_realloc)}
};
static const ctl_named_node_t tcache_node[] = {
@@ -354,6 +495,11 @@ static const ctl_named_node_t arena_i_node[] = {
{NAME("reset"), CTL(arena_i_reset)},
{NAME("destroy"), CTL(arena_i_destroy)},
{NAME("dss"), CTL(arena_i_dss)},
+ /*
+ * Undocumented for now, since we anticipate an arena API in flux after
+ * we cut the last 5-series release.
+ */
+ {NAME("oversize_threshold"), CTL(arena_i_oversize_threshold)},
{NAME("dirty_decay_ms"), CTL(arena_i_dirty_decay_ms)},
{NAME("muzzy_decay_ms"), CTL(arena_i_muzzy_decay_ms)},
{NAME("extent_hooks"), CTL(arena_i_extent_hooks)},
@@ -408,17 +554,51 @@ static const ctl_named_node_t arenas_node[] = {
{NAME("lookup"), CTL(arenas_lookup)}
};
+static const ctl_named_node_t prof_stats_bins_i_node[] = {
+ {NAME("live"), CTL(prof_stats_bins_i_live)},
+ {NAME("accum"), CTL(prof_stats_bins_i_accum)}
+};
+
+static const ctl_named_node_t super_prof_stats_bins_i_node[] = {
+ {NAME(""), CHILD(named, prof_stats_bins_i)}
+};
+
+static const ctl_indexed_node_t prof_stats_bins_node[] = {
+ {INDEX(prof_stats_bins_i)}
+};
+
+static const ctl_named_node_t prof_stats_lextents_i_node[] = {
+ {NAME("live"), CTL(prof_stats_lextents_i_live)},
+ {NAME("accum"), CTL(prof_stats_lextents_i_accum)}
+};
+
+static const ctl_named_node_t super_prof_stats_lextents_i_node[] = {
+ {NAME(""), CHILD(named, prof_stats_lextents_i)}
+};
+
+static const ctl_indexed_node_t prof_stats_lextents_node[] = {
+ {INDEX(prof_stats_lextents_i)}
+};
+
+static const ctl_named_node_t prof_stats_node[] = {
+ {NAME("bins"), CHILD(indexed, prof_stats_bins)},
+ {NAME("lextents"), CHILD(indexed, prof_stats_lextents)},
+};
+
static const ctl_named_node_t prof_node[] = {
{NAME("thread_active_init"), CTL(prof_thread_active_init)},
{NAME("active"), CTL(prof_active)},
{NAME("dump"), CTL(prof_dump)},
{NAME("gdump"), CTL(prof_gdump)},
+ {NAME("prefix"), CTL(prof_prefix)},
{NAME("reset"), CTL(prof_reset)},
{NAME("interval"), CTL(prof_interval)},
{NAME("lg_sample"), CTL(lg_prof_sample)},
{NAME("log_start"), CTL(prof_log_start)},
- {NAME("log_stop"), CTL(prof_log_stop)}
+ {NAME("log_stop"), CTL(prof_log_stop)},
+ {NAME("stats"), CHILD(named, prof_stats)}
};
+
static const ctl_named_node_t stats_arenas_i_small_node[] = {
{NAME("allocated"), CTL(stats_arenas_i_small_allocated)},
{NAME("nmalloc"), CTL(stats_arenas_i_small_nmalloc)},
@@ -521,6 +701,75 @@ MUTEX_PROF_ARENA_MUTEXES
#undef OP
};
+static const ctl_named_node_t stats_arenas_i_hpa_shard_full_slabs_node[] = {
+ {NAME("npageslabs_nonhuge"),
+ CTL(stats_arenas_i_hpa_shard_full_slabs_npageslabs_nonhuge)},
+ {NAME("npageslabs_huge"),
+ CTL(stats_arenas_i_hpa_shard_full_slabs_npageslabs_huge)},
+ {NAME("nactive_nonhuge"),
+ CTL(stats_arenas_i_hpa_shard_full_slabs_nactive_nonhuge)},
+ {NAME("nactive_huge"),
+ CTL(stats_arenas_i_hpa_shard_full_slabs_nactive_huge)},
+ {NAME("ndirty_nonhuge"),
+ CTL(stats_arenas_i_hpa_shard_full_slabs_ndirty_nonhuge)},
+ {NAME("ndirty_huge"),
+ CTL(stats_arenas_i_hpa_shard_full_slabs_ndirty_huge)}
+};
+
+static const ctl_named_node_t stats_arenas_i_hpa_shard_empty_slabs_node[] = {
+ {NAME("npageslabs_nonhuge"),
+ CTL(stats_arenas_i_hpa_shard_empty_slabs_npageslabs_nonhuge)},
+ {NAME("npageslabs_huge"),
+ CTL(stats_arenas_i_hpa_shard_empty_slabs_npageslabs_huge)},
+ {NAME("nactive_nonhuge"),
+ CTL(stats_arenas_i_hpa_shard_empty_slabs_nactive_nonhuge)},
+ {NAME("nactive_huge"),
+ CTL(stats_arenas_i_hpa_shard_empty_slabs_nactive_huge)},
+ {NAME("ndirty_nonhuge"),
+ CTL(stats_arenas_i_hpa_shard_empty_slabs_ndirty_nonhuge)},
+ {NAME("ndirty_huge"),
+ CTL(stats_arenas_i_hpa_shard_empty_slabs_ndirty_huge)}
+};
+
+static const ctl_named_node_t stats_arenas_i_hpa_shard_nonfull_slabs_j_node[] = {
+ {NAME("npageslabs_nonhuge"),
+ CTL(stats_arenas_i_hpa_shard_nonfull_slabs_j_npageslabs_nonhuge)},
+ {NAME("npageslabs_huge"),
+ CTL(stats_arenas_i_hpa_shard_nonfull_slabs_j_npageslabs_huge)},
+ {NAME("nactive_nonhuge"),
+ CTL(stats_arenas_i_hpa_shard_nonfull_slabs_j_nactive_nonhuge)},
+ {NAME("nactive_huge"),
+ CTL(stats_arenas_i_hpa_shard_nonfull_slabs_j_nactive_huge)},
+ {NAME("ndirty_nonhuge"),
+ CTL(stats_arenas_i_hpa_shard_nonfull_slabs_j_ndirty_nonhuge)},
+ {NAME("ndirty_huge"),
+ CTL(stats_arenas_i_hpa_shard_nonfull_slabs_j_ndirty_huge)}
+};
+
+static const ctl_named_node_t super_stats_arenas_i_hpa_shard_nonfull_slabs_j_node[] = {
+ {NAME(""),
+ CHILD(named, stats_arenas_i_hpa_shard_nonfull_slabs_j)}
+};
+
+static const ctl_indexed_node_t stats_arenas_i_hpa_shard_nonfull_slabs_node[] =
+{
+ {INDEX(stats_arenas_i_hpa_shard_nonfull_slabs_j)}
+};
+
+static const ctl_named_node_t stats_arenas_i_hpa_shard_node[] = {
+ {NAME("full_slabs"), CHILD(named,
+ stats_arenas_i_hpa_shard_full_slabs)},
+ {NAME("empty_slabs"), CHILD(named,
+ stats_arenas_i_hpa_shard_empty_slabs)},
+ {NAME("nonfull_slabs"), CHILD(indexed,
+ stats_arenas_i_hpa_shard_nonfull_slabs)},
+
+ {NAME("npurge_passes"), CTL(stats_arenas_i_hpa_shard_npurge_passes)},
+ {NAME("npurges"), CTL(stats_arenas_i_hpa_shard_npurges)},
+ {NAME("nhugifies"), CTL(stats_arenas_i_hpa_shard_nhugifies)},
+ {NAME("ndehugifies"), CTL(stats_arenas_i_hpa_shard_ndehugifies)}
+};
+
static const ctl_named_node_t stats_arenas_i_node[] = {
{NAME("nthreads"), CTL(stats_arenas_i_nthreads)},
{NAME("uptime"), CTL(stats_arenas_i_uptime)},
@@ -543,14 +792,18 @@ static const ctl_named_node_t stats_arenas_i_node[] = {
{NAME("internal"), CTL(stats_arenas_i_internal)},
{NAME("metadata_thp"), CTL(stats_arenas_i_metadata_thp)},
{NAME("tcache_bytes"), CTL(stats_arenas_i_tcache_bytes)},
+ {NAME("tcache_stashed_bytes"),
+ CTL(stats_arenas_i_tcache_stashed_bytes)},
{NAME("resident"), CTL(stats_arenas_i_resident)},
{NAME("abandoned_vm"), CTL(stats_arenas_i_abandoned_vm)},
+ {NAME("hpa_sec_bytes"), CTL(stats_arenas_i_hpa_sec_bytes)},
{NAME("small"), CHILD(named, stats_arenas_i_small)},
{NAME("large"), CHILD(named, stats_arenas_i_large)},
{NAME("bins"), CHILD(indexed, stats_arenas_i_bins)},
{NAME("lextents"), CHILD(indexed, stats_arenas_i_lextents)},
{NAME("extents"), CHILD(indexed, stats_arenas_i_extents)},
- {NAME("mutexes"), CHILD(named, stats_arenas_i_mutexes)}
+ {NAME("mutexes"), CHILD(named, stats_arenas_i_mutexes)},
+ {NAME("hpa_shard"), CHILD(named, stats_arenas_i_hpa_shard)}
};
static const ctl_named_node_t super_stats_arenas_i_node[] = {
{NAME(""), CHILD(named, stats_arenas_i)}
@@ -589,12 +842,21 @@ static const ctl_named_node_t stats_node[] = {
{NAME("background_thread"),
CHILD(named, stats_background_thread)},
{NAME("mutexes"), CHILD(named, stats_mutexes)},
- {NAME("arenas"), CHILD(indexed, stats_arenas)}
+ {NAME("arenas"), CHILD(indexed, stats_arenas)},
+ {NAME("zero_reallocs"), CTL(stats_zero_reallocs)},
};
static const ctl_named_node_t experimental_hooks_node[] = {
{NAME("install"), CTL(experimental_hooks_install)},
- {NAME("remove"), CTL(experimental_hooks_remove)}
+ {NAME("remove"), CTL(experimental_hooks_remove)},
+ {NAME("prof_backtrace"), CTL(experimental_hooks_prof_backtrace)},
+ {NAME("prof_dump"), CTL(experimental_hooks_prof_dump)},
+ {NAME("safety_check_abort"), CTL(experimental_hooks_safety_check_abort)},
+};
+
+static const ctl_named_node_t experimental_thread_node[] = {
+ {NAME("activity_callback"),
+ CTL(experimental_thread_activity_callback)}
};
static const ctl_named_node_t experimental_utilization_node[] = {
@@ -613,10 +875,19 @@ static const ctl_indexed_node_t experimental_arenas_node[] = {
{INDEX(experimental_arenas_i)}
};
+static const ctl_named_node_t experimental_prof_recent_node[] = {
+ {NAME("alloc_max"), CTL(experimental_prof_recent_alloc_max)},
+ {NAME("alloc_dump"), CTL(experimental_prof_recent_alloc_dump)},
+};
+
static const ctl_named_node_t experimental_node[] = {
{NAME("hooks"), CHILD(named, experimental_hooks)},
{NAME("utilization"), CHILD(named, experimental_utilization)},
- {NAME("arenas"), CHILD(indexed, experimental_arenas)}
+ {NAME("arenas"), CHILD(indexed, experimental_arenas)},
+ {NAME("arenas_create_ext"), CTL(experimental_arenas_create_ext)},
+ {NAME("prof_recent"), CHILD(named, experimental_prof_recent)},
+ {NAME("batch_alloc"), CTL(experimental_batch_alloc)},
+ {NAME("thread"), CHILD(named, experimental_thread)}
};
static const ctl_named_node_t root_node[] = {
@@ -650,28 +921,13 @@ static const ctl_named_node_t super_root_node[] = {
* synchronized by the ctl mutex.
*/
static void
-ctl_accum_arena_stats_u64(arena_stats_u64_t *dst, arena_stats_u64_t *src) {
-#ifdef JEMALLOC_ATOMIC_U64
- uint64_t cur_dst = atomic_load_u64(dst, ATOMIC_RELAXED);
- uint64_t cur_src = atomic_load_u64(src, ATOMIC_RELAXED);
- atomic_store_u64(dst, cur_dst + cur_src, ATOMIC_RELAXED);
-#else
- *dst += *src;
-#endif
-}
-
-/* Likewise: with ctl mutex synchronization, reading is simple. */
-static uint64_t
-ctl_arena_stats_read_u64(arena_stats_u64_t *p) {
-#ifdef JEMALLOC_ATOMIC_U64
- return atomic_load_u64(p, ATOMIC_RELAXED);
-#else
- return *p;
-#endif
+ctl_accum_locked_u64(locked_u64_t *dst, locked_u64_t *src) {
+ locked_inc_u64_unsynchronized(dst,
+ locked_read_u64_unsynchronized(src));
}
static void
-accum_atomic_zu(atomic_zu_t *dst, atomic_zu_t *src) {
+ctl_accum_atomic_zu(atomic_zu_t *dst, atomic_zu_t *src) {
size_t cur_dst = atomic_load_zu(dst, ATOMIC_RELAXED);
size_t cur_src = atomic_load_zu(src, ATOMIC_RELAXED);
atomic_store_zu(dst, cur_dst + cur_src, ATOMIC_RELAXED);
@@ -783,11 +1039,15 @@ ctl_arena_clear(ctl_arena_t *ctl_arena) {
ctl_arena->astats->nfills_small = 0;
ctl_arena->astats->nflushes_small = 0;
memset(ctl_arena->astats->bstats, 0, SC_NBINS *
- sizeof(bin_stats_t));
+ sizeof(bin_stats_data_t));
memset(ctl_arena->astats->lstats, 0, (SC_NSIZES - SC_NBINS) *
sizeof(arena_stats_large_t));
memset(ctl_arena->astats->estats, 0, SC_NPSIZES *
- sizeof(arena_stats_extents_t));
+ sizeof(pac_estats_t));
+ memset(&ctl_arena->astats->hpastats, 0,
+ sizeof(hpa_shard_stats_t));
+ memset(&ctl_arena->astats->secstats, 0,
+ sizeof(sec_stats_t));
}
}
@@ -801,22 +1061,19 @@ ctl_arena_stats_amerge(tsdn_t *tsdn, ctl_arena_t *ctl_arena, arena_t *arena) {
&ctl_arena->muzzy_decay_ms, &ctl_arena->pactive,
&ctl_arena->pdirty, &ctl_arena->pmuzzy,
&ctl_arena->astats->astats, ctl_arena->astats->bstats,
- ctl_arena->astats->lstats, ctl_arena->astats->estats);
+ ctl_arena->astats->lstats, ctl_arena->astats->estats,
+ &ctl_arena->astats->hpastats, &ctl_arena->astats->secstats);
for (i = 0; i < SC_NBINS; i++) {
- ctl_arena->astats->allocated_small +=
- ctl_arena->astats->bstats[i].curregs *
+ bin_stats_t *bstats =
+ &ctl_arena->astats->bstats[i].stats_data;
+ ctl_arena->astats->allocated_small += bstats->curregs *
sz_index2size(i);
- ctl_arena->astats->nmalloc_small +=
- ctl_arena->astats->bstats[i].nmalloc;
- ctl_arena->astats->ndalloc_small +=
- ctl_arena->astats->bstats[i].ndalloc;
- ctl_arena->astats->nrequests_small +=
- ctl_arena->astats->bstats[i].nrequests;
- ctl_arena->astats->nfills_small +=
- ctl_arena->astats->bstats[i].nfills;
- ctl_arena->astats->nflushes_small +=
- ctl_arena->astats->bstats[i].nflushes;
+ ctl_arena->astats->nmalloc_small += bstats->nmalloc;
+ ctl_arena->astats->ndalloc_small += bstats->ndalloc;
+ ctl_arena->astats->nrequests_small += bstats->nrequests;
+ ctl_arena->astats->nfills_small += bstats->nfills;
+ ctl_arena->astats->nflushes_small += bstats->nflushes;
}
} else {
arena_basic_stats_merge(tsdn, arena, &ctl_arena->nthreads,
@@ -848,27 +1105,32 @@ ctl_arena_stats_sdmerge(ctl_arena_t *ctl_sdarena, ctl_arena_t *ctl_arena,
ctl_arena_stats_t *astats = ctl_arena->astats;
if (!destroyed) {
- accum_atomic_zu(&sdstats->astats.mapped,
- &astats->astats.mapped);
- accum_atomic_zu(&sdstats->astats.retained,
- &astats->astats.retained);
- accum_atomic_zu(&sdstats->astats.extent_avail,
- &astats->astats.extent_avail);
- }
-
- ctl_accum_arena_stats_u64(&sdstats->astats.decay_dirty.npurge,
- &astats->astats.decay_dirty.npurge);
- ctl_accum_arena_stats_u64(&sdstats->astats.decay_dirty.nmadvise,
- &astats->astats.decay_dirty.nmadvise);
- ctl_accum_arena_stats_u64(&sdstats->astats.decay_dirty.purged,
- &astats->astats.decay_dirty.purged);
-
- ctl_accum_arena_stats_u64(&sdstats->astats.decay_muzzy.npurge,
- &astats->astats.decay_muzzy.npurge);
- ctl_accum_arena_stats_u64(&sdstats->astats.decay_muzzy.nmadvise,
- &astats->astats.decay_muzzy.nmadvise);
- ctl_accum_arena_stats_u64(&sdstats->astats.decay_muzzy.purged,
- &astats->astats.decay_muzzy.purged);
+ sdstats->astats.mapped += astats->astats.mapped;
+ sdstats->astats.pa_shard_stats.pac_stats.retained
+ += astats->astats.pa_shard_stats.pac_stats.retained;
+ sdstats->astats.pa_shard_stats.edata_avail
+ += astats->astats.pa_shard_stats.edata_avail;
+ }
+
+ ctl_accum_locked_u64(
+ &sdstats->astats.pa_shard_stats.pac_stats.decay_dirty.npurge,
+ &astats->astats.pa_shard_stats.pac_stats.decay_dirty.npurge);
+ ctl_accum_locked_u64(
+ &sdstats->astats.pa_shard_stats.pac_stats.decay_dirty.nmadvise,
+ &astats->astats.pa_shard_stats.pac_stats.decay_dirty.nmadvise);
+ ctl_accum_locked_u64(
+ &sdstats->astats.pa_shard_stats.pac_stats.decay_dirty.purged,
+ &astats->astats.pa_shard_stats.pac_stats.decay_dirty.purged);
+
+ ctl_accum_locked_u64(
+ &sdstats->astats.pa_shard_stats.pac_stats.decay_muzzy.npurge,
+ &astats->astats.pa_shard_stats.pac_stats.decay_muzzy.npurge);
+ ctl_accum_locked_u64(
+ &sdstats->astats.pa_shard_stats.pac_stats.decay_muzzy.nmadvise,
+ &astats->astats.pa_shard_stats.pac_stats.decay_muzzy.nmadvise);
+ ctl_accum_locked_u64(
+ &sdstats->astats.pa_shard_stats.pac_stats.decay_muzzy.purged,
+ &astats->astats.pa_shard_stats.pac_stats.decay_muzzy.purged);
#define OP(mtx) malloc_mutex_prof_merge( \
&(sdstats->astats.mutex_prof_data[ \
@@ -878,14 +1140,11 @@ ctl_arena_stats_sdmerge(ctl_arena_t *ctl_sdarena, ctl_arena_t *ctl_arena,
MUTEX_PROF_ARENA_MUTEXES
#undef OP
if (!destroyed) {
- accum_atomic_zu(&sdstats->astats.base,
- &astats->astats.base);
- accum_atomic_zu(&sdstats->astats.internal,
+ sdstats->astats.base += astats->astats.base;
+ sdstats->astats.resident += astats->astats.resident;
+ sdstats->astats.metadata_thp += astats->astats.metadata_thp;
+ ctl_accum_atomic_zu(&sdstats->astats.internal,
&astats->astats.internal);
- accum_atomic_zu(&sdstats->astats.resident,
- &astats->astats.resident);
- accum_atomic_zu(&sdstats->astats.metadata_thp,
- &astats->astats.metadata_thp);
} else {
assert(atomic_load_zu(
&astats->astats.internal, ATOMIC_RELAXED) == 0);
@@ -903,23 +1162,23 @@ MUTEX_PROF_ARENA_MUTEXES
sdstats->nflushes_small += astats->nflushes_small;
if (!destroyed) {
- accum_atomic_zu(&sdstats->astats.allocated_large,
- &astats->astats.allocated_large);
+ sdstats->astats.allocated_large +=
+ astats->astats.allocated_large;
} else {
- assert(atomic_load_zu(&astats->astats.allocated_large,
- ATOMIC_RELAXED) == 0);
+ assert(astats->astats.allocated_large == 0);
}
- ctl_accum_arena_stats_u64(&sdstats->astats.nmalloc_large,
- &astats->astats.nmalloc_large);
- ctl_accum_arena_stats_u64(&sdstats->astats.ndalloc_large,
- &astats->astats.ndalloc_large);
- ctl_accum_arena_stats_u64(&sdstats->astats.nrequests_large,
- &astats->astats.nrequests_large);
- accum_atomic_zu(&sdstats->astats.abandoned_vm,
- &astats->astats.abandoned_vm);
-
- accum_atomic_zu(&sdstats->astats.tcache_bytes,
- &astats->astats.tcache_bytes);
+ sdstats->astats.nmalloc_large += astats->astats.nmalloc_large;
+ sdstats->astats.ndalloc_large += astats->astats.ndalloc_large;
+ sdstats->astats.nrequests_large
+ += astats->astats.nrequests_large;
+ sdstats->astats.nflushes_large += astats->astats.nflushes_large;
+ ctl_accum_atomic_zu(
+ &sdstats->astats.pa_shard_stats.pac_stats.abandoned_vm,
+ &astats->astats.pa_shard_stats.pac_stats.abandoned_vm);
+
+ sdstats->astats.tcache_bytes += astats->astats.tcache_bytes;
+ sdstats->astats.tcache_stashed_bytes +=
+ astats->astats.tcache_stashed_bytes;
if (ctl_arena->arena_ind == 0) {
sdstats->astats.uptime = astats->astats.uptime;
@@ -927,29 +1186,26 @@ MUTEX_PROF_ARENA_MUTEXES
/* Merge bin stats. */
for (i = 0; i < SC_NBINS; i++) {
- sdstats->bstats[i].nmalloc += astats->bstats[i].nmalloc;
- sdstats->bstats[i].ndalloc += astats->bstats[i].ndalloc;
- sdstats->bstats[i].nrequests +=
- astats->bstats[i].nrequests;
+ bin_stats_t *bstats = &astats->bstats[i].stats_data;
+ bin_stats_t *merged = &sdstats->bstats[i].stats_data;
+ merged->nmalloc += bstats->nmalloc;
+ merged->ndalloc += bstats->ndalloc;
+ merged->nrequests += bstats->nrequests;
if (!destroyed) {
- sdstats->bstats[i].curregs +=
- astats->bstats[i].curregs;
+ merged->curregs += bstats->curregs;
} else {
- assert(astats->bstats[i].curregs == 0);
+ assert(bstats->curregs == 0);
}
- sdstats->bstats[i].nfills += astats->bstats[i].nfills;
- sdstats->bstats[i].nflushes +=
- astats->bstats[i].nflushes;
- sdstats->bstats[i].nslabs += astats->bstats[i].nslabs;
- sdstats->bstats[i].reslabs += astats->bstats[i].reslabs;
+ merged->nfills += bstats->nfills;
+ merged->nflushes += bstats->nflushes;
+ merged->nslabs += bstats->nslabs;
+ merged->reslabs += bstats->reslabs;
if (!destroyed) {
- sdstats->bstats[i].curslabs +=
- astats->bstats[i].curslabs;
- sdstats->bstats[i].nonfull_slabs +=
- astats->bstats[i].nonfull_slabs;
+ merged->curslabs += bstats->curslabs;
+ merged->nonfull_slabs += bstats->nonfull_slabs;
} else {
- assert(astats->bstats[i].curslabs == 0);
- assert(astats->bstats[i].nonfull_slabs == 0);
+ assert(bstats->curslabs == 0);
+ assert(bstats->nonfull_slabs == 0);
}
malloc_mutex_prof_merge(&sdstats->bstats[i].mutex_data,
&astats->bstats[i].mutex_data);
@@ -957,11 +1213,11 @@ MUTEX_PROF_ARENA_MUTEXES
/* Merge stats for large allocations. */
for (i = 0; i < SC_NSIZES - SC_NBINS; i++) {
- ctl_accum_arena_stats_u64(&sdstats->lstats[i].nmalloc,
+ ctl_accum_locked_u64(&sdstats->lstats[i].nmalloc,
&astats->lstats[i].nmalloc);
- ctl_accum_arena_stats_u64(&sdstats->lstats[i].ndalloc,
+ ctl_accum_locked_u64(&sdstats->lstats[i].ndalloc,
&astats->lstats[i].ndalloc);
- ctl_accum_arena_stats_u64(&sdstats->lstats[i].nrequests,
+ ctl_accum_locked_u64(&sdstats->lstats[i].nrequests,
&astats->lstats[i].nrequests);
if (!destroyed) {
sdstats->lstats[i].curlextents +=
@@ -973,19 +1229,21 @@ MUTEX_PROF_ARENA_MUTEXES
/* Merge extents stats. */
for (i = 0; i < SC_NPSIZES; i++) {
- accum_atomic_zu(&sdstats->estats[i].ndirty,
- &astats->estats[i].ndirty);
- accum_atomic_zu(&sdstats->estats[i].nmuzzy,
- &astats->estats[i].nmuzzy);
- accum_atomic_zu(&sdstats->estats[i].nretained,
- &astats->estats[i].nretained);
- accum_atomic_zu(&sdstats->estats[i].dirty_bytes,
- &astats->estats[i].dirty_bytes);
- accum_atomic_zu(&sdstats->estats[i].muzzy_bytes,
- &astats->estats[i].muzzy_bytes);
- accum_atomic_zu(&sdstats->estats[i].retained_bytes,
- &astats->estats[i].retained_bytes);
+ sdstats->estats[i].ndirty += astats->estats[i].ndirty;
+ sdstats->estats[i].nmuzzy += astats->estats[i].nmuzzy;
+ sdstats->estats[i].nretained
+ += astats->estats[i].nretained;
+ sdstats->estats[i].dirty_bytes
+ += astats->estats[i].dirty_bytes;
+ sdstats->estats[i].muzzy_bytes
+ += astats->estats[i].muzzy_bytes;
+ sdstats->estats[i].retained_bytes
+ += astats->estats[i].retained_bytes;
}
+
+ /* Merge HPA stats. */
+ hpa_shard_stats_accum(&sdstats->hpastats, &astats->hpastats);
+ sec_stats_accum(&sdstats->secstats, &astats->secstats);
}
}
@@ -1001,7 +1259,7 @@ ctl_arena_refresh(tsdn_t *tsdn, arena_t *arena, ctl_arena_t *ctl_sdarena,
}
static unsigned
-ctl_arena_init(tsd_t *tsd, extent_hooks_t *extent_hooks) {
+ctl_arena_init(tsd_t *tsd, const arena_config_t *config) {
unsigned arena_ind;
ctl_arena_t *ctl_arena;
@@ -1019,7 +1277,7 @@ ctl_arena_init(tsd_t *tsd, extent_hooks_t *extent_hooks) {
}
/* Initialize new arena. */
- if (arena_init(tsd_tsdn(tsd), arena_ind, extent_hooks) == NULL) {
+ if (arena_init(tsd_tsdn(tsd), arena_ind, config) == NULL) {
return UINT_MAX;
}
@@ -1036,8 +1294,11 @@ ctl_background_thread_stats_read(tsdn_t *tsdn) {
if (!have_background_thread ||
background_thread_stats_read(tsdn, stats)) {
memset(stats, 0, sizeof(background_thread_stats_t));
- nstime_init(&stats->run_interval, 0);
+ nstime_init_zero(&stats->run_interval);
}
+ malloc_mutex_prof_copy(
+ &ctl_stats->mutex_prof_data[global_prof_mutex_max_per_bg_thd],
+ &stats->max_counter_per_bg_thd);
}
static void
@@ -1069,21 +1330,17 @@ ctl_refresh(tsdn_t *tsdn) {
if (config_stats) {
ctl_stats->allocated = ctl_sarena->astats->allocated_small +
- atomic_load_zu(&ctl_sarena->astats->astats.allocated_large,
- ATOMIC_RELAXED);
+ ctl_sarena->astats->astats.allocated_large;
ctl_stats->active = (ctl_sarena->pactive << LG_PAGE);
- ctl_stats->metadata = atomic_load_zu(
- &ctl_sarena->astats->astats.base, ATOMIC_RELAXED) +
+ ctl_stats->metadata = ctl_sarena->astats->astats.base +
atomic_load_zu(&ctl_sarena->astats->astats.internal,
ATOMIC_RELAXED);
- ctl_stats->metadata_thp = atomic_load_zu(
- &ctl_sarena->astats->astats.metadata_thp, ATOMIC_RELAXED);
- ctl_stats->resident = atomic_load_zu(
- &ctl_sarena->astats->astats.resident, ATOMIC_RELAXED);
- ctl_stats->mapped = atomic_load_zu(
- &ctl_sarena->astats->astats.mapped, ATOMIC_RELAXED);
- ctl_stats->retained = atomic_load_zu(
- &ctl_sarena->astats->astats.retained, ATOMIC_RELAXED);
+ ctl_stats->resident = ctl_sarena->astats->astats.resident;
+ ctl_stats->metadata_thp =
+ ctl_sarena->astats->astats.metadata_thp;
+ ctl_stats->mapped = ctl_sarena->astats->astats.mapped;
+ ctl_stats->retained = ctl_sarena->astats->astats
+ .pa_shard_stats.pac_stats.retained;
ctl_background_thread_stats_read(tsdn);
@@ -1093,8 +1350,20 @@ ctl_refresh(tsdn_t *tsdn) {
malloc_mutex_unlock(tsdn, &mtx);
if (config_prof && opt_prof) {
- READ_GLOBAL_MUTEX_PROF_DATA(global_prof_mutex_prof,
- bt2gctx_mtx);
+ READ_GLOBAL_MUTEX_PROF_DATA(
+ global_prof_mutex_prof, bt2gctx_mtx);
+ READ_GLOBAL_MUTEX_PROF_DATA(
+ global_prof_mutex_prof_thds_data, tdatas_mtx);
+ READ_GLOBAL_MUTEX_PROF_DATA(
+ global_prof_mutex_prof_dump, prof_dump_mtx);
+ READ_GLOBAL_MUTEX_PROF_DATA(
+ global_prof_mutex_prof_recent_alloc,
+ prof_recent_alloc_mtx);
+ READ_GLOBAL_MUTEX_PROF_DATA(
+ global_prof_mutex_prof_recent_dump,
+ prof_recent_dump_mtx);
+ READ_GLOBAL_MUTEX_PROF_DATA(
+ global_prof_mutex_prof_stats, prof_stats_mtx);
}
if (have_background_thread) {
READ_GLOBAL_MUTEX_PROF_DATA(
@@ -1191,8 +1460,9 @@ label_return:
}
static int
-ctl_lookup(tsdn_t *tsdn, const char *name, ctl_node_t const **nodesp,
- size_t *mibp, size_t *depthp) {
+ctl_lookup(tsdn_t *tsdn, const ctl_named_node_t *starting_node,
+ const char *name, const ctl_named_node_t **ending_nodep, size_t *mibp,
+ size_t *depthp) {
int ret;
const char *elm, *tdot, *dot;
size_t elen, i, j;
@@ -1206,7 +1476,7 @@ ctl_lookup(tsdn_t *tsdn, const char *name, ctl_node_t const **nodesp,
ret = ENOENT;
goto label_return;
}
- node = super_root_node;
+ node = starting_node;
for (i = 0; i < *depthp; i++) {
assert(node);
assert(node->nchildren > 0);
@@ -1220,10 +1490,6 @@ ctl_lookup(tsdn_t *tsdn, const char *name, ctl_node_t const **nodesp,
if (strlen(child->name) == elen &&
strncmp(elm, child->name, elen) == 0) {
node = child;
- if (nodesp != NULL) {
- nodesp[i] =
- (const ctl_node_t *)node;
- }
mibp[i] = j;
break;
}
@@ -1250,13 +1516,11 @@ ctl_lookup(tsdn_t *tsdn, const char *name, ctl_node_t const **nodesp,
goto label_return;
}
- if (nodesp != NULL) {
- nodesp[i] = (const ctl_node_t *)node;
- }
mibp[i] = (size_t)index;
}
- if (node->ctl != NULL) {
+ /* Reached the end? */
+ if (node->ctl != NULL || *dot == '\0') {
/* Terminal node. */
if (*dot != '\0') {
/*
@@ -1272,16 +1536,14 @@ ctl_lookup(tsdn_t *tsdn, const char *name, ctl_node_t const **nodesp,
}
/* Update elm. */
- if (*dot == '\0') {
- /* No more elements. */
- ret = ENOENT;
- goto label_return;
- }
elm = &dot[1];
dot = ((tdot = strchr(elm, '.')) != NULL) ? tdot :
strchr(elm, '\0');
elen = (size_t)((uintptr_t)dot - (uintptr_t)elm);
}
+ if (ending_nodep != NULL) {
+ *ending_nodep = node;
+ }
ret = 0;
label_return:
@@ -1293,7 +1555,6 @@ ctl_byname(tsd_t *tsd, const char *name, void *oldp, size_t *oldlenp,
void *newp, size_t newlen) {
int ret;
size_t depth;
- ctl_node_t const *nodes[CTL_MAX_DEPTH];
size_t mib[CTL_MAX_DEPTH];
const ctl_named_node_t *node;
@@ -1303,12 +1564,12 @@ ctl_byname(tsd_t *tsd, const char *name, void *oldp, size_t *oldlenp,
}
depth = CTL_MAX_DEPTH;
- ret = ctl_lookup(tsd_tsdn(tsd), name, nodes, mib, &depth);
+ ret = ctl_lookup(tsd_tsdn(tsd), super_root_node, name, &node, mib,
+ &depth);
if (ret != 0) {
goto label_return;
}
- node = ctl_named_node(nodes[depth-1]);
if (node != NULL && node->ctl) {
ret = node->ctl(tsd, mib, depth, oldp, oldlenp, newp, newlen);
} else {
@@ -1329,26 +1590,19 @@ ctl_nametomib(tsd_t *tsd, const char *name, size_t *mibp, size_t *miblenp) {
goto label_return;
}
- ret = ctl_lookup(tsd_tsdn(tsd), name, NULL, mibp, miblenp);
+ ret = ctl_lookup(tsd_tsdn(tsd), super_root_node, name, NULL, mibp,
+ miblenp);
label_return:
return(ret);
}
-int
-ctl_bymib(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,
- size_t *oldlenp, void *newp, size_t newlen) {
+static int
+ctl_lookupbymib(tsdn_t *tsdn, const ctl_named_node_t **ending_nodep,
+ const size_t *mib, size_t miblen) {
int ret;
- const ctl_named_node_t *node;
- size_t i;
- if (!ctl_initialized && ctl_init(tsd)) {
- ret = EAGAIN;
- goto label_return;
- }
-
- /* Iterate down the tree. */
- node = super_root_node;
- for (i = 0; i < miblen; i++) {
+ const ctl_named_node_t *node = super_root_node;
+ for (size_t i = 0; i < miblen; i++) {
assert(node);
assert(node->nchildren > 0);
if (ctl_named_node(node->children) != NULL) {
@@ -1363,13 +1617,36 @@ ctl_bymib(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,
/* Indexed element. */
inode = ctl_indexed_node(node->children);
- node = inode->index(tsd_tsdn(tsd), mib, miblen, mib[i]);
+ node = inode->index(tsdn, mib, miblen, mib[i]);
if (node == NULL) {
ret = ENOENT;
goto label_return;
}
}
}
+ assert(ending_nodep != NULL);
+ *ending_nodep = node;
+ ret = 0;
+
+label_return:
+ return(ret);
+}
+
+int
+ctl_bymib(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,
+ size_t *oldlenp, void *newp, size_t newlen) {
+ int ret;
+ const ctl_named_node_t *node;
+
+ if (!ctl_initialized && ctl_init(tsd)) {
+ ret = EAGAIN;
+ goto label_return;
+ }
+
+ ret = ctl_lookupbymib(tsd_tsdn(tsd), &node, mib, miblen);
+ if (ret != 0) {
+ goto label_return;
+ }
/* Call the ctl function. */
if (node && node->ctl) {
@@ -1383,6 +1660,81 @@ label_return:
return(ret);
}
+int
+ctl_mibnametomib(tsd_t *tsd, size_t *mib, size_t miblen, const char *name,
+ size_t *miblenp) {
+ int ret;
+ const ctl_named_node_t *node;
+
+ if (!ctl_initialized && ctl_init(tsd)) {
+ ret = EAGAIN;
+ goto label_return;
+ }
+
+ ret = ctl_lookupbymib(tsd_tsdn(tsd), &node, mib, miblen);
+ if (ret != 0) {
+ goto label_return;
+ }
+ if (node == NULL || node->ctl != NULL) {
+ ret = ENOENT;
+ goto label_return;
+ }
+
+ assert(miblenp != NULL);
+ assert(*miblenp >= miblen);
+ *miblenp -= miblen;
+ ret = ctl_lookup(tsd_tsdn(tsd), node, name, NULL, mib + miblen,
+ miblenp);
+ *miblenp += miblen;
+label_return:
+ return(ret);
+}
+
+int
+ctl_bymibname(tsd_t *tsd, size_t *mib, size_t miblen, const char *name,
+ size_t *miblenp, void *oldp, size_t *oldlenp, void *newp, size_t newlen) {
+ int ret;
+ const ctl_named_node_t *node;
+
+ if (!ctl_initialized && ctl_init(tsd)) {
+ ret = EAGAIN;
+ goto label_return;
+ }
+
+ ret = ctl_lookupbymib(tsd_tsdn(tsd), &node, mib, miblen);
+ if (ret != 0) {
+ goto label_return;
+ }
+ if (node == NULL || node->ctl != NULL) {
+ ret = ENOENT;
+ goto label_return;
+ }
+
+ assert(miblenp != NULL);
+ assert(*miblenp >= miblen);
+ *miblenp -= miblen;
+ /*
+ * The same node supplies the starting node and stores the ending node.
+ */
+ ret = ctl_lookup(tsd_tsdn(tsd), node, name, &node, mib + miblen,
+ miblenp);
+ *miblenp += miblen;
+ if (ret != 0) {
+ goto label_return;
+ }
+
+ if (node != NULL && node->ctl) {
+ ret = node->ctl(tsd, mib, *miblenp, oldp, oldlenp, newp,
+ newlen);
+ } else {
+ /* The name refers to a partial path through the ctl tree. */
+ ret = ENOENT;
+ }
+
+label_return:
+ return(ret);
+}
+
bool
ctl_boot(void) {
if (malloc_mutex_init(&ctl_mtx, "ctl", WITNESS_RANK_CTL,
@@ -1410,6 +1762,11 @@ ctl_postfork_child(tsdn_t *tsdn) {
malloc_mutex_postfork_child(tsdn, &ctl_mtx);
}
+void
+ctl_mtx_assert_held(tsdn_t *tsdn) {
+ malloc_mutex_assert_owner(tsdn, &ctl_mtx);
+}
+
/******************************************************************************/
/* *_ctl() functions. */
@@ -1427,6 +1784,7 @@ ctl_postfork_child(tsdn_t *tsdn) {
} \
} while (0)
+/* Can read or write, but not both. */
#define READ_XOR_WRITE() do { \
if ((oldp != NULL && oldlenp != NULL) && (newp != NULL || \
newlen != 0)) { \
@@ -1435,12 +1793,31 @@ ctl_postfork_child(tsdn_t *tsdn) {
} \
} while (0)
+/* Can neither read nor write. */
+#define NEITHER_READ_NOR_WRITE() do { \
+ if (oldp != NULL || oldlenp != NULL || newp != NULL || \
+ newlen != 0) { \
+ ret = EPERM; \
+ goto label_return; \
+ } \
+} while (0)
+
+/* Verify that the space provided is enough. */
+#define VERIFY_READ(t) do { \
+ if (oldp == NULL || oldlenp == NULL || *oldlenp != sizeof(t)) { \
+ *oldlenp = 0; \
+ ret = EINVAL; \
+ goto label_return; \
+ } \
+} while (0)
+
#define READ(v, t) do { \
if (oldp != NULL && oldlenp != NULL) { \
if (*oldlenp != sizeof(t)) { \
size_t copylen = (sizeof(t) <= *oldlenp) \
? sizeof(t) : *oldlenp; \
memcpy(oldp, (void *)&(v), copylen); \
+ *oldlenp = copylen; \
ret = EINVAL; \
goto label_return; \
} \
@@ -1458,6 +1835,14 @@ ctl_postfork_child(tsdn_t *tsdn) {
} \
} while (0)
+#define ASSURED_WRITE(v, t) do { \
+ if (newp == NULL || newlen != sizeof(t)) { \
+ ret = EINVAL; \
+ goto label_return; \
+ } \
+ (v) = *(t *)newp; \
+} while (0)
+
#define MIB_UNSIGNED(v, i) do { \
if (mib[i] > UINT_MAX) { \
ret = EFAULT; \
@@ -1497,8 +1882,8 @@ label_return: \
#define CTL_RO_CGEN(c, n, v, t) \
static int \
-n##_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, \
- void *oldp, size_t *oldlenp, void *newp, size_t newlen) { \
+n##_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, \
+ void *oldp, size_t *oldlenp, void *newp, size_t newlen) { \
int ret; \
t oldval; \
\
@@ -1540,8 +1925,8 @@ label_return: \
*/
#define CTL_RO_NL_CGEN(c, n, v, t) \
static int \
-n##_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, \
- void *oldp, size_t *oldlenp, void *newp, size_t newlen) { \
+n##_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, \
+ void *oldp, size_t *oldlenp, void *newp, size_t newlen) { \
int ret; \
t oldval; \
\
@@ -1559,8 +1944,8 @@ label_return: \
#define CTL_RO_NL_GEN(n, v, t) \
static int \
-n##_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, \
- void *oldp, size_t *oldlenp, void *newp, size_t newlen) { \
+n##_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, \
+ void *oldp, size_t *oldlenp, void *newp, size_t newlen) { \
int ret; \
t oldval; \
\
@@ -1573,29 +1958,10 @@ label_return: \
return ret; \
}
-#define CTL_TSD_RO_NL_CGEN(c, n, m, t) \
-static int \
-n##_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp, \
- size_t *oldlenp, void *newp, size_t newlen) { \
- int ret; \
- t oldval; \
- \
- if (!(c)) { \
- return ENOENT; \
- } \
- READONLY(); \
- oldval = (m(tsd)); \
- READ(oldval, t); \
- \
- ret = 0; \
-label_return: \
- return ret; \
-}
-
#define CTL_RO_CONFIG_GEN(n, t) \
static int \
-n##_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, \
- void *oldp, size_t *oldlenp, void *newp, size_t newlen) { \
+n##_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, \
+ void *oldp, size_t *oldlenp, void *newp, size_t newlen) { \
int ret; \
t oldval; \
\
@@ -1761,7 +2127,34 @@ CTL_RO_CONFIG_GEN(config_xmalloc, bool)
CTL_RO_NL_GEN(opt_abort, opt_abort, bool)
CTL_RO_NL_GEN(opt_abort_conf, opt_abort_conf, bool)
+CTL_RO_NL_GEN(opt_cache_oblivious, opt_cache_oblivious, bool)
+CTL_RO_NL_GEN(opt_trust_madvise, opt_trust_madvise, bool)
CTL_RO_NL_GEN(opt_confirm_conf, opt_confirm_conf, bool)
+
+/* HPA options. */
+CTL_RO_NL_GEN(opt_hpa, opt_hpa, bool)
+CTL_RO_NL_GEN(opt_hpa_hugification_threshold,
+ opt_hpa_opts.hugification_threshold, size_t)
+CTL_RO_NL_GEN(opt_hpa_hugify_delay_ms, opt_hpa_opts.hugify_delay_ms, uint64_t)
+CTL_RO_NL_GEN(opt_hpa_min_purge_interval_ms, opt_hpa_opts.min_purge_interval_ms,
+ uint64_t)
+
+/*
+ * This will have to change before we publicly document this option; fxp_t and
+ * its representation are internal implementation details.
+ */
+CTL_RO_NL_GEN(opt_hpa_dirty_mult, opt_hpa_opts.dirty_mult, fxp_t)
+CTL_RO_NL_GEN(opt_hpa_slab_max_alloc, opt_hpa_opts.slab_max_alloc, size_t)
+
+/* HPA SEC options */
+CTL_RO_NL_GEN(opt_hpa_sec_nshards, opt_hpa_sec_opts.nshards, size_t)
+CTL_RO_NL_GEN(opt_hpa_sec_max_alloc, opt_hpa_sec_opts.max_alloc, size_t)
+CTL_RO_NL_GEN(opt_hpa_sec_max_bytes, opt_hpa_sec_opts.max_bytes, size_t)
+CTL_RO_NL_GEN(opt_hpa_sec_bytes_after_flush, opt_hpa_sec_opts.bytes_after_flush,
+ size_t)
+CTL_RO_NL_GEN(opt_hpa_sec_batch_fill_extra, opt_hpa_sec_opts.batch_fill_extra,
+ size_t)
+
CTL_RO_NL_GEN(opt_metadata_thp, metadata_thp_mode_names[opt_metadata_thp],
const char *)
CTL_RO_NL_GEN(opt_retain, opt_retain, bool)
@@ -1769,6 +2162,7 @@ CTL_RO_NL_GEN(opt_dss, opt_dss, const char *)
CTL_RO_NL_GEN(opt_narenas, opt_narenas, unsigned)
CTL_RO_NL_GEN(opt_percpu_arena, percpu_arena_mode_names[opt_percpu_arena],
const char *)
+CTL_RO_NL_GEN(opt_mutex_max_spin, opt_mutex_max_spin, int64_t)
CTL_RO_NL_GEN(opt_oversize_threshold, opt_oversize_threshold, size_t)
CTL_RO_NL_GEN(opt_background_thread, opt_background_thread, bool)
CTL_RO_NL_GEN(opt_max_background_threads, opt_max_background_threads, size_t)
@@ -1776,15 +2170,31 @@ CTL_RO_NL_GEN(opt_dirty_decay_ms, opt_dirty_decay_ms, ssize_t)
CTL_RO_NL_GEN(opt_muzzy_decay_ms, opt_muzzy_decay_ms, ssize_t)
CTL_RO_NL_GEN(opt_stats_print, opt_stats_print, bool)
CTL_RO_NL_GEN(opt_stats_print_opts, opt_stats_print_opts, const char *)
+CTL_RO_NL_GEN(opt_stats_interval, opt_stats_interval, int64_t)
+CTL_RO_NL_GEN(opt_stats_interval_opts, opt_stats_interval_opts, const char *)
CTL_RO_NL_CGEN(config_fill, opt_junk, opt_junk, const char *)
CTL_RO_NL_CGEN(config_fill, opt_zero, opt_zero, bool)
CTL_RO_NL_CGEN(config_utrace, opt_utrace, opt_utrace, bool)
CTL_RO_NL_CGEN(config_xmalloc, opt_xmalloc, opt_xmalloc, bool)
+CTL_RO_NL_CGEN(config_enable_cxx, opt_experimental_infallible_new,
+ opt_experimental_infallible_new, bool)
CTL_RO_NL_GEN(opt_tcache, opt_tcache, bool)
+CTL_RO_NL_GEN(opt_tcache_max, opt_tcache_max, size_t)
+CTL_RO_NL_GEN(opt_tcache_nslots_small_min, opt_tcache_nslots_small_min,
+ unsigned)
+CTL_RO_NL_GEN(opt_tcache_nslots_small_max, opt_tcache_nslots_small_max,
+ unsigned)
+CTL_RO_NL_GEN(opt_tcache_nslots_large, opt_tcache_nslots_large, unsigned)
+CTL_RO_NL_GEN(opt_lg_tcache_nslots_mul, opt_lg_tcache_nslots_mul, ssize_t)
+CTL_RO_NL_GEN(opt_tcache_gc_incr_bytes, opt_tcache_gc_incr_bytes, size_t)
+CTL_RO_NL_GEN(opt_tcache_gc_delay_bytes, opt_tcache_gc_delay_bytes, size_t)
+CTL_RO_NL_GEN(opt_lg_tcache_flush_small_div, opt_lg_tcache_flush_small_div,
+ unsigned)
+CTL_RO_NL_GEN(opt_lg_tcache_flush_large_div, opt_lg_tcache_flush_large_div,
+ unsigned)
CTL_RO_NL_GEN(opt_thp, thp_mode_names[opt_thp], const char *)
CTL_RO_NL_GEN(opt_lg_extent_max_active_fit, opt_lg_extent_max_active_fit,
size_t)
-CTL_RO_NL_GEN(opt_lg_tcache_max, opt_lg_tcache_max, ssize_t)
CTL_RO_NL_CGEN(config_prof, opt_prof, opt_prof, bool)
CTL_RO_NL_CGEN(config_prof, opt_prof_prefix, opt_prof_prefix, const char *)
CTL_RO_NL_CGEN(config_prof, opt_prof_active, opt_prof_active, bool)
@@ -1796,6 +2206,18 @@ CTL_RO_NL_CGEN(config_prof, opt_lg_prof_interval, opt_lg_prof_interval, ssize_t)
CTL_RO_NL_CGEN(config_prof, opt_prof_gdump, opt_prof_gdump, bool)
CTL_RO_NL_CGEN(config_prof, opt_prof_final, opt_prof_final, bool)
CTL_RO_NL_CGEN(config_prof, opt_prof_leak, opt_prof_leak, bool)
+CTL_RO_NL_CGEN(config_prof, opt_prof_leak_error, opt_prof_leak_error, bool)
+CTL_RO_NL_CGEN(config_prof, opt_prof_recent_alloc_max,
+ opt_prof_recent_alloc_max, ssize_t)
+CTL_RO_NL_CGEN(config_prof, opt_prof_stats, opt_prof_stats, bool)
+CTL_RO_NL_CGEN(config_prof, opt_prof_sys_thread_name, opt_prof_sys_thread_name,
+ bool)
+CTL_RO_NL_CGEN(config_prof, opt_prof_time_res,
+ prof_time_res_mode_names[opt_prof_time_res], const char *)
+CTL_RO_NL_CGEN(config_uaf_detection, opt_lg_san_uaf_align,
+ opt_lg_san_uaf_align, ssize_t)
+CTL_RO_NL_GEN(opt_zero_realloc,
+ zero_realloc_mode_names[opt_zero_realloc_action], const char *)
/******************************************************************************/
@@ -1843,10 +2265,11 @@ thread_arena_ctl(tsd_t *tsd, const size_t *mib, size_t miblen,
goto label_return;
}
/* Set new arena/tcache associations. */
- arena_migrate(tsd, oldind, newind);
+ arena_migrate(tsd, oldarena, newarena);
if (tcache_available(tsd)) {
tcache_arena_reassociate(tsd_tsdn(tsd),
- tsd_tcachep_get(tsd), newarena);
+ tsd_tcache_slowp_get(tsd), tsd_tcachep_get(tsd),
+ newarena);
}
}
@@ -1855,14 +2278,10 @@ label_return:
return ret;
}
-CTL_TSD_RO_NL_CGEN(config_stats, thread_allocated, tsd_thread_allocated_get,
- uint64_t)
-CTL_TSD_RO_NL_CGEN(config_stats, thread_allocatedp, tsd_thread_allocatedp_get,
- uint64_t *)
-CTL_TSD_RO_NL_CGEN(config_stats, thread_deallocated, tsd_thread_deallocated_get,
- uint64_t)
-CTL_TSD_RO_NL_CGEN(config_stats, thread_deallocatedp,
- tsd_thread_deallocatedp_get, uint64_t *)
+CTL_RO_NL_GEN(thread_allocated, tsd_thread_allocated_get(tsd), uint64_t)
+CTL_RO_NL_GEN(thread_allocatedp, tsd_thread_allocatedp_get(tsd), uint64_t *)
+CTL_RO_NL_GEN(thread_deallocated, tsd_thread_deallocated_get(tsd), uint64_t)
+CTL_RO_NL_GEN(thread_deallocatedp, tsd_thread_deallocatedp_get(tsd), uint64_t *)
static int
thread_tcache_enabled_ctl(tsd_t *tsd, const size_t *mib,
@@ -1897,8 +2316,7 @@ thread_tcache_flush_ctl(tsd_t *tsd, const size_t *mib,
goto label_return;
}
- READONLY();
- WRITEONLY();
+ NEITHER_READ_NOR_WRITE();
tcache_flush(tsd);
@@ -1908,12 +2326,44 @@ label_return:
}
static int
+thread_peak_read_ctl(tsd_t *tsd, const size_t *mib,
+ size_t miblen, void *oldp, size_t *oldlenp, void *newp,
+ size_t newlen) {
+ int ret;
+ if (!config_stats) {
+ return ENOENT;
+ }
+ READONLY();
+ peak_event_update(tsd);
+ uint64_t result = peak_event_max(tsd);
+ READ(result, uint64_t);
+ ret = 0;
+label_return:
+ return ret;
+}
+
+static int
+thread_peak_reset_ctl(tsd_t *tsd, const size_t *mib,
+ size_t miblen, void *oldp, size_t *oldlenp, void *newp,
+ size_t newlen) {
+ int ret;
+ if (!config_stats) {
+ return ENOENT;
+ }
+ NEITHER_READ_NOR_WRITE();
+ peak_event_zero(tsd);
+ ret = 0;
+label_return:
+ return ret;
+}
+
+static int
thread_prof_name_ctl(tsd_t *tsd, const size_t *mib,
size_t miblen, void *oldp, size_t *oldlenp, void *newp,
size_t newlen) {
int ret;
- if (!config_prof) {
+ if (!config_prof || !opt_prof) {
return ENOENT;
}
@@ -1950,8 +2400,12 @@ thread_prof_active_ctl(tsd_t *tsd, const size_t *mib,
return ENOENT;
}
- oldval = prof_thread_active_get(tsd);
+ oldval = opt_prof ? prof_thread_active_get(tsd) : false;
if (newp != NULL) {
+ if (!opt_prof) {
+ ret = ENOENT;
+ goto label_return;
+ }
if (newlen != sizeof(bool)) {
ret = EINVAL;
goto label_return;
@@ -1968,6 +2422,39 @@ label_return:
return ret;
}
+static int
+thread_idle_ctl(tsd_t *tsd, const size_t *mib,
+ size_t miblen, void *oldp, size_t *oldlenp, void *newp,
+ size_t newlen) {
+ int ret;
+
+ NEITHER_READ_NOR_WRITE();
+
+ if (tcache_available(tsd)) {
+ tcache_flush(tsd);
+ }
+ /*
+ * This heuristic is perhaps not the most well-considered. But it
+ * matches the only idling policy we have experience with in the status
+ * quo. Over time we should investigate more principled approaches.
+ */
+ if (opt_narenas > ncpus * 2) {
+ arena_t *arena = arena_choose(tsd, NULL);
+ if (arena != NULL) {
+ arena_decay(tsd_tsdn(tsd), arena, false, true);
+ }
+ /*
+ * The missing arena case is not actually an error; a thread
+ * might be idle before it associates itself to one. This is
+ * unusual, but not wrong.
+ */
+ }
+
+ ret = 0;
+label_return:
+ return ret;
+}
+
/******************************************************************************/
static int
@@ -1977,7 +2464,8 @@ tcache_create_ctl(tsd_t *tsd, const size_t *mib, size_t miblen,
unsigned tcache_ind;
READONLY();
- if (tcaches_create(tsd, &tcache_ind)) {
+ VERIFY_READ(unsigned);
+ if (tcaches_create(tsd, b0get(), &tcache_ind)) {
ret = EFAULT;
goto label_return;
}
@@ -1995,12 +2483,7 @@ tcache_flush_ctl(tsd_t *tsd, const size_t *mib, size_t miblen,
unsigned tcache_ind;
WRITEONLY();
- tcache_ind = UINT_MAX;
- WRITE(tcache_ind, unsigned);
- if (tcache_ind == UINT_MAX) {
- ret = EFAULT;
- goto label_return;
- }
+ ASSURED_WRITE(tcache_ind, unsigned);
tcaches_flush(tsd, tcache_ind);
ret = 0;
@@ -2015,12 +2498,7 @@ tcache_destroy_ctl(tsd_t *tsd, const size_t *mib, size_t miblen,
unsigned tcache_ind;
WRITEONLY();
- tcache_ind = UINT_MAX;
- WRITE(tcache_ind, unsigned);
- if (tcache_ind == UINT_MAX) {
- ret = EFAULT;
- goto label_return;
- }
+ ASSURED_WRITE(tcache_ind, unsigned);
tcaches_destroy(tsd, tcache_ind);
ret = 0;
@@ -2105,8 +2583,7 @@ arena_i_decay_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,
int ret;
unsigned arena_ind;
- READONLY();
- WRITEONLY();
+ NEITHER_READ_NOR_WRITE();
MIB_UNSIGNED(arena_ind, 1);
arena_i_decay(tsd_tsdn(tsd), arena_ind, false);
@@ -2121,8 +2598,7 @@ arena_i_purge_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,
int ret;
unsigned arena_ind;
- READONLY();
- WRITEONLY();
+ NEITHER_READ_NOR_WRITE();
MIB_UNSIGNED(arena_ind, 1);
arena_i_decay(tsd_tsdn(tsd), arena_ind, true);
@@ -2137,8 +2613,7 @@ arena_i_reset_destroy_helper(tsd_t *tsd, const size_t *mib, size_t miblen,
arena_t **arena) {
int ret;
- READONLY();
- WRITEONLY();
+ NEITHER_READ_NOR_WRITE();
MIB_UNSIGNED(*arena_ind, 1);
*arena = arena_get(tsd_tsdn(tsd), *arena_ind, false);
@@ -2211,6 +2686,8 @@ arena_i_destroy_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,
arena_t *arena;
ctl_arena_t *ctl_darena, *ctl_arena;
+ malloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx);
+
ret = arena_i_reset_destroy_helper(tsd, mib, miblen, oldp, oldlenp,
newp, newlen, &arena_ind, &arena);
if (ret != 0) {
@@ -2241,6 +2718,8 @@ arena_i_destroy_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,
assert(ret == 0);
label_return:
+ malloc_mutex_unlock(tsd_tsdn(tsd), &ctl_mtx);
+
return ret;
}
@@ -2306,6 +2785,38 @@ label_return:
}
static int
+arena_i_oversize_threshold_ctl(tsd_t *tsd, const size_t *mib, size_t miblen,
+ void *oldp, size_t *oldlenp, void *newp, size_t newlen) {
+ int ret;
+
+ unsigned arena_ind;
+ MIB_UNSIGNED(arena_ind, 1);
+
+ arena_t *arena = arena_get(tsd_tsdn(tsd), arena_ind, false);
+ if (arena == NULL) {
+ ret = EFAULT;
+ goto label_return;
+ }
+
+ if (oldp != NULL && oldlenp != NULL) {
+ size_t oldval = atomic_load_zu(
+ &arena->pa_shard.pac.oversize_threshold, ATOMIC_RELAXED);
+ READ(oldval, size_t);
+ }
+ if (newp != NULL) {
+ if (newlen != sizeof(size_t)) {
+ ret = EINVAL;
+ goto label_return;
+ }
+ atomic_store_zu(&arena->pa_shard.pac.oversize_threshold,
+ *(size_t *)newp, ATOMIC_RELAXED);
+ }
+ ret = 0;
+label_return:
+ return ret;
+}
+
+static int
arena_i_decay_ms_ctl_impl(tsd_t *tsd, const size_t *mib, size_t miblen,
void *oldp, size_t *oldlenp, void *newp, size_t newlen, bool dirty) {
int ret;
@@ -2318,10 +2829,10 @@ arena_i_decay_ms_ctl_impl(tsd_t *tsd, const size_t *mib, size_t miblen,
ret = EFAULT;
goto label_return;
}
+ extent_state_t state = dirty ? extent_state_dirty : extent_state_muzzy;
if (oldp != NULL && oldlenp != NULL) {
- size_t oldval = dirty ? arena_dirty_decay_ms_get(arena) :
- arena_muzzy_decay_ms_get(arena);
+ size_t oldval = arena_decay_ms_get(arena, state);
READ(oldval, ssize_t);
}
if (newp != NULL) {
@@ -2340,9 +2851,9 @@ arena_i_decay_ms_ctl_impl(tsd_t *tsd, const size_t *mib, size_t miblen,
goto label_return;
}
}
- if (dirty ? arena_dirty_decay_ms_set(tsd_tsdn(tsd), arena,
- *(ssize_t *)newp) : arena_muzzy_decay_ms_set(tsd_tsdn(tsd),
- arena, *(ssize_t *)newp)) {
+
+ if (arena_decay_ms_set(tsd_tsdn(tsd), arena, state,
+ *(ssize_t *)newp)) {
ret = EFAULT;
goto label_return;
}
@@ -2385,15 +2896,18 @@ arena_i_extent_hooks_ctl(tsd_t *tsd, const size_t *mib, size_t miblen,
goto label_return;
}
old_extent_hooks =
- (extent_hooks_t *)&extent_hooks_default;
+ (extent_hooks_t *)&ehooks_default_extent_hooks;
READ(old_extent_hooks, extent_hooks_t *);
if (newp != NULL) {
/* Initialize a new arena as a side effect. */
extent_hooks_t *new_extent_hooks
JEMALLOC_CC_SILENCE_INIT(NULL);
WRITE(new_extent_hooks, extent_hooks_t *);
+ arena_config_t config = arena_config_default;
+ config.extent_hooks = new_extent_hooks;
+
arena = arena_init(tsd_tsdn(tsd), arena_ind,
- new_extent_hooks);
+ &config);
if (arena == NULL) {
ret = EFAULT;
goto label_return;
@@ -2404,11 +2918,13 @@ arena_i_extent_hooks_ctl(tsd_t *tsd, const size_t *mib, size_t miblen,
extent_hooks_t *new_extent_hooks
JEMALLOC_CC_SILENCE_INIT(NULL);
WRITE(new_extent_hooks, extent_hooks_t *);
- old_extent_hooks = extent_hooks_set(tsd, arena,
- new_extent_hooks);
+ old_extent_hooks = arena_set_extent_hooks(tsd,
+ arena, new_extent_hooks);
READ(old_extent_hooks, extent_hooks_t *);
} else {
- old_extent_hooks = extent_hooks_get(arena);
+ old_extent_hooks =
+ ehooks_get_extent_hooks_ptr(
+ arena_get_ehooks(arena));
READ(old_extent_hooks, extent_hooks_t *);
}
}
@@ -2493,10 +3009,6 @@ arenas_narenas_ctl(tsd_t *tsd, const size_t *mib, size_t miblen,
malloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx);
READONLY();
- if (*oldlenp != sizeof(unsigned)) {
- ret = EINVAL;
- goto label_return;
- }
narenas = ctl_arenas->narenas;
READ(narenas, unsigned);
@@ -2582,14 +3094,14 @@ static int
arenas_create_ctl(tsd_t *tsd, const size_t *mib, size_t miblen,
void *oldp, size_t *oldlenp, void *newp, size_t newlen) {
int ret;
- extent_hooks_t *extent_hooks;
unsigned arena_ind;
malloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx);
- extent_hooks = (extent_hooks_t *)&extent_hooks_default;
- WRITE(extent_hooks, extent_hooks_t *);
- if ((arena_ind = ctl_arena_init(tsd, extent_hooks)) == UINT_MAX) {
+ VERIFY_READ(unsigned);
+ arena_config_t config = arena_config_default;
+ WRITE(config.extent_hooks, extent_hooks_t *);
+ if ((arena_ind = ctl_arena_init(tsd, &config)) == UINT_MAX) {
ret = EAGAIN;
goto label_return;
}
@@ -2602,26 +3114,52 @@ label_return:
}
static int
+experimental_arenas_create_ext_ctl(tsd_t *tsd,
+ const size_t *mib, size_t miblen,
+ void *oldp, size_t *oldlenp, void *newp, size_t newlen) {
+ int ret;
+ unsigned arena_ind;
+
+ malloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx);
+
+ arena_config_t config = arena_config_default;
+ VERIFY_READ(unsigned);
+ WRITE(config, arena_config_t);
+
+ if ((arena_ind = ctl_arena_init(tsd, &config)) == UINT_MAX) {
+ ret = EAGAIN;
+ goto label_return;
+ }
+ READ(arena_ind, unsigned);
+ ret = 0;
+label_return:
+ malloc_mutex_unlock(tsd_tsdn(tsd), &ctl_mtx);
+ return ret;
+}
+
+static int
arenas_lookup_ctl(tsd_t *tsd, const size_t *mib,
size_t miblen, void *oldp, size_t *oldlenp, void *newp,
size_t newlen) {
int ret;
unsigned arena_ind;
void *ptr;
- extent_t *extent;
+ edata_t *edata;
arena_t *arena;
ptr = NULL;
ret = EINVAL;
malloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx);
WRITE(ptr, void *);
- extent = iealloc(tsd_tsdn(tsd), ptr);
- if (extent == NULL)
+ edata = emap_edata_lookup(tsd_tsdn(tsd), &arena_emap_global, ptr);
+ if (edata == NULL) {
goto label_return;
+ }
- arena = extent_arena_get(extent);
- if (arena == NULL)
+ arena = arena_get_from_edata(edata);
+ if (arena == NULL) {
goto label_return;
+ }
arena_ind = arena_ind_get(arena);
READ(arena_ind, unsigned);
@@ -2646,6 +3184,10 @@ prof_thread_active_init_ctl(tsd_t *tsd, const size_t *mib,
}
if (newp != NULL) {
+ if (!opt_prof) {
+ ret = ENOENT;
+ goto label_return;
+ }
if (newlen != sizeof(bool)) {
ret = EINVAL;
goto label_return;
@@ -2653,7 +3195,8 @@ prof_thread_active_init_ctl(tsd_t *tsd, const size_t *mib,
oldval = prof_thread_active_init_set(tsd_tsdn(tsd),
*(bool *)newp);
} else {
- oldval = prof_thread_active_init_get(tsd_tsdn(tsd));
+ oldval = opt_prof ? prof_thread_active_init_get(tsd_tsdn(tsd)) :
+ false;
}
READ(oldval, bool);
@@ -2669,7 +3212,8 @@ prof_active_ctl(tsd_t *tsd, const size_t *mib, size_t miblen,
bool oldval;
if (!config_prof) {
- return ENOENT;
+ ret = ENOENT;
+ goto label_return;
}
if (newp != NULL) {
@@ -2677,9 +3221,20 @@ prof_active_ctl(tsd_t *tsd, const size_t *mib, size_t miblen,
ret = EINVAL;
goto label_return;
}
- oldval = prof_active_set(tsd_tsdn(tsd), *(bool *)newp);
+ bool val = *(bool *)newp;
+ if (!opt_prof) {
+ if (val) {
+ ret = ENOENT;
+ goto label_return;
+ } else {
+ /* No change needed (already off). */
+ oldval = false;
+ }
+ } else {
+ oldval = prof_active_set(tsd_tsdn(tsd), val);
+ }
} else {
- oldval = prof_active_get(tsd_tsdn(tsd));
+ oldval = opt_prof ? prof_active_get(tsd_tsdn(tsd)) : false;
}
READ(oldval, bool);
@@ -2694,7 +3249,7 @@ prof_dump_ctl(tsd_t *tsd, const size_t *mib, size_t miblen,
int ret;
const char *filename = NULL;
- if (!config_prof) {
+ if (!config_prof || !opt_prof) {
return ENOENT;
}
@@ -2722,13 +3277,17 @@ prof_gdump_ctl(tsd_t *tsd, const size_t *mib, size_t miblen,
}
if (newp != NULL) {
+ if (!opt_prof) {
+ ret = ENOENT;
+ goto label_return;
+ }
if (newlen != sizeof(bool)) {
ret = EINVAL;
goto label_return;
}
oldval = prof_gdump_set(tsd_tsdn(tsd), *(bool *)newp);
} else {
- oldval = prof_gdump_get(tsd_tsdn(tsd));
+ oldval = opt_prof ? prof_gdump_get(tsd_tsdn(tsd)) : false;
}
READ(oldval, bool);
@@ -2738,12 +3297,32 @@ label_return:
}
static int
+prof_prefix_ctl(tsd_t *tsd, const size_t *mib, size_t miblen,
+ void *oldp, size_t *oldlenp, void *newp, size_t newlen) {
+ int ret;
+ const char *prefix = NULL;
+
+ if (!config_prof || !opt_prof) {
+ return ENOENT;
+ }
+
+ malloc_mutex_lock(tsd_tsdn(tsd), &ctl_mtx);
+ WRITEONLY();
+ WRITE(prefix, const char *);
+
+ ret = prof_prefix_set(tsd_tsdn(tsd), prefix) ? EFAULT : 0;
+label_return:
+ malloc_mutex_unlock(tsd_tsdn(tsd), &ctl_mtx);
+ return ret;
+}
+
+static int
prof_reset_ctl(tsd_t *tsd, const size_t *mib, size_t miblen,
void *oldp, size_t *oldlenp, void *newp, size_t newlen) {
int ret;
size_t lg_sample = lg_prof_sample;
- if (!config_prof) {
+ if (!config_prof || !opt_prof) {
return ENOENT;
}
@@ -2770,7 +3349,7 @@ prof_log_start_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,
const char *filename = NULL;
- if (!config_prof) {
+ if (!config_prof || !opt_prof) {
return ENOENT;
}
@@ -2790,7 +3369,7 @@ label_return:
static int
prof_log_stop_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,
size_t *oldlenp, void *newp, size_t newlen) {
- if (!config_prof) {
+ if (!config_prof || !opt_prof) {
return ENOENT;
}
@@ -2801,6 +3380,87 @@ prof_log_stop_ctl(tsd_t *tsd, const size_t *mib, size_t miblen, void *oldp,
return 0;
}
+static int
+experimental_hooks_prof_backtrace_ctl(tsd_t *tsd, const size_t *mib,
+ size_t miblen, void *oldp, size_t *oldlenp, void *newp, size_t newlen) {
+ int ret;
+
+ if (oldp == NULL && newp == NULL) {
+ ret = EINVAL;
+ goto label_return;
+ }
+ if (oldp != NULL) {
+ prof_backtrace_hook_t old_hook =
+ prof_backtrace_hook_get();
+ READ(old_hook, prof_backtrace_hook_t);
+ }
+ if (newp != NULL) {
+ if (!opt_prof) {
+ ret = ENOENT;
+ goto label_return;
+ }
+ prof_backtrace_hook_t new_hook JEMALLOC_CC_SILENCE_INIT(NULL);
+ WRITE(new_hook, prof_backtrace_hook_t);
+ if (new_hook == NULL) {
+ ret = EINVAL;
+ goto label_return;
+ }
+ prof_backtrace_hook_set(new_hook);
+ }
+ ret = 0;
+label_return:
+ return ret;
+}
+
+static int
+experimental_hooks_prof_dump_ctl(tsd_t *tsd, const size_t *mib,
+ size_t miblen, void *oldp, size_t *oldlenp, void *newp, size_t newlen) {
+ int ret;
+
+ if (oldp == NULL && newp == NULL) {
+ ret = EINVAL;
+ goto label_return;
+ }
+ if (oldp != NULL) {
+ prof_dump_hook_t old_hook =
+ prof_dump_hook_get();
+ READ(old_hook, prof_dump_hook_t);
+ }
+ if (newp != NULL) {
+ if (!opt_prof) {
+ ret = ENOENT;
+ goto label_return;
+ }
+ prof_dump_hook_t new_hook JEMALLOC_CC_SILENCE_INIT(NULL);
+ WRITE(new_hook, prof_dump_hook_t);
+ prof_dump_hook_set(new_hook);
+ }
+ ret = 0;
+label_return:
+ return ret;
+}
+
+/* For integration test purpose only. No plan to move out of experimental. */
+static int
+experimental_hooks_safety_check_abort_ctl(tsd_t *tsd, const size_t *mib,
+ size_t miblen, void *oldp, size_t *oldlenp, void *newp, size_t newlen) {
+ int ret;
+
+ WRITEONLY();
+ if (newp != NULL) {
+ if (newlen != sizeof(safety_check_abort_hook_t)) {
+ ret = EINVAL;
+ goto label_return;
+ }
+ safety_check_abort_hook_t hook JEMALLOC_CC_SILENCE_INIT(NULL);
+ WRITE(hook, safety_check_abort_hook_t);
+ safety_check_set_abort(hook);
+ }
+ ret = 0;
+label_return:
+ return ret;
+}
+
/******************************************************************************/
CTL_RO_CGEN(config_stats, stats_allocated, ctl_stats->allocated, size_t)
@@ -2818,6 +3478,9 @@ CTL_RO_CGEN(config_stats, stats_background_thread_num_runs,
CTL_RO_CGEN(config_stats, stats_background_thread_run_interval,
nstime_ns(&ctl_stats->background_thread.run_interval), uint64_t)
+CTL_RO_CGEN(config_stats, stats_zero_reallocs,
+ atomic_load_zu(&zero_realloc_count, ATOMIC_RELAXED), size_t)
+
CTL_RO_GEN(stats_arenas_i_dss, arenas_i(mib[2])->dss, const char *)
CTL_RO_GEN(stats_arenas_i_dirty_decay_ms, arenas_i(mib[2])->dirty_decay_ms,
ssize_t)
@@ -2830,55 +3493,61 @@ CTL_RO_GEN(stats_arenas_i_pactive, arenas_i(mib[2])->pactive, size_t)
CTL_RO_GEN(stats_arenas_i_pdirty, arenas_i(mib[2])->pdirty, size_t)
CTL_RO_GEN(stats_arenas_i_pmuzzy, arenas_i(mib[2])->pmuzzy, size_t)
CTL_RO_CGEN(config_stats, stats_arenas_i_mapped,
- atomic_load_zu(&arenas_i(mib[2])->astats->astats.mapped, ATOMIC_RELAXED),
- size_t)
+ arenas_i(mib[2])->astats->astats.mapped, size_t)
CTL_RO_CGEN(config_stats, stats_arenas_i_retained,
- atomic_load_zu(&arenas_i(mib[2])->astats->astats.retained, ATOMIC_RELAXED),
- size_t)
+ arenas_i(mib[2])->astats->astats.pa_shard_stats.pac_stats.retained, size_t)
CTL_RO_CGEN(config_stats, stats_arenas_i_extent_avail,
- atomic_load_zu(&arenas_i(mib[2])->astats->astats.extent_avail,
- ATOMIC_RELAXED),
- size_t)
+ arenas_i(mib[2])->astats->astats.pa_shard_stats.edata_avail, size_t)
CTL_RO_CGEN(config_stats, stats_arenas_i_dirty_npurge,
- ctl_arena_stats_read_u64(
- &arenas_i(mib[2])->astats->astats.decay_dirty.npurge), uint64_t)
+ locked_read_u64_unsynchronized(
+ &arenas_i(mib[2])->astats->astats.pa_shard_stats.pac_stats.decay_dirty.npurge),
+ uint64_t)
CTL_RO_CGEN(config_stats, stats_arenas_i_dirty_nmadvise,
- ctl_arena_stats_read_u64(
- &arenas_i(mib[2])->astats->astats.decay_dirty.nmadvise), uint64_t)
+ locked_read_u64_unsynchronized(
+ &arenas_i(mib[2])->astats->astats.pa_shard_stats.pac_stats.decay_dirty.nmadvise),
+ uint64_t)
CTL_RO_CGEN(config_stats, stats_arenas_i_dirty_purged,
- ctl_arena_stats_read_u64(
- &arenas_i(mib[2])->astats->astats.decay_dirty.purged), uint64_t)
+ locked_read_u64_unsynchronized(
+ &arenas_i(mib[2])->astats->astats.pa_shard_stats.pac_stats.decay_dirty.purged),
+ uint64_t)
CTL_RO_CGEN(config_stats, stats_arenas_i_muzzy_npurge,
- ctl_arena_stats_read_u64(
- &arenas_i(mib[2])->astats->astats.decay_muzzy.npurge), uint64_t)
+ locked_read_u64_unsynchronized(
+ &arenas_i(mib[2])->astats->astats.pa_shard_stats.pac_stats.decay_muzzy.npurge),
+ uint64_t)
CTL_RO_CGEN(config_stats, stats_arenas_i_muzzy_nmadvise,
- ctl_arena_stats_read_u64(
- &arenas_i(mib[2])->astats->astats.decay_muzzy.nmadvise), uint64_t)
+ locked_read_u64_unsynchronized(
+ &arenas_i(mib[2])->astats->astats.pa_shard_stats.pac_stats.decay_muzzy.nmadvise),
+ uint64_t)
CTL_RO_CGEN(config_stats, stats_arenas_i_muzzy_purged,
- ctl_arena_stats_read_u64(
- &arenas_i(mib[2])->astats->astats.decay_muzzy.purged), uint64_t)
+ locked_read_u64_unsynchronized(
+ &arenas_i(mib[2])->astats->astats.pa_shard_stats.pac_stats.decay_muzzy.purged),
+ uint64_t)
CTL_RO_CGEN(config_stats, stats_arenas_i_base,
- atomic_load_zu(&arenas_i(mib[2])->astats->astats.base, ATOMIC_RELAXED),
+ arenas_i(mib[2])->astats->astats.base,
size_t)
CTL_RO_CGEN(config_stats, stats_arenas_i_internal,
atomic_load_zu(&arenas_i(mib[2])->astats->astats.internal, ATOMIC_RELAXED),
size_t)
CTL_RO_CGEN(config_stats, stats_arenas_i_metadata_thp,
- atomic_load_zu(&arenas_i(mib[2])->astats->astats.metadata_thp,
- ATOMIC_RELAXED), size_t)
+ arenas_i(mib[2])->astats->astats.metadata_thp, size_t)
CTL_RO_CGEN(config_stats, stats_arenas_i_tcache_bytes,
- atomic_load_zu(&arenas_i(mib[2])->astats->astats.tcache_bytes,
- ATOMIC_RELAXED), size_t)
+ arenas_i(mib[2])->astats->astats.tcache_bytes, size_t)
+CTL_RO_CGEN(config_stats, stats_arenas_i_tcache_stashed_bytes,
+ arenas_i(mib[2])->astats->astats.tcache_stashed_bytes, size_t)
CTL_RO_CGEN(config_stats, stats_arenas_i_resident,
- atomic_load_zu(&arenas_i(mib[2])->astats->astats.resident, ATOMIC_RELAXED),
+ arenas_i(mib[2])->astats->astats.resident,
size_t)
CTL_RO_CGEN(config_stats, stats_arenas_i_abandoned_vm,
- atomic_load_zu(&arenas_i(mib[2])->astats->astats.abandoned_vm,
+ atomic_load_zu(
+ &arenas_i(mib[2])->astats->astats.pa_shard_stats.pac_stats.abandoned_vm,
ATOMIC_RELAXED), size_t)
+CTL_RO_CGEN(config_stats, stats_arenas_i_hpa_sec_bytes,
+ arenas_i(mib[2])->astats->secstats.bytes, size_t)
+
CTL_RO_CGEN(config_stats, stats_arenas_i_small_allocated,
arenas_i(mib[2])->astats->allocated_small, size_t)
CTL_RO_CGEN(config_stats, stats_arenas_i_small_nmalloc,
@@ -2892,27 +3561,21 @@ CTL_RO_CGEN(config_stats, stats_arenas_i_small_nfills,
CTL_RO_CGEN(config_stats, stats_arenas_i_small_nflushes,
arenas_i(mib[2])->astats->nflushes_small, uint64_t)
CTL_RO_CGEN(config_stats, stats_arenas_i_large_allocated,
- atomic_load_zu(&arenas_i(mib[2])->astats->astats.allocated_large,
- ATOMIC_RELAXED), size_t)
+ arenas_i(mib[2])->astats->astats.allocated_large, size_t)
CTL_RO_CGEN(config_stats, stats_arenas_i_large_nmalloc,
- ctl_arena_stats_read_u64(
- &arenas_i(mib[2])->astats->astats.nmalloc_large), uint64_t)
+ arenas_i(mib[2])->astats->astats.nmalloc_large, uint64_t)
CTL_RO_CGEN(config_stats, stats_arenas_i_large_ndalloc,
- ctl_arena_stats_read_u64(
- &arenas_i(mib[2])->astats->astats.ndalloc_large), uint64_t)
+ arenas_i(mib[2])->astats->astats.ndalloc_large, uint64_t)
CTL_RO_CGEN(config_stats, stats_arenas_i_large_nrequests,
- ctl_arena_stats_read_u64(
- &arenas_i(mib[2])->astats->astats.nrequests_large), uint64_t)
+ arenas_i(mib[2])->astats->astats.nrequests_large, uint64_t)
/*
* Note: "nmalloc_large" here instead of "nfills" in the read. This is
* intentional (large has no batch fill).
*/
CTL_RO_CGEN(config_stats, stats_arenas_i_large_nfills,
- ctl_arena_stats_read_u64(
- &arenas_i(mib[2])->astats->astats.nmalloc_large), uint64_t)
+ arenas_i(mib[2])->astats->astats.nmalloc_large, uint64_t)
CTL_RO_CGEN(config_stats, stats_arenas_i_large_nflushes,
- ctl_arena_stats_read_u64(
- &arenas_i(mib[2])->astats->astats.nflushes_large), uint64_t)
+ arenas_i(mib[2])->astats->astats.nflushes_large, uint64_t)
/* Lock profiling related APIs below. */
#define RO_MUTEX_CTL_GEN(n, l) \
@@ -2972,9 +3635,13 @@ stats_mutexes_reset_ctl(tsd_t *tsd, const size_t *mib,
}
if (config_prof && opt_prof) {
MUTEX_PROF_RESET(bt2gctx_mtx);
+ MUTEX_PROF_RESET(tdatas_mtx);
+ MUTEX_PROF_RESET(prof_dump_mtx);
+ MUTEX_PROF_RESET(prof_recent_alloc_mtx);
+ MUTEX_PROF_RESET(prof_recent_dump_mtx);
+ MUTEX_PROF_RESET(prof_stats_mtx);
}
-
/* Per arena mutexes. */
unsigned n = narenas_total_get();
@@ -2984,18 +3651,18 @@ stats_mutexes_reset_ctl(tsd_t *tsd, const size_t *mib,
continue;
}
MUTEX_PROF_RESET(arena->large_mtx);
- MUTEX_PROF_RESET(arena->extent_avail_mtx);
- MUTEX_PROF_RESET(arena->extents_dirty.mtx);
- MUTEX_PROF_RESET(arena->extents_muzzy.mtx);
- MUTEX_PROF_RESET(arena->extents_retained.mtx);
- MUTEX_PROF_RESET(arena->decay_dirty.mtx);
- MUTEX_PROF_RESET(arena->decay_muzzy.mtx);
+ MUTEX_PROF_RESET(arena->pa_shard.edata_cache.mtx);
+ MUTEX_PROF_RESET(arena->pa_shard.pac.ecache_dirty.mtx);
+ MUTEX_PROF_RESET(arena->pa_shard.pac.ecache_muzzy.mtx);
+ MUTEX_PROF_RESET(arena->pa_shard.pac.ecache_retained.mtx);
+ MUTEX_PROF_RESET(arena->pa_shard.pac.decay_dirty.mtx);
+ MUTEX_PROF_RESET(arena->pa_shard.pac.decay_muzzy.mtx);
MUTEX_PROF_RESET(arena->tcache_ql_mtx);
MUTEX_PROF_RESET(arena->base->mtx);
- for (szind_t i = 0; i < SC_NBINS; i++) {
- for (unsigned j = 0; j < bin_infos[i].n_shards; j++) {
- bin_t *bin = &arena->bins[i].bin_shards[j];
+ for (szind_t j = 0; j < SC_NBINS; j++) {
+ for (unsigned k = 0; k < bin_infos[j].n_shards; k++) {
+ bin_t *bin = arena_get_bin(arena, j, k);
MUTEX_PROF_RESET(bin->lock);
}
}
@@ -3005,25 +3672,25 @@ stats_mutexes_reset_ctl(tsd_t *tsd, const size_t *mib,
}
CTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_nmalloc,
- arenas_i(mib[2])->astats->bstats[mib[4]].nmalloc, uint64_t)
+ arenas_i(mib[2])->astats->bstats[mib[4]].stats_data.nmalloc, uint64_t)
CTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_ndalloc,
- arenas_i(mib[2])->astats->bstats[mib[4]].ndalloc, uint64_t)
+ arenas_i(mib[2])->astats->bstats[mib[4]].stats_data.ndalloc, uint64_t)
CTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_nrequests,
- arenas_i(mib[2])->astats->bstats[mib[4]].nrequests, uint64_t)
+ arenas_i(mib[2])->astats->bstats[mib[4]].stats_data.nrequests, uint64_t)
CTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_curregs,
- arenas_i(mib[2])->astats->bstats[mib[4]].curregs, size_t)
+ arenas_i(mib[2])->astats->bstats[mib[4]].stats_data.curregs, size_t)
CTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_nfills,
- arenas_i(mib[2])->astats->bstats[mib[4]].nfills, uint64_t)
+ arenas_i(mib[2])->astats->bstats[mib[4]].stats_data.nfills, uint64_t)
CTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_nflushes,
- arenas_i(mib[2])->astats->bstats[mib[4]].nflushes, uint64_t)
+ arenas_i(mib[2])->astats->bstats[mib[4]].stats_data.nflushes, uint64_t)
CTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_nslabs,
- arenas_i(mib[2])->astats->bstats[mib[4]].nslabs, uint64_t)
+ arenas_i(mib[2])->astats->bstats[mib[4]].stats_data.nslabs, uint64_t)
CTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_nreslabs,
- arenas_i(mib[2])->astats->bstats[mib[4]].reslabs, uint64_t)
+ arenas_i(mib[2])->astats->bstats[mib[4]].stats_data.reslabs, uint64_t)
CTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_curslabs,
- arenas_i(mib[2])->astats->bstats[mib[4]].curslabs, size_t)
+ arenas_i(mib[2])->astats->bstats[mib[4]].stats_data.curslabs, size_t)
CTL_RO_CGEN(config_stats, stats_arenas_i_bins_j_nonfull_slabs,
- arenas_i(mib[2])->astats->bstats[mib[4]].nonfull_slabs, size_t)
+ arenas_i(mib[2])->astats->bstats[mib[4]].stats_data.nonfull_slabs, size_t)
static const ctl_named_node_t *
stats_arenas_i_bins_j_index(tsdn_t *tsdn, const size_t *mib,
@@ -3035,13 +3702,13 @@ stats_arenas_i_bins_j_index(tsdn_t *tsdn, const size_t *mib,
}
CTL_RO_CGEN(config_stats, stats_arenas_i_lextents_j_nmalloc,
- ctl_arena_stats_read_u64(
+ locked_read_u64_unsynchronized(
&arenas_i(mib[2])->astats->lstats[mib[4]].nmalloc), uint64_t)
CTL_RO_CGEN(config_stats, stats_arenas_i_lextents_j_ndalloc,
- ctl_arena_stats_read_u64(
+ locked_read_u64_unsynchronized(
&arenas_i(mib[2])->astats->lstats[mib[4]].ndalloc), uint64_t)
CTL_RO_CGEN(config_stats, stats_arenas_i_lextents_j_nrequests,
- ctl_arena_stats_read_u64(
+ locked_read_u64_unsynchronized(
&arenas_i(mib[2])->astats->lstats[mib[4]].nrequests), uint64_t)
CTL_RO_CGEN(config_stats, stats_arenas_i_lextents_j_curlextents,
arenas_i(mib[2])->astats->lstats[mib[4]].curlextents, size_t)
@@ -3056,29 +3723,17 @@ stats_arenas_i_lextents_j_index(tsdn_t *tsdn, const size_t *mib,
}
CTL_RO_CGEN(config_stats, stats_arenas_i_extents_j_ndirty,
- atomic_load_zu(
- &arenas_i(mib[2])->astats->estats[mib[4]].ndirty,
- ATOMIC_RELAXED), size_t);
+ arenas_i(mib[2])->astats->estats[mib[4]].ndirty, size_t);
CTL_RO_CGEN(config_stats, stats_arenas_i_extents_j_nmuzzy,
- atomic_load_zu(
- &arenas_i(mib[2])->astats->estats[mib[4]].nmuzzy,
- ATOMIC_RELAXED), size_t);
+ arenas_i(mib[2])->astats->estats[mib[4]].nmuzzy, size_t);
CTL_RO_CGEN(config_stats, stats_arenas_i_extents_j_nretained,
- atomic_load_zu(
- &arenas_i(mib[2])->astats->estats[mib[4]].nretained,
- ATOMIC_RELAXED), size_t);
+ arenas_i(mib[2])->astats->estats[mib[4]].nretained, size_t);
CTL_RO_CGEN(config_stats, stats_arenas_i_extents_j_dirty_bytes,
- atomic_load_zu(
- &arenas_i(mib[2])->astats->estats[mib[4]].dirty_bytes,
- ATOMIC_RELAXED), size_t);
+ arenas_i(mib[2])->astats->estats[mib[4]].dirty_bytes, size_t);
CTL_RO_CGEN(config_stats, stats_arenas_i_extents_j_muzzy_bytes,
- atomic_load_zu(
- &arenas_i(mib[2])->astats->estats[mib[4]].muzzy_bytes,
- ATOMIC_RELAXED), size_t);
+ arenas_i(mib[2])->astats->estats[mib[4]].muzzy_bytes, size_t);
CTL_RO_CGEN(config_stats, stats_arenas_i_extents_j_retained_bytes,
- atomic_load_zu(
- &arenas_i(mib[2])->astats->estats[mib[4]].retained_bytes,
- ATOMIC_RELAXED), size_t);
+ arenas_i(mib[2])->astats->estats[mib[4]].retained_bytes, size_t);
static const ctl_named_node_t *
stats_arenas_i_extents_j_index(tsdn_t *tsdn, const size_t *mib,
@@ -3089,6 +3744,82 @@ stats_arenas_i_extents_j_index(tsdn_t *tsdn, const size_t *mib,
return super_stats_arenas_i_extents_j_node;
}
+CTL_RO_CGEN(config_stats, stats_arenas_i_hpa_shard_npurge_passes,
+ arenas_i(mib[2])->astats->hpastats.nonderived_stats.npurge_passes, uint64_t);
+CTL_RO_CGEN(config_stats, stats_arenas_i_hpa_shard_npurges,
+ arenas_i(mib[2])->astats->hpastats.nonderived_stats.npurges, uint64_t);
+CTL_RO_CGEN(config_stats, stats_arenas_i_hpa_shard_nhugifies,
+ arenas_i(mib[2])->astats->hpastats.nonderived_stats.nhugifies, uint64_t);
+CTL_RO_CGEN(config_stats, stats_arenas_i_hpa_shard_ndehugifies,
+ arenas_i(mib[2])->astats->hpastats.nonderived_stats.ndehugifies, uint64_t);
+
+/* Full, nonhuge */
+CTL_RO_CGEN(config_stats, stats_arenas_i_hpa_shard_full_slabs_npageslabs_nonhuge,
+ arenas_i(mib[2])->astats->hpastats.psset_stats.full_slabs[0].npageslabs,
+ size_t);
+CTL_RO_CGEN(config_stats, stats_arenas_i_hpa_shard_full_slabs_nactive_nonhuge,
+ arenas_i(mib[2])->astats->hpastats.psset_stats.full_slabs[0].nactive, size_t);
+CTL_RO_CGEN(config_stats, stats_arenas_i_hpa_shard_full_slabs_ndirty_nonhuge,
+ arenas_i(mib[2])->astats->hpastats.psset_stats.full_slabs[0].ndirty, size_t);
+
+/* Full, huge */
+CTL_RO_CGEN(config_stats, stats_arenas_i_hpa_shard_full_slabs_npageslabs_huge,
+ arenas_i(mib[2])->astats->hpastats.psset_stats.full_slabs[1].npageslabs,
+ size_t);
+CTL_RO_CGEN(config_stats, stats_arenas_i_hpa_shard_full_slabs_nactive_huge,
+ arenas_i(mib[2])->astats->hpastats.psset_stats.full_slabs[1].nactive, size_t);
+CTL_RO_CGEN(config_stats, stats_arenas_i_hpa_shard_full_slabs_ndirty_huge,
+ arenas_i(mib[2])->astats->hpastats.psset_stats.full_slabs[1].ndirty, size_t);
+
+/* Empty, nonhuge */
+CTL_RO_CGEN(config_stats, stats_arenas_i_hpa_shard_empty_slabs_npageslabs_nonhuge,
+ arenas_i(mib[2])->astats->hpastats.psset_stats.empty_slabs[0].npageslabs,
+ size_t);
+CTL_RO_CGEN(config_stats, stats_arenas_i_hpa_shard_empty_slabs_nactive_nonhuge,
+ arenas_i(mib[2])->astats->hpastats.psset_stats.empty_slabs[0].nactive, size_t);
+CTL_RO_CGEN(config_stats, stats_arenas_i_hpa_shard_empty_slabs_ndirty_nonhuge,
+ arenas_i(mib[2])->astats->hpastats.psset_stats.empty_slabs[0].ndirty, size_t);
+
+/* Empty, huge */
+CTL_RO_CGEN(config_stats, stats_arenas_i_hpa_shard_empty_slabs_npageslabs_huge,
+ arenas_i(mib[2])->astats->hpastats.psset_stats.empty_slabs[1].npageslabs,
+ size_t);
+CTL_RO_CGEN(config_stats, stats_arenas_i_hpa_shard_empty_slabs_nactive_huge,
+ arenas_i(mib[2])->astats->hpastats.psset_stats.empty_slabs[1].nactive, size_t);
+CTL_RO_CGEN(config_stats, stats_arenas_i_hpa_shard_empty_slabs_ndirty_huge,
+ arenas_i(mib[2])->astats->hpastats.psset_stats.empty_slabs[1].ndirty, size_t);
+
+/* Nonfull, nonhuge */
+CTL_RO_CGEN(config_stats, stats_arenas_i_hpa_shard_nonfull_slabs_j_npageslabs_nonhuge,
+ arenas_i(mib[2])->astats->hpastats.psset_stats.nonfull_slabs[mib[5]][0].npageslabs,
+ size_t);
+CTL_RO_CGEN(config_stats, stats_arenas_i_hpa_shard_nonfull_slabs_j_nactive_nonhuge,
+ arenas_i(mib[2])->astats->hpastats.psset_stats.nonfull_slabs[mib[5]][0].nactive,
+ size_t);
+CTL_RO_CGEN(config_stats, stats_arenas_i_hpa_shard_nonfull_slabs_j_ndirty_nonhuge,
+ arenas_i(mib[2])->astats->hpastats.psset_stats.nonfull_slabs[mib[5]][0].ndirty,
+ size_t);
+
+/* Nonfull, huge */
+CTL_RO_CGEN(config_stats, stats_arenas_i_hpa_shard_nonfull_slabs_j_npageslabs_huge,
+ arenas_i(mib[2])->astats->hpastats.psset_stats.nonfull_slabs[mib[5]][1].npageslabs,
+ size_t);
+CTL_RO_CGEN(config_stats, stats_arenas_i_hpa_shard_nonfull_slabs_j_nactive_huge,
+ arenas_i(mib[2])->astats->hpastats.psset_stats.nonfull_slabs[mib[5]][1].nactive,
+ size_t);
+CTL_RO_CGEN(config_stats, stats_arenas_i_hpa_shard_nonfull_slabs_j_ndirty_huge,
+ arenas_i(mib[2])->astats->hpastats.psset_stats.nonfull_slabs[mib[5]][1].ndirty,
+ size_t);
+
+static const ctl_named_node_t *
+stats_arenas_i_hpa_shard_nonfull_slabs_j_index(tsdn_t *tsdn, const size_t *mib,
+ size_t miblen, size_t j) {
+ if (j >= PSSET_NPSIZES) {
+ return NULL;
+ }
+ return super_stats_arenas_i_hpa_shard_nonfull_slabs_j_node;
+}
+
static bool
ctl_arenas_i_verify(size_t i) {
size_t a = arenas_i2a_impl(i, true, true);
@@ -3161,6 +3892,32 @@ label_return:
return ret;
}
+static int
+experimental_thread_activity_callback_ctl(tsd_t *tsd, const size_t *mib,
+ size_t miblen, void *oldp, size_t *oldlenp, void *newp, size_t newlen) {
+ int ret;
+
+ if (!config_stats) {
+ return ENOENT;
+ }
+
+ activity_callback_thunk_t t_old = tsd_activity_callback_thunk_get(tsd);
+ READ(t_old, activity_callback_thunk_t);
+
+ if (newp != NULL) {
+ /*
+ * This initialization is unnecessary. If it's omitted, though,
+ * clang gets confused and warns on the subsequent use of t_new.
+ */
+ activity_callback_thunk_t t_new = {NULL, NULL};
+ WRITE(t_new, activity_callback_thunk_t);
+ tsd_activity_callback_thunk_set(tsd, t_new);
+ }
+ ret = 0;
+label_return:
+ return ret;
+}
+
/*
* Output six memory utilization entries for an input pointer, the first one of
* type (void *) and the remaining five of type size_t, describing the following
@@ -3178,7 +3935,8 @@ label_return:
* otherwise their values are undefined.
*
* This API is mainly intended for small class allocations, where extents are
- * used as slab.
+ * used as slab. Note that if the bin the extent belongs to is completely
+ * full, "(a)" will be NULL.
*
* In case of large class allocations, "(a)" will be NULL, and "(e)" and "(f)"
* will be zero (if stats are enabled; otherwise undefined). The other three
@@ -3232,11 +3990,11 @@ experimental_utilization_query_ctl(tsd_t *tsd, const size_t *mib,
size_t miblen, void *oldp, size_t *oldlenp, void *newp, size_t newlen) {
int ret;
- assert(sizeof(extent_util_stats_verbose_t)
+ assert(sizeof(inspect_extent_util_stats_verbose_t)
== sizeof(void *) + sizeof(size_t) * 5);
if (oldp == NULL || oldlenp == NULL
- || *oldlenp != sizeof(extent_util_stats_verbose_t)
+ || *oldlenp != sizeof(inspect_extent_util_stats_verbose_t)
|| newp == NULL) {
ret = EINVAL;
goto label_return;
@@ -3244,9 +4002,9 @@ experimental_utilization_query_ctl(tsd_t *tsd, const size_t *mib,
void *ptr = NULL;
WRITE(ptr, void *);
- extent_util_stats_verbose_t *util_stats
- = (extent_util_stats_verbose_t *)oldp;
- extent_util_stats_verbose_get(tsd_tsdn(tsd), ptr,
+ inspect_extent_util_stats_verbose_t *util_stats
+ = (inspect_extent_util_stats_verbose_t *)oldp;
+ inspect_extent_util_stats_verbose_get(tsd_tsdn(tsd), ptr,
&util_stats->nfree, &util_stats->nregs, &util_stats->size,
&util_stats->bin_nfree, &util_stats->bin_nregs,
&util_stats->slabcur_addr);
@@ -3357,21 +4115,22 @@ experimental_utilization_batch_query_ctl(tsd_t *tsd, const size_t *mib,
size_t miblen, void *oldp, size_t *oldlenp, void *newp, size_t newlen) {
int ret;
- assert(sizeof(extent_util_stats_t) == sizeof(size_t) * 3);
+ assert(sizeof(inspect_extent_util_stats_t) == sizeof(size_t) * 3);
const size_t len = newlen / sizeof(const void *);
if (oldp == NULL || oldlenp == NULL || newp == NULL || newlen == 0
|| newlen != len * sizeof(const void *)
- || *oldlenp != len * sizeof(extent_util_stats_t)) {
+ || *oldlenp != len * sizeof(inspect_extent_util_stats_t)) {
ret = EINVAL;
goto label_return;
}
void **ptrs = (void **)newp;
- extent_util_stats_t *util_stats = (extent_util_stats_t *)oldp;
+ inspect_extent_util_stats_t *util_stats =
+ (inspect_extent_util_stats_t *)oldp;
size_t i;
for (i = 0; i < len; ++i) {
- extent_util_stats_get(tsd_tsdn(tsd), ptrs[i],
+ inspect_extent_util_stats_get(tsd_tsdn(tsd), ptrs[i],
&util_stats[i].nfree, &util_stats[i].nregs,
&util_stats[i].size);
}
@@ -3420,7 +4179,7 @@ experimental_arenas_i_pactivep_ctl(tsd_t *tsd, const size_t *mib,
#if defined(JEMALLOC_GCC_ATOMIC_ATOMICS) || \
defined(JEMALLOC_GCC_SYNC_ATOMICS) || defined(_MSC_VER)
/* Expose the underlying counter for fast read. */
- pactivep = (size_t *)&(arena->nactive.repr);
+ pactivep = (size_t *)&(arena->pa_shard.nactive.repr);
READ(pactivep, size_t *);
ret = 0;
#else
@@ -3433,3 +4192,223 @@ label_return:
malloc_mutex_unlock(tsd_tsdn(tsd), &ctl_mtx);
return ret;
}
+
+static int
+experimental_prof_recent_alloc_max_ctl(tsd_t *tsd, const size_t *mib,
+ size_t miblen, void *oldp, size_t *oldlenp, void *newp, size_t newlen) {
+ int ret;
+
+ if (!(config_prof && opt_prof)) {
+ ret = ENOENT;
+ goto label_return;
+ }
+
+ ssize_t old_max;
+ if (newp != NULL) {
+ if (newlen != sizeof(ssize_t)) {
+ ret = EINVAL;
+ goto label_return;
+ }
+ ssize_t max = *(ssize_t *)newp;
+ if (max < -1) {
+ ret = EINVAL;
+ goto label_return;
+ }
+ old_max = prof_recent_alloc_max_ctl_write(tsd, max);
+ } else {
+ old_max = prof_recent_alloc_max_ctl_read();
+ }
+ READ(old_max, ssize_t);
+
+ ret = 0;
+
+label_return:
+ return ret;
+}
+
+typedef struct write_cb_packet_s write_cb_packet_t;
+struct write_cb_packet_s {
+ write_cb_t *write_cb;
+ void *cbopaque;
+};
+
+static int
+experimental_prof_recent_alloc_dump_ctl(tsd_t *tsd, const size_t *mib,
+ size_t miblen, void *oldp, size_t *oldlenp, void *newp, size_t newlen) {
+ int ret;
+
+ if (!(config_prof && opt_prof)) {
+ ret = ENOENT;
+ goto label_return;
+ }
+
+ assert(sizeof(write_cb_packet_t) == sizeof(void *) * 2);
+
+ WRITEONLY();
+ write_cb_packet_t write_cb_packet;
+ ASSURED_WRITE(write_cb_packet, write_cb_packet_t);
+
+ prof_recent_alloc_dump(tsd, write_cb_packet.write_cb,
+ write_cb_packet.cbopaque);
+
+ ret = 0;
+
+label_return:
+ return ret;
+}
+
+typedef struct batch_alloc_packet_s batch_alloc_packet_t;
+struct batch_alloc_packet_s {
+ void **ptrs;
+ size_t num;
+ size_t size;
+ int flags;
+};
+
+static int
+experimental_batch_alloc_ctl(tsd_t *tsd, const size_t *mib,
+ size_t miblen, void *oldp, size_t *oldlenp, void *newp, size_t newlen) {
+ int ret;
+
+ VERIFY_READ(size_t);
+
+ batch_alloc_packet_t batch_alloc_packet;
+ ASSURED_WRITE(batch_alloc_packet, batch_alloc_packet_t);
+ size_t filled = batch_alloc(batch_alloc_packet.ptrs,
+ batch_alloc_packet.num, batch_alloc_packet.size,
+ batch_alloc_packet.flags);
+ READ(filled, size_t);
+
+ ret = 0;
+
+label_return:
+ return ret;
+}
+
+static int
+prof_stats_bins_i_live_ctl(tsd_t *tsd, const size_t *mib, size_t miblen,
+ void *oldp, size_t *oldlenp, void *newp, size_t newlen) {
+ int ret;
+ unsigned binind;
+ prof_stats_t stats;
+
+ if (!(config_prof && opt_prof && opt_prof_stats)) {
+ ret = ENOENT;
+ goto label_return;
+ }
+
+ READONLY();
+ MIB_UNSIGNED(binind, 3);
+ if (binind >= SC_NBINS) {
+ ret = EINVAL;
+ goto label_return;
+ }
+ prof_stats_get_live(tsd, (szind_t)binind, &stats);
+ READ(stats, prof_stats_t);
+
+ ret = 0;
+label_return:
+ return ret;
+}
+
+static int
+prof_stats_bins_i_accum_ctl(tsd_t *tsd, const size_t *mib, size_t miblen,
+ void *oldp, size_t *oldlenp, void *newp, size_t newlen) {
+ int ret;
+ unsigned binind;
+ prof_stats_t stats;
+
+ if (!(config_prof && opt_prof && opt_prof_stats)) {
+ ret = ENOENT;
+ goto label_return;
+ }
+
+ READONLY();
+ MIB_UNSIGNED(binind, 3);
+ if (binind >= SC_NBINS) {
+ ret = EINVAL;
+ goto label_return;
+ }
+ prof_stats_get_accum(tsd, (szind_t)binind, &stats);
+ READ(stats, prof_stats_t);
+
+ ret = 0;
+label_return:
+ return ret;
+}
+
+static const ctl_named_node_t *
+prof_stats_bins_i_index(tsdn_t *tsdn, const size_t *mib, size_t miblen,
+ size_t i) {
+ if (!(config_prof && opt_prof && opt_prof_stats)) {
+ return NULL;
+ }
+ if (i >= SC_NBINS) {
+ return NULL;
+ }
+ return super_prof_stats_bins_i_node;
+}
+
+static int
+prof_stats_lextents_i_live_ctl(tsd_t *tsd, const size_t *mib, size_t miblen,
+ void *oldp, size_t *oldlenp, void *newp, size_t newlen) {
+ int ret;
+ unsigned lextent_ind;
+ prof_stats_t stats;
+
+ if (!(config_prof && opt_prof && opt_prof_stats)) {
+ ret = ENOENT;
+ goto label_return;
+ }
+
+ READONLY();
+ MIB_UNSIGNED(lextent_ind, 3);
+ if (lextent_ind >= SC_NSIZES - SC_NBINS) {
+ ret = EINVAL;
+ goto label_return;
+ }
+ prof_stats_get_live(tsd, (szind_t)(lextent_ind + SC_NBINS), &stats);
+ READ(stats, prof_stats_t);
+
+ ret = 0;
+label_return:
+ return ret;
+}
+
+static int
+prof_stats_lextents_i_accum_ctl(tsd_t *tsd, const size_t *mib, size_t miblen,
+ void *oldp, size_t *oldlenp, void *newp, size_t newlen) {
+ int ret;
+ unsigned lextent_ind;
+ prof_stats_t stats;
+
+ if (!(config_prof && opt_prof && opt_prof_stats)) {
+ ret = ENOENT;
+ goto label_return;
+ }
+
+ READONLY();
+ MIB_UNSIGNED(lextent_ind, 3);
+ if (lextent_ind >= SC_NSIZES - SC_NBINS) {
+ ret = EINVAL;
+ goto label_return;
+ }
+ prof_stats_get_accum(tsd, (szind_t)(lextent_ind + SC_NBINS), &stats);
+ READ(stats, prof_stats_t);
+
+ ret = 0;
+label_return:
+ return ret;
+}
+
+static const ctl_named_node_t *
+prof_stats_lextents_i_index(tsdn_t *tsdn, const size_t *mib, size_t miblen,
+ size_t i) {
+ if (!(config_prof && opt_prof && opt_prof_stats)) {
+ return NULL;
+ }
+ if (i >= SC_NSIZES - SC_NBINS) {
+ return NULL;
+ }
+ return super_prof_stats_lextents_i_node;
+}
diff --git a/contrib/jemalloc/src/decay.c b/contrib/jemalloc/src/decay.c
new file mode 100644
index 000000000000..d801b2bc08ea
--- /dev/null
+++ b/contrib/jemalloc/src/decay.c
@@ -0,0 +1,295 @@
+#include "jemalloc/internal/jemalloc_preamble.h"
+#include "jemalloc/internal/jemalloc_internal_includes.h"
+
+#include "jemalloc/internal/decay.h"
+
+static const uint64_t h_steps[SMOOTHSTEP_NSTEPS] = {
+#define STEP(step, h, x, y) \
+ h,
+ SMOOTHSTEP
+#undef STEP
+};
+
+/*
+ * Generate a new deadline that is uniformly random within the next epoch after
+ * the current one.
+ */
+void
+decay_deadline_init(decay_t *decay) {
+ nstime_copy(&decay->deadline, &decay->epoch);
+ nstime_add(&decay->deadline, &decay->interval);
+ if (decay_ms_read(decay) > 0) {
+ nstime_t jitter;
+
+ nstime_init(&jitter, prng_range_u64(&decay->jitter_state,
+ nstime_ns(&decay->interval)));
+ nstime_add(&decay->deadline, &jitter);
+ }
+}
+
+void
+decay_reinit(decay_t *decay, nstime_t *cur_time, ssize_t decay_ms) {
+ atomic_store_zd(&decay->time_ms, decay_ms, ATOMIC_RELAXED);
+ if (decay_ms > 0) {
+ nstime_init(&decay->interval, (uint64_t)decay_ms *
+ KQU(1000000));
+ nstime_idivide(&decay->interval, SMOOTHSTEP_NSTEPS);
+ }
+
+ nstime_copy(&decay->epoch, cur_time);
+ decay->jitter_state = (uint64_t)(uintptr_t)decay;
+ decay_deadline_init(decay);
+ decay->nunpurged = 0;
+ memset(decay->backlog, 0, SMOOTHSTEP_NSTEPS * sizeof(size_t));
+}
+
+bool
+decay_init(decay_t *decay, nstime_t *cur_time, ssize_t decay_ms) {
+ if (config_debug) {
+ for (size_t i = 0; i < sizeof(decay_t); i++) {
+ assert(((char *)decay)[i] == 0);
+ }
+ decay->ceil_npages = 0;
+ }
+ if (malloc_mutex_init(&decay->mtx, "decay", WITNESS_RANK_DECAY,
+ malloc_mutex_rank_exclusive)) {
+ return true;
+ }
+ decay->purging = false;
+ decay_reinit(decay, cur_time, decay_ms);
+ return false;
+}
+
+bool
+decay_ms_valid(ssize_t decay_ms) {
+ if (decay_ms < -1) {
+ return false;
+ }
+ if (decay_ms == -1 || (uint64_t)decay_ms <= NSTIME_SEC_MAX *
+ KQU(1000)) {
+ return true;
+ }
+ return false;
+}
+
+static void
+decay_maybe_update_time(decay_t *decay, nstime_t *new_time) {
+ if (unlikely(!nstime_monotonic() && nstime_compare(&decay->epoch,
+ new_time) > 0)) {
+ /*
+ * Time went backwards. Move the epoch back in time and
+ * generate a new deadline, with the expectation that time
+ * typically flows forward for long enough periods of time that
+ * epochs complete. Unfortunately, this strategy is susceptible
+ * to clock jitter triggering premature epoch advances, but
+ * clock jitter estimation and compensation isn't feasible here
+ * because calls into this code are event-driven.
+ */
+ nstime_copy(&decay->epoch, new_time);
+ decay_deadline_init(decay);
+ } else {
+ /* Verify that time does not go backwards. */
+ assert(nstime_compare(&decay->epoch, new_time) <= 0);
+ }
+}
+
+static size_t
+decay_backlog_npages_limit(const decay_t *decay) {
+ /*
+ * For each element of decay_backlog, multiply by the corresponding
+ * fixed-point smoothstep decay factor. Sum the products, then divide
+ * to round down to the nearest whole number of pages.
+ */
+ uint64_t sum = 0;
+ for (unsigned i = 0; i < SMOOTHSTEP_NSTEPS; i++) {
+ sum += decay->backlog[i] * h_steps[i];
+ }
+ size_t npages_limit_backlog = (size_t)(sum >> SMOOTHSTEP_BFP);
+
+ return npages_limit_backlog;
+}
+
+/*
+ * Update backlog, assuming that 'nadvance_u64' time intervals have passed.
+ * Trailing 'nadvance_u64' records should be erased and 'current_npages' is
+ * placed as the newest record.
+ */
+static void
+decay_backlog_update(decay_t *decay, uint64_t nadvance_u64,
+ size_t current_npages) {
+ if (nadvance_u64 >= SMOOTHSTEP_NSTEPS) {
+ memset(decay->backlog, 0, (SMOOTHSTEP_NSTEPS-1) *
+ sizeof(size_t));
+ } else {
+ size_t nadvance_z = (size_t)nadvance_u64;
+
+ assert((uint64_t)nadvance_z == nadvance_u64);
+
+ memmove(decay->backlog, &decay->backlog[nadvance_z],
+ (SMOOTHSTEP_NSTEPS - nadvance_z) * sizeof(size_t));
+ if (nadvance_z > 1) {
+ memset(&decay->backlog[SMOOTHSTEP_NSTEPS -
+ nadvance_z], 0, (nadvance_z-1) * sizeof(size_t));
+ }
+ }
+
+ size_t npages_delta = (current_npages > decay->nunpurged) ?
+ current_npages - decay->nunpurged : 0;
+ decay->backlog[SMOOTHSTEP_NSTEPS-1] = npages_delta;
+
+ if (config_debug) {
+ if (current_npages > decay->ceil_npages) {
+ decay->ceil_npages = current_npages;
+ }
+ size_t npages_limit = decay_backlog_npages_limit(decay);
+ assert(decay->ceil_npages >= npages_limit);
+ if (decay->ceil_npages > npages_limit) {
+ decay->ceil_npages = npages_limit;
+ }
+ }
+}
+
+static inline bool
+decay_deadline_reached(const decay_t *decay, const nstime_t *time) {
+ return (nstime_compare(&decay->deadline, time) <= 0);
+}
+
+uint64_t
+decay_npages_purge_in(decay_t *decay, nstime_t *time, size_t npages_new) {
+ uint64_t decay_interval_ns = decay_epoch_duration_ns(decay);
+ size_t n_epoch = (size_t)(nstime_ns(time) / decay_interval_ns);
+
+ uint64_t npages_purge;
+ if (n_epoch >= SMOOTHSTEP_NSTEPS) {
+ npages_purge = npages_new;
+ } else {
+ uint64_t h_steps_max = h_steps[SMOOTHSTEP_NSTEPS - 1];
+ assert(h_steps_max >=
+ h_steps[SMOOTHSTEP_NSTEPS - 1 - n_epoch]);
+ npages_purge = npages_new * (h_steps_max -
+ h_steps[SMOOTHSTEP_NSTEPS - 1 - n_epoch]);
+ npages_purge >>= SMOOTHSTEP_BFP;
+ }
+ return npages_purge;
+}
+
+bool
+decay_maybe_advance_epoch(decay_t *decay, nstime_t *new_time,
+ size_t npages_current) {
+ /* Handle possible non-monotonicity of time. */
+ decay_maybe_update_time(decay, new_time);
+
+ if (!decay_deadline_reached(decay, new_time)) {
+ return false;
+ }
+ nstime_t delta;
+ nstime_copy(&delta, new_time);
+ nstime_subtract(&delta, &decay->epoch);
+
+ uint64_t nadvance_u64 = nstime_divide(&delta, &decay->interval);
+ assert(nadvance_u64 > 0);
+
+ /* Add nadvance_u64 decay intervals to epoch. */
+ nstime_copy(&delta, &decay->interval);
+ nstime_imultiply(&delta, nadvance_u64);
+ nstime_add(&decay->epoch, &delta);
+
+ /* Set a new deadline. */
+ decay_deadline_init(decay);
+
+ /* Update the backlog. */
+ decay_backlog_update(decay, nadvance_u64, npages_current);
+
+ decay->npages_limit = decay_backlog_npages_limit(decay);
+ decay->nunpurged = (decay->npages_limit > npages_current) ?
+ decay->npages_limit : npages_current;
+
+ return true;
+}
+
+/*
+ * Calculate how many pages should be purged after 'interval'.
+ *
+ * First, calculate how many pages should remain at the moment, then subtract
+ * the number of pages that should remain after 'interval'. The difference is
+ * how many pages should be purged until then.
+ *
+ * The number of pages that should remain at a specific moment is calculated
+ * like this: pages(now) = sum(backlog[i] * h_steps[i]). After 'interval'
+ * passes, backlog would shift 'interval' positions to the left and sigmoid
+ * curve would be applied starting with backlog[interval].
+ *
+ * The implementation doesn't directly map to the description, but it's
+ * essentially the same calculation, optimized to avoid iterating over
+ * [interval..SMOOTHSTEP_NSTEPS) twice.
+ */
+static inline size_t
+decay_npurge_after_interval(decay_t *decay, size_t interval) {
+ size_t i;
+ uint64_t sum = 0;
+ for (i = 0; i < interval; i++) {
+ sum += decay->backlog[i] * h_steps[i];
+ }
+ for (; i < SMOOTHSTEP_NSTEPS; i++) {
+ sum += decay->backlog[i] *
+ (h_steps[i] - h_steps[i - interval]);
+ }
+
+ return (size_t)(sum >> SMOOTHSTEP_BFP);
+}
+
+uint64_t decay_ns_until_purge(decay_t *decay, size_t npages_current,
+ uint64_t npages_threshold) {
+ if (!decay_gradually(decay)) {
+ return DECAY_UNBOUNDED_TIME_TO_PURGE;
+ }
+ uint64_t decay_interval_ns = decay_epoch_duration_ns(decay);
+ assert(decay_interval_ns > 0);
+ if (npages_current == 0) {
+ unsigned i;
+ for (i = 0; i < SMOOTHSTEP_NSTEPS; i++) {
+ if (decay->backlog[i] > 0) {
+ break;
+ }
+ }
+ if (i == SMOOTHSTEP_NSTEPS) {
+ /* No dirty pages recorded. Sleep indefinitely. */
+ return DECAY_UNBOUNDED_TIME_TO_PURGE;
+ }
+ }
+ if (npages_current <= npages_threshold) {
+ /* Use max interval. */
+ return decay_interval_ns * SMOOTHSTEP_NSTEPS;
+ }
+
+ /* Minimal 2 intervals to ensure reaching next epoch deadline. */
+ size_t lb = 2;
+ size_t ub = SMOOTHSTEP_NSTEPS;
+
+ size_t npurge_lb, npurge_ub;
+ npurge_lb = decay_npurge_after_interval(decay, lb);
+ if (npurge_lb > npages_threshold) {
+ return decay_interval_ns * lb;
+ }
+ npurge_ub = decay_npurge_after_interval(decay, ub);
+ if (npurge_ub < npages_threshold) {
+ return decay_interval_ns * ub;
+ }
+
+ unsigned n_search = 0;
+ size_t target, npurge;
+ while ((npurge_lb + npages_threshold < npurge_ub) && (lb + 2 < ub)) {
+ target = (lb + ub) / 2;
+ npurge = decay_npurge_after_interval(decay, target);
+ if (npurge > npages_threshold) {
+ ub = target;
+ npurge_ub = npurge;
+ } else {
+ lb = target;
+ npurge_lb = npurge;
+ }
+ assert(n_search < lg_floor(SMOOTHSTEP_NSTEPS) + 1);
+ ++n_search;
+ }
+ return decay_interval_ns * (ub + lb) / 2;
+}
diff --git a/contrib/jemalloc/src/ecache.c b/contrib/jemalloc/src/ecache.c
new file mode 100644
index 000000000000..a242227d32d6
--- /dev/null
+++ b/contrib/jemalloc/src/ecache.c
@@ -0,0 +1,35 @@
+#include "jemalloc/internal/jemalloc_preamble.h"
+#include "jemalloc/internal/jemalloc_internal_includes.h"
+
+#include "jemalloc/internal/san.h"
+
+bool
+ecache_init(tsdn_t *tsdn, ecache_t *ecache, extent_state_t state, unsigned ind,
+ bool delay_coalesce) {
+ if (malloc_mutex_init(&ecache->mtx, "extents", WITNESS_RANK_EXTENTS,
+ malloc_mutex_rank_exclusive)) {
+ return true;
+ }
+ ecache->state = state;
+ ecache->ind = ind;
+ ecache->delay_coalesce = delay_coalesce;
+ eset_init(&ecache->eset, state);
+ eset_init(&ecache->guarded_eset, state);
+
+ return false;
+}
+
+void
+ecache_prefork(tsdn_t *tsdn, ecache_t *ecache) {
+ malloc_mutex_prefork(tsdn, &ecache->mtx);
+}
+
+void
+ecache_postfork_parent(tsdn_t *tsdn, ecache_t *ecache) {
+ malloc_mutex_postfork_parent(tsdn, &ecache->mtx);
+}
+
+void
+ecache_postfork_child(tsdn_t *tsdn, ecache_t *ecache) {
+ malloc_mutex_postfork_child(tsdn, &ecache->mtx);
+}
diff --git a/contrib/jemalloc/src/edata.c b/contrib/jemalloc/src/edata.c
new file mode 100644
index 000000000000..82b6f5654b51
--- /dev/null
+++ b/contrib/jemalloc/src/edata.c
@@ -0,0 +1,6 @@
+#include "jemalloc/internal/jemalloc_preamble.h"
+#include "jemalloc/internal/jemalloc_internal_includes.h"
+
+ph_gen(, edata_avail, edata_t, avail_link,
+ edata_esnead_comp)
+ph_gen(, edata_heap, edata_t, heap_link, edata_snad_comp)
diff --git a/contrib/jemalloc/src/edata_cache.c b/contrib/jemalloc/src/edata_cache.c
new file mode 100644
index 000000000000..6bc1848cbcb8
--- /dev/null
+++ b/contrib/jemalloc/src/edata_cache.c
@@ -0,0 +1,154 @@
+#include "jemalloc/internal/jemalloc_preamble.h"
+#include "jemalloc/internal/jemalloc_internal_includes.h"
+
+bool
+edata_cache_init(edata_cache_t *edata_cache, base_t *base) {
+ edata_avail_new(&edata_cache->avail);
+ /*
+ * This is not strictly necessary, since the edata_cache_t is only
+ * created inside an arena, which is zeroed on creation. But this is
+ * handy as a safety measure.
+ */
+ atomic_store_zu(&edata_cache->count, 0, ATOMIC_RELAXED);
+ if (malloc_mutex_init(&edata_cache->mtx, "edata_cache",
+ WITNESS_RANK_EDATA_CACHE, malloc_mutex_rank_exclusive)) {
+ return true;
+ }
+ edata_cache->base = base;
+ return false;
+}
+
+edata_t *
+edata_cache_get(tsdn_t *tsdn, edata_cache_t *edata_cache) {
+ malloc_mutex_lock(tsdn, &edata_cache->mtx);
+ edata_t *edata = edata_avail_first(&edata_cache->avail);
+ if (edata == NULL) {
+ malloc_mutex_unlock(tsdn, &edata_cache->mtx);
+ return base_alloc_edata(tsdn, edata_cache->base);
+ }
+ edata_avail_remove(&edata_cache->avail, edata);
+ atomic_load_sub_store_zu(&edata_cache->count, 1);
+ malloc_mutex_unlock(tsdn, &edata_cache->mtx);
+ return edata;
+}
+
+void
+edata_cache_put(tsdn_t *tsdn, edata_cache_t *edata_cache, edata_t *edata) {
+ malloc_mutex_lock(tsdn, &edata_cache->mtx);
+ edata_avail_insert(&edata_cache->avail, edata);
+ atomic_load_add_store_zu(&edata_cache->count, 1);
+ malloc_mutex_unlock(tsdn, &edata_cache->mtx);
+}
+
+void
+edata_cache_prefork(tsdn_t *tsdn, edata_cache_t *edata_cache) {
+ malloc_mutex_prefork(tsdn, &edata_cache->mtx);
+}
+
+void
+edata_cache_postfork_parent(tsdn_t *tsdn, edata_cache_t *edata_cache) {
+ malloc_mutex_postfork_parent(tsdn, &edata_cache->mtx);
+}
+
+void
+edata_cache_postfork_child(tsdn_t *tsdn, edata_cache_t *edata_cache) {
+ malloc_mutex_postfork_child(tsdn, &edata_cache->mtx);
+}
+
+void
+edata_cache_fast_init(edata_cache_fast_t *ecs, edata_cache_t *fallback) {
+ edata_list_inactive_init(&ecs->list);
+ ecs->fallback = fallback;
+ ecs->disabled = false;
+}
+
+static void
+edata_cache_fast_try_fill_from_fallback(tsdn_t *tsdn,
+ edata_cache_fast_t *ecs) {
+ edata_t *edata;
+ malloc_mutex_lock(tsdn, &ecs->fallback->mtx);
+ for (int i = 0; i < EDATA_CACHE_FAST_FILL; i++) {
+ edata = edata_avail_remove_first(&ecs->fallback->avail);
+ if (edata == NULL) {
+ break;
+ }
+ edata_list_inactive_append(&ecs->list, edata);
+ atomic_load_sub_store_zu(&ecs->fallback->count, 1);
+ }
+ malloc_mutex_unlock(tsdn, &ecs->fallback->mtx);
+}
+
+edata_t *
+edata_cache_fast_get(tsdn_t *tsdn, edata_cache_fast_t *ecs) {
+ witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
+ WITNESS_RANK_EDATA_CACHE, 0);
+
+ if (ecs->disabled) {
+ assert(edata_list_inactive_first(&ecs->list) == NULL);
+ return edata_cache_get(tsdn, ecs->fallback);
+ }
+
+ edata_t *edata = edata_list_inactive_first(&ecs->list);
+ if (edata != NULL) {
+ edata_list_inactive_remove(&ecs->list, edata);
+ return edata;
+ }
+ /* Slow path; requires synchronization. */
+ edata_cache_fast_try_fill_from_fallback(tsdn, ecs);
+ edata = edata_list_inactive_first(&ecs->list);
+ if (edata != NULL) {
+ edata_list_inactive_remove(&ecs->list, edata);
+ } else {
+ /*
+ * Slowest path (fallback was also empty); allocate something
+ * new.
+ */
+ edata = base_alloc_edata(tsdn, ecs->fallback->base);
+ }
+ return edata;
+}
+
+static void
+edata_cache_fast_flush_all(tsdn_t *tsdn, edata_cache_fast_t *ecs) {
+ /*
+ * You could imagine smarter cache management policies (like
+ * only flushing down to some threshold in anticipation of
+ * future get requests). But just flushing everything provides
+ * a good opportunity to defrag too, and lets us share code between the
+ * flush and disable pathways.
+ */
+ edata_t *edata;
+ size_t nflushed = 0;
+ malloc_mutex_lock(tsdn, &ecs->fallback->mtx);
+ while ((edata = edata_list_inactive_first(&ecs->list)) != NULL) {
+ edata_list_inactive_remove(&ecs->list, edata);
+ edata_avail_insert(&ecs->fallback->avail, edata);
+ nflushed++;
+ }
+ atomic_load_add_store_zu(&ecs->fallback->count, nflushed);
+ malloc_mutex_unlock(tsdn, &ecs->fallback->mtx);
+}
+
+void
+edata_cache_fast_put(tsdn_t *tsdn, edata_cache_fast_t *ecs, edata_t *edata) {
+ witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
+ WITNESS_RANK_EDATA_CACHE, 0);
+
+ if (ecs->disabled) {
+ assert(edata_list_inactive_first(&ecs->list) == NULL);
+ edata_cache_put(tsdn, ecs->fallback, edata);
+ return;
+ }
+
+ /*
+ * Prepend rather than append, to do LIFO ordering in the hopes of some
+ * cache locality.
+ */
+ edata_list_inactive_prepend(&ecs->list, edata);
+}
+
+void
+edata_cache_fast_disable(tsdn_t *tsdn, edata_cache_fast_t *ecs) {
+ edata_cache_fast_flush_all(tsdn, ecs);
+ ecs->disabled = true;
+}
diff --git a/contrib/jemalloc/src/ehooks.c b/contrib/jemalloc/src/ehooks.c
new file mode 100644
index 000000000000..383e9de6a6b9
--- /dev/null
+++ b/contrib/jemalloc/src/ehooks.c
@@ -0,0 +1,275 @@
+#include "jemalloc/internal/jemalloc_preamble.h"
+#include "jemalloc/internal/jemalloc_internal_includes.h"
+
+#include "jemalloc/internal/ehooks.h"
+#include "jemalloc/internal/extent_mmap.h"
+
+void
+ehooks_init(ehooks_t *ehooks, extent_hooks_t *extent_hooks, unsigned ind) {
+ /* All other hooks are optional; this one is not. */
+ assert(extent_hooks->alloc != NULL);
+ ehooks->ind = ind;
+ ehooks_set_extent_hooks_ptr(ehooks, extent_hooks);
+}
+
+/*
+ * If the caller specifies (!*zero), it is still possible to receive zeroed
+ * memory, in which case *zero is toggled to true. arena_extent_alloc() takes
+ * advantage of this to avoid demanding zeroed extents, but taking advantage of
+ * them if they are returned.
+ */
+static void *
+extent_alloc_core(tsdn_t *tsdn, arena_t *arena, void *new_addr, size_t size,
+ size_t alignment, bool *zero, bool *commit, dss_prec_t dss_prec) {
+ void *ret;
+
+ assert(size != 0);
+ assert(alignment != 0);
+
+ /* "primary" dss. */
+ if (have_dss && dss_prec == dss_prec_primary && (ret =
+ extent_alloc_dss(tsdn, arena, new_addr, size, alignment, zero,
+ commit)) != NULL) {
+ return ret;
+ }
+ /* mmap. */
+ if ((ret = extent_alloc_mmap(new_addr, size, alignment, zero, commit))
+ != NULL) {
+ return ret;
+ }
+ /* "secondary" dss. */
+ if (have_dss && dss_prec == dss_prec_secondary && (ret =
+ extent_alloc_dss(tsdn, arena, new_addr, size, alignment, zero,
+ commit)) != NULL) {
+ return ret;
+ }
+
+ /* All strategies for allocation failed. */
+ return NULL;
+}
+
+void *
+ehooks_default_alloc_impl(tsdn_t *tsdn, void *new_addr, size_t size,
+ size_t alignment, bool *zero, bool *commit, unsigned arena_ind) {
+ arena_t *arena = arena_get(tsdn, arena_ind, false);
+ /* NULL arena indicates arena_create. */
+ assert(arena != NULL || alignment == HUGEPAGE);
+ dss_prec_t dss = (arena == NULL) ? dss_prec_disabled :
+ (dss_prec_t)atomic_load_u(&arena->dss_prec, ATOMIC_RELAXED);
+ void *ret = extent_alloc_core(tsdn, arena, new_addr, size, alignment,
+ zero, commit, dss);
+ if (have_madvise_huge && ret) {
+ pages_set_thp_state(ret, size);
+ }
+ return ret;
+}
+
+static void *
+ehooks_default_alloc(extent_hooks_t *extent_hooks, void *new_addr, size_t size,
+ size_t alignment, bool *zero, bool *commit, unsigned arena_ind) {
+ return ehooks_default_alloc_impl(tsdn_fetch(), new_addr, size,
+ ALIGNMENT_CEILING(alignment, PAGE), zero, commit, arena_ind);
+}
+
+bool
+ehooks_default_dalloc_impl(void *addr, size_t size) {
+ if (!have_dss || !extent_in_dss(addr)) {
+ return extent_dalloc_mmap(addr, size);
+ }
+ return true;
+}
+
+static bool
+ehooks_default_dalloc(extent_hooks_t *extent_hooks, void *addr, size_t size,
+ bool committed, unsigned arena_ind) {
+ return ehooks_default_dalloc_impl(addr, size);
+}
+
+void
+ehooks_default_destroy_impl(void *addr, size_t size) {
+ if (!have_dss || !extent_in_dss(addr)) {
+ pages_unmap(addr, size);
+ }
+}
+
+static void
+ehooks_default_destroy(extent_hooks_t *extent_hooks, void *addr, size_t size,
+ bool committed, unsigned arena_ind) {
+ ehooks_default_destroy_impl(addr, size);
+}
+
+bool
+ehooks_default_commit_impl(void *addr, size_t offset, size_t length) {
+ return pages_commit((void *)((uintptr_t)addr + (uintptr_t)offset),
+ length);
+}
+
+static bool
+ehooks_default_commit(extent_hooks_t *extent_hooks, void *addr, size_t size,
+ size_t offset, size_t length, unsigned arena_ind) {
+ return ehooks_default_commit_impl(addr, offset, length);
+}
+
+bool
+ehooks_default_decommit_impl(void *addr, size_t offset, size_t length) {
+ return pages_decommit((void *)((uintptr_t)addr + (uintptr_t)offset),
+ length);
+}
+
+static bool
+ehooks_default_decommit(extent_hooks_t *extent_hooks, void *addr, size_t size,
+ size_t offset, size_t length, unsigned arena_ind) {
+ return ehooks_default_decommit_impl(addr, offset, length);
+}
+
+#ifdef PAGES_CAN_PURGE_LAZY
+bool
+ehooks_default_purge_lazy_impl(void *addr, size_t offset, size_t length) {
+ return pages_purge_lazy((void *)((uintptr_t)addr + (uintptr_t)offset),
+ length);
+}
+
+static bool
+ehooks_default_purge_lazy(extent_hooks_t *extent_hooks, void *addr, size_t size,
+ size_t offset, size_t length, unsigned arena_ind) {
+ assert(addr != NULL);
+ assert((offset & PAGE_MASK) == 0);
+ assert(length != 0);
+ assert((length & PAGE_MASK) == 0);
+ return ehooks_default_purge_lazy_impl(addr, offset, length);
+}
+#endif
+
+#ifdef PAGES_CAN_PURGE_FORCED
+bool
+ehooks_default_purge_forced_impl(void *addr, size_t offset, size_t length) {
+ return pages_purge_forced((void *)((uintptr_t)addr +
+ (uintptr_t)offset), length);
+}
+
+static bool
+ehooks_default_purge_forced(extent_hooks_t *extent_hooks, void *addr,
+ size_t size, size_t offset, size_t length, unsigned arena_ind) {
+ assert(addr != NULL);
+ assert((offset & PAGE_MASK) == 0);
+ assert(length != 0);
+ assert((length & PAGE_MASK) == 0);
+ return ehooks_default_purge_forced_impl(addr, offset, length);
+}
+#endif
+
+bool
+ehooks_default_split_impl() {
+ if (!maps_coalesce) {
+ /*
+ * Without retain, only whole regions can be purged (required by
+ * MEM_RELEASE on Windows) -- therefore disallow splitting. See
+ * comments in extent_head_no_merge().
+ */
+ return !opt_retain;
+ }
+
+ return false;
+}
+
+static bool
+ehooks_default_split(extent_hooks_t *extent_hooks, void *addr, size_t size,
+ size_t size_a, size_t size_b, bool committed, unsigned arena_ind) {
+ return ehooks_default_split_impl();
+}
+
+bool
+ehooks_default_merge_impl(tsdn_t *tsdn, void *addr_a, void *addr_b) {
+ assert(addr_a < addr_b);
+ /*
+ * For non-DSS cases --
+ * a) W/o maps_coalesce, merge is not always allowed (Windows):
+ * 1) w/o retain, never merge (first branch below).
+ * 2) with retain, only merge extents from the same VirtualAlloc
+ * region (in which case MEM_DECOMMIT is utilized for purging).
+ *
+ * b) With maps_coalesce, it's always possible to merge.
+ * 1) w/o retain, always allow merge (only about dirty / muzzy).
+ * 2) with retain, to preserve the SN / first-fit, merge is still
+ * disallowed if b is a head extent, i.e. no merging across
+ * different mmap regions.
+ *
+ * a2) and b2) are implemented in emap_try_acquire_edata_neighbor, and
+ * sanity checked in the second branch below.
+ */
+ if (!maps_coalesce && !opt_retain) {
+ return true;
+ }
+ if (config_debug) {
+ edata_t *a = emap_edata_lookup(tsdn, &arena_emap_global,
+ addr_a);
+ bool head_a = edata_is_head_get(a);
+ edata_t *b = emap_edata_lookup(tsdn, &arena_emap_global,
+ addr_b);
+ bool head_b = edata_is_head_get(b);
+ emap_assert_mapped(tsdn, &arena_emap_global, a);
+ emap_assert_mapped(tsdn, &arena_emap_global, b);
+ assert(extent_neighbor_head_state_mergeable(head_a, head_b,
+ /* forward */ true));
+ }
+ if (have_dss && !extent_dss_mergeable(addr_a, addr_b)) {
+ return true;
+ }
+
+ return false;
+}
+
+bool
+ehooks_default_merge(extent_hooks_t *extent_hooks, void *addr_a, size_t size_a,
+ void *addr_b, size_t size_b, bool committed, unsigned arena_ind) {
+ tsdn_t *tsdn = tsdn_fetch();
+
+ return ehooks_default_merge_impl(tsdn, addr_a, addr_b);
+}
+
+void
+ehooks_default_zero_impl(void *addr, size_t size) {
+ /*
+ * By default, we try to zero out memory using OS-provided demand-zeroed
+ * pages. If the user has specifically requested hugepages, though, we
+ * don't want to purge in the middle of a hugepage (which would break it
+ * up), so we act conservatively and use memset.
+ */
+ bool needs_memset = true;
+ if (opt_thp != thp_mode_always) {
+ needs_memset = pages_purge_forced(addr, size);
+ }
+ if (needs_memset) {
+ memset(addr, 0, size);
+ }
+}
+
+void
+ehooks_default_guard_impl(void *guard1, void *guard2) {
+ pages_mark_guards(guard1, guard2);
+}
+
+void
+ehooks_default_unguard_impl(void *guard1, void *guard2) {
+ pages_unmark_guards(guard1, guard2);
+}
+
+const extent_hooks_t ehooks_default_extent_hooks = {
+ ehooks_default_alloc,
+ ehooks_default_dalloc,
+ ehooks_default_destroy,
+ ehooks_default_commit,
+ ehooks_default_decommit,
+#ifdef PAGES_CAN_PURGE_LAZY
+ ehooks_default_purge_lazy,
+#else
+ NULL,
+#endif
+#ifdef PAGES_CAN_PURGE_FORCED
+ ehooks_default_purge_forced,
+#else
+ NULL,
+#endif
+ ehooks_default_split,
+ ehooks_default_merge
+};
diff --git a/contrib/jemalloc/src/emap.c b/contrib/jemalloc/src/emap.c
new file mode 100644
index 000000000000..9cc95a724a9b
--- /dev/null
+++ b/contrib/jemalloc/src/emap.c
@@ -0,0 +1,386 @@
+#include "jemalloc/internal/jemalloc_preamble.h"
+#include "jemalloc/internal/jemalloc_internal_includes.h"
+
+#include "jemalloc/internal/emap.h"
+
+enum emap_lock_result_e {
+ emap_lock_result_success,
+ emap_lock_result_failure,
+ emap_lock_result_no_extent
+};
+typedef enum emap_lock_result_e emap_lock_result_t;
+
+bool
+emap_init(emap_t *emap, base_t *base, bool zeroed) {
+ return rtree_new(&emap->rtree, base, zeroed);
+}
+
+void
+emap_update_edata_state(tsdn_t *tsdn, emap_t *emap, edata_t *edata,
+ extent_state_t state) {
+ witness_assert_positive_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
+ WITNESS_RANK_CORE);
+
+ edata_state_set(edata, state);
+
+ EMAP_DECLARE_RTREE_CTX;
+ rtree_leaf_elm_t *elm1 = rtree_leaf_elm_lookup(tsdn, &emap->rtree,
+ rtree_ctx, (uintptr_t)edata_base_get(edata), /* dependent */ true,
+ /* init_missing */ false);
+ assert(elm1 != NULL);
+ rtree_leaf_elm_t *elm2 = edata_size_get(edata) == PAGE ? NULL :
+ rtree_leaf_elm_lookup(tsdn, &emap->rtree, rtree_ctx,
+ (uintptr_t)edata_last_get(edata), /* dependent */ true,
+ /* init_missing */ false);
+
+ rtree_leaf_elm_state_update(tsdn, &emap->rtree, elm1, elm2, state);
+
+ emap_assert_mapped(tsdn, emap, edata);
+}
+
+static inline edata_t *
+emap_try_acquire_edata_neighbor_impl(tsdn_t *tsdn, emap_t *emap, edata_t *edata,
+ extent_pai_t pai, extent_state_t expected_state, bool forward,
+ bool expanding) {
+ witness_assert_positive_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
+ WITNESS_RANK_CORE);
+ assert(!edata_guarded_get(edata));
+ assert(!expanding || forward);
+ assert(!edata_state_in_transition(expected_state));
+ assert(expected_state == extent_state_dirty ||
+ expected_state == extent_state_muzzy ||
+ expected_state == extent_state_retained);
+
+ void *neighbor_addr = forward ? edata_past_get(edata) :
+ edata_before_get(edata);
+ /*
+ * This is subtle; the rtree code asserts that its input pointer is
+ * non-NULL, and this is a useful thing to check. But it's possible
+ * that edata corresponds to an address of (void *)PAGE (in practice,
+ * this has only been observed on FreeBSD when address-space
+ * randomization is on, but it could in principle happen anywhere). In
+ * this case, edata_before_get(edata) is NULL, triggering the assert.
+ */
+ if (neighbor_addr == NULL) {
+ return NULL;
+ }
+
+ EMAP_DECLARE_RTREE_CTX;
+ rtree_leaf_elm_t *elm = rtree_leaf_elm_lookup(tsdn, &emap->rtree,
+ rtree_ctx, (uintptr_t)neighbor_addr, /* dependent*/ false,
+ /* init_missing */ false);
+ if (elm == NULL) {
+ return NULL;
+ }
+
+ rtree_contents_t neighbor_contents = rtree_leaf_elm_read(tsdn,
+ &emap->rtree, elm, /* dependent */ true);
+ if (!extent_can_acquire_neighbor(edata, neighbor_contents, pai,
+ expected_state, forward, expanding)) {
+ return NULL;
+ }
+
+ /* From this point, the neighbor edata can be safely acquired. */
+ edata_t *neighbor = neighbor_contents.edata;
+ assert(edata_state_get(neighbor) == expected_state);
+ emap_update_edata_state(tsdn, emap, neighbor, extent_state_merging);
+ if (expanding) {
+ extent_assert_can_expand(edata, neighbor);
+ } else {
+ extent_assert_can_coalesce(edata, neighbor);
+ }
+
+ return neighbor;
+}
+
+edata_t *
+emap_try_acquire_edata_neighbor(tsdn_t *tsdn, emap_t *emap, edata_t *edata,
+ extent_pai_t pai, extent_state_t expected_state, bool forward) {
+ return emap_try_acquire_edata_neighbor_impl(tsdn, emap, edata, pai,
+ expected_state, forward, /* expand */ false);
+}
+
+edata_t *
+emap_try_acquire_edata_neighbor_expand(tsdn_t *tsdn, emap_t *emap,
+ edata_t *edata, extent_pai_t pai, extent_state_t expected_state) {
+ /* Try expanding forward. */
+ return emap_try_acquire_edata_neighbor_impl(tsdn, emap, edata, pai,
+ expected_state, /* forward */ true, /* expand */ true);
+}
+
+void
+emap_release_edata(tsdn_t *tsdn, emap_t *emap, edata_t *edata,
+ extent_state_t new_state) {
+ assert(emap_edata_in_transition(tsdn, emap, edata));
+ assert(emap_edata_is_acquired(tsdn, emap, edata));
+
+ emap_update_edata_state(tsdn, emap, edata, new_state);
+}
+
+static bool
+emap_rtree_leaf_elms_lookup(tsdn_t *tsdn, emap_t *emap, rtree_ctx_t *rtree_ctx,
+ const edata_t *edata, bool dependent, bool init_missing,
+ rtree_leaf_elm_t **r_elm_a, rtree_leaf_elm_t **r_elm_b) {
+ *r_elm_a = rtree_leaf_elm_lookup(tsdn, &emap->rtree, rtree_ctx,
+ (uintptr_t)edata_base_get(edata), dependent, init_missing);
+ if (!dependent && *r_elm_a == NULL) {
+ return true;
+ }
+ assert(*r_elm_a != NULL);
+
+ *r_elm_b = rtree_leaf_elm_lookup(tsdn, &emap->rtree, rtree_ctx,
+ (uintptr_t)edata_last_get(edata), dependent, init_missing);
+ if (!dependent && *r_elm_b == NULL) {
+ return true;
+ }
+ assert(*r_elm_b != NULL);
+
+ return false;
+}
+
+static void
+emap_rtree_write_acquired(tsdn_t *tsdn, emap_t *emap, rtree_leaf_elm_t *elm_a,
+ rtree_leaf_elm_t *elm_b, edata_t *edata, szind_t szind, bool slab) {
+ rtree_contents_t contents;
+ contents.edata = edata;
+ contents.metadata.szind = szind;
+ contents.metadata.slab = slab;
+ contents.metadata.is_head = (edata == NULL) ? false :
+ edata_is_head_get(edata);
+ contents.metadata.state = (edata == NULL) ? 0 : edata_state_get(edata);
+ rtree_leaf_elm_write(tsdn, &emap->rtree, elm_a, contents);
+ if (elm_b != NULL) {
+ rtree_leaf_elm_write(tsdn, &emap->rtree, elm_b, contents);
+ }
+}
+
+bool
+emap_register_boundary(tsdn_t *tsdn, emap_t *emap, edata_t *edata,
+ szind_t szind, bool slab) {
+ assert(edata_state_get(edata) == extent_state_active);
+ EMAP_DECLARE_RTREE_CTX;
+
+ rtree_leaf_elm_t *elm_a, *elm_b;
+ bool err = emap_rtree_leaf_elms_lookup(tsdn, emap, rtree_ctx, edata,
+ false, true, &elm_a, &elm_b);
+ if (err) {
+ return true;
+ }
+ assert(rtree_leaf_elm_read(tsdn, &emap->rtree, elm_a,
+ /* dependent */ false).edata == NULL);
+ assert(rtree_leaf_elm_read(tsdn, &emap->rtree, elm_b,
+ /* dependent */ false).edata == NULL);
+ emap_rtree_write_acquired(tsdn, emap, elm_a, elm_b, edata, szind, slab);
+ return false;
+}
+
+/* Invoked *after* emap_register_boundary. */
+void
+emap_register_interior(tsdn_t *tsdn, emap_t *emap, edata_t *edata,
+ szind_t szind) {
+ EMAP_DECLARE_RTREE_CTX;
+
+ assert(edata_slab_get(edata));
+ assert(edata_state_get(edata) == extent_state_active);
+
+ if (config_debug) {
+ /* Making sure the boundary is registered already. */
+ rtree_leaf_elm_t *elm_a, *elm_b;
+ bool err = emap_rtree_leaf_elms_lookup(tsdn, emap, rtree_ctx,
+ edata, /* dependent */ true, /* init_missing */ false,
+ &elm_a, &elm_b);
+ assert(!err);
+ rtree_contents_t contents_a, contents_b;
+ contents_a = rtree_leaf_elm_read(tsdn, &emap->rtree, elm_a,
+ /* dependent */ true);
+ contents_b = rtree_leaf_elm_read(tsdn, &emap->rtree, elm_b,
+ /* dependent */ true);
+ assert(contents_a.edata == edata && contents_b.edata == edata);
+ assert(contents_a.metadata.slab && contents_b.metadata.slab);
+ }
+
+ rtree_contents_t contents;
+ contents.edata = edata;
+ contents.metadata.szind = szind;
+ contents.metadata.slab = true;
+ contents.metadata.state = extent_state_active;
+ contents.metadata.is_head = false; /* Not allowed to access. */
+
+ assert(edata_size_get(edata) > (2 << LG_PAGE));
+ rtree_write_range(tsdn, &emap->rtree, rtree_ctx,
+ (uintptr_t)edata_base_get(edata) + PAGE,
+ (uintptr_t)edata_last_get(edata) - PAGE, contents);
+}
+
+void
+emap_deregister_boundary(tsdn_t *tsdn, emap_t *emap, edata_t *edata) {
+ /*
+ * The edata must be either in an acquired state, or protected by state
+ * based locks.
+ */
+ if (!emap_edata_is_acquired(tsdn, emap, edata)) {
+ witness_assert_positive_depth_to_rank(
+ tsdn_witness_tsdp_get(tsdn), WITNESS_RANK_CORE);
+ }
+
+ EMAP_DECLARE_RTREE_CTX;
+ rtree_leaf_elm_t *elm_a, *elm_b;
+
+ emap_rtree_leaf_elms_lookup(tsdn, emap, rtree_ctx, edata,
+ true, false, &elm_a, &elm_b);
+ emap_rtree_write_acquired(tsdn, emap, elm_a, elm_b, NULL, SC_NSIZES,
+ false);
+}
+
+void
+emap_deregister_interior(tsdn_t *tsdn, emap_t *emap, edata_t *edata) {
+ EMAP_DECLARE_RTREE_CTX;
+
+ assert(edata_slab_get(edata));
+ if (edata_size_get(edata) > (2 << LG_PAGE)) {
+ rtree_clear_range(tsdn, &emap->rtree, rtree_ctx,
+ (uintptr_t)edata_base_get(edata) + PAGE,
+ (uintptr_t)edata_last_get(edata) - PAGE);
+ }
+}
+
+void
+emap_remap(tsdn_t *tsdn, emap_t *emap, edata_t *edata, szind_t szind,
+ bool slab) {
+ EMAP_DECLARE_RTREE_CTX;
+
+ if (szind != SC_NSIZES) {
+ rtree_contents_t contents;
+ contents.edata = edata;
+ contents.metadata.szind = szind;
+ contents.metadata.slab = slab;
+ contents.metadata.is_head = edata_is_head_get(edata);
+ contents.metadata.state = edata_state_get(edata);
+
+ rtree_write(tsdn, &emap->rtree, rtree_ctx,
+ (uintptr_t)edata_addr_get(edata), contents);
+ /*
+ * Recall that this is called only for active->inactive and
+ * inactive->active transitions (since only active extents have
+ * meaningful values for szind and slab). Active, non-slab
+ * extents only need to handle lookups at their head (on
+ * deallocation), so we don't bother filling in the end
+ * boundary.
+ *
+ * For slab extents, we do the end-mapping change. This still
+ * leaves the interior unmodified; an emap_register_interior
+ * call is coming in those cases, though.
+ */
+ if (slab && edata_size_get(edata) > PAGE) {
+ uintptr_t key = (uintptr_t)edata_past_get(edata)
+ - (uintptr_t)PAGE;
+ rtree_write(tsdn, &emap->rtree, rtree_ctx, key,
+ contents);
+ }
+ }
+}
+
+bool
+emap_split_prepare(tsdn_t *tsdn, emap_t *emap, emap_prepare_t *prepare,
+ edata_t *edata, size_t size_a, edata_t *trail, size_t size_b) {
+ EMAP_DECLARE_RTREE_CTX;
+
+ /*
+ * We use incorrect constants for things like arena ind, zero, ranged,
+ * and commit state, and head status. This is a fake edata_t, used to
+ * facilitate a lookup.
+ */
+ edata_t lead = {0};
+ edata_init(&lead, 0U, edata_addr_get(edata), size_a, false, 0, 0,
+ extent_state_active, false, false, EXTENT_PAI_PAC, EXTENT_NOT_HEAD);
+
+ emap_rtree_leaf_elms_lookup(tsdn, emap, rtree_ctx, &lead, false, true,
+ &prepare->lead_elm_a, &prepare->lead_elm_b);
+ emap_rtree_leaf_elms_lookup(tsdn, emap, rtree_ctx, trail, false, true,
+ &prepare->trail_elm_a, &prepare->trail_elm_b);
+
+ if (prepare->lead_elm_a == NULL || prepare->lead_elm_b == NULL
+ || prepare->trail_elm_a == NULL || prepare->trail_elm_b == NULL) {
+ return true;
+ }
+ return false;
+}
+
+void
+emap_split_commit(tsdn_t *tsdn, emap_t *emap, emap_prepare_t *prepare,
+ edata_t *lead, size_t size_a, edata_t *trail, size_t size_b) {
+ /*
+ * We should think about not writing to the lead leaf element. We can
+ * get into situations where a racing realloc-like call can disagree
+ * with a size lookup request. I think it's fine to declare that these
+ * situations are race bugs, but there's an argument to be made that for
+ * things like xallocx, a size lookup call should return either the old
+ * size or the new size, but not anything else.
+ */
+ emap_rtree_write_acquired(tsdn, emap, prepare->lead_elm_a,
+ prepare->lead_elm_b, lead, SC_NSIZES, /* slab */ false);
+ emap_rtree_write_acquired(tsdn, emap, prepare->trail_elm_a,
+ prepare->trail_elm_b, trail, SC_NSIZES, /* slab */ false);
+}
+
+void
+emap_merge_prepare(tsdn_t *tsdn, emap_t *emap, emap_prepare_t *prepare,
+ edata_t *lead, edata_t *trail) {
+ EMAP_DECLARE_RTREE_CTX;
+ emap_rtree_leaf_elms_lookup(tsdn, emap, rtree_ctx, lead, true, false,
+ &prepare->lead_elm_a, &prepare->lead_elm_b);
+ emap_rtree_leaf_elms_lookup(tsdn, emap, rtree_ctx, trail, true, false,
+ &prepare->trail_elm_a, &prepare->trail_elm_b);
+}
+
+void
+emap_merge_commit(tsdn_t *tsdn, emap_t *emap, emap_prepare_t *prepare,
+ edata_t *lead, edata_t *trail) {
+ rtree_contents_t clear_contents;
+ clear_contents.edata = NULL;
+ clear_contents.metadata.szind = SC_NSIZES;
+ clear_contents.metadata.slab = false;
+ clear_contents.metadata.is_head = false;
+ clear_contents.metadata.state = (extent_state_t)0;
+
+ if (prepare->lead_elm_b != NULL) {
+ rtree_leaf_elm_write(tsdn, &emap->rtree,
+ prepare->lead_elm_b, clear_contents);
+ }
+
+ rtree_leaf_elm_t *merged_b;
+ if (prepare->trail_elm_b != NULL) {
+ rtree_leaf_elm_write(tsdn, &emap->rtree,
+ prepare->trail_elm_a, clear_contents);
+ merged_b = prepare->trail_elm_b;
+ } else {
+ merged_b = prepare->trail_elm_a;
+ }
+
+ emap_rtree_write_acquired(tsdn, emap, prepare->lead_elm_a, merged_b,
+ lead, SC_NSIZES, false);
+}
+
+void
+emap_do_assert_mapped(tsdn_t *tsdn, emap_t *emap, edata_t *edata) {
+ EMAP_DECLARE_RTREE_CTX;
+
+ rtree_contents_t contents = rtree_read(tsdn, &emap->rtree, rtree_ctx,
+ (uintptr_t)edata_base_get(edata));
+ assert(contents.edata == edata);
+ assert(contents.metadata.is_head == edata_is_head_get(edata));
+ assert(contents.metadata.state == edata_state_get(edata));
+}
+
+void
+emap_do_assert_not_mapped(tsdn_t *tsdn, emap_t *emap, edata_t *edata) {
+ emap_full_alloc_ctx_t context1 = {0};
+ emap_full_alloc_ctx_try_lookup(tsdn, emap, edata_base_get(edata),
+ &context1);
+ assert(context1.edata == NULL);
+
+ emap_full_alloc_ctx_t context2 = {0};
+ emap_full_alloc_ctx_try_lookup(tsdn, emap, edata_last_get(edata),
+ &context2);
+ assert(context2.edata == NULL);
+}
diff --git a/contrib/jemalloc/src/eset.c b/contrib/jemalloc/src/eset.c
new file mode 100644
index 000000000000..6f8f335e198b
--- /dev/null
+++ b/contrib/jemalloc/src/eset.c
@@ -0,0 +1,282 @@
+#include "jemalloc/internal/jemalloc_preamble.h"
+#include "jemalloc/internal/jemalloc_internal_includes.h"
+
+#include "jemalloc/internal/eset.h"
+
+#define ESET_NPSIZES (SC_NPSIZES + 1)
+
+static void
+eset_bin_init(eset_bin_t *bin) {
+ edata_heap_new(&bin->heap);
+ /*
+ * heap_min doesn't need initialization; it gets filled in when the bin
+ * goes from non-empty to empty.
+ */
+}
+
+static void
+eset_bin_stats_init(eset_bin_stats_t *bin_stats) {
+ atomic_store_zu(&bin_stats->nextents, 0, ATOMIC_RELAXED);
+ atomic_store_zu(&bin_stats->nbytes, 0, ATOMIC_RELAXED);
+}
+
+void
+eset_init(eset_t *eset, extent_state_t state) {
+ for (unsigned i = 0; i < ESET_NPSIZES; i++) {
+ eset_bin_init(&eset->bins[i]);
+ eset_bin_stats_init(&eset->bin_stats[i]);
+ }
+ fb_init(eset->bitmap, ESET_NPSIZES);
+ edata_list_inactive_init(&eset->lru);
+ eset->state = state;
+}
+
+size_t
+eset_npages_get(eset_t *eset) {
+ return atomic_load_zu(&eset->npages, ATOMIC_RELAXED);
+}
+
+size_t
+eset_nextents_get(eset_t *eset, pszind_t pind) {
+ return atomic_load_zu(&eset->bin_stats[pind].nextents, ATOMIC_RELAXED);
+}
+
+size_t
+eset_nbytes_get(eset_t *eset, pszind_t pind) {
+ return atomic_load_zu(&eset->bin_stats[pind].nbytes, ATOMIC_RELAXED);
+}
+
+static void
+eset_stats_add(eset_t *eset, pszind_t pind, size_t sz) {
+ size_t cur = atomic_load_zu(&eset->bin_stats[pind].nextents,
+ ATOMIC_RELAXED);
+ atomic_store_zu(&eset->bin_stats[pind].nextents, cur + 1,
+ ATOMIC_RELAXED);
+ cur = atomic_load_zu(&eset->bin_stats[pind].nbytes, ATOMIC_RELAXED);
+ atomic_store_zu(&eset->bin_stats[pind].nbytes, cur + sz,
+ ATOMIC_RELAXED);
+}
+
+static void
+eset_stats_sub(eset_t *eset, pszind_t pind, size_t sz) {
+ size_t cur = atomic_load_zu(&eset->bin_stats[pind].nextents,
+ ATOMIC_RELAXED);
+ atomic_store_zu(&eset->bin_stats[pind].nextents, cur - 1,
+ ATOMIC_RELAXED);
+ cur = atomic_load_zu(&eset->bin_stats[pind].nbytes, ATOMIC_RELAXED);
+ atomic_store_zu(&eset->bin_stats[pind].nbytes, cur - sz,
+ ATOMIC_RELAXED);
+}
+
+void
+eset_insert(eset_t *eset, edata_t *edata) {
+ assert(edata_state_get(edata) == eset->state);
+
+ size_t size = edata_size_get(edata);
+ size_t psz = sz_psz_quantize_floor(size);
+ pszind_t pind = sz_psz2ind(psz);
+
+ edata_cmp_summary_t edata_cmp_summary = edata_cmp_summary_get(edata);
+ if (edata_heap_empty(&eset->bins[pind].heap)) {
+ fb_set(eset->bitmap, ESET_NPSIZES, (size_t)pind);
+ /* Only element is automatically the min element. */
+ eset->bins[pind].heap_min = edata_cmp_summary;
+ } else {
+ /*
+ * There's already a min element; update the summary if we're
+ * about to insert a lower one.
+ */
+ if (edata_cmp_summary_comp(edata_cmp_summary,
+ eset->bins[pind].heap_min) < 0) {
+ eset->bins[pind].heap_min = edata_cmp_summary;
+ }
+ }
+ edata_heap_insert(&eset->bins[pind].heap, edata);
+
+ if (config_stats) {
+ eset_stats_add(eset, pind, size);
+ }
+
+ edata_list_inactive_append(&eset->lru, edata);
+ size_t npages = size >> LG_PAGE;
+ /*
+ * All modifications to npages hold the mutex (as asserted above), so we
+ * don't need an atomic fetch-add; we can get by with a load followed by
+ * a store.
+ */
+ size_t cur_eset_npages =
+ atomic_load_zu(&eset->npages, ATOMIC_RELAXED);
+ atomic_store_zu(&eset->npages, cur_eset_npages + npages,
+ ATOMIC_RELAXED);
+}
+
+void
+eset_remove(eset_t *eset, edata_t *edata) {
+ assert(edata_state_get(edata) == eset->state ||
+ edata_state_in_transition(edata_state_get(edata)));
+
+ size_t size = edata_size_get(edata);
+ size_t psz = sz_psz_quantize_floor(size);
+ pszind_t pind = sz_psz2ind(psz);
+ if (config_stats) {
+ eset_stats_sub(eset, pind, size);
+ }
+
+ edata_cmp_summary_t edata_cmp_summary = edata_cmp_summary_get(edata);
+ edata_heap_remove(&eset->bins[pind].heap, edata);
+ if (edata_heap_empty(&eset->bins[pind].heap)) {
+ fb_unset(eset->bitmap, ESET_NPSIZES, (size_t)pind);
+ } else {
+ /*
+ * This is a little weird; we compare if the summaries are
+ * equal, rather than if the edata we removed was the heap
+ * minimum. The reason why is that getting the heap minimum
+ * can cause a pairing heap merge operation. We can avoid this
+ * if we only update the min if it's changed, in which case the
+ * summaries of the removed element and the min element should
+ * compare equal.
+ */
+ if (edata_cmp_summary_comp(edata_cmp_summary,
+ eset->bins[pind].heap_min) == 0) {
+ eset->bins[pind].heap_min = edata_cmp_summary_get(
+ edata_heap_first(&eset->bins[pind].heap));
+ }
+ }
+ edata_list_inactive_remove(&eset->lru, edata);
+ size_t npages = size >> LG_PAGE;
+ /*
+ * As in eset_insert, we hold eset->mtx and so don't need atomic
+ * operations for updating eset->npages.
+ */
+ size_t cur_extents_npages =
+ atomic_load_zu(&eset->npages, ATOMIC_RELAXED);
+ assert(cur_extents_npages >= npages);
+ atomic_store_zu(&eset->npages,
+ cur_extents_npages - (size >> LG_PAGE), ATOMIC_RELAXED);
+}
+
+/*
+ * Find an extent with size [min_size, max_size) to satisfy the alignment
+ * requirement. For each size, try only the first extent in the heap.
+ */
+static edata_t *
+eset_fit_alignment(eset_t *eset, size_t min_size, size_t max_size,
+ size_t alignment) {
+ pszind_t pind = sz_psz2ind(sz_psz_quantize_ceil(min_size));
+ pszind_t pind_max = sz_psz2ind(sz_psz_quantize_ceil(max_size));
+
+ for (pszind_t i =
+ (pszind_t)fb_ffs(eset->bitmap, ESET_NPSIZES, (size_t)pind);
+ i < pind_max;
+ i = (pszind_t)fb_ffs(eset->bitmap, ESET_NPSIZES, (size_t)i + 1)) {
+ assert(i < SC_NPSIZES);
+ assert(!edata_heap_empty(&eset->bins[i].heap));
+ edata_t *edata = edata_heap_first(&eset->bins[i].heap);
+ uintptr_t base = (uintptr_t)edata_base_get(edata);
+ size_t candidate_size = edata_size_get(edata);
+ assert(candidate_size >= min_size);
+
+ uintptr_t next_align = ALIGNMENT_CEILING((uintptr_t)base,
+ PAGE_CEILING(alignment));
+ if (base > next_align || base + candidate_size <= next_align) {
+ /* Overflow or not crossing the next alignment. */
+ continue;
+ }
+
+ size_t leadsize = next_align - base;
+ if (candidate_size - leadsize >= min_size) {
+ return edata;
+ }
+ }
+
+ return NULL;
+}
+
+/*
+ * Do first-fit extent selection, i.e. select the oldest/lowest extent that is
+ * large enough.
+ *
+ * lg_max_fit is the (log of the) maximum ratio between the requested size and
+ * the returned size that we'll allow. This can reduce fragmentation by
+ * avoiding reusing and splitting large extents for smaller sizes. In practice,
+ * it's set to opt_lg_extent_max_active_fit for the dirty eset and SC_PTR_BITS
+ * for others.
+ */
+static edata_t *
+eset_first_fit(eset_t *eset, size_t size, bool exact_only,
+ unsigned lg_max_fit) {
+ edata_t *ret = NULL;
+ edata_cmp_summary_t ret_summ JEMALLOC_CC_SILENCE_INIT({0});
+
+ pszind_t pind = sz_psz2ind(sz_psz_quantize_ceil(size));
+
+ if (exact_only) {
+ return edata_heap_empty(&eset->bins[pind].heap) ? NULL :
+ edata_heap_first(&eset->bins[pind].heap);
+ }
+
+ for (pszind_t i =
+ (pszind_t)fb_ffs(eset->bitmap, ESET_NPSIZES, (size_t)pind);
+ i < ESET_NPSIZES;
+ i = (pszind_t)fb_ffs(eset->bitmap, ESET_NPSIZES, (size_t)i + 1)) {
+ assert(!edata_heap_empty(&eset->bins[i].heap));
+ if (lg_max_fit == SC_PTR_BITS) {
+ /*
+ * We'll shift by this below, and shifting out all the
+ * bits is undefined. Decreasing is safe, since the
+ * page size is larger than 1 byte.
+ */
+ lg_max_fit = SC_PTR_BITS - 1;
+ }
+ if ((sz_pind2sz(i) >> lg_max_fit) > size) {
+ break;
+ }
+ if (ret == NULL || edata_cmp_summary_comp(
+ eset->bins[i].heap_min, ret_summ) < 0) {
+ /*
+ * We grab the edata as early as possible, even though
+ * we might change it later. Practically, a large
+ * portion of eset_fit calls succeed at the first valid
+ * index, so this doesn't cost much, and we get the
+ * effect of prefetching the edata as early as possible.
+ */
+ edata_t *edata = edata_heap_first(&eset->bins[i].heap);
+ assert(edata_size_get(edata) >= size);
+ assert(ret == NULL || edata_snad_comp(edata, ret) < 0);
+ assert(ret == NULL || edata_cmp_summary_comp(
+ eset->bins[i].heap_min,
+ edata_cmp_summary_get(edata)) == 0);
+ ret = edata;
+ ret_summ = eset->bins[i].heap_min;
+ }
+ if (i == SC_NPSIZES) {
+ break;
+ }
+ assert(i < SC_NPSIZES);
+ }
+
+ return ret;
+}
+
+edata_t *
+eset_fit(eset_t *eset, size_t esize, size_t alignment, bool exact_only,
+ unsigned lg_max_fit) {
+ size_t max_size = esize + PAGE_CEILING(alignment) - PAGE;
+ /* Beware size_t wrap-around. */
+ if (max_size < esize) {
+ return NULL;
+ }
+
+ edata_t *edata = eset_first_fit(eset, max_size, exact_only, lg_max_fit);
+
+ if (alignment > PAGE && edata == NULL) {
+ /*
+ * max_size guarantees the alignment requirement but is rather
+ * pessimistic. Next we try to satisfy the aligned allocation
+ * with sizes in [esize, max_size).
+ */
+ edata = eset_fit_alignment(eset, esize, max_size, alignment);
+ }
+
+ return edata;
+}
diff --git a/contrib/jemalloc/src/exp_grow.c b/contrib/jemalloc/src/exp_grow.c
new file mode 100644
index 000000000000..386471f49fde
--- /dev/null
+++ b/contrib/jemalloc/src/exp_grow.c
@@ -0,0 +1,8 @@
+#include "jemalloc/internal/jemalloc_preamble.h"
+#include "jemalloc/internal/jemalloc_internal_includes.h"
+
+void
+exp_grow_init(exp_grow_t *exp_grow) {
+ exp_grow->next = sz_psz2ind(HUGEPAGE);
+ exp_grow->limit = sz_psz2ind(SC_LARGE_MAXCLASS);
+}
diff --git a/contrib/jemalloc/src/extent.c b/contrib/jemalloc/src/extent.c
index b4ef382676be..cf3d1f3112df 100644
--- a/contrib/jemalloc/src/extent.c
+++ b/contrib/jemalloc/src/extent.c
@@ -1,93 +1,28 @@
-#define JEMALLOC_EXTENT_C_
#include "jemalloc/internal/jemalloc_preamble.h"
#include "jemalloc/internal/jemalloc_internal_includes.h"
#include "jemalloc/internal/assert.h"
+#include "jemalloc/internal/emap.h"
#include "jemalloc/internal/extent_dss.h"
#include "jemalloc/internal/extent_mmap.h"
#include "jemalloc/internal/ph.h"
-#include "jemalloc/internal/rtree.h"
#include "jemalloc/internal/mutex.h"
-#include "jemalloc/internal/mutex_pool.h"
/******************************************************************************/
/* Data. */
-rtree_t extents_rtree;
-/* Keyed by the address of the extent_t being protected. */
-mutex_pool_t extent_mutex_pool;
-
size_t opt_lg_extent_max_active_fit = LG_EXTENT_MAX_ACTIVE_FIT_DEFAULT;
-static const bitmap_info_t extents_bitmap_info =
- BITMAP_INFO_INITIALIZER(SC_NPSIZES+1);
-
-static void *extent_alloc_default(extent_hooks_t *extent_hooks, void *new_addr,
- size_t size, size_t alignment, bool *zero, bool *commit,
- unsigned arena_ind);
-static bool extent_dalloc_default(extent_hooks_t *extent_hooks, void *addr,
- size_t size, bool committed, unsigned arena_ind);
-static void extent_destroy_default(extent_hooks_t *extent_hooks, void *addr,
- size_t size, bool committed, unsigned arena_ind);
-static bool extent_commit_default(extent_hooks_t *extent_hooks, void *addr,
- size_t size, size_t offset, size_t length, unsigned arena_ind);
-static bool extent_commit_impl(tsdn_t *tsdn, arena_t *arena,
- extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset,
- size_t length, bool growing_retained);
-static bool extent_decommit_default(extent_hooks_t *extent_hooks,
- void *addr, size_t size, size_t offset, size_t length, unsigned arena_ind);
-#ifdef PAGES_CAN_PURGE_LAZY
-static bool extent_purge_lazy_default(extent_hooks_t *extent_hooks, void *addr,
- size_t size, size_t offset, size_t length, unsigned arena_ind);
-#endif
-static bool extent_purge_lazy_impl(tsdn_t *tsdn, arena_t *arena,
- extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset,
- size_t length, bool growing_retained);
-#ifdef PAGES_CAN_PURGE_FORCED
-static bool extent_purge_forced_default(extent_hooks_t *extent_hooks,
- void *addr, size_t size, size_t offset, size_t length, unsigned arena_ind);
-#endif
-static bool extent_purge_forced_impl(tsdn_t *tsdn, arena_t *arena,
- extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset,
- size_t length, bool growing_retained);
-static bool extent_split_default(extent_hooks_t *extent_hooks, void *addr,
- size_t size, size_t size_a, size_t size_b, bool committed,
- unsigned arena_ind);
-static extent_t *extent_split_impl(tsdn_t *tsdn, arena_t *arena,
- extent_hooks_t **r_extent_hooks, extent_t *extent, size_t size_a,
- szind_t szind_a, bool slab_a, size_t size_b, szind_t szind_b, bool slab_b,
- bool growing_retained);
-static bool extent_merge_default(extent_hooks_t *extent_hooks, void *addr_a,
- size_t size_a, void *addr_b, size_t size_b, bool committed,
- unsigned arena_ind);
-static bool extent_merge_impl(tsdn_t *tsdn, arena_t *arena,
- extent_hooks_t **r_extent_hooks, extent_t *a, extent_t *b,
- bool growing_retained);
-
-const extent_hooks_t extent_hooks_default = {
- extent_alloc_default,
- extent_dalloc_default,
- extent_destroy_default,
- extent_commit_default,
- extent_decommit_default
-#ifdef PAGES_CAN_PURGE_LAZY
- ,
- extent_purge_lazy_default
-#else
- ,
- NULL
-#endif
-#ifdef PAGES_CAN_PURGE_FORCED
- ,
- extent_purge_forced_default
-#else
- ,
- NULL
-#endif
- ,
- extent_split_default,
- extent_merge_default
-};
+static bool extent_commit_impl(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata,
+ size_t offset, size_t length, bool growing_retained);
+static bool extent_purge_lazy_impl(tsdn_t *tsdn, ehooks_t *ehooks,
+ edata_t *edata, size_t offset, size_t length, bool growing_retained);
+static bool extent_purge_forced_impl(tsdn_t *tsdn, ehooks_t *ehooks,
+ edata_t *edata, size_t offset, size_t length, bool growing_retained);
+static edata_t *extent_split_impl(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
+ edata_t *edata, size_t size_a, size_t size_b, bool holding_core_locks);
+static bool extent_merge_impl(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
+ edata_t *a, edata_t *b, bool holding_core_locks);
/* Used exclusively for gdump triggering. */
static atomic_zu_t curpages;
@@ -99,503 +34,158 @@ static atomic_zu_t highpages;
* definition.
*/
-static void extent_deregister(tsdn_t *tsdn, extent_t *extent);
-static extent_t *extent_recycle(tsdn_t *tsdn, arena_t *arena,
- extent_hooks_t **r_extent_hooks, extents_t *extents, void *new_addr,
- size_t usize, size_t pad, size_t alignment, bool slab, szind_t szind,
- bool *zero, bool *commit, bool growing_retained);
-static extent_t *extent_try_coalesce(tsdn_t *tsdn, arena_t *arena,
- extent_hooks_t **r_extent_hooks, rtree_ctx_t *rtree_ctx, extents_t *extents,
- extent_t *extent, bool *coalesced, bool growing_retained);
-static void extent_record(tsdn_t *tsdn, arena_t *arena,
- extent_hooks_t **r_extent_hooks, extents_t *extents, extent_t *extent,
- bool growing_retained);
+static void extent_deregister(tsdn_t *tsdn, pac_t *pac, edata_t *edata);
+static edata_t *extent_recycle(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
+ ecache_t *ecache, edata_t *expand_edata, size_t usize, size_t alignment,
+ bool zero, bool *commit, bool growing_retained, bool guarded);
+static edata_t *extent_try_coalesce(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
+ ecache_t *ecache, edata_t *edata, bool *coalesced);
+static edata_t *extent_alloc_retained(tsdn_t *tsdn, pac_t *pac,
+ ehooks_t *ehooks, edata_t *expand_edata, size_t size, size_t alignment,
+ bool zero, bool *commit, bool guarded);
/******************************************************************************/
-#define ATTR_NONE /* does nothing */
-
-ph_gen(ATTR_NONE, extent_avail_, extent_tree_t, extent_t, ph_link,
- extent_esnead_comp)
-
-#undef ATTR_NONE
-
-typedef enum {
- lock_result_success,
- lock_result_failure,
- lock_result_no_extent
-} lock_result_t;
-
-static lock_result_t
-extent_rtree_leaf_elm_try_lock(tsdn_t *tsdn, rtree_leaf_elm_t *elm,
- extent_t **result, bool inactive_only) {
- extent_t *extent1 = rtree_leaf_elm_extent_read(tsdn, &extents_rtree,
- elm, true);
-
- /* Slab implies active extents and should be skipped. */
- if (extent1 == NULL || (inactive_only && rtree_leaf_elm_slab_read(tsdn,
- &extents_rtree, elm, true))) {
- return lock_result_no_extent;
- }
-
- /*
- * It's possible that the extent changed out from under us, and with it
- * the leaf->extent mapping. We have to recheck while holding the lock.
- */
- extent_lock(tsdn, extent1);
- extent_t *extent2 = rtree_leaf_elm_extent_read(tsdn,
- &extents_rtree, elm, true);
-
- if (extent1 == extent2) {
- *result = extent1;
- return lock_result_success;
- } else {
- extent_unlock(tsdn, extent1);
- return lock_result_failure;
- }
-}
-
-/*
- * Returns a pool-locked extent_t * if there's one associated with the given
- * address, and NULL otherwise.
- */
-static extent_t *
-extent_lock_from_addr(tsdn_t *tsdn, rtree_ctx_t *rtree_ctx, void *addr,
- bool inactive_only) {
- extent_t *ret = NULL;
- rtree_leaf_elm_t *elm = rtree_leaf_elm_lookup(tsdn, &extents_rtree,
- rtree_ctx, (uintptr_t)addr, false, false);
- if (elm == NULL) {
- return NULL;
- }
- lock_result_t lock_result;
- do {
- lock_result = extent_rtree_leaf_elm_try_lock(tsdn, elm, &ret,
- inactive_only);
- } while (lock_result == lock_result_failure);
- return ret;
-}
-
-extent_t *
-extent_alloc(tsdn_t *tsdn, arena_t *arena) {
- malloc_mutex_lock(tsdn, &arena->extent_avail_mtx);
- extent_t *extent = extent_avail_first(&arena->extent_avail);
- if (extent == NULL) {
- malloc_mutex_unlock(tsdn, &arena->extent_avail_mtx);
- return base_alloc_extent(tsdn, arena->base);
- }
- extent_avail_remove(&arena->extent_avail, extent);
- atomic_fetch_sub_zu(&arena->extent_avail_cnt, 1, ATOMIC_RELAXED);
- malloc_mutex_unlock(tsdn, &arena->extent_avail_mtx);
- return extent;
-}
-
-void
-extent_dalloc(tsdn_t *tsdn, arena_t *arena, extent_t *extent) {
- malloc_mutex_lock(tsdn, &arena->extent_avail_mtx);
- extent_avail_insert(&arena->extent_avail, extent);
- atomic_fetch_add_zu(&arena->extent_avail_cnt, 1, ATOMIC_RELAXED);
- malloc_mutex_unlock(tsdn, &arena->extent_avail_mtx);
-}
-
-extent_hooks_t *
-extent_hooks_get(arena_t *arena) {
- return base_extent_hooks_get(arena->base);
-}
-
-extent_hooks_t *
-extent_hooks_set(tsd_t *tsd, arena_t *arena, extent_hooks_t *extent_hooks) {
- background_thread_info_t *info;
- if (have_background_thread) {
- info = arena_background_thread_info_get(arena);
- malloc_mutex_lock(tsd_tsdn(tsd), &info->mtx);
- }
- extent_hooks_t *ret = base_extent_hooks_set(arena->base, extent_hooks);
- if (have_background_thread) {
- malloc_mutex_unlock(tsd_tsdn(tsd), &info->mtx);
- }
-
- return ret;
-}
-
-static void
-extent_hooks_assure_initialized(arena_t *arena,
- extent_hooks_t **r_extent_hooks) {
- if (*r_extent_hooks == EXTENT_HOOKS_INITIALIZER) {
- *r_extent_hooks = extent_hooks_get(arena);
- }
-}
-
-#ifndef JEMALLOC_JET
-static
-#endif
size_t
-extent_size_quantize_floor(size_t size) {
- size_t ret;
- pszind_t pind;
-
- assert(size > 0);
- assert((size & PAGE_MASK) == 0);
-
- pind = sz_psz2ind(size - sz_large_pad + 1);
- if (pind == 0) {
- /*
- * Avoid underflow. This short-circuit would also do the right
- * thing for all sizes in the range for which there are
- * PAGE-spaced size classes, but it's simplest to just handle
- * the one case that would cause erroneous results.
- */
- return size;
- }
- ret = sz_pind2sz(pind - 1) + sz_large_pad;
- assert(ret <= size);
- return ret;
+extent_sn_next(pac_t *pac) {
+ return atomic_fetch_add_zu(&pac->extent_sn_next, 1, ATOMIC_RELAXED);
}
-#ifndef JEMALLOC_JET
-static
-#endif
-size_t
-extent_size_quantize_ceil(size_t size) {
- size_t ret;
-
- assert(size > 0);
- assert(size - sz_large_pad <= SC_LARGE_MAXCLASS);
- assert((size & PAGE_MASK) == 0);
-
- ret = extent_size_quantize_floor(size);
- if (ret < size) {
- /*
- * Skip a quantization that may have an adequately large extent,
- * because under-sized extents may be mixed in. This only
- * happens when an unusual size is requested, i.e. for aligned
- * allocation, and is just one of several places where linear
- * search would potentially find sufficiently aligned available
- * memory somewhere lower.
- */
- ret = sz_pind2sz(sz_psz2ind(ret - sz_large_pad + 1)) +
- sz_large_pad;
- }
- return ret;
+static inline bool
+extent_may_force_decay(pac_t *pac) {
+ return !(pac_decay_ms_get(pac, extent_state_dirty) == -1
+ || pac_decay_ms_get(pac, extent_state_muzzy) == -1);
}
-/* Generate pairing heap functions. */
-ph_gen(, extent_heap_, extent_heap_t, extent_t, ph_link, extent_snad_comp)
+static bool
+extent_try_delayed_coalesce(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
+ ecache_t *ecache, edata_t *edata) {
+ emap_update_edata_state(tsdn, pac->emap, edata, extent_state_active);
-bool
-extents_init(tsdn_t *tsdn, extents_t *extents, extent_state_t state,
- bool delay_coalesce) {
- if (malloc_mutex_init(&extents->mtx, "extents", WITNESS_RANK_EXTENTS,
- malloc_mutex_rank_exclusive)) {
+ bool coalesced;
+ edata = extent_try_coalesce(tsdn, pac, ehooks, ecache,
+ edata, &coalesced);
+ emap_update_edata_state(tsdn, pac->emap, edata, ecache->state);
+
+ if (!coalesced) {
return true;
}
- for (unsigned i = 0; i < SC_NPSIZES + 1; i++) {
- extent_heap_new(&extents->heaps[i]);
- }
- bitmap_init(extents->bitmap, &extents_bitmap_info, true);
- extent_list_init(&extents->lru);
- atomic_store_zu(&extents->npages, 0, ATOMIC_RELAXED);
- extents->state = state;
- extents->delay_coalesce = delay_coalesce;
+ eset_insert(&ecache->eset, edata);
return false;
}
-extent_state_t
-extents_state_get(const extents_t *extents) {
- return extents->state;
-}
-
-size_t
-extents_npages_get(extents_t *extents) {
- return atomic_load_zu(&extents->npages, ATOMIC_RELAXED);
-}
-
-size_t
-extents_nextents_get(extents_t *extents, pszind_t pind) {
- return atomic_load_zu(&extents->nextents[pind], ATOMIC_RELAXED);
-}
-
-size_t
-extents_nbytes_get(extents_t *extents, pszind_t pind) {
- return atomic_load_zu(&extents->nbytes[pind], ATOMIC_RELAXED);
-}
-
-static void
-extents_stats_add(extents_t *extent, pszind_t pind, size_t sz) {
- size_t cur = atomic_load_zu(&extent->nextents[pind], ATOMIC_RELAXED);
- atomic_store_zu(&extent->nextents[pind], cur + 1, ATOMIC_RELAXED);
- cur = atomic_load_zu(&extent->nbytes[pind], ATOMIC_RELAXED);
- atomic_store_zu(&extent->nbytes[pind], cur + sz, ATOMIC_RELAXED);
-}
-
-static void
-extents_stats_sub(extents_t *extent, pszind_t pind, size_t sz) {
- size_t cur = atomic_load_zu(&extent->nextents[pind], ATOMIC_RELAXED);
- atomic_store_zu(&extent->nextents[pind], cur - 1, ATOMIC_RELAXED);
- cur = atomic_load_zu(&extent->nbytes[pind], ATOMIC_RELAXED);
- atomic_store_zu(&extent->nbytes[pind], cur - sz, ATOMIC_RELAXED);
-}
-
-static void
-extents_insert_locked(tsdn_t *tsdn, extents_t *extents, extent_t *extent) {
- malloc_mutex_assert_owner(tsdn, &extents->mtx);
- assert(extent_state_get(extent) == extents->state);
-
- size_t size = extent_size_get(extent);
- size_t psz = extent_size_quantize_floor(size);
- pszind_t pind = sz_psz2ind(psz);
- if (extent_heap_empty(&extents->heaps[pind])) {
- bitmap_unset(extents->bitmap, &extents_bitmap_info,
- (size_t)pind);
- }
- extent_heap_insert(&extents->heaps[pind], extent);
-
- if (config_stats) {
- extents_stats_add(extents, pind, size);
- }
-
- extent_list_append(&extents->lru, extent);
- size_t npages = size >> LG_PAGE;
- /*
- * All modifications to npages hold the mutex (as asserted above), so we
- * don't need an atomic fetch-add; we can get by with a load followed by
- * a store.
- */
- size_t cur_extents_npages =
- atomic_load_zu(&extents->npages, ATOMIC_RELAXED);
- atomic_store_zu(&extents->npages, cur_extents_npages + npages,
- ATOMIC_RELAXED);
-}
-
-static void
-extents_remove_locked(tsdn_t *tsdn, extents_t *extents, extent_t *extent) {
- malloc_mutex_assert_owner(tsdn, &extents->mtx);
- assert(extent_state_get(extent) == extents->state);
-
- size_t size = extent_size_get(extent);
- size_t psz = extent_size_quantize_floor(size);
- pszind_t pind = sz_psz2ind(psz);
- extent_heap_remove(&extents->heaps[pind], extent);
-
- if (config_stats) {
- extents_stats_sub(extents, pind, size);
- }
-
- if (extent_heap_empty(&extents->heaps[pind])) {
- bitmap_set(extents->bitmap, &extents_bitmap_info,
- (size_t)pind);
- }
- extent_list_remove(&extents->lru, extent);
- size_t npages = size >> LG_PAGE;
- /*
- * As in extents_insert_locked, we hold extents->mtx and so don't need
- * atomic operations for updating extents->npages.
- */
- size_t cur_extents_npages =
- atomic_load_zu(&extents->npages, ATOMIC_RELAXED);
- assert(cur_extents_npages >= npages);
- atomic_store_zu(&extents->npages,
- cur_extents_npages - (size >> LG_PAGE), ATOMIC_RELAXED);
-}
-
-/*
- * Find an extent with size [min_size, max_size) to satisfy the alignment
- * requirement. For each size, try only the first extent in the heap.
- */
-static extent_t *
-extents_fit_alignment(extents_t *extents, size_t min_size, size_t max_size,
- size_t alignment) {
- pszind_t pind = sz_psz2ind(extent_size_quantize_ceil(min_size));
- pszind_t pind_max = sz_psz2ind(extent_size_quantize_ceil(max_size));
-
- for (pszind_t i = (pszind_t)bitmap_ffu(extents->bitmap,
- &extents_bitmap_info, (size_t)pind); i < pind_max; i =
- (pszind_t)bitmap_ffu(extents->bitmap, &extents_bitmap_info,
- (size_t)i+1)) {
- assert(i < SC_NPSIZES);
- assert(!extent_heap_empty(&extents->heaps[i]));
- extent_t *extent = extent_heap_first(&extents->heaps[i]);
- uintptr_t base = (uintptr_t)extent_base_get(extent);
- size_t candidate_size = extent_size_get(extent);
- assert(candidate_size >= min_size);
-
- uintptr_t next_align = ALIGNMENT_CEILING((uintptr_t)base,
- PAGE_CEILING(alignment));
- if (base > next_align || base + candidate_size <= next_align) {
- /* Overflow or not crossing the next alignment. */
- continue;
- }
-
- size_t leadsize = next_align - base;
- if (candidate_size - leadsize >= min_size) {
- return extent;
- }
- }
+edata_t *
+ecache_alloc(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, ecache_t *ecache,
+ edata_t *expand_edata, size_t size, size_t alignment, bool zero,
+ bool guarded) {
+ assert(size != 0);
+ assert(alignment != 0);
+ witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
+ WITNESS_RANK_CORE, 0);
- return NULL;
+ bool commit = true;
+ edata_t *edata = extent_recycle(tsdn, pac, ehooks, ecache, expand_edata,
+ size, alignment, zero, &commit, false, guarded);
+ assert(edata == NULL || edata_pai_get(edata) == EXTENT_PAI_PAC);
+ assert(edata == NULL || edata_guarded_get(edata) == guarded);
+ return edata;
}
-/*
- * Do first-fit extent selection, i.e. select the oldest/lowest extent that is
- * large enough.
- */
-static extent_t *
-extents_first_fit_locked(tsdn_t *tsdn, arena_t *arena, extents_t *extents,
- size_t size) {
- extent_t *ret = NULL;
-
- pszind_t pind = sz_psz2ind(extent_size_quantize_ceil(size));
-
- if (!maps_coalesce && !opt_retain) {
- /*
- * No split / merge allowed (Windows w/o retain). Try exact fit
- * only.
- */
- return extent_heap_empty(&extents->heaps[pind]) ? NULL :
- extent_heap_first(&extents->heaps[pind]);
- }
+edata_t *
+ecache_alloc_grow(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, ecache_t *ecache,
+ edata_t *expand_edata, size_t size, size_t alignment, bool zero,
+ bool guarded) {
+ assert(size != 0);
+ assert(alignment != 0);
+ witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
+ WITNESS_RANK_CORE, 0);
- for (pszind_t i = (pszind_t)bitmap_ffu(extents->bitmap,
- &extents_bitmap_info, (size_t)pind);
- i < SC_NPSIZES + 1;
- i = (pszind_t)bitmap_ffu(extents->bitmap, &extents_bitmap_info,
- (size_t)i+1)) {
- assert(!extent_heap_empty(&extents->heaps[i]));
- extent_t *extent = extent_heap_first(&extents->heaps[i]);
- assert(extent_size_get(extent) >= size);
- /*
- * In order to reduce fragmentation, avoid reusing and splitting
- * large extents for much smaller sizes.
- *
- * Only do check for dirty extents (delay_coalesce).
- */
- if (extents->delay_coalesce &&
- (sz_pind2sz(i) >> opt_lg_extent_max_active_fit) > size) {
- break;
- }
- if (ret == NULL || extent_snad_comp(extent, ret) < 0) {
- ret = extent;
+ bool commit = true;
+ edata_t *edata = extent_alloc_retained(tsdn, pac, ehooks, expand_edata,
+ size, alignment, zero, &commit, guarded);
+ if (edata == NULL) {
+ if (opt_retain && expand_edata != NULL) {
+ /*
+ * When retain is enabled and trying to expand, we do
+ * not attempt extent_alloc_wrapper which does mmap that
+ * is very unlikely to succeed (unless it happens to be
+ * at the end).
+ */
+ return NULL;
}
- if (i == SC_NPSIZES) {
- break;
+ if (guarded) {
+ /*
+ * Means no cached guarded extents available (and no
+ * grow_retained was attempted). The pac_alloc flow
+ * will alloc regular extents to make new guarded ones.
+ */
+ return NULL;
}
- assert(i < SC_NPSIZES);
- }
-
- return ret;
-}
-
-/*
- * Do first-fit extent selection, where the selection policy choice is
- * based on extents->delay_coalesce.
- */
-static extent_t *
-extents_fit_locked(tsdn_t *tsdn, arena_t *arena, extents_t *extents,
- size_t esize, size_t alignment) {
- malloc_mutex_assert_owner(tsdn, &extents->mtx);
-
- size_t max_size = esize + PAGE_CEILING(alignment) - PAGE;
- /* Beware size_t wrap-around. */
- if (max_size < esize) {
- return NULL;
+ void *new_addr = (expand_edata == NULL) ? NULL :
+ edata_past_get(expand_edata);
+ edata = extent_alloc_wrapper(tsdn, pac, ehooks, new_addr,
+ size, alignment, zero, &commit,
+ /* growing_retained */ false);
}
- extent_t *extent =
- extents_first_fit_locked(tsdn, arena, extents, max_size);
-
- if (alignment > PAGE && extent == NULL) {
- /*
- * max_size guarantees the alignment requirement but is rather
- * pessimistic. Next we try to satisfy the aligned allocation
- * with sizes in [esize, max_size).
- */
- extent = extents_fit_alignment(extents, esize, max_size,
- alignment);
- }
-
- return extent;
-}
-
-static bool
-extent_try_delayed_coalesce(tsdn_t *tsdn, arena_t *arena,
- extent_hooks_t **r_extent_hooks, rtree_ctx_t *rtree_ctx, extents_t *extents,
- extent_t *extent) {
- extent_state_set(extent, extent_state_active);
- bool coalesced;
- extent = extent_try_coalesce(tsdn, arena, r_extent_hooks, rtree_ctx,
- extents, extent, &coalesced, false);
- extent_state_set(extent, extents_state_get(extents));
-
- if (!coalesced) {
- return true;
- }
- extents_insert_locked(tsdn, extents, extent);
- return false;
-}
-
-extent_t *
-extents_alloc(tsdn_t *tsdn, arena_t *arena, extent_hooks_t **r_extent_hooks,
- extents_t *extents, void *new_addr, size_t size, size_t pad,
- size_t alignment, bool slab, szind_t szind, bool *zero, bool *commit) {
- assert(size + pad != 0);
- assert(alignment != 0);
- witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
- WITNESS_RANK_CORE, 0);
-
- extent_t *extent = extent_recycle(tsdn, arena, r_extent_hooks, extents,
- new_addr, size, pad, alignment, slab, szind, zero, commit, false);
- assert(extent == NULL || extent_dumpable_get(extent));
- return extent;
+ assert(edata == NULL || edata_pai_get(edata) == EXTENT_PAI_PAC);
+ return edata;
}
void
-extents_dalloc(tsdn_t *tsdn, arena_t *arena, extent_hooks_t **r_extent_hooks,
- extents_t *extents, extent_t *extent) {
- assert(extent_base_get(extent) != NULL);
- assert(extent_size_get(extent) != 0);
- assert(extent_dumpable_get(extent));
+ecache_dalloc(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, ecache_t *ecache,
+ edata_t *edata) {
+ assert(edata_base_get(edata) != NULL);
+ assert(edata_size_get(edata) != 0);
+ assert(edata_pai_get(edata) == EXTENT_PAI_PAC);
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
WITNESS_RANK_CORE, 0);
- extent_addr_set(extent, extent_base_get(extent));
- extent_zeroed_set(extent, false);
+ edata_addr_set(edata, edata_base_get(edata));
+ edata_zeroed_set(edata, false);
- extent_record(tsdn, arena, r_extent_hooks, extents, extent, false);
+ extent_record(tsdn, pac, ehooks, ecache, edata);
}
-extent_t *
-extents_evict(tsdn_t *tsdn, arena_t *arena, extent_hooks_t **r_extent_hooks,
- extents_t *extents, size_t npages_min) {
- rtree_ctx_t rtree_ctx_fallback;
- rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);
-
- malloc_mutex_lock(tsdn, &extents->mtx);
+edata_t *
+ecache_evict(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
+ ecache_t *ecache, size_t npages_min) {
+ malloc_mutex_lock(tsdn, &ecache->mtx);
/*
* Get the LRU coalesced extent, if any. If coalescing was delayed,
* the loop will iterate until the LRU extent is fully coalesced.
*/
- extent_t *extent;
+ edata_t *edata;
while (true) {
/* Get the LRU extent, if any. */
- extent = extent_list_first(&extents->lru);
- if (extent == NULL) {
- goto label_return;
+ eset_t *eset = &ecache->eset;
+ edata = edata_list_inactive_first(&eset->lru);
+ if (edata == NULL) {
+ /*
+ * Next check if there are guarded extents. They are
+ * more expensive to purge (since they are not
+ * mergeable), thus in favor of caching them longer.
+ */
+ eset = &ecache->guarded_eset;
+ edata = edata_list_inactive_first(&eset->lru);
+ if (edata == NULL) {
+ goto label_return;
+ }
}
/* Check the eviction limit. */
- size_t extents_npages = atomic_load_zu(&extents->npages,
- ATOMIC_RELAXED);
+ size_t extents_npages = ecache_npages_get(ecache);
if (extents_npages <= npages_min) {
- extent = NULL;
+ edata = NULL;
goto label_return;
}
- extents_remove_locked(tsdn, extents, extent);
- if (!extents->delay_coalesce) {
+ eset_remove(eset, edata);
+ if (!ecache->delay_coalesce || edata_guarded_get(edata)) {
break;
}
/* Try to coalesce. */
- if (extent_try_delayed_coalesce(tsdn, arena, r_extent_hooks,
- rtree_ctx, extents, extent)) {
+ if (extent_try_delayed_coalesce(tsdn, pac, ehooks, ecache,
+ edata)) {
break;
}
/*
@@ -608,23 +198,24 @@ extents_evict(tsdn_t *tsdn, arena_t *arena, extent_hooks_t **r_extent_hooks,
* Either mark the extent active or deregister it to protect against
* concurrent operations.
*/
- switch (extents_state_get(extents)) {
+ switch (ecache->state) {
case extent_state_active:
not_reached();
case extent_state_dirty:
case extent_state_muzzy:
- extent_state_set(extent, extent_state_active);
+ emap_update_edata_state(tsdn, pac->emap, edata,
+ extent_state_active);
break;
case extent_state_retained:
- extent_deregister(tsdn, extent);
+ extent_deregister(tsdn, pac, edata);
break;
default:
not_reached();
}
label_return:
- malloc_mutex_unlock(tsdn, &extents->mtx);
- return extent;
+ malloc_mutex_unlock(tsdn, &ecache->mtx);
+ return edata;
}
/*
@@ -632,123 +223,73 @@ label_return:
* indicates OOM), e.g. when trying to split an existing extent.
*/
static void
-extents_abandon_vm(tsdn_t *tsdn, arena_t *arena, extent_hooks_t **r_extent_hooks,
- extents_t *extents, extent_t *extent, bool growing_retained) {
- size_t sz = extent_size_get(extent);
+extents_abandon_vm(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, ecache_t *ecache,
+ edata_t *edata, bool growing_retained) {
+ size_t sz = edata_size_get(edata);
if (config_stats) {
- arena_stats_accum_zu(&arena->stats.abandoned_vm, sz);
+ atomic_fetch_add_zu(&pac->stats->abandoned_vm, sz,
+ ATOMIC_RELAXED);
}
/*
* Leak extent after making sure its pages have already been purged, so
* that this is only a virtual memory leak.
*/
- if (extents_state_get(extents) == extent_state_dirty) {
- if (extent_purge_lazy_impl(tsdn, arena, r_extent_hooks,
- extent, 0, sz, growing_retained)) {
- extent_purge_forced_impl(tsdn, arena, r_extent_hooks,
- extent, 0, extent_size_get(extent),
- growing_retained);
+ if (ecache->state == extent_state_dirty) {
+ if (extent_purge_lazy_impl(tsdn, ehooks, edata, 0, sz,
+ growing_retained)) {
+ extent_purge_forced_impl(tsdn, ehooks, edata, 0,
+ edata_size_get(edata), growing_retained);
}
}
- extent_dalloc(tsdn, arena, extent);
-}
-
-void
-extents_prefork(tsdn_t *tsdn, extents_t *extents) {
- malloc_mutex_prefork(tsdn, &extents->mtx);
-}
-
-void
-extents_postfork_parent(tsdn_t *tsdn, extents_t *extents) {
- malloc_mutex_postfork_parent(tsdn, &extents->mtx);
-}
-
-void
-extents_postfork_child(tsdn_t *tsdn, extents_t *extents) {
- malloc_mutex_postfork_child(tsdn, &extents->mtx);
+ edata_cache_put(tsdn, pac->edata_cache, edata);
}
static void
-extent_deactivate_locked(tsdn_t *tsdn, arena_t *arena, extents_t *extents,
- extent_t *extent) {
- assert(extent_arena_get(extent) == arena);
- assert(extent_state_get(extent) == extent_state_active);
+extent_deactivate_locked_impl(tsdn_t *tsdn, pac_t *pac, ecache_t *ecache,
+ edata_t *edata) {
+ malloc_mutex_assert_owner(tsdn, &ecache->mtx);
+ assert(edata_arena_ind_get(edata) == ecache_ind_get(ecache));
- extent_state_set(extent, extents_state_get(extents));
- extents_insert_locked(tsdn, extents, extent);
+ emap_update_edata_state(tsdn, pac->emap, edata, ecache->state);
+ eset_t *eset = edata_guarded_get(edata) ? &ecache->guarded_eset :
+ &ecache->eset;
+ eset_insert(eset, edata);
}
static void
-extent_deactivate(tsdn_t *tsdn, arena_t *arena, extents_t *extents,
- extent_t *extent) {
- malloc_mutex_lock(tsdn, &extents->mtx);
- extent_deactivate_locked(tsdn, arena, extents, extent);
- malloc_mutex_unlock(tsdn, &extents->mtx);
+extent_deactivate_locked(tsdn_t *tsdn, pac_t *pac, ecache_t *ecache,
+ edata_t *edata) {
+ assert(edata_state_get(edata) == extent_state_active);
+ extent_deactivate_locked_impl(tsdn, pac, ecache, edata);
}
static void
-extent_activate_locked(tsdn_t *tsdn, arena_t *arena, extents_t *extents,
- extent_t *extent) {
- assert(extent_arena_get(extent) == arena);
- assert(extent_state_get(extent) == extents_state_get(extents));
-
- extents_remove_locked(tsdn, extents, extent);
- extent_state_set(extent, extent_state_active);
-}
-
-static bool
-extent_rtree_leaf_elms_lookup(tsdn_t *tsdn, rtree_ctx_t *rtree_ctx,
- const extent_t *extent, bool dependent, bool init_missing,
- rtree_leaf_elm_t **r_elm_a, rtree_leaf_elm_t **r_elm_b) {
- *r_elm_a = rtree_leaf_elm_lookup(tsdn, &extents_rtree, rtree_ctx,
- (uintptr_t)extent_base_get(extent), dependent, init_missing);
- if (!dependent && *r_elm_a == NULL) {
- return true;
- }
- assert(*r_elm_a != NULL);
-
- *r_elm_b = rtree_leaf_elm_lookup(tsdn, &extents_rtree, rtree_ctx,
- (uintptr_t)extent_last_get(extent), dependent, init_missing);
- if (!dependent && *r_elm_b == NULL) {
- return true;
- }
- assert(*r_elm_b != NULL);
-
- return false;
+extent_deactivate_check_state_locked(tsdn_t *tsdn, pac_t *pac, ecache_t *ecache,
+ edata_t *edata, extent_state_t expected_state) {
+ assert(edata_state_get(edata) == expected_state);
+ extent_deactivate_locked_impl(tsdn, pac, ecache, edata);
}
static void
-extent_rtree_write_acquired(tsdn_t *tsdn, rtree_leaf_elm_t *elm_a,
- rtree_leaf_elm_t *elm_b, extent_t *extent, szind_t szind, bool slab) {
- rtree_leaf_elm_write(tsdn, &extents_rtree, elm_a, extent, szind, slab);
- if (elm_b != NULL) {
- rtree_leaf_elm_write(tsdn, &extents_rtree, elm_b, extent, szind,
- slab);
- }
-}
+extent_activate_locked(tsdn_t *tsdn, pac_t *pac, ecache_t *ecache, eset_t *eset,
+ edata_t *edata) {
+ assert(edata_arena_ind_get(edata) == ecache_ind_get(ecache));
+ assert(edata_state_get(edata) == ecache->state ||
+ edata_state_get(edata) == extent_state_merging);
-static void
-extent_interior_register(tsdn_t *tsdn, rtree_ctx_t *rtree_ctx, extent_t *extent,
- szind_t szind) {
- assert(extent_slab_get(extent));
-
- /* Register interior. */
- for (size_t i = 1; i < (extent_size_get(extent) >> LG_PAGE) - 1; i++) {
- rtree_write(tsdn, &extents_rtree, rtree_ctx,
- (uintptr_t)extent_base_get(extent) + (uintptr_t)(i <<
- LG_PAGE), extent, szind, true);
- }
+ eset_remove(eset, edata);
+ emap_update_edata_state(tsdn, pac->emap, edata, extent_state_active);
}
-static void
-extent_gdump_add(tsdn_t *tsdn, const extent_t *extent) {
+void
+extent_gdump_add(tsdn_t *tsdn, const edata_t *edata) {
cassert(config_prof);
/* prof_gdump() requirement. */
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
WITNESS_RANK_CORE, 0);
- if (opt_prof && extent_state_get(extent) == extent_state_active) {
- size_t nadd = extent_size_get(extent) >> LG_PAGE;
+ if (opt_prof && edata_state_get(edata) == extent_state_active) {
+ size_t nadd = edata_size_get(edata) >> LG_PAGE;
size_t cur = atomic_fetch_add_zu(&curpages, nadd,
ATOMIC_RELAXED) + nadd;
size_t high = atomic_load_zu(&highpages, ATOMIC_RELAXED);
@@ -767,232 +308,184 @@ extent_gdump_add(tsdn_t *tsdn, const extent_t *extent) {
}
static void
-extent_gdump_sub(tsdn_t *tsdn, const extent_t *extent) {
+extent_gdump_sub(tsdn_t *tsdn, const edata_t *edata) {
cassert(config_prof);
- if (opt_prof && extent_state_get(extent) == extent_state_active) {
- size_t nsub = extent_size_get(extent) >> LG_PAGE;
+ if (opt_prof && edata_state_get(edata) == extent_state_active) {
+ size_t nsub = edata_size_get(edata) >> LG_PAGE;
assert(atomic_load_zu(&curpages, ATOMIC_RELAXED) >= nsub);
atomic_fetch_sub_zu(&curpages, nsub, ATOMIC_RELAXED);
}
}
static bool
-extent_register_impl(tsdn_t *tsdn, extent_t *extent, bool gdump_add) {
- rtree_ctx_t rtree_ctx_fallback;
- rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);
- rtree_leaf_elm_t *elm_a, *elm_b;
-
+extent_register_impl(tsdn_t *tsdn, pac_t *pac, edata_t *edata, bool gdump_add) {
+ assert(edata_state_get(edata) == extent_state_active);
/*
- * We need to hold the lock to protect against a concurrent coalesce
- * operation that sees us in a partial state.
+ * No locking needed, as the edata must be in active state, which
+ * prevents other threads from accessing the edata.
*/
- extent_lock(tsdn, extent);
-
- if (extent_rtree_leaf_elms_lookup(tsdn, rtree_ctx, extent, false, true,
- &elm_a, &elm_b)) {
- extent_unlock(tsdn, extent);
+ if (emap_register_boundary(tsdn, pac->emap, edata, SC_NSIZES,
+ /* slab */ false)) {
return true;
}
- szind_t szind = extent_szind_get_maybe_invalid(extent);
- bool slab = extent_slab_get(extent);
- extent_rtree_write_acquired(tsdn, elm_a, elm_b, extent, szind, slab);
- if (slab) {
- extent_interior_register(tsdn, rtree_ctx, extent, szind);
- }
-
- extent_unlock(tsdn, extent);
-
if (config_prof && gdump_add) {
- extent_gdump_add(tsdn, extent);
+ extent_gdump_add(tsdn, edata);
}
return false;
}
static bool
-extent_register(tsdn_t *tsdn, extent_t *extent) {
- return extent_register_impl(tsdn, extent, true);
+extent_register(tsdn_t *tsdn, pac_t *pac, edata_t *edata) {
+ return extent_register_impl(tsdn, pac, edata, true);
}
static bool
-extent_register_no_gdump_add(tsdn_t *tsdn, extent_t *extent) {
- return extent_register_impl(tsdn, extent, false);
+extent_register_no_gdump_add(tsdn_t *tsdn, pac_t *pac, edata_t *edata) {
+ return extent_register_impl(tsdn, pac, edata, false);
}
static void
-extent_reregister(tsdn_t *tsdn, extent_t *extent) {
- bool err = extent_register(tsdn, extent);
+extent_reregister(tsdn_t *tsdn, pac_t *pac, edata_t *edata) {
+ bool err = extent_register(tsdn, pac, edata);
assert(!err);
}
/*
- * Removes all pointers to the given extent from the global rtree indices for
- * its interior. This is relevant for slab extents, for which we need to do
- * metadata lookups at places other than the head of the extent. We deregister
- * on the interior, then, when an extent moves from being an active slab to an
- * inactive state.
- */
-static void
-extent_interior_deregister(tsdn_t *tsdn, rtree_ctx_t *rtree_ctx,
- extent_t *extent) {
- size_t i;
-
- assert(extent_slab_get(extent));
-
- for (i = 1; i < (extent_size_get(extent) >> LG_PAGE) - 1; i++) {
- rtree_clear(tsdn, &extents_rtree, rtree_ctx,
- (uintptr_t)extent_base_get(extent) + (uintptr_t)(i <<
- LG_PAGE));
- }
-}
-
-/*
* Removes all pointers to the given extent from the global rtree.
*/
static void
-extent_deregister_impl(tsdn_t *tsdn, extent_t *extent, bool gdump) {
- rtree_ctx_t rtree_ctx_fallback;
- rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);
- rtree_leaf_elm_t *elm_a, *elm_b;
- extent_rtree_leaf_elms_lookup(tsdn, rtree_ctx, extent, true, false,
- &elm_a, &elm_b);
-
- extent_lock(tsdn, extent);
-
- extent_rtree_write_acquired(tsdn, elm_a, elm_b, NULL, SC_NSIZES, false);
- if (extent_slab_get(extent)) {
- extent_interior_deregister(tsdn, rtree_ctx, extent);
- extent_slab_set(extent, false);
- }
-
- extent_unlock(tsdn, extent);
+extent_deregister_impl(tsdn_t *tsdn, pac_t *pac, edata_t *edata,
+ bool gdump) {
+ emap_deregister_boundary(tsdn, pac->emap, edata);
if (config_prof && gdump) {
- extent_gdump_sub(tsdn, extent);
+ extent_gdump_sub(tsdn, edata);
}
}
static void
-extent_deregister(tsdn_t *tsdn, extent_t *extent) {
- extent_deregister_impl(tsdn, extent, true);
+extent_deregister(tsdn_t *tsdn, pac_t *pac, edata_t *edata) {
+ extent_deregister_impl(tsdn, pac, edata, true);
}
static void
-extent_deregister_no_gdump_sub(tsdn_t *tsdn, extent_t *extent) {
- extent_deregister_impl(tsdn, extent, false);
+extent_deregister_no_gdump_sub(tsdn_t *tsdn, pac_t *pac,
+ edata_t *edata) {
+ extent_deregister_impl(tsdn, pac, edata, false);
}
/*
- * Tries to find and remove an extent from extents that can be used for the
+ * Tries to find and remove an extent from ecache that can be used for the
* given allocation request.
*/
-static extent_t *
-extent_recycle_extract(tsdn_t *tsdn, arena_t *arena,
- extent_hooks_t **r_extent_hooks, rtree_ctx_t *rtree_ctx, extents_t *extents,
- void *new_addr, size_t size, size_t pad, size_t alignment, bool slab,
- bool growing_retained) {
- witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
- WITNESS_RANK_CORE, growing_retained ? 1 : 0);
+static edata_t *
+extent_recycle_extract(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
+ ecache_t *ecache, edata_t *expand_edata, size_t size, size_t alignment,
+ bool guarded) {
+ malloc_mutex_assert_owner(tsdn, &ecache->mtx);
assert(alignment > 0);
- if (config_debug && new_addr != NULL) {
+ if (config_debug && expand_edata != NULL) {
/*
- * Non-NULL new_addr has two use cases:
- *
- * 1) Recycle a known-extant extent, e.g. during purging.
- * 2) Perform in-place expanding reallocation.
- *
- * Regardless of use case, new_addr must either refer to a
- * non-existing extent, or to the base of an extant extent,
- * since only active slabs support interior lookups (which of
- * course cannot be recycled).
+ * Non-NULL expand_edata indicates in-place expanding realloc.
+ * new_addr must either refer to a non-existing extent, or to
+ * the base of an extant extent, since only active slabs support
+ * interior lookups (which of course cannot be recycled).
*/
+ void *new_addr = edata_past_get(expand_edata);
assert(PAGE_ADDR2BASE(new_addr) == new_addr);
- assert(pad == 0);
assert(alignment <= PAGE);
}
- size_t esize = size + pad;
- malloc_mutex_lock(tsdn, &extents->mtx);
- extent_hooks_assure_initialized(arena, r_extent_hooks);
- extent_t *extent;
- if (new_addr != NULL) {
- extent = extent_lock_from_addr(tsdn, rtree_ctx, new_addr,
- false);
- if (extent != NULL) {
- /*
- * We might null-out extent to report an error, but we
- * still need to unlock the associated mutex after.
- */
- extent_t *unlock_extent = extent;
- assert(extent_base_get(extent) == new_addr);
- if (extent_arena_get(extent) != arena ||
- extent_size_get(extent) < esize ||
- extent_state_get(extent) !=
- extents_state_get(extents)) {
- extent = NULL;
+ edata_t *edata;
+ eset_t *eset = guarded ? &ecache->guarded_eset : &ecache->eset;
+ if (expand_edata != NULL) {
+ edata = emap_try_acquire_edata_neighbor_expand(tsdn, pac->emap,
+ expand_edata, EXTENT_PAI_PAC, ecache->state);
+ if (edata != NULL) {
+ extent_assert_can_expand(expand_edata, edata);
+ if (edata_size_get(edata) < size) {
+ emap_release_edata(tsdn, pac->emap, edata,
+ ecache->state);
+ edata = NULL;
}
- extent_unlock(tsdn, unlock_extent);
}
} else {
- extent = extents_fit_locked(tsdn, arena, extents, esize,
- alignment);
+ /*
+ * A large extent might be broken up from its original size to
+ * some small size to satisfy a small request. When that small
+ * request is freed, though, it won't merge back with the larger
+ * extent if delayed coalescing is on. The large extent can
+ * then no longer satify a request for its original size. To
+ * limit this effect, when delayed coalescing is enabled, we
+ * put a cap on how big an extent we can split for a request.
+ */
+ unsigned lg_max_fit = ecache->delay_coalesce
+ ? (unsigned)opt_lg_extent_max_active_fit : SC_PTR_BITS;
+
+ /*
+ * If split and merge are not allowed (Windows w/o retain), try
+ * exact fit only.
+ *
+ * For simplicity purposes, splitting guarded extents is not
+ * supported. Hence, we do only exact fit for guarded
+ * allocations.
+ */
+ bool exact_only = (!maps_coalesce && !opt_retain) || guarded;
+ edata = eset_fit(eset, size, alignment, exact_only,
+ lg_max_fit);
}
- if (extent == NULL) {
- malloc_mutex_unlock(tsdn, &extents->mtx);
+ if (edata == NULL) {
return NULL;
}
+ assert(!guarded || edata_guarded_get(edata));
+ extent_activate_locked(tsdn, pac, ecache, eset, edata);
- extent_activate_locked(tsdn, arena, extents, extent);
- malloc_mutex_unlock(tsdn, &extents->mtx);
-
- return extent;
+ return edata;
}
/*
* Given an allocation request and an extent guaranteed to be able to satisfy
- * it, this splits off lead and trail extents, leaving extent pointing to an
+ * it, this splits off lead and trail extents, leaving edata pointing to an
* extent satisfying the allocation.
- * This function doesn't put lead or trail into any extents_t; it's the caller's
+ * This function doesn't put lead or trail into any ecache; it's the caller's
* job to ensure that they can be reused.
*/
typedef enum {
/*
- * Split successfully. lead, extent, and trail, are modified to extents
+ * Split successfully. lead, edata, and trail, are modified to extents
* describing the ranges before, in, and after the given allocation.
*/
extent_split_interior_ok,
/*
* The extent can't satisfy the given allocation request. None of the
- * input extent_t *s are touched.
+ * input edata_t *s are touched.
*/
extent_split_interior_cant_alloc,
/*
* In a potentially invalid state. Must leak (if *to_leak is non-NULL),
* and salvage what's still salvageable (if *to_salvage is non-NULL).
- * None of lead, extent, or trail are valid.
+ * None of lead, edata, or trail are valid.
*/
extent_split_interior_error
} extent_split_interior_result_t;
static extent_split_interior_result_t
-extent_split_interior(tsdn_t *tsdn, arena_t *arena,
- extent_hooks_t **r_extent_hooks, rtree_ctx_t *rtree_ctx,
+extent_split_interior(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
/* The result of splitting, in case of success. */
- extent_t **extent, extent_t **lead, extent_t **trail,
+ edata_t **edata, edata_t **lead, edata_t **trail,
/* The mess to clean up, in case of error. */
- extent_t **to_leak, extent_t **to_salvage,
- void *new_addr, size_t size, size_t pad, size_t alignment, bool slab,
- szind_t szind, bool growing_retained) {
- size_t esize = size + pad;
- size_t leadsize = ALIGNMENT_CEILING((uintptr_t)extent_base_get(*extent),
- PAGE_CEILING(alignment)) - (uintptr_t)extent_base_get(*extent);
- assert(new_addr == NULL || leadsize == 0);
- if (extent_size_get(*extent) < leadsize + esize) {
+ edata_t **to_leak, edata_t **to_salvage,
+ edata_t *expand_edata, size_t size, size_t alignment) {
+ size_t leadsize = ALIGNMENT_CEILING((uintptr_t)edata_base_get(*edata),
+ PAGE_CEILING(alignment)) - (uintptr_t)edata_base_get(*edata);
+ assert(expand_edata == NULL || leadsize == 0);
+ if (edata_size_get(*edata) < leadsize + size) {
return extent_split_interior_cant_alloc;
}
- size_t trailsize = extent_size_get(*extent) - leadsize - esize;
+ size_t trailsize = edata_size_get(*edata) - leadsize - size;
*lead = NULL;
*trail = NULL;
@@ -1001,11 +494,11 @@ extent_split_interior(tsdn_t *tsdn, arena_t *arena,
/* Split the lead. */
if (leadsize != 0) {
- *lead = *extent;
- *extent = extent_split_impl(tsdn, arena, r_extent_hooks,
- *lead, leadsize, SC_NSIZES, false, esize + trailsize, szind,
- slab, growing_retained);
- if (*extent == NULL) {
+ assert(!edata_guarded_get(*edata));
+ *lead = *edata;
+ *edata = extent_split_impl(tsdn, pac, ehooks, *lead, leadsize,
+ size + trailsize, /* holding_core_locks*/ true);
+ if (*edata == NULL) {
*to_leak = *lead;
*lead = NULL;
return extent_split_interior_error;
@@ -1014,36 +507,18 @@ extent_split_interior(tsdn_t *tsdn, arena_t *arena,
/* Split the trail. */
if (trailsize != 0) {
- *trail = extent_split_impl(tsdn, arena, r_extent_hooks, *extent,
- esize, szind, slab, trailsize, SC_NSIZES, false,
- growing_retained);
+ assert(!edata_guarded_get(*edata));
+ *trail = extent_split_impl(tsdn, pac, ehooks, *edata, size,
+ trailsize, /* holding_core_locks */ true);
if (*trail == NULL) {
- *to_leak = *extent;
+ *to_leak = *edata;
*to_salvage = *lead;
*lead = NULL;
- *extent = NULL;
+ *edata = NULL;
return extent_split_interior_error;
}
}
- if (leadsize == 0 && trailsize == 0) {
- /*
- * Splitting causes szind to be set as a side effect, but no
- * splitting occurred.
- */
- extent_szind_set(*extent, szind);
- if (szind != SC_NSIZES) {
- rtree_szind_slab_update(tsdn, &extents_rtree, rtree_ctx,
- (uintptr_t)extent_addr_get(*extent), szind, slab);
- if (slab && extent_size_get(*extent) > PAGE) {
- rtree_szind_slab_update(tsdn, &extents_rtree,
- rtree_ctx,
- (uintptr_t)extent_past_get(*extent) -
- (uintptr_t)PAGE, szind, slab);
- }
- }
- }
-
return extent_split_interior_ok;
}
@@ -1051,42 +526,43 @@ extent_split_interior(tsdn_t *tsdn, arena_t *arena,
* This fulfills the indicated allocation request out of the given extent (which
* the caller should have ensured was big enough). If there's any unused space
* before or after the resulting allocation, that space is given its own extent
- * and put back into extents.
+ * and put back into ecache.
*/
-static extent_t *
-extent_recycle_split(tsdn_t *tsdn, arena_t *arena,
- extent_hooks_t **r_extent_hooks, rtree_ctx_t *rtree_ctx, extents_t *extents,
- void *new_addr, size_t size, size_t pad, size_t alignment, bool slab,
- szind_t szind, extent_t *extent, bool growing_retained) {
- extent_t *lead;
- extent_t *trail;
- extent_t *to_leak;
- extent_t *to_salvage;
+static edata_t *
+extent_recycle_split(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
+ ecache_t *ecache, edata_t *expand_edata, size_t size, size_t alignment,
+ edata_t *edata, bool growing_retained) {
+ assert(!edata_guarded_get(edata) || size == edata_size_get(edata));
+ malloc_mutex_assert_owner(tsdn, &ecache->mtx);
+
+ edata_t *lead;
+ edata_t *trail;
+ edata_t *to_leak JEMALLOC_CC_SILENCE_INIT(NULL);
+ edata_t *to_salvage JEMALLOC_CC_SILENCE_INIT(NULL);
extent_split_interior_result_t result = extent_split_interior(
- tsdn, arena, r_extent_hooks, rtree_ctx, &extent, &lead, &trail,
- &to_leak, &to_salvage, new_addr, size, pad, alignment, slab, szind,
- growing_retained);
+ tsdn, pac, ehooks, &edata, &lead, &trail, &to_leak, &to_salvage,
+ expand_edata, size, alignment);
if (!maps_coalesce && result != extent_split_interior_ok
&& !opt_retain) {
/*
* Split isn't supported (implies Windows w/o retain). Avoid
- * leaking the extents.
+ * leaking the extent.
*/
assert(to_leak != NULL && lead == NULL && trail == NULL);
- extent_deactivate(tsdn, arena, extents, to_leak);
+ extent_deactivate_locked(tsdn, pac, ecache, to_leak);
return NULL;
}
if (result == extent_split_interior_ok) {
if (lead != NULL) {
- extent_deactivate(tsdn, arena, extents, lead);
+ extent_deactivate_locked(tsdn, pac, ecache, lead);
}
if (trail != NULL) {
- extent_deactivate(tsdn, arena, extents, trail);
+ extent_deactivate_locked(tsdn, pac, ecache, trail);
}
- return extent;
+ return edata;
} else {
/*
* We should have picked an extent that was large enough to
@@ -1094,294 +570,144 @@ extent_recycle_split(tsdn_t *tsdn, arena_t *arena,
*/
assert(result == extent_split_interior_error);
if (to_salvage != NULL) {
- extent_deregister(tsdn, to_salvage);
+ extent_deregister(tsdn, pac, to_salvage);
}
if (to_leak != NULL) {
- void *leak = extent_base_get(to_leak);
- extent_deregister_no_gdump_sub(tsdn, to_leak);
- extents_abandon_vm(tsdn, arena, r_extent_hooks, extents,
- to_leak, growing_retained);
- assert(extent_lock_from_addr(tsdn, rtree_ctx, leak,
- false) == NULL);
+ extent_deregister_no_gdump_sub(tsdn, pac, to_leak);
+ /*
+ * May go down the purge path (which assume no ecache
+ * locks). Only happens with OOM caused split failures.
+ */
+ malloc_mutex_unlock(tsdn, &ecache->mtx);
+ extents_abandon_vm(tsdn, pac, ehooks, ecache, to_leak,
+ growing_retained);
+ malloc_mutex_lock(tsdn, &ecache->mtx);
}
return NULL;
}
unreachable();
}
-static bool
-extent_need_manual_zero(arena_t *arena) {
- /*
- * Need to manually zero the extent on repopulating if either; 1) non
- * default extent hooks installed (in which case the purge semantics may
- * change); or 2) transparent huge pages enabled.
- */
- return (!arena_has_default_hooks(arena) ||
- (opt_thp == thp_mode_always));
-}
-
/*
* Tries to satisfy the given allocation request by reusing one of the extents
- * in the given extents_t.
+ * in the given ecache_t.
*/
-static extent_t *
-extent_recycle(tsdn_t *tsdn, arena_t *arena, extent_hooks_t **r_extent_hooks,
- extents_t *extents, void *new_addr, size_t size, size_t pad,
- size_t alignment, bool slab, szind_t szind, bool *zero, bool *commit,
- bool growing_retained) {
+static edata_t *
+extent_recycle(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, ecache_t *ecache,
+ edata_t *expand_edata, size_t size, size_t alignment, bool zero,
+ bool *commit, bool growing_retained, bool guarded) {
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
WITNESS_RANK_CORE, growing_retained ? 1 : 0);
- assert(new_addr == NULL || !slab);
- assert(pad == 0 || !slab);
- assert(!*zero || !slab);
+ assert(!guarded || expand_edata == NULL);
+ assert(!guarded || alignment <= PAGE);
- rtree_ctx_t rtree_ctx_fallback;
- rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);
+ malloc_mutex_lock(tsdn, &ecache->mtx);
- extent_t *extent = extent_recycle_extract(tsdn, arena, r_extent_hooks,
- rtree_ctx, extents, new_addr, size, pad, alignment, slab,
- growing_retained);
- if (extent == NULL) {
+ edata_t *edata = extent_recycle_extract(tsdn, pac, ehooks, ecache,
+ expand_edata, size, alignment, guarded);
+ if (edata == NULL) {
+ malloc_mutex_unlock(tsdn, &ecache->mtx);
return NULL;
}
- extent = extent_recycle_split(tsdn, arena, r_extent_hooks, rtree_ctx,
- extents, new_addr, size, pad, alignment, slab, szind, extent,
- growing_retained);
- if (extent == NULL) {
+ edata = extent_recycle_split(tsdn, pac, ehooks, ecache, expand_edata,
+ size, alignment, edata, growing_retained);
+ malloc_mutex_unlock(tsdn, &ecache->mtx);
+ if (edata == NULL) {
return NULL;
}
- if (*commit && !extent_committed_get(extent)) {
- if (extent_commit_impl(tsdn, arena, r_extent_hooks, extent,
- 0, extent_size_get(extent), growing_retained)) {
- extent_record(tsdn, arena, r_extent_hooks, extents,
- extent, growing_retained);
- return NULL;
- }
- if (!extent_need_manual_zero(arena)) {
- extent_zeroed_set(extent, true);
- }
- }
-
- if (extent_committed_get(extent)) {
- *commit = true;
- }
- if (extent_zeroed_get(extent)) {
- *zero = true;
- }
-
- if (pad != 0) {
- extent_addr_randomize(tsdn, extent, alignment);
- }
- assert(extent_state_get(extent) == extent_state_active);
- if (slab) {
- extent_slab_set(extent, slab);
- extent_interior_register(tsdn, rtree_ctx, extent, szind);
- }
-
- if (*zero) {
- void *addr = extent_base_get(extent);
- if (!extent_zeroed_get(extent)) {
- size_t size = extent_size_get(extent);
- if (extent_need_manual_zero(arena) ||
- pages_purge_forced(addr, size)) {
- memset(addr, 0, size);
- }
- } else if (config_debug) {
- size_t *p = (size_t *)(uintptr_t)addr;
- /* Check the first page only. */
- for (size_t i = 0; i < PAGE / sizeof(size_t); i++) {
- assert(p[i] == 0);
- }
- }
- }
- return extent;
-}
-
-/*
- * If the caller specifies (!*zero), it is still possible to receive zeroed
- * memory, in which case *zero is toggled to true. arena_extent_alloc() takes
- * advantage of this to avoid demanding zeroed extents, but taking advantage of
- * them if they are returned.
- */
-static void *
-extent_alloc_core(tsdn_t *tsdn, arena_t *arena, void *new_addr, size_t size,
- size_t alignment, bool *zero, bool *commit, dss_prec_t dss_prec) {
- void *ret;
-
- assert(size != 0);
- assert(alignment != 0);
-
- /* "primary" dss. */
- if (have_dss && dss_prec == dss_prec_primary && (ret =
- extent_alloc_dss(tsdn, arena, new_addr, size, alignment, zero,
- commit)) != NULL) {
- return ret;
- }
- /* mmap. */
- if ((ret = extent_alloc_mmap(new_addr, size, alignment, zero, commit))
- != NULL) {
- return ret;
- }
- /* "secondary" dss. */
- if (have_dss && dss_prec == dss_prec_secondary && (ret =
- extent_alloc_dss(tsdn, arena, new_addr, size, alignment, zero,
- commit)) != NULL) {
- return ret;
- }
-
- /* All strategies for allocation failed. */
- return NULL;
-}
-
-static void *
-extent_alloc_default_impl(tsdn_t *tsdn, arena_t *arena, void *new_addr,
- size_t size, size_t alignment, bool *zero, bool *commit) {
- void *ret = extent_alloc_core(tsdn, arena, new_addr, size, alignment, zero,
- commit, (dss_prec_t)atomic_load_u(&arena->dss_prec,
- ATOMIC_RELAXED));
- if (have_madvise_huge && ret) {
- pages_set_thp_state(ret, size);
+ assert(edata_state_get(edata) == extent_state_active);
+ if (extent_commit_zero(tsdn, ehooks, edata, *commit, zero,
+ growing_retained)) {
+ extent_record(tsdn, pac, ehooks, ecache, edata);
+ return NULL;
}
- return ret;
-}
-
-static void *
-extent_alloc_default(extent_hooks_t *extent_hooks, void *new_addr, size_t size,
- size_t alignment, bool *zero, bool *commit, unsigned arena_ind) {
- tsdn_t *tsdn;
- arena_t *arena;
-
- tsdn = tsdn_fetch();
- arena = arena_get(tsdn, arena_ind, false);
- /*
- * The arena we're allocating on behalf of must have been initialized
- * already.
- */
- assert(arena != NULL);
-
- return extent_alloc_default_impl(tsdn, arena, new_addr, size,
- ALIGNMENT_CEILING(alignment, PAGE), zero, commit);
-}
-
-static void
-extent_hook_pre_reentrancy(tsdn_t *tsdn, arena_t *arena) {
- tsd_t *tsd = tsdn_null(tsdn) ? tsd_fetch() : tsdn_tsd(tsdn);
- if (arena == arena_get(tsd_tsdn(tsd), 0, false)) {
+ if (edata_committed_get(edata)) {
/*
- * The only legitimate case of customized extent hooks for a0 is
- * hooks with no allocation activities. One such example is to
- * place metadata on pre-allocated resources such as huge pages.
- * In that case, rely on reentrancy_level checks to catch
- * infinite recursions.
+ * This reverses the purpose of this variable - previously it
+ * was treated as an input parameter, now it turns into an
+ * output parameter, reporting if the edata has actually been
+ * committed.
*/
- pre_reentrancy(tsd, NULL);
- } else {
- pre_reentrancy(tsd, arena);
+ *commit = true;
}
-}
-
-static void
-extent_hook_post_reentrancy(tsdn_t *tsdn) {
- tsd_t *tsd = tsdn_null(tsdn) ? tsd_fetch() : tsdn_tsd(tsdn);
- post_reentrancy(tsd);
+ return edata;
}
/*
* If virtual memory is retained, create increasingly larger extents from which
* to split requested extents in order to limit the total number of disjoint
- * virtual memory ranges retained by each arena.
+ * virtual memory ranges retained by each shard.
*/
-static extent_t *
-extent_grow_retained(tsdn_t *tsdn, arena_t *arena,
- extent_hooks_t **r_extent_hooks, size_t size, size_t pad, size_t alignment,
- bool slab, szind_t szind, bool *zero, bool *commit) {
- malloc_mutex_assert_owner(tsdn, &arena->extent_grow_mtx);
- assert(pad == 0 || !slab);
- assert(!*zero || !slab);
-
- size_t esize = size + pad;
- size_t alloc_size_min = esize + PAGE_CEILING(alignment) - PAGE;
+static edata_t *
+extent_grow_retained(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
+ size_t size, size_t alignment, bool zero, bool *commit) {
+ malloc_mutex_assert_owner(tsdn, &pac->grow_mtx);
+
+ size_t alloc_size_min = size + PAGE_CEILING(alignment) - PAGE;
/* Beware size_t wrap-around. */
- if (alloc_size_min < esize) {
+ if (alloc_size_min < size) {
goto label_err;
}
/*
* Find the next extent size in the series that would be large enough to
* satisfy this request.
*/
- pszind_t egn_skip = 0;
- size_t alloc_size = sz_pind2sz(arena->extent_grow_next + egn_skip);
- while (alloc_size < alloc_size_min) {
- egn_skip++;
- if (arena->extent_grow_next + egn_skip >=
- sz_psz2ind(SC_LARGE_MAXCLASS)) {
- /* Outside legal range. */
- goto label_err;
- }
- alloc_size = sz_pind2sz(arena->extent_grow_next + egn_skip);
+ size_t alloc_size;
+ pszind_t exp_grow_skip;
+ bool err = exp_grow_size_prepare(&pac->exp_grow, alloc_size_min,
+ &alloc_size, &exp_grow_skip);
+ if (err) {
+ goto label_err;
}
- extent_t *extent = extent_alloc(tsdn, arena);
- if (extent == NULL) {
+ edata_t *edata = edata_cache_get(tsdn, pac->edata_cache);
+ if (edata == NULL) {
goto label_err;
}
bool zeroed = false;
bool committed = false;
- void *ptr;
- if (*r_extent_hooks == &extent_hooks_default) {
- ptr = extent_alloc_default_impl(tsdn, arena, NULL,
- alloc_size, PAGE, &zeroed, &committed);
- } else {
- extent_hook_pre_reentrancy(tsdn, arena);
- ptr = (*r_extent_hooks)->alloc(*r_extent_hooks, NULL,
- alloc_size, PAGE, &zeroed, &committed,
- arena_ind_get(arena));
- extent_hook_post_reentrancy(tsdn);
- }
+ void *ptr = ehooks_alloc(tsdn, ehooks, NULL, alloc_size, PAGE, &zeroed,
+ &committed);
- extent_init(extent, arena, ptr, alloc_size, false, SC_NSIZES,
- arena_extent_sn_next(arena), extent_state_active, zeroed,
- committed, true, EXTENT_IS_HEAD);
if (ptr == NULL) {
- extent_dalloc(tsdn, arena, extent);
+ edata_cache_put(tsdn, pac->edata_cache, edata);
goto label_err;
}
- if (extent_register_no_gdump_add(tsdn, extent)) {
- extent_dalloc(tsdn, arena, extent);
+ edata_init(edata, ecache_ind_get(&pac->ecache_retained), ptr,
+ alloc_size, false, SC_NSIZES, extent_sn_next(pac),
+ extent_state_active, zeroed, committed, EXTENT_PAI_PAC,
+ EXTENT_IS_HEAD);
+
+ if (extent_register_no_gdump_add(tsdn, pac, edata)) {
+ edata_cache_put(tsdn, pac->edata_cache, edata);
goto label_err;
}
- if (extent_zeroed_get(extent) && extent_committed_get(extent)) {
- *zero = true;
- }
- if (extent_committed_get(extent)) {
+ if (edata_committed_get(edata)) {
*commit = true;
}
- rtree_ctx_t rtree_ctx_fallback;
- rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);
+ edata_t *lead;
+ edata_t *trail;
+ edata_t *to_leak JEMALLOC_CC_SILENCE_INIT(NULL);
+ edata_t *to_salvage JEMALLOC_CC_SILENCE_INIT(NULL);
- extent_t *lead;
- extent_t *trail;
- extent_t *to_leak;
- extent_t *to_salvage;
- extent_split_interior_result_t result = extent_split_interior(
- tsdn, arena, r_extent_hooks, rtree_ctx, &extent, &lead, &trail,
- &to_leak, &to_salvage, NULL, size, pad, alignment, slab, szind,
- true);
+ extent_split_interior_result_t result = extent_split_interior(tsdn,
+ pac, ehooks, &edata, &lead, &trail, &to_leak, &to_salvage, NULL,
+ size, alignment);
if (result == extent_split_interior_ok) {
if (lead != NULL) {
- extent_record(tsdn, arena, r_extent_hooks,
- &arena->extents_retained, lead, true);
+ extent_record(tsdn, pac, ehooks, &pac->ecache_retained,
+ lead);
}
if (trail != NULL) {
- extent_record(tsdn, arena, r_extent_hooks,
- &arena->extents_retained, trail, true);
+ extent_record(tsdn, pac, ehooks, &pac->ecache_retained,
+ trail);
}
} else {
/*
@@ -1393,26 +719,32 @@ extent_grow_retained(tsdn_t *tsdn, arena_t *arena,
if (config_prof) {
extent_gdump_add(tsdn, to_salvage);
}
- extent_record(tsdn, arena, r_extent_hooks,
- &arena->extents_retained, to_salvage, true);
+ extent_record(tsdn, pac, ehooks, &pac->ecache_retained,
+ to_salvage);
}
if (to_leak != NULL) {
- extent_deregister_no_gdump_sub(tsdn, to_leak);
- extents_abandon_vm(tsdn, arena, r_extent_hooks,
- &arena->extents_retained, to_leak, true);
+ extent_deregister_no_gdump_sub(tsdn, pac, to_leak);
+ extents_abandon_vm(tsdn, pac, ehooks,
+ &pac->ecache_retained, to_leak, true);
}
goto label_err;
}
- if (*commit && !extent_committed_get(extent)) {
- if (extent_commit_impl(tsdn, arena, r_extent_hooks, extent, 0,
- extent_size_get(extent), true)) {
- extent_record(tsdn, arena, r_extent_hooks,
- &arena->extents_retained, extent, true);
+ if (*commit && !edata_committed_get(edata)) {
+ if (extent_commit_impl(tsdn, ehooks, edata, 0,
+ edata_size_get(edata), true)) {
+ extent_record(tsdn, pac, ehooks,
+ &pac->ecache_retained, edata);
goto label_err;
}
- if (!extent_need_manual_zero(arena)) {
- extent_zeroed_set(extent, true);
+ /* A successful commit should return zeroed memory. */
+ if (config_debug) {
+ void *addr = edata_addr_get(edata);
+ size_t *p = (size_t *)(uintptr_t)addr;
+ /* Check the first page only. */
+ for (size_t i = 0; i < PAGE / sizeof(size_t); i++) {
+ assert(p[i] == 0);
+ }
}
}
@@ -1420,187 +752,74 @@ extent_grow_retained(tsdn_t *tsdn, arena_t *arena,
* Increment extent_grow_next if doing so wouldn't exceed the allowed
* range.
*/
- if (arena->extent_grow_next + egn_skip + 1 <=
- arena->retain_grow_limit) {
- arena->extent_grow_next += egn_skip + 1;
- } else {
- arena->extent_grow_next = arena->retain_grow_limit;
- }
/* All opportunities for failure are past. */
- malloc_mutex_unlock(tsdn, &arena->extent_grow_mtx);
+ exp_grow_size_commit(&pac->exp_grow, exp_grow_skip);
+ malloc_mutex_unlock(tsdn, &pac->grow_mtx);
if (config_prof) {
/* Adjust gdump stats now that extent is final size. */
- extent_gdump_add(tsdn, extent);
- }
- if (pad != 0) {
- extent_addr_randomize(tsdn, extent, alignment);
+ extent_gdump_add(tsdn, edata);
}
- if (slab) {
- rtree_ctx_t rtree_ctx_fallback;
- rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn,
- &rtree_ctx_fallback);
-
- extent_slab_set(extent, true);
- extent_interior_register(tsdn, rtree_ctx, extent, szind);
- }
- if (*zero && !extent_zeroed_get(extent)) {
- void *addr = extent_base_get(extent);
- size_t size = extent_size_get(extent);
- if (extent_need_manual_zero(arena) ||
- pages_purge_forced(addr, size)) {
- memset(addr, 0, size);
- }
+ if (zero && !edata_zeroed_get(edata)) {
+ ehooks_zero(tsdn, ehooks, edata_base_get(edata),
+ edata_size_get(edata));
}
-
- return extent;
+ return edata;
label_err:
- malloc_mutex_unlock(tsdn, &arena->extent_grow_mtx);
+ malloc_mutex_unlock(tsdn, &pac->grow_mtx);
return NULL;
}
-static extent_t *
-extent_alloc_retained(tsdn_t *tsdn, arena_t *arena,
- extent_hooks_t **r_extent_hooks, void *new_addr, size_t size, size_t pad,
- size_t alignment, bool slab, szind_t szind, bool *zero, bool *commit) {
+static edata_t *
+extent_alloc_retained(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
+ edata_t *expand_edata, size_t size, size_t alignment, bool zero,
+ bool *commit, bool guarded) {
assert(size != 0);
assert(alignment != 0);
- malloc_mutex_lock(tsdn, &arena->extent_grow_mtx);
+ malloc_mutex_lock(tsdn, &pac->grow_mtx);
- extent_t *extent = extent_recycle(tsdn, arena, r_extent_hooks,
- &arena->extents_retained, new_addr, size, pad, alignment, slab,
- szind, zero, commit, true);
- if (extent != NULL) {
- malloc_mutex_unlock(tsdn, &arena->extent_grow_mtx);
+ edata_t *edata = extent_recycle(tsdn, pac, ehooks,
+ &pac->ecache_retained, expand_edata, size, alignment, zero, commit,
+ /* growing_retained */ true, guarded);
+ if (edata != NULL) {
+ malloc_mutex_unlock(tsdn, &pac->grow_mtx);
if (config_prof) {
- extent_gdump_add(tsdn, extent);
+ extent_gdump_add(tsdn, edata);
}
- } else if (opt_retain && new_addr == NULL) {
- extent = extent_grow_retained(tsdn, arena, r_extent_hooks, size,
- pad, alignment, slab, szind, zero, commit);
- /* extent_grow_retained() always releases extent_grow_mtx. */
+ } else if (opt_retain && expand_edata == NULL && !guarded) {
+ edata = extent_grow_retained(tsdn, pac, ehooks, size,
+ alignment, zero, commit);
+ /* extent_grow_retained() always releases pac->grow_mtx. */
} else {
- malloc_mutex_unlock(tsdn, &arena->extent_grow_mtx);
- }
- malloc_mutex_assert_not_owner(tsdn, &arena->extent_grow_mtx);
-
- return extent;
-}
-
-static extent_t *
-extent_alloc_wrapper_hard(tsdn_t *tsdn, arena_t *arena,
- extent_hooks_t **r_extent_hooks, void *new_addr, size_t size, size_t pad,
- size_t alignment, bool slab, szind_t szind, bool *zero, bool *commit) {
- size_t esize = size + pad;
- extent_t *extent = extent_alloc(tsdn, arena);
- if (extent == NULL) {
- return NULL;
- }
- void *addr;
- size_t palignment = ALIGNMENT_CEILING(alignment, PAGE);
- if (*r_extent_hooks == &extent_hooks_default) {
- /* Call directly to propagate tsdn. */
- addr = extent_alloc_default_impl(tsdn, arena, new_addr, esize,
- palignment, zero, commit);
- } else {
- extent_hook_pre_reentrancy(tsdn, arena);
- addr = (*r_extent_hooks)->alloc(*r_extent_hooks, new_addr,
- esize, palignment, zero, commit, arena_ind_get(arena));
- extent_hook_post_reentrancy(tsdn);
- }
- if (addr == NULL) {
- extent_dalloc(tsdn, arena, extent);
- return NULL;
- }
- extent_init(extent, arena, addr, esize, slab, szind,
- arena_extent_sn_next(arena), extent_state_active, *zero, *commit,
- true, EXTENT_NOT_HEAD);
- if (pad != 0) {
- extent_addr_randomize(tsdn, extent, alignment);
- }
- if (extent_register(tsdn, extent)) {
- extent_dalloc(tsdn, arena, extent);
- return NULL;
- }
-
- return extent;
-}
-
-extent_t *
-extent_alloc_wrapper(tsdn_t *tsdn, arena_t *arena,
- extent_hooks_t **r_extent_hooks, void *new_addr, size_t size, size_t pad,
- size_t alignment, bool slab, szind_t szind, bool *zero, bool *commit) {
- witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
- WITNESS_RANK_CORE, 0);
-
- extent_hooks_assure_initialized(arena, r_extent_hooks);
-
- extent_t *extent = extent_alloc_retained(tsdn, arena, r_extent_hooks,
- new_addr, size, pad, alignment, slab, szind, zero, commit);
- if (extent == NULL) {
- if (opt_retain && new_addr != NULL) {
- /*
- * When retain is enabled and new_addr is set, we do not
- * attempt extent_alloc_wrapper_hard which does mmap
- * that is very unlikely to succeed (unless it happens
- * to be at the end).
- */
- return NULL;
- }
- extent = extent_alloc_wrapper_hard(tsdn, arena, r_extent_hooks,
- new_addr, size, pad, alignment, slab, szind, zero, commit);
- }
-
- assert(extent == NULL || extent_dumpable_get(extent));
- return extent;
-}
-
-static bool
-extent_can_coalesce(arena_t *arena, extents_t *extents, const extent_t *inner,
- const extent_t *outer) {
- assert(extent_arena_get(inner) == arena);
- if (extent_arena_get(outer) != arena) {
- return false;
- }
-
- assert(extent_state_get(inner) == extent_state_active);
- if (extent_state_get(outer) != extents->state) {
- return false;
- }
-
- if (extent_committed_get(inner) != extent_committed_get(outer)) {
- return false;
+ malloc_mutex_unlock(tsdn, &pac->grow_mtx);
}
+ malloc_mutex_assert_not_owner(tsdn, &pac->grow_mtx);
- return true;
+ return edata;
}
static bool
-extent_coalesce(tsdn_t *tsdn, arena_t *arena, extent_hooks_t **r_extent_hooks,
- extents_t *extents, extent_t *inner, extent_t *outer, bool forward,
- bool growing_retained) {
- assert(extent_can_coalesce(arena, extents, inner, outer));
-
- extent_activate_locked(tsdn, arena, extents, outer);
-
- malloc_mutex_unlock(tsdn, &extents->mtx);
- bool err = extent_merge_impl(tsdn, arena, r_extent_hooks,
- forward ? inner : outer, forward ? outer : inner, growing_retained);
- malloc_mutex_lock(tsdn, &extents->mtx);
-
+extent_coalesce(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, ecache_t *ecache,
+ edata_t *inner, edata_t *outer, bool forward) {
+ extent_assert_can_coalesce(inner, outer);
+ eset_remove(&ecache->eset, outer);
+
+ bool err = extent_merge_impl(tsdn, pac, ehooks,
+ forward ? inner : outer, forward ? outer : inner,
+ /* holding_core_locks */ true);
if (err) {
- extent_deactivate_locked(tsdn, arena, extents, outer);
+ extent_deactivate_check_state_locked(tsdn, pac, ecache, outer,
+ extent_state_merging);
}
return err;
}
-static extent_t *
-extent_try_coalesce_impl(tsdn_t *tsdn, arena_t *arena,
- extent_hooks_t **r_extent_hooks, rtree_ctx_t *rtree_ctx, extents_t *extents,
- extent_t *extent, bool *coalesced, bool growing_retained,
- bool inactive_only) {
+static edata_t *
+extent_try_coalesce_impl(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
+ ecache_t *ecache, edata_t *edata, bool *coalesced) {
+ assert(!edata_guarded_get(edata));
/*
* We avoid checking / locking inactive neighbors for large size
* classes, since they are eagerly coalesced on deallocation which can
@@ -1615,470 +834,333 @@ extent_try_coalesce_impl(tsdn_t *tsdn, arena_t *arena,
again = false;
/* Try to coalesce forward. */
- extent_t *next = extent_lock_from_addr(tsdn, rtree_ctx,
- extent_past_get(extent), inactive_only);
+ edata_t *next = emap_try_acquire_edata_neighbor(tsdn, pac->emap,
+ edata, EXTENT_PAI_PAC, ecache->state, /* forward */ true);
if (next != NULL) {
- /*
- * extents->mtx only protects against races for
- * like-state extents, so call extent_can_coalesce()
- * before releasing next's pool lock.
- */
- bool can_coalesce = extent_can_coalesce(arena, extents,
- extent, next);
-
- extent_unlock(tsdn, next);
-
- if (can_coalesce && !extent_coalesce(tsdn, arena,
- r_extent_hooks, extents, extent, next, true,
- growing_retained)) {
- if (extents->delay_coalesce) {
+ if (!extent_coalesce(tsdn, pac, ehooks, ecache, edata,
+ next, true)) {
+ if (ecache->delay_coalesce) {
/* Do minimal coalescing. */
*coalesced = true;
- return extent;
+ return edata;
}
again = true;
}
}
/* Try to coalesce backward. */
- extent_t *prev = NULL;
- if (extent_before_get(extent) != NULL) {
- prev = extent_lock_from_addr(tsdn, rtree_ctx,
- extent_before_get(extent), inactive_only);
- }
+ edata_t *prev = emap_try_acquire_edata_neighbor(tsdn, pac->emap,
+ edata, EXTENT_PAI_PAC, ecache->state, /* forward */ false);
if (prev != NULL) {
- bool can_coalesce = extent_can_coalesce(arena, extents,
- extent, prev);
- extent_unlock(tsdn, prev);
-
- if (can_coalesce && !extent_coalesce(tsdn, arena,
- r_extent_hooks, extents, extent, prev, false,
- growing_retained)) {
- extent = prev;
- if (extents->delay_coalesce) {
+ if (!extent_coalesce(tsdn, pac, ehooks, ecache, edata,
+ prev, false)) {
+ edata = prev;
+ if (ecache->delay_coalesce) {
/* Do minimal coalescing. */
*coalesced = true;
- return extent;
+ return edata;
}
again = true;
}
}
} while (again);
- if (extents->delay_coalesce) {
+ if (ecache->delay_coalesce) {
*coalesced = false;
}
- return extent;
+ return edata;
+}
+
+static edata_t *
+extent_try_coalesce(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
+ ecache_t *ecache, edata_t *edata, bool *coalesced) {
+ return extent_try_coalesce_impl(tsdn, pac, ehooks, ecache, edata,
+ coalesced);
}
-static extent_t *
-extent_try_coalesce(tsdn_t *tsdn, arena_t *arena,
- extent_hooks_t **r_extent_hooks, rtree_ctx_t *rtree_ctx, extents_t *extents,
- extent_t *extent, bool *coalesced, bool growing_retained) {
- return extent_try_coalesce_impl(tsdn, arena, r_extent_hooks, rtree_ctx,
- extents, extent, coalesced, growing_retained, false);
+static edata_t *
+extent_try_coalesce_large(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
+ ecache_t *ecache, edata_t *edata, bool *coalesced) {
+ return extent_try_coalesce_impl(tsdn, pac, ehooks, ecache, edata,
+ coalesced);
}
-static extent_t *
-extent_try_coalesce_large(tsdn_t *tsdn, arena_t *arena,
- extent_hooks_t **r_extent_hooks, rtree_ctx_t *rtree_ctx, extents_t *extents,
- extent_t *extent, bool *coalesced, bool growing_retained) {
- return extent_try_coalesce_impl(tsdn, arena, r_extent_hooks, rtree_ctx,
- extents, extent, coalesced, growing_retained, true);
+/* Purge a single extent to retained / unmapped directly. */
+static void
+extent_maximally_purge(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
+ edata_t *edata) {
+ size_t extent_size = edata_size_get(edata);
+ extent_dalloc_wrapper(tsdn, pac, ehooks, edata);
+ if (config_stats) {
+ /* Update stats accordingly. */
+ LOCKEDINT_MTX_LOCK(tsdn, *pac->stats_mtx);
+ locked_inc_u64(tsdn,
+ LOCKEDINT_MTX(*pac->stats_mtx),
+ &pac->stats->decay_dirty.nmadvise, 1);
+ locked_inc_u64(tsdn,
+ LOCKEDINT_MTX(*pac->stats_mtx),
+ &pac->stats->decay_dirty.purged,
+ extent_size >> LG_PAGE);
+ LOCKEDINT_MTX_UNLOCK(tsdn, *pac->stats_mtx);
+ atomic_fetch_sub_zu(&pac->stats->pac_mapped, extent_size,
+ ATOMIC_RELAXED);
+ }
}
/*
* Does the metadata management portions of putting an unused extent into the
- * given extents_t (coalesces, deregisters slab interiors, the heap operations).
+ * given ecache_t (coalesces and inserts into the eset).
*/
-static void
-extent_record(tsdn_t *tsdn, arena_t *arena, extent_hooks_t **r_extent_hooks,
- extents_t *extents, extent_t *extent, bool growing_retained) {
- rtree_ctx_t rtree_ctx_fallback;
- rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);
-
- assert((extents_state_get(extents) != extent_state_dirty &&
- extents_state_get(extents) != extent_state_muzzy) ||
- !extent_zeroed_get(extent));
-
- malloc_mutex_lock(tsdn, &extents->mtx);
- extent_hooks_assure_initialized(arena, r_extent_hooks);
-
- extent_szind_set(extent, SC_NSIZES);
- if (extent_slab_get(extent)) {
- extent_interior_deregister(tsdn, rtree_ctx, extent);
- extent_slab_set(extent, false);
- }
+void
+extent_record(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, ecache_t *ecache,
+ edata_t *edata) {
+ assert((ecache->state != extent_state_dirty &&
+ ecache->state != extent_state_muzzy) ||
+ !edata_zeroed_get(edata));
+
+ malloc_mutex_lock(tsdn, &ecache->mtx);
- assert(rtree_extent_read(tsdn, &extents_rtree, rtree_ctx,
- (uintptr_t)extent_base_get(extent), true) == extent);
+ emap_assert_mapped(tsdn, pac->emap, edata);
- if (!extents->delay_coalesce) {
- extent = extent_try_coalesce(tsdn, arena, r_extent_hooks,
- rtree_ctx, extents, extent, NULL, growing_retained);
- } else if (extent_size_get(extent) >= SC_LARGE_MINCLASS) {
- assert(extents == &arena->extents_dirty);
+ if (edata_guarded_get(edata)) {
+ goto label_skip_coalesce;
+ }
+ if (!ecache->delay_coalesce) {
+ edata = extent_try_coalesce(tsdn, pac, ehooks, ecache, edata,
+ NULL);
+ } else if (edata_size_get(edata) >= SC_LARGE_MINCLASS) {
+ assert(ecache == &pac->ecache_dirty);
/* Always coalesce large extents eagerly. */
bool coalesced;
do {
- assert(extent_state_get(extent) == extent_state_active);
- extent = extent_try_coalesce_large(tsdn, arena,
- r_extent_hooks, rtree_ctx, extents, extent,
- &coalesced, growing_retained);
+ assert(edata_state_get(edata) == extent_state_active);
+ edata = extent_try_coalesce_large(tsdn, pac, ehooks,
+ ecache, edata, &coalesced);
} while (coalesced);
- if (extent_size_get(extent) >= oversize_threshold) {
+ if (edata_size_get(edata) >=
+ atomic_load_zu(&pac->oversize_threshold, ATOMIC_RELAXED)
+ && extent_may_force_decay(pac)) {
/* Shortcut to purge the oversize extent eagerly. */
- malloc_mutex_unlock(tsdn, &extents->mtx);
- arena_decay_extent(tsdn, arena, r_extent_hooks, extent);
+ malloc_mutex_unlock(tsdn, &ecache->mtx);
+ extent_maximally_purge(tsdn, pac, ehooks, edata);
return;
}
}
- extent_deactivate_locked(tsdn, arena, extents, extent);
+label_skip_coalesce:
+ extent_deactivate_locked(tsdn, pac, ecache, edata);
- malloc_mutex_unlock(tsdn, &extents->mtx);
+ malloc_mutex_unlock(tsdn, &ecache->mtx);
}
void
-extent_dalloc_gap(tsdn_t *tsdn, arena_t *arena, extent_t *extent) {
- extent_hooks_t *extent_hooks = EXTENT_HOOKS_INITIALIZER;
-
+extent_dalloc_gap(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
+ edata_t *edata) {
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
WITNESS_RANK_CORE, 0);
- if (extent_register(tsdn, extent)) {
- extent_dalloc(tsdn, arena, extent);
+ if (extent_register(tsdn, pac, edata)) {
+ edata_cache_put(tsdn, pac->edata_cache, edata);
return;
}
- extent_dalloc_wrapper(tsdn, arena, &extent_hooks, extent);
+ extent_dalloc_wrapper(tsdn, pac, ehooks, edata);
}
static bool
-extent_may_dalloc(void) {
- /* With retain enabled, the default dalloc always fails. */
- return !opt_retain;
-}
-
-static bool
-extent_dalloc_default_impl(void *addr, size_t size) {
- if (!have_dss || !extent_in_dss(addr)) {
- return extent_dalloc_mmap(addr, size);
- }
- return true;
-}
-
-static bool
-extent_dalloc_default(extent_hooks_t *extent_hooks, void *addr, size_t size,
- bool committed, unsigned arena_ind) {
- return extent_dalloc_default_impl(addr, size);
-}
-
-static bool
-extent_dalloc_wrapper_try(tsdn_t *tsdn, arena_t *arena,
- extent_hooks_t **r_extent_hooks, extent_t *extent) {
+extent_dalloc_wrapper_try(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
+ edata_t *edata) {
bool err;
- assert(extent_base_get(extent) != NULL);
- assert(extent_size_get(extent) != 0);
+ assert(edata_base_get(edata) != NULL);
+ assert(edata_size_get(edata) != 0);
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
WITNESS_RANK_CORE, 0);
- extent_addr_set(extent, extent_base_get(extent));
+ edata_addr_set(edata, edata_base_get(edata));
- extent_hooks_assure_initialized(arena, r_extent_hooks);
/* Try to deallocate. */
- if (*r_extent_hooks == &extent_hooks_default) {
- /* Call directly to propagate tsdn. */
- err = extent_dalloc_default_impl(extent_base_get(extent),
- extent_size_get(extent));
- } else {
- extent_hook_pre_reentrancy(tsdn, arena);
- err = ((*r_extent_hooks)->dalloc == NULL ||
- (*r_extent_hooks)->dalloc(*r_extent_hooks,
- extent_base_get(extent), extent_size_get(extent),
- extent_committed_get(extent), arena_ind_get(arena)));
- extent_hook_post_reentrancy(tsdn);
- }
+ err = ehooks_dalloc(tsdn, ehooks, edata_base_get(edata),
+ edata_size_get(edata), edata_committed_get(edata));
if (!err) {
- extent_dalloc(tsdn, arena, extent);
+ edata_cache_put(tsdn, pac->edata_cache, edata);
}
return err;
}
+edata_t *
+extent_alloc_wrapper(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
+ void *new_addr, size_t size, size_t alignment, bool zero, bool *commit,
+ bool growing_retained) {
+ witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
+ WITNESS_RANK_CORE, growing_retained ? 1 : 0);
+
+ edata_t *edata = edata_cache_get(tsdn, pac->edata_cache);
+ if (edata == NULL) {
+ return NULL;
+ }
+ size_t palignment = ALIGNMENT_CEILING(alignment, PAGE);
+ void *addr = ehooks_alloc(tsdn, ehooks, new_addr, size, palignment,
+ &zero, commit);
+ if (addr == NULL) {
+ edata_cache_put(tsdn, pac->edata_cache, edata);
+ return NULL;
+ }
+ edata_init(edata, ecache_ind_get(&pac->ecache_dirty), addr,
+ size, /* slab */ false, SC_NSIZES, extent_sn_next(pac),
+ extent_state_active, zero, *commit, EXTENT_PAI_PAC,
+ opt_retain ? EXTENT_IS_HEAD : EXTENT_NOT_HEAD);
+ /*
+ * Retained memory is not counted towards gdump. Only if an extent is
+ * allocated as a separate mapping, i.e. growing_retained is false, then
+ * gdump should be updated.
+ */
+ bool gdump_add = !growing_retained;
+ if (extent_register_impl(tsdn, pac, edata, gdump_add)) {
+ edata_cache_put(tsdn, pac->edata_cache, edata);
+ return NULL;
+ }
+
+ return edata;
+}
+
void
-extent_dalloc_wrapper(tsdn_t *tsdn, arena_t *arena,
- extent_hooks_t **r_extent_hooks, extent_t *extent) {
- assert(extent_dumpable_get(extent));
+extent_dalloc_wrapper(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
+ edata_t *edata) {
+ assert(edata_pai_get(edata) == EXTENT_PAI_PAC);
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
WITNESS_RANK_CORE, 0);
/* Avoid calling the default extent_dalloc unless have to. */
- if (*r_extent_hooks != &extent_hooks_default || extent_may_dalloc()) {
+ if (!ehooks_dalloc_will_fail(ehooks)) {
+ /* Remove guard pages for dalloc / unmap. */
+ if (edata_guarded_get(edata)) {
+ assert(ehooks_are_default(ehooks));
+ san_unguard_pages_two_sided(tsdn, ehooks, edata,
+ pac->emap);
+ }
/*
* Deregister first to avoid a race with other allocating
* threads, and reregister if deallocation fails.
*/
- extent_deregister(tsdn, extent);
- if (!extent_dalloc_wrapper_try(tsdn, arena, r_extent_hooks,
- extent)) {
+ extent_deregister(tsdn, pac, edata);
+ if (!extent_dalloc_wrapper_try(tsdn, pac, ehooks, edata)) {
return;
}
- extent_reregister(tsdn, extent);
+ extent_reregister(tsdn, pac, edata);
}
- if (*r_extent_hooks != &extent_hooks_default) {
- extent_hook_pre_reentrancy(tsdn, arena);
- }
/* Try to decommit; purge if that fails. */
bool zeroed;
- if (!extent_committed_get(extent)) {
+ if (!edata_committed_get(edata)) {
zeroed = true;
- } else if (!extent_decommit_wrapper(tsdn, arena, r_extent_hooks, extent,
- 0, extent_size_get(extent))) {
+ } else if (!extent_decommit_wrapper(tsdn, ehooks, edata, 0,
+ edata_size_get(edata))) {
zeroed = true;
- } else if ((*r_extent_hooks)->purge_forced != NULL &&
- !(*r_extent_hooks)->purge_forced(*r_extent_hooks,
- extent_base_get(extent), extent_size_get(extent), 0,
- extent_size_get(extent), arena_ind_get(arena))) {
+ } else if (!ehooks_purge_forced(tsdn, ehooks, edata_base_get(edata),
+ edata_size_get(edata), 0, edata_size_get(edata))) {
zeroed = true;
- } else if (extent_state_get(extent) == extent_state_muzzy ||
- ((*r_extent_hooks)->purge_lazy != NULL &&
- !(*r_extent_hooks)->purge_lazy(*r_extent_hooks,
- extent_base_get(extent), extent_size_get(extent), 0,
- extent_size_get(extent), arena_ind_get(arena)))) {
+ } else if (edata_state_get(edata) == extent_state_muzzy ||
+ !ehooks_purge_lazy(tsdn, ehooks, edata_base_get(edata),
+ edata_size_get(edata), 0, edata_size_get(edata))) {
zeroed = false;
} else {
zeroed = false;
}
- if (*r_extent_hooks != &extent_hooks_default) {
- extent_hook_post_reentrancy(tsdn);
- }
- extent_zeroed_set(extent, zeroed);
+ edata_zeroed_set(edata, zeroed);
if (config_prof) {
- extent_gdump_sub(tsdn, extent);
+ extent_gdump_sub(tsdn, edata);
}
- extent_record(tsdn, arena, r_extent_hooks, &arena->extents_retained,
- extent, false);
-}
-
-static void
-extent_destroy_default_impl(void *addr, size_t size) {
- if (!have_dss || !extent_in_dss(addr)) {
- pages_unmap(addr, size);
- }
-}
-
-static void
-extent_destroy_default(extent_hooks_t *extent_hooks, void *addr, size_t size,
- bool committed, unsigned arena_ind) {
- extent_destroy_default_impl(addr, size);
+ extent_record(tsdn, pac, ehooks, &pac->ecache_retained, edata);
}
void
-extent_destroy_wrapper(tsdn_t *tsdn, arena_t *arena,
- extent_hooks_t **r_extent_hooks, extent_t *extent) {
- assert(extent_base_get(extent) != NULL);
- assert(extent_size_get(extent) != 0);
+extent_destroy_wrapper(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
+ edata_t *edata) {
+ assert(edata_base_get(edata) != NULL);
+ assert(edata_size_get(edata) != 0);
+ extent_state_t state = edata_state_get(edata);
+ assert(state == extent_state_retained || state == extent_state_active);
+ assert(emap_edata_is_acquired(tsdn, pac->emap, edata));
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
WITNESS_RANK_CORE, 0);
- /* Deregister first to avoid a race with other allocating threads. */
- extent_deregister(tsdn, extent);
-
- extent_addr_set(extent, extent_base_get(extent));
-
- extent_hooks_assure_initialized(arena, r_extent_hooks);
- /* Try to destroy; silently fail otherwise. */
- if (*r_extent_hooks == &extent_hooks_default) {
- /* Call directly to propagate tsdn. */
- extent_destroy_default_impl(extent_base_get(extent),
- extent_size_get(extent));
- } else if ((*r_extent_hooks)->destroy != NULL) {
- extent_hook_pre_reentrancy(tsdn, arena);
- (*r_extent_hooks)->destroy(*r_extent_hooks,
- extent_base_get(extent), extent_size_get(extent),
- extent_committed_get(extent), arena_ind_get(arena));
- extent_hook_post_reentrancy(tsdn);
+ if (edata_guarded_get(edata)) {
+ assert(opt_retain);
+ san_unguard_pages_pre_destroy(tsdn, ehooks, edata, pac->emap);
}
+ edata_addr_set(edata, edata_base_get(edata));
- extent_dalloc(tsdn, arena, extent);
-}
+ /* Try to destroy; silently fail otherwise. */
+ ehooks_destroy(tsdn, ehooks, edata_base_get(edata),
+ edata_size_get(edata), edata_committed_get(edata));
-static bool
-extent_commit_default(extent_hooks_t *extent_hooks, void *addr, size_t size,
- size_t offset, size_t length, unsigned arena_ind) {
- return pages_commit((void *)((uintptr_t)addr + (uintptr_t)offset),
- length);
+ edata_cache_put(tsdn, pac->edata_cache, edata);
}
static bool
-extent_commit_impl(tsdn_t *tsdn, arena_t *arena,
- extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset,
- size_t length, bool growing_retained) {
+extent_commit_impl(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata,
+ size_t offset, size_t length, bool growing_retained) {
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
WITNESS_RANK_CORE, growing_retained ? 1 : 0);
-
- extent_hooks_assure_initialized(arena, r_extent_hooks);
- if (*r_extent_hooks != &extent_hooks_default) {
- extent_hook_pre_reentrancy(tsdn, arena);
- }
- bool err = ((*r_extent_hooks)->commit == NULL ||
- (*r_extent_hooks)->commit(*r_extent_hooks, extent_base_get(extent),
- extent_size_get(extent), offset, length, arena_ind_get(arena)));
- if (*r_extent_hooks != &extent_hooks_default) {
- extent_hook_post_reentrancy(tsdn);
- }
- extent_committed_set(extent, extent_committed_get(extent) || !err);
+ bool err = ehooks_commit(tsdn, ehooks, edata_base_get(edata),
+ edata_size_get(edata), offset, length);
+ edata_committed_set(edata, edata_committed_get(edata) || !err);
return err;
}
bool
-extent_commit_wrapper(tsdn_t *tsdn, arena_t *arena,
- extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset,
- size_t length) {
- return extent_commit_impl(tsdn, arena, r_extent_hooks, extent, offset,
- length, false);
-}
-
-static bool
-extent_decommit_default(extent_hooks_t *extent_hooks, void *addr, size_t size,
- size_t offset, size_t length, unsigned arena_ind) {
- return pages_decommit((void *)((uintptr_t)addr + (uintptr_t)offset),
- length);
+extent_commit_wrapper(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata,
+ size_t offset, size_t length) {
+ return extent_commit_impl(tsdn, ehooks, edata, offset, length,
+ /* growing_retained */ false);
}
bool
-extent_decommit_wrapper(tsdn_t *tsdn, arena_t *arena,
- extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset,
- size_t length) {
+extent_decommit_wrapper(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata,
+ size_t offset, size_t length) {
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
WITNESS_RANK_CORE, 0);
-
- extent_hooks_assure_initialized(arena, r_extent_hooks);
-
- if (*r_extent_hooks != &extent_hooks_default) {
- extent_hook_pre_reentrancy(tsdn, arena);
- }
- bool err = ((*r_extent_hooks)->decommit == NULL ||
- (*r_extent_hooks)->decommit(*r_extent_hooks,
- extent_base_get(extent), extent_size_get(extent), offset, length,
- arena_ind_get(arena)));
- if (*r_extent_hooks != &extent_hooks_default) {
- extent_hook_post_reentrancy(tsdn);
- }
- extent_committed_set(extent, extent_committed_get(extent) && err);
+ bool err = ehooks_decommit(tsdn, ehooks, edata_base_get(edata),
+ edata_size_get(edata), offset, length);
+ edata_committed_set(edata, edata_committed_get(edata) && err);
return err;
}
-#ifdef PAGES_CAN_PURGE_LAZY
-static bool
-extent_purge_lazy_default(extent_hooks_t *extent_hooks, void *addr, size_t size,
- size_t offset, size_t length, unsigned arena_ind) {
- assert(addr != NULL);
- assert((offset & PAGE_MASK) == 0);
- assert(length != 0);
- assert((length & PAGE_MASK) == 0);
-
- return pages_purge_lazy((void *)((uintptr_t)addr + (uintptr_t)offset),
- length);
-}
-#endif
-
static bool
-extent_purge_lazy_impl(tsdn_t *tsdn, arena_t *arena,
- extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset,
- size_t length, bool growing_retained) {
+extent_purge_lazy_impl(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata,
+ size_t offset, size_t length, bool growing_retained) {
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
WITNESS_RANK_CORE, growing_retained ? 1 : 0);
-
- extent_hooks_assure_initialized(arena, r_extent_hooks);
-
- if ((*r_extent_hooks)->purge_lazy == NULL) {
- return true;
- }
- if (*r_extent_hooks != &extent_hooks_default) {
- extent_hook_pre_reentrancy(tsdn, arena);
- }
- bool err = (*r_extent_hooks)->purge_lazy(*r_extent_hooks,
- extent_base_get(extent), extent_size_get(extent), offset, length,
- arena_ind_get(arena));
- if (*r_extent_hooks != &extent_hooks_default) {
- extent_hook_post_reentrancy(tsdn);
- }
-
+ bool err = ehooks_purge_lazy(tsdn, ehooks, edata_base_get(edata),
+ edata_size_get(edata), offset, length);
return err;
}
bool
-extent_purge_lazy_wrapper(tsdn_t *tsdn, arena_t *arena,
- extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset,
- size_t length) {
- return extent_purge_lazy_impl(tsdn, arena, r_extent_hooks, extent,
- offset, length, false);
-}
-
-#ifdef PAGES_CAN_PURGE_FORCED
-static bool
-extent_purge_forced_default(extent_hooks_t *extent_hooks, void *addr,
- size_t size, size_t offset, size_t length, unsigned arena_ind) {
- assert(addr != NULL);
- assert((offset & PAGE_MASK) == 0);
- assert(length != 0);
- assert((length & PAGE_MASK) == 0);
-
- return pages_purge_forced((void *)((uintptr_t)addr +
- (uintptr_t)offset), length);
+extent_purge_lazy_wrapper(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata,
+ size_t offset, size_t length) {
+ return extent_purge_lazy_impl(tsdn, ehooks, edata, offset,
+ length, false);
}
-#endif
static bool
-extent_purge_forced_impl(tsdn_t *tsdn, arena_t *arena,
- extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset,
- size_t length, bool growing_retained) {
+extent_purge_forced_impl(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata,
+ size_t offset, size_t length, bool growing_retained) {
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
WITNESS_RANK_CORE, growing_retained ? 1 : 0);
-
- extent_hooks_assure_initialized(arena, r_extent_hooks);
-
- if ((*r_extent_hooks)->purge_forced == NULL) {
- return true;
- }
- if (*r_extent_hooks != &extent_hooks_default) {
- extent_hook_pre_reentrancy(tsdn, arena);
- }
- bool err = (*r_extent_hooks)->purge_forced(*r_extent_hooks,
- extent_base_get(extent), extent_size_get(extent), offset, length,
- arena_ind_get(arena));
- if (*r_extent_hooks != &extent_hooks_default) {
- extent_hook_post_reentrancy(tsdn);
- }
+ bool err = ehooks_purge_forced(tsdn, ehooks, edata_base_get(edata),
+ edata_size_get(edata), offset, length);
return err;
}
bool
-extent_purge_forced_wrapper(tsdn_t *tsdn, arena_t *arena,
- extent_hooks_t **r_extent_hooks, extent_t *extent, size_t offset,
- size_t length) {
- return extent_purge_forced_impl(tsdn, arena, r_extent_hooks, extent,
- offset, length, false);
-}
-
-static bool
-extent_split_default(extent_hooks_t *extent_hooks, void *addr, size_t size,
- size_t size_a, size_t size_b, bool committed, unsigned arena_ind) {
- if (!maps_coalesce) {
- /*
- * Without retain, only whole regions can be purged (required by
- * MEM_RELEASE on Windows) -- therefore disallow splitting. See
- * comments in extent_head_no_merge().
- */
- return !opt_retain;
- }
-
- return false;
+extent_purge_forced_wrapper(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata,
+ size_t offset, size_t length) {
+ return extent_purge_forced_impl(tsdn, ehooks, edata, offset, length,
+ false);
}
/*
@@ -2088,183 +1170,95 @@ extent_split_default(extent_hooks_t *extent_hooks, void *addr, size_t size,
* with the trail (the higher addressed portion). This makes 'extent' the lead,
* and returns the trail (except in case of error).
*/
-static extent_t *
-extent_split_impl(tsdn_t *tsdn, arena_t *arena,
- extent_hooks_t **r_extent_hooks, extent_t *extent, size_t size_a,
- szind_t szind_a, bool slab_a, size_t size_b, szind_t szind_b, bool slab_b,
- bool growing_retained) {
- assert(extent_size_get(extent) == size_a + size_b);
- witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
- WITNESS_RANK_CORE, growing_retained ? 1 : 0);
-
- extent_hooks_assure_initialized(arena, r_extent_hooks);
+static edata_t *
+extent_split_impl(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
+ edata_t *edata, size_t size_a, size_t size_b, bool holding_core_locks) {
+ assert(edata_size_get(edata) == size_a + size_b);
+ /* Only the shrink path may split w/o holding core locks. */
+ if (holding_core_locks) {
+ witness_assert_positive_depth_to_rank(
+ tsdn_witness_tsdp_get(tsdn), WITNESS_RANK_CORE);
+ } else {
+ witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
+ WITNESS_RANK_CORE, 0);
+ }
- if ((*r_extent_hooks)->split == NULL) {
+ if (ehooks_split_will_fail(ehooks)) {
return NULL;
}
- extent_t *trail = extent_alloc(tsdn, arena);
+ edata_t *trail = edata_cache_get(tsdn, pac->edata_cache);
if (trail == NULL) {
goto label_error_a;
}
- extent_init(trail, arena, (void *)((uintptr_t)extent_base_get(extent) +
- size_a), size_b, slab_b, szind_b, extent_sn_get(extent),
- extent_state_get(extent), extent_zeroed_get(extent),
- extent_committed_get(extent), extent_dumpable_get(extent),
- EXTENT_NOT_HEAD);
-
- rtree_ctx_t rtree_ctx_fallback;
- rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);
- rtree_leaf_elm_t *lead_elm_a, *lead_elm_b;
- {
- extent_t lead;
-
- extent_init(&lead, arena, extent_addr_get(extent), size_a,
- slab_a, szind_a, extent_sn_get(extent),
- extent_state_get(extent), extent_zeroed_get(extent),
- extent_committed_get(extent), extent_dumpable_get(extent),
- EXTENT_NOT_HEAD);
-
- extent_rtree_leaf_elms_lookup(tsdn, rtree_ctx, &lead, false,
- true, &lead_elm_a, &lead_elm_b);
- }
- rtree_leaf_elm_t *trail_elm_a, *trail_elm_b;
- extent_rtree_leaf_elms_lookup(tsdn, rtree_ctx, trail, false, true,
- &trail_elm_a, &trail_elm_b);
-
- if (lead_elm_a == NULL || lead_elm_b == NULL || trail_elm_a == NULL
- || trail_elm_b == NULL) {
+ edata_init(trail, edata_arena_ind_get(edata),
+ (void *)((uintptr_t)edata_base_get(edata) + size_a), size_b,
+ /* slab */ false, SC_NSIZES, edata_sn_get(edata),
+ edata_state_get(edata), edata_zeroed_get(edata),
+ edata_committed_get(edata), EXTENT_PAI_PAC, EXTENT_NOT_HEAD);
+ emap_prepare_t prepare;
+ bool err = emap_split_prepare(tsdn, pac->emap, &prepare, edata,
+ size_a, trail, size_b);
+ if (err) {
goto label_error_b;
}
- extent_lock2(tsdn, extent, trail);
+ /*
+ * No need to acquire trail or edata, because: 1) trail was new (just
+ * allocated); and 2) edata is either an active allocation (the shrink
+ * path), or in an acquired state (extracted from the ecache on the
+ * extent_recycle_split path).
+ */
+ assert(emap_edata_is_acquired(tsdn, pac->emap, edata));
+ assert(emap_edata_is_acquired(tsdn, pac->emap, trail));
+
+ err = ehooks_split(tsdn, ehooks, edata_base_get(edata), size_a + size_b,
+ size_a, size_b, edata_committed_get(edata));
- if (*r_extent_hooks != &extent_hooks_default) {
- extent_hook_pre_reentrancy(tsdn, arena);
- }
- bool err = (*r_extent_hooks)->split(*r_extent_hooks, extent_base_get(extent),
- size_a + size_b, size_a, size_b, extent_committed_get(extent),
- arena_ind_get(arena));
- if (*r_extent_hooks != &extent_hooks_default) {
- extent_hook_post_reentrancy(tsdn);
- }
if (err) {
- goto label_error_c;
+ goto label_error_b;
}
- extent_size_set(extent, size_a);
- extent_szind_set(extent, szind_a);
-
- extent_rtree_write_acquired(tsdn, lead_elm_a, lead_elm_b, extent,
- szind_a, slab_a);
- extent_rtree_write_acquired(tsdn, trail_elm_a, trail_elm_b, trail,
- szind_b, slab_b);
-
- extent_unlock2(tsdn, extent, trail);
+ edata_size_set(edata, size_a);
+ emap_split_commit(tsdn, pac->emap, &prepare, edata, size_a, trail,
+ size_b);
return trail;
-label_error_c:
- extent_unlock2(tsdn, extent, trail);
label_error_b:
- extent_dalloc(tsdn, arena, trail);
+ edata_cache_put(tsdn, pac->edata_cache, trail);
label_error_a:
return NULL;
}
-extent_t *
-extent_split_wrapper(tsdn_t *tsdn, arena_t *arena,
- extent_hooks_t **r_extent_hooks, extent_t *extent, size_t size_a,
- szind_t szind_a, bool slab_a, size_t size_b, szind_t szind_b, bool slab_b) {
- return extent_split_impl(tsdn, arena, r_extent_hooks, extent, size_a,
- szind_a, slab_a, size_b, szind_b, slab_b, false);
-}
-
-static bool
-extent_merge_default_impl(void *addr_a, void *addr_b) {
- if (!maps_coalesce && !opt_retain) {
- return true;
- }
- if (have_dss && !extent_dss_mergeable(addr_a, addr_b)) {
- return true;
- }
-
- return false;
-}
-
-/*
- * Returns true if the given extents can't be merged because of their head bit
- * settings. Assumes the second extent has the higher address.
- */
-static bool
-extent_head_no_merge(extent_t *a, extent_t *b) {
- assert(extent_base_get(a) < extent_base_get(b));
- /*
- * When coalesce is not always allowed (Windows), only merge extents
- * from the same VirtualAlloc region under opt.retain (in which case
- * MEM_DECOMMIT is utilized for purging).
- */
- if (maps_coalesce) {
- return false;
- }
- if (!opt_retain) {
- return true;
- }
- /* If b is a head extent, disallow the cross-region merge. */
- if (extent_is_head_get(b)) {
- /*
- * Additionally, sn should not overflow with retain; sanity
- * check that different regions have unique sn.
- */
- assert(extent_sn_comp(a, b) != 0);
- return true;
- }
- assert(extent_sn_comp(a, b) == 0);
-
- return false;
+edata_t *
+extent_split_wrapper(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, edata_t *edata,
+ size_t size_a, size_t size_b, bool holding_core_locks) {
+ return extent_split_impl(tsdn, pac, ehooks, edata, size_a, size_b,
+ holding_core_locks);
}
static bool
-extent_merge_default(extent_hooks_t *extent_hooks, void *addr_a, size_t size_a,
- void *addr_b, size_t size_b, bool committed, unsigned arena_ind) {
- if (!maps_coalesce) {
- tsdn_t *tsdn = tsdn_fetch();
- extent_t *a = iealloc(tsdn, addr_a);
- extent_t *b = iealloc(tsdn, addr_b);
- if (extent_head_no_merge(a, b)) {
- return true;
- }
+extent_merge_impl(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, edata_t *a,
+ edata_t *b, bool holding_core_locks) {
+ /* Only the expanding path may merge w/o holding ecache locks. */
+ if (holding_core_locks) {
+ witness_assert_positive_depth_to_rank(
+ tsdn_witness_tsdp_get(tsdn), WITNESS_RANK_CORE);
+ } else {
+ witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
+ WITNESS_RANK_CORE, 0);
}
- return extent_merge_default_impl(addr_a, addr_b);
-}
-static bool
-extent_merge_impl(tsdn_t *tsdn, arena_t *arena,
- extent_hooks_t **r_extent_hooks, extent_t *a, extent_t *b,
- bool growing_retained) {
- witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
- WITNESS_RANK_CORE, growing_retained ? 1 : 0);
- assert(extent_base_get(a) < extent_base_get(b));
+ assert(edata_base_get(a) < edata_base_get(b));
+ assert(edata_arena_ind_get(a) == edata_arena_ind_get(b));
+ assert(edata_arena_ind_get(a) == ehooks_ind_get(ehooks));
+ emap_assert_mapped(tsdn, pac->emap, a);
+ emap_assert_mapped(tsdn, pac->emap, b);
- extent_hooks_assure_initialized(arena, r_extent_hooks);
-
- if ((*r_extent_hooks)->merge == NULL || extent_head_no_merge(a, b)) {
- return true;
- }
-
- bool err;
- if (*r_extent_hooks == &extent_hooks_default) {
- /* Call directly to propagate tsdn. */
- err = extent_merge_default_impl(extent_base_get(a),
- extent_base_get(b));
- } else {
- extent_hook_pre_reentrancy(tsdn, arena);
- err = (*r_extent_hooks)->merge(*r_extent_hooks,
- extent_base_get(a), extent_size_get(a), extent_base_get(b),
- extent_size_get(b), extent_committed_get(a),
- arena_ind_get(arena));
- extent_hook_post_reentrancy(tsdn);
- }
+ bool err = ehooks_merge(tsdn, ehooks, edata_base_get(a),
+ edata_size_get(a), edata_base_get(b), edata_size_get(b),
+ edata_committed_get(a));
if (err) {
return true;
@@ -2275,132 +1269,58 @@ extent_merge_impl(tsdn_t *tsdn, arena_t *arena,
* owned, so the following code uses decomposed helper functions rather
* than extent_{,de}register() to do things in the right order.
*/
- rtree_ctx_t rtree_ctx_fallback;
- rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);
- rtree_leaf_elm_t *a_elm_a, *a_elm_b, *b_elm_a, *b_elm_b;
- extent_rtree_leaf_elms_lookup(tsdn, rtree_ctx, a, true, false, &a_elm_a,
- &a_elm_b);
- extent_rtree_leaf_elms_lookup(tsdn, rtree_ctx, b, true, false, &b_elm_a,
- &b_elm_b);
-
- extent_lock2(tsdn, a, b);
-
- if (a_elm_b != NULL) {
- rtree_leaf_elm_write(tsdn, &extents_rtree, a_elm_b, NULL,
- SC_NSIZES, false);
- }
- if (b_elm_b != NULL) {
- rtree_leaf_elm_write(tsdn, &extents_rtree, b_elm_a, NULL,
- SC_NSIZES, false);
- } else {
- b_elm_b = b_elm_a;
- }
-
- extent_size_set(a, extent_size_get(a) + extent_size_get(b));
- extent_szind_set(a, SC_NSIZES);
- extent_sn_set(a, (extent_sn_get(a) < extent_sn_get(b)) ?
- extent_sn_get(a) : extent_sn_get(b));
- extent_zeroed_set(a, extent_zeroed_get(a) && extent_zeroed_get(b));
+ emap_prepare_t prepare;
+ emap_merge_prepare(tsdn, pac->emap, &prepare, a, b);
- extent_rtree_write_acquired(tsdn, a_elm_a, b_elm_b, a, SC_NSIZES,
- false);
+ assert(edata_state_get(a) == extent_state_active ||
+ edata_state_get(a) == extent_state_merging);
+ edata_state_set(a, extent_state_active);
+ edata_size_set(a, edata_size_get(a) + edata_size_get(b));
+ edata_sn_set(a, (edata_sn_get(a) < edata_sn_get(b)) ?
+ edata_sn_get(a) : edata_sn_get(b));
+ edata_zeroed_set(a, edata_zeroed_get(a) && edata_zeroed_get(b));
- extent_unlock2(tsdn, a, b);
+ emap_merge_commit(tsdn, pac->emap, &prepare, a, b);
- extent_dalloc(tsdn, extent_arena_get(b), b);
+ edata_cache_put(tsdn, pac->edata_cache, b);
return false;
}
bool
-extent_merge_wrapper(tsdn_t *tsdn, arena_t *arena,
- extent_hooks_t **r_extent_hooks, extent_t *a, extent_t *b) {
- return extent_merge_impl(tsdn, arena, r_extent_hooks, a, b, false);
+extent_merge_wrapper(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks,
+ edata_t *a, edata_t *b) {
+ return extent_merge_impl(tsdn, pac, ehooks, a, b,
+ /* holding_core_locks */ false);
}
bool
-extent_boot(void) {
- if (rtree_new(&extents_rtree, true)) {
- return true;
- }
+extent_commit_zero(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata,
+ bool commit, bool zero, bool growing_retained) {
+ witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
+ WITNESS_RANK_CORE, growing_retained ? 1 : 0);
- if (mutex_pool_init(&extent_mutex_pool, "extent_mutex_pool",
- WITNESS_RANK_EXTENT_POOL)) {
- return true;
+ if (commit && !edata_committed_get(edata)) {
+ if (extent_commit_impl(tsdn, ehooks, edata, 0,
+ edata_size_get(edata), growing_retained)) {
+ return true;
+ }
}
-
- if (have_dss) {
- extent_dss_boot();
+ if (zero && !edata_zeroed_get(edata)) {
+ void *addr = edata_base_get(edata);
+ size_t size = edata_size_get(edata);
+ ehooks_zero(tsdn, ehooks, addr, size);
}
-
return false;
}
-void
-extent_util_stats_get(tsdn_t *tsdn, const void *ptr,
- size_t *nfree, size_t *nregs, size_t *size) {
- assert(ptr != NULL && nfree != NULL && nregs != NULL && size != NULL);
-
- const extent_t *extent = iealloc(tsdn, ptr);
- if (unlikely(extent == NULL)) {
- *nfree = *nregs = *size = 0;
- return;
- }
-
- *size = extent_size_get(extent);
- if (!extent_slab_get(extent)) {
- *nfree = 0;
- *nregs = 1;
- } else {
- *nfree = extent_nfree_get(extent);
- *nregs = bin_infos[extent_szind_get(extent)].nregs;
- assert(*nfree <= *nregs);
- assert(*nfree * extent_usize_get(extent) <= *size);
- }
-}
-
-void
-extent_util_stats_verbose_get(tsdn_t *tsdn, const void *ptr,
- size_t *nfree, size_t *nregs, size_t *size,
- size_t *bin_nfree, size_t *bin_nregs, void **slabcur_addr) {
- assert(ptr != NULL && nfree != NULL && nregs != NULL && size != NULL
- && bin_nfree != NULL && bin_nregs != NULL && slabcur_addr != NULL);
-
- const extent_t *extent = iealloc(tsdn, ptr);
- if (unlikely(extent == NULL)) {
- *nfree = *nregs = *size = *bin_nfree = *bin_nregs = 0;
- *slabcur_addr = NULL;
- return;
- }
+bool
+extent_boot(void) {
+ assert(sizeof(slab_data_t) >= sizeof(e_prof_info_t));
- *size = extent_size_get(extent);
- if (!extent_slab_get(extent)) {
- *nfree = *bin_nfree = *bin_nregs = 0;
- *nregs = 1;
- *slabcur_addr = NULL;
- return;
+ if (have_dss) {
+ extent_dss_boot();
}
- *nfree = extent_nfree_get(extent);
- const szind_t szind = extent_szind_get(extent);
- *nregs = bin_infos[szind].nregs;
- assert(*nfree <= *nregs);
- assert(*nfree * extent_usize_get(extent) <= *size);
-
- const arena_t *arena = extent_arena_get(extent);
- assert(arena != NULL);
- const unsigned binshard = extent_binshard_get(extent);
- bin_t *bin = &arena->bins[szind].bin_shards[binshard];
-
- malloc_mutex_lock(tsdn, &bin->lock);
- if (config_stats) {
- *bin_nregs = *nregs * bin->stats.curslabs;
- assert(*bin_nregs >= bin->stats.curregs);
- *bin_nfree = *bin_nregs - bin->stats.curregs;
- } else {
- *bin_nfree = *bin_nregs = 0;
- }
- *slabcur_addr = extent_addr_get(bin->slabcur);
- assert(*slabcur_addr != NULL);
- malloc_mutex_unlock(tsdn, &bin->lock);
+ return false;
}
diff --git a/contrib/jemalloc/src/extent_dss.c b/contrib/jemalloc/src/extent_dss.c
index 858178911051..9a35bacfb4f8 100644
--- a/contrib/jemalloc/src/extent_dss.c
+++ b/contrib/jemalloc/src/extent_dss.c
@@ -1,4 +1,3 @@
-#define JEMALLOC_EXTENT_DSS_C_
#include "jemalloc/internal/jemalloc_preamble.h"
#include "jemalloc/internal/jemalloc_internal_includes.h"
@@ -109,7 +108,7 @@ extent_dss_max_update(void *new_addr) {
void *
extent_alloc_dss(tsdn_t *tsdn, arena_t *arena, void *new_addr, size_t size,
size_t alignment, bool *zero, bool *commit) {
- extent_t *gap;
+ edata_t *gap;
cassert(have_dss);
assert(size > 0);
@@ -123,7 +122,7 @@ extent_alloc_dss(tsdn_t *tsdn, arena_t *arena, void *new_addr, size_t size,
return NULL;
}
- gap = extent_alloc(tsdn, arena);
+ gap = edata_cache_get(tsdn, &arena->pa_shard.edata_cache);
if (gap == NULL) {
return NULL;
}
@@ -141,6 +140,8 @@ extent_alloc_dss(tsdn_t *tsdn, arena_t *arena, void *new_addr, size_t size,
goto label_oom;
}
+ bool head_state = opt_retain ? EXTENT_IS_HEAD :
+ EXTENT_NOT_HEAD;
/*
* Compute how much page-aligned gap space (if any) is
* necessary to satisfy alignment. This space can be
@@ -153,11 +154,12 @@ extent_alloc_dss(tsdn_t *tsdn, arena_t *arena, void *new_addr, size_t size,
size_t gap_size_page = (uintptr_t)ret -
(uintptr_t)gap_addr_page;
if (gap_size_page != 0) {
- extent_init(gap, arena, gap_addr_page,
- gap_size_page, false, SC_NSIZES,
- arena_extent_sn_next(arena),
- extent_state_active, false, true, true,
- EXTENT_NOT_HEAD);
+ edata_init(gap, arena_ind_get(arena),
+ gap_addr_page, gap_size_page, false,
+ SC_NSIZES, extent_sn_next(
+ &arena->pa_shard.pac),
+ extent_state_active, false, true,
+ EXTENT_PAI_PAC, head_state);
}
/*
* Compute the address just past the end of the desired
@@ -186,25 +188,29 @@ extent_alloc_dss(tsdn_t *tsdn, arena_t *arena, void *new_addr, size_t size,
extent_dss_extending_finish();
if (gap_size_page != 0) {
- extent_dalloc_gap(tsdn, arena, gap);
+ ehooks_t *ehooks = arena_get_ehooks(
+ arena);
+ extent_dalloc_gap(tsdn,
+ &arena->pa_shard.pac, ehooks, gap);
} else {
- extent_dalloc(tsdn, arena, gap);
+ edata_cache_put(tsdn,
+ &arena->pa_shard.edata_cache, gap);
}
if (!*commit) {
*commit = pages_decommit(ret, size);
}
if (*zero && *commit) {
- extent_hooks_t *extent_hooks =
- EXTENT_HOOKS_INITIALIZER;
- extent_t extent;
+ edata_t edata = {0};
+ ehooks_t *ehooks = arena_get_ehooks(
+ arena);
- extent_init(&extent, arena, ret, size,
+ edata_init(&edata,
+ arena_ind_get(arena), ret, size,
size, false, SC_NSIZES,
extent_state_active, false, true,
- true, EXTENT_NOT_HEAD);
+ EXTENT_PAI_PAC, head_state);
if (extent_purge_forced_wrapper(tsdn,
- arena, &extent_hooks, &extent, 0,
- size)) {
+ ehooks, &edata, 0, size)) {
memset(ret, 0, size);
}
}
@@ -224,7 +230,7 @@ extent_alloc_dss(tsdn_t *tsdn, arena_t *arena, void *new_addr, size_t size,
}
label_oom:
extent_dss_extending_finish();
- extent_dalloc(tsdn, arena, gap);
+ edata_cache_put(tsdn, &arena->pa_shard.edata_cache, gap);
return NULL;
}
diff --git a/contrib/jemalloc/src/extent_mmap.c b/contrib/jemalloc/src/extent_mmap.c
index 17fd1c8f9577..5f0ee2d24b1b 100644
--- a/contrib/jemalloc/src/extent_mmap.c
+++ b/contrib/jemalloc/src/extent_mmap.c
@@ -1,4 +1,3 @@
-#define JEMALLOC_EXTENT_MMAP_C_
#include "jemalloc/internal/jemalloc_preamble.h"
#include "jemalloc/internal/jemalloc_internal_includes.h"
diff --git a/contrib/jemalloc/src/fxp.c b/contrib/jemalloc/src/fxp.c
new file mode 100644
index 000000000000..96585f0a65ac
--- /dev/null
+++ b/contrib/jemalloc/src/fxp.c
@@ -0,0 +1,124 @@
+#include "jemalloc/internal/jemalloc_preamble.h"
+#include "jemalloc/internal/jemalloc_internal_includes.h"
+
+#include "jemalloc/internal/fxp.h"
+
+static bool
+fxp_isdigit(char c) {
+ return '0' <= c && c <= '9';
+}
+
+bool
+fxp_parse(fxp_t *result, const char *str, char **end) {
+ /*
+ * Using malloc_strtoumax in this method isn't as handy as you might
+ * expect (I tried). In the fractional part, significant leading zeros
+ * mean that you still need to do your own parsing, now with trickier
+ * math. In the integer part, the casting (uintmax_t to uint32_t)
+ * forces more reasoning about bounds than just checking for overflow as
+ * we parse.
+ */
+ uint32_t integer_part = 0;
+
+ const char *cur = str;
+
+ /* The string must start with a digit or a decimal point. */
+ if (*cur != '.' && !fxp_isdigit(*cur)) {
+ return true;
+ }
+
+ while ('0' <= *cur && *cur <= '9') {
+ integer_part *= 10;
+ integer_part += *cur - '0';
+ if (integer_part >= (1U << 16)) {
+ return true;
+ }
+ cur++;
+ }
+
+ /*
+ * We've parsed all digits at the beginning of the string, without
+ * overflow. Either we're done, or there's a fractional part.
+ */
+ if (*cur != '.') {
+ *result = (integer_part << 16);
+ if (end != NULL) {
+ *end = (char *)cur;
+ }
+ return false;
+ }
+
+ /* There's a fractional part. */
+ cur++;
+ if (!fxp_isdigit(*cur)) {
+ /* Shouldn't end on the decimal point. */
+ return true;
+ }
+
+ /*
+ * We use a lot of precision for the fractional part, even though we'll
+ * discard most of it; this lets us get exact values for the important
+ * special case where the denominator is a small power of 2 (for
+ * instance, 1/512 == 0.001953125 is exactly representable even with
+ * only 16 bits of fractional precision). We need to left-shift by 16
+ * before dividing so we pick the number of digits to be
+ * floor(log(2**48)) = 14.
+ */
+ uint64_t fractional_part = 0;
+ uint64_t frac_div = 1;
+ for (int i = 0; i < FXP_FRACTIONAL_PART_DIGITS; i++) {
+ fractional_part *= 10;
+ frac_div *= 10;
+ if (fxp_isdigit(*cur)) {
+ fractional_part += *cur - '0';
+ cur++;
+ }
+ }
+ /*
+ * We only parse the first maxdigits characters, but we can still ignore
+ * any digits after that.
+ */
+ while (fxp_isdigit(*cur)) {
+ cur++;
+ }
+
+ assert(fractional_part < frac_div);
+ uint32_t fractional_repr = (uint32_t)(
+ (fractional_part << 16) / frac_div);
+
+ /* Success! */
+ *result = (integer_part << 16) + fractional_repr;
+ if (end != NULL) {
+ *end = (char *)cur;
+ }
+ return false;
+}
+
+void
+fxp_print(fxp_t a, char buf[FXP_BUF_SIZE]) {
+ uint32_t integer_part = fxp_round_down(a);
+ uint32_t fractional_part = (a & ((1U << 16) - 1));
+
+ int leading_fraction_zeros = 0;
+ uint64_t fraction_digits = fractional_part;
+ for (int i = 0; i < FXP_FRACTIONAL_PART_DIGITS; i++) {
+ if (fraction_digits < (1U << 16)
+ && fraction_digits * 10 >= (1U << 16)) {
+ leading_fraction_zeros = i;
+ }
+ fraction_digits *= 10;
+ }
+ fraction_digits >>= 16;
+ while (fraction_digits > 0 && fraction_digits % 10 == 0) {
+ fraction_digits /= 10;
+ }
+
+ size_t printed = malloc_snprintf(buf, FXP_BUF_SIZE, "%"FMTu32".",
+ integer_part);
+ for (int i = 0; i < leading_fraction_zeros; i++) {
+ buf[printed] = '0';
+ printed++;
+ }
+ malloc_snprintf(&buf[printed], FXP_BUF_SIZE - printed, "%"FMTu64,
+ fraction_digits);
+}
diff --git a/contrib/jemalloc/src/hash.c b/contrib/jemalloc/src/hash.c
deleted file mode 100644
index 7b2bdc2bd6f4..000000000000
--- a/contrib/jemalloc/src/hash.c
+++ /dev/null
@@ -1,3 +0,0 @@
-#define JEMALLOC_HASH_C_
-#include "jemalloc/internal/jemalloc_preamble.h"
-#include "jemalloc/internal/jemalloc_internal_includes.h"
diff --git a/contrib/jemalloc/src/hook.c b/contrib/jemalloc/src/hook.c
index 9ac703cf9f51..493edbbe512d 100644
--- a/contrib/jemalloc/src/hook.c
+++ b/contrib/jemalloc/src/hook.c
@@ -130,9 +130,9 @@ hook_reentrantp() {
*/
static bool in_hook_global = true;
tsdn_t *tsdn = tsdn_fetch();
- tcache_t *tcache = tsdn_tcachep_get(tsdn);
- if (tcache != NULL) {
- return &tcache->in_hook;
+ bool *in_hook = tsdn_in_hookp_get(tsdn);
+ if (in_hook!= NULL) {
+ return in_hook;
}
return &in_hook_global;
}
diff --git a/contrib/jemalloc/src/hpa.c b/contrib/jemalloc/src/hpa.c
new file mode 100644
index 000000000000..7e2aeba0c0ff
--- /dev/null
+++ b/contrib/jemalloc/src/hpa.c
@@ -0,0 +1,1044 @@
+#include "jemalloc/internal/jemalloc_preamble.h"
+#include "jemalloc/internal/jemalloc_internal_includes.h"
+
+#include "jemalloc/internal/hpa.h"
+
+#include "jemalloc/internal/fb.h"
+#include "jemalloc/internal/witness.h"
+
+#define HPA_EDEN_SIZE (128 * HUGEPAGE)
+
+static edata_t *hpa_alloc(tsdn_t *tsdn, pai_t *self, size_t size,
+ size_t alignment, bool zero, bool guarded, bool frequent_reuse,
+ bool *deferred_work_generated);
+static size_t hpa_alloc_batch(tsdn_t *tsdn, pai_t *self, size_t size,
+ size_t nallocs, edata_list_active_t *results, bool *deferred_work_generated);
+static bool hpa_expand(tsdn_t *tsdn, pai_t *self, edata_t *edata,
+ size_t old_size, size_t new_size, bool zero, bool *deferred_work_generated);
+static bool hpa_shrink(tsdn_t *tsdn, pai_t *self, edata_t *edata,
+ size_t old_size, size_t new_size, bool *deferred_work_generated);
+static void hpa_dalloc(tsdn_t *tsdn, pai_t *self, edata_t *edata,
+ bool *deferred_work_generated);
+static void hpa_dalloc_batch(tsdn_t *tsdn, pai_t *self,
+ edata_list_active_t *list, bool *deferred_work_generated);
+static uint64_t hpa_time_until_deferred_work(tsdn_t *tsdn, pai_t *self);
+
+bool
+hpa_supported() {
+#ifdef _WIN32
+ /*
+ * At least until the API and implementation is somewhat settled, we
+ * don't want to try to debug the VM subsystem on the hardest-to-test
+ * platform.
+ */
+ return false;
+#endif
+ if (!pages_can_hugify) {
+ return false;
+ }
+ /*
+ * We fundamentally rely on a address-space-hungry growth strategy for
+ * hugepages.
+ */
+ if (LG_SIZEOF_PTR != 3) {
+ return false;
+ }
+ /*
+ * If we couldn't detect the value of HUGEPAGE, HUGEPAGE_PAGES becomes
+ * this sentinel value -- see the comment in pages.h.
+ */
+ if (HUGEPAGE_PAGES == 1) {
+ return false;
+ }
+ return true;
+}
+
+static void
+hpa_do_consistency_checks(hpa_shard_t *shard) {
+ assert(shard->base != NULL);
+}
+
+bool
+hpa_central_init(hpa_central_t *central, base_t *base, const hpa_hooks_t *hooks) {
+ /* malloc_conf processing should have filtered out these cases. */
+ assert(hpa_supported());
+ bool err;
+ err = malloc_mutex_init(&central->grow_mtx, "hpa_central_grow",
+ WITNESS_RANK_HPA_CENTRAL_GROW, malloc_mutex_rank_exclusive);
+ if (err) {
+ return true;
+ }
+ err = malloc_mutex_init(&central->mtx, "hpa_central",
+ WITNESS_RANK_HPA_CENTRAL, malloc_mutex_rank_exclusive);
+ if (err) {
+ return true;
+ }
+ central->base = base;
+ central->eden = NULL;
+ central->eden_len = 0;
+ central->age_counter = 0;
+ central->hooks = *hooks;
+ return false;
+}
+
+static hpdata_t *
+hpa_alloc_ps(tsdn_t *tsdn, hpa_central_t *central) {
+ return (hpdata_t *)base_alloc(tsdn, central->base, sizeof(hpdata_t),
+ CACHELINE);
+}
+
+hpdata_t *
+hpa_central_extract(tsdn_t *tsdn, hpa_central_t *central, size_t size,
+ bool *oom) {
+ /* Don't yet support big allocations; these should get filtered out. */
+ assert(size <= HUGEPAGE);
+ /*
+ * Should only try to extract from the central allocator if the local
+ * shard is exhausted. We should hold the grow_mtx on that shard.
+ */
+ witness_assert_positive_depth_to_rank(
+ tsdn_witness_tsdp_get(tsdn), WITNESS_RANK_HPA_SHARD_GROW);
+
+ malloc_mutex_lock(tsdn, &central->grow_mtx);
+ *oom = false;
+
+ hpdata_t *ps = NULL;
+
+ /* Is eden a perfect fit? */
+ if (central->eden != NULL && central->eden_len == HUGEPAGE) {
+ ps = hpa_alloc_ps(tsdn, central);
+ if (ps == NULL) {
+ *oom = true;
+ malloc_mutex_unlock(tsdn, &central->grow_mtx);
+ return NULL;
+ }
+ hpdata_init(ps, central->eden, central->age_counter++);
+ central->eden = NULL;
+ central->eden_len = 0;
+ malloc_mutex_unlock(tsdn, &central->grow_mtx);
+ return ps;
+ }
+
+ /*
+ * We're about to try to allocate from eden by splitting. If eden is
+ * NULL, we have to allocate it too. Otherwise, we just have to
+ * allocate an edata_t for the new psset.
+ */
+ if (central->eden == NULL) {
+ /*
+ * During development, we're primarily concerned with systems
+ * with overcommit. Eventually, we should be more careful here.
+ */
+ bool commit = true;
+ /* Allocate address space, bailing if we fail. */
+ void *new_eden = pages_map(NULL, HPA_EDEN_SIZE, HUGEPAGE,
+ &commit);
+ if (new_eden == NULL) {
+ *oom = true;
+ malloc_mutex_unlock(tsdn, &central->grow_mtx);
+ return NULL;
+ }
+ ps = hpa_alloc_ps(tsdn, central);
+ if (ps == NULL) {
+ pages_unmap(new_eden, HPA_EDEN_SIZE);
+ *oom = true;
+ malloc_mutex_unlock(tsdn, &central->grow_mtx);
+ return NULL;
+ }
+ central->eden = new_eden;
+ central->eden_len = HPA_EDEN_SIZE;
+ } else {
+ /* Eden is already nonempty; only need an edata for ps. */
+ ps = hpa_alloc_ps(tsdn, central);
+ if (ps == NULL) {
+ *oom = true;
+ malloc_mutex_unlock(tsdn, &central->grow_mtx);
+ return NULL;
+ }
+ }
+ assert(ps != NULL);
+ assert(central->eden != NULL);
+ assert(central->eden_len > HUGEPAGE);
+ assert(central->eden_len % HUGEPAGE == 0);
+ assert(HUGEPAGE_ADDR2BASE(central->eden) == central->eden);
+
+ hpdata_init(ps, central->eden, central->age_counter++);
+
+ char *eden_char = (char *)central->eden;
+ eden_char += HUGEPAGE;
+ central->eden = (void *)eden_char;
+ central->eden_len -= HUGEPAGE;
+
+ malloc_mutex_unlock(tsdn, &central->grow_mtx);
+
+ return ps;
+}
+
+bool
+hpa_shard_init(hpa_shard_t *shard, hpa_central_t *central, emap_t *emap,
+ base_t *base, edata_cache_t *edata_cache, unsigned ind,
+ const hpa_shard_opts_t *opts) {
+ /* malloc_conf processing should have filtered out these cases. */
+ assert(hpa_supported());
+ bool err;
+ err = malloc_mutex_init(&shard->grow_mtx, "hpa_shard_grow",
+ WITNESS_RANK_HPA_SHARD_GROW, malloc_mutex_rank_exclusive);
+ if (err) {
+ return true;
+ }
+ err = malloc_mutex_init(&shard->mtx, "hpa_shard",
+ WITNESS_RANK_HPA_SHARD, malloc_mutex_rank_exclusive);
+ if (err) {
+ return true;
+ }
+
+ assert(edata_cache != NULL);
+ shard->central = central;
+ shard->base = base;
+ edata_cache_fast_init(&shard->ecf, edata_cache);
+ psset_init(&shard->psset);
+ shard->age_counter = 0;
+ shard->ind = ind;
+ shard->emap = emap;
+
+ shard->opts = *opts;
+
+ shard->npending_purge = 0;
+ nstime_init_zero(&shard->last_purge);
+
+ shard->stats.npurge_passes = 0;
+ shard->stats.npurges = 0;
+ shard->stats.nhugifies = 0;
+ shard->stats.ndehugifies = 0;
+
+ /*
+ * Fill these in last, so that if an hpa_shard gets used despite
+ * initialization failing, we'll at least crash instead of just
+ * operating on corrupted data.
+ */
+ shard->pai.alloc = &hpa_alloc;
+ shard->pai.alloc_batch = &hpa_alloc_batch;
+ shard->pai.expand = &hpa_expand;
+ shard->pai.shrink = &hpa_shrink;
+ shard->pai.dalloc = &hpa_dalloc;
+ shard->pai.dalloc_batch = &hpa_dalloc_batch;
+ shard->pai.time_until_deferred_work = &hpa_time_until_deferred_work;
+
+ hpa_do_consistency_checks(shard);
+
+ return false;
+}
+
+/*
+ * Note that the stats functions here follow the usual stats naming conventions;
+ * "merge" obtains the stats from some live object of instance, while "accum"
+ * only combines the stats from one stats objet to another. Hence the lack of
+ * locking here.
+ */
+static void
+hpa_shard_nonderived_stats_accum(hpa_shard_nonderived_stats_t *dst,
+ hpa_shard_nonderived_stats_t *src) {
+ dst->npurge_passes += src->npurge_passes;
+ dst->npurges += src->npurges;
+ dst->nhugifies += src->nhugifies;
+ dst->ndehugifies += src->ndehugifies;
+}
+
+void
+hpa_shard_stats_accum(hpa_shard_stats_t *dst, hpa_shard_stats_t *src) {
+ psset_stats_accum(&dst->psset_stats, &src->psset_stats);
+ hpa_shard_nonderived_stats_accum(&dst->nonderived_stats,
+ &src->nonderived_stats);
+}
+
+void
+hpa_shard_stats_merge(tsdn_t *tsdn, hpa_shard_t *shard,
+ hpa_shard_stats_t *dst) {
+ hpa_do_consistency_checks(shard);
+
+ malloc_mutex_lock(tsdn, &shard->grow_mtx);
+ malloc_mutex_lock(tsdn, &shard->mtx);
+ psset_stats_accum(&dst->psset_stats, &shard->psset.stats);
+ hpa_shard_nonderived_stats_accum(&dst->nonderived_stats, &shard->stats);
+ malloc_mutex_unlock(tsdn, &shard->mtx);
+ malloc_mutex_unlock(tsdn, &shard->grow_mtx);
+}
+
+static bool
+hpa_good_hugification_candidate(hpa_shard_t *shard, hpdata_t *ps) {
+ /*
+ * Note that this needs to be >= rather than just >, because of the
+ * important special case in which the hugification threshold is exactly
+ * HUGEPAGE.
+ */
+ return hpdata_nactive_get(ps) * PAGE
+ >= shard->opts.hugification_threshold;
+}
+
+static size_t
+hpa_adjusted_ndirty(tsdn_t *tsdn, hpa_shard_t *shard) {
+ malloc_mutex_assert_owner(tsdn, &shard->mtx);
+ return psset_ndirty(&shard->psset) - shard->npending_purge;
+}
+
+static size_t
+hpa_ndirty_max(tsdn_t *tsdn, hpa_shard_t *shard) {
+ malloc_mutex_assert_owner(tsdn, &shard->mtx);
+ if (shard->opts.dirty_mult == (fxp_t)-1) {
+ return (size_t)-1;
+ }
+ return fxp_mul_frac(psset_nactive(&shard->psset),
+ shard->opts.dirty_mult);
+}
+
+static bool
+hpa_hugify_blocked_by_ndirty(tsdn_t *tsdn, hpa_shard_t *shard) {
+ malloc_mutex_assert_owner(tsdn, &shard->mtx);
+ hpdata_t *to_hugify = psset_pick_hugify(&shard->psset);
+ if (to_hugify == NULL) {
+ return false;
+ }
+ return hpa_adjusted_ndirty(tsdn, shard)
+ + hpdata_nretained_get(to_hugify) > hpa_ndirty_max(tsdn, shard);
+}
+
+static bool
+hpa_should_purge(tsdn_t *tsdn, hpa_shard_t *shard) {
+ malloc_mutex_assert_owner(tsdn, &shard->mtx);
+ if (hpa_adjusted_ndirty(tsdn, shard) > hpa_ndirty_max(tsdn, shard)) {
+ return true;
+ }
+ if (hpa_hugify_blocked_by_ndirty(tsdn, shard)) {
+ return true;
+ }
+ return false;
+}
+
+static void
+hpa_update_purge_hugify_eligibility(tsdn_t *tsdn, hpa_shard_t *shard,
+ hpdata_t *ps) {
+ malloc_mutex_assert_owner(tsdn, &shard->mtx);
+ if (hpdata_changing_state_get(ps)) {
+ hpdata_purge_allowed_set(ps, false);
+ hpdata_disallow_hugify(ps);
+ return;
+ }
+ /*
+ * Hugepages are distinctly costly to purge, so try to avoid it unless
+ * they're *particularly* full of dirty pages. Eventually, we should
+ * use a smarter / more dynamic heuristic for situations where we have
+ * to manually hugify.
+ *
+ * In situations where we don't manually hugify, this problem is
+ * reduced. The "bad" situation we're trying to avoid is one's that's
+ * common in some Linux configurations (where both enabled and defrag
+ * are set to madvise) that can lead to long latency spikes on the first
+ * access after a hugification. The ideal policy in such configurations
+ * is probably time-based for both purging and hugifying; only hugify a
+ * hugepage if it's met the criteria for some extended period of time,
+ * and only dehugify it if it's failed to meet the criteria for an
+ * extended period of time. When background threads are on, we should
+ * try to take this hit on one of them, as well.
+ *
+ * I think the ideal setting is THP always enabled, and defrag set to
+ * deferred; in that case we don't need any explicit calls on the
+ * allocator's end at all; we just try to pack allocations in a
+ * hugepage-friendly manner and let the OS hugify in the background.
+ */
+ hpdata_purge_allowed_set(ps, hpdata_ndirty_get(ps) > 0);
+ if (hpa_good_hugification_candidate(shard, ps)
+ && !hpdata_huge_get(ps)) {
+ nstime_t now;
+ shard->central->hooks.curtime(&now, /* first_reading */ true);
+ hpdata_allow_hugify(ps, now);
+ }
+ /*
+ * Once a hugepage has become eligible for hugification, we don't mark
+ * it as ineligible just because it stops meeting the criteria (this
+ * could lead to situations where a hugepage that spends most of its
+ * time meeting the criteria never quite getting hugified if there are
+ * intervening deallocations). The idea is that the hugification delay
+ * will allow them to get purged, reseting their "hugify-allowed" bit.
+ * If they don't get purged, then the hugification isn't hurting and
+ * might help. As an exception, we don't hugify hugepages that are now
+ * empty; it definitely doesn't help there until the hugepage gets
+ * reused, which is likely not for a while.
+ */
+ if (hpdata_nactive_get(ps) == 0) {
+ hpdata_disallow_hugify(ps);
+ }
+}
+
+static bool
+hpa_shard_has_deferred_work(tsdn_t *tsdn, hpa_shard_t *shard) {
+ malloc_mutex_assert_owner(tsdn, &shard->mtx);
+ hpdata_t *to_hugify = psset_pick_hugify(&shard->psset);
+ return to_hugify != NULL || hpa_should_purge(tsdn, shard);
+}
+
+/* Returns whether or not we purged anything. */
+static bool
+hpa_try_purge(tsdn_t *tsdn, hpa_shard_t *shard) {
+ malloc_mutex_assert_owner(tsdn, &shard->mtx);
+
+ hpdata_t *to_purge = psset_pick_purge(&shard->psset);
+ if (to_purge == NULL) {
+ return false;
+ }
+ assert(hpdata_purge_allowed_get(to_purge));
+ assert(!hpdata_changing_state_get(to_purge));
+
+ /*
+ * Don't let anyone else purge or hugify this page while
+ * we're purging it (allocations and deallocations are
+ * OK).
+ */
+ psset_update_begin(&shard->psset, to_purge);
+ assert(hpdata_alloc_allowed_get(to_purge));
+ hpdata_mid_purge_set(to_purge, true);
+ hpdata_purge_allowed_set(to_purge, false);
+ hpdata_disallow_hugify(to_purge);
+ /*
+ * Unlike with hugification (where concurrent
+ * allocations are allowed), concurrent allocation out
+ * of a hugepage being purged is unsafe; we might hand
+ * out an extent for an allocation and then purge it
+ * (clearing out user data).
+ */
+ hpdata_alloc_allowed_set(to_purge, false);
+ psset_update_end(&shard->psset, to_purge);
+
+ /* Gather all the metadata we'll need during the purge. */
+ bool dehugify = hpdata_huge_get(to_purge);
+ hpdata_purge_state_t purge_state;
+ size_t num_to_purge = hpdata_purge_begin(to_purge, &purge_state);
+
+ shard->npending_purge += num_to_purge;
+
+ malloc_mutex_unlock(tsdn, &shard->mtx);
+
+ /* Actually do the purging, now that the lock is dropped. */
+ if (dehugify) {
+ shard->central->hooks.dehugify(hpdata_addr_get(to_purge),
+ HUGEPAGE);
+ }
+ size_t total_purged = 0;
+ uint64_t purges_this_pass = 0;
+ void *purge_addr;
+ size_t purge_size;
+ while (hpdata_purge_next(to_purge, &purge_state, &purge_addr,
+ &purge_size)) {
+ total_purged += purge_size;
+ assert(total_purged <= HUGEPAGE);
+ purges_this_pass++;
+ shard->central->hooks.purge(purge_addr, purge_size);
+ }
+
+ malloc_mutex_lock(tsdn, &shard->mtx);
+ /* The shard updates */
+ shard->npending_purge -= num_to_purge;
+ shard->stats.npurge_passes++;
+ shard->stats.npurges += purges_this_pass;
+ shard->central->hooks.curtime(&shard->last_purge,
+ /* first_reading */ false);
+ if (dehugify) {
+ shard->stats.ndehugifies++;
+ }
+
+ /* The hpdata updates. */
+ psset_update_begin(&shard->psset, to_purge);
+ if (dehugify) {
+ hpdata_dehugify(to_purge);
+ }
+ hpdata_purge_end(to_purge, &purge_state);
+ hpdata_mid_purge_set(to_purge, false);
+
+ hpdata_alloc_allowed_set(to_purge, true);
+ hpa_update_purge_hugify_eligibility(tsdn, shard, to_purge);
+
+ psset_update_end(&shard->psset, to_purge);
+
+ return true;
+}
+
+/* Returns whether or not we hugified anything. */
+static bool
+hpa_try_hugify(tsdn_t *tsdn, hpa_shard_t *shard) {
+ malloc_mutex_assert_owner(tsdn, &shard->mtx);
+
+ if (hpa_hugify_blocked_by_ndirty(tsdn, shard)) {
+ return false;
+ }
+
+ hpdata_t *to_hugify = psset_pick_hugify(&shard->psset);
+ if (to_hugify == NULL) {
+ return false;
+ }
+ assert(hpdata_hugify_allowed_get(to_hugify));
+ assert(!hpdata_changing_state_get(to_hugify));
+
+ /* Make sure that it's been hugifiable for long enough. */
+ nstime_t time_hugify_allowed = hpdata_time_hugify_allowed(to_hugify);
+ uint64_t millis = shard->central->hooks.ms_since(&time_hugify_allowed);
+ if (millis < shard->opts.hugify_delay_ms) {
+ return false;
+ }
+
+ /*
+ * Don't let anyone else purge or hugify this page while
+ * we're hugifying it (allocations and deallocations are
+ * OK).
+ */
+ psset_update_begin(&shard->psset, to_hugify);
+ hpdata_mid_hugify_set(to_hugify, true);
+ hpdata_purge_allowed_set(to_hugify, false);
+ hpdata_disallow_hugify(to_hugify);
+ assert(hpdata_alloc_allowed_get(to_hugify));
+ psset_update_end(&shard->psset, to_hugify);
+
+ malloc_mutex_unlock(tsdn, &shard->mtx);
+
+ shard->central->hooks.hugify(hpdata_addr_get(to_hugify), HUGEPAGE);
+
+ malloc_mutex_lock(tsdn, &shard->mtx);
+ shard->stats.nhugifies++;
+
+ psset_update_begin(&shard->psset, to_hugify);
+ hpdata_hugify(to_hugify);
+ hpdata_mid_hugify_set(to_hugify, false);
+ hpa_update_purge_hugify_eligibility(tsdn, shard, to_hugify);
+ psset_update_end(&shard->psset, to_hugify);
+
+ return true;
+}
+
+/*
+ * Execution of deferred work is forced if it's triggered by an explicit
+ * hpa_shard_do_deferred_work() call.
+ */
+static void
+hpa_shard_maybe_do_deferred_work(tsdn_t *tsdn, hpa_shard_t *shard,
+ bool forced) {
+ malloc_mutex_assert_owner(tsdn, &shard->mtx);
+ if (!forced && shard->opts.deferral_allowed) {
+ return;
+ }
+ /*
+ * If we're on a background thread, do work so long as there's work to
+ * be done. Otherwise, bound latency to not be *too* bad by doing at
+ * most a small fixed number of operations.
+ */
+ bool hugified = false;
+ bool purged = false;
+ size_t max_ops = (forced ? (size_t)-1 : 16);
+ size_t nops = 0;
+ do {
+ /*
+ * Always purge before hugifying, to make sure we get some
+ * ability to hit our quiescence targets.
+ */
+ purged = false;
+ while (hpa_should_purge(tsdn, shard) && nops < max_ops) {
+ purged = hpa_try_purge(tsdn, shard);
+ if (purged) {
+ nops++;
+ }
+ }
+ hugified = hpa_try_hugify(tsdn, shard);
+ if (hugified) {
+ nops++;
+ }
+ malloc_mutex_assert_owner(tsdn, &shard->mtx);
+ malloc_mutex_assert_owner(tsdn, &shard->mtx);
+ } while ((hugified || purged) && nops < max_ops);
+}
+
+static edata_t *
+hpa_try_alloc_one_no_grow(tsdn_t *tsdn, hpa_shard_t *shard, size_t size,
+ bool *oom) {
+ bool err;
+ edata_t *edata = edata_cache_fast_get(tsdn, &shard->ecf);
+ if (edata == NULL) {
+ *oom = true;
+ return NULL;
+ }
+
+ hpdata_t *ps = psset_pick_alloc(&shard->psset, size);
+ if (ps == NULL) {
+ edata_cache_fast_put(tsdn, &shard->ecf, edata);
+ return NULL;
+ }
+
+ psset_update_begin(&shard->psset, ps);
+
+ if (hpdata_empty(ps)) {
+ /*
+ * If the pageslab used to be empty, treat it as though it's
+ * brand new for fragmentation-avoidance purposes; what we're
+ * trying to approximate is the age of the allocations *in* that
+ * pageslab, and the allocations in the new pageslab are
+ * definitionally the youngest in this hpa shard.
+ */
+ hpdata_age_set(ps, shard->age_counter++);
+ }
+
+ void *addr = hpdata_reserve_alloc(ps, size);
+ edata_init(edata, shard->ind, addr, size, /* slab */ false,
+ SC_NSIZES, /* sn */ hpdata_age_get(ps), extent_state_active,
+ /* zeroed */ false, /* committed */ true, EXTENT_PAI_HPA,
+ EXTENT_NOT_HEAD);
+ edata_ps_set(edata, ps);
+
+ /*
+ * This could theoretically be moved outside of the critical section,
+ * but that introduces the potential for a race. Without the lock, the
+ * (initially nonempty, since this is the reuse pathway) pageslab we
+ * allocated out of could become otherwise empty while the lock is
+ * dropped. This would force us to deal with a pageslab eviction down
+ * the error pathway, which is a pain.
+ */
+ err = emap_register_boundary(tsdn, shard->emap, edata,
+ SC_NSIZES, /* slab */ false);
+ if (err) {
+ hpdata_unreserve(ps, edata_addr_get(edata),
+ edata_size_get(edata));
+ /*
+ * We should arguably reset dirty state here, but this would
+ * require some sort of prepare + commit functionality that's a
+ * little much to deal with for now.
+ *
+ * We don't have a do_deferred_work down this pathway, on the
+ * principle that we didn't *really* affect shard state (we
+ * tweaked the stats, but our tweaks weren't really accurate).
+ */
+ psset_update_end(&shard->psset, ps);
+ edata_cache_fast_put(tsdn, &shard->ecf, edata);
+ *oom = true;
+ return NULL;
+ }
+
+ hpa_update_purge_hugify_eligibility(tsdn, shard, ps);
+ psset_update_end(&shard->psset, ps);
+ return edata;
+}
+
+static size_t
+hpa_try_alloc_batch_no_grow(tsdn_t *tsdn, hpa_shard_t *shard, size_t size,
+ bool *oom, size_t nallocs, edata_list_active_t *results,
+ bool *deferred_work_generated) {
+ malloc_mutex_lock(tsdn, &shard->mtx);
+ size_t nsuccess = 0;
+ for (; nsuccess < nallocs; nsuccess++) {
+ edata_t *edata = hpa_try_alloc_one_no_grow(tsdn, shard, size,
+ oom);
+ if (edata == NULL) {
+ break;
+ }
+ edata_list_active_append(results, edata);
+ }
+
+ hpa_shard_maybe_do_deferred_work(tsdn, shard, /* forced */ false);
+ *deferred_work_generated = hpa_shard_has_deferred_work(tsdn, shard);
+ malloc_mutex_unlock(tsdn, &shard->mtx);
+ return nsuccess;
+}
+
+static size_t
+hpa_alloc_batch_psset(tsdn_t *tsdn, hpa_shard_t *shard, size_t size,
+ size_t nallocs, edata_list_active_t *results,
+ bool *deferred_work_generated) {
+ assert(size <= shard->opts.slab_max_alloc);
+ bool oom = false;
+
+ size_t nsuccess = hpa_try_alloc_batch_no_grow(tsdn, shard, size, &oom,
+ nallocs, results, deferred_work_generated);
+
+ if (nsuccess == nallocs || oom) {
+ return nsuccess;
+ }
+
+ /*
+ * We didn't OOM, but weren't able to fill everything requested of us;
+ * try to grow.
+ */
+ malloc_mutex_lock(tsdn, &shard->grow_mtx);
+ /*
+ * Check for grow races; maybe some earlier thread expanded the psset
+ * in between when we dropped the main mutex and grabbed the grow mutex.
+ */
+ nsuccess += hpa_try_alloc_batch_no_grow(tsdn, shard, size, &oom,
+ nallocs - nsuccess, results, deferred_work_generated);
+ if (nsuccess == nallocs || oom) {
+ malloc_mutex_unlock(tsdn, &shard->grow_mtx);
+ return nsuccess;
+ }
+
+ /*
+ * Note that we don't hold shard->mtx here (while growing);
+ * deallocations (and allocations of smaller sizes) may still succeed
+ * while we're doing this potentially expensive system call.
+ */
+ hpdata_t *ps = hpa_central_extract(tsdn, shard->central, size, &oom);
+ if (ps == NULL) {
+ malloc_mutex_unlock(tsdn, &shard->grow_mtx);
+ return nsuccess;
+ }
+
+ /*
+ * We got the pageslab; allocate from it. This does an unlock followed
+ * by a lock on the same mutex, and holds the grow mutex while doing
+ * deferred work, but this is an uncommon path; the simplicity is worth
+ * it.
+ */
+ malloc_mutex_lock(tsdn, &shard->mtx);
+ psset_insert(&shard->psset, ps);
+ malloc_mutex_unlock(tsdn, &shard->mtx);
+
+ nsuccess += hpa_try_alloc_batch_no_grow(tsdn, shard, size, &oom,
+ nallocs - nsuccess, results, deferred_work_generated);
+ /*
+ * Drop grow_mtx before doing deferred work; other threads blocked on it
+ * should be allowed to proceed while we're working.
+ */
+ malloc_mutex_unlock(tsdn, &shard->grow_mtx);
+
+ return nsuccess;
+}
+
+static hpa_shard_t *
+hpa_from_pai(pai_t *self) {
+ assert(self->alloc = &hpa_alloc);
+ assert(self->expand = &hpa_expand);
+ assert(self->shrink = &hpa_shrink);
+ assert(self->dalloc = &hpa_dalloc);
+ return (hpa_shard_t *)self;
+}
+
+static size_t
+hpa_alloc_batch(tsdn_t *tsdn, pai_t *self, size_t size, size_t nallocs,
+ edata_list_active_t *results, bool *deferred_work_generated) {
+ assert(nallocs > 0);
+ assert((size & PAGE_MASK) == 0);
+ witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
+ WITNESS_RANK_CORE, 0);
+ hpa_shard_t *shard = hpa_from_pai(self);
+
+ if (size > shard->opts.slab_max_alloc) {
+ return 0;
+ }
+
+ size_t nsuccess = hpa_alloc_batch_psset(tsdn, shard, size, nallocs,
+ results, deferred_work_generated);
+
+ witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
+ WITNESS_RANK_CORE, 0);
+
+ /*
+ * Guard the sanity checks with config_debug because the loop cannot be
+ * proven non-circular by the compiler, even if everything within the
+ * loop is optimized away.
+ */
+ if (config_debug) {
+ edata_t *edata;
+ ql_foreach(edata, &results->head, ql_link_active) {
+ emap_assert_mapped(tsdn, shard->emap, edata);
+ assert(edata_pai_get(edata) == EXTENT_PAI_HPA);
+ assert(edata_state_get(edata) == extent_state_active);
+ assert(edata_arena_ind_get(edata) == shard->ind);
+ assert(edata_szind_get_maybe_invalid(edata) ==
+ SC_NSIZES);
+ assert(!edata_slab_get(edata));
+ assert(edata_committed_get(edata));
+ assert(edata_base_get(edata) == edata_addr_get(edata));
+ assert(edata_base_get(edata) != NULL);
+ }
+ }
+ return nsuccess;
+}
+
+static edata_t *
+hpa_alloc(tsdn_t *tsdn, pai_t *self, size_t size, size_t alignment, bool zero,
+ bool guarded, bool frequent_reuse, bool *deferred_work_generated) {
+ assert((size & PAGE_MASK) == 0);
+ assert(!guarded);
+ witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
+ WITNESS_RANK_CORE, 0);
+
+ /* We don't handle alignment or zeroing for now. */
+ if (alignment > PAGE || zero) {
+ return NULL;
+ }
+ /*
+ * An alloc with alignment == PAGE and zero == false is equivalent to a
+ * batch alloc of 1. Just do that, so we can share code.
+ */
+ edata_list_active_t results;
+ edata_list_active_init(&results);
+ size_t nallocs = hpa_alloc_batch(tsdn, self, size, /* nallocs */ 1,
+ &results, deferred_work_generated);
+ assert(nallocs == 0 || nallocs == 1);
+ edata_t *edata = edata_list_active_first(&results);
+ return edata;
+}
+
+static bool
+hpa_expand(tsdn_t *tsdn, pai_t *self, edata_t *edata, size_t old_size,
+ size_t new_size, bool zero, bool *deferred_work_generated) {
+ /* Expand not yet supported. */
+ return true;
+}
+
+static bool
+hpa_shrink(tsdn_t *tsdn, pai_t *self, edata_t *edata,
+ size_t old_size, size_t new_size, bool *deferred_work_generated) {
+ /* Shrink not yet supported. */
+ return true;
+}
+
+static void
+hpa_dalloc_prepare_unlocked(tsdn_t *tsdn, hpa_shard_t *shard, edata_t *edata) {
+ malloc_mutex_assert_not_owner(tsdn, &shard->mtx);
+
+ assert(edata_pai_get(edata) == EXTENT_PAI_HPA);
+ assert(edata_state_get(edata) == extent_state_active);
+ assert(edata_arena_ind_get(edata) == shard->ind);
+ assert(edata_szind_get_maybe_invalid(edata) == SC_NSIZES);
+ assert(edata_committed_get(edata));
+ assert(edata_base_get(edata) != NULL);
+
+ /*
+ * Another thread shouldn't be trying to touch the metadata of an
+ * allocation being freed. The one exception is a merge attempt from a
+ * lower-addressed PAC extent; in this case we have a nominal race on
+ * the edata metadata bits, but in practice the fact that the PAI bits
+ * are different will prevent any further access. The race is bad, but
+ * benign in practice, and the long term plan is to track enough state
+ * in the rtree to prevent these merge attempts in the first place.
+ */
+ edata_addr_set(edata, edata_base_get(edata));
+ edata_zeroed_set(edata, false);
+ emap_deregister_boundary(tsdn, shard->emap, edata);
+}
+
+static void
+hpa_dalloc_locked(tsdn_t *tsdn, hpa_shard_t *shard, edata_t *edata) {
+ malloc_mutex_assert_owner(tsdn, &shard->mtx);
+
+ /*
+ * Release the metadata early, to avoid having to remember to do it
+ * while we're also doing tricky purging logic. First, we need to grab
+ * a few bits of metadata from it.
+ *
+ * Note that the shard mutex protects ps's metadata too; it wouldn't be
+ * correct to try to read most information out of it without the lock.
+ */
+ hpdata_t *ps = edata_ps_get(edata);
+ /* Currently, all edatas come from pageslabs. */
+ assert(ps != NULL);
+ void *unreserve_addr = edata_addr_get(edata);
+ size_t unreserve_size = edata_size_get(edata);
+ edata_cache_fast_put(tsdn, &shard->ecf, edata);
+
+ psset_update_begin(&shard->psset, ps);
+ hpdata_unreserve(ps, unreserve_addr, unreserve_size);
+ hpa_update_purge_hugify_eligibility(tsdn, shard, ps);
+ psset_update_end(&shard->psset, ps);
+}
+
+static void
+hpa_dalloc_batch(tsdn_t *tsdn, pai_t *self, edata_list_active_t *list,
+ bool *deferred_work_generated) {
+ hpa_shard_t *shard = hpa_from_pai(self);
+
+ edata_t *edata;
+ ql_foreach(edata, &list->head, ql_link_active) {
+ hpa_dalloc_prepare_unlocked(tsdn, shard, edata);
+ }
+
+ malloc_mutex_lock(tsdn, &shard->mtx);
+ /* Now, remove from the list. */
+ while ((edata = edata_list_active_first(list)) != NULL) {
+ edata_list_active_remove(list, edata);
+ hpa_dalloc_locked(tsdn, shard, edata);
+ }
+ hpa_shard_maybe_do_deferred_work(tsdn, shard, /* forced */ false);
+ *deferred_work_generated =
+ hpa_shard_has_deferred_work(tsdn, shard);
+
+ malloc_mutex_unlock(tsdn, &shard->mtx);
+}
+
+static void
+hpa_dalloc(tsdn_t *tsdn, pai_t *self, edata_t *edata,
+ bool *deferred_work_generated) {
+ assert(!edata_guarded_get(edata));
+ /* Just a dalloc_batch of size 1; this lets us share logic. */
+ edata_list_active_t dalloc_list;
+ edata_list_active_init(&dalloc_list);
+ edata_list_active_append(&dalloc_list, edata);
+ hpa_dalloc_batch(tsdn, self, &dalloc_list, deferred_work_generated);
+}
+
+/*
+ * Calculate time until either purging or hugification ought to happen.
+ * Called by background threads.
+ */
+static uint64_t
+hpa_time_until_deferred_work(tsdn_t *tsdn, pai_t *self) {
+ hpa_shard_t *shard = hpa_from_pai(self);
+ uint64_t time_ns = BACKGROUND_THREAD_DEFERRED_MAX;
+
+ malloc_mutex_lock(tsdn, &shard->mtx);
+
+ hpdata_t *to_hugify = psset_pick_hugify(&shard->psset);
+ if (to_hugify != NULL) {
+ nstime_t time_hugify_allowed =
+ hpdata_time_hugify_allowed(to_hugify);
+ uint64_t since_hugify_allowed_ms =
+ shard->central->hooks.ms_since(&time_hugify_allowed);
+ /*
+ * If not enough time has passed since hugification was allowed,
+ * sleep for the rest.
+ */
+ if (since_hugify_allowed_ms < shard->opts.hugify_delay_ms) {
+ time_ns = shard->opts.hugify_delay_ms -
+ since_hugify_allowed_ms;
+ time_ns *= 1000 * 1000;
+ } else {
+ malloc_mutex_unlock(tsdn, &shard->mtx);
+ return BACKGROUND_THREAD_DEFERRED_MIN;
+ }
+ }
+
+ if (hpa_should_purge(tsdn, shard)) {
+ /*
+ * If we haven't purged before, no need to check interval
+ * between purges. Simply purge as soon as possible.
+ */
+ if (shard->stats.npurge_passes == 0) {
+ malloc_mutex_unlock(tsdn, &shard->mtx);
+ return BACKGROUND_THREAD_DEFERRED_MIN;
+ }
+ uint64_t since_last_purge_ms = shard->central->hooks.ms_since(
+ &shard->last_purge);
+
+ if (since_last_purge_ms < shard->opts.min_purge_interval_ms) {
+ uint64_t until_purge_ns;
+ until_purge_ns = shard->opts.min_purge_interval_ms -
+ since_last_purge_ms;
+ until_purge_ns *= 1000 * 1000;
+
+ if (until_purge_ns < time_ns) {
+ time_ns = until_purge_ns;
+ }
+ } else {
+ time_ns = BACKGROUND_THREAD_DEFERRED_MIN;
+ }
+ }
+ malloc_mutex_unlock(tsdn, &shard->mtx);
+ return time_ns;
+}
+
+void
+hpa_shard_disable(tsdn_t *tsdn, hpa_shard_t *shard) {
+ hpa_do_consistency_checks(shard);
+
+ malloc_mutex_lock(tsdn, &shard->mtx);
+ edata_cache_fast_disable(tsdn, &shard->ecf);
+ malloc_mutex_unlock(tsdn, &shard->mtx);
+}
+
+static void
+hpa_shard_assert_stats_empty(psset_bin_stats_t *bin_stats) {
+ assert(bin_stats->npageslabs == 0);
+ assert(bin_stats->nactive == 0);
+}
+
+static void
+hpa_assert_empty(tsdn_t *tsdn, hpa_shard_t *shard, psset_t *psset) {
+ malloc_mutex_assert_owner(tsdn, &shard->mtx);
+ for (int huge = 0; huge <= 1; huge++) {
+ hpa_shard_assert_stats_empty(&psset->stats.full_slabs[huge]);
+ for (pszind_t i = 0; i < PSSET_NPSIZES; i++) {
+ hpa_shard_assert_stats_empty(
+ &psset->stats.nonfull_slabs[i][huge]);
+ }
+ }
+}
+
+void
+hpa_shard_destroy(tsdn_t *tsdn, hpa_shard_t *shard) {
+ hpa_do_consistency_checks(shard);
+ /*
+ * By the time we're here, the arena code should have dalloc'd all the
+ * active extents, which means we should have eventually evicted
+ * everything from the psset, so it shouldn't be able to serve even a
+ * 1-page allocation.
+ */
+ if (config_debug) {
+ malloc_mutex_lock(tsdn, &shard->mtx);
+ hpa_assert_empty(tsdn, shard, &shard->psset);
+ malloc_mutex_unlock(tsdn, &shard->mtx);
+ }
+ hpdata_t *ps;
+ while ((ps = psset_pick_alloc(&shard->psset, PAGE)) != NULL) {
+ /* There should be no allocations anywhere. */
+ assert(hpdata_empty(ps));
+ psset_remove(&shard->psset, ps);
+ shard->central->hooks.unmap(hpdata_addr_get(ps), HUGEPAGE);
+ }
+}
+
+void
+hpa_shard_set_deferral_allowed(tsdn_t *tsdn, hpa_shard_t *shard,
+ bool deferral_allowed) {
+ hpa_do_consistency_checks(shard);
+
+ malloc_mutex_lock(tsdn, &shard->mtx);
+ bool deferral_previously_allowed = shard->opts.deferral_allowed;
+ shard->opts.deferral_allowed = deferral_allowed;
+ if (deferral_previously_allowed && !deferral_allowed) {
+ hpa_shard_maybe_do_deferred_work(tsdn, shard,
+ /* forced */ true);
+ }
+ malloc_mutex_unlock(tsdn, &shard->mtx);
+}
+
+void
+hpa_shard_do_deferred_work(tsdn_t *tsdn, hpa_shard_t *shard) {
+ hpa_do_consistency_checks(shard);
+
+ malloc_mutex_lock(tsdn, &shard->mtx);
+ hpa_shard_maybe_do_deferred_work(tsdn, shard, /* forced */ true);
+ malloc_mutex_unlock(tsdn, &shard->mtx);
+}
+
+void
+hpa_shard_prefork3(tsdn_t *tsdn, hpa_shard_t *shard) {
+ hpa_do_consistency_checks(shard);
+
+ malloc_mutex_prefork(tsdn, &shard->grow_mtx);
+}
+
+void
+hpa_shard_prefork4(tsdn_t *tsdn, hpa_shard_t *shard) {
+ hpa_do_consistency_checks(shard);
+
+ malloc_mutex_prefork(tsdn, &shard->mtx);
+}
+
+void
+hpa_shard_postfork_parent(tsdn_t *tsdn, hpa_shard_t *shard) {
+ hpa_do_consistency_checks(shard);
+
+ malloc_mutex_postfork_parent(tsdn, &shard->grow_mtx);
+ malloc_mutex_postfork_parent(tsdn, &shard->mtx);
+}
+
+void
+hpa_shard_postfork_child(tsdn_t *tsdn, hpa_shard_t *shard) {
+ hpa_do_consistency_checks(shard);
+
+ malloc_mutex_postfork_child(tsdn, &shard->grow_mtx);
+ malloc_mutex_postfork_child(tsdn, &shard->mtx);
+}
diff --git a/contrib/jemalloc/src/hpa_hooks.c b/contrib/jemalloc/src/hpa_hooks.c
new file mode 100644
index 000000000000..ade581e8dc03
--- /dev/null
+++ b/contrib/jemalloc/src/hpa_hooks.c
@@ -0,0 +1,63 @@
+#include "jemalloc/internal/jemalloc_preamble.h"
+#include "jemalloc/internal/jemalloc_internal_includes.h"
+
+#include "jemalloc/internal/hpa_hooks.h"
+
+static void *hpa_hooks_map(size_t size);
+static void hpa_hooks_unmap(void *ptr, size_t size);
+static void hpa_hooks_purge(void *ptr, size_t size);
+static void hpa_hooks_hugify(void *ptr, size_t size);
+static void hpa_hooks_dehugify(void *ptr, size_t size);
+static void hpa_hooks_curtime(nstime_t *r_nstime, bool first_reading);
+static uint64_t hpa_hooks_ms_since(nstime_t *past_nstime);
+
+hpa_hooks_t hpa_hooks_default = {
+ &hpa_hooks_map,
+ &hpa_hooks_unmap,
+ &hpa_hooks_purge,
+ &hpa_hooks_hugify,
+ &hpa_hooks_dehugify,
+ &hpa_hooks_curtime,
+ &hpa_hooks_ms_since
+};
+
+static void *
+hpa_hooks_map(size_t size) {
+ bool commit = true;
+ return pages_map(NULL, size, HUGEPAGE, &commit);
+}
+
+static void
+hpa_hooks_unmap(void *ptr, size_t size) {
+ pages_unmap(ptr, size);
+}
+
+static void
+hpa_hooks_purge(void *ptr, size_t size) {
+ pages_purge_forced(ptr, size);
+}
+
+static void
+hpa_hooks_hugify(void *ptr, size_t size) {
+ bool err = pages_huge(ptr, size);
+ (void)err;
+}
+
+static void
+hpa_hooks_dehugify(void *ptr, size_t size) {
+ bool err = pages_nohuge(ptr, size);
+ (void)err;
+}
+
+static void
+hpa_hooks_curtime(nstime_t *r_nstime, bool first_reading) {
+ if (first_reading) {
+ nstime_init_zero(r_nstime);
+ }
+ nstime_update(r_nstime);
+}
+
+static uint64_t
+hpa_hooks_ms_since(nstime_t *past_nstime) {
+ return nstime_ns_since(past_nstime) / 1000 / 1000;
+}
diff --git a/contrib/jemalloc/src/hpdata.c b/contrib/jemalloc/src/hpdata.c
new file mode 100644
index 000000000000..e7d7294c7822
--- /dev/null
+++ b/contrib/jemalloc/src/hpdata.c
@@ -0,0 +1,325 @@
+#include "jemalloc/internal/jemalloc_preamble.h"
+#include "jemalloc/internal/jemalloc_internal_includes.h"
+
+#include "jemalloc/internal/hpdata.h"
+
+static int
+hpdata_age_comp(const hpdata_t *a, const hpdata_t *b) {
+ uint64_t a_age = hpdata_age_get(a);
+ uint64_t b_age = hpdata_age_get(b);
+ /*
+ * hpdata ages are operation counts in the psset; no two should be the
+ * same.
+ */
+ assert(a_age != b_age);
+ return (a_age > b_age) - (a_age < b_age);
+}
+
+ph_gen(, hpdata_age_heap, hpdata_t, age_link, hpdata_age_comp)
+
+void
+hpdata_init(hpdata_t *hpdata, void *addr, uint64_t age) {
+ hpdata_addr_set(hpdata, addr);
+ hpdata_age_set(hpdata, age);
+ hpdata->h_huge = false;
+ hpdata->h_alloc_allowed = true;
+ hpdata->h_in_psset_alloc_container = false;
+ hpdata->h_purge_allowed = false;
+ hpdata->h_hugify_allowed = false;
+ hpdata->h_in_psset_hugify_container = false;
+ hpdata->h_mid_purge = false;
+ hpdata->h_mid_hugify = false;
+ hpdata->h_updating = false;
+ hpdata->h_in_psset = false;
+ hpdata_longest_free_range_set(hpdata, HUGEPAGE_PAGES);
+ hpdata->h_nactive = 0;
+ fb_init(hpdata->active_pages, HUGEPAGE_PAGES);
+ hpdata->h_ntouched = 0;
+ fb_init(hpdata->touched_pages, HUGEPAGE_PAGES);
+
+ hpdata_assert_consistent(hpdata);
+}
+
+void *
+hpdata_reserve_alloc(hpdata_t *hpdata, size_t sz) {
+ hpdata_assert_consistent(hpdata);
+ /*
+ * This is a metadata change; the hpdata should therefore either not be
+ * in the psset, or should have explicitly marked itself as being
+ * mid-update.
+ */
+ assert(!hpdata->h_in_psset || hpdata->h_updating);
+ assert(hpdata->h_alloc_allowed);
+ assert((sz & PAGE_MASK) == 0);
+ size_t npages = sz >> LG_PAGE;
+ assert(npages <= hpdata_longest_free_range_get(hpdata));
+
+ size_t result;
+
+ size_t start = 0;
+ /*
+ * These are dead stores, but the compiler will issue warnings on them
+ * since it can't tell statically that found is always true below.
+ */
+ size_t begin = 0;
+ size_t len = 0;
+
+ size_t largest_unchosen_range = 0;
+ while (true) {
+ bool found = fb_urange_iter(hpdata->active_pages,
+ HUGEPAGE_PAGES, start, &begin, &len);
+ /*
+ * A precondition to this function is that hpdata must be able
+ * to serve the allocation.
+ */
+ assert(found);
+ assert(len <= hpdata_longest_free_range_get(hpdata));
+ if (len >= npages) {
+ /*
+ * We use first-fit within the page slabs; this gives
+ * bounded worst-case fragmentation within a slab. It's
+ * not necessarily right; we could experiment with
+ * various other options.
+ */
+ break;
+ }
+ if (len > largest_unchosen_range) {
+ largest_unchosen_range = len;
+ }
+ start = begin + len;
+ }
+ /* We found a range; remember it. */
+ result = begin;
+ fb_set_range(hpdata->active_pages, HUGEPAGE_PAGES, begin, npages);
+ hpdata->h_nactive += npages;
+
+ /*
+ * We might be about to dirty some memory for the first time; update our
+ * count if so.
+ */
+ size_t new_dirty = fb_ucount(hpdata->touched_pages, HUGEPAGE_PAGES,
+ result, npages);
+ fb_set_range(hpdata->touched_pages, HUGEPAGE_PAGES, result, npages);
+ hpdata->h_ntouched += new_dirty;
+
+ /*
+ * If we allocated out of a range that was the longest in the hpdata, it
+ * might be the only one of that size and we'll have to adjust the
+ * metadata.
+ */
+ if (len == hpdata_longest_free_range_get(hpdata)) {
+ start = begin + npages;
+ while (start < HUGEPAGE_PAGES) {
+ bool found = fb_urange_iter(hpdata->active_pages,
+ HUGEPAGE_PAGES, start, &begin, &len);
+ if (!found) {
+ break;
+ }
+ assert(len <= hpdata_longest_free_range_get(hpdata));
+ if (len == hpdata_longest_free_range_get(hpdata)) {
+ largest_unchosen_range = len;
+ break;
+ }
+ if (len > largest_unchosen_range) {
+ largest_unchosen_range = len;
+ }
+ start = begin + len;
+ }
+ hpdata_longest_free_range_set(hpdata, largest_unchosen_range);
+ }
+
+ hpdata_assert_consistent(hpdata);
+ return (void *)(
+ (uintptr_t)hpdata_addr_get(hpdata) + (result << LG_PAGE));
+}
+
+void
+hpdata_unreserve(hpdata_t *hpdata, void *addr, size_t sz) {
+ hpdata_assert_consistent(hpdata);
+ /* See the comment in reserve. */
+ assert(!hpdata->h_in_psset || hpdata->h_updating);
+ assert(((uintptr_t)addr & PAGE_MASK) == 0);
+ assert((sz & PAGE_MASK) == 0);
+ size_t begin = ((uintptr_t)addr - (uintptr_t)hpdata_addr_get(hpdata))
+ >> LG_PAGE;
+ assert(begin < HUGEPAGE_PAGES);
+ size_t npages = sz >> LG_PAGE;
+ size_t old_longest_range = hpdata_longest_free_range_get(hpdata);
+
+ fb_unset_range(hpdata->active_pages, HUGEPAGE_PAGES, begin, npages);
+ /* We might have just created a new, larger range. */
+ size_t new_begin = (fb_fls(hpdata->active_pages, HUGEPAGE_PAGES,
+ begin) + 1);
+ size_t new_end = fb_ffs(hpdata->active_pages, HUGEPAGE_PAGES,
+ begin + npages - 1);
+ size_t new_range_len = new_end - new_begin;
+
+ if (new_range_len > old_longest_range) {
+ hpdata_longest_free_range_set(hpdata, new_range_len);
+ }
+
+ hpdata->h_nactive -= npages;
+
+ hpdata_assert_consistent(hpdata);
+}
+
+size_t
+hpdata_purge_begin(hpdata_t *hpdata, hpdata_purge_state_t *purge_state) {
+ hpdata_assert_consistent(hpdata);
+ /*
+ * See the comment below; we might purge any inactive extent, so it's
+ * unsafe for any other thread to turn any inactive extent active while
+ * we're operating on it.
+ */
+ assert(!hpdata_alloc_allowed_get(hpdata));
+
+ purge_state->npurged = 0;
+ purge_state->next_purge_search_begin = 0;
+
+ /*
+ * Initialize to_purge.
+ *
+ * It's possible to end up in situations where two dirty extents are
+ * separated by a retained extent:
+ * - 1 page allocated.
+ * - 1 page allocated.
+ * - 1 pages allocated.
+ *
+ * If the middle page is freed and purged, and then the first and third
+ * pages are freed, and then another purge pass happens, the hpdata
+ * looks like this:
+ * - 1 page dirty.
+ * - 1 page retained.
+ * - 1 page dirty.
+ *
+ * But it's safe to do a single 3-page purge.
+ *
+ * We do this by first computing the dirty pages, and then filling in
+ * any gaps by extending each range in the dirty bitmap to extend until
+ * the next active page. This purges more pages, but the expensive part
+ * of purging is the TLB shootdowns, rather than the kernel state
+ * tracking; doing a little bit more of the latter is fine if it saves
+ * us from doing some of the former.
+ */
+
+ /*
+ * The dirty pages are those that are touched but not active. Note that
+ * in a normal-ish case, HUGEPAGE_PAGES is something like 512 and the
+ * fb_group_t is 64 bits, so this is 64 bytes, spread across 8
+ * fb_group_ts.
+ */
+ fb_group_t dirty_pages[FB_NGROUPS(HUGEPAGE_PAGES)];
+ fb_init(dirty_pages, HUGEPAGE_PAGES);
+ fb_bit_not(dirty_pages, hpdata->active_pages, HUGEPAGE_PAGES);
+ fb_bit_and(dirty_pages, dirty_pages, hpdata->touched_pages,
+ HUGEPAGE_PAGES);
+
+ fb_init(purge_state->to_purge, HUGEPAGE_PAGES);
+ size_t next_bit = 0;
+ while (next_bit < HUGEPAGE_PAGES) {
+ size_t next_dirty = fb_ffs(dirty_pages, HUGEPAGE_PAGES,
+ next_bit);
+ /* Recall that fb_ffs returns nbits if no set bit is found. */
+ if (next_dirty == HUGEPAGE_PAGES) {
+ break;
+ }
+ size_t next_active = fb_ffs(hpdata->active_pages,
+ HUGEPAGE_PAGES, next_dirty);
+ /*
+ * Don't purge past the end of the dirty extent, into retained
+ * pages. This helps the kernel a tiny bit, but honestly it's
+ * mostly helpful for testing (where we tend to write test cases
+ * that think in terms of the dirty ranges).
+ */
+ ssize_t last_dirty = fb_fls(dirty_pages, HUGEPAGE_PAGES,
+ next_active - 1);
+ assert(last_dirty >= 0);
+ assert((size_t)last_dirty >= next_dirty);
+ assert((size_t)last_dirty - next_dirty + 1 <= HUGEPAGE_PAGES);
+
+ fb_set_range(purge_state->to_purge, HUGEPAGE_PAGES, next_dirty,
+ last_dirty - next_dirty + 1);
+ next_bit = next_active + 1;
+ }
+
+ /* We should purge, at least, everything dirty. */
+ size_t ndirty = hpdata->h_ntouched - hpdata->h_nactive;
+ purge_state->ndirty_to_purge = ndirty;
+ assert(ndirty <= fb_scount(
+ purge_state->to_purge, HUGEPAGE_PAGES, 0, HUGEPAGE_PAGES));
+ assert(ndirty == fb_scount(dirty_pages, HUGEPAGE_PAGES, 0,
+ HUGEPAGE_PAGES));
+
+ hpdata_assert_consistent(hpdata);
+
+ return ndirty;
+}
+
+bool
+hpdata_purge_next(hpdata_t *hpdata, hpdata_purge_state_t *purge_state,
+ void **r_purge_addr, size_t *r_purge_size) {
+ /*
+ * Note that we don't have a consistency check here; we're accessing
+ * hpdata without synchronization, and therefore have no right to expect
+ * a consistent state.
+ */
+ assert(!hpdata_alloc_allowed_get(hpdata));
+
+ if (purge_state->next_purge_search_begin == HUGEPAGE_PAGES) {
+ return false;
+ }
+ size_t purge_begin;
+ size_t purge_len;
+ bool found_range = fb_srange_iter(purge_state->to_purge, HUGEPAGE_PAGES,
+ purge_state->next_purge_search_begin, &purge_begin, &purge_len);
+ if (!found_range) {
+ return false;
+ }
+
+ *r_purge_addr = (void *)(
+ (uintptr_t)hpdata_addr_get(hpdata) + purge_begin * PAGE);
+ *r_purge_size = purge_len * PAGE;
+
+ purge_state->next_purge_search_begin = purge_begin + purge_len;
+ purge_state->npurged += purge_len;
+ assert(purge_state->npurged <= HUGEPAGE_PAGES);
+
+ return true;
+}
+
+void
+hpdata_purge_end(hpdata_t *hpdata, hpdata_purge_state_t *purge_state) {
+ assert(!hpdata_alloc_allowed_get(hpdata));
+ hpdata_assert_consistent(hpdata);
+ /* See the comment in reserve. */
+ assert(!hpdata->h_in_psset || hpdata->h_updating);
+
+ assert(purge_state->npurged == fb_scount(purge_state->to_purge,
+ HUGEPAGE_PAGES, 0, HUGEPAGE_PAGES));
+ assert(purge_state->npurged >= purge_state->ndirty_to_purge);
+
+ fb_bit_not(purge_state->to_purge, purge_state->to_purge,
+ HUGEPAGE_PAGES);
+ fb_bit_and(hpdata->touched_pages, hpdata->touched_pages,
+ purge_state->to_purge, HUGEPAGE_PAGES);
+ assert(hpdata->h_ntouched >= purge_state->ndirty_to_purge);
+ hpdata->h_ntouched -= purge_state->ndirty_to_purge;
+
+ hpdata_assert_consistent(hpdata);
+}
+
+void
+hpdata_hugify(hpdata_t *hpdata) {
+ hpdata_assert_consistent(hpdata);
+ hpdata->h_huge = true;
+ fb_set_range(hpdata->touched_pages, HUGEPAGE_PAGES, 0, HUGEPAGE_PAGES);
+ hpdata->h_ntouched = HUGEPAGE_PAGES;
+ hpdata_assert_consistent(hpdata);
+}
+
+void
+hpdata_dehugify(hpdata_t *hpdata) {
+ hpdata_assert_consistent(hpdata);
+ hpdata->h_huge = false;
+ hpdata_assert_consistent(hpdata);
+}
diff --git a/contrib/jemalloc/src/inspect.c b/contrib/jemalloc/src/inspect.c
new file mode 100644
index 000000000000..911b5d524ac3
--- /dev/null
+++ b/contrib/jemalloc/src/inspect.c
@@ -0,0 +1,77 @@
+#include "jemalloc/internal/jemalloc_preamble.h"
+#include "jemalloc/internal/jemalloc_internal_includes.h"
+
+void
+inspect_extent_util_stats_get(tsdn_t *tsdn, const void *ptr, size_t *nfree,
+ size_t *nregs, size_t *size) {
+ assert(ptr != NULL && nfree != NULL && nregs != NULL && size != NULL);
+
+ const edata_t *edata = emap_edata_lookup(tsdn, &arena_emap_global, ptr);
+ if (unlikely(edata == NULL)) {
+ *nfree = *nregs = *size = 0;
+ return;
+ }
+
+ *size = edata_size_get(edata);
+ if (!edata_slab_get(edata)) {
+ *nfree = 0;
+ *nregs = 1;
+ } else {
+ *nfree = edata_nfree_get(edata);
+ *nregs = bin_infos[edata_szind_get(edata)].nregs;
+ assert(*nfree <= *nregs);
+ assert(*nfree * edata_usize_get(edata) <= *size);
+ }
+}
+
+void
+inspect_extent_util_stats_verbose_get(tsdn_t *tsdn, const void *ptr,
+ size_t *nfree, size_t *nregs, size_t *size, size_t *bin_nfree,
+ size_t *bin_nregs, void **slabcur_addr) {
+ assert(ptr != NULL && nfree != NULL && nregs != NULL && size != NULL
+ && bin_nfree != NULL && bin_nregs != NULL && slabcur_addr != NULL);
+
+ const edata_t *edata = emap_edata_lookup(tsdn, &arena_emap_global, ptr);
+ if (unlikely(edata == NULL)) {
+ *nfree = *nregs = *size = *bin_nfree = *bin_nregs = 0;
+ *slabcur_addr = NULL;
+ return;
+ }
+
+ *size = edata_size_get(edata);
+ if (!edata_slab_get(edata)) {
+ *nfree = *bin_nfree = *bin_nregs = 0;
+ *nregs = 1;
+ *slabcur_addr = NULL;
+ return;
+ }
+
+ *nfree = edata_nfree_get(edata);
+ const szind_t szind = edata_szind_get(edata);
+ *nregs = bin_infos[szind].nregs;
+ assert(*nfree <= *nregs);
+ assert(*nfree * edata_usize_get(edata) <= *size);
+
+ arena_t *arena = (arena_t *)atomic_load_p(
+ &arenas[edata_arena_ind_get(edata)], ATOMIC_RELAXED);
+ assert(arena != NULL);
+ const unsigned binshard = edata_binshard_get(edata);
+ bin_t *bin = arena_get_bin(arena, szind, binshard);
+
+ malloc_mutex_lock(tsdn, &bin->lock);
+ if (config_stats) {
+ *bin_nregs = *nregs * bin->stats.curslabs;
+ assert(*bin_nregs >= bin->stats.curregs);
+ *bin_nfree = *bin_nregs - bin->stats.curregs;
+ } else {
+ *bin_nfree = *bin_nregs = 0;
+ }
+ edata_t *slab;
+ if (bin->slabcur != NULL) {
+ slab = bin->slabcur;
+ } else {
+ slab = edata_heap_first(&bin->slabs_nonfull);
+ }
+ *slabcur_addr = slab != NULL ? edata_addr_get(slab) : NULL;
+ malloc_mutex_unlock(tsdn, &bin->lock);
+}
diff --git a/contrib/jemalloc/src/jemalloc.c b/contrib/jemalloc/src/jemalloc.c
index fefb719ac5c4..e4b183d1a24d 100644
--- a/contrib/jemalloc/src/jemalloc.c
+++ b/contrib/jemalloc/src/jemalloc.c
@@ -4,20 +4,26 @@
#include "jemalloc/internal/assert.h"
#include "jemalloc/internal/atomic.h"
+#include "jemalloc/internal/buf_writer.h"
#include "jemalloc/internal/ctl.h"
+#include "jemalloc/internal/emap.h"
#include "jemalloc/internal/extent_dss.h"
#include "jemalloc/internal/extent_mmap.h"
+#include "jemalloc/internal/fxp.h"
+#include "jemalloc/internal/san.h"
#include "jemalloc/internal/hook.h"
#include "jemalloc/internal/jemalloc_internal_types.h"
#include "jemalloc/internal/log.h"
#include "jemalloc/internal/malloc_io.h"
#include "jemalloc/internal/mutex.h"
+#include "jemalloc/internal/nstime.h"
#include "jemalloc/internal/rtree.h"
#include "jemalloc/internal/safety_check.h"
#include "jemalloc/internal/sc.h"
#include "jemalloc/internal/spin.h"
#include "jemalloc/internal/sz.h"
#include "jemalloc/internal/ticker.h"
+#include "jemalloc/internal/thread_event.h"
#include "jemalloc/internal/util.h"
/******************************************************************************/
@@ -33,6 +39,29 @@ const char *je_malloc_conf
JEMALLOC_ATTR(weak)
#endif
;
+/*
+ * The usual rule is that the closer to runtime you are, the higher priority
+ * your configuration settings are (so the jemalloc config options get lower
+ * priority than the per-binary setting, which gets lower priority than the /etc
+ * setting, which gets lower priority than the environment settings).
+ *
+ * But it's a fairly common use case in some testing environments for a user to
+ * be able to control the binary, but nothing else (e.g. a performancy canary
+ * uses the production OS and environment variables, but can run any binary in
+ * those circumstances). For these use cases, it's handy to have an in-binary
+ * mechanism for overriding environment variable settings, with the idea that if
+ * the results are positive they get promoted to the official settings, and
+ * moved from the binary to the environment variable.
+ *
+ * We don't actually want this to be widespread, so we'll give it a silly name
+ * and not mention it in headers or documentation.
+ */
+const char *je_malloc_conf_2_conf_harder
+#ifndef _WIN32
+ JEMALLOC_ATTR(weak)
+#endif
+ ;
+
bool opt_abort =
#ifdef JEMALLOC_DEBUG
true
@@ -70,16 +99,73 @@ bool opt_junk_free =
false
#endif
;
+bool opt_trust_madvise =
+#ifdef JEMALLOC_PURGE_MADVISE_DONTNEED_ZEROS
+ false
+#else
+ true
+#endif
+ ;
+
+bool opt_cache_oblivious =
+#ifdef JEMALLOC_CACHE_OBLIVIOUS
+ true
+#else
+ false
+#endif
+ ;
+
+zero_realloc_action_t opt_zero_realloc_action =
+#ifdef JEMALLOC_ZERO_REALLOC_DEFAULT_FREE
+ zero_realloc_action_free
+#else
+ zero_realloc_action_alloc
+#endif
+ ;
+
+atomic_zu_t zero_realloc_count = ATOMIC_INIT(0);
+
+const char *zero_realloc_mode_names[] = {
+ "alloc",
+ "free",
+ "abort",
+};
+
+/*
+ * These are the documented values for junk fill debugging facilities -- see the
+ * man page.
+ */
+static const uint8_t junk_alloc_byte = 0xa5;
+static const uint8_t junk_free_byte = 0x5a;
+
+static void default_junk_alloc(void *ptr, size_t usize) {
+ memset(ptr, junk_alloc_byte, usize);
+}
+
+static void default_junk_free(void *ptr, size_t usize) {
+ memset(ptr, junk_free_byte, usize);
+}
+
+void (*junk_alloc_callback)(void *ptr, size_t size) = &default_junk_alloc;
+void (*junk_free_callback)(void *ptr, size_t size) = &default_junk_free;
bool opt_utrace = false;
bool opt_xmalloc = false;
+bool opt_experimental_infallible_new = false;
bool opt_zero = false;
unsigned opt_narenas = 0;
+fxp_t opt_narenas_ratio = FXP_INIT_INT(4);
unsigned ncpus;
/* Protects arenas initialization. */
malloc_mutex_t arenas_lock;
+
+/* The global hpa, and whether it's on. */
+bool opt_hpa = false;
+hpa_shard_opts_t opt_hpa_opts = HPA_SHARD_OPTS_DEFAULT;
+sec_opts_t opt_hpa_sec_opts = SEC_OPTS_DEFAULT;
+
/*
* Arenas that are used to service external requests. Not all elements of the
* arenas array are necessarily used; arenas are created lazily as needed.
@@ -98,13 +184,7 @@ static arena_t *a0; /* arenas[0]. */
unsigned narenas_auto;
unsigned manual_arena_base;
-typedef enum {
- malloc_init_uninitialized = 3,
- malloc_init_a0_initialized = 2,
- malloc_init_recursible = 1,
- malloc_init_initialized = 0 /* Common case --> jnz. */
-} malloc_init_t;
-static malloc_init_t malloc_init_state = malloc_init_uninitialized;
+malloc_init_t malloc_init_state = malloc_init_uninitialized;
/* False should be the common case. Set to true to trigger initialization. */
bool malloc_slow = true;
@@ -184,7 +264,7 @@ typedef struct {
ut.p = (a); \
ut.s = (b); \
ut.r = (c); \
- utrace(&ut, sizeof(ut)); \
+ UTRACE_CALL(&ut, sizeof(ut)); \
errno = utrace_serrno; \
} \
} while (0)
@@ -209,11 +289,6 @@ static bool malloc_init_hard(void);
* Begin miscellaneous support functions.
*/
-bool
-malloc_initialized(void) {
- return (malloc_init_state == malloc_init_initialized);
-}
-
JEMALLOC_ALWAYS_INLINE bool
malloc_init_a0(void) {
if (unlikely(malloc_init_state == malloc_init_uninitialized)) {
@@ -261,7 +336,7 @@ a0dalloc(void *ptr) {
}
/*
- * FreeBSD's libc uses the bootstrap_*() functions in bootstrap-senstive
+ * FreeBSD's libc uses the bootstrap_*() functions in bootstrap-sensitive
* situations that cannot tolerate TLS variable access (TLS allocation and very
* early internal data structure initialization).
*/
@@ -319,7 +394,7 @@ narenas_total_get(void) {
/* Create a new arena and insert it into the arenas array at index ind. */
static arena_t *
-arena_init_locked(tsdn_t *tsdn, unsigned ind, extent_hooks_t *extent_hooks) {
+arena_init_locked(tsdn_t *tsdn, unsigned ind, const arena_config_t *config) {
arena_t *arena;
assert(ind <= narenas_total_get());
@@ -341,7 +416,7 @@ arena_init_locked(tsdn_t *tsdn, unsigned ind, extent_hooks_t *extent_hooks) {
}
/* Actually initialize the arena. */
- arena = arena_new(tsdn, ind, extent_hooks);
+ arena = arena_new(tsdn, ind, config);
return arena;
}
@@ -365,11 +440,11 @@ arena_new_create_background_thread(tsdn_t *tsdn, unsigned ind) {
}
arena_t *
-arena_init(tsdn_t *tsdn, unsigned ind, extent_hooks_t *extent_hooks) {
+arena_init(tsdn_t *tsdn, unsigned ind, const arena_config_t *config) {
arena_t *arena;
malloc_mutex_lock(tsdn, &arenas_lock);
- arena = arena_init_locked(tsdn, ind, extent_hooks);
+ arena = arena_init_locked(tsdn, ind, config);
malloc_mutex_unlock(tsdn, &arenas_lock);
arena_new_create_background_thread(tsdn, ind);
@@ -398,14 +473,19 @@ arena_bind(tsd_t *tsd, unsigned ind, bool internal) {
}
void
-arena_migrate(tsd_t *tsd, unsigned oldind, unsigned newind) {
- arena_t *oldarena, *newarena;
+arena_migrate(tsd_t *tsd, arena_t *oldarena, arena_t *newarena) {
+ assert(oldarena != NULL);
+ assert(newarena != NULL);
- oldarena = arena_get(tsd_tsdn(tsd), oldind, false);
- newarena = arena_get(tsd_tsdn(tsd), newind, false);
arena_nthreads_dec(oldarena, false);
arena_nthreads_inc(newarena, false);
tsd_arena_set(tsd, newarena);
+
+ if (arena_nthreads_get(oldarena, false) == 0) {
+ /* Purge if the old arena has no associated threads anymore. */
+ arena_decay(tsd_tsdn(tsd), oldarena,
+ /* is_background_thread */ false, /* all */ true);
+ }
}
static void
@@ -422,82 +502,6 @@ arena_unbind(tsd_t *tsd, unsigned ind, bool internal) {
}
}
-arena_tdata_t *
-arena_tdata_get_hard(tsd_t *tsd, unsigned ind) {
- arena_tdata_t *tdata, *arenas_tdata_old;
- arena_tdata_t *arenas_tdata = tsd_arenas_tdata_get(tsd);
- unsigned narenas_tdata_old, i;
- unsigned narenas_tdata = tsd_narenas_tdata_get(tsd);
- unsigned narenas_actual = narenas_total_get();
-
- /*
- * Dissociate old tdata array (and set up for deallocation upon return)
- * if it's too small.
- */
- if (arenas_tdata != NULL && narenas_tdata < narenas_actual) {
- arenas_tdata_old = arenas_tdata;
- narenas_tdata_old = narenas_tdata;
- arenas_tdata = NULL;
- narenas_tdata = 0;
- tsd_arenas_tdata_set(tsd, arenas_tdata);
- tsd_narenas_tdata_set(tsd, narenas_tdata);
- } else {
- arenas_tdata_old = NULL;
- narenas_tdata_old = 0;
- }
-
- /* Allocate tdata array if it's missing. */
- if (arenas_tdata == NULL) {
- bool *arenas_tdata_bypassp = tsd_arenas_tdata_bypassp_get(tsd);
- narenas_tdata = (ind < narenas_actual) ? narenas_actual : ind+1;
-
- if (tsd_nominal(tsd) && !*arenas_tdata_bypassp) {
- *arenas_tdata_bypassp = true;
- arenas_tdata = (arena_tdata_t *)a0malloc(
- sizeof(arena_tdata_t) * narenas_tdata);
- *arenas_tdata_bypassp = false;
- }
- if (arenas_tdata == NULL) {
- tdata = NULL;
- goto label_return;
- }
- assert(tsd_nominal(tsd) && !*arenas_tdata_bypassp);
- tsd_arenas_tdata_set(tsd, arenas_tdata);
- tsd_narenas_tdata_set(tsd, narenas_tdata);
- }
-
- /*
- * Copy to tdata array. It's possible that the actual number of arenas
- * has increased since narenas_total_get() was called above, but that
- * causes no correctness issues unless two threads concurrently execute
- * the arenas.create mallctl, which we trust mallctl synchronization to
- * prevent.
- */
-
- /* Copy/initialize tickers. */
- for (i = 0; i < narenas_actual; i++) {
- if (i < narenas_tdata_old) {
- ticker_copy(&arenas_tdata[i].decay_ticker,
- &arenas_tdata_old[i].decay_ticker);
- } else {
- ticker_init(&arenas_tdata[i].decay_ticker,
- DECAY_NTICKS_PER_UPDATE);
- }
- }
- if (narenas_tdata > narenas_actual) {
- memset(&arenas_tdata[narenas_actual], 0, sizeof(arena_tdata_t)
- * (narenas_tdata - narenas_actual));
- }
-
- /* Read the refreshed tdata array. */
- tdata = &arenas_tdata[ind];
-label_return:
- if (arenas_tdata_old != NULL) {
- a0dalloc(arenas_tdata_old);
- }
- return tdata;
-}
-
/* Slow path, called only by arena_choose(). */
arena_t *
arena_choose_hard(tsd_t *tsd, bool internal) {
@@ -580,8 +584,7 @@ arena_choose_hard(tsd_t *tsd, bool internal) {
/* Initialize a new arena. */
choose[j] = first_null;
arena = arena_init_locked(tsd_tsdn(tsd),
- choose[j],
- (extent_hooks_t *)&extent_hooks_default);
+ choose[j], &arena_config_default);
if (arena == NULL) {
malloc_mutex_unlock(tsd_tsdn(tsd),
&arenas_lock);
@@ -633,20 +636,6 @@ arena_cleanup(tsd_t *tsd) {
}
}
-void
-arenas_tdata_cleanup(tsd_t *tsd) {
- arena_tdata_t *arenas_tdata;
-
- /* Prevent tsd->arenas_tdata from being (re)created. */
- *tsd_arenas_tdata_bypassp_get(tsd) = true;
-
- arenas_tdata = tsd_arenas_tdata_get(tsd);
- if (arenas_tdata != NULL) {
- tsd_arenas_tdata_set(tsd, NULL);
- a0dalloc(arenas_tdata);
- }
-}
-
static void
stats_print_atexit(void) {
if (config_stats) {
@@ -665,11 +654,13 @@ stats_print_atexit(void) {
for (i = 0, narenas = narenas_total_get(); i < narenas; i++) {
arena_t *arena = arena_get(tsdn, i, false);
if (arena != NULL) {
- tcache_t *tcache;
+ tcache_slow_t *tcache_slow;
malloc_mutex_lock(tsdn, &arena->tcache_ql_mtx);
- ql_foreach(tcache, &arena->tcache_ql, link) {
- tcache_stats_merge(tsdn, tcache, arena);
+ ql_foreach(tcache_slow, &arena->tcache_ql,
+ link) {
+ tcache_stats_merge(tsdn,
+ tcache_slow->tcache, arena);
}
malloc_mutex_unlock(tsdn,
&arena->tcache_ql_mtx);
@@ -734,18 +725,28 @@ malloc_ncpus(void) {
SYSTEM_INFO si;
GetSystemInfo(&si);
result = si.dwNumberOfProcessors;
-#elif defined(JEMALLOC_GLIBC_MALLOC_HOOK) && defined(CPU_COUNT)
+#elif defined(CPU_COUNT)
/*
* glibc >= 2.6 has the CPU_COUNT macro.
*
* glibc's sysconf() uses isspace(). glibc allocates for the first time
* *before* setting up the isspace tables. Therefore we need a
* different method to get the number of CPUs.
+ *
+ * The getaffinity approach is also preferred when only a subset of CPUs
+ * is available, to avoid using more arenas than necessary.
*/
{
+# if defined(__FreeBSD__) || defined(__DragonFly__)
+ cpuset_t set;
+# else
cpu_set_t set;
-
+# endif
+# if defined(JEMALLOC_HAVE_SCHED_SETAFFINITY)
+ sched_getaffinity(0, sizeof(set), &set);
+# else
pthread_getaffinity_np(pthread_self(), sizeof(set), &set);
+# endif
result = CPU_COUNT(&set);
}
#else
@@ -754,9 +755,47 @@ malloc_ncpus(void) {
return ((result == -1) ? 1 : (unsigned)result);
}
+/*
+ * Ensure that number of CPUs is determistinc, i.e. it is the same based on:
+ * - sched_getaffinity()
+ * - _SC_NPROCESSORS_ONLN
+ * - _SC_NPROCESSORS_CONF
+ * Since otherwise tricky things is possible with percpu arenas in use.
+ */
+static bool
+malloc_cpu_count_is_deterministic()
+{
+#ifdef _WIN32
+ return true;
+#else
+ long cpu_onln = sysconf(_SC_NPROCESSORS_ONLN);
+ long cpu_conf = sysconf(_SC_NPROCESSORS_CONF);
+ if (cpu_onln != cpu_conf) {
+ return false;
+ }
+# if defined(CPU_COUNT)
+# if defined(__FreeBSD__) || defined(__DragonFly__)
+ cpuset_t set;
+# else
+ cpu_set_t set;
+# endif /* __FreeBSD__ */
+# if defined(JEMALLOC_HAVE_SCHED_SETAFFINITY)
+ sched_getaffinity(0, sizeof(set), &set);
+# else /* !JEMALLOC_HAVE_SCHED_SETAFFINITY */
+ pthread_getaffinity_np(pthread_self(), sizeof(set), &set);
+# endif /* JEMALLOC_HAVE_SCHED_SETAFFINITY */
+ long cpu_affinity = CPU_COUNT(&set);
+ if (cpu_affinity != cpu_conf) {
+ return false;
+ }
+# endif /* CPU_COUNT */
+ return true;
+#endif
+}
+
static void
-init_opt_stats_print_opts(const char *v, size_t vlen) {
- size_t opts_len = strlen(opt_stats_print_opts);
+init_opt_stats_opts(const char *v, size_t vlen, char *dest) {
+ size_t opts_len = strlen(dest);
assert(opts_len <= stats_print_tot_num_options);
for (size_t i = 0; i < vlen; i++) {
@@ -767,16 +806,16 @@ init_opt_stats_print_opts(const char *v, size_t vlen) {
default: continue;
}
- if (strchr(opt_stats_print_opts, v[i]) != NULL) {
+ if (strchr(dest, v[i]) != NULL) {
/* Ignore repeated. */
continue;
}
- opt_stats_print_opts[opts_len++] = v[i];
- opt_stats_print_opts[opts_len] = '\0';
+ dest[opts_len++] = v[i];
+ dest[opts_len] = '\0';
assert(opts_len <= stats_print_tot_num_options);
}
- assert(opts_len == strlen(opt_stats_print_opts));
+ assert(opts_len == strlen(dest));
}
/* Reads the next size pair in a multi-sized option. */
@@ -858,10 +897,12 @@ malloc_conf_next(char const **opts_p, char const **k_p, size_t *klen_p,
if (opts != *opts_p) {
malloc_write("<jemalloc>: Conf string ends "
"with key\n");
+ had_conf_error = true;
}
return true;
default:
malloc_write("<jemalloc>: Malformed conf string\n");
+ had_conf_error = true;
return true;
}
}
@@ -880,6 +921,7 @@ malloc_conf_next(char const **opts_p, char const **k_p, size_t *klen_p,
if (*opts == '\0') {
malloc_write("<jemalloc>: Conf string ends "
"with comma\n");
+ had_conf_error = true;
}
*vlen_p = (uintptr_t)opts - 1 - (uintptr_t)*v_p;
accept = true;
@@ -936,7 +978,7 @@ malloc_slow_flag_init(void) {
}
/* Number of sources for initializing malloc_conf */
-#define MALLOC_CONF_NSOURCES 4
+#define MALLOC_CONF_NSOURCES 5
static const char *
obtain_malloc_conf(unsigned which_source, char buf[PATH_MAX + 1]) {
@@ -1014,6 +1056,9 @@ obtain_malloc_conf(unsigned which_source, char buf[PATH_MAX + 1]) {
ret = NULL;
}
break;
+ } case 4: {
+ ret = je_malloc_conf_2_conf_harder;
+ break;
} default:
not_reached();
ret = NULL;
@@ -1030,7 +1075,9 @@ malloc_conf_init_helper(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS],
"string pointed to by the global variable malloc_conf",
"\"name\" of the file referenced by the symbolic link named "
"/etc/malloc.conf",
- "value of the environment variable MALLOC_CONF"
+ "value of the environment variable MALLOC_CONF",
+ "string pointed to by the global variable "
+ "malloc_conf_2_conf_harder",
};
unsigned i;
const char *opts, *k, *v;
@@ -1098,39 +1145,50 @@ malloc_conf_init_helper(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS],
#define CONF_CHECK_MIN(um, min) ((um) < (min))
#define CONF_DONT_CHECK_MAX(um, max) false
#define CONF_CHECK_MAX(um, max) ((um) > (max))
-#define CONF_HANDLE_T_U(t, o, n, min, max, check_min, check_max, clip) \
+
+#define CONF_VALUE_READ(max_t, result) \
+ char *end; \
+ set_errno(0); \
+ result = (max_t)malloc_strtoumax(v, &end, 0);
+#define CONF_VALUE_READ_FAIL() \
+ (get_errno() != 0 || (uintptr_t)end - (uintptr_t)v != vlen)
+
+#define CONF_HANDLE_T(t, max_t, o, n, min, max, check_min, check_max, clip) \
if (CONF_MATCH(n)) { \
- uintmax_t um; \
- char *end; \
- \
- set_errno(0); \
- um = malloc_strtoumax(v, &end, 0); \
- if (get_errno() != 0 || (uintptr_t)end -\
- (uintptr_t)v != vlen) { \
+ max_t mv; \
+ CONF_VALUE_READ(max_t, mv) \
+ if (CONF_VALUE_READ_FAIL()) { \
CONF_ERROR("Invalid conf value",\
k, klen, v, vlen); \
} else if (clip) { \
- if (check_min(um, (t)(min))) { \
+ if (check_min(mv, (t)(min))) { \
o = (t)(min); \
} else if ( \
- check_max(um, (t)(max))) { \
+ check_max(mv, (t)(max))) { \
o = (t)(max); \
} else { \
- o = (t)um; \
+ o = (t)mv; \
} \
} else { \
- if (check_min(um, (t)(min)) || \
- check_max(um, (t)(max))) { \
+ if (check_min(mv, (t)(min)) || \
+ check_max(mv, (t)(max))) { \
CONF_ERROR( \
"Out-of-range " \
"conf value", \
k, klen, v, vlen); \
} else { \
- o = (t)um; \
+ o = (t)mv; \
} \
} \
CONF_CONTINUE; \
}
+#define CONF_HANDLE_T_U(t, o, n, min, max, check_min, check_max, clip) \
+ CONF_HANDLE_T(t, uintmax_t, o, n, min, max, check_min, \
+ check_max, clip)
+#define CONF_HANDLE_T_SIGNED(t, o, n, min, max, check_min, check_max, clip)\
+ CONF_HANDLE_T(t, intmax_t, o, n, min, max, check_min, \
+ check_max, clip)
+
#define CONF_HANDLE_UNSIGNED(o, n, min, max, check_min, check_max, \
clip) \
CONF_HANDLE_T_U(unsigned, o, n, min, max, \
@@ -1138,27 +1196,15 @@ malloc_conf_init_helper(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS],
#define CONF_HANDLE_SIZE_T(o, n, min, max, check_min, check_max, clip) \
CONF_HANDLE_T_U(size_t, o, n, min, max, \
check_min, check_max, clip)
+#define CONF_HANDLE_INT64_T(o, n, min, max, check_min, check_max, clip) \
+ CONF_HANDLE_T_SIGNED(int64_t, o, n, min, max, \
+ check_min, check_max, clip)
+#define CONF_HANDLE_UINT64_T(o, n, min, max, check_min, check_max, clip)\
+ CONF_HANDLE_T_U(uint64_t, o, n, min, max, \
+ check_min, check_max, clip)
#define CONF_HANDLE_SSIZE_T(o, n, min, max) \
- if (CONF_MATCH(n)) { \
- long l; \
- char *end; \
- \
- set_errno(0); \
- l = strtol(v, &end, 0); \
- if (get_errno() != 0 || (uintptr_t)end -\
- (uintptr_t)v != vlen) { \
- CONF_ERROR("Invalid conf value",\
- k, klen, v, vlen); \
- } else if (l < (ssize_t)(min) || l > \
- (ssize_t)(max)) { \
- CONF_ERROR( \
- "Out-of-range conf value", \
- k, klen, v, vlen); \
- } else { \
- o = l; \
- } \
- CONF_CONTINUE; \
- }
+ CONF_HANDLE_T_SIGNED(ssize_t, o, n, min, max, \
+ CONF_CHECK_MIN, CONF_CHECK_MAX, false)
#define CONF_HANDLE_CHAR_P(o, n, d) \
if (CONF_MATCH(n)) { \
size_t cpylen = (vlen <= \
@@ -1178,13 +1224,14 @@ malloc_conf_init_helper(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS],
CONF_HANDLE_BOOL(opt_abort, "abort")
CONF_HANDLE_BOOL(opt_abort_conf, "abort_conf")
+ CONF_HANDLE_BOOL(opt_trust_madvise, "trust_madvise")
if (strncmp("metadata_thp", k, klen) == 0) {
- int i;
+ int m;
bool match = false;
- for (i = 0; i < metadata_thp_mode_limit; i++) {
- if (strncmp(metadata_thp_mode_names[i],
+ for (m = 0; m < metadata_thp_mode_limit; m++) {
+ if (strncmp(metadata_thp_mode_names[m],
v, vlen) == 0) {
- opt_metadata_thp = i;
+ opt_metadata_thp = m;
match = true;
break;
}
@@ -1197,18 +1244,18 @@ malloc_conf_init_helper(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS],
}
CONF_HANDLE_BOOL(opt_retain, "retain")
if (strncmp("dss", k, klen) == 0) {
- int i;
+ int m;
bool match = false;
- for (i = 0; i < dss_prec_limit; i++) {
- if (strncmp(dss_prec_names[i], v, vlen)
+ for (m = 0; m < dss_prec_limit; m++) {
+ if (strncmp(dss_prec_names[m], v, vlen)
== 0) {
- if (extent_dss_prec_set(i)) {
+ if (extent_dss_prec_set(m)) {
CONF_ERROR(
"Error setting dss",
k, klen, v, vlen);
} else {
opt_dss =
- dss_prec_names[i];
+ dss_prec_names[m];
match = true;
break;
}
@@ -1220,9 +1267,27 @@ malloc_conf_init_helper(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS],
}
CONF_CONTINUE;
}
- CONF_HANDLE_UNSIGNED(opt_narenas, "narenas", 1,
- UINT_MAX, CONF_CHECK_MIN, CONF_DONT_CHECK_MAX,
- false)
+ if (CONF_MATCH("narenas")) {
+ if (CONF_MATCH_VALUE("default")) {
+ opt_narenas = 0;
+ CONF_CONTINUE;
+ } else {
+ CONF_HANDLE_UNSIGNED(opt_narenas,
+ "narenas", 1, UINT_MAX,
+ CONF_CHECK_MIN, CONF_DONT_CHECK_MAX,
+ /* clip */ false)
+ }
+ }
+ if (CONF_MATCH("narenas_ratio")) {
+ char *end;
+ bool err = fxp_parse(&opt_narenas_ratio, v,
+ &end);
+ if (err || (size_t)(end - v) != vlen) {
+ CONF_ERROR("Invalid conf value",
+ k, klen, v, vlen);
+ }
+ CONF_CONTINUE;
+ }
if (CONF_MATCH("bin_shards")) {
const char *bin_shards_segment_cur = v;
size_t vlen_left = vlen;
@@ -1245,6 +1310,9 @@ malloc_conf_init_helper(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS],
} while (vlen_left > 0);
CONF_CONTINUE;
}
+ CONF_HANDLE_INT64_T(opt_mutex_max_spin,
+ "mutex_max_spin", -1, INT64_MAX, CONF_CHECK_MIN,
+ CONF_DONT_CHECK_MAX, false);
CONF_HANDLE_SSIZE_T(opt_dirty_decay_ms,
"dirty_decay_ms", -1, NSTIME_SEC_MAX * KQU(1000) <
QU(SSIZE_MAX) ? NSTIME_SEC_MAX * KQU(1000) :
@@ -1255,7 +1323,16 @@ malloc_conf_init_helper(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS],
SSIZE_MAX);
CONF_HANDLE_BOOL(opt_stats_print, "stats_print")
if (CONF_MATCH("stats_print_opts")) {
- init_opt_stats_print_opts(v, vlen);
+ init_opt_stats_opts(v, vlen,
+ opt_stats_print_opts);
+ CONF_CONTINUE;
+ }
+ CONF_HANDLE_INT64_T(opt_stats_interval,
+ "stats_interval", -1, INT64_MAX,
+ CONF_CHECK_MIN, CONF_DONT_CHECK_MAX, false)
+ if (CONF_MATCH("stats_interval_opts")) {
+ init_opt_stats_opts(v, vlen,
+ opt_stats_interval_opts);
CONF_CONTINUE;
}
if (config_fill) {
@@ -1291,9 +1368,61 @@ malloc_conf_init_helper(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS],
if (config_xmalloc) {
CONF_HANDLE_BOOL(opt_xmalloc, "xmalloc")
}
+ if (config_enable_cxx) {
+ CONF_HANDLE_BOOL(
+ opt_experimental_infallible_new,
+ "experimental_infallible_new")
+ }
+
CONF_HANDLE_BOOL(opt_tcache, "tcache")
- CONF_HANDLE_SSIZE_T(opt_lg_tcache_max, "lg_tcache_max",
- -1, (sizeof(size_t) << 3) - 1)
+ CONF_HANDLE_SIZE_T(opt_tcache_max, "tcache_max",
+ 0, TCACHE_MAXCLASS_LIMIT, CONF_DONT_CHECK_MIN,
+ CONF_CHECK_MAX, /* clip */ true)
+ if (CONF_MATCH("lg_tcache_max")) {
+ size_t m;
+ CONF_VALUE_READ(size_t, m)
+ if (CONF_VALUE_READ_FAIL()) {
+ CONF_ERROR("Invalid conf value",
+ k, klen, v, vlen);
+ } else {
+ /* clip if necessary */
+ if (m > TCACHE_LG_MAXCLASS_LIMIT) {
+ m = TCACHE_LG_MAXCLASS_LIMIT;
+ }
+ opt_tcache_max = (size_t)1 << m;
+ }
+ CONF_CONTINUE;
+ }
+ /*
+ * Anyone trying to set a value outside -16 to 16 is
+ * deeply confused.
+ */
+ CONF_HANDLE_SSIZE_T(opt_lg_tcache_nslots_mul,
+ "lg_tcache_nslots_mul", -16, 16)
+ /* Ditto with values past 2048. */
+ CONF_HANDLE_UNSIGNED(opt_tcache_nslots_small_min,
+ "tcache_nslots_small_min", 1, 2048,
+ CONF_CHECK_MIN, CONF_CHECK_MAX, /* clip */ true)
+ CONF_HANDLE_UNSIGNED(opt_tcache_nslots_small_max,
+ "tcache_nslots_small_max", 1, 2048,
+ CONF_CHECK_MIN, CONF_CHECK_MAX, /* clip */ true)
+ CONF_HANDLE_UNSIGNED(opt_tcache_nslots_large,
+ "tcache_nslots_large", 1, 2048,
+ CONF_CHECK_MIN, CONF_CHECK_MAX, /* clip */ true)
+ CONF_HANDLE_SIZE_T(opt_tcache_gc_incr_bytes,
+ "tcache_gc_incr_bytes", 1024, SIZE_T_MAX,
+ CONF_CHECK_MIN, CONF_DONT_CHECK_MAX,
+ /* clip */ true)
+ CONF_HANDLE_SIZE_T(opt_tcache_gc_delay_bytes,
+ "tcache_gc_delay_bytes", 0, SIZE_T_MAX,
+ CONF_DONT_CHECK_MIN, CONF_DONT_CHECK_MAX,
+ /* clip */ false)
+ CONF_HANDLE_UNSIGNED(opt_lg_tcache_flush_small_div,
+ "lg_tcache_flush_small_div", 1, 16,
+ CONF_CHECK_MIN, CONF_CHECK_MAX, /* clip */ true)
+ CONF_HANDLE_UNSIGNED(opt_lg_tcache_flush_large_div,
+ "lg_tcache_flush_large_div", 1, 16,
+ CONF_CHECK_MIN, CONF_CHECK_MAX, /* clip */ true)
/*
* The runtime option of oversize_threshold remains
@@ -1313,16 +1442,16 @@ malloc_conf_init_helper(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS],
if (strncmp("percpu_arena", k, klen) == 0) {
bool match = false;
- for (int i = percpu_arena_mode_names_base; i <
- percpu_arena_mode_names_limit; i++) {
- if (strncmp(percpu_arena_mode_names[i],
+ for (int m = percpu_arena_mode_names_base; m <
+ percpu_arena_mode_names_limit; m++) {
+ if (strncmp(percpu_arena_mode_names[m],
v, vlen) == 0) {
if (!have_percpu_arena) {
CONF_ERROR(
"No getcpu support",
k, klen, v, vlen);
}
- opt_percpu_arena = i;
+ opt_percpu_arena = m;
match = true;
break;
}
@@ -1340,7 +1469,83 @@ malloc_conf_init_helper(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS],
opt_max_background_threads,
CONF_CHECK_MIN, CONF_CHECK_MAX,
true);
+ CONF_HANDLE_BOOL(opt_hpa, "hpa")
+ CONF_HANDLE_SIZE_T(opt_hpa_opts.slab_max_alloc,
+ "hpa_slab_max_alloc", PAGE, HUGEPAGE,
+ CONF_CHECK_MIN, CONF_CHECK_MAX, true);
+
+ /*
+ * Accept either a ratio-based or an exact hugification
+ * threshold.
+ */
+ CONF_HANDLE_SIZE_T(opt_hpa_opts.hugification_threshold,
+ "hpa_hugification_threshold", PAGE, HUGEPAGE,
+ CONF_CHECK_MIN, CONF_CHECK_MAX, true);
+ if (CONF_MATCH("hpa_hugification_threshold_ratio")) {
+ fxp_t ratio;
+ char *end;
+ bool err = fxp_parse(&ratio, v,
+ &end);
+ if (err || (size_t)(end - v) != vlen
+ || ratio > FXP_INIT_INT(1)) {
+ CONF_ERROR("Invalid conf value",
+ k, klen, v, vlen);
+ } else {
+ opt_hpa_opts.hugification_threshold =
+ fxp_mul_frac(HUGEPAGE, ratio);
+ }
+ CONF_CONTINUE;
+ }
+
+ CONF_HANDLE_UINT64_T(
+ opt_hpa_opts.hugify_delay_ms, "hpa_hugify_delay_ms",
+ 0, 0, CONF_DONT_CHECK_MIN, CONF_DONT_CHECK_MAX,
+ false);
+
+ CONF_HANDLE_UINT64_T(
+ opt_hpa_opts.min_purge_interval_ms,
+ "hpa_min_purge_interval_ms", 0, 0,
+ CONF_DONT_CHECK_MIN, CONF_DONT_CHECK_MAX, false);
+
+ if (CONF_MATCH("hpa_dirty_mult")) {
+ if (CONF_MATCH_VALUE("-1")) {
+ opt_hpa_opts.dirty_mult = (fxp_t)-1;
+ CONF_CONTINUE;
+ }
+ fxp_t ratio;
+ char *end;
+ bool err = fxp_parse(&ratio, v,
+ &end);
+ if (err || (size_t)(end - v) != vlen) {
+ CONF_ERROR("Invalid conf value",
+ k, klen, v, vlen);
+ } else {
+ opt_hpa_opts.dirty_mult = ratio;
+ }
+ CONF_CONTINUE;
+ }
+
+ CONF_HANDLE_SIZE_T(opt_hpa_sec_opts.nshards,
+ "hpa_sec_nshards", 0, 0, CONF_CHECK_MIN,
+ CONF_DONT_CHECK_MAX, true);
+ CONF_HANDLE_SIZE_T(opt_hpa_sec_opts.max_alloc,
+ "hpa_sec_max_alloc", PAGE, 0, CONF_CHECK_MIN,
+ CONF_DONT_CHECK_MAX, true);
+ CONF_HANDLE_SIZE_T(opt_hpa_sec_opts.max_bytes,
+ "hpa_sec_max_bytes", PAGE, 0, CONF_CHECK_MIN,
+ CONF_DONT_CHECK_MAX, true);
+ CONF_HANDLE_SIZE_T(opt_hpa_sec_opts.bytes_after_flush,
+ "hpa_sec_bytes_after_flush", PAGE, 0,
+ CONF_CHECK_MIN, CONF_DONT_CHECK_MAX, true);
+ CONF_HANDLE_SIZE_T(opt_hpa_sec_opts.batch_fill_extra,
+ "hpa_sec_batch_fill_extra", 0, HUGEPAGE_PAGES,
+ CONF_CHECK_MIN, CONF_CHECK_MAX, true);
+
if (CONF_MATCH("slab_sizes")) {
+ if (CONF_MATCH_VALUE("default")) {
+ sc_data_init(sc_data);
+ CONF_CONTINUE;
+ }
bool err;
const char *slab_size_segment_cur = v;
size_t vlen_left = vlen;
@@ -1382,7 +1587,44 @@ malloc_conf_init_helper(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS],
CONF_HANDLE_BOOL(opt_prof_gdump, "prof_gdump")
CONF_HANDLE_BOOL(opt_prof_final, "prof_final")
CONF_HANDLE_BOOL(opt_prof_leak, "prof_leak")
+ CONF_HANDLE_BOOL(opt_prof_leak_error,
+ "prof_leak_error")
CONF_HANDLE_BOOL(opt_prof_log, "prof_log")
+ CONF_HANDLE_SSIZE_T(opt_prof_recent_alloc_max,
+ "prof_recent_alloc_max", -1, SSIZE_MAX)
+ CONF_HANDLE_BOOL(opt_prof_stats, "prof_stats")
+ CONF_HANDLE_BOOL(opt_prof_sys_thread_name,
+ "prof_sys_thread_name")
+ if (CONF_MATCH("prof_time_resolution")) {
+ if (CONF_MATCH_VALUE("default")) {
+ opt_prof_time_res =
+ prof_time_res_default;
+ } else if (CONF_MATCH_VALUE("high")) {
+ if (!config_high_res_timer) {
+ CONF_ERROR(
+ "No high resolution"
+ " timer support",
+ k, klen, v, vlen);
+ } else {
+ opt_prof_time_res =
+ prof_time_res_high;
+ }
+ } else {
+ CONF_ERROR("Invalid conf value",
+ k, klen, v, vlen);
+ }
+ CONF_CONTINUE;
+ }
+ /*
+ * Undocumented. When set to false, don't
+ * correct for an unbiasing bug in jeprof
+ * attribution. This can be handy if you want
+ * to get consistent numbers from your binary
+ * across different jemalloc versions, even if
+ * those numbers are incorrect. The default is
+ * true.
+ */
+ CONF_HANDLE_BOOL(opt_prof_unbias, "prof_unbias")
}
if (config_log) {
if (CONF_MATCH("log")) {
@@ -1396,15 +1638,15 @@ malloc_conf_init_helper(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS],
}
if (CONF_MATCH("thp")) {
bool match = false;
- for (int i = 0; i < thp_mode_names_limit; i++) {
- if (strncmp(thp_mode_names[i],v, vlen)
+ for (int m = 0; m < thp_mode_names_limit; m++) {
+ if (strncmp(thp_mode_names[m],v, vlen)
== 0) {
- if (!have_madvise_huge) {
+ if (!have_madvise_huge && !have_memcntl) {
CONF_ERROR(
"No THP support",
k, klen, v, vlen);
}
- opt_thp = i;
+ opt_thp = m;
match = true;
break;
}
@@ -1415,6 +1657,55 @@ malloc_conf_init_helper(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS],
}
CONF_CONTINUE;
}
+ if (CONF_MATCH("zero_realloc")) {
+ if (CONF_MATCH_VALUE("alloc")) {
+ opt_zero_realloc_action
+ = zero_realloc_action_alloc;
+ } else if (CONF_MATCH_VALUE("free")) {
+ opt_zero_realloc_action
+ = zero_realloc_action_free;
+ } else if (CONF_MATCH_VALUE("abort")) {
+ opt_zero_realloc_action
+ = zero_realloc_action_abort;
+ } else {
+ CONF_ERROR("Invalid conf value",
+ k, klen, v, vlen);
+ }
+ CONF_CONTINUE;
+ }
+ if (config_uaf_detection &&
+ CONF_MATCH("lg_san_uaf_align")) {
+ ssize_t a;
+ CONF_VALUE_READ(ssize_t, a)
+ if (CONF_VALUE_READ_FAIL() || a < -1) {
+ CONF_ERROR("Invalid conf value",
+ k, klen, v, vlen);
+ }
+ if (a == -1) {
+ opt_lg_san_uaf_align = -1;
+ CONF_CONTINUE;
+ }
+
+ /* clip if necessary */
+ ssize_t max_allowed = (sizeof(size_t) << 3) - 1;
+ ssize_t min_allowed = LG_PAGE;
+ if (a > max_allowed) {
+ a = max_allowed;
+ } else if (a < min_allowed) {
+ a = min_allowed;
+ }
+
+ opt_lg_san_uaf_align = a;
+ CONF_CONTINUE;
+ }
+
+ CONF_HANDLE_SIZE_T(opt_san_guard_small,
+ "san_guard_small", 0, SIZE_T_MAX,
+ CONF_DONT_CHECK_MIN, CONF_DONT_CHECK_MAX, false)
+ CONF_HANDLE_SIZE_T(opt_san_guard_large,
+ "san_guard_large", 0, SIZE_T_MAX,
+ CONF_DONT_CHECK_MIN, CONF_DONT_CHECK_MAX, false)
+
CONF_ERROR("Invalid conf pair", k, klen, v, vlen);
#undef CONF_ERROR
#undef CONF_CONTINUE
@@ -1425,7 +1716,9 @@ malloc_conf_init_helper(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS],
#undef CONF_CHECK_MIN
#undef CONF_DONT_CHECK_MAX
#undef CONF_CHECK_MAX
+#undef CONF_HANDLE_T
#undef CONF_HANDLE_T_U
+#undef CONF_HANDLE_T_SIGNED
#undef CONF_HANDLE_UNSIGNED
#undef CONF_HANDLE_SIZE_T
#undef CONF_HANDLE_SSIZE_T
@@ -1440,15 +1733,33 @@ malloc_conf_init_helper(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS],
atomic_store_b(&log_init_done, true, ATOMIC_RELEASE);
}
+static bool
+malloc_conf_init_check_deps(void) {
+ if (opt_prof_leak_error && !opt_prof_final) {
+ malloc_printf("<jemalloc>: prof_leak_error is set w/o "
+ "prof_final.\n");
+ return true;
+ }
+
+ return false;
+}
+
static void
malloc_conf_init(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS]) {
- const char *opts_cache[MALLOC_CONF_NSOURCES] = {NULL, NULL, NULL, NULL};
+ const char *opts_cache[MALLOC_CONF_NSOURCES] = {NULL, NULL, NULL, NULL,
+ NULL};
char buf[PATH_MAX + 1];
/* The first call only set the confirm_conf option and opts_cache */
malloc_conf_init_helper(NULL, NULL, true, opts_cache, buf);
malloc_conf_init_helper(sc_data, bin_shard_sizes, false, opts_cache,
NULL);
+ if (malloc_conf_init_check_deps()) {
+ /* check_deps does warning msg only; abort below if needed. */
+ if (opt_abort_conf) {
+ malloc_abort_invalid_conf();
+ }
+ }
}
#undef MALLOC_CONF_NSOURCES
@@ -1492,8 +1803,8 @@ malloc_init_hard_a0_locked() {
* Ordering here is somewhat tricky; we need sc_boot() first, since that
* determines what the size classes will be, and then
* malloc_conf_init(), since any slab size tweaking will need to be done
- * before sz_boot and bin_boot, which assume that the values they read
- * out of sc_data_global are final.
+ * before sz_boot and bin_info_boot, which assume that the values they
+ * read out of sc_data_global are final.
*/
sc_boot(&sc_data);
unsigned bin_shard_sizes[SC_NBINS];
@@ -1507,8 +1818,9 @@ malloc_init_hard_a0_locked() {
prof_boot0();
}
malloc_conf_init(&sc_data, bin_shard_sizes);
- sz_boot(&sc_data);
- bin_boot(&sc_data, bin_shard_sizes);
+ san_init(opt_lg_san_uaf_align);
+ sz_boot(&sc_data, opt_cache_oblivious);
+ bin_info_boot(&sc_data, bin_shard_sizes);
if (opt_stats_print) {
/* Print statistics at exit. */
@@ -1519,12 +1831,20 @@ malloc_init_hard_a0_locked() {
}
}
}
+
+ if (stats_boot()) {
+ return true;
+ }
if (pages_boot()) {
return true;
}
if (base_boot(TSDN_NULL)) {
return true;
}
+ /* emap_global is static, hence zeroed. */
+ if (emap_init(&arena_emap_global, b0get(), /* zeroed */ true)) {
+ return true;
+ }
if (extent_boot()) {
return true;
}
@@ -1534,8 +1854,20 @@ malloc_init_hard_a0_locked() {
if (config_prof) {
prof_boot1();
}
- arena_boot(&sc_data);
- if (tcache_boot(TSDN_NULL)) {
+ if (opt_hpa && !hpa_supported()) {
+ malloc_printf("<jemalloc>: HPA not supported in the current "
+ "configuration; %s.",
+ opt_abort_conf ? "aborting" : "disabling");
+ if (opt_abort_conf) {
+ malloc_abort_invalid_conf();
+ } else {
+ opt_hpa = false;
+ }
+ }
+ if (arena_boot(&sc_data, b0get(), opt_hpa)) {
+ return true;
+ }
+ if (tcache_boot(TSDN_NULL, b0get())) {
return true;
}
if (malloc_mutex_init(&arenas_lock, "arenas", WITNESS_RANK_ARENAS,
@@ -1554,11 +1886,29 @@ malloc_init_hard_a0_locked() {
* Initialize one arena here. The rest are lazily created in
* arena_choose_hard().
*/
- if (arena_init(TSDN_NULL, 0, (extent_hooks_t *)&extent_hooks_default)
- == NULL) {
+ if (arena_init(TSDN_NULL, 0, &arena_config_default) == NULL) {
return true;
}
a0 = arena_get(TSDN_NULL, 0, false);
+
+ if (opt_hpa && !hpa_supported()) {
+ malloc_printf("<jemalloc>: HPA not supported in the current "
+ "configuration; %s.",
+ opt_abort_conf ? "aborting" : "disabling");
+ if (opt_abort_conf) {
+ malloc_abort_invalid_conf();
+ } else {
+ opt_hpa = false;
+ }
+ } else if (opt_hpa) {
+ hpa_shard_opts_t hpa_shard_opts = opt_hpa_opts;
+ hpa_shard_opts.deferral_allowed = background_thread_enabled();
+ if (pa_shard_enable_hpa(TSDN_NULL, &a0->pa_shard,
+ &hpa_shard_opts, &opt_hpa_sec_opts)) {
+ return true;
+ }
+ }
+
malloc_init_state = malloc_init_a0_initialized;
return false;
@@ -1580,6 +1930,29 @@ malloc_init_hard_recursible(void) {
malloc_init_state = malloc_init_recursible;
ncpus = malloc_ncpus();
+ if (opt_percpu_arena != percpu_arena_disabled) {
+ bool cpu_count_is_deterministic =
+ malloc_cpu_count_is_deterministic();
+ if (!cpu_count_is_deterministic) {
+ /*
+ * If # of CPU is not deterministic, and narenas not
+ * specified, disables per cpu arena since it may not
+ * detect CPU IDs properly.
+ */
+ if (opt_narenas == 0) {
+ opt_percpu_arena = percpu_arena_disabled;
+ malloc_write("<jemalloc>: Number of CPUs "
+ "detected is not deterministic. Per-CPU "
+ "arena disabled.\n");
+ if (opt_abort_conf) {
+ malloc_abort_invalid_conf();
+ }
+ if (opt_abort) {
+ abort();
+ }
+ }
+ }
+ }
#if (defined(JEMALLOC_HAVE_PTHREAD_ATFORK) && !defined(JEMALLOC_MUTEX_INIT_CB) \
&& !defined(JEMALLOC_ZONE) && !defined(_WIN32) && \
@@ -1610,7 +1983,13 @@ malloc_narenas_default(void) {
* default.
*/
if (ncpus > 1) {
- return ncpus << 2;
+ fxp_t fxp_ncpus = FXP_INIT_INT(ncpus);
+ fxp_t goal = fxp_mul(fxp_ncpus, opt_narenas_ratio);
+ uint32_t int_goal = fxp_round_nearest(goal);
+ if (int_goal == 0) {
+ return 1;
+ }
+ return int_goal;
} else {
return 1;
}
@@ -1769,10 +2148,11 @@ malloc_init_hard(void) {
/* Set reentrancy level to 1 during init. */
pre_reentrancy(tsd, NULL);
/* Initialize narenas before prof_boot2 (for allocation). */
- if (malloc_init_narenas() || background_thread_boot1(tsd_tsdn(tsd))) {
+ if (malloc_init_narenas()
+ || background_thread_boot1(tsd_tsdn(tsd), b0get())) {
UNLOCK_RETURN(tsd_tsdn(tsd), true, true)
}
- if (config_prof && prof_boot2(tsd)) {
+ if (config_prof && prof_boot2(tsd, b0get())) {
UNLOCK_RETURN(tsd_tsdn(tsd), true, true)
}
@@ -1911,38 +2291,107 @@ dynamic_opts_init(dynamic_opts_t *dynamic_opts) {
dynamic_opts->arena_ind = ARENA_IND_AUTOMATIC;
}
-/* ind is ignored if dopts->alignment > 0. */
-JEMALLOC_ALWAYS_INLINE void *
-imalloc_no_sample(static_opts_t *sopts, dynamic_opts_t *dopts, tsd_t *tsd,
- size_t size, size_t usize, szind_t ind) {
- tcache_t *tcache;
- arena_t *arena;
+/*
+ * ind parameter is optional and is only checked and filled if alignment == 0;
+ * return true if result is out of range.
+ */
+JEMALLOC_ALWAYS_INLINE bool
+aligned_usize_get(size_t size, size_t alignment, size_t *usize, szind_t *ind,
+ bool bump_empty_aligned_alloc) {
+ assert(usize != NULL);
+ if (alignment == 0) {
+ if (ind != NULL) {
+ *ind = sz_size2index(size);
+ if (unlikely(*ind >= SC_NSIZES)) {
+ return true;
+ }
+ *usize = sz_index2size(*ind);
+ assert(*usize > 0 && *usize <= SC_LARGE_MAXCLASS);
+ return false;
+ }
+ *usize = sz_s2u(size);
+ } else {
+ if (bump_empty_aligned_alloc && unlikely(size == 0)) {
+ size = 1;
+ }
+ *usize = sz_sa2u(size, alignment);
+ }
+ if (unlikely(*usize == 0 || *usize > SC_LARGE_MAXCLASS)) {
+ return true;
+ }
+ return false;
+}
- /* Fill in the tcache. */
- if (dopts->tcache_ind == TCACHE_IND_AUTOMATIC) {
- if (likely(!sopts->slow)) {
+JEMALLOC_ALWAYS_INLINE bool
+zero_get(bool guarantee, bool slow) {
+ if (config_fill && slow && unlikely(opt_zero)) {
+ return true;
+ } else {
+ return guarantee;
+ }
+}
+
+JEMALLOC_ALWAYS_INLINE tcache_t *
+tcache_get_from_ind(tsd_t *tsd, unsigned tcache_ind, bool slow, bool is_alloc) {
+ tcache_t *tcache;
+ if (tcache_ind == TCACHE_IND_AUTOMATIC) {
+ if (likely(!slow)) {
/* Getting tcache ptr unconditionally. */
tcache = tsd_tcachep_get(tsd);
assert(tcache == tcache_get(tsd));
- } else {
+ } else if (is_alloc ||
+ likely(tsd_reentrancy_level_get(tsd) == 0)) {
tcache = tcache_get(tsd);
+ } else {
+ tcache = NULL;
}
- } else if (dopts->tcache_ind == TCACHE_IND_NONE) {
- tcache = NULL;
} else {
- tcache = tcaches_get(tsd, dopts->tcache_ind);
+ /*
+ * Should not specify tcache on deallocation path when being
+ * reentrant.
+ */
+ assert(is_alloc || tsd_reentrancy_level_get(tsd) == 0 ||
+ tsd_state_nocleanup(tsd));
+ if (tcache_ind == TCACHE_IND_NONE) {
+ tcache = NULL;
+ } else {
+ tcache = tcaches_get(tsd, tcache_ind);
+ }
}
+ return tcache;
+}
- /* Fill in the arena. */
- if (dopts->arena_ind == ARENA_IND_AUTOMATIC) {
+/* Return true if a manual arena is specified and arena_get() OOMs. */
+JEMALLOC_ALWAYS_INLINE bool
+arena_get_from_ind(tsd_t *tsd, unsigned arena_ind, arena_t **arena_p) {
+ if (arena_ind == ARENA_IND_AUTOMATIC) {
/*
* In case of automatic arena management, we defer arena
* computation until as late as we can, hoping to fill the
* allocation out of the tcache.
*/
- arena = NULL;
+ *arena_p = NULL;
} else {
- arena = arena_get(tsd_tsdn(tsd), dopts->arena_ind, true);
+ *arena_p = arena_get(tsd_tsdn(tsd), arena_ind, true);
+ if (unlikely(*arena_p == NULL) && arena_ind >= narenas_auto) {
+ return true;
+ }
+ }
+ return false;
+}
+
+/* ind is ignored if dopts->alignment > 0. */
+JEMALLOC_ALWAYS_INLINE void *
+imalloc_no_sample(static_opts_t *sopts, dynamic_opts_t *dopts, tsd_t *tsd,
+ size_t size, size_t usize, szind_t ind) {
+ /* Fill in the tcache. */
+ tcache_t *tcache = tcache_get_from_ind(tsd, dopts->tcache_ind,
+ sopts->slow, /* is_alloc */ true);
+
+ /* Fill in the arena. */
+ arena_t *arena;
+ if (arena_get_from_ind(tsd, dopts->arena_ind, &arena)) {
+ return NULL;
}
if (unlikely(dopts->alignment != 0)) {
@@ -1966,6 +2415,7 @@ imalloc_sample(static_opts_t *sopts, dynamic_opts_t *dopts, tsd_t *tsd,
szind_t ind_large;
size_t bumped_usize = usize;
+ dopts->alignment = prof_sample_align(dopts->alignment);
if (usize <= SC_SMALL_MAXCLASS) {
assert(((dopts->alignment == 0) ?
sz_s2u(SC_LARGE_MINCLASS) :
@@ -1982,6 +2432,7 @@ imalloc_sample(static_opts_t *sopts, dynamic_opts_t *dopts, tsd_t *tsd,
} else {
ret = imalloc_no_sample(sopts, dopts, tsd, usize, usize, ind);
}
+ assert(prof_sample_aligned(ret));
return ret;
}
@@ -2035,16 +2486,14 @@ imalloc_body(static_opts_t *sopts, dynamic_opts_t *dopts, tsd_t *tsd) {
/* Filled in by compute_size_with_overflow below. */
size_t size = 0;
/*
- * For unaligned allocations, we need only ind. For aligned
- * allocations, or in case of stats or profiling we need usize.
- *
- * These are actually dead stores, in that their values are reset before
- * any branch on their value is taken. Sometimes though, it's
- * convenient to pass them as arguments before this point. To avoid
- * undefined behavior then, we initialize them with dummy stores.
+ * The zero initialization for ind is actually dead store, in that its
+ * value is reset before any branch on its value is taken. Sometimes
+ * though, it's convenient to pass it as arguments before this point.
+ * To avoid undefined behavior then, we initialize it with dummy stores.
*/
szind_t ind = 0;
- size_t usize = 0;
+ /* usize will always be properly initialized. */
+ size_t usize;
/* Reentrancy is only checked on slow path. */
int8_t reentrancy_level;
@@ -2061,31 +2510,12 @@ imalloc_body(static_opts_t *sopts, dynamic_opts_t *dopts, tsd_t *tsd) {
}
/* This is the beginning of the "core" algorithm. */
-
- if (dopts->alignment == 0) {
- ind = sz_size2index(size);
- if (unlikely(ind >= SC_NSIZES)) {
- goto label_oom;
- }
- if (config_stats || (config_prof && opt_prof) || sopts->usize) {
- usize = sz_index2size(ind);
- dopts->usize = usize;
- assert(usize > 0 && usize
- <= SC_LARGE_MAXCLASS);
- }
- } else {
- if (sopts->bump_empty_aligned_alloc) {
- if (unlikely(size == 0)) {
- size = 1;
- }
- }
- usize = sz_sa2u(size, dopts->alignment);
- dopts->usize = usize;
- if (unlikely(usize == 0
- || usize > SC_LARGE_MAXCLASS)) {
- goto label_oom;
- }
+ dopts->zero = zero_get(dopts->zero, sopts->slow);
+ if (aligned_usize_get(size, dopts->alignment, &usize, &ind,
+ sopts->bump_empty_aligned_alloc)) {
+ goto label_oom;
}
+ dopts->usize = usize;
/* Validate the user input. */
if (sopts->assert_nonempty_alloc) {
assert (size != 0);
@@ -2111,26 +2541,25 @@ imalloc_body(static_opts_t *sopts, dynamic_opts_t *dopts, tsd_t *tsd) {
dopts->arena_ind = 0;
}
+ /*
+ * If dopts->alignment > 0, then ind is still 0, but usize was computed
+ * in the previous if statement. Down the positive alignment path,
+ * imalloc_no_sample and imalloc_sample will ignore ind.
+ */
+
/* If profiling is on, get our profiling context. */
if (config_prof && opt_prof) {
- /*
- * Note that if we're going down this path, usize must have been
- * initialized in the previous if statement.
- */
- prof_tctx_t *tctx = prof_alloc_prep(
- tsd, usize, prof_active_get_unlocked(), true);
+ bool prof_active = prof_active_get_unlocked();
+ bool sample_event = te_prof_sample_event_lookahead(tsd, usize);
+ prof_tctx_t *tctx = prof_alloc_prep(tsd, prof_active,
+ sample_event);
- alloc_ctx_t alloc_ctx;
+ emap_alloc_ctx_t alloc_ctx;
if (likely((uintptr_t)tctx == (uintptr_t)1U)) {
- alloc_ctx.slab = (usize
- <= SC_SMALL_MAXCLASS);
+ alloc_ctx.slab = (usize <= SC_SMALL_MAXCLASS);
allocation = imalloc_no_sample(
sopts, dopts, tsd, usize, usize, ind);
} else if ((uintptr_t)tctx > (uintptr_t)1U) {
- /*
- * Note that ind might still be 0 here. This is fine;
- * imalloc_sample ignores ind if dopts->alignment > 0.
- */
allocation = imalloc_sample(
sopts, dopts, tsd, usize, ind);
alloc_ctx.slab = false;
@@ -2139,17 +2568,12 @@ imalloc_body(static_opts_t *sopts, dynamic_opts_t *dopts, tsd_t *tsd) {
}
if (unlikely(allocation == NULL)) {
- prof_alloc_rollback(tsd, tctx, true);
+ prof_alloc_rollback(tsd, tctx);
goto label_oom;
}
- prof_malloc(tsd_tsdn(tsd), allocation, usize, &alloc_ctx, tctx);
+ prof_malloc(tsd, allocation, size, usize, &alloc_ctx, tctx);
} else {
- /*
- * If dopts->alignment > 0, then ind is still 0, but usize was
- * computed in the previous if statement. Down the positive
- * alignment path, imalloc_no_sample ignores ind and size
- * (relying only on usize).
- */
+ assert(!opt_prof);
allocation = imalloc_no_sample(sopts, dopts, tsd, size, usize,
ind);
if (unlikely(allocation == NULL)) {
@@ -2161,12 +2585,17 @@ imalloc_body(static_opts_t *sopts, dynamic_opts_t *dopts, tsd_t *tsd) {
* Allocation has been done at this point. We still have some
* post-allocation work to do though.
*/
+
+ thread_alloc_event(tsd, usize);
+
assert(dopts->alignment == 0
|| ((uintptr_t)allocation & (dopts->alignment - 1)) == ZU(0));
- if (config_stats) {
- assert(usize == isalloc(tsd_tsdn(tsd), allocation));
- *tsd_thread_allocatedp_get(tsd) += usize;
+ assert(usize == isalloc(tsd_tsdn(tsd), allocation));
+
+ if (config_fill && sopts->slow && !dopts->zero
+ && unlikely(opt_junk_alloc)) {
+ junk_alloc_callback(allocation, usize);
}
if (sopts->slow) {
@@ -2277,7 +2706,11 @@ malloc_default(size_t size) {
static_opts_t sopts;
dynamic_opts_t dopts;
- LOG("core.malloc.entry", "size: %zu", size);
+ /*
+ * This variant has logging hook on exit but not on entry. It's callled
+ * only by je_malloc, below, which emits the entry one for us (and, if
+ * it calls us, does so only via tail call).
+ */
static_opts_init(&sopts);
dynamic_opts_init(&dopts);
@@ -2310,86 +2743,11 @@ malloc_default(size_t size) {
* Begin malloc(3)-compatible functions.
*/
-/*
- * malloc() fastpath.
- *
- * Fastpath assumes size <= SC_LOOKUP_MAXCLASS, and that we hit
- * tcache. If either of these is false, we tail-call to the slowpath,
- * malloc_default(). Tail-calling is used to avoid any caller-saved
- * registers.
- *
- * fastpath supports ticker and profiling, both of which will also
- * tail-call to the slowpath if they fire.
- */
JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
void JEMALLOC_NOTHROW *
JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE(1)
je_malloc(size_t size) {
- LOG("core.malloc.entry", "size: %zu", size);
-
- if (tsd_get_allocates() && unlikely(!malloc_initialized())) {
- return malloc_default(size);
- }
-
- tsd_t *tsd = tsd_get(false);
- if (unlikely(!tsd || !tsd_fast(tsd) || (size > SC_LOOKUP_MAXCLASS))) {
- return malloc_default(size);
- }
-
- tcache_t *tcache = tsd_tcachep_get(tsd);
-
- if (unlikely(ticker_trytick(&tcache->gc_ticker))) {
- return malloc_default(size);
- }
-
- szind_t ind = sz_size2index_lookup(size);
- size_t usize;
- if (config_stats || config_prof) {
- usize = sz_index2size(ind);
- }
- /* Fast path relies on size being a bin. I.e. SC_LOOKUP_MAXCLASS < SC_SMALL_MAXCLASS */
- assert(ind < SC_NBINS);
- assert(size <= SC_SMALL_MAXCLASS);
-
- if (config_prof) {
- int64_t bytes_until_sample = tsd_bytes_until_sample_get(tsd);
- bytes_until_sample -= usize;
- tsd_bytes_until_sample_set(tsd, bytes_until_sample);
-
- if (unlikely(bytes_until_sample < 0)) {
- /*
- * Avoid a prof_active check on the fastpath.
- * If prof_active is false, set bytes_until_sample to
- * a large value. If prof_active is set to true,
- * bytes_until_sample will be reset.
- */
- if (!prof_active) {
- tsd_bytes_until_sample_set(tsd, SSIZE_MAX);
- }
- return malloc_default(size);
- }
- }
-
- cache_bin_t *bin = tcache_small_bin_get(tcache, ind);
- bool tcache_success;
- void* ret = cache_bin_alloc_easy(bin, &tcache_success);
-
- if (tcache_success) {
- if (config_stats) {
- *tsd_thread_allocatedp_get(tsd) += usize;
- bin->tstats.nrequests++;
- }
- if (config_prof) {
- tcache->prof_accumbytes += usize;
- }
-
- LOG("core.malloc.exit", "result: %p", ret);
-
- /* Fastpath success */
- return ret;
- }
-
- return malloc_default(size);
+ return imalloc_fastpath(size, &malloc_default);
}
JEMALLOC_EXPORT int JEMALLOC_NOTHROW
@@ -2506,56 +2864,6 @@ je_calloc(size_t num, size_t size) {
return ret;
}
-static void *
-irealloc_prof_sample(tsd_t *tsd, void *old_ptr, size_t old_usize, size_t usize,
- prof_tctx_t *tctx, hook_ralloc_args_t *hook_args) {
- void *p;
-
- if (tctx == NULL) {
- return NULL;
- }
- if (usize <= SC_SMALL_MAXCLASS) {
- p = iralloc(tsd, old_ptr, old_usize,
- SC_LARGE_MINCLASS, 0, false, hook_args);
- if (p == NULL) {
- return NULL;
- }
- arena_prof_promote(tsd_tsdn(tsd), p, usize);
- } else {
- p = iralloc(tsd, old_ptr, old_usize, usize, 0, false,
- hook_args);
- }
-
- return p;
-}
-
-JEMALLOC_ALWAYS_INLINE void *
-irealloc_prof(tsd_t *tsd, void *old_ptr, size_t old_usize, size_t usize,
- alloc_ctx_t *alloc_ctx, hook_ralloc_args_t *hook_args) {
- void *p;
- bool prof_active;
- prof_tctx_t *old_tctx, *tctx;
-
- prof_active = prof_active_get_unlocked();
- old_tctx = prof_tctx_get(tsd_tsdn(tsd), old_ptr, alloc_ctx);
- tctx = prof_alloc_prep(tsd, usize, prof_active, true);
- if (unlikely((uintptr_t)tctx != (uintptr_t)1U)) {
- p = irealloc_prof_sample(tsd, old_ptr, old_usize, usize, tctx,
- hook_args);
- } else {
- p = iralloc(tsd, old_ptr, old_usize, usize, 0, false,
- hook_args);
- }
- if (unlikely(p == NULL)) {
- prof_alloc_rollback(tsd, tctx, true);
- return NULL;
- }
- prof_realloc(tsd, p, usize, tctx, prof_active, true, old_ptr, old_usize,
- old_tctx);
-
- return p;
-}
-
JEMALLOC_ALWAYS_INLINE void
ifree(tsd_t *tsd, void *ptr, tcache_t *tcache, bool slow_path) {
if (!slow_path) {
@@ -2569,30 +2877,50 @@ ifree(tsd_t *tsd, void *ptr, tcache_t *tcache, bool slow_path) {
assert(ptr != NULL);
assert(malloc_initialized() || IS_INITIALIZER);
- alloc_ctx_t alloc_ctx;
- rtree_ctx_t *rtree_ctx = tsd_rtree_ctx(tsd);
- rtree_szind_slab_read(tsd_tsdn(tsd), &extents_rtree, rtree_ctx,
- (uintptr_t)ptr, true, &alloc_ctx.szind, &alloc_ctx.slab);
+ emap_alloc_ctx_t alloc_ctx;
+ emap_alloc_ctx_lookup(tsd_tsdn(tsd), &arena_emap_global, ptr,
+ &alloc_ctx);
assert(alloc_ctx.szind != SC_NSIZES);
- size_t usize;
+ size_t usize = sz_index2size(alloc_ctx.szind);
if (config_prof && opt_prof) {
- usize = sz_index2size(alloc_ctx.szind);
prof_free(tsd, ptr, usize, &alloc_ctx);
- } else if (config_stats) {
- usize = sz_index2size(alloc_ctx.szind);
- }
- if (config_stats) {
- *tsd_thread_deallocatedp_get(tsd) += usize;
}
if (likely(!slow_path)) {
idalloctm(tsd_tsdn(tsd), ptr, tcache, &alloc_ctx, false,
false);
} else {
+ if (config_fill && slow_path && opt_junk_free) {
+ junk_free_callback(ptr, usize);
+ }
idalloctm(tsd_tsdn(tsd), ptr, tcache, &alloc_ctx, false,
true);
}
+ thread_dalloc_event(tsd, usize);
+}
+
+JEMALLOC_ALWAYS_INLINE bool
+maybe_check_alloc_ctx(tsd_t *tsd, void *ptr, emap_alloc_ctx_t *alloc_ctx) {
+ if (config_opt_size_checks) {
+ emap_alloc_ctx_t dbg_ctx;
+ emap_alloc_ctx_lookup(tsd_tsdn(tsd), &arena_emap_global, ptr,
+ &dbg_ctx);
+ if (alloc_ctx->szind != dbg_ctx.szind) {
+ safety_check_fail_sized_dealloc(
+ /* current_dealloc */ true, ptr,
+ /* true_size */ sz_size2index(dbg_ctx.szind),
+ /* input_size */ sz_size2index(alloc_ctx->szind));
+ return true;
+ }
+ if (alloc_ctx->slab != dbg_ctx.slab) {
+ safety_check_fail(
+ "Internal heap corruption detected: "
+ "mismatch in slab bit");
+ return true;
+ }
+ }
+ return false;
}
JEMALLOC_ALWAYS_INLINE void
@@ -2608,147 +2936,63 @@ isfree(tsd_t *tsd, void *ptr, size_t usize, tcache_t *tcache, bool slow_path) {
assert(ptr != NULL);
assert(malloc_initialized() || IS_INITIALIZER);
- alloc_ctx_t alloc_ctx, *ctx;
- if (!config_cache_oblivious && ((uintptr_t)ptr & PAGE_MASK) != 0) {
- /*
- * When cache_oblivious is disabled and ptr is not page aligned,
- * the allocation was not sampled -- usize can be used to
- * determine szind directly.
- */
+ emap_alloc_ctx_t alloc_ctx;
+ if (!config_prof) {
alloc_ctx.szind = sz_size2index(usize);
- alloc_ctx.slab = true;
- ctx = &alloc_ctx;
- if (config_debug) {
- alloc_ctx_t dbg_ctx;
- rtree_ctx_t *rtree_ctx = tsd_rtree_ctx(tsd);
- rtree_szind_slab_read(tsd_tsdn(tsd), &extents_rtree,
- rtree_ctx, (uintptr_t)ptr, true, &dbg_ctx.szind,
- &dbg_ctx.slab);
- assert(dbg_ctx.szind == alloc_ctx.szind);
- assert(dbg_ctx.slab == alloc_ctx.slab);
- }
- } else if (config_prof && opt_prof) {
- rtree_ctx_t *rtree_ctx = tsd_rtree_ctx(tsd);
- rtree_szind_slab_read(tsd_tsdn(tsd), &extents_rtree, rtree_ctx,
- (uintptr_t)ptr, true, &alloc_ctx.szind, &alloc_ctx.slab);
- assert(alloc_ctx.szind == sz_size2index(usize));
- ctx = &alloc_ctx;
+ alloc_ctx.slab = (alloc_ctx.szind < SC_NBINS);
} else {
- ctx = NULL;
+ if (likely(!prof_sample_aligned(ptr))) {
+ /*
+ * When the ptr is not page aligned, it was not sampled.
+ * usize can be trusted to determine szind and slab.
+ */
+ alloc_ctx.szind = sz_size2index(usize);
+ alloc_ctx.slab = (alloc_ctx.szind < SC_NBINS);
+ } else if (opt_prof) {
+ emap_alloc_ctx_lookup(tsd_tsdn(tsd), &arena_emap_global,
+ ptr, &alloc_ctx);
+
+ if (config_opt_safety_checks) {
+ /* Small alloc may have !slab (sampled). */
+ if (unlikely(alloc_ctx.szind !=
+ sz_size2index(usize))) {
+ safety_check_fail_sized_dealloc(
+ /* current_dealloc */ true, ptr,
+ /* true_size */ sz_index2size(
+ alloc_ctx.szind),
+ /* input_size */ usize);
+ }
+ }
+ } else {
+ alloc_ctx.szind = sz_size2index(usize);
+ alloc_ctx.slab = (alloc_ctx.szind < SC_NBINS);
+ }
+ }
+ bool fail = maybe_check_alloc_ctx(tsd, ptr, &alloc_ctx);
+ if (fail) {
+ /*
+ * This is a heap corruption bug. In real life we'll crash; for
+ * the unit test we just want to avoid breaking anything too
+ * badly to get a test result out. Let's leak instead of trying
+ * to free.
+ */
+ return;
}
if (config_prof && opt_prof) {
- prof_free(tsd, ptr, usize, ctx);
- }
- if (config_stats) {
- *tsd_thread_deallocatedp_get(tsd) += usize;
+ prof_free(tsd, ptr, usize, &alloc_ctx);
}
-
if (likely(!slow_path)) {
- isdalloct(tsd_tsdn(tsd), ptr, usize, tcache, ctx, false);
- } else {
- isdalloct(tsd_tsdn(tsd), ptr, usize, tcache, ctx, true);
- }
-}
-
-JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
-void JEMALLOC_NOTHROW *
-JEMALLOC_ALLOC_SIZE(2)
-je_realloc(void *ptr, size_t arg_size) {
- void *ret;
- tsdn_t *tsdn JEMALLOC_CC_SILENCE_INIT(NULL);
- size_t usize JEMALLOC_CC_SILENCE_INIT(0);
- size_t old_usize = 0;
- size_t size = arg_size;
-
- LOG("core.realloc.entry", "ptr: %p, size: %zu\n", ptr, size);
-
- if (unlikely(size == 0)) {
- size = 1;
- }
-
- if (likely(ptr != NULL)) {
- assert(malloc_initialized() || IS_INITIALIZER);
- tsd_t *tsd = tsd_fetch();
-
- check_entry_exit_locking(tsd_tsdn(tsd));
-
-
- hook_ralloc_args_t hook_args = {true, {(uintptr_t)ptr,
- (uintptr_t)arg_size, 0, 0}};
-
- alloc_ctx_t alloc_ctx;
- rtree_ctx_t *rtree_ctx = tsd_rtree_ctx(tsd);
- rtree_szind_slab_read(tsd_tsdn(tsd), &extents_rtree, rtree_ctx,
- (uintptr_t)ptr, true, &alloc_ctx.szind, &alloc_ctx.slab);
- assert(alloc_ctx.szind != SC_NSIZES);
- old_usize = sz_index2size(alloc_ctx.szind);
- assert(old_usize == isalloc(tsd_tsdn(tsd), ptr));
- if (config_prof && opt_prof) {
- usize = sz_s2u(size);
- if (unlikely(usize == 0
- || usize > SC_LARGE_MAXCLASS)) {
- ret = NULL;
- } else {
- ret = irealloc_prof(tsd, ptr, old_usize, usize,
- &alloc_ctx, &hook_args);
- }
- } else {
- if (config_stats) {
- usize = sz_s2u(size);
- }
- ret = iralloc(tsd, ptr, old_usize, size, 0, false,
- &hook_args);
- }
- tsdn = tsd_tsdn(tsd);
+ isdalloct(tsd_tsdn(tsd), ptr, usize, tcache, &alloc_ctx,
+ false);
} else {
- /* realloc(NULL, size) is equivalent to malloc(size). */
- static_opts_t sopts;
- dynamic_opts_t dopts;
-
- static_opts_init(&sopts);
- dynamic_opts_init(&dopts);
-
- sopts.null_out_result_on_error = true;
- sopts.set_errno_on_error = true;
- sopts.oom_string =
- "<jemalloc>: Error in realloc(): out of memory\n";
-
- dopts.result = &ret;
- dopts.num_items = 1;
- dopts.item_size = size;
-
- imalloc(&sopts, &dopts);
- if (sopts.slow) {
- uintptr_t args[3] = {(uintptr_t)ptr, arg_size};
- hook_invoke_alloc(hook_alloc_realloc, ret,
- (uintptr_t)ret, args);
- }
-
- return ret;
- }
-
- if (unlikely(ret == NULL)) {
- if (config_xmalloc && unlikely(opt_xmalloc)) {
- malloc_write("<jemalloc>: Error in realloc(): "
- "out of memory\n");
- abort();
+ if (config_fill && slow_path && opt_junk_free) {
+ junk_free_callback(ptr, usize);
}
- set_errno(ENOMEM);
- }
- if (config_stats && likely(ret != NULL)) {
- tsd_t *tsd;
-
- assert(usize == isalloc(tsdn, ret));
- tsd = tsdn_tsd(tsdn);
- *tsd_thread_allocatedp_get(tsd) += usize;
- *tsd_thread_deallocatedp_get(tsd) += old_usize;
+ isdalloct(tsd_tsdn(tsd), ptr, usize, tcache, &alloc_ctx,
+ true);
}
- UTRACE(ptr, size, ret);
- check_entry_exit_locking(tsdn);
-
- LOG("core.realloc.exit", "result: %p", ret);
- return ret;
+ thread_dalloc_event(tsd, usize);
}
JEMALLOC_NOINLINE
@@ -2767,79 +3011,149 @@ free_default(void *ptr) {
tsd_t *tsd = tsd_fetch_min();
check_entry_exit_locking(tsd_tsdn(tsd));
- tcache_t *tcache;
if (likely(tsd_fast(tsd))) {
- tsd_assert_fast(tsd);
- /* Unconditionally get tcache ptr on fast path. */
- tcache = tsd_tcachep_get(tsd);
- ifree(tsd, ptr, tcache, false);
+ tcache_t *tcache = tcache_get_from_ind(tsd,
+ TCACHE_IND_AUTOMATIC, /* slow */ false,
+ /* is_alloc */ false);
+ ifree(tsd, ptr, tcache, /* slow */ false);
} else {
- if (likely(tsd_reentrancy_level_get(tsd) == 0)) {
- tcache = tcache_get(tsd);
- } else {
- tcache = NULL;
- }
+ tcache_t *tcache = tcache_get_from_ind(tsd,
+ TCACHE_IND_AUTOMATIC, /* slow */ true,
+ /* is_alloc */ false);
uintptr_t args_raw[3] = {(uintptr_t)ptr};
hook_invoke_dalloc(hook_dalloc_free, ptr, args_raw);
- ifree(tsd, ptr, tcache, true);
+ ifree(tsd, ptr, tcache, /* slow */ true);
}
+
check_entry_exit_locking(tsd_tsdn(tsd));
}
}
+JEMALLOC_ALWAYS_INLINE bool
+free_fastpath_nonfast_aligned(void *ptr, bool check_prof) {
+ /*
+ * free_fastpath do not handle two uncommon cases: 1) sampled profiled
+ * objects and 2) sampled junk & stash for use-after-free detection.
+ * Both have special alignments which are used to escape the fastpath.
+ *
+ * prof_sample is page-aligned, which covers the UAF check when both
+ * are enabled (the assertion below). Avoiding redundant checks since
+ * this is on the fastpath -- at most one runtime branch from this.
+ */
+ if (config_debug && cache_bin_nonfast_aligned(ptr)) {
+ assert(prof_sample_aligned(ptr));
+ }
+
+ if (config_prof && check_prof) {
+ /* When prof is enabled, the prof_sample alignment is enough. */
+ if (prof_sample_aligned(ptr)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ if (config_uaf_detection) {
+ if (cache_bin_nonfast_aligned(ptr)) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ return false;
+}
+
+/* Returns whether or not the free attempt was successful. */
JEMALLOC_ALWAYS_INLINE
bool free_fastpath(void *ptr, size_t size, bool size_hint) {
tsd_t *tsd = tsd_get(false);
- if (unlikely(!tsd || !tsd_fast(tsd))) {
+ /* The branch gets optimized away unless tsd_get_allocates(). */
+ if (unlikely(tsd == NULL)) {
return false;
}
-
- tcache_t *tcache = tsd_tcachep_get(tsd);
-
- alloc_ctx_t alloc_ctx;
/*
- * If !config_cache_oblivious, we can check PAGE alignment to
- * detect sampled objects. Otherwise addresses are
- * randomized, and we have to look it up in the rtree anyway.
- * See also isfree().
+ * The tsd_fast() / initialized checks are folded into the branch
+ * testing (deallocated_after >= threshold) later in this function.
+ * The threshold will be set to 0 when !tsd_fast.
*/
- if (!size_hint || config_cache_oblivious) {
- rtree_ctx_t *rtree_ctx = tsd_rtree_ctx(tsd);
- bool res = rtree_szind_slab_read_fast(tsd_tsdn(tsd), &extents_rtree,
- rtree_ctx, (uintptr_t)ptr,
- &alloc_ctx.szind, &alloc_ctx.slab);
+ assert(tsd_fast(tsd) ||
+ *tsd_thread_deallocated_next_event_fastp_get_unsafe(tsd) == 0);
+
+ emap_alloc_ctx_t alloc_ctx;
+ if (!size_hint) {
+ bool err = emap_alloc_ctx_try_lookup_fast(tsd,
+ &arena_emap_global, ptr, &alloc_ctx);
/* Note: profiled objects will have alloc_ctx.slab set */
- if (!res || !alloc_ctx.slab) {
+ if (unlikely(err || !alloc_ctx.slab ||
+ free_fastpath_nonfast_aligned(ptr,
+ /* check_prof */ false))) {
return false;
}
assert(alloc_ctx.szind != SC_NSIZES);
} else {
/*
- * Check for both sizes that are too large, and for sampled objects.
- * Sampled objects are always page-aligned. The sampled object check
- * will also check for null ptr.
+ * Check for both sizes that are too large, and for sampled /
+ * special aligned objects. The alignment check will also check
+ * for null ptr.
*/
- if (size > SC_LOOKUP_MAXCLASS || (((uintptr_t)ptr & PAGE_MASK) == 0)) {
+ if (unlikely(size > SC_LOOKUP_MAXCLASS ||
+ free_fastpath_nonfast_aligned(ptr,
+ /* check_prof */ true))) {
return false;
}
alloc_ctx.szind = sz_size2index_lookup(size);
+ /* Max lookup class must be small. */
+ assert(alloc_ctx.szind < SC_NBINS);
+ /* This is a dead store, except when opt size checking is on. */
+ alloc_ctx.slab = true;
}
+ /*
+ * Currently the fastpath only handles small sizes. The branch on
+ * SC_LOOKUP_MAXCLASS makes sure of it. This lets us avoid checking
+ * tcache szind upper limit (i.e. tcache_maxclass) as well.
+ */
+ assert(alloc_ctx.slab);
+
+ uint64_t deallocated, threshold;
+ te_free_fastpath_ctx(tsd, &deallocated, &threshold);
- if (unlikely(ticker_trytick(&tcache->gc_ticker))) {
+ size_t usize = sz_index2size(alloc_ctx.szind);
+ uint64_t deallocated_after = deallocated + usize;
+ /*
+ * Check for events and tsd non-nominal (fast_threshold will be set to
+ * 0) in a single branch. Note that this handles the uninitialized case
+ * as well (TSD init will be triggered on the non-fastpath). Therefore
+ * anything depends on a functional TSD (e.g. the alloc_ctx sanity check
+ * below) needs to be after this branch.
+ */
+ if (unlikely(deallocated_after >= threshold)) {
return false;
}
+ assert(tsd_fast(tsd));
+ bool fail = maybe_check_alloc_ctx(tsd, ptr, &alloc_ctx);
+ if (fail) {
+ /* See the comment in isfree. */
+ return true;
+ }
+
+ tcache_t *tcache = tcache_get_from_ind(tsd, TCACHE_IND_AUTOMATIC,
+ /* slow */ false, /* is_alloc */ false);
+ cache_bin_t *bin = &tcache->bins[alloc_ctx.szind];
+
+ /*
+ * If junking were enabled, this is where we would do it. It's not
+ * though, since we ensured above that we're on the fast path. Assert
+ * that to double-check.
+ */
+ assert(!opt_junk_free);
- cache_bin_t *bin = tcache_small_bin_get(tcache, alloc_ctx.szind);
- cache_bin_info_t *bin_info = &tcache_bin_info[alloc_ctx.szind];
- if (!cache_bin_dalloc_easy(bin, bin_info, ptr)) {
+ if (!cache_bin_dalloc_easy(bin, ptr)) {
return false;
}
- if (config_stats) {
- size_t usize = sz_index2size(alloc_ctx.szind);
- *tsd_thread_deallocatedp_get(tsd) += usize;
- }
+ *tsd_thread_deallocatedp_get(tsd) = deallocated_after;
return true;
}
@@ -2950,6 +3264,8 @@ je_valloc(size_t size) {
* passed an extra argument for the caller return address, which will be
* ignored.
*/
+#include <features.h> // defines __GLIBC__ if we are compiling against glibc
+
JEMALLOC_EXPORT void (*__free_hook)(void *ptr) = je_free;
JEMALLOC_EXPORT void *(*__malloc_hook)(size_t size) = je_malloc;
JEMALLOC_EXPORT void *(*__realloc_hook)(void *ptr, size_t size) = je_realloc;
@@ -2958,7 +3274,7 @@ JEMALLOC_EXPORT void *(*__memalign_hook)(size_t alignment, size_t size) =
je_memalign;
# endif
-# ifdef CPU_COUNT
+# ifdef __GLIBC__
/*
* To enable static linking with glibc, the libc specific malloc interface must
* be implemented also, so none of glibc's malloc.o functions are added to the
@@ -3001,6 +3317,26 @@ int __posix_memalign(void** r, size_t a, size_t s) PREALIAS(je_posix_memalign);
* Begin non-standard functions.
*/
+JEMALLOC_ALWAYS_INLINE unsigned
+mallocx_tcache_get(int flags) {
+ if (likely((flags & MALLOCX_TCACHE_MASK) == 0)) {
+ return TCACHE_IND_AUTOMATIC;
+ } else if ((flags & MALLOCX_TCACHE_MASK) == MALLOCX_TCACHE_NONE) {
+ return TCACHE_IND_NONE;
+ } else {
+ return MALLOCX_TCACHE_GET(flags);
+ }
+}
+
+JEMALLOC_ALWAYS_INLINE unsigned
+mallocx_arena_get(int flags) {
+ if (unlikely((flags & MALLOCX_ARENA_MASK) != 0)) {
+ return MALLOCX_ARENA_GET(flags);
+ } else {
+ return ARENA_IND_AUTOMATIC;
+ }
+}
+
#ifdef JEMALLOC_EXPERIMENTAL_SMALLOCX_API
#define JEMALLOC_SMALLOCX_CONCAT_HELPER(x, y) x ## y
@@ -3045,25 +3381,10 @@ JEMALLOC_SMALLOCX_CONCAT_HELPER2(je_smallocx_, JEMALLOC_VERSION_GID_IDENT)
dopts.num_items = 1;
dopts.item_size = size;
if (unlikely(flags != 0)) {
- if ((flags & MALLOCX_LG_ALIGN_MASK) != 0) {
- dopts.alignment = MALLOCX_ALIGN_GET_SPECIFIED(flags);
- }
-
+ dopts.alignment = MALLOCX_ALIGN_GET(flags);
dopts.zero = MALLOCX_ZERO_GET(flags);
-
- if ((flags & MALLOCX_TCACHE_MASK) != 0) {
- if ((flags & MALLOCX_TCACHE_MASK)
- == MALLOCX_TCACHE_NONE) {
- dopts.tcache_ind = TCACHE_IND_NONE;
- } else {
- dopts.tcache_ind = MALLOCX_TCACHE_GET(flags);
- }
- } else {
- dopts.tcache_ind = TCACHE_IND_AUTOMATIC;
- }
-
- if ((flags & MALLOCX_ARENA_MASK) != 0)
- dopts.arena_ind = MALLOCX_ARENA_GET(flags);
+ dopts.tcache_ind = mallocx_tcache_get(flags);
+ dopts.arena_ind = mallocx_arena_get(flags);
}
imalloc(&sopts, &dopts);
@@ -3098,25 +3419,10 @@ je_mallocx(size_t size, int flags) {
dopts.num_items = 1;
dopts.item_size = size;
if (unlikely(flags != 0)) {
- if ((flags & MALLOCX_LG_ALIGN_MASK) != 0) {
- dopts.alignment = MALLOCX_ALIGN_GET_SPECIFIED(flags);
- }
-
+ dopts.alignment = MALLOCX_ALIGN_GET(flags);
dopts.zero = MALLOCX_ZERO_GET(flags);
-
- if ((flags & MALLOCX_TCACHE_MASK) != 0) {
- if ((flags & MALLOCX_TCACHE_MASK)
- == MALLOCX_TCACHE_NONE) {
- dopts.tcache_ind = TCACHE_IND_NONE;
- } else {
- dopts.tcache_ind = MALLOCX_TCACHE_GET(flags);
- }
- } else {
- dopts.tcache_ind = TCACHE_IND_AUTOMATIC;
- }
-
- if ((flags & MALLOCX_ARENA_MASK) != 0)
- dopts.arena_ind = MALLOCX_ARENA_GET(flags);
+ dopts.tcache_ind = mallocx_tcache_get(flags);
+ dopts.arena_ind = mallocx_arena_get(flags);
}
imalloc(&sopts, &dopts);
@@ -3139,6 +3445,8 @@ irallocx_prof_sample(tsdn_t *tsdn, void *old_ptr, size_t old_usize,
if (tctx == NULL) {
return NULL;
}
+
+ alignment = prof_sample_align(alignment);
if (usize <= SC_SMALL_MAXCLASS) {
p = iralloct(tsdn, old_ptr, old_usize,
SC_LARGE_MINCLASS, alignment, zero, tcache,
@@ -3151,66 +3459,48 @@ irallocx_prof_sample(tsdn_t *tsdn, void *old_ptr, size_t old_usize,
p = iralloct(tsdn, old_ptr, old_usize, usize, alignment, zero,
tcache, arena, hook_args);
}
+ assert(prof_sample_aligned(p));
return p;
}
JEMALLOC_ALWAYS_INLINE void *
irallocx_prof(tsd_t *tsd, void *old_ptr, size_t old_usize, size_t size,
- size_t alignment, size_t *usize, bool zero, tcache_t *tcache,
- arena_t *arena, alloc_ctx_t *alloc_ctx, hook_ralloc_args_t *hook_args) {
+ size_t alignment, size_t usize, bool zero, tcache_t *tcache,
+ arena_t *arena, emap_alloc_ctx_t *alloc_ctx,
+ hook_ralloc_args_t *hook_args) {
+ prof_info_t old_prof_info;
+ prof_info_get_and_reset_recent(tsd, old_ptr, alloc_ctx, &old_prof_info);
+ bool prof_active = prof_active_get_unlocked();
+ bool sample_event = te_prof_sample_event_lookahead(tsd, usize);
+ prof_tctx_t *tctx = prof_alloc_prep(tsd, prof_active, sample_event);
void *p;
- bool prof_active;
- prof_tctx_t *old_tctx, *tctx;
-
- prof_active = prof_active_get_unlocked();
- old_tctx = prof_tctx_get(tsd_tsdn(tsd), old_ptr, alloc_ctx);
- tctx = prof_alloc_prep(tsd, *usize, prof_active, false);
if (unlikely((uintptr_t)tctx != (uintptr_t)1U)) {
p = irallocx_prof_sample(tsd_tsdn(tsd), old_ptr, old_usize,
- *usize, alignment, zero, tcache, arena, tctx, hook_args);
+ usize, alignment, zero, tcache, arena, tctx, hook_args);
} else {
p = iralloct(tsd_tsdn(tsd), old_ptr, old_usize, size, alignment,
zero, tcache, arena, hook_args);
}
if (unlikely(p == NULL)) {
- prof_alloc_rollback(tsd, tctx, false);
+ prof_alloc_rollback(tsd, tctx);
return NULL;
}
-
- if (p == old_ptr && alignment != 0) {
- /*
- * The allocation did not move, so it is possible that the size
- * class is smaller than would guarantee the requested
- * alignment, and that the alignment constraint was
- * serendipitously satisfied. Additionally, old_usize may not
- * be the same as the current usize because of in-place large
- * reallocation. Therefore, query the actual value of usize.
- */
- *usize = isalloc(tsd_tsdn(tsd), p);
- }
- prof_realloc(tsd, p, *usize, tctx, prof_active, false, old_ptr,
- old_usize, old_tctx);
+ assert(usize == isalloc(tsd_tsdn(tsd), p));
+ prof_realloc(tsd, p, size, usize, tctx, prof_active, old_ptr,
+ old_usize, &old_prof_info, sample_event);
return p;
}
-JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
-void JEMALLOC_NOTHROW *
-JEMALLOC_ALLOC_SIZE(2)
-je_rallocx(void *ptr, size_t size, int flags) {
+static void *
+do_rallocx(void *ptr, size_t size, int flags, bool is_realloc) {
void *p;
tsd_t *tsd;
size_t usize;
size_t old_usize;
size_t alignment = MALLOCX_ALIGN_GET(flags);
- bool zero = flags & MALLOCX_ZERO;
arena_t *arena;
- tcache_t *tcache;
-
- LOG("core.rallocx.entry", "ptr: %p, size: %zu, flags: %d", ptr,
- size, flags);
-
assert(ptr != NULL);
assert(size != 0);
@@ -3218,44 +3508,31 @@ je_rallocx(void *ptr, size_t size, int flags) {
tsd = tsd_fetch();
check_entry_exit_locking(tsd_tsdn(tsd));
- if (unlikely((flags & MALLOCX_ARENA_MASK) != 0)) {
- unsigned arena_ind = MALLOCX_ARENA_GET(flags);
- arena = arena_get(tsd_tsdn(tsd), arena_ind, true);
- if (unlikely(arena == NULL)) {
- goto label_oom;
- }
- } else {
- arena = NULL;
- }
+ bool zero = zero_get(MALLOCX_ZERO_GET(flags), /* slow */ true);
- if (unlikely((flags & MALLOCX_TCACHE_MASK) != 0)) {
- if ((flags & MALLOCX_TCACHE_MASK) == MALLOCX_TCACHE_NONE) {
- tcache = NULL;
- } else {
- tcache = tcaches_get(tsd, MALLOCX_TCACHE_GET(flags));
- }
- } else {
- tcache = tcache_get(tsd);
+ unsigned arena_ind = mallocx_arena_get(flags);
+ if (arena_get_from_ind(tsd, arena_ind, &arena)) {
+ goto label_oom;
}
- alloc_ctx_t alloc_ctx;
- rtree_ctx_t *rtree_ctx = tsd_rtree_ctx(tsd);
- rtree_szind_slab_read(tsd_tsdn(tsd), &extents_rtree, rtree_ctx,
- (uintptr_t)ptr, true, &alloc_ctx.szind, &alloc_ctx.slab);
+ unsigned tcache_ind = mallocx_tcache_get(flags);
+ tcache_t *tcache = tcache_get_from_ind(tsd, tcache_ind,
+ /* slow */ true, /* is_alloc */ true);
+
+ emap_alloc_ctx_t alloc_ctx;
+ emap_alloc_ctx_lookup(tsd_tsdn(tsd), &arena_emap_global, ptr,
+ &alloc_ctx);
assert(alloc_ctx.szind != SC_NSIZES);
old_usize = sz_index2size(alloc_ctx.szind);
assert(old_usize == isalloc(tsd_tsdn(tsd), ptr));
+ if (aligned_usize_get(size, alignment, &usize, NULL, false)) {
+ goto label_oom;
+ }
- hook_ralloc_args_t hook_args = {false, {(uintptr_t)ptr, size, flags,
- 0}};
+ hook_ralloc_args_t hook_args = {is_realloc, {(uintptr_t)ptr, size,
+ flags, 0}};
if (config_prof && opt_prof) {
- usize = (alignment == 0) ?
- sz_s2u(size) : sz_sa2u(size, alignment);
- if (unlikely(usize == 0
- || usize > SC_LARGE_MAXCLASS)) {
- goto label_oom;
- }
- p = irallocx_prof(tsd, ptr, old_usize, size, alignment, &usize,
+ p = irallocx_prof(tsd, ptr, old_usize, size, alignment, usize,
zero, tcache, arena, &alloc_ctx, &hook_args);
if (unlikely(p == NULL)) {
goto label_oom;
@@ -3266,20 +3543,22 @@ je_rallocx(void *ptr, size_t size, int flags) {
if (unlikely(p == NULL)) {
goto label_oom;
}
- if (config_stats) {
- usize = isalloc(tsd_tsdn(tsd), p);
- }
+ assert(usize == isalloc(tsd_tsdn(tsd), p));
}
assert(alignment == 0 || ((uintptr_t)p & (alignment - 1)) == ZU(0));
+ thread_alloc_event(tsd, usize);
+ thread_dalloc_event(tsd, old_usize);
- if (config_stats) {
- *tsd_thread_allocatedp_get(tsd) += usize;
- *tsd_thread_deallocatedp_get(tsd) += old_usize;
- }
UTRACE(ptr, size, p);
check_entry_exit_locking(tsd_tsdn(tsd));
- LOG("core.rallocx.exit", "result: %p", p);
+ if (config_fill && unlikely(opt_junk_alloc) && usize > old_usize
+ && !zero) {
+ size_t excess_len = usize - old_usize;
+ void *excess_start = (void *)((uintptr_t)p + old_usize);
+ junk_alloc_callback(excess_start, excess_len);
+ }
+
return p;
label_oom:
if (config_xmalloc && unlikely(opt_xmalloc)) {
@@ -3289,10 +3568,103 @@ label_oom:
UTRACE(ptr, size, 0);
check_entry_exit_locking(tsd_tsdn(tsd));
- LOG("core.rallocx.exit", "result: %p", NULL);
return NULL;
}
+JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
+void JEMALLOC_NOTHROW *
+JEMALLOC_ALLOC_SIZE(2)
+je_rallocx(void *ptr, size_t size, int flags) {
+ LOG("core.rallocx.entry", "ptr: %p, size: %zu, flags: %d", ptr,
+ size, flags);
+ void *ret = do_rallocx(ptr, size, flags, false);
+ LOG("core.rallocx.exit", "result: %p", ret);
+ return ret;
+}
+
+static void *
+do_realloc_nonnull_zero(void *ptr) {
+ if (config_stats) {
+ atomic_fetch_add_zu(&zero_realloc_count, 1, ATOMIC_RELAXED);
+ }
+ if (opt_zero_realloc_action == zero_realloc_action_alloc) {
+ /*
+ * The user might have gotten an alloc setting while expecting a
+ * free setting. If that's the case, we at least try to
+ * reduce the harm, and turn off the tcache while allocating, so
+ * that we'll get a true first fit.
+ */
+ return do_rallocx(ptr, 1, MALLOCX_TCACHE_NONE, true);
+ } else if (opt_zero_realloc_action == zero_realloc_action_free) {
+ UTRACE(ptr, 0, 0);
+ tsd_t *tsd = tsd_fetch();
+ check_entry_exit_locking(tsd_tsdn(tsd));
+
+ tcache_t *tcache = tcache_get_from_ind(tsd,
+ TCACHE_IND_AUTOMATIC, /* slow */ true,
+ /* is_alloc */ false);
+ uintptr_t args[3] = {(uintptr_t)ptr, 0};
+ hook_invoke_dalloc(hook_dalloc_realloc, ptr, args);
+ ifree(tsd, ptr, tcache, true);
+
+ check_entry_exit_locking(tsd_tsdn(tsd));
+ return NULL;
+ } else {
+ safety_check_fail("Called realloc(non-null-ptr, 0) with "
+ "zero_realloc:abort set\n");
+ /* In real code, this will never run; the safety check failure
+ * will call abort. In the unit test, we just want to bail out
+ * without corrupting internal state that the test needs to
+ * finish.
+ */
+ return NULL;
+ }
+}
+
+JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
+void JEMALLOC_NOTHROW *
+JEMALLOC_ALLOC_SIZE(2)
+je_realloc(void *ptr, size_t size) {
+ LOG("core.realloc.entry", "ptr: %p, size: %zu\n", ptr, size);
+
+ if (likely(ptr != NULL && size != 0)) {
+ void *ret = do_rallocx(ptr, size, 0, true);
+ LOG("core.realloc.exit", "result: %p", ret);
+ return ret;
+ } else if (ptr != NULL && size == 0) {
+ void *ret = do_realloc_nonnull_zero(ptr);
+ LOG("core.realloc.exit", "result: %p", ret);
+ return ret;
+ } else {
+ /* realloc(NULL, size) is equivalent to malloc(size). */
+ void *ret;
+
+ static_opts_t sopts;
+ dynamic_opts_t dopts;
+
+ static_opts_init(&sopts);
+ dynamic_opts_init(&dopts);
+
+ sopts.null_out_result_on_error = true;
+ sopts.set_errno_on_error = true;
+ sopts.oom_string =
+ "<jemalloc>: Error in realloc(): out of memory\n";
+
+ dopts.result = &ret;
+ dopts.num_items = 1;
+ dopts.item_size = size;
+
+ imalloc(&sopts, &dopts);
+ if (sopts.slow) {
+ uintptr_t args[3] = {(uintptr_t)ptr, size};
+ hook_invoke_alloc(hook_alloc_realloc, ret,
+ (uintptr_t)ret, args);
+ }
+ LOG("core.realloc.exit", "result: %p", ret);
+ return ret;
+ }
+}
+
JEMALLOC_ALWAYS_INLINE size_t
ixallocx_helper(tsdn_t *tsdn, void *ptr, size_t old_usize, size_t size,
size_t extra, size_t alignment, bool zero) {
@@ -3309,51 +3681,46 @@ ixallocx_helper(tsdn_t *tsdn, void *ptr, size_t old_usize, size_t size,
static size_t
ixallocx_prof_sample(tsdn_t *tsdn, void *ptr, size_t old_usize, size_t size,
size_t extra, size_t alignment, bool zero, prof_tctx_t *tctx) {
- size_t usize;
-
- if (tctx == NULL) {
+ /* Sampled allocation needs to be page aligned. */
+ if (tctx == NULL || !prof_sample_aligned(ptr)) {
return old_usize;
}
- usize = ixallocx_helper(tsdn, ptr, old_usize, size, extra, alignment,
- zero);
- return usize;
+ return ixallocx_helper(tsdn, ptr, old_usize, size, extra, alignment,
+ zero);
}
JEMALLOC_ALWAYS_INLINE size_t
ixallocx_prof(tsd_t *tsd, void *ptr, size_t old_usize, size_t size,
- size_t extra, size_t alignment, bool zero, alloc_ctx_t *alloc_ctx) {
- size_t usize_max, usize;
- bool prof_active;
- prof_tctx_t *old_tctx, *tctx;
+ size_t extra, size_t alignment, bool zero, emap_alloc_ctx_t *alloc_ctx) {
+ /*
+ * old_prof_info is only used for asserting that the profiling info
+ * isn't changed by the ixalloc() call.
+ */
+ prof_info_t old_prof_info;
+ prof_info_get(tsd, ptr, alloc_ctx, &old_prof_info);
- prof_active = prof_active_get_unlocked();
- old_tctx = prof_tctx_get(tsd_tsdn(tsd), ptr, alloc_ctx);
/*
* usize isn't knowable before ixalloc() returns when extra is non-zero.
* Therefore, compute its maximum possible value and use that in
* prof_alloc_prep() to decide whether to capture a backtrace.
* prof_realloc() will use the actual usize to decide whether to sample.
*/
- if (alignment == 0) {
- usize_max = sz_s2u(size+extra);
- assert(usize_max > 0
- && usize_max <= SC_LARGE_MAXCLASS);
- } else {
- usize_max = sz_sa2u(size+extra, alignment);
- if (unlikely(usize_max == 0
- || usize_max > SC_LARGE_MAXCLASS)) {
- /*
- * usize_max is out of range, and chances are that
- * allocation will fail, but use the maximum possible
- * value and carry on with prof_alloc_prep(), just in
- * case allocation succeeds.
- */
- usize_max = SC_LARGE_MAXCLASS;
- }
+ size_t usize_max;
+ if (aligned_usize_get(size + extra, alignment, &usize_max, NULL,
+ false)) {
+ /*
+ * usize_max is out of range, and chances are that allocation
+ * will fail, but use the maximum possible value and carry on
+ * with prof_alloc_prep(), just in case allocation succeeds.
+ */
+ usize_max = SC_LARGE_MAXCLASS;
}
- tctx = prof_alloc_prep(tsd, usize_max, prof_active, false);
+ bool prof_active = prof_active_get_unlocked();
+ bool sample_event = te_prof_sample_event_lookahead(tsd, usize_max);
+ prof_tctx_t *tctx = prof_alloc_prep(tsd, prof_active, sample_event);
+ size_t usize;
if (unlikely((uintptr_t)tctx != (uintptr_t)1U)) {
usize = ixallocx_prof_sample(tsd_tsdn(tsd), ptr, old_usize,
size, extra, alignment, zero, tctx);
@@ -3361,13 +3728,28 @@ ixallocx_prof(tsd_t *tsd, void *ptr, size_t old_usize, size_t size,
usize = ixallocx_helper(tsd_tsdn(tsd), ptr, old_usize, size,
extra, alignment, zero);
}
+
+ /*
+ * At this point we can still safely get the original profiling
+ * information associated with the ptr, because (a) the edata_t object
+ * associated with the ptr still lives and (b) the profiling info
+ * fields are not touched. "(a)" is asserted in the outer je_xallocx()
+ * function, and "(b)" is indirectly verified below by checking that
+ * the alloc_tctx field is unchanged.
+ */
+ prof_info_t prof_info;
if (usize == old_usize) {
- prof_alloc_rollback(tsd, tctx, false);
- return usize;
+ prof_info_get(tsd, ptr, alloc_ctx, &prof_info);
+ prof_alloc_rollback(tsd, tctx);
+ } else {
+ prof_info_get_and_reset_recent(tsd, ptr, alloc_ctx, &prof_info);
+ assert(usize <= usize_max);
+ sample_event = te_prof_sample_event_lookahead(tsd, usize);
+ prof_realloc(tsd, ptr, size, usize, tctx, prof_active, ptr,
+ old_usize, &prof_info, sample_event);
}
- prof_realloc(tsd, ptr, usize, tctx, prof_active, false, ptr, old_usize,
- old_tctx);
+ assert(old_prof_info.alloc_tctx == prof_info.alloc_tctx);
return usize;
}
@@ -3376,7 +3758,7 @@ je_xallocx(void *ptr, size_t size, size_t extra, int flags) {
tsd_t *tsd;
size_t usize, old_usize;
size_t alignment = MALLOCX_ALIGN_GET(flags);
- bool zero = flags & MALLOCX_ZERO;
+ bool zero = zero_get(MALLOCX_ZERO_GET(flags), /* slow */ true);
LOG("core.xallocx.entry", "ptr: %p, size: %zu, extra: %zu, "
"flags: %d", ptr, size, extra, flags);
@@ -3388,10 +3770,17 @@ je_xallocx(void *ptr, size_t size, size_t extra, int flags) {
tsd = tsd_fetch();
check_entry_exit_locking(tsd_tsdn(tsd));
- alloc_ctx_t alloc_ctx;
- rtree_ctx_t *rtree_ctx = tsd_rtree_ctx(tsd);
- rtree_szind_slab_read(tsd_tsdn(tsd), &extents_rtree, rtree_ctx,
- (uintptr_t)ptr, true, &alloc_ctx.szind, &alloc_ctx.slab);
+ /*
+ * old_edata is only for verifying that xallocx() keeps the edata_t
+ * object associated with the ptr (though the content of the edata_t
+ * object can be changed).
+ */
+ edata_t *old_edata = emap_edata_lookup(tsd_tsdn(tsd),
+ &arena_emap_global, ptr);
+
+ emap_alloc_ctx_t alloc_ctx;
+ emap_alloc_ctx_lookup(tsd_tsdn(tsd), &arena_emap_global, ptr,
+ &alloc_ctx);
assert(alloc_ctx.szind != SC_NSIZES);
old_usize = sz_index2size(alloc_ctx.szind);
assert(old_usize == isalloc(tsd_tsdn(tsd), ptr));
@@ -3419,13 +3808,25 @@ je_xallocx(void *ptr, size_t size, size_t extra, int flags) {
usize = ixallocx_helper(tsd_tsdn(tsd), ptr, old_usize, size,
extra, alignment, zero);
}
+
+ /*
+ * xallocx() should keep using the same edata_t object (though its
+ * content can be changed).
+ */
+ assert(emap_edata_lookup(tsd_tsdn(tsd), &arena_emap_global, ptr)
+ == old_edata);
+
if (unlikely(usize == old_usize)) {
goto label_not_resized;
}
+ thread_alloc_event(tsd, usize);
+ thread_dalloc_event(tsd, old_usize);
- if (config_stats) {
- *tsd_thread_allocatedp_get(tsd) += usize;
- *tsd_thread_deallocatedp_get(tsd) += old_usize;
+ if (config_fill && unlikely(opt_junk_alloc) && usize > old_usize &&
+ !zero) {
+ size_t excess_len = usize - old_usize;
+ void *excess_start = (void *)((uintptr_t)ptr + old_usize);
+ junk_alloc_callback(excess_start, excess_len);
}
label_not_resized:
if (unlikely(!tsd_fast(tsd))) {
@@ -3475,31 +3876,13 @@ je_dallocx(void *ptr, int flags) {
assert(ptr != NULL);
assert(malloc_initialized() || IS_INITIALIZER);
- tsd_t *tsd = tsd_fetch();
+ tsd_t *tsd = tsd_fetch_min();
bool fast = tsd_fast(tsd);
check_entry_exit_locking(tsd_tsdn(tsd));
- tcache_t *tcache;
- if (unlikely((flags & MALLOCX_TCACHE_MASK) != 0)) {
- /* Not allowed to be reentrant and specify a custom tcache. */
- assert(tsd_reentrancy_level_get(tsd) == 0);
- if ((flags & MALLOCX_TCACHE_MASK) == MALLOCX_TCACHE_NONE) {
- tcache = NULL;
- } else {
- tcache = tcaches_get(tsd, MALLOCX_TCACHE_GET(flags));
- }
- } else {
- if (likely(fast)) {
- tcache = tsd_tcachep_get(tsd);
- assert(tcache == tcache_get(tsd));
- } else {
- if (likely(tsd_reentrancy_level_get(tsd) == 0)) {
- tcache = tcache_get(tsd);
- } else {
- tcache = NULL;
- }
- }
- }
+ unsigned tcache_ind = mallocx_tcache_get(flags);
+ tcache_t *tcache = tcache_get_from_ind(tsd, tcache_ind, !fast,
+ /* is_alloc */ false);
UTRACE(ptr, 0, 0);
if (likely(fast)) {
@@ -3518,13 +3901,9 @@ je_dallocx(void *ptr, int flags) {
JEMALLOC_ALWAYS_INLINE size_t
inallocx(tsdn_t *tsdn, size_t size, int flags) {
check_entry_exit_locking(tsdn);
-
size_t usize;
- if (likely((flags & MALLOCX_LG_ALIGN_MASK) == 0)) {
- usize = sz_s2u(size);
- } else {
- usize = sz_sa2u(size, MALLOCX_ALIGN_GET_SPECIFIED(flags));
- }
+ /* In case of out of range, let the user see it rather than fail. */
+ aligned_usize_get(size, MALLOCX_ALIGN_GET(flags), &usize, NULL, false);
check_entry_exit_locking(tsdn);
return usize;
}
@@ -3534,33 +3913,14 @@ sdallocx_default(void *ptr, size_t size, int flags) {
assert(ptr != NULL);
assert(malloc_initialized() || IS_INITIALIZER);
- tsd_t *tsd = tsd_fetch();
+ tsd_t *tsd = tsd_fetch_min();
bool fast = tsd_fast(tsd);
size_t usize = inallocx(tsd_tsdn(tsd), size, flags);
- assert(usize == isalloc(tsd_tsdn(tsd), ptr));
check_entry_exit_locking(tsd_tsdn(tsd));
- tcache_t *tcache;
- if (unlikely((flags & MALLOCX_TCACHE_MASK) != 0)) {
- /* Not allowed to be reentrant and specify a custom tcache. */
- assert(tsd_reentrancy_level_get(tsd) == 0);
- if ((flags & MALLOCX_TCACHE_MASK) == MALLOCX_TCACHE_NONE) {
- tcache = NULL;
- } else {
- tcache = tcaches_get(tsd, MALLOCX_TCACHE_GET(flags));
- }
- } else {
- if (likely(fast)) {
- tcache = tsd_tcachep_get(tsd);
- assert(tcache == tcache_get(tsd));
- } else {
- if (likely(tsd_reentrancy_level_get(tsd) == 0)) {
- tcache = tcache_get(tsd);
- } else {
- tcache = NULL;
- }
- }
- }
+ unsigned tcache_ind = mallocx_tcache_get(flags);
+ tcache_t *tcache = tcache_get_from_ind(tsd, tcache_ind, !fast,
+ /* is_alloc */ false);
UTRACE(ptr, 0, 0);
if (likely(fast)) {
@@ -3572,7 +3932,6 @@ sdallocx_default(void *ptr, size_t size, int flags) {
isfree(tsd, ptr, usize, tcache, true);
}
check_entry_exit_locking(tsd_tsdn(tsd));
-
}
JEMALLOC_EXPORT void JEMALLOC_NOTHROW
@@ -3580,7 +3939,7 @@ je_sdallocx(void *ptr, size_t size, int flags) {
LOG("core.sdallocx.entry", "ptr: %p, size: %zu, flags: %d", ptr,
size, flags);
- if (flags !=0 || !free_fastpath(ptr, size, true)) {
+ if (flags != 0 || !free_fastpath(ptr, size, true)) {
sdallocx_default(ptr, size, flags);
}
@@ -3689,6 +4048,7 @@ je_mallctlbymib(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,
return ret;
}
+#define STATS_PRINT_BUFSIZE 65536
JEMALLOC_EXPORT void JEMALLOC_NOTHROW
je_malloc_stats_print(void (*write_cb)(void *, const char *), void *cbopaque,
const char *opts) {
@@ -3698,23 +4058,30 @@ je_malloc_stats_print(void (*write_cb)(void *, const char *), void *cbopaque,
tsdn = tsdn_fetch();
check_entry_exit_locking(tsdn);
- stats_print(write_cb, cbopaque, opts);
+
+ if (config_debug) {
+ stats_print(write_cb, cbopaque, opts);
+ } else {
+ buf_writer_t buf_writer;
+ buf_writer_init(tsdn, &buf_writer, write_cb, cbopaque, NULL,
+ STATS_PRINT_BUFSIZE);
+ stats_print(buf_writer_cb, &buf_writer, opts);
+ buf_writer_terminate(tsdn, &buf_writer);
+ }
+
check_entry_exit_locking(tsdn);
LOG("core.malloc_stats_print.exit", "");
}
+#undef STATS_PRINT_BUFSIZE
-JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW
-je_malloc_usable_size(JEMALLOC_USABLE_SIZE_CONST void *ptr) {
- size_t ret;
- tsdn_t *tsdn;
-
- LOG("core.malloc_usable_size.entry", "ptr: %p", ptr);
-
+JEMALLOC_ALWAYS_INLINE size_t
+je_malloc_usable_size_impl(JEMALLOC_USABLE_SIZE_CONST void *ptr) {
assert(malloc_initialized() || IS_INITIALIZER);
- tsdn = tsdn_fetch();
+ tsdn_t *tsdn = tsdn_fetch();
check_entry_exit_locking(tsdn);
+ size_t ret;
if (unlikely(ptr == NULL)) {
ret = 0;
} else {
@@ -3725,12 +4092,211 @@ je_malloc_usable_size(JEMALLOC_USABLE_SIZE_CONST void *ptr) {
ret = isalloc(tsdn, ptr);
}
}
-
check_entry_exit_locking(tsdn);
+
+ return ret;
+}
+
+JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW
+je_malloc_usable_size(JEMALLOC_USABLE_SIZE_CONST void *ptr) {
+ LOG("core.malloc_usable_size.entry", "ptr: %p", ptr);
+
+ size_t ret = je_malloc_usable_size_impl(ptr);
+
LOG("core.malloc_usable_size.exit", "result: %zu", ret);
return ret;
}
+#ifdef JEMALLOC_HAVE_MALLOC_SIZE
+JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW
+je_malloc_size(const void *ptr) {
+ LOG("core.malloc_size.entry", "ptr: %p", ptr);
+
+ size_t ret = je_malloc_usable_size_impl(ptr);
+
+ LOG("core.malloc_size.exit", "result: %zu", ret);
+ return ret;
+}
+#endif
+
+static void
+batch_alloc_prof_sample_assert(tsd_t *tsd, size_t batch, size_t usize) {
+ assert(config_prof && opt_prof);
+ bool prof_sample_event = te_prof_sample_event_lookahead(tsd,
+ batch * usize);
+ assert(!prof_sample_event);
+ size_t surplus;
+ prof_sample_event = te_prof_sample_event_lookahead_surplus(tsd,
+ (batch + 1) * usize, &surplus);
+ assert(prof_sample_event);
+ assert(surplus < usize);
+}
+
+size_t
+batch_alloc(void **ptrs, size_t num, size_t size, int flags) {
+ LOG("core.batch_alloc.entry",
+ "ptrs: %p, num: %zu, size: %zu, flags: %d", ptrs, num, size, flags);
+
+ tsd_t *tsd = tsd_fetch();
+ check_entry_exit_locking(tsd_tsdn(tsd));
+
+ size_t filled = 0;
+
+ if (unlikely(tsd == NULL || tsd_reentrancy_level_get(tsd) > 0)) {
+ goto label_done;
+ }
+
+ size_t alignment = MALLOCX_ALIGN_GET(flags);
+ size_t usize;
+ if (aligned_usize_get(size, alignment, &usize, NULL, false)) {
+ goto label_done;
+ }
+ szind_t ind = sz_size2index(usize);
+ bool zero = zero_get(MALLOCX_ZERO_GET(flags), /* slow */ true);
+
+ /*
+ * The cache bin and arena will be lazily initialized; it's hard to
+ * know in advance whether each of them needs to be initialized.
+ */
+ cache_bin_t *bin = NULL;
+ arena_t *arena = NULL;
+
+ size_t nregs = 0;
+ if (likely(ind < SC_NBINS)) {
+ nregs = bin_infos[ind].nregs;
+ assert(nregs > 0);
+ }
+
+ while (filled < num) {
+ size_t batch = num - filled;
+ size_t surplus = SIZE_MAX; /* Dead store. */
+ bool prof_sample_event = config_prof && opt_prof
+ && prof_active_get_unlocked()
+ && te_prof_sample_event_lookahead_surplus(tsd,
+ batch * usize, &surplus);
+
+ if (prof_sample_event) {
+ /*
+ * Adjust so that the batch does not trigger prof
+ * sampling.
+ */
+ batch -= surplus / usize + 1;
+ batch_alloc_prof_sample_assert(tsd, batch, usize);
+ }
+
+ size_t progress = 0;
+
+ if (likely(ind < SC_NBINS) && batch >= nregs) {
+ if (arena == NULL) {
+ unsigned arena_ind = mallocx_arena_get(flags);
+ if (arena_get_from_ind(tsd, arena_ind,
+ &arena)) {
+ goto label_done;
+ }
+ if (arena == NULL) {
+ arena = arena_choose(tsd, NULL);
+ }
+ if (unlikely(arena == NULL)) {
+ goto label_done;
+ }
+ }
+ size_t arena_batch = batch - batch % nregs;
+ size_t n = arena_fill_small_fresh(tsd_tsdn(tsd), arena,
+ ind, ptrs + filled, arena_batch, zero);
+ progress += n;
+ filled += n;
+ }
+
+ if (likely(ind < nhbins) && progress < batch) {
+ if (bin == NULL) {
+ unsigned tcache_ind = mallocx_tcache_get(flags);
+ tcache_t *tcache = tcache_get_from_ind(tsd,
+ tcache_ind, /* slow */ true,
+ /* is_alloc */ true);
+ if (tcache != NULL) {
+ bin = &tcache->bins[ind];
+ }
+ }
+ /*
+ * If we don't have a tcache bin, we don't want to
+ * immediately give up, because there's the possibility
+ * that the user explicitly requested to bypass the
+ * tcache, or that the user explicitly turned off the
+ * tcache; in such cases, we go through the slow path,
+ * i.e. the mallocx() call at the end of the while loop.
+ */
+ if (bin != NULL) {
+ size_t bin_batch = batch - progress;
+ /*
+ * n can be less than bin_batch, meaning that
+ * the cache bin does not have enough memory.
+ * In such cases, we rely on the slow path,
+ * i.e. the mallocx() call at the end of the
+ * while loop, to fill in the cache, and in the
+ * next iteration of the while loop, the tcache
+ * will contain a lot of memory, and we can
+ * harvest them here. Compared to the
+ * alternative approach where we directly go to
+ * the arena bins here, the overhead of our
+ * current approach should usually be minimal,
+ * since we never try to fetch more memory than
+ * what a slab contains via the tcache. An
+ * additional benefit is that the tcache will
+ * not be empty for the next allocation request.
+ */
+ size_t n = cache_bin_alloc_batch(bin, bin_batch,
+ ptrs + filled);
+ if (config_stats) {
+ bin->tstats.nrequests += n;
+ }
+ if (zero) {
+ for (size_t i = 0; i < n; ++i) {
+ memset(ptrs[filled + i], 0,
+ usize);
+ }
+ }
+ if (config_prof && opt_prof
+ && unlikely(ind >= SC_NBINS)) {
+ for (size_t i = 0; i < n; ++i) {
+ prof_tctx_reset_sampled(tsd,
+ ptrs[filled + i]);
+ }
+ }
+ progress += n;
+ filled += n;
+ }
+ }
+
+ /*
+ * For thread events other than prof sampling, trigger them as
+ * if there's a single allocation of size (n * usize). This is
+ * fine because:
+ * (a) these events do not alter the allocation itself, and
+ * (b) it's possible that some event would have been triggered
+ * multiple times, instead of only once, if the allocations
+ * were handled individually, but it would do no harm (or
+ * even be beneficial) to coalesce the triggerings.
+ */
+ thread_alloc_event(tsd, progress * usize);
+
+ if (progress < batch || prof_sample_event) {
+ void *p = je_mallocx(size, flags);
+ if (p == NULL) { /* OOM */
+ break;
+ }
+ if (progress == batch) {
+ assert(prof_sampled(tsd, p));
+ }
+ ptrs[filled++] = p;
+ }
+ }
+
+label_done:
+ check_entry_exit_locking(tsd_tsdn(tsd));
+ LOG("core.batch_alloc.exit", "result: %zu", filled);
+ return filled;
+}
+
/*
* End non-standard functions.
*/
@@ -3894,7 +4460,7 @@ _malloc_prefork(void)
background_thread_prefork1(tsd_tsdn(tsd));
}
/* Break arena prefork into stages to preserve lock order. */
- for (i = 0; i < 8; i++) {
+ for (i = 0; i < 9; i++) {
for (j = 0; j < narenas; j++) {
if ((arena = arena_get(tsd_tsdn(tsd), j, false)) !=
NULL) {
@@ -3923,12 +4489,17 @@ _malloc_prefork(void)
case 7:
arena_prefork7(tsd_tsdn(tsd), arena);
break;
+ case 8:
+ arena_prefork8(tsd_tsdn(tsd), arena);
+ break;
default: not_reached();
}
}
}
+
}
prof_prefork1(tsd_tsdn(tsd));
+ stats_prefork(tsd_tsdn(tsd));
tsd_prefork(tsd);
}
@@ -3956,6 +4527,7 @@ _malloc_postfork(void)
witness_postfork_parent(tsd_witness_tsdp_get(tsd));
/* Release all mutexes, now that fork() has completed. */
+ stats_postfork_parent(tsd_tsdn(tsd));
for (i = 0, narenas = narenas_total_get(); i < narenas; i++) {
arena_t *arena;
@@ -3985,6 +4557,7 @@ jemalloc_postfork_child(void) {
witness_postfork_child(tsd_witness_tsdp_get(tsd));
/* Release all mutexes, now that fork() has completed. */
+ stats_postfork_child(tsd_tsdn(tsd));
for (i = 0, narenas = narenas_total_get(); i < narenas; i++) {
arena_t *arena;
diff --git a/contrib/jemalloc/src/jemalloc_cpp.cpp b/contrib/jemalloc/src/jemalloc_cpp.cpp
new file mode 100644
index 000000000000..451655f1b5a5
--- /dev/null
+++ b/contrib/jemalloc/src/jemalloc_cpp.cpp
@@ -0,0 +1,254 @@
+#include <mutex>
+#include <new>
+
+#define JEMALLOC_CPP_CPP_
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "jemalloc/internal/jemalloc_preamble.h"
+#include "jemalloc/internal/jemalloc_internal_includes.h"
+
+#ifdef __cplusplus
+}
+#endif
+
+// All operators in this file are exported.
+
+// Possibly alias hidden versions of malloc and sdallocx to avoid an extra plt
+// thunk?
+//
+// extern __typeof (sdallocx) sdallocx_int
+// __attribute ((alias ("sdallocx"),
+// visibility ("hidden")));
+//
+// ... but it needs to work with jemalloc namespaces.
+
+void *operator new(std::size_t size);
+void *operator new[](std::size_t size);
+void *operator new(std::size_t size, const std::nothrow_t &) noexcept;
+void *operator new[](std::size_t size, const std::nothrow_t &) noexcept;
+void operator delete(void *ptr) noexcept;
+void operator delete[](void *ptr) noexcept;
+void operator delete(void *ptr, const std::nothrow_t &) noexcept;
+void operator delete[](void *ptr, const std::nothrow_t &) noexcept;
+
+#if __cpp_sized_deallocation >= 201309
+/* C++14's sized-delete operators. */
+void operator delete(void *ptr, std::size_t size) noexcept;
+void operator delete[](void *ptr, std::size_t size) noexcept;
+#endif
+
+#if __cpp_aligned_new >= 201606
+/* C++17's over-aligned operators. */
+void *operator new(std::size_t size, std::align_val_t);
+void *operator new(std::size_t size, std::align_val_t, const std::nothrow_t &) noexcept;
+void *operator new[](std::size_t size, std::align_val_t);
+void *operator new[](std::size_t size, std::align_val_t, const std::nothrow_t &) noexcept;
+void operator delete(void* ptr, std::align_val_t) noexcept;
+void operator delete(void* ptr, std::align_val_t, const std::nothrow_t &) noexcept;
+void operator delete(void* ptr, std::size_t size, std::align_val_t al) noexcept;
+void operator delete[](void* ptr, std::align_val_t) noexcept;
+void operator delete[](void* ptr, std::align_val_t, const std::nothrow_t &) noexcept;
+void operator delete[](void* ptr, std::size_t size, std::align_val_t al) noexcept;
+#endif
+
+JEMALLOC_NOINLINE
+static void *
+handleOOM(std::size_t size, bool nothrow) {
+ if (opt_experimental_infallible_new) {
+ safety_check_fail("<jemalloc>: Allocation failed and "
+ "opt.experimental_infallible_new is true. Aborting.\n");
+ return nullptr;
+ }
+
+ void *ptr = nullptr;
+
+ while (ptr == nullptr) {
+ std::new_handler handler;
+ // GCC-4.8 and clang 4.0 do not have std::get_new_handler.
+ {
+ static std::mutex mtx;
+ std::lock_guard<std::mutex> lock(mtx);
+
+ handler = std::set_new_handler(nullptr);
+ std::set_new_handler(handler);
+ }
+ if (handler == nullptr)
+ break;
+
+ try {
+ handler();
+ } catch (const std::bad_alloc &) {
+ break;
+ }
+
+ ptr = je_malloc(size);
+ }
+
+ if (ptr == nullptr && !nothrow)
+ std::__throw_bad_alloc();
+ return ptr;
+}
+
+template <bool IsNoExcept>
+JEMALLOC_NOINLINE
+static void *
+fallback_impl(std::size_t size) noexcept(IsNoExcept) {
+ void *ptr = malloc_default(size);
+ if (likely(ptr != nullptr)) {
+ return ptr;
+ }
+ return handleOOM(size, IsNoExcept);
+}
+
+template <bool IsNoExcept>
+JEMALLOC_ALWAYS_INLINE
+void *
+newImpl(std::size_t size) noexcept(IsNoExcept) {
+ return imalloc_fastpath(size, &fallback_impl<IsNoExcept>);
+}
+
+void *
+operator new(std::size_t size) {
+ return newImpl<false>(size);
+}
+
+void *
+operator new[](std::size_t size) {
+ return newImpl<false>(size);
+}
+
+void *
+operator new(std::size_t size, const std::nothrow_t &) noexcept {
+ return newImpl<true>(size);
+}
+
+void *
+operator new[](std::size_t size, const std::nothrow_t &) noexcept {
+ return newImpl<true>(size);
+}
+
+#if __cpp_aligned_new >= 201606
+
+template <bool IsNoExcept>
+JEMALLOC_ALWAYS_INLINE
+void *
+alignedNewImpl(std::size_t size, std::align_val_t alignment) noexcept(IsNoExcept) {
+ void *ptr = je_aligned_alloc(static_cast<std::size_t>(alignment), size);
+ if (likely(ptr != nullptr)) {
+ return ptr;
+ }
+
+ return handleOOM(size, IsNoExcept);
+}
+
+void *
+operator new(std::size_t size, std::align_val_t alignment) {
+ return alignedNewImpl<false>(size, alignment);
+}
+
+void *
+operator new[](std::size_t size, std::align_val_t alignment) {
+ return alignedNewImpl<false>(size, alignment);
+}
+
+void *
+operator new(std::size_t size, std::align_val_t alignment, const std::nothrow_t &) noexcept {
+ return alignedNewImpl<true>(size, alignment);
+}
+
+void *
+operator new[](std::size_t size, std::align_val_t alignment, const std::nothrow_t &) noexcept {
+ return alignedNewImpl<true>(size, alignment);
+}
+
+#endif // __cpp_aligned_new
+
+void
+operator delete(void *ptr) noexcept {
+ je_free(ptr);
+}
+
+void
+operator delete[](void *ptr) noexcept {
+ je_free(ptr);
+}
+
+void
+operator delete(void *ptr, const std::nothrow_t &) noexcept {
+ je_free(ptr);
+}
+
+void operator delete[](void *ptr, const std::nothrow_t &) noexcept {
+ je_free(ptr);
+}
+
+#if __cpp_sized_deallocation >= 201309
+
+JEMALLOC_ALWAYS_INLINE
+void
+sizedDeleteImpl(void* ptr, std::size_t size) noexcept {
+ if (unlikely(ptr == nullptr)) {
+ return;
+ }
+ je_sdallocx_noflags(ptr, size);
+}
+
+void
+operator delete(void *ptr, std::size_t size) noexcept {
+ sizedDeleteImpl(ptr, size);
+}
+
+void
+operator delete[](void *ptr, std::size_t size) noexcept {
+ sizedDeleteImpl(ptr, size);
+}
+
+#endif // __cpp_sized_deallocation
+
+#if __cpp_aligned_new >= 201606
+
+JEMALLOC_ALWAYS_INLINE
+void
+alignedSizedDeleteImpl(void* ptr, std::size_t size, std::align_val_t alignment) noexcept {
+ if (config_debug) {
+ assert(((size_t)alignment & ((size_t)alignment - 1)) == 0);
+ }
+ if (unlikely(ptr == nullptr)) {
+ return;
+ }
+ je_sdallocx(ptr, size, MALLOCX_ALIGN(alignment));
+}
+
+void
+operator delete(void* ptr, std::align_val_t) noexcept {
+ je_free(ptr);
+}
+
+void
+operator delete[](void* ptr, std::align_val_t) noexcept {
+ je_free(ptr);
+}
+
+void
+operator delete(void* ptr, std::align_val_t, const std::nothrow_t&) noexcept {
+ je_free(ptr);
+}
+
+void
+operator delete[](void* ptr, std::align_val_t, const std::nothrow_t&) noexcept {
+ je_free(ptr);
+}
+
+void
+operator delete(void* ptr, std::size_t size, std::align_val_t alignment) noexcept {
+ alignedSizedDeleteImpl(ptr, size, alignment);
+}
+
+void
+operator delete[](void* ptr, std::size_t size, std::align_val_t alignment) noexcept {
+ alignedSizedDeleteImpl(ptr, size, alignment);
+}
+
+#endif // __cpp_aligned_new
diff --git a/contrib/jemalloc/src/large.c b/contrib/jemalloc/src/large.c
index 8e7a781d3300..5fc4bf584c45 100644
--- a/contrib/jemalloc/src/large.c
+++ b/contrib/jemalloc/src/large.c
@@ -1,11 +1,11 @@
-#define JEMALLOC_LARGE_C_
#include "jemalloc/internal/jemalloc_preamble.h"
#include "jemalloc/internal/jemalloc_internal_includes.h"
#include "jemalloc/internal/assert.h"
+#include "jemalloc/internal/emap.h"
#include "jemalloc/internal/extent_mmap.h"
#include "jemalloc/internal/mutex.h"
-#include "jemalloc/internal/rtree.h"
+#include "jemalloc/internal/prof_recent.h"
#include "jemalloc/internal/util.h"
/******************************************************************************/
@@ -21,8 +21,7 @@ void *
large_palloc(tsdn_t *tsdn, arena_t *arena, size_t usize, size_t alignment,
bool zero) {
size_t ausize;
- extent_t *extent;
- bool is_zeroed;
+ edata_t *edata;
UNUSED bool idump JEMALLOC_CC_SILENCE_INIT(false);
assert(!tsdn_null(tsdn) || arena != NULL);
@@ -32,163 +31,80 @@ large_palloc(tsdn_t *tsdn, arena_t *arena, size_t usize, size_t alignment,
return NULL;
}
- if (config_fill && unlikely(opt_zero)) {
- zero = true;
- }
- /*
- * Copy zero into is_zeroed and pass the copy when allocating the
- * extent, so that it is possible to make correct junk/zero fill
- * decisions below, even if is_zeroed ends up true when zero is false.
- */
- is_zeroed = zero;
if (likely(!tsdn_null(tsdn))) {
arena = arena_choose_maybe_huge(tsdn_tsd(tsdn), arena, usize);
}
- if (unlikely(arena == NULL) || (extent = arena_extent_alloc_large(tsdn,
- arena, usize, alignment, &is_zeroed)) == NULL) {
+ if (unlikely(arena == NULL) || (edata = arena_extent_alloc_large(tsdn,
+ arena, usize, alignment, zero)) == NULL) {
return NULL;
}
/* See comments in arena_bin_slabs_full_insert(). */
if (!arena_is_auto(arena)) {
- /* Insert extent into large. */
+ /* Insert edata into large. */
malloc_mutex_lock(tsdn, &arena->large_mtx);
- extent_list_append(&arena->large, extent);
+ edata_list_active_append(&arena->large, edata);
malloc_mutex_unlock(tsdn, &arena->large_mtx);
}
- if (config_prof && arena_prof_accum(tsdn, arena, usize)) {
- prof_idump(tsdn);
- }
-
- if (zero) {
- assert(is_zeroed);
- } else if (config_fill && unlikely(opt_junk_alloc)) {
- memset(extent_addr_get(extent), JEMALLOC_ALLOC_JUNK,
- extent_usize_get(extent));
- }
arena_decay_tick(tsdn, arena);
- return extent_addr_get(extent);
+ return edata_addr_get(edata);
}
-static void
-large_dalloc_junk_impl(void *ptr, size_t size) {
- memset(ptr, JEMALLOC_FREE_JUNK, size);
-}
-large_dalloc_junk_t *JET_MUTABLE large_dalloc_junk = large_dalloc_junk_impl;
-
-static void
-large_dalloc_maybe_junk_impl(void *ptr, size_t size) {
- if (config_fill && have_dss && unlikely(opt_junk_free)) {
- /*
- * Only bother junk filling if the extent isn't about to be
- * unmapped.
- */
- if (opt_retain || (have_dss && extent_in_dss(ptr))) {
- large_dalloc_junk(ptr, size);
- }
- }
-}
-large_dalloc_maybe_junk_t *JET_MUTABLE large_dalloc_maybe_junk =
- large_dalloc_maybe_junk_impl;
-
static bool
-large_ralloc_no_move_shrink(tsdn_t *tsdn, extent_t *extent, size_t usize) {
- arena_t *arena = extent_arena_get(extent);
- size_t oldusize = extent_usize_get(extent);
- extent_hooks_t *extent_hooks = extent_hooks_get(arena);
- size_t diff = extent_size_get(extent) - (usize + sz_large_pad);
+large_ralloc_no_move_shrink(tsdn_t *tsdn, edata_t *edata, size_t usize) {
+ arena_t *arena = arena_get_from_edata(edata);
+ ehooks_t *ehooks = arena_get_ehooks(arena);
+ size_t old_size = edata_size_get(edata);
+ size_t old_usize = edata_usize_get(edata);
- assert(oldusize > usize);
+ assert(old_usize > usize);
- if (extent_hooks->split == NULL) {
+ if (ehooks_split_will_fail(ehooks)) {
return true;
}
- /* Split excess pages. */
- if (diff != 0) {
- extent_t *trail = extent_split_wrapper(tsdn, arena,
- &extent_hooks, extent, usize + sz_large_pad,
- sz_size2index(usize), false, diff, SC_NSIZES, false);
- if (trail == NULL) {
- return true;
- }
-
- if (config_fill && unlikely(opt_junk_free)) {
- large_dalloc_maybe_junk(extent_addr_get(trail),
- extent_size_get(trail));
- }
-
- arena_extents_dirty_dalloc(tsdn, arena, &extent_hooks, trail);
+ bool deferred_work_generated = false;
+ bool err = pa_shrink(tsdn, &arena->pa_shard, edata, old_size,
+ usize + sz_large_pad, sz_size2index(usize),
+ &deferred_work_generated);
+ if (err) {
+ return true;
}
-
- arena_extent_ralloc_large_shrink(tsdn, arena, extent, oldusize);
+ if (deferred_work_generated) {
+ arena_handle_deferred_work(tsdn, arena);
+ }
+ arena_extent_ralloc_large_shrink(tsdn, arena, edata, old_usize);
return false;
}
static bool
-large_ralloc_no_move_expand(tsdn_t *tsdn, extent_t *extent, size_t usize,
+large_ralloc_no_move_expand(tsdn_t *tsdn, edata_t *edata, size_t usize,
bool zero) {
- arena_t *arena = extent_arena_get(extent);
- size_t oldusize = extent_usize_get(extent);
- extent_hooks_t *extent_hooks = extent_hooks_get(arena);
- size_t trailsize = usize - oldusize;
+ arena_t *arena = arena_get_from_edata(edata);
- if (extent_hooks->merge == NULL) {
- return true;
- }
+ size_t old_size = edata_size_get(edata);
+ size_t old_usize = edata_usize_get(edata);
+ size_t new_size = usize + sz_large_pad;
- if (config_fill && unlikely(opt_zero)) {
- zero = true;
- }
- /*
- * Copy zero into is_zeroed_trail and pass the copy when allocating the
- * extent, so that it is possible to make correct junk/zero fill
- * decisions below, even if is_zeroed_trail ends up true when zero is
- * false.
- */
- bool is_zeroed_trail = zero;
- bool commit = true;
- extent_t *trail;
- bool new_mapping;
- if ((trail = extents_alloc(tsdn, arena, &extent_hooks,
- &arena->extents_dirty, extent_past_get(extent), trailsize, 0,
- CACHELINE, false, SC_NSIZES, &is_zeroed_trail, &commit)) != NULL
- || (trail = extents_alloc(tsdn, arena, &extent_hooks,
- &arena->extents_muzzy, extent_past_get(extent), trailsize, 0,
- CACHELINE, false, SC_NSIZES, &is_zeroed_trail, &commit)) != NULL) {
- if (config_stats) {
- new_mapping = false;
- }
- } else {
- if ((trail = extent_alloc_wrapper(tsdn, arena, &extent_hooks,
- extent_past_get(extent), trailsize, 0, CACHELINE, false,
- SC_NSIZES, &is_zeroed_trail, &commit)) == NULL) {
- return true;
- }
- if (config_stats) {
- new_mapping = true;
- }
- }
+ szind_t szind = sz_size2index(usize);
- if (extent_merge_wrapper(tsdn, arena, &extent_hooks, extent, trail)) {
- extent_dalloc_wrapper(tsdn, arena, &extent_hooks, trail);
- return true;
+ bool deferred_work_generated = false;
+ bool err = pa_expand(tsdn, &arena->pa_shard, edata, old_size, new_size,
+ szind, zero, &deferred_work_generated);
+
+ if (deferred_work_generated) {
+ arena_handle_deferred_work(tsdn, arena);
}
- rtree_ctx_t rtree_ctx_fallback;
- rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);
- szind_t szind = sz_size2index(usize);
- extent_szind_set(extent, szind);
- rtree_szind_slab_update(tsdn, &extents_rtree, rtree_ctx,
- (uintptr_t)extent_addr_get(extent), szind, false);
- if (config_stats && new_mapping) {
- arena_stats_mapped_add(tsdn, &arena->stats, trailsize);
+ if (err) {
+ return true;
}
if (zero) {
- if (config_cache_oblivious) {
+ if (opt_cache_oblivious) {
+ assert(sz_large_pad == PAGE);
/*
* Zero the trailing bytes of the original allocation's
* last page, since they are in an indeterminate state.
@@ -197,28 +113,23 @@ large_ralloc_no_move_expand(tsdn_t *tsdn, extent_t *extent, size_t usize,
* of CACHELINE in [0 .. PAGE).
*/
void *zbase = (void *)
- ((uintptr_t)extent_addr_get(extent) + oldusize);
+ ((uintptr_t)edata_addr_get(edata) + old_usize);
void *zpast = PAGE_ADDR2BASE((void *)((uintptr_t)zbase +
PAGE));
size_t nzero = (uintptr_t)zpast - (uintptr_t)zbase;
assert(nzero > 0);
memset(zbase, 0, nzero);
}
- assert(is_zeroed_trail);
- } else if (config_fill && unlikely(opt_junk_alloc)) {
- memset((void *)((uintptr_t)extent_addr_get(extent) + oldusize),
- JEMALLOC_ALLOC_JUNK, usize - oldusize);
}
-
- arena_extent_ralloc_large_expand(tsdn, arena, extent, oldusize);
+ arena_extent_ralloc_large_expand(tsdn, arena, edata, old_usize);
return false;
}
bool
-large_ralloc_no_move(tsdn_t *tsdn, extent_t *extent, size_t usize_min,
+large_ralloc_no_move(tsdn_t *tsdn, edata_t *edata, size_t usize_min,
size_t usize_max, bool zero) {
- size_t oldusize = extent_usize_get(extent);
+ size_t oldusize = edata_usize_get(edata);
/* The following should have been caught by callers. */
assert(usize_min > 0 && usize_max <= SC_LARGE_MAXCLASS);
@@ -228,16 +139,15 @@ large_ralloc_no_move(tsdn_t *tsdn, extent_t *extent, size_t usize_min,
if (usize_max > oldusize) {
/* Attempt to expand the allocation in-place. */
- if (!large_ralloc_no_move_expand(tsdn, extent, usize_max,
+ if (!large_ralloc_no_move_expand(tsdn, edata, usize_max,
zero)) {
- arena_decay_tick(tsdn, extent_arena_get(extent));
+ arena_decay_tick(tsdn, arena_get_from_edata(edata));
return false;
}
/* Try again, this time with usize_min. */
if (usize_min < usize_max && usize_min > oldusize &&
- large_ralloc_no_move_expand(tsdn, extent, usize_min,
- zero)) {
- arena_decay_tick(tsdn, extent_arena_get(extent));
+ large_ralloc_no_move_expand(tsdn, edata, usize_min, zero)) {
+ arena_decay_tick(tsdn, arena_get_from_edata(edata));
return false;
}
}
@@ -247,14 +157,14 @@ large_ralloc_no_move(tsdn_t *tsdn, extent_t *extent, size_t usize_min,
* the new size.
*/
if (oldusize >= usize_min && oldusize <= usize_max) {
- arena_decay_tick(tsdn, extent_arena_get(extent));
+ arena_decay_tick(tsdn, arena_get_from_edata(edata));
return false;
}
/* Attempt to shrink the allocation in-place. */
if (oldusize > usize_max) {
- if (!large_ralloc_no_move_shrink(tsdn, extent, usize_max)) {
- arena_decay_tick(tsdn, extent_arena_get(extent));
+ if (!large_ralloc_no_move_shrink(tsdn, edata, usize_max)) {
+ arena_decay_tick(tsdn, arena_get_from_edata(edata));
return false;
}
}
@@ -274,9 +184,9 @@ void *
large_ralloc(tsdn_t *tsdn, arena_t *arena, void *ptr, size_t usize,
size_t alignment, bool zero, tcache_t *tcache,
hook_ralloc_args_t *hook_args) {
- extent_t *extent = iealloc(tsdn, ptr);
+ edata_t *edata = emap_edata_lookup(tsdn, &arena_emap_global, ptr);
- size_t oldusize = extent_usize_get(extent);
+ size_t oldusize = edata_usize_get(edata);
/* The following should have been caught by callers. */
assert(usize > 0 && usize <= SC_LARGE_MAXCLASS);
/* Both allocation sizes must be large to avoid a move. */
@@ -284,11 +194,11 @@ large_ralloc(tsdn_t *tsdn, arena_t *arena, void *ptr, size_t usize,
&& usize >= SC_LARGE_MINCLASS);
/* Try to avoid moving the allocation. */
- if (!large_ralloc_no_move(tsdn, extent, usize, usize, zero)) {
+ if (!large_ralloc_no_move(tsdn, edata, usize, usize, zero)) {
hook_invoke_expand(hook_args->is_realloc
? hook_expand_realloc : hook_expand_rallocx, ptr, oldusize,
usize, (uintptr_t)ptr, hook_args->args);
- return extent_addr_get(extent);
+ return edata_addr_get(edata);
}
/*
@@ -309,87 +219,104 @@ large_ralloc(tsdn_t *tsdn, arena_t *arena, void *ptr, size_t usize,
? hook_dalloc_realloc : hook_dalloc_rallocx, ptr, hook_args->args);
size_t copysize = (usize < oldusize) ? usize : oldusize;
- memcpy(ret, extent_addr_get(extent), copysize);
- isdalloct(tsdn, extent_addr_get(extent), oldusize, tcache, NULL, true);
+ memcpy(ret, edata_addr_get(edata), copysize);
+ isdalloct(tsdn, edata_addr_get(edata), oldusize, tcache, NULL, true);
return ret;
}
/*
- * junked_locked indicates whether the extent's data have been junk-filled, and
- * whether the arena's large_mtx is currently held.
+ * locked indicates whether the arena's large_mtx is currently held.
*/
static void
-large_dalloc_prep_impl(tsdn_t *tsdn, arena_t *arena, extent_t *extent,
- bool junked_locked) {
- if (!junked_locked) {
+large_dalloc_prep_impl(tsdn_t *tsdn, arena_t *arena, edata_t *edata,
+ bool locked) {
+ if (!locked) {
/* See comments in arena_bin_slabs_full_insert(). */
if (!arena_is_auto(arena)) {
malloc_mutex_lock(tsdn, &arena->large_mtx);
- extent_list_remove(&arena->large, extent);
+ edata_list_active_remove(&arena->large, edata);
malloc_mutex_unlock(tsdn, &arena->large_mtx);
}
- large_dalloc_maybe_junk(extent_addr_get(extent),
- extent_usize_get(extent));
} else {
/* Only hold the large_mtx if necessary. */
if (!arena_is_auto(arena)) {
malloc_mutex_assert_owner(tsdn, &arena->large_mtx);
- extent_list_remove(&arena->large, extent);
+ edata_list_active_remove(&arena->large, edata);
}
}
- arena_extent_dalloc_large_prep(tsdn, arena, extent);
+ arena_extent_dalloc_large_prep(tsdn, arena, edata);
}
static void
-large_dalloc_finish_impl(tsdn_t *tsdn, arena_t *arena, extent_t *extent) {
- extent_hooks_t *extent_hooks = EXTENT_HOOKS_INITIALIZER;
- arena_extents_dirty_dalloc(tsdn, arena, &extent_hooks, extent);
+large_dalloc_finish_impl(tsdn_t *tsdn, arena_t *arena, edata_t *edata) {
+ bool deferred_work_generated = false;
+ pa_dalloc(tsdn, &arena->pa_shard, edata, &deferred_work_generated);
+ if (deferred_work_generated) {
+ arena_handle_deferred_work(tsdn, arena);
+ }
}
void
-large_dalloc_prep_junked_locked(tsdn_t *tsdn, extent_t *extent) {
- large_dalloc_prep_impl(tsdn, extent_arena_get(extent), extent, true);
+large_dalloc_prep_locked(tsdn_t *tsdn, edata_t *edata) {
+ large_dalloc_prep_impl(tsdn, arena_get_from_edata(edata), edata, true);
}
void
-large_dalloc_finish(tsdn_t *tsdn, extent_t *extent) {
- large_dalloc_finish_impl(tsdn, extent_arena_get(extent), extent);
+large_dalloc_finish(tsdn_t *tsdn, edata_t *edata) {
+ large_dalloc_finish_impl(tsdn, arena_get_from_edata(edata), edata);
}
void
-large_dalloc(tsdn_t *tsdn, extent_t *extent) {
- arena_t *arena = extent_arena_get(extent);
- large_dalloc_prep_impl(tsdn, arena, extent, false);
- large_dalloc_finish_impl(tsdn, arena, extent);
+large_dalloc(tsdn_t *tsdn, edata_t *edata) {
+ arena_t *arena = arena_get_from_edata(edata);
+ large_dalloc_prep_impl(tsdn, arena, edata, false);
+ large_dalloc_finish_impl(tsdn, arena, edata);
arena_decay_tick(tsdn, arena);
}
size_t
-large_salloc(tsdn_t *tsdn, const extent_t *extent) {
- return extent_usize_get(extent);
-}
-
-prof_tctx_t *
-large_prof_tctx_get(tsdn_t *tsdn, const extent_t *extent) {
- return extent_prof_tctx_get(extent);
+large_salloc(tsdn_t *tsdn, const edata_t *edata) {
+ return edata_usize_get(edata);
}
void
-large_prof_tctx_set(tsdn_t *tsdn, extent_t *extent, prof_tctx_t *tctx) {
- extent_prof_tctx_set(extent, tctx);
+large_prof_info_get(tsd_t *tsd, edata_t *edata, prof_info_t *prof_info,
+ bool reset_recent) {
+ assert(prof_info != NULL);
+
+ prof_tctx_t *alloc_tctx = edata_prof_tctx_get(edata);
+ prof_info->alloc_tctx = alloc_tctx;
+
+ if ((uintptr_t)alloc_tctx > (uintptr_t)1U) {
+ nstime_copy(&prof_info->alloc_time,
+ edata_prof_alloc_time_get(edata));
+ prof_info->alloc_size = edata_prof_alloc_size_get(edata);
+ if (reset_recent) {
+ /*
+ * Reset the pointer on the recent allocation record,
+ * so that this allocation is recorded as released.
+ */
+ prof_recent_alloc_reset(tsd, edata);
+ }
+ }
}
-void
-large_prof_tctx_reset(tsdn_t *tsdn, extent_t *extent) {
- large_prof_tctx_set(tsdn, extent, (prof_tctx_t *)(uintptr_t)1U);
+static void
+large_prof_tctx_set(edata_t *edata, prof_tctx_t *tctx) {
+ edata_prof_tctx_set(edata, tctx);
}
-nstime_t
-large_prof_alloc_time_get(const extent_t *extent) {
- return extent_prof_alloc_time_get(extent);
+void
+large_prof_tctx_reset(edata_t *edata) {
+ large_prof_tctx_set(edata, (prof_tctx_t *)(uintptr_t)1U);
}
void
-large_prof_alloc_time_set(extent_t *extent, nstime_t t) {
- extent_prof_alloc_time_set(extent, t);
+large_prof_info_set(edata_t *edata, prof_tctx_t *tctx, size_t size) {
+ nstime_t t;
+ nstime_prof_init_update(&t);
+ edata_prof_alloc_time_set(edata, &t);
+ edata_prof_alloc_size_set(edata, size);
+ edata_prof_recent_alloc_init(edata);
+ large_prof_tctx_set(edata, tctx);
}
diff --git a/contrib/jemalloc/src/malloc_io.c b/contrib/jemalloc/src/malloc_io.c
index cda589c467c7..93a30497fc95 100644
--- a/contrib/jemalloc/src/malloc_io.c
+++ b/contrib/jemalloc/src/malloc_io.c
@@ -1,4 +1,3 @@
-#define JEMALLOC_MALLOC_IO_C_
#include "jemalloc/internal/jemalloc_preamble.h"
#include "jemalloc/internal/jemalloc_internal_includes.h"
@@ -53,7 +52,6 @@
/******************************************************************************/
/* Function prototypes for non-inline static functions. */
-static void wrtmessage(void *cbopaque, const char *s);
#define U2S_BUFSIZE ((1U << (LG_SIZEOF_INTMAX_T + 3)) + 1)
static char *u2s(uintmax_t x, unsigned base, bool uppercase, char *s,
size_t *slen_p);
@@ -68,7 +66,7 @@ static char *x2s(uintmax_t x, bool alt_form, bool uppercase, char *s,
/******************************************************************************/
/* malloc_message() setup. */
-static void
+void
wrtmessage(void *cbopaque, const char *s) {
malloc_write_fd(STDERR_FILENO, s, strlen(s));
}
@@ -149,10 +147,10 @@ malloc_strtoumax(const char *restrict nptr, char **restrict endptr, int base) {
break;
case '-':
neg = true;
- /* Fall through. */
+ JEMALLOC_FALLTHROUGH;
case '+':
p++;
- /* Fall through. */
+ JEMALLOC_FALLTHROUGH;
default:
goto label_prefix;
}
@@ -303,7 +301,7 @@ d2s(intmax_t x, char sign, char *s, size_t *slen_p) {
if (!neg) {
break;
}
- /* Fall through. */
+ JEMALLOC_FALLTHROUGH;
case ' ':
case '+':
s--;
@@ -337,6 +335,7 @@ x2s(uintmax_t x, bool alt_form, bool uppercase, char *s, size_t *slen_p) {
return s;
}
+JEMALLOC_COLD
size_t
malloc_vsnprintf(char *str, size_t size, const char *format, va_list ap) {
size_t i;
@@ -362,7 +361,11 @@ malloc_vsnprintf(char *str, size_t size, const char *format, va_list ap) {
if (!left_justify && pad_len != 0) { \
size_t j; \
for (j = 0; j < pad_len; j++) { \
- APPEND_C(' '); \
+ if (pad_zero) { \
+ APPEND_C('0'); \
+ } else { \
+ APPEND_C(' '); \
+ } \
} \
} \
/* Value. */ \
@@ -434,6 +437,8 @@ malloc_vsnprintf(char *str, size_t size, const char *format, va_list ap) {
unsigned char len = '?';
char *s;
size_t slen;
+ bool first_width_digit = true;
+ bool pad_zero = false;
f++;
/* Flags. */
@@ -470,7 +475,12 @@ malloc_vsnprintf(char *str, size_t size, const char *format, va_list ap) {
width = -width;
}
break;
- case '0': case '1': case '2': case '3': case '4':
+ case '0':
+ if (first_width_digit) {
+ pad_zero = true;
+ }
+ JEMALLOC_FALLTHROUGH;
+ case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9': {
uintmax_t uwidth;
set_errno(0);
@@ -478,6 +488,7 @@ malloc_vsnprintf(char *str, size_t size, const char *format, va_list ap) {
assert(uwidth != UINTMAX_MAX || get_errno() !=
ERANGE);
width = (int)uwidth;
+ first_width_digit = false;
break;
} default:
break;
@@ -535,6 +546,18 @@ malloc_vsnprintf(char *str, size_t size, const char *format, va_list ap) {
intmax_t val JEMALLOC_CC_SILENCE_INIT(0);
char buf[D2S_BUFSIZE];
+ /*
+ * Outputting negative, zero-padded numbers
+ * would require a nontrivial rework of the
+ * interaction between the width and padding
+ * (since 0 padding goes between the '-' and the
+ * number, while ' ' padding goes either before
+ * the - or after the number. Since we
+ * currently don't ever need 0-padded negative
+ * numbers, just don't bother supporting it.
+ */
+ assert(!pad_zero);
+
GET_ARG_NUMERIC(val, len);
s = d2s(val, (plus_plus ? '+' : (plus_space ?
' ' : '-')), buf, &slen);
@@ -634,8 +657,8 @@ malloc_snprintf(char *str, size_t size, const char *format, ...) {
}
void
-malloc_vcprintf(void (*write_cb)(void *, const char *), void *cbopaque,
- const char *format, va_list ap) {
+malloc_vcprintf(write_cb_t *write_cb, void *cbopaque, const char *format,
+ va_list ap) {
char buf[MALLOC_PRINTF_BUFSIZE];
if (write_cb == NULL) {
@@ -658,8 +681,7 @@ malloc_vcprintf(void (*write_cb)(void *, const char *), void *cbopaque,
*/
JEMALLOC_FORMAT_PRINTF(3, 4)
void
-malloc_cprintf(void (*write_cb)(void *, const char *), void *cbopaque,
- const char *format, ...) {
+malloc_cprintf(write_cb_t *write_cb, void *cbopaque, const char *format, ...) {
va_list ap;
va_start(ap, format);
diff --git a/contrib/jemalloc/src/mutex.c b/contrib/jemalloc/src/mutex.c
index 88a7730c64c1..ceb1d031d7df 100644
--- a/contrib/jemalloc/src/mutex.c
+++ b/contrib/jemalloc/src/mutex.c
@@ -1,4 +1,3 @@
-#define JEMALLOC_MUTEX_C_
#include "jemalloc/internal/jemalloc_preamble.h"
#include "jemalloc/internal/jemalloc_internal_includes.h"
@@ -10,6 +9,12 @@
#define _CRT_SPINCOUNT 4000
#endif
+/*
+ * Based on benchmark results, a fixed spin with this amount of retries works
+ * well for our critical sections.
+ */
+int64_t opt_mutex_max_spin = 600;
+
/******************************************************************************/
/* Data. */
@@ -42,6 +47,7 @@ pthread_create(pthread_t *__restrict thread,
JEMALLOC_EXPORT int _pthread_mutex_init_calloc_cb(pthread_mutex_t *mutex,
void *(calloc_cb)(size_t, size_t));
+#ifndef JEMALLOC_NO_PRIVATE_NAMESPACE
#pragma weak _pthread_mutex_init_calloc_cb
int
_pthread_mutex_init_calloc_cb(pthread_mutex_t *mutex,
@@ -53,17 +59,18 @@ _pthread_mutex_init_calloc_cb(pthread_mutex_t *mutex,
calloc_cb));
}
#endif
+#endif
void
malloc_mutex_lock_slow(malloc_mutex_t *mutex) {
mutex_prof_data_t *data = &mutex->prof_data;
- nstime_t before = NSTIME_ZERO_INITIALIZER;
+ nstime_t before;
if (ncpus == 1) {
goto label_spin_done;
}
- int cnt = 0, max_cnt = MALLOC_MUTEX_MAX_SPIN;
+ int cnt = 0;
do {
spin_cpu_spinwait();
if (!atomic_load_b(&mutex->locked, ATOMIC_RELAXED)
@@ -71,7 +78,7 @@ malloc_mutex_lock_slow(malloc_mutex_t *mutex) {
data->n_spin_acquired++;
return;
}
- } while (cnt++ < max_cnt);
+ } while (cnt++ < opt_mutex_max_spin || opt_mutex_max_spin == -1);
if (!config_stats) {
/* Only spin is useful when stats is off. */
@@ -79,7 +86,7 @@ malloc_mutex_lock_slow(malloc_mutex_t *mutex) {
return;
}
label_spin_done:
- nstime_update(&before);
+ nstime_init_update(&before);
/* Copy before to after to avoid clock skews. */
nstime_t after;
nstime_copy(&after, &before);
@@ -115,8 +122,8 @@ label_spin_done:
static void
mutex_prof_data_init(mutex_prof_data_t *data) {
memset(data, 0, sizeof(mutex_prof_data_t));
- nstime_init(&data->max_wait_time, 0);
- nstime_init(&data->tot_wait_time, 0);
+ nstime_init_zero(&data->max_wait_time);
+ nstime_init_zero(&data->tot_wait_time);
data->prev_owner = NULL;
}
diff --git a/contrib/jemalloc/src/mutex_pool.c b/contrib/jemalloc/src/mutex_pool.c
deleted file mode 100644
index f24d10e44a80..000000000000
--- a/contrib/jemalloc/src/mutex_pool.c
+++ /dev/null
@@ -1,18 +0,0 @@
-#define JEMALLOC_MUTEX_POOL_C_
-
-#include "jemalloc/internal/jemalloc_preamble.h"
-#include "jemalloc/internal/jemalloc_internal_includes.h"
-
-#include "jemalloc/internal/mutex.h"
-#include "jemalloc/internal/mutex_pool.h"
-
-bool
-mutex_pool_init(mutex_pool_t *pool, const char *name, witness_rank_t rank) {
- for (int i = 0; i < MUTEX_POOL_SIZE; ++i) {
- if (malloc_mutex_init(&pool->mutexes[i], name, rank,
- malloc_mutex_address_ordered)) {
- return true;
- }
- }
- return false;
-}
diff --git a/contrib/jemalloc/src/nstime.c b/contrib/jemalloc/src/nstime.c
index 71db353965ff..a1a53777febb 100644
--- a/contrib/jemalloc/src/nstime.c
+++ b/contrib/jemalloc/src/nstime.c
@@ -8,96 +8,169 @@
#define BILLION UINT64_C(1000000000)
#define MILLION UINT64_C(1000000)
+static void
+nstime_set_initialized(nstime_t *time) {
+#ifdef JEMALLOC_DEBUG
+ time->magic = NSTIME_MAGIC;
+#endif
+}
+
+static void
+nstime_assert_initialized(const nstime_t *time) {
+#ifdef JEMALLOC_DEBUG
+ /*
+ * Some parts (e.g. stats) rely on memset to zero initialize. Treat
+ * these as valid initialization.
+ */
+ assert(time->magic == NSTIME_MAGIC ||
+ (time->magic == 0 && time->ns == 0));
+#endif
+}
+
+static void
+nstime_pair_assert_initialized(const nstime_t *t1, const nstime_t *t2) {
+ nstime_assert_initialized(t1);
+ nstime_assert_initialized(t2);
+}
+
+static void
+nstime_initialize_operand(nstime_t *time) {
+ /*
+ * Operations like nstime_add may have the initial operand being zero
+ * initialized (covered by the assert below). Full-initialize needed
+ * before changing it to non-zero.
+ */
+ nstime_assert_initialized(time);
+ nstime_set_initialized(time);
+}
+
void
nstime_init(nstime_t *time, uint64_t ns) {
+ nstime_set_initialized(time);
time->ns = ns;
}
void
nstime_init2(nstime_t *time, uint64_t sec, uint64_t nsec) {
+ nstime_set_initialized(time);
time->ns = sec * BILLION + nsec;
}
uint64_t
nstime_ns(const nstime_t *time) {
+ nstime_assert_initialized(time);
return time->ns;
}
uint64_t
nstime_msec(const nstime_t *time) {
+ nstime_assert_initialized(time);
return time->ns / MILLION;
}
uint64_t
nstime_sec(const nstime_t *time) {
+ nstime_assert_initialized(time);
return time->ns / BILLION;
}
uint64_t
nstime_nsec(const nstime_t *time) {
+ nstime_assert_initialized(time);
return time->ns % BILLION;
}
void
nstime_copy(nstime_t *time, const nstime_t *source) {
+ /* Source is required to be initialized. */
+ nstime_assert_initialized(source);
*time = *source;
+ nstime_assert_initialized(time);
}
int
nstime_compare(const nstime_t *a, const nstime_t *b) {
+ nstime_pair_assert_initialized(a, b);
return (a->ns > b->ns) - (a->ns < b->ns);
}
void
nstime_add(nstime_t *time, const nstime_t *addend) {
+ nstime_pair_assert_initialized(time, addend);
assert(UINT64_MAX - time->ns >= addend->ns);
+ nstime_initialize_operand(time);
time->ns += addend->ns;
}
void
nstime_iadd(nstime_t *time, uint64_t addend) {
+ nstime_assert_initialized(time);
assert(UINT64_MAX - time->ns >= addend);
+ nstime_initialize_operand(time);
time->ns += addend;
}
void
nstime_subtract(nstime_t *time, const nstime_t *subtrahend) {
+ nstime_pair_assert_initialized(time, subtrahend);
assert(nstime_compare(time, subtrahend) >= 0);
+ /* No initialize operand -- subtraction must be initialized. */
time->ns -= subtrahend->ns;
}
void
nstime_isubtract(nstime_t *time, uint64_t subtrahend) {
+ nstime_assert_initialized(time);
assert(time->ns >= subtrahend);
+ /* No initialize operand -- subtraction must be initialized. */
time->ns -= subtrahend;
}
void
nstime_imultiply(nstime_t *time, uint64_t multiplier) {
+ nstime_assert_initialized(time);
assert((((time->ns | multiplier) & (UINT64_MAX << (sizeof(uint64_t) <<
2))) == 0) || ((time->ns * multiplier) / multiplier == time->ns));
+ nstime_initialize_operand(time);
time->ns *= multiplier;
}
void
nstime_idivide(nstime_t *time, uint64_t divisor) {
+ nstime_assert_initialized(time);
assert(divisor != 0);
+ nstime_initialize_operand(time);
time->ns /= divisor;
}
uint64_t
nstime_divide(const nstime_t *time, const nstime_t *divisor) {
+ nstime_pair_assert_initialized(time, divisor);
assert(divisor->ns != 0);
+ /* No initialize operand -- *time itself remains unchanged. */
return time->ns / divisor->ns;
}
+/* Returns time since *past, w/o updating *past. */
+uint64_t
+nstime_ns_since(const nstime_t *past) {
+ nstime_assert_initialized(past);
+
+ nstime_t now;
+ nstime_copy(&now, past);
+ nstime_update(&now);
+
+ assert(nstime_compare(&now, past) >= 0);
+ return now.ns - past->ns;
+}
+
#ifdef _WIN32
# define NSTIME_MONOTONIC true
static void
@@ -152,7 +225,42 @@ nstime_monotonic_impl(void) {
}
nstime_monotonic_t *JET_MUTABLE nstime_monotonic = nstime_monotonic_impl;
-static bool
+prof_time_res_t opt_prof_time_res =
+ prof_time_res_default;
+
+const char *prof_time_res_mode_names[] = {
+ "default",
+ "high",
+};
+
+
+static void
+nstime_get_realtime(nstime_t *time) {
+#if defined(JEMALLOC_HAVE_CLOCK_REALTIME) && !defined(_WIN32)
+ struct timespec ts;
+
+ clock_gettime(CLOCK_REALTIME, &ts);
+ nstime_init2(time, ts.tv_sec, ts.tv_nsec);
+#else
+ unreachable();
+#endif
+}
+
+static void
+nstime_prof_update_impl(nstime_t *time) {
+ nstime_t old_time;
+
+ nstime_copy(&old_time, time);
+
+ if (opt_prof_time_res == prof_time_res_high) {
+ nstime_get_realtime(time);
+ } else {
+ nstime_get(time);
+ }
+}
+nstime_prof_update_t *JET_MUTABLE nstime_prof_update = nstime_prof_update_impl;
+
+static void
nstime_update_impl(nstime_t *time) {
nstime_t old_time;
@@ -162,9 +270,20 @@ nstime_update_impl(nstime_t *time) {
/* Handle non-monotonic clocks. */
if (unlikely(nstime_compare(&old_time, time) > 0)) {
nstime_copy(time, &old_time);
- return true;
}
-
- return false;
}
nstime_update_t *JET_MUTABLE nstime_update = nstime_update_impl;
+
+void
+nstime_init_update(nstime_t *time) {
+ nstime_init_zero(time);
+ nstime_update(time);
+}
+
+void
+nstime_prof_init_update(nstime_t *time) {
+ nstime_init_zero(time);
+ nstime_prof_update(time);
+}
+
+
diff --git a/contrib/jemalloc/src/pa.c b/contrib/jemalloc/src/pa.c
new file mode 100644
index 000000000000..eb7e4620ea53
--- /dev/null
+++ b/contrib/jemalloc/src/pa.c
@@ -0,0 +1,277 @@
+#include "jemalloc/internal/jemalloc_preamble.h"
+#include "jemalloc/internal/jemalloc_internal_includes.h"
+
+#include "jemalloc/internal/san.h"
+#include "jemalloc/internal/hpa.h"
+
+static void
+pa_nactive_add(pa_shard_t *shard, size_t add_pages) {
+ atomic_fetch_add_zu(&shard->nactive, add_pages, ATOMIC_RELAXED);
+}
+
+static void
+pa_nactive_sub(pa_shard_t *shard, size_t sub_pages) {
+ assert(atomic_load_zu(&shard->nactive, ATOMIC_RELAXED) >= sub_pages);
+ atomic_fetch_sub_zu(&shard->nactive, sub_pages, ATOMIC_RELAXED);
+}
+
+bool
+pa_central_init(pa_central_t *central, base_t *base, bool hpa,
+ hpa_hooks_t *hpa_hooks) {
+ bool err;
+ if (hpa) {
+ err = hpa_central_init(&central->hpa, base, hpa_hooks);
+ if (err) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool
+pa_shard_init(tsdn_t *tsdn, pa_shard_t *shard, pa_central_t *central,
+ emap_t *emap, base_t *base, unsigned ind, pa_shard_stats_t *stats,
+ malloc_mutex_t *stats_mtx, nstime_t *cur_time,
+ size_t pac_oversize_threshold, ssize_t dirty_decay_ms,
+ ssize_t muzzy_decay_ms) {
+ /* This will change eventually, but for now it should hold. */
+ assert(base_ind_get(base) == ind);
+ if (edata_cache_init(&shard->edata_cache, base)) {
+ return true;
+ }
+
+ if (pac_init(tsdn, &shard->pac, base, emap, &shard->edata_cache,
+ cur_time, pac_oversize_threshold, dirty_decay_ms, muzzy_decay_ms,
+ &stats->pac_stats, stats_mtx)) {
+ return true;
+ }
+
+ shard->ind = ind;
+
+ shard->ever_used_hpa = false;
+ atomic_store_b(&shard->use_hpa, false, ATOMIC_RELAXED);
+
+ atomic_store_zu(&shard->nactive, 0, ATOMIC_RELAXED);
+
+ shard->stats_mtx = stats_mtx;
+ shard->stats = stats;
+ memset(shard->stats, 0, sizeof(*shard->stats));
+
+ shard->central = central;
+ shard->emap = emap;
+ shard->base = base;
+
+ return false;
+}
+
+bool
+pa_shard_enable_hpa(tsdn_t *tsdn, pa_shard_t *shard,
+ const hpa_shard_opts_t *hpa_opts, const sec_opts_t *hpa_sec_opts) {
+ if (hpa_shard_init(&shard->hpa_shard, &shard->central->hpa, shard->emap,
+ shard->base, &shard->edata_cache, shard->ind, hpa_opts)) {
+ return true;
+ }
+ if (sec_init(tsdn, &shard->hpa_sec, shard->base, &shard->hpa_shard.pai,
+ hpa_sec_opts)) {
+ return true;
+ }
+ shard->ever_used_hpa = true;
+ atomic_store_b(&shard->use_hpa, true, ATOMIC_RELAXED);
+
+ return false;
+}
+
+void
+pa_shard_disable_hpa(tsdn_t *tsdn, pa_shard_t *shard) {
+ atomic_store_b(&shard->use_hpa, false, ATOMIC_RELAXED);
+ if (shard->ever_used_hpa) {
+ sec_disable(tsdn, &shard->hpa_sec);
+ hpa_shard_disable(tsdn, &shard->hpa_shard);
+ }
+}
+
+void
+pa_shard_reset(tsdn_t *tsdn, pa_shard_t *shard) {
+ atomic_store_zu(&shard->nactive, 0, ATOMIC_RELAXED);
+ if (shard->ever_used_hpa) {
+ sec_flush(tsdn, &shard->hpa_sec);
+ }
+}
+
+static bool
+pa_shard_uses_hpa(pa_shard_t *shard) {
+ return atomic_load_b(&shard->use_hpa, ATOMIC_RELAXED);
+}
+
+void
+pa_shard_destroy(tsdn_t *tsdn, pa_shard_t *shard) {
+ pac_destroy(tsdn, &shard->pac);
+ if (shard->ever_used_hpa) {
+ sec_flush(tsdn, &shard->hpa_sec);
+ hpa_shard_disable(tsdn, &shard->hpa_shard);
+ }
+}
+
+static pai_t *
+pa_get_pai(pa_shard_t *shard, edata_t *edata) {
+ return (edata_pai_get(edata) == EXTENT_PAI_PAC
+ ? &shard->pac.pai : &shard->hpa_sec.pai);
+}
+
+edata_t *
+pa_alloc(tsdn_t *tsdn, pa_shard_t *shard, size_t size, size_t alignment,
+ bool slab, szind_t szind, bool zero, bool guarded,
+ bool *deferred_work_generated) {
+ witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
+ WITNESS_RANK_CORE, 0);
+ assert(!guarded || alignment <= PAGE);
+
+ edata_t *edata = NULL;
+ if (!guarded && pa_shard_uses_hpa(shard)) {
+ edata = pai_alloc(tsdn, &shard->hpa_sec.pai, size, alignment,
+ zero, /* guarded */ false, slab, deferred_work_generated);
+ }
+ /*
+ * Fall back to the PAC if the HPA is off or couldn't serve the given
+ * allocation request.
+ */
+ if (edata == NULL) {
+ edata = pai_alloc(tsdn, &shard->pac.pai, size, alignment, zero,
+ guarded, slab, deferred_work_generated);
+ }
+ if (edata != NULL) {
+ assert(edata_size_get(edata) == size);
+ pa_nactive_add(shard, size >> LG_PAGE);
+ emap_remap(tsdn, shard->emap, edata, szind, slab);
+ edata_szind_set(edata, szind);
+ edata_slab_set(edata, slab);
+ if (slab && (size > 2 * PAGE)) {
+ emap_register_interior(tsdn, shard->emap, edata, szind);
+ }
+ assert(edata_arena_ind_get(edata) == shard->ind);
+ }
+ return edata;
+}
+
+bool
+pa_expand(tsdn_t *tsdn, pa_shard_t *shard, edata_t *edata, size_t old_size,
+ size_t new_size, szind_t szind, bool zero, bool *deferred_work_generated) {
+ assert(new_size > old_size);
+ assert(edata_size_get(edata) == old_size);
+ assert((new_size & PAGE_MASK) == 0);
+ if (edata_guarded_get(edata)) {
+ return true;
+ }
+ size_t expand_amount = new_size - old_size;
+
+ pai_t *pai = pa_get_pai(shard, edata);
+
+ bool error = pai_expand(tsdn, pai, edata, old_size, new_size, zero,
+ deferred_work_generated);
+ if (error) {
+ return true;
+ }
+
+ pa_nactive_add(shard, expand_amount >> LG_PAGE);
+ edata_szind_set(edata, szind);
+ emap_remap(tsdn, shard->emap, edata, szind, /* slab */ false);
+ return false;
+}
+
+bool
+pa_shrink(tsdn_t *tsdn, pa_shard_t *shard, edata_t *edata, size_t old_size,
+ size_t new_size, szind_t szind, bool *deferred_work_generated) {
+ assert(new_size < old_size);
+ assert(edata_size_get(edata) == old_size);
+ assert((new_size & PAGE_MASK) == 0);
+ if (edata_guarded_get(edata)) {
+ return true;
+ }
+ size_t shrink_amount = old_size - new_size;
+
+ pai_t *pai = pa_get_pai(shard, edata);
+ bool error = pai_shrink(tsdn, pai, edata, old_size, new_size,
+ deferred_work_generated);
+ if (error) {
+ return true;
+ }
+ pa_nactive_sub(shard, shrink_amount >> LG_PAGE);
+
+ edata_szind_set(edata, szind);
+ emap_remap(tsdn, shard->emap, edata, szind, /* slab */ false);
+ return false;
+}
+
+void
+pa_dalloc(tsdn_t *tsdn, pa_shard_t *shard, edata_t *edata,
+ bool *deferred_work_generated) {
+ emap_remap(tsdn, shard->emap, edata, SC_NSIZES, /* slab */ false);
+ if (edata_slab_get(edata)) {
+ emap_deregister_interior(tsdn, shard->emap, edata);
+ /*
+ * The slab state of the extent isn't cleared. It may be used
+ * by the pai implementation, e.g. to make caching decisions.
+ */
+ }
+ edata_addr_set(edata, edata_base_get(edata));
+ edata_szind_set(edata, SC_NSIZES);
+ pa_nactive_sub(shard, edata_size_get(edata) >> LG_PAGE);
+ pai_t *pai = pa_get_pai(shard, edata);
+ pai_dalloc(tsdn, pai, edata, deferred_work_generated);
+}
+
+bool
+pa_shard_retain_grow_limit_get_set(tsdn_t *tsdn, pa_shard_t *shard,
+ size_t *old_limit, size_t *new_limit) {
+ return pac_retain_grow_limit_get_set(tsdn, &shard->pac, old_limit,
+ new_limit);
+}
+
+bool
+pa_decay_ms_set(tsdn_t *tsdn, pa_shard_t *shard, extent_state_t state,
+ ssize_t decay_ms, pac_purge_eagerness_t eagerness) {
+ return pac_decay_ms_set(tsdn, &shard->pac, state, decay_ms, eagerness);
+}
+
+ssize_t
+pa_decay_ms_get(pa_shard_t *shard, extent_state_t state) {
+ return pac_decay_ms_get(&shard->pac, state);
+}
+
+void
+pa_shard_set_deferral_allowed(tsdn_t *tsdn, pa_shard_t *shard,
+ bool deferral_allowed) {
+ if (pa_shard_uses_hpa(shard)) {
+ hpa_shard_set_deferral_allowed(tsdn, &shard->hpa_shard,
+ deferral_allowed);
+ }
+}
+
+void
+pa_shard_do_deferred_work(tsdn_t *tsdn, pa_shard_t *shard) {
+ if (pa_shard_uses_hpa(shard)) {
+ hpa_shard_do_deferred_work(tsdn, &shard->hpa_shard);
+ }
+}
+
+/*
+ * Get time until next deferred work ought to happen. If there are multiple
+ * things that have been deferred, this function calculates the time until
+ * the soonest of those things.
+ */
+uint64_t
+pa_shard_time_until_deferred_work(tsdn_t *tsdn, pa_shard_t *shard) {
+ uint64_t time = pai_time_until_deferred_work(tsdn, &shard->pac.pai);
+ if (time == BACKGROUND_THREAD_DEFERRED_MIN) {
+ return time;
+ }
+
+ if (pa_shard_uses_hpa(shard)) {
+ uint64_t hpa =
+ pai_time_until_deferred_work(tsdn, &shard->hpa_shard.pai);
+ if (hpa < time) {
+ time = hpa;
+ }
+ }
+ return time;
+}
diff --git a/contrib/jemalloc/src/pa_extra.c b/contrib/jemalloc/src/pa_extra.c
new file mode 100644
index 000000000000..0f488be69c5d
--- /dev/null
+++ b/contrib/jemalloc/src/pa_extra.c
@@ -0,0 +1,191 @@
+#include "jemalloc/internal/jemalloc_preamble.h"
+#include "jemalloc/internal/jemalloc_internal_includes.h"
+
+/*
+ * This file is logically part of the PA module. While pa.c contains the core
+ * allocator functionality, this file contains boring integration functionality;
+ * things like the pre- and post- fork handlers, and stats merging for CTL
+ * refreshes.
+ */
+
+void
+pa_shard_prefork0(tsdn_t *tsdn, pa_shard_t *shard) {
+ malloc_mutex_prefork(tsdn, &shard->pac.decay_dirty.mtx);
+ malloc_mutex_prefork(tsdn, &shard->pac.decay_muzzy.mtx);
+}
+
+void
+pa_shard_prefork2(tsdn_t *tsdn, pa_shard_t *shard) {
+ if (shard->ever_used_hpa) {
+ sec_prefork2(tsdn, &shard->hpa_sec);
+ }
+}
+
+void
+pa_shard_prefork3(tsdn_t *tsdn, pa_shard_t *shard) {
+ malloc_mutex_prefork(tsdn, &shard->pac.grow_mtx);
+ if (shard->ever_used_hpa) {
+ hpa_shard_prefork3(tsdn, &shard->hpa_shard);
+ }
+}
+
+void
+pa_shard_prefork4(tsdn_t *tsdn, pa_shard_t *shard) {
+ ecache_prefork(tsdn, &shard->pac.ecache_dirty);
+ ecache_prefork(tsdn, &shard->pac.ecache_muzzy);
+ ecache_prefork(tsdn, &shard->pac.ecache_retained);
+ if (shard->ever_used_hpa) {
+ hpa_shard_prefork4(tsdn, &shard->hpa_shard);
+ }
+}
+
+void
+pa_shard_prefork5(tsdn_t *tsdn, pa_shard_t *shard) {
+ edata_cache_prefork(tsdn, &shard->edata_cache);
+}
+
+void
+pa_shard_postfork_parent(tsdn_t *tsdn, pa_shard_t *shard) {
+ edata_cache_postfork_parent(tsdn, &shard->edata_cache);
+ ecache_postfork_parent(tsdn, &shard->pac.ecache_dirty);
+ ecache_postfork_parent(tsdn, &shard->pac.ecache_muzzy);
+ ecache_postfork_parent(tsdn, &shard->pac.ecache_retained);
+ malloc_mutex_postfork_parent(tsdn, &shard->pac.grow_mtx);
+ malloc_mutex_postfork_parent(tsdn, &shard->pac.decay_dirty.mtx);
+ malloc_mutex_postfork_parent(tsdn, &shard->pac.decay_muzzy.mtx);
+ if (shard->ever_used_hpa) {
+ sec_postfork_parent(tsdn, &shard->hpa_sec);
+ hpa_shard_postfork_parent(tsdn, &shard->hpa_shard);
+ }
+}
+
+void
+pa_shard_postfork_child(tsdn_t *tsdn, pa_shard_t *shard) {
+ edata_cache_postfork_child(tsdn, &shard->edata_cache);
+ ecache_postfork_child(tsdn, &shard->pac.ecache_dirty);
+ ecache_postfork_child(tsdn, &shard->pac.ecache_muzzy);
+ ecache_postfork_child(tsdn, &shard->pac.ecache_retained);
+ malloc_mutex_postfork_child(tsdn, &shard->pac.grow_mtx);
+ malloc_mutex_postfork_child(tsdn, &shard->pac.decay_dirty.mtx);
+ malloc_mutex_postfork_child(tsdn, &shard->pac.decay_muzzy.mtx);
+ if (shard->ever_used_hpa) {
+ sec_postfork_child(tsdn, &shard->hpa_sec);
+ hpa_shard_postfork_child(tsdn, &shard->hpa_shard);
+ }
+}
+
+void
+pa_shard_basic_stats_merge(pa_shard_t *shard, size_t *nactive, size_t *ndirty,
+ size_t *nmuzzy) {
+ *nactive += atomic_load_zu(&shard->nactive, ATOMIC_RELAXED);
+ *ndirty += ecache_npages_get(&shard->pac.ecache_dirty);
+ *nmuzzy += ecache_npages_get(&shard->pac.ecache_muzzy);
+}
+
+void
+pa_shard_stats_merge(tsdn_t *tsdn, pa_shard_t *shard,
+ pa_shard_stats_t *pa_shard_stats_out, pac_estats_t *estats_out,
+ hpa_shard_stats_t *hpa_stats_out, sec_stats_t *sec_stats_out,
+ size_t *resident) {
+ cassert(config_stats);
+
+ pa_shard_stats_out->pac_stats.retained +=
+ ecache_npages_get(&shard->pac.ecache_retained) << LG_PAGE;
+ pa_shard_stats_out->edata_avail += atomic_load_zu(
+ &shard->edata_cache.count, ATOMIC_RELAXED);
+
+ size_t resident_pgs = 0;
+ resident_pgs += atomic_load_zu(&shard->nactive, ATOMIC_RELAXED);
+ resident_pgs += ecache_npages_get(&shard->pac.ecache_dirty);
+ *resident += (resident_pgs << LG_PAGE);
+
+ /* Dirty decay stats */
+ locked_inc_u64_unsynchronized(
+ &pa_shard_stats_out->pac_stats.decay_dirty.npurge,
+ locked_read_u64(tsdn, LOCKEDINT_MTX(*shard->stats_mtx),
+ &shard->pac.stats->decay_dirty.npurge));
+ locked_inc_u64_unsynchronized(
+ &pa_shard_stats_out->pac_stats.decay_dirty.nmadvise,
+ locked_read_u64(tsdn, LOCKEDINT_MTX(*shard->stats_mtx),
+ &shard->pac.stats->decay_dirty.nmadvise));
+ locked_inc_u64_unsynchronized(
+ &pa_shard_stats_out->pac_stats.decay_dirty.purged,
+ locked_read_u64(tsdn, LOCKEDINT_MTX(*shard->stats_mtx),
+ &shard->pac.stats->decay_dirty.purged));
+
+ /* Muzzy decay stats */
+ locked_inc_u64_unsynchronized(
+ &pa_shard_stats_out->pac_stats.decay_muzzy.npurge,
+ locked_read_u64(tsdn, LOCKEDINT_MTX(*shard->stats_mtx),
+ &shard->pac.stats->decay_muzzy.npurge));
+ locked_inc_u64_unsynchronized(
+ &pa_shard_stats_out->pac_stats.decay_muzzy.nmadvise,
+ locked_read_u64(tsdn, LOCKEDINT_MTX(*shard->stats_mtx),
+ &shard->pac.stats->decay_muzzy.nmadvise));
+ locked_inc_u64_unsynchronized(
+ &pa_shard_stats_out->pac_stats.decay_muzzy.purged,
+ locked_read_u64(tsdn, LOCKEDINT_MTX(*shard->stats_mtx),
+ &shard->pac.stats->decay_muzzy.purged));
+
+ atomic_load_add_store_zu(&pa_shard_stats_out->pac_stats.abandoned_vm,
+ atomic_load_zu(&shard->pac.stats->abandoned_vm, ATOMIC_RELAXED));
+
+ for (pszind_t i = 0; i < SC_NPSIZES; i++) {
+ size_t dirty, muzzy, retained, dirty_bytes, muzzy_bytes,
+ retained_bytes;
+ dirty = ecache_nextents_get(&shard->pac.ecache_dirty, i);
+ muzzy = ecache_nextents_get(&shard->pac.ecache_muzzy, i);
+ retained = ecache_nextents_get(&shard->pac.ecache_retained, i);
+ dirty_bytes = ecache_nbytes_get(&shard->pac.ecache_dirty, i);
+ muzzy_bytes = ecache_nbytes_get(&shard->pac.ecache_muzzy, i);
+ retained_bytes = ecache_nbytes_get(&shard->pac.ecache_retained,
+ i);
+
+ estats_out[i].ndirty = dirty;
+ estats_out[i].nmuzzy = muzzy;
+ estats_out[i].nretained = retained;
+ estats_out[i].dirty_bytes = dirty_bytes;
+ estats_out[i].muzzy_bytes = muzzy_bytes;
+ estats_out[i].retained_bytes = retained_bytes;
+ }
+
+ if (shard->ever_used_hpa) {
+ hpa_shard_stats_merge(tsdn, &shard->hpa_shard, hpa_stats_out);
+ sec_stats_merge(tsdn, &shard->hpa_sec, sec_stats_out);
+ }
+}
+
+static void
+pa_shard_mtx_stats_read_single(tsdn_t *tsdn, mutex_prof_data_t *mutex_prof_data,
+ malloc_mutex_t *mtx, int ind) {
+ malloc_mutex_lock(tsdn, mtx);
+ malloc_mutex_prof_read(tsdn, &mutex_prof_data[ind], mtx);
+ malloc_mutex_unlock(tsdn, mtx);
+}
+
+void
+pa_shard_mtx_stats_read(tsdn_t *tsdn, pa_shard_t *shard,
+ mutex_prof_data_t mutex_prof_data[mutex_prof_num_arena_mutexes]) {
+ pa_shard_mtx_stats_read_single(tsdn, mutex_prof_data,
+ &shard->edata_cache.mtx, arena_prof_mutex_extent_avail);
+ pa_shard_mtx_stats_read_single(tsdn, mutex_prof_data,
+ &shard->pac.ecache_dirty.mtx, arena_prof_mutex_extents_dirty);
+ pa_shard_mtx_stats_read_single(tsdn, mutex_prof_data,
+ &shard->pac.ecache_muzzy.mtx, arena_prof_mutex_extents_muzzy);
+ pa_shard_mtx_stats_read_single(tsdn, mutex_prof_data,
+ &shard->pac.ecache_retained.mtx, arena_prof_mutex_extents_retained);
+ pa_shard_mtx_stats_read_single(tsdn, mutex_prof_data,
+ &shard->pac.decay_dirty.mtx, arena_prof_mutex_decay_dirty);
+ pa_shard_mtx_stats_read_single(tsdn, mutex_prof_data,
+ &shard->pac.decay_muzzy.mtx, arena_prof_mutex_decay_muzzy);
+
+ if (shard->ever_used_hpa) {
+ pa_shard_mtx_stats_read_single(tsdn, mutex_prof_data,
+ &shard->hpa_shard.mtx, arena_prof_mutex_hpa_shard);
+ pa_shard_mtx_stats_read_single(tsdn, mutex_prof_data,
+ &shard->hpa_shard.grow_mtx,
+ arena_prof_mutex_hpa_shard_grow);
+ sec_mutex_stats_read(tsdn, &shard->hpa_sec,
+ &mutex_prof_data[arena_prof_mutex_hpa_sec]);
+ }
+}
diff --git a/contrib/jemalloc/src/pac.c b/contrib/jemalloc/src/pac.c
new file mode 100644
index 000000000000..53e3d823758e
--- /dev/null
+++ b/contrib/jemalloc/src/pac.c
@@ -0,0 +1,587 @@
+#include "jemalloc/internal/jemalloc_preamble.h"
+#include "jemalloc/internal/jemalloc_internal_includes.h"
+
+#include "jemalloc/internal/pac.h"
+#include "jemalloc/internal/san.h"
+
+static edata_t *pac_alloc_impl(tsdn_t *tsdn, pai_t *self, size_t size,
+ size_t alignment, bool zero, bool guarded, bool frequent_reuse,
+ bool *deferred_work_generated);
+static bool pac_expand_impl(tsdn_t *tsdn, pai_t *self, edata_t *edata,
+ size_t old_size, size_t new_size, bool zero, bool *deferred_work_generated);
+static bool pac_shrink_impl(tsdn_t *tsdn, pai_t *self, edata_t *edata,
+ size_t old_size, size_t new_size, bool *deferred_work_generated);
+static void pac_dalloc_impl(tsdn_t *tsdn, pai_t *self, edata_t *edata,
+ bool *deferred_work_generated);
+static uint64_t pac_time_until_deferred_work(tsdn_t *tsdn, pai_t *self);
+
+static inline void
+pac_decay_data_get(pac_t *pac, extent_state_t state,
+ decay_t **r_decay, pac_decay_stats_t **r_decay_stats, ecache_t **r_ecache) {
+ switch(state) {
+ case extent_state_dirty:
+ *r_decay = &pac->decay_dirty;
+ *r_decay_stats = &pac->stats->decay_dirty;
+ *r_ecache = &pac->ecache_dirty;
+ return;
+ case extent_state_muzzy:
+ *r_decay = &pac->decay_muzzy;
+ *r_decay_stats = &pac->stats->decay_muzzy;
+ *r_ecache = &pac->ecache_muzzy;
+ return;
+ default:
+ unreachable();
+ }
+}
+
+bool
+pac_init(tsdn_t *tsdn, pac_t *pac, base_t *base, emap_t *emap,
+ edata_cache_t *edata_cache, nstime_t *cur_time,
+ size_t pac_oversize_threshold, ssize_t dirty_decay_ms,
+ ssize_t muzzy_decay_ms, pac_stats_t *pac_stats, malloc_mutex_t *stats_mtx) {
+ unsigned ind = base_ind_get(base);
+ /*
+ * Delay coalescing for dirty extents despite the disruptive effect on
+ * memory layout for best-fit extent allocation, since cached extents
+ * are likely to be reused soon after deallocation, and the cost of
+ * merging/splitting extents is non-trivial.
+ */
+ if (ecache_init(tsdn, &pac->ecache_dirty, extent_state_dirty, ind,
+ /* delay_coalesce */ true)) {
+ return true;
+ }
+ /*
+ * Coalesce muzzy extents immediately, because operations on them are in
+ * the critical path much less often than for dirty extents.
+ */
+ if (ecache_init(tsdn, &pac->ecache_muzzy, extent_state_muzzy, ind,
+ /* delay_coalesce */ false)) {
+ return true;
+ }
+ /*
+ * Coalesce retained extents immediately, in part because they will
+ * never be evicted (and therefore there's no opportunity for delayed
+ * coalescing), but also because operations on retained extents are not
+ * in the critical path.
+ */
+ if (ecache_init(tsdn, &pac->ecache_retained, extent_state_retained,
+ ind, /* delay_coalesce */ false)) {
+ return true;
+ }
+ exp_grow_init(&pac->exp_grow);
+ if (malloc_mutex_init(&pac->grow_mtx, "extent_grow",
+ WITNESS_RANK_EXTENT_GROW, malloc_mutex_rank_exclusive)) {
+ return true;
+ }
+ atomic_store_zu(&pac->oversize_threshold, pac_oversize_threshold,
+ ATOMIC_RELAXED);
+ if (decay_init(&pac->decay_dirty, cur_time, dirty_decay_ms)) {
+ return true;
+ }
+ if (decay_init(&pac->decay_muzzy, cur_time, muzzy_decay_ms)) {
+ return true;
+ }
+ if (san_bump_alloc_init(&pac->sba)) {
+ return true;
+ }
+
+ pac->base = base;
+ pac->emap = emap;
+ pac->edata_cache = edata_cache;
+ pac->stats = pac_stats;
+ pac->stats_mtx = stats_mtx;
+ atomic_store_zu(&pac->extent_sn_next, 0, ATOMIC_RELAXED);
+
+ pac->pai.alloc = &pac_alloc_impl;
+ pac->pai.alloc_batch = &pai_alloc_batch_default;
+ pac->pai.expand = &pac_expand_impl;
+ pac->pai.shrink = &pac_shrink_impl;
+ pac->pai.dalloc = &pac_dalloc_impl;
+ pac->pai.dalloc_batch = &pai_dalloc_batch_default;
+ pac->pai.time_until_deferred_work = &pac_time_until_deferred_work;
+
+ return false;
+}
+
+static inline bool
+pac_may_have_muzzy(pac_t *pac) {
+ return pac_decay_ms_get(pac, extent_state_muzzy) != 0;
+}
+
+static edata_t *
+pac_alloc_real(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, size_t size,
+ size_t alignment, bool zero, bool guarded) {
+ assert(!guarded || alignment <= PAGE);
+
+ edata_t *edata = ecache_alloc(tsdn, pac, ehooks, &pac->ecache_dirty,
+ NULL, size, alignment, zero, guarded);
+
+ if (edata == NULL && pac_may_have_muzzy(pac)) {
+ edata = ecache_alloc(tsdn, pac, ehooks, &pac->ecache_muzzy,
+ NULL, size, alignment, zero, guarded);
+ }
+ if (edata == NULL) {
+ edata = ecache_alloc_grow(tsdn, pac, ehooks,
+ &pac->ecache_retained, NULL, size, alignment, zero,
+ guarded);
+ if (config_stats && edata != NULL) {
+ atomic_fetch_add_zu(&pac->stats->pac_mapped, size,
+ ATOMIC_RELAXED);
+ }
+ }
+
+ return edata;
+}
+
+static edata_t *
+pac_alloc_new_guarded(tsdn_t *tsdn, pac_t *pac, ehooks_t *ehooks, size_t size,
+ size_t alignment, bool zero, bool frequent_reuse) {
+ assert(alignment <= PAGE);
+
+ edata_t *edata;
+ if (san_bump_enabled() && frequent_reuse) {
+ edata = san_bump_alloc(tsdn, &pac->sba, pac, ehooks, size,
+ zero);
+ } else {
+ size_t size_with_guards = san_two_side_guarded_sz(size);
+ /* Alloc a non-guarded extent first.*/
+ edata = pac_alloc_real(tsdn, pac, ehooks, size_with_guards,
+ /* alignment */ PAGE, zero, /* guarded */ false);
+ if (edata != NULL) {
+ /* Add guards around it. */
+ assert(edata_size_get(edata) == size_with_guards);
+ san_guard_pages_two_sided(tsdn, ehooks, edata,
+ pac->emap, true);
+ }
+ }
+ assert(edata == NULL || (edata_guarded_get(edata) &&
+ edata_size_get(edata) == size));
+
+ return edata;
+}
+
+static edata_t *
+pac_alloc_impl(tsdn_t *tsdn, pai_t *self, size_t size, size_t alignment,
+ bool zero, bool guarded, bool frequent_reuse,
+ bool *deferred_work_generated) {
+ pac_t *pac = (pac_t *)self;
+ ehooks_t *ehooks = pac_ehooks_get(pac);
+
+ edata_t *edata = NULL;
+ /*
+ * The condition is an optimization - not frequently reused guarded
+ * allocations are never put in the ecache. pac_alloc_real also
+ * doesn't grow retained for guarded allocations. So pac_alloc_real
+ * for such allocations would always return NULL.
+ * */
+ if (!guarded || frequent_reuse) {
+ edata = pac_alloc_real(tsdn, pac, ehooks, size, alignment,
+ zero, guarded);
+ }
+ if (edata == NULL && guarded) {
+ /* No cached guarded extents; creating a new one. */
+ edata = pac_alloc_new_guarded(tsdn, pac, ehooks, size,
+ alignment, zero, frequent_reuse);
+ }
+
+ return edata;
+}
+
+static bool
+pac_expand_impl(tsdn_t *tsdn, pai_t *self, edata_t *edata, size_t old_size,
+ size_t new_size, bool zero, bool *deferred_work_generated) {
+ pac_t *pac = (pac_t *)self;
+ ehooks_t *ehooks = pac_ehooks_get(pac);
+
+ size_t mapped_add = 0;
+ size_t expand_amount = new_size - old_size;
+
+ if (ehooks_merge_will_fail(ehooks)) {
+ return true;
+ }
+ edata_t *trail = ecache_alloc(tsdn, pac, ehooks, &pac->ecache_dirty,
+ edata, expand_amount, PAGE, zero, /* guarded*/ false);
+ if (trail == NULL) {
+ trail = ecache_alloc(tsdn, pac, ehooks, &pac->ecache_muzzy,
+ edata, expand_amount, PAGE, zero, /* guarded*/ false);
+ }
+ if (trail == NULL) {
+ trail = ecache_alloc_grow(tsdn, pac, ehooks,
+ &pac->ecache_retained, edata, expand_amount, PAGE, zero,
+ /* guarded */ false);
+ mapped_add = expand_amount;
+ }
+ if (trail == NULL) {
+ return true;
+ }
+ if (extent_merge_wrapper(tsdn, pac, ehooks, edata, trail)) {
+ extent_dalloc_wrapper(tsdn, pac, ehooks, trail);
+ return true;
+ }
+ if (config_stats && mapped_add > 0) {
+ atomic_fetch_add_zu(&pac->stats->pac_mapped, mapped_add,
+ ATOMIC_RELAXED);
+ }
+ return false;
+}
+
+static bool
+pac_shrink_impl(tsdn_t *tsdn, pai_t *self, edata_t *edata, size_t old_size,
+ size_t new_size, bool *deferred_work_generated) {
+ pac_t *pac = (pac_t *)self;
+ ehooks_t *ehooks = pac_ehooks_get(pac);
+
+ size_t shrink_amount = old_size - new_size;
+
+ if (ehooks_split_will_fail(ehooks)) {
+ return true;
+ }
+
+ edata_t *trail = extent_split_wrapper(tsdn, pac, ehooks, edata,
+ new_size, shrink_amount, /* holding_core_locks */ false);
+ if (trail == NULL) {
+ return true;
+ }
+ ecache_dalloc(tsdn, pac, ehooks, &pac->ecache_dirty, trail);
+ *deferred_work_generated = true;
+ return false;
+}
+
+static void
+pac_dalloc_impl(tsdn_t *tsdn, pai_t *self, edata_t *edata,
+ bool *deferred_work_generated) {
+ pac_t *pac = (pac_t *)self;
+ ehooks_t *ehooks = pac_ehooks_get(pac);
+
+ if (edata_guarded_get(edata)) {
+ /*
+ * Because cached guarded extents do exact fit only, large
+ * guarded extents are restored on dalloc eagerly (otherwise
+ * they will not be reused efficiently). Slab sizes have a
+ * limited number of size classes, and tend to cycle faster.
+ *
+ * In the case where coalesce is restrained (VirtualFree on
+ * Windows), guarded extents are also not cached -- otherwise
+ * during arena destroy / reset, the retained extents would not
+ * be whole regions (i.e. they are split between regular and
+ * guarded).
+ */
+ if (!edata_slab_get(edata) || !maps_coalesce) {
+ assert(edata_size_get(edata) >= SC_LARGE_MINCLASS ||
+ !maps_coalesce);
+ san_unguard_pages_two_sided(tsdn, ehooks, edata,
+ pac->emap);
+ }
+ }
+
+ ecache_dalloc(tsdn, pac, ehooks, &pac->ecache_dirty, edata);
+ /* Purging of deallocated pages is deferred */
+ *deferred_work_generated = true;
+}
+
+static inline uint64_t
+pac_ns_until_purge(tsdn_t *tsdn, decay_t *decay, size_t npages) {
+ if (malloc_mutex_trylock(tsdn, &decay->mtx)) {
+ /* Use minimal interval if decay is contended. */
+ return BACKGROUND_THREAD_DEFERRED_MIN;
+ }
+ uint64_t result = decay_ns_until_purge(decay, npages,
+ ARENA_DEFERRED_PURGE_NPAGES_THRESHOLD);
+
+ malloc_mutex_unlock(tsdn, &decay->mtx);
+ return result;
+}
+
+static uint64_t
+pac_time_until_deferred_work(tsdn_t *tsdn, pai_t *self) {
+ uint64_t time;
+ pac_t *pac = (pac_t *)self;
+
+ time = pac_ns_until_purge(tsdn,
+ &pac->decay_dirty,
+ ecache_npages_get(&pac->ecache_dirty));
+ if (time == BACKGROUND_THREAD_DEFERRED_MIN) {
+ return time;
+ }
+
+ uint64_t muzzy = pac_ns_until_purge(tsdn,
+ &pac->decay_muzzy,
+ ecache_npages_get(&pac->ecache_muzzy));
+ if (muzzy < time) {
+ time = muzzy;
+ }
+ return time;
+}
+
+bool
+pac_retain_grow_limit_get_set(tsdn_t *tsdn, pac_t *pac, size_t *old_limit,
+ size_t *new_limit) {
+ pszind_t new_ind JEMALLOC_CC_SILENCE_INIT(0);
+ if (new_limit != NULL) {
+ size_t limit = *new_limit;
+ /* Grow no more than the new limit. */
+ if ((new_ind = sz_psz2ind(limit + 1) - 1) >= SC_NPSIZES) {
+ return true;
+ }
+ }
+
+ malloc_mutex_lock(tsdn, &pac->grow_mtx);
+ if (old_limit != NULL) {
+ *old_limit = sz_pind2sz(pac->exp_grow.limit);
+ }
+ if (new_limit != NULL) {
+ pac->exp_grow.limit = new_ind;
+ }
+ malloc_mutex_unlock(tsdn, &pac->grow_mtx);
+
+ return false;
+}
+
+static size_t
+pac_stash_decayed(tsdn_t *tsdn, pac_t *pac, ecache_t *ecache,
+ size_t npages_limit, size_t npages_decay_max,
+ edata_list_inactive_t *result) {
+ witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
+ WITNESS_RANK_CORE, 0);
+ ehooks_t *ehooks = pac_ehooks_get(pac);
+
+ /* Stash extents according to npages_limit. */
+ size_t nstashed = 0;
+ while (nstashed < npages_decay_max) {
+ edata_t *edata = ecache_evict(tsdn, pac, ehooks, ecache,
+ npages_limit);
+ if (edata == NULL) {
+ break;
+ }
+ edata_list_inactive_append(result, edata);
+ nstashed += edata_size_get(edata) >> LG_PAGE;
+ }
+ return nstashed;
+}
+
+static size_t
+pac_decay_stashed(tsdn_t *tsdn, pac_t *pac, decay_t *decay,
+ pac_decay_stats_t *decay_stats, ecache_t *ecache, bool fully_decay,
+ edata_list_inactive_t *decay_extents) {
+ bool err;
+
+ size_t nmadvise = 0;
+ size_t nunmapped = 0;
+ size_t npurged = 0;
+
+ ehooks_t *ehooks = pac_ehooks_get(pac);
+
+ bool try_muzzy = !fully_decay
+ && pac_decay_ms_get(pac, extent_state_muzzy) != 0;
+
+ for (edata_t *edata = edata_list_inactive_first(decay_extents); edata !=
+ NULL; edata = edata_list_inactive_first(decay_extents)) {
+ edata_list_inactive_remove(decay_extents, edata);
+
+ size_t size = edata_size_get(edata);
+ size_t npages = size >> LG_PAGE;
+
+ nmadvise++;
+ npurged += npages;
+
+ switch (ecache->state) {
+ case extent_state_active:
+ not_reached();
+ case extent_state_dirty:
+ if (try_muzzy) {
+ err = extent_purge_lazy_wrapper(tsdn, ehooks,
+ edata, /* offset */ 0, size);
+ if (!err) {
+ ecache_dalloc(tsdn, pac, ehooks,
+ &pac->ecache_muzzy, edata);
+ break;
+ }
+ }
+ JEMALLOC_FALLTHROUGH;
+ case extent_state_muzzy:
+ extent_dalloc_wrapper(tsdn, pac, ehooks, edata);
+ nunmapped += npages;
+ break;
+ case extent_state_retained:
+ default:
+ not_reached();
+ }
+ }
+
+ if (config_stats) {
+ LOCKEDINT_MTX_LOCK(tsdn, *pac->stats_mtx);
+ locked_inc_u64(tsdn, LOCKEDINT_MTX(*pac->stats_mtx),
+ &decay_stats->npurge, 1);
+ locked_inc_u64(tsdn, LOCKEDINT_MTX(*pac->stats_mtx),
+ &decay_stats->nmadvise, nmadvise);
+ locked_inc_u64(tsdn, LOCKEDINT_MTX(*pac->stats_mtx),
+ &decay_stats->purged, npurged);
+ LOCKEDINT_MTX_UNLOCK(tsdn, *pac->stats_mtx);
+ atomic_fetch_sub_zu(&pac->stats->pac_mapped,
+ nunmapped << LG_PAGE, ATOMIC_RELAXED);
+ }
+
+ return npurged;
+}
+
+/*
+ * npages_limit: Decay at most npages_decay_max pages without violating the
+ * invariant: (ecache_npages_get(ecache) >= npages_limit). We need an upper
+ * bound on number of pages in order to prevent unbounded growth (namely in
+ * stashed), otherwise unbounded new pages could be added to extents during the
+ * current decay run, so that the purging thread never finishes.
+ */
+static void
+pac_decay_to_limit(tsdn_t *tsdn, pac_t *pac, decay_t *decay,
+ pac_decay_stats_t *decay_stats, ecache_t *ecache, bool fully_decay,
+ size_t npages_limit, size_t npages_decay_max) {
+ witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
+ WITNESS_RANK_CORE, 1);
+
+ if (decay->purging || npages_decay_max == 0) {
+ return;
+ }
+ decay->purging = true;
+ malloc_mutex_unlock(tsdn, &decay->mtx);
+
+ edata_list_inactive_t decay_extents;
+ edata_list_inactive_init(&decay_extents);
+ size_t npurge = pac_stash_decayed(tsdn, pac, ecache, npages_limit,
+ npages_decay_max, &decay_extents);
+ if (npurge != 0) {
+ size_t npurged = pac_decay_stashed(tsdn, pac, decay,
+ decay_stats, ecache, fully_decay, &decay_extents);
+ assert(npurged == npurge);
+ }
+
+ malloc_mutex_lock(tsdn, &decay->mtx);
+ decay->purging = false;
+}
+
+void
+pac_decay_all(tsdn_t *tsdn, pac_t *pac, decay_t *decay,
+ pac_decay_stats_t *decay_stats, ecache_t *ecache, bool fully_decay) {
+ malloc_mutex_assert_owner(tsdn, &decay->mtx);
+ pac_decay_to_limit(tsdn, pac, decay, decay_stats, ecache, fully_decay,
+ /* npages_limit */ 0, ecache_npages_get(ecache));
+}
+
+static void
+pac_decay_try_purge(tsdn_t *tsdn, pac_t *pac, decay_t *decay,
+ pac_decay_stats_t *decay_stats, ecache_t *ecache,
+ size_t current_npages, size_t npages_limit) {
+ if (current_npages > npages_limit) {
+ pac_decay_to_limit(tsdn, pac, decay, decay_stats, ecache,
+ /* fully_decay */ false, npages_limit,
+ current_npages - npages_limit);
+ }
+}
+
+bool
+pac_maybe_decay_purge(tsdn_t *tsdn, pac_t *pac, decay_t *decay,
+ pac_decay_stats_t *decay_stats, ecache_t *ecache,
+ pac_purge_eagerness_t eagerness) {
+ malloc_mutex_assert_owner(tsdn, &decay->mtx);
+
+ /* Purge all or nothing if the option is disabled. */
+ ssize_t decay_ms = decay_ms_read(decay);
+ if (decay_ms <= 0) {
+ if (decay_ms == 0) {
+ pac_decay_to_limit(tsdn, pac, decay, decay_stats,
+ ecache, /* fully_decay */ false,
+ /* npages_limit */ 0, ecache_npages_get(ecache));
+ }
+ return false;
+ }
+
+ /*
+ * If the deadline has been reached, advance to the current epoch and
+ * purge to the new limit if necessary. Note that dirty pages created
+ * during the current epoch are not subject to purge until a future
+ * epoch, so as a result purging only happens during epoch advances, or
+ * being triggered by background threads (scheduled event).
+ */
+ nstime_t time;
+ nstime_init_update(&time);
+ size_t npages_current = ecache_npages_get(ecache);
+ bool epoch_advanced = decay_maybe_advance_epoch(decay, &time,
+ npages_current);
+ if (eagerness == PAC_PURGE_ALWAYS
+ || (epoch_advanced && eagerness == PAC_PURGE_ON_EPOCH_ADVANCE)) {
+ size_t npages_limit = decay_npages_limit_get(decay);
+ pac_decay_try_purge(tsdn, pac, decay, decay_stats, ecache,
+ npages_current, npages_limit);
+ }
+
+ return epoch_advanced;
+}
+
+bool
+pac_decay_ms_set(tsdn_t *tsdn, pac_t *pac, extent_state_t state,
+ ssize_t decay_ms, pac_purge_eagerness_t eagerness) {
+ decay_t *decay;
+ pac_decay_stats_t *decay_stats;
+ ecache_t *ecache;
+ pac_decay_data_get(pac, state, &decay, &decay_stats, &ecache);
+
+ if (!decay_ms_valid(decay_ms)) {
+ return true;
+ }
+
+ malloc_mutex_lock(tsdn, &decay->mtx);
+ /*
+ * Restart decay backlog from scratch, which may cause many dirty pages
+ * to be immediately purged. It would conceptually be possible to map
+ * the old backlog onto the new backlog, but there is no justification
+ * for such complexity since decay_ms changes are intended to be
+ * infrequent, either between the {-1, 0, >0} states, or a one-time
+ * arbitrary change during initial arena configuration.
+ */
+ nstime_t cur_time;
+ nstime_init_update(&cur_time);
+ decay_reinit(decay, &cur_time, decay_ms);
+ pac_maybe_decay_purge(tsdn, pac, decay, decay_stats, ecache, eagerness);
+ malloc_mutex_unlock(tsdn, &decay->mtx);
+
+ return false;
+}
+
+ssize_t
+pac_decay_ms_get(pac_t *pac, extent_state_t state) {
+ decay_t *decay;
+ pac_decay_stats_t *decay_stats;
+ ecache_t *ecache;
+ pac_decay_data_get(pac, state, &decay, &decay_stats, &ecache);
+ return decay_ms_read(decay);
+}
+
+void
+pac_reset(tsdn_t *tsdn, pac_t *pac) {
+ /*
+ * No-op for now; purging is still done at the arena-level. It should
+ * get moved in here, though.
+ */
+ (void)tsdn;
+ (void)pac;
+}
+
+void
+pac_destroy(tsdn_t *tsdn, pac_t *pac) {
+ assert(ecache_npages_get(&pac->ecache_dirty) == 0);
+ assert(ecache_npages_get(&pac->ecache_muzzy) == 0);
+ /*
+ * Iterate over the retained extents and destroy them. This gives the
+ * extent allocator underlying the extent hooks an opportunity to unmap
+ * all retained memory without having to keep its own metadata
+ * structures. In practice, virtual memory for dss-allocated extents is
+ * leaked here, so best practice is to avoid dss for arenas to be
+ * destroyed, or provide custom extent hooks that track retained
+ * dss-based extents for later reuse.
+ */
+ ehooks_t *ehooks = pac_ehooks_get(pac);
+ edata_t *edata;
+ while ((edata = ecache_evict(tsdn, pac, ehooks,
+ &pac->ecache_retained, 0)) != NULL) {
+ extent_destroy_wrapper(tsdn, pac, ehooks, edata);
+ }
+}
diff --git a/contrib/jemalloc/src/pages.c b/contrib/jemalloc/src/pages.c
index 1050c3925153..4b46687d70fc 100644
--- a/contrib/jemalloc/src/pages.c
+++ b/contrib/jemalloc/src/pages.c
@@ -1,4 +1,3 @@
-#define JEMALLOC_PAGES_C_
#include "jemalloc/internal/jemalloc_preamble.h"
#include "jemalloc/internal/pages.h"
@@ -16,6 +15,14 @@
#include <vm/vm.h>
#endif
#endif
+#ifdef __NetBSD__
+#include <sys/bitops.h> /* ilog2 */
+#endif
+#ifdef JEMALLOC_HAVE_VM_MAKE_TAG
+#define PAGES_FD_TAG VM_MAKE_TAG(101U)
+#else
+#define PAGES_FD_TAG -1
+#endif
/******************************************************************************/
/* Data. */
@@ -42,6 +49,57 @@ thp_mode_t init_system_thp_mode;
/* Runtime support for lazy purge. Irrelevant when !pages_can_purge_lazy. */
static bool pages_can_purge_lazy_runtime = true;
+#ifdef JEMALLOC_PURGE_MADVISE_DONTNEED_ZEROS
+static int madvise_dont_need_zeros_is_faulty = -1;
+/**
+ * Check that MADV_DONTNEED will actually zero pages on subsequent access.
+ *
+ * Since qemu does not support this, yet [1], and you can get very tricky
+ * assert if you will run program with jemalloc in use under qemu:
+ *
+ * <jemalloc>: ../contrib/jemalloc/src/extent.c:1195: Failed assertion: "p[i] == 0"
+ *
+ * [1]: https://patchwork.kernel.org/patch/10576637/
+ */
+static int madvise_MADV_DONTNEED_zeroes_pages()
+{
+ int works = -1;
+ size_t size = PAGE;
+
+ void * addr = mmap(NULL, size, PROT_READ|PROT_WRITE,
+ MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
+
+ if (addr == MAP_FAILED) {
+ malloc_write("<jemalloc>: Cannot allocate memory for "
+ "MADV_DONTNEED check\n");
+ if (opt_abort) {
+ abort();
+ }
+ }
+
+ memset(addr, 'A', size);
+ if (madvise(addr, size, MADV_DONTNEED) == 0) {
+ works = memchr(addr, 'A', size) == NULL;
+ } else {
+ /*
+ * If madvise() does not support MADV_DONTNEED, then we can
+ * call it anyway, and use it's return code.
+ */
+ works = 1;
+ }
+
+ if (munmap(addr, size) != 0) {
+ malloc_write("<jemalloc>: Cannot deallocate memory for "
+ "MADV_DONTNEED check\n");
+ if (opt_abort) {
+ abort();
+ }
+ }
+
+ return works;
+}
+#endif
+
/******************************************************************************/
/*
* Function prototypes for static functions that are referenced prior to
@@ -76,9 +134,21 @@ os_pages_map(void *addr, size_t size, size_t alignment, bool *commit) {
* of existing mappings, and we only want to create new mappings.
*/
{
+#ifdef __NetBSD__
+ /*
+ * On NetBSD PAGE for a platform is defined to the
+ * maximum page size of all machine architectures
+ * for that platform, so that we can use the same
+ * binaries across all machine architectures.
+ */
+ if (alignment > os_page || PAGE > os_page) {
+ unsigned int a = ilog2(MAX(alignment, PAGE));
+ mmap_flags |= MAP_ALIGNED(a);
+ }
+#endif
int prot = *commit ? PAGES_PROT_COMMIT : PAGES_PROT_DECOMMIT;
- ret = mmap(addr, size, prot, mmap_flags, -1, 0);
+ ret = mmap(addr, size, prot, mmap_flags, PAGES_FD_TAG, 0);
}
assert(ret != NULL);
@@ -199,8 +269,8 @@ pages_map(void *addr, size_t size, size_t alignment, bool *commit) {
flags |= MAP_FIXED | MAP_EXCL;
} else {
unsigned alignment_bits = ffs_zu(alignment);
- assert(alignment_bits > 1);
- flags |= MAP_ALIGNED(alignment_bits - 1);
+ assert(alignment_bits > 0);
+ flags |= MAP_ALIGNED(alignment_bits);
}
void *ret = mmap(addr, size, prot, flags, -1, 0);
@@ -248,14 +318,10 @@ pages_unmap(void *addr, size_t size) {
}
static bool
-pages_commit_impl(void *addr, size_t size, bool commit) {
+os_pages_commit(void *addr, size_t size, bool commit) {
assert(PAGE_ADDR2BASE(addr) == addr);
assert(PAGE_CEILING(size) == size);
- if (os_overcommits) {
- return true;
- }
-
#ifdef _WIN32
return (commit ? (addr != VirtualAlloc(addr, size, MEM_COMMIT,
PAGE_READWRITE)) : (!VirtualFree(addr, size, MEM_DECOMMIT)));
@@ -263,7 +329,7 @@ pages_commit_impl(void *addr, size_t size, bool commit) {
{
int prot = commit ? PAGES_PROT_COMMIT : PAGES_PROT_DECOMMIT;
void *result = mmap(addr, size, prot, mmap_flags | MAP_FIXED,
- -1, 0);
+ PAGES_FD_TAG, 0);
if (result == MAP_FAILED) {
return true;
}
@@ -280,6 +346,15 @@ pages_commit_impl(void *addr, size_t size, bool commit) {
#endif
}
+static bool
+pages_commit_impl(void *addr, size_t size, bool commit) {
+ if (os_overcommits) {
+ return true;
+ }
+
+ return os_pages_commit(addr, size, commit);
+}
+
bool
pages_commit(void *addr, size_t size) {
return pages_commit_impl(addr, size, true);
@@ -290,6 +365,66 @@ pages_decommit(void *addr, size_t size) {
return pages_commit_impl(addr, size, false);
}
+void
+pages_mark_guards(void *head, void *tail) {
+ assert(head != NULL || tail != NULL);
+ assert(head == NULL || tail == NULL ||
+ (uintptr_t)head < (uintptr_t)tail);
+#ifdef JEMALLOC_HAVE_MPROTECT
+ if (head != NULL) {
+ mprotect(head, PAGE, PROT_NONE);
+ }
+ if (tail != NULL) {
+ mprotect(tail, PAGE, PROT_NONE);
+ }
+#else
+ /* Decommit sets to PROT_NONE / MEM_DECOMMIT. */
+ if (head != NULL) {
+ os_pages_commit(head, PAGE, false);
+ }
+ if (tail != NULL) {
+ os_pages_commit(tail, PAGE, false);
+ }
+#endif
+}
+
+void
+pages_unmark_guards(void *head, void *tail) {
+ assert(head != NULL || tail != NULL);
+ assert(head == NULL || tail == NULL ||
+ (uintptr_t)head < (uintptr_t)tail);
+#ifdef JEMALLOC_HAVE_MPROTECT
+ bool head_and_tail = (head != NULL) && (tail != NULL);
+ size_t range = head_and_tail ?
+ (uintptr_t)tail - (uintptr_t)head + PAGE :
+ SIZE_T_MAX;
+ /*
+ * The amount of work that the kernel does in mprotect depends on the
+ * range argument. SC_LARGE_MINCLASS is an arbitrary threshold chosen
+ * to prevent kernel from doing too much work that would outweigh the
+ * savings of performing one less system call.
+ */
+ bool ranged_mprotect = head_and_tail && range <= SC_LARGE_MINCLASS;
+ if (ranged_mprotect) {
+ mprotect(head, range, PROT_READ | PROT_WRITE);
+ } else {
+ if (head != NULL) {
+ mprotect(head, PAGE, PROT_READ | PROT_WRITE);
+ }
+ if (tail != NULL) {
+ mprotect(tail, PAGE, PROT_READ | PROT_WRITE);
+ }
+ }
+#else
+ if (head != NULL) {
+ os_pages_commit(head, PAGE, true);
+ }
+ if (tail != NULL) {
+ os_pages_commit(tail, PAGE, true);
+ }
+#endif
+}
+
bool
pages_purge_lazy(void *addr, size_t size) {
assert(ALIGNMENT_ADDR2BASE(addr, os_page) == addr);
@@ -320,6 +455,9 @@ pages_purge_lazy(void *addr, size_t size) {
#elif defined(JEMALLOC_PURGE_MADVISE_DONTNEED) && \
!defined(JEMALLOC_PURGE_MADVISE_DONTNEED_ZEROS)
return (madvise(addr, size, MADV_DONTNEED) != 0);
+#elif defined(JEMALLOC_PURGE_POSIX_MADVISE_DONTNEED) && \
+ !defined(JEMALLOC_PURGE_POSIX_MADVISE_DONTNEED_ZEROS)
+ return (posix_madvise(addr, size, POSIX_MADV_DONTNEED) != 0);
#else
not_reached();
#endif
@@ -336,7 +474,12 @@ pages_purge_forced(void *addr, size_t size) {
#if defined(JEMALLOC_PURGE_MADVISE_DONTNEED) && \
defined(JEMALLOC_PURGE_MADVISE_DONTNEED_ZEROS)
- return (madvise(addr, size, MADV_DONTNEED) != 0);
+ return (unlikely(madvise_dont_need_zeros_is_faulty) ||
+ madvise(addr, size, MADV_DONTNEED) != 0);
+#elif defined(JEMALLOC_PURGE_POSIX_MADVISE_DONTNEED) && \
+ defined(JEMALLOC_PURGE_POSIX_MADVISE_DONTNEED_ZEROS)
+ return (unlikely(madvise_dont_need_zeros_is_faulty) ||
+ posix_madvise(addr, size, POSIX_MADV_DONTNEED) != 0);
#elif defined(JEMALLOC_MAPS_COALESCE)
/* Try to overlay a new demand-zeroed mapping. */
return pages_commit(addr, size);
@@ -351,8 +494,13 @@ pages_huge_impl(void *addr, size_t size, bool aligned) {
assert(HUGEPAGE_ADDR2BASE(addr) == addr);
assert(HUGEPAGE_CEILING(size) == size);
}
-#ifdef JEMALLOC_HAVE_MADVISE_HUGE
+#if defined(JEMALLOC_HAVE_MADVISE_HUGE)
return (madvise(addr, size, MADV_HUGEPAGE) != 0);
+#elif defined(JEMALLOC_HAVE_MEMCNTL)
+ struct memcntl_mha m = {0};
+ m.mha_cmd = MHA_MAPSIZE_VA;
+ m.mha_pagesize = HUGEPAGE;
+ return (memcntl(addr, size, MC_HAT_ADVISE, (caddr_t)&m, 0, 0) == 0);
#else
return true;
#endif
@@ -396,8 +544,10 @@ bool
pages_dontdump(void *addr, size_t size) {
assert(PAGE_ADDR2BASE(addr) == addr);
assert(PAGE_CEILING(size) == size);
-#ifdef JEMALLOC_MADVISE_DONTDUMP
+#if defined(JEMALLOC_MADVISE_DONTDUMP)
return madvise(addr, size, MADV_DONTDUMP) != 0;
+#elif defined(JEMALLOC_MADVISE_NOCORE)
+ return madvise(addr, size, MADV_NOCORE) != 0;
#else
return false;
#endif
@@ -407,8 +557,10 @@ bool
pages_dodump(void *addr, size_t size) {
assert(PAGE_ADDR2BASE(addr) == addr);
assert(PAGE_CEILING(size) == size);
-#ifdef JEMALLOC_MADVISE_DONTDUMP
+#if defined(JEMALLOC_MADVISE_DONTDUMP)
return madvise(addr, size, MADV_DODUMP) != 0;
+#elif defined(JEMALLOC_MADVISE_NOCORE)
+ return madvise(addr, size, MADV_CORE) != 0;
#else
return false;
#endif
@@ -554,14 +706,14 @@ pages_set_thp_state (void *ptr, size_t size) {
static void
init_thp_state(void) {
- if (!have_madvise_huge) {
+ if (!have_madvise_huge && !have_memcntl) {
if (metadata_thp_enabled() && opt_abort) {
malloc_write("<jemalloc>: no MADV_HUGEPAGE support\n");
abort();
}
goto label_error;
}
-
+#if defined(JEMALLOC_HAVE_MADVISE_HUGE)
static const char sys_state_madvise[] = "always [madvise] never\n";
static const char sys_state_always[] = "[always] madvise never\n";
static const char sys_state_never[] = "always madvise [never]\n";
@@ -570,6 +722,9 @@ init_thp_state(void) {
#if defined(JEMALLOC_USE_SYSCALL) && defined(SYS_open)
int fd = (int)syscall(SYS_open,
"/sys/kernel/mm/transparent_hugepage/enabled", O_RDONLY);
+#elif defined(JEMALLOC_USE_SYSCALL) && defined(SYS_openat)
+ int fd = (int)syscall(SYS_openat,
+ AT_FDCWD, "/sys/kernel/mm/transparent_hugepage/enabled", O_RDONLY);
#else
int fd = open("/sys/kernel/mm/transparent_hugepage/enabled", O_RDONLY);
#endif
@@ -585,7 +740,7 @@ init_thp_state(void) {
#endif
if (nread < 0) {
- goto label_error;
+ goto label_error;
}
if (strncmp(buf, sys_state_madvise, (size_t)nread) == 0) {
@@ -598,6 +753,10 @@ init_thp_state(void) {
goto label_error;
}
return;
+#elif defined(JEMALLOC_HAVE_MEMCNTL)
+ init_system_thp_mode = thp_mode_default;
+ return;
+#endif
label_error:
opt_thp = init_system_thp_mode = thp_mode_not_supported;
}
@@ -613,6 +772,20 @@ pages_boot(void) {
return true;
}
+#ifdef JEMALLOC_PURGE_MADVISE_DONTNEED_ZEROS
+ if (!opt_trust_madvise) {
+ madvise_dont_need_zeros_is_faulty = !madvise_MADV_DONTNEED_zeroes_pages();
+ if (madvise_dont_need_zeros_is_faulty) {
+ malloc_write("<jemalloc>: MADV_DONTNEED does not work (memset will be used instead)\n");
+ malloc_write("<jemalloc>: (This is the expected behaviour if you are running under QEMU)\n");
+ }
+ } else {
+ /* In case opt_trust_madvise is disable,
+ * do not do runtime check */
+ madvise_dont_need_zeros_is_faulty = 0;
+ }
+#endif
+
#ifndef _WIN32
mmap_flags = MAP_PRIVATE | MAP_ANON;
#endif
@@ -626,6 +799,8 @@ pages_boot(void) {
mmap_flags |= MAP_NORESERVE;
}
# endif
+#elif defined(__NetBSD__)
+ os_overcommits = true;
#else
os_overcommits = false;
#endif
diff --git a/contrib/jemalloc/src/pai.c b/contrib/jemalloc/src/pai.c
new file mode 100644
index 000000000000..45c87729278d
--- /dev/null
+++ b/contrib/jemalloc/src/pai.c
@@ -0,0 +1,31 @@
+#include "jemalloc/internal/jemalloc_preamble.h"
+#include "jemalloc/internal/jemalloc_internal_includes.h"
+
+size_t
+pai_alloc_batch_default(tsdn_t *tsdn, pai_t *self, size_t size, size_t nallocs,
+ edata_list_active_t *results, bool *deferred_work_generated) {
+ for (size_t i = 0; i < nallocs; i++) {
+ bool deferred_by_alloc = false;
+ edata_t *edata = pai_alloc(tsdn, self, size, PAGE,
+ /* zero */ false, /* guarded */ false,
+ /* frequent_reuse */ false, &deferred_by_alloc);
+ *deferred_work_generated |= deferred_by_alloc;
+ if (edata == NULL) {
+ return i;
+ }
+ edata_list_active_append(results, edata);
+ }
+ return nallocs;
+}
+
+void
+pai_dalloc_batch_default(tsdn_t *tsdn, pai_t *self,
+ edata_list_active_t *list, bool *deferred_work_generated) {
+ edata_t *edata;
+ while ((edata = edata_list_active_first(list)) != NULL) {
+ bool deferred_by_dalloc = false;
+ edata_list_active_remove(list, edata);
+ pai_dalloc(tsdn, self, edata, &deferred_by_dalloc);
+ *deferred_work_generated |= deferred_by_dalloc;
+ }
+}
diff --git a/contrib/jemalloc/src/peak_event.c b/contrib/jemalloc/src/peak_event.c
new file mode 100644
index 000000000000..4093fbcc691e
--- /dev/null
+++ b/contrib/jemalloc/src/peak_event.c
@@ -0,0 +1,82 @@
+#include "jemalloc/internal/jemalloc_preamble.h"
+#include "jemalloc/internal/jemalloc_internal_includes.h"
+
+#include "jemalloc/internal/peak_event.h"
+
+#include "jemalloc/internal/activity_callback.h"
+#include "jemalloc/internal/peak.h"
+
+/*
+ * Update every 64K by default. We're not exposing this as a configuration
+ * option for now; we don't want to bind ourselves too tightly to any particular
+ * performance requirements for small values, or guarantee that we'll even be
+ * able to provide fine-grained accuracy.
+ */
+#define PEAK_EVENT_WAIT (64 * 1024)
+
+/* Update the peak with current tsd state. */
+void
+peak_event_update(tsd_t *tsd) {
+ uint64_t alloc = tsd_thread_allocated_get(tsd);
+ uint64_t dalloc = tsd_thread_deallocated_get(tsd);
+ peak_t *peak = tsd_peakp_get(tsd);
+ peak_update(peak, alloc, dalloc);
+}
+
+static void
+peak_event_activity_callback(tsd_t *tsd) {
+ activity_callback_thunk_t *thunk = tsd_activity_callback_thunkp_get(
+ tsd);
+ uint64_t alloc = tsd_thread_allocated_get(tsd);
+ uint64_t dalloc = tsd_thread_deallocated_get(tsd);
+ if (thunk->callback != NULL) {
+ thunk->callback(thunk->uctx, alloc, dalloc);
+ }
+}
+
+/* Set current state to zero. */
+void
+peak_event_zero(tsd_t *tsd) {
+ uint64_t alloc = tsd_thread_allocated_get(tsd);
+ uint64_t dalloc = tsd_thread_deallocated_get(tsd);
+ peak_t *peak = tsd_peakp_get(tsd);
+ peak_set_zero(peak, alloc, dalloc);
+}
+
+uint64_t
+peak_event_max(tsd_t *tsd) {
+ peak_t *peak = tsd_peakp_get(tsd);
+ return peak_max(peak);
+}
+
+uint64_t
+peak_alloc_new_event_wait(tsd_t *tsd) {
+ return PEAK_EVENT_WAIT;
+}
+
+uint64_t
+peak_alloc_postponed_event_wait(tsd_t *tsd) {
+ return TE_MIN_START_WAIT;
+}
+
+void
+peak_alloc_event_handler(tsd_t *tsd, uint64_t elapsed) {
+ peak_event_update(tsd);
+ peak_event_activity_callback(tsd);
+}
+
+uint64_t
+peak_dalloc_new_event_wait(tsd_t *tsd) {
+ return PEAK_EVENT_WAIT;
+}
+
+uint64_t
+peak_dalloc_postponed_event_wait(tsd_t *tsd) {
+ return TE_MIN_START_WAIT;
+}
+
+void
+peak_dalloc_event_handler(tsd_t *tsd, uint64_t elapsed) {
+ peak_event_update(tsd);
+ peak_event_activity_callback(tsd);
+}
diff --git a/contrib/jemalloc/src/prng.c b/contrib/jemalloc/src/prng.c
deleted file mode 100644
index 83c04bf9b5dd..000000000000
--- a/contrib/jemalloc/src/prng.c
+++ /dev/null
@@ -1,3 +0,0 @@
-#define JEMALLOC_PRNG_C_
-#include "jemalloc/internal/jemalloc_preamble.h"
-#include "jemalloc/internal/jemalloc_internal_includes.h"
diff --git a/contrib/jemalloc/src/prof.c b/contrib/jemalloc/src/prof.c
index 13334cb4c0ba..7a6d5d569a48 100644
--- a/contrib/jemalloc/src/prof.c
+++ b/contrib/jemalloc/src/prof.c
@@ -1,1126 +1,199 @@
-#define JEMALLOC_PROF_C_
#include "jemalloc/internal/jemalloc_preamble.h"
#include "jemalloc/internal/jemalloc_internal_includes.h"
+#include "jemalloc/internal/ctl.h"
#include "jemalloc/internal/assert.h"
-#include "jemalloc/internal/ckh.h"
-#include "jemalloc/internal/hash.h"
-#include "jemalloc/internal/malloc_io.h"
#include "jemalloc/internal/mutex.h"
-#include "jemalloc/internal/emitter.h"
+#include "jemalloc/internal/counter.h"
+#include "jemalloc/internal/prof_data.h"
+#include "jemalloc/internal/prof_log.h"
+#include "jemalloc/internal/prof_recent.h"
+#include "jemalloc/internal/prof_stats.h"
+#include "jemalloc/internal/prof_sys.h"
+#include "jemalloc/internal/prof_hook.h"
+#include "jemalloc/internal/thread_event.h"
-/******************************************************************************/
-
-#ifdef JEMALLOC_PROF_LIBUNWIND
-#define UNW_LOCAL_ONLY
-#include <libunwind.h>
-#endif
-
-#ifdef JEMALLOC_PROF_LIBGCC
/*
- * We have a circular dependency -- jemalloc_internal.h tells us if we should
- * use libgcc's unwinding functionality, but after we've included that, we've
- * already hooked _Unwind_Backtrace. We'll temporarily disable hooking.
+ * This file implements the profiling "APIs" needed by other parts of jemalloc,
+ * and also manages the relevant "operational" data, mainly options and mutexes;
+ * the core profiling data structures are encapsulated in prof_data.c.
*/
-#undef _Unwind_Backtrace
-#include <unwind.h>
-#define _Unwind_Backtrace JEMALLOC_HOOK(_Unwind_Backtrace, test_hooks_libc_hook)
-#endif
/******************************************************************************/
+
/* Data. */
-bool opt_prof = false;
-bool opt_prof_active = true;
-bool opt_prof_thread_active_init = true;
-size_t opt_lg_prof_sample = LG_PROF_SAMPLE_DEFAULT;
-ssize_t opt_lg_prof_interval = LG_PROF_INTERVAL_DEFAULT;
-bool opt_prof_gdump = false;
-bool opt_prof_final = false;
-bool opt_prof_leak = false;
-bool opt_prof_accum = false;
-bool opt_prof_log = false;
-char opt_prof_prefix[
- /* Minimize memory bloat for non-prof builds. */
-#ifdef JEMALLOC_PROF
- PATH_MAX +
-#endif
- 1];
+bool opt_prof = false;
+bool opt_prof_active = true;
+bool opt_prof_thread_active_init = true;
+size_t opt_lg_prof_sample = LG_PROF_SAMPLE_DEFAULT;
+ssize_t opt_lg_prof_interval = LG_PROF_INTERVAL_DEFAULT;
+bool opt_prof_gdump = false;
+bool opt_prof_final = false;
+bool opt_prof_leak = false;
+bool opt_prof_leak_error = false;
+bool opt_prof_accum = false;
+char opt_prof_prefix[PROF_DUMP_FILENAME_LEN];
+bool opt_prof_sys_thread_name = false;
+bool opt_prof_unbias = true;
+
+/* Accessed via prof_sample_event_handler(). */
+static counter_accum_t prof_idump_accumulated;
/*
* Initialized as opt_prof_active, and accessed via
* prof_active_[gs]et{_unlocked,}().
*/
-bool prof_active;
-static malloc_mutex_t prof_active_mtx;
+bool prof_active_state;
+static malloc_mutex_t prof_active_mtx;
/*
* Initialized as opt_prof_thread_active_init, and accessed via
* prof_thread_active_init_[gs]et().
*/
-static bool prof_thread_active_init;
-static malloc_mutex_t prof_thread_active_init_mtx;
+static bool prof_thread_active_init;
+static malloc_mutex_t prof_thread_active_init_mtx;
/*
* Initialized as opt_prof_gdump, and accessed via
* prof_gdump_[gs]et{_unlocked,}().
*/
-bool prof_gdump_val;
-static malloc_mutex_t prof_gdump_mtx;
-
-uint64_t prof_interval = 0;
-
-size_t lg_prof_sample;
-
-typedef enum prof_logging_state_e prof_logging_state_t;
-enum prof_logging_state_e {
- prof_logging_state_stopped,
- prof_logging_state_started,
- prof_logging_state_dumping
-};
-
-/*
- * - stopped: log_start never called, or previous log_stop has completed.
- * - started: log_start called, log_stop not called yet. Allocations are logged.
- * - dumping: log_stop called but not finished; samples are not logged anymore.
- */
-prof_logging_state_t prof_logging_state = prof_logging_state_stopped;
-
-#ifdef JEMALLOC_JET
-static bool prof_log_dummy = false;
-#endif
-
-/* Incremented for every log file that is output. */
-static uint64_t log_seq = 0;
-static char log_filename[
- /* Minimize memory bloat for non-prof builds. */
-#ifdef JEMALLOC_PROF
- PATH_MAX +
-#endif
- 1];
-
-/* Timestamp for most recent call to log_start(). */
-static nstime_t log_start_timestamp = NSTIME_ZERO_INITIALIZER;
-
-/* Increment these when adding to the log_bt and log_thr linked lists. */
-static size_t log_bt_index = 0;
-static size_t log_thr_index = 0;
-
-/* Linked list node definitions. These are only used in prof.c. */
-typedef struct prof_bt_node_s prof_bt_node_t;
-
-struct prof_bt_node_s {
- prof_bt_node_t *next;
- size_t index;
- prof_bt_t bt;
- /* Variable size backtrace vector pointed to by bt. */
- void *vec[1];
-};
-
-typedef struct prof_thr_node_s prof_thr_node_t;
-
-struct prof_thr_node_s {
- prof_thr_node_t *next;
- size_t index;
- uint64_t thr_uid;
- /* Variable size based on thr_name_sz. */
- char name[1];
-};
-
-typedef struct prof_alloc_node_s prof_alloc_node_t;
-
-/* This is output when logging sampled allocations. */
-struct prof_alloc_node_s {
- prof_alloc_node_t *next;
- /* Indices into an array of thread data. */
- size_t alloc_thr_ind;
- size_t free_thr_ind;
+bool prof_gdump_val;
+static malloc_mutex_t prof_gdump_mtx;
- /* Indices into an array of backtraces. */
- size_t alloc_bt_ind;
- size_t free_bt_ind;
+uint64_t prof_interval = 0;
- uint64_t alloc_time_ns;
- uint64_t free_time_ns;
+size_t lg_prof_sample;
- size_t usize;
-};
-
-/*
- * Created on the first call to prof_log_start and deleted on prof_log_stop.
- * These are the backtraces and threads that have already been logged by an
- * allocation.
- */
-static bool log_tables_initialized = false;
-static ckh_t log_bt_node_set;
-static ckh_t log_thr_node_set;
-
-/* Store linked lists for logged data. */
-static prof_bt_node_t *log_bt_first = NULL;
-static prof_bt_node_t *log_bt_last = NULL;
-static prof_thr_node_t *log_thr_first = NULL;
-static prof_thr_node_t *log_thr_last = NULL;
-static prof_alloc_node_t *log_alloc_first = NULL;
-static prof_alloc_node_t *log_alloc_last = NULL;
-
-/* Protects the prof_logging_state and any log_{...} variable. */
-static malloc_mutex_t log_mtx;
-
-/*
- * Table of mutexes that are shared among gctx's. These are leaf locks, so
- * there is no problem with using them for more than one gctx at the same time.
- * The primary motivation for this sharing though is that gctx's are ephemeral,
- * and destroying mutexes causes complications for systems that allocate when
- * creating/destroying mutexes.
- */
-static malloc_mutex_t *gctx_locks;
-static atomic_u_t cum_gctxs; /* Atomic counter. */
-
-/*
- * Table of mutexes that are shared among tdata's. No operations require
- * holding multiple tdata locks, so there is no problem with using them for more
- * than one tdata at the same time, even though a gctx lock may be acquired
- * while holding a tdata lock.
- */
-static malloc_mutex_t *tdata_locks;
-
-/*
- * Global hash of (prof_bt_t *)-->(prof_gctx_t *). This is the master data
- * structure that knows about all backtraces currently captured.
- */
-static ckh_t bt2gctx;
-/* Non static to enable profiling. */
-malloc_mutex_t bt2gctx_mtx;
-
-/*
- * Tree of all extant prof_tdata_t structures, regardless of state,
- * {attached,detached,expired}.
- */
-static prof_tdata_tree_t tdatas;
-static malloc_mutex_t tdatas_mtx;
-
-static uint64_t next_thr_uid;
-static malloc_mutex_t next_thr_uid_mtx;
-
-static malloc_mutex_t prof_dump_seq_mtx;
-static uint64_t prof_dump_seq;
-static uint64_t prof_dump_iseq;
-static uint64_t prof_dump_mseq;
-static uint64_t prof_dump_useq;
-
-/*
- * This buffer is rather large for stack allocation, so use a single buffer for
- * all profile dumps.
- */
-static malloc_mutex_t prof_dump_mtx;
-static char prof_dump_buf[
- /* Minimize memory bloat for non-prof builds. */
-#ifdef JEMALLOC_PROF
- PROF_DUMP_BUFSIZE
-#else
- 1
-#endif
-];
-static size_t prof_dump_buf_end;
-static int prof_dump_fd;
+static uint64_t next_thr_uid;
+static malloc_mutex_t next_thr_uid_mtx;
/* Do not dump any profiles until bootstrapping is complete. */
-static bool prof_booted = false;
+bool prof_booted = false;
-/******************************************************************************/
-/*
- * Function prototypes for static functions that are referenced prior to
- * definition.
- */
+/* Logically a prof_backtrace_hook_t. */
+atomic_p_t prof_backtrace_hook;
-static bool prof_tctx_should_destroy(tsdn_t *tsdn, prof_tctx_t *tctx);
-static void prof_tctx_destroy(tsd_t *tsd, prof_tctx_t *tctx);
-static bool prof_tdata_should_destroy(tsdn_t *tsdn, prof_tdata_t *tdata,
- bool even_if_attached);
-static void prof_tdata_destroy(tsd_t *tsd, prof_tdata_t *tdata,
- bool even_if_attached);
-static char *prof_thread_name_alloc(tsdn_t *tsdn, const char *thread_name);
-
-/* Hashtable functions for log_bt_node_set and log_thr_node_set. */
-static void prof_thr_node_hash(const void *key, size_t r_hash[2]);
-static bool prof_thr_node_keycomp(const void *k1, const void *k2);
-static void prof_bt_node_hash(const void *key, size_t r_hash[2]);
-static bool prof_bt_node_keycomp(const void *k1, const void *k2);
-
-/******************************************************************************/
-/* Red-black trees. */
-
-static int
-prof_tctx_comp(const prof_tctx_t *a, const prof_tctx_t *b) {
- uint64_t a_thr_uid = a->thr_uid;
- uint64_t b_thr_uid = b->thr_uid;
- int ret = (a_thr_uid > b_thr_uid) - (a_thr_uid < b_thr_uid);
- if (ret == 0) {
- uint64_t a_thr_discrim = a->thr_discrim;
- uint64_t b_thr_discrim = b->thr_discrim;
- ret = (a_thr_discrim > b_thr_discrim) - (a_thr_discrim <
- b_thr_discrim);
- if (ret == 0) {
- uint64_t a_tctx_uid = a->tctx_uid;
- uint64_t b_tctx_uid = b->tctx_uid;
- ret = (a_tctx_uid > b_tctx_uid) - (a_tctx_uid <
- b_tctx_uid);
- }
- }
- return ret;
-}
-
-rb_gen(static UNUSED, tctx_tree_, prof_tctx_tree_t, prof_tctx_t,
- tctx_link, prof_tctx_comp)
-
-static int
-prof_gctx_comp(const prof_gctx_t *a, const prof_gctx_t *b) {
- unsigned a_len = a->bt.len;
- unsigned b_len = b->bt.len;
- unsigned comp_len = (a_len < b_len) ? a_len : b_len;
- int ret = memcmp(a->bt.vec, b->bt.vec, comp_len * sizeof(void *));
- if (ret == 0) {
- ret = (a_len > b_len) - (a_len < b_len);
- }
- return ret;
-}
-
-rb_gen(static UNUSED, gctx_tree_, prof_gctx_tree_t, prof_gctx_t, dump_link,
- prof_gctx_comp)
-
-static int
-prof_tdata_comp(const prof_tdata_t *a, const prof_tdata_t *b) {
- int ret;
- uint64_t a_uid = a->thr_uid;
- uint64_t b_uid = b->thr_uid;
-
- ret = ((a_uid > b_uid) - (a_uid < b_uid));
- if (ret == 0) {
- uint64_t a_discrim = a->thr_discrim;
- uint64_t b_discrim = b->thr_discrim;
-
- ret = ((a_discrim > b_discrim) - (a_discrim < b_discrim));
- }
- return ret;
-}
-
-rb_gen(static UNUSED, tdata_tree_, prof_tdata_tree_t, prof_tdata_t, tdata_link,
- prof_tdata_comp)
+/* Logically a prof_dump_hook_t. */
+atomic_p_t prof_dump_hook;
/******************************************************************************/
void
-prof_alloc_rollback(tsd_t *tsd, prof_tctx_t *tctx, bool updated) {
- prof_tdata_t *tdata;
-
+prof_alloc_rollback(tsd_t *tsd, prof_tctx_t *tctx) {
cassert(config_prof);
- if (updated) {
- /*
- * Compute a new sample threshold. This isn't very important in
- * practice, because this function is rarely executed, so the
- * potential for sample bias is minimal except in contrived
- * programs.
- */
- tdata = prof_tdata_get(tsd, true);
- if (tdata != NULL) {
- prof_sample_threshold_update(tdata);
- }
+ if (tsd_reentrancy_level_get(tsd) > 0) {
+ assert((uintptr_t)tctx == (uintptr_t)1U);
+ return;
}
if ((uintptr_t)tctx > (uintptr_t)1U) {
malloc_mutex_lock(tsd_tsdn(tsd), tctx->tdata->lock);
tctx->prepared = false;
- if (prof_tctx_should_destroy(tsd_tsdn(tsd), tctx)) {
- prof_tctx_destroy(tsd, tctx);
- } else {
- malloc_mutex_unlock(tsd_tsdn(tsd), tctx->tdata->lock);
- }
+ prof_tctx_try_destroy(tsd, tctx);
}
}
void
-prof_malloc_sample_object(tsdn_t *tsdn, const void *ptr, size_t usize,
- prof_tctx_t *tctx) {
- prof_tctx_set(tsdn, ptr, usize, NULL, tctx);
+prof_malloc_sample_object(tsd_t *tsd, const void *ptr, size_t size,
+ size_t usize, prof_tctx_t *tctx) {
+ cassert(config_prof);
+
+ if (opt_prof_sys_thread_name) {
+ prof_sys_thread_name_fetch(tsd);
+ }
+
+ edata_t *edata = emap_edata_lookup(tsd_tsdn(tsd), &arena_emap_global,
+ ptr);
+ prof_info_set(tsd, edata, tctx, size);
- /* Get the current time and set this in the extent_t. We'll read this
- * when free() is called. */
- nstime_t t = NSTIME_ZERO_INITIALIZER;
- nstime_update(&t);
- prof_alloc_time_set(tsdn, ptr, NULL, t);
+ szind_t szind = sz_size2index(usize);
- malloc_mutex_lock(tsdn, tctx->tdata->lock);
+ malloc_mutex_lock(tsd_tsdn(tsd), tctx->tdata->lock);
+ /*
+ * We need to do these map lookups while holding the lock, to avoid the
+ * possibility of races with prof_reset calls, which update the map and
+ * then acquire the lock. This actually still leaves a data race on the
+ * contents of the unbias map, but we have not yet gone through and
+ * atomic-ified the prof module, and compilers are not yet causing us
+ * issues. The key thing is to make sure that, if we read garbage data,
+ * the prof_reset call is about to mark our tctx as expired before any
+ * dumping of our corrupted output is attempted.
+ */
+ size_t shifted_unbiased_cnt = prof_shifted_unbiased_cnt[szind];
+ size_t unbiased_bytes = prof_unbiased_sz[szind];
tctx->cnts.curobjs++;
+ tctx->cnts.curobjs_shifted_unbiased += shifted_unbiased_cnt;
tctx->cnts.curbytes += usize;
+ tctx->cnts.curbytes_unbiased += unbiased_bytes;
if (opt_prof_accum) {
tctx->cnts.accumobjs++;
+ tctx->cnts.accumobjs_shifted_unbiased += shifted_unbiased_cnt;
tctx->cnts.accumbytes += usize;
+ tctx->cnts.accumbytes_unbiased += unbiased_bytes;
}
+ bool record_recent = prof_recent_alloc_prepare(tsd, tctx);
tctx->prepared = false;
- malloc_mutex_unlock(tsdn, tctx->tdata->lock);
-}
-
-static size_t
-prof_log_bt_index(tsd_t *tsd, prof_bt_t *bt) {
- assert(prof_logging_state == prof_logging_state_started);
- malloc_mutex_assert_owner(tsd_tsdn(tsd), &log_mtx);
-
- prof_bt_node_t dummy_node;
- dummy_node.bt = *bt;
- prof_bt_node_t *node;
-
- /* See if this backtrace is already cached in the table. */
- if (ckh_search(&log_bt_node_set, (void *)(&dummy_node),
- (void **)(&node), NULL)) {
- size_t sz = offsetof(prof_bt_node_t, vec) +
- (bt->len * sizeof(void *));
- prof_bt_node_t *new_node = (prof_bt_node_t *)
- iallocztm(tsd_tsdn(tsd), sz, sz_size2index(sz), false, NULL,
- true, arena_get(TSDN_NULL, 0, true), true);
- if (log_bt_first == NULL) {
- log_bt_first = new_node;
- log_bt_last = new_node;
- } else {
- log_bt_last->next = new_node;
- log_bt_last = new_node;
- }
-
- new_node->next = NULL;
- new_node->index = log_bt_index;
- /*
- * Copy the backtrace: bt is inside a tdata or gctx, which
- * might die before prof_log_stop is called.
- */
- new_node->bt.len = bt->len;
- memcpy(new_node->vec, bt->vec, bt->len * sizeof(void *));
- new_node->bt.vec = new_node->vec;
-
- log_bt_index++;
- ckh_insert(tsd, &log_bt_node_set, (void *)new_node, NULL);
- return new_node->index;
- } else {
- return node->index;
+ malloc_mutex_unlock(tsd_tsdn(tsd), tctx->tdata->lock);
+ if (record_recent) {
+ assert(tctx == edata_prof_tctx_get(edata));
+ prof_recent_alloc(tsd, edata, size, usize);
}
-}
-static size_t
-prof_log_thr_index(tsd_t *tsd, uint64_t thr_uid, const char *name) {
- assert(prof_logging_state == prof_logging_state_started);
- malloc_mutex_assert_owner(tsd_tsdn(tsd), &log_mtx);
-
- prof_thr_node_t dummy_node;
- dummy_node.thr_uid = thr_uid;
- prof_thr_node_t *node;
-
- /* See if this thread is already cached in the table. */
- if (ckh_search(&log_thr_node_set, (void *)(&dummy_node),
- (void **)(&node), NULL)) {
- size_t sz = offsetof(prof_thr_node_t, name) + strlen(name) + 1;
- prof_thr_node_t *new_node = (prof_thr_node_t *)
- iallocztm(tsd_tsdn(tsd), sz, sz_size2index(sz), false, NULL,
- true, arena_get(TSDN_NULL, 0, true), true);
- if (log_thr_first == NULL) {
- log_thr_first = new_node;
- log_thr_last = new_node;
- } else {
- log_thr_last->next = new_node;
- log_thr_last = new_node;
- }
-
- new_node->next = NULL;
- new_node->index = log_thr_index;
- new_node->thr_uid = thr_uid;
- strcpy(new_node->name, name);
- log_thr_index++;
- ckh_insert(tsd, &log_thr_node_set, (void *)new_node, NULL);
- return new_node->index;
- } else {
- return node->index;
+ if (opt_prof_stats) {
+ prof_stats_inc(tsd, szind, size);
}
}
-static void
-prof_try_log(tsd_t *tsd, const void *ptr, size_t usize, prof_tctx_t *tctx) {
- malloc_mutex_assert_owner(tsd_tsdn(tsd), tctx->tdata->lock);
-
- prof_tdata_t *cons_tdata = prof_tdata_get(tsd, false);
- if (cons_tdata == NULL) {
- /*
- * We decide not to log these allocations. cons_tdata will be
- * NULL only when the current thread is in a weird state (e.g.
- * it's being destroyed).
- */
- return;
- }
-
- malloc_mutex_lock(tsd_tsdn(tsd), &log_mtx);
-
- if (prof_logging_state != prof_logging_state_started) {
- goto label_done;
- }
-
- if (!log_tables_initialized) {
- bool err1 = ckh_new(tsd, &log_bt_node_set, PROF_CKH_MINITEMS,
- prof_bt_node_hash, prof_bt_node_keycomp);
- bool err2 = ckh_new(tsd, &log_thr_node_set, PROF_CKH_MINITEMS,
- prof_thr_node_hash, prof_thr_node_keycomp);
- if (err1 || err2) {
- goto label_done;
- }
- log_tables_initialized = true;
- }
-
- nstime_t alloc_time = prof_alloc_time_get(tsd_tsdn(tsd), ptr,
- (alloc_ctx_t *)NULL);
- nstime_t free_time = NSTIME_ZERO_INITIALIZER;
- nstime_update(&free_time);
-
- size_t sz = sizeof(prof_alloc_node_t);
- prof_alloc_node_t *new_node = (prof_alloc_node_t *)
- iallocztm(tsd_tsdn(tsd), sz, sz_size2index(sz), false, NULL, true,
- arena_get(TSDN_NULL, 0, true), true);
-
- const char *prod_thr_name = (tctx->tdata->thread_name == NULL)?
- "" : tctx->tdata->thread_name;
- const char *cons_thr_name = prof_thread_name_get(tsd);
-
- prof_bt_t bt;
- /* Initialize the backtrace, using the buffer in tdata to store it. */
- bt_init(&bt, cons_tdata->vec);
- prof_backtrace(&bt);
- prof_bt_t *cons_bt = &bt;
-
- /* We haven't destroyed tctx yet, so gctx should be good to read. */
- prof_bt_t *prod_bt = &tctx->gctx->bt;
-
- new_node->next = NULL;
- new_node->alloc_thr_ind = prof_log_thr_index(tsd, tctx->tdata->thr_uid,
- prod_thr_name);
- new_node->free_thr_ind = prof_log_thr_index(tsd, cons_tdata->thr_uid,
- cons_thr_name);
- new_node->alloc_bt_ind = prof_log_bt_index(tsd, prod_bt);
- new_node->free_bt_ind = prof_log_bt_index(tsd, cons_bt);
- new_node->alloc_time_ns = nstime_ns(&alloc_time);
- new_node->free_time_ns = nstime_ns(&free_time);
- new_node->usize = usize;
-
- if (log_alloc_first == NULL) {
- log_alloc_first = new_node;
- log_alloc_last = new_node;
- } else {
- log_alloc_last->next = new_node;
- log_alloc_last = new_node;
- }
+void
+prof_free_sampled_object(tsd_t *tsd, size_t usize, prof_info_t *prof_info) {
+ cassert(config_prof);
-label_done:
- malloc_mutex_unlock(tsd_tsdn(tsd), &log_mtx);
-}
+ assert(prof_info != NULL);
+ prof_tctx_t *tctx = prof_info->alloc_tctx;
+ assert((uintptr_t)tctx > (uintptr_t)1U);
-void
-prof_free_sampled_object(tsd_t *tsd, const void *ptr, size_t usize,
- prof_tctx_t *tctx) {
+ szind_t szind = sz_size2index(usize);
malloc_mutex_lock(tsd_tsdn(tsd), tctx->tdata->lock);
assert(tctx->cnts.curobjs > 0);
assert(tctx->cnts.curbytes >= usize);
+ /*
+ * It's not correct to do equivalent asserts for unbiased bytes, because
+ * of the potential for races with prof.reset calls. The map contents
+ * should really be atomic, but we have not atomic-ified the prof module
+ * yet.
+ */
tctx->cnts.curobjs--;
+ tctx->cnts.curobjs_shifted_unbiased -= prof_shifted_unbiased_cnt[szind];
tctx->cnts.curbytes -= usize;
+ tctx->cnts.curbytes_unbiased -= prof_unbiased_sz[szind];
- prof_try_log(tsd, ptr, usize, tctx);
-
- if (prof_tctx_should_destroy(tsd_tsdn(tsd), tctx)) {
- prof_tctx_destroy(tsd, tctx);
- } else {
- malloc_mutex_unlock(tsd_tsdn(tsd), tctx->tdata->lock);
- }
-}
-
-void
-bt_init(prof_bt_t *bt, void **vec) {
- cassert(config_prof);
-
- bt->vec = vec;
- bt->len = 0;
-}
-
-static void
-prof_enter(tsd_t *tsd, prof_tdata_t *tdata) {
- cassert(config_prof);
- assert(tdata == prof_tdata_get(tsd, false));
-
- if (tdata != NULL) {
- assert(!tdata->enq);
- tdata->enq = true;
- }
-
- malloc_mutex_lock(tsd_tsdn(tsd), &bt2gctx_mtx);
-}
-
-static void
-prof_leave(tsd_t *tsd, prof_tdata_t *tdata) {
- cassert(config_prof);
- assert(tdata == prof_tdata_get(tsd, false));
-
- malloc_mutex_unlock(tsd_tsdn(tsd), &bt2gctx_mtx);
-
- if (tdata != NULL) {
- bool idump, gdump;
-
- assert(tdata->enq);
- tdata->enq = false;
- idump = tdata->enq_idump;
- tdata->enq_idump = false;
- gdump = tdata->enq_gdump;
- tdata->enq_gdump = false;
-
- if (idump) {
- prof_idump(tsd_tsdn(tsd));
- }
- if (gdump) {
- prof_gdump(tsd_tsdn(tsd));
- }
- }
-}
-
-#ifdef JEMALLOC_PROF_LIBUNWIND
-void
-prof_backtrace(prof_bt_t *bt) {
- int nframes;
-
- cassert(config_prof);
- assert(bt->len == 0);
- assert(bt->vec != NULL);
-
- nframes = unw_backtrace(bt->vec, PROF_BT_MAX);
- if (nframes <= 0) {
- return;
- }
- bt->len = nframes;
-}
-#elif (defined(JEMALLOC_PROF_LIBGCC))
-static _Unwind_Reason_Code
-prof_unwind_init_callback(struct _Unwind_Context *context, void *arg) {
- cassert(config_prof);
-
- return _URC_NO_REASON;
-}
-
-static _Unwind_Reason_Code
-prof_unwind_callback(struct _Unwind_Context *context, void *arg) {
- prof_unwind_data_t *data = (prof_unwind_data_t *)arg;
- void *ip;
-
- cassert(config_prof);
-
- ip = (void *)_Unwind_GetIP(context);
- if (ip == NULL) {
- return _URC_END_OF_STACK;
- }
- data->bt->vec[data->bt->len] = ip;
- data->bt->len++;
- if (data->bt->len == data->max) {
- return _URC_END_OF_STACK;
- }
-
- return _URC_NO_REASON;
-}
+ prof_try_log(tsd, usize, prof_info);
-void
-prof_backtrace(prof_bt_t *bt) {
- prof_unwind_data_t data = {bt, PROF_BT_MAX};
-
- cassert(config_prof);
+ prof_tctx_try_destroy(tsd, tctx);
- _Unwind_Backtrace(prof_unwind_callback, &data);
-}
-#elif (defined(JEMALLOC_PROF_GCC))
-void
-prof_backtrace(prof_bt_t *bt) {
-#define BT_FRAME(i) \
- if ((i) < PROF_BT_MAX) { \
- void *p; \
- if (__builtin_frame_address(i) == 0) { \
- return; \
- } \
- p = __builtin_return_address(i); \
- if (p == NULL) { \
- return; \
- } \
- bt->vec[(i)] = p; \
- bt->len = (i) + 1; \
- } else { \
- return; \
+ if (opt_prof_stats) {
+ prof_stats_dec(tsd, szind, prof_info->alloc_size);
}
-
- cassert(config_prof);
-
- BT_FRAME(0)
- BT_FRAME(1)
- BT_FRAME(2)
- BT_FRAME(3)
- BT_FRAME(4)
- BT_FRAME(5)
- BT_FRAME(6)
- BT_FRAME(7)
- BT_FRAME(8)
- BT_FRAME(9)
-
- BT_FRAME(10)
- BT_FRAME(11)
- BT_FRAME(12)
- BT_FRAME(13)
- BT_FRAME(14)
- BT_FRAME(15)
- BT_FRAME(16)
- BT_FRAME(17)
- BT_FRAME(18)
- BT_FRAME(19)
-
- BT_FRAME(20)
- BT_FRAME(21)
- BT_FRAME(22)
- BT_FRAME(23)
- BT_FRAME(24)
- BT_FRAME(25)
- BT_FRAME(26)
- BT_FRAME(27)
- BT_FRAME(28)
- BT_FRAME(29)
-
- BT_FRAME(30)
- BT_FRAME(31)
- BT_FRAME(32)
- BT_FRAME(33)
- BT_FRAME(34)
- BT_FRAME(35)
- BT_FRAME(36)
- BT_FRAME(37)
- BT_FRAME(38)
- BT_FRAME(39)
-
- BT_FRAME(40)
- BT_FRAME(41)
- BT_FRAME(42)
- BT_FRAME(43)
- BT_FRAME(44)
- BT_FRAME(45)
- BT_FRAME(46)
- BT_FRAME(47)
- BT_FRAME(48)
- BT_FRAME(49)
-
- BT_FRAME(50)
- BT_FRAME(51)
- BT_FRAME(52)
- BT_FRAME(53)
- BT_FRAME(54)
- BT_FRAME(55)
- BT_FRAME(56)
- BT_FRAME(57)
- BT_FRAME(58)
- BT_FRAME(59)
-
- BT_FRAME(60)
- BT_FRAME(61)
- BT_FRAME(62)
- BT_FRAME(63)
- BT_FRAME(64)
- BT_FRAME(65)
- BT_FRAME(66)
- BT_FRAME(67)
- BT_FRAME(68)
- BT_FRAME(69)
-
- BT_FRAME(70)
- BT_FRAME(71)
- BT_FRAME(72)
- BT_FRAME(73)
- BT_FRAME(74)
- BT_FRAME(75)
- BT_FRAME(76)
- BT_FRAME(77)
- BT_FRAME(78)
- BT_FRAME(79)
-
- BT_FRAME(80)
- BT_FRAME(81)
- BT_FRAME(82)
- BT_FRAME(83)
- BT_FRAME(84)
- BT_FRAME(85)
- BT_FRAME(86)
- BT_FRAME(87)
- BT_FRAME(88)
- BT_FRAME(89)
-
- BT_FRAME(90)
- BT_FRAME(91)
- BT_FRAME(92)
- BT_FRAME(93)
- BT_FRAME(94)
- BT_FRAME(95)
- BT_FRAME(96)
- BT_FRAME(97)
- BT_FRAME(98)
- BT_FRAME(99)
-
- BT_FRAME(100)
- BT_FRAME(101)
- BT_FRAME(102)
- BT_FRAME(103)
- BT_FRAME(104)
- BT_FRAME(105)
- BT_FRAME(106)
- BT_FRAME(107)
- BT_FRAME(108)
- BT_FRAME(109)
-
- BT_FRAME(110)
- BT_FRAME(111)
- BT_FRAME(112)
- BT_FRAME(113)
- BT_FRAME(114)
- BT_FRAME(115)
- BT_FRAME(116)
- BT_FRAME(117)
- BT_FRAME(118)
- BT_FRAME(119)
-
- BT_FRAME(120)
- BT_FRAME(121)
- BT_FRAME(122)
- BT_FRAME(123)
- BT_FRAME(124)
- BT_FRAME(125)
- BT_FRAME(126)
- BT_FRAME(127)
-#undef BT_FRAME
-}
-#else
-void
-prof_backtrace(prof_bt_t *bt) {
- cassert(config_prof);
- not_reached();
-}
-#endif
-
-static malloc_mutex_t *
-prof_gctx_mutex_choose(void) {
- unsigned ngctxs = atomic_fetch_add_u(&cum_gctxs, 1, ATOMIC_RELAXED);
-
- return &gctx_locks[(ngctxs - 1) % PROF_NCTX_LOCKS];
}
-static malloc_mutex_t *
-prof_tdata_mutex_choose(uint64_t thr_uid) {
- return &tdata_locks[thr_uid % PROF_NTDATA_LOCKS];
-}
-
-static prof_gctx_t *
-prof_gctx_create(tsdn_t *tsdn, prof_bt_t *bt) {
- /*
- * Create a single allocation that has space for vec of length bt->len.
- */
- size_t size = offsetof(prof_gctx_t, vec) + (bt->len * sizeof(void *));
- prof_gctx_t *gctx = (prof_gctx_t *)iallocztm(tsdn, size,
- sz_size2index(size), false, NULL, true, arena_get(TSDN_NULL, 0, true),
- true);
- if (gctx == NULL) {
+prof_tctx_t *
+prof_tctx_create(tsd_t *tsd) {
+ if (!tsd_nominal(tsd) || tsd_reentrancy_level_get(tsd) > 0) {
return NULL;
}
- gctx->lock = prof_gctx_mutex_choose();
- /*
- * Set nlimbo to 1, in order to avoid a race condition with
- * prof_tctx_destroy()/prof_gctx_try_destroy().
- */
- gctx->nlimbo = 1;
- tctx_tree_new(&gctx->tctxs);
- /* Duplicate bt. */
- memcpy(gctx->vec, bt->vec, bt->len * sizeof(void *));
- gctx->bt.vec = gctx->vec;
- gctx->bt.len = bt->len;
- return gctx;
-}
-
-static void
-prof_gctx_try_destroy(tsd_t *tsd, prof_tdata_t *tdata_self, prof_gctx_t *gctx,
- prof_tdata_t *tdata) {
- cassert(config_prof);
-
- /*
- * Check that gctx is still unused by any thread cache before destroying
- * it. prof_lookup() increments gctx->nlimbo in order to avoid a race
- * condition with this function, as does prof_tctx_destroy() in order to
- * avoid a race between the main body of prof_tctx_destroy() and entry
- * into this function.
- */
- prof_enter(tsd, tdata_self);
- malloc_mutex_lock(tsd_tsdn(tsd), gctx->lock);
- assert(gctx->nlimbo != 0);
- if (tctx_tree_empty(&gctx->tctxs) && gctx->nlimbo == 1) {
- /* Remove gctx from bt2gctx. */
- if (ckh_remove(tsd, &bt2gctx, &gctx->bt, NULL, NULL)) {
- not_reached();
- }
- prof_leave(tsd, tdata_self);
- /* Destroy gctx. */
- malloc_mutex_unlock(tsd_tsdn(tsd), gctx->lock);
- idalloctm(tsd_tsdn(tsd), gctx, NULL, NULL, true, true);
- } else {
- /*
- * Compensate for increment in prof_tctx_destroy() or
- * prof_lookup().
- */
- gctx->nlimbo--;
- malloc_mutex_unlock(tsd_tsdn(tsd), gctx->lock);
- prof_leave(tsd, tdata_self);
- }
-}
-
-static bool
-prof_tctx_should_destroy(tsdn_t *tsdn, prof_tctx_t *tctx) {
- malloc_mutex_assert_owner(tsdn, tctx->tdata->lock);
-
- if (opt_prof_accum) {
- return false;
- }
- if (tctx->cnts.curobjs != 0) {
- return false;
- }
- if (tctx->prepared) {
- return false;
- }
- return true;
-}
-static bool
-prof_gctx_should_destroy(prof_gctx_t *gctx) {
- if (opt_prof_accum) {
- return false;
- }
- if (!tctx_tree_empty(&gctx->tctxs)) {
- return false;
- }
- if (gctx->nlimbo != 0) {
- return false;
- }
- return true;
-}
-
-static void
-prof_tctx_destroy(tsd_t *tsd, prof_tctx_t *tctx) {
- prof_tdata_t *tdata = tctx->tdata;
- prof_gctx_t *gctx = tctx->gctx;
- bool destroy_tdata, destroy_tctx, destroy_gctx;
-
- malloc_mutex_assert_owner(tsd_tsdn(tsd), tctx->tdata->lock);
-
- assert(tctx->cnts.curobjs == 0);
- assert(tctx->cnts.curbytes == 0);
- assert(!opt_prof_accum);
- assert(tctx->cnts.accumobjs == 0);
- assert(tctx->cnts.accumbytes == 0);
-
- ckh_remove(tsd, &tdata->bt2tctx, &gctx->bt, NULL, NULL);
- destroy_tdata = prof_tdata_should_destroy(tsd_tsdn(tsd), tdata, false);
- malloc_mutex_unlock(tsd_tsdn(tsd), tdata->lock);
-
- malloc_mutex_lock(tsd_tsdn(tsd), gctx->lock);
- switch (tctx->state) {
- case prof_tctx_state_nominal:
- tctx_tree_remove(&gctx->tctxs, tctx);
- destroy_tctx = true;
- if (prof_gctx_should_destroy(gctx)) {
- /*
- * Increment gctx->nlimbo in order to keep another
- * thread from winning the race to destroy gctx while
- * this one has gctx->lock dropped. Without this, it
- * would be possible for another thread to:
- *
- * 1) Sample an allocation associated with gctx.
- * 2) Deallocate the sampled object.
- * 3) Successfully prof_gctx_try_destroy(gctx).
- *
- * The result would be that gctx no longer exists by the
- * time this thread accesses it in
- * prof_gctx_try_destroy().
- */
- gctx->nlimbo++;
- destroy_gctx = true;
- } else {
- destroy_gctx = false;
- }
- break;
- case prof_tctx_state_dumping:
- /*
- * A dumping thread needs tctx to remain valid until dumping
- * has finished. Change state such that the dumping thread will
- * complete destruction during a late dump iteration phase.
- */
- tctx->state = prof_tctx_state_purgatory;
- destroy_tctx = false;
- destroy_gctx = false;
- break;
- default:
- not_reached();
- destroy_tctx = false;
- destroy_gctx = false;
- }
- malloc_mutex_unlock(tsd_tsdn(tsd), gctx->lock);
- if (destroy_gctx) {
- prof_gctx_try_destroy(tsd, prof_tdata_get(tsd, false), gctx,
- tdata);
- }
-
- malloc_mutex_assert_not_owner(tsd_tsdn(tsd), tctx->tdata->lock);
-
- if (destroy_tdata) {
- prof_tdata_destroy(tsd, tdata, false);
- }
-
- if (destroy_tctx) {
- idalloctm(tsd_tsdn(tsd), tctx, NULL, NULL, true, true);
- }
-}
-
-static bool
-prof_lookup_global(tsd_t *tsd, prof_bt_t *bt, prof_tdata_t *tdata,
- void **p_btkey, prof_gctx_t **p_gctx, bool *p_new_gctx) {
- union {
- prof_gctx_t *p;
- void *v;
- } gctx, tgctx;
- union {
- prof_bt_t *p;
- void *v;
- } btkey;
- bool new_gctx;
-
- prof_enter(tsd, tdata);
- if (ckh_search(&bt2gctx, bt, &btkey.v, &gctx.v)) {
- /* bt has never been seen before. Insert it. */
- prof_leave(tsd, tdata);
- tgctx.p = prof_gctx_create(tsd_tsdn(tsd), bt);
- if (tgctx.v == NULL) {
- return true;
- }
- prof_enter(tsd, tdata);
- if (ckh_search(&bt2gctx, bt, &btkey.v, &gctx.v)) {
- gctx.p = tgctx.p;
- btkey.p = &gctx.p->bt;
- if (ckh_insert(tsd, &bt2gctx, btkey.v, gctx.v)) {
- /* OOM. */
- prof_leave(tsd, tdata);
- idalloctm(tsd_tsdn(tsd), gctx.v, NULL, NULL,
- true, true);
- return true;
- }
- new_gctx = true;
- } else {
- new_gctx = false;
- }
- } else {
- tgctx.v = NULL;
- new_gctx = false;
- }
-
- if (!new_gctx) {
- /*
- * Increment nlimbo, in order to avoid a race condition with
- * prof_tctx_destroy()/prof_gctx_try_destroy().
- */
- malloc_mutex_lock(tsd_tsdn(tsd), gctx.p->lock);
- gctx.p->nlimbo++;
- malloc_mutex_unlock(tsd_tsdn(tsd), gctx.p->lock);
- new_gctx = false;
-
- if (tgctx.v != NULL) {
- /* Lost race to insert. */
- idalloctm(tsd_tsdn(tsd), tgctx.v, NULL, NULL, true,
- true);
- }
- }
- prof_leave(tsd, tdata);
-
- *p_btkey = btkey.v;
- *p_gctx = gctx.p;
- *p_new_gctx = new_gctx;
- return false;
-}
-
-prof_tctx_t *
-prof_lookup(tsd_t *tsd, prof_bt_t *bt) {
- union {
- prof_tctx_t *p;
- void *v;
- } ret;
- prof_tdata_t *tdata;
- bool not_found;
-
- cassert(config_prof);
-
- tdata = prof_tdata_get(tsd, false);
+ prof_tdata_t *tdata = prof_tdata_get(tsd, true);
if (tdata == NULL) {
return NULL;
}
- malloc_mutex_lock(tsd_tsdn(tsd), tdata->lock);
- not_found = ckh_search(&tdata->bt2tctx, bt, NULL, &ret.v);
- if (!not_found) { /* Note double negative! */
- ret.p->prepared = true;
- }
- malloc_mutex_unlock(tsd_tsdn(tsd), tdata->lock);
- if (not_found) {
- void *btkey;
- prof_gctx_t *gctx;
- bool new_gctx, error;
-
- /*
- * This thread's cache lacks bt. Look for it in the global
- * cache.
- */
- if (prof_lookup_global(tsd, bt, tdata, &btkey, &gctx,
- &new_gctx)) {
- return NULL;
- }
-
- /* Link a prof_tctx_t into gctx for this thread. */
- ret.v = iallocztm(tsd_tsdn(tsd), sizeof(prof_tctx_t),
- sz_size2index(sizeof(prof_tctx_t)), false, NULL, true,
- arena_ichoose(tsd, NULL), true);
- if (ret.p == NULL) {
- if (new_gctx) {
- prof_gctx_try_destroy(tsd, tdata, gctx, tdata);
- }
- return NULL;
- }
- ret.p->tdata = tdata;
- ret.p->thr_uid = tdata->thr_uid;
- ret.p->thr_discrim = tdata->thr_discrim;
- memset(&ret.p->cnts, 0, sizeof(prof_cnt_t));
- ret.p->gctx = gctx;
- ret.p->tctx_uid = tdata->tctx_uid_next++;
- ret.p->prepared = true;
- ret.p->state = prof_tctx_state_initializing;
- malloc_mutex_lock(tsd_tsdn(tsd), tdata->lock);
- error = ckh_insert(tsd, &tdata->bt2tctx, btkey, ret.v);
- malloc_mutex_unlock(tsd_tsdn(tsd), tdata->lock);
- if (error) {
- if (new_gctx) {
- prof_gctx_try_destroy(tsd, tdata, gctx, tdata);
- }
- idalloctm(tsd_tsdn(tsd), ret.v, NULL, NULL, true, true);
- return NULL;
- }
- malloc_mutex_lock(tsd_tsdn(tsd), gctx->lock);
- ret.p->state = prof_tctx_state_nominal;
- tctx_tree_insert(&gctx->tctxs, ret.p);
- gctx->nlimbo--;
- malloc_mutex_unlock(tsd_tsdn(tsd), gctx->lock);
- }
-
- return ret.p;
+ prof_bt_t bt;
+ bt_init(&bt, tdata->vec);
+ prof_backtrace(tsd, &bt);
+ return prof_lookup(tsd, &bt);
}
/*
@@ -1136,27 +209,22 @@ prof_lookup(tsd_t *tsd, prof_bt_t *bt) {
* (e.g.
* -mno-sse) in order for the workaround to be complete.
*/
-void
-prof_sample_threshold_update(prof_tdata_t *tdata) {
+uint64_t
+prof_sample_new_event_wait(tsd_t *tsd) {
#ifdef JEMALLOC_PROF
- if (!config_prof) {
- return;
- }
-
if (lg_prof_sample == 0) {
- tsd_bytes_until_sample_set(tsd_fetch(), 0);
- return;
+ return TE_MIN_START_WAIT;
}
/*
* Compute sample interval as a geometrically distributed random
* variable with mean (2^lg_prof_sample).
*
- * __ __
- * | log(u) | 1
- * tdata->bytes_until_sample = | -------- |, where p = ---------------
- * | log(1-p) | lg_prof_sample
- * 2
+ * __ __
+ * | log(u) | 1
+ * bytes_until_sample = | -------- |, where p = ---------------
+ * | log(1-p) | lg_prof_sample
+ * 2
*
* For more information on the math, see:
*
@@ -1165,857 +233,56 @@ prof_sample_threshold_update(prof_tdata_t *tdata) {
* Springer-Verlag, New York, 1986
* pp 500
* (http://luc.devroye.org/rnbookindex.html)
+ *
+ * In the actual computation, there's a non-zero probability that our
+ * pseudo random number generator generates an exact 0, and to avoid
+ * log(0), we set u to 1.0 in case r is 0. Therefore u effectively is
+ * uniformly distributed in (0, 1] instead of [0, 1). Further, rather
+ * than taking the ceiling, we take the floor and then add 1, since
+ * otherwise bytes_until_sample would be 0 if u is exactly 1.0.
*/
- uint64_t r = prng_lg_range_u64(&tdata->prng_state, 53);
- double u = (double)r * (1.0/9007199254740992.0L);
- uint64_t bytes_until_sample = (uint64_t)(log(u) /
+ uint64_t r = prng_lg_range_u64(tsd_prng_statep_get(tsd), 53);
+ double u = (r == 0U) ? 1.0 : (double)r * (1.0/9007199254740992.0L);
+ return (uint64_t)(log(u) /
log(1.0 - (1.0 / (double)((uint64_t)1U << lg_prof_sample))))
+ (uint64_t)1U;
- if (bytes_until_sample > SSIZE_MAX) {
- bytes_until_sample = SSIZE_MAX;
- }
- tsd_bytes_until_sample_set(tsd_fetch(), bytes_until_sample);
-
-#endif
-}
-
-#ifdef JEMALLOC_JET
-static prof_tdata_t *
-prof_tdata_count_iter(prof_tdata_tree_t *tdatas, prof_tdata_t *tdata,
- void *arg) {
- size_t *tdata_count = (size_t *)arg;
-
- (*tdata_count)++;
-
- return NULL;
-}
-
-size_t
-prof_tdata_count(void) {
- size_t tdata_count = 0;
- tsdn_t *tsdn;
-
- tsdn = tsdn_fetch();
- malloc_mutex_lock(tsdn, &tdatas_mtx);
- tdata_tree_iter(&tdatas, NULL, prof_tdata_count_iter,
- (void *)&tdata_count);
- malloc_mutex_unlock(tsdn, &tdatas_mtx);
-
- return tdata_count;
-}
-
-size_t
-prof_bt_count(void) {
- size_t bt_count;
- tsd_t *tsd;
- prof_tdata_t *tdata;
-
- tsd = tsd_fetch();
- tdata = prof_tdata_get(tsd, false);
- if (tdata == NULL) {
- return 0;
- }
-
- malloc_mutex_lock(tsd_tsdn(tsd), &bt2gctx_mtx);
- bt_count = ckh_count(&bt2gctx);
- malloc_mutex_unlock(tsd_tsdn(tsd), &bt2gctx_mtx);
-
- return bt_count;
-}
-#endif
-
-static int
-prof_dump_open_impl(bool propagate_err, const char *filename) {
- int fd;
-
- fd = creat(filename, 0644);
- if (fd == -1 && !propagate_err) {
- malloc_printf("<jemalloc>: creat(\"%s\"), 0644) failed\n",
- filename);
- if (opt_abort) {
- abort();
- }
- }
-
- return fd;
-}
-prof_dump_open_t *JET_MUTABLE prof_dump_open = prof_dump_open_impl;
-
-static bool
-prof_dump_flush(bool propagate_err) {
- bool ret = false;
- ssize_t err;
-
- cassert(config_prof);
-
- err = malloc_write_fd(prof_dump_fd, prof_dump_buf, prof_dump_buf_end);
- if (err == -1) {
- if (!propagate_err) {
- malloc_write("<jemalloc>: write() failed during heap "
- "profile flush\n");
- if (opt_abort) {
- abort();
- }
- }
- ret = true;
- }
- prof_dump_buf_end = 0;
-
- return ret;
-}
-
-static bool
-prof_dump_close(bool propagate_err) {
- bool ret;
-
- assert(prof_dump_fd != -1);
- ret = prof_dump_flush(propagate_err);
- close(prof_dump_fd);
- prof_dump_fd = -1;
-
- return ret;
-}
-
-static bool
-prof_dump_write(bool propagate_err, const char *s) {
- size_t i, slen, n;
-
- cassert(config_prof);
-
- i = 0;
- slen = strlen(s);
- while (i < slen) {
- /* Flush the buffer if it is full. */
- if (prof_dump_buf_end == PROF_DUMP_BUFSIZE) {
- if (prof_dump_flush(propagate_err) && propagate_err) {
- return true;
- }
- }
-
- if (prof_dump_buf_end + slen - i <= PROF_DUMP_BUFSIZE) {
- /* Finish writing. */
- n = slen - i;
- } else {
- /* Write as much of s as will fit. */
- n = PROF_DUMP_BUFSIZE - prof_dump_buf_end;
- }
- memcpy(&prof_dump_buf[prof_dump_buf_end], &s[i], n);
- prof_dump_buf_end += n;
- i += n;
- }
- assert(i == slen);
-
- return false;
-}
-
-JEMALLOC_FORMAT_PRINTF(2, 3)
-static bool
-prof_dump_printf(bool propagate_err, const char *format, ...) {
- bool ret;
- va_list ap;
- char buf[PROF_PRINTF_BUFSIZE];
-
- va_start(ap, format);
- malloc_vsnprintf(buf, sizeof(buf), format, ap);
- va_end(ap);
- ret = prof_dump_write(propagate_err, buf);
-
- return ret;
-}
-
-static void
-prof_tctx_merge_tdata(tsdn_t *tsdn, prof_tctx_t *tctx, prof_tdata_t *tdata) {
- malloc_mutex_assert_owner(tsdn, tctx->tdata->lock);
-
- malloc_mutex_lock(tsdn, tctx->gctx->lock);
-
- switch (tctx->state) {
- case prof_tctx_state_initializing:
- malloc_mutex_unlock(tsdn, tctx->gctx->lock);
- return;
- case prof_tctx_state_nominal:
- tctx->state = prof_tctx_state_dumping;
- malloc_mutex_unlock(tsdn, tctx->gctx->lock);
-
- memcpy(&tctx->dump_cnts, &tctx->cnts, sizeof(prof_cnt_t));
-
- tdata->cnt_summed.curobjs += tctx->dump_cnts.curobjs;
- tdata->cnt_summed.curbytes += tctx->dump_cnts.curbytes;
- if (opt_prof_accum) {
- tdata->cnt_summed.accumobjs +=
- tctx->dump_cnts.accumobjs;
- tdata->cnt_summed.accumbytes +=
- tctx->dump_cnts.accumbytes;
- }
- break;
- case prof_tctx_state_dumping:
- case prof_tctx_state_purgatory:
- not_reached();
- }
-}
-
-static void
-prof_tctx_merge_gctx(tsdn_t *tsdn, prof_tctx_t *tctx, prof_gctx_t *gctx) {
- malloc_mutex_assert_owner(tsdn, gctx->lock);
-
- gctx->cnt_summed.curobjs += tctx->dump_cnts.curobjs;
- gctx->cnt_summed.curbytes += tctx->dump_cnts.curbytes;
- if (opt_prof_accum) {
- gctx->cnt_summed.accumobjs += tctx->dump_cnts.accumobjs;
- gctx->cnt_summed.accumbytes += tctx->dump_cnts.accumbytes;
- }
-}
-
-static prof_tctx_t *
-prof_tctx_merge_iter(prof_tctx_tree_t *tctxs, prof_tctx_t *tctx, void *arg) {
- tsdn_t *tsdn = (tsdn_t *)arg;
-
- malloc_mutex_assert_owner(tsdn, tctx->gctx->lock);
-
- switch (tctx->state) {
- case prof_tctx_state_nominal:
- /* New since dumping started; ignore. */
- break;
- case prof_tctx_state_dumping:
- case prof_tctx_state_purgatory:
- prof_tctx_merge_gctx(tsdn, tctx, tctx->gctx);
- break;
- default:
- not_reached();
- }
-
- return NULL;
-}
-
-struct prof_tctx_dump_iter_arg_s {
- tsdn_t *tsdn;
- bool propagate_err;
-};
-
-static prof_tctx_t *
-prof_tctx_dump_iter(prof_tctx_tree_t *tctxs, prof_tctx_t *tctx, void *opaque) {
- struct prof_tctx_dump_iter_arg_s *arg =
- (struct prof_tctx_dump_iter_arg_s *)opaque;
-
- malloc_mutex_assert_owner(arg->tsdn, tctx->gctx->lock);
-
- switch (tctx->state) {
- case prof_tctx_state_initializing:
- case prof_tctx_state_nominal:
- /* Not captured by this dump. */
- break;
- case prof_tctx_state_dumping:
- case prof_tctx_state_purgatory:
- if (prof_dump_printf(arg->propagate_err,
- " t%"FMTu64": %"FMTu64": %"FMTu64" [%"FMTu64": "
- "%"FMTu64"]\n", tctx->thr_uid, tctx->dump_cnts.curobjs,
- tctx->dump_cnts.curbytes, tctx->dump_cnts.accumobjs,
- tctx->dump_cnts.accumbytes)) {
- return tctx;
- }
- break;
- default:
- not_reached();
- }
- return NULL;
-}
-
-static prof_tctx_t *
-prof_tctx_finish_iter(prof_tctx_tree_t *tctxs, prof_tctx_t *tctx, void *arg) {
- tsdn_t *tsdn = (tsdn_t *)arg;
- prof_tctx_t *ret;
-
- malloc_mutex_assert_owner(tsdn, tctx->gctx->lock);
-
- switch (tctx->state) {
- case prof_tctx_state_nominal:
- /* New since dumping started; ignore. */
- break;
- case prof_tctx_state_dumping:
- tctx->state = prof_tctx_state_nominal;
- break;
- case prof_tctx_state_purgatory:
- ret = tctx;
- goto label_return;
- default:
- not_reached();
- }
-
- ret = NULL;
-label_return:
- return ret;
-}
-
-static void
-prof_dump_gctx_prep(tsdn_t *tsdn, prof_gctx_t *gctx, prof_gctx_tree_t *gctxs) {
- cassert(config_prof);
-
- malloc_mutex_lock(tsdn, gctx->lock);
-
- /*
- * Increment nlimbo so that gctx won't go away before dump.
- * Additionally, link gctx into the dump list so that it is included in
- * prof_dump()'s second pass.
- */
- gctx->nlimbo++;
- gctx_tree_insert(gctxs, gctx);
-
- memset(&gctx->cnt_summed, 0, sizeof(prof_cnt_t));
-
- malloc_mutex_unlock(tsdn, gctx->lock);
-}
-
-struct prof_gctx_merge_iter_arg_s {
- tsdn_t *tsdn;
- size_t leak_ngctx;
-};
-
-static prof_gctx_t *
-prof_gctx_merge_iter(prof_gctx_tree_t *gctxs, prof_gctx_t *gctx, void *opaque) {
- struct prof_gctx_merge_iter_arg_s *arg =
- (struct prof_gctx_merge_iter_arg_s *)opaque;
-
- malloc_mutex_lock(arg->tsdn, gctx->lock);
- tctx_tree_iter(&gctx->tctxs, NULL, prof_tctx_merge_iter,
- (void *)arg->tsdn);
- if (gctx->cnt_summed.curobjs != 0) {
- arg->leak_ngctx++;
- }
- malloc_mutex_unlock(arg->tsdn, gctx->lock);
-
- return NULL;
-}
-
-static void
-prof_gctx_finish(tsd_t *tsd, prof_gctx_tree_t *gctxs) {
- prof_tdata_t *tdata = prof_tdata_get(tsd, false);
- prof_gctx_t *gctx;
-
- /*
- * Standard tree iteration won't work here, because as soon as we
- * decrement gctx->nlimbo and unlock gctx, another thread can
- * concurrently destroy it, which will corrupt the tree. Therefore,
- * tear down the tree one node at a time during iteration.
- */
- while ((gctx = gctx_tree_first(gctxs)) != NULL) {
- gctx_tree_remove(gctxs, gctx);
- malloc_mutex_lock(tsd_tsdn(tsd), gctx->lock);
- {
- prof_tctx_t *next;
-
- next = NULL;
- do {
- prof_tctx_t *to_destroy =
- tctx_tree_iter(&gctx->tctxs, next,
- prof_tctx_finish_iter,
- (void *)tsd_tsdn(tsd));
- if (to_destroy != NULL) {
- next = tctx_tree_next(&gctx->tctxs,
- to_destroy);
- tctx_tree_remove(&gctx->tctxs,
- to_destroy);
- idalloctm(tsd_tsdn(tsd), to_destroy,
- NULL, NULL, true, true);
- } else {
- next = NULL;
- }
- } while (next != NULL);
- }
- gctx->nlimbo--;
- if (prof_gctx_should_destroy(gctx)) {
- gctx->nlimbo++;
- malloc_mutex_unlock(tsd_tsdn(tsd), gctx->lock);
- prof_gctx_try_destroy(tsd, tdata, gctx, tdata);
- } else {
- malloc_mutex_unlock(tsd_tsdn(tsd), gctx->lock);
- }
- }
-}
-
-struct prof_tdata_merge_iter_arg_s {
- tsdn_t *tsdn;
- prof_cnt_t cnt_all;
-};
-
-static prof_tdata_t *
-prof_tdata_merge_iter(prof_tdata_tree_t *tdatas, prof_tdata_t *tdata,
- void *opaque) {
- struct prof_tdata_merge_iter_arg_s *arg =
- (struct prof_tdata_merge_iter_arg_s *)opaque;
-
- malloc_mutex_lock(arg->tsdn, tdata->lock);
- if (!tdata->expired) {
- size_t tabind;
- union {
- prof_tctx_t *p;
- void *v;
- } tctx;
-
- tdata->dumping = true;
- memset(&tdata->cnt_summed, 0, sizeof(prof_cnt_t));
- for (tabind = 0; !ckh_iter(&tdata->bt2tctx, &tabind, NULL,
- &tctx.v);) {
- prof_tctx_merge_tdata(arg->tsdn, tctx.p, tdata);
- }
-
- arg->cnt_all.curobjs += tdata->cnt_summed.curobjs;
- arg->cnt_all.curbytes += tdata->cnt_summed.curbytes;
- if (opt_prof_accum) {
- arg->cnt_all.accumobjs += tdata->cnt_summed.accumobjs;
- arg->cnt_all.accumbytes += tdata->cnt_summed.accumbytes;
- }
- } else {
- tdata->dumping = false;
- }
- malloc_mutex_unlock(arg->tsdn, tdata->lock);
-
- return NULL;
-}
-
-static prof_tdata_t *
-prof_tdata_dump_iter(prof_tdata_tree_t *tdatas, prof_tdata_t *tdata,
- void *arg) {
- bool propagate_err = *(bool *)arg;
-
- if (!tdata->dumping) {
- return NULL;
- }
-
- if (prof_dump_printf(propagate_err,
- " t%"FMTu64": %"FMTu64": %"FMTu64" [%"FMTu64": %"FMTu64"]%s%s\n",
- tdata->thr_uid, tdata->cnt_summed.curobjs,
- tdata->cnt_summed.curbytes, tdata->cnt_summed.accumobjs,
- tdata->cnt_summed.accumbytes,
- (tdata->thread_name != NULL) ? " " : "",
- (tdata->thread_name != NULL) ? tdata->thread_name : "")) {
- return tdata;
- }
- return NULL;
-}
-
-static bool
-prof_dump_header_impl(tsdn_t *tsdn, bool propagate_err,
- const prof_cnt_t *cnt_all) {
- bool ret;
-
- if (prof_dump_printf(propagate_err,
- "heap_v2/%"FMTu64"\n"
- " t*: %"FMTu64": %"FMTu64" [%"FMTu64": %"FMTu64"]\n",
- ((uint64_t)1U << lg_prof_sample), cnt_all->curobjs,
- cnt_all->curbytes, cnt_all->accumobjs, cnt_all->accumbytes)) {
- return true;
- }
-
- malloc_mutex_lock(tsdn, &tdatas_mtx);
- ret = (tdata_tree_iter(&tdatas, NULL, prof_tdata_dump_iter,
- (void *)&propagate_err) != NULL);
- malloc_mutex_unlock(tsdn, &tdatas_mtx);
- return ret;
-}
-prof_dump_header_t *JET_MUTABLE prof_dump_header = prof_dump_header_impl;
-
-static bool
-prof_dump_gctx(tsdn_t *tsdn, bool propagate_err, prof_gctx_t *gctx,
- const prof_bt_t *bt, prof_gctx_tree_t *gctxs) {
- bool ret;
- unsigned i;
- struct prof_tctx_dump_iter_arg_s prof_tctx_dump_iter_arg;
-
- cassert(config_prof);
- malloc_mutex_assert_owner(tsdn, gctx->lock);
-
- /* Avoid dumping such gctx's that have no useful data. */
- if ((!opt_prof_accum && gctx->cnt_summed.curobjs == 0) ||
- (opt_prof_accum && gctx->cnt_summed.accumobjs == 0)) {
- assert(gctx->cnt_summed.curobjs == 0);
- assert(gctx->cnt_summed.curbytes == 0);
- assert(gctx->cnt_summed.accumobjs == 0);
- assert(gctx->cnt_summed.accumbytes == 0);
- ret = false;
- goto label_return;
- }
-
- if (prof_dump_printf(propagate_err, "@")) {
- ret = true;
- goto label_return;
- }
- for (i = 0; i < bt->len; i++) {
- if (prof_dump_printf(propagate_err, " %#"FMTxPTR,
- (uintptr_t)bt->vec[i])) {
- ret = true;
- goto label_return;
- }
- }
-
- if (prof_dump_printf(propagate_err,
- "\n"
- " t*: %"FMTu64": %"FMTu64" [%"FMTu64": %"FMTu64"]\n",
- gctx->cnt_summed.curobjs, gctx->cnt_summed.curbytes,
- gctx->cnt_summed.accumobjs, gctx->cnt_summed.accumbytes)) {
- ret = true;
- goto label_return;
- }
-
- prof_tctx_dump_iter_arg.tsdn = tsdn;
- prof_tctx_dump_iter_arg.propagate_err = propagate_err;
- if (tctx_tree_iter(&gctx->tctxs, NULL, prof_tctx_dump_iter,
- (void *)&prof_tctx_dump_iter_arg) != NULL) {
- ret = true;
- goto label_return;
- }
-
- ret = false;
-label_return:
- return ret;
-}
-
-#ifndef _WIN32
-JEMALLOC_FORMAT_PRINTF(1, 2)
-static int
-prof_open_maps(const char *format, ...) {
- int mfd;
- va_list ap;
- char filename[PATH_MAX + 1];
-
- va_start(ap, format);
- malloc_vsnprintf(filename, sizeof(filename), format, ap);
- va_end(ap);
-
-#if defined(O_CLOEXEC)
- mfd = open(filename, O_RDONLY | O_CLOEXEC);
-#else
- mfd = open(filename, O_RDONLY);
- if (mfd != -1) {
- fcntl(mfd, F_SETFD, fcntl(mfd, F_GETFD) | FD_CLOEXEC);
- }
-#endif
-
- return mfd;
-}
-#endif
-
-static int
-prof_getpid(void) {
-#ifdef _WIN32
- return GetCurrentProcessId();
#else
- return getpid();
-#endif
-}
-
-static bool
-prof_dump_maps(bool propagate_err) {
- bool ret;
- int mfd;
-
- cassert(config_prof);
-#ifdef __FreeBSD__
- mfd = prof_open_maps("/proc/curproc/map");
-#elif defined(_WIN32)
- mfd = -1; // Not implemented
-#else
- {
- int pid = prof_getpid();
-
- mfd = prof_open_maps("/proc/%d/task/%d/maps", pid, pid);
- if (mfd == -1) {
- mfd = prof_open_maps("/proc/%d/maps", pid);
- }
- }
-#endif
- if (mfd != -1) {
- ssize_t nread;
-
- if (prof_dump_write(propagate_err, "\nMAPPED_LIBRARIES:\n") &&
- propagate_err) {
- ret = true;
- goto label_return;
- }
- nread = 0;
- do {
- prof_dump_buf_end += nread;
- if (prof_dump_buf_end == PROF_DUMP_BUFSIZE) {
- /* Make space in prof_dump_buf before read(). */
- if (prof_dump_flush(propagate_err) &&
- propagate_err) {
- ret = true;
- goto label_return;
- }
- }
- nread = malloc_read_fd(mfd,
- &prof_dump_buf[prof_dump_buf_end], PROF_DUMP_BUFSIZE
- - prof_dump_buf_end);
- } while (nread > 0);
- } else {
- ret = true;
- goto label_return;
- }
-
- ret = false;
-label_return:
- if (mfd != -1) {
- close(mfd);
- }
- return ret;
-}
-
-/*
- * See prof_sample_threshold_update() comment for why the body of this function
- * is conditionally compiled.
- */
-static void
-prof_leakcheck(const prof_cnt_t *cnt_all, size_t leak_ngctx,
- const char *filename) {
-#ifdef JEMALLOC_PROF
- /*
- * Scaling is equivalent AdjustSamples() in jeprof, but the result may
- * differ slightly from what jeprof reports, because here we scale the
- * summary values, whereas jeprof scales each context individually and
- * reports the sums of the scaled values.
- */
- if (cnt_all->curbytes != 0) {
- double sample_period = (double)((uint64_t)1 << lg_prof_sample);
- double ratio = (((double)cnt_all->curbytes) /
- (double)cnt_all->curobjs) / sample_period;
- double scale_factor = 1.0 / (1.0 - exp(-ratio));
- uint64_t curbytes = (uint64_t)round(((double)cnt_all->curbytes)
- * scale_factor);
- uint64_t curobjs = (uint64_t)round(((double)cnt_all->curobjs) *
- scale_factor);
-
- malloc_printf("<jemalloc>: Leak approximation summary: ~%"FMTu64
- " byte%s, ~%"FMTu64" object%s, >= %zu context%s\n",
- curbytes, (curbytes != 1) ? "s" : "", curobjs, (curobjs !=
- 1) ? "s" : "", leak_ngctx, (leak_ngctx != 1) ? "s" : "");
- malloc_printf(
- "<jemalloc>: Run jeprof on \"%s\" for leak detail\n",
- filename);
- }
+ not_reached();
+ return TE_MAX_START_WAIT;
#endif
}
-struct prof_gctx_dump_iter_arg_s {
- tsdn_t *tsdn;
- bool propagate_err;
-};
-
-static prof_gctx_t *
-prof_gctx_dump_iter(prof_gctx_tree_t *gctxs, prof_gctx_t *gctx, void *opaque) {
- prof_gctx_t *ret;
- struct prof_gctx_dump_iter_arg_s *arg =
- (struct prof_gctx_dump_iter_arg_s *)opaque;
-
- malloc_mutex_lock(arg->tsdn, gctx->lock);
-
- if (prof_dump_gctx(arg->tsdn, arg->propagate_err, gctx, &gctx->bt,
- gctxs)) {
- ret = gctx;
- goto label_return;
- }
-
- ret = NULL;
-label_return:
- malloc_mutex_unlock(arg->tsdn, gctx->lock);
- return ret;
-}
-
-static void
-prof_dump_prep(tsd_t *tsd, prof_tdata_t *tdata,
- struct prof_tdata_merge_iter_arg_s *prof_tdata_merge_iter_arg,
- struct prof_gctx_merge_iter_arg_s *prof_gctx_merge_iter_arg,
- prof_gctx_tree_t *gctxs) {
- size_t tabind;
- union {
- prof_gctx_t *p;
- void *v;
- } gctx;
-
- prof_enter(tsd, tdata);
-
+uint64_t
+prof_sample_postponed_event_wait(tsd_t *tsd) {
/*
- * Put gctx's in limbo and clear their counters in preparation for
- * summing.
+ * The postponed wait time for prof sample event is computed as if we
+ * want a new wait time (i.e. as if the event were triggered). If we
+ * instead postpone to the immediate next allocation, like how we're
+ * handling the other events, then we can have sampling bias, if e.g.
+ * the allocation immediately following a reentrancy always comes from
+ * the same stack trace.
*/
- gctx_tree_new(gctxs);
- for (tabind = 0; !ckh_iter(&bt2gctx, &tabind, NULL, &gctx.v);) {
- prof_dump_gctx_prep(tsd_tsdn(tsd), gctx.p, gctxs);
- }
-
- /*
- * Iterate over tdatas, and for the non-expired ones snapshot their tctx
- * stats and merge them into the associated gctx's.
- */
- prof_tdata_merge_iter_arg->tsdn = tsd_tsdn(tsd);
- memset(&prof_tdata_merge_iter_arg->cnt_all, 0, sizeof(prof_cnt_t));
- malloc_mutex_lock(tsd_tsdn(tsd), &tdatas_mtx);
- tdata_tree_iter(&tdatas, NULL, prof_tdata_merge_iter,
- (void *)prof_tdata_merge_iter_arg);
- malloc_mutex_unlock(tsd_tsdn(tsd), &tdatas_mtx);
-
- /* Merge tctx stats into gctx's. */
- prof_gctx_merge_iter_arg->tsdn = tsd_tsdn(tsd);
- prof_gctx_merge_iter_arg->leak_ngctx = 0;
- gctx_tree_iter(gctxs, NULL, prof_gctx_merge_iter,
- (void *)prof_gctx_merge_iter_arg);
-
- prof_leave(tsd, tdata);
-}
-
-static bool
-prof_dump_file(tsd_t *tsd, bool propagate_err, const char *filename,
- bool leakcheck, prof_tdata_t *tdata,
- struct prof_tdata_merge_iter_arg_s *prof_tdata_merge_iter_arg,
- struct prof_gctx_merge_iter_arg_s *prof_gctx_merge_iter_arg,
- struct prof_gctx_dump_iter_arg_s *prof_gctx_dump_iter_arg,
- prof_gctx_tree_t *gctxs) {
- /* Create dump file. */
- if ((prof_dump_fd = prof_dump_open(propagate_err, filename)) == -1) {
- return true;
- }
-
- /* Dump profile header. */
- if (prof_dump_header(tsd_tsdn(tsd), propagate_err,
- &prof_tdata_merge_iter_arg->cnt_all)) {
- goto label_write_error;
- }
-
- /* Dump per gctx profile stats. */
- prof_gctx_dump_iter_arg->tsdn = tsd_tsdn(tsd);
- prof_gctx_dump_iter_arg->propagate_err = propagate_err;
- if (gctx_tree_iter(gctxs, NULL, prof_gctx_dump_iter,
- (void *)prof_gctx_dump_iter_arg) != NULL) {
- goto label_write_error;
- }
-
- /* Dump /proc/<pid>/maps if possible. */
- if (prof_dump_maps(propagate_err)) {
- goto label_write_error;
- }
-
- if (prof_dump_close(propagate_err)) {
- return true;
- }
-
- return false;
-label_write_error:
- prof_dump_close(propagate_err);
- return true;
+ return prof_sample_new_event_wait(tsd);
}
-static bool
-prof_dump(tsd_t *tsd, bool propagate_err, const char *filename,
- bool leakcheck) {
- cassert(config_prof);
- assert(tsd_reentrancy_level_get(tsd) == 0);
-
- prof_tdata_t * tdata = prof_tdata_get(tsd, true);
- if (tdata == NULL) {
- return true;
- }
-
- pre_reentrancy(tsd, NULL);
- malloc_mutex_lock(tsd_tsdn(tsd), &prof_dump_mtx);
-
- prof_gctx_tree_t gctxs;
- struct prof_tdata_merge_iter_arg_s prof_tdata_merge_iter_arg;
- struct prof_gctx_merge_iter_arg_s prof_gctx_merge_iter_arg;
- struct prof_gctx_dump_iter_arg_s prof_gctx_dump_iter_arg;
- prof_dump_prep(tsd, tdata, &prof_tdata_merge_iter_arg,
- &prof_gctx_merge_iter_arg, &gctxs);
- bool err = prof_dump_file(tsd, propagate_err, filename, leakcheck, tdata,
- &prof_tdata_merge_iter_arg, &prof_gctx_merge_iter_arg,
- &prof_gctx_dump_iter_arg, &gctxs);
- prof_gctx_finish(tsd, &gctxs);
-
- malloc_mutex_unlock(tsd_tsdn(tsd), &prof_dump_mtx);
- post_reentrancy(tsd);
-
- if (err) {
- return true;
- }
-
- if (leakcheck) {
- prof_leakcheck(&prof_tdata_merge_iter_arg.cnt_all,
- prof_gctx_merge_iter_arg.leak_ngctx, filename);
- }
- return false;
-}
-
-#ifdef JEMALLOC_JET
void
-prof_cnt_all(uint64_t *curobjs, uint64_t *curbytes, uint64_t *accumobjs,
- uint64_t *accumbytes) {
- tsd_t *tsd;
- prof_tdata_t *tdata;
- struct prof_tdata_merge_iter_arg_s prof_tdata_merge_iter_arg;
- struct prof_gctx_merge_iter_arg_s prof_gctx_merge_iter_arg;
- prof_gctx_tree_t gctxs;
-
- tsd = tsd_fetch();
- tdata = prof_tdata_get(tsd, false);
- if (tdata == NULL) {
- if (curobjs != NULL) {
- *curobjs = 0;
- }
- if (curbytes != NULL) {
- *curbytes = 0;
- }
- if (accumobjs != NULL) {
- *accumobjs = 0;
- }
- if (accumbytes != NULL) {
- *accumbytes = 0;
- }
+prof_sample_event_handler(tsd_t *tsd, uint64_t elapsed) {
+ cassert(config_prof);
+ assert(elapsed > 0 && elapsed != TE_INVALID_ELAPSED);
+ if (prof_interval == 0 || !prof_active_get_unlocked()) {
return;
}
-
- prof_dump_prep(tsd, tdata, &prof_tdata_merge_iter_arg,
- &prof_gctx_merge_iter_arg, &gctxs);
- prof_gctx_finish(tsd, &gctxs);
-
- if (curobjs != NULL) {
- *curobjs = prof_tdata_merge_iter_arg.cnt_all.curobjs;
- }
- if (curbytes != NULL) {
- *curbytes = prof_tdata_merge_iter_arg.cnt_all.curbytes;
- }
- if (accumobjs != NULL) {
- *accumobjs = prof_tdata_merge_iter_arg.cnt_all.accumobjs;
- }
- if (accumbytes != NULL) {
- *accumbytes = prof_tdata_merge_iter_arg.cnt_all.accumbytes;
- }
-}
-#endif
-
-#define DUMP_FILENAME_BUFSIZE (PATH_MAX + 1)
-#define VSEQ_INVALID UINT64_C(0xffffffffffffffff)
-static void
-prof_dump_filename(char *filename, char v, uint64_t vseq) {
- cassert(config_prof);
-
- if (vseq != VSEQ_INVALID) {
- /* "<prefix>.<pid>.<seq>.v<vseq>.heap" */
- malloc_snprintf(filename, DUMP_FILENAME_BUFSIZE,
- "%s.%d.%"FMTu64".%c%"FMTu64".heap",
- opt_prof_prefix, prof_getpid(), prof_dump_seq, v, vseq);
- } else {
- /* "<prefix>.<pid>.<seq>.<v>.heap" */
- malloc_snprintf(filename, DUMP_FILENAME_BUFSIZE,
- "%s.%d.%"FMTu64".%c.heap",
- opt_prof_prefix, prof_getpid(), prof_dump_seq, v);
+ if (counter_accum(tsd_tsdn(tsd), &prof_idump_accumulated, elapsed)) {
+ prof_idump(tsd_tsdn(tsd));
}
- prof_dump_seq++;
}
static void
prof_fdump(void) {
tsd_t *tsd;
- char filename[DUMP_FILENAME_BUFSIZE];
cassert(config_prof);
assert(opt_prof_final);
- assert(opt_prof_prefix[0] != '\0');
if (!prof_booted) {
return;
@@ -2023,26 +290,14 @@ prof_fdump(void) {
tsd = tsd_fetch();
assert(tsd_reentrancy_level_get(tsd) == 0);
- malloc_mutex_lock(tsd_tsdn(tsd), &prof_dump_seq_mtx);
- prof_dump_filename(filename, 'f', VSEQ_INVALID);
- malloc_mutex_unlock(tsd_tsdn(tsd), &prof_dump_seq_mtx);
- prof_dump(tsd, false, filename, opt_prof_leak);
+ prof_fdump_impl(tsd);
}
-bool
-prof_accum_init(tsdn_t *tsdn, prof_accum_t *prof_accum) {
+static bool
+prof_idump_accum_init(void) {
cassert(config_prof);
-#ifndef JEMALLOC_ATOMIC_U64
- if (malloc_mutex_init(&prof_accum->mtx, "prof_accum",
- WITNESS_RANK_PROF_ACCUM, malloc_mutex_rank_exclusive)) {
- return true;
- }
- prof_accum->accumbytes = 0;
-#else
- atomic_store_u64(&prof_accum->accumbytes, 0, ATOMIC_RELAXED);
-#endif
- return false;
+ return counter_accum_init(&prof_idump_accumulated, prof_interval);
}
void
@@ -2060,7 +315,7 @@ prof_idump(tsdn_t *tsdn) {
return;
}
- tdata = prof_tdata_get(tsd, false);
+ tdata = prof_tdata_get(tsd, true);
if (tdata == NULL) {
return;
}
@@ -2069,14 +324,7 @@ prof_idump(tsdn_t *tsdn) {
return;
}
- if (opt_prof_prefix[0] != '\0') {
- char filename[PATH_MAX + 1];
- malloc_mutex_lock(tsd_tsdn(tsd), &prof_dump_seq_mtx);
- prof_dump_filename(filename, 'i', prof_dump_iseq);
- prof_dump_iseq++;
- malloc_mutex_unlock(tsd_tsdn(tsd), &prof_dump_seq_mtx);
- prof_dump(tsd, false, filename, false);
- }
+ prof_idump_impl(tsd);
}
bool
@@ -2087,19 +335,8 @@ prof_mdump(tsd_t *tsd, const char *filename) {
if (!opt_prof || !prof_booted) {
return true;
}
- char filename_buf[DUMP_FILENAME_BUFSIZE];
- if (filename == NULL) {
- /* No filename specified, so automatically generate one. */
- if (opt_prof_prefix[0] == '\0') {
- return true;
- }
- malloc_mutex_lock(tsd_tsdn(tsd), &prof_dump_seq_mtx);
- prof_dump_filename(filename_buf, 'm', prof_dump_mseq);
- prof_dump_mseq++;
- malloc_mutex_unlock(tsd_tsdn(tsd), &prof_dump_seq_mtx);
- filename = filename_buf;
- }
- return prof_dump(tsd, true, filename, false);
+
+ return prof_mdump_impl(tsd, filename);
}
void
@@ -2126,63 +363,7 @@ prof_gdump(tsdn_t *tsdn) {
return;
}
- if (opt_prof_prefix[0] != '\0') {
- char filename[DUMP_FILENAME_BUFSIZE];
- malloc_mutex_lock(tsdn, &prof_dump_seq_mtx);
- prof_dump_filename(filename, 'u', prof_dump_useq);
- prof_dump_useq++;
- malloc_mutex_unlock(tsdn, &prof_dump_seq_mtx);
- prof_dump(tsd, false, filename, false);
- }
-}
-
-static void
-prof_bt_hash(const void *key, size_t r_hash[2]) {
- prof_bt_t *bt = (prof_bt_t *)key;
-
- cassert(config_prof);
-
- hash(bt->vec, bt->len * sizeof(void *), 0x94122f33U, r_hash);
-}
-
-static bool
-prof_bt_keycomp(const void *k1, const void *k2) {
- const prof_bt_t *bt1 = (prof_bt_t *)k1;
- const prof_bt_t *bt2 = (prof_bt_t *)k2;
-
- cassert(config_prof);
-
- if (bt1->len != bt2->len) {
- return false;
- }
- return (memcmp(bt1->vec, bt2->vec, bt1->len * sizeof(void *)) == 0);
-}
-
-static void
-prof_bt_node_hash(const void *key, size_t r_hash[2]) {
- const prof_bt_node_t *bt_node = (prof_bt_node_t *)key;
- prof_bt_hash((void *)(&bt_node->bt), r_hash);
-}
-
-static bool
-prof_bt_node_keycomp(const void *k1, const void *k2) {
- const prof_bt_node_t *bt_node1 = (prof_bt_node_t *)k1;
- const prof_bt_node_t *bt_node2 = (prof_bt_node_t *)k2;
- return prof_bt_keycomp((void *)(&bt_node1->bt),
- (void *)(&bt_node2->bt));
-}
-
-static void
-prof_thr_node_hash(const void *key, size_t r_hash[2]) {
- const prof_thr_node_t *thr_node = (prof_thr_node_t *)key;
- hash(&thr_node->thr_uid, sizeof(uint64_t), 0x94122f35U, r_hash);
-}
-
-static bool
-prof_thr_node_keycomp(const void *k1, const void *k2) {
- const prof_thr_node_t *thr_node1 = (prof_thr_node_t *)k1;
- const prof_thr_node_t *thr_node2 = (prof_thr_node_t *)k2;
- return thr_node1->thr_uid == thr_node2->thr_uid;
+ prof_gdump_impl(tsd);
}
static uint64_t
@@ -2197,132 +378,18 @@ prof_thr_uid_alloc(tsdn_t *tsdn) {
return thr_uid;
}
-static prof_tdata_t *
-prof_tdata_init_impl(tsd_t *tsd, uint64_t thr_uid, uint64_t thr_discrim,
- char *thread_name, bool active) {
- prof_tdata_t *tdata;
-
- cassert(config_prof);
-
- /* Initialize an empty cache for this thread. */
- tdata = (prof_tdata_t *)iallocztm(tsd_tsdn(tsd), sizeof(prof_tdata_t),
- sz_size2index(sizeof(prof_tdata_t)), false, NULL, true,
- arena_get(TSDN_NULL, 0, true), true);
- if (tdata == NULL) {
- return NULL;
- }
-
- tdata->lock = prof_tdata_mutex_choose(thr_uid);
- tdata->thr_uid = thr_uid;
- tdata->thr_discrim = thr_discrim;
- tdata->thread_name = thread_name;
- tdata->attached = true;
- tdata->expired = false;
- tdata->tctx_uid_next = 0;
-
- if (ckh_new(tsd, &tdata->bt2tctx, PROF_CKH_MINITEMS, prof_bt_hash,
- prof_bt_keycomp)) {
- idalloctm(tsd_tsdn(tsd), tdata, NULL, NULL, true, true);
- return NULL;
- }
-
- tdata->prng_state = (uint64_t)(uintptr_t)tdata;
- prof_sample_threshold_update(tdata);
-
- tdata->enq = false;
- tdata->enq_idump = false;
- tdata->enq_gdump = false;
-
- tdata->dumping = false;
- tdata->active = active;
-
- malloc_mutex_lock(tsd_tsdn(tsd), &tdatas_mtx);
- tdata_tree_insert(&tdatas, tdata);
- malloc_mutex_unlock(tsd_tsdn(tsd), &tdatas_mtx);
-
- return tdata;
-}
-
prof_tdata_t *
prof_tdata_init(tsd_t *tsd) {
return prof_tdata_init_impl(tsd, prof_thr_uid_alloc(tsd_tsdn(tsd)), 0,
NULL, prof_thread_active_init_get(tsd_tsdn(tsd)));
}
-static bool
-prof_tdata_should_destroy_unlocked(prof_tdata_t *tdata, bool even_if_attached) {
- if (tdata->attached && !even_if_attached) {
- return false;
- }
- if (ckh_count(&tdata->bt2tctx) != 0) {
- return false;
- }
- return true;
-}
-
-static bool
-prof_tdata_should_destroy(tsdn_t *tsdn, prof_tdata_t *tdata,
- bool even_if_attached) {
- malloc_mutex_assert_owner(tsdn, tdata->lock);
-
- return prof_tdata_should_destroy_unlocked(tdata, even_if_attached);
-}
-
-static void
-prof_tdata_destroy_locked(tsd_t *tsd, prof_tdata_t *tdata,
- bool even_if_attached) {
- malloc_mutex_assert_owner(tsd_tsdn(tsd), &tdatas_mtx);
-
- tdata_tree_remove(&tdatas, tdata);
-
- assert(prof_tdata_should_destroy_unlocked(tdata, even_if_attached));
-
- if (tdata->thread_name != NULL) {
- idalloctm(tsd_tsdn(tsd), tdata->thread_name, NULL, NULL, true,
- true);
- }
- ckh_delete(tsd, &tdata->bt2tctx);
- idalloctm(tsd_tsdn(tsd), tdata, NULL, NULL, true, true);
-}
-
-static void
-prof_tdata_destroy(tsd_t *tsd, prof_tdata_t *tdata, bool even_if_attached) {
- malloc_mutex_lock(tsd_tsdn(tsd), &tdatas_mtx);
- prof_tdata_destroy_locked(tsd, tdata, even_if_attached);
- malloc_mutex_unlock(tsd_tsdn(tsd), &tdatas_mtx);
-}
-
-static void
-prof_tdata_detach(tsd_t *tsd, prof_tdata_t *tdata) {
- bool destroy_tdata;
-
- malloc_mutex_lock(tsd_tsdn(tsd), tdata->lock);
- if (tdata->attached) {
- destroy_tdata = prof_tdata_should_destroy(tsd_tsdn(tsd), tdata,
- true);
- /*
- * Only detach if !destroy_tdata, because detaching would allow
- * another thread to win the race to destroy tdata.
- */
- if (!destroy_tdata) {
- tdata->attached = false;
- }
- tsd_prof_tdata_set(tsd, NULL);
- } else {
- destroy_tdata = false;
- }
- malloc_mutex_unlock(tsd_tsdn(tsd), tdata->lock);
- if (destroy_tdata) {
- prof_tdata_destroy(tsd, tdata, true);
- }
-}
-
prof_tdata_t *
prof_tdata_reinit(tsd_t *tsd, prof_tdata_t *tdata) {
uint64_t thr_uid = tdata->thr_uid;
uint64_t thr_discrim = tdata->thr_discrim + 1;
char *thread_name = (tdata->thread_name != NULL) ?
- prof_thread_name_alloc(tsd_tsdn(tsd), tdata->thread_name) : NULL;
+ prof_thread_name_alloc(tsd, tdata->thread_name) : NULL;
bool active = tdata->active;
prof_tdata_detach(tsd, tdata);
@@ -2330,58 +397,6 @@ prof_tdata_reinit(tsd_t *tsd, prof_tdata_t *tdata) {
active);
}
-static bool
-prof_tdata_expire(tsdn_t *tsdn, prof_tdata_t *tdata) {
- bool destroy_tdata;
-
- malloc_mutex_lock(tsdn, tdata->lock);
- if (!tdata->expired) {
- tdata->expired = true;
- destroy_tdata = tdata->attached ? false :
- prof_tdata_should_destroy(tsdn, tdata, false);
- } else {
- destroy_tdata = false;
- }
- malloc_mutex_unlock(tsdn, tdata->lock);
-
- return destroy_tdata;
-}
-
-static prof_tdata_t *
-prof_tdata_reset_iter(prof_tdata_tree_t *tdatas, prof_tdata_t *tdata,
- void *arg) {
- tsdn_t *tsdn = (tsdn_t *)arg;
-
- return (prof_tdata_expire(tsdn, tdata) ? tdata : NULL);
-}
-
-void
-prof_reset(tsd_t *tsd, size_t lg_sample) {
- prof_tdata_t *next;
-
- assert(lg_sample < (sizeof(uint64_t) << 3));
-
- malloc_mutex_lock(tsd_tsdn(tsd), &prof_dump_mtx);
- malloc_mutex_lock(tsd_tsdn(tsd), &tdatas_mtx);
-
- lg_prof_sample = lg_sample;
-
- next = NULL;
- do {
- prof_tdata_t *to_destroy = tdata_tree_iter(&tdatas, next,
- prof_tdata_reset_iter, (void *)tsd);
- if (to_destroy != NULL) {
- next = tdata_tree_next(&tdatas, to_destroy);
- prof_tdata_destroy_locked(tsd, to_destroy, false);
- } else {
- next = NULL;
- }
- } while (next != NULL);
-
- malloc_mutex_unlock(tsd_tsdn(tsd), &tdatas_mtx);
- malloc_mutex_unlock(tsd_tsdn(tsd), &prof_dump_mtx);
-}
-
void
prof_tdata_cleanup(tsd_t *tsd) {
prof_tdata_t *tdata;
@@ -2400,8 +415,9 @@ bool
prof_active_get(tsdn_t *tsdn) {
bool prof_active_current;
+ prof_active_assert();
malloc_mutex_lock(tsdn, &prof_active_mtx);
- prof_active_current = prof_active;
+ prof_active_current = prof_active_state;
malloc_mutex_unlock(tsdn, &prof_active_mtx);
return prof_active_current;
}
@@ -2410,377 +426,19 @@ bool
prof_active_set(tsdn_t *tsdn, bool active) {
bool prof_active_old;
+ prof_active_assert();
malloc_mutex_lock(tsdn, &prof_active_mtx);
- prof_active_old = prof_active;
- prof_active = active;
+ prof_active_old = prof_active_state;
+ prof_active_state = active;
malloc_mutex_unlock(tsdn, &prof_active_mtx);
+ prof_active_assert();
return prof_active_old;
}
-#ifdef JEMALLOC_JET
-size_t
-prof_log_bt_count(void) {
- size_t cnt = 0;
- prof_bt_node_t *node = log_bt_first;
- while (node != NULL) {
- cnt++;
- node = node->next;
- }
- return cnt;
-}
-
-size_t
-prof_log_alloc_count(void) {
- size_t cnt = 0;
- prof_alloc_node_t *node = log_alloc_first;
- while (node != NULL) {
- cnt++;
- node = node->next;
- }
- return cnt;
-}
-
-size_t
-prof_log_thr_count(void) {
- size_t cnt = 0;
- prof_thr_node_t *node = log_thr_first;
- while (node != NULL) {
- cnt++;
- node = node->next;
- }
- return cnt;
-}
-
-bool
-prof_log_is_logging(void) {
- return prof_logging_state == prof_logging_state_started;
-}
-
-bool
-prof_log_rep_check(void) {
- if (prof_logging_state == prof_logging_state_stopped
- && log_tables_initialized) {
- return true;
- }
-
- if (log_bt_last != NULL && log_bt_last->next != NULL) {
- return true;
- }
- if (log_thr_last != NULL && log_thr_last->next != NULL) {
- return true;
- }
- if (log_alloc_last != NULL && log_alloc_last->next != NULL) {
- return true;
- }
-
- size_t bt_count = prof_log_bt_count();
- size_t thr_count = prof_log_thr_count();
- size_t alloc_count = prof_log_alloc_count();
-
-
- if (prof_logging_state == prof_logging_state_stopped) {
- if (bt_count != 0 || thr_count != 0 || alloc_count || 0) {
- return true;
- }
- }
-
- prof_alloc_node_t *node = log_alloc_first;
- while (node != NULL) {
- if (node->alloc_bt_ind >= bt_count) {
- return true;
- }
- if (node->free_bt_ind >= bt_count) {
- return true;
- }
- if (node->alloc_thr_ind >= thr_count) {
- return true;
- }
- if (node->free_thr_ind >= thr_count) {
- return true;
- }
- if (node->alloc_time_ns > node->free_time_ns) {
- return true;
- }
- node = node->next;
- }
-
- return false;
-}
-
-void
-prof_log_dummy_set(bool new_value) {
- prof_log_dummy = new_value;
-}
-#endif
-
-bool
-prof_log_start(tsdn_t *tsdn, const char *filename) {
- if (!opt_prof || !prof_booted) {
- return true;
- }
-
- bool ret = false;
- size_t buf_size = PATH_MAX + 1;
-
- malloc_mutex_lock(tsdn, &log_mtx);
-
- if (prof_logging_state != prof_logging_state_stopped) {
- ret = true;
- } else if (filename == NULL) {
- /* Make default name. */
- malloc_snprintf(log_filename, buf_size, "%s.%d.%"FMTu64".json",
- opt_prof_prefix, prof_getpid(), log_seq);
- log_seq++;
- prof_logging_state = prof_logging_state_started;
- } else if (strlen(filename) >= buf_size) {
- ret = true;
- } else {
- strcpy(log_filename, filename);
- prof_logging_state = prof_logging_state_started;
- }
-
- if (!ret) {
- nstime_update(&log_start_timestamp);
- }
-
- malloc_mutex_unlock(tsdn, &log_mtx);
-
- return ret;
-}
-
-/* Used as an atexit function to stop logging on exit. */
-static void
-prof_log_stop_final(void) {
- tsd_t *tsd = tsd_fetch();
- prof_log_stop(tsd_tsdn(tsd));
-}
-
-struct prof_emitter_cb_arg_s {
- int fd;
- ssize_t ret;
-};
-
-static void
-prof_emitter_write_cb(void *opaque, const char *to_write) {
- struct prof_emitter_cb_arg_s *arg =
- (struct prof_emitter_cb_arg_s *)opaque;
- size_t bytes = strlen(to_write);
-#ifdef JEMALLOC_JET
- if (prof_log_dummy) {
- return;
- }
-#endif
- arg->ret = write(arg->fd, (void *)to_write, bytes);
-}
-
-/*
- * prof_log_emit_{...} goes through the appropriate linked list, emitting each
- * node to the json and deallocating it.
- */
-static void
-prof_log_emit_threads(tsd_t *tsd, emitter_t *emitter) {
- emitter_json_array_kv_begin(emitter, "threads");
- prof_thr_node_t *thr_node = log_thr_first;
- prof_thr_node_t *thr_old_node;
- while (thr_node != NULL) {
- emitter_json_object_begin(emitter);
-
- emitter_json_kv(emitter, "thr_uid", emitter_type_uint64,
- &thr_node->thr_uid);
-
- char *thr_name = thr_node->name;
-
- emitter_json_kv(emitter, "thr_name", emitter_type_string,
- &thr_name);
-
- emitter_json_object_end(emitter);
- thr_old_node = thr_node;
- thr_node = thr_node->next;
- idalloc(tsd, thr_old_node);
- }
- emitter_json_array_end(emitter);
-}
-
-static void
-prof_log_emit_traces(tsd_t *tsd, emitter_t *emitter) {
- emitter_json_array_kv_begin(emitter, "stack_traces");
- prof_bt_node_t *bt_node = log_bt_first;
- prof_bt_node_t *bt_old_node;
- /*
- * Calculate how many hex digits we need: twice number of bytes, two for
- * "0x", and then one more for terminating '\0'.
- */
- char buf[2 * sizeof(intptr_t) + 3];
- size_t buf_sz = sizeof(buf);
- while (bt_node != NULL) {
- emitter_json_array_begin(emitter);
- size_t i;
- for (i = 0; i < bt_node->bt.len; i++) {
- malloc_snprintf(buf, buf_sz, "%p", bt_node->bt.vec[i]);
- char *trace_str = buf;
- emitter_json_value(emitter, emitter_type_string,
- &trace_str);
- }
- emitter_json_array_end(emitter);
-
- bt_old_node = bt_node;
- bt_node = bt_node->next;
- idalloc(tsd, bt_old_node);
- }
- emitter_json_array_end(emitter);
-}
-
-static void
-prof_log_emit_allocs(tsd_t *tsd, emitter_t *emitter) {
- emitter_json_array_kv_begin(emitter, "allocations");
- prof_alloc_node_t *alloc_node = log_alloc_first;
- prof_alloc_node_t *alloc_old_node;
- while (alloc_node != NULL) {
- emitter_json_object_begin(emitter);
-
- emitter_json_kv(emitter, "alloc_thread", emitter_type_size,
- &alloc_node->alloc_thr_ind);
-
- emitter_json_kv(emitter, "free_thread", emitter_type_size,
- &alloc_node->free_thr_ind);
-
- emitter_json_kv(emitter, "alloc_trace", emitter_type_size,
- &alloc_node->alloc_bt_ind);
-
- emitter_json_kv(emitter, "free_trace", emitter_type_size,
- &alloc_node->free_bt_ind);
-
- emitter_json_kv(emitter, "alloc_timestamp",
- emitter_type_uint64, &alloc_node->alloc_time_ns);
-
- emitter_json_kv(emitter, "free_timestamp", emitter_type_uint64,
- &alloc_node->free_time_ns);
-
- emitter_json_kv(emitter, "usize", emitter_type_uint64,
- &alloc_node->usize);
-
- emitter_json_object_end(emitter);
-
- alloc_old_node = alloc_node;
- alloc_node = alloc_node->next;
- idalloc(tsd, alloc_old_node);
- }
- emitter_json_array_end(emitter);
-}
-
-static void
-prof_log_emit_metadata(emitter_t *emitter) {
- emitter_json_object_kv_begin(emitter, "info");
-
- nstime_t now = NSTIME_ZERO_INITIALIZER;
-
- nstime_update(&now);
- uint64_t ns = nstime_ns(&now) - nstime_ns(&log_start_timestamp);
- emitter_json_kv(emitter, "duration", emitter_type_uint64, &ns);
-
- char *vers = JEMALLOC_VERSION;
- emitter_json_kv(emitter, "version",
- emitter_type_string, &vers);
-
- emitter_json_kv(emitter, "lg_sample_rate",
- emitter_type_int, &lg_prof_sample);
-
- int pid = prof_getpid();
- emitter_json_kv(emitter, "pid", emitter_type_int, &pid);
-
- emitter_json_object_end(emitter);
-}
-
-
-bool
-prof_log_stop(tsdn_t *tsdn) {
- if (!opt_prof || !prof_booted) {
- return true;
- }
-
- tsd_t *tsd = tsdn_tsd(tsdn);
- malloc_mutex_lock(tsdn, &log_mtx);
-
- if (prof_logging_state != prof_logging_state_started) {
- malloc_mutex_unlock(tsdn, &log_mtx);
- return true;
- }
-
- /*
- * Set the state to dumping. We'll set it to stopped when we're done.
- * Since other threads won't be able to start/stop/log when the state is
- * dumping, we don't have to hold the lock during the whole method.
- */
- prof_logging_state = prof_logging_state_dumping;
- malloc_mutex_unlock(tsdn, &log_mtx);
-
-
- emitter_t emitter;
-
- /* Create a file. */
-
- int fd;
-#ifdef JEMALLOC_JET
- if (prof_log_dummy) {
- fd = 0;
- } else {
- fd = creat(log_filename, 0644);
- }
-#else
- fd = creat(log_filename, 0644);
-#endif
-
- if (fd == -1) {
- malloc_printf("<jemalloc>: creat() for log file \"%s\" "
- " failed with %d\n", log_filename, errno);
- if (opt_abort) {
- abort();
- }
- return true;
- }
-
- /* Emit to json. */
- struct prof_emitter_cb_arg_s arg;
- arg.fd = fd;
- emitter_init(&emitter, emitter_output_json, &prof_emitter_write_cb,
- (void *)(&arg));
-
- emitter_begin(&emitter);
- prof_log_emit_metadata(&emitter);
- prof_log_emit_threads(tsd, &emitter);
- prof_log_emit_traces(tsd, &emitter);
- prof_log_emit_allocs(tsd, &emitter);
- emitter_end(&emitter);
-
- /* Reset global state. */
- if (log_tables_initialized) {
- ckh_delete(tsd, &log_bt_node_set);
- ckh_delete(tsd, &log_thr_node_set);
- }
- log_tables_initialized = false;
- log_bt_index = 0;
- log_thr_index = 0;
- log_bt_first = NULL;
- log_bt_last = NULL;
- log_thr_first = NULL;
- log_thr_last = NULL;
- log_alloc_first = NULL;
- log_alloc_last = NULL;
-
- malloc_mutex_lock(tsdn, &log_mtx);
- prof_logging_state = prof_logging_state_stopped;
- malloc_mutex_unlock(tsdn, &log_mtx);
-
-#ifdef JEMALLOC_JET
- if (prof_log_dummy) {
- return false;
- }
-#endif
- return close(fd);
-}
-
const char *
prof_thread_name_get(tsd_t *tsd) {
+ assert(tsd_reentrancy_level_get(tsd) == 0);
+
prof_tdata_t *tdata;
tdata = prof_tdata_get(tsd, true);
@@ -2790,69 +448,19 @@ prof_thread_name_get(tsd_t *tsd) {
return (tdata->thread_name != NULL ? tdata->thread_name : "");
}
-static char *
-prof_thread_name_alloc(tsdn_t *tsdn, const char *thread_name) {
- char *ret;
- size_t size;
-
- if (thread_name == NULL) {
- return NULL;
- }
-
- size = strlen(thread_name) + 1;
- if (size == 1) {
- return "";
- }
-
- ret = iallocztm(tsdn, size, sz_size2index(size), false, NULL, true,
- arena_get(TSDN_NULL, 0, true), true);
- if (ret == NULL) {
- return NULL;
- }
- memcpy(ret, thread_name, size);
- return ret;
-}
-
int
prof_thread_name_set(tsd_t *tsd, const char *thread_name) {
- prof_tdata_t *tdata;
- unsigned i;
- char *s;
-
- tdata = prof_tdata_get(tsd, true);
- if (tdata == NULL) {
- return EAGAIN;
- }
-
- /* Validate input. */
- if (thread_name == NULL) {
- return EFAULT;
- }
- for (i = 0; thread_name[i] != '\0'; i++) {
- char c = thread_name[i];
- if (!isgraph(c) && !isblank(c)) {
- return EFAULT;
- }
- }
-
- s = prof_thread_name_alloc(tsd_tsdn(tsd), thread_name);
- if (s == NULL) {
- return EAGAIN;
- }
-
- if (tdata->thread_name != NULL) {
- idalloctm(tsd_tsdn(tsd), tdata->thread_name, NULL, NULL, true,
- true);
- tdata->thread_name = NULL;
- }
- if (strlen(s) > 0) {
- tdata->thread_name = s;
+ if (opt_prof_sys_thread_name) {
+ return ENOENT;
+ } else {
+ return prof_thread_name_set_impl(tsd, thread_name);
}
- return 0;
}
bool
prof_thread_active_get(tsd_t *tsd) {
+ assert(tsd_reentrancy_level_get(tsd) == 0);
+
prof_tdata_t *tdata;
tdata = prof_tdata_get(tsd, true);
@@ -2864,6 +472,8 @@ prof_thread_active_get(tsd_t *tsd) {
bool
prof_thread_active_set(tsd_t *tsd, bool active) {
+ assert(tsd_reentrancy_level_get(tsd) == 0);
+
prof_tdata_t *tdata;
tdata = prof_tdata_get(tsd, true);
@@ -2917,6 +527,28 @@ prof_gdump_set(tsdn_t *tsdn, bool gdump) {
}
void
+prof_backtrace_hook_set(prof_backtrace_hook_t hook) {
+ atomic_store_p(&prof_backtrace_hook, hook, ATOMIC_RELEASE);
+}
+
+prof_backtrace_hook_t
+prof_backtrace_hook_get() {
+ return (prof_backtrace_hook_t)atomic_load_p(&prof_backtrace_hook,
+ ATOMIC_ACQUIRE);
+}
+
+void
+prof_dump_hook_set(prof_dump_hook_t hook) {
+ atomic_store_p(&prof_dump_hook, hook, ATOMIC_RELEASE);
+}
+
+prof_dump_hook_t
+prof_dump_hook_get() {
+ return (prof_dump_hook_t)atomic_load_p(&prof_dump_hook,
+ ATOMIC_ACQUIRE);
+}
+
+void
prof_boot0(void) {
cassert(config_prof);
@@ -2932,6 +564,9 @@ prof_boot1(void) {
* opt_prof must be in its final state before any arenas are
* initialized, so this function must be executed early.
*/
+ if (opt_prof_leak_error && !opt_prof_leak) {
+ opt_prof_leak = true;
+ }
if (opt_prof_leak && !opt_prof) {
/*
@@ -2949,61 +584,65 @@ prof_boot1(void) {
}
bool
-prof_boot2(tsd_t *tsd) {
+prof_boot2(tsd_t *tsd, base_t *base) {
cassert(config_prof);
- if (opt_prof) {
- unsigned i;
+ /*
+ * Initialize the global mutexes unconditionally to maintain correct
+ * stats when opt_prof is false.
+ */
+ if (malloc_mutex_init(&prof_active_mtx, "prof_active",
+ WITNESS_RANK_PROF_ACTIVE, malloc_mutex_rank_exclusive)) {
+ return true;
+ }
+ if (malloc_mutex_init(&prof_gdump_mtx, "prof_gdump",
+ WITNESS_RANK_PROF_GDUMP, malloc_mutex_rank_exclusive)) {
+ return true;
+ }
+ if (malloc_mutex_init(&prof_thread_active_init_mtx,
+ "prof_thread_active_init", WITNESS_RANK_PROF_THREAD_ACTIVE_INIT,
+ malloc_mutex_rank_exclusive)) {
+ return true;
+ }
+ if (malloc_mutex_init(&bt2gctx_mtx, "prof_bt2gctx",
+ WITNESS_RANK_PROF_BT2GCTX, malloc_mutex_rank_exclusive)) {
+ return true;
+ }
+ if (malloc_mutex_init(&tdatas_mtx, "prof_tdatas",
+ WITNESS_RANK_PROF_TDATAS, malloc_mutex_rank_exclusive)) {
+ return true;
+ }
+ if (malloc_mutex_init(&next_thr_uid_mtx, "prof_next_thr_uid",
+ WITNESS_RANK_PROF_NEXT_THR_UID, malloc_mutex_rank_exclusive)) {
+ return true;
+ }
+ if (malloc_mutex_init(&prof_stats_mtx, "prof_stats",
+ WITNESS_RANK_PROF_STATS, malloc_mutex_rank_exclusive)) {
+ return true;
+ }
+ if (malloc_mutex_init(&prof_dump_filename_mtx,
+ "prof_dump_filename", WITNESS_RANK_PROF_DUMP_FILENAME,
+ malloc_mutex_rank_exclusive)) {
+ return true;
+ }
+ if (malloc_mutex_init(&prof_dump_mtx, "prof_dump",
+ WITNESS_RANK_PROF_DUMP, malloc_mutex_rank_exclusive)) {
+ return true;
+ }
+ if (opt_prof) {
lg_prof_sample = opt_lg_prof_sample;
-
- prof_active = opt_prof_active;
- if (malloc_mutex_init(&prof_active_mtx, "prof_active",
- WITNESS_RANK_PROF_ACTIVE, malloc_mutex_rank_exclusive)) {
- return true;
- }
-
+ prof_unbias_map_init();
+ prof_active_state = opt_prof_active;
prof_gdump_val = opt_prof_gdump;
- if (malloc_mutex_init(&prof_gdump_mtx, "prof_gdump",
- WITNESS_RANK_PROF_GDUMP, malloc_mutex_rank_exclusive)) {
- return true;
- }
-
prof_thread_active_init = opt_prof_thread_active_init;
- if (malloc_mutex_init(&prof_thread_active_init_mtx,
- "prof_thread_active_init",
- WITNESS_RANK_PROF_THREAD_ACTIVE_INIT,
- malloc_mutex_rank_exclusive)) {
- return true;
- }
-
- if (ckh_new(tsd, &bt2gctx, PROF_CKH_MINITEMS, prof_bt_hash,
- prof_bt_keycomp)) {
- return true;
- }
- if (malloc_mutex_init(&bt2gctx_mtx, "prof_bt2gctx",
- WITNESS_RANK_PROF_BT2GCTX, malloc_mutex_rank_exclusive)) {
- return true;
- }
- tdata_tree_new(&tdatas);
- if (malloc_mutex_init(&tdatas_mtx, "prof_tdatas",
- WITNESS_RANK_PROF_TDATAS, malloc_mutex_rank_exclusive)) {
+ if (prof_data_init(tsd)) {
return true;
}
next_thr_uid = 0;
- if (malloc_mutex_init(&next_thr_uid_mtx, "prof_next_thr_uid",
- WITNESS_RANK_PROF_NEXT_THR_UID, malloc_mutex_rank_exclusive)) {
- return true;
- }
-
- if (malloc_mutex_init(&prof_dump_seq_mtx, "prof_dump_seq",
- WITNESS_RANK_PROF_DUMP_SEQ, malloc_mutex_rank_exclusive)) {
- return true;
- }
- if (malloc_mutex_init(&prof_dump_mtx, "prof_dump",
- WITNESS_RANK_PROF_DUMP, malloc_mutex_rank_exclusive)) {
+ if (prof_idump_accum_init()) {
return true;
}
@@ -3015,42 +654,22 @@ prof_boot2(tsd_t *tsd) {
}
}
- if (opt_prof_log) {
- prof_log_start(tsd_tsdn(tsd), NULL);
- }
-
- if (atexit(prof_log_stop_final) != 0) {
- malloc_write("<jemalloc>: Error in atexit() "
- "for logging\n");
- if (opt_abort) {
- abort();
- }
- }
-
- if (malloc_mutex_init(&log_mtx, "prof_log",
- WITNESS_RANK_PROF_LOG, malloc_mutex_rank_exclusive)) {
- return true;
- }
-
- if (ckh_new(tsd, &log_bt_node_set, PROF_CKH_MINITEMS,
- prof_bt_node_hash, prof_bt_node_keycomp)) {
+ if (prof_log_init(tsd)) {
return true;
}
- if (ckh_new(tsd, &log_thr_node_set, PROF_CKH_MINITEMS,
- prof_thr_node_hash, prof_thr_node_keycomp)) {
+ if (prof_recent_init()) {
return true;
}
- log_tables_initialized = true;
+ prof_base = base;
- gctx_locks = (malloc_mutex_t *)base_alloc(tsd_tsdn(tsd),
- b0get(), PROF_NCTX_LOCKS * sizeof(malloc_mutex_t),
- CACHELINE);
+ gctx_locks = (malloc_mutex_t *)base_alloc(tsd_tsdn(tsd), base,
+ PROF_NCTX_LOCKS * sizeof(malloc_mutex_t), CACHELINE);
if (gctx_locks == NULL) {
return true;
}
- for (i = 0; i < PROF_NCTX_LOCKS; i++) {
+ for (unsigned i = 0; i < PROF_NCTX_LOCKS; i++) {
if (malloc_mutex_init(&gctx_locks[i], "prof_gctx",
WITNESS_RANK_PROF_GCTX,
malloc_mutex_rank_exclusive)) {
@@ -3058,26 +677,21 @@ prof_boot2(tsd_t *tsd) {
}
}
- tdata_locks = (malloc_mutex_t *)base_alloc(tsd_tsdn(tsd),
- b0get(), PROF_NTDATA_LOCKS * sizeof(malloc_mutex_t),
- CACHELINE);
+ tdata_locks = (malloc_mutex_t *)base_alloc(tsd_tsdn(tsd), base,
+ PROF_NTDATA_LOCKS * sizeof(malloc_mutex_t), CACHELINE);
if (tdata_locks == NULL) {
return true;
}
- for (i = 0; i < PROF_NTDATA_LOCKS; i++) {
+ for (unsigned i = 0; i < PROF_NTDATA_LOCKS; i++) {
if (malloc_mutex_init(&tdata_locks[i], "prof_tdata",
WITNESS_RANK_PROF_TDATA,
malloc_mutex_rank_exclusive)) {
return true;
}
}
-#ifdef JEMALLOC_PROF_LIBGCC
- /*
- * Cause the backtracing machinery to allocate its internal
- * state before enabling profiling.
- */
- _Unwind_Backtrace(prof_unwind_init_callback, NULL);
-#endif
+
+ prof_unwind_init();
+ prof_hooks_init();
}
prof_booted = true;
@@ -3095,18 +709,23 @@ prof_prefork0(tsdn_t *tsdn) {
for (i = 0; i < PROF_NTDATA_LOCKS; i++) {
malloc_mutex_prefork(tsdn, &tdata_locks[i]);
}
+ malloc_mutex_prefork(tsdn, &log_mtx);
for (i = 0; i < PROF_NCTX_LOCKS; i++) {
malloc_mutex_prefork(tsdn, &gctx_locks[i]);
}
+ malloc_mutex_prefork(tsdn, &prof_recent_dump_mtx);
}
}
void
prof_prefork1(tsdn_t *tsdn) {
if (config_prof && opt_prof) {
+ counter_prefork(tsdn, &prof_idump_accumulated);
malloc_mutex_prefork(tsdn, &prof_active_mtx);
- malloc_mutex_prefork(tsdn, &prof_dump_seq_mtx);
+ malloc_mutex_prefork(tsdn, &prof_dump_filename_mtx);
malloc_mutex_prefork(tsdn, &prof_gdump_mtx);
+ malloc_mutex_prefork(tsdn, &prof_recent_alloc_mtx);
+ malloc_mutex_prefork(tsdn, &prof_stats_mtx);
malloc_mutex_prefork(tsdn, &next_thr_uid_mtx);
malloc_mutex_prefork(tsdn, &prof_thread_active_init_mtx);
}
@@ -3120,12 +739,17 @@ prof_postfork_parent(tsdn_t *tsdn) {
malloc_mutex_postfork_parent(tsdn,
&prof_thread_active_init_mtx);
malloc_mutex_postfork_parent(tsdn, &next_thr_uid_mtx);
+ malloc_mutex_postfork_parent(tsdn, &prof_stats_mtx);
+ malloc_mutex_postfork_parent(tsdn, &prof_recent_alloc_mtx);
malloc_mutex_postfork_parent(tsdn, &prof_gdump_mtx);
- malloc_mutex_postfork_parent(tsdn, &prof_dump_seq_mtx);
+ malloc_mutex_postfork_parent(tsdn, &prof_dump_filename_mtx);
malloc_mutex_postfork_parent(tsdn, &prof_active_mtx);
+ counter_postfork_parent(tsdn, &prof_idump_accumulated);
+ malloc_mutex_postfork_parent(tsdn, &prof_recent_dump_mtx);
for (i = 0; i < PROF_NCTX_LOCKS; i++) {
malloc_mutex_postfork_parent(tsdn, &gctx_locks[i]);
}
+ malloc_mutex_postfork_parent(tsdn, &log_mtx);
for (i = 0; i < PROF_NTDATA_LOCKS; i++) {
malloc_mutex_postfork_parent(tsdn, &tdata_locks[i]);
}
@@ -3142,12 +766,17 @@ prof_postfork_child(tsdn_t *tsdn) {
malloc_mutex_postfork_child(tsdn, &prof_thread_active_init_mtx);
malloc_mutex_postfork_child(tsdn, &next_thr_uid_mtx);
+ malloc_mutex_postfork_child(tsdn, &prof_stats_mtx);
+ malloc_mutex_postfork_child(tsdn, &prof_recent_alloc_mtx);
malloc_mutex_postfork_child(tsdn, &prof_gdump_mtx);
- malloc_mutex_postfork_child(tsdn, &prof_dump_seq_mtx);
+ malloc_mutex_postfork_child(tsdn, &prof_dump_filename_mtx);
malloc_mutex_postfork_child(tsdn, &prof_active_mtx);
+ counter_postfork_child(tsdn, &prof_idump_accumulated);
+ malloc_mutex_postfork_child(tsdn, &prof_recent_dump_mtx);
for (i = 0; i < PROF_NCTX_LOCKS; i++) {
malloc_mutex_postfork_child(tsdn, &gctx_locks[i]);
}
+ malloc_mutex_postfork_child(tsdn, &log_mtx);
for (i = 0; i < PROF_NTDATA_LOCKS; i++) {
malloc_mutex_postfork_child(tsdn, &tdata_locks[i]);
}
diff --git a/contrib/jemalloc/src/prof_data.c b/contrib/jemalloc/src/prof_data.c
new file mode 100644
index 000000000000..bfa55be1ca55
--- /dev/null
+++ b/contrib/jemalloc/src/prof_data.c
@@ -0,0 +1,1447 @@
+#include "jemalloc/internal/jemalloc_preamble.h"
+#include "jemalloc/internal/jemalloc_internal_includes.h"
+
+#include "jemalloc/internal/assert.h"
+#include "jemalloc/internal/ckh.h"
+#include "jemalloc/internal/hash.h"
+#include "jemalloc/internal/malloc_io.h"
+#include "jemalloc/internal/prof_data.h"
+
+/*
+ * This file defines and manages the core profiling data structures.
+ *
+ * Conceptually, profiling data can be imagined as a table with three columns:
+ * thread, stack trace, and current allocation size. (When prof_accum is on,
+ * there's one additional column which is the cumulative allocation size.)
+ *
+ * Implementation wise, each thread maintains a hash recording the stack trace
+ * to allocation size correspondences, which are basically the individual rows
+ * in the table. In addition, two global "indices" are built to make data
+ * aggregation efficient (for dumping): bt2gctx and tdatas, which are basically
+ * the "grouped by stack trace" and "grouped by thread" views of the same table,
+ * respectively. Note that the allocation size is only aggregated to the two
+ * indices at dumping time, so as to optimize for performance.
+ */
+
+/******************************************************************************/
+
+malloc_mutex_t bt2gctx_mtx;
+malloc_mutex_t tdatas_mtx;
+malloc_mutex_t prof_dump_mtx;
+
+/*
+ * Table of mutexes that are shared among gctx's. These are leaf locks, so
+ * there is no problem with using them for more than one gctx at the same time.
+ * The primary motivation for this sharing though is that gctx's are ephemeral,
+ * and destroying mutexes causes complications for systems that allocate when
+ * creating/destroying mutexes.
+ */
+malloc_mutex_t *gctx_locks;
+static atomic_u_t cum_gctxs; /* Atomic counter. */
+
+/*
+ * Table of mutexes that are shared among tdata's. No operations require
+ * holding multiple tdata locks, so there is no problem with using them for more
+ * than one tdata at the same time, even though a gctx lock may be acquired
+ * while holding a tdata lock.
+ */
+malloc_mutex_t *tdata_locks;
+
+/*
+ * Global hash of (prof_bt_t *)-->(prof_gctx_t *). This is the master data
+ * structure that knows about all backtraces currently captured.
+ */
+static ckh_t bt2gctx;
+
+/*
+ * Tree of all extant prof_tdata_t structures, regardless of state,
+ * {attached,detached,expired}.
+ */
+static prof_tdata_tree_t tdatas;
+
+size_t prof_unbiased_sz[PROF_SC_NSIZES];
+size_t prof_shifted_unbiased_cnt[PROF_SC_NSIZES];
+
+/******************************************************************************/
+/* Red-black trees. */
+
+static int
+prof_tctx_comp(const prof_tctx_t *a, const prof_tctx_t *b) {
+ uint64_t a_thr_uid = a->thr_uid;
+ uint64_t b_thr_uid = b->thr_uid;
+ int ret = (a_thr_uid > b_thr_uid) - (a_thr_uid < b_thr_uid);
+ if (ret == 0) {
+ uint64_t a_thr_discrim = a->thr_discrim;
+ uint64_t b_thr_discrim = b->thr_discrim;
+ ret = (a_thr_discrim > b_thr_discrim) - (a_thr_discrim <
+ b_thr_discrim);
+ if (ret == 0) {
+ uint64_t a_tctx_uid = a->tctx_uid;
+ uint64_t b_tctx_uid = b->tctx_uid;
+ ret = (a_tctx_uid > b_tctx_uid) - (a_tctx_uid <
+ b_tctx_uid);
+ }
+ }
+ return ret;
+}
+
+rb_gen(static UNUSED, tctx_tree_, prof_tctx_tree_t, prof_tctx_t,
+ tctx_link, prof_tctx_comp)
+
+static int
+prof_gctx_comp(const prof_gctx_t *a, const prof_gctx_t *b) {
+ unsigned a_len = a->bt.len;
+ unsigned b_len = b->bt.len;
+ unsigned comp_len = (a_len < b_len) ? a_len : b_len;
+ int ret = memcmp(a->bt.vec, b->bt.vec, comp_len * sizeof(void *));
+ if (ret == 0) {
+ ret = (a_len > b_len) - (a_len < b_len);
+ }
+ return ret;
+}
+
+rb_gen(static UNUSED, gctx_tree_, prof_gctx_tree_t, prof_gctx_t, dump_link,
+ prof_gctx_comp)
+
+static int
+prof_tdata_comp(const prof_tdata_t *a, const prof_tdata_t *b) {
+ int ret;
+ uint64_t a_uid = a->thr_uid;
+ uint64_t b_uid = b->thr_uid;
+
+ ret = ((a_uid > b_uid) - (a_uid < b_uid));
+ if (ret == 0) {
+ uint64_t a_discrim = a->thr_discrim;
+ uint64_t b_discrim = b->thr_discrim;
+
+ ret = ((a_discrim > b_discrim) - (a_discrim < b_discrim));
+ }
+ return ret;
+}
+
+rb_gen(static UNUSED, tdata_tree_, prof_tdata_tree_t, prof_tdata_t, tdata_link,
+ prof_tdata_comp)
+
+/******************************************************************************/
+
+static malloc_mutex_t *
+prof_gctx_mutex_choose(void) {
+ unsigned ngctxs = atomic_fetch_add_u(&cum_gctxs, 1, ATOMIC_RELAXED);
+
+ return &gctx_locks[(ngctxs - 1) % PROF_NCTX_LOCKS];
+}
+
+static malloc_mutex_t *
+prof_tdata_mutex_choose(uint64_t thr_uid) {
+ return &tdata_locks[thr_uid % PROF_NTDATA_LOCKS];
+}
+
+bool
+prof_data_init(tsd_t *tsd) {
+ tdata_tree_new(&tdatas);
+ return ckh_new(tsd, &bt2gctx, PROF_CKH_MINITEMS,
+ prof_bt_hash, prof_bt_keycomp);
+}
+
+static void
+prof_enter(tsd_t *tsd, prof_tdata_t *tdata) {
+ cassert(config_prof);
+ assert(tdata == prof_tdata_get(tsd, false));
+
+ if (tdata != NULL) {
+ assert(!tdata->enq);
+ tdata->enq = true;
+ }
+
+ malloc_mutex_lock(tsd_tsdn(tsd), &bt2gctx_mtx);
+}
+
+static void
+prof_leave(tsd_t *tsd, prof_tdata_t *tdata) {
+ cassert(config_prof);
+ assert(tdata == prof_tdata_get(tsd, false));
+
+ malloc_mutex_unlock(tsd_tsdn(tsd), &bt2gctx_mtx);
+
+ if (tdata != NULL) {
+ bool idump, gdump;
+
+ assert(tdata->enq);
+ tdata->enq = false;
+ idump = tdata->enq_idump;
+ tdata->enq_idump = false;
+ gdump = tdata->enq_gdump;
+ tdata->enq_gdump = false;
+
+ if (idump) {
+ prof_idump(tsd_tsdn(tsd));
+ }
+ if (gdump) {
+ prof_gdump(tsd_tsdn(tsd));
+ }
+ }
+}
+
+static prof_gctx_t *
+prof_gctx_create(tsdn_t *tsdn, prof_bt_t *bt) {
+ /*
+ * Create a single allocation that has space for vec of length bt->len.
+ */
+ size_t size = offsetof(prof_gctx_t, vec) + (bt->len * sizeof(void *));
+ prof_gctx_t *gctx = (prof_gctx_t *)iallocztm(tsdn, size,
+ sz_size2index(size), false, NULL, true, arena_get(TSDN_NULL, 0, true),
+ true);
+ if (gctx == NULL) {
+ return NULL;
+ }
+ gctx->lock = prof_gctx_mutex_choose();
+ /*
+ * Set nlimbo to 1, in order to avoid a race condition with
+ * prof_tctx_destroy()/prof_gctx_try_destroy().
+ */
+ gctx->nlimbo = 1;
+ tctx_tree_new(&gctx->tctxs);
+ /* Duplicate bt. */
+ memcpy(gctx->vec, bt->vec, bt->len * sizeof(void *));
+ gctx->bt.vec = gctx->vec;
+ gctx->bt.len = bt->len;
+ return gctx;
+}
+
+static void
+prof_gctx_try_destroy(tsd_t *tsd, prof_tdata_t *tdata_self,
+ prof_gctx_t *gctx) {
+ cassert(config_prof);
+
+ /*
+ * Check that gctx is still unused by any thread cache before destroying
+ * it. prof_lookup() increments gctx->nlimbo in order to avoid a race
+ * condition with this function, as does prof_tctx_destroy() in order to
+ * avoid a race between the main body of prof_tctx_destroy() and entry
+ * into this function.
+ */
+ prof_enter(tsd, tdata_self);
+ malloc_mutex_lock(tsd_tsdn(tsd), gctx->lock);
+ assert(gctx->nlimbo != 0);
+ if (tctx_tree_empty(&gctx->tctxs) && gctx->nlimbo == 1) {
+ /* Remove gctx from bt2gctx. */
+ if (ckh_remove(tsd, &bt2gctx, &gctx->bt, NULL, NULL)) {
+ not_reached();
+ }
+ prof_leave(tsd, tdata_self);
+ /* Destroy gctx. */
+ malloc_mutex_unlock(tsd_tsdn(tsd), gctx->lock);
+ idalloctm(tsd_tsdn(tsd), gctx, NULL, NULL, true, true);
+ } else {
+ /*
+ * Compensate for increment in prof_tctx_destroy() or
+ * prof_lookup().
+ */
+ gctx->nlimbo--;
+ malloc_mutex_unlock(tsd_tsdn(tsd), gctx->lock);
+ prof_leave(tsd, tdata_self);
+ }
+}
+
+static bool
+prof_gctx_should_destroy(prof_gctx_t *gctx) {
+ if (opt_prof_accum) {
+ return false;
+ }
+ if (!tctx_tree_empty(&gctx->tctxs)) {
+ return false;
+ }
+ if (gctx->nlimbo != 0) {
+ return false;
+ }
+ return true;
+}
+
+static bool
+prof_lookup_global(tsd_t *tsd, prof_bt_t *bt, prof_tdata_t *tdata,
+ void **p_btkey, prof_gctx_t **p_gctx, bool *p_new_gctx) {
+ union {
+ prof_gctx_t *p;
+ void *v;
+ } gctx, tgctx;
+ union {
+ prof_bt_t *p;
+ void *v;
+ } btkey;
+ bool new_gctx;
+
+ prof_enter(tsd, tdata);
+ if (ckh_search(&bt2gctx, bt, &btkey.v, &gctx.v)) {
+ /* bt has never been seen before. Insert it. */
+ prof_leave(tsd, tdata);
+ tgctx.p = prof_gctx_create(tsd_tsdn(tsd), bt);
+ if (tgctx.v == NULL) {
+ return true;
+ }
+ prof_enter(tsd, tdata);
+ if (ckh_search(&bt2gctx, bt, &btkey.v, &gctx.v)) {
+ gctx.p = tgctx.p;
+ btkey.p = &gctx.p->bt;
+ if (ckh_insert(tsd, &bt2gctx, btkey.v, gctx.v)) {
+ /* OOM. */
+ prof_leave(tsd, tdata);
+ idalloctm(tsd_tsdn(tsd), gctx.v, NULL, NULL,
+ true, true);
+ return true;
+ }
+ new_gctx = true;
+ } else {
+ new_gctx = false;
+ }
+ } else {
+ tgctx.v = NULL;
+ new_gctx = false;
+ }
+
+ if (!new_gctx) {
+ /*
+ * Increment nlimbo, in order to avoid a race condition with
+ * prof_tctx_destroy()/prof_gctx_try_destroy().
+ */
+ malloc_mutex_lock(tsd_tsdn(tsd), gctx.p->lock);
+ gctx.p->nlimbo++;
+ malloc_mutex_unlock(tsd_tsdn(tsd), gctx.p->lock);
+ new_gctx = false;
+
+ if (tgctx.v != NULL) {
+ /* Lost race to insert. */
+ idalloctm(tsd_tsdn(tsd), tgctx.v, NULL, NULL, true,
+ true);
+ }
+ }
+ prof_leave(tsd, tdata);
+
+ *p_btkey = btkey.v;
+ *p_gctx = gctx.p;
+ *p_new_gctx = new_gctx;
+ return false;
+}
+
+prof_tctx_t *
+prof_lookup(tsd_t *tsd, prof_bt_t *bt) {
+ union {
+ prof_tctx_t *p;
+ void *v;
+ } ret;
+ prof_tdata_t *tdata;
+ bool not_found;
+
+ cassert(config_prof);
+
+ tdata = prof_tdata_get(tsd, false);
+ assert(tdata != NULL);
+
+ malloc_mutex_lock(tsd_tsdn(tsd), tdata->lock);
+ not_found = ckh_search(&tdata->bt2tctx, bt, NULL, &ret.v);
+ if (!not_found) { /* Note double negative! */
+ ret.p->prepared = true;
+ }
+ malloc_mutex_unlock(tsd_tsdn(tsd), tdata->lock);
+ if (not_found) {
+ void *btkey;
+ prof_gctx_t *gctx;
+ bool new_gctx, error;
+
+ /*
+ * This thread's cache lacks bt. Look for it in the global
+ * cache.
+ */
+ if (prof_lookup_global(tsd, bt, tdata, &btkey, &gctx,
+ &new_gctx)) {
+ return NULL;
+ }
+
+ /* Link a prof_tctx_t into gctx for this thread. */
+ ret.v = iallocztm(tsd_tsdn(tsd), sizeof(prof_tctx_t),
+ sz_size2index(sizeof(prof_tctx_t)), false, NULL, true,
+ arena_ichoose(tsd, NULL), true);
+ if (ret.p == NULL) {
+ if (new_gctx) {
+ prof_gctx_try_destroy(tsd, tdata, gctx);
+ }
+ return NULL;
+ }
+ ret.p->tdata = tdata;
+ ret.p->thr_uid = tdata->thr_uid;
+ ret.p->thr_discrim = tdata->thr_discrim;
+ ret.p->recent_count = 0;
+ memset(&ret.p->cnts, 0, sizeof(prof_cnt_t));
+ ret.p->gctx = gctx;
+ ret.p->tctx_uid = tdata->tctx_uid_next++;
+ ret.p->prepared = true;
+ ret.p->state = prof_tctx_state_initializing;
+ malloc_mutex_lock(tsd_tsdn(tsd), tdata->lock);
+ error = ckh_insert(tsd, &tdata->bt2tctx, btkey, ret.v);
+ malloc_mutex_unlock(tsd_tsdn(tsd), tdata->lock);
+ if (error) {
+ if (new_gctx) {
+ prof_gctx_try_destroy(tsd, tdata, gctx);
+ }
+ idalloctm(tsd_tsdn(tsd), ret.v, NULL, NULL, true, true);
+ return NULL;
+ }
+ malloc_mutex_lock(tsd_tsdn(tsd), gctx->lock);
+ ret.p->state = prof_tctx_state_nominal;
+ tctx_tree_insert(&gctx->tctxs, ret.p);
+ gctx->nlimbo--;
+ malloc_mutex_unlock(tsd_tsdn(tsd), gctx->lock);
+ }
+
+ return ret.p;
+}
+
+/* Used in unit tests. */
+static prof_tdata_t *
+prof_tdata_count_iter(prof_tdata_tree_t *tdatas_ptr, prof_tdata_t *tdata,
+ void *arg) {
+ size_t *tdata_count = (size_t *)arg;
+
+ (*tdata_count)++;
+
+ return NULL;
+}
+
+/* Used in unit tests. */
+size_t
+prof_tdata_count(void) {
+ size_t tdata_count = 0;
+ tsdn_t *tsdn;
+
+ tsdn = tsdn_fetch();
+ malloc_mutex_lock(tsdn, &tdatas_mtx);
+ tdata_tree_iter(&tdatas, NULL, prof_tdata_count_iter,
+ (void *)&tdata_count);
+ malloc_mutex_unlock(tsdn, &tdatas_mtx);
+
+ return tdata_count;
+}
+
+/* Used in unit tests. */
+size_t
+prof_bt_count(void) {
+ size_t bt_count;
+ tsd_t *tsd;
+ prof_tdata_t *tdata;
+
+ tsd = tsd_fetch();
+ tdata = prof_tdata_get(tsd, false);
+ if (tdata == NULL) {
+ return 0;
+ }
+
+ malloc_mutex_lock(tsd_tsdn(tsd), &bt2gctx_mtx);
+ bt_count = ckh_count(&bt2gctx);
+ malloc_mutex_unlock(tsd_tsdn(tsd), &bt2gctx_mtx);
+
+ return bt_count;
+}
+
+char *
+prof_thread_name_alloc(tsd_t *tsd, const char *thread_name) {
+ char *ret;
+ size_t size;
+
+ if (thread_name == NULL) {
+ return NULL;
+ }
+
+ size = strlen(thread_name) + 1;
+ if (size == 1) {
+ return "";
+ }
+
+ ret = iallocztm(tsd_tsdn(tsd), size, sz_size2index(size), false, NULL,
+ true, arena_get(TSDN_NULL, 0, true), true);
+ if (ret == NULL) {
+ return NULL;
+ }
+ memcpy(ret, thread_name, size);
+ return ret;
+}
+
+int
+prof_thread_name_set_impl(tsd_t *tsd, const char *thread_name) {
+ assert(tsd_reentrancy_level_get(tsd) == 0);
+
+ prof_tdata_t *tdata;
+ unsigned i;
+ char *s;
+
+ tdata = prof_tdata_get(tsd, true);
+ if (tdata == NULL) {
+ return EAGAIN;
+ }
+
+ /* Validate input. */
+ if (thread_name == NULL) {
+ return EFAULT;
+ }
+ for (i = 0; thread_name[i] != '\0'; i++) {
+ char c = thread_name[i];
+ if (!isgraph(c) && !isblank(c)) {
+ return EFAULT;
+ }
+ }
+
+ s = prof_thread_name_alloc(tsd, thread_name);
+ if (s == NULL) {
+ return EAGAIN;
+ }
+
+ if (tdata->thread_name != NULL) {
+ idalloctm(tsd_tsdn(tsd), tdata->thread_name, NULL, NULL, true,
+ true);
+ tdata->thread_name = NULL;
+ }
+ if (strlen(s) > 0) {
+ tdata->thread_name = s;
+ }
+ return 0;
+}
+
+JEMALLOC_FORMAT_PRINTF(3, 4)
+static void
+prof_dump_printf(write_cb_t *prof_dump_write, void *cbopaque,
+ const char *format, ...) {
+ va_list ap;
+ char buf[PROF_PRINTF_BUFSIZE];
+
+ va_start(ap, format);
+ malloc_vsnprintf(buf, sizeof(buf), format, ap);
+ va_end(ap);
+ prof_dump_write(cbopaque, buf);
+}
+
+/*
+ * Casting a double to a uint64_t may not necessarily be in range; this can be
+ * UB. I don't think this is practically possible with the cur counters, but
+ * plausibly could be with the accum counters.
+ */
+#ifdef JEMALLOC_PROF
+static uint64_t
+prof_double_uint64_cast(double d) {
+ /*
+ * Note: UINT64_MAX + 1 is exactly representable as a double on all
+ * reasonable platforms (certainly those we'll support). Writing this
+ * as !(a < b) instead of (a >= b) means that we're NaN-safe.
+ */
+ double rounded = round(d);
+ if (!(rounded < (double)UINT64_MAX)) {
+ return UINT64_MAX;
+ }
+ return (uint64_t)rounded;
+}
+#endif
+
+void prof_unbias_map_init() {
+ /* See the comment in prof_sample_new_event_wait */
+#ifdef JEMALLOC_PROF
+ for (szind_t i = 0; i < SC_NSIZES; i++) {
+ double sz = (double)sz_index2size(i);
+ double rate = (double)(ZU(1) << lg_prof_sample);
+ double div_val = 1.0 - exp(-sz / rate);
+ double unbiased_sz = sz / div_val;
+ /*
+ * The "true" right value for the unbiased count is
+ * 1.0/(1 - exp(-sz/rate)). The problem is, we keep the counts
+ * as integers (for a variety of reasons -- rounding errors
+ * could trigger asserts, and not all libcs can properly handle
+ * floating point arithmetic during malloc calls inside libc).
+ * Rounding to an integer, though, can lead to rounding errors
+ * of over 30% for sizes close to the sampling rate. So
+ * instead, we multiply by a constant, dividing the maximum
+ * possible roundoff error by that constant. To avoid overflow
+ * in summing up size_t values, the largest safe constant we can
+ * pick is the size of the smallest allocation.
+ */
+ double cnt_shift = (double)(ZU(1) << SC_LG_TINY_MIN);
+ double shifted_unbiased_cnt = cnt_shift / div_val;
+ prof_unbiased_sz[i] = (size_t)round(unbiased_sz);
+ prof_shifted_unbiased_cnt[i] = (size_t)round(
+ shifted_unbiased_cnt);
+ }
+#else
+ unreachable();
+#endif
+}
+
+/*
+ * The unbiasing story is long. The jeprof unbiasing logic was copied from
+ * pprof. Both shared an issue: they unbiased using the average size of the
+ * allocations at a particular stack trace. This can work out OK if allocations
+ * are mostly of the same size given some stack, but not otherwise. We now
+ * internally track what the unbiased results ought to be. We can't just report
+ * them as they are though; they'll still go through the jeprof unbiasing
+ * process. Instead, we figure out what values we can feed *into* jeprof's
+ * unbiasing mechanism that will lead to getting the right values out.
+ *
+ * It'll unbias count and aggregate size as:
+ *
+ * c_out = c_in * 1/(1-exp(-s_in/c_in/R)
+ * s_out = s_in * 1/(1-exp(-s_in/c_in/R)
+ *
+ * We want to solve for the values of c_in and s_in that will
+ * give the c_out and s_out that we've computed internally.
+ *
+ * Let's do a change of variables (both to make the math easier and to make it
+ * easier to write):
+ * x = s_in / c_in
+ * y = s_in
+ * k = 1/R.
+ *
+ * Then
+ * c_out = y/x * 1/(1-exp(-k*x))
+ * s_out = y * 1/(1-exp(-k*x))
+ *
+ * The first equation gives:
+ * y = x * c_out * (1-exp(-k*x))
+ * The second gives:
+ * y = s_out * (1-exp(-k*x))
+ * So we have
+ * x = s_out / c_out.
+ * And all the other values fall out from that.
+ *
+ * This is all a fair bit of work. The thing we get out of it is that we don't
+ * break backwards compatibility with jeprof (and the various tools that have
+ * copied its unbiasing logic). Eventually, we anticipate a v3 heap profile
+ * dump format based on JSON, at which point I think much of this logic can get
+ * cleaned up (since we'll be taking a compatibility break there anyways).
+ */
+static void
+prof_do_unbias(uint64_t c_out_shifted_i, uint64_t s_out_i, uint64_t *r_c_in,
+ uint64_t *r_s_in) {
+#ifdef JEMALLOC_PROF
+ if (c_out_shifted_i == 0 || s_out_i == 0) {
+ *r_c_in = 0;
+ *r_s_in = 0;
+ return;
+ }
+ /*
+ * See the note in prof_unbias_map_init() to see why we take c_out in a
+ * shifted form.
+ */
+ double c_out = (double)c_out_shifted_i
+ / (double)(ZU(1) << SC_LG_TINY_MIN);
+ double s_out = (double)s_out_i;
+ double R = (double)(ZU(1) << lg_prof_sample);
+
+ double x = s_out / c_out;
+ double y = s_out * (1.0 - exp(-x / R));
+
+ double c_in = y / x;
+ double s_in = y;
+
+ *r_c_in = prof_double_uint64_cast(c_in);
+ *r_s_in = prof_double_uint64_cast(s_in);
+#else
+ unreachable();
+#endif
+}
+
+static void
+prof_dump_print_cnts(write_cb_t *prof_dump_write, void *cbopaque,
+ const prof_cnt_t *cnts) {
+ uint64_t curobjs;
+ uint64_t curbytes;
+ uint64_t accumobjs;
+ uint64_t accumbytes;
+ if (opt_prof_unbias) {
+ prof_do_unbias(cnts->curobjs_shifted_unbiased,
+ cnts->curbytes_unbiased, &curobjs, &curbytes);
+ prof_do_unbias(cnts->accumobjs_shifted_unbiased,
+ cnts->accumbytes_unbiased, &accumobjs, &accumbytes);
+ } else {
+ curobjs = cnts->curobjs;
+ curbytes = cnts->curbytes;
+ accumobjs = cnts->accumobjs;
+ accumbytes = cnts->accumbytes;
+ }
+ prof_dump_printf(prof_dump_write, cbopaque,
+ "%"FMTu64": %"FMTu64" [%"FMTu64": %"FMTu64"]",
+ curobjs, curbytes, accumobjs, accumbytes);
+}
+
+static void
+prof_tctx_merge_tdata(tsdn_t *tsdn, prof_tctx_t *tctx, prof_tdata_t *tdata) {
+ malloc_mutex_assert_owner(tsdn, tctx->tdata->lock);
+
+ malloc_mutex_lock(tsdn, tctx->gctx->lock);
+
+ switch (tctx->state) {
+ case prof_tctx_state_initializing:
+ malloc_mutex_unlock(tsdn, tctx->gctx->lock);
+ return;
+ case prof_tctx_state_nominal:
+ tctx->state = prof_tctx_state_dumping;
+ malloc_mutex_unlock(tsdn, tctx->gctx->lock);
+
+ memcpy(&tctx->dump_cnts, &tctx->cnts, sizeof(prof_cnt_t));
+
+ tdata->cnt_summed.curobjs += tctx->dump_cnts.curobjs;
+ tdata->cnt_summed.curobjs_shifted_unbiased
+ += tctx->dump_cnts.curobjs_shifted_unbiased;
+ tdata->cnt_summed.curbytes += tctx->dump_cnts.curbytes;
+ tdata->cnt_summed.curbytes_unbiased
+ += tctx->dump_cnts.curbytes_unbiased;
+ if (opt_prof_accum) {
+ tdata->cnt_summed.accumobjs +=
+ tctx->dump_cnts.accumobjs;
+ tdata->cnt_summed.accumobjs_shifted_unbiased +=
+ tctx->dump_cnts.accumobjs_shifted_unbiased;
+ tdata->cnt_summed.accumbytes +=
+ tctx->dump_cnts.accumbytes;
+ tdata->cnt_summed.accumbytes_unbiased +=
+ tctx->dump_cnts.accumbytes_unbiased;
+ }
+ break;
+ case prof_tctx_state_dumping:
+ case prof_tctx_state_purgatory:
+ not_reached();
+ }
+}
+
+static void
+prof_tctx_merge_gctx(tsdn_t *tsdn, prof_tctx_t *tctx, prof_gctx_t *gctx) {
+ malloc_mutex_assert_owner(tsdn, gctx->lock);
+
+ gctx->cnt_summed.curobjs += tctx->dump_cnts.curobjs;
+ gctx->cnt_summed.curobjs_shifted_unbiased
+ += tctx->dump_cnts.curobjs_shifted_unbiased;
+ gctx->cnt_summed.curbytes += tctx->dump_cnts.curbytes;
+ gctx->cnt_summed.curbytes_unbiased += tctx->dump_cnts.curbytes_unbiased;
+ if (opt_prof_accum) {
+ gctx->cnt_summed.accumobjs += tctx->dump_cnts.accumobjs;
+ gctx->cnt_summed.accumobjs_shifted_unbiased
+ += tctx->dump_cnts.accumobjs_shifted_unbiased;
+ gctx->cnt_summed.accumbytes += tctx->dump_cnts.accumbytes;
+ gctx->cnt_summed.accumbytes_unbiased
+ += tctx->dump_cnts.accumbytes_unbiased;
+ }
+}
+
+static prof_tctx_t *
+prof_tctx_merge_iter(prof_tctx_tree_t *tctxs, prof_tctx_t *tctx, void *arg) {
+ tsdn_t *tsdn = (tsdn_t *)arg;
+
+ malloc_mutex_assert_owner(tsdn, tctx->gctx->lock);
+
+ switch (tctx->state) {
+ case prof_tctx_state_nominal:
+ /* New since dumping started; ignore. */
+ break;
+ case prof_tctx_state_dumping:
+ case prof_tctx_state_purgatory:
+ prof_tctx_merge_gctx(tsdn, tctx, tctx->gctx);
+ break;
+ default:
+ not_reached();
+ }
+
+ return NULL;
+}
+
+typedef struct prof_dump_iter_arg_s prof_dump_iter_arg_t;
+struct prof_dump_iter_arg_s {
+ tsdn_t *tsdn;
+ write_cb_t *prof_dump_write;
+ void *cbopaque;
+};
+
+static prof_tctx_t *
+prof_tctx_dump_iter(prof_tctx_tree_t *tctxs, prof_tctx_t *tctx, void *opaque) {
+ prof_dump_iter_arg_t *arg = (prof_dump_iter_arg_t *)opaque;
+ malloc_mutex_assert_owner(arg->tsdn, tctx->gctx->lock);
+
+ switch (tctx->state) {
+ case prof_tctx_state_initializing:
+ case prof_tctx_state_nominal:
+ /* Not captured by this dump. */
+ break;
+ case prof_tctx_state_dumping:
+ case prof_tctx_state_purgatory:
+ prof_dump_printf(arg->prof_dump_write, arg->cbopaque,
+ " t%"FMTu64": ", tctx->thr_uid);
+ prof_dump_print_cnts(arg->prof_dump_write, arg->cbopaque,
+ &tctx->dump_cnts);
+ arg->prof_dump_write(arg->cbopaque, "\n");
+ break;
+ default:
+ not_reached();
+ }
+ return NULL;
+}
+
+static prof_tctx_t *
+prof_tctx_finish_iter(prof_tctx_tree_t *tctxs, prof_tctx_t *tctx, void *arg) {
+ tsdn_t *tsdn = (tsdn_t *)arg;
+ prof_tctx_t *ret;
+
+ malloc_mutex_assert_owner(tsdn, tctx->gctx->lock);
+
+ switch (tctx->state) {
+ case prof_tctx_state_nominal:
+ /* New since dumping started; ignore. */
+ break;
+ case prof_tctx_state_dumping:
+ tctx->state = prof_tctx_state_nominal;
+ break;
+ case prof_tctx_state_purgatory:
+ ret = tctx;
+ goto label_return;
+ default:
+ not_reached();
+ }
+
+ ret = NULL;
+label_return:
+ return ret;
+}
+
+static void
+prof_dump_gctx_prep(tsdn_t *tsdn, prof_gctx_t *gctx, prof_gctx_tree_t *gctxs) {
+ cassert(config_prof);
+
+ malloc_mutex_lock(tsdn, gctx->lock);
+
+ /*
+ * Increment nlimbo so that gctx won't go away before dump.
+ * Additionally, link gctx into the dump list so that it is included in
+ * prof_dump()'s second pass.
+ */
+ gctx->nlimbo++;
+ gctx_tree_insert(gctxs, gctx);
+
+ memset(&gctx->cnt_summed, 0, sizeof(prof_cnt_t));
+
+ malloc_mutex_unlock(tsdn, gctx->lock);
+}
+
+typedef struct prof_gctx_merge_iter_arg_s prof_gctx_merge_iter_arg_t;
+struct prof_gctx_merge_iter_arg_s {
+ tsdn_t *tsdn;
+ size_t *leak_ngctx;
+};
+
+static prof_gctx_t *
+prof_gctx_merge_iter(prof_gctx_tree_t *gctxs, prof_gctx_t *gctx, void *opaque) {
+ prof_gctx_merge_iter_arg_t *arg = (prof_gctx_merge_iter_arg_t *)opaque;
+
+ malloc_mutex_lock(arg->tsdn, gctx->lock);
+ tctx_tree_iter(&gctx->tctxs, NULL, prof_tctx_merge_iter,
+ (void *)arg->tsdn);
+ if (gctx->cnt_summed.curobjs != 0) {
+ (*arg->leak_ngctx)++;
+ }
+ malloc_mutex_unlock(arg->tsdn, gctx->lock);
+
+ return NULL;
+}
+
+static void
+prof_gctx_finish(tsd_t *tsd, prof_gctx_tree_t *gctxs) {
+ prof_tdata_t *tdata = prof_tdata_get(tsd, false);
+ prof_gctx_t *gctx;
+
+ /*
+ * Standard tree iteration won't work here, because as soon as we
+ * decrement gctx->nlimbo and unlock gctx, another thread can
+ * concurrently destroy it, which will corrupt the tree. Therefore,
+ * tear down the tree one node at a time during iteration.
+ */
+ while ((gctx = gctx_tree_first(gctxs)) != NULL) {
+ gctx_tree_remove(gctxs, gctx);
+ malloc_mutex_lock(tsd_tsdn(tsd), gctx->lock);
+ {
+ prof_tctx_t *next;
+
+ next = NULL;
+ do {
+ prof_tctx_t *to_destroy =
+ tctx_tree_iter(&gctx->tctxs, next,
+ prof_tctx_finish_iter,
+ (void *)tsd_tsdn(tsd));
+ if (to_destroy != NULL) {
+ next = tctx_tree_next(&gctx->tctxs,
+ to_destroy);
+ tctx_tree_remove(&gctx->tctxs,
+ to_destroy);
+ idalloctm(tsd_tsdn(tsd), to_destroy,
+ NULL, NULL, true, true);
+ } else {
+ next = NULL;
+ }
+ } while (next != NULL);
+ }
+ gctx->nlimbo--;
+ if (prof_gctx_should_destroy(gctx)) {
+ gctx->nlimbo++;
+ malloc_mutex_unlock(tsd_tsdn(tsd), gctx->lock);
+ prof_gctx_try_destroy(tsd, tdata, gctx);
+ } else {
+ malloc_mutex_unlock(tsd_tsdn(tsd), gctx->lock);
+ }
+ }
+}
+
+typedef struct prof_tdata_merge_iter_arg_s prof_tdata_merge_iter_arg_t;
+struct prof_tdata_merge_iter_arg_s {
+ tsdn_t *tsdn;
+ prof_cnt_t *cnt_all;
+};
+
+static prof_tdata_t *
+prof_tdata_merge_iter(prof_tdata_tree_t *tdatas_ptr, prof_tdata_t *tdata,
+ void *opaque) {
+ prof_tdata_merge_iter_arg_t *arg =
+ (prof_tdata_merge_iter_arg_t *)opaque;
+
+ malloc_mutex_lock(arg->tsdn, tdata->lock);
+ if (!tdata->expired) {
+ size_t tabind;
+ union {
+ prof_tctx_t *p;
+ void *v;
+ } tctx;
+
+ tdata->dumping = true;
+ memset(&tdata->cnt_summed, 0, sizeof(prof_cnt_t));
+ for (tabind = 0; !ckh_iter(&tdata->bt2tctx, &tabind, NULL,
+ &tctx.v);) {
+ prof_tctx_merge_tdata(arg->tsdn, tctx.p, tdata);
+ }
+
+ arg->cnt_all->curobjs += tdata->cnt_summed.curobjs;
+ arg->cnt_all->curobjs_shifted_unbiased
+ += tdata->cnt_summed.curobjs_shifted_unbiased;
+ arg->cnt_all->curbytes += tdata->cnt_summed.curbytes;
+ arg->cnt_all->curbytes_unbiased
+ += tdata->cnt_summed.curbytes_unbiased;
+ if (opt_prof_accum) {
+ arg->cnt_all->accumobjs += tdata->cnt_summed.accumobjs;
+ arg->cnt_all->accumobjs_shifted_unbiased
+ += tdata->cnt_summed.accumobjs_shifted_unbiased;
+ arg->cnt_all->accumbytes +=
+ tdata->cnt_summed.accumbytes;
+ arg->cnt_all->accumbytes_unbiased +=
+ tdata->cnt_summed.accumbytes_unbiased;
+ }
+ } else {
+ tdata->dumping = false;
+ }
+ malloc_mutex_unlock(arg->tsdn, tdata->lock);
+
+ return NULL;
+}
+
+static prof_tdata_t *
+prof_tdata_dump_iter(prof_tdata_tree_t *tdatas_ptr, prof_tdata_t *tdata,
+ void *opaque) {
+ if (!tdata->dumping) {
+ return NULL;
+ }
+
+ prof_dump_iter_arg_t *arg = (prof_dump_iter_arg_t *)opaque;
+ prof_dump_printf(arg->prof_dump_write, arg->cbopaque, " t%"FMTu64": ",
+ tdata->thr_uid);
+ prof_dump_print_cnts(arg->prof_dump_write, arg->cbopaque,
+ &tdata->cnt_summed);
+ if (tdata->thread_name != NULL) {
+ arg->prof_dump_write(arg->cbopaque, " ");
+ arg->prof_dump_write(arg->cbopaque, tdata->thread_name);
+ }
+ arg->prof_dump_write(arg->cbopaque, "\n");
+ return NULL;
+}
+
+static void
+prof_dump_header(prof_dump_iter_arg_t *arg, const prof_cnt_t *cnt_all) {
+ prof_dump_printf(arg->prof_dump_write, arg->cbopaque,
+ "heap_v2/%"FMTu64"\n t*: ", ((uint64_t)1U << lg_prof_sample));
+ prof_dump_print_cnts(arg->prof_dump_write, arg->cbopaque, cnt_all);
+ arg->prof_dump_write(arg->cbopaque, "\n");
+
+ malloc_mutex_lock(arg->tsdn, &tdatas_mtx);
+ tdata_tree_iter(&tdatas, NULL, prof_tdata_dump_iter, arg);
+ malloc_mutex_unlock(arg->tsdn, &tdatas_mtx);
+}
+
+static void
+prof_dump_gctx(prof_dump_iter_arg_t *arg, prof_gctx_t *gctx,
+ const prof_bt_t *bt, prof_gctx_tree_t *gctxs) {
+ cassert(config_prof);
+ malloc_mutex_assert_owner(arg->tsdn, gctx->lock);
+
+ /* Avoid dumping such gctx's that have no useful data. */
+ if ((!opt_prof_accum && gctx->cnt_summed.curobjs == 0) ||
+ (opt_prof_accum && gctx->cnt_summed.accumobjs == 0)) {
+ assert(gctx->cnt_summed.curobjs == 0);
+ assert(gctx->cnt_summed.curbytes == 0);
+ /*
+ * These asserts would not be correct -- see the comment on races
+ * in prof.c
+ * assert(gctx->cnt_summed.curobjs_unbiased == 0);
+ * assert(gctx->cnt_summed.curbytes_unbiased == 0);
+ */
+ assert(gctx->cnt_summed.accumobjs == 0);
+ assert(gctx->cnt_summed.accumobjs_shifted_unbiased == 0);
+ assert(gctx->cnt_summed.accumbytes == 0);
+ assert(gctx->cnt_summed.accumbytes_unbiased == 0);
+ return;
+ }
+
+ arg->prof_dump_write(arg->cbopaque, "@");
+ for (unsigned i = 0; i < bt->len; i++) {
+ prof_dump_printf(arg->prof_dump_write, arg->cbopaque,
+ " %#"FMTxPTR, (uintptr_t)bt->vec[i]);
+ }
+
+ arg->prof_dump_write(arg->cbopaque, "\n t*: ");
+ prof_dump_print_cnts(arg->prof_dump_write, arg->cbopaque,
+ &gctx->cnt_summed);
+ arg->prof_dump_write(arg->cbopaque, "\n");
+
+ tctx_tree_iter(&gctx->tctxs, NULL, prof_tctx_dump_iter, arg);
+}
+
+/*
+ * See prof_sample_new_event_wait() comment for why the body of this function
+ * is conditionally compiled.
+ */
+static void
+prof_leakcheck(const prof_cnt_t *cnt_all, size_t leak_ngctx) {
+#ifdef JEMALLOC_PROF
+ /*
+ * Scaling is equivalent AdjustSamples() in jeprof, but the result may
+ * differ slightly from what jeprof reports, because here we scale the
+ * summary values, whereas jeprof scales each context individually and
+ * reports the sums of the scaled values.
+ */
+ if (cnt_all->curbytes != 0) {
+ double sample_period = (double)((uint64_t)1 << lg_prof_sample);
+ double ratio = (((double)cnt_all->curbytes) /
+ (double)cnt_all->curobjs) / sample_period;
+ double scale_factor = 1.0 / (1.0 - exp(-ratio));
+ uint64_t curbytes = (uint64_t)round(((double)cnt_all->curbytes)
+ * scale_factor);
+ uint64_t curobjs = (uint64_t)round(((double)cnt_all->curobjs) *
+ scale_factor);
+
+ malloc_printf("<jemalloc>: Leak approximation summary: ~%"FMTu64
+ " byte%s, ~%"FMTu64" object%s, >= %zu context%s\n",
+ curbytes, (curbytes != 1) ? "s" : "", curobjs, (curobjs !=
+ 1) ? "s" : "", leak_ngctx, (leak_ngctx != 1) ? "s" : "");
+ malloc_printf(
+ "<jemalloc>: Run jeprof on dump output for leak detail\n");
+ if (opt_prof_leak_error) {
+ malloc_printf(
+ "<jemalloc>: Exiting with error code because memory"
+ " leaks were detected\n");
+ /*
+ * Use _exit() with underscore to avoid calling atexit()
+ * and entering endless cycle.
+ */
+ _exit(1);
+ }
+ }
+#endif
+}
+
+static prof_gctx_t *
+prof_gctx_dump_iter(prof_gctx_tree_t *gctxs, prof_gctx_t *gctx, void *opaque) {
+ prof_dump_iter_arg_t *arg = (prof_dump_iter_arg_t *)opaque;
+ malloc_mutex_lock(arg->tsdn, gctx->lock);
+ prof_dump_gctx(arg, gctx, &gctx->bt, gctxs);
+ malloc_mutex_unlock(arg->tsdn, gctx->lock);
+ return NULL;
+}
+
+static void
+prof_dump_prep(tsd_t *tsd, prof_tdata_t *tdata, prof_cnt_t *cnt_all,
+ size_t *leak_ngctx, prof_gctx_tree_t *gctxs) {
+ size_t tabind;
+ union {
+ prof_gctx_t *p;
+ void *v;
+ } gctx;
+
+ prof_enter(tsd, tdata);
+
+ /*
+ * Put gctx's in limbo and clear their counters in preparation for
+ * summing.
+ */
+ gctx_tree_new(gctxs);
+ for (tabind = 0; !ckh_iter(&bt2gctx, &tabind, NULL, &gctx.v);) {
+ prof_dump_gctx_prep(tsd_tsdn(tsd), gctx.p, gctxs);
+ }
+
+ /*
+ * Iterate over tdatas, and for the non-expired ones snapshot their tctx
+ * stats and merge them into the associated gctx's.
+ */
+ memset(cnt_all, 0, sizeof(prof_cnt_t));
+ prof_tdata_merge_iter_arg_t prof_tdata_merge_iter_arg = {tsd_tsdn(tsd),
+ cnt_all};
+ malloc_mutex_lock(tsd_tsdn(tsd), &tdatas_mtx);
+ tdata_tree_iter(&tdatas, NULL, prof_tdata_merge_iter,
+ &prof_tdata_merge_iter_arg);
+ malloc_mutex_unlock(tsd_tsdn(tsd), &tdatas_mtx);
+
+ /* Merge tctx stats into gctx's. */
+ *leak_ngctx = 0;
+ prof_gctx_merge_iter_arg_t prof_gctx_merge_iter_arg = {tsd_tsdn(tsd),
+ leak_ngctx};
+ gctx_tree_iter(gctxs, NULL, prof_gctx_merge_iter,
+ &prof_gctx_merge_iter_arg);
+
+ prof_leave(tsd, tdata);
+}
+
+void
+prof_dump_impl(tsd_t *tsd, write_cb_t *prof_dump_write, void *cbopaque,
+ prof_tdata_t *tdata, bool leakcheck) {
+ malloc_mutex_assert_owner(tsd_tsdn(tsd), &prof_dump_mtx);
+ prof_cnt_t cnt_all;
+ size_t leak_ngctx;
+ prof_gctx_tree_t gctxs;
+ prof_dump_prep(tsd, tdata, &cnt_all, &leak_ngctx, &gctxs);
+ prof_dump_iter_arg_t prof_dump_iter_arg = {tsd_tsdn(tsd),
+ prof_dump_write, cbopaque};
+ prof_dump_header(&prof_dump_iter_arg, &cnt_all);
+ gctx_tree_iter(&gctxs, NULL, prof_gctx_dump_iter, &prof_dump_iter_arg);
+ prof_gctx_finish(tsd, &gctxs);
+ if (leakcheck) {
+ prof_leakcheck(&cnt_all, leak_ngctx);
+ }
+}
+
+/* Used in unit tests. */
+void
+prof_cnt_all(prof_cnt_t *cnt_all) {
+ tsd_t *tsd = tsd_fetch();
+ prof_tdata_t *tdata = prof_tdata_get(tsd, false);
+ if (tdata == NULL) {
+ memset(cnt_all, 0, sizeof(prof_cnt_t));
+ } else {
+ size_t leak_ngctx;
+ prof_gctx_tree_t gctxs;
+ prof_dump_prep(tsd, tdata, cnt_all, &leak_ngctx, &gctxs);
+ prof_gctx_finish(tsd, &gctxs);
+ }
+}
+
+void
+prof_bt_hash(const void *key, size_t r_hash[2]) {
+ prof_bt_t *bt = (prof_bt_t *)key;
+
+ cassert(config_prof);
+
+ hash(bt->vec, bt->len * sizeof(void *), 0x94122f33U, r_hash);
+}
+
+bool
+prof_bt_keycomp(const void *k1, const void *k2) {
+ const prof_bt_t *bt1 = (prof_bt_t *)k1;
+ const prof_bt_t *bt2 = (prof_bt_t *)k2;
+
+ cassert(config_prof);
+
+ if (bt1->len != bt2->len) {
+ return false;
+ }
+ return (memcmp(bt1->vec, bt2->vec, bt1->len * sizeof(void *)) == 0);
+}
+
+prof_tdata_t *
+prof_tdata_init_impl(tsd_t *tsd, uint64_t thr_uid, uint64_t thr_discrim,
+ char *thread_name, bool active) {
+ assert(tsd_reentrancy_level_get(tsd) == 0);
+
+ prof_tdata_t *tdata;
+
+ cassert(config_prof);
+
+ /* Initialize an empty cache for this thread. */
+ tdata = (prof_tdata_t *)iallocztm(tsd_tsdn(tsd), sizeof(prof_tdata_t),
+ sz_size2index(sizeof(prof_tdata_t)), false, NULL, true,
+ arena_get(TSDN_NULL, 0, true), true);
+ if (tdata == NULL) {
+ return NULL;
+ }
+
+ tdata->lock = prof_tdata_mutex_choose(thr_uid);
+ tdata->thr_uid = thr_uid;
+ tdata->thr_discrim = thr_discrim;
+ tdata->thread_name = thread_name;
+ tdata->attached = true;
+ tdata->expired = false;
+ tdata->tctx_uid_next = 0;
+
+ if (ckh_new(tsd, &tdata->bt2tctx, PROF_CKH_MINITEMS, prof_bt_hash,
+ prof_bt_keycomp)) {
+ idalloctm(tsd_tsdn(tsd), tdata, NULL, NULL, true, true);
+ return NULL;
+ }
+
+ tdata->enq = false;
+ tdata->enq_idump = false;
+ tdata->enq_gdump = false;
+
+ tdata->dumping = false;
+ tdata->active = active;
+
+ malloc_mutex_lock(tsd_tsdn(tsd), &tdatas_mtx);
+ tdata_tree_insert(&tdatas, tdata);
+ malloc_mutex_unlock(tsd_tsdn(tsd), &tdatas_mtx);
+
+ return tdata;
+}
+
+static bool
+prof_tdata_should_destroy_unlocked(prof_tdata_t *tdata, bool even_if_attached) {
+ if (tdata->attached && !even_if_attached) {
+ return false;
+ }
+ if (ckh_count(&tdata->bt2tctx) != 0) {
+ return false;
+ }
+ return true;
+}
+
+static bool
+prof_tdata_should_destroy(tsdn_t *tsdn, prof_tdata_t *tdata,
+ bool even_if_attached) {
+ malloc_mutex_assert_owner(tsdn, tdata->lock);
+
+ return prof_tdata_should_destroy_unlocked(tdata, even_if_attached);
+}
+
+static void
+prof_tdata_destroy_locked(tsd_t *tsd, prof_tdata_t *tdata,
+ bool even_if_attached) {
+ malloc_mutex_assert_owner(tsd_tsdn(tsd), &tdatas_mtx);
+ malloc_mutex_assert_not_owner(tsd_tsdn(tsd), tdata->lock);
+
+ tdata_tree_remove(&tdatas, tdata);
+
+ assert(prof_tdata_should_destroy_unlocked(tdata, even_if_attached));
+
+ if (tdata->thread_name != NULL) {
+ idalloctm(tsd_tsdn(tsd), tdata->thread_name, NULL, NULL, true,
+ true);
+ }
+ ckh_delete(tsd, &tdata->bt2tctx);
+ idalloctm(tsd_tsdn(tsd), tdata, NULL, NULL, true, true);
+}
+
+static void
+prof_tdata_destroy(tsd_t *tsd, prof_tdata_t *tdata, bool even_if_attached) {
+ malloc_mutex_lock(tsd_tsdn(tsd), &tdatas_mtx);
+ prof_tdata_destroy_locked(tsd, tdata, even_if_attached);
+ malloc_mutex_unlock(tsd_tsdn(tsd), &tdatas_mtx);
+}
+
+void
+prof_tdata_detach(tsd_t *tsd, prof_tdata_t *tdata) {
+ bool destroy_tdata;
+
+ malloc_mutex_lock(tsd_tsdn(tsd), tdata->lock);
+ if (tdata->attached) {
+ destroy_tdata = prof_tdata_should_destroy(tsd_tsdn(tsd), tdata,
+ true);
+ /*
+ * Only detach if !destroy_tdata, because detaching would allow
+ * another thread to win the race to destroy tdata.
+ */
+ if (!destroy_tdata) {
+ tdata->attached = false;
+ }
+ tsd_prof_tdata_set(tsd, NULL);
+ } else {
+ destroy_tdata = false;
+ }
+ malloc_mutex_unlock(tsd_tsdn(tsd), tdata->lock);
+ if (destroy_tdata) {
+ prof_tdata_destroy(tsd, tdata, true);
+ }
+}
+
+static bool
+prof_tdata_expire(tsdn_t *tsdn, prof_tdata_t *tdata) {
+ bool destroy_tdata;
+
+ malloc_mutex_lock(tsdn, tdata->lock);
+ if (!tdata->expired) {
+ tdata->expired = true;
+ destroy_tdata = prof_tdata_should_destroy(tsdn, tdata, false);
+ } else {
+ destroy_tdata = false;
+ }
+ malloc_mutex_unlock(tsdn, tdata->lock);
+
+ return destroy_tdata;
+}
+
+static prof_tdata_t *
+prof_tdata_reset_iter(prof_tdata_tree_t *tdatas_ptr, prof_tdata_t *tdata,
+ void *arg) {
+ tsdn_t *tsdn = (tsdn_t *)arg;
+
+ return (prof_tdata_expire(tsdn, tdata) ? tdata : NULL);
+}
+
+void
+prof_reset(tsd_t *tsd, size_t lg_sample) {
+ prof_tdata_t *next;
+
+ assert(lg_sample < (sizeof(uint64_t) << 3));
+
+ malloc_mutex_lock(tsd_tsdn(tsd), &prof_dump_mtx);
+ malloc_mutex_lock(tsd_tsdn(tsd), &tdatas_mtx);
+
+ lg_prof_sample = lg_sample;
+ prof_unbias_map_init();
+
+ next = NULL;
+ do {
+ prof_tdata_t *to_destroy = tdata_tree_iter(&tdatas, next,
+ prof_tdata_reset_iter, (void *)tsd);
+ if (to_destroy != NULL) {
+ next = tdata_tree_next(&tdatas, to_destroy);
+ prof_tdata_destroy_locked(tsd, to_destroy, false);
+ } else {
+ next = NULL;
+ }
+ } while (next != NULL);
+
+ malloc_mutex_unlock(tsd_tsdn(tsd), &tdatas_mtx);
+ malloc_mutex_unlock(tsd_tsdn(tsd), &prof_dump_mtx);
+}
+
+static bool
+prof_tctx_should_destroy(tsd_t *tsd, prof_tctx_t *tctx) {
+ malloc_mutex_assert_owner(tsd_tsdn(tsd), tctx->tdata->lock);
+
+ if (opt_prof_accum) {
+ return false;
+ }
+ if (tctx->cnts.curobjs != 0) {
+ return false;
+ }
+ if (tctx->prepared) {
+ return false;
+ }
+ if (tctx->recent_count != 0) {
+ return false;
+ }
+ return true;
+}
+
+static void
+prof_tctx_destroy(tsd_t *tsd, prof_tctx_t *tctx) {
+ malloc_mutex_assert_owner(tsd_tsdn(tsd), tctx->tdata->lock);
+
+ assert(tctx->cnts.curobjs == 0);
+ assert(tctx->cnts.curbytes == 0);
+ /*
+ * These asserts are not correct -- see the comment about races in
+ * prof.c
+ *
+ * assert(tctx->cnts.curobjs_shifted_unbiased == 0);
+ * assert(tctx->cnts.curbytes_unbiased == 0);
+ */
+ assert(!opt_prof_accum);
+ assert(tctx->cnts.accumobjs == 0);
+ assert(tctx->cnts.accumbytes == 0);
+ /*
+ * These ones are, since accumbyte counts never go down. Either
+ * prof_accum is off (in which case these should never have changed from
+ * their initial value of zero), or it's on (in which case we shouldn't
+ * be destroying this tctx).
+ */
+ assert(tctx->cnts.accumobjs_shifted_unbiased == 0);
+ assert(tctx->cnts.accumbytes_unbiased == 0);
+
+ prof_gctx_t *gctx = tctx->gctx;
+
+ {
+ prof_tdata_t *tdata = tctx->tdata;
+ tctx->tdata = NULL;
+ ckh_remove(tsd, &tdata->bt2tctx, &gctx->bt, NULL, NULL);
+ bool destroy_tdata = prof_tdata_should_destroy(tsd_tsdn(tsd),
+ tdata, false);
+ malloc_mutex_unlock(tsd_tsdn(tsd), tdata->lock);
+ if (destroy_tdata) {
+ prof_tdata_destroy(tsd, tdata, false);
+ }
+ }
+
+ bool destroy_tctx, destroy_gctx;
+
+ malloc_mutex_lock(tsd_tsdn(tsd), gctx->lock);
+ switch (tctx->state) {
+ case prof_tctx_state_nominal:
+ tctx_tree_remove(&gctx->tctxs, tctx);
+ destroy_tctx = true;
+ if (prof_gctx_should_destroy(gctx)) {
+ /*
+ * Increment gctx->nlimbo in order to keep another
+ * thread from winning the race to destroy gctx while
+ * this one has gctx->lock dropped. Without this, it
+ * would be possible for another thread to:
+ *
+ * 1) Sample an allocation associated with gctx.
+ * 2) Deallocate the sampled object.
+ * 3) Successfully prof_gctx_try_destroy(gctx).
+ *
+ * The result would be that gctx no longer exists by the
+ * time this thread accesses it in
+ * prof_gctx_try_destroy().
+ */
+ gctx->nlimbo++;
+ destroy_gctx = true;
+ } else {
+ destroy_gctx = false;
+ }
+ break;
+ case prof_tctx_state_dumping:
+ /*
+ * A dumping thread needs tctx to remain valid until dumping
+ * has finished. Change state such that the dumping thread will
+ * complete destruction during a late dump iteration phase.
+ */
+ tctx->state = prof_tctx_state_purgatory;
+ destroy_tctx = false;
+ destroy_gctx = false;
+ break;
+ default:
+ not_reached();
+ destroy_tctx = false;
+ destroy_gctx = false;
+ }
+ malloc_mutex_unlock(tsd_tsdn(tsd), gctx->lock);
+ if (destroy_gctx) {
+ prof_gctx_try_destroy(tsd, prof_tdata_get(tsd, false), gctx);
+ }
+ if (destroy_tctx) {
+ idalloctm(tsd_tsdn(tsd), tctx, NULL, NULL, true, true);
+ }
+}
+
+void
+prof_tctx_try_destroy(tsd_t *tsd, prof_tctx_t *tctx) {
+ malloc_mutex_assert_owner(tsd_tsdn(tsd), tctx->tdata->lock);
+ if (prof_tctx_should_destroy(tsd, tctx)) {
+ /* tctx->tdata->lock will be released in prof_tctx_destroy(). */
+ prof_tctx_destroy(tsd, tctx);
+ } else {
+ malloc_mutex_unlock(tsd_tsdn(tsd), tctx->tdata->lock);
+ }
+}
+
+/******************************************************************************/
diff --git a/contrib/jemalloc/src/prof_log.c b/contrib/jemalloc/src/prof_log.c
new file mode 100644
index 000000000000..0632c3b37e55
--- /dev/null
+++ b/contrib/jemalloc/src/prof_log.c
@@ -0,0 +1,717 @@
+#include "jemalloc/internal/jemalloc_preamble.h"
+#include "jemalloc/internal/jemalloc_internal_includes.h"
+
+#include "jemalloc/internal/assert.h"
+#include "jemalloc/internal/buf_writer.h"
+#include "jemalloc/internal/ckh.h"
+#include "jemalloc/internal/emitter.h"
+#include "jemalloc/internal/hash.h"
+#include "jemalloc/internal/malloc_io.h"
+#include "jemalloc/internal/mutex.h"
+#include "jemalloc/internal/prof_data.h"
+#include "jemalloc/internal/prof_log.h"
+#include "jemalloc/internal/prof_sys.h"
+
+bool opt_prof_log = false;
+typedef enum prof_logging_state_e prof_logging_state_t;
+enum prof_logging_state_e {
+ prof_logging_state_stopped,
+ prof_logging_state_started,
+ prof_logging_state_dumping
+};
+
+/*
+ * - stopped: log_start never called, or previous log_stop has completed.
+ * - started: log_start called, log_stop not called yet. Allocations are logged.
+ * - dumping: log_stop called but not finished; samples are not logged anymore.
+ */
+prof_logging_state_t prof_logging_state = prof_logging_state_stopped;
+
+/* Used in unit tests. */
+static bool prof_log_dummy = false;
+
+/* Incremented for every log file that is output. */
+static uint64_t log_seq = 0;
+static char log_filename[
+ /* Minimize memory bloat for non-prof builds. */
+#ifdef JEMALLOC_PROF
+ PATH_MAX +
+#endif
+ 1];
+
+/* Timestamp for most recent call to log_start(). */
+static nstime_t log_start_timestamp;
+
+/* Increment these when adding to the log_bt and log_thr linked lists. */
+static size_t log_bt_index = 0;
+static size_t log_thr_index = 0;
+
+/* Linked list node definitions. These are only used in this file. */
+typedef struct prof_bt_node_s prof_bt_node_t;
+
+struct prof_bt_node_s {
+ prof_bt_node_t *next;
+ size_t index;
+ prof_bt_t bt;
+ /* Variable size backtrace vector pointed to by bt. */
+ void *vec[1];
+};
+
+typedef struct prof_thr_node_s prof_thr_node_t;
+
+struct prof_thr_node_s {
+ prof_thr_node_t *next;
+ size_t index;
+ uint64_t thr_uid;
+ /* Variable size based on thr_name_sz. */
+ char name[1];
+};
+
+typedef struct prof_alloc_node_s prof_alloc_node_t;
+
+/* This is output when logging sampled allocations. */
+struct prof_alloc_node_s {
+ prof_alloc_node_t *next;
+ /* Indices into an array of thread data. */
+ size_t alloc_thr_ind;
+ size_t free_thr_ind;
+
+ /* Indices into an array of backtraces. */
+ size_t alloc_bt_ind;
+ size_t free_bt_ind;
+
+ uint64_t alloc_time_ns;
+ uint64_t free_time_ns;
+
+ size_t usize;
+};
+
+/*
+ * Created on the first call to prof_try_log and deleted on prof_log_stop.
+ * These are the backtraces and threads that have already been logged by an
+ * allocation.
+ */
+static bool log_tables_initialized = false;
+static ckh_t log_bt_node_set;
+static ckh_t log_thr_node_set;
+
+/* Store linked lists for logged data. */
+static prof_bt_node_t *log_bt_first = NULL;
+static prof_bt_node_t *log_bt_last = NULL;
+static prof_thr_node_t *log_thr_first = NULL;
+static prof_thr_node_t *log_thr_last = NULL;
+static prof_alloc_node_t *log_alloc_first = NULL;
+static prof_alloc_node_t *log_alloc_last = NULL;
+
+/* Protects the prof_logging_state and any log_{...} variable. */
+malloc_mutex_t log_mtx;
+
+/******************************************************************************/
+/*
+ * Function prototypes for static functions that are referenced prior to
+ * definition.
+ */
+
+/* Hashtable functions for log_bt_node_set and log_thr_node_set. */
+static void prof_thr_node_hash(const void *key, size_t r_hash[2]);
+static bool prof_thr_node_keycomp(const void *k1, const void *k2);
+static void prof_bt_node_hash(const void *key, size_t r_hash[2]);
+static bool prof_bt_node_keycomp(const void *k1, const void *k2);
+
+/******************************************************************************/
+
+static size_t
+prof_log_bt_index(tsd_t *tsd, prof_bt_t *bt) {
+ assert(prof_logging_state == prof_logging_state_started);
+ malloc_mutex_assert_owner(tsd_tsdn(tsd), &log_mtx);
+
+ prof_bt_node_t dummy_node;
+ dummy_node.bt = *bt;
+ prof_bt_node_t *node;
+
+ /* See if this backtrace is already cached in the table. */
+ if (ckh_search(&log_bt_node_set, (void *)(&dummy_node),
+ (void **)(&node), NULL)) {
+ size_t sz = offsetof(prof_bt_node_t, vec) +
+ (bt->len * sizeof(void *));
+ prof_bt_node_t *new_node = (prof_bt_node_t *)
+ iallocztm(tsd_tsdn(tsd), sz, sz_size2index(sz), false, NULL,
+ true, arena_get(TSDN_NULL, 0, true), true);
+ if (log_bt_first == NULL) {
+ log_bt_first = new_node;
+ log_bt_last = new_node;
+ } else {
+ log_bt_last->next = new_node;
+ log_bt_last = new_node;
+ }
+
+ new_node->next = NULL;
+ new_node->index = log_bt_index;
+ /*
+ * Copy the backtrace: bt is inside a tdata or gctx, which
+ * might die before prof_log_stop is called.
+ */
+ new_node->bt.len = bt->len;
+ memcpy(new_node->vec, bt->vec, bt->len * sizeof(void *));
+ new_node->bt.vec = new_node->vec;
+
+ log_bt_index++;
+ ckh_insert(tsd, &log_bt_node_set, (void *)new_node, NULL);
+ return new_node->index;
+ } else {
+ return node->index;
+ }
+}
+
+static size_t
+prof_log_thr_index(tsd_t *tsd, uint64_t thr_uid, const char *name) {
+ assert(prof_logging_state == prof_logging_state_started);
+ malloc_mutex_assert_owner(tsd_tsdn(tsd), &log_mtx);
+
+ prof_thr_node_t dummy_node;
+ dummy_node.thr_uid = thr_uid;
+ prof_thr_node_t *node;
+
+ /* See if this thread is already cached in the table. */
+ if (ckh_search(&log_thr_node_set, (void *)(&dummy_node),
+ (void **)(&node), NULL)) {
+ size_t sz = offsetof(prof_thr_node_t, name) + strlen(name) + 1;
+ prof_thr_node_t *new_node = (prof_thr_node_t *)
+ iallocztm(tsd_tsdn(tsd), sz, sz_size2index(sz), false, NULL,
+ true, arena_get(TSDN_NULL, 0, true), true);
+ if (log_thr_first == NULL) {
+ log_thr_first = new_node;
+ log_thr_last = new_node;
+ } else {
+ log_thr_last->next = new_node;
+ log_thr_last = new_node;
+ }
+
+ new_node->next = NULL;
+ new_node->index = log_thr_index;
+ new_node->thr_uid = thr_uid;
+ strcpy(new_node->name, name);
+
+ log_thr_index++;
+ ckh_insert(tsd, &log_thr_node_set, (void *)new_node, NULL);
+ return new_node->index;
+ } else {
+ return node->index;
+ }
+}
+
+JEMALLOC_COLD
+void
+prof_try_log(tsd_t *tsd, size_t usize, prof_info_t *prof_info) {
+ cassert(config_prof);
+ prof_tctx_t *tctx = prof_info->alloc_tctx;
+ malloc_mutex_assert_owner(tsd_tsdn(tsd), tctx->tdata->lock);
+
+ prof_tdata_t *cons_tdata = prof_tdata_get(tsd, false);
+ if (cons_tdata == NULL) {
+ /*
+ * We decide not to log these allocations. cons_tdata will be
+ * NULL only when the current thread is in a weird state (e.g.
+ * it's being destroyed).
+ */
+ return;
+ }
+
+ malloc_mutex_lock(tsd_tsdn(tsd), &log_mtx);
+
+ if (prof_logging_state != prof_logging_state_started) {
+ goto label_done;
+ }
+
+ if (!log_tables_initialized) {
+ bool err1 = ckh_new(tsd, &log_bt_node_set, PROF_CKH_MINITEMS,
+ prof_bt_node_hash, prof_bt_node_keycomp);
+ bool err2 = ckh_new(tsd, &log_thr_node_set, PROF_CKH_MINITEMS,
+ prof_thr_node_hash, prof_thr_node_keycomp);
+ if (err1 || err2) {
+ goto label_done;
+ }
+ log_tables_initialized = true;
+ }
+
+ nstime_t alloc_time = prof_info->alloc_time;
+ nstime_t free_time;
+ nstime_prof_init_update(&free_time);
+
+ size_t sz = sizeof(prof_alloc_node_t);
+ prof_alloc_node_t *new_node = (prof_alloc_node_t *)
+ iallocztm(tsd_tsdn(tsd), sz, sz_size2index(sz), false, NULL, true,
+ arena_get(TSDN_NULL, 0, true), true);
+
+ const char *prod_thr_name = (tctx->tdata->thread_name == NULL)?
+ "" : tctx->tdata->thread_name;
+ const char *cons_thr_name = prof_thread_name_get(tsd);
+
+ prof_bt_t bt;
+ /* Initialize the backtrace, using the buffer in tdata to store it. */
+ bt_init(&bt, cons_tdata->vec);
+ prof_backtrace(tsd, &bt);
+ prof_bt_t *cons_bt = &bt;
+
+ /* We haven't destroyed tctx yet, so gctx should be good to read. */
+ prof_bt_t *prod_bt = &tctx->gctx->bt;
+
+ new_node->next = NULL;
+ new_node->alloc_thr_ind = prof_log_thr_index(tsd, tctx->tdata->thr_uid,
+ prod_thr_name);
+ new_node->free_thr_ind = prof_log_thr_index(tsd, cons_tdata->thr_uid,
+ cons_thr_name);
+ new_node->alloc_bt_ind = prof_log_bt_index(tsd, prod_bt);
+ new_node->free_bt_ind = prof_log_bt_index(tsd, cons_bt);
+ new_node->alloc_time_ns = nstime_ns(&alloc_time);
+ new_node->free_time_ns = nstime_ns(&free_time);
+ new_node->usize = usize;
+
+ if (log_alloc_first == NULL) {
+ log_alloc_first = new_node;
+ log_alloc_last = new_node;
+ } else {
+ log_alloc_last->next = new_node;
+ log_alloc_last = new_node;
+ }
+
+label_done:
+ malloc_mutex_unlock(tsd_tsdn(tsd), &log_mtx);
+}
+
+static void
+prof_bt_node_hash(const void *key, size_t r_hash[2]) {
+ const prof_bt_node_t *bt_node = (prof_bt_node_t *)key;
+ prof_bt_hash((void *)(&bt_node->bt), r_hash);
+}
+
+static bool
+prof_bt_node_keycomp(const void *k1, const void *k2) {
+ const prof_bt_node_t *bt_node1 = (prof_bt_node_t *)k1;
+ const prof_bt_node_t *bt_node2 = (prof_bt_node_t *)k2;
+ return prof_bt_keycomp((void *)(&bt_node1->bt),
+ (void *)(&bt_node2->bt));
+}
+
+static void
+prof_thr_node_hash(const void *key, size_t r_hash[2]) {
+ const prof_thr_node_t *thr_node = (prof_thr_node_t *)key;
+ hash(&thr_node->thr_uid, sizeof(uint64_t), 0x94122f35U, r_hash);
+}
+
+static bool
+prof_thr_node_keycomp(const void *k1, const void *k2) {
+ const prof_thr_node_t *thr_node1 = (prof_thr_node_t *)k1;
+ const prof_thr_node_t *thr_node2 = (prof_thr_node_t *)k2;
+ return thr_node1->thr_uid == thr_node2->thr_uid;
+}
+
+/* Used in unit tests. */
+size_t
+prof_log_bt_count(void) {
+ cassert(config_prof);
+ size_t cnt = 0;
+ prof_bt_node_t *node = log_bt_first;
+ while (node != NULL) {
+ cnt++;
+ node = node->next;
+ }
+ return cnt;
+}
+
+/* Used in unit tests. */
+size_t
+prof_log_alloc_count(void) {
+ cassert(config_prof);
+ size_t cnt = 0;
+ prof_alloc_node_t *node = log_alloc_first;
+ while (node != NULL) {
+ cnt++;
+ node = node->next;
+ }
+ return cnt;
+}
+
+/* Used in unit tests. */
+size_t
+prof_log_thr_count(void) {
+ cassert(config_prof);
+ size_t cnt = 0;
+ prof_thr_node_t *node = log_thr_first;
+ while (node != NULL) {
+ cnt++;
+ node = node->next;
+ }
+ return cnt;
+}
+
+/* Used in unit tests. */
+bool
+prof_log_is_logging(void) {
+ cassert(config_prof);
+ return prof_logging_state == prof_logging_state_started;
+}
+
+/* Used in unit tests. */
+bool
+prof_log_rep_check(void) {
+ cassert(config_prof);
+ if (prof_logging_state == prof_logging_state_stopped
+ && log_tables_initialized) {
+ return true;
+ }
+
+ if (log_bt_last != NULL && log_bt_last->next != NULL) {
+ return true;
+ }
+ if (log_thr_last != NULL && log_thr_last->next != NULL) {
+ return true;
+ }
+ if (log_alloc_last != NULL && log_alloc_last->next != NULL) {
+ return true;
+ }
+
+ size_t bt_count = prof_log_bt_count();
+ size_t thr_count = prof_log_thr_count();
+ size_t alloc_count = prof_log_alloc_count();
+
+
+ if (prof_logging_state == prof_logging_state_stopped) {
+ if (bt_count != 0 || thr_count != 0 || alloc_count || 0) {
+ return true;
+ }
+ }
+
+ prof_alloc_node_t *node = log_alloc_first;
+ while (node != NULL) {
+ if (node->alloc_bt_ind >= bt_count) {
+ return true;
+ }
+ if (node->free_bt_ind >= bt_count) {
+ return true;
+ }
+ if (node->alloc_thr_ind >= thr_count) {
+ return true;
+ }
+ if (node->free_thr_ind >= thr_count) {
+ return true;
+ }
+ if (node->alloc_time_ns > node->free_time_ns) {
+ return true;
+ }
+ node = node->next;
+ }
+
+ return false;
+}
+
+/* Used in unit tests. */
+void
+prof_log_dummy_set(bool new_value) {
+ cassert(config_prof);
+ prof_log_dummy = new_value;
+}
+
+/* Used as an atexit function to stop logging on exit. */
+static void
+prof_log_stop_final(void) {
+ tsd_t *tsd = tsd_fetch();
+ prof_log_stop(tsd_tsdn(tsd));
+}
+
+JEMALLOC_COLD
+bool
+prof_log_start(tsdn_t *tsdn, const char *filename) {
+ cassert(config_prof);
+
+ if (!opt_prof) {
+ return true;
+ }
+
+ bool ret = false;
+
+ malloc_mutex_lock(tsdn, &log_mtx);
+
+ static bool prof_log_atexit_called = false;
+ if (!prof_log_atexit_called) {
+ prof_log_atexit_called = true;
+ if (atexit(prof_log_stop_final) != 0) {
+ malloc_write("<jemalloc>: Error in atexit() "
+ "for logging\n");
+ if (opt_abort) {
+ abort();
+ }
+ ret = true;
+ goto label_done;
+ }
+ }
+
+ if (prof_logging_state != prof_logging_state_stopped) {
+ ret = true;
+ } else if (filename == NULL) {
+ /* Make default name. */
+ prof_get_default_filename(tsdn, log_filename, log_seq);
+ log_seq++;
+ prof_logging_state = prof_logging_state_started;
+ } else if (strlen(filename) >= PROF_DUMP_FILENAME_LEN) {
+ ret = true;
+ } else {
+ strcpy(log_filename, filename);
+ prof_logging_state = prof_logging_state_started;
+ }
+
+ if (!ret) {
+ nstime_prof_init_update(&log_start_timestamp);
+ }
+label_done:
+ malloc_mutex_unlock(tsdn, &log_mtx);
+
+ return ret;
+}
+
+struct prof_emitter_cb_arg_s {
+ int fd;
+ ssize_t ret;
+};
+
+static void
+prof_emitter_write_cb(void *opaque, const char *to_write) {
+ struct prof_emitter_cb_arg_s *arg =
+ (struct prof_emitter_cb_arg_s *)opaque;
+ size_t bytes = strlen(to_write);
+ if (prof_log_dummy) {
+ return;
+ }
+ arg->ret = malloc_write_fd(arg->fd, to_write, bytes);
+}
+
+/*
+ * prof_log_emit_{...} goes through the appropriate linked list, emitting each
+ * node to the json and deallocating it.
+ */
+static void
+prof_log_emit_threads(tsd_t *tsd, emitter_t *emitter) {
+ emitter_json_array_kv_begin(emitter, "threads");
+ prof_thr_node_t *thr_node = log_thr_first;
+ prof_thr_node_t *thr_old_node;
+ while (thr_node != NULL) {
+ emitter_json_object_begin(emitter);
+
+ emitter_json_kv(emitter, "thr_uid", emitter_type_uint64,
+ &thr_node->thr_uid);
+
+ char *thr_name = thr_node->name;
+
+ emitter_json_kv(emitter, "thr_name", emitter_type_string,
+ &thr_name);
+
+ emitter_json_object_end(emitter);
+ thr_old_node = thr_node;
+ thr_node = thr_node->next;
+ idalloctm(tsd_tsdn(tsd), thr_old_node, NULL, NULL, true, true);
+ }
+ emitter_json_array_end(emitter);
+}
+
+static void
+prof_log_emit_traces(tsd_t *tsd, emitter_t *emitter) {
+ emitter_json_array_kv_begin(emitter, "stack_traces");
+ prof_bt_node_t *bt_node = log_bt_first;
+ prof_bt_node_t *bt_old_node;
+ /*
+ * Calculate how many hex digits we need: twice number of bytes, two for
+ * "0x", and then one more for terminating '\0'.
+ */
+ char buf[2 * sizeof(intptr_t) + 3];
+ size_t buf_sz = sizeof(buf);
+ while (bt_node != NULL) {
+ emitter_json_array_begin(emitter);
+ size_t i;
+ for (i = 0; i < bt_node->bt.len; i++) {
+ malloc_snprintf(buf, buf_sz, "%p", bt_node->bt.vec[i]);
+ char *trace_str = buf;
+ emitter_json_value(emitter, emitter_type_string,
+ &trace_str);
+ }
+ emitter_json_array_end(emitter);
+
+ bt_old_node = bt_node;
+ bt_node = bt_node->next;
+ idalloctm(tsd_tsdn(tsd), bt_old_node, NULL, NULL, true, true);
+ }
+ emitter_json_array_end(emitter);
+}
+
+static void
+prof_log_emit_allocs(tsd_t *tsd, emitter_t *emitter) {
+ emitter_json_array_kv_begin(emitter, "allocations");
+ prof_alloc_node_t *alloc_node = log_alloc_first;
+ prof_alloc_node_t *alloc_old_node;
+ while (alloc_node != NULL) {
+ emitter_json_object_begin(emitter);
+
+ emitter_json_kv(emitter, "alloc_thread", emitter_type_size,
+ &alloc_node->alloc_thr_ind);
+
+ emitter_json_kv(emitter, "free_thread", emitter_type_size,
+ &alloc_node->free_thr_ind);
+
+ emitter_json_kv(emitter, "alloc_trace", emitter_type_size,
+ &alloc_node->alloc_bt_ind);
+
+ emitter_json_kv(emitter, "free_trace", emitter_type_size,
+ &alloc_node->free_bt_ind);
+
+ emitter_json_kv(emitter, "alloc_timestamp",
+ emitter_type_uint64, &alloc_node->alloc_time_ns);
+
+ emitter_json_kv(emitter, "free_timestamp", emitter_type_uint64,
+ &alloc_node->free_time_ns);
+
+ emitter_json_kv(emitter, "usize", emitter_type_uint64,
+ &alloc_node->usize);
+
+ emitter_json_object_end(emitter);
+
+ alloc_old_node = alloc_node;
+ alloc_node = alloc_node->next;
+ idalloctm(tsd_tsdn(tsd), alloc_old_node, NULL, NULL, true,
+ true);
+ }
+ emitter_json_array_end(emitter);
+}
+
+static void
+prof_log_emit_metadata(emitter_t *emitter) {
+ emitter_json_object_kv_begin(emitter, "info");
+
+ nstime_t now;
+
+ nstime_prof_init_update(&now);
+ uint64_t ns = nstime_ns(&now) - nstime_ns(&log_start_timestamp);
+ emitter_json_kv(emitter, "duration", emitter_type_uint64, &ns);
+
+ char *vers = JEMALLOC_VERSION;
+ emitter_json_kv(emitter, "version",
+ emitter_type_string, &vers);
+
+ emitter_json_kv(emitter, "lg_sample_rate",
+ emitter_type_int, &lg_prof_sample);
+
+ const char *res_type = prof_time_res_mode_names[opt_prof_time_res];
+ emitter_json_kv(emitter, "prof_time_resolution", emitter_type_string,
+ &res_type);
+
+ int pid = prof_getpid();
+ emitter_json_kv(emitter, "pid", emitter_type_int, &pid);
+
+ emitter_json_object_end(emitter);
+}
+
+#define PROF_LOG_STOP_BUFSIZE PROF_DUMP_BUFSIZE
+JEMALLOC_COLD
+bool
+prof_log_stop(tsdn_t *tsdn) {
+ cassert(config_prof);
+ if (!opt_prof || !prof_booted) {
+ return true;
+ }
+
+ tsd_t *tsd = tsdn_tsd(tsdn);
+ malloc_mutex_lock(tsdn, &log_mtx);
+
+ if (prof_logging_state != prof_logging_state_started) {
+ malloc_mutex_unlock(tsdn, &log_mtx);
+ return true;
+ }
+
+ /*
+ * Set the state to dumping. We'll set it to stopped when we're done.
+ * Since other threads won't be able to start/stop/log when the state is
+ * dumping, we don't have to hold the lock during the whole method.
+ */
+ prof_logging_state = prof_logging_state_dumping;
+ malloc_mutex_unlock(tsdn, &log_mtx);
+
+
+ emitter_t emitter;
+
+ /* Create a file. */
+
+ int fd;
+ if (prof_log_dummy) {
+ fd = 0;
+ } else {
+ fd = creat(log_filename, 0644);
+ }
+
+ if (fd == -1) {
+ malloc_printf("<jemalloc>: creat() for log file \"%s\" "
+ " failed with %d\n", log_filename, errno);
+ if (opt_abort) {
+ abort();
+ }
+ return true;
+ }
+
+ struct prof_emitter_cb_arg_s arg;
+ arg.fd = fd;
+
+ buf_writer_t buf_writer;
+ buf_writer_init(tsdn, &buf_writer, prof_emitter_write_cb, &arg, NULL,
+ PROF_LOG_STOP_BUFSIZE);
+ emitter_init(&emitter, emitter_output_json_compact, buf_writer_cb,
+ &buf_writer);
+
+ emitter_begin(&emitter);
+ prof_log_emit_metadata(&emitter);
+ prof_log_emit_threads(tsd, &emitter);
+ prof_log_emit_traces(tsd, &emitter);
+ prof_log_emit_allocs(tsd, &emitter);
+ emitter_end(&emitter);
+
+ buf_writer_terminate(tsdn, &buf_writer);
+
+ /* Reset global state. */
+ if (log_tables_initialized) {
+ ckh_delete(tsd, &log_bt_node_set);
+ ckh_delete(tsd, &log_thr_node_set);
+ }
+ log_tables_initialized = false;
+ log_bt_index = 0;
+ log_thr_index = 0;
+ log_bt_first = NULL;
+ log_bt_last = NULL;
+ log_thr_first = NULL;
+ log_thr_last = NULL;
+ log_alloc_first = NULL;
+ log_alloc_last = NULL;
+
+ malloc_mutex_lock(tsdn, &log_mtx);
+ prof_logging_state = prof_logging_state_stopped;
+ malloc_mutex_unlock(tsdn, &log_mtx);
+
+ if (prof_log_dummy) {
+ return false;
+ }
+ return close(fd) || arg.ret == -1;
+}
+#undef PROF_LOG_STOP_BUFSIZE
+
+JEMALLOC_COLD
+bool
+prof_log_init(tsd_t *tsd) {
+ cassert(config_prof);
+ if (malloc_mutex_init(&log_mtx, "prof_log",
+ WITNESS_RANK_PROF_LOG, malloc_mutex_rank_exclusive)) {
+ return true;
+ }
+
+ if (opt_prof_log) {
+ prof_log_start(tsd_tsdn(tsd), NULL);
+ }
+
+ return false;
+}
+
+/******************************************************************************/
diff --git a/contrib/jemalloc/src/prof_recent.c b/contrib/jemalloc/src/prof_recent.c
new file mode 100644
index 000000000000..834a9446c16f
--- /dev/null
+++ b/contrib/jemalloc/src/prof_recent.c
@@ -0,0 +1,600 @@
+#include "jemalloc/internal/jemalloc_preamble.h"
+#include "jemalloc/internal/jemalloc_internal_includes.h"
+
+#include "jemalloc/internal/assert.h"
+#include "jemalloc/internal/buf_writer.h"
+#include "jemalloc/internal/emitter.h"
+#include "jemalloc/internal/prof_data.h"
+#include "jemalloc/internal/prof_recent.h"
+
+ssize_t opt_prof_recent_alloc_max = PROF_RECENT_ALLOC_MAX_DEFAULT;
+malloc_mutex_t prof_recent_alloc_mtx; /* Protects the fields below */
+static atomic_zd_t prof_recent_alloc_max;
+static ssize_t prof_recent_alloc_count = 0;
+prof_recent_list_t prof_recent_alloc_list;
+
+malloc_mutex_t prof_recent_dump_mtx; /* Protects dumping. */
+
+static void
+prof_recent_alloc_max_init() {
+ atomic_store_zd(&prof_recent_alloc_max, opt_prof_recent_alloc_max,
+ ATOMIC_RELAXED);
+}
+
+static inline ssize_t
+prof_recent_alloc_max_get_no_lock() {
+ return atomic_load_zd(&prof_recent_alloc_max, ATOMIC_RELAXED);
+}
+
+static inline ssize_t
+prof_recent_alloc_max_get(tsd_t *tsd) {
+ malloc_mutex_assert_owner(tsd_tsdn(tsd), &prof_recent_alloc_mtx);
+ return prof_recent_alloc_max_get_no_lock();
+}
+
+static inline ssize_t
+prof_recent_alloc_max_update(tsd_t *tsd, ssize_t max) {
+ malloc_mutex_assert_owner(tsd_tsdn(tsd), &prof_recent_alloc_mtx);
+ ssize_t old_max = prof_recent_alloc_max_get(tsd);
+ atomic_store_zd(&prof_recent_alloc_max, max, ATOMIC_RELAXED);
+ return old_max;
+}
+
+static prof_recent_t *
+prof_recent_allocate_node(tsdn_t *tsdn) {
+ return (prof_recent_t *)iallocztm(tsdn, sizeof(prof_recent_t),
+ sz_size2index(sizeof(prof_recent_t)), false, NULL, true,
+ arena_get(tsdn, 0, false), true);
+}
+
+static void
+prof_recent_free_node(tsdn_t *tsdn, prof_recent_t *node) {
+ assert(node != NULL);
+ assert(isalloc(tsdn, node) == sz_s2u(sizeof(prof_recent_t)));
+ idalloctm(tsdn, node, NULL, NULL, true, true);
+}
+
+static inline void
+increment_recent_count(tsd_t *tsd, prof_tctx_t *tctx) {
+ malloc_mutex_assert_owner(tsd_tsdn(tsd), tctx->tdata->lock);
+ ++tctx->recent_count;
+ assert(tctx->recent_count > 0);
+}
+
+bool
+prof_recent_alloc_prepare(tsd_t *tsd, prof_tctx_t *tctx) {
+ cassert(config_prof);
+ assert(opt_prof && prof_booted);
+ malloc_mutex_assert_owner(tsd_tsdn(tsd), tctx->tdata->lock);
+ malloc_mutex_assert_not_owner(tsd_tsdn(tsd), &prof_recent_alloc_mtx);
+
+ /*
+ * Check whether last-N mode is turned on without trying to acquire the
+ * lock, so as to optimize for the following two scenarios:
+ * (1) Last-N mode is switched off;
+ * (2) Dumping, during which last-N mode is temporarily turned off so
+ * as not to block sampled allocations.
+ */
+ if (prof_recent_alloc_max_get_no_lock() == 0) {
+ return false;
+ }
+
+ /*
+ * Increment recent_count to hold the tctx so that it won't be gone
+ * even after tctx->tdata->lock is released. This acts as a
+ * "placeholder"; the real recording of the allocation requires a lock
+ * on prof_recent_alloc_mtx and is done in prof_recent_alloc (when
+ * tctx->tdata->lock has been released).
+ */
+ increment_recent_count(tsd, tctx);
+ return true;
+}
+
+static void
+decrement_recent_count(tsd_t *tsd, prof_tctx_t *tctx) {
+ malloc_mutex_assert_not_owner(tsd_tsdn(tsd), &prof_recent_alloc_mtx);
+ assert(tctx != NULL);
+ malloc_mutex_lock(tsd_tsdn(tsd), tctx->tdata->lock);
+ assert(tctx->recent_count > 0);
+ --tctx->recent_count;
+ prof_tctx_try_destroy(tsd, tctx);
+}
+
+static inline edata_t *
+prof_recent_alloc_edata_get_no_lock(const prof_recent_t *n) {
+ return (edata_t *)atomic_load_p(&n->alloc_edata, ATOMIC_ACQUIRE);
+}
+
+edata_t *
+prof_recent_alloc_edata_get_no_lock_test(const prof_recent_t *n) {
+ cassert(config_prof);
+ return prof_recent_alloc_edata_get_no_lock(n);
+}
+
+static inline edata_t *
+prof_recent_alloc_edata_get(tsd_t *tsd, const prof_recent_t *n) {
+ malloc_mutex_assert_owner(tsd_tsdn(tsd), &prof_recent_alloc_mtx);
+ return prof_recent_alloc_edata_get_no_lock(n);
+}
+
+static void
+prof_recent_alloc_edata_set(tsd_t *tsd, prof_recent_t *n, edata_t *edata) {
+ malloc_mutex_assert_owner(tsd_tsdn(tsd), &prof_recent_alloc_mtx);
+ atomic_store_p(&n->alloc_edata, edata, ATOMIC_RELEASE);
+}
+
+void
+edata_prof_recent_alloc_init(edata_t *edata) {
+ cassert(config_prof);
+ edata_prof_recent_alloc_set_dont_call_directly(edata, NULL);
+}
+
+static inline prof_recent_t *
+edata_prof_recent_alloc_get_no_lock(const edata_t *edata) {
+ cassert(config_prof);
+ return edata_prof_recent_alloc_get_dont_call_directly(edata);
+}
+
+prof_recent_t *
+edata_prof_recent_alloc_get_no_lock_test(const edata_t *edata) {
+ cassert(config_prof);
+ return edata_prof_recent_alloc_get_no_lock(edata);
+}
+
+static inline prof_recent_t *
+edata_prof_recent_alloc_get(tsd_t *tsd, const edata_t *edata) {
+ malloc_mutex_assert_owner(tsd_tsdn(tsd), &prof_recent_alloc_mtx);
+ prof_recent_t *recent_alloc =
+ edata_prof_recent_alloc_get_no_lock(edata);
+ assert(recent_alloc == NULL ||
+ prof_recent_alloc_edata_get(tsd, recent_alloc) == edata);
+ return recent_alloc;
+}
+
+static prof_recent_t *
+edata_prof_recent_alloc_update_internal(tsd_t *tsd, edata_t *edata,
+ prof_recent_t *recent_alloc) {
+ malloc_mutex_assert_owner(tsd_tsdn(tsd), &prof_recent_alloc_mtx);
+ prof_recent_t *old_recent_alloc =
+ edata_prof_recent_alloc_get(tsd, edata);
+ edata_prof_recent_alloc_set_dont_call_directly(edata, recent_alloc);
+ return old_recent_alloc;
+}
+
+static void
+edata_prof_recent_alloc_set(tsd_t *tsd, edata_t *edata,
+ prof_recent_t *recent_alloc) {
+ malloc_mutex_assert_owner(tsd_tsdn(tsd), &prof_recent_alloc_mtx);
+ assert(recent_alloc != NULL);
+ prof_recent_t *old_recent_alloc =
+ edata_prof_recent_alloc_update_internal(tsd, edata, recent_alloc);
+ assert(old_recent_alloc == NULL);
+ prof_recent_alloc_edata_set(tsd, recent_alloc, edata);
+}
+
+static void
+edata_prof_recent_alloc_reset(tsd_t *tsd, edata_t *edata,
+ prof_recent_t *recent_alloc) {
+ malloc_mutex_assert_owner(tsd_tsdn(tsd), &prof_recent_alloc_mtx);
+ assert(recent_alloc != NULL);
+ prof_recent_t *old_recent_alloc =
+ edata_prof_recent_alloc_update_internal(tsd, edata, NULL);
+ assert(old_recent_alloc == recent_alloc);
+ assert(edata == prof_recent_alloc_edata_get(tsd, recent_alloc));
+ prof_recent_alloc_edata_set(tsd, recent_alloc, NULL);
+}
+
+/*
+ * This function should be called right before an allocation is released, so
+ * that the associated recent allocation record can contain the following
+ * information:
+ * (1) The allocation is released;
+ * (2) The time of the deallocation; and
+ * (3) The prof_tctx associated with the deallocation.
+ */
+void
+prof_recent_alloc_reset(tsd_t *tsd, edata_t *edata) {
+ cassert(config_prof);
+ /*
+ * Check whether the recent allocation record still exists without
+ * trying to acquire the lock.
+ */
+ if (edata_prof_recent_alloc_get_no_lock(edata) == NULL) {
+ return;
+ }
+
+ prof_tctx_t *dalloc_tctx = prof_tctx_create(tsd);
+ /*
+ * In case dalloc_tctx is NULL, e.g. due to OOM, we will not record the
+ * deallocation time / tctx, which is handled later, after we check
+ * again when holding the lock.
+ */
+
+ if (dalloc_tctx != NULL) {
+ malloc_mutex_lock(tsd_tsdn(tsd), dalloc_tctx->tdata->lock);
+ increment_recent_count(tsd, dalloc_tctx);
+ dalloc_tctx->prepared = false;
+ malloc_mutex_unlock(tsd_tsdn(tsd), dalloc_tctx->tdata->lock);
+ }
+
+ malloc_mutex_lock(tsd_tsdn(tsd), &prof_recent_alloc_mtx);
+ /* Check again after acquiring the lock. */
+ prof_recent_t *recent = edata_prof_recent_alloc_get(tsd, edata);
+ if (recent != NULL) {
+ assert(nstime_equals_zero(&recent->dalloc_time));
+ assert(recent->dalloc_tctx == NULL);
+ if (dalloc_tctx != NULL) {
+ nstime_prof_update(&recent->dalloc_time);
+ recent->dalloc_tctx = dalloc_tctx;
+ dalloc_tctx = NULL;
+ }
+ edata_prof_recent_alloc_reset(tsd, edata, recent);
+ }
+ malloc_mutex_unlock(tsd_tsdn(tsd), &prof_recent_alloc_mtx);
+
+ if (dalloc_tctx != NULL) {
+ /* We lost the rase - the allocation record was just gone. */
+ decrement_recent_count(tsd, dalloc_tctx);
+ }
+}
+
+static void
+prof_recent_alloc_evict_edata(tsd_t *tsd, prof_recent_t *recent_alloc) {
+ malloc_mutex_assert_owner(tsd_tsdn(tsd), &prof_recent_alloc_mtx);
+ edata_t *edata = prof_recent_alloc_edata_get(tsd, recent_alloc);
+ if (edata != NULL) {
+ edata_prof_recent_alloc_reset(tsd, edata, recent_alloc);
+ }
+}
+
+static bool
+prof_recent_alloc_is_empty(tsd_t *tsd) {
+ malloc_mutex_assert_owner(tsd_tsdn(tsd), &prof_recent_alloc_mtx);
+ if (ql_empty(&prof_recent_alloc_list)) {
+ assert(prof_recent_alloc_count == 0);
+ return true;
+ } else {
+ assert(prof_recent_alloc_count > 0);
+ return false;
+ }
+}
+
+static void
+prof_recent_alloc_assert_count(tsd_t *tsd) {
+ malloc_mutex_assert_owner(tsd_tsdn(tsd), &prof_recent_alloc_mtx);
+ if (!config_debug) {
+ return;
+ }
+ ssize_t count = 0;
+ prof_recent_t *n;
+ ql_foreach(n, &prof_recent_alloc_list, link) {
+ ++count;
+ }
+ assert(count == prof_recent_alloc_count);
+ assert(prof_recent_alloc_max_get(tsd) == -1 ||
+ count <= prof_recent_alloc_max_get(tsd));
+}
+
+void
+prof_recent_alloc(tsd_t *tsd, edata_t *edata, size_t size, size_t usize) {
+ cassert(config_prof);
+ assert(edata != NULL);
+ prof_tctx_t *tctx = edata_prof_tctx_get(edata);
+
+ malloc_mutex_assert_not_owner(tsd_tsdn(tsd), tctx->tdata->lock);
+ malloc_mutex_lock(tsd_tsdn(tsd), &prof_recent_alloc_mtx);
+ prof_recent_alloc_assert_count(tsd);
+
+ /*
+ * Reserve a new prof_recent_t node if needed. If needed, we release
+ * the prof_recent_alloc_mtx lock and allocate. Then, rather than
+ * immediately checking for OOM, we regain the lock and try to make use
+ * of the reserve node if needed. There are six scenarios:
+ *
+ * \ now | no need | need but OOMed | need and allocated
+ * later \ | | |
+ * ------------------------------------------------------------
+ * no need | (1) | (2) | (3)
+ * ------------------------------------------------------------
+ * need | (4) | (5) | (6)
+ *
+ * First, "(4)" never happens, because we don't release the lock in the
+ * middle if there's no need for a new node; in such cases "(1)" always
+ * takes place, which is trivial.
+ *
+ * Out of the remaining four scenarios, "(6)" is the common case and is
+ * trivial. "(5)" is also trivial, in which case we'll rollback the
+ * effect of prof_recent_alloc_prepare() as expected.
+ *
+ * "(2)" / "(3)" occurs when the need for a new node is gone after we
+ * regain the lock. If the new node is successfully allocated, i.e. in
+ * the case of "(3)", we'll release it in the end; otherwise, i.e. in
+ * the case of "(2)", we do nothing - we're lucky that the OOM ends up
+ * doing no harm at all.
+ *
+ * Therefore, the only performance cost of the "release lock" ->
+ * "allocate" -> "regain lock" design is the "(3)" case, but it happens
+ * very rarely, so the cost is relatively small compared to the gain of
+ * not having to have the lock order of prof_recent_alloc_mtx above all
+ * the allocation locks.
+ */
+ prof_recent_t *reserve = NULL;
+ if (prof_recent_alloc_max_get(tsd) == -1 ||
+ prof_recent_alloc_count < prof_recent_alloc_max_get(tsd)) {
+ assert(prof_recent_alloc_max_get(tsd) != 0);
+ malloc_mutex_unlock(tsd_tsdn(tsd), &prof_recent_alloc_mtx);
+ reserve = prof_recent_allocate_node(tsd_tsdn(tsd));
+ malloc_mutex_lock(tsd_tsdn(tsd), &prof_recent_alloc_mtx);
+ prof_recent_alloc_assert_count(tsd);
+ }
+
+ if (prof_recent_alloc_max_get(tsd) == 0) {
+ assert(prof_recent_alloc_is_empty(tsd));
+ goto label_rollback;
+ }
+
+ prof_tctx_t *old_alloc_tctx, *old_dalloc_tctx;
+ if (prof_recent_alloc_count == prof_recent_alloc_max_get(tsd)) {
+ /* If upper limit is reached, rotate the head. */
+ assert(prof_recent_alloc_max_get(tsd) != -1);
+ assert(!prof_recent_alloc_is_empty(tsd));
+ prof_recent_t *head = ql_first(&prof_recent_alloc_list);
+ old_alloc_tctx = head->alloc_tctx;
+ assert(old_alloc_tctx != NULL);
+ old_dalloc_tctx = head->dalloc_tctx;
+ prof_recent_alloc_evict_edata(tsd, head);
+ ql_rotate(&prof_recent_alloc_list, link);
+ } else {
+ /* Otherwise make use of the new node. */
+ assert(prof_recent_alloc_max_get(tsd) == -1 ||
+ prof_recent_alloc_count < prof_recent_alloc_max_get(tsd));
+ if (reserve == NULL) {
+ goto label_rollback;
+ }
+ ql_elm_new(reserve, link);
+ ql_tail_insert(&prof_recent_alloc_list, reserve, link);
+ reserve = NULL;
+ old_alloc_tctx = NULL;
+ old_dalloc_tctx = NULL;
+ ++prof_recent_alloc_count;
+ }
+
+ /* Fill content into the tail node. */
+ prof_recent_t *tail = ql_last(&prof_recent_alloc_list, link);
+ assert(tail != NULL);
+ tail->size = size;
+ tail->usize = usize;
+ nstime_copy(&tail->alloc_time, edata_prof_alloc_time_get(edata));
+ tail->alloc_tctx = tctx;
+ nstime_init_zero(&tail->dalloc_time);
+ tail->dalloc_tctx = NULL;
+ edata_prof_recent_alloc_set(tsd, edata, tail);
+
+ assert(!prof_recent_alloc_is_empty(tsd));
+ prof_recent_alloc_assert_count(tsd);
+ malloc_mutex_unlock(tsd_tsdn(tsd), &prof_recent_alloc_mtx);
+
+ if (reserve != NULL) {
+ prof_recent_free_node(tsd_tsdn(tsd), reserve);
+ }
+
+ /*
+ * Asynchronously handle the tctx of the old node, so that there's no
+ * simultaneous holdings of prof_recent_alloc_mtx and tdata->lock.
+ * In the worst case this may delay the tctx release but it's better
+ * than holding prof_recent_alloc_mtx for longer.
+ */
+ if (old_alloc_tctx != NULL) {
+ decrement_recent_count(tsd, old_alloc_tctx);
+ }
+ if (old_dalloc_tctx != NULL) {
+ decrement_recent_count(tsd, old_dalloc_tctx);
+ }
+ return;
+
+label_rollback:
+ assert(edata_prof_recent_alloc_get(tsd, edata) == NULL);
+ prof_recent_alloc_assert_count(tsd);
+ malloc_mutex_unlock(tsd_tsdn(tsd), &prof_recent_alloc_mtx);
+ if (reserve != NULL) {
+ prof_recent_free_node(tsd_tsdn(tsd), reserve);
+ }
+ decrement_recent_count(tsd, tctx);
+}
+
+ssize_t
+prof_recent_alloc_max_ctl_read() {
+ cassert(config_prof);
+ /* Don't bother to acquire the lock. */
+ return prof_recent_alloc_max_get_no_lock();
+}
+
+static void
+prof_recent_alloc_restore_locked(tsd_t *tsd, prof_recent_list_t *to_delete) {
+ malloc_mutex_assert_owner(tsd_tsdn(tsd), &prof_recent_alloc_mtx);
+ ssize_t max = prof_recent_alloc_max_get(tsd);
+ if (max == -1 || prof_recent_alloc_count <= max) {
+ /* Easy case - no need to alter the list. */
+ ql_new(to_delete);
+ prof_recent_alloc_assert_count(tsd);
+ return;
+ }
+
+ prof_recent_t *node;
+ ql_foreach(node, &prof_recent_alloc_list, link) {
+ if (prof_recent_alloc_count == max) {
+ break;
+ }
+ prof_recent_alloc_evict_edata(tsd, node);
+ --prof_recent_alloc_count;
+ }
+ assert(prof_recent_alloc_count == max);
+
+ ql_move(to_delete, &prof_recent_alloc_list);
+ if (max == 0) {
+ assert(node == NULL);
+ } else {
+ assert(node != NULL);
+ ql_split(to_delete, node, &prof_recent_alloc_list, link);
+ }
+ assert(!ql_empty(to_delete));
+ prof_recent_alloc_assert_count(tsd);
+}
+
+static void
+prof_recent_alloc_async_cleanup(tsd_t *tsd, prof_recent_list_t *to_delete) {
+ malloc_mutex_assert_not_owner(tsd_tsdn(tsd), &prof_recent_dump_mtx);
+ malloc_mutex_assert_not_owner(tsd_tsdn(tsd), &prof_recent_alloc_mtx);
+ while (!ql_empty(to_delete)) {
+ prof_recent_t *node = ql_first(to_delete);
+ ql_remove(to_delete, node, link);
+ decrement_recent_count(tsd, node->alloc_tctx);
+ if (node->dalloc_tctx != NULL) {
+ decrement_recent_count(tsd, node->dalloc_tctx);
+ }
+ prof_recent_free_node(tsd_tsdn(tsd), node);
+ }
+}
+
+ssize_t
+prof_recent_alloc_max_ctl_write(tsd_t *tsd, ssize_t max) {
+ cassert(config_prof);
+ assert(max >= -1);
+ malloc_mutex_lock(tsd_tsdn(tsd), &prof_recent_alloc_mtx);
+ prof_recent_alloc_assert_count(tsd);
+ const ssize_t old_max = prof_recent_alloc_max_update(tsd, max);
+ prof_recent_list_t to_delete;
+ prof_recent_alloc_restore_locked(tsd, &to_delete);
+ malloc_mutex_unlock(tsd_tsdn(tsd), &prof_recent_alloc_mtx);
+ prof_recent_alloc_async_cleanup(tsd, &to_delete);
+ return old_max;
+}
+
+static void
+prof_recent_alloc_dump_bt(emitter_t *emitter, prof_tctx_t *tctx) {
+ char bt_buf[2 * sizeof(intptr_t) + 3];
+ char *s = bt_buf;
+ assert(tctx != NULL);
+ prof_bt_t *bt = &tctx->gctx->bt;
+ for (size_t i = 0; i < bt->len; ++i) {
+ malloc_snprintf(bt_buf, sizeof(bt_buf), "%p", bt->vec[i]);
+ emitter_json_value(emitter, emitter_type_string, &s);
+ }
+}
+
+static void
+prof_recent_alloc_dump_node(emitter_t *emitter, prof_recent_t *node) {
+ emitter_json_object_begin(emitter);
+
+ emitter_json_kv(emitter, "size", emitter_type_size, &node->size);
+ emitter_json_kv(emitter, "usize", emitter_type_size, &node->usize);
+ bool released = prof_recent_alloc_edata_get_no_lock(node) == NULL;
+ emitter_json_kv(emitter, "released", emitter_type_bool, &released);
+
+ emitter_json_kv(emitter, "alloc_thread_uid", emitter_type_uint64,
+ &node->alloc_tctx->thr_uid);
+ prof_tdata_t *alloc_tdata = node->alloc_tctx->tdata;
+ assert(alloc_tdata != NULL);
+ if (alloc_tdata->thread_name != NULL) {
+ emitter_json_kv(emitter, "alloc_thread_name",
+ emitter_type_string, &alloc_tdata->thread_name);
+ }
+ uint64_t alloc_time_ns = nstime_ns(&node->alloc_time);
+ emitter_json_kv(emitter, "alloc_time", emitter_type_uint64,
+ &alloc_time_ns);
+ emitter_json_array_kv_begin(emitter, "alloc_trace");
+ prof_recent_alloc_dump_bt(emitter, node->alloc_tctx);
+ emitter_json_array_end(emitter);
+
+ if (released && node->dalloc_tctx != NULL) {
+ emitter_json_kv(emitter, "dalloc_thread_uid",
+ emitter_type_uint64, &node->dalloc_tctx->thr_uid);
+ prof_tdata_t *dalloc_tdata = node->dalloc_tctx->tdata;
+ assert(dalloc_tdata != NULL);
+ if (dalloc_tdata->thread_name != NULL) {
+ emitter_json_kv(emitter, "dalloc_thread_name",
+ emitter_type_string, &dalloc_tdata->thread_name);
+ }
+ assert(!nstime_equals_zero(&node->dalloc_time));
+ uint64_t dalloc_time_ns = nstime_ns(&node->dalloc_time);
+ emitter_json_kv(emitter, "dalloc_time", emitter_type_uint64,
+ &dalloc_time_ns);
+ emitter_json_array_kv_begin(emitter, "dalloc_trace");
+ prof_recent_alloc_dump_bt(emitter, node->dalloc_tctx);
+ emitter_json_array_end(emitter);
+ }
+
+ emitter_json_object_end(emitter);
+}
+
+#define PROF_RECENT_PRINT_BUFSIZE 65536
+JEMALLOC_COLD
+void
+prof_recent_alloc_dump(tsd_t *tsd, write_cb_t *write_cb, void *cbopaque) {
+ cassert(config_prof);
+ malloc_mutex_lock(tsd_tsdn(tsd), &prof_recent_dump_mtx);
+ buf_writer_t buf_writer;
+ buf_writer_init(tsd_tsdn(tsd), &buf_writer, write_cb, cbopaque, NULL,
+ PROF_RECENT_PRINT_BUFSIZE);
+ emitter_t emitter;
+ emitter_init(&emitter, emitter_output_json_compact, buf_writer_cb,
+ &buf_writer);
+ prof_recent_list_t temp_list;
+
+ malloc_mutex_lock(tsd_tsdn(tsd), &prof_recent_alloc_mtx);
+ prof_recent_alloc_assert_count(tsd);
+ ssize_t dump_max = prof_recent_alloc_max_get(tsd);
+ ql_move(&temp_list, &prof_recent_alloc_list);
+ ssize_t dump_count = prof_recent_alloc_count;
+ prof_recent_alloc_count = 0;
+ prof_recent_alloc_assert_count(tsd);
+ malloc_mutex_unlock(tsd_tsdn(tsd), &prof_recent_alloc_mtx);
+
+ emitter_begin(&emitter);
+ uint64_t sample_interval = (uint64_t)1U << lg_prof_sample;
+ emitter_json_kv(&emitter, "sample_interval", emitter_type_uint64,
+ &sample_interval);
+ emitter_json_kv(&emitter, "recent_alloc_max", emitter_type_ssize,
+ &dump_max);
+ emitter_json_array_kv_begin(&emitter, "recent_alloc");
+ prof_recent_t *node;
+ ql_foreach(node, &temp_list, link) {
+ prof_recent_alloc_dump_node(&emitter, node);
+ }
+ emitter_json_array_end(&emitter);
+ emitter_end(&emitter);
+
+ malloc_mutex_lock(tsd_tsdn(tsd), &prof_recent_alloc_mtx);
+ prof_recent_alloc_assert_count(tsd);
+ ql_concat(&temp_list, &prof_recent_alloc_list, link);
+ ql_move(&prof_recent_alloc_list, &temp_list);
+ prof_recent_alloc_count += dump_count;
+ prof_recent_alloc_restore_locked(tsd, &temp_list);
+ malloc_mutex_unlock(tsd_tsdn(tsd), &prof_recent_alloc_mtx);
+
+ buf_writer_terminate(tsd_tsdn(tsd), &buf_writer);
+ malloc_mutex_unlock(tsd_tsdn(tsd), &prof_recent_dump_mtx);
+
+ prof_recent_alloc_async_cleanup(tsd, &temp_list);
+}
+#undef PROF_RECENT_PRINT_BUFSIZE
+
+bool
+prof_recent_init() {
+ cassert(config_prof);
+ prof_recent_alloc_max_init();
+
+ if (malloc_mutex_init(&prof_recent_alloc_mtx, "prof_recent_alloc",
+ WITNESS_RANK_PROF_RECENT_ALLOC, malloc_mutex_rank_exclusive)) {
+ return true;
+ }
+
+ if (malloc_mutex_init(&prof_recent_dump_mtx, "prof_recent_dump",
+ WITNESS_RANK_PROF_RECENT_DUMP, malloc_mutex_rank_exclusive)) {
+ return true;
+ }
+
+ ql_new(&prof_recent_alloc_list);
+
+ return false;
+}
diff --git a/contrib/jemalloc/src/prof_stats.c b/contrib/jemalloc/src/prof_stats.c
new file mode 100644
index 000000000000..5d1a506bb72d
--- /dev/null
+++ b/contrib/jemalloc/src/prof_stats.c
@@ -0,0 +1,57 @@
+#include "jemalloc/internal/jemalloc_preamble.h"
+#include "jemalloc/internal/jemalloc_internal_includes.h"
+
+#include "jemalloc/internal/prof_stats.h"
+
+bool opt_prof_stats = false;
+malloc_mutex_t prof_stats_mtx;
+static prof_stats_t prof_stats_live[PROF_SC_NSIZES];
+static prof_stats_t prof_stats_accum[PROF_SC_NSIZES];
+
+static void
+prof_stats_enter(tsd_t *tsd, szind_t ind) {
+ assert(opt_prof && opt_prof_stats);
+ assert(ind < SC_NSIZES);
+ malloc_mutex_lock(tsd_tsdn(tsd), &prof_stats_mtx);
+}
+
+static void
+prof_stats_leave(tsd_t *tsd) {
+ malloc_mutex_unlock(tsd_tsdn(tsd), &prof_stats_mtx);
+}
+
+void
+prof_stats_inc(tsd_t *tsd, szind_t ind, size_t size) {
+ cassert(config_prof);
+ prof_stats_enter(tsd, ind);
+ prof_stats_live[ind].req_sum += size;
+ prof_stats_live[ind].count++;
+ prof_stats_accum[ind].req_sum += size;
+ prof_stats_accum[ind].count++;
+ prof_stats_leave(tsd);
+}
+
+void
+prof_stats_dec(tsd_t *tsd, szind_t ind, size_t size) {
+ cassert(config_prof);
+ prof_stats_enter(tsd, ind);
+ prof_stats_live[ind].req_sum -= size;
+ prof_stats_live[ind].count--;
+ prof_stats_leave(tsd);
+}
+
+void
+prof_stats_get_live(tsd_t *tsd, szind_t ind, prof_stats_t *stats) {
+ cassert(config_prof);
+ prof_stats_enter(tsd, ind);
+ memcpy(stats, &prof_stats_live[ind], sizeof(prof_stats_t));
+ prof_stats_leave(tsd);
+}
+
+void
+prof_stats_get_accum(tsd_t *tsd, szind_t ind, prof_stats_t *stats) {
+ cassert(config_prof);
+ prof_stats_enter(tsd, ind);
+ memcpy(stats, &prof_stats_accum[ind], sizeof(prof_stats_t));
+ prof_stats_leave(tsd);
+}
diff --git a/contrib/jemalloc/src/prof_sys.c b/contrib/jemalloc/src/prof_sys.c
new file mode 100644
index 000000000000..b5f1f5b225e1
--- /dev/null
+++ b/contrib/jemalloc/src/prof_sys.c
@@ -0,0 +1,669 @@
+#define JEMALLOC_PROF_SYS_C_
+#include "jemalloc/internal/jemalloc_preamble.h"
+#include "jemalloc/internal/jemalloc_internal_includes.h"
+
+#include "jemalloc/internal/buf_writer.h"
+#include "jemalloc/internal/ctl.h"
+#include "jemalloc/internal/prof_data.h"
+#include "jemalloc/internal/prof_sys.h"
+
+#ifdef JEMALLOC_PROF_LIBUNWIND
+#define UNW_LOCAL_ONLY
+#include <libunwind.h>
+#endif
+
+#ifdef JEMALLOC_PROF_LIBGCC
+/*
+ * We have a circular dependency -- jemalloc_internal.h tells us if we should
+ * use libgcc's unwinding functionality, but after we've included that, we've
+ * already hooked _Unwind_Backtrace. We'll temporarily disable hooking.
+ */
+#undef _Unwind_Backtrace
+#include <unwind.h>
+#define _Unwind_Backtrace JEMALLOC_TEST_HOOK(_Unwind_Backtrace, test_hooks_libc_hook)
+#endif
+
+/******************************************************************************/
+
+malloc_mutex_t prof_dump_filename_mtx;
+
+bool prof_do_mock = false;
+
+static uint64_t prof_dump_seq;
+static uint64_t prof_dump_iseq;
+static uint64_t prof_dump_mseq;
+static uint64_t prof_dump_useq;
+
+static char *prof_prefix = NULL;
+
+/* The fallback allocator profiling functionality will use. */
+base_t *prof_base;
+
+void
+bt_init(prof_bt_t *bt, void **vec) {
+ cassert(config_prof);
+
+ bt->vec = vec;
+ bt->len = 0;
+}
+
+#ifdef JEMALLOC_PROF_LIBUNWIND
+static void
+prof_backtrace_impl(void **vec, unsigned *len, unsigned max_len) {
+ int nframes;
+
+ cassert(config_prof);
+ assert(*len == 0);
+ assert(vec != NULL);
+ assert(max_len == PROF_BT_MAX);
+
+ nframes = unw_backtrace(vec, PROF_BT_MAX);
+ if (nframes <= 0) {
+ return;
+ }
+ *len = nframes;
+}
+#elif (defined(JEMALLOC_PROF_LIBGCC))
+static _Unwind_Reason_Code
+prof_unwind_init_callback(struct _Unwind_Context *context, void *arg) {
+ cassert(config_prof);
+
+ return _URC_NO_REASON;
+}
+
+static _Unwind_Reason_Code
+prof_unwind_callback(struct _Unwind_Context *context, void *arg) {
+ prof_unwind_data_t *data = (prof_unwind_data_t *)arg;
+ void *ip;
+
+ cassert(config_prof);
+
+ ip = (void *)_Unwind_GetIP(context);
+ if (ip == NULL) {
+ return _URC_END_OF_STACK;
+ }
+ data->vec[*data->len] = ip;
+ (*data->len)++;
+ if (*data->len == data->max) {
+ return _URC_END_OF_STACK;
+ }
+
+ return _URC_NO_REASON;
+}
+
+static void
+prof_backtrace_impl(void **vec, unsigned *len, unsigned max_len) {
+ prof_unwind_data_t data = {vec, len, max_len};
+
+ cassert(config_prof);
+ assert(vec != NULL);
+ assert(max_len == PROF_BT_MAX);
+
+ _Unwind_Backtrace(prof_unwind_callback, &data);
+}
+#elif (defined(JEMALLOC_PROF_GCC))
+static void
+prof_backtrace_impl(void **vec, unsigned *len, unsigned max_len) {
+#define BT_FRAME(i) \
+ if ((i) < max_len) { \
+ void *p; \
+ if (__builtin_frame_address(i) == 0) { \
+ return; \
+ } \
+ p = __builtin_return_address(i); \
+ if (p == NULL) { \
+ return; \
+ } \
+ vec[(i)] = p; \
+ *len = (i) + 1; \
+ } else { \
+ return; \
+ }
+
+ cassert(config_prof);
+ assert(vec != NULL);
+ assert(max_len == PROF_BT_MAX);
+
+ BT_FRAME(0)
+ BT_FRAME(1)
+ BT_FRAME(2)
+ BT_FRAME(3)
+ BT_FRAME(4)
+ BT_FRAME(5)
+ BT_FRAME(6)
+ BT_FRAME(7)
+ BT_FRAME(8)
+ BT_FRAME(9)
+
+ BT_FRAME(10)
+ BT_FRAME(11)
+ BT_FRAME(12)
+ BT_FRAME(13)
+ BT_FRAME(14)
+ BT_FRAME(15)
+ BT_FRAME(16)
+ BT_FRAME(17)
+ BT_FRAME(18)
+ BT_FRAME(19)
+
+ BT_FRAME(20)
+ BT_FRAME(21)
+ BT_FRAME(22)
+ BT_FRAME(23)
+ BT_FRAME(24)
+ BT_FRAME(25)
+ BT_FRAME(26)
+ BT_FRAME(27)
+ BT_FRAME(28)
+ BT_FRAME(29)
+
+ BT_FRAME(30)
+ BT_FRAME(31)
+ BT_FRAME(32)
+ BT_FRAME(33)
+ BT_FRAME(34)
+ BT_FRAME(35)
+ BT_FRAME(36)
+ BT_FRAME(37)
+ BT_FRAME(38)
+ BT_FRAME(39)
+
+ BT_FRAME(40)
+ BT_FRAME(41)
+ BT_FRAME(42)
+ BT_FRAME(43)
+ BT_FRAME(44)
+ BT_FRAME(45)
+ BT_FRAME(46)
+ BT_FRAME(47)
+ BT_FRAME(48)
+ BT_FRAME(49)
+
+ BT_FRAME(50)
+ BT_FRAME(51)
+ BT_FRAME(52)
+ BT_FRAME(53)
+ BT_FRAME(54)
+ BT_FRAME(55)
+ BT_FRAME(56)
+ BT_FRAME(57)
+ BT_FRAME(58)
+ BT_FRAME(59)
+
+ BT_FRAME(60)
+ BT_FRAME(61)
+ BT_FRAME(62)
+ BT_FRAME(63)
+ BT_FRAME(64)
+ BT_FRAME(65)
+ BT_FRAME(66)
+ BT_FRAME(67)
+ BT_FRAME(68)
+ BT_FRAME(69)
+
+ BT_FRAME(70)
+ BT_FRAME(71)
+ BT_FRAME(72)
+ BT_FRAME(73)
+ BT_FRAME(74)
+ BT_FRAME(75)
+ BT_FRAME(76)
+ BT_FRAME(77)
+ BT_FRAME(78)
+ BT_FRAME(79)
+
+ BT_FRAME(80)
+ BT_FRAME(81)
+ BT_FRAME(82)
+ BT_FRAME(83)
+ BT_FRAME(84)
+ BT_FRAME(85)
+ BT_FRAME(86)
+ BT_FRAME(87)
+ BT_FRAME(88)
+ BT_FRAME(89)
+
+ BT_FRAME(90)
+ BT_FRAME(91)
+ BT_FRAME(92)
+ BT_FRAME(93)
+ BT_FRAME(94)
+ BT_FRAME(95)
+ BT_FRAME(96)
+ BT_FRAME(97)
+ BT_FRAME(98)
+ BT_FRAME(99)
+
+ BT_FRAME(100)
+ BT_FRAME(101)
+ BT_FRAME(102)
+ BT_FRAME(103)
+ BT_FRAME(104)
+ BT_FRAME(105)
+ BT_FRAME(106)
+ BT_FRAME(107)
+ BT_FRAME(108)
+ BT_FRAME(109)
+
+ BT_FRAME(110)
+ BT_FRAME(111)
+ BT_FRAME(112)
+ BT_FRAME(113)
+ BT_FRAME(114)
+ BT_FRAME(115)
+ BT_FRAME(116)
+ BT_FRAME(117)
+ BT_FRAME(118)
+ BT_FRAME(119)
+
+ BT_FRAME(120)
+ BT_FRAME(121)
+ BT_FRAME(122)
+ BT_FRAME(123)
+ BT_FRAME(124)
+ BT_FRAME(125)
+ BT_FRAME(126)
+ BT_FRAME(127)
+#undef BT_FRAME
+}
+#else
+static void
+prof_backtrace_impl(void **vec, unsigned *len, unsigned max_len) {
+ cassert(config_prof);
+ not_reached();
+}
+#endif
+
+void
+prof_backtrace(tsd_t *tsd, prof_bt_t *bt) {
+ cassert(config_prof);
+ prof_backtrace_hook_t prof_backtrace_hook = prof_backtrace_hook_get();
+ assert(prof_backtrace_hook != NULL);
+
+ pre_reentrancy(tsd, NULL);
+ prof_backtrace_hook(bt->vec, &bt->len, PROF_BT_MAX);
+ post_reentrancy(tsd);
+}
+
+void
+prof_hooks_init() {
+ prof_backtrace_hook_set(&prof_backtrace_impl);
+ prof_dump_hook_set(NULL);
+}
+
+void
+prof_unwind_init() {
+#ifdef JEMALLOC_PROF_LIBGCC
+ /*
+ * Cause the backtracing machinery to allocate its internal
+ * state before enabling profiling.
+ */
+ _Unwind_Backtrace(prof_unwind_init_callback, NULL);
+#endif
+}
+
+static int
+prof_sys_thread_name_read_impl(char *buf, size_t limit) {
+#if defined(JEMALLOC_HAVE_PTHREAD_GETNAME_NP)
+ return pthread_getname_np(pthread_self(), buf, limit);
+#elif defined(JEMALLOC_HAVE_PTHREAD_GET_NAME_NP)
+ pthread_get_name_np(pthread_self(), buf, limit);
+ return 0;
+#else
+ return ENOSYS;
+#endif
+}
+prof_sys_thread_name_read_t *JET_MUTABLE prof_sys_thread_name_read =
+ prof_sys_thread_name_read_impl;
+
+void
+prof_sys_thread_name_fetch(tsd_t *tsd) {
+#define THREAD_NAME_MAX_LEN 16
+ char buf[THREAD_NAME_MAX_LEN];
+ if (!prof_sys_thread_name_read(buf, THREAD_NAME_MAX_LEN)) {
+ prof_thread_name_set_impl(tsd, buf);
+ }
+#undef THREAD_NAME_MAX_LEN
+}
+
+int
+prof_getpid(void) {
+#ifdef _WIN32
+ return GetCurrentProcessId();
+#else
+ return getpid();
+#endif
+}
+
+/*
+ * This buffer is rather large for stack allocation, so use a single buffer for
+ * all profile dumps; protected by prof_dump_mtx.
+ */
+static char prof_dump_buf[PROF_DUMP_BUFSIZE];
+
+typedef struct prof_dump_arg_s prof_dump_arg_t;
+struct prof_dump_arg_s {
+ /*
+ * Whether error should be handled locally: if true, then we print out
+ * error message as well as abort (if opt_abort is true) when an error
+ * occurred, and we also report the error back to the caller in the end;
+ * if false, then we only report the error back to the caller in the
+ * end.
+ */
+ const bool handle_error_locally;
+ /*
+ * Whether there has been an error in the dumping process, which could
+ * have happened either in file opening or in file writing. When an
+ * error has already occurred, we will stop further writing to the file.
+ */
+ bool error;
+ /* File descriptor of the dump file. */
+ int prof_dump_fd;
+};
+
+static void
+prof_dump_check_possible_error(prof_dump_arg_t *arg, bool err_cond,
+ const char *format, ...) {
+ assert(!arg->error);
+ if (!err_cond) {
+ return;
+ }
+
+ arg->error = true;
+ if (!arg->handle_error_locally) {
+ return;
+ }
+
+ va_list ap;
+ char buf[PROF_PRINTF_BUFSIZE];
+ va_start(ap, format);
+ malloc_vsnprintf(buf, sizeof(buf), format, ap);
+ va_end(ap);
+ malloc_write(buf);
+
+ if (opt_abort) {
+ abort();
+ }
+}
+
+static int
+prof_dump_open_file_impl(const char *filename, int mode) {
+ return creat(filename, mode);
+}
+prof_dump_open_file_t *JET_MUTABLE prof_dump_open_file =
+ prof_dump_open_file_impl;
+
+static void
+prof_dump_open(prof_dump_arg_t *arg, const char *filename) {
+ arg->prof_dump_fd = prof_dump_open_file(filename, 0644);
+ prof_dump_check_possible_error(arg, arg->prof_dump_fd == -1,
+ "<jemalloc>: failed to open \"%s\"\n", filename);
+}
+
+prof_dump_write_file_t *JET_MUTABLE prof_dump_write_file = malloc_write_fd;
+
+static void
+prof_dump_flush(void *opaque, const char *s) {
+ cassert(config_prof);
+ prof_dump_arg_t *arg = (prof_dump_arg_t *)opaque;
+ if (!arg->error) {
+ ssize_t err = prof_dump_write_file(arg->prof_dump_fd, s,
+ strlen(s));
+ prof_dump_check_possible_error(arg, err == -1,
+ "<jemalloc>: failed to write during heap profile flush\n");
+ }
+}
+
+static void
+prof_dump_close(prof_dump_arg_t *arg) {
+ if (arg->prof_dump_fd != -1) {
+ close(arg->prof_dump_fd);
+ }
+}
+
+#ifndef _WIN32
+JEMALLOC_FORMAT_PRINTF(1, 2)
+static int
+prof_open_maps_internal(const char *format, ...) {
+ int mfd;
+ va_list ap;
+ char filename[PATH_MAX + 1];
+
+ va_start(ap, format);
+ malloc_vsnprintf(filename, sizeof(filename), format, ap);
+ va_end(ap);
+
+#if defined(O_CLOEXEC)
+ mfd = open(filename, O_RDONLY | O_CLOEXEC);
+#else
+ mfd = open(filename, O_RDONLY);
+ if (mfd != -1) {
+ fcntl(mfd, F_SETFD, fcntl(mfd, F_GETFD) | FD_CLOEXEC);
+ }
+#endif
+
+ return mfd;
+}
+#endif
+
+static int
+prof_dump_open_maps_impl() {
+ int mfd;
+
+ cassert(config_prof);
+#if defined(__FreeBSD__) || defined(__DragonFly__)
+ mfd = prof_open_maps_internal("/proc/curproc/map");
+#elif defined(_WIN32)
+ mfd = -1; // Not implemented
+#else
+ int pid = prof_getpid();
+
+ mfd = prof_open_maps_internal("/proc/%d/task/%d/maps", pid, pid);
+ if (mfd == -1) {
+ mfd = prof_open_maps_internal("/proc/%d/maps", pid);
+ }
+#endif
+ return mfd;
+}
+prof_dump_open_maps_t *JET_MUTABLE prof_dump_open_maps =
+ prof_dump_open_maps_impl;
+
+static ssize_t
+prof_dump_read_maps_cb(void *read_cbopaque, void *buf, size_t limit) {
+ int mfd = *(int *)read_cbopaque;
+ assert(mfd != -1);
+ return malloc_read_fd(mfd, buf, limit);
+}
+
+static void
+prof_dump_maps(buf_writer_t *buf_writer) {
+ int mfd = prof_dump_open_maps();
+ if (mfd == -1) {
+ return;
+ }
+
+ buf_writer_cb(buf_writer, "\nMAPPED_LIBRARIES:\n");
+ buf_writer_pipe(buf_writer, prof_dump_read_maps_cb, &mfd);
+ close(mfd);
+}
+
+static bool
+prof_dump(tsd_t *tsd, bool propagate_err, const char *filename,
+ bool leakcheck) {
+ cassert(config_prof);
+ assert(tsd_reentrancy_level_get(tsd) == 0);
+
+ prof_tdata_t * tdata = prof_tdata_get(tsd, true);
+ if (tdata == NULL) {
+ return true;
+ }
+
+ prof_dump_arg_t arg = {/* handle_error_locally */ !propagate_err,
+ /* error */ false, /* prof_dump_fd */ -1};
+
+ pre_reentrancy(tsd, NULL);
+ malloc_mutex_lock(tsd_tsdn(tsd), &prof_dump_mtx);
+
+ prof_dump_open(&arg, filename);
+ buf_writer_t buf_writer;
+ bool err = buf_writer_init(tsd_tsdn(tsd), &buf_writer, prof_dump_flush,
+ &arg, prof_dump_buf, PROF_DUMP_BUFSIZE);
+ assert(!err);
+ prof_dump_impl(tsd, buf_writer_cb, &buf_writer, tdata, leakcheck);
+ prof_dump_maps(&buf_writer);
+ buf_writer_terminate(tsd_tsdn(tsd), &buf_writer);
+ prof_dump_close(&arg);
+
+ prof_dump_hook_t dump_hook = prof_dump_hook_get();
+ if (dump_hook != NULL) {
+ dump_hook(filename);
+ }
+ malloc_mutex_unlock(tsd_tsdn(tsd), &prof_dump_mtx);
+ post_reentrancy(tsd);
+
+ return arg.error;
+}
+
+/*
+ * If profiling is off, then PROF_DUMP_FILENAME_LEN is 1, so we'll end up
+ * calling strncpy with a size of 0, which triggers a -Wstringop-truncation
+ * warning (strncpy can never actually be called in this case, since we bail out
+ * much earlier when config_prof is false). This function works around the
+ * warning to let us leave the warning on.
+ */
+static inline void
+prof_strncpy(char *UNUSED dest, const char *UNUSED src, size_t UNUSED size) {
+ cassert(config_prof);
+#ifdef JEMALLOC_PROF
+ strncpy(dest, src, size);
+#endif
+}
+
+static const char *
+prof_prefix_get(tsdn_t* tsdn) {
+ malloc_mutex_assert_owner(tsdn, &prof_dump_filename_mtx);
+
+ return prof_prefix == NULL ? opt_prof_prefix : prof_prefix;
+}
+
+static bool
+prof_prefix_is_empty(tsdn_t *tsdn) {
+ malloc_mutex_lock(tsdn, &prof_dump_filename_mtx);
+ bool ret = (prof_prefix_get(tsdn)[0] == '\0');
+ malloc_mutex_unlock(tsdn, &prof_dump_filename_mtx);
+ return ret;
+}
+
+#define DUMP_FILENAME_BUFSIZE (PATH_MAX + 1)
+#define VSEQ_INVALID UINT64_C(0xffffffffffffffff)
+static void
+prof_dump_filename(tsd_t *tsd, char *filename, char v, uint64_t vseq) {
+ cassert(config_prof);
+
+ assert(tsd_reentrancy_level_get(tsd) == 0);
+ const char *prefix = prof_prefix_get(tsd_tsdn(tsd));
+
+ if (vseq != VSEQ_INVALID) {
+ /* "<prefix>.<pid>.<seq>.v<vseq>.heap" */
+ malloc_snprintf(filename, DUMP_FILENAME_BUFSIZE,
+ "%s.%d.%"FMTu64".%c%"FMTu64".heap", prefix, prof_getpid(),
+ prof_dump_seq, v, vseq);
+ } else {
+ /* "<prefix>.<pid>.<seq>.<v>.heap" */
+ malloc_snprintf(filename, DUMP_FILENAME_BUFSIZE,
+ "%s.%d.%"FMTu64".%c.heap", prefix, prof_getpid(),
+ prof_dump_seq, v);
+ }
+ prof_dump_seq++;
+}
+
+void
+prof_get_default_filename(tsdn_t *tsdn, char *filename, uint64_t ind) {
+ malloc_mutex_lock(tsdn, &prof_dump_filename_mtx);
+ malloc_snprintf(filename, PROF_DUMP_FILENAME_LEN,
+ "%s.%d.%"FMTu64".json", prof_prefix_get(tsdn), prof_getpid(), ind);
+ malloc_mutex_unlock(tsdn, &prof_dump_filename_mtx);
+}
+
+void
+prof_fdump_impl(tsd_t *tsd) {
+ char filename[DUMP_FILENAME_BUFSIZE];
+
+ assert(!prof_prefix_is_empty(tsd_tsdn(tsd)));
+ malloc_mutex_lock(tsd_tsdn(tsd), &prof_dump_filename_mtx);
+ prof_dump_filename(tsd, filename, 'f', VSEQ_INVALID);
+ malloc_mutex_unlock(tsd_tsdn(tsd), &prof_dump_filename_mtx);
+ prof_dump(tsd, false, filename, opt_prof_leak);
+}
+
+bool
+prof_prefix_set(tsdn_t *tsdn, const char *prefix) {
+ cassert(config_prof);
+ ctl_mtx_assert_held(tsdn);
+ malloc_mutex_lock(tsdn, &prof_dump_filename_mtx);
+ if (prof_prefix == NULL) {
+ malloc_mutex_unlock(tsdn, &prof_dump_filename_mtx);
+ /* Everything is still guarded by ctl_mtx. */
+ char *buffer = base_alloc(tsdn, prof_base,
+ PROF_DUMP_FILENAME_LEN, QUANTUM);
+ if (buffer == NULL) {
+ return true;
+ }
+ malloc_mutex_lock(tsdn, &prof_dump_filename_mtx);
+ prof_prefix = buffer;
+ }
+ assert(prof_prefix != NULL);
+
+ prof_strncpy(prof_prefix, prefix, PROF_DUMP_FILENAME_LEN - 1);
+ prof_prefix[PROF_DUMP_FILENAME_LEN - 1] = '\0';
+ malloc_mutex_unlock(tsdn, &prof_dump_filename_mtx);
+
+ return false;
+}
+
+void
+prof_idump_impl(tsd_t *tsd) {
+ malloc_mutex_lock(tsd_tsdn(tsd), &prof_dump_filename_mtx);
+ if (prof_prefix_get(tsd_tsdn(tsd))[0] == '\0') {
+ malloc_mutex_unlock(tsd_tsdn(tsd), &prof_dump_filename_mtx);
+ return;
+ }
+ char filename[PATH_MAX + 1];
+ prof_dump_filename(tsd, filename, 'i', prof_dump_iseq);
+ prof_dump_iseq++;
+ malloc_mutex_unlock(tsd_tsdn(tsd), &prof_dump_filename_mtx);
+ prof_dump(tsd, false, filename, false);
+}
+
+bool
+prof_mdump_impl(tsd_t *tsd, const char *filename) {
+ char filename_buf[DUMP_FILENAME_BUFSIZE];
+ if (filename == NULL) {
+ /* No filename specified, so automatically generate one. */
+ malloc_mutex_lock(tsd_tsdn(tsd), &prof_dump_filename_mtx);
+ if (prof_prefix_get(tsd_tsdn(tsd))[0] == '\0') {
+ malloc_mutex_unlock(tsd_tsdn(tsd), &prof_dump_filename_mtx);
+ return true;
+ }
+ prof_dump_filename(tsd, filename_buf, 'm', prof_dump_mseq);
+ prof_dump_mseq++;
+ malloc_mutex_unlock(tsd_tsdn(tsd), &prof_dump_filename_mtx);
+ filename = filename_buf;
+ }
+ return prof_dump(tsd, true, filename, false);
+}
+
+void
+prof_gdump_impl(tsd_t *tsd) {
+ tsdn_t *tsdn = tsd_tsdn(tsd);
+ malloc_mutex_lock(tsdn, &prof_dump_filename_mtx);
+ if (prof_prefix_get(tsdn)[0] == '\0') {
+ malloc_mutex_unlock(tsdn, &prof_dump_filename_mtx);
+ return;
+ }
+ char filename[DUMP_FILENAME_BUFSIZE];
+ prof_dump_filename(tsd, filename, 'u', prof_dump_useq);
+ prof_dump_useq++;
+ malloc_mutex_unlock(tsdn, &prof_dump_filename_mtx);
+ prof_dump(tsd, false, filename, false);
+}
diff --git a/contrib/jemalloc/src/psset.c b/contrib/jemalloc/src/psset.c
new file mode 100644
index 000000000000..9a8f054f111c
--- /dev/null
+++ b/contrib/jemalloc/src/psset.c
@@ -0,0 +1,385 @@
+#include "jemalloc/internal/jemalloc_preamble.h"
+#include "jemalloc/internal/jemalloc_internal_includes.h"
+
+#include "jemalloc/internal/psset.h"
+
+#include "jemalloc/internal/fb.h"
+
+void
+psset_init(psset_t *psset) {
+ for (unsigned i = 0; i < PSSET_NPSIZES; i++) {
+ hpdata_age_heap_new(&psset->pageslabs[i]);
+ }
+ fb_init(psset->pageslab_bitmap, PSSET_NPSIZES);
+ memset(&psset->merged_stats, 0, sizeof(psset->merged_stats));
+ memset(&psset->stats, 0, sizeof(psset->stats));
+ hpdata_empty_list_init(&psset->empty);
+ for (int i = 0; i < PSSET_NPURGE_LISTS; i++) {
+ hpdata_purge_list_init(&psset->to_purge[i]);
+ }
+ fb_init(psset->purge_bitmap, PSSET_NPURGE_LISTS);
+ hpdata_hugify_list_init(&psset->to_hugify);
+}
+
+static void
+psset_bin_stats_accum(psset_bin_stats_t *dst, psset_bin_stats_t *src) {
+ dst->npageslabs += src->npageslabs;
+ dst->nactive += src->nactive;
+ dst->ndirty += src->ndirty;
+}
+
+void
+psset_stats_accum(psset_stats_t *dst, psset_stats_t *src) {
+ psset_bin_stats_accum(&dst->full_slabs[0], &src->full_slabs[0]);
+ psset_bin_stats_accum(&dst->full_slabs[1], &src->full_slabs[1]);
+ psset_bin_stats_accum(&dst->empty_slabs[0], &src->empty_slabs[0]);
+ psset_bin_stats_accum(&dst->empty_slabs[1], &src->empty_slabs[1]);
+ for (pszind_t i = 0; i < PSSET_NPSIZES; i++) {
+ psset_bin_stats_accum(&dst->nonfull_slabs[i][0],
+ &src->nonfull_slabs[i][0]);
+ psset_bin_stats_accum(&dst->nonfull_slabs[i][1],
+ &src->nonfull_slabs[i][1]);
+ }
+}
+
+/*
+ * The stats maintenance strategy is to remove a pageslab's contribution to the
+ * stats when we call psset_update_begin, and re-add it (to a potentially new
+ * bin) when we call psset_update_end.
+ */
+JEMALLOC_ALWAYS_INLINE void
+psset_bin_stats_insert_remove(psset_t *psset, psset_bin_stats_t *binstats,
+ hpdata_t *ps, bool insert) {
+ size_t mul = insert ? (size_t)1 : (size_t)-1;
+ size_t huge_idx = (size_t)hpdata_huge_get(ps);
+
+ binstats[huge_idx].npageslabs += mul * 1;
+ binstats[huge_idx].nactive += mul * hpdata_nactive_get(ps);
+ binstats[huge_idx].ndirty += mul * hpdata_ndirty_get(ps);
+
+ psset->merged_stats.npageslabs += mul * 1;
+ psset->merged_stats.nactive += mul * hpdata_nactive_get(ps);
+ psset->merged_stats.ndirty += mul * hpdata_ndirty_get(ps);
+
+ if (config_debug) {
+ psset_bin_stats_t check_stats = {0};
+ for (size_t huge = 0; huge <= 1; huge++) {
+ psset_bin_stats_accum(&check_stats,
+ &psset->stats.full_slabs[huge]);
+ psset_bin_stats_accum(&check_stats,
+ &psset->stats.empty_slabs[huge]);
+ for (pszind_t pind = 0; pind < PSSET_NPSIZES; pind++) {
+ psset_bin_stats_accum(&check_stats,
+ &psset->stats.nonfull_slabs[pind][huge]);
+ }
+ }
+ assert(psset->merged_stats.npageslabs
+ == check_stats.npageslabs);
+ assert(psset->merged_stats.nactive == check_stats.nactive);
+ assert(psset->merged_stats.ndirty == check_stats.ndirty);
+ }
+}
+
+static void
+psset_bin_stats_insert(psset_t *psset, psset_bin_stats_t *binstats,
+ hpdata_t *ps) {
+ psset_bin_stats_insert_remove(psset, binstats, ps, true);
+}
+
+static void
+psset_bin_stats_remove(psset_t *psset, psset_bin_stats_t *binstats,
+ hpdata_t *ps) {
+ psset_bin_stats_insert_remove(psset, binstats, ps, false);
+}
+
+static void
+psset_hpdata_heap_remove(psset_t *psset, pszind_t pind, hpdata_t *ps) {
+ hpdata_age_heap_remove(&psset->pageslabs[pind], ps);
+ if (hpdata_age_heap_empty(&psset->pageslabs[pind])) {
+ fb_unset(psset->pageslab_bitmap, PSSET_NPSIZES, (size_t)pind);
+ }
+}
+
+static void
+psset_hpdata_heap_insert(psset_t *psset, pszind_t pind, hpdata_t *ps) {
+ if (hpdata_age_heap_empty(&psset->pageslabs[pind])) {
+ fb_set(psset->pageslab_bitmap, PSSET_NPSIZES, (size_t)pind);
+ }
+ hpdata_age_heap_insert(&psset->pageslabs[pind], ps);
+}
+
+static void
+psset_stats_insert(psset_t* psset, hpdata_t *ps) {
+ if (hpdata_empty(ps)) {
+ psset_bin_stats_insert(psset, psset->stats.empty_slabs, ps);
+ } else if (hpdata_full(ps)) {
+ psset_bin_stats_insert(psset, psset->stats.full_slabs, ps);
+ } else {
+ size_t longest_free_range = hpdata_longest_free_range_get(ps);
+
+ pszind_t pind = sz_psz2ind(sz_psz_quantize_floor(
+ longest_free_range << LG_PAGE));
+ assert(pind < PSSET_NPSIZES);
+
+ psset_bin_stats_insert(psset, psset->stats.nonfull_slabs[pind],
+ ps);
+ }
+}
+
+static void
+psset_stats_remove(psset_t *psset, hpdata_t *ps) {
+ if (hpdata_empty(ps)) {
+ psset_bin_stats_remove(psset, psset->stats.empty_slabs, ps);
+ } else if (hpdata_full(ps)) {
+ psset_bin_stats_remove(psset, psset->stats.full_slabs, ps);
+ } else {
+ size_t longest_free_range = hpdata_longest_free_range_get(ps);
+
+ pszind_t pind = sz_psz2ind(sz_psz_quantize_floor(
+ longest_free_range << LG_PAGE));
+ assert(pind < PSSET_NPSIZES);
+
+ psset_bin_stats_remove(psset, psset->stats.nonfull_slabs[pind],
+ ps);
+ }
+}
+
+/*
+ * Put ps into some container so that it can be found during future allocation
+ * requests.
+ */
+static void
+psset_alloc_container_insert(psset_t *psset, hpdata_t *ps) {
+ assert(!hpdata_in_psset_alloc_container_get(ps));
+ hpdata_in_psset_alloc_container_set(ps, true);
+ if (hpdata_empty(ps)) {
+ /*
+ * This prepend, paired with popping the head in psset_fit,
+ * means we implement LIFO ordering for the empty slabs set,
+ * which seems reasonable.
+ */
+ hpdata_empty_list_prepend(&psset->empty, ps);
+ } else if (hpdata_full(ps)) {
+ /*
+ * We don't need to keep track of the full slabs; we're never
+ * going to return them from a psset_pick_alloc call.
+ */
+ } else {
+ size_t longest_free_range = hpdata_longest_free_range_get(ps);
+
+ pszind_t pind = sz_psz2ind(sz_psz_quantize_floor(
+ longest_free_range << LG_PAGE));
+ assert(pind < PSSET_NPSIZES);
+
+ psset_hpdata_heap_insert(psset, pind, ps);
+ }
+}
+
+/* Remove ps from those collections. */
+static void
+psset_alloc_container_remove(psset_t *psset, hpdata_t *ps) {
+ assert(hpdata_in_psset_alloc_container_get(ps));
+ hpdata_in_psset_alloc_container_set(ps, false);
+
+ if (hpdata_empty(ps)) {
+ hpdata_empty_list_remove(&psset->empty, ps);
+ } else if (hpdata_full(ps)) {
+ /* Same as above -- do nothing in this case. */
+ } else {
+ size_t longest_free_range = hpdata_longest_free_range_get(ps);
+
+ pszind_t pind = sz_psz2ind(sz_psz_quantize_floor(
+ longest_free_range << LG_PAGE));
+ assert(pind < PSSET_NPSIZES);
+
+ psset_hpdata_heap_remove(psset, pind, ps);
+ }
+}
+
+static size_t
+psset_purge_list_ind(hpdata_t *ps) {
+ size_t ndirty = hpdata_ndirty_get(ps);
+ /* Shouldn't have something with no dirty pages purgeable. */
+ assert(ndirty > 0);
+ /*
+ * Higher indices correspond to lists we'd like to purge earlier; make
+ * the two highest indices correspond to empty lists, which we attempt
+ * to purge before purging any non-empty list. This has two advantages:
+ * - Empty page slabs are the least likely to get reused (we'll only
+ * pick them for an allocation if we have no other choice).
+ * - Empty page slabs can purge every dirty page they contain in a
+ * single call, which is not usually the case.
+ *
+ * We purge hugeified empty slabs before nonhugeified ones, on the basis
+ * that they are fully dirty, while nonhugified slabs might not be, so
+ * we free up more pages more easily.
+ */
+ if (hpdata_nactive_get(ps) == 0) {
+ if (hpdata_huge_get(ps)) {
+ return PSSET_NPURGE_LISTS - 1;
+ } else {
+ return PSSET_NPURGE_LISTS - 2;
+ }
+ }
+
+ pszind_t pind = sz_psz2ind(sz_psz_quantize_floor(ndirty << LG_PAGE));
+ /*
+ * For non-empty slabs, we may reuse them again. Prefer purging
+ * non-hugeified slabs before hugeified ones then, among pages of
+ * similar dirtiness. We still get some benefit from the hugification.
+ */
+ return (size_t)pind * 2 + (hpdata_huge_get(ps) ? 0 : 1);
+}
+
+static void
+psset_maybe_remove_purge_list(psset_t *psset, hpdata_t *ps) {
+ /*
+ * Remove the hpdata from its purge list (if it's in one). Even if it's
+ * going to stay in the same one, by appending it during
+ * psset_update_end, we move it to the end of its queue, so that we
+ * purge LRU within a given dirtiness bucket.
+ */
+ if (hpdata_purge_allowed_get(ps)) {
+ size_t ind = psset_purge_list_ind(ps);
+ hpdata_purge_list_t *purge_list = &psset->to_purge[ind];
+ hpdata_purge_list_remove(purge_list, ps);
+ if (hpdata_purge_list_empty(purge_list)) {
+ fb_unset(psset->purge_bitmap, PSSET_NPURGE_LISTS, ind);
+ }
+ }
+}
+
+static void
+psset_maybe_insert_purge_list(psset_t *psset, hpdata_t *ps) {
+ if (hpdata_purge_allowed_get(ps)) {
+ size_t ind = psset_purge_list_ind(ps);
+ hpdata_purge_list_t *purge_list = &psset->to_purge[ind];
+ if (hpdata_purge_list_empty(purge_list)) {
+ fb_set(psset->purge_bitmap, PSSET_NPURGE_LISTS, ind);
+ }
+ hpdata_purge_list_append(purge_list, ps);
+ }
+
+}
+
+void
+psset_update_begin(psset_t *psset, hpdata_t *ps) {
+ hpdata_assert_consistent(ps);
+ assert(hpdata_in_psset_get(ps));
+ hpdata_updating_set(ps, true);
+ psset_stats_remove(psset, ps);
+ if (hpdata_in_psset_alloc_container_get(ps)) {
+ /*
+ * Some metadata updates can break alloc container invariants
+ * (e.g. the longest free range determines the hpdata_heap_t the
+ * pageslab lives in).
+ */
+ assert(hpdata_alloc_allowed_get(ps));
+ psset_alloc_container_remove(psset, ps);
+ }
+ psset_maybe_remove_purge_list(psset, ps);
+ /*
+ * We don't update presence in the hugify list; we try to keep it FIFO,
+ * even in the presence of other metadata updates. We'll update
+ * presence at the end of the metadata update if necessary.
+ */
+}
+
+void
+psset_update_end(psset_t *psset, hpdata_t *ps) {
+ assert(hpdata_in_psset_get(ps));
+ hpdata_updating_set(ps, false);
+ psset_stats_insert(psset, ps);
+
+ /*
+ * The update begin should have removed ps from whatever alloc container
+ * it was in.
+ */
+ assert(!hpdata_in_psset_alloc_container_get(ps));
+ if (hpdata_alloc_allowed_get(ps)) {
+ psset_alloc_container_insert(psset, ps);
+ }
+ psset_maybe_insert_purge_list(psset, ps);
+
+ if (hpdata_hugify_allowed_get(ps)
+ && !hpdata_in_psset_hugify_container_get(ps)) {
+ hpdata_in_psset_hugify_container_set(ps, true);
+ hpdata_hugify_list_append(&psset->to_hugify, ps);
+ } else if (!hpdata_hugify_allowed_get(ps)
+ && hpdata_in_psset_hugify_container_get(ps)) {
+ hpdata_in_psset_hugify_container_set(ps, false);
+ hpdata_hugify_list_remove(&psset->to_hugify, ps);
+ }
+ hpdata_assert_consistent(ps);
+}
+
+hpdata_t *
+psset_pick_alloc(psset_t *psset, size_t size) {
+ assert((size & PAGE_MASK) == 0);
+ assert(size <= HUGEPAGE);
+
+ pszind_t min_pind = sz_psz2ind(sz_psz_quantize_ceil(size));
+ pszind_t pind = (pszind_t)fb_ffs(psset->pageslab_bitmap, PSSET_NPSIZES,
+ (size_t)min_pind);
+ if (pind == PSSET_NPSIZES) {
+ return hpdata_empty_list_first(&psset->empty);
+ }
+ hpdata_t *ps = hpdata_age_heap_first(&psset->pageslabs[pind]);
+ if (ps == NULL) {
+ return NULL;
+ }
+
+ hpdata_assert_consistent(ps);
+
+ return ps;
+}
+
+hpdata_t *
+psset_pick_purge(psset_t *psset) {
+ ssize_t ind_ssz = fb_fls(psset->purge_bitmap, PSSET_NPURGE_LISTS,
+ PSSET_NPURGE_LISTS - 1);
+ if (ind_ssz < 0) {
+ return NULL;
+ }
+ pszind_t ind = (pszind_t)ind_ssz;
+ assert(ind < PSSET_NPURGE_LISTS);
+ hpdata_t *ps = hpdata_purge_list_first(&psset->to_purge[ind]);
+ assert(ps != NULL);
+ return ps;
+}
+
+hpdata_t *
+psset_pick_hugify(psset_t *psset) {
+ return hpdata_hugify_list_first(&psset->to_hugify);
+}
+
+void
+psset_insert(psset_t *psset, hpdata_t *ps) {
+ hpdata_in_psset_set(ps, true);
+
+ psset_stats_insert(psset, ps);
+ if (hpdata_alloc_allowed_get(ps)) {
+ psset_alloc_container_insert(psset, ps);
+ }
+ psset_maybe_insert_purge_list(psset, ps);
+
+ if (hpdata_hugify_allowed_get(ps)) {
+ hpdata_in_psset_hugify_container_set(ps, true);
+ hpdata_hugify_list_append(&psset->to_hugify, ps);
+ }
+}
+
+void
+psset_remove(psset_t *psset, hpdata_t *ps) {
+ hpdata_in_psset_set(ps, false);
+
+ psset_stats_remove(psset, ps);
+ if (hpdata_in_psset_alloc_container_get(ps)) {
+ psset_alloc_container_remove(psset, ps);
+ }
+ psset_maybe_remove_purge_list(psset, ps);
+ if (hpdata_in_psset_hugify_container_get(ps)) {
+ hpdata_in_psset_hugify_container_set(ps, false);
+ hpdata_hugify_list_remove(&psset->to_hugify, ps);
+ }
+}
diff --git a/contrib/jemalloc/src/rtree.c b/contrib/jemalloc/src/rtree.c
index 4ae41fe2fec9..6496b5afdc4a 100644
--- a/contrib/jemalloc/src/rtree.c
+++ b/contrib/jemalloc/src/rtree.c
@@ -1,4 +1,3 @@
-#define JEMALLOC_RTREE_C_
#include "jemalloc/internal/jemalloc_preamble.h"
#include "jemalloc/internal/jemalloc_internal_includes.h"
@@ -10,7 +9,7 @@
* used.
*/
bool
-rtree_new(rtree_t *rtree, bool zeroed) {
+rtree_new(rtree_t *rtree, base_t *base, bool zeroed) {
#ifdef JEMALLOC_JET
if (!zeroed) {
memset(rtree, 0, sizeof(rtree_t)); /* Clear root. */
@@ -18,6 +17,7 @@ rtree_new(rtree_t *rtree, bool zeroed) {
#else
assert(zeroed);
#endif
+ rtree->base = base;
if (malloc_mutex_init(&rtree->init_lock, "rtree", WITNESS_RANK_RTREE,
malloc_mutex_rank_exclusive)) {
@@ -28,75 +28,16 @@ rtree_new(rtree_t *rtree, bool zeroed) {
}
static rtree_node_elm_t *
-rtree_node_alloc_impl(tsdn_t *tsdn, rtree_t *rtree, size_t nelms) {
- return (rtree_node_elm_t *)base_alloc(tsdn, b0get(), nelms *
- sizeof(rtree_node_elm_t), CACHELINE);
+rtree_node_alloc(tsdn_t *tsdn, rtree_t *rtree, size_t nelms) {
+ return (rtree_node_elm_t *)base_alloc(tsdn, rtree->base,
+ nelms * sizeof(rtree_node_elm_t), CACHELINE);
}
-rtree_node_alloc_t *JET_MUTABLE rtree_node_alloc = rtree_node_alloc_impl;
-
-static void
-rtree_node_dalloc_impl(tsdn_t *tsdn, rtree_t *rtree, rtree_node_elm_t *node) {
- /* Nodes are never deleted during normal operation. */
- not_reached();
-}
-rtree_node_dalloc_t *JET_MUTABLE rtree_node_dalloc =
- rtree_node_dalloc_impl;
static rtree_leaf_elm_t *
-rtree_leaf_alloc_impl(tsdn_t *tsdn, rtree_t *rtree, size_t nelms) {
- return (rtree_leaf_elm_t *)base_alloc(tsdn, b0get(), nelms *
- sizeof(rtree_leaf_elm_t), CACHELINE);
-}
-rtree_leaf_alloc_t *JET_MUTABLE rtree_leaf_alloc = rtree_leaf_alloc_impl;
-
-static void
-rtree_leaf_dalloc_impl(tsdn_t *tsdn, rtree_t *rtree, rtree_leaf_elm_t *leaf) {
- /* Leaves are never deleted during normal operation. */
- not_reached();
+rtree_leaf_alloc(tsdn_t *tsdn, rtree_t *rtree, size_t nelms) {
+ return (rtree_leaf_elm_t *)base_alloc(tsdn, rtree->base,
+ nelms * sizeof(rtree_leaf_elm_t), CACHELINE);
}
-rtree_leaf_dalloc_t *JET_MUTABLE rtree_leaf_dalloc =
- rtree_leaf_dalloc_impl;
-
-#ifdef JEMALLOC_JET
-# if RTREE_HEIGHT > 1
-static void
-rtree_delete_subtree(tsdn_t *tsdn, rtree_t *rtree, rtree_node_elm_t *subtree,
- unsigned level) {
- size_t nchildren = ZU(1) << rtree_levels[level].bits;
- if (level + 2 < RTREE_HEIGHT) {
- for (size_t i = 0; i < nchildren; i++) {
- rtree_node_elm_t *node =
- (rtree_node_elm_t *)atomic_load_p(&subtree[i].child,
- ATOMIC_RELAXED);
- if (node != NULL) {
- rtree_delete_subtree(tsdn, rtree, node, level +
- 1);
- }
- }
- } else {
- for (size_t i = 0; i < nchildren; i++) {
- rtree_leaf_elm_t *leaf =
- (rtree_leaf_elm_t *)atomic_load_p(&subtree[i].child,
- ATOMIC_RELAXED);
- if (leaf != NULL) {
- rtree_leaf_dalloc(tsdn, rtree, leaf);
- }
- }
- }
-
- if (subtree != rtree->root) {
- rtree_node_dalloc(tsdn, rtree, subtree);
- }
-}
-# endif
-
-void
-rtree_delete(tsdn_t *tsdn, rtree_t *rtree) {
-# if RTREE_HEIGHT > 1
- rtree_delete_subtree(tsdn, rtree, rtree->root, 0);
-# endif
-}
-#endif
static rtree_node_elm_t *
rtree_node_init(tsdn_t *tsdn, rtree_t *rtree, unsigned level,
diff --git a/contrib/jemalloc/src/safety_check.c b/contrib/jemalloc/src/safety_check.c
index 804155dcfc6a..209fdda92b5c 100644
--- a/contrib/jemalloc/src/safety_check.c
+++ b/contrib/jemalloc/src/safety_check.c
@@ -1,9 +1,21 @@
#include "jemalloc/internal/jemalloc_preamble.h"
#include "jemalloc/internal/jemalloc_internal_includes.h"
-static void (*safety_check_abort)(const char *message);
+static safety_check_abort_hook_t safety_check_abort;
-void safety_check_set_abort(void (*abort_fn)(const char *)) {
+void safety_check_fail_sized_dealloc(bool current_dealloc, const void *ptr,
+ size_t true_size, size_t input_size) {
+ char *src = current_dealloc ? "the current pointer being freed" :
+ "in thread cache, possibly from previous deallocations";
+
+ safety_check_fail("<jemalloc>: size mismatch detected (true size %zu "
+ "vs input size %zu), likely caused by application sized "
+ "deallocation bugs (source address: %p, %s). Suggest building with "
+ "--enable-debug or address sanitizer for debugging. Abort.\n",
+ true_size, input_size, ptr, src);
+}
+
+void safety_check_set_abort(safety_check_abort_hook_t abort_fn) {
safety_check_abort = abort_fn;
}
diff --git a/contrib/jemalloc/src/san.c b/contrib/jemalloc/src/san.c
new file mode 100644
index 000000000000..6e51291135c7
--- /dev/null
+++ b/contrib/jemalloc/src/san.c
@@ -0,0 +1,208 @@
+#include "jemalloc/internal/jemalloc_preamble.h"
+#include "jemalloc/internal/jemalloc_internal_includes.h"
+
+#include "jemalloc/internal/assert.h"
+#include "jemalloc/internal/ehooks.h"
+#include "jemalloc/internal/san.h"
+#include "jemalloc/internal/tsd.h"
+
+/* The sanitizer options. */
+size_t opt_san_guard_large = SAN_GUARD_LARGE_EVERY_N_EXTENTS_DEFAULT;
+size_t opt_san_guard_small = SAN_GUARD_SMALL_EVERY_N_EXTENTS_DEFAULT;
+
+/* Aligned (-1 is off) ptrs will be junked & stashed on dealloc. */
+ssize_t opt_lg_san_uaf_align = SAN_LG_UAF_ALIGN_DEFAULT;
+
+/*
+ * Initialized in san_init(). When disabled, the mask is set to (uintptr_t)-1
+ * to always fail the nonfast_align check.
+ */
+uintptr_t san_cache_bin_nonfast_mask = SAN_CACHE_BIN_NONFAST_MASK_DEFAULT;
+
+static inline void
+san_find_guarded_addr(edata_t *edata, uintptr_t *guard1, uintptr_t *guard2,
+ uintptr_t *addr, size_t size, bool left, bool right) {
+ assert(!edata_guarded_get(edata));
+ assert(size % PAGE == 0);
+ *addr = (uintptr_t)edata_base_get(edata);
+ if (left) {
+ *guard1 = *addr;
+ *addr += SAN_PAGE_GUARD;
+ } else {
+ *guard1 = 0;
+ }
+
+ if (right) {
+ *guard2 = *addr + size;
+ } else {
+ *guard2 = 0;
+ }
+}
+
+static inline void
+san_find_unguarded_addr(edata_t *edata, uintptr_t *guard1, uintptr_t *guard2,
+ uintptr_t *addr, size_t size, bool left, bool right) {
+ assert(edata_guarded_get(edata));
+ assert(size % PAGE == 0);
+ *addr = (uintptr_t)edata_base_get(edata);
+ if (right) {
+ *guard2 = *addr + size;
+ } else {
+ *guard2 = 0;
+ }
+
+ if (left) {
+ *guard1 = *addr - SAN_PAGE_GUARD;
+ assert(*guard1 != 0);
+ *addr = *guard1;
+ } else {
+ *guard1 = 0;
+ }
+}
+
+void
+san_guard_pages(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata, emap_t *emap,
+ bool left, bool right, bool remap) {
+ assert(left || right);
+ if (remap) {
+ emap_deregister_boundary(tsdn, emap, edata);
+ }
+
+ size_t size_with_guards = edata_size_get(edata);
+ size_t usize = (left && right)
+ ? san_two_side_unguarded_sz(size_with_guards)
+ : san_one_side_unguarded_sz(size_with_guards);
+
+ uintptr_t guard1, guard2, addr;
+ san_find_guarded_addr(edata, &guard1, &guard2, &addr, usize, left,
+ right);
+
+ assert(edata_state_get(edata) == extent_state_active);
+ ehooks_guard(tsdn, ehooks, (void *)guard1, (void *)guard2);
+
+ /* Update the guarded addr and usable size of the edata. */
+ edata_size_set(edata, usize);
+ edata_addr_set(edata, (void *)addr);
+ edata_guarded_set(edata, true);
+
+ if (remap) {
+ emap_register_boundary(tsdn, emap, edata, SC_NSIZES,
+ /* slab */ false);
+ }
+}
+
+static void
+san_unguard_pages_impl(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata,
+ emap_t *emap, bool left, bool right, bool remap) {
+ assert(left || right);
+ /* Remove the inner boundary which no longer exists. */
+ if (remap) {
+ assert(edata_state_get(edata) == extent_state_active);
+ emap_deregister_boundary(tsdn, emap, edata);
+ } else {
+ assert(edata_state_get(edata) == extent_state_retained);
+ }
+
+ size_t size = edata_size_get(edata);
+ size_t size_with_guards = (left && right)
+ ? san_two_side_guarded_sz(size)
+ : san_one_side_guarded_sz(size);
+
+ uintptr_t guard1, guard2, addr;
+ san_find_unguarded_addr(edata, &guard1, &guard2, &addr, size, left,
+ right);
+
+ ehooks_unguard(tsdn, ehooks, (void *)guard1, (void *)guard2);
+
+ /* Update the true addr and usable size of the edata. */
+ edata_size_set(edata, size_with_guards);
+ edata_addr_set(edata, (void *)addr);
+ edata_guarded_set(edata, false);
+
+ /*
+ * Then re-register the outer boundary including the guards, if
+ * requested.
+ */
+ if (remap) {
+ emap_register_boundary(tsdn, emap, edata, SC_NSIZES,
+ /* slab */ false);
+ }
+}
+
+void
+san_unguard_pages(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata,
+ emap_t *emap, bool left, bool right) {
+ san_unguard_pages_impl(tsdn, ehooks, edata, emap, left, right,
+ /* remap */ true);
+}
+
+void
+san_unguard_pages_pre_destroy(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata,
+ emap_t *emap) {
+ emap_assert_not_mapped(tsdn, emap, edata);
+ /*
+ * We don't want to touch the emap of about to be destroyed extents, as
+ * they have been unmapped upon eviction from the retained ecache. Also,
+ * we unguard the extents to the right, because retained extents only
+ * own their right guard page per san_bump_alloc's logic.
+ */
+ san_unguard_pages_impl(tsdn, ehooks, edata, emap, /* left */ false,
+ /* right */ true, /* remap */ false);
+}
+
+static bool
+san_stashed_corrupted(void *ptr, size_t size) {
+ if (san_junk_ptr_should_slow()) {
+ for (size_t i = 0; i < size; i++) {
+ if (((char *)ptr)[i] != (char)uaf_detect_junk) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void *first, *mid, *last;
+ san_junk_ptr_locations(ptr, size, &first, &mid, &last);
+ if (*(uintptr_t *)first != uaf_detect_junk ||
+ *(uintptr_t *)mid != uaf_detect_junk ||
+ *(uintptr_t *)last != uaf_detect_junk) {
+ return true;
+ }
+
+ return false;
+}
+
+void
+san_check_stashed_ptrs(void **ptrs, size_t nstashed, size_t usize) {
+ /*
+ * Verify that the junked-filled & stashed pointers remain unchanged, to
+ * detect write-after-free.
+ */
+ for (size_t n = 0; n < nstashed; n++) {
+ void *stashed = ptrs[n];
+ assert(stashed != NULL);
+ assert(cache_bin_nonfast_aligned(stashed));
+ if (unlikely(san_stashed_corrupted(stashed, usize))) {
+ safety_check_fail("<jemalloc>: Write-after-free "
+ "detected on deallocated pointer %p (size %zu).\n",
+ stashed, usize);
+ }
+ }
+}
+
+void
+tsd_san_init(tsd_t *tsd) {
+ *tsd_san_extents_until_guard_smallp_get(tsd) = opt_san_guard_small;
+ *tsd_san_extents_until_guard_largep_get(tsd) = opt_san_guard_large;
+}
+
+void
+san_init(ssize_t lg_san_uaf_align) {
+ assert(lg_san_uaf_align == -1 || lg_san_uaf_align >= LG_PAGE);
+ if (lg_san_uaf_align == -1) {
+ san_cache_bin_nonfast_mask = (uintptr_t)-1;
+ return;
+ }
+
+ san_cache_bin_nonfast_mask = ((uintptr_t)1 << lg_san_uaf_align) - 1;
+}
diff --git a/contrib/jemalloc/src/san_bump.c b/contrib/jemalloc/src/san_bump.c
new file mode 100644
index 000000000000..888974555f28
--- /dev/null
+++ b/contrib/jemalloc/src/san_bump.c
@@ -0,0 +1,104 @@
+#include "jemalloc/internal/jemalloc_preamble.h"
+#include "jemalloc/internal/jemalloc_internal_includes.h"
+
+#include "jemalloc/internal/san_bump.h"
+#include "jemalloc/internal/pac.h"
+#include "jemalloc/internal/san.h"
+#include "jemalloc/internal/ehooks.h"
+#include "jemalloc/internal/edata_cache.h"
+
+static bool
+san_bump_grow_locked(tsdn_t *tsdn, san_bump_alloc_t *sba, pac_t *pac,
+ ehooks_t *ehooks, size_t size);
+
+edata_t *
+san_bump_alloc(tsdn_t *tsdn, san_bump_alloc_t* sba, pac_t *pac,
+ ehooks_t *ehooks, size_t size, bool zero) {
+ assert(san_bump_enabled());
+
+ edata_t* to_destroy;
+ size_t guarded_size = san_one_side_guarded_sz(size);
+
+ malloc_mutex_lock(tsdn, &sba->mtx);
+
+ if (sba->curr_reg == NULL ||
+ edata_size_get(sba->curr_reg) < guarded_size) {
+ /*
+ * If the current region can't accommodate the allocation,
+ * try replacing it with a larger one and destroy current if the
+ * replacement succeeds.
+ */
+ to_destroy = sba->curr_reg;
+ bool err = san_bump_grow_locked(tsdn, sba, pac, ehooks,
+ guarded_size);
+ if (err) {
+ goto label_err;
+ }
+ } else {
+ to_destroy = NULL;
+ }
+ assert(guarded_size <= edata_size_get(sba->curr_reg));
+ size_t trail_size = edata_size_get(sba->curr_reg) - guarded_size;
+
+ edata_t* edata;
+ if (trail_size != 0) {
+ edata_t* curr_reg_trail = extent_split_wrapper(tsdn, pac,
+ ehooks, sba->curr_reg, guarded_size, trail_size,
+ /* holding_core_locks */ true);
+ if (curr_reg_trail == NULL) {
+ goto label_err;
+ }
+ edata = sba->curr_reg;
+ sba->curr_reg = curr_reg_trail;
+ } else {
+ edata = sba->curr_reg;
+ sba->curr_reg = NULL;
+ }
+
+ malloc_mutex_unlock(tsdn, &sba->mtx);
+
+ assert(!edata_guarded_get(edata));
+ assert(sba->curr_reg == NULL || !edata_guarded_get(sba->curr_reg));
+ assert(to_destroy == NULL || !edata_guarded_get(to_destroy));
+
+ if (to_destroy != NULL) {
+ extent_destroy_wrapper(tsdn, pac, ehooks, to_destroy);
+ }
+
+ san_guard_pages(tsdn, ehooks, edata, pac->emap, /* left */ false,
+ /* right */ true, /* remap */ true);
+
+ if (extent_commit_zero(tsdn, ehooks, edata, /* commit */ true, zero,
+ /* growing_retained */ false)) {
+ extent_record(tsdn, pac, ehooks, &pac->ecache_retained,
+ edata);
+ return NULL;
+ }
+
+ if (config_prof) {
+ extent_gdump_add(tsdn, edata);
+ }
+
+ return edata;
+label_err:
+ malloc_mutex_unlock(tsdn, &sba->mtx);
+ return NULL;
+}
+
+static bool
+san_bump_grow_locked(tsdn_t *tsdn, san_bump_alloc_t *sba, pac_t *pac,
+ ehooks_t *ehooks, size_t size) {
+ malloc_mutex_assert_owner(tsdn, &sba->mtx);
+
+ bool committed = false, zeroed = false;
+ size_t alloc_size = size > SBA_RETAINED_ALLOC_SIZE ? size :
+ SBA_RETAINED_ALLOC_SIZE;
+ assert((alloc_size & PAGE_MASK) == 0);
+ sba->curr_reg = extent_alloc_wrapper(tsdn, pac, ehooks, NULL,
+ alloc_size, PAGE, zeroed, &committed,
+ /* growing_retained */ true);
+ if (sba->curr_reg == NULL) {
+ return true;
+ }
+ return false;
+}
diff --git a/contrib/jemalloc/src/sc.c b/contrib/jemalloc/src/sc.c
index 89ddb6ba6a91..e4a94d89f245 100644
--- a/contrib/jemalloc/src/sc.c
+++ b/contrib/jemalloc/src/sc.c
@@ -13,9 +13,7 @@
* at least the damage is compartmentalized to this file.
*/
-sc_data_t sc_data_global;
-
-static size_t
+size_t
reg_size_compute(int lg_base, int lg_delta, int ndelta) {
return (ZU(1) << lg_base) + (ZU(ndelta) << lg_delta);
}
@@ -64,9 +62,8 @@ size_class(
sc->lg_base = lg_base;
sc->lg_delta = lg_delta;
sc->ndelta = ndelta;
- sc->psz = (reg_size_compute(lg_base, lg_delta, ndelta)
- % (ZU(1) << lg_page) == 0);
- size_t size = (ZU(1) << lg_base) + (ZU(ndelta) << lg_delta);
+ size_t size = reg_size_compute(lg_base, lg_delta, ndelta);
+ sc->psz = (size % (ZU(1) << lg_page) == 0);
if (index == 0) {
assert(!sc->psz);
}
@@ -245,7 +242,7 @@ size_classes(
assert(sc_data->lg_large_minclass == SC_LG_LARGE_MINCLASS);
assert(sc_data->large_maxclass == SC_LARGE_MAXCLASS);
- /*
+ /*
* In the allocation fastpath, we want to assume that we can
* unconditionally subtract the requested allocation size from
* a ssize_t, and detect passing through 0 correctly. This
@@ -257,12 +254,8 @@ size_classes(
void
sc_data_init(sc_data_t *sc_data) {
- assert(!sc_data->initialized);
-
- int lg_max_lookup = 12;
-
size_classes(sc_data, LG_SIZEOF_PTR, LG_QUANTUM, SC_LG_TINY_MIN,
- lg_max_lookup, LG_PAGE, 2);
+ SC_LG_MAX_LOOKUP, LG_PAGE, SC_LG_NGROUP);
sc_data->initialized = true;
}
diff --git a/contrib/jemalloc/src/sec.c b/contrib/jemalloc/src/sec.c
new file mode 100644
index 000000000000..df6755904951
--- /dev/null
+++ b/contrib/jemalloc/src/sec.c
@@ -0,0 +1,422 @@
+#include "jemalloc/internal/jemalloc_preamble.h"
+#include "jemalloc/internal/jemalloc_internal_includes.h"
+
+#include "jemalloc/internal/sec.h"
+
+static edata_t *sec_alloc(tsdn_t *tsdn, pai_t *self, size_t size,
+ size_t alignment, bool zero, bool guarded, bool frequent_reuse,
+ bool *deferred_work_generated);
+static bool sec_expand(tsdn_t *tsdn, pai_t *self, edata_t *edata,
+ size_t old_size, size_t new_size, bool zero, bool *deferred_work_generated);
+static bool sec_shrink(tsdn_t *tsdn, pai_t *self, edata_t *edata,
+ size_t old_size, size_t new_size, bool *deferred_work_generated);
+static void sec_dalloc(tsdn_t *tsdn, pai_t *self, edata_t *edata,
+ bool *deferred_work_generated);
+
+static void
+sec_bin_init(sec_bin_t *bin) {
+ bin->being_batch_filled = false;
+ bin->bytes_cur = 0;
+ edata_list_active_init(&bin->freelist);
+}
+
+bool
+sec_init(tsdn_t *tsdn, sec_t *sec, base_t *base, pai_t *fallback,
+ const sec_opts_t *opts) {
+ assert(opts->max_alloc >= PAGE);
+
+ size_t max_alloc = PAGE_FLOOR(opts->max_alloc);
+ pszind_t npsizes = sz_psz2ind(max_alloc) + 1;
+
+ size_t sz_shards = opts->nshards * sizeof(sec_shard_t);
+ size_t sz_bins = opts->nshards * (size_t)npsizes * sizeof(sec_bin_t);
+ size_t sz_alloc = sz_shards + sz_bins;
+ void *dynalloc = base_alloc(tsdn, base, sz_alloc, CACHELINE);
+ if (dynalloc == NULL) {
+ return true;
+ }
+ sec_shard_t *shard_cur = (sec_shard_t *)dynalloc;
+ sec->shards = shard_cur;
+ sec_bin_t *bin_cur = (sec_bin_t *)&shard_cur[opts->nshards];
+ /* Just for asserts, below. */
+ sec_bin_t *bin_start = bin_cur;
+
+ for (size_t i = 0; i < opts->nshards; i++) {
+ sec_shard_t *shard = shard_cur;
+ shard_cur++;
+ bool err = malloc_mutex_init(&shard->mtx, "sec_shard",
+ WITNESS_RANK_SEC_SHARD, malloc_mutex_rank_exclusive);
+ if (err) {
+ return true;
+ }
+ shard->enabled = true;
+ shard->bins = bin_cur;
+ for (pszind_t j = 0; j < npsizes; j++) {
+ sec_bin_init(&shard->bins[j]);
+ bin_cur++;
+ }
+ shard->bytes_cur = 0;
+ shard->to_flush_next = 0;
+ }
+ /*
+ * Should have exactly matched the bin_start to the first unused byte
+ * after the shards.
+ */
+ assert((void *)shard_cur == (void *)bin_start);
+ /* And the last bin to use up the last bytes of the allocation. */
+ assert((char *)bin_cur == ((char *)dynalloc + sz_alloc));
+ sec->fallback = fallback;
+
+
+ sec->opts = *opts;
+ sec->npsizes = npsizes;
+
+ /*
+ * Initialize these last so that an improper use of an SEC whose
+ * initialization failed will segfault in an easy-to-spot way.
+ */
+ sec->pai.alloc = &sec_alloc;
+ sec->pai.alloc_batch = &pai_alloc_batch_default;
+ sec->pai.expand = &sec_expand;
+ sec->pai.shrink = &sec_shrink;
+ sec->pai.dalloc = &sec_dalloc;
+ sec->pai.dalloc_batch = &pai_dalloc_batch_default;
+
+ return false;
+}
+
+static sec_shard_t *
+sec_shard_pick(tsdn_t *tsdn, sec_t *sec) {
+ /*
+ * Eventually, we should implement affinity, tracking source shard using
+ * the edata_t's newly freed up fields. For now, just randomly
+ * distribute across all shards.
+ */
+ if (tsdn_null(tsdn)) {
+ return &sec->shards[0];
+ }
+ tsd_t *tsd = tsdn_tsd(tsdn);
+ uint8_t *idxp = tsd_sec_shardp_get(tsd);
+ if (*idxp == (uint8_t)-1) {
+ /*
+ * First use; initialize using the trick from Daniel Lemire's
+ * "A fast alternative to the modulo reduction. Use a 64 bit
+ * number to store 32 bits, since we'll deliberately overflow
+ * when we multiply by the number of shards.
+ */
+ uint64_t rand32 = prng_lg_range_u64(tsd_prng_statep_get(tsd), 32);
+ uint32_t idx =
+ (uint32_t)((rand32 * (uint64_t)sec->opts.nshards) >> 32);
+ assert(idx < (uint32_t)sec->opts.nshards);
+ *idxp = (uint8_t)idx;
+ }
+ return &sec->shards[*idxp];
+}
+
+/*
+ * Perhaps surprisingly, this can be called on the alloc pathways; if we hit an
+ * empty cache, we'll try to fill it, which can push the shard over it's limit.
+ */
+static void
+sec_flush_some_and_unlock(tsdn_t *tsdn, sec_t *sec, sec_shard_t *shard) {
+ malloc_mutex_assert_owner(tsdn, &shard->mtx);
+ edata_list_active_t to_flush;
+ edata_list_active_init(&to_flush);
+ while (shard->bytes_cur > sec->opts.bytes_after_flush) {
+ /* Pick a victim. */
+ sec_bin_t *bin = &shard->bins[shard->to_flush_next];
+
+ /* Update our victim-picking state. */
+ shard->to_flush_next++;
+ if (shard->to_flush_next == sec->npsizes) {
+ shard->to_flush_next = 0;
+ }
+
+ assert(shard->bytes_cur >= bin->bytes_cur);
+ if (bin->bytes_cur != 0) {
+ shard->bytes_cur -= bin->bytes_cur;
+ bin->bytes_cur = 0;
+ edata_list_active_concat(&to_flush, &bin->freelist);
+ }
+ /*
+ * Either bin->bytes_cur was 0, in which case we didn't touch
+ * the bin list but it should be empty anyways (or else we
+ * missed a bytes_cur update on a list modification), or it
+ * *was* 0 and we emptied it ourselves. Either way, it should
+ * be empty now.
+ */
+ assert(edata_list_active_empty(&bin->freelist));
+ }
+
+ malloc_mutex_unlock(tsdn, &shard->mtx);
+ bool deferred_work_generated = false;
+ pai_dalloc_batch(tsdn, sec->fallback, &to_flush,
+ &deferred_work_generated);
+}
+
+static edata_t *
+sec_shard_alloc_locked(tsdn_t *tsdn, sec_t *sec, sec_shard_t *shard,
+ sec_bin_t *bin) {
+ malloc_mutex_assert_owner(tsdn, &shard->mtx);
+ if (!shard->enabled) {
+ return NULL;
+ }
+ edata_t *edata = edata_list_active_first(&bin->freelist);
+ if (edata != NULL) {
+ edata_list_active_remove(&bin->freelist, edata);
+ assert(edata_size_get(edata) <= bin->bytes_cur);
+ bin->bytes_cur -= edata_size_get(edata);
+ assert(edata_size_get(edata) <= shard->bytes_cur);
+ shard->bytes_cur -= edata_size_get(edata);
+ }
+ return edata;
+}
+
+static edata_t *
+sec_batch_fill_and_alloc(tsdn_t *tsdn, sec_t *sec, sec_shard_t *shard,
+ sec_bin_t *bin, size_t size) {
+ malloc_mutex_assert_not_owner(tsdn, &shard->mtx);
+
+ edata_list_active_t result;
+ edata_list_active_init(&result);
+ bool deferred_work_generated = false;
+ size_t nalloc = pai_alloc_batch(tsdn, sec->fallback, size,
+ 1 + sec->opts.batch_fill_extra, &result, &deferred_work_generated);
+
+ edata_t *ret = edata_list_active_first(&result);
+ if (ret != NULL) {
+ edata_list_active_remove(&result, ret);
+ }
+
+ malloc_mutex_lock(tsdn, &shard->mtx);
+ bin->being_batch_filled = false;
+ /*
+ * Handle the easy case first: nothing to cache. Note that this can
+ * only happen in case of OOM, since sec_alloc checks the expected
+ * number of allocs, and doesn't bother going down the batch_fill
+ * pathway if there won't be anything left to cache. So to be in this
+ * code path, we must have asked for > 1 alloc, but only gotten 1 back.
+ */
+ if (nalloc <= 1) {
+ malloc_mutex_unlock(tsdn, &shard->mtx);
+ return ret;
+ }
+
+ size_t new_cached_bytes = (nalloc - 1) * size;
+
+ edata_list_active_concat(&bin->freelist, &result);
+ bin->bytes_cur += new_cached_bytes;
+ shard->bytes_cur += new_cached_bytes;
+
+ if (shard->bytes_cur > sec->opts.max_bytes) {
+ sec_flush_some_and_unlock(tsdn, sec, shard);
+ } else {
+ malloc_mutex_unlock(tsdn, &shard->mtx);
+ }
+
+ return ret;
+}
+
+static edata_t *
+sec_alloc(tsdn_t *tsdn, pai_t *self, size_t size, size_t alignment, bool zero,
+ bool guarded, bool frequent_reuse, bool *deferred_work_generated) {
+ assert((size & PAGE_MASK) == 0);
+ assert(!guarded);
+
+ sec_t *sec = (sec_t *)self;
+
+ if (zero || alignment > PAGE || sec->opts.nshards == 0
+ || size > sec->opts.max_alloc) {
+ return pai_alloc(tsdn, sec->fallback, size, alignment, zero,
+ /* guarded */ false, frequent_reuse,
+ deferred_work_generated);
+ }
+ pszind_t pszind = sz_psz2ind(size);
+ assert(pszind < sec->npsizes);
+
+ sec_shard_t *shard = sec_shard_pick(tsdn, sec);
+ sec_bin_t *bin = &shard->bins[pszind];
+ bool do_batch_fill = false;
+
+ malloc_mutex_lock(tsdn, &shard->mtx);
+ edata_t *edata = sec_shard_alloc_locked(tsdn, sec, shard, bin);
+ if (edata == NULL) {
+ if (!bin->being_batch_filled
+ && sec->opts.batch_fill_extra > 0) {
+ bin->being_batch_filled = true;
+ do_batch_fill = true;
+ }
+ }
+ malloc_mutex_unlock(tsdn, &shard->mtx);
+ if (edata == NULL) {
+ if (do_batch_fill) {
+ edata = sec_batch_fill_and_alloc(tsdn, sec, shard, bin,
+ size);
+ } else {
+ edata = pai_alloc(tsdn, sec->fallback, size, alignment,
+ zero, /* guarded */ false, frequent_reuse,
+ deferred_work_generated);
+ }
+ }
+ return edata;
+}
+
+static bool
+sec_expand(tsdn_t *tsdn, pai_t *self, edata_t *edata, size_t old_size,
+ size_t new_size, bool zero, bool *deferred_work_generated) {
+ sec_t *sec = (sec_t *)self;
+ return pai_expand(tsdn, sec->fallback, edata, old_size, new_size, zero,
+ deferred_work_generated);
+}
+
+static bool
+sec_shrink(tsdn_t *tsdn, pai_t *self, edata_t *edata, size_t old_size,
+ size_t new_size, bool *deferred_work_generated) {
+ sec_t *sec = (sec_t *)self;
+ return pai_shrink(tsdn, sec->fallback, edata, old_size, new_size,
+ deferred_work_generated);
+}
+
+static void
+sec_flush_all_locked(tsdn_t *tsdn, sec_t *sec, sec_shard_t *shard) {
+ malloc_mutex_assert_owner(tsdn, &shard->mtx);
+ shard->bytes_cur = 0;
+ edata_list_active_t to_flush;
+ edata_list_active_init(&to_flush);
+ for (pszind_t i = 0; i < sec->npsizes; i++) {
+ sec_bin_t *bin = &shard->bins[i];
+ bin->bytes_cur = 0;
+ edata_list_active_concat(&to_flush, &bin->freelist);
+ }
+
+ /*
+ * Ordinarily we would try to avoid doing the batch deallocation while
+ * holding the shard mutex, but the flush_all pathways only happen when
+ * we're disabling the HPA or resetting the arena, both of which are
+ * rare pathways.
+ */
+ bool deferred_work_generated = false;
+ pai_dalloc_batch(tsdn, sec->fallback, &to_flush,
+ &deferred_work_generated);
+}
+
+static void
+sec_shard_dalloc_and_unlock(tsdn_t *tsdn, sec_t *sec, sec_shard_t *shard,
+ edata_t *edata) {
+ malloc_mutex_assert_owner(tsdn, &shard->mtx);
+ assert(shard->bytes_cur <= sec->opts.max_bytes);
+ size_t size = edata_size_get(edata);
+ pszind_t pszind = sz_psz2ind(size);
+ assert(pszind < sec->npsizes);
+ /*
+ * Prepending here results in LIFO allocation per bin, which seems
+ * reasonable.
+ */
+ sec_bin_t *bin = &shard->bins[pszind];
+ edata_list_active_prepend(&bin->freelist, edata);
+ bin->bytes_cur += size;
+ shard->bytes_cur += size;
+ if (shard->bytes_cur > sec->opts.max_bytes) {
+ /*
+ * We've exceeded the shard limit. We make two nods in the
+ * direction of fragmentation avoidance: we flush everything in
+ * the shard, rather than one particular bin, and we hold the
+ * lock while flushing (in case one of the extents we flush is
+ * highly preferred from a fragmentation-avoidance perspective
+ * in the backing allocator). This has the extra advantage of
+ * not requiring advanced cache balancing strategies.
+ */
+ sec_flush_some_and_unlock(tsdn, sec, shard);
+ malloc_mutex_assert_not_owner(tsdn, &shard->mtx);
+ } else {
+ malloc_mutex_unlock(tsdn, &shard->mtx);
+ }
+}
+
+static void
+sec_dalloc(tsdn_t *tsdn, pai_t *self, edata_t *edata,
+ bool *deferred_work_generated) {
+ sec_t *sec = (sec_t *)self;
+ if (sec->opts.nshards == 0
+ || edata_size_get(edata) > sec->opts.max_alloc) {
+ pai_dalloc(tsdn, sec->fallback, edata,
+ deferred_work_generated);
+ return;
+ }
+ sec_shard_t *shard = sec_shard_pick(tsdn, sec);
+ malloc_mutex_lock(tsdn, &shard->mtx);
+ if (shard->enabled) {
+ sec_shard_dalloc_and_unlock(tsdn, sec, shard, edata);
+ } else {
+ malloc_mutex_unlock(tsdn, &shard->mtx);
+ pai_dalloc(tsdn, sec->fallback, edata,
+ deferred_work_generated);
+ }
+}
+
+void
+sec_flush(tsdn_t *tsdn, sec_t *sec) {
+ for (size_t i = 0; i < sec->opts.nshards; i++) {
+ malloc_mutex_lock(tsdn, &sec->shards[i].mtx);
+ sec_flush_all_locked(tsdn, sec, &sec->shards[i]);
+ malloc_mutex_unlock(tsdn, &sec->shards[i].mtx);
+ }
+}
+
+void
+sec_disable(tsdn_t *tsdn, sec_t *sec) {
+ for (size_t i = 0; i < sec->opts.nshards; i++) {
+ malloc_mutex_lock(tsdn, &sec->shards[i].mtx);
+ sec->shards[i].enabled = false;
+ sec_flush_all_locked(tsdn, sec, &sec->shards[i]);
+ malloc_mutex_unlock(tsdn, &sec->shards[i].mtx);
+ }
+}
+
+void
+sec_stats_merge(tsdn_t *tsdn, sec_t *sec, sec_stats_t *stats) {
+ size_t sum = 0;
+ for (size_t i = 0; i < sec->opts.nshards; i++) {
+ /*
+ * We could save these lock acquisitions by making bytes_cur
+ * atomic, but stats collection is rare anyways and we expect
+ * the number and type of stats to get more interesting.
+ */
+ malloc_mutex_lock(tsdn, &sec->shards[i].mtx);
+ sum += sec->shards[i].bytes_cur;
+ malloc_mutex_unlock(tsdn, &sec->shards[i].mtx);
+ }
+ stats->bytes += sum;
+}
+
+void
+sec_mutex_stats_read(tsdn_t *tsdn, sec_t *sec,
+ mutex_prof_data_t *mutex_prof_data) {
+ for (size_t i = 0; i < sec->opts.nshards; i++) {
+ malloc_mutex_lock(tsdn, &sec->shards[i].mtx);
+ malloc_mutex_prof_accum(tsdn, mutex_prof_data,
+ &sec->shards[i].mtx);
+ malloc_mutex_unlock(tsdn, &sec->shards[i].mtx);
+ }
+}
+
+void
+sec_prefork2(tsdn_t *tsdn, sec_t *sec) {
+ for (size_t i = 0; i < sec->opts.nshards; i++) {
+ malloc_mutex_prefork(tsdn, &sec->shards[i].mtx);
+ }
+}
+
+void
+sec_postfork_parent(tsdn_t *tsdn, sec_t *sec) {
+ for (size_t i = 0; i < sec->opts.nshards; i++) {
+ malloc_mutex_postfork_parent(tsdn, &sec->shards[i].mtx);
+ }
+}
+
+void
+sec_postfork_child(tsdn_t *tsdn, sec_t *sec) {
+ for (size_t i = 0; i < sec->opts.nshards; i++) {
+ malloc_mutex_postfork_child(tsdn, &sec->shards[i].mtx);
+ }
+}
diff --git a/contrib/jemalloc/src/stats.c b/contrib/jemalloc/src/stats.c
index 118e05d2911a..efc70fd3c8b2 100644
--- a/contrib/jemalloc/src/stats.c
+++ b/contrib/jemalloc/src/stats.c
@@ -1,12 +1,13 @@
-#define JEMALLOC_STATS_C_
#include "jemalloc/internal/jemalloc_preamble.h"
#include "jemalloc/internal/jemalloc_internal_includes.h"
#include "jemalloc/internal/assert.h"
#include "jemalloc/internal/ctl.h"
#include "jemalloc/internal/emitter.h"
+#include "jemalloc/internal/fxp.h"
#include "jemalloc/internal/mutex.h"
#include "jemalloc/internal/mutex_prof.h"
+#include "jemalloc/internal/prof_stats.h"
const char *global_mutex_names[mutex_prof_num_global_mutexes] = {
#define OP(mtx) #mtx,
@@ -25,22 +26,28 @@ const char *arena_mutex_names[mutex_prof_num_arena_mutexes] = {
xmallctl(n, (void *)v, &sz, NULL, 0); \
} while (0)
-#define CTL_M2_GET(n, i, v, t) do { \
- size_t mib[CTL_MAX_DEPTH]; \
- size_t miblen = sizeof(mib) / sizeof(size_t); \
+#define CTL_LEAF_PREPARE(mib, miblen, name) do { \
+ assert(miblen < CTL_MAX_DEPTH); \
+ size_t miblen_new = CTL_MAX_DEPTH; \
+ xmallctlmibnametomib(mib, miblen, name, &miblen_new); \
+ assert(miblen_new > miblen); \
+} while (0)
+
+#define CTL_LEAF(mib, miblen, leaf, v, t) do { \
+ assert(miblen < CTL_MAX_DEPTH); \
+ size_t miblen_new = CTL_MAX_DEPTH; \
size_t sz = sizeof(t); \
- xmallctlnametomib(n, mib, &miblen); \
- mib[2] = (i); \
- xmallctlbymib(mib, miblen, (void *)v, &sz, NULL, 0); \
+ xmallctlbymibname(mib, miblen, leaf, &miblen_new, (void *)v, \
+ &sz, NULL, 0); \
+ assert(miblen_new == miblen + 1); \
} while (0)
-#define CTL_M2_M4_GET(n, i, j, v, t) do { \
+#define CTL_M2_GET(n, i, v, t) do { \
size_t mib[CTL_MAX_DEPTH]; \
size_t miblen = sizeof(mib) / sizeof(size_t); \
size_t sz = sizeof(t); \
xmallctlnametomib(n, mib, &miblen); \
mib[2] = (i); \
- mib[4] = (j); \
xmallctlbymib(mib, miblen, (void *)v, &sz, NULL, 0); \
} while (0)
@@ -50,6 +57,13 @@ const char *arena_mutex_names[mutex_prof_num_arena_mutexes] = {
bool opt_stats_print = false;
char opt_stats_print_opts[stats_print_tot_num_options+1] = "";
+int64_t opt_stats_interval = STATS_INTERVAL_DEFAULT;
+char opt_stats_interval_opts[stats_print_tot_num_options+1] = "";
+
+static counter_accum_t stats_interval_accumulated;
+/* Per thread batch accum size for stats_interval. */
+static uint64_t stats_interval_accum_batch;
+
/******************************************************************************/
static uint64_t
@@ -91,13 +105,6 @@ get_rate_str(uint64_t dividend, uint64_t divisor, char str[6]) {
return false;
}
-#define MUTEX_CTL_STR_MAX_LENGTH 128
-static void
-gen_mutex_ctl_str(char *str, size_t buf_len, const char *prefix,
- const char *mutex, const char *counter) {
- malloc_snprintf(str, buf_len, "stats.%s.%s.%s", prefix, mutex, counter);
-}
-
static void
mutex_stats_init_cols(emitter_row_t *row, const char *table_name,
emitter_col_t *name,
@@ -118,7 +125,7 @@ mutex_stats_init_cols(emitter_row_t *row, const char *table_name,
#define WIDTH_uint32_t 12
#define WIDTH_uint64_t 16
-#define OP(counter, counter_type, human, derived, base_counter) \
+#define OP(counter, counter_type, human, derived, base_counter) \
col = &col_##counter_type[k_##counter_type]; \
++k_##counter_type; \
emitter_col_init(col, row); \
@@ -134,27 +141,31 @@ mutex_stats_init_cols(emitter_row_t *row, const char *table_name,
}
static void
-mutex_stats_read_global(const char *name, emitter_col_t *col_name,
+mutex_stats_read_global(size_t mib[], size_t miblen, const char *name,
+ emitter_col_t *col_name,
emitter_col_t col_uint64_t[mutex_prof_num_uint64_t_counters],
emitter_col_t col_uint32_t[mutex_prof_num_uint32_t_counters],
uint64_t uptime) {
- char cmd[MUTEX_CTL_STR_MAX_LENGTH];
+ CTL_LEAF_PREPARE(mib, miblen, name);
+ size_t miblen_name = miblen + 1;
col_name->str_val = name;
emitter_col_t *dst;
#define EMITTER_TYPE_uint32_t emitter_type_uint32
#define EMITTER_TYPE_uint64_t emitter_type_uint64
-#define OP(counter, counter_type, human, derived, base_counter) \
+#define OP(counter, counter_type, human, derived, base_counter) \
dst = &col_##counter_type[mutex_counter_##counter]; \
dst->type = EMITTER_TYPE_##counter_type; \
if (!derived) { \
- gen_mutex_ctl_str(cmd, MUTEX_CTL_STR_MAX_LENGTH, \
- "mutexes", name, #counter); \
- CTL_GET(cmd, (counter_type *)&dst->bool_val, counter_type); \
- } else { \
- emitter_col_t *base = &col_##counter_type[mutex_counter_##base_counter]; \
- dst->counter_type##_val = rate_per_second(base->counter_type##_val, uptime); \
+ CTL_LEAF(mib, miblen_name, #counter, \
+ (counter_type *)&dst->bool_val, counter_type); \
+ } else { \
+ emitter_col_t *base = \
+ &col_##counter_type[mutex_counter_##base_counter]; \
+ dst->counter_type##_val = \
+ (counter_type)rate_per_second( \
+ base->counter_type##_val, uptime); \
}
MUTEX_PROF_COUNTERS
#undef OP
@@ -163,28 +174,31 @@ mutex_stats_read_global(const char *name, emitter_col_t *col_name,
}
static void
-mutex_stats_read_arena(unsigned arena_ind, mutex_prof_arena_ind_t mutex_ind,
- const char *name, emitter_col_t *col_name,
+mutex_stats_read_arena(size_t mib[], size_t miblen, const char *name,
+ emitter_col_t *col_name,
emitter_col_t col_uint64_t[mutex_prof_num_uint64_t_counters],
emitter_col_t col_uint32_t[mutex_prof_num_uint32_t_counters],
uint64_t uptime) {
- char cmd[MUTEX_CTL_STR_MAX_LENGTH];
+ CTL_LEAF_PREPARE(mib, miblen, name);
+ size_t miblen_name = miblen + 1;
col_name->str_val = name;
emitter_col_t *dst;
#define EMITTER_TYPE_uint32_t emitter_type_uint32
#define EMITTER_TYPE_uint64_t emitter_type_uint64
-#define OP(counter, counter_type, human, derived, base_counter) \
+#define OP(counter, counter_type, human, derived, base_counter) \
dst = &col_##counter_type[mutex_counter_##counter]; \
dst->type = EMITTER_TYPE_##counter_type; \
- if (!derived) { \
- gen_mutex_ctl_str(cmd, MUTEX_CTL_STR_MAX_LENGTH, \
- "arenas.0.mutexes", arena_mutex_names[mutex_ind], #counter);\
- CTL_M2_GET(cmd, arena_ind, (counter_type *)&dst->bool_val, counter_type); \
- } else { \
- emitter_col_t *base = &col_##counter_type[mutex_counter_##base_counter]; \
- dst->counter_type##_val = rate_per_second(base->counter_type##_val, uptime); \
+ if (!derived) { \
+ CTL_LEAF(mib, miblen_name, #counter, \
+ (counter_type *)&dst->bool_val, counter_type); \
+ } else { \
+ emitter_col_t *base = \
+ &col_##counter_type[mutex_counter_##base_counter]; \
+ dst->counter_type##_val = \
+ (counter_type)rate_per_second( \
+ base->counter_type##_val, uptime); \
}
MUTEX_PROF_COUNTERS
#undef OP
@@ -193,26 +207,29 @@ mutex_stats_read_arena(unsigned arena_ind, mutex_prof_arena_ind_t mutex_ind,
}
static void
-mutex_stats_read_arena_bin(unsigned arena_ind, unsigned bin_ind,
+mutex_stats_read_arena_bin(size_t mib[], size_t miblen,
emitter_col_t col_uint64_t[mutex_prof_num_uint64_t_counters],
emitter_col_t col_uint32_t[mutex_prof_num_uint32_t_counters],
uint64_t uptime) {
- char cmd[MUTEX_CTL_STR_MAX_LENGTH];
+ CTL_LEAF_PREPARE(mib, miblen, "mutex");
+ size_t miblen_mutex = miblen + 1;
+
emitter_col_t *dst;
#define EMITTER_TYPE_uint32_t emitter_type_uint32
#define EMITTER_TYPE_uint64_t emitter_type_uint64
-#define OP(counter, counter_type, human, derived, base_counter) \
+#define OP(counter, counter_type, human, derived, base_counter) \
dst = &col_##counter_type[mutex_counter_##counter]; \
dst->type = EMITTER_TYPE_##counter_type; \
- if (!derived) { \
- gen_mutex_ctl_str(cmd, MUTEX_CTL_STR_MAX_LENGTH, \
- "arenas.0.bins.0","mutex", #counter); \
- CTL_M2_M4_GET(cmd, arena_ind, bin_ind, \
- (counter_type *)&dst->bool_val, counter_type); \
- } else { \
- emitter_col_t *base = &col_##counter_type[mutex_counter_##base_counter]; \
- dst->counter_type##_val = rate_per_second(base->counter_type##_val, uptime); \
+ if (!derived) { \
+ CTL_LEAF(mib, miblen_mutex, #counter, \
+ (counter_type *)&dst->bool_val, counter_type); \
+ } else { \
+ emitter_col_t *base = \
+ &col_##counter_type[mutex_counter_##base_counter]; \
+ dst->counter_type##_val = \
+ (counter_type)rate_per_second( \
+ base->counter_type##_val, uptime); \
}
MUTEX_PROF_COUNTERS
#undef OP
@@ -249,25 +266,42 @@ mutex_stats_emit(emitter_t *emitter, emitter_row_t *row,
#undef EMITTER_TYPE_uint64_t
}
-#define COL(row_name, column_name, left_or_right, col_width, etype) \
- emitter_col_t col_##column_name; \
- emitter_col_init(&col_##column_name, &row_name); \
- col_##column_name.justify = emitter_justify_##left_or_right; \
- col_##column_name.width = col_width; \
+#define COL_DECLARE(column_name) \
+ emitter_col_t col_##column_name;
+
+#define COL_INIT(row_name, column_name, left_or_right, col_width, etype)\
+ emitter_col_init(&col_##column_name, &row_name); \
+ col_##column_name.justify = emitter_justify_##left_or_right; \
+ col_##column_name.width = col_width; \
col_##column_name.type = emitter_type_##etype;
-#define COL_HDR(row_name, column_name, human, left_or_right, col_width, etype) \
- COL(row_name, column_name, left_or_right, col_width, etype) \
- emitter_col_t header_##column_name; \
- emitter_col_init(&header_##column_name, &header_##row_name); \
- header_##column_name.justify = emitter_justify_##left_or_right; \
- header_##column_name.width = col_width; \
- header_##column_name.type = emitter_type_title; \
+#define COL(row_name, column_name, left_or_right, col_width, etype) \
+ COL_DECLARE(column_name); \
+ COL_INIT(row_name, column_name, left_or_right, col_width, etype)
+
+#define COL_HDR_DECLARE(column_name) \
+ COL_DECLARE(column_name); \
+ emitter_col_t header_##column_name;
+
+#define COL_HDR_INIT(row_name, column_name, human, left_or_right, \
+ col_width, etype) \
+ COL_INIT(row_name, column_name, left_or_right, col_width, etype)\
+ emitter_col_init(&header_##column_name, &header_##row_name); \
+ header_##column_name.justify = emitter_justify_##left_or_right; \
+ header_##column_name.width = col_width; \
+ header_##column_name.type = emitter_type_title; \
header_##column_name.str_val = human ? human : #column_name;
+#define COL_HDR(row_name, column_name, human, left_or_right, col_width, \
+ etype) \
+ COL_HDR_DECLARE(column_name) \
+ COL_HDR_INIT(row_name, column_name, human, left_or_right, \
+ col_width, etype)
+JEMALLOC_COLD
static void
-stats_arena_bins_print(emitter_t *emitter, bool mutex, unsigned i, uint64_t uptime) {
+stats_arena_bins_print(emitter_t *emitter, bool mutex, unsigned i,
+ uint64_t uptime) {
size_t page;
bool in_gap, in_gap_prev;
unsigned nbins, j;
@@ -282,6 +316,9 @@ stats_arena_bins_print(emitter_t *emitter, bool mutex, unsigned i, uint64_t upti
emitter_row_t row;
emitter_row_init(&row);
+ bool prof_stats_on = config_prof && opt_prof && opt_prof_stats
+ && i == MALLCTL_ARENAS_ALL;
+
COL_HDR(row, size, NULL, right, 20, size)
COL_HDR(row, ind, NULL, right, 4, unsigned)
COL_HDR(row, allocated, NULL, right, 13, uint64)
@@ -291,6 +328,16 @@ stats_arena_bins_print(emitter_t *emitter, bool mutex, unsigned i, uint64_t upti
COL_HDR(row, ndalloc_ps, "(#/sec)", right, 8, uint64)
COL_HDR(row, nrequests, NULL, right, 13, uint64)
COL_HDR(row, nrequests_ps, "(#/sec)", right, 10, uint64)
+ COL_HDR_DECLARE(prof_live_requested);
+ COL_HDR_DECLARE(prof_live_count);
+ COL_HDR_DECLARE(prof_accum_requested);
+ COL_HDR_DECLARE(prof_accum_count);
+ if (prof_stats_on) {
+ COL_HDR_INIT(row, prof_live_requested, NULL, right, 21, uint64)
+ COL_HDR_INIT(row, prof_live_count, NULL, right, 17, uint64)
+ COL_HDR_INIT(row, prof_accum_requested, NULL, right, 21, uint64)
+ COL_HDR_INIT(row, prof_accum_count, NULL, right, 17, uint64)
+ }
COL_HDR(row, nshards, NULL, right, 9, unsigned)
COL_HDR(row, curregs, NULL, right, 13, size)
COL_HDR(row, curslabs, NULL, right, 13, size)
@@ -334,6 +381,19 @@ stats_arena_bins_print(emitter_t *emitter, bool mutex, unsigned i, uint64_t upti
emitter_table_row(emitter, &header_row);
emitter_json_array_kv_begin(emitter, "bins");
+ size_t stats_arenas_mib[CTL_MAX_DEPTH];
+ CTL_LEAF_PREPARE(stats_arenas_mib, 0, "stats.arenas");
+ stats_arenas_mib[2] = i;
+ CTL_LEAF_PREPARE(stats_arenas_mib, 3, "bins");
+
+ size_t arenas_bin_mib[CTL_MAX_DEPTH];
+ CTL_LEAF_PREPARE(arenas_bin_mib, 0, "arenas.bin");
+
+ size_t prof_stats_mib[CTL_MAX_DEPTH];
+ if (prof_stats_on) {
+ CTL_LEAF_PREPARE(prof_stats_mib, 0, "prof.stats.bins");
+ }
+
for (j = 0, in_gap = false; j < nbins; j++) {
uint64_t nslabs;
size_t reg_size, slab_size, curregs;
@@ -342,44 +402,57 @@ stats_arena_bins_print(emitter_t *emitter, bool mutex, unsigned i, uint64_t upti
uint32_t nregs, nshards;
uint64_t nmalloc, ndalloc, nrequests, nfills, nflushes;
uint64_t nreslabs;
+ prof_stats_t prof_live;
+ prof_stats_t prof_accum;
+
+ stats_arenas_mib[4] = j;
+ arenas_bin_mib[2] = j;
+
+ CTL_LEAF(stats_arenas_mib, 5, "nslabs", &nslabs, uint64_t);
+
+ if (prof_stats_on) {
+ prof_stats_mib[3] = j;
+ CTL_LEAF(prof_stats_mib, 4, "live", &prof_live,
+ prof_stats_t);
+ CTL_LEAF(prof_stats_mib, 4, "accum", &prof_accum,
+ prof_stats_t);
+ }
- CTL_M2_M4_GET("stats.arenas.0.bins.0.nslabs", i, j, &nslabs,
- uint64_t);
in_gap_prev = in_gap;
- in_gap = (nslabs == 0);
+ if (prof_stats_on) {
+ in_gap = (nslabs == 0 && prof_accum.count == 0);
+ } else {
+ in_gap = (nslabs == 0);
+ }
if (in_gap_prev && !in_gap) {
emitter_table_printf(emitter,
" ---\n");
}
- CTL_M2_GET("arenas.bin.0.size", j, &reg_size, size_t);
- CTL_M2_GET("arenas.bin.0.nregs", j, &nregs, uint32_t);
- CTL_M2_GET("arenas.bin.0.slab_size", j, &slab_size, size_t);
- CTL_M2_GET("arenas.bin.0.nshards", j, &nshards, uint32_t);
+ if (in_gap && !emitter_outputs_json(emitter)) {
+ continue;
+ }
- CTL_M2_M4_GET("stats.arenas.0.bins.0.nmalloc", i, j, &nmalloc,
- uint64_t);
- CTL_M2_M4_GET("stats.arenas.0.bins.0.ndalloc", i, j, &ndalloc,
- uint64_t);
- CTL_M2_M4_GET("stats.arenas.0.bins.0.curregs", i, j, &curregs,
- size_t);
- CTL_M2_M4_GET("stats.arenas.0.bins.0.nrequests", i, j,
- &nrequests, uint64_t);
- CTL_M2_M4_GET("stats.arenas.0.bins.0.nfills", i, j, &nfills,
+ CTL_LEAF(arenas_bin_mib, 3, "size", &reg_size, size_t);
+ CTL_LEAF(arenas_bin_mib, 3, "nregs", &nregs, uint32_t);
+ CTL_LEAF(arenas_bin_mib, 3, "slab_size", &slab_size, size_t);
+ CTL_LEAF(arenas_bin_mib, 3, "nshards", &nshards, uint32_t);
+ CTL_LEAF(stats_arenas_mib, 5, "nmalloc", &nmalloc, uint64_t);
+ CTL_LEAF(stats_arenas_mib, 5, "ndalloc", &ndalloc, uint64_t);
+ CTL_LEAF(stats_arenas_mib, 5, "curregs", &curregs, size_t);
+ CTL_LEAF(stats_arenas_mib, 5, "nrequests", &nrequests,
uint64_t);
- CTL_M2_M4_GET("stats.arenas.0.bins.0.nflushes", i, j, &nflushes,
- uint64_t);
- CTL_M2_M4_GET("stats.arenas.0.bins.0.nreslabs", i, j, &nreslabs,
- uint64_t);
- CTL_M2_M4_GET("stats.arenas.0.bins.0.curslabs", i, j, &curslabs,
- size_t);
- CTL_M2_M4_GET("stats.arenas.0.bins.0.nonfull_slabs", i, j, &nonfull_slabs,
+ CTL_LEAF(stats_arenas_mib, 5, "nfills", &nfills, uint64_t);
+ CTL_LEAF(stats_arenas_mib, 5, "nflushes", &nflushes, uint64_t);
+ CTL_LEAF(stats_arenas_mib, 5, "nreslabs", &nreslabs, uint64_t);
+ CTL_LEAF(stats_arenas_mib, 5, "curslabs", &curslabs, size_t);
+ CTL_LEAF(stats_arenas_mib, 5, "nonfull_slabs", &nonfull_slabs,
size_t);
if (mutex) {
- mutex_stats_read_arena_bin(i, j, col_mutex64,
- col_mutex32, uptime);
+ mutex_stats_read_arena_bin(stats_arenas_mib, 5,
+ col_mutex64, col_mutex32, uptime);
}
emitter_json_object_begin(emitter);
@@ -391,6 +464,16 @@ stats_arena_bins_print(emitter_t *emitter, bool mutex, unsigned i, uint64_t upti
&curregs);
emitter_json_kv(emitter, "nrequests", emitter_type_uint64,
&nrequests);
+ if (prof_stats_on) {
+ emitter_json_kv(emitter, "prof_live_requested",
+ emitter_type_uint64, &prof_live.req_sum);
+ emitter_json_kv(emitter, "prof_live_count",
+ emitter_type_uint64, &prof_live.count);
+ emitter_json_kv(emitter, "prof_accum_requested",
+ emitter_type_uint64, &prof_accum.req_sum);
+ emitter_json_kv(emitter, "prof_accum_count",
+ emitter_type_uint64, &prof_accum.count);
+ }
emitter_json_kv(emitter, "nfills", emitter_type_uint64,
&nfills);
emitter_json_kv(emitter, "nflushes", emitter_type_uint64,
@@ -437,6 +520,13 @@ stats_arena_bins_print(emitter_t *emitter, bool mutex, unsigned i, uint64_t upti
col_ndalloc_ps.uint64_val = rate_per_second(ndalloc, uptime);
col_nrequests.uint64_val = nrequests;
col_nrequests_ps.uint64_val = rate_per_second(nrequests, uptime);
+ if (prof_stats_on) {
+ col_prof_live_requested.uint64_val = prof_live.req_sum;
+ col_prof_live_count.uint64_val = prof_live.count;
+ col_prof_accum_requested.uint64_val =
+ prof_accum.req_sum;
+ col_prof_accum_count.uint64_val = prof_accum.count;
+ }
col_nshards.unsigned_val = nshards;
col_curregs.size_val = curregs;
col_curslabs.size_val = curslabs;
@@ -466,6 +556,7 @@ stats_arena_bins_print(emitter_t *emitter, bool mutex, unsigned i, uint64_t upti
}
}
+JEMALLOC_COLD
static void
stats_arena_lextents_print(emitter_t *emitter, unsigned i, uint64_t uptime) {
unsigned nbins, nlextents, j;
@@ -479,6 +570,9 @@ stats_arena_lextents_print(emitter_t *emitter, unsigned i, uint64_t uptime) {
emitter_row_t row;
emitter_row_init(&row);
+ bool prof_stats_on = config_prof && opt_prof && opt_prof_stats
+ && i == MALLCTL_ARENAS_ALL;
+
COL_HDR(row, size, NULL, right, 20, size)
COL_HDR(row, ind, NULL, right, 4, unsigned)
COL_HDR(row, allocated, NULL, right, 13, size)
@@ -488,6 +582,16 @@ stats_arena_lextents_print(emitter_t *emitter, unsigned i, uint64_t uptime) {
COL_HDR(row, ndalloc_ps, "(#/sec)", right, 8, uint64)
COL_HDR(row, nrequests, NULL, right, 13, uint64)
COL_HDR(row, nrequests_ps, "(#/sec)", right, 8, uint64)
+ COL_HDR_DECLARE(prof_live_requested)
+ COL_HDR_DECLARE(prof_live_count)
+ COL_HDR_DECLARE(prof_accum_requested)
+ COL_HDR_DECLARE(prof_accum_count)
+ if (prof_stats_on) {
+ COL_HDR_INIT(row, prof_live_requested, NULL, right, 21, uint64)
+ COL_HDR_INIT(row, prof_live_count, NULL, right, 17, uint64)
+ COL_HDR_INIT(row, prof_accum_requested, NULL, right, 21, uint64)
+ COL_HDR_INIT(row, prof_accum_count, NULL, right, 17, uint64)
+ }
COL_HDR(row, curlextents, NULL, right, 13, size)
/* As with bins, we label the large extents table. */
@@ -496,16 +600,33 @@ stats_arena_lextents_print(emitter_t *emitter, unsigned i, uint64_t uptime) {
emitter_table_row(emitter, &header_row);
emitter_json_array_kv_begin(emitter, "lextents");
+ size_t stats_arenas_mib[CTL_MAX_DEPTH];
+ CTL_LEAF_PREPARE(stats_arenas_mib, 0, "stats.arenas");
+ stats_arenas_mib[2] = i;
+ CTL_LEAF_PREPARE(stats_arenas_mib, 3, "lextents");
+
+ size_t arenas_lextent_mib[CTL_MAX_DEPTH];
+ CTL_LEAF_PREPARE(arenas_lextent_mib, 0, "arenas.lextent");
+
+ size_t prof_stats_mib[CTL_MAX_DEPTH];
+ if (prof_stats_on) {
+ CTL_LEAF_PREPARE(prof_stats_mib, 0, "prof.stats.lextents");
+ }
+
for (j = 0, in_gap = false; j < nlextents; j++) {
uint64_t nmalloc, ndalloc, nrequests;
size_t lextent_size, curlextents;
+ prof_stats_t prof_live;
+ prof_stats_t prof_accum;
+
+ stats_arenas_mib[4] = j;
+ arenas_lextent_mib[2] = j;
+
+ CTL_LEAF(stats_arenas_mib, 5, "nmalloc", &nmalloc, uint64_t);
+ CTL_LEAF(stats_arenas_mib, 5, "ndalloc", &ndalloc, uint64_t);
+ CTL_LEAF(stats_arenas_mib, 5, "nrequests", &nrequests,
+ uint64_t);
- CTL_M2_M4_GET("stats.arenas.0.lextents.0.nmalloc", i, j,
- &nmalloc, uint64_t);
- CTL_M2_M4_GET("stats.arenas.0.lextents.0.ndalloc", i, j,
- &ndalloc, uint64_t);
- CTL_M2_M4_GET("stats.arenas.0.lextents.0.nrequests", i, j,
- &nrequests, uint64_t);
in_gap_prev = in_gap;
in_gap = (nrequests == 0);
@@ -514,11 +635,29 @@ stats_arena_lextents_print(emitter_t *emitter, unsigned i, uint64_t uptime) {
" ---\n");
}
- CTL_M2_GET("arenas.lextent.0.size", j, &lextent_size, size_t);
- CTL_M2_M4_GET("stats.arenas.0.lextents.0.curlextents", i, j,
- &curlextents, size_t);
+ CTL_LEAF(arenas_lextent_mib, 3, "size", &lextent_size, size_t);
+ CTL_LEAF(stats_arenas_mib, 5, "curlextents", &curlextents,
+ size_t);
+
+ if (prof_stats_on) {
+ prof_stats_mib[3] = j;
+ CTL_LEAF(prof_stats_mib, 4, "live", &prof_live,
+ prof_stats_t);
+ CTL_LEAF(prof_stats_mib, 4, "accum", &prof_accum,
+ prof_stats_t);
+ }
emitter_json_object_begin(emitter);
+ if (prof_stats_on) {
+ emitter_json_kv(emitter, "prof_live_requested",
+ emitter_type_uint64, &prof_live.req_sum);
+ emitter_json_kv(emitter, "prof_live_count",
+ emitter_type_uint64, &prof_live.count);
+ emitter_json_kv(emitter, "prof_accum_requested",
+ emitter_type_uint64, &prof_accum.req_sum);
+ emitter_json_kv(emitter, "prof_accum_count",
+ emitter_type_uint64, &prof_accum.count);
+ }
emitter_json_kv(emitter, "curlextents", emitter_type_size,
&curlextents);
emitter_json_object_end(emitter);
@@ -532,6 +671,13 @@ stats_arena_lextents_print(emitter_t *emitter, unsigned i, uint64_t uptime) {
col_ndalloc_ps.uint64_val = rate_per_second(ndalloc, uptime);
col_nrequests.uint64_val = nrequests;
col_nrequests_ps.uint64_val = rate_per_second(nrequests, uptime);
+ if (prof_stats_on) {
+ col_prof_live_requested.uint64_val = prof_live.req_sum;
+ col_prof_live_count.uint64_val = prof_live.count;
+ col_prof_accum_requested.uint64_val =
+ prof_accum.req_sum;
+ col_prof_accum_count.uint64_val = prof_accum.count;
+ }
col_curlextents.size_val = curlextents;
if (!in_gap) {
@@ -544,6 +690,7 @@ stats_arena_lextents_print(emitter_t *emitter, unsigned i, uint64_t uptime) {
}
}
+JEMALLOC_COLD
static void
stats_arena_extents_print(emitter_t *emitter, unsigned i) {
unsigned j;
@@ -570,22 +717,27 @@ stats_arena_extents_print(emitter_t *emitter, unsigned i) {
emitter_table_row(emitter, &header_row);
emitter_json_array_kv_begin(emitter, "extents");
+ size_t stats_arenas_mib[CTL_MAX_DEPTH];
+ CTL_LEAF_PREPARE(stats_arenas_mib, 0, "stats.arenas");
+ stats_arenas_mib[2] = i;
+ CTL_LEAF_PREPARE(stats_arenas_mib, 3, "extents");
+
in_gap = false;
for (j = 0; j < SC_NPSIZES; j++) {
size_t ndirty, nmuzzy, nretained, total, dirty_bytes,
muzzy_bytes, retained_bytes, total_bytes;
- CTL_M2_M4_GET("stats.arenas.0.extents.0.ndirty", i, j,
- &ndirty, size_t);
- CTL_M2_M4_GET("stats.arenas.0.extents.0.nmuzzy", i, j,
- &nmuzzy, size_t);
- CTL_M2_M4_GET("stats.arenas.0.extents.0.nretained", i, j,
- &nretained, size_t);
- CTL_M2_M4_GET("stats.arenas.0.extents.0.dirty_bytes", i, j,
- &dirty_bytes, size_t);
- CTL_M2_M4_GET("stats.arenas.0.extents.0.muzzy_bytes", i, j,
- &muzzy_bytes, size_t);
- CTL_M2_M4_GET("stats.arenas.0.extents.0.retained_bytes", i, j,
+ stats_arenas_mib[4] = j;
+
+ CTL_LEAF(stats_arenas_mib, 5, "ndirty", &ndirty, size_t);
+ CTL_LEAF(stats_arenas_mib, 5, "nmuzzy", &nmuzzy, size_t);
+ CTL_LEAF(stats_arenas_mib, 5, "nretained", &nretained, size_t);
+ CTL_LEAF(stats_arenas_mib, 5, "dirty_bytes", &dirty_bytes,
+ size_t);
+ CTL_LEAF(stats_arenas_mib, 5, "muzzy_bytes", &muzzy_bytes,
+ size_t);
+ CTL_LEAF(stats_arenas_mib, 5, "retained_bytes",
&retained_bytes, size_t);
+
total = ndirty + nmuzzy + nretained;
total_bytes = dirty_bytes + muzzy_bytes + retained_bytes;
@@ -633,6 +785,230 @@ stats_arena_extents_print(emitter_t *emitter, unsigned i) {
}
static void
+stats_arena_hpa_shard_print(emitter_t *emitter, unsigned i, uint64_t uptime) {
+ emitter_row_t header_row;
+ emitter_row_init(&header_row);
+ emitter_row_t row;
+ emitter_row_init(&row);
+
+ uint64_t npurge_passes;
+ uint64_t npurges;
+ uint64_t nhugifies;
+ uint64_t ndehugifies;
+
+ CTL_M2_GET("stats.arenas.0.hpa_shard.npurge_passes",
+ i, &npurge_passes, uint64_t);
+ CTL_M2_GET("stats.arenas.0.hpa_shard.npurges",
+ i, &npurges, uint64_t);
+ CTL_M2_GET("stats.arenas.0.hpa_shard.nhugifies",
+ i, &nhugifies, uint64_t);
+ CTL_M2_GET("stats.arenas.0.hpa_shard.ndehugifies",
+ i, &ndehugifies, uint64_t);
+
+ size_t npageslabs_huge;
+ size_t nactive_huge;
+ size_t ndirty_huge;
+
+ size_t npageslabs_nonhuge;
+ size_t nactive_nonhuge;
+ size_t ndirty_nonhuge;
+ size_t nretained_nonhuge;
+
+ size_t sec_bytes;
+ CTL_M2_GET("stats.arenas.0.hpa_sec_bytes", i, &sec_bytes, size_t);
+ emitter_kv(emitter, "sec_bytes", "Bytes in small extent cache",
+ emitter_type_size, &sec_bytes);
+
+ /* First, global stats. */
+ emitter_table_printf(emitter,
+ "HPA shard stats:\n"
+ " Purge passes: %" FMTu64 " (%" FMTu64 " / sec)\n"
+ " Purges: %" FMTu64 " (%" FMTu64 " / sec)\n"
+ " Hugeifies: %" FMTu64 " (%" FMTu64 " / sec)\n"
+ " Dehugifies: %" FMTu64 " (%" FMTu64 " / sec)\n"
+ "\n",
+ npurge_passes, rate_per_second(npurge_passes, uptime),
+ npurges, rate_per_second(npurges, uptime),
+ nhugifies, rate_per_second(nhugifies, uptime),
+ ndehugifies, rate_per_second(ndehugifies, uptime));
+
+ emitter_json_object_kv_begin(emitter, "hpa_shard");
+ emitter_json_kv(emitter, "npurge_passes", emitter_type_uint64,
+ &npurge_passes);
+ emitter_json_kv(emitter, "npurges", emitter_type_uint64,
+ &npurges);
+ emitter_json_kv(emitter, "nhugifies", emitter_type_uint64,
+ &nhugifies);
+ emitter_json_kv(emitter, "ndehugifies", emitter_type_uint64,
+ &ndehugifies);
+
+ /* Next, full slab stats. */
+ CTL_M2_GET("stats.arenas.0.hpa_shard.full_slabs.npageslabs_huge",
+ i, &npageslabs_huge, size_t);
+ CTL_M2_GET("stats.arenas.0.hpa_shard.full_slabs.nactive_huge",
+ i, &nactive_huge, size_t);
+ CTL_M2_GET("stats.arenas.0.hpa_shard.full_slabs.ndirty_huge",
+ i, &ndirty_huge, size_t);
+
+ CTL_M2_GET("stats.arenas.0.hpa_shard.full_slabs.npageslabs_nonhuge",
+ i, &npageslabs_nonhuge, size_t);
+ CTL_M2_GET("stats.arenas.0.hpa_shard.full_slabs.nactive_nonhuge",
+ i, &nactive_nonhuge, size_t);
+ CTL_M2_GET("stats.arenas.0.hpa_shard.full_slabs.ndirty_nonhuge",
+ i, &ndirty_nonhuge, size_t);
+ nretained_nonhuge = npageslabs_nonhuge * HUGEPAGE_PAGES
+ - nactive_nonhuge - ndirty_nonhuge;
+
+ emitter_table_printf(emitter,
+ " In full slabs:\n"
+ " npageslabs: %zu huge, %zu nonhuge\n"
+ " nactive: %zu huge, %zu nonhuge \n"
+ " ndirty: %zu huge, %zu nonhuge \n"
+ " nretained: 0 huge, %zu nonhuge \n",
+ npageslabs_huge, npageslabs_nonhuge,
+ nactive_huge, nactive_nonhuge,
+ ndirty_huge, ndirty_nonhuge,
+ nretained_nonhuge);
+
+ emitter_json_object_kv_begin(emitter, "full_slabs");
+ emitter_json_kv(emitter, "npageslabs_huge", emitter_type_size,
+ &npageslabs_huge);
+ emitter_json_kv(emitter, "nactive_huge", emitter_type_size,
+ &nactive_huge);
+ emitter_json_kv(emitter, "nactive_huge", emitter_type_size,
+ &nactive_huge);
+ emitter_json_kv(emitter, "npageslabs_nonhuge", emitter_type_size,
+ &npageslabs_nonhuge);
+ emitter_json_kv(emitter, "nactive_nonhuge", emitter_type_size,
+ &nactive_nonhuge);
+ emitter_json_kv(emitter, "ndirty_nonhuge", emitter_type_size,
+ &ndirty_nonhuge);
+ emitter_json_object_end(emitter); /* End "full_slabs" */
+
+ /* Next, empty slab stats. */
+ CTL_M2_GET("stats.arenas.0.hpa_shard.empty_slabs.npageslabs_huge",
+ i, &npageslabs_huge, size_t);
+ CTL_M2_GET("stats.arenas.0.hpa_shard.empty_slabs.nactive_huge",
+ i, &nactive_huge, size_t);
+ CTL_M2_GET("stats.arenas.0.hpa_shard.empty_slabs.ndirty_huge",
+ i, &ndirty_huge, size_t);
+
+ CTL_M2_GET("stats.arenas.0.hpa_shard.empty_slabs.npageslabs_nonhuge",
+ i, &npageslabs_nonhuge, size_t);
+ CTL_M2_GET("stats.arenas.0.hpa_shard.empty_slabs.nactive_nonhuge",
+ i, &nactive_nonhuge, size_t);
+ CTL_M2_GET("stats.arenas.0.hpa_shard.empty_slabs.ndirty_nonhuge",
+ i, &ndirty_nonhuge, size_t);
+ nretained_nonhuge = npageslabs_nonhuge * HUGEPAGE_PAGES
+ - nactive_nonhuge - ndirty_nonhuge;
+
+ emitter_table_printf(emitter,
+ " In empty slabs:\n"
+ " npageslabs: %zu huge, %zu nonhuge\n"
+ " nactive: %zu huge, %zu nonhuge \n"
+ " ndirty: %zu huge, %zu nonhuge \n"
+ " nretained: 0 huge, %zu nonhuge \n"
+ "\n",
+ npageslabs_huge, npageslabs_nonhuge,
+ nactive_huge, nactive_nonhuge,
+ ndirty_huge, ndirty_nonhuge,
+ nretained_nonhuge);
+
+ emitter_json_object_kv_begin(emitter, "empty_slabs");
+ emitter_json_kv(emitter, "npageslabs_huge", emitter_type_size,
+ &npageslabs_huge);
+ emitter_json_kv(emitter, "nactive_huge", emitter_type_size,
+ &nactive_huge);
+ emitter_json_kv(emitter, "nactive_huge", emitter_type_size,
+ &nactive_huge);
+ emitter_json_kv(emitter, "npageslabs_nonhuge", emitter_type_size,
+ &npageslabs_nonhuge);
+ emitter_json_kv(emitter, "nactive_nonhuge", emitter_type_size,
+ &nactive_nonhuge);
+ emitter_json_kv(emitter, "ndirty_nonhuge", emitter_type_size,
+ &ndirty_nonhuge);
+ emitter_json_object_end(emitter); /* End "empty_slabs" */
+
+ COL_HDR(row, size, NULL, right, 20, size)
+ COL_HDR(row, ind, NULL, right, 4, unsigned)
+ COL_HDR(row, npageslabs_huge, NULL, right, 16, size)
+ COL_HDR(row, nactive_huge, NULL, right, 16, size)
+ COL_HDR(row, ndirty_huge, NULL, right, 16, size)
+ COL_HDR(row, npageslabs_nonhuge, NULL, right, 20, size)
+ COL_HDR(row, nactive_nonhuge, NULL, right, 20, size)
+ COL_HDR(row, ndirty_nonhuge, NULL, right, 20, size)
+ COL_HDR(row, nretained_nonhuge, NULL, right, 20, size)
+
+ size_t stats_arenas_mib[CTL_MAX_DEPTH];
+ CTL_LEAF_PREPARE(stats_arenas_mib, 0, "stats.arenas");
+ stats_arenas_mib[2] = i;
+ CTL_LEAF_PREPARE(stats_arenas_mib, 3, "hpa_shard.nonfull_slabs");
+
+ emitter_table_row(emitter, &header_row);
+ emitter_json_array_kv_begin(emitter, "nonfull_slabs");
+ bool in_gap = false;
+ for (pszind_t j = 0; j < PSSET_NPSIZES && j < SC_NPSIZES; j++) {
+ stats_arenas_mib[5] = j;
+
+ CTL_LEAF(stats_arenas_mib, 6, "npageslabs_huge",
+ &npageslabs_huge, size_t);
+ CTL_LEAF(stats_arenas_mib, 6, "nactive_huge",
+ &nactive_huge, size_t);
+ CTL_LEAF(stats_arenas_mib, 6, "ndirty_huge",
+ &ndirty_huge, size_t);
+
+ CTL_LEAF(stats_arenas_mib, 6, "npageslabs_nonhuge",
+ &npageslabs_nonhuge, size_t);
+ CTL_LEAF(stats_arenas_mib, 6, "nactive_nonhuge",
+ &nactive_nonhuge, size_t);
+ CTL_LEAF(stats_arenas_mib, 6, "ndirty_nonhuge",
+ &ndirty_nonhuge, size_t);
+ nretained_nonhuge = npageslabs_nonhuge * HUGEPAGE_PAGES
+ - nactive_nonhuge - ndirty_nonhuge;
+
+ bool in_gap_prev = in_gap;
+ in_gap = (npageslabs_huge == 0 && npageslabs_nonhuge == 0);
+ if (in_gap_prev && !in_gap) {
+ emitter_table_printf(emitter,
+ " ---\n");
+ }
+
+ col_size.size_val = sz_pind2sz(j);
+ col_ind.size_val = j;
+ col_npageslabs_huge.size_val = npageslabs_huge;
+ col_nactive_huge.size_val = nactive_huge;
+ col_ndirty_huge.size_val = ndirty_huge;
+ col_npageslabs_nonhuge.size_val = npageslabs_nonhuge;
+ col_nactive_nonhuge.size_val = nactive_nonhuge;
+ col_ndirty_nonhuge.size_val = ndirty_nonhuge;
+ col_nretained_nonhuge.size_val = nretained_nonhuge;
+ if (!in_gap) {
+ emitter_table_row(emitter, &row);
+ }
+
+ emitter_json_object_begin(emitter);
+ emitter_json_kv(emitter, "npageslabs_huge", emitter_type_size,
+ &npageslabs_huge);
+ emitter_json_kv(emitter, "nactive_huge", emitter_type_size,
+ &nactive_huge);
+ emitter_json_kv(emitter, "ndirty_huge", emitter_type_size,
+ &ndirty_huge);
+ emitter_json_kv(emitter, "npageslabs_nonhuge", emitter_type_size,
+ &npageslabs_nonhuge);
+ emitter_json_kv(emitter, "nactive_nonhuge", emitter_type_size,
+ &nactive_nonhuge);
+ emitter_json_kv(emitter, "ndirty_nonhuge", emitter_type_size,
+ &ndirty_nonhuge);
+ emitter_json_object_end(emitter);
+ }
+ emitter_json_array_end(emitter); /* End "nonfull_slabs" */
+ emitter_json_object_end(emitter); /* End "hpa_shard" */
+ if (in_gap) {
+ emitter_table_printf(emitter, " ---\n");
+ }
+}
+
+static void
stats_arena_mutexes_print(emitter_t *emitter, unsigned arena_ind, uint64_t uptime) {
emitter_row_t row;
emitter_col_t col_name;
@@ -645,21 +1021,27 @@ stats_arena_mutexes_print(emitter_t *emitter, unsigned arena_ind, uint64_t uptim
emitter_json_object_kv_begin(emitter, "mutexes");
emitter_table_row(emitter, &row);
+ size_t stats_arenas_mib[CTL_MAX_DEPTH];
+ CTL_LEAF_PREPARE(stats_arenas_mib, 0, "stats.arenas");
+ stats_arenas_mib[2] = arena_ind;
+ CTL_LEAF_PREPARE(stats_arenas_mib, 3, "mutexes");
+
for (mutex_prof_arena_ind_t i = 0; i < mutex_prof_num_arena_mutexes;
i++) {
const char *name = arena_mutex_names[i];
emitter_json_object_kv_begin(emitter, name);
- mutex_stats_read_arena(arena_ind, i, name, &col_name, col64,
- col32, uptime);
+ mutex_stats_read_arena(stats_arenas_mib, 4, name, &col_name,
+ col64, col32, uptime);
mutex_stats_emit(emitter, &row, col64, col32);
emitter_json_object_end(emitter); /* Close the mutex dict. */
}
emitter_json_object_end(emitter); /* End "mutexes". */
}
+JEMALLOC_COLD
static void
stats_arena_print(emitter_t *emitter, unsigned i, bool bins, bool large,
- bool mutex, bool extents) {
+ bool mutex, bool extents, bool hpa) {
unsigned nthreads;
const char *dss;
ssize_t dirty_decay_ms, muzzy_decay_ms;
@@ -673,7 +1055,7 @@ stats_arena_print(emitter_t *emitter, unsigned i, bool bins, bool large,
size_t large_allocated;
uint64_t large_nmalloc, large_ndalloc, large_nrequests, large_nfills,
large_nflushes;
- size_t tcache_bytes, abandoned_vm;
+ size_t tcache_bytes, tcache_stashed_bytes, abandoned_vm;
uint64_t uptime;
CTL_GET("arenas.page", &page, size_t);
@@ -817,12 +1199,12 @@ stats_arena_print(emitter_t *emitter, unsigned i, bool bins, bool large,
COL(alloc_count_row, count_nmalloc, right, 16, title);
col_count_nmalloc.str_val = "nmalloc";
- COL(alloc_count_row, count_nmalloc_ps, right, 8, title);
+ COL(alloc_count_row, count_nmalloc_ps, right, 10, title);
col_count_nmalloc_ps.str_val = "(#/sec)";
COL(alloc_count_row, count_ndalloc, right, 16, title);
col_count_ndalloc.str_val = "ndalloc";
- COL(alloc_count_row, count_ndalloc_ps, right, 8, title);
+ COL(alloc_count_row, count_ndalloc_ps, right, 10, title);
col_count_ndalloc_ps.str_val = "(#/sec)";
COL(alloc_count_row, count_nrequests, right, 16, title);
@@ -962,6 +1344,7 @@ stats_arena_print(emitter_t *emitter, unsigned i, bool bins, bool large,
GET_AND_EMIT_MEM_STAT(internal)
GET_AND_EMIT_MEM_STAT(metadata_thp)
GET_AND_EMIT_MEM_STAT(tcache_bytes)
+ GET_AND_EMIT_MEM_STAT(tcache_stashed_bytes)
GET_AND_EMIT_MEM_STAT(resident)
GET_AND_EMIT_MEM_STAT(abandoned_vm)
GET_AND_EMIT_MEM_STAT(extent_avail)
@@ -979,8 +1362,12 @@ stats_arena_print(emitter_t *emitter, unsigned i, bool bins, bool large,
if (extents) {
stats_arena_extents_print(emitter, i);
}
+ if (hpa) {
+ stats_arena_hpa_shard_print(emitter, i, uptime);
+ }
}
+JEMALLOC_COLD
static void
stats_general_print(emitter_t *emitter) {
const char *cpv;
@@ -988,14 +1375,18 @@ stats_general_print(emitter_t *emitter) {
unsigned uv;
uint32_t u32v;
uint64_t u64v;
+ int64_t i64v;
ssize_t ssv, ssv2;
- size_t sv, bsz, usz, ssz, sssz, cpsz;
+ size_t sv, bsz, usz, u32sz, u64sz, i64sz, ssz, sssz, cpsz;
bsz = sizeof(bool);
usz = sizeof(unsigned);
ssz = sizeof(size_t);
sssz = sizeof(ssize_t);
cpsz = sizeof(const char *);
+ u32sz = sizeof(uint32_t);
+ i64sz = sizeof(int64_t);
+ u64sz = sizeof(uint64_t);
CTL_GET("version", &cpv, const char *);
emitter_kv(emitter, "version", "Version", emitter_type_string, &cpv);
@@ -1051,6 +1442,11 @@ stats_general_print(emitter_t *emitter) {
#define OPT_WRITE_UNSIGNED(name) \
OPT_WRITE(name, uv, usz, emitter_type_unsigned)
+#define OPT_WRITE_INT64(name) \
+ OPT_WRITE(name, i64v, i64sz, emitter_type_int64)
+#define OPT_WRITE_UINT64(name) \
+ OPT_WRITE(name, u64v, u64sz, emitter_type_uint64)
+
#define OPT_WRITE_SIZE_T(name) \
OPT_WRITE(name, sv, ssz, emitter_type_size)
#define OPT_WRITE_SSIZE_T(name) \
@@ -1066,13 +1462,43 @@ stats_general_print(emitter_t *emitter) {
OPT_WRITE_BOOL("abort")
OPT_WRITE_BOOL("abort_conf")
+ OPT_WRITE_BOOL("cache_oblivious")
OPT_WRITE_BOOL("confirm_conf")
OPT_WRITE_BOOL("retain")
OPT_WRITE_CHAR_P("dss")
OPT_WRITE_UNSIGNED("narenas")
OPT_WRITE_CHAR_P("percpu_arena")
OPT_WRITE_SIZE_T("oversize_threshold")
+ OPT_WRITE_BOOL("hpa")
+ OPT_WRITE_SIZE_T("hpa_slab_max_alloc")
+ OPT_WRITE_SIZE_T("hpa_hugification_threshold")
+ OPT_WRITE_UINT64("hpa_hugify_delay_ms")
+ OPT_WRITE_UINT64("hpa_min_purge_interval_ms")
+ if (je_mallctl("opt.hpa_dirty_mult", (void *)&u32v, &u32sz, NULL, 0)
+ == 0) {
+ /*
+ * We cheat a little and "know" the secret meaning of this
+ * representation.
+ */
+ if (u32v == (uint32_t)-1) {
+ const char *neg1 = "-1";
+ emitter_kv(emitter, "hpa_dirty_mult",
+ "opt.hpa_dirty_mult", emitter_type_string, &neg1);
+ } else {
+ char buf[FXP_BUF_SIZE];
+ fxp_print(u32v, buf);
+ const char *bufp = buf;
+ emitter_kv(emitter, "hpa_dirty_mult",
+ "opt.hpa_dirty_mult", emitter_type_string, &bufp);
+ }
+ }
+ OPT_WRITE_SIZE_T("hpa_sec_nshards")
+ OPT_WRITE_SIZE_T("hpa_sec_max_alloc")
+ OPT_WRITE_SIZE_T("hpa_sec_max_bytes")
+ OPT_WRITE_SIZE_T("hpa_sec_bytes_after_flush")
+ OPT_WRITE_SIZE_T("hpa_sec_batch_fill_extra")
OPT_WRITE_CHAR_P("metadata_thp")
+ OPT_WRITE_INT64("mutex_max_spin")
OPT_WRITE_BOOL_MUTABLE("background_thread", "background_thread")
OPT_WRITE_SSIZE_T_MUTABLE("dirty_decay_ms", "arenas.dirty_decay_ms")
OPT_WRITE_SSIZE_T_MUTABLE("muzzy_decay_ms", "arenas.muzzy_decay_ms")
@@ -1081,8 +1507,17 @@ stats_general_print(emitter_t *emitter) {
OPT_WRITE_BOOL("zero")
OPT_WRITE_BOOL("utrace")
OPT_WRITE_BOOL("xmalloc")
+ OPT_WRITE_BOOL("experimental_infallible_new")
OPT_WRITE_BOOL("tcache")
- OPT_WRITE_SSIZE_T("lg_tcache_max")
+ OPT_WRITE_SIZE_T("tcache_max")
+ OPT_WRITE_UNSIGNED("tcache_nslots_small_min")
+ OPT_WRITE_UNSIGNED("tcache_nslots_small_max")
+ OPT_WRITE_UNSIGNED("tcache_nslots_large")
+ OPT_WRITE_SSIZE_T("lg_tcache_nslots_mul")
+ OPT_WRITE_SIZE_T("tcache_gc_incr_bytes")
+ OPT_WRITE_SIZE_T("tcache_gc_delay_bytes")
+ OPT_WRITE_UNSIGNED("lg_tcache_flush_small_div")
+ OPT_WRITE_UNSIGNED("lg_tcache_flush_large_div")
OPT_WRITE_CHAR_P("thp")
OPT_WRITE_BOOL("prof")
OPT_WRITE_CHAR_P("prof_prefix")
@@ -1095,8 +1530,14 @@ stats_general_print(emitter_t *emitter) {
OPT_WRITE_BOOL("prof_gdump")
OPT_WRITE_BOOL("prof_final")
OPT_WRITE_BOOL("prof_leak")
+ OPT_WRITE_BOOL("prof_leak_error")
OPT_WRITE_BOOL("stats_print")
OPT_WRITE_CHAR_P("stats_print_opts")
+ OPT_WRITE_BOOL("stats_print")
+ OPT_WRITE_CHAR_P("stats_print_opts")
+ OPT_WRITE_INT64("stats_interval")
+ OPT_WRITE_CHAR_P("stats_interval_opts")
+ OPT_WRITE_CHAR_P("zero_realloc")
emitter_dict_end(emitter);
@@ -1167,38 +1608,41 @@ stats_general_print(emitter_t *emitter) {
"Maximum thread-cached size class", emitter_type_size, &sv);
}
- unsigned nbins;
- CTL_GET("arenas.nbins", &nbins, unsigned);
+ unsigned arenas_nbins;
+ CTL_GET("arenas.nbins", &arenas_nbins, unsigned);
emitter_kv(emitter, "nbins", "Number of bin size classes",
- emitter_type_unsigned, &nbins);
+ emitter_type_unsigned, &arenas_nbins);
- unsigned nhbins;
- CTL_GET("arenas.nhbins", &nhbins, unsigned);
+ unsigned arenas_nhbins;
+ CTL_GET("arenas.nhbins", &arenas_nhbins, unsigned);
emitter_kv(emitter, "nhbins", "Number of thread-cache bin size classes",
- emitter_type_unsigned, &nhbins);
+ emitter_type_unsigned, &arenas_nhbins);
/*
* We do enough mallctls in a loop that we actually want to omit them
* (not just omit the printing).
*/
- if (emitter->output == emitter_output_json) {
+ if (emitter_outputs_json(emitter)) {
emitter_json_array_kv_begin(emitter, "bin");
- for (unsigned i = 0; i < nbins; i++) {
+ size_t arenas_bin_mib[CTL_MAX_DEPTH];
+ CTL_LEAF_PREPARE(arenas_bin_mib, 0, "arenas.bin");
+ for (unsigned i = 0; i < arenas_nbins; i++) {
+ arenas_bin_mib[2] = i;
emitter_json_object_begin(emitter);
- CTL_M2_GET("arenas.bin.0.size", i, &sv, size_t);
+ CTL_LEAF(arenas_bin_mib, 3, "size", &sv, size_t);
emitter_json_kv(emitter, "size", emitter_type_size,
&sv);
- CTL_M2_GET("arenas.bin.0.nregs", i, &u32v, uint32_t);
+ CTL_LEAF(arenas_bin_mib, 3, "nregs", &u32v, uint32_t);
emitter_json_kv(emitter, "nregs", emitter_type_uint32,
&u32v);
- CTL_M2_GET("arenas.bin.0.slab_size", i, &sv, size_t);
+ CTL_LEAF(arenas_bin_mib, 3, "slab_size", &sv, size_t);
emitter_json_kv(emitter, "slab_size", emitter_type_size,
&sv);
- CTL_M2_GET("arenas.bin.0.nshards", i, &u32v, uint32_t);
+ CTL_LEAF(arenas_bin_mib, 3, "nshards", &u32v, uint32_t);
emitter_json_kv(emitter, "nshards", emitter_type_uint32,
&u32v);
@@ -1212,12 +1656,15 @@ stats_general_print(emitter_t *emitter) {
emitter_kv(emitter, "nlextents", "Number of large size classes",
emitter_type_unsigned, &nlextents);
- if (emitter->output == emitter_output_json) {
+ if (emitter_outputs_json(emitter)) {
emitter_json_array_kv_begin(emitter, "lextent");
+ size_t arenas_lextent_mib[CTL_MAX_DEPTH];
+ CTL_LEAF_PREPARE(arenas_lextent_mib, 0, "arenas.lextent");
for (unsigned i = 0; i < nlextents; i++) {
+ arenas_lextent_mib[2] = i;
emitter_json_object_begin(emitter);
- CTL_M2_GET("arenas.lextent.0.size", i, &sv, size_t);
+ CTL_LEAF(arenas_lextent_mib, 3, "size", &sv, size_t);
emitter_json_kv(emitter, "size", emitter_type_size,
&sv);
@@ -1229,9 +1676,10 @@ stats_general_print(emitter_t *emitter) {
emitter_json_object_end(emitter); /* Close "arenas" */
}
+JEMALLOC_COLD
static void
stats_print_helper(emitter_t *emitter, bool merged, bool destroyed,
- bool unmerged, bool bins, bool large, bool mutex, bool extents) {
+ bool unmerged, bool bins, bool large, bool mutex, bool extents, bool hpa) {
/*
* These should be deleted. We keep them around for a while, to aid in
* the transition to the emitter code.
@@ -1239,6 +1687,7 @@ stats_print_helper(emitter_t *emitter, bool merged, bool destroyed,
size_t allocated, active, metadata, metadata_thp, resident, mapped,
retained;
size_t num_background_threads;
+ size_t zero_reallocs;
uint64_t background_thread_num_runs, background_thread_run_interval;
CTL_GET("stats.allocated", &allocated, size_t);
@@ -1249,6 +1698,8 @@ stats_print_helper(emitter_t *emitter, bool merged, bool destroyed,
CTL_GET("stats.mapped", &mapped, size_t);
CTL_GET("stats.retained", &retained, size_t);
+ CTL_GET("stats.zero_reallocs", &zero_reallocs, size_t);
+
if (have_background_thread) {
CTL_GET("stats.background_thread.num_threads",
&num_background_threads, size_t);
@@ -1272,12 +1723,18 @@ stats_print_helper(emitter_t *emitter, bool merged, bool destroyed,
emitter_json_kv(emitter, "resident", emitter_type_size, &resident);
emitter_json_kv(emitter, "mapped", emitter_type_size, &mapped);
emitter_json_kv(emitter, "retained", emitter_type_size, &retained);
+ emitter_json_kv(emitter, "zero_reallocs", emitter_type_size,
+ &zero_reallocs);
emitter_table_printf(emitter, "Allocated: %zu, active: %zu, "
"metadata: %zu (n_thp %zu), resident: %zu, mapped: %zu, "
"retained: %zu\n", allocated, active, metadata, metadata_thp,
resident, mapped, retained);
+ /* Strange behaviors */
+ emitter_table_printf(emitter,
+ "Count of realloc(non-null-ptr, 0) calls: %zu\n", zero_reallocs);
+
/* Background thread stats. */
emitter_json_object_kv_begin(emitter, "background_thread");
emitter_json_kv(emitter, "num_threads", emitter_type_size,
@@ -1308,9 +1765,11 @@ stats_print_helper(emitter_t *emitter, bool merged, bool destroyed,
CTL_M2_GET("stats.arenas.0.uptime", 0, &uptime, uint64_t);
+ size_t stats_mutexes_mib[CTL_MAX_DEPTH];
+ CTL_LEAF_PREPARE(stats_mutexes_mib, 0, "stats.mutexes");
for (int i = 0; i < mutex_prof_num_global_mutexes; i++) {
- mutex_stats_read_global(global_mutex_names[i], &name,
- col64, col32, uptime);
+ mutex_stats_read_global(stats_mutexes_mib, 2,
+ global_mutex_names[i], &name, col64, col32, uptime);
emitter_json_object_kv_begin(emitter, global_mutex_names[i]);
mutex_stats_emit(emitter, &row, col64, col32);
emitter_json_object_end(emitter);
@@ -1355,7 +1814,7 @@ stats_print_helper(emitter_t *emitter, bool merged, bool destroyed,
emitter_table_printf(emitter, "Merged arenas stats:\n");
emitter_json_object_kv_begin(emitter, "merged");
stats_arena_print(emitter, MALLCTL_ARENAS_ALL, bins,
- large, mutex, extents);
+ large, mutex, extents, hpa);
emitter_json_object_end(emitter); /* Close "merged". */
}
@@ -1366,7 +1825,7 @@ stats_print_helper(emitter_t *emitter, bool merged, bool destroyed,
"Destroyed arenas stats:\n");
emitter_json_object_kv_begin(emitter, "destroyed");
stats_arena_print(emitter, MALLCTL_ARENAS_DESTROYED,
- bins, large, mutex, extents);
+ bins, large, mutex, extents, hpa);
emitter_json_object_end(emitter); /* Close "destroyed". */
}
@@ -1382,7 +1841,7 @@ stats_print_helper(emitter_t *emitter, bool merged, bool destroyed,
emitter_table_printf(emitter,
"arenas[%s]:\n", arena_ind_str);
stats_arena_print(emitter, i, bins,
- large, mutex, extents);
+ large, mutex, extents, hpa);
/* Close "<arena-ind>". */
emitter_json_object_end(emitter);
}
@@ -1393,8 +1852,7 @@ stats_print_helper(emitter_t *emitter, bool merged, bool destroyed,
}
void
-stats_print(void (*write_cb)(void *, const char *), void *cbopaque,
- const char *opts) {
+stats_print(write_cb_t *write_cb, void *cbopaque, const char *opts) {
int err;
uint64_t epoch;
size_t u64sz;
@@ -1437,8 +1895,8 @@ stats_print(void (*write_cb)(void *, const char *), void *cbopaque,
emitter_t emitter;
emitter_init(&emitter,
- json ? emitter_output_json : emitter_output_table, write_cb,
- cbopaque);
+ json ? emitter_output_json_compact : emitter_output_table,
+ write_cb, cbopaque);
emitter_begin(&emitter);
emitter_table_printf(&emitter, "___ Begin jemalloc statistics ___\n");
emitter_json_object_kv_begin(&emitter, "jemalloc");
@@ -1448,10 +1906,68 @@ stats_print(void (*write_cb)(void *, const char *), void *cbopaque,
}
if (config_stats) {
stats_print_helper(&emitter, merged, destroyed, unmerged,
- bins, large, mutex, extents);
+ bins, large, mutex, extents, hpa);
}
emitter_json_object_end(&emitter); /* Closes the "jemalloc" dict. */
emitter_table_printf(&emitter, "--- End jemalloc statistics ---\n");
emitter_end(&emitter);
}
+
+uint64_t
+stats_interval_new_event_wait(tsd_t *tsd) {
+ return stats_interval_accum_batch;
+}
+
+uint64_t
+stats_interval_postponed_event_wait(tsd_t *tsd) {
+ return TE_MIN_START_WAIT;
+}
+
+void
+stats_interval_event_handler(tsd_t *tsd, uint64_t elapsed) {
+ assert(elapsed > 0 && elapsed != TE_INVALID_ELAPSED);
+ if (counter_accum(tsd_tsdn(tsd), &stats_interval_accumulated,
+ elapsed)) {
+ je_malloc_stats_print(NULL, NULL, opt_stats_interval_opts);
+ }
+}
+
+bool
+stats_boot(void) {
+ uint64_t stats_interval;
+ if (opt_stats_interval < 0) {
+ assert(opt_stats_interval == -1);
+ stats_interval = 0;
+ stats_interval_accum_batch = 0;
+ } else{
+ /* See comments in stats.h */
+ stats_interval = (opt_stats_interval > 0) ?
+ opt_stats_interval : 1;
+ uint64_t batch = stats_interval >>
+ STATS_INTERVAL_ACCUM_LG_BATCH_SIZE;
+ if (batch > STATS_INTERVAL_ACCUM_BATCH_MAX) {
+ batch = STATS_INTERVAL_ACCUM_BATCH_MAX;
+ } else if (batch == 0) {
+ batch = 1;
+ }
+ stats_interval_accum_batch = batch;
+ }
+
+ return counter_accum_init(&stats_interval_accumulated, stats_interval);
+}
+
+void
+stats_prefork(tsdn_t *tsdn) {
+ counter_prefork(tsdn, &stats_interval_accumulated);
+}
+
+void
+stats_postfork_parent(tsdn_t *tsdn) {
+ counter_postfork_parent(tsdn, &stats_interval_accumulated);
+}
+
+void
+stats_postfork_child(tsdn_t *tsdn) {
+ counter_postfork_child(tsdn, &stats_interval_accumulated);
+}
diff --git a/contrib/jemalloc/src/sz.c b/contrib/jemalloc/src/sz.c
index 8633fb05005e..d3115dda7c96 100644
--- a/contrib/jemalloc/src/sz.c
+++ b/contrib/jemalloc/src/sz.c
@@ -1,8 +1,57 @@
#include "jemalloc/internal/jemalloc_preamble.h"
+#include "jemalloc/internal/jemalloc_internal_includes.h"
#include "jemalloc/internal/sz.h"
JEMALLOC_ALIGNED(CACHELINE)
size_t sz_pind2sz_tab[SC_NPSIZES+1];
+size_t sz_large_pad;
+
+size_t
+sz_psz_quantize_floor(size_t size) {
+ size_t ret;
+ pszind_t pind;
+
+ assert(size > 0);
+ assert((size & PAGE_MASK) == 0);
+
+ pind = sz_psz2ind(size - sz_large_pad + 1);
+ if (pind == 0) {
+ /*
+ * Avoid underflow. This short-circuit would also do the right
+ * thing for all sizes in the range for which there are
+ * PAGE-spaced size classes, but it's simplest to just handle
+ * the one case that would cause erroneous results.
+ */
+ return size;
+ }
+ ret = sz_pind2sz(pind - 1) + sz_large_pad;
+ assert(ret <= size);
+ return ret;
+}
+
+size_t
+sz_psz_quantize_ceil(size_t size) {
+ size_t ret;
+
+ assert(size > 0);
+ assert(size - sz_large_pad <= SC_LARGE_MAXCLASS);
+ assert((size & PAGE_MASK) == 0);
+
+ ret = sz_psz_quantize_floor(size);
+ if (ret < size) {
+ /*
+ * Skip a quantization that may have an adequately large extent,
+ * because under-sized extents may be mixed in. This only
+ * happens when an unusual size is requested, i.e. for aligned
+ * allocation, and is just one of several places where linear
+ * search would potentially find sufficiently aligned available
+ * memory somewhere lower.
+ */
+ ret = sz_pind2sz(sz_psz2ind(ret - sz_large_pad + 1)) +
+ sz_large_pad;
+ }
+ return ret;
+}
static void
sz_boot_pind2sz_tab(const sc_data_t *sc_data) {
@@ -57,7 +106,8 @@ sz_boot_size2index_tab(const sc_data_t *sc_data) {
}
void
-sz_boot(const sc_data_t *sc_data) {
+sz_boot(const sc_data_t *sc_data, bool cache_oblivious) {
+ sz_large_pad = cache_oblivious ? PAGE : 0;
sz_boot_pind2sz_tab(sc_data);
sz_boot_index2size_tab(sc_data);
sz_boot_size2index_tab(sc_data);
diff --git a/contrib/jemalloc/src/tcache.c b/contrib/jemalloc/src/tcache.c
index 50099a9f2cdc..fa16732e4abf 100644
--- a/contrib/jemalloc/src/tcache.c
+++ b/contrib/jemalloc/src/tcache.c
@@ -1,22 +1,71 @@
-#define JEMALLOC_TCACHE_C_
#include "jemalloc/internal/jemalloc_preamble.h"
#include "jemalloc/internal/jemalloc_internal_includes.h"
#include "jemalloc/internal/assert.h"
#include "jemalloc/internal/mutex.h"
#include "jemalloc/internal/safety_check.h"
+#include "jemalloc/internal/san.h"
#include "jemalloc/internal/sc.h"
/******************************************************************************/
/* Data. */
-bool opt_tcache = true;
-ssize_t opt_lg_tcache_max = LG_TCACHE_MAXCLASS_DEFAULT;
+bool opt_tcache = true;
+
+/* tcache_maxclass is set to 32KB by default. */
+size_t opt_tcache_max = ((size_t)1) << 15;
+
+/* Reasonable defaults for min and max values. */
+unsigned opt_tcache_nslots_small_min = 20;
+unsigned opt_tcache_nslots_small_max = 200;
+unsigned opt_tcache_nslots_large = 20;
+
+/*
+ * We attempt to make the number of slots in a tcache bin for a given size class
+ * equal to the number of objects in a slab times some multiplier. By default,
+ * the multiplier is 2 (i.e. we set the maximum number of objects in the tcache
+ * to twice the number of objects in a slab).
+ * This is bounded by some other constraints as well, like the fact that it
+ * must be even, must be less than opt_tcache_nslots_small_max, etc..
+ */
+ssize_t opt_lg_tcache_nslots_mul = 1;
+
+/*
+ * Number of allocation bytes between tcache incremental GCs. Again, this
+ * default just seems to work well; more tuning is possible.
+ */
+size_t opt_tcache_gc_incr_bytes = 65536;
+
+/*
+ * With default settings, we may end up flushing small bins frequently with
+ * small flush amounts. To limit this tendency, we can set a number of bytes to
+ * "delay" by. If we try to flush N M-byte items, we decrease that size-class's
+ * delay by N * M. So, if delay is 1024 and we're looking at the 64-byte size
+ * class, we won't do any flushing until we've been asked to flush 1024/64 == 16
+ * items. This can happen in any configuration (i.e. being asked to flush 16
+ * items once, or 4 items 4 times).
+ *
+ * Practically, this is stored as a count of items in a uint8_t, so the
+ * effective maximum value for a size class is 255 * sz.
+ */
+size_t opt_tcache_gc_delay_bytes = 0;
+
+/*
+ * When a cache bin is flushed because it's full, how much of it do we flush?
+ * By default, we flush half the maximum number of items.
+ */
+unsigned opt_lg_tcache_flush_small_div = 1;
+unsigned opt_lg_tcache_flush_large_div = 1;
cache_bin_info_t *tcache_bin_info;
-static unsigned stack_nelms; /* Total stack elms per tcache. */
+/* Total stack size required (per tcache). Include the padding above. */
+static size_t tcache_bin_alloc_size;
+static size_t tcache_bin_alloc_alignment;
+
+/* Number of cache bins enabled, including both large and small. */
unsigned nhbins;
+/* Max size class to be cached (can be small or large). */
size_t tcache_maxclass;
tcaches_t *tcaches;
@@ -37,358 +86,551 @@ tcache_salloc(tsdn_t *tsdn, const void *ptr) {
return arena_salloc(tsdn, ptr);
}
-void
-tcache_event_hard(tsd_t *tsd, tcache_t *tcache) {
- szind_t binind = tcache->next_gc_bin;
+uint64_t
+tcache_gc_new_event_wait(tsd_t *tsd) {
+ return opt_tcache_gc_incr_bytes;
+}
+
+uint64_t
+tcache_gc_postponed_event_wait(tsd_t *tsd) {
+ return TE_MIN_START_WAIT;
+}
+
+uint64_t
+tcache_gc_dalloc_new_event_wait(tsd_t *tsd) {
+ return opt_tcache_gc_incr_bytes;
+}
- cache_bin_t *tbin;
- if (binind < SC_NBINS) {
- tbin = tcache_small_bin_get(tcache, binind);
+uint64_t
+tcache_gc_dalloc_postponed_event_wait(tsd_t *tsd) {
+ return TE_MIN_START_WAIT;
+}
+
+static uint8_t
+tcache_gc_item_delay_compute(szind_t szind) {
+ assert(szind < SC_NBINS);
+ size_t sz = sz_index2size(szind);
+ size_t item_delay = opt_tcache_gc_delay_bytes / sz;
+ size_t delay_max = ZU(1)
+ << (sizeof(((tcache_slow_t *)NULL)->bin_flush_delay_items[0]) * 8);
+ if (item_delay >= delay_max) {
+ item_delay = delay_max - 1;
+ }
+ return (uint8_t)item_delay;
+}
+
+static void
+tcache_gc_small(tsd_t *tsd, tcache_slow_t *tcache_slow, tcache_t *tcache,
+ szind_t szind) {
+ /* Aim to flush 3/4 of items below low-water. */
+ assert(szind < SC_NBINS);
+
+ cache_bin_t *cache_bin = &tcache->bins[szind];
+ cache_bin_sz_t ncached = cache_bin_ncached_get_local(cache_bin,
+ &tcache_bin_info[szind]);
+ cache_bin_sz_t low_water = cache_bin_low_water_get(cache_bin,
+ &tcache_bin_info[szind]);
+ assert(!tcache_slow->bin_refilled[szind]);
+
+ size_t nflush = low_water - (low_water >> 2);
+ if (nflush < tcache_slow->bin_flush_delay_items[szind]) {
+ /* Workaround for a conversion warning. */
+ uint8_t nflush_uint8 = (uint8_t)nflush;
+ assert(sizeof(tcache_slow->bin_flush_delay_items[0]) ==
+ sizeof(nflush_uint8));
+ tcache_slow->bin_flush_delay_items[szind] -= nflush_uint8;
+ return;
} else {
- tbin = tcache_large_bin_get(tcache, binind);
+ tcache_slow->bin_flush_delay_items[szind]
+ = tcache_gc_item_delay_compute(szind);
}
- if (tbin->low_water > 0) {
- /*
- * Flush (ceiling) 3/4 of the objects below the low water mark.
- */
- if (binind < SC_NBINS) {
- tcache_bin_flush_small(tsd, tcache, tbin, binind,
- tbin->ncached - tbin->low_water + (tbin->low_water
- >> 2));
- /*
- * Reduce fill count by 2X. Limit lg_fill_div such that
- * the fill count is always at least 1.
- */
- cache_bin_info_t *tbin_info = &tcache_bin_info[binind];
- if ((tbin_info->ncached_max >>
- (tcache->lg_fill_div[binind] + 1)) >= 1) {
- tcache->lg_fill_div[binind]++;
- }
+
+ tcache_bin_flush_small(tsd, tcache, cache_bin, szind,
+ (unsigned)(ncached - nflush));
+
+ /*
+ * Reduce fill count by 2X. Limit lg_fill_div such that
+ * the fill count is always at least 1.
+ */
+ if ((cache_bin_info_ncached_max(&tcache_bin_info[szind])
+ >> (tcache_slow->lg_fill_div[szind] + 1)) >= 1) {
+ tcache_slow->lg_fill_div[szind]++;
+ }
+}
+
+static void
+tcache_gc_large(tsd_t *tsd, tcache_slow_t *tcache_slow, tcache_t *tcache,
+ szind_t szind) {
+ /* Like the small GC; flush 3/4 of untouched items. */
+ assert(szind >= SC_NBINS);
+ cache_bin_t *cache_bin = &tcache->bins[szind];
+ cache_bin_sz_t ncached = cache_bin_ncached_get_local(cache_bin,
+ &tcache_bin_info[szind]);
+ cache_bin_sz_t low_water = cache_bin_low_water_get(cache_bin,
+ &tcache_bin_info[szind]);
+ tcache_bin_flush_large(tsd, tcache, cache_bin, szind,
+ (unsigned)(ncached - low_water + (low_water >> 2)));
+}
+
+static void
+tcache_event(tsd_t *tsd) {
+ tcache_t *tcache = tcache_get(tsd);
+ if (tcache == NULL) {
+ return;
+ }
+
+ tcache_slow_t *tcache_slow = tsd_tcache_slowp_get(tsd);
+ szind_t szind = tcache_slow->next_gc_bin;
+ bool is_small = (szind < SC_NBINS);
+ cache_bin_t *cache_bin = &tcache->bins[szind];
+
+ tcache_bin_flush_stashed(tsd, tcache, cache_bin, szind, is_small);
+
+ cache_bin_sz_t low_water = cache_bin_low_water_get(cache_bin,
+ &tcache_bin_info[szind]);
+ if (low_water > 0) {
+ if (is_small) {
+ tcache_gc_small(tsd, tcache_slow, tcache, szind);
} else {
- tcache_bin_flush_large(tsd, tbin, binind, tbin->ncached
- - tbin->low_water + (tbin->low_water >> 2), tcache);
+ tcache_gc_large(tsd, tcache_slow, tcache, szind);
}
- } else if (tbin->low_water < 0) {
+ } else if (is_small && tcache_slow->bin_refilled[szind]) {
+ assert(low_water == 0);
/*
* Increase fill count by 2X for small bins. Make sure
* lg_fill_div stays greater than 0.
*/
- if (binind < SC_NBINS && tcache->lg_fill_div[binind] > 1) {
- tcache->lg_fill_div[binind]--;
+ if (tcache_slow->lg_fill_div[szind] > 1) {
+ tcache_slow->lg_fill_div[szind]--;
}
+ tcache_slow->bin_refilled[szind] = false;
}
- tbin->low_water = tbin->ncached;
+ cache_bin_low_water_set(cache_bin);
- tcache->next_gc_bin++;
- if (tcache->next_gc_bin == nhbins) {
- tcache->next_gc_bin = 0;
+ tcache_slow->next_gc_bin++;
+ if (tcache_slow->next_gc_bin == nhbins) {
+ tcache_slow->next_gc_bin = 0;
}
}
+void
+tcache_gc_event_handler(tsd_t *tsd, uint64_t elapsed) {
+ assert(elapsed == TE_INVALID_ELAPSED);
+ tcache_event(tsd);
+}
+
+void
+tcache_gc_dalloc_event_handler(tsd_t *tsd, uint64_t elapsed) {
+ assert(elapsed == TE_INVALID_ELAPSED);
+ tcache_event(tsd);
+}
+
void *
-tcache_alloc_small_hard(tsdn_t *tsdn, arena_t *arena, tcache_t *tcache,
- cache_bin_t *tbin, szind_t binind, bool *tcache_success) {
+tcache_alloc_small_hard(tsdn_t *tsdn, arena_t *arena,
+ tcache_t *tcache, cache_bin_t *cache_bin, szind_t binind,
+ bool *tcache_success) {
+ tcache_slow_t *tcache_slow = tcache->tcache_slow;
void *ret;
- assert(tcache->arena != NULL);
- arena_tcache_fill_small(tsdn, arena, tcache, tbin, binind,
- config_prof ? tcache->prof_accumbytes : 0);
- if (config_prof) {
- tcache->prof_accumbytes = 0;
- }
- ret = cache_bin_alloc_easy(tbin, tcache_success);
+ assert(tcache_slow->arena != NULL);
+ unsigned nfill = cache_bin_info_ncached_max(&tcache_bin_info[binind])
+ >> tcache_slow->lg_fill_div[binind];
+ arena_cache_bin_fill_small(tsdn, arena, cache_bin,
+ &tcache_bin_info[binind], binind, nfill);
+ tcache_slow->bin_refilled[binind] = true;
+ ret = cache_bin_alloc(cache_bin, tcache_success);
return ret;
}
-/* Enabled with --enable-extra-size-check. */
+static const void *
+tcache_bin_flush_ptr_getter(void *arr_ctx, size_t ind) {
+ cache_bin_ptr_array_t *arr = (cache_bin_ptr_array_t *)arr_ctx;
+ return arr->ptr[ind];
+}
+
static void
-tbin_extents_lookup_size_check(tsdn_t *tsdn, cache_bin_t *tbin, szind_t binind,
- size_t nflush, extent_t **extents){
- rtree_ctx_t rtree_ctx_fallback;
- rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);
+tcache_bin_flush_metadata_visitor(void *szind_sum_ctx,
+ emap_full_alloc_ctx_t *alloc_ctx) {
+ size_t *szind_sum = (size_t *)szind_sum_ctx;
+ *szind_sum -= alloc_ctx->szind;
+ util_prefetch_write_range(alloc_ctx->edata, sizeof(edata_t));
+}
- /*
- * Verify that the items in the tcache all have the correct size; this
- * is useful for catching sized deallocation bugs, also to fail early
- * instead of corrupting metadata. Since this can be turned on for opt
- * builds, avoid the branch in the loop.
- */
- szind_t szind;
- size_t sz_sum = binind * nflush;
- for (unsigned i = 0 ; i < nflush; i++) {
- rtree_extent_szind_read(tsdn, &extents_rtree,
- rtree_ctx, (uintptr_t)*(tbin->avail - 1 - i), true,
- &extents[i], &szind);
- sz_sum -= szind;
- }
- if (sz_sum != 0) {
- safety_check_fail("<jemalloc>: size mismatch in thread cache "
- "detected, likely caused by sized deallocation bugs by "
- "application. Abort.\n");
- abort();
+JEMALLOC_NOINLINE static void
+tcache_bin_flush_size_check_fail(cache_bin_ptr_array_t *arr, szind_t szind,
+ size_t nptrs, emap_batch_lookup_result_t *edatas) {
+ bool found_mismatch = false;
+ for (size_t i = 0; i < nptrs; i++) {
+ szind_t true_szind = edata_szind_get(edatas[i].edata);
+ if (true_szind != szind) {
+ found_mismatch = true;
+ safety_check_fail_sized_dealloc(
+ /* current_dealloc */ false,
+ /* ptr */ tcache_bin_flush_ptr_getter(arr, i),
+ /* true_size */ sz_index2size(true_szind),
+ /* input_size */ sz_index2size(szind));
+ }
}
+ assert(found_mismatch);
}
-void
-tcache_bin_flush_small(tsd_t *tsd, tcache_t *tcache, cache_bin_t *tbin,
- szind_t binind, unsigned rem) {
- bool merged_stats = false;
-
- assert(binind < SC_NBINS);
- assert((cache_bin_sz_t)rem <= tbin->ncached);
+static void
+tcache_bin_flush_edatas_lookup(tsd_t *tsd, cache_bin_ptr_array_t *arr,
+ szind_t binind, size_t nflush, emap_batch_lookup_result_t *edatas) {
- arena_t *arena = tcache->arena;
- assert(arena != NULL);
- unsigned nflush = tbin->ncached - rem;
- VARIABLE_ARRAY(extent_t *, item_extent, nflush);
+ /*
+ * This gets compiled away when config_opt_safety_checks is false.
+ * Checks for sized deallocation bugs, failing early rather than
+ * corrupting metadata.
+ */
+ size_t szind_sum = binind * nflush;
+ emap_edata_lookup_batch(tsd, &arena_emap_global, nflush,
+ &tcache_bin_flush_ptr_getter, (void *)arr,
+ &tcache_bin_flush_metadata_visitor, (void *)&szind_sum,
+ edatas);
+ if (config_opt_safety_checks && unlikely(szind_sum != 0)) {
+ tcache_bin_flush_size_check_fail(arr, binind, nflush, edatas);
+ }
+}
- /* Look up extent once per item. */
- if (config_opt_safety_checks) {
- tbin_extents_lookup_size_check(tsd_tsdn(tsd), tbin, binind,
- nflush, item_extent);
+JEMALLOC_ALWAYS_INLINE bool
+tcache_bin_flush_match(edata_t *edata, unsigned cur_arena_ind,
+ unsigned cur_binshard, bool small) {
+ if (small) {
+ return edata_arena_ind_get(edata) == cur_arena_ind
+ && edata_binshard_get(edata) == cur_binshard;
} else {
- for (unsigned i = 0 ; i < nflush; i++) {
- item_extent[i] = iealloc(tsd_tsdn(tsd),
- *(tbin->avail - 1 - i));
- }
+ return edata_arena_ind_get(edata) == cur_arena_ind;
}
- while (nflush > 0) {
- /* Lock the arena bin associated with the first object. */
- extent_t *extent = item_extent[0];
- unsigned bin_arena_ind = extent_arena_ind_get(extent);
- arena_t *bin_arena = arena_get(tsd_tsdn(tsd), bin_arena_ind,
- false);
- unsigned binshard = extent_binshard_get(extent);
- assert(binshard < bin_infos[binind].n_shards);
- bin_t *bin = &bin_arena->bins[binind].bin_shards[binshard];
-
- if (config_prof && bin_arena == arena) {
- if (arena_prof_accum(tsd_tsdn(tsd), arena,
- tcache->prof_accumbytes)) {
- prof_idump(tsd_tsdn(tsd));
- }
- tcache->prof_accumbytes = 0;
- }
+}
- malloc_mutex_lock(tsd_tsdn(tsd), &bin->lock);
- if (config_stats && bin_arena == arena && !merged_stats) {
- merged_stats = true;
- bin->stats.nflushes++;
- bin->stats.nrequests += tbin->tstats.nrequests;
- tbin->tstats.nrequests = 0;
- }
- unsigned ndeferred = 0;
- for (unsigned i = 0; i < nflush; i++) {
- void *ptr = *(tbin->avail - 1 - i);
- extent = item_extent[i];
- assert(ptr != NULL && extent != NULL);
-
- if (extent_arena_ind_get(extent) == bin_arena_ind
- && extent_binshard_get(extent) == binshard) {
- arena_dalloc_bin_junked_locked(tsd_tsdn(tsd),
- bin_arena, bin, binind, extent, ptr);
- } else {
- /*
- * This object was allocated via a different
- * arena bin than the one that is currently
- * locked. Stash the object, so that it can be
- * handled in a future pass.
- */
- *(tbin->avail - 1 - ndeferred) = ptr;
- item_extent[ndeferred] = extent;
- ndeferred++;
- }
- }
- malloc_mutex_unlock(tsd_tsdn(tsd), &bin->lock);
- arena_decay_ticks(tsd_tsdn(tsd), bin_arena, nflush - ndeferred);
- nflush = ndeferred;
- }
- if (config_stats && !merged_stats) {
- /*
- * The flush loop didn't happen to flush to this thread's
- * arena, so the stats didn't get merged. Manually do so now.
- */
- unsigned binshard;
- bin_t *bin = arena_bin_choose_lock(tsd_tsdn(tsd), arena, binind,
- &binshard);
- bin->stats.nflushes++;
- bin->stats.nrequests += tbin->tstats.nrequests;
- tbin->tstats.nrequests = 0;
- malloc_mutex_unlock(tsd_tsdn(tsd), &bin->lock);
- }
+JEMALLOC_ALWAYS_INLINE void
+tcache_bin_flush_impl(tsd_t *tsd, tcache_t *tcache, cache_bin_t *cache_bin,
+ szind_t binind, cache_bin_ptr_array_t *ptrs, unsigned nflush, bool small) {
+ tcache_slow_t *tcache_slow = tcache->tcache_slow;
+ /*
+ * A couple lookup calls take tsdn; declare it once for convenience
+ * instead of calling tsd_tsdn(tsd) all the time.
+ */
+ tsdn_t *tsdn = tsd_tsdn(tsd);
- memmove(tbin->avail - rem, tbin->avail - tbin->ncached, rem *
- sizeof(void *));
- tbin->ncached = rem;
- if (tbin->ncached < tbin->low_water) {
- tbin->low_water = tbin->ncached;
+ if (small) {
+ assert(binind < SC_NBINS);
+ } else {
+ assert(binind < nhbins);
}
-}
+ arena_t *tcache_arena = tcache_slow->arena;
+ assert(tcache_arena != NULL);
-void
-tcache_bin_flush_large(tsd_t *tsd, cache_bin_t *tbin, szind_t binind,
- unsigned rem, tcache_t *tcache) {
- bool merged_stats = false;
+ /*
+ * Variable length array must have > 0 length; the last element is never
+ * touched (it's just included to satisfy the no-zero-length rule).
+ */
+ VARIABLE_ARRAY(emap_batch_lookup_result_t, item_edata, nflush + 1);
+ tcache_bin_flush_edatas_lookup(tsd, ptrs, binind, nflush, item_edata);
- assert(binind < nhbins);
- assert((cache_bin_sz_t)rem <= tbin->ncached);
+ /*
+ * The slabs where we freed the last remaining object in the slab (and
+ * so need to free the slab itself).
+ * Used only if small == true.
+ */
+ unsigned dalloc_count = 0;
+ VARIABLE_ARRAY(edata_t *, dalloc_slabs, nflush + 1);
- arena_t *tcache_arena = tcache->arena;
- assert(tcache_arena != NULL);
- unsigned nflush = tbin->ncached - rem;
- VARIABLE_ARRAY(extent_t *, item_extent, nflush);
-
-#ifndef JEMALLOC_EXTRA_SIZE_CHECK
- /* Look up extent once per item. */
- for (unsigned i = 0 ; i < nflush; i++) {
- item_extent[i] = iealloc(tsd_tsdn(tsd), *(tbin->avail - 1 - i));
- }
-#else
- tbin_extents_lookup_size_check(tsd_tsdn(tsd), tbin, binind, nflush,
- item_extent);
-#endif
+ /*
+ * We're about to grab a bunch of locks. If one of them happens to be
+ * the one guarding the arena-level stats counters we flush our
+ * thread-local ones to, we do so under one critical section.
+ */
+ bool merged_stats = false;
while (nflush > 0) {
- /* Lock the arena associated with the first object. */
- extent_t *extent = item_extent[0];
- unsigned locked_arena_ind = extent_arena_ind_get(extent);
- arena_t *locked_arena = arena_get(tsd_tsdn(tsd),
- locked_arena_ind, false);
- bool idump;
-
- if (config_prof) {
- idump = false;
+ /* Lock the arena, or bin, associated with the first object. */
+ edata_t *edata = item_edata[0].edata;
+ unsigned cur_arena_ind = edata_arena_ind_get(edata);
+ arena_t *cur_arena = arena_get(tsdn, cur_arena_ind, false);
+
+ /*
+ * These assignments are always overwritten when small is true,
+ * and their values are always ignored when small is false, but
+ * to avoid the technical UB when we pass them as parameters, we
+ * need to intialize them.
+ */
+ unsigned cur_binshard = 0;
+ bin_t *cur_bin = NULL;
+ if (small) {
+ cur_binshard = edata_binshard_get(edata);
+ cur_bin = arena_get_bin(cur_arena, binind,
+ cur_binshard);
+ assert(cur_binshard < bin_infos[binind].n_shards);
+ /*
+ * If you're looking at profiles, you might think this
+ * is a good place to prefetch the bin stats, which are
+ * often a cache miss. This turns out not to be
+ * helpful on the workloads we've looked at, with moving
+ * the bin stats next to the lock seeming to do better.
+ */
}
- bool lock_large = !arena_is_auto(locked_arena);
- if (lock_large) {
- malloc_mutex_lock(tsd_tsdn(tsd), &locked_arena->large_mtx);
+ if (small) {
+ malloc_mutex_lock(tsdn, &cur_bin->lock);
}
- for (unsigned i = 0; i < nflush; i++) {
- void *ptr = *(tbin->avail - 1 - i);
- assert(ptr != NULL);
- extent = item_extent[i];
- if (extent_arena_ind_get(extent) == locked_arena_ind) {
- large_dalloc_prep_junked_locked(tsd_tsdn(tsd),
- extent);
- }
+ if (!small && !arena_is_auto(cur_arena)) {
+ malloc_mutex_lock(tsdn, &cur_arena->large_mtx);
}
- if ((config_prof || config_stats) &&
- (locked_arena == tcache_arena)) {
- if (config_prof) {
- idump = arena_prof_accum(tsd_tsdn(tsd),
- tcache_arena, tcache->prof_accumbytes);
- tcache->prof_accumbytes = 0;
+
+ /*
+ * If we acquired the right lock and have some stats to flush,
+ * flush them.
+ */
+ if (config_stats && tcache_arena == cur_arena
+ && !merged_stats) {
+ merged_stats = true;
+ if (small) {
+ cur_bin->stats.nflushes++;
+ cur_bin->stats.nrequests +=
+ cache_bin->tstats.nrequests;
+ cache_bin->tstats.nrequests = 0;
+ } else {
+ arena_stats_large_flush_nrequests_add(tsdn,
+ &tcache_arena->stats, binind,
+ cache_bin->tstats.nrequests);
+ cache_bin->tstats.nrequests = 0;
}
- if (config_stats) {
- merged_stats = true;
- arena_stats_large_flush_nrequests_add(
- tsd_tsdn(tsd), &tcache_arena->stats, binind,
- tbin->tstats.nrequests);
- tbin->tstats.nrequests = 0;
+ }
+
+ /*
+ * Large allocations need special prep done. Afterwards, we can
+ * drop the large lock.
+ */
+ if (!small) {
+ for (unsigned i = 0; i < nflush; i++) {
+ void *ptr = ptrs->ptr[i];
+ edata = item_edata[i].edata;
+ assert(ptr != NULL && edata != NULL);
+
+ if (tcache_bin_flush_match(edata, cur_arena_ind,
+ cur_binshard, small)) {
+ large_dalloc_prep_locked(tsdn,
+ edata);
+ }
}
}
- if (lock_large) {
- malloc_mutex_unlock(tsd_tsdn(tsd), &locked_arena->large_mtx);
+ if (!small && !arena_is_auto(cur_arena)) {
+ malloc_mutex_unlock(tsdn, &cur_arena->large_mtx);
}
+ /* Deallocate whatever we can. */
unsigned ndeferred = 0;
+ /* Init only to avoid used-uninitialized warning. */
+ arena_dalloc_bin_locked_info_t dalloc_bin_info = {0};
+ if (small) {
+ arena_dalloc_bin_locked_begin(&dalloc_bin_info, binind);
+ }
for (unsigned i = 0; i < nflush; i++) {
- void *ptr = *(tbin->avail - 1 - i);
- extent = item_extent[i];
- assert(ptr != NULL && extent != NULL);
-
- if (extent_arena_ind_get(extent) == locked_arena_ind) {
- large_dalloc_finish(tsd_tsdn(tsd), extent);
- } else {
+ void *ptr = ptrs->ptr[i];
+ edata = item_edata[i].edata;
+ assert(ptr != NULL && edata != NULL);
+ if (!tcache_bin_flush_match(edata, cur_arena_ind,
+ cur_binshard, small)) {
/*
- * This object was allocated via a different
- * arena than the one that is currently locked.
- * Stash the object, so that it can be handled
- * in a future pass.
+ * The object was allocated either via a
+ * different arena, or a different bin in this
+ * arena. Either way, stash the object so that
+ * it can be handled in a future pass.
*/
- *(tbin->avail - 1 - ndeferred) = ptr;
- item_extent[ndeferred] = extent;
+ ptrs->ptr[ndeferred] = ptr;
+ item_edata[ndeferred].edata = edata;
ndeferred++;
+ continue;
+ }
+ if (small) {
+ if (arena_dalloc_bin_locked_step(tsdn,
+ cur_arena, cur_bin, &dalloc_bin_info,
+ binind, edata, ptr)) {
+ dalloc_slabs[dalloc_count] = edata;
+ dalloc_count++;
+ }
+ } else {
+ if (large_dalloc_safety_checks(edata, ptr,
+ binind)) {
+ /* See the comment in isfree. */
+ continue;
+ }
+ large_dalloc_finish(tsdn, edata);
}
}
- if (config_prof && idump) {
- prof_idump(tsd_tsdn(tsd));
+
+ if (small) {
+ arena_dalloc_bin_locked_finish(tsdn, cur_arena, cur_bin,
+ &dalloc_bin_info);
+ malloc_mutex_unlock(tsdn, &cur_bin->lock);
}
- arena_decay_ticks(tsd_tsdn(tsd), locked_arena, nflush -
- ndeferred);
+ arena_decay_ticks(tsdn, cur_arena, nflush - ndeferred);
nflush = ndeferred;
}
+
+ /* Handle all deferred slab dalloc. */
+ assert(small || dalloc_count == 0);
+ for (unsigned i = 0; i < dalloc_count; i++) {
+ edata_t *slab = dalloc_slabs[i];
+ arena_slab_dalloc(tsdn, arena_get_from_edata(slab), slab);
+
+ }
+
if (config_stats && !merged_stats) {
- /*
- * The flush loop didn't happen to flush to this thread's
- * arena, so the stats didn't get merged. Manually do so now.
- */
- arena_stats_large_flush_nrequests_add(tsd_tsdn(tsd),
- &tcache_arena->stats, binind, tbin->tstats.nrequests);
- tbin->tstats.nrequests = 0;
+ if (small) {
+ /*
+ * The flush loop didn't happen to flush to this
+ * thread's arena, so the stats didn't get merged.
+ * Manually do so now.
+ */
+ bin_t *bin = arena_bin_choose(tsdn, tcache_arena,
+ binind, NULL);
+ malloc_mutex_lock(tsdn, &bin->lock);
+ bin->stats.nflushes++;
+ bin->stats.nrequests += cache_bin->tstats.nrequests;
+ cache_bin->tstats.nrequests = 0;
+ malloc_mutex_unlock(tsdn, &bin->lock);
+ } else {
+ arena_stats_large_flush_nrequests_add(tsdn,
+ &tcache_arena->stats, binind,
+ cache_bin->tstats.nrequests);
+ cache_bin->tstats.nrequests = 0;
+ }
}
- memmove(tbin->avail - rem, tbin->avail - tbin->ncached, rem *
- sizeof(void *));
- tbin->ncached = rem;
- if (tbin->ncached < tbin->low_water) {
- tbin->low_water = tbin->ncached;
+}
+
+JEMALLOC_ALWAYS_INLINE void
+tcache_bin_flush_bottom(tsd_t *tsd, tcache_t *tcache, cache_bin_t *cache_bin,
+ szind_t binind, unsigned rem, bool small) {
+ tcache_bin_flush_stashed(tsd, tcache, cache_bin, binind, small);
+
+ cache_bin_sz_t ncached = cache_bin_ncached_get_local(cache_bin,
+ &tcache_bin_info[binind]);
+ assert((cache_bin_sz_t)rem <= ncached);
+ unsigned nflush = ncached - rem;
+
+ CACHE_BIN_PTR_ARRAY_DECLARE(ptrs, nflush);
+ cache_bin_init_ptr_array_for_flush(cache_bin, &tcache_bin_info[binind],
+ &ptrs, nflush);
+
+ tcache_bin_flush_impl(tsd, tcache, cache_bin, binind, &ptrs, nflush,
+ small);
+
+ cache_bin_finish_flush(cache_bin, &tcache_bin_info[binind], &ptrs,
+ ncached - rem);
+}
+
+void
+tcache_bin_flush_small(tsd_t *tsd, tcache_t *tcache, cache_bin_t *cache_bin,
+ szind_t binind, unsigned rem) {
+ tcache_bin_flush_bottom(tsd, tcache, cache_bin, binind, rem, true);
+}
+
+void
+tcache_bin_flush_large(tsd_t *tsd, tcache_t *tcache, cache_bin_t *cache_bin,
+ szind_t binind, unsigned rem) {
+ tcache_bin_flush_bottom(tsd, tcache, cache_bin, binind, rem, false);
+}
+
+/*
+ * Flushing stashed happens when 1) tcache fill, 2) tcache flush, or 3) tcache
+ * GC event. This makes sure that the stashed items do not hold memory for too
+ * long, and new buffers can only be allocated when nothing is stashed.
+ *
+ * The downside is, the time between stash and flush may be relatively short,
+ * especially when the request rate is high. It lowers the chance of detecting
+ * write-after-free -- however that is a delayed detection anyway, and is less
+ * of a focus than the memory overhead.
+ */
+void
+tcache_bin_flush_stashed(tsd_t *tsd, tcache_t *tcache, cache_bin_t *cache_bin,
+ szind_t binind, bool is_small) {
+ cache_bin_info_t *info = &tcache_bin_info[binind];
+ /*
+ * The two below are for assertion only. The content of original cached
+ * items remain unchanged -- the stashed items reside on the other end
+ * of the stack. Checking the stack head and ncached to verify.
+ */
+ void *head_content = *cache_bin->stack_head;
+ cache_bin_sz_t orig_cached = cache_bin_ncached_get_local(cache_bin,
+ info);
+
+ cache_bin_sz_t nstashed = cache_bin_nstashed_get_local(cache_bin, info);
+ assert(orig_cached + nstashed <= cache_bin_info_ncached_max(info));
+ if (nstashed == 0) {
+ return;
}
+
+ CACHE_BIN_PTR_ARRAY_DECLARE(ptrs, nstashed);
+ cache_bin_init_ptr_array_for_stashed(cache_bin, binind, info, &ptrs,
+ nstashed);
+ san_check_stashed_ptrs(ptrs.ptr, nstashed, sz_index2size(binind));
+ tcache_bin_flush_impl(tsd, tcache, cache_bin, binind, &ptrs, nstashed,
+ is_small);
+ cache_bin_finish_flush_stashed(cache_bin, info);
+
+ assert(cache_bin_nstashed_get_local(cache_bin, info) == 0);
+ assert(cache_bin_ncached_get_local(cache_bin, info) == orig_cached);
+ assert(head_content == *cache_bin->stack_head);
}
void
-tcache_arena_associate(tsdn_t *tsdn, tcache_t *tcache, arena_t *arena) {
- assert(tcache->arena == NULL);
- tcache->arena = arena;
+tcache_arena_associate(tsdn_t *tsdn, tcache_slow_t *tcache_slow,
+ tcache_t *tcache, arena_t *arena) {
+ assert(tcache_slow->arena == NULL);
+ tcache_slow->arena = arena;
if (config_stats) {
/* Link into list of extant tcaches. */
malloc_mutex_lock(tsdn, &arena->tcache_ql_mtx);
- ql_elm_new(tcache, link);
- ql_tail_insert(&arena->tcache_ql, tcache, link);
+ ql_elm_new(tcache_slow, link);
+ ql_tail_insert(&arena->tcache_ql, tcache_slow, link);
cache_bin_array_descriptor_init(
- &tcache->cache_bin_array_descriptor, tcache->bins_small,
- tcache->bins_large);
+ &tcache_slow->cache_bin_array_descriptor, tcache->bins);
ql_tail_insert(&arena->cache_bin_array_descriptor_ql,
- &tcache->cache_bin_array_descriptor, link);
+ &tcache_slow->cache_bin_array_descriptor, link);
malloc_mutex_unlock(tsdn, &arena->tcache_ql_mtx);
}
}
static void
-tcache_arena_dissociate(tsdn_t *tsdn, tcache_t *tcache) {
- arena_t *arena = tcache->arena;
+tcache_arena_dissociate(tsdn_t *tsdn, tcache_slow_t *tcache_slow,
+ tcache_t *tcache) {
+ arena_t *arena = tcache_slow->arena;
assert(arena != NULL);
if (config_stats) {
/* Unlink from list of extant tcaches. */
malloc_mutex_lock(tsdn, &arena->tcache_ql_mtx);
if (config_debug) {
bool in_ql = false;
- tcache_t *iter;
+ tcache_slow_t *iter;
ql_foreach(iter, &arena->tcache_ql, link) {
- if (iter == tcache) {
+ if (iter == tcache_slow) {
in_ql = true;
break;
}
}
assert(in_ql);
}
- ql_remove(&arena->tcache_ql, tcache, link);
+ ql_remove(&arena->tcache_ql, tcache_slow, link);
ql_remove(&arena->cache_bin_array_descriptor_ql,
- &tcache->cache_bin_array_descriptor, link);
- tcache_stats_merge(tsdn, tcache, arena);
+ &tcache_slow->cache_bin_array_descriptor, link);
+ tcache_stats_merge(tsdn, tcache_slow->tcache, arena);
malloc_mutex_unlock(tsdn, &arena->tcache_ql_mtx);
}
- tcache->arena = NULL;
+ tcache_slow->arena = NULL;
}
void
-tcache_arena_reassociate(tsdn_t *tsdn, tcache_t *tcache, arena_t *arena) {
- tcache_arena_dissociate(tsdn, tcache);
- tcache_arena_associate(tsdn, tcache, arena);
+tcache_arena_reassociate(tsdn_t *tsdn, tcache_slow_t *tcache_slow,
+ tcache_t *tcache, arena_t *arena) {
+ tcache_arena_dissociate(tsdn, tcache_slow, tcache);
+ tcache_arena_associate(tsdn, tcache_slow, tcache, arena);
}
bool
@@ -405,56 +647,80 @@ tsd_tcache_enabled_data_init(tsd_t *tsd) {
return false;
}
-/* Initialize auto tcache (embedded in TSD). */
static void
-tcache_init(tsd_t *tsd, tcache_t *tcache, void *avail_stack) {
- memset(&tcache->link, 0, sizeof(ql_elm(tcache_t)));
- tcache->prof_accumbytes = 0;
- tcache->next_gc_bin = 0;
- tcache->arena = NULL;
-
- ticker_init(&tcache->gc_ticker, TCACHE_GC_INCR);
-
- size_t stack_offset = 0;
- assert((TCACHE_NSLOTS_SMALL_MAX & 1U) == 0);
- memset(tcache->bins_small, 0, sizeof(cache_bin_t) * SC_NBINS);
- memset(tcache->bins_large, 0, sizeof(cache_bin_t) * (nhbins - SC_NBINS));
- unsigned i = 0;
- for (; i < SC_NBINS; i++) {
- tcache->lg_fill_div[i] = 1;
- stack_offset += tcache_bin_info[i].ncached_max * sizeof(void *);
- /*
- * avail points past the available space. Allocations will
- * access the slots toward higher addresses (for the benefit of
- * prefetch).
- */
- tcache_small_bin_get(tcache, i)->avail =
- (void **)((uintptr_t)avail_stack + (uintptr_t)stack_offset);
+tcache_init(tsd_t *tsd, tcache_slow_t *tcache_slow, tcache_t *tcache,
+ void *mem) {
+ tcache->tcache_slow = tcache_slow;
+ tcache_slow->tcache = tcache;
+
+ memset(&tcache_slow->link, 0, sizeof(ql_elm(tcache_t)));
+ tcache_slow->next_gc_bin = 0;
+ tcache_slow->arena = NULL;
+ tcache_slow->dyn_alloc = mem;
+
+ /*
+ * We reserve cache bins for all small size classes, even if some may
+ * not get used (i.e. bins higher than nhbins). This allows the fast
+ * and common paths to access cache bin metadata safely w/o worrying
+ * about which ones are disabled.
+ */
+ unsigned n_reserved_bins = nhbins < SC_NBINS ? SC_NBINS : nhbins;
+ memset(tcache->bins, 0, sizeof(cache_bin_t) * n_reserved_bins);
+
+ size_t cur_offset = 0;
+ cache_bin_preincrement(tcache_bin_info, nhbins, mem,
+ &cur_offset);
+ for (unsigned i = 0; i < nhbins; i++) {
+ if (i < SC_NBINS) {
+ tcache_slow->lg_fill_div[i] = 1;
+ tcache_slow->bin_refilled[i] = false;
+ tcache_slow->bin_flush_delay_items[i]
+ = tcache_gc_item_delay_compute(i);
+ }
+ cache_bin_t *cache_bin = &tcache->bins[i];
+ cache_bin_init(cache_bin, &tcache_bin_info[i], mem,
+ &cur_offset);
}
- for (; i < nhbins; i++) {
- stack_offset += tcache_bin_info[i].ncached_max * sizeof(void *);
- tcache_large_bin_get(tcache, i)->avail =
- (void **)((uintptr_t)avail_stack + (uintptr_t)stack_offset);
+ /*
+ * For small size classes beyond tcache_maxclass (i.e. nhbins < NBINS),
+ * their cache bins are initialized to a state to safely and efficiently
+ * fail all fastpath alloc / free, so that no additional check around
+ * nhbins is needed on fastpath.
+ */
+ for (unsigned i = nhbins; i < SC_NBINS; i++) {
+ /* Disabled small bins. */
+ cache_bin_t *cache_bin = &tcache->bins[i];
+ void *fake_stack = mem;
+ size_t fake_offset = 0;
+
+ cache_bin_init(cache_bin, &tcache_bin_info[i], fake_stack,
+ &fake_offset);
+ assert(tcache_small_bin_disabled(i, cache_bin));
}
- assert(stack_offset == stack_nelms * sizeof(void *));
+
+ cache_bin_postincrement(tcache_bin_info, nhbins, mem,
+ &cur_offset);
+ /* Sanity check that the whole stack is used. */
+ assert(cur_offset == tcache_bin_alloc_size);
}
/* Initialize auto tcache (embedded in TSD). */
bool
tsd_tcache_data_init(tsd_t *tsd) {
+ tcache_slow_t *tcache_slow = tsd_tcache_slowp_get_unsafe(tsd);
tcache_t *tcache = tsd_tcachep_get_unsafe(tsd);
- assert(tcache_small_bin_get(tcache, 0)->avail == NULL);
- size_t size = stack_nelms * sizeof(void *);
- /* Avoid false cacheline sharing. */
- size = sz_sa2u(size, CACHELINE);
-
- void *avail_array = ipallocztm(tsd_tsdn(tsd), size, CACHELINE, true,
- NULL, true, arena_get(TSDN_NULL, 0, true));
- if (avail_array == NULL) {
+
+ assert(cache_bin_still_zero_initialized(&tcache->bins[0]));
+ size_t alignment = tcache_bin_alloc_alignment;
+ size_t size = sz_sa2u(tcache_bin_alloc_size, alignment);
+
+ void *mem = ipallocztm(tsd_tsdn(tsd), size, alignment, true, NULL,
+ true, arena_get(TSDN_NULL, 0, true));
+ if (mem == NULL) {
return true;
}
- tcache_init(tsd, tcache, avail_array);
+ tcache_init(tsd, tcache_slow, tcache, mem);
/*
* Initialization is a bit tricky here. After malloc init is done, all
* threads can rely on arena_choose and associate tcache accordingly.
@@ -463,20 +729,22 @@ tsd_tcache_data_init(tsd_t *tsd) {
* associate its tcache to a0 temporarily, and later on
* arena_choose_hard() will re-associate properly.
*/
- tcache->arena = NULL;
+ tcache_slow->arena = NULL;
arena_t *arena;
if (!malloc_initialized()) {
/* If in initialization, assign to a0. */
arena = arena_get(tsd_tsdn(tsd), 0, false);
- tcache_arena_associate(tsd_tsdn(tsd), tcache, arena);
+ tcache_arena_associate(tsd_tsdn(tsd), tcache_slow, tcache,
+ arena);
} else {
arena = arena_choose(tsd, NULL);
/* This may happen if thread.tcache.enabled is used. */
- if (tcache->arena == NULL) {
- tcache_arena_associate(tsd_tsdn(tsd), tcache, arena);
+ if (tcache_slow->arena == NULL) {
+ tcache_arena_associate(tsd_tsdn(tsd), tcache_slow,
+ tcache, arena);
}
}
- assert(arena == tcache->arena);
+ assert(arena == tcache_slow->arena);
return false;
}
@@ -484,56 +752,49 @@ tsd_tcache_data_init(tsd_t *tsd) {
/* Created manual tcache for tcache.create mallctl. */
tcache_t *
tcache_create_explicit(tsd_t *tsd) {
- tcache_t *tcache;
- size_t size, stack_offset;
-
- size = sizeof(tcache_t);
+ /*
+ * We place the cache bin stacks, then the tcache_t, then a pointer to
+ * the beginning of the whole allocation (for freeing). The makes sure
+ * the cache bins have the requested alignment.
+ */
+ size_t size = tcache_bin_alloc_size + sizeof(tcache_t)
+ + sizeof(tcache_slow_t);
/* Naturally align the pointer stacks. */
size = PTR_CEILING(size);
- stack_offset = size;
- size += stack_nelms * sizeof(void *);
- /* Avoid false cacheline sharing. */
- size = sz_sa2u(size, CACHELINE);
+ size = sz_sa2u(size, tcache_bin_alloc_alignment);
- tcache = ipallocztm(tsd_tsdn(tsd), size, CACHELINE, true, NULL, true,
- arena_get(TSDN_NULL, 0, true));
- if (tcache == NULL) {
+ void *mem = ipallocztm(tsd_tsdn(tsd), size, tcache_bin_alloc_alignment,
+ true, NULL, true, arena_get(TSDN_NULL, 0, true));
+ if (mem == NULL) {
return NULL;
}
+ tcache_t *tcache = (void *)((uintptr_t)mem + tcache_bin_alloc_size);
+ tcache_slow_t *tcache_slow =
+ (void *)((uintptr_t)mem + tcache_bin_alloc_size + sizeof(tcache_t));
+ tcache_init(tsd, tcache_slow, tcache, mem);
- tcache_init(tsd, tcache,
- (void *)((uintptr_t)tcache + (uintptr_t)stack_offset));
- tcache_arena_associate(tsd_tsdn(tsd), tcache, arena_ichoose(tsd, NULL));
+ tcache_arena_associate(tsd_tsdn(tsd), tcache_slow, tcache,
+ arena_ichoose(tsd, NULL));
return tcache;
}
static void
tcache_flush_cache(tsd_t *tsd, tcache_t *tcache) {
- assert(tcache->arena != NULL);
-
- for (unsigned i = 0; i < SC_NBINS; i++) {
- cache_bin_t *tbin = tcache_small_bin_get(tcache, i);
- tcache_bin_flush_small(tsd, tcache, tbin, i, 0);
+ tcache_slow_t *tcache_slow = tcache->tcache_slow;
+ assert(tcache_slow->arena != NULL);
- if (config_stats) {
- assert(tbin->tstats.nrequests == 0);
+ for (unsigned i = 0; i < nhbins; i++) {
+ cache_bin_t *cache_bin = &tcache->bins[i];
+ if (i < SC_NBINS) {
+ tcache_bin_flush_small(tsd, tcache, cache_bin, i, 0);
+ } else {
+ tcache_bin_flush_large(tsd, tcache, cache_bin, i, 0);
}
- }
- for (unsigned i = SC_NBINS; i < nhbins; i++) {
- cache_bin_t *tbin = tcache_large_bin_get(tcache, i);
- tcache_bin_flush_large(tsd, tbin, i, 0, tcache);
-
if (config_stats) {
- assert(tbin->tstats.nrequests == 0);
+ assert(cache_bin->tstats.nrequests == 0);
}
}
-
- if (config_prof && tcache->prof_accumbytes > 0 &&
- arena_prof_accum(tsd_tsdn(tsd), tcache->arena,
- tcache->prof_accumbytes)) {
- prof_idump(tsd_tsdn(tsd));
- }
}
void
@@ -544,20 +805,17 @@ tcache_flush(tsd_t *tsd) {
static void
tcache_destroy(tsd_t *tsd, tcache_t *tcache, bool tsd_tcache) {
+ tcache_slow_t *tcache_slow = tcache->tcache_slow;
tcache_flush_cache(tsd, tcache);
- arena_t *arena = tcache->arena;
- tcache_arena_dissociate(tsd_tsdn(tsd), tcache);
+ arena_t *arena = tcache_slow->arena;
+ tcache_arena_dissociate(tsd_tsdn(tsd), tcache_slow, tcache);
if (tsd_tcache) {
- /* Release the avail array for the TSD embedded auto tcache. */
- void *avail_array =
- (void *)((uintptr_t)tcache_small_bin_get(tcache, 0)->avail -
- (uintptr_t)tcache_bin_info[0].ncached_max * sizeof(void *));
- idalloctm(tsd_tsdn(tsd), avail_array, NULL, NULL, true, true);
- } else {
- /* Release both the tcache struct and avail array. */
- idalloctm(tsd_tsdn(tsd), tcache, NULL, NULL, true, true);
+ cache_bin_t *cache_bin = &tcache->bins[0];
+ cache_bin_assert_empty(cache_bin, &tcache_bin_info[0]);
}
+ idalloctm(tsd_tsdn(tsd), tcache_slow->dyn_alloc, NULL, NULL, true,
+ true);
/*
* The deallocation and tcache flush above may not trigger decay since
@@ -571,9 +829,11 @@ tcache_destroy(tsd_t *tsd, tcache_t *tcache, bool tsd_tcache) {
if (arena_nthreads_get(arena, false) == 0 &&
!background_thread_enabled()) {
/* Force purging when no threads assigned to the arena anymore. */
- arena_decay(tsd_tsdn(tsd), arena, false, true);
+ arena_decay(tsd_tsdn(tsd), arena,
+ /* is_background_thread */ false, /* all */ true);
} else {
- arena_decay(tsd_tsdn(tsd), arena, false, false);
+ arena_decay(tsd_tsdn(tsd), arena,
+ /* is_background_thread */ false, /* all */ false);
}
}
@@ -583,53 +843,51 @@ tcache_cleanup(tsd_t *tsd) {
tcache_t *tcache = tsd_tcachep_get(tsd);
if (!tcache_available(tsd)) {
assert(tsd_tcache_enabled_get(tsd) == false);
- if (config_debug) {
- assert(tcache_small_bin_get(tcache, 0)->avail == NULL);
- }
+ assert(cache_bin_still_zero_initialized(&tcache->bins[0]));
return;
}
assert(tsd_tcache_enabled_get(tsd));
- assert(tcache_small_bin_get(tcache, 0)->avail != NULL);
+ assert(!cache_bin_still_zero_initialized(&tcache->bins[0]));
tcache_destroy(tsd, tcache, true);
if (config_debug) {
- tcache_small_bin_get(tcache, 0)->avail = NULL;
+ /*
+ * For debug testing only, we want to pretend we're still in the
+ * zero-initialized state.
+ */
+ memset(tcache->bins, 0, sizeof(cache_bin_t) * nhbins);
}
}
void
tcache_stats_merge(tsdn_t *tsdn, tcache_t *tcache, arena_t *arena) {
- unsigned i;
-
cassert(config_stats);
/* Merge and reset tcache stats. */
- for (i = 0; i < SC_NBINS; i++) {
- cache_bin_t *tbin = tcache_small_bin_get(tcache, i);
- unsigned binshard;
- bin_t *bin = arena_bin_choose_lock(tsdn, arena, i, &binshard);
- bin->stats.nrequests += tbin->tstats.nrequests;
- malloc_mutex_unlock(tsdn, &bin->lock);
- tbin->tstats.nrequests = 0;
- }
-
- for (; i < nhbins; i++) {
- cache_bin_t *tbin = tcache_large_bin_get(tcache, i);
- arena_stats_large_flush_nrequests_add(tsdn, &arena->stats, i,
- tbin->tstats.nrequests);
- tbin->tstats.nrequests = 0;
+ for (unsigned i = 0; i < nhbins; i++) {
+ cache_bin_t *cache_bin = &tcache->bins[i];
+ if (i < SC_NBINS) {
+ bin_t *bin = arena_bin_choose(tsdn, arena, i, NULL);
+ malloc_mutex_lock(tsdn, &bin->lock);
+ bin->stats.nrequests += cache_bin->tstats.nrequests;
+ malloc_mutex_unlock(tsdn, &bin->lock);
+ } else {
+ arena_stats_large_flush_nrequests_add(tsdn,
+ &arena->stats, i, cache_bin->tstats.nrequests);
+ }
+ cache_bin->tstats.nrequests = 0;
}
}
static bool
-tcaches_create_prep(tsd_t *tsd) {
+tcaches_create_prep(tsd_t *tsd, base_t *base) {
bool err;
- malloc_mutex_lock(tsd_tsdn(tsd), &tcaches_mtx);
+ malloc_mutex_assert_owner(tsd_tsdn(tsd), &tcaches_mtx);
if (tcaches == NULL) {
- tcaches = base_alloc(tsd_tsdn(tsd), b0get(), sizeof(tcache_t *)
- * (MALLOCX_TCACHE_MAX+1), CACHELINE);
+ tcaches = base_alloc(tsd_tsdn(tsd), base,
+ sizeof(tcache_t *) * (MALLOCX_TCACHE_MAX+1), CACHELINE);
if (tcaches == NULL) {
err = true;
goto label_return;
@@ -643,17 +901,18 @@ tcaches_create_prep(tsd_t *tsd) {
err = false;
label_return:
- malloc_mutex_unlock(tsd_tsdn(tsd), &tcaches_mtx);
return err;
}
bool
-tcaches_create(tsd_t *tsd, unsigned *r_ind) {
+tcaches_create(tsd_t *tsd, base_t *base, unsigned *r_ind) {
witness_assert_depth(tsdn_witness_tsdp_get(tsd_tsdn(tsd)), 0);
bool err;
- if (tcaches_create_prep(tsd)) {
+ malloc_mutex_lock(tsd_tsdn(tsd), &tcaches_mtx);
+
+ if (tcaches_create_prep(tsd, base)) {
err = true;
goto label_return;
}
@@ -665,7 +924,6 @@ tcaches_create(tsd_t *tsd, unsigned *r_ind) {
}
tcaches_t *elm;
- malloc_mutex_lock(tsd_tsdn(tsd), &tcaches_mtx);
if (tcaches_avail != NULL) {
elm = tcaches_avail;
tcaches_avail = tcaches_avail->next;
@@ -677,10 +935,10 @@ tcaches_create(tsd_t *tsd, unsigned *r_ind) {
*r_ind = tcaches_past;
tcaches_past++;
}
- malloc_mutex_unlock(tsd_tsdn(tsd), &tcaches_mtx);
err = false;
label_return:
+ malloc_mutex_unlock(tsd_tsdn(tsd), &tcaches_mtx);
witness_assert_depth(tsdn_witness_tsdp_get(tsd_tsdn(tsd)), 0);
return err;
}
@@ -729,70 +987,115 @@ tcaches_destroy(tsd_t *tsd, unsigned ind) {
}
}
-bool
-tcache_boot(tsdn_t *tsdn) {
- /* If necessary, clamp opt_lg_tcache_max. */
- if (opt_lg_tcache_max < 0 || (ZU(1) << opt_lg_tcache_max) <
- SC_SMALL_MAXCLASS) {
- tcache_maxclass = SC_SMALL_MAXCLASS;
+static unsigned
+tcache_ncached_max_compute(szind_t szind) {
+ if (szind >= SC_NBINS) {
+ assert(szind < nhbins);
+ return opt_tcache_nslots_large;
+ }
+ unsigned slab_nregs = bin_infos[szind].nregs;
+
+ /* We may modify these values; start with the opt versions. */
+ unsigned nslots_small_min = opt_tcache_nslots_small_min;
+ unsigned nslots_small_max = opt_tcache_nslots_small_max;
+
+ /*
+ * Clamp values to meet our constraints -- even, nonzero, min < max, and
+ * suitable for a cache bin size.
+ */
+ if (opt_tcache_nslots_small_max > CACHE_BIN_NCACHED_MAX) {
+ nslots_small_max = CACHE_BIN_NCACHED_MAX;
+ }
+ if (nslots_small_min % 2 != 0) {
+ nslots_small_min++;
+ }
+ if (nslots_small_max % 2 != 0) {
+ nslots_small_max--;
+ }
+ if (nslots_small_min < 2) {
+ nslots_small_min = 2;
+ }
+ if (nslots_small_max < 2) {
+ nslots_small_max = 2;
+ }
+ if (nslots_small_min > nslots_small_max) {
+ nslots_small_min = nslots_small_max;
+ }
+
+ unsigned candidate;
+ if (opt_lg_tcache_nslots_mul < 0) {
+ candidate = slab_nregs >> (-opt_lg_tcache_nslots_mul);
} else {
- tcache_maxclass = (ZU(1) << opt_lg_tcache_max);
+ candidate = slab_nregs << opt_lg_tcache_nslots_mul;
+ }
+ if (candidate % 2 != 0) {
+ /*
+ * We need the candidate size to be even -- we assume that we
+ * can divide by two and get a positive number (e.g. when
+ * flushing).
+ */
+ ++candidate;
}
+ if (candidate <= nslots_small_min) {
+ return nslots_small_min;
+ } else if (candidate <= nslots_small_max) {
+ return candidate;
+ } else {
+ return nslots_small_max;
+ }
+}
+
+bool
+tcache_boot(tsdn_t *tsdn, base_t *base) {
+ tcache_maxclass = sz_s2u(opt_tcache_max);
+ assert(tcache_maxclass <= TCACHE_MAXCLASS_LIMIT);
+ nhbins = sz_size2index(tcache_maxclass) + 1;
if (malloc_mutex_init(&tcaches_mtx, "tcaches", WITNESS_RANK_TCACHES,
malloc_mutex_rank_exclusive)) {
return true;
}
- nhbins = sz_size2index(tcache_maxclass) + 1;
-
- /* Initialize tcache_bin_info. */
- tcache_bin_info = (cache_bin_info_t *)base_alloc(tsdn, b0get(), nhbins
- * sizeof(cache_bin_info_t), CACHELINE);
+ /* Initialize tcache_bin_info. See comments in tcache_init(). */
+ unsigned n_reserved_bins = nhbins < SC_NBINS ? SC_NBINS : nhbins;
+ size_t size = n_reserved_bins * sizeof(cache_bin_info_t);
+ tcache_bin_info = (cache_bin_info_t *)base_alloc(tsdn, base, size,
+ CACHELINE);
if (tcache_bin_info == NULL) {
return true;
}
- stack_nelms = 0;
- unsigned i;
- for (i = 0; i < SC_NBINS; i++) {
- if ((bin_infos[i].nregs << 1) <= TCACHE_NSLOTS_SMALL_MIN) {
- tcache_bin_info[i].ncached_max =
- TCACHE_NSLOTS_SMALL_MIN;
- } else if ((bin_infos[i].nregs << 1) <=
- TCACHE_NSLOTS_SMALL_MAX) {
- tcache_bin_info[i].ncached_max =
- (bin_infos[i].nregs << 1);
- } else {
- tcache_bin_info[i].ncached_max =
- TCACHE_NSLOTS_SMALL_MAX;
- }
- stack_nelms += tcache_bin_info[i].ncached_max;
+
+ for (szind_t i = 0; i < nhbins; i++) {
+ unsigned ncached_max = tcache_ncached_max_compute(i);
+ cache_bin_info_init(&tcache_bin_info[i], ncached_max);
}
- for (; i < nhbins; i++) {
- tcache_bin_info[i].ncached_max = TCACHE_NSLOTS_LARGE;
- stack_nelms += tcache_bin_info[i].ncached_max;
+ for (szind_t i = nhbins; i < SC_NBINS; i++) {
+ /* Disabled small bins. */
+ cache_bin_info_init(&tcache_bin_info[i], 0);
+ assert(tcache_small_bin_disabled(i, NULL));
}
+ cache_bin_info_compute_alloc(tcache_bin_info, nhbins,
+ &tcache_bin_alloc_size, &tcache_bin_alloc_alignment);
+
return false;
}
void
tcache_prefork(tsdn_t *tsdn) {
- if (!config_prof && opt_tcache) {
- malloc_mutex_prefork(tsdn, &tcaches_mtx);
- }
+ malloc_mutex_prefork(tsdn, &tcaches_mtx);
}
void
tcache_postfork_parent(tsdn_t *tsdn) {
- if (!config_prof && opt_tcache) {
- malloc_mutex_postfork_parent(tsdn, &tcaches_mtx);
- }
+ malloc_mutex_postfork_parent(tsdn, &tcaches_mtx);
}
void
tcache_postfork_child(tsdn_t *tsdn) {
- if (!config_prof && opt_tcache) {
- malloc_mutex_postfork_child(tsdn, &tcaches_mtx);
- }
+ malloc_mutex_postfork_child(tsdn, &tcaches_mtx);
+}
+
+void tcache_assert_initialized(tcache_t *tcache) {
+ assert(!cache_bin_still_zero_initialized(&tcache->bins[0]));
}
diff --git a/contrib/jemalloc/src/thread_event.c b/contrib/jemalloc/src/thread_event.c
new file mode 100644
index 000000000000..37eb5827d3c9
--- /dev/null
+++ b/contrib/jemalloc/src/thread_event.c
@@ -0,0 +1,343 @@
+#include "jemalloc/internal/jemalloc_preamble.h"
+#include "jemalloc/internal/jemalloc_internal_includes.h"
+
+#include "jemalloc/internal/thread_event.h"
+
+/*
+ * Signatures for event specific functions. These functions should be defined
+ * by the modules owning each event. The signatures here verify that the
+ * definitions follow the right format.
+ *
+ * The first two are functions computing new / postponed event wait time. New
+ * event wait time is the time till the next event if an event is currently
+ * being triggered; postponed event wait time is the time till the next event
+ * if an event should be triggered but needs to be postponed, e.g. when the TSD
+ * is not nominal or during reentrancy.
+ *
+ * The third is the event handler function, which is called whenever an event
+ * is triggered. The parameter is the elapsed time since the last time an
+ * event of the same type was triggered.
+ */
+#define E(event, condition_unused, is_alloc_event_unused) \
+uint64_t event##_new_event_wait(tsd_t *tsd); \
+uint64_t event##_postponed_event_wait(tsd_t *tsd); \
+void event##_event_handler(tsd_t *tsd, uint64_t elapsed);
+
+ITERATE_OVER_ALL_EVENTS
+#undef E
+
+/* Signatures for internal functions fetching elapsed time. */
+#define E(event, condition_unused, is_alloc_event_unused) \
+static uint64_t event##_fetch_elapsed(tsd_t *tsd);
+
+ITERATE_OVER_ALL_EVENTS
+#undef E
+
+static uint64_t
+tcache_gc_fetch_elapsed(tsd_t *tsd) {
+ return TE_INVALID_ELAPSED;
+}
+
+static uint64_t
+tcache_gc_dalloc_fetch_elapsed(tsd_t *tsd) {
+ return TE_INVALID_ELAPSED;
+}
+
+static uint64_t
+prof_sample_fetch_elapsed(tsd_t *tsd) {
+ uint64_t last_event = thread_allocated_last_event_get(tsd);
+ uint64_t last_sample_event = prof_sample_last_event_get(tsd);
+ prof_sample_last_event_set(tsd, last_event);
+ return last_event - last_sample_event;
+}
+
+static uint64_t
+stats_interval_fetch_elapsed(tsd_t *tsd) {
+ uint64_t last_event = thread_allocated_last_event_get(tsd);
+ uint64_t last_stats_event = stats_interval_last_event_get(tsd);
+ stats_interval_last_event_set(tsd, last_event);
+ return last_event - last_stats_event;
+}
+
+static uint64_t
+peak_alloc_fetch_elapsed(tsd_t *tsd) {
+ return TE_INVALID_ELAPSED;
+}
+
+static uint64_t
+peak_dalloc_fetch_elapsed(tsd_t *tsd) {
+ return TE_INVALID_ELAPSED;
+}
+
+/* Per event facilities done. */
+
+static bool
+te_ctx_has_active_events(te_ctx_t *ctx) {
+ assert(config_debug);
+#define E(event, condition, alloc_event) \
+ if (condition && alloc_event == ctx->is_alloc) { \
+ return true; \
+ }
+ ITERATE_OVER_ALL_EVENTS
+#undef E
+ return false;
+}
+
+static uint64_t
+te_next_event_compute(tsd_t *tsd, bool is_alloc) {
+ uint64_t wait = TE_MAX_START_WAIT;
+#define E(event, condition, alloc_event) \
+ if (is_alloc == alloc_event && condition) { \
+ uint64_t event_wait = \
+ event##_event_wait_get(tsd); \
+ assert(event_wait <= TE_MAX_START_WAIT); \
+ if (event_wait > 0U && event_wait < wait) { \
+ wait = event_wait; \
+ } \
+ }
+
+ ITERATE_OVER_ALL_EVENTS
+#undef E
+ assert(wait <= TE_MAX_START_WAIT);
+ return wait;
+}
+
+static void
+te_assert_invariants_impl(tsd_t *tsd, te_ctx_t *ctx) {
+ uint64_t current_bytes = te_ctx_current_bytes_get(ctx);
+ uint64_t last_event = te_ctx_last_event_get(ctx);
+ uint64_t next_event = te_ctx_next_event_get(ctx);
+ uint64_t next_event_fast = te_ctx_next_event_fast_get(ctx);
+
+ assert(last_event != next_event);
+ if (next_event > TE_NEXT_EVENT_FAST_MAX || !tsd_fast(tsd)) {
+ assert(next_event_fast == 0U);
+ } else {
+ assert(next_event_fast == next_event);
+ }
+
+ /* The subtraction is intentionally susceptible to underflow. */
+ uint64_t interval = next_event - last_event;
+
+ /* The subtraction is intentionally susceptible to underflow. */
+ assert(current_bytes - last_event < interval);
+ uint64_t min_wait = te_next_event_compute(tsd, te_ctx_is_alloc(ctx));
+ /*
+ * next_event should have been pushed up only except when no event is
+ * on and the TSD is just initialized. The last_event == 0U guard
+ * below is stronger than needed, but having an exactly accurate guard
+ * is more complicated to implement.
+ */
+ assert((!te_ctx_has_active_events(ctx) && last_event == 0U) ||
+ interval == min_wait ||
+ (interval < min_wait && interval == TE_MAX_INTERVAL));
+}
+
+void
+te_assert_invariants_debug(tsd_t *tsd) {
+ te_ctx_t ctx;
+ te_ctx_get(tsd, &ctx, true);
+ te_assert_invariants_impl(tsd, &ctx);
+
+ te_ctx_get(tsd, &ctx, false);
+ te_assert_invariants_impl(tsd, &ctx);
+}
+
+/*
+ * Synchronization around the fast threshold in tsd --
+ * There are two threads to consider in the synchronization here:
+ * - The owner of the tsd being updated by a slow path change
+ * - The remote thread, doing that slow path change.
+ *
+ * As a design constraint, we want to ensure that a slow-path transition cannot
+ * be ignored for arbitrarily long, and that if the remote thread causes a
+ * slow-path transition and then communicates with the owner thread that it has
+ * occurred, then the owner will go down the slow path on the next allocator
+ * operation (so that we don't want to just wait until the owner hits its slow
+ * path reset condition on its own).
+ *
+ * Here's our strategy to do that:
+ *
+ * The remote thread will update the slow-path stores to TSD variables, issue a
+ * SEQ_CST fence, and then update the TSD next_event_fast counter. The owner
+ * thread will update next_event_fast, issue an SEQ_CST fence, and then check
+ * its TSD to see if it's on the slow path.
+
+ * This is fairly straightforward when 64-bit atomics are supported. Assume that
+ * the remote fence is sandwiched between two owner fences in the reset pathway.
+ * The case where there is no preceding or trailing owner fence (i.e. because
+ * the owner thread is near the beginning or end of its life) can be analyzed
+ * similarly. The owner store to next_event_fast preceding the earlier owner
+ * fence will be earlier in coherence order than the remote store to it, so that
+ * the owner thread will go down the slow path once the store becomes visible to
+ * it, which is no later than the time of the second fence.
+
+ * The case where we don't support 64-bit atomics is trickier, since word
+ * tearing is possible. We'll repeat the same analysis, and look at the two
+ * owner fences sandwiching the remote fence. The next_event_fast stores done
+ * alongside the earlier owner fence cannot overwrite any of the remote stores
+ * (since they precede the earlier owner fence in sb, which precedes the remote
+ * fence in sc, which precedes the remote stores in sb). After the second owner
+ * fence there will be a re-check of the slow-path variables anyways, so the
+ * "owner will notice that it's on the slow path eventually" guarantee is
+ * satisfied. To make sure that the out-of-band-messaging constraint is as well,
+ * note that either the message passing is sequenced before the second owner
+ * fence (in which case the remote stores happen before the second set of owner
+ * stores, so malloc sees a value of zero for next_event_fast and goes down the
+ * slow path), or it is not (in which case the owner sees the tsd slow-path
+ * writes on its previous update). This leaves open the possibility that the
+ * remote thread will (at some arbitrary point in the future) zero out one half
+ * of the owner thread's next_event_fast, but that's always safe (it just sends
+ * it down the slow path earlier).
+ */
+static void
+te_ctx_next_event_fast_update(te_ctx_t *ctx) {
+ uint64_t next_event = te_ctx_next_event_get(ctx);
+ uint64_t next_event_fast = (next_event <= TE_NEXT_EVENT_FAST_MAX) ?
+ next_event : 0U;
+ te_ctx_next_event_fast_set(ctx, next_event_fast);
+}
+
+void
+te_recompute_fast_threshold(tsd_t *tsd) {
+ if (tsd_state_get(tsd) != tsd_state_nominal) {
+ /* Check first because this is also called on purgatory. */
+ te_next_event_fast_set_non_nominal(tsd);
+ return;
+ }
+
+ te_ctx_t ctx;
+ te_ctx_get(tsd, &ctx, true);
+ te_ctx_next_event_fast_update(&ctx);
+ te_ctx_get(tsd, &ctx, false);
+ te_ctx_next_event_fast_update(&ctx);
+
+ atomic_fence(ATOMIC_SEQ_CST);
+ if (tsd_state_get(tsd) != tsd_state_nominal) {
+ te_next_event_fast_set_non_nominal(tsd);
+ }
+}
+
+static void
+te_adjust_thresholds_helper(tsd_t *tsd, te_ctx_t *ctx,
+ uint64_t wait) {
+ /*
+ * The next threshold based on future events can only be adjusted after
+ * progressing the last_event counter (which is set to current).
+ */
+ assert(te_ctx_current_bytes_get(ctx) == te_ctx_last_event_get(ctx));
+ assert(wait <= TE_MAX_START_WAIT);
+
+ uint64_t next_event = te_ctx_last_event_get(ctx) + (wait <=
+ TE_MAX_INTERVAL ? wait : TE_MAX_INTERVAL);
+ te_ctx_next_event_set(tsd, ctx, next_event);
+}
+
+static uint64_t
+te_clip_event_wait(uint64_t event_wait) {
+ assert(event_wait > 0U);
+ if (TE_MIN_START_WAIT > 1U &&
+ unlikely(event_wait < TE_MIN_START_WAIT)) {
+ event_wait = TE_MIN_START_WAIT;
+ }
+ if (TE_MAX_START_WAIT < UINT64_MAX &&
+ unlikely(event_wait > TE_MAX_START_WAIT)) {
+ event_wait = TE_MAX_START_WAIT;
+ }
+ return event_wait;
+}
+
+void
+te_event_trigger(tsd_t *tsd, te_ctx_t *ctx) {
+ /* usize has already been added to thread_allocated. */
+ uint64_t bytes_after = te_ctx_current_bytes_get(ctx);
+ /* The subtraction is intentionally susceptible to underflow. */
+ uint64_t accumbytes = bytes_after - te_ctx_last_event_get(ctx);
+
+ te_ctx_last_event_set(ctx, bytes_after);
+
+ bool allow_event_trigger = tsd_nominal(tsd) &&
+ tsd_reentrancy_level_get(tsd) == 0;
+ bool is_alloc = ctx->is_alloc;
+ uint64_t wait = TE_MAX_START_WAIT;
+
+#define E(event, condition, alloc_event) \
+ bool is_##event##_triggered = false; \
+ if (is_alloc == alloc_event && condition) { \
+ uint64_t event_wait = event##_event_wait_get(tsd); \
+ assert(event_wait <= TE_MAX_START_WAIT); \
+ if (event_wait > accumbytes) { \
+ event_wait -= accumbytes; \
+ } else if (!allow_event_trigger) { \
+ event_wait = event##_postponed_event_wait(tsd); \
+ } else { \
+ is_##event##_triggered = true; \
+ event_wait = event##_new_event_wait(tsd); \
+ } \
+ event_wait = te_clip_event_wait(event_wait); \
+ event##_event_wait_set(tsd, event_wait); \
+ if (event_wait < wait) { \
+ wait = event_wait; \
+ } \
+ }
+
+ ITERATE_OVER_ALL_EVENTS
+#undef E
+
+ assert(wait <= TE_MAX_START_WAIT);
+ te_adjust_thresholds_helper(tsd, ctx, wait);
+ te_assert_invariants(tsd);
+
+#define E(event, condition, alloc_event) \
+ if (is_alloc == alloc_event && condition && \
+ is_##event##_triggered) { \
+ assert(allow_event_trigger); \
+ uint64_t elapsed = event##_fetch_elapsed(tsd); \
+ event##_event_handler(tsd, elapsed); \
+ }
+
+ ITERATE_OVER_ALL_EVENTS
+#undef E
+
+ te_assert_invariants(tsd);
+}
+
+static void
+te_init(tsd_t *tsd, bool is_alloc) {
+ te_ctx_t ctx;
+ te_ctx_get(tsd, &ctx, is_alloc);
+ /*
+ * Reset the last event to current, which starts the events from a clean
+ * state. This is necessary when re-init the tsd event counters.
+ *
+ * The event counters maintain a relationship with the current bytes:
+ * last_event <= current < next_event. When a reinit happens (e.g.
+ * reincarnated tsd), the last event needs progressing because all
+ * events start fresh from the current bytes.
+ */
+ te_ctx_last_event_set(&ctx, te_ctx_current_bytes_get(&ctx));
+
+ uint64_t wait = TE_MAX_START_WAIT;
+#define E(event, condition, alloc_event) \
+ if (is_alloc == alloc_event && condition) { \
+ uint64_t event_wait = event##_new_event_wait(tsd); \
+ event_wait = te_clip_event_wait(event_wait); \
+ event##_event_wait_set(tsd, event_wait); \
+ if (event_wait < wait) { \
+ wait = event_wait; \
+ } \
+ }
+
+ ITERATE_OVER_ALL_EVENTS
+#undef E
+ te_adjust_thresholds_helper(tsd, &ctx, wait);
+}
+
+void
+tsd_te_init(tsd_t *tsd) {
+ /* Make sure no overflow for the bytes accumulated on event_trigger. */
+ assert(TE_MAX_INTERVAL <= UINT64_MAX - SC_LARGE_MAXCLASS + 1);
+ te_init(tsd, true);
+ te_init(tsd, false);
+ te_assert_invariants(tsd);
+}
diff --git a/contrib/jemalloc/src/ticker.c b/contrib/jemalloc/src/ticker.c
index d7b8cd26c068..790b5c20079c 100644
--- a/contrib/jemalloc/src/ticker.c
+++ b/contrib/jemalloc/src/ticker.c
@@ -1,3 +1,32 @@
-#define JEMALLOC_TICKER_C_
#include "jemalloc/internal/jemalloc_preamble.h"
#include "jemalloc/internal/jemalloc_internal_includes.h"
+
+/*
+ * To avoid using floating point math down core paths (still necessary because
+ * versions of the glibc dynamic loader that did not preserve xmm registers are
+ * still somewhat common, requiring us to be compilable with -mno-sse), and also
+ * to avoid generally expensive library calls, we use a precomputed table of
+ * values. We want to sample U uniformly on [0, 1], and then compute
+ * ceil(log(u)/log(1-1/nticks)). We're mostly interested in the case where
+ * nticks is reasonably big, so 1/log(1-1/nticks) is well-approximated by
+ * -nticks.
+ *
+ * To compute log(u), we sample an integer in [1, 64] and divide, then just look
+ * up results in a table. As a space-compression mechanism, we store these as
+ * uint8_t by dividing the range (255) by the highest-magnitude value the log
+ * can take on, and using that as a multiplier. We then have to divide by that
+ * multiplier at the end of the computation.
+ *
+ * The values here are computed in src/ticker.py
+ */
+
+const uint8_t ticker_geom_table[1 << TICKER_GEOM_NBITS] = {
+ 254, 211, 187, 169, 156, 144, 135, 127,
+ 120, 113, 107, 102, 97, 93, 89, 85,
+ 81, 77, 74, 71, 68, 65, 62, 60,
+ 57, 55, 53, 50, 48, 46, 44, 42,
+ 40, 39, 37, 35, 33, 32, 30, 29,
+ 27, 26, 24, 23, 21, 20, 19, 18,
+ 16, 15, 14, 13, 12, 10, 9, 8,
+ 7, 6, 5, 4, 3, 2, 1, 0
+};
diff --git a/contrib/jemalloc/src/ticker.py b/contrib/jemalloc/src/ticker.py
new file mode 100755
index 000000000000..3807740c30f2
--- /dev/null
+++ b/contrib/jemalloc/src/ticker.py
@@ -0,0 +1,15 @@
+#!/usr/bin/env python3
+
+import math
+
+# Must match TICKER_GEOM_NBITS
+lg_table_size = 6
+table_size = 2**lg_table_size
+byte_max = 255
+mul = math.floor(-byte_max/math.log(1 / table_size))
+values = [round(-mul * math.log(i / table_size))
+ for i in range(1, table_size+1)]
+print("mul =", mul)
+print("values:")
+for i in range(table_size // 8):
+ print(", ".join((str(x) for x in values[i*8 : i*8 + 8])))
diff --git a/contrib/jemalloc/src/tsd.c b/contrib/jemalloc/src/tsd.c
index a31f6b9698e5..e8e4f3a33959 100644
--- a/contrib/jemalloc/src/tsd.c
+++ b/contrib/jemalloc/src/tsd.c
@@ -1,17 +1,14 @@
-#define JEMALLOC_TSD_C_
#include "jemalloc/internal/jemalloc_preamble.h"
#include "jemalloc/internal/jemalloc_internal_includes.h"
#include "jemalloc/internal/assert.h"
+#include "jemalloc/internal/san.h"
#include "jemalloc/internal/mutex.h"
#include "jemalloc/internal/rtree.h"
/******************************************************************************/
/* Data. */
-static unsigned ncleanups;
-static malloc_tsd_cleanup_t cleanups[MALLOC_TSD_CLEANUPS_MAX];
-
/* TSD_INITIALIZER triggers "-Wmissing-field-initializer" */
JEMALLOC_DIAGNOSTIC_PUSH
JEMALLOC_DIAGNOSTIC_IGNORE_MISSING_STRUCT_FIELD_INITIALIZERS
@@ -74,7 +71,7 @@ tsd_in_nominal_list(tsd_t *tsd) {
* out of it here.
*/
malloc_mutex_lock(TSDN_NULL, &tsd_nominal_tsds_lock);
- ql_foreach(tsd_list, &tsd_nominal_tsds, TSD_MANGLE(tcache).tsd_link) {
+ ql_foreach(tsd_list, &tsd_nominal_tsds, TSD_MANGLE(tsd_link)) {
if (tsd == tsd_list) {
found = true;
break;
@@ -88,9 +85,9 @@ static void
tsd_add_nominal(tsd_t *tsd) {
assert(!tsd_in_nominal_list(tsd));
assert(tsd_state_get(tsd) <= tsd_state_nominal_max);
- ql_elm_new(tsd, TSD_MANGLE(tcache).tsd_link);
+ ql_elm_new(tsd, TSD_MANGLE(tsd_link));
malloc_mutex_lock(tsd_tsdn(tsd), &tsd_nominal_tsds_lock);
- ql_tail_insert(&tsd_nominal_tsds, tsd, TSD_MANGLE(tcache).tsd_link);
+ ql_tail_insert(&tsd_nominal_tsds, tsd, TSD_MANGLE(tsd_link));
malloc_mutex_unlock(tsd_tsdn(tsd), &tsd_nominal_tsds_lock);
}
@@ -99,7 +96,7 @@ tsd_remove_nominal(tsd_t *tsd) {
assert(tsd_in_nominal_list(tsd));
assert(tsd_state_get(tsd) <= tsd_state_nominal_max);
malloc_mutex_lock(tsd_tsdn(tsd), &tsd_nominal_tsds_lock);
- ql_remove(&tsd_nominal_tsds, tsd, TSD_MANGLE(tcache).tsd_link);
+ ql_remove(&tsd_nominal_tsds, tsd, TSD_MANGLE(tsd_link));
malloc_mutex_unlock(tsd_tsdn(tsd), &tsd_nominal_tsds_lock);
}
@@ -112,11 +109,14 @@ tsd_force_recompute(tsdn_t *tsdn) {
atomic_fence(ATOMIC_RELEASE);
malloc_mutex_lock(tsdn, &tsd_nominal_tsds_lock);
tsd_t *remote_tsd;
- ql_foreach(remote_tsd, &tsd_nominal_tsds, TSD_MANGLE(tcache).tsd_link) {
+ ql_foreach(remote_tsd, &tsd_nominal_tsds, TSD_MANGLE(tsd_link)) {
assert(tsd_atomic_load(&remote_tsd->state, ATOMIC_RELAXED)
<= tsd_state_nominal_max);
- tsd_atomic_store(&remote_tsd->state, tsd_state_nominal_recompute,
- ATOMIC_RELAXED);
+ tsd_atomic_store(&remote_tsd->state,
+ tsd_state_nominal_recompute, ATOMIC_RELAXED);
+ /* See comments in te_recompute_fast_threshold(). */
+ atomic_fence(ATOMIC_SEQ_CST);
+ te_next_event_fast_set_non_nominal(remote_tsd);
}
malloc_mutex_unlock(tsdn, &tsd_nominal_tsds_lock);
}
@@ -175,6 +175,8 @@ tsd_slow_update(tsd_t *tsd) {
old_state = tsd_atomic_exchange(&tsd->state, new_state,
ATOMIC_ACQUIRE);
} while (old_state == tsd_state_nominal_recompute);
+
+ te_recompute_fast_threshold(tsd);
}
void
@@ -207,22 +209,17 @@ tsd_state_set(tsd_t *tsd, uint8_t new_state) {
/*
* This is the tricky case. We're transitioning from
* one nominal state to another. The caller can't know
- * about any races that are occuring at the same time,
+ * about any races that are occurring at the same time,
* so we always have to recompute no matter what.
*/
tsd_slow_update(tsd);
}
}
+ te_recompute_fast_threshold(tsd);
}
-static bool
-tsd_data_init(tsd_t *tsd) {
- /*
- * We initialize the rtree context first (before the tcache), since the
- * tcache initialization depends on it.
- */
- rtree_ctx_data_init(tsd_rtree_ctxp_get_unsafe(tsd));
-
+static void
+tsd_prng_state_init(tsd_t *tsd) {
/*
* A nondeterministic seed based on the address of tsd reduces
* the likelihood of lockstep non-uniform cache index
@@ -230,9 +227,20 @@ tsd_data_init(tsd_t *tsd) {
* cost of test repeatability. For debug builds, instead use a
* deterministic seed.
*/
- *tsd_offset_statep_get(tsd) = config_debug ? 0 :
+ *tsd_prng_statep_get(tsd) = config_debug ? 0 :
(uint64_t)(uintptr_t)tsd;
+}
+static bool
+tsd_data_init(tsd_t *tsd) {
+ /*
+ * We initialize the rtree context first (before the tcache), since the
+ * tcache initialization depends on it.
+ */
+ rtree_ctx_data_init(tsd_rtree_ctxp_get_unsafe(tsd));
+ tsd_prng_state_init(tsd);
+ tsd_te_init(tsd); /* event_init may use the prng state above. */
+ tsd_san_init(tsd);
return tsd_tcache_enabled_data_init(tsd);
}
@@ -242,8 +250,6 @@ assert_tsd_data_cleanup_done(tsd_t *tsd) {
assert(!tsd_in_nominal_list(tsd));
assert(*tsd_arenap_get_unsafe(tsd) == NULL);
assert(*tsd_iarenap_get_unsafe(tsd) == NULL);
- assert(*tsd_arenas_tdata_bypassp_get_unsafe(tsd) == true);
- assert(*tsd_arenas_tdatap_get_unsafe(tsd) == NULL);
assert(*tsd_tcache_enabledp_get_unsafe(tsd) == false);
assert(*tsd_prof_tdatap_get_unsafe(tsd) == NULL);
}
@@ -258,9 +264,11 @@ tsd_data_init_nocleanup(tsd_t *tsd) {
* We set up tsd in a way that no cleanup is needed.
*/
rtree_ctx_data_init(tsd_rtree_ctxp_get_unsafe(tsd));
- *tsd_arenas_tdata_bypassp_get(tsd) = true;
*tsd_tcache_enabledp_get_unsafe(tsd) = false;
*tsd_reentrancy_levelp_get(tsd) = 1;
+ tsd_prng_state_init(tsd);
+ tsd_te_init(tsd); /* event_init may use the prng state above. */
+ tsd_san_init(tsd);
assert_tsd_data_cleanup_done(tsd);
return false;
@@ -326,6 +334,9 @@ malloc_tsd_dalloc(void *wrapper) {
}
#if defined(JEMALLOC_MALLOC_THREAD_CLEANUP) || defined(_WIN32)
+static unsigned ncleanups;
+static malloc_tsd_cleanup_t cleanups[MALLOC_TSD_CLEANUPS_MAX];
+
#ifndef _WIN32
JEMALLOC_EXPORT
#endif
@@ -350,23 +361,27 @@ _malloc_thread_cleanup(void) {
}
} while (again);
}
-#endif
+#ifndef _WIN32
+JEMALLOC_EXPORT
+#endif
void
-malloc_tsd_cleanup_register(bool (*f)(void)) {
+_malloc_tsd_cleanup_register(bool (*f)(void)) {
assert(ncleanups < MALLOC_TSD_CLEANUPS_MAX);
cleanups[ncleanups] = f;
ncleanups++;
}
+#endif
+
static void
tsd_do_data_cleanup(tsd_t *tsd) {
prof_tdata_cleanup(tsd);
iarena_cleanup(tsd);
arena_cleanup(tsd);
- arenas_tdata_cleanup(tsd);
tcache_cleanup(tsd);
witnesses_cleanup(tsd_witness_tsdp_get_unsafe(tsd));
+ *tsd_reentrancy_levelp_get(tsd) = 1;
}
void
@@ -387,7 +402,7 @@ tsd_cleanup(void *arg) {
* is still called for testing and completeness.
*/
assert_tsd_data_cleanup_done(tsd);
- /* Fall through. */
+ JEMALLOC_FALLTHROUGH;
case tsd_state_nominal:
case tsd_state_nominal_slow:
tsd_do_data_cleanup(tsd);
@@ -418,7 +433,9 @@ tsd_t *
malloc_tsd_boot0(void) {
tsd_t *tsd;
+#if defined(JEMALLOC_MALLOC_THREAD_CLEANUP) || defined(_WIN32)
ncleanups = 0;
+#endif
if (malloc_mutex_init(&tsd_nominal_tsds_lock, "tsd_nominal_tsds_lock",
WITNESS_RANK_OMIT, malloc_mutex_rank_exclusive)) {
return NULL;
@@ -427,7 +444,6 @@ malloc_tsd_boot0(void) {
return NULL;
}
tsd = tsd_fetch();
- *tsd_arenas_tdata_bypassp_get(tsd) = true;
return tsd;
}
@@ -437,7 +453,6 @@ malloc_tsd_boot1(void) {
tsd_t *tsd = tsd_fetch();
/* malloc_slow has been set properly. Update tsd_slow. */
tsd_slow_update(tsd);
- *tsd_arenas_tdata_bypassp_get(tsd) = false;
}
#ifdef _WIN32
diff --git a/contrib/jemalloc/src/witness.c b/contrib/jemalloc/src/witness.c
index f42b72ad1a2c..4474af04c8dc 100644
--- a/contrib/jemalloc/src/witness.c
+++ b/contrib/jemalloc/src/witness.c
@@ -1,4 +1,3 @@
-#define JEMALLOC_WITNESS_C_
#include "jemalloc/internal/jemalloc_preamble.h"
#include "jemalloc/internal/jemalloc_internal_includes.h"
@@ -15,14 +14,41 @@ witness_init(witness_t *witness, const char *name, witness_rank_t rank,
}
static void
-witness_lock_error_impl(const witness_list_t *witnesses,
- const witness_t *witness) {
- witness_t *w;
+witness_print_witness(witness_t *w, unsigned n) {
+ assert(n > 0);
+ if (n == 1) {
+ malloc_printf(" %s(%u)", w->name, w->rank);
+ } else {
+ malloc_printf(" %s(%u)X%u", w->name, w->rank, n);
+ }
+}
- malloc_printf("<jemalloc>: Lock rank order reversal:");
+static void
+witness_print_witnesses(const witness_list_t *witnesses) {
+ witness_t *w, *last = NULL;
+ unsigned n = 0;
ql_foreach(w, witnesses, link) {
- malloc_printf(" %s(%u)", w->name, w->rank);
+ if (last != NULL && w->rank > last->rank) {
+ assert(w->name != last->name);
+ witness_print_witness(last, n);
+ n = 0;
+ } else if (last != NULL) {
+ assert(w->rank == last->rank);
+ assert(w->name == last->name);
+ }
+ last = w;
+ ++n;
}
+ if (last != NULL) {
+ witness_print_witness(last, n);
+ }
+}
+
+static void
+witness_lock_error_impl(const witness_list_t *witnesses,
+ const witness_t *witness) {
+ malloc_printf("<jemalloc>: Lock rank order reversal:");
+ witness_print_witnesses(witnesses);
malloc_printf(" %s(%u)\n", witness->name, witness->rank);
abort();
}
@@ -49,13 +75,9 @@ witness_not_owner_error_t *JET_MUTABLE witness_not_owner_error =
static void
witness_depth_error_impl(const witness_list_t *witnesses,
witness_rank_t rank_inclusive, unsigned depth) {
- witness_t *w;
-
malloc_printf("<jemalloc>: Should own %u lock%s of rank >= %u:", depth,
(depth != 1) ? "s" : "", rank_inclusive);
- ql_foreach(w, witnesses, link) {
- malloc_printf(" %s(%u)", w->name, w->rank);
- }
+ witness_print_witnesses(witnesses);
malloc_printf("\n");
abort();
}
diff --git a/contrib/jemalloc/src/zone.c b/contrib/jemalloc/src/zone.c
new file mode 100644
index 000000000000..23dfdd04a91d
--- /dev/null
+++ b/contrib/jemalloc/src/zone.c
@@ -0,0 +1,469 @@
+#include "jemalloc/internal/jemalloc_preamble.h"
+#include "jemalloc/internal/jemalloc_internal_includes.h"
+
+#include "jemalloc/internal/assert.h"
+
+#ifndef JEMALLOC_ZONE
+# error "This source file is for zones on Darwin (OS X)."
+#endif
+
+/* Definitions of the following structs in malloc/malloc.h might be too old
+ * for the built binary to run on newer versions of OSX. So use the newest
+ * possible version of those structs.
+ */
+typedef struct _malloc_zone_t {
+ void *reserved1;
+ void *reserved2;
+ size_t (*size)(struct _malloc_zone_t *, const void *);
+ void *(*malloc)(struct _malloc_zone_t *, size_t);
+ void *(*calloc)(struct _malloc_zone_t *, size_t, size_t);
+ void *(*valloc)(struct _malloc_zone_t *, size_t);
+ void (*free)(struct _malloc_zone_t *, void *);
+ void *(*realloc)(struct _malloc_zone_t *, void *, size_t);
+ void (*destroy)(struct _malloc_zone_t *);
+ const char *zone_name;
+ unsigned (*batch_malloc)(struct _malloc_zone_t *, size_t, void **, unsigned);
+ void (*batch_free)(struct _malloc_zone_t *, void **, unsigned);
+ struct malloc_introspection_t *introspect;
+ unsigned version;
+ void *(*memalign)(struct _malloc_zone_t *, size_t, size_t);
+ void (*free_definite_size)(struct _malloc_zone_t *, void *, size_t);
+ size_t (*pressure_relief)(struct _malloc_zone_t *, size_t);
+} malloc_zone_t;
+
+typedef struct {
+ vm_address_t address;
+ vm_size_t size;
+} vm_range_t;
+
+typedef struct malloc_statistics_t {
+ unsigned blocks_in_use;
+ size_t size_in_use;
+ size_t max_size_in_use;
+ size_t size_allocated;
+} malloc_statistics_t;
+
+typedef kern_return_t memory_reader_t(task_t, vm_address_t, vm_size_t, void **);
+
+typedef void vm_range_recorder_t(task_t, void *, unsigned type, vm_range_t *, unsigned);
+
+typedef struct malloc_introspection_t {
+ kern_return_t (*enumerator)(task_t, void *, unsigned, vm_address_t, memory_reader_t, vm_range_recorder_t);
+ size_t (*good_size)(malloc_zone_t *, size_t);
+ boolean_t (*check)(malloc_zone_t *);
+ void (*print)(malloc_zone_t *, boolean_t);
+ void (*log)(malloc_zone_t *, void *);
+ void (*force_lock)(malloc_zone_t *);
+ void (*force_unlock)(malloc_zone_t *);
+ void (*statistics)(malloc_zone_t *, malloc_statistics_t *);
+ boolean_t (*zone_locked)(malloc_zone_t *);
+ boolean_t (*enable_discharge_checking)(malloc_zone_t *);
+ boolean_t (*disable_discharge_checking)(malloc_zone_t *);
+ void (*discharge)(malloc_zone_t *, void *);
+#ifdef __BLOCKS__
+ void (*enumerate_discharged_pointers)(malloc_zone_t *, void (^)(void *, void *));
+#else
+ void *enumerate_unavailable_without_blocks;
+#endif
+ void (*reinit_lock)(malloc_zone_t *);
+} malloc_introspection_t;
+
+extern kern_return_t malloc_get_all_zones(task_t, memory_reader_t, vm_address_t **, unsigned *);
+
+extern malloc_zone_t *malloc_default_zone(void);
+
+extern void malloc_zone_register(malloc_zone_t *zone);
+
+extern void malloc_zone_unregister(malloc_zone_t *zone);
+
+/*
+ * The malloc_default_purgeable_zone() function is only available on >= 10.6.
+ * We need to check whether it is present at runtime, thus the weak_import.
+ */
+extern malloc_zone_t *malloc_default_purgeable_zone(void)
+JEMALLOC_ATTR(weak_import);
+
+/******************************************************************************/
+/* Data. */
+
+static malloc_zone_t *default_zone, *purgeable_zone;
+static malloc_zone_t jemalloc_zone;
+static struct malloc_introspection_t jemalloc_zone_introspect;
+static pid_t zone_force_lock_pid = -1;
+
+/******************************************************************************/
+/* Function prototypes for non-inline static functions. */
+
+static size_t zone_size(malloc_zone_t *zone, const void *ptr);
+static void *zone_malloc(malloc_zone_t *zone, size_t size);
+static void *zone_calloc(malloc_zone_t *zone, size_t num, size_t size);
+static void *zone_valloc(malloc_zone_t *zone, size_t size);
+static void zone_free(malloc_zone_t *zone, void *ptr);
+static void *zone_realloc(malloc_zone_t *zone, void *ptr, size_t size);
+static void *zone_memalign(malloc_zone_t *zone, size_t alignment,
+ size_t size);
+static void zone_free_definite_size(malloc_zone_t *zone, void *ptr,
+ size_t size);
+static void zone_destroy(malloc_zone_t *zone);
+static unsigned zone_batch_malloc(struct _malloc_zone_t *zone, size_t size,
+ void **results, unsigned num_requested);
+static void zone_batch_free(struct _malloc_zone_t *zone,
+ void **to_be_freed, unsigned num_to_be_freed);
+static size_t zone_pressure_relief(struct _malloc_zone_t *zone, size_t goal);
+static size_t zone_good_size(malloc_zone_t *zone, size_t size);
+static kern_return_t zone_enumerator(task_t task, void *data, unsigned type_mask,
+ vm_address_t zone_address, memory_reader_t reader,
+ vm_range_recorder_t recorder);
+static boolean_t zone_check(malloc_zone_t *zone);
+static void zone_print(malloc_zone_t *zone, boolean_t verbose);
+static void zone_log(malloc_zone_t *zone, void *address);
+static void zone_force_lock(malloc_zone_t *zone);
+static void zone_force_unlock(malloc_zone_t *zone);
+static void zone_statistics(malloc_zone_t *zone,
+ malloc_statistics_t *stats);
+static boolean_t zone_locked(malloc_zone_t *zone);
+static void zone_reinit_lock(malloc_zone_t *zone);
+
+/******************************************************************************/
+/*
+ * Functions.
+ */
+
+static size_t
+zone_size(malloc_zone_t *zone, const void *ptr) {
+ /*
+ * There appear to be places within Darwin (such as setenv(3)) that
+ * cause calls to this function with pointers that *no* zone owns. If
+ * we knew that all pointers were owned by *some* zone, we could split
+ * our zone into two parts, and use one as the default allocator and
+ * the other as the default deallocator/reallocator. Since that will
+ * not work in practice, we must check all pointers to assure that they
+ * reside within a mapped extent before determining size.
+ */
+ return ivsalloc(tsdn_fetch(), ptr);
+}
+
+static void *
+zone_malloc(malloc_zone_t *zone, size_t size) {
+ return je_malloc(size);
+}
+
+static void *
+zone_calloc(malloc_zone_t *zone, size_t num, size_t size) {
+ return je_calloc(num, size);
+}
+
+static void *
+zone_valloc(malloc_zone_t *zone, size_t size) {
+ void *ret = NULL; /* Assignment avoids useless compiler warning. */
+
+ je_posix_memalign(&ret, PAGE, size);
+
+ return ret;
+}
+
+static void
+zone_free(malloc_zone_t *zone, void *ptr) {
+ if (ivsalloc(tsdn_fetch(), ptr) != 0) {
+ je_free(ptr);
+ return;
+ }
+
+ free(ptr);
+}
+
+static void *
+zone_realloc(malloc_zone_t *zone, void *ptr, size_t size) {
+ if (ivsalloc(tsdn_fetch(), ptr) != 0) {
+ return je_realloc(ptr, size);
+ }
+
+ return realloc(ptr, size);
+}
+
+static void *
+zone_memalign(malloc_zone_t *zone, size_t alignment, size_t size) {
+ void *ret = NULL; /* Assignment avoids useless compiler warning. */
+
+ je_posix_memalign(&ret, alignment, size);
+
+ return ret;
+}
+
+static void
+zone_free_definite_size(malloc_zone_t *zone, void *ptr, size_t size) {
+ size_t alloc_size;
+
+ alloc_size = ivsalloc(tsdn_fetch(), ptr);
+ if (alloc_size != 0) {
+ assert(alloc_size == size);
+ je_free(ptr);
+ return;
+ }
+
+ free(ptr);
+}
+
+static void
+zone_destroy(malloc_zone_t *zone) {
+ /* This function should never be called. */
+ not_reached();
+}
+
+static unsigned
+zone_batch_malloc(struct _malloc_zone_t *zone, size_t size, void **results,
+ unsigned num_requested) {
+ unsigned i;
+
+ for (i = 0; i < num_requested; i++) {
+ results[i] = je_malloc(size);
+ if (!results[i])
+ break;
+ }
+
+ return i;
+}
+
+static void
+zone_batch_free(struct _malloc_zone_t *zone, void **to_be_freed,
+ unsigned num_to_be_freed) {
+ unsigned i;
+
+ for (i = 0; i < num_to_be_freed; i++) {
+ zone_free(zone, to_be_freed[i]);
+ to_be_freed[i] = NULL;
+ }
+}
+
+static size_t
+zone_pressure_relief(struct _malloc_zone_t *zone, size_t goal) {
+ return 0;
+}
+
+static size_t
+zone_good_size(malloc_zone_t *zone, size_t size) {
+ if (size == 0) {
+ size = 1;
+ }
+ return sz_s2u(size);
+}
+
+static kern_return_t
+zone_enumerator(task_t task, void *data, unsigned type_mask,
+ vm_address_t zone_address, memory_reader_t reader,
+ vm_range_recorder_t recorder) {
+ return KERN_SUCCESS;
+}
+
+static boolean_t
+zone_check(malloc_zone_t *zone) {
+ return true;
+}
+
+static void
+zone_print(malloc_zone_t *zone, boolean_t verbose) {
+}
+
+static void
+zone_log(malloc_zone_t *zone, void *address) {
+}
+
+static void
+zone_force_lock(malloc_zone_t *zone) {
+ if (isthreaded) {
+ /*
+ * See the note in zone_force_unlock, below, to see why we need
+ * this.
+ */
+ assert(zone_force_lock_pid == -1);
+ zone_force_lock_pid = getpid();
+ jemalloc_prefork();
+ }
+}
+
+static void
+zone_force_unlock(malloc_zone_t *zone) {
+ /*
+ * zone_force_lock and zone_force_unlock are the entry points to the
+ * forking machinery on OS X. The tricky thing is, the child is not
+ * allowed to unlock mutexes locked in the parent, even if owned by the
+ * forking thread (and the mutex type we use in OS X will fail an assert
+ * if we try). In the child, we can get away with reinitializing all
+ * the mutexes, which has the effect of unlocking them. In the parent,
+ * doing this would mean we wouldn't wake any waiters blocked on the
+ * mutexes we unlock. So, we record the pid of the current thread in
+ * zone_force_lock, and use that to detect if we're in the parent or
+ * child here, to decide which unlock logic we need.
+ */
+ if (isthreaded) {
+ assert(zone_force_lock_pid != -1);
+ if (getpid() == zone_force_lock_pid) {
+ jemalloc_postfork_parent();
+ } else {
+ jemalloc_postfork_child();
+ }
+ zone_force_lock_pid = -1;
+ }
+}
+
+static void
+zone_statistics(malloc_zone_t *zone, malloc_statistics_t *stats) {
+ /* We make no effort to actually fill the values */
+ stats->blocks_in_use = 0;
+ stats->size_in_use = 0;
+ stats->max_size_in_use = 0;
+ stats->size_allocated = 0;
+}
+
+static boolean_t
+zone_locked(malloc_zone_t *zone) {
+ /* Pretend no lock is being held */
+ return false;
+}
+
+static void
+zone_reinit_lock(malloc_zone_t *zone) {
+ /* As of OSX 10.12, this function is only used when force_unlock would
+ * be used if the zone version were < 9. So just use force_unlock. */
+ zone_force_unlock(zone);
+}
+
+static void
+zone_init(void) {
+ jemalloc_zone.size = zone_size;
+ jemalloc_zone.malloc = zone_malloc;
+ jemalloc_zone.calloc = zone_calloc;
+ jemalloc_zone.valloc = zone_valloc;
+ jemalloc_zone.free = zone_free;
+ jemalloc_zone.realloc = zone_realloc;
+ jemalloc_zone.destroy = zone_destroy;
+ jemalloc_zone.zone_name = "jemalloc_zone";
+ jemalloc_zone.batch_malloc = zone_batch_malloc;
+ jemalloc_zone.batch_free = zone_batch_free;
+ jemalloc_zone.introspect = &jemalloc_zone_introspect;
+ jemalloc_zone.version = 9;
+ jemalloc_zone.memalign = zone_memalign;
+ jemalloc_zone.free_definite_size = zone_free_definite_size;
+ jemalloc_zone.pressure_relief = zone_pressure_relief;
+
+ jemalloc_zone_introspect.enumerator = zone_enumerator;
+ jemalloc_zone_introspect.good_size = zone_good_size;
+ jemalloc_zone_introspect.check = zone_check;
+ jemalloc_zone_introspect.print = zone_print;
+ jemalloc_zone_introspect.log = zone_log;
+ jemalloc_zone_introspect.force_lock = zone_force_lock;
+ jemalloc_zone_introspect.force_unlock = zone_force_unlock;
+ jemalloc_zone_introspect.statistics = zone_statistics;
+ jemalloc_zone_introspect.zone_locked = zone_locked;
+ jemalloc_zone_introspect.enable_discharge_checking = NULL;
+ jemalloc_zone_introspect.disable_discharge_checking = NULL;
+ jemalloc_zone_introspect.discharge = NULL;
+#ifdef __BLOCKS__
+ jemalloc_zone_introspect.enumerate_discharged_pointers = NULL;
+#else
+ jemalloc_zone_introspect.enumerate_unavailable_without_blocks = NULL;
+#endif
+ jemalloc_zone_introspect.reinit_lock = zone_reinit_lock;
+}
+
+static malloc_zone_t *
+zone_default_get(void) {
+ malloc_zone_t **zones = NULL;
+ unsigned int num_zones = 0;
+
+ /*
+ * On OSX 10.12, malloc_default_zone returns a special zone that is not
+ * present in the list of registered zones. That zone uses a "lite zone"
+ * if one is present (apparently enabled when malloc stack logging is
+ * enabled), or the first registered zone otherwise. In practice this
+ * means unless malloc stack logging is enabled, the first registered
+ * zone is the default. So get the list of zones to get the first one,
+ * instead of relying on malloc_default_zone.
+ */
+ if (KERN_SUCCESS != malloc_get_all_zones(0, NULL,
+ (vm_address_t**)&zones, &num_zones)) {
+ /*
+ * Reset the value in case the failure happened after it was
+ * set.
+ */
+ num_zones = 0;
+ }
+
+ if (num_zones) {
+ return zones[0];
+ }
+
+ return malloc_default_zone();
+}
+
+/* As written, this function can only promote jemalloc_zone. */
+static void
+zone_promote(void) {
+ malloc_zone_t *zone;
+
+ do {
+ /*
+ * Unregister and reregister the default zone. On OSX >= 10.6,
+ * unregistering takes the last registered zone and places it
+ * at the location of the specified zone. Unregistering the
+ * default zone thus makes the last registered one the default.
+ * On OSX < 10.6, unregistering shifts all registered zones.
+ * The first registered zone then becomes the default.
+ */
+ malloc_zone_unregister(default_zone);
+ malloc_zone_register(default_zone);
+
+ /*
+ * On OSX 10.6, having the default purgeable zone appear before
+ * the default zone makes some things crash because it thinks it
+ * owns the default zone allocated pointers. We thus
+ * unregister/re-register it in order to ensure it's always
+ * after the default zone. On OSX < 10.6, there is no purgeable
+ * zone, so this does nothing. On OSX >= 10.6, unregistering
+ * replaces the purgeable zone with the last registered zone
+ * above, i.e. the default zone. Registering it again then puts
+ * it at the end, obviously after the default zone.
+ */
+ if (purgeable_zone != NULL) {
+ malloc_zone_unregister(purgeable_zone);
+ malloc_zone_register(purgeable_zone);
+ }
+
+ zone = zone_default_get();
+ } while (zone != &jemalloc_zone);
+}
+
+JEMALLOC_ATTR(constructor)
+void
+zone_register(void) {
+ /*
+ * If something else replaced the system default zone allocator, don't
+ * register jemalloc's.
+ */
+ default_zone = zone_default_get();
+ if (!default_zone->zone_name || strcmp(default_zone->zone_name,
+ "DefaultMallocZone") != 0) {
+ return;
+ }
+
+ /*
+ * The default purgeable zone is created lazily by OSX's libc. It uses
+ * the default zone when it is created for "small" allocations
+ * (< 15 KiB), but assumes the default zone is a scalable_zone. This
+ * obviously fails when the default zone is the jemalloc zone, so
+ * malloc_default_purgeable_zone() is called beforehand so that the
+ * default purgeable zone is created when the default zone is still
+ * a scalable_zone. As purgeable zones only exist on >= 10.6, we need
+ * to check for the existence of malloc_default_purgeable_zone() at
+ * run time.
+ */
+ purgeable_zone = (malloc_default_purgeable_zone == NULL) ? NULL :
+ malloc_default_purgeable_zone();
+
+ /* Register the custom zone. At this point it won't be the default. */
+ zone_init();
+ malloc_zone_register(&jemalloc_zone);
+
+ /* Promote the custom zone to be default. */
+ zone_promote();
+}
diff --git a/contrib/kyua/doc/kyuafile.5.in b/contrib/kyua/doc/kyuafile.5.in
index ae1e4fe40e32..a9106e95d790 100644
--- a/contrib/kyua/doc/kyuafile.5.in
+++ b/contrib/kyua/doc/kyuafile.5.in
@@ -290,6 +290,16 @@ it can run.
.Pp
ATF:
.Va require.files
+.It Va required_kmods
+Whitespace-separated list of kernel module names that the test requires to
+be loaded before it can run.
+This requirement checking is platform-dependent.
+It is ignored for a non-supported platform.
+Supported platforms:
+.Fx .
+.Pp
+ATF:
+.Va require.kmods
.It Va required_memory
Amount of physical memory that the test needs to run successfully.
.Pp
@@ -475,7 +485,7 @@ plain_test_program{name='the_test',
.Ss FreeBSD jail execution environment
The following example configures the test to be run within a temporary jail
with
-.Xr vnet 9
+.Xr VNET 9
support and the permission to create raw sockets:
.Bd -literal -offset indent
syntax(2)
diff --git a/contrib/kyua/drivers/report_junit_test.cpp b/contrib/kyua/drivers/report_junit_test.cpp
index 0f009c6befd3..1c0929c0fef2 100644
--- a/contrib/kyua/drivers/report_junit_test.cpp
+++ b/contrib/kyua/drivers/report_junit_test.cpp
@@ -70,6 +70,7 @@ static const char* const default_metadata =
"required_configs is empty\n"
"required_disk_space = 0\n"
"required_files is empty\n"
+ "required_kmods is empty\n"
"required_memory = 0\n"
"required_programs is empty\n"
"required_user is empty\n"
@@ -89,6 +90,7 @@ static const char* const overriden_metadata =
"required_configs is empty\n"
"required_disk_space = 0\n"
"required_files is empty\n"
+ "required_kmods is empty\n"
"required_memory = 0\n"
"required_programs is empty\n"
"required_user is empty\n"
@@ -228,6 +230,7 @@ ATF_TEST_CASE_BODY(junit_metadata__overrides)
+ "required_configs = config1\n"
+ "required_disk_space = 456\n"
+ "required_files = file1\n"
+ + "required_kmods is empty\n"
+ "required_memory = 123\n"
+ "required_programs = prog1\n"
+ "required_user = root\n"
diff --git a/contrib/kyua/engine/atf_list.cpp b/contrib/kyua/engine/atf_list.cpp
index e0c4170605d1..5c74a80be913 100644
--- a/contrib/kyua/engine/atf_list.cpp
+++ b/contrib/kyua/engine/atf_list.cpp
@@ -133,10 +133,8 @@ engine::parse_atf_metadata(const model::properties_map& props)
mdbuilder.set_string("required_disk_space", value);
} else if (name == "require.files") {
mdbuilder.set_string("required_files", value);
-#ifdef __FreeBSD__
} else if (name == "require.kmods") {
mdbuilder.set_string("required_kmods", value);
-#endif
} else if (name == "require.machine") {
mdbuilder.set_string("allowed_platforms", value);
} else if (name == "require.memory") {
diff --git a/contrib/kyua/engine/requirements.cpp b/contrib/kyua/engine/requirements.cpp
index dff43e531a57..d5838b83f33a 100644
--- a/contrib/kyua/engine/requirements.cpp
+++ b/contrib/kyua/engine/requirements.cpp
@@ -41,10 +41,6 @@
#include "utils/sanity.hpp"
#include "utils/units.hpp"
-#ifdef __FreeBSD__
-#include <libutil.h>
-#endif
-
namespace config = utils::config;
namespace fs = utils::fs;
namespace passwd = utils::passwd;
@@ -224,26 +220,6 @@ check_required_programs(const model::paths_set& required_programs)
}
-#ifdef __FreeBSD__
-/// Checks if all required kmods are loaded.
-///
-/// \param required_programs Set of kmods.
-///
-/// \return Empty if the required kmods are all loaded or an error
-/// message otherwise.
-static std::string
-check_required_kmods(const model::strings_set& required_kmods)
-{
- for (model::strings_set::const_iterator iter = required_kmods.begin();
- iter != required_kmods.end(); iter++) {
- if (!kld_isloaded((*iter).c_str()))
- return F("Required kmod '%s' not loaded") % *iter;
- }
- return "";
-}
-#endif
-
-
/// Checks if the current system has the specified amount of memory.
///
/// \param required_memory Amount of required physical memory, or zero if not
@@ -289,9 +265,29 @@ check_required_disk_space(const units::bytes& required_disk_space,
}
+/// List of registered extra requirement checkers.
+///
+/// Use register_reqs_checker() to add an entry to this global list.
+static std::vector< std::shared_ptr< engine::reqs_checker > > _reqs_checkers;
+
+
} // anonymous namespace
+const std::vector< std::shared_ptr< engine::reqs_checker > >
+engine::reqs_checkers()
+{
+ return _reqs_checkers;
+}
+
+void
+engine::register_reqs_checker(
+ const std::shared_ptr< engine::reqs_checker > checker)
+{
+ _reqs_checkers.push_back(checker);
+}
+
+
/// Checks if all the requirements specified by the test case are met.
///
/// \param md The test metadata.
@@ -336,12 +332,6 @@ engine::check_reqs(const model::metadata& md, const config::tree& cfg,
if (!reason.empty())
return reason;
-#ifdef __FreeBSD__
- reason = check_required_kmods(md.required_kmods());
- if (!reason.empty())
- return reason;
-#endif
-
reason = check_required_memory(md.required_memory());
if (!reason.empty())
return reason;
@@ -351,6 +341,13 @@ engine::check_reqs(const model::metadata& md, const config::tree& cfg,
if (!reason.empty())
return reason;
+ // Iterate over extra checkers registered.
+ for (auto& checker : engine::reqs_checkers()) {
+ reason = checker->exec(md, cfg, test_suite, work_directory);
+ if (!reason.empty())
+ return reason;
+ }
+
INV(reason.empty());
return reason;
}
diff --git a/contrib/kyua/engine/requirements.hpp b/contrib/kyua/engine/requirements.hpp
index a36a938b3034..92e80c5122aa 100644
--- a/contrib/kyua/engine/requirements.hpp
+++ b/contrib/kyua/engine/requirements.hpp
@@ -44,6 +44,32 @@ namespace engine {
std::string check_reqs(const model::metadata&, const utils::config::tree&,
const std::string&, const utils::fs::path&);
+/// Abstract interface of a requirement checker.
+class reqs_checker {
+public:
+ /// Constructor.
+ reqs_checker() {}
+
+ /// Destructor.
+ virtual ~reqs_checker() {}
+
+ /// Run the checker.
+ virtual std::string exec(const model::metadata&,
+ const utils::config::tree&,
+ const std::string&,
+ const utils::fs::path&) const = 0;
+};
+
+/// Register an extra requirement checker.
+///
+/// \param checker A requirement checker.
+void register_reqs_checker(const std::shared_ptr< reqs_checker > checker);
+
+/// Returns the list of registered extra requirement checkers.
+///
+/// \return A vector of pointers to extra requirement checkers.
+const std::vector< std::shared_ptr< reqs_checker > > reqs_checkers();
+
} // namespace engine
diff --git a/contrib/kyua/integration/cmd_report_junit_test.sh b/contrib/kyua/integration/cmd_report_junit_test.sh
index d86228acf7e5..49b8c5790167 100644
--- a/contrib/kyua/integration/cmd_report_junit_test.sh
+++ b/contrib/kyua/integration/cmd_report_junit_test.sh
@@ -104,6 +104,7 @@ is_exclusive = false
required_configs is empty
required_disk_space = 0
required_files is empty
+required_kmods is empty
required_memory = 0
required_programs is empty
required_user is empty
@@ -144,6 +145,7 @@ is_exclusive = false
required_configs is empty
required_disk_space = 0
required_files is empty
+required_kmods is empty
required_memory = 0
required_programs is empty
required_user is empty
@@ -222,6 +224,7 @@ is_exclusive = false
required_configs is empty
required_disk_space = 0
required_files is empty
+required_kmods is empty
required_memory = 0
required_programs is empty
required_user is empty
@@ -262,6 +265,7 @@ is_exclusive = false
required_configs is empty
required_disk_space = 0
required_files is empty
+required_kmods is empty
required_memory = 0
required_programs is empty
required_user is empty
diff --git a/contrib/kyua/integration/cmd_report_test.sh b/contrib/kyua/integration/cmd_report_test.sh
index 8b2b97f9cb4a..1fc1932d3c47 100644
--- a/contrib/kyua/integration/cmd_report_test.sh
+++ b/contrib/kyua/integration/cmd_report_test.sh
@@ -258,6 +258,7 @@ Metadata:
required_configs is empty
required_disk_space = 0
required_files is empty
+ required_kmods is empty
required_memory = 0
required_programs is empty
required_user is empty
diff --git a/contrib/kyua/model/metadata.cpp b/contrib/kyua/model/metadata.cpp
index a5a9a1315964..afb31435a238 100644
--- a/contrib/kyua/model/metadata.cpp
+++ b/contrib/kyua/model/metadata.cpp
@@ -256,9 +256,7 @@ init_tree(config::tree& tree)
tree.define< bytes_node >("required_disk_space");
tree.define< paths_set_node >("required_files");
tree.define< bytes_node >("required_memory");
-#ifdef __FreeBSD__
tree.define< config::strings_set_node >("required_kmods");
-#endif
tree.define< paths_set_node >("required_programs");
tree.define< user_node >("required_user");
tree.define< delta_node >("timeout");
@@ -285,9 +283,7 @@ set_defaults(config::tree& tree)
tree.set< bytes_node >("required_disk_space", units::bytes(0));
tree.set< paths_set_node >("required_files", model::paths_set());
tree.set< bytes_node >("required_memory", units::bytes(0));
-#ifdef __FreeBSD__
tree.set< config::strings_set_node >("required_kmods", model::strings_set());
-#endif
tree.set< paths_set_node >("required_programs", model::paths_set());
tree.set< user_node >("required_user", "");
// TODO(jmmv): We shouldn't be setting a default timeout like this. See
@@ -603,20 +599,20 @@ model::metadata::required_memory(void) const
}
-#ifdef __FreeBSD__
-/// Returns the list of kmods needed by the test.
+/// Returns the list of kernel modules needed by the test.
///
-/// \return Set of strings.
+/// \return Set of kernel module names.
const model::strings_set&
model::metadata::required_kmods(void) const
{
if (_pimpl->props.is_set("required_kmods")) {
- return _pimpl->props.lookup< config::strings_set_node >("required_kmods");
+ return _pimpl->props.lookup< config::strings_set_node >(
+ "required_kmods");
} else {
- return get_defaults().lookup< config::strings_set_node >("required_kmods");
+ return get_defaults().lookup< config::strings_set_node >(
+ "required_kmods");
}
}
-#endif
/// Returns the list of programs needed by the test.
diff --git a/contrib/kyua/model/metadata.hpp b/contrib/kyua/model/metadata.hpp
index 8af6c7c161af..eee7eaf0f7c4 100644
--- a/contrib/kyua/model/metadata.hpp
+++ b/contrib/kyua/model/metadata.hpp
@@ -76,9 +76,7 @@ public:
const utils::units::bytes& required_disk_space(void) const;
const paths_set& required_files(void) const;
const utils::units::bytes& required_memory(void) const;
-#ifdef __FreeBSD__
const strings_set& required_kmods(void) const;
-#endif
const paths_set& required_programs(void) const;
const std::string& required_user(void) const;
const utils::datetime::delta& timeout(void) const;
@@ -124,9 +122,7 @@ public:
metadata_builder& set_required_disk_space(const utils::units::bytes&);
metadata_builder& set_required_files(const paths_set&);
metadata_builder& set_required_memory(const utils::units::bytes&);
-#ifdef __FreeBSD__
metadata_builder& set_required_kmods(const strings_set&);
-#endif
metadata_builder& set_required_programs(const paths_set&);
metadata_builder& set_required_user(const std::string&);
metadata_builder& set_string(const std::string&, const std::string&);
diff --git a/contrib/kyua/model/metadata_test.cpp b/contrib/kyua/model/metadata_test.cpp
index b4c3dff5b029..bdb1d3655c33 100644
--- a/contrib/kyua/model/metadata_test.cpp
+++ b/contrib/kyua/model/metadata_test.cpp
@@ -57,6 +57,7 @@ ATF_TEST_CASE_BODY(defaults)
ATF_REQUIRE(md.required_configs().empty());
ATF_REQUIRE_EQ(units::bytes(0), md.required_disk_space());
ATF_REQUIRE(md.required_files().empty());
+ ATF_REQUIRE(md.required_kmods().empty());
ATF_REQUIRE_EQ(units::bytes(0), md.required_memory());
ATF_REQUIRE(md.required_programs().empty());
ATF_REQUIRE(md.required_user().empty());
@@ -322,6 +323,7 @@ ATF_TEST_CASE_BODY(to_properties)
props["required_configs"] = "";
props["required_disk_space"] = "0";
props["required_files"] = "bar foo";
+ props["required_kmods"] = "";
props["required_memory"] = "1.00K";
props["required_programs"] = "";
props["required_user"] = "";
@@ -412,7 +414,7 @@ ATF_TEST_CASE_BODY(output__defaults)
"has_cleanup='false', is_exclusive='false', "
"required_configs='', "
"required_disk_space='0', required_files='', "
- "required_memory='0', "
+ "required_kmods='', required_memory='0', "
"required_programs='', required_user='', timeout='300'}",
str.str());
}
@@ -435,7 +437,7 @@ ATF_TEST_CASE_BODY(output__some_values)
"has_cleanup='false', is_exclusive='true', "
"required_configs='', "
"required_disk_space='0', required_files='bar foo', "
- "required_memory='1.00K', "
+ "required_kmods='', required_memory='1.00K', "
"required_programs='', required_user='', timeout='300'}",
str.str());
}
diff --git a/contrib/kyua/model/test_case_test.cpp b/contrib/kyua/model/test_case_test.cpp
index 1e2597d1501e..29df7ee35863 100644
--- a/contrib/kyua/model/test_case_test.cpp
+++ b/contrib/kyua/model/test_case_test.cpp
@@ -204,7 +204,7 @@ ATF_TEST_CASE_BODY(test_case__output)
"has_cleanup='false', "
"is_exclusive='false', "
"required_configs='', required_disk_space='0', required_files='', "
- "required_memory='0', "
+ "required_kmods='', required_memory='0', "
"required_programs='', required_user='', timeout='300'}}",
str.str());
}
diff --git a/contrib/kyua/model/test_program_test.cpp b/contrib/kyua/model/test_program_test.cpp
index ddfbc430387c..f7a84d770fc0 100644
--- a/contrib/kyua/model/test_program_test.cpp
+++ b/contrib/kyua/model/test_program_test.cpp
@@ -547,7 +547,7 @@ check_output__no_test_cases(void)
"description='', execenv='', execenv_jail_params='', "
"has_cleanup='false', is_exclusive='false', "
"required_configs='', required_disk_space='0', required_files='', "
- "required_memory='0', "
+ "required_kmods='', required_memory='0', "
"required_programs='', required_user='', timeout='300'}, "
"test_cases=map()}",
str.str());
@@ -597,7 +597,7 @@ check_output__some_test_cases(void)
"description='', execenv='', execenv_jail_params='', "
"has_cleanup='false', is_exclusive='false', "
"required_configs='', required_disk_space='0', required_files='', "
- "required_memory='0', "
+ "required_kmods='', required_memory='0', "
"required_programs='', required_user='', timeout='300'}, "
"test_cases=map("
"another-name=test_case{name='another-name', "
@@ -605,14 +605,14 @@ check_output__some_test_cases(void)
"description='', execenv='', execenv_jail_params='', "
"has_cleanup='false', is_exclusive='false', "
"required_configs='', required_disk_space='0', required_files='', "
- "required_memory='0', "
+ "required_kmods='', required_memory='0', "
"required_programs='', required_user='', timeout='300'}}, "
"the-name=test_case{name='the-name', "
"metadata=metadata{allowed_architectures='a', allowed_platforms='foo', "
"custom.bar='baz', description='', execenv='', execenv_jail_params='', "
"has_cleanup='false', is_exclusive='false', "
"required_configs='', required_disk_space='0', required_files='', "
- "required_memory='0', "
+ "required_kmods='', required_memory='0', "
"required_programs='', required_user='', timeout='300'}})}",
str.str());
}
diff --git a/contrib/kyua/os/freebsd/main.cpp b/contrib/kyua/os/freebsd/main.cpp
index 13e5dcf0e023..700284b64b78 100644
--- a/contrib/kyua/os/freebsd/main.cpp
+++ b/contrib/kyua/os/freebsd/main.cpp
@@ -31,6 +31,9 @@
#include "engine/execenv/execenv.hpp"
#include "os/freebsd/execenv_jail_manager.hpp"
+#include "engine/requirements.hpp"
+#include "os/freebsd/reqs_checker_kmods.hpp"
+
namespace execenv = engine::execenv;
/// FreeBSD related features initialization.
@@ -50,5 +53,13 @@ freebsd::main(const int, const char* const* const)
std::shared_ptr< execenv::manager >(new freebsd::execenv_jail_manager())
);
+#ifdef __FreeBSD__
+ engine::register_reqs_checker(
+ std::shared_ptr< engine::reqs_checker >(
+ new freebsd::reqs_checker_kmods()
+ )
+ );
+#endif
+
return 0;
}
diff --git a/contrib/kyua/os/freebsd/reqs_checker_kmods.cpp b/contrib/kyua/os/freebsd/reqs_checker_kmods.cpp
new file mode 100644
index 000000000000..3ae3446a7815
--- /dev/null
+++ b/contrib/kyua/os/freebsd/reqs_checker_kmods.cpp
@@ -0,0 +1,50 @@
+// Copyright 2025 The Kyua Authors.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of Google Inc. nor the names of its contributors
+// may be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "os/freebsd/reqs_checker_kmods.hpp"
+
+#include "model/metadata.hpp"
+
+extern "C" {
+#include "libutil.h"
+}
+
+std::string
+freebsd::reqs_checker_kmods::exec(const model::metadata& md,
+ const utils::config::tree&,
+ const std::string&,
+ const utils::fs::path&) const
+{
+ std::string reason = "";
+ for (auto& kmod : md.required_kmods())
+ if (!::kld_isloaded((kmod).c_str()))
+ reason += " " + kmod;
+ if (!reason.empty())
+ reason = "Required kmods are not loaded:" + reason + ".";
+ return reason;
+}
diff --git a/contrib/kyua/os/freebsd/reqs_checker_kmods.hpp b/contrib/kyua/os/freebsd/reqs_checker_kmods.hpp
new file mode 100644
index 000000000000..8c7c69e35d07
--- /dev/null
+++ b/contrib/kyua/os/freebsd/reqs_checker_kmods.hpp
@@ -0,0 +1,54 @@
+// Copyright 2025 The Kyua Authors.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+// * Neither the name of Google Inc. nor the names of its contributors
+// may be used to endorse or promote products derived from this software
+// without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/// \file os/freebsd/reqs_checker_kmods.hpp
+/// FreeBSD kernel module requirement checker.
+
+#if !defined(FREEBSD_REQS_CHECKER_KMODS)
+#define FREEBSD_REQS_CHECKER_KMODS
+
+#include "engine/requirements.hpp"
+#include "model/metadata_fwd.hpp"
+#include "utils/config/tree_fwd.hpp"
+#include "utils/fs/path_fwd.hpp"
+
+namespace freebsd {
+
+
+class reqs_checker_kmods : public engine::reqs_checker {
+public:
+ std::string exec(const model::metadata&,
+ const utils::config::tree&,
+ const std::string&,
+ const utils::fs::path&) const;
+};
+
+
+} // namespace freebsd
+
+#endif // !defined(FREEBSD_REQS_CHECKER_KMODS)
diff --git a/contrib/libucl/CMakeLists.txt b/contrib/libucl/CMakeLists.txt
deleted file mode 100644
index 5fe772a30f88..000000000000
--- a/contrib/libucl/CMakeLists.txt
+++ /dev/null
@@ -1,314 +0,0 @@
-PROJECT(libucl C)
-CMAKE_MINIMUM_REQUIRED(VERSION 2.6.0 FATAL_ERROR)
-
-SET(LIBUCL_VERSION_MAJOR 0)
-SET(LIBUCL_VERSION_MINOR 5)
-SET(LIBUCL_VERSION_PATCH 0)
-
-SET(LIBUCL_VERSION
- "${LIBUCL_VERSION_MAJOR}.${LIBUCL_VERSION_MINOR}.${LIBUCL_VERSION_PATCH}")
-
-INCLUDE(CheckCCompilerFlag)
-INCLUDE(CheckCSourceCompiles)
-INCLUDE(FindOpenSSL)
-INCLUDE(GNUInstallDirs)
-
-OPTION(ENABLE_URL_INCLUDE "Enable urls in ucl includes (requires libcurl or libfetch) [default: OFF]" OFF)
-OPTION(ENABLE_URL_SIGN "Enable signatures check in ucl includes (requires openssl) [default: OFF]" OFF)
-OPTION(BUILD_SHARED_LIBS "Build Shared Libraries [default: OFF]" OFF)
-OPTION(ENABLE_LUA "Enable lua support [default: OFF]" OFF)
-OPTION(ENABLE_LUAJIT "Enable luajit support [default: OFF]" OFF)
-OPTION(ENABLE_UTILS "Enable building utility binaries [default: OFF]" OFF)
-
-# Find lua installation
-MACRO(FindLua)
- # Find lua libraries
- UNSET(LUA_INCLUDE_DIR CACHE)
- UNSET(LUA_LIBRARY CACHE)
- CMAKE_PARSE_ARGUMENTS(LUA "" "VERSION_MAJOR;VERSION_MINOR;ROOT" "" ${ARGN})
-
- IF(NOT LUA_VERSION_MAJOR OR NOT LUA_VERSION_MINOR)
- MESSAGE(FATAL_ERROR "Invalid FindLua invocation: ${ARGN}")
- ENDIF()
-
- IF(ENABLE_LUAJIT MATCHES "ON")
- MESSAGE(STATUS "Check for luajit ${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}")
- FIND_PATH(LUA_INCLUDE_DIR luajit.h
- HINTS
- "${RSPAMD_SEARCH_PATH}" "${LUA_ROOT}"
- $ENV{LUA_DIR}
- PATH_SUFFIXES "include/luajit-2.0"
- "include/luajit${LUA_VERSION_MAJOR}${LUA_VERSION_MINOR}"
- "include/luajit${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}"
- "include/luajit-${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}"
- "include/luajit"
- "include/lua${LUA_VERSION_MAJOR}${LUA_VERSION_MINOR}"
- "include/lua${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}"
- "include/lua-${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}"
- include/lua include
- PATHS ${RSPAMD_DEFAULT_INCLUDE_PATHS}
- )
- FIND_LIBRARY(LUA_LIBRARY
- NAMES luajit
- "luajit-2.0"
- "luajit2.0"
- "luajit${LUA_VERSION_MAJOR}${LUA_VERSION_MINOR}"
- "luajit${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}"
- "luajit-${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}"
- HINTS
- "${RSPAMD_SEARCH_PATH}" "${LUA_ROOT}"
- $ENV{LUA_DIR}
- PATH_SUFFIXES lib64 lib
- PATHS ${RSPAMD_DEFAULT_LIBRARY_PATHS}
- DOC "Lua library"
- )
-
- IF(NOT LUA_LIBRARY OR NOT LUA_INCLUDE_DIR)
- MESSAGE(STATUS "Fallback from luajit to plain lua")
- SET(ENABLE_LUAJIT "OFF")
- MESSAGE(STATUS "Check for lua ${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}")
- FIND_PATH(LUA_INCLUDE_DIR lua.h
- HINTS
- "${RSPAMD_SEARCH_PATH}" "${LUA_ROOT}"
- $ENV{LUA_DIR}
- PATH_SUFFIXES "include/lua${LUA_VERSION_MAJOR}${LUA_VERSION_MINOR}"
- "include/lua${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}"
- "include/lua-${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}"
- include/lua include
- PATHS ${RSPAMD_DEFAULT_INCLUDE_PATHS}
- )
- FIND_LIBRARY(LUA_LIBRARY
- NAMES lua
- "lua${LUA_VERSION_MAJOR}${LUA_VERSION_MINOR}"
- "lua${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}"
- "lua-${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}"
- HINTS
- "${RSPAMD_SEARCH_PATH}" "${LUA_ROOT}"
- $ENV{LUA_DIR}
- PATH_SUFFIXES lib64 lib
- PATHS ${RSPAMD_DEFAULT_LIBRARY_PATHS}
- DOC "Lua library"
- )
- ENDIF()
- ELSE(ENABLE_LUAJIT MATCHES "ON")
- MESSAGE(STATUS "Check for lua ${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}")
- FIND_PATH(LUA_INCLUDE_DIR lua.h
- HINTS
- "${RSPAMD_SEARCH_PATH}" "${LUA_ROOT}"
- $ENV{LUA_DIR}
- PATH_SUFFIXES "include/lua${LUA_VERSION_MAJOR}${LUA_VERSION_MINOR}"
- "include/lua${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}"
- "include/lua-${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}"
- include/lua include
- PATHS ${RSPAMD_DEFAULT_INCLUDE_PATHS}
- )
- FIND_LIBRARY(LUA_LIBRARY
- NAMES lua
- "lua${LUA_VERSION_MAJOR}${LUA_VERSION_MINOR}"
- "lua${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}"
- "lua-${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}"
- HINTS
- "${RSPAMD_SEARCH_PATH}" "${LUA_ROOT}"
- $ENV{LUA_DIR}
- PATH_SUFFIXES lib64 lib
- PATHS ${RSPAMD_DEFAULT_LIBRARY_PATHS}
- DOC "Lua library"
- )
- ENDIF(ENABLE_LUAJIT MATCHES "ON")
-
- IF(LUA_LIBRARY AND LUA_INCLUDE_DIR)
- SET(LUA_FOUND 1)
- IF(NOT LUA_VERSION_MAJOR OR NOT LUA_VERSION_MINOR)
- SET(LUA_VERSION_MAJOR ${LUA_VERSION_MAJOR})
- SET(LUA_VERSION_MINOR ${LUA_VERSION_MINOR})
- ENDIF(NOT LUA_VERSION_MAJOR OR NOT LUA_VERSION_MINOR)
- IF(ENABLE_LUAJIT MATCHES "ON")
- MESSAGE(STATUS "Found luajit ${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}")
- ELSE(ENABLE_LUAJIT MATCHES "ON")
- MESSAGE(STATUS "Found lua ${LUA_VERSION_MAJOR}.${LUA_VERSION_MINOR}")
- ENDIF(ENABLE_LUAJIT MATCHES "ON")
- ENDIF(LUA_LIBRARY AND LUA_INCLUDE_DIR)
-ENDMACRO()
-
-IF(CMAKE_SYSTEM_NAME STREQUAL "Linux")
- LIST(APPEND CMAKE_REQUIRED_LIBRARIES rt)
-ENDIF(CMAKE_SYSTEM_NAME STREQUAL "Linux")
-
-IF(ENABLE_URL_INCLUDE MATCHES "ON")
- FIND_LIBRARY(LIBFETCH_LIBRARY NAMES fetch PATHS PATH_SUFFIXES lib64 lib
- PATHS
- ~/Library/Frameworks
- /Library/Frameworks
- /usr/local
- /usr
- /sw
- /opt/local
- /opt/csw
- /opt
- DOC "Path where the libfetch library can be found")
- IF(LIBFETCH_LIBRARY)
- FIND_FILE(HAVE_FETCH_H NAMES fetch.h PATHS /usr/include
- /opt/include
- /usr/local/include
- DOC "Path to libfetch header")
- ELSE(LIBFETCH_LIBRARY)
- # Try to find libcurl
- FIND_PACKAGE(CURL)
- IF(NOT CURL_FOUND)
- MESSAGE(WARNING "Neither libcurl nor libfetch were found, no support of URL includes in configuration")
- ENDIF(NOT CURL_FOUND)
- ENDIF(LIBFETCH_LIBRARY)
-ENDIF(ENABLE_URL_INCLUDE MATCHES "ON")
-
-set(SYNC_BUILTINS_TEST_SOURCE [====[
-int main()
-{
- unsigned long val;
-
- __sync_bool_compare_and_swap(&val, 0, 1);
- __sync_add_and_fetch(&val, 1);
- __sync_fetch_and_add(&val, 0);
- __sync_sub_and_fetch(&val, 1);
-
- return 0;
-}
-]====])
-
-CHECK_C_SOURCE_COMPILES("${SYNC_BUILTINS_TEST_SOURCE}" HAVE_ATOMIC_BUILTINS)
-IF(NOT HAVE_ATOMIC_BUILTINS)
- MESSAGE(WARNING "Libucl references could be thread-unsafe because atomic builtins are missing")
-ENDIF(NOT HAVE_ATOMIC_BUILTINS)
-
-SET(CMAKE_C_WARN_FLAGS "")
-CHECK_C_COMPILER_FLAG(-W SUPPORT_W)
-CHECK_C_COMPILER_FLAG(-Wno-pointer-sign SUPPORT_WPOINTER_SIGN)
-CHECK_C_COMPILER_FLAG(-Wno-unused-parameter SUPPORT_WUNUSED_PARAMETER)
-IF(SUPPORT_W)
- SET(CMAKE_C_WARN_FLAGS "${CMAKE_C_WARN_FLAGS} -W")
-ENDIF(SUPPORT_W)
-IF(SUPPORT_WPOINTER_SIGN)
- SET(CMAKE_C_WARN_FLAGS "${CMAKE_C_WARN_FLAGS} -Wno-pointer-sign")
-ENDIF(SUPPORT_WPOINTER_SIGN)
-IF(SUPPORT_WUNUSED_PARAMETER)
- SET(CMAKE_C_WARN_FLAGS "${CMAKE_C_WARN_FLAGS} -Wno-unused-parameter")
-ENDIF(SUPPORT_WUNUSED_PARAMETER)
-
-SET( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${CMAKE_C_WARN_FLAGS}" )
-
-IF(ENABLE_URL_SIGN MATCHES "ON")
- IF(OPENSSL_FOUND)
- SET(HAVE_OPENSSL 1)
- INCLUDE_DIRECTORIES("${OPENSSL_INCLUDE_DIR}")
- ENDIF(OPENSSL_FOUND)
-ENDIF(ENABLE_URL_SIGN MATCHES "ON")
-
-SET(UCL_COMPILE_DEFS)
-IF(HAVE_FETCH_H)
- LIST(APPEND UCL_COMPILE_DEFS -DHAVE_FETCH_H=1)
-ENDIF(HAVE_FETCH_H)
-IF(CURL_FOUND)
- LIST(APPEND UCL_COMPILE_DEFS -DCURL_FOUND=1)
-ENDIF(CURL_FOUND)
-IF(HAVE_OPENSSL)
- LIST(APPEND UCL_COMPILE_DEFS -DHAVE_OPENSSL=1)
-ENDIF(HAVE_OPENSSL)
-IF(HAVE_ATOMIC_BUILTINS)
- LIST(APPEND UCL_COMPILE_DEFS -DHAVE_ATOMIC_BUILTINS=1)
-ENDIF(HAVE_ATOMIC_BUILTINS)
-
-SET(UCLSRC src/ucl_util.c
- src/ucl_parser.c
- src/ucl_emitter.c
- src/ucl_emitter_streamline.c
- src/ucl_emitter_utils.c
- src/ucl_hash.c
- src/ucl_schema.c
- src/ucl_msgpack.c
- src/ucl_sexp.c)
-
-SET(UCLHDR include/ucl.h
- include/ucl++.h)
-
-SET (LIB_TYPE STATIC)
-IF (BUILD_SHARED_LIBS)
- SET (LIB_TYPE SHARED)
-ENDIF (BUILD_SHARED_LIBS)
-ADD_LIBRARY(ucl ${LIB_TYPE} ${UCLSRC})
-ADD_LIBRARY(ucl::ucl ALIAS ucl)
-SET_TARGET_PROPERTIES(ucl PROPERTIES VERSION ${LIBUCL_VERSION} SOVERSION ${LIBUCL_VERSION_MAJOR})
-TARGET_INCLUDE_DIRECTORIES(ucl
- PUBLIC
- include
- PRIVATE
- src
- uthash
- klib)
-TARGET_COMPILE_DEFINITIONS(ucl
- PRIVATE
- ${UCL_COMPILE_DEFS}
-)
-
-IF(ENABLE_LUA MATCHES "ON")
- IF(ENABLE_LUAJIT MATCHES "ON")
- FindLua(VERSION_MAJOR "5" VERSION_MINOR "1" ROOT "${LUA_ROOT}")
- IF(NOT LUA_FOUND)
- MESSAGE(FATAL_ERROR "Lua not found, lua support is required")
- ELSE(NOT LUA_FOUND)
- INCLUDE_DIRECTORIES("${LUA_INCLUDE_DIR}")
- ENDIF(NOT LUA_FOUND)
- ELSE(ENABLE_LUAJIT MATCHES "ON")
- FindLua(VERSION_MAJOR "5" VERSION_MINOR "2" ROOT "${LUA_ROOT}")
- IF(NOT LUA_FOUND)
- FindLua(VERSION_MAJOR "5" VERSION_MINOR "1" ROOT "${LUA_ROOT}")
- ENDIF(NOT LUA_FOUND)
- IF(NOT LUA_FOUND)
- MESSAGE(FATAL_ERROR "Lua not found, lua support is required")
- ELSE(NOT LUA_FOUND)
- INCLUDE_DIRECTORIES("${LUA_INCLUDE_DIR}")
- ENDIF(NOT LUA_FOUND)
- ENDIF(ENABLE_LUAJIT MATCHES "ON")
- SET(UCL_LUA_SRC lua/lua_ucl.c)
- ADD_LIBRARY(lua-ucl ${LIB_TYPE} ${UCL_LUA_SRC})
- ADD_LIBRARY(ucl::lua ALIAS lua-ucl)
- IF(ENABLE_LUAJIT MATCHES "ON")
- TARGET_LINK_LIBRARIES(lua-ucl "${LUAJIT_LIBRARY}")
- ELSE(ENABLE_LUAJIT MATCHES "ON")
- TARGET_LINK_LIBRARIES(lua-ucl "${LUA_LIBRARY}")
- ENDIF(ENABLE_LUAJIT MATCHES "ON")
- TARGET_LINK_LIBRARIES(lua-ucl ucl)
- TARGET_INCLUDE_DIRECTORIES(lua-ucl PUBLIC include PRIVATE src uthash)
- SET_TARGET_PROPERTIES(lua-ucl PROPERTIES
- VERSION ${LIBUCL_VERSION}
- SOVERSION ${LIBUCL_VERSION_MAJOR}
- PUBLIC_HEADER include/lua_ucl.h)
- INSTALL(TARGETS lua-ucl DESTINATION ${CMAKE_INSTALL_LIBDIR}
- PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
-ENDIF()
-
-IF(HAVE_FETCH_H)
- TARGET_LINK_LIBRARIES(ucl fetch)
-ELSE(HAVE_FETCH_H)
- IF(CURL_FOUND)
- TARGET_LINK_LIBRARIES(ucl ${CURL_LIBRARIES})
- ENDIF(CURL_FOUND)
-ENDIF(HAVE_FETCH_H)
-IF(ENABLE_URL_SIGN MATCHES "ON")
- IF(OPENSSL_FOUND)
- TARGET_LINK_LIBRARIES(ucl ${OPENSSL_LIBRARIES})
- ENDIF(OPENSSL_FOUND)
-ENDIF(ENABLE_URL_SIGN MATCHES "ON")
-
-IF(UNIX)
- TARGET_LINK_LIBRARIES(ucl -lm)
-ENDIF(UNIX)
-
-SET_TARGET_PROPERTIES(ucl PROPERTIES
- PUBLIC_HEADER "${UCLHDR}")
-
-INSTALL(TARGETS ucl DESTINATION ${CMAKE_INSTALL_LIBDIR}
- PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
-
-IF(ENABLE_UTILS MATCHES "ON")
- ADD_SUBDIRECTORY(utils)
-ENDIF()
-
diff --git a/contrib/libucl/ChangeLog.md b/contrib/libucl/ChangeLog.md
deleted file mode 100644
index cba29aa9a7b5..000000000000
--- a/contrib/libucl/ChangeLog.md
+++ /dev/null
@@ -1,103 +0,0 @@
-# Version history
-
-## Libucl 0.5
-
-- Streamline emitter has been added, so it is now possible to output partial `ucl` objects
-- Emitter now is more flexible due to emitter_context structure
-
-### 0.5.1
-- Fixed number of bugs and memory leaks
-
-### 0.5.2
-
-- Allow userdata objects to be emitted and destructed
-- Use userdata objects to store lua function references
-
-### Libucl 0.6
-
-- Reworked macro interface
-
-### Libucl 0.6.1
-
-- Various utilities fixes
-
-### Libucl 0.7.0
-
-- Move to klib library from uthash to reduce memory overhead and increase performance
-
-### Libucl 0.7.1
-
-- Added safe iterators API
-
-### Libucl 0.7.2
-
-- Fixed serious bugs in schema and arrays iteration
-
-### Libucl 0.7.3
-
-- Fixed a bug with macros that come after an empty object
-- Fixed a bug in include processing when an incorrect variable has been destroyed (use-after-free)
-
-### Libucl 0.8.0
-
-- Allow to save comments and macros when parsing UCL documents
-- C++ API
-- Python bindings (by Eitan Adler)
-- Add msgpack support for parser and emitter
-- Add Canonical S-expressions parser for libucl
-- CLI interface for parsing and validation (by Maxim Ignatenko)
-- Implement include with priority
-- Add 'nested' functionality to .include macro (by Allan Jude)
-- Allow searching an array of paths for includes (by Allan Jude)
-- Add new .load macro (by Allan Jude)
-- Implement .inherit macro (#100)
-- Add merge strategies
-- Add schema validation to lua API
-- Add support for external references to schema validation
-- Add coveralls integration to libucl
-- Implement tests for 80% of libucl code lines
-- Fix tonns of minor and major bugs
-- Improve documentation
-- Rework function names to the common conventions (old names are preserved for backwards compatibility)
-- Add Coverity scan integration
-- Add fuzz tests
-
-**Incompatible changes**:
-
-- `ucl_object_emit_full` now accepts additional argument `comments` that could be used to emit comments with UCL output
-
-### Libucl 0.8.1
-
-- Create ucl_parser_add_file_full() to be able to specify merge mode and parser type (by Allan Jude)
-- C++ wrapper improvements (by @ftilde)
-- C++ wrapper: add convenience method at() and lookup() (by Yonghee Kim)
-- C++ wrapper: add assignment operator to Ucl class (by Yonghee Kim)
-- C++ wrapper: support variables in parser (by Yonghee Kim)
-- C++ wrapper: refactoring C++ interface (by Yonghee Kim):
- - use auto variables (if possible)
- - remove dangling expressions
- - use std::set::emplace instead of std::set::insert
- - not use std::move in return statement; considering copy elision
-- C++ wrapper: fix compilation error and warnings (by Zhe Wang)
-- C++ wrapper: fix iteration over objects in which the first value is `false` (by Zhe Wang)
-- C++ wrapper: Macro helper functions (by Chris Meacham)
-- C++ wrapper: Changing the duplicate strategy in the C++ API (by Chris Meacham)
-- C++ wrapper: Added access functions for the size of a UCL_ARRAY (by Chris Meacham)
-- Fix caseless comparison
-- Fix include when EPERM is issued
-- Fix Windows build
-- Allow to reserve space in arrays and hashes
-- Fix bug with including of empty files
-- Move to mum_hash from xxhash
-- Fix msgpack on non-x86
-- python: Add support to Python 3 (by Denis Volpato Martins)
-- python: Add support for Python 2.6 tests (by Denis Volpato Martins)
-- python: Implement validation function and tests (by Denis Volpato Martins)
-- python: Added UCL_NULL handling and tests (by Denis Volpato Martins)
-- Fix schema validation for patternProperties with object data (by Denis Volpato Martins)
-- Remove the dependency on NBBY, add missing <strings.h> include (by Ed Schouten)
-- Allow to emit msgpack from Lua
-- Performance improvements in Lua API
-- Allow to pass opaque objects in Lua API for transparent C passthrough
-- Various bugs fixed
-- Couple of memory leaks plugged \ No newline at end of file
diff --git a/contrib/libucl/FREEBSD-Xlist b/contrib/libucl/FREEBSD-Xlist
new file mode 100644
index 000000000000..6d8cb4ff8f5b
--- /dev/null
+++ b/contrib/libucl/FREEBSD-Xlist
@@ -0,0 +1,40 @@
+.github
+.gitignore
+CMakeLists.txt
+ChangeLog.md
+Makefile.am
+Makefile.unix
+Makefile.w32
+README.md
+autogen.sh
+configure.ac
+doc/Makefile.am
+doc/api.md
+doc/lua_api.md
+doc/pandoc.template
+examples/ucl_cpp.cc
+haskell/hucl.hs
+libucl.pc.in
+lua/Makefile.am
+lua/libucl.rockspec.in
+m4/.gitignore
+m4/ax_lua.m4
+m4/gcov.m4
+python/MANIFEST.in
+python/setup.py
+python/src/uclmodule.c
+python/tests/__init__.py
+python/tests/compat.py
+python/tests/test_dump.py
+python/tests/test_example.py
+python/tests/test_load.py
+python/tests/test_validation.py
+python/ucl.pyi
+src/Makefile.am
+stamp-h.in
+tests/Makefile.am
+utils/CMakeLists.txt
+utils/Makefile.am
+utils/chargen.c
+utils/objdump.c
+utils/ucl-tool.c
diff --git a/contrib/libucl/FREEBSD-upgrade b/contrib/libucl/FREEBSD-upgrade
new file mode 100644
index 000000000000..b80736d7877b
--- /dev/null
+++ b/contrib/libucl/FREEBSD-upgrade
@@ -0,0 +1,39 @@
+# FreeBSD libucl import instruction
+#
+# At least the following ports are required when importing libucl:
+# - devel/autoconf
+# - devel/automake
+# - devel/git
+# - devel/gmake
+# - devel/libtool
+#
+# 1. Vendor import
+#
+# $ git clone https://github.com/vstakhov/libucl.git /tmp/libucl
+# $ cd /tmp/libucl
+# $ git checkout <REF_BRANCH_TO_BE_IMPORTED>
+# $ cd /usr/src
+# $ git checkout vendor/libucl
+# $ rsync -va --delete --exclude=.git /tmp/libucl/ /usr/src/contrib/libucl/
+# $ git add .
+# $ git commit -m "vendor import libucl <REF_BRANCH_TO_BE_IMPORTED>"
+# $ git tag -a vendor/libucl/<REF_BRANCH_TO_BE_IMPORTED> -m "vendor import libucl <REF_BRANCH_TO_BE_IMPORTED>"
+# $ git push --follow-tags freebsd vendor/libucl/<REF_BRANCH_TO_BE_IMPORTED>
+#
+# 2. Test
+#
+# $ cd /usr/src
+# $ git checkout vendor/libucl/<REF_BRANCH_TO_BE_IMPORTED>
+# $ ./autogen.sh
+# $ ./configure
+# $ gmake
+# $ gmake check
+# $ gmake clean
+#
+# 3. Merge vendor tree
+#
+# $ git subtree merge -P contrib/libucl vendor/libucl/<REF_BRANCH_TO_BE_IMPORTED>
+# $ sh -c 'for F in `cat FREEBSD-Xlist | grep -v FreeBSD`; do rm -rf ./$F ; done'
+#
+# Recheck if there were any new files were added which are not necessary in the
+# contrib tree. If so, remove them and also add them to the FREEBSD-Xlist file.
diff --git a/contrib/libucl/Makefile.am b/contrib/libucl/Makefile.am
deleted file mode 100644
index 5b51bcc3b468..000000000000
--- a/contrib/libucl/Makefile.am
+++ /dev/null
@@ -1,81 +0,0 @@
-ACLOCAL_AMFLAGS = -I m4
-EXTRA_DIST = uthash klib README.md
-
-pkgconfigdir = $(libdir)/pkgconfig
-pkgconfig_DATA = libucl.pc
-
-if LUA_SUB
- LUA_SUBDIR = lua
-endif
-
-COVERAGE_INFO_FILE = $(top_builddir)/coverage.info
-COVERAGE_REPORT_DIR = $(top_builddir)/coverage
-
-.PHONY = coverage-requirement-check clean-coverage-report
-
-coverage-requirement-check:
- @if test ! -e $(GCOV); then \
- echo "Cannot find $(GCOV). Please install gcov."; \
- exit 1; \
- fi
-
-coverage: coverage-requirement-check clean-coverage coverage-build coverage-check coverage-report
- @echo "Please execute 'make clean' before 'make' or 'make check' to remove instrumented object files(compiled with -O0 etc.). Note that 'make clean' also remove coverage data."
-
-coverage-build: coverage-requirement-check
- @if test `find $(top_builddir) -name "*.gcno" | wc -l` -eq 0; then \
- echo "Start to remove old non-instrumented object files..."; \
- $(MAKE) $(AM_MAKEFLAGS) clean; \
- echo "Successfully removed old non-instrumented object files."; \
- fi
- @echo "Start to build libraries with coverage options..."
- $(MAKE) $(AM_MAKEFLAGS) \
- CFLAGS="$(CFLAGS) $(COVERAGE_CFLAGS) $(COVERAGE_OPTFLAGS)" \
- CXXFLAGS="$(CXXFLAGS) $(COVERAGE_CXXFLAGS) $(COVERAGE_OPTFLAGS)" \
- LDFLAGS="$(LDFLAGS) $(COVERAGE_LDFLAGS)" \
- LIBS="$(LIBS) $(COVERAGE_LIBS)"
- @echo "Successfully built libraries with coverage options."
-
-coverage-check: coverage-requirement-check
- @echo "Start to run tests with instrumented libraries..."
- $(MAKE) $(AM_MAKEFLAGS) check \
- CFLAGS="$(CFLAGS) $(COVERAGE_CFLAGS) $(COVERAGE_OPTFLAGS)" \
- CXXFLAGS="$(CXXFLAGS) $(COVERAGE_CXXFLAGS) $(COVERAGE_OPTFLAGS)" \
- LDFLAGS="$(LDFLAGS) $(COVERAGE_LDFLAGS)" \
- LIBS="$(LIBS) $(COVERAGE_LIBS)"
- @echo "Successfully run tests with instrumented libraries."
-
-coverage-lcov: coverage-check coverage-requirement-check
- $(LCOV) --capture \
- --directory "$(top_builddir)/" \
- --output-file $(COVERAGE_INFO_FILE) \
- --gcov-tool $(GCOV) \
- --compat-libtool --checksum
- $(LCOV) --extract $(COVERAGE_INFO_FILE) `pwd`/src/ucl_\* \
- --output-file $(COVERAGE_INFO_FILE)
-
-coverage-report: coverage-lcov
- @echo "Start to create coverage reports..."
- $(GENHTML) --prefix "$(top_srcdir)" \
- --output-directory $(COVERAGE_REPORT_DIR) \
- --title $(PACKAGE_NAME) \
- --legend --show-details \
- $(GENHTML_OPTIONS) \
- $(COVERAGE_INFO_FILE)
- @echo "Successfully created coverage reports into $(COVERAGE_REPORT_DIR) directory."
-
-clean-coverage-report:
- -rm -rf $(COVERAGE_INFO_FILE)
- -rm -rf $(COVERAGE_REPORT_DIR)
-
-clean-coverage: clean-coverage-report
- -$(LCOV) --gcov-tool $(GCOV) --zerocounters --directory $(top_builddir)
- @if xargs --version 2>/dev/null; then \
- find $(top_builddir) -name "*.gcno" | xargs --no-run-if-empty rm; \
- else \
- find $(top_builddir) -name "*.gcno" | xargs rm; \
- fi
-
-clean-local: clean-coverage
-
-SUBDIRS = src tests utils doc $(LUA_SUBDIR)
diff --git a/contrib/libucl/Makefile.unix b/contrib/libucl/Makefile.unix
deleted file mode 100644
index 0653d4843f7e..000000000000
--- a/contrib/libucl/Makefile.unix
+++ /dev/null
@@ -1,89 +0,0 @@
-CC ?= gcc
-DESTDIR ?= /usr/local
-LD ?= gcc
-C_COMMON_FLAGS ?= -fPIC -Wall -W -Wno-unused-parameter -Wno-pointer-sign -I./include -I./uthash -I./src -I./klib
-MAJOR_VERSION = 0
-MINOR_VERSION = 2
-PATCH_VERSION = 9
-VERSION = "$(MAJOR_VERSION).$(MINOR_VERSION).$(PATCH_VERSION)"
-SONAME = libucl.so
-SONAME_FULL = $(SONAME).$(MAJOR_VERSION)
-OBJDIR ?= .obj
-TESTDIR ?= tests
-SRCDIR ?= src
-INCLUDEDIR ?= include
-MKDIR ?= mkdir
-INSTALL ?= install
-RM ?= rm
-RMDIR ?= rmdir
-LN ?= ln
-LD_SHARED_FLAGS ?= -Wl,-soname,$(SONAME) -shared -lm
-LD_UCL_FLAGS ?= -L$(OBJDIR) -Wl,-rpath,$(OBJDIR) -lucl
-LD_ADD ?= -lrt
-COPT_FLAGS ?= -O2
-HDEPS = $(SRCDIR)/ucl_hash.h \
- $(SRCDIR)/ucl_chartable.h \
- $(SRCDIR)/ucl_internal.h \
- $(INCLUDEDIR)/ucl.h \
- $(SRCDIR)/mum.h
-OBJECTS = $(OBJDIR)/ucl_hash.o \
- $(OBJDIR)/ucl_util.o \
- $(OBJDIR)/ucl_parser.o \
- $(OBJDIR)/ucl_emitter.o \
- $(OBJDIR)/ucl_schema.o
-
-all: $(OBJDIR) $(OBJDIR)/$(SONAME)
-
-$(OBJDIR)/$(SONAME): $(OBJDIR)/$(SONAME_FULL)
- $(LN) -sf $(SONAME_FULL) $(OBJDIR)/$(SONAME)
-
-$(OBJDIR)/$(SONAME_FULL): $(OBJECTS)
- $(CC) -o $(OBJDIR)/$(SONAME_FULL) $(OBJECTS) $(LD_SHARED_FLAGS) $(LDFLAGS) $(SSL_LIBS) $(FETCH_LIBS)
-
-$(OBJDIR):
- @$(MKDIR) -p $(OBJDIR)
-
-# Compile rules
-$(OBJDIR)/ucl_util.o: $(SRCDIR)/ucl_util.c $(HDEPS)
- $(CC) -o $(OBJDIR)/ucl_util.o $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) -c $(SRCDIR)/ucl_util.c
-$(OBJDIR)/ucl_parser.o: $(SRCDIR)/ucl_parser.c $(HDEPS)
- $(CC) -o $(OBJDIR)/ucl_parser.o $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) -c $(SRCDIR)/ucl_parser.c
-$(OBJDIR)/ucl_emitter.o: $(SRCDIR)/ucl_emitter.c $(HDEPS)
- $(CC) -o $(OBJDIR)/ucl_emitter.o $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) -c $(SRCDIR)/ucl_emitter.c
-$(OBJDIR)/ucl_hash.o: $(SRCDIR)/ucl_hash.c $(HDEPS)
- $(CC) -o $(OBJDIR)/ucl_hash.o $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) -c $(SRCDIR)/ucl_hash.c
-$(OBJDIR)/ucl_schema.o: $(SRCDIR)/ucl_schema.c $(HDEPS)
- $(CC) -o $(OBJDIR)/ucl_schema.o $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) -c $(SRCDIR)/ucl_schema.c
-
-clean:
- $(RM) $(OBJDIR)/*.o $(OBJDIR)/$(SONAME_FULL) $(OBJDIR)/$(SONAME) $(OBJDIR)/chargen $(OBJDIR)/test_basic $(OBJDIR)/test_speed $(OBJDIR)/objdump $(OBJDIR)/test_generate $(OBJDIR)/test_schema || true
- $(RMDIR) $(OBJDIR)
-
-# Utils
-
-chargen: utils/chargen.c $(OBJDIR)/$(SONAME)
- $(CC) -o $(OBJDIR)/chargen $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) $(LDFLAGS) utils/chargen.c
-objdump: utils/objdump.c $(OBJDIR)/$(SONAME)
- $(CC) -o $(OBJDIR)/objdump $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) $(LDFLAGS) utils/objdump.c $(LD_UCL_FLAGS)
-
-# Tests
-
-test: $(OBJDIR) $(OBJDIR)/$(SONAME) $(OBJDIR)/test_basic $(OBJDIR)/test_speed $(OBJDIR)/test_generate $(OBJDIR)/test_schema
-
-run-test: test
- TEST_DIR=$(TESTDIR) $(TESTDIR)/run_tests.sh $(OBJDIR)/test_basic $(OBJDIR)/test_speed $(OBJDIR)/test_generate $(OBJDIR)/test_schema
-
-$(OBJDIR)/test_basic: $(TESTDIR)/test_basic.c $(OBJDIR)/$(SONAME)
- $(CC) -o $(OBJDIR)/test_basic $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) $(LDFLAGS) $(TESTDIR)/test_basic.c $(LD_UCL_FLAGS)
-$(OBJDIR)/test_schema: $(TESTDIR)/test_schema.c $(OBJDIR)/$(SONAME)
- $(CC) -o $(OBJDIR)/test_schema $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) $(LDFLAGS) $(TESTDIR)/test_schema.c $(LD_UCL_FLAGS)
-$(OBJDIR)/test_speed: $(TESTDIR)/test_speed.c $(OBJDIR)/$(SONAME)
- $(CC) -o $(OBJDIR)/test_speed $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) $(LDFLAGS) $(TESTDIR)/test_speed.c $(LD_UCL_FLAGS) $(LD_ADD)
-$(OBJDIR)/test_generate: $(TESTDIR)/test_generate.c $(OBJDIR)/$(SONAME)
- $(CC) -o $(OBJDIR)/test_generate $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) $(LDFLAGS) $(TESTDIR)/test_generate.c $(LD_UCL_FLAGS) $(LD_ADD)
-
-install: $(OBJDIR)/$(SONAME)
- $(INSTALL) -m0755 $(SONAME) $(DESTDIR)/lib/$(SONAME)
- $(INSTALL) -m0644 include/ucl.h $(DESTDIR)/include/ucl.h
-
-.PHONY: clean $(OBJDIR)
diff --git a/contrib/libucl/Makefile.w32 b/contrib/libucl/Makefile.w32
deleted file mode 100644
index 5d9398bf1988..000000000000
--- a/contrib/libucl/Makefile.w32
+++ /dev/null
@@ -1,92 +0,0 @@
-CC ?= gcc
-DESTDIR ?= /usr/local
-LD ?= gcc
-C_COMMON_FLAGS ?= -fPIC -Wall -W -Wno-unused-parameter -Wno-pointer-sign -I./include -I./uthash -I./src
-MAJOR_VERSION = 0
-MINOR_VERSION = 2
-PATCH_VERSION = 9
-VERSION = "$(MAJOR_VERSION).$(MINOR_VERSION).$(PATCH_VERSION)"
-SONAME = libucl.dll
-OBJDIR ?= .obj
-TESTDIR ?= tests
-SRCDIR ?= src
-INCLUDEDIR ?= include
-MKDIR ?= mkdir
-INSTALL ?= install
-RM ?= rm
-RMDIR ?= rmdir
-ifeq (Windows_NT, $(OS))
-LN ?= ln
-else
-LN ?= rem ln
-endif
-LD_SHARED_FLAGS ?= -Wl,-soname,$(SONAME) -shared -lm
-LD_UCL_FLAGS ?= -L$(OBJDIR) -Wl,-rpath,$(OBJDIR) -lucl
-LD_ADD ?= -lrt
-COPT_FLAGS ?= -O2
-HDEPS = $(SRCDIR)/ucl_hash.h \
- $(SRCDIR)/ucl_chartable.h \
- $(SRCDIR)/ucl_internal.h \
- $(INCLUDEDIR)/ucl.h \
- $(SRCDIR)/mum.h
-OBJECTS = $(OBJDIR)/ucl_hash.o \
- $(OBJDIR)/ucl_util.o \
- $(OBJDIR)/ucl_parser.o \
- $(OBJDIR)/ucl_emitter.o \
- $(OBJDIR)/ucl_emitter_utils.o \
- $(OBJDIR)/ucl_schema.o
-
-all: $(OBJDIR) $(OBJDIR)/$(SONAME)
-
-$(OBJDIR)/$(SONAME): $(OBJECTS)
- $(CC) -o $(OBJDIR)/$(SONAME) $(OBJECTS) $(LD_SHARED_FLAGS) $(LDFLAGS) $(SSL_LIBS) $(FETCH_LIBS)
-
-$(OBJDIR):
- @$(MKDIR) -p $(OBJDIR)
-
-# Compile rules
-$(OBJDIR)/ucl_util.o: $(SRCDIR)/ucl_util.c $(HDEPS)
- $(CC) -o $(OBJDIR)/ucl_util.o $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) -c $(SRCDIR)/ucl_util.c
-$(OBJDIR)/ucl_parser.o: $(SRCDIR)/ucl_parser.c $(HDEPS)
- $(CC) -o $(OBJDIR)/ucl_parser.o $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) -c $(SRCDIR)/ucl_parser.c
-$(OBJDIR)/ucl_emitter.o: $(SRCDIR)/ucl_emitter.c $(HDEPS)
- $(CC) -o $(OBJDIR)/ucl_emitter.o $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) -c $(SRCDIR)/ucl_emitter.c
-$(OBJDIR)/ucl_emitter_utils.o: $(SRCDIR)/ucl_emitter_utils.c $(HDEPS)
- $(CC) -o $(OBJDIR)/ucl_emitter_utils.o $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) -c $(SRCDIR)/ucl_emitter_utils.c
-$(OBJDIR)/ucl_hash.o: $(SRCDIR)/ucl_hash.c $(HDEPS)
- $(CC) -o $(OBJDIR)/ucl_hash.o $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) -c $(SRCDIR)/ucl_hash.c
-$(OBJDIR)/ucl_schema.o: $(SRCDIR)/ucl_schema.c $(HDEPS)
- $(CC) -o $(OBJDIR)/ucl_schema.o $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) -c $(SRCDIR)/ucl_schema.c
-$(OBJDIR)/xxhash.o: $(SRCDIR)/xxhash.c $(HDEPS)
- $(CC) -o $(OBJDIR)/xxhash.o $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) -c $(SRCDIR)/xxhash.c
-
-clean:
- $(RM) $(OBJDIR)/*.o $(OBJDIR)/$(SONAME) $(OBJDIR)/$(SONAME) $(OBJDIR)/chargen $(OBJDIR)/test_basic $(OBJDIR)/test_speed $(OBJDIR)/objdump $(OBJDIR)/test_generate
- $(RMDIR) $(OBJDIR)
-
-# Utils
-
-chargen: utils/chargen.c $(OBJDIR)/$(SONAME)
- $(CC) -o $(OBJDIR)/chargen $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) $(LDFLAGS) utils/chargen.c
-objdump: utils/objdump.c $(OBJDIR)/$(SONAME)
- $(CC) -o $(OBJDIR)/objdump $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) $(LDFLAGS) utils/objdump.c $(LD_UCL_FLAGS)
-
-# Tests
-
-test: $(OBJDIR) $(OBJDIR)/$(SONAME) $(OBJDIR)/test_basic $(OBJDIR)/test_speed $(OBJDIR)/test_generate
-
-run-test: test
- TEST_DIR=$(TESTDIR) $(TESTDIR)/run_tests.sh $(OBJDIR)/test_basic $(OBJDIR)/test_speed $(OBJDIR)/test_generate
-
-$(OBJDIR)/test_basic: $(TESTDIR)/test_basic.c $(OBJDIR)/$(SONAME)
- $(CC) -o $(OBJDIR)/test_basic $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) $(LDFLAGS) $(TESTDIR)/test_basic.c $(LD_UCL_FLAGS)
-$(OBJDIR)/test_speed: $(TESTDIR)/test_speed.c $(OBJDIR)/$(SONAME)
- $(CC) -o $(OBJDIR)/test_speed $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) $(LDFLAGS) $(TESTDIR)/test_speed.c $(LD_UCL_FLAGS) $(LD_ADD)
-$(OBJDIR)/test_generate: $(TESTDIR)/test_generate.c $(OBJDIR)/$(SONAME)
- $(CC) -o $(OBJDIR)/test_generate $(CPPFLAGS) $(COPT_FLAGS) $(CFLAGS) $(C_COMMON_FLAGS) $(SSL_CFLAGS) $(FETCH_FLAGS) $(LDFLAGS) $(TESTDIR)/test_generate.c $(LD_UCL_FLAGS) $(LD_ADD)
-
-install: $(OBJDIR)/$(SONAME)
- $(INSTALL) -m0755 $(SONAME) $(DESTDIR)/lib/$(SONAME)
- $(INSTALL) -m0644 include/ucl.h $(DESTDIR)/include/ucl.h
-
-.PHONY: clean $(OBJDIR)
diff --git a/contrib/libucl/README.md b/contrib/libucl/README.md
deleted file mode 100644
index 53d8a651d73b..000000000000
--- a/contrib/libucl/README.md
+++ /dev/null
@@ -1,418 +0,0 @@
-# LIBUCL
-
-[![CircleCI](https://circleci.com/gh/vstakhov/libucl.svg?style=svg)](https://circleci.com/gh/vstakhov/libucl)
-[![Coverity](https://scan.coverity.com/projects/4138/badge.svg)](https://scan.coverity.com/projects/4138)
-[![Coverage Status](https://coveralls.io/repos/github/vstakhov/libucl/badge.svg?branch=master)](https://coveralls.io/github/vstakhov/libucl?branch=master)
-
-**Table of Contents** *generated with [DocToc](http://doctoc.herokuapp.com/)*
-
-- [Introduction](#introduction)
-- [Basic structure](#basic-structure)
-- [Improvements to the json notation](#improvements-to-the-json-notation)
- - [General syntax sugar](#general-syntax-sugar)
- - [Automatic arrays creation](#automatic-arrays-creation)
- - [Named keys hierarchy](#named-keys-hierarchy)
- - [Convenient numbers and booleans](#convenient-numbers-and-booleans)
-- [General improvements](#general-improvements)
- - [Comments](#comments)
- - [Macros support](#macros-support)
- - [Variables support](#variables-support)
- - [Multiline strings](#multiline-strings)
- - [Single quoted strings](#single-quoted-strings)
-- [Emitter](#emitter)
-- [Validation](#validation)
-- [Performance](#performance)
-- [Conclusion](#conclusion)
-
-## Introduction
-
-This document describes the main features and principles of the configuration
-language called `UCL` - universal configuration language.
-
-If you are looking for the libucl API documentation you can find it at [this page](doc/api.md).
-
-## Basic structure
-
-UCL is heavily infused by `nginx` configuration as the example of a convenient configuration
-system. However, UCL is fully compatible with `JSON` format and is able to parse json files.
-For example, you can write the same configuration in the following ways:
-
-* in nginx like:
-
-```nginx
-param = value;
-section {
- param = value;
- param1 = value1;
- flag = true;
- number = 10k;
- time = 0.2s;
- string = "something";
- subsection {
- host = {
- host = "hostname";
- port = 900;
- }
- host = {
- host = "hostname";
- port = 901;
- }
- }
-}
-```
-
-* or in JSON:
-
-```json
-{
- "param": "value",
- "section": {
- "param": "value",
- "param1": "value1",
- "flag": true,
- "number": 10000,
- "time": "0.2s",
- "string": "something",
- "subsection": {
- "host": [
- {
- "host": "hostname",
- "port": 900
- },
- {
- "host": "hostname",
- "port": 901
- }
- ]
- }
- }
-}
-```
-
-## Improvements to the json notation.
-
-There are various things that make ucl configuration more convenient for editing than strict json:
-
-### General syntax sugar
-
-* Braces are not necessary to enclose a top object: it is automatically treated as an object:
-
-```json
-"key": "value"
-```
-is equal to:
-```json
-{"key": "value"}
-```
-
-* There is no requirement of quotes for strings and keys, moreover, `:` may be replaced `=` or even be skipped for objects:
-
-```nginx
-key = value;
-section {
- key = value;
-}
-```
-is equal to:
-```json
-{
- "key": "value",
- "section": {
- "key": "value"
- }
-}
-```
-
-* No commas mess: you can safely place a comma or semicolon for the last element in an array or an object:
-
-```json
-{
- "key1": "value",
- "key2": "value",
-}
-```
-### Automatic arrays creation
-
-* Non-unique keys in an object are allowed and are automatically converted to the arrays internally:
-
-```json
-{
- "key": "value1",
- "key": "value2"
-}
-```
-is converted to:
-```json
-{
- "key": ["value1", "value2"]
-}
-```
-
-### Named keys hierarchy
-
-UCL accepts named keys and organize them into objects hierarchy internally. Here is an example of this process:
-```nginx
-section "blah" {
- key = value;
-}
-section foo {
- key = value;
-}
-```
-
-is converted to the following object:
-
-```nginx
-section {
- blah {
- key = value;
- }
- foo {
- key = value;
- }
-}
-```
-
-Plain definitions may be more complex and contain more than a single level of nested objects:
-
-```nginx
-section "blah" "foo" {
- key = value;
-}
-```
-
-is presented as:
-
-```nginx
-section {
- blah {
- foo {
- key = value;
- }
- }
-}
-```
-
-### Convenient numbers and booleans
-
-* Numbers can have suffixes to specify standard multipliers:
- + `[kKmMgG]` - standard 10 base multipliers (so `1k` is translated to 1000)
- + `[kKmMgG]b` - 2 power multipliers (so `1kb` is translated to 1024)
- + `[s|min|d|w|y]` - time multipliers, all time values are translated to float number of seconds, for example `10min` is translated to 600.0 and `10ms` is translated to 0.01
-* Hexadecimal integers can be used by `0x` prefix, for example `key = 0xff`. However, floating point values can use decimal base only.
-* Booleans can be specified as `true` or `yes` or `on` and `false` or `no` or `off`.
-* It is still possible to treat numbers and booleans as strings by enclosing them in double quotes.
-
-## General improvements
-
-### Comments
-
-UCL supports different style of comments:
-
-* single line: `#`
-* multiline: `/* ... */`
-
-Multiline comments may be nested:
-```c
-# Sample single line comment
-/*
- some comment
- /* nested comment */
- end of comment
-*/
-```
-
-### Macros support
-
-UCL supports external macros both multiline and single line ones:
-```nginx
-.macro_name "sometext";
-.macro_name {
- Some long text
- ....
-};
-```
-
-Moreover, each macro can accept an optional list of arguments in braces. These
-arguments themselves are the UCL object that is parsed and passed to a macro as
-options:
-
-```nginx
-.macro_name(param=value) "something";
-.macro_name(param={key=value}) "something";
-.macro_name(.include "params.conf") "something";
-.macro_name(#this is multiline macro
-param = [value1, value2]) "something";
-.macro_name(key="()") "something";
-```
-
-UCL also provide a convenient `include` macro to load content from another files
-to the current UCL object. This macro accepts either path to file:
-
-```nginx
-.include "/full/path.conf"
-.include "./relative/path.conf"
-.include "${CURDIR}/path.conf"
-```
-
-or URL (if ucl is built with url support provided by either `libcurl` or `libfetch`):
-
- .include "http://example.com/file.conf"
-
-`.include` macro supports a set of options:
-
-* `try` (default: **false**) - if this option is `true` than UCL treats errors on loading of
-this file as non-fatal. For example, such a file can be absent but it won't stop the parsing
-of the top-level document.
-* `sign` (default: **false**) - if this option is `true` UCL loads and checks the signature for
-a file from path named `<FILEPATH>.sig`. Trusted public keys should be provided for UCL API after
-parser is created but before any configurations are parsed.
-* `glob` (default: **false**) - if this option is `true` UCL treats the filename as GLOB pattern and load
-all files that matches the specified pattern (normally the format of patterns is defined in `glob` manual page
-for your operating system). This option is meaningless for URL includes.
-* `url` (default: **true**) - allow URL includes.
-* `path` (default: empty) - A UCL_ARRAY of directories to search for the include file.
-Search ends after the first match, unless `glob` is true, then all matches are included.
-* `prefix` (default false) - Put included contents inside an object, instead
-of loading them into the root. If no `key` is provided, one is automatically generated based on each files basename()
-* `key` (default: <empty string>) - Key to load contents of include into. If
-the key already exists, it must be the correct type
-* `target` (default: object) - Specify if the `prefix` `key` should be an
-object or an array.
-* `priority` (default: 0) - specify priority for the include (see below).
-* `duplicate` (default: 'append') - specify policy of duplicates resolving:
- - `append` - default strategy, if we have new object of higher priority then it replaces old one, if we have new object with less priority it is ignored completely, and if we have two duplicate objects with the same priority then we have a multi-value key (implicit array)
- - `merge` - if we have object or array, then new keys are merged inside, if we have a plain object then an implicit array is formed (regardless of priorities)
- - `error` - create error on duplicate keys and stop parsing
- - `rewrite` - always rewrite an old value with new one (ignoring priorities)
-
-Priorities are used by UCL parser to manage the policy of objects rewriting during including other files
-as following:
-
-* If we have two objects with the same priority then we form an implicit array
-* If a new object has bigger priority then we overwrite an old one
-* If a new object has lower priority then we ignore it
-
-By default, the priority of top-level object is set to zero (lowest priority). Currently,
-you can define up to 16 priorities (from 0 to 15). Includes with bigger priorities will
-rewrite keys from the objects with lower priorities as specified by the policy. The priority
-of the top-level or any other object can be changed with the `.priority` macro, which has no
-options and takes the new priority:
-
-```
-# Default priority: 0.
-foo = 6
-.priority 5
-# The following will have priority 5.
-bar = 6
-baz = 7
-# The following will be included with a priority of 3, 5, and 6 respectively.
-.include(priority=3) "path.conf"
-.include(priority=5) "equivalent-path.conf"
-.include(priority=6) "highpriority-path.conf"
-```
-
-### Variables support
-
-UCL supports variables in input. Variables are registered by a user of the UCL parser and can be presented in the following forms:
-
-* `${VARIABLE}`
-* `$VARIABLE`
-
-UCL currently does not support nested variables. To escape variables one could use double dollar signs:
-
-* `$${VARIABLE}` is converted to `${VARIABLE}`
-* `$$VARIABLE` is converted to `$VARIABLE`
-
-However, if no valid variables are found in a string, no expansion will be performed (and `$$` thus remains unchanged). This may be a subject
-to change in future libucl releases.
-
-### Multiline strings
-
-UCL can handle multiline strings as well as single line ones. It uses shell/perl like notation for such objects:
-```
-key = <<EOD
-some text
-splitted to
-lines
-EOD
-```
-
-In this example `key` will be interpreted as the following string: `some text\nsplitted to\nlines`.
-Here are some rules for this syntax:
-
-* Multiline terminator must start just after `<<` symbols and it must consist of capital letters only (e.g. `<<eof` or `<< EOF` won't work);
-* Terminator must end with a single newline character (and no spaces are allowed between terminator and newline character);
-* To finish multiline string you need to include a terminator string just after newline and followed by a newline (no spaces or other characters are allowed as well);
-* The initial and the final newlines are not inserted to the resulting string, but you can still specify newlines at the beginning and at the end of a value, for example:
-
-```
-key <<EOD
-
-some
-text
-
-EOD
-```
-
-### Single quoted strings
-
-It is possible to use single quoted strings to simplify escaping rules. All values passed in single quoted strings are *NOT* escaped, with two exceptions: a single `'` character just before `\` character, and a newline character just after `\` character that is ignored.
-
-```
-key = 'value'; # Read as value
-key = 'value\n\'; # Read as value\n\
-key = 'value\''; # Read as value'
-key = 'value\
-bla'; # Read as valuebla
-```
-
-## Emitter
-
-Each UCL object can be serialized to one of the four supported formats:
-
-* `JSON` - canonic json notation (with spaces indented structure);
-* `Compacted JSON` - compact json notation (without spaces or newlines);
-* `Configuration` - nginx like notation;
-* `YAML` - yaml inlined notation.
-
-## Validation
-
-UCL allows validation of objects. It uses the same schema that is used for json: [json schema v4](http://json-schema.org). UCL supports the full set of json schema with the exception of remote references. This feature is unlikely useful for configuration objects. Of course, a schema definition can be in UCL format instead of JSON that simplifies schemas writing. Moreover, since UCL supports multiple values for keys in an object it is possible to specify generic integer constraints `maxValues` and `minValues` to define the limits of values count in a single key. UCL currently is not absolutely strict about validation schemas themselves, therefore UCL users should supply valid schemas (as it is defined in json-schema draft v4) to ensure that the input objects are validated properly.
-
-## Performance
-
-Are UCL parser and emitter fast enough? Well, there are some numbers.
-I got a 19Mb file that consist of ~700 thousand lines of json (obtained via
-http://www.json-generator.com/). Then I checked jansson library that performs json
-parsing and emitting and compared it with UCL. Here are results:
-
-```
-jansson: parsed json in 1.3899 seconds
-jansson: emitted object in 0.2609 seconds
-
-ucl: parsed input in 0.6649 seconds
-ucl: emitted config in 0.2423 seconds
-ucl: emitted json in 0.2329 seconds
-ucl: emitted compact json in 0.1811 seconds
-ucl: emitted yaml in 0.2489 seconds
-```
-
-So far, UCL seems to be significantly faster than jansson on parsing and slightly faster on emitting. Moreover,
-UCL compiled with optimizations (-O3) performs significantly faster:
-```
-ucl: parsed input in 0.3002 seconds
-ucl: emitted config in 0.1174 seconds
-ucl: emitted json in 0.1174 seconds
-ucl: emitted compact json in 0.0991 seconds
-ucl: emitted yaml in 0.1354 seconds
-```
-
-You can do your own benchmarks by running `make check` in libucl top directory.
-
-## Conclusion
-
-UCL has clear design that should be very convenient for reading and writing. At the same time it is compatible with
-JSON language and therefore can be used as a simple JSON parser. Macro logic provides an ability to extend configuration
-language (for example by including some lua code) and comments allow to disable or enable the parts of a configuration
-quickly.
diff --git a/contrib/libucl/autogen.sh b/contrib/libucl/autogen.sh
deleted file mode 100755
index 68f4a174b46e..000000000000
--- a/contrib/libucl/autogen.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/bin/sh
-autoreconf -i
diff --git a/contrib/libucl/configure.ac b/contrib/libucl/configure.ac
deleted file mode 100644
index 731b7113e689..000000000000
--- a/contrib/libucl/configure.ac
+++ /dev/null
@@ -1,188 +0,0 @@
-m4_define([maj_ver], [0])
-m4_define([med_ver], [8])
-m4_define([min_ver], [1])
-m4_define([so_version], [6:0:1])
-m4_define([ucl_version], [maj_ver.med_ver.min_ver])
-
-AC_INIT([libucl],[ucl_version],[https://github.com/vstakhov/libucl],[libucl])
-AC_CONFIG_SRCDIR([configure.ac])
-AM_INIT_AUTOMAKE([1.11 foreign -Wall -Wportability no-dist-gzip dist-xz])
-m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
-
-UCL_VERSION=ucl_version
-SO_VERSION=so_version
-
-AC_SUBST(UCL_VERSION)
-AC_SUBST(SO_VERSION)
-
-AC_PROG_CC_C99
-AM_PROG_CC_C_O
-AM_PROG_AR
-LT_INIT
-AC_CONFIG_MACRO_DIR([m4])
-AC_CONFIG_HEADERS([config.h])
-
-AC_C_CONST
-AC_TYPE_SIZE_T
-
-AC_CHECK_HEADERS_ONCE([fcntl.h unistd.h])
-AC_TYPE_OFF_T
-AC_FUNC_MMAP
-AC_CHECK_HEADERS_ONCE([fcntl.h])
-AC_CHECK_HEADERS_ONCE([sys/types.h])
-AC_CHECK_HEADERS_ONCE([sys/stat.h])
-AC_CHECK_HEADERS_ONCE([sys/param.h])
-AC_CHECK_HEADERS_ONCE([sys/mman.h])
-AC_CHECK_HEADERS_ONCE([stdlib.h])
-AC_CHECK_HEADERS_ONCE([stddef.h])
-AC_CHECK_HEADERS_ONCE([stdarg.h])
-AC_CHECK_HEADERS_ONCE([stdbool.h])
-AC_CHECK_HEADERS_ONCE([stdint.h])
-AC_CHECK_HEADERS_ONCE([string.h])
-AC_CHECK_HEADERS_ONCE([strings.h])
-AC_CHECK_HEADERS_ONCE([unistd.h])
-AC_CHECK_HEADERS_ONCE([ctype.h])
-AC_CHECK_HEADERS_ONCE([errno.h])
-AC_CHECK_HEADERS_ONCE([limits.h])
-AC_CHECK_HEADERS_ONCE([libgen.h])
-AC_CHECK_HEADERS_ONCE([stdio.h])
-AC_CHECK_HEADERS_ONCE([float.h])
-AC_CHECK_HEADERS_ONCE([math.h])
-AC_CHECK_HEADERS_ONCE([endian.h sys/endian.h machine/endian.h])
-
-dnl Example of default-disabled feature
-AC_ARG_ENABLE([urls], AS_HELP_STRING([--enable-urls],
- [Enable URLs fetch (requires libfetch or libcurl) @<:@default=no@:>@]), [],
- [enable_urls=no])
-AC_ARG_ENABLE([regex], AS_HELP_STRING([--enable-regex],
- [Enable regex checking for schema @<:@default=yes@:>@]), [],
- [enable_regex=yes])
-AC_ARG_ENABLE([signatures], AS_HELP_STRING([--enable-signatures],
- [Enable signatures check (requires openssl) @<:@default=no@:>@]), [],
- [enable_signatures=no])
-AC_ARG_ENABLE([lua], AS_HELP_STRING([--enable-lua],
- [Enable lua API build (requires lua libraries and headers) @<:@default=no@:>@]), [],
- [enable_lua=no])
-AC_ARG_ENABLE([utils],
- AS_HELP_STRING([--enable-utils], [Build and install utils @<:@default=no@:>@]),
- [case "${enableval}" in
- yes) utils=true ;;
- no) utils=false ;;
- *) AC_MSG_ERROR([bad value ${enableval} for --enable-utils]) ;;
- esac],[utils=false])
-AM_CONDITIONAL([UTILS], [test x$utils = xtrue])
-
-AS_IF([test "x$enable_signatures" = "xyes"], [
- AC_SEARCH_LIBS([CRYPTO_new_ex_data], [crypto], [
- AC_DEFINE(HAVE_OPENSSL, 1, [Define to 1 if you have the 'crypto' library (-lcrypto).])
- LIBCRYPTO_LIB="-lcrypto"
- LIBS_EXTRA="${LIBS_EXTRA} -lcrypto"
- ], [AC_MSG_ERROR([unable to find the CRYPTO_new_ex_data() function])])
-])
-AC_SUBST(LIBCRYPTO_LIB)
-AC_PATH_PROG(PANDOC, pandoc, [/non/existent])
-
-AC_SEARCH_LIBS([clock_gettime], [rt], [], [
- AC_CHECK_HEADER([mach/mach_time.h], [
- AC_DEFINE(HAVE_MACH_MACH_TIME_H, 1, [Define to 1 on Darwin])
- ], [AC_MSG_ERROR([unable to find clock_gettime or mach_absolute_time])])
-])
-AC_SEARCH_LIBS([remainder], [m], [], [AC_MSG_ERROR([unable to find remainder() function])])
-
-AS_IF([test "x$enable_regex" = "xyes"], [
- AC_CHECK_HEADER([regex.h], [
- AC_DEFINE(HAVE_REGEX_H, 1, [Define to 1 if you have the <regex.h> header file.])
- AC_SEARCH_LIBS([regexec], [regex], [
- AS_IF([test "x$ac_cv_search_regexec" = "x-lregex"], [
- LIBREGEX_LIB="-lregex"
- LIBS_EXTRA="${LIBS_EXTRA} -lregex"
- ]
- )],
- [AC_MSG_ERROR([unable to find the regexec() function])])],
- [AC_MSG_ERROR([unable to find the regex.h header])
- ],
- [#include <sys/types.h>])
-])
-AC_SUBST(LIBREGEX_LIB)
-
-AS_IF([test "x$enable_lua" = "xyes"], [
- AX_PROG_LUA([5.1], [], [
- AX_LUA_HEADERS([
- AX_LUA_LIBS([
- AC_DEFINE(HAVE_LUA, 1, [Define to 1 for lua support.])
- with_lua="yes"
- ], [AC_MSG_ERROR([unable to find the lua libraries])
- ])
- ], [AC_MSG_ERROR([unable to find the lua header files])
- ])
- ], [AC_MSG_ERROR([unable to find the lua interpreter])])
-], [with_lua="no"])
-
-AM_CONDITIONAL([LUA_SUB], [test "$with_lua" = "yes"])
-
-AS_IF([test "x$enable_urls" = "xyes"], [
- AC_CHECK_HEADER([fetch.h], [
- AC_DEFINE(HAVE_FETCH_H, 1, [Define to 1 if you have the <fetch.h> header file.])
- AC_CHECK_LIB(fetch, fetchXGet, [
- AC_DEFINE(HAVE_LIBFETCH, 1, [Define to 1 if you have the 'fetch' library (-lfetch).])
- LIBFETCH_LIBS="-lfetch"
- have_libfetch="yes"
- LIBS_EXTRA="${LIBS_EXTRA} -lfetch"
- ])
- ], [],[
- #include <stdio.h>
- #ifdef HAVE_SYS_PARAM_H
- #include <sys/param.h>
- #endif
- ])
- AC_SUBST(LIBFETCH_LIBS)
-
- AS_IF([ test "x$have_libfetch" != "xyes"], [
- dnl Fallback to libcurl
- PKG_CHECK_MODULES([CURL], [libcurl], [
- AC_DEFINE(CURL_FOUND, 1, [Use libcurl])
- LIBS_EXTRA="${LIBS_EXTRA} -lcurl"],
- [AC_MSG_ERROR([unable to find neither libfetch nor libcurl])])
- ])
- AC_SUBST(CURL_FOUND)
- AC_SUBST(CURL_LIBS)
- AC_SUBST(CURL_CFLAGS)
-])
-
-AC_SUBST(LIBS_EXTRA)
-
-AC_MSG_CHECKING(for GCC atomic builtins)
-AC_LINK_IFELSE([
- AC_LANG_SOURCE([[
- int main() {
- volatile unsigned long val = 1;
- __sync_synchronize();
- __sync_val_compare_and_swap(&val, 1, 0);
- __sync_add_and_fetch(&val, 1);
- __sync_sub_and_fetch(&val, 1);
- return 0;
- }
- ]])
-],
-[
- AC_MSG_RESULT([yes])
- AC_DEFINE([HAVE_ATOMIC_BUILTINS], [1], [Has gcc/MSVC atomic intrinsics])
-],
-[
- AC_MSG_RESULT([no])
- AC_DEFINE([HAVE_ATOMIC_BUILTINS], [0], [Has gcc/MSVC atomic intrinsics])
- AC_MSG_WARN([Libucl references could be thread-unsafe because atomic builtins are missing])
-])
-
-AX_CODE_COVERAGE
-
-AC_CONFIG_FILES(Makefile \
- src/Makefile \
- lua/Makefile
- tests/Makefile \
- utils/Makefile \
- doc/Makefile \
- lua/libucl.rockspec \
- libucl.pc)
-AC_CONFIG_FILES([stamp-h], [echo timestamp > stamp-h])
-AC_OUTPUT
diff --git a/contrib/libucl/doc/Makefile.am b/contrib/libucl/doc/Makefile.am
deleted file mode 100644
index dcfacf6a9a25..000000000000
--- a/contrib/libucl/doc/Makefile.am
+++ /dev/null
@@ -1,9 +0,0 @@
-EXTRA_DIST = api.md
-
-dist_man_MANS = libucl.3
-
-gen-man: @PANDOC@
- tail -n +$$(grep -n '# Synopsis' api.md | cut -d':' -f1) api.md | \
- cat pandoc.template - | sed -e 's/^# \(.*\)/# \U\1/' \
- -e "s/%%date%%/$$(LANG=C date +'%d %B, %Y')/" | \
- @PANDOC@ -s -f markdown -t man -o libucl.3
diff --git a/contrib/libucl/doc/api.md b/contrib/libucl/doc/api.md
deleted file mode 100644
index a0d33c0e68a9..000000000000
--- a/contrib/libucl/doc/api.md
+++ /dev/null
@@ -1,506 +0,0 @@
-# API documentation
-
-**Table of Contents** *generated with [DocToc](http://doctoc.herokuapp.com/)*
-
-- [Synopsis](#synopsis)
-- [Description](#description)
- - [Parser functions](#parser-functions)
- - [Emitting functions](#emitting-functions)
- - [Conversion functions](#conversion-functions)
- - [Generation functions](#generation-functions)
- - [Iteration functions](#iteration-functions)
- - [Validation functions](#validation-functions)
- - [Utility functions](#utility-functions)
-- [Parser functions](#parser-functions-1)
- - [ucl_parser_new](#ucl_parser_new)
- - [ucl_parser_register_macro](#ucl_parser_register_macro)
- - [ucl_parser_register_variable](#ucl_parser_register_variable)
- - [ucl_parser_add_chunk](#ucl_parser_add_chunk)
- - [ucl_parser_add_string](#ucl_parser_add_string)
- - [ucl_parser_add_file](#ucl_parser_add_file)
- - [ucl_parser_get_object](#ucl_parser_get_object)
- - [ucl_parser_get_error](#ucl_parser_get_error)
- - [ucl_parser_free](#ucl_parser_free)
- - [ucl_pubkey_add](#ucl_pubkey_add)
- - [ucl_parser_set_filevars](#ucl_parser_set_filevars)
- - [Parser usage example](#parser-usage-example)
-- [Emitting functions](#emitting-functions-1)
- - [ucl_object_emit](#ucl_object_emit)
- - [ucl_object_emit_full](#ucl_object_emit_full)
-- [Conversion functions](#conversion-functions-1)
-- [Generation functions](#generation-functions-1)
- - [ucl_object_new](#ucl_object_new)
- - [ucl_object_typed_new](#ucl_object_typed_new)
- - [Primitive objects generation](#primitive-objects-generation)
- - [ucl_object_fromstring_common](#ucl_object_fromstring_common)
-- [Iteration functions](#iteration-functions-1)
- - [ucl_iterate_object](#ucl_iterate_object)
-- [Validation functions](#validation-functions-1)
- - [ucl_object_validate](#ucl_object_validate)
-
-# Synopsis
-
-`#include <ucl.h>`
-
-# Description
-
-Libucl is a parser and `C` API to parse and generate `ucl` objects. Libucl consist of several groups of functions:
-
-### Parser functions
-Used to parse `ucl` files and provide interface to extract `ucl` object. Currently, `libucl` can parse only full `ucl` documents, for instance, it is impossible to parse a part of document and therefore it is impossible to use `libucl` as a streaming parser. In future, this limitation can be removed.
-
-### Emitting functions
-Convert `ucl` objects to some textual or binary representation. Currently, libucl supports the following exports:
-
-- `JSON` - valid json format (can possibly lose some original data, such as implicit arrays)
-- `Config` - human-readable configuration format (lossless)
-- `YAML` - embedded yaml format (has the same limitations as `json` output)
-
-### Conversion functions
-Help to convert `ucl` objects to C types. These functions are used to convert `ucl_object_t` to C primitive types, such as numbers, strings or boolean values.
-
-### Generation functions
-Allow creation of `ucl` objects from C types and creating of complex `ucl` objects, such as hashes or arrays from primitive `ucl` objects, such as numbers or strings.
-
-### Iteration functions
-Iterate over `ucl` complex objects or over a chain of values, for example when a key in an object has multiple values (that can be treated as implicit array or implicit consolidation).
-
-### Validation functions
-Validation functions are used to validate some object `obj` using json-schema compatible object `schema`. Both input and schema must be UCL objects to perform validation.
-
-### Utility functions
-Provide basic utilities to manage `ucl` objects: creating, removing, retaining and releasing reference count and so on.
-
-# Parser functions
-
-Parser functions operates with `struct ucl_parser`.
-
-### ucl_parser_new
-
-~~~C
-struct ucl_parser* ucl_parser_new (int flags);
-~~~
-
-Creates new parser with the specified flags:
-
-- `UCL_PARSER_KEY_LOWERCASE` - lowercase keys parsed
-- `UCL_PARSER_ZEROCOPY` - try to use zero-copy mode when reading files (in zero-copy mode text chunk being parsed without copying strings so it should exist till any object parsed is used)
-- `UCL_PARSER_NO_TIME` - treat time values as strings without parsing them as floats
-
-### ucl_parser_register_macro
-
-~~~C
-void ucl_parser_register_macro (struct ucl_parser *parser,
- const char *macro, ucl_macro_handler handler, void* ud);
-~~~
-
-Register new macro with name .`macro` parsed by handler `handler` that accepts opaque data pointer `ud`. Macro handler should be of the following type:
-
-~~~C
-bool (*ucl_macro_handler) (const unsigned char *data,
- size_t len, void* ud);`
-~~~
-
-Handler function accepts macro text `data` of length `len` and the opaque pointer `ud`. If macro is parsed successfully the handler should return `true`. `false` indicates parsing failure and the parser can be terminated.
-
-### ucl_parser_register_variable
-
-~~~C
-void ucl_parser_register_variable (struct ucl_parser *parser,
- const char *var, const char *value);
-~~~
-
-Register new variable $`var` that should be replaced by the parser to the `value` string.
-
-### ucl_parser_add_chunk
-
-~~~C
-bool ucl_parser_add_chunk (struct ucl_parser *parser,
- const unsigned char *data, size_t len);
-~~~
-
-Add new text chunk with `data` of length `len` to the parser. At the moment, `libucl` parser is not a streamlined parser and chunk *must* contain the *valid* ucl object. For example, this object should be valid:
-
-~~~json
-{ "var": "value" }
-~~~
-
-while this one won't be parsed correctly:
-
-~~~json
-{ "var":
-~~~
-
-This limitation may possible be removed in future.
-
-### ucl_parser_add_string
-~~~C
-bool ucl_parser_add_string (struct ucl_parser *parser,
- const char *data, size_t len);
-~~~
-
-This function acts exactly like `ucl_parser_add_chunk` does but if `len` argument is zero, then the string `data` must be zero-terminated and the actual length is calculated up to `\0` character.
-
-### ucl_parser_add_file
-
-~~~C
-bool ucl_parser_add_file (struct ucl_parser *parser,
- const char *filename);
-~~~
-
-Load file `filename` and parse it with the specified `parser`. This function uses `mmap` call to load file, therefore, it should not be `shrunk` during parsing. Otherwise, `libucl` can cause memory corruption and terminate the calling application. This function is also used by the internal handler of `include` macro, hence, this macro has the same limitation.
-
-### ucl_parser_get_object
-
-~~~C
-ucl_object_t* ucl_parser_get_object (struct ucl_parser *parser);
-~~~
-
-If the `ucl` data has been parsed correctly this function returns the top object for the parser. Otherwise, this function returns the `NULL` pointer. The reference count for `ucl` object returned is increased by one, therefore, a caller should decrease reference by using `ucl_object_unref` to free object after usage.
-
-### ucl_parser_get_error
-
-~~~C
-const char *ucl_parser_get_error(struct ucl_parser *parser);
-~~~
-
-Returns the constant error string for the parser object. If no error occurred during parsing a `NULL` object is returned. A caller should not try to free or modify this string.
-
-### ucl_parser_free
-
-~~~C
-void ucl_parser_free (struct ucl_parser *parser);
-~~~
-
-Frees memory occupied by the parser object. The reference count for top object is decreased as well, however if the function `ucl_parser_get_object` was called previously then the top object won't be freed.
-
-### ucl_pubkey_add
-
-~~~C
-bool ucl_pubkey_add (struct ucl_parser *parser,
- const unsigned char *key, size_t len);
-~~~
-
-This function adds a public key from text blob `key` of length `len` to the `parser` object. This public key should be in the `PEM` format and can be used by `.includes` macro for checking signatures of files included. `Openssl` support should be enabled to make this function working. If a key cannot be added (e.g. due to format error) or `openssl` was not linked to `libucl` then this function returns `false`.
-
-### ucl_parser_set_filevars
-
-~~~C
-bool ucl_parser_set_filevars (struct ucl_parser *parser,
- const char *filename, bool need_expand);
-~~~
-
-Add the standard file variables to the `parser` based on the `filename` specified:
-
-- `$FILENAME` - a filename of `ucl` input
-- `$CURDIR` - a current directory of the input
-
-For example, if a `filename` param is `../something.conf` then the variables will have the following values:
-
-- `$FILENAME` - "../something.conf"
-- `$CURDIR` - ".."
-
-if `need_expand` parameter is `true` then all relative paths are expanded using `realpath` call. In this example if `..` is `/etc/dir` then variables will have these values:
-
-- `$FILENAME` - "/etc/something.conf"
-- `$CURDIR` - "/etc"
-
-## Parser usage example
-
-The following example loads, parses and extracts `ucl` object from stdin using `libucl` parser functions (the length of input is limited to 8K):
-
-~~~C
-char inbuf[8192];
-struct ucl_parser *parser = NULL;
-int ret = 0, r = 0;
-ucl_object_t *obj = NULL;
-FILE *in;
-
-in = stdin;
-parser = ucl_parser_new (0);
-while (!feof (in) && r < (int)sizeof (inbuf)) {
- r += fread (inbuf + r, 1, sizeof (inbuf) - r, in);
-}
-ucl_parser_add_chunk (parser, inbuf, r);
-fclose (in);
-
-if (ucl_parser_get_error (parser)) {
- printf ("Error occurred: %s\n", ucl_parser_get_error (parser));
- ret = 1;
-}
-else {
- obj = ucl_parser_get_object (parser);
-}
-
-if (parser != NULL) {
- ucl_parser_free (parser);
-}
-if (obj != NULL) {
- ucl_object_unref (obj);
-}
-return ret;
-~~~
-
-# Emitting functions
-
-Libucl can transform UCL objects to a number of textual formats:
-
-- configuration (`UCL_EMIT_CONFIG`) - nginx like human readable configuration file where implicit arrays are transformed to the duplicate keys
-- compact json: `UCL_EMIT_JSON_COMPACT` - single line valid json without spaces
-- formatted json: `UCL_EMIT_JSON` - pretty formatted JSON with newlines and spaces
-- compact yaml: `UCL_EMIT_YAML` - compact YAML output
-
-Moreover, libucl API allows to select a custom set of emitting functions allowing
-efficient and zero-copy output of libucl objects. Libucl uses the following structure to support this feature:
-
-~~~C
-struct ucl_emitter_functions {
- /** Append a single character */
- int (*ucl_emitter_append_character) (unsigned char c, size_t nchars, void *ud);
- /** Append a string of a specified length */
- int (*ucl_emitter_append_len) (unsigned const char *str, size_t len, void *ud);
- /** Append a 64 bit integer */
- int (*ucl_emitter_append_int) (int64_t elt, void *ud);
- /** Append floating point element */
- int (*ucl_emitter_append_double) (double elt, void *ud);
- /** Opaque userdata pointer */
- void *ud;
-};
-~~~
-
-This structure defines the following callbacks:
-
-- `ucl_emitter_append_character` - a function that is called to append `nchars` characters equal to `c`
-- `ucl_emitter_append_len` - used to append a string of length `len` starting from pointer `str`
-- `ucl_emitter_append_int` - this function applies to integer numbers
-- `ucl_emitter_append_double` - this function is intended to output floating point variable
-
-The set of these functions could be used to output text formats of `UCL` objects to different structures or streams.
-
-Libucl provides the following functions for emitting UCL objects:
-
-### ucl_object_emit
-
-~~~C
-unsigned char *ucl_object_emit (const ucl_object_t *obj, enum ucl_emitter emit_type);
-~~~
-
-Allocate a string that is suitable to fit the underlying UCL object `obj` and fill it with the textual representation of the object `obj` according to style `emit_type`. The caller should free the returned string after using.
-
-### ucl_object_emit_full
-
-~~~C
-bool ucl_object_emit_full (const ucl_object_t *obj, enum ucl_emitter emit_type,
- struct ucl_emitter_functions *emitter);
-~~~
-
-This function is similar to the previous with the exception that it accepts the additional argument `emitter` that defines the concrete set of output functions. This emit function could be useful for custom structures or streams emitters (including C++ ones, for example).
-
-# Conversion functions
-
-Conversion functions are used to convert UCL objects to primitive types, such as strings, numbers, or boolean values. There are two types of conversion functions:
-
-- safe: try to convert an ucl object to a primitive type and fail if such a conversion is not possible
-- unsafe: return primitive type without additional checks, if the object cannot be converted then some reasonable default is returned (NULL for strings and 0 for numbers)
-
-Also there is a single `ucl_object_tostring_forced` function that converts any UCL object (including compound types - arrays and objects) to a string representation. For objects, arrays, booleans and numeric types this function performs emitting to a compact json format actually.
-
-Here is a list of all conversion functions:
-
-- `ucl_object_toint` - returns `int64_t` of UCL object
-- `ucl_object_todouble` - returns `double` of UCL object
-- `ucl_object_toboolean` - returns `bool` of UCL object
-- `ucl_object_tostring` - returns `const char *` of UCL object (this string is NULL terminated)
-- `ucl_object_tolstring` - returns `const char *` and `size_t` len of UCL object (string does not need to be NULL terminated)
-- `ucl_object_tostring_forced` - returns string representation of any UCL object
-
-Strings returned by these pointers are associated with the UCL object and exist over its lifetime. A caller should not free this memory.
-
-# Generation functions
-
-It is possible to generate UCL objects from C primitive types. Moreover, libucl allows creation and modifying complex UCL objects, such as arrays or associative objects.
-
-## ucl_object_new
-~~~C
-ucl_object_t * ucl_object_new (void)
-~~~
-
-Creates new object of type `UCL_NULL`. This object should be released by caller.
-
-## ucl_object_typed_new
-~~~C
-ucl_object_t * ucl_object_typed_new (unsigned int type)
-~~~
-
-Create an object of a specified type:
-- `UCL_OBJECT` - UCL object - key/value pairs
-- `UCL_ARRAY` - UCL array
-- `UCL_INT` - integer number
-- `UCL_FLOAT` - floating point number
-- `UCL_STRING` - NULL terminated string
-- `UCL_BOOLEAN` - boolean value
-- `UCL_TIME` - time value (floating point number of seconds)
-- `UCL_USERDATA` - opaque userdata pointer (may be used in macros)
-- `UCL_NULL` - null value
-
-This object should be released by caller.
-
-## Primitive objects generation
-Libucl provides the functions similar to inverse conversion functions called with the specific C type:
-- `ucl_object_fromint` - converts `int64_t` to UCL object
-- `ucl_object_fromdouble` - converts `double` to UCL object
-- `ucl_object_frombool` - converts `bool` to UCL object
-- `ucl_object_fromstring` - converts `const char *` to UCL object (this string should be NULL terminated)
-- `ucl_object_fromlstring` - converts `const char *` and `size_t` len to UCL object (string does not need to be NULL terminated)
-
-Also there is a function to generate UCL object from a string performing various parsing or conversion operations called `ucl_object_fromstring_common`.
-
-## ucl_object_fromstring_common
-~~~C
-ucl_object_t * ucl_object_fromstring_common (const char *str,
- size_t len, enum ucl_string_flags flags)
-~~~
-
-This function is used to convert a string `str` of size `len` to a UCL object applying `flags` conversions. If `len` is equal to zero then a `str` is assumed as NULL-terminated. This function supports the following flags (a set of flags can be specified using logical `OR` operation):
-
-- `UCL_STRING_ESCAPE` - perform JSON escape
-- `UCL_STRING_TRIM` - trim leading and trailing whitespaces
-- `UCL_STRING_PARSE_BOOLEAN` - parse passed string and detect boolean
-- `UCL_STRING_PARSE_INT` - parse passed string and detect integer number
-- `UCL_STRING_PARSE_DOUBLE` - parse passed string and detect integer or float number
-- `UCL_STRING_PARSE_TIME` - parse time values as floating point numbers
-- `UCL_STRING_PARSE_NUMBER` - parse passed string and detect number (both float, integer and time types)
-- `UCL_STRING_PARSE` - parse passed string (and detect booleans, numbers and time values)
-- `UCL_STRING_PARSE_BYTES` - assume that numeric multipliers are in bytes notation, for example `10k` means `10*1024` and not `10*1000` as assumed without this flag
-
-If parsing operations fail then the resulting UCL object will be a `UCL_STRING`. A caller should always check the type of the returned object and release it after using.
-
-# Iteration functions
-
-Iteration are used to iterate over UCL compound types: arrays and objects. Moreover, iterations could be performed over the keys with multiple values (implicit arrays).
-There are two types of iterators API: old and unsafe one via `ucl_iterate_object` and the proposed interface of safe iterators.
-
-
-## ucl_iterate_object
-~~~C
-const ucl_object_t* ucl_iterate_object (const ucl_object_t *obj,
- ucl_object_iter_t *iter, bool expand_values);
-~~~
-
-This function accepts opaque iterator pointer `iter`. In the first call this iterator *must* be initialized to `NULL`. Iterator is changed by this function call. `ucl_iterate_object` returns the next UCL object in the compound object `obj` or `NULL` if all objects have been iterated. The reference count of the object returned is not increased, so a caller should not unref the object or modify its content (e.g. by inserting to another compound object). The object `obj` should not be changed during the iteration process as well. `expand_values` flag speicifies whether `ucl_iterate_object` should expand keys with multiple values. The general rule is that if you need to iterate through the *object* or *explicit array*, then you always need to set this flag to `true`. However, if you get some key in the object and want to extract all its values then you should set `expand_values` to `false`. Mixing of iteration types is not permitted since the iterator is set according to the iteration type and cannot be reused. Here is an example of iteration over the objects using libucl API (assuming that `top` is `UCL_OBJECT` in this example):
-
-~~~C
-ucl_object_iter_t it = NULL, it_obj = NULL;
-const ucl_object_t *cur, *tmp;
-
-/* Iterate over the object */
-while ((obj = ucl_iterate_object (top, &it, true))) {
- printf ("key: \"%s\"\n", ucl_object_key (obj));
- /* Iterate over the values of a key */
- while ((cur = ucl_iterate_object (obj, &it_obj, false))) {
- printf ("value: \"%s\"\n",
- ucl_object_tostring_forced (cur));
- }
-}
-~~~
-
-## Safe iterators API
-
-Safe iterators are defined to clarify iterating over UCL objects and simplify flattening of UCL objects in non-trivial cases.
-For example, if there is an implicit array that contains another array and a boolean value it is extremely unclear how to iterate over
-such an object. Safe iterators are desinged to define two sorts of iteration:
-
-1. Iteration over complex objects with expanding all values
-2. Iteration over complex objects without expanding of values
-
-The following example demonstrates the difference between these two types of iteration:
-
-~~~
-key = 1;
-key = [2, 3, 4];
-
-Iteration with expansion:
-
-1, 2, 3, 4
-
-Iteration without expansion:
-
-1, [2, 3, 4]
-~~~
-
-UCL defines the following functions to manage safe iterators:
-
-- `ucl_object_iterate_new` - creates new safe iterator
-- `ucl_object_iterate_reset` - resets iterator to a new object
-- `ucl_object_iterate_safe` - safely iterate the object inside iterator. Note: function may allocate and free memory during its operation. Therefore it returns `NULL` either while trying to access item after the last one or when exception (such as memory allocation failure) happens.
-- `ucl_object_iter_chk_excpn` - check if the last call to `ucl_object_iterate_safe` ended up in unrecoverable exception (e.g. `ENOMEM`).
-- `ucl_object_iterate_free` - free memory associated with the safe iterator
-
-Please note that unlike unsafe iterators, safe iterators *must* be explicitly initialized and freed.
-An assert is likely generated if you use uninitialized or `NULL` iterator in all safe iterators functions.
-
-~~~C
-ucl_object_iter_t it;
-const ucl_object_t *cur;
-
-it = ucl_object_iterate_new (obj);
-
-while ((cur = ucl_object_iterate_safe (it, true)) != NULL) {
- /* Do something */
-}
-/* Check error condition */
-if (ucl_object_iter_chk_excpn (it)) {
- ucl_object_iterate_free (it);
- exit (1);
-}
-
-/* Switch to another object */
-it = ucl_object_iterate_reset (it, another_obj);
-
-while ((cur = ucl_object_iterate_safe (it, true)) != NULL) {
- /* Do something else */
-}
-/* Check error condition */
-if (ucl_object_iter_chk_excpn (it)) {
- ucl_object_iterate_free (it);
- exit (1);
-}
-
-ucl_object_iterate_free (it);
-~~~
-
-# Validation functions
-
-Currently, there is only one validation function called `ucl_object_validate`. It performs validation of object using the specified schema. This function is defined as following:
-
-## ucl_object_validate
-~~~C
-bool ucl_object_validate (const ucl_object_t *schema,
- const ucl_object_t *obj, struct ucl_schema_error *err);
-~~~
-
-This function uses ucl object `schema`, that must be valid in terms of `json-schema` draft v4, to validate input object `obj`. If this function returns `true` then validation procedure has been succeed. Otherwise, `false` is returned and `err` is set to a specific value. If a caller sets `err` to NULL then this function does not set any error just returning `false`. Error is the structure defined as following:
-
-~~~C
-struct ucl_schema_error {
- enum ucl_schema_error_code code; /* error code */
- char msg[128]; /* error message */
- ucl_object_t *obj; /* object where error occurred */
-};
-~~~
-
-Caller may use `code` field to get a numeric error code:
-
-~~~C
-enum ucl_schema_error_code {
- UCL_SCHEMA_OK = 0, /* no error */
- UCL_SCHEMA_TYPE_MISMATCH, /* type of object is incorrect */
- UCL_SCHEMA_INVALID_SCHEMA, /* schema is invalid */
- UCL_SCHEMA_MISSING_PROPERTY,/* missing properties */
- UCL_SCHEMA_CONSTRAINT, /* constraint found */
- UCL_SCHEMA_MISSING_DEPENDENCY, /* missing dependency */
- UCL_SCHEMA_UNKNOWN /* generic error */
-};
-~~~
-
-`msg` is a string description of an error and `obj` is an object where error has occurred. Error object is not allocated by libucl, so there is no need to free it after validation (a static object should thus be used).
diff --git a/contrib/libucl/doc/lua_api.md b/contrib/libucl/doc/lua_api.md
deleted file mode 100644
index 7da414903b01..000000000000
--- a/contrib/libucl/doc/lua_api.md
+++ /dev/null
@@ -1,196 +0,0 @@
-## Module `ucl`
-
-This lua module allows to parse objects from strings and to store data into
-ucl objects. It uses `libucl` C library to parse and manipulate with ucl objects.
-
-Example:
-
-~~~lua
-local ucl = require("ucl")
-
-local parser = ucl.parser()
-local res,err = parser:parse_string('{key=value}')
-
-if not res then
- print('parser error: ' .. err)
-else
- local obj = parser:get_object()
- local got = ucl.to_format(obj, 'json')
-end
-
-local table = {
- str = 'value',
- num = 100500,
- null = ucl.null,
- func = function ()
- return 'huh'
- end
-}
-
-
-print(ucl.to_format(table, 'ucl'))
--- Output:
---[[
-num = 100500;
-str = "value";
-null = null;
-func = "huh";
---]]
-~~~
-
-###Brief content:
-
-**Functions**:
-
-> [`ucl_object_push_lua(L, obj, allow_array)`](#function-ucl_object_push_lual-obj-allow_array)
-
-> [`ucl.to_format(var, format)`](#function-uclto_formatvar-format)
-
-
-
-**Methods**:
-
-> [`parser:parse_file(name)`](#method-parserparse_filename)
-
-> [`parser:parse_string(input)`](#method-parserparse_stringinput)
-
-> [`parser:get_object()`](#method-parserget_object)
-
-
-## Functions
-
-The module `ucl` defines the following functions.
-
-### Function `ucl_object_push_lua(L, obj, allow_array)`
-
-This is a `C` function to push `UCL` object as lua variable. This function
-converts `obj` to lua representation using the following conversions:
-
-- *scalar* values are directly presented by lua objects
-- *userdata* values are converted to lua function objects using `LUA_REGISTRYINDEX`,
-this can be used to pass functions from lua to c and vice-versa
-- *arrays* are converted to lua tables with numeric indices suitable for `ipairs` iterations
-- *objects* are converted to lua tables with string indices
-
-**Parameters:**
-
-- `L {lua_State}`: lua state pointer
-- `obj {ucl_object_t}`: object to push
-- `allow_array {bool}`: expand implicit arrays (should be true for all but partial arrays)
-
-**Returns:**
-
-- `{int}`: `1` if an object is pushed to lua
-
-Back to [module description](#module-ucl).
-
-### Function `ucl.to_format(var, format)`
-
-Converts lua variable `var` to the specified `format`. Formats supported are:
-
-- `json` - fine printed json
-- `json-compact` - compacted json
-- `config` - fine printed configuration
-- `ucl` - same as `config`
-- `yaml` - embedded yaml
-
-If `var` contains function, they are called during output formatting and if
-they return string value, then this value is used for ouptut.
-
-**Parameters:**
-
-- `var {variant}`: any sort of lua variable (if userdata then metafield `__to_ucl` is searched for output)
-- `format {string}`: any available format
-
-**Returns:**
-
-- `{string}`: string representation of `var` in the specific `format`.
-
-Example:
-
-~~~lua
-local table = {
- str = 'value',
- num = 100500,
- null = ucl.null,
- func = function ()
- return 'huh'
- end
-}
-
-
-print(ucl.to_format(table, 'ucl'))
--- Output:
---[[
-num = 100500;
-str = "value";
-null = null;
-func = "huh";
---]]
-~~~
-
-Back to [module description](#module-ucl).
-
-
-## Methods
-
-The module `ucl` defines the following methods.
-
-### Method `parser:parse_file(name)`
-
-Parse UCL object from file.
-
-**Parameters:**
-
-- `name {string}`: filename to parse
-
-**Returns:**
-
-- `{bool[, string]}`: if res is `true` then file has been parsed successfully, otherwise an error string is also returned
-
-Example:
-
-~~~lua
-local parser = ucl.parser()
-local res,err = parser:parse_file('/some/file.conf')
-
-if not res then
- print('parser error: ' .. err)
-else
- -- Do something with object
-end
-~~~
-
-Back to [module description](#module-ucl).
-
-### Method `parser:parse_string(input)`
-
-Parse UCL object from file.
-
-**Parameters:**
-
-- `input {string}`: string to parse
-
-**Returns:**
-
-- `{bool[, string]}`: if res is `true` then file has been parsed successfully, otherwise an error string is also returned
-
-Back to [module description](#module-ucl).
-
-### Method `parser:get_object()`
-
-Get top object from parser and export it to lua representation.
-
-**Parameters:**
-
- nothing
-
-**Returns:**
-
-- `{variant or nil}`: ucl object as lua native variable
-
-Back to [module description](#module-ucl).
-
-
-Back to [top](#).
-
diff --git a/contrib/libucl/doc/pandoc.template b/contrib/libucl/doc/pandoc.template
deleted file mode 100644
index 2effe1a157ef..000000000000
--- a/contrib/libucl/doc/pandoc.template
+++ /dev/null
@@ -1,12 +0,0 @@
-% LIBUCL(3) Libucl manual
-% Vsevolod Stakhov <vsevolod@highsecure.ru>
-% %%date%%
-
-# Name
-
-**ucl_parser_new**, **ucl_parser_register_macro**, **ucl_parser_register_variable**, **ucl_parser_add_chunk**, **ucl_parser_add_string**, **ucl_parser_add_file**, **ucl_parser_get_object**, **ucl_parser_get_error**, **ucl_parser_free**, **ucl_pubkey_add**, **ucl_parser_set_filevars** - universal configuration library parser and utility functions
-
-# Library
-
-UCL library (libucl, -lucl)
-
diff --git a/contrib/libucl/examples/ucl_cpp.cc b/contrib/libucl/examples/ucl_cpp.cc
deleted file mode 100644
index 2d15d84a6c8d..000000000000
--- a/contrib/libucl/examples/ucl_cpp.cc
+++ /dev/null
@@ -1,26 +0,0 @@
-#include <iostream>
-#include <string>
-#include "ucl++.h"
-
-int main(int argc, char **argv)
-{
- std::string input, err;
-
- input.assign((std::istreambuf_iterator<char>(std::cin)),
- std::istreambuf_iterator<char>());
-
- auto obj = ucl::Ucl::parse(input, err);
-
- if (obj) {
- std::cout << obj.dump(UCL_EMIT_CONFIG) << std::endl;
-
- for (const auto &o : obj) {
- std::cout << o.dump(UCL_EMIT_CONFIG) << std::endl;
- }
- }
- else {
- std::cerr << "Error: " << err << std::endl;
-
- return 1;
- }
-}
diff --git a/contrib/libucl/haskell/hucl.hs b/contrib/libucl/haskell/hucl.hs
deleted file mode 100644
index 2dd3ac01e4c0..000000000000
--- a/contrib/libucl/haskell/hucl.hs
+++ /dev/null
@@ -1,123 +0,0 @@
-{-# LANGUAGE ForeignFunctionInterface #-}
-
--- an example UCL FFI module:
--- uses the Object Model from Messagepack to emit
---
-
-module Data.UCL ( unpack ) where
-import Foreign.C
-import Foreign.Ptr
-import System.IO.Unsafe ( unsafePerformIO )
-import qualified Data.Text as T
-import qualified Data.Vector as V
-import qualified Data.MessagePack as MSG
-
-type ParserHandle = Ptr ()
-type UCLObjectHandle = Ptr ()
-type UCLIterHandle = Ptr ()
-type UCLEmitterType = CInt
-type ErrorString = String
-
-
-foreign import ccall "ucl_parser_new" ucl_parser_new :: CInt -> ParserHandle
-foreign import ccall "ucl_parser_add_string" ucl_parser_add_string :: ParserHandle -> CString -> CUInt -> IO Bool
-foreign import ccall "ucl_parser_add_file" ucl_parser_add_file :: ParserHandle -> CString -> IO Bool
-foreign import ccall "ucl_parser_get_object" ucl_parser_get_object :: ParserHandle -> UCLObjectHandle
-foreign import ccall "ucl_parser_get_error" ucl_parser_get_error :: ParserHandle -> CString
-
-foreign import ccall "ucl_object_iterate_new" ucl_object_iterate_new :: UCLObjectHandle -> UCLIterHandle
-foreign import ccall "ucl_object_iterate_safe" ucl_object_iterate_safe :: UCLIterHandle -> Bool -> UCLObjectHandle
-foreign import ccall "ucl_object_type" ucl_object_type :: UCLObjectHandle -> CUInt
-foreign import ccall "ucl_object_key" ucl_object_key :: UCLObjectHandle -> CString
-foreign import ccall "ucl_object_toint" ucl_object_toint :: UCLObjectHandle -> CInt
-foreign import ccall "ucl_object_todouble" ucl_object_todouble :: UCLObjectHandle -> CDouble
-foreign import ccall "ucl_object_tostring" ucl_object_tostring :: UCLObjectHandle -> CString
-foreign import ccall "ucl_object_toboolean" ucl_object_toboolean :: UCLObjectHandle -> Bool
-
-foreign import ccall "ucl_object_emit" ucl_object_emit :: UCLObjectHandle -> UCLEmitterType -> CString
-foreign import ccall "ucl_object_emit_len" ucl_object_emit_len :: UCLObjectHandle -> UCLEmitterType -> Ptr CSize -> IO CString
-
-type UCL_TYPE = CUInt
-ucl_OBJECT :: UCL_TYPE
-ucl_OBJECT = 0
-ucl_ARRAY :: UCL_TYPE
-ucl_ARRAY = 1
-ucl_INT :: UCL_TYPE
-ucl_INT = 2
-ucl_FLOAT :: UCL_TYPE
-ucl_FLOAT = 3
-ucl_STRING :: UCL_TYPE
-ucl_STRING = 4
-ucl_BOOLEAN :: UCL_TYPE
-ucl_BOOLEAN = 5
-ucl_TIME :: UCL_TYPE
-ucl_TIME = 6
-ucl_USERDATA :: UCL_TYPE
-ucl_USERDATA = 7
-ucl_NULL :: UCL_TYPE
-ucl_NULL = 8
-
-ucl_emit_json :: UCLEmitterType
-ucl_emit_json = 0
-ucl_emit_json_compact :: UCLEmitterType
-ucl_emit_json_compact = 1 :: UCLEmitterType
-ucl_emit_msgpack :: UCLEmitterType
-ucl_emit_msgpack = 4 :: UCLEmitterType
-
-ucl_parser_parse_string_pure :: String -> Either UCLObjectHandle ErrorString
-ucl_parser_parse_string_pure s = unsafePerformIO $ do
- cs <- newCString s
- let p = ucl_parser_new 0x4
- didParse <- ucl_parser_add_string p cs (toEnum $ length s)
- if didParse
- then return $ Left $ ucl_parser_get_object p
- else Right <$> peekCString ( ucl_parser_get_error p)
-
-ucl_parser_add_file_pure :: String -> Either UCLObjectHandle ErrorString
-ucl_parser_add_file_pure s = unsafePerformIO $ do
- cs <- newCString s
- let p = ucl_parser_new 0x4
- didParse <- ucl_parser_add_file p cs
- if didParse
- then return $ Left $ ucl_parser_get_object p
- else Right <$> peekCString ( ucl_parser_get_error p)
-
-unpack :: MSG.MessagePack a => String -> Either a ErrorString
-unpack s = case ucl_parser_parse_string_pure s of
- (Right err) -> Right err
- (Left obj) -> case MSG.fromObject (ucl_to_msgpack_object obj) of
- Nothing -> Right "MessagePack fromObject Error"
- (Just a) -> Left a
-
-ucl_to_msgpack_object :: UCLObjectHandle -> MSG.Object
-ucl_to_msgpack_object o = toMsgPackObj (ucl_object_type o) o
- where
- toMsgPackObj n obj
- |n==ucl_OBJECT = MSG.ObjectMap $ uclObjectToVector obj
- |n==ucl_ARRAY = MSG.ObjectArray undefined
- |n==ucl_INT = MSG.ObjectInt $ fromEnum $ ucl_object_toint obj
- |n==ucl_FLOAT = MSG.ObjectDouble $ realToFrac $ ucl_object_todouble obj
- |n==ucl_STRING = MSG.ObjectStr $ T.pack $ unsafePerformIO $ peekCString $ ucl_object_tostring obj
- |n==ucl_BOOLEAN = MSG.ObjectBool $ ucl_object_toboolean obj
- |n==ucl_TIME = error "time undefined"
- |n==ucl_USERDATA = error "userdata undefined"
- |n==ucl_NULL = error "null undefined"
- |otherwise = error "\"Unknown Type\" Error"
-
-uclObjectToVector :: UCLObjectHandle -> V.Vector (MSG.Object,MSG.Object)
-uclObjectToVector o = iterateObject (ucl_object_iterate_safe iter True ) iter V.empty
- where
- iter = ucl_object_iterate_new o
- iterateObject obj it vec = if ucl_object_type obj == ucl_NULL
- then vec
- else iterateObject (ucl_object_iterate_safe it True) it (V.snoc vec ( getUclKey obj , ucl_to_msgpack_object obj))
- getUclKey obj = MSG.ObjectStr $ T.pack $ unsafePerformIO $ peekCString $ ucl_object_key obj
-
-uclArrayToVector :: UCLObjectHandle -> V.Vector MSG.Object
-uclArrayToVector o = iterateArray (ucl_object_iterate_safe iter True ) iter V.empty
- where
- iter = ucl_object_iterate_new o
- iterateArray obj it vec = if ucl_object_type obj == ucl_NULL
- then vec
- else iterateArray (ucl_object_iterate_safe it True) it (V.snoc vec (ucl_to_msgpack_object obj))
-
diff --git a/contrib/libucl/include/ucl.h b/contrib/libucl/include/ucl.h
index 39da2593648d..b8625b9fce2f 100644
--- a/contrib/libucl/include/ucl.h
+++ b/contrib/libucl/include/ucl.h
@@ -1411,13 +1411,13 @@ struct ucl_emitter_operations {
const ucl_object_t *obj, bool first, bool print_key);
/** Start ucl object */
void (*ucl_emitter_start_object) (struct ucl_emitter_context *ctx,
- const ucl_object_t *obj, bool print_key);
+ const ucl_object_t *obj, bool first, bool print_key);
/** End ucl object */
void (*ucl_emitter_end_object) (struct ucl_emitter_context *ctx,
const ucl_object_t *obj);
/** Start ucl array */
void (*ucl_emitter_start_array) (struct ucl_emitter_context *ctx,
- const ucl_object_t *obj, bool print_key);
+ const ucl_object_t *obj, bool first, bool print_key);
void (*ucl_emitter_end_array) (struct ucl_emitter_context *ctx,
const ucl_object_t *obj);
};
diff --git a/contrib/libucl/libucl.pc b/contrib/libucl/libucl.pc
deleted file mode 100644
index 4878bebafcdd..000000000000
--- a/contrib/libucl/libucl.pc
+++ /dev/null
@@ -1,11 +0,0 @@
-prefix=/usr/local
-exec_prefix=${prefix}
-libdir=${exec_prefix}/lib
-includedir=${prefix}/include
-
-Name: LibUCL
-Description: Universal configuration library
-Version: 0.9.0
-Libs: -L${libdir} -lucl
-Libs.private:
-Cflags: -I${includedir}/
diff --git a/contrib/libucl/libucl.pc.in b/contrib/libucl/libucl.pc.in
deleted file mode 100644
index 3433fa9d8b1c..000000000000
--- a/contrib/libucl/libucl.pc.in
+++ /dev/null
@@ -1,11 +0,0 @@
-prefix=@prefix@
-exec_prefix=@exec_prefix@
-libdir=@libdir@
-includedir=@includedir@
-
-Name: LibUCL
-Description: Universal configuration library
-Version: @UCL_VERSION@
-Libs: -L${libdir} -lucl
-Libs.private: @LIBS_EXTRA@ @LUA_LIB@
-Cflags: -I${includedir}/
diff --git a/contrib/libucl/lua/Makefile.am b/contrib/libucl/lua/Makefile.am
deleted file mode 100644
index 95beafbfc94e..000000000000
--- a/contrib/libucl/lua/Makefile.am
+++ /dev/null
@@ -1,26 +0,0 @@
-ucl_common_cflags= -I$(top_srcdir)/src \
- -I$(top_srcdir)/include \
- -I$(top_srcdir)/uthash \
- -Wall -W -Wno-unused-parameter -Wno-pointer-sign
-luaexec_LTLIBRARIES= ucl.la
-ucl_la_SOURCES= lua_ucl.c
-ucl_la_CFLAGS= $(ucl_common_cflags) \
- @LUA_INCLUDE@
-ucl_la_LDFLAGS = -module -export-dynamic -avoid-version
-ucl_la_LIBADD= $(top_srcdir)/src/libucl.la \
- @LIBFETCH_LIBS@ \
- @LIBCRYPTO_LIB@ \
- @LIBREGEX_LIB@ \
- @CURL_LIBS@ \
- @LUA_LIB@
-
-include_HEADERS= $(top_srcdir)/include/lua_ucl.h
-
-ROCKSPEC = $(PACKAGE)-$(VERSION)-1.rockspec
-EXTRA_DIST = $(PACKAGE).rockspec.in \
- test.lua
-DISTCLEANFILES = $(PACKAGE).rockspec
-
-$(ROCKSPEC): $(PACKAGE).rockspec dist
- sed -e 's/@MD5@/'`$(MD5SUM) $(distdir).tar.gz | \
- cut -d " " -f 1`'/g' < $(PACKAGE).rockspec > $@ \ No newline at end of file
diff --git a/contrib/libucl/lua/libucl.rockspec.in b/contrib/libucl/lua/libucl.rockspec.in
deleted file mode 100644
index 52f39176a7bd..000000000000
--- a/contrib/libucl/lua/libucl.rockspec.in
+++ /dev/null
@@ -1,26 +0,0 @@
-package="@PACKAGE@"
-version="@VERSION@-1"
-source = {
- url = "https://github.com/downloads/vstakhov/@PACKAGE@/@PACKAGE@-@VERSION@.tar.gz",
- md5 = "@MD5@",
- dir = "@PACKAGE@-@VERSION@"
-}
-description = {
- summary = "UCL - json like configuration language",
- detailed = [[
- UCL is heavily infused by nginx configuration as the example
- of a convenient configuration system.
- However, UCL is fully compatible with JSON format and is able
- to parse json files.
- ]],
- homepage = "http://github.com/vstakhov/@PACKAGE@/",
- license = ""
-}
-dependencies = {
- "lua >= 5.1"
-}
-build = {
- type = "command",
- build_command = "LUA=$(LUA) CPPFLAGS=-I$(LUA_INCDIR) ./configure --prefix=$(PREFIX) --libdir=$(LIBDIR) --datadir=$(LUADIR) && make clean && make",
- install_command = "make install"
-}
diff --git a/contrib/libucl/lua/lua_ucl.c b/contrib/libucl/lua/lua_ucl.c
index b34fd56878b8..1b3f9dfd111c 100644
--- a/contrib/libucl/lua/lua_ucl.c
+++ b/contrib/libucl/lua/lua_ucl.c
@@ -30,6 +30,8 @@
#include "lua_ucl.h"
#include <strings.h>
+#include "bootstrap.h"
+
/***
* @module ucl
* This lua module allows to parse objects from strings and to store data into
@@ -82,6 +84,11 @@ static ucl_object_t* ucl_object_lua_fromelt (lua_State *L, int idx, ucl_string_f
static void *ucl_null;
+struct _rspamd_lua_text {
+ const char *start;
+ unsigned int len;
+ unsigned int flags;
+};
enum lua_ucl_push_flags {
LUA_UCL_DEFAULT_FLAGS = 0,
@@ -240,7 +247,7 @@ ucl_object_lua_push_scalar (lua_State *L, const ucl_object_t *obj,
lua_pushboolean (L, ucl_obj_toboolean (obj));
break;
case UCL_STRING:
- lua_pushstring (L, ucl_obj_tostring (obj));
+ lua_pushlstring (L, ucl_obj_tostring (obj), obj->len);
break;
case UCL_INT:
#if LUA_VERSION_NUM >= 501
@@ -401,7 +408,6 @@ ucl_object_lua_fromtable (lua_State *L, int idx, ucl_string_flags_t flags)
/* Table iterate */
if (is_array) {
- int i;
if (!is_implicit) {
top = ucl_object_typed_new (UCL_ARRAY);
@@ -411,7 +417,7 @@ ucl_object_lua_fromtable (lua_State *L, int idx, ucl_string_flags_t flags)
top = NULL;
}
- for (i = 1; i <= max; i ++) {
+ for (size_t i = 1; i <= max; i ++) {
lua_pushinteger (L, i);
lua_gettable (L, idx);
@@ -479,7 +485,16 @@ ucl_object_lua_fromelt (lua_State *L, int idx, ucl_string_flags_t flags)
str = lua_tolstring (L, idx, &sz);
if (str) {
- obj = ucl_object_fromstring_common (str, sz, flags);
+ /*
+ * ucl_object_fromstring_common has a `logic` to use strlen if sz is zero
+ * which is totally broken...
+ */
+ if (sz > 0) {
+ obj = ucl_object_fromstring_common(str, sz, flags);
+ }
+ else {
+ obj = ucl_object_fromstring_common("", sz, flags);
+ }
}
else {
obj = ucl_object_typed_new (UCL_NULL);
@@ -501,6 +516,24 @@ ucl_object_lua_fromelt (lua_State *L, int idx, ucl_string_flags_t flags)
if (lua_topointer (L, idx) == ucl_null) {
obj = ucl_object_typed_new (UCL_NULL);
}
+ else {
+ /* Assume it is a text like object */
+ struct _rspamd_lua_text *t = lua_touserdata (L, idx);
+
+ if (t) {
+ if (t->len >0) {
+ obj = ucl_object_fromstring_common(t->start, t->len, 0);
+ }
+ else {
+ obj = ucl_object_fromstring_common("", 0, 0);
+ }
+
+ /* Binary text */
+ if (t->flags & (1u << 5u)) {
+ obj->flags |= UCL_OBJECT_BINARY;
+ }
+ }
+ }
break;
case LUA_TTABLE:
case LUA_TFUNCTION:
@@ -556,10 +589,10 @@ ucl_object_lua_import (lua_State *L, int idx)
t = lua_type (L, idx);
switch (t) {
case LUA_TTABLE:
- obj = ucl_object_lua_fromtable (L, idx, 0);
+ obj = ucl_object_lua_fromtable (L, idx, UCL_STRING_RAW);
break;
default:
- obj = ucl_object_lua_fromelt (L, idx, 0);
+ obj = ucl_object_lua_fromelt (L, idx, UCL_STRING_RAW);
break;
}
@@ -584,10 +617,10 @@ ucl_object_lua_import_escape (lua_State *L, int idx)
t = lua_type (L, idx);
switch (t) {
case LUA_TTABLE:
- obj = ucl_object_lua_fromtable (L, idx, UCL_STRING_RAW);
+ obj = ucl_object_lua_fromtable (L, idx, UCL_STRING_ESCAPE);
break;
default:
- obj = ucl_object_lua_fromelt (L, idx, UCL_STRING_RAW);
+ obj = ucl_object_lua_fromelt (L, idx, UCL_STRING_ESCAPE);
break;
}
@@ -598,11 +631,12 @@ static int
lua_ucl_to_string (lua_State *L, const ucl_object_t *obj, enum ucl_emitter type)
{
unsigned char *result;
+ size_t outlen;
- result = ucl_object_emit (obj, type);
+ result = ucl_object_emit_len (obj, type, &outlen);
if (result != NULL) {
- lua_pushstring (L, (const char *)result);
+ lua_pushlstring (L, (const char *)result, outlen);
free (result);
}
else {
@@ -625,7 +659,6 @@ lua_ucl_parser_init (lua_State *L)
parser = ucl_parser_new (flags);
if (parser == NULL) {
lua_pushnil (L);
- return 1;
}
pparser = lua_newuserdata (L, sizeof (parser));
@@ -834,12 +867,6 @@ lua_ucl_parser_parse_string (lua_State *L)
return ret;
}
-struct _rspamd_lua_text {
- const char *start;
- unsigned int len;
- unsigned int flags;
-};
-
/***
* @method parser:parse_text(input)
* Parse UCL object from text object (Rspamd specific).
@@ -855,7 +882,24 @@ lua_ucl_parser_parse_text (lua_State *L)
int ret = 2;
parser = lua_ucl_parser_get (L, 1);
- t = lua_touserdata (L, 2);
+
+ if (lua_type (L, 2) == LUA_TUSERDATA) {
+ t = lua_touserdata (L, 2);
+ }
+ else if (lua_type (L, 2) == LUA_TSTRING) {
+ const char *s;
+ size_t len;
+ static struct _rspamd_lua_text st_t;
+
+ s = lua_tolstring (L, 2, &len);
+ st_t.start = s;
+ st_t.len = len;
+
+ t = &st_t;
+ }
+ else {
+ return luaL_error(L, "invalid argument as input, expected userdata or a string");
+ }
if (lua_type (L, 3) == LUA_TSTRING) {
type = lua_ucl_str_to_parse_type (lua_tostring (L, 3));
@@ -1426,10 +1470,11 @@ lua_ucl_to_format (lua_State *L)
format = UCL_EMIT_YAML;
}
else if (strcasecmp (strtype, "config") == 0 ||
- strcasecmp (strtype, "ucl") == 0) {
+ strcasecmp (strtype, "ucl") == 0) {
format = UCL_EMIT_CONFIG;
}
- else if (strcasecmp (strtype, "msgpack") == 0) {
+ else if (strcasecmp (strtype, "msgpack") == 0 ||
+ strcasecmp (strtype, "messagepack") == 0) {
format = UCL_EMIT_MSGPACK;
}
}
@@ -1528,3 +1573,5 @@ ucl_object_toclosure (const ucl_object_t *obj)
return (struct ucl_lua_funcdata*)obj->value.ud;
}
+
+FLUA_MODULE(ucl);
diff --git a/contrib/libucl/m4/ax_lua.m4 b/contrib/libucl/m4/ax_lua.m4
deleted file mode 100644
index f8e2fd4c85ce..000000000000
--- a/contrib/libucl/m4/ax_lua.m4
+++ /dev/null
@@ -1,664 +0,0 @@
-# ===========================================================================
-# http://www.gnu.org/software/autoconf-archive/ax_lua.html
-# ===========================================================================
-#
-# SYNOPSIS
-#
-# AX_PROG_LUA[([MINIMUM-VERSION], [TOO-BIG-VERSION], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])]
-# AX_LUA_HEADERS[([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])]
-# AX_LUA_LIBS[([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])]
-# AX_LUA_READLINE[([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])]
-#
-# DESCRIPTION
-#
-# Detect a Lua interpreter, optionally specifying a minimum and maximum
-# version number. Set up important Lua paths, such as the directories in
-# which to install scripts and modules (shared libraries).
-#
-# Also detect Lua headers and libraries. The Lua version contained in the
-# header is checked to match the Lua interpreter version exactly. When
-# searching for Lua libraries, the version number is used as a suffix.
-# This is done with the goal of supporting multiple Lua installs (5.1,
-# 5.2, and 5.3 side-by-side).
-#
-# A note on compatibility with previous versions: This file has been
-# mostly rewritten for serial 18. Most developers should be able to use
-# these macros without needing to modify configure.ac. Care has been taken
-# to preserve each macro's behavior, but there are some differences:
-#
-# 1) AX_WITH_LUA is deprecated; it now expands to the exact same thing as
-# AX_PROG_LUA with no arguments.
-#
-# 2) AX_LUA_HEADERS now checks that the version number defined in lua.h
-# matches the interpreter version. AX_LUA_HEADERS_VERSION is therefore
-# unnecessary, so it is deprecated and does not expand to anything.
-#
-# 3) The configure flag --with-lua-suffix no longer exists; the user
-# should instead specify the LUA precious variable on the command line.
-# See the AX_PROG_LUA description for details.
-#
-# Please read the macro descriptions below for more information.
-#
-# This file was inspired by Andrew Dalke's and James Henstridge's
-# python.m4 and Tom Payne's, Matthieu Moy's, and Reuben Thomas's ax_lua.m4
-# (serial 17). Basically, this file is a mash-up of those two files. I
-# like to think it combines the best of the two!
-#
-# AX_PROG_LUA: Search for the Lua interpreter, and set up important Lua
-# paths. Adds precious variable LUA, which may contain the path of the Lua
-# interpreter. If LUA is blank, the user's path is searched for an
-# suitable interpreter.
-#
-# If MINIMUM-VERSION is supplied, then only Lua interpreters with a
-# version number greater or equal to MINIMUM-VERSION will be accepted. If
-# TOO-BIG-VERSION is also supplied, then only Lua interpreters with a
-# version number greater or equal to MINIMUM-VERSION and less than
-# TOO-BIG-VERSION will be accepted.
-#
-# The Lua version number, LUA_VERSION, is found from the interpreter, and
-# substituted. LUA_PLATFORM is also found, but not currently supported (no
-# standard representation).
-#
-# Finally, the macro finds four paths:
-#
-# luadir Directory to install Lua scripts.
-# pkgluadir $luadir/$PACKAGE
-# luaexecdir Directory to install Lua modules.
-# pkgluaexecdir $luaexecdir/$PACKAGE
-#
-# These paths are found based on $prefix, $exec_prefix, Lua's
-# package.path, and package.cpath. The first path of package.path
-# beginning with $prefix is selected as luadir. The first path of
-# package.cpath beginning with $exec_prefix is used as luaexecdir. This
-# should work on all reasonable Lua installations. If a path cannot be
-# determined, a default path is used. Of course, the user can override
-# these later when invoking make.
-#
-# luadir Default: $prefix/share/lua/$LUA_VERSION
-# luaexecdir Default: $exec_prefix/lib/lua/$LUA_VERSION
-#
-# These directories can be used by Automake as install destinations. The
-# variable name minus 'dir' needs to be used as a prefix to the
-# appropriate Automake primary, e.g. lua_SCRIPS or luaexec_LIBRARIES.
-#
-# If an acceptable Lua interpreter is found, then ACTION-IF-FOUND is
-# performed, otherwise ACTION-IF-NOT-FOUND is preformed. If ACTION-IF-NOT-
-# FOUND is blank, then it will default to printing an error. To prevent
-# the default behavior, give ':' as an action.
-#
-# AX_LUA_HEADERS: Search for Lua headers. Requires that AX_PROG_LUA be
-# expanded before this macro. Adds precious variable LUA_INCLUDE, which
-# may contain Lua specific include flags, e.g. -I/usr/include/lua5.1. If
-# LUA_INCLUDE is blank, then this macro will attempt to find suitable
-# flags.
-#
-# LUA_INCLUDE can be used by Automake to compile Lua modules or
-# executables with embedded interpreters. The *_CPPFLAGS variables should
-# be used for this purpose, e.g. myprog_CPPFLAGS = $(LUA_INCLUDE).
-#
-# This macro searches for the header lua.h (and others). The search is
-# performed with a combination of CPPFLAGS, CPATH, etc, and LUA_INCLUDE.
-# If the search is unsuccessful, then some common directories are tried.
-# If the headers are then found, then LUA_INCLUDE is set accordingly.
-#
-# The paths automatically searched are:
-#
-# * /usr/include/luaX.Y
-# * /usr/include/lua/X.Y
-# * /usr/include/luaXY
-# * /usr/local/include/luaX.Y
-# * /usr/local/include/lua-X.Y
-# * /usr/local/include/lua/X.Y
-# * /usr/local/include/luaXY
-#
-# (Where X.Y is the Lua version number, e.g. 5.1.)
-#
-# The Lua version number found in the headers is always checked to match
-# the Lua interpreter's version number. Lua headers with mismatched
-# version numbers are not accepted.
-#
-# If headers are found, then ACTION-IF-FOUND is performed, otherwise
-# ACTION-IF-NOT-FOUND is performed. If ACTION-IF-NOT-FOUND is blank, then
-# it will default to printing an error. To prevent the default behavior,
-# set the action to ':'.
-#
-# AX_LUA_LIBS: Search for Lua libraries. Requires that AX_PROG_LUA be
-# expanded before this macro. Adds precious variable LUA_LIB, which may
-# contain Lua specific linker flags, e.g. -llua5.1. If LUA_LIB is blank,
-# then this macro will attempt to find suitable flags.
-#
-# LUA_LIB can be used by Automake to link Lua modules or executables with
-# embedded interpreters. The *_LIBADD and *_LDADD variables should be used
-# for this purpose, e.g. mymod_LIBADD = $(LUA_LIB).
-#
-# This macro searches for the Lua library. More technically, it searches
-# for a library containing the function lua_load. The search is performed
-# with a combination of LIBS, LIBRARY_PATH, and LUA_LIB.
-#
-# If the search determines that some linker flags are missing, then those
-# flags will be added to LUA_LIB.
-#
-# If libraries are found, then ACTION-IF-FOUND is performed, otherwise
-# ACTION-IF-NOT-FOUND is performed. If ACTION-IF-NOT-FOUND is blank, then
-# it will default to printing an error. To prevent the default behavior,
-# set the action to ':'.
-#
-# AX_LUA_READLINE: Search for readline headers and libraries. Requires the
-# AX_LIB_READLINE macro, which is provided by ax_lib_readline.m4 from the
-# Autoconf Archive.
-#
-# If a readline compatible library is found, then ACTION-IF-FOUND is
-# performed, otherwise ACTION-IF-NOT-FOUND is performed.
-#
-# LICENSE
-#
-# Copyright (c) 2015 Reuben Thomas <rrt@sc3d.org>
-# Copyright (c) 2014 Tim Perkins <tprk77@gmail.com>
-#
-# This program is free software: you can redistribute it and/or modify it
-# under the terms of the GNU General Public License as published by the
-# Free Software Foundation, either version 3 of the License, or (at your
-# option) any later version.
-#
-# This program is distributed in the hope that it will be useful, but
-# WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
-# Public License for more details.
-#
-# You should have received a copy of the GNU General Public License along
-# with this program. If not, see <http://www.gnu.org/licenses/>.
-#
-# As a special exception, the respective Autoconf Macro's copyright owner
-# gives unlimited permission to copy, distribute and modify the configure
-# scripts that are the output of Autoconf when processing the Macro. You
-# need not follow the terms of the GNU General Public License when using
-# or distributing such scripts, even though portions of the text of the
-# Macro appear in them. The GNU General Public License (GPL) does govern
-# all other use of the material that constitutes the Autoconf Macro.
-#
-# This special exception to the GPL applies to versions of the Autoconf
-# Macro released by the Autoconf Archive. When you make and distribute a
-# modified version of the Autoconf Macro, you may extend this special
-# exception to the GPL to apply to your modified version as well.
-
-#serial 39
-
-dnl =========================================================================
-dnl AX_PROG_LUA([MINIMUM-VERSION], [TOO-BIG-VERSION],
-dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
-dnl =========================================================================
-AC_DEFUN([AX_PROG_LUA],
-[
- dnl Check for required tools.
- AC_REQUIRE([AC_PROG_GREP])
- AC_REQUIRE([AC_PROG_SED])
-
- dnl Make LUA a precious variable.
- AC_ARG_VAR([LUA], [The Lua interpreter, e.g. /usr/bin/lua5.1])
-
- dnl Find a Lua interpreter.
- m4_define_default([_AX_LUA_INTERPRETER_LIST],
- [lua lua5.3 lua53 lua5.2 lua52 lua5.1 lua51 lua50])
-
- m4_if([$1], [],
- [ dnl No version check is needed. Find any Lua interpreter.
- AS_IF([test "x$LUA" = 'x'],
- [AC_PATH_PROGS([LUA], [_AX_LUA_INTERPRETER_LIST], [:])])
- ax_display_LUA='lua'
-
- AS_IF([test "x$LUA" != 'x:'],
- [ dnl At least check if this is a Lua interpreter.
- AC_MSG_CHECKING([if $LUA is a Lua interpreter])
- _AX_LUA_CHK_IS_INTRP([$LUA],
- [AC_MSG_RESULT([yes])],
- [ AC_MSG_RESULT([no])
- AC_MSG_ERROR([not a Lua interpreter])
- ])
- ])
- ],
- [ dnl A version check is needed.
- AS_IF([test "x$LUA" != 'x'],
- [ dnl Check if this is a Lua interpreter.
- AC_MSG_CHECKING([if $LUA is a Lua interpreter])
- _AX_LUA_CHK_IS_INTRP([$LUA],
- [AC_MSG_RESULT([yes])],
- [ AC_MSG_RESULT([no])
- AC_MSG_ERROR([not a Lua interpreter])
- ])
- dnl Check the version.
- m4_if([$2], [],
- [_ax_check_text="whether $LUA version >= $1"],
- [_ax_check_text="whether $LUA version >= $1, < $2"])
- AC_MSG_CHECKING([$_ax_check_text])
- _AX_LUA_CHK_VER([$LUA], [$1], [$2],
- [AC_MSG_RESULT([yes])],
- [ AC_MSG_RESULT([no])
- AC_MSG_ERROR([version is out of range for specified LUA])])
- ax_display_LUA=$LUA
- ],
- [ dnl Try each interpreter until we find one that satisfies VERSION.
- m4_if([$2], [],
- [_ax_check_text="for a Lua interpreter with version >= $1"],
- [_ax_check_text="for a Lua interpreter with version >= $1, < $2"])
- AC_CACHE_CHECK([$_ax_check_text],
- [ax_cv_pathless_LUA],
- [ for ax_cv_pathless_LUA in _AX_LUA_INTERPRETER_LIST none; do
- test "x$ax_cv_pathless_LUA" = 'xnone' && break
- _AX_LUA_CHK_IS_INTRP([$ax_cv_pathless_LUA], [], [continue])
- _AX_LUA_CHK_VER([$ax_cv_pathless_LUA], [$1], [$2], [break])
- done
- ])
- dnl Set $LUA to the absolute path of $ax_cv_pathless_LUA.
- AS_IF([test "x$ax_cv_pathless_LUA" = 'xnone'],
- [LUA=':'],
- [AC_PATH_PROG([LUA], [$ax_cv_pathless_LUA])])
- ax_display_LUA=$ax_cv_pathless_LUA
- ])
- ])
-
- AS_IF([test "x$LUA" = 'x:'],
- [ dnl Run any user-specified action, or abort.
- m4_default([$4], [AC_MSG_ERROR([cannot find suitable Lua interpreter])])
- ],
- [ dnl Query Lua for its version number.
- AC_CACHE_CHECK([for $ax_display_LUA version],
- [ax_cv_lua_version],
- [ dnl Get the interpreter version in X.Y format. This should work for
- dnl interpreters version 5.0 and beyond.
- ax_cv_lua_version=[`$LUA -e '
- -- return a version number in X.Y format
- local _, _, ver = string.find(_VERSION, "^Lua (%d+%.%d+)")
- print(ver)'`]
- ])
- AS_IF([test "x$ax_cv_lua_version" = 'x'],
- [AC_MSG_ERROR([invalid Lua version number])])
- AC_SUBST([LUA_VERSION], [$ax_cv_lua_version])
- AC_SUBST([LUA_SHORT_VERSION], [`echo "$LUA_VERSION" | $SED 's|\.||'`])
-
- dnl The following check is not supported:
- dnl At times (like when building shared libraries) you may want to know
- dnl which OS platform Lua thinks this is.
- AC_CACHE_CHECK([for $ax_display_LUA platform],
- [ax_cv_lua_platform],
- [ax_cv_lua_platform=[`$LUA -e 'print("unknown")'`]])
- AC_SUBST([LUA_PLATFORM], [$ax_cv_lua_platform])
-
- dnl Use the values of $prefix and $exec_prefix for the corresponding
- dnl values of LUA_PREFIX and LUA_EXEC_PREFIX. These are made distinct
- dnl variables so they can be overridden if need be. However, the general
- dnl consensus is that you shouldn't need this ability.
- AC_SUBST([LUA_PREFIX], ['${prefix}'])
- AC_SUBST([LUA_EXEC_PREFIX], ['${exec_prefix}'])
-
- dnl Lua provides no way to query the script directory, and instead
- dnl provides LUA_PATH. However, we should be able to make a safe educated
- dnl guess. If the built-in search path contains a directory which is
- dnl prefixed by $prefix, then we can store scripts there. The first
- dnl matching path will be used.
- AC_CACHE_CHECK([for $ax_display_LUA script directory],
- [ax_cv_lua_luadir],
- [ AS_IF([test "x$prefix" = 'xNONE'],
- [ax_lua_prefix=$ac_default_prefix],
- [ax_lua_prefix=$prefix])
-
- dnl Initialize to the default path.
- ax_cv_lua_luadir="$LUA_PREFIX/share/lua/$LUA_VERSION"
-
- dnl Try to find a path with the prefix.
- _AX_LUA_FND_PRFX_PTH([$LUA], [$ax_lua_prefix], [script])
- AS_IF([test "x$ax_lua_prefixed_path" != 'x'],
- [ dnl Fix the prefix.
- _ax_strip_prefix=`echo "$ax_lua_prefix" | $SED 's|.|.|g'`
- ax_cv_lua_luadir=`echo "$ax_lua_prefixed_path" | \
- $SED "s|^$_ax_strip_prefix|$LUA_PREFIX|"`
- ])
- ])
- AC_SUBST([luadir], [$ax_cv_lua_luadir])
- AC_SUBST([pkgluadir], [\${luadir}/$PACKAGE])
-
- dnl Lua provides no way to query the module directory, and instead
- dnl provides LUA_PATH. However, we should be able to make a safe educated
- dnl guess. If the built-in search path contains a directory which is
- dnl prefixed by $exec_prefix, then we can store modules there. The first
- dnl matching path will be used.
- AC_CACHE_CHECK([for $ax_display_LUA module directory],
- [ax_cv_lua_luaexecdir],
- [ AS_IF([test "x$exec_prefix" = 'xNONE'],
- [ax_lua_exec_prefix=$ax_lua_prefix],
- [ax_lua_exec_prefix=$exec_prefix])
-
- dnl Initialize to the default path.
- ax_cv_lua_luaexecdir="$LUA_EXEC_PREFIX/lib/lua/$LUA_VERSION"
-
- dnl Try to find a path with the prefix.
- _AX_LUA_FND_PRFX_PTH([$LUA],
- [$ax_lua_exec_prefix], [module])
- AS_IF([test "x$ax_lua_prefixed_path" != 'x'],
- [ dnl Fix the prefix.
- _ax_strip_prefix=`echo "$ax_lua_exec_prefix" | $SED 's|.|.|g'`
- ax_cv_lua_luaexecdir=`echo "$ax_lua_prefixed_path" | \
- $SED "s|^$_ax_strip_prefix|$LUA_EXEC_PREFIX|"`
- ])
- ])
- AC_SUBST([luaexecdir], [$ax_cv_lua_luaexecdir])
- AC_SUBST([pkgluaexecdir], [\${luaexecdir}/$PACKAGE])
-
- dnl Run any user specified action.
- $3
- ])
-])
-
-dnl AX_WITH_LUA is now the same thing as AX_PROG_LUA.
-AC_DEFUN([AX_WITH_LUA],
-[
- AC_MSG_WARN([[$0 is deprecated, please use AX_PROG_LUA instead]])
- AX_PROG_LUA
-])
-
-
-dnl =========================================================================
-dnl _AX_LUA_CHK_IS_INTRP(PROG, [ACTION-IF-TRUE], [ACTION-IF-FALSE])
-dnl =========================================================================
-AC_DEFUN([_AX_LUA_CHK_IS_INTRP],
-[
- dnl A minimal Lua factorial to prove this is an interpreter. This should work
- dnl for Lua interpreters version 5.0 and beyond.
- _ax_lua_factorial=[`$1 2>/dev/null -e '
- -- a simple factorial
- function fact (n)
- if n == 0 then
- return 1
- else
- return n * fact(n-1)
- end
- end
- print("fact(5) is " .. fact(5))'`]
- AS_IF([test "$_ax_lua_factorial" = 'fact(5) is 120'],
- [$2], [$3])
-])
-
-
-dnl =========================================================================
-dnl _AX_LUA_CHK_VER(PROG, MINIMUM-VERSION, [TOO-BIG-VERSION],
-dnl [ACTION-IF-TRUE], [ACTION-IF-FALSE])
-dnl =========================================================================
-AC_DEFUN([_AX_LUA_CHK_VER],
-[
- dnl Check that the Lua version is within the bounds. Only the major and minor
- dnl version numbers are considered. This should work for Lua interpreters
- dnl version 5.0 and beyond.
- _ax_lua_good_version=[`$1 -e '
- -- a script to compare versions
- function verstr2num(verstr)
- local _, _, majorver, minorver = string.find(verstr, "^(%d+)%.(%d+)")
- if majorver and minorver then
- return tonumber(majorver) * 100 + tonumber(minorver)
- end
- end
- local minver = verstr2num("$2")
- local _, _, trimver = string.find(_VERSION, "^Lua (.*)")
- local ver = verstr2num(trimver)
- local maxver = verstr2num("$3") or 1e9
- if minver <= ver and ver < maxver then
- print("yes")
- else
- print("no")
- end'`]
- AS_IF([test "x$_ax_lua_good_version" = "xyes"],
- [$4], [$5])
-])
-
-
-dnl =========================================================================
-dnl _AX_LUA_FND_PRFX_PTH(PROG, PREFIX, SCRIPT-OR-MODULE-DIR)
-dnl =========================================================================
-AC_DEFUN([_AX_LUA_FND_PRFX_PTH],
-[
- dnl Get the script or module directory by querying the Lua interpreter,
- dnl filtering on the given prefix, and selecting the shallowest path. If no
- dnl path is found matching the prefix, the result will be an empty string.
- dnl The third argument determines the type of search, it can be 'script' or
- dnl 'module'. Supplying 'script' will perform the search with package.path
- dnl and LUA_PATH, and supplying 'module' will search with package.cpath and
- dnl LUA_CPATH. This is done for compatibility with Lua 5.0.
-
- ax_lua_prefixed_path=[`$1 -e '
- -- get the path based on search type
- local searchtype = "$3"
- local paths = ""
- if searchtype == "script" then
- paths = (package and package.path) or LUA_PATH
- elseif searchtype == "module" then
- paths = (package and package.cpath) or LUA_CPATH
- end
- -- search for the prefix
- local prefix = "'$2'"
- local minpath = ""
- local mindepth = 1e9
- string.gsub(paths, "(@<:@^;@:>@+)",
- function (path)
- path = string.gsub(path, "%?.*$", "")
- path = string.gsub(path, "/@<:@^/@:>@*$", "")
- if string.find(path, prefix) then
- local depth = string.len(string.gsub(path, "@<:@^/@:>@", ""))
- if depth < mindepth then
- minpath = path
- mindepth = depth
- end
- end
- end)
- print(minpath)'`]
-])
-
-
-dnl =========================================================================
-dnl AX_LUA_HEADERS([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
-dnl =========================================================================
-AC_DEFUN([AX_LUA_HEADERS],
-[
- dnl Check for LUA_VERSION.
- AC_MSG_CHECKING([if LUA_VERSION is defined])
- AS_IF([test "x$LUA_VERSION" != 'x'],
- [AC_MSG_RESULT([yes])],
- [ AC_MSG_RESULT([no])
- AC_MSG_ERROR([cannot check Lua headers without knowing LUA_VERSION])
- ])
-
- dnl Make LUA_INCLUDE a precious variable.
- AC_ARG_VAR([LUA_INCLUDE], [The Lua includes, e.g. -I/usr/include/lua5.1])
-
- dnl Some default directories to search.
- LUA_SHORT_VERSION=`echo "$LUA_VERSION" | $SED 's|\.||'`
- m4_define_default([_AX_LUA_INCLUDE_LIST],
- [ /usr/include/lua$LUA_VERSION \
- /usr/include/lua-$LUA_VERSION \
- /usr/include/lua/$LUA_VERSION \
- /usr/include/lua$LUA_SHORT_VERSION \
- /usr/local/include/lua$LUA_VERSION \
- /usr/local/include/lua-$LUA_VERSION \
- /usr/local/include/lua/$LUA_VERSION \
- /usr/local/include/lua$LUA_SHORT_VERSION \
- ])
-
- dnl Try to find the headers.
- _ax_lua_saved_cppflags=$CPPFLAGS
- CPPFLAGS="$CPPFLAGS $LUA_INCLUDE"
- AC_CHECK_HEADERS([lua.h lualib.h lauxlib.h luaconf.h])
- CPPFLAGS=$_ax_lua_saved_cppflags
-
- dnl Try some other directories if LUA_INCLUDE was not set.
- AS_IF([test "x$LUA_INCLUDE" = 'x' &&
- test "x$ac_cv_header_lua_h" != 'xyes'],
- [ dnl Try some common include paths.
- for _ax_include_path in _AX_LUA_INCLUDE_LIST; do
- test ! -d "$_ax_include_path" && continue
-
- AC_MSG_CHECKING([for Lua headers in])
- AC_MSG_RESULT([$_ax_include_path])
-
- AS_UNSET([ac_cv_header_lua_h])
- AS_UNSET([ac_cv_header_lualib_h])
- AS_UNSET([ac_cv_header_lauxlib_h])
- AS_UNSET([ac_cv_header_luaconf_h])
-
- _ax_lua_saved_cppflags=$CPPFLAGS
- CPPFLAGS="$CPPFLAGS -I$_ax_include_path"
- AC_CHECK_HEADERS([lua.h lualib.h lauxlib.h luaconf.h])
- CPPFLAGS=$_ax_lua_saved_cppflags
-
- AS_IF([test "x$ac_cv_header_lua_h" = 'xyes'],
- [ LUA_INCLUDE="-I$_ax_include_path"
- break
- ])
- done
- ])
-
- AS_IF([test "x$ac_cv_header_lua_h" = 'xyes'],
- [ dnl Make a program to print LUA_VERSION defined in the header.
- dnl TODO It would be really nice if we could do this without compiling a
- dnl program, then it would work when cross compiling. But I'm not sure how
- dnl to do this reliably. For now, assume versions match when cross compiling.
-
- AS_IF([test "x$cross_compiling" != 'xyes'],
- [ AC_CACHE_CHECK([for Lua header version],
- [ax_cv_lua_header_version],
- [ _ax_lua_saved_cppflags=$CPPFLAGS
- CPPFLAGS="$CPPFLAGS $LUA_INCLUDE"
- AC_RUN_IFELSE(
- [ AC_LANG_SOURCE([[
-#include <lua.h>
-#include <stdlib.h>
-#include <stdio.h>
-int main(int argc, char ** argv)
-{
- if(argc > 1) printf("%s", LUA_VERSION);
- exit(EXIT_SUCCESS);
-}
-]])
- ],
- [ ax_cv_lua_header_version=`./conftest$EXEEXT p | \
- $SED -n "s|^Lua \(@<:@0-9@:>@\{1,\}\.@<:@0-9@:>@\{1,\}\).\{0,\}|\1|p"`
- ],
- [ax_cv_lua_header_version='unknown'])
- CPPFLAGS=$_ax_lua_saved_cppflags
- ])
-
- dnl Compare this to the previously found LUA_VERSION.
- AC_MSG_CHECKING([if Lua header version matches $LUA_VERSION])
- AS_IF([test "x$ax_cv_lua_header_version" = "x$LUA_VERSION"],
- [ AC_MSG_RESULT([yes])
- ax_header_version_match='yes'
- ],
- [ AC_MSG_RESULT([no])
- ax_header_version_match='no'
- ])
- ],
- [ AC_MSG_WARN([cross compiling so assuming header version number matches])
- ax_header_version_match='yes'
- ])
- ])
-
- dnl Was LUA_INCLUDE specified?
- AS_IF([test "x$ax_header_version_match" != 'xyes' &&
- test "x$LUA_INCLUDE" != 'x'],
- [AC_MSG_ERROR([cannot find headers for specified LUA_INCLUDE])])
-
- dnl Test the final result and run user code.
- AS_IF([test "x$ax_header_version_match" = 'xyes'], [$1],
- [m4_default([$2], [AC_MSG_ERROR([cannot find Lua includes])])])
-])
-
-dnl AX_LUA_HEADERS_VERSION no longer exists, use AX_LUA_HEADERS.
-AC_DEFUN([AX_LUA_HEADERS_VERSION],
-[
- AC_MSG_WARN([[$0 is deprecated, please use AX_LUA_HEADERS instead]])
-])
-
-
-dnl =========================================================================
-dnl AX_LUA_LIBS([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
-dnl =========================================================================
-AC_DEFUN([AX_LUA_LIBS],
-[
- dnl TODO Should this macro also check various -L flags?
-
- dnl Check for LUA_VERSION.
- AC_MSG_CHECKING([if LUA_VERSION is defined])
- AS_IF([test "x$LUA_VERSION" != 'x'],
- [AC_MSG_RESULT([yes])],
- [ AC_MSG_RESULT([no])
- AC_MSG_ERROR([cannot check Lua libs without knowing LUA_VERSION])
- ])
-
- dnl Make LUA_LIB a precious variable.
- AC_ARG_VAR([LUA_LIB], [The Lua library, e.g. -llua5.1])
-
- AS_IF([test "x$LUA_LIB" != 'x'],
- [ dnl Check that LUA_LIBS works.
- _ax_lua_saved_libs=$LIBS
- LIBS="$LUA_LIB $LIBS"
- AC_SEARCH_LIBS([lua_load], [],
- [_ax_found_lua_libs='yes'],
- [_ax_found_lua_libs='no'])
- LIBS=$_ax_lua_saved_libs
-
- dnl Check the result.
- AS_IF([test "x$_ax_found_lua_libs" != 'xyes'],
- [AC_MSG_ERROR([cannot find libs for specified LUA_LIB])])
- ],
- [ dnl First search for extra libs.
- _ax_lua_extra_libs=''
-
- _ax_lua_saved_libs=$LIBS
- LIBS="$LUA_LIB $LIBS"
- AC_SEARCH_LIBS([exp], [m])
- AC_SEARCH_LIBS([dlopen], [dl])
- LIBS=$_ax_lua_saved_libs
-
- AS_IF([test "x$ac_cv_search_exp" != 'xno' &&
- test "x$ac_cv_search_exp" != 'xnone required'],
- [_ax_lua_extra_libs="$_ax_lua_extra_libs $ac_cv_search_exp"])
-
- AS_IF([test "x$ac_cv_search_dlopen" != 'xno' &&
- test "x$ac_cv_search_dlopen" != 'xnone required'],
- [_ax_lua_extra_libs="$_ax_lua_extra_libs $ac_cv_search_dlopen"])
-
- dnl Try to find the Lua libs.
- _ax_lua_saved_libs=$LIBS
- LIBS="$LUA_LIB $LIBS"
- AC_SEARCH_LIBS([lua_load],
- [ lua$LUA_VERSION \
- lua$LUA_SHORT_VERSION \
- lua-$LUA_VERSION \
- lua-$LUA_SHORT_VERSION \
- lua \
- ],
- [_ax_found_lua_libs='yes'],
- [_ax_found_lua_libs='no'],
- [$_ax_lua_extra_libs])
- LIBS=$_ax_lua_saved_libs
-
- AS_IF([test "x$ac_cv_search_lua_load" != 'xno' &&
- test "x$ac_cv_search_lua_load" != 'xnone required'],
- [LUA_LIB="$ac_cv_search_lua_load $_ax_lua_extra_libs"])
- ])
-
- dnl Test the result and run user code.
- AS_IF([test "x$_ax_found_lua_libs" = 'xyes'], [$1],
- [m4_default([$2], [AC_MSG_ERROR([cannot find Lua libs])])])
-])
-
-
-dnl =========================================================================
-dnl AX_LUA_READLINE([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
-dnl =========================================================================
-AC_DEFUN([AX_LUA_READLINE],
-[
- AX_LIB_READLINE
- AS_IF([test "x$ac_cv_header_readline_readline_h" != 'x' &&
- test "x$ac_cv_header_readline_history_h" != 'x'],
- [ LUA_LIBS_CFLAGS="-DLUA_USE_READLINE $LUA_LIBS_CFLAGS"
- $1
- ],
- [$2])
-])
diff --git a/contrib/libucl/m4/gcov.m4 b/contrib/libucl/m4/gcov.m4
deleted file mode 100644
index a1359a0de64b..000000000000
--- a/contrib/libucl/m4/gcov.m4
+++ /dev/null
@@ -1,89 +0,0 @@
-# SYNOPSIS
-#
-# Add code coverage support with gcov/lcov.
-#
-# AX_CODE_COVERAGE()
-#
-# DESCRIPTION
-#
-# Provides a --enable-coverage option which checks for available
-# gcov/lcov binaries and provides ENABLE_CODE_COVERAGE conditional.
-#
-# LAST MODIFICATION
-#
-# $Id: coverage.m4 40881 2013-08-20 17:54:39Z damon $
-#
-# COPYLEFT
-#
-# Copyright (c) 2012 Roy H. Stogner <roystgnr@ices.utexas.edu>
-# Copyright (c) 2010 Karl W. Schulz <karl@ices.utexas.edu>
-#
-# Copying and distribution of this file, with or without modification, are
-# permitted in any medium without royalty provided the copyright notice
-# and this notice are preserved.
-
-AC_DEFUN([AX_CODE_COVERAGE],
-[
-
-AC_ARG_ENABLE(coverage, AC_HELP_STRING([--enable-coverage],[configure code coverage analysis tools]))
-
-HAVE_GCOV_TOOLS=0
-
-GCOV_FLAGS=""
-
-if test "x$enable_coverage" = "xyes"; then
-
- # ----------------------------
- # Check for gcov/lcov binaries
- # ----------------------------
-
- AC_ARG_VAR([GCOV], [Coverage testing command])
- if test "x$GCOV" = "x"; then
- AC_PATH_PROG(GCOV, gcov, no)
- else
- AC_PATH_PROG(GCOV, $GCOV, no)
- fi
-
- AC_PATH_PROG(LCOV, lcov, no)
- AC_PATH_PROG(GENHTML, genhtml)
-
- # ----------------------------------
- # include coverage compiler options
- # ----------------------------------
- AC_MSG_CHECKING([for clang])
-
- AC_COMPILE_IFELSE(
- [AC_LANG_PROGRAM([], [[
- #ifndef __clang__
- not clang
- #endif
- ]])],
- [CLANG=yes], [CLANG=no])
-
- AC_MSG_RESULT([$CLANG])
- HAVE_GCOV_TOOLS=1
- COVERAGE_CFLAGS="-fprofile-arcs -ftest-coverage"
- COVERAGE_LDFLAGS="--coverage -fprofile-arcs -ftest-coverage"
- COVERAGE_OPTFLAGS="-O0"
-
- # Test for C...
- CFLAGS="${GCOV_FLAGS} ${CFLAGS}"
- CXXFLAGS="${GCOV_FLAGS} ${CXXFLAGS}"
- if test "x$GCC" = "xyes" -a "x$CLANG" = "xno"; then
- COVERAGE_LIBS="-lgcov"
- else
- COVERAGE_LIBS=""
- fi
-fi
-
-AC_SUBST([GCOV])
-AC_SUBST([LCOV])
-AC_SUBST([GENHTML])
-AC_SUBST([GENHTML_OPTIONS])
-AC_SUBST([COVERAGE_CFLAGS])
-AC_SUBST([COVERAGE_OPTFLAGS])
-AC_SUBST([COVERAGE_LDFLAGS])
-AC_SUBST([COVERAGE_LIBS])
-AM_CONDITIONAL(CODE_COVERAGE_ENABLED,test x$HAVE_GCOV_TOOLS = x1)
-
-])
diff --git a/contrib/libucl/python/MANIFEST.in b/contrib/libucl/python/MANIFEST.in
deleted file mode 100644
index 336a4b3a7a07..000000000000
--- a/contrib/libucl/python/MANIFEST.in
+++ /dev/null
@@ -1,5 +0,0 @@
-include COPYING
-recursive-include include *.h
-recursive-include src *.h
-recursive-include klib *.h
-recursive-include uthash *.h
diff --git a/contrib/libucl/python/setup.py b/contrib/libucl/python/setup.py
deleted file mode 100644
index 8da832bac381..000000000000
--- a/contrib/libucl/python/setup.py
+++ /dev/null
@@ -1,75 +0,0 @@
-try:
- from setuptools import setup, Extension
- # setuptools doesn't support template param for MANIFEST.in
- from setuptools.command.egg_info import manifest_maker
- manifest_maker.template = 'python/MANIFEST.in'
-except ImportError:
- from distutils.core import setup, Extension
-
-import os
-import sys
-
-LIB_ROOT = os.path.abspath(os.path.join(__file__, os.pardir, os.pardir))
-if os.getcwd() != LIB_ROOT:
- os.chdir(LIB_ROOT)
-if LIB_ROOT not in sys.path:
- sys.path.append(LIB_ROOT)
-
-tests_require = []
-
-if sys.version < '2.7':
- tests_require.append('unittest2')
-
-uclmodule = Extension(
- 'ucl',
- libraries=['ucl', 'curl'],
- sources=['python/src/uclmodule.c'],
- include_dirs=['include'],
- language='c',
-)
-
-ucl_lib = {
- 'sources': ['src/' + fn for fn in os.listdir('src') if fn.endswith('.c')],
- 'include_dirs': ['include', 'src', 'uthash', 'klib'],
- 'macros': [('CURL_FOUND', '1')],
-}
-
-# sdist setup() will pull in the *.c files automatically, but not headers
-# MANIFEST.in will include the headers for sdist only
-template = 'python/MANIFEST.in'
-
-# distutils assume setup.py is in the root of the project
-# we need to include C source from the parent so trick it
-in_ucl_root = 'setup.py' in os.listdir('python')
-if in_ucl_root:
- os.link('python/setup.py', 'setup.py')
-
-setup(
- name = 'ucl',
- version = '0.8.1',
- description = 'ucl parser and emitter',
- ext_modules = [uclmodule],
- template=template, # no longer supported with setuptools but doesn't hurt
- libraries = [('ucl', ucl_lib)],
- test_suite = 'tests',
- tests_require = tests_require,
- author = "Eitan Adler, Denis Volpato Martins",
- author_email = "lists@eitanadler.com",
- url = "https://github.com/vstakhov/libucl/",
- license = "MIT",
- classifiers = [
- "Development Status :: 3 - Alpha",
- "Intended Audience :: Developers",
- "License :: DFSG approved",
- "License :: OSI Approved :: MIT License",
- "Programming Language :: C",
- "Programming Language :: Python :: 2",
- "Programming Language :: Python :: 3",
- "Programming Language :: Python :: Implementation :: CPython",
- "Topic :: Software Development :: Libraries",
- ]
-)
-
-# clean up the trick after the build
-if in_ucl_root:
- os.unlink("setup.py")
diff --git a/contrib/libucl/python/src/uclmodule.c b/contrib/libucl/python/src/uclmodule.c
deleted file mode 100644
index d1051fbb0d12..000000000000
--- a/contrib/libucl/python/src/uclmodule.c
+++ /dev/null
@@ -1,335 +0,0 @@
-// Attempts to load a UCL structure from a string
-#include <ucl.h>
-#include <Python.h>
-
-static PyObject *SchemaError;
-
-static PyObject *
-_basic_ucl_type (ucl_object_t const *obj)
-{
- switch (obj->type) {
- case UCL_INT:
- return Py_BuildValue ("L", (long long)ucl_object_toint (obj));
- case UCL_FLOAT:
- return Py_BuildValue ("d", ucl_object_todouble (obj));
- case UCL_STRING:
- return Py_BuildValue ("s", ucl_object_tostring (obj));
- case UCL_BOOLEAN:
- return PyBool_FromLong (ucl_object_toboolean (obj));
- case UCL_TIME:
- return Py_BuildValue ("d", ucl_object_todouble (obj));
- case UCL_NULL:
- Py_RETURN_NONE;
- }
- return NULL;
-}
-
-static PyObject *
-_iterate_valid_ucl (ucl_object_t const *obj)
-{
- const ucl_object_t *tmp;
- ucl_object_iter_t it = NULL;
-
- tmp = obj;
-
- while ((obj = ucl_object_iterate (tmp, &it, false))) {
- PyObject *val;
-
- val = _basic_ucl_type(obj);
- if (!val) {
- PyObject *key = NULL;
-
- if (obj->key != NULL) {
- key = Py_BuildValue("s", ucl_object_key(obj));
- }
-
- if (obj->type == UCL_OBJECT) {
- const ucl_object_t *cur;
- ucl_object_iter_t it_obj = NULL;
-
- val = PyDict_New();
-
- while ((cur = ucl_object_iterate (obj, &it_obj, true))) {
- PyObject *keyobj = Py_BuildValue("s",ucl_object_key(cur));
- PyDict_SetItem(val, keyobj, _iterate_valid_ucl(cur));
- }
- } else if (obj->type == UCL_ARRAY) {
- const ucl_object_t *cur;
- ucl_object_iter_t it_obj = NULL;
-
- val = PyList_New(0);
-
- while ((cur = ucl_object_iterate (obj, &it_obj, true))) {
- PyList_Append(val, _iterate_valid_ucl(cur));
- }
- } else if (obj->type == UCL_USERDATA) {
- // XXX: this should be
- // PyBytes_FromStringAndSize; where is the
- // length from?
- val = PyBytes_FromString(obj->value.ud);
- }
- }
- return val;
- }
-
- PyErr_SetString(PyExc_SystemError, "unhandled type");
- return NULL;
-}
-
-static PyObject *
-_internal_load_ucl (char *uclstr)
-{
- PyObject *ret;
- struct ucl_parser *parser =
- ucl_parser_new (UCL_PARSER_NO_TIME|UCL_PARSER_NO_IMPLICIT_ARRAYS);
- bool r = ucl_parser_add_string(parser, uclstr, 0);
-
- if (r) {
- if (ucl_parser_get_error (parser)) {
- PyErr_SetString(PyExc_ValueError, ucl_parser_get_error(parser));
- ucl_parser_free(parser);
- ret = NULL;
- goto return_with_parser;
- } else {
- ucl_object_t *uclobj = ucl_parser_get_object(parser);
- ret = _iterate_valid_ucl(uclobj);
- ucl_object_unref(uclobj);
- goto return_with_parser;
- }
- }
- else {
- PyErr_SetString(PyExc_ValueError, ucl_parser_get_error (parser));
- ret = NULL;
- goto return_with_parser;
- }
-
-return_with_parser:
- ucl_parser_free(parser);
- return ret;
-}
-
-static PyObject*
-ucl_load (PyObject *self, PyObject *args)
-{
- char *uclstr;
-
- if (PyArg_ParseTuple(args, "z", &uclstr)) {
- if (!uclstr) {
- Py_RETURN_NONE;
- }
-
- return _internal_load_ucl(uclstr);
- }
-
- return NULL;
-}
-
-static ucl_object_t *
-_iterate_python (PyObject *obj)
-{
- if (obj == Py_None) {
- return ucl_object_new();
- }
- else if (PyBool_Check (obj)) {
- return ucl_object_frombool (obj == Py_True);
- }
-#if PY_MAJOR_VERSION < 3
- else if (PyInt_Check (obj)) {
- return ucl_object_fromint (PyInt_AsLong (obj));
- }
-#endif
- else if (PyLong_Check (obj)) {
- return ucl_object_fromint (PyLong_AsLong (obj));
- }
- else if (PyFloat_Check (obj)) {
- return ucl_object_fromdouble (PyFloat_AsDouble (obj));
- }
- else if (PyUnicode_Check (obj)) {
- ucl_object_t *ucl_str;
- PyObject *str = PyUnicode_AsASCIIString(obj);
- ucl_str = ucl_object_fromstring (PyBytes_AsString (str));
- Py_DECREF(str);
- return ucl_str;
- }
-#if PY_MAJOR_VERSION < 3
- else if (PyString_Check (obj)) {
- return ucl_object_fromstring (PyString_AsString (obj));
- }
-#endif
- else if (PyDict_Check(obj)) {
- PyObject *key, *value;
- Py_ssize_t pos = 0;
- ucl_object_t *top, *elm;
- char *keystr = NULL;
-
- top = ucl_object_typed_new (UCL_OBJECT);
-
- while (PyDict_Next(obj, &pos, &key, &value)) {
- elm = _iterate_python(value);
-
- if (PyUnicode_Check(key)) {
- PyObject *keyascii = PyUnicode_AsASCIIString(key);
- keystr = PyBytes_AsString(keyascii);
- Py_DECREF(keyascii);
- }
-#if PY_MAJOR_VERSION < 3
- else if (PyString_Check(key)) {
- keystr = PyString_AsString(key);
- }
-#endif
- else {
- PyErr_SetString(PyExc_TypeError, "Unknown key type");
- return NULL;
- }
-
- ucl_object_insert_key (top, elm, keystr, 0, true);
- }
-
- return top;
- }
- else if (PySequence_Check(obj)) {
- PyObject *value;
- Py_ssize_t len, pos;
- ucl_object_t *top, *elm;
-
- len = PySequence_Length(obj);
- top = ucl_object_typed_new (UCL_ARRAY);
-
- for (pos = 0; pos < len; pos++) {
- value = PySequence_GetItem(obj, pos);
- elm = _iterate_python(value);
- ucl_array_append(top, elm);
- }
-
- return top;
- }
- else {
- PyErr_SetString(PyExc_TypeError, "Unhandled object type");
- return NULL;
- }
-
- return NULL;
-}
-
-static PyObject *
-ucl_dump (PyObject *self, PyObject *args)
-{
- PyObject *obj;
- ucl_emitter_t emitter;
- ucl_object_t *root = NULL;
-
- emitter = UCL_EMIT_CONFIG;
-
- if (!PyArg_ParseTuple(args, "O|i", &obj, &emitter)) {
- PyErr_SetString(PyExc_TypeError, "Unhandled object type");
- return NULL;
- }
-
- if (emitter >= UCL_EMIT_MAX) {
- PyErr_SetString(PyExc_TypeError, "Invalid emitter type");
- return NULL;
- }
-
- if (obj == Py_None) {
- Py_RETURN_NONE;
- }
-
- root = _iterate_python(obj);
- if (root) {
- PyObject *ret;
- char *buf;
-
- buf = (char *) ucl_object_emit (root, emitter);
- ucl_object_unref (root);
-#if PY_MAJOR_VERSION < 3
- ret = PyString_FromString (buf);
-#else
- ret = PyUnicode_FromString (buf);
-#endif
- free(buf);
-
- return ret;
- }
-
- return NULL;
-}
-
-static PyObject *
-ucl_validate (PyObject *self, PyObject *args)
-{
- PyObject *dataobj, *schemaobj;
- ucl_object_t *data, *schema;
- bool r;
- struct ucl_schema_error err;
-
- if (!PyArg_ParseTuple (args, "OO", &schemaobj, &dataobj)) {
- PyErr_SetString (PyExc_TypeError, "Unhandled object type");
- return NULL;
- }
-
- schema = _iterate_python(schemaobj);
- if (!schema)
- return NULL;
-
- data = _iterate_python(dataobj);
- if (!data)
- return NULL;
-
- // validation
- r = ucl_object_validate (schema, data, &err);
- ucl_object_unref (schema);
- ucl_object_unref (data);
-
- if (!r) {
- PyErr_SetString (SchemaError, err.msg);
- return NULL;
- }
-
- Py_RETURN_TRUE;
-}
-
-static PyMethodDef uclMethods[] = {
- {"load", ucl_load, METH_VARARGS, "Load UCL from stream"},
- {"dump", ucl_dump, METH_VARARGS, "Dump UCL to stream"},
- {"validate", ucl_validate, METH_VARARGS, "Validate ucl stream against schema"},
- {NULL, NULL, 0, NULL}
-};
-
-static void
-init_macros(PyObject *mod)
-{
- PyModule_AddIntMacro(mod, UCL_EMIT_JSON);
- PyModule_AddIntMacro(mod, UCL_EMIT_JSON_COMPACT);
- PyModule_AddIntMacro(mod, UCL_EMIT_CONFIG);
- PyModule_AddIntMacro(mod, UCL_EMIT_YAML);
- PyModule_AddIntMacro(mod, UCL_EMIT_MSGPACK);
-
- SchemaError = PyErr_NewException("ucl.SchemaError", NULL, NULL);
- Py_INCREF(SchemaError);
- PyModule_AddObject(mod, "SchemaError", SchemaError);
-}
-
-#if PY_MAJOR_VERSION >= 3
-static struct PyModuleDef uclmodule = {
- PyModuleDef_HEAD_INIT,
- "ucl",
- NULL,
- -1,
- uclMethods
-};
-
-PyMODINIT_FUNC
-PyInit_ucl (void)
-{
- PyObject *mod = PyModule_Create (&uclmodule);
- init_macros (mod);
-
- return mod;
-}
-#else
-void initucl (void)
-{
- PyObject *mod = Py_InitModule ("ucl", uclMethods);
- init_macros (mod);
-}
-#endif
diff --git a/contrib/libucl/python/tests/compat.py b/contrib/libucl/python/tests/compat.py
deleted file mode 100644
index 50138262e9b7..000000000000
--- a/contrib/libucl/python/tests/compat.py
+++ /dev/null
@@ -1,8 +0,0 @@
-try:
- import unittest2 as unittest
-except ImportError:
- import unittest
-
-# Python 2.7 - 3.1
-if not hasattr(unittest.TestCase, 'assertRaisesRegex'):
- unittest.TestCase.assertRaisesRegex = unittest.TestCase.assertRaisesRegexp
diff --git a/contrib/libucl/python/tests/test_dump.py b/contrib/libucl/python/tests/test_dump.py
deleted file mode 100644
index 369241468509..000000000000
--- a/contrib/libucl/python/tests/test_dump.py
+++ /dev/null
@@ -1,66 +0,0 @@
-from .compat import unittest
-import ucl
-import sys
-
-class DumpTest(unittest.TestCase):
- def test_no_args(self):
- with self.assertRaises(TypeError):
- ucl.dump()
-
- def test_none(self):
- self.assertEqual(ucl.dump(None), None)
-
- def test_null(self):
- data = { "a" : None }
- valid = "a = null;\n"
- self.assertEqual(ucl.dump(data), valid)
-
- def test_int(self):
- data = { "a" : 1 }
- valid = "a = 1;\n"
- self.assertEqual(ucl.dump(data), valid)
-
- def test_nested_int(self):
- data = { "a" : { "b" : 1 } }
- valid = "a {\n b = 1;\n}\n"
- self.assertEqual(ucl.dump(data), valid)
-
- def test_int_array(self):
- data = { "a" : [1,2,3,4] }
- valid = "a [\n 1,\n 2,\n 3,\n 4,\n]\n"
- self.assertEqual(ucl.dump(data), valid)
-
- def test_str(self):
- data = { "a" : "b" }
- valid = "a = \"b\";\n"
- self.assertEqual(ucl.dump(data), valid)
-
- @unittest.skipIf(sys.version_info[0] > 2, "Python3 uses unicode only")
- def test_unicode(self):
- data = { unicode("a") : unicode("b") }
- valid = unicode("a = \"b\";\n")
- self.assertEqual(ucl.dump(data), valid)
-
- def test_float(self):
- data = { "a" : 1.1 }
- valid = "a = 1.100000;\n"
- self.assertEqual(ucl.dump(data), valid)
-
- def test_boolean(self):
- data = { "a" : True, "b" : False }
- valid = [
- "a = true;\nb = false;\n",
- "b = false;\na = true;\n"
- ]
- self.assertIn(ucl.dump(data), valid)
-
- def test_empty_ucl(self):
- self.assertEqual(ucl.dump({}), "")
-
- def test_json(self):
- data = { "a" : 1, "b": "bleh;" }
- valid = [
- '{\n "a": 1,\n "b": "bleh;"\n}',
- '{\n "b": "bleh;",\n "a": 1\n}'
- ]
- self.assertIn(ucl.dump(data, ucl.UCL_EMIT_JSON), valid)
diff --git a/contrib/libucl/python/tests/test_example.py b/contrib/libucl/python/tests/test_example.py
deleted file mode 100644
index f0785531f4e2..000000000000
--- a/contrib/libucl/python/tests/test_example.py
+++ /dev/null
@@ -1,59 +0,0 @@
-from .compat import unittest
-import json
-import ucl
-
-_ucl_inp = '''
-param = value;
-section {
- param = value;
- param1 = value1;
- flag = true;
- number = 10k;
- time = 0.2s;
- string = "something";
- subsection {
- host = {
- host = "hostname";
- port = 900;
- }
- host = {
- host = "hostname";
- port = 901;
- }
- }
-}
-'''
-
-_json_res = {
- 'param': 'value',
- 'section': {
- 'param': 'value',
- 'param1': 'value1',
- 'flag': True,
- 'number': 10000,
- 'time': '0.2s',
- 'string': 'something',
- 'subsection': {
- 'host': [
- {
- 'host': 'hostname',
- 'port': 900,
- },
- {
- 'host': 'hostname',
- 'port': 901,
- }
- ]
- }
- }
-}
-
-class TestExample(unittest.TestCase):
- def test_example(self):
- # load in sample UCL
- u = ucl.load(_ucl_inp)
-
- # Output and read back the JSON
- uj = json.loads(json.dumps(u))
-
- self.assertEqual(uj, _json_res)
diff --git a/contrib/libucl/python/tests/test_load.py b/contrib/libucl/python/tests/test_load.py
deleted file mode 100644
index 73d43188f3d5..000000000000
--- a/contrib/libucl/python/tests/test_load.py
+++ /dev/null
@@ -1,122 +0,0 @@
-from .compat import unittest
-import ucl
-
-class LoadTest(unittest.TestCase):
- def test_no_args(self):
- with self.assertRaises(TypeError):
- ucl.load()
-
- def test_multi_args(self):
- with self.assertRaises(TypeError):
- ucl.load(0,0)
-
- def test_none(self):
- self.assertEqual(ucl.load(None), None)
-
- def test_null(self):
- data = "a: null"
- valid = { "a" : None }
- self.assertEqual(ucl.load(data), valid)
-
- def test_int(self):
- data = "a : 1"
- valid = { "a" : 1 }
- self.assertEqual(ucl.load(data), valid)
-
- def test_braced_int(self):
- data = "{a : 1}"
- valid = { "a" : 1 }
- self.assertEqual(ucl.load(data), valid)
-
- def test_nested_int(self):
- data = "a : { b : 1 }"
- valid = { "a" : { "b" : 1 } }
- self.assertEqual(ucl.load(data), valid)
-
- def test_str(self):
- data = "a : b"
- valid = { "a" : "b" }
- self.assertEqual(ucl.load(data), valid)
-
- def test_float(self):
- data = "a : 1.1"
- valid = {"a" : 1.1}
- self.assertEqual(ucl.load(data), valid)
-
- def test_boolean(self):
- data = (
- "a : True;" \
- "b : False"
- )
- valid = { "a" : True, "b" : False }
- self.assertEqual(ucl.load(data), valid)
-
- def test_empty_ucl(self):
- self.assertEqual(ucl.load("{}"), {})
-
- def test_single_brace(self):
- self.assertEqual(ucl.load("{"), {})
-
- def test_single_back_brace(self):
- self.assertEqual(ucl.load("}"), {})
-
- def test_single_square_forward(self):
- self.assertEqual(ucl.load("["), [])
-
- def test_invalid_ucl(self):
- with self.assertRaisesRegex(ValueError, "unfinished key$"):
- ucl.load('{ "var"')
-
- def test_comment_ignored(self):
- self.assertEqual(ucl.load("{/*1*/}"), {})
-
- def test_1_in(self):
- valid = {
- 'key1': [
- 'value',
- 'value2',
- 'value;',
- 1.0,
- -0xdeadbeef,
- '0xdeadbeef.1',
- '0xreadbeef',
- -1e-10,
- 1,
- True,
- False,
- True,
- ]
- }
- with open("../tests/basic/1.in", "r") as in1:
- self.assertEqual(ucl.load(in1.read()), valid)
-
- def test_every_type(self):
- data = ("""{
- "key1": value;
- "key2": value2;
- "key3": "value;"
- "key4": 1.0,
- "key5": -0xdeadbeef
- "key6": 0xdeadbeef.1
- "key7": 0xreadbeef
- "key8": -1e-10,
- "key9": 1
- "key10": true
- "key11": no
- "key12": yes
- }""")
- valid = {
- 'key1': 'value',
- 'key2': 'value2',
- 'key3': 'value;',
- 'key4': 1.0,
- 'key5': -3735928559,
- 'key6': '0xdeadbeef.1',
- 'key7': '0xreadbeef',
- 'key8': -1e-10,
- 'key9': 1,
- 'key10': True,
- 'key11': False,
- 'key12': True,
- }
- self.assertEqual(ucl.load(data), valid)
diff --git a/contrib/libucl/python/tests/test_validation.py b/contrib/libucl/python/tests/test_validation.py
deleted file mode 100644
index f7c853ad69a7..000000000000
--- a/contrib/libucl/python/tests/test_validation.py
+++ /dev/null
@@ -1,50 +0,0 @@
-from .compat import unittest
-import ucl
-import json
-import os.path
-import glob
-import re
-
-TESTS_SCHEMA_FOLDER = '../tests/schema/*.json'
-
-comment_re = re.compile('\/\*((?!\*\/).)*?\*\/', re.DOTALL | re.MULTILINE)
-def json_remove_comments(content):
- return comment_re.sub('', content)
-
-class ValidationTest(unittest.TestCase):
- def validate(self, jsonfile):
- def perform_test(schema, data, valid, description):
- msg = '%s (valid=%r)' % (description, valid)
- if valid:
- self.assertTrue(ucl.validate(schema, data), msg)
- else:
- with self.assertRaises(ucl.SchemaError):
- ucl.validate(schema, data)
- self.fail(msg) # fail() will be called only if SchemaError is not raised
-
- with open(jsonfile) as f:
- try:
- # data = json.load(f)
- data = json.loads(json_remove_comments(f.read()))
- except ValueError as e:
- raise self.skipTest('Failed to load JSON: %s' % str(e))
-
- for testgroup in data:
- for test in testgroup['tests']:
- perform_test(testgroup['schema'], test['data'],
- test['valid'], test['description'])
-
- @classmethod
- def setupValidationTests(cls):
- """Creates each test dynamically from a folder"""
- def test_gen(filename):
- def run_test(self):
- self.validate(filename)
- return run_test
-
- for jsonfile in glob.glob(TESTS_SCHEMA_FOLDER):
- testname = os.path.splitext(os.path.basename(jsonfile))[0]
- setattr(cls, 'test_%s' % testname, test_gen(jsonfile))
-
-
-ValidationTest.setupValidationTests()
diff --git a/contrib/libucl/python/ucl.pyi b/contrib/libucl/python/ucl.pyi
deleted file mode 100644
index 79fa2901ba9f..000000000000
--- a/contrib/libucl/python/ucl.pyi
+++ /dev/null
@@ -1,15 +0,0 @@
-# Stubs for ucl (Python 3.6)
-#
-# NOTE: This dynamically typed stub was automatically generated by stubgen.
-
-UCL_EMIT_CONFIG = ... # type: int
-UCL_EMIT_JSON = ... # type: int
-UCL_EMIT_JSON_COMPACT = ... # type: int
-UCL_EMIT_MSGPACK = ... # type: int
-UCL_EMIT_YAML = ... # type: int
-
-def dump(*args, **kwargs): ...
-def load(*args, **kwargs): ...
-def validate(*args, **kwargs): ...
-
-class SchemaError(Exception): ...
diff --git a/contrib/libucl/src/Makefile.am b/contrib/libucl/src/Makefile.am
deleted file mode 100644
index 80ce5b185b83..000000000000
--- a/contrib/libucl/src/Makefile.am
+++ /dev/null
@@ -1,30 +0,0 @@
-libucl_common_cflags= -I$(top_srcdir)/src \
- -I$(top_srcdir)/include \
- -I$(top_srcdir)/uthash \
- -I$(top_srcdir)/klib \
- -Wall -W -Wno-unused-parameter -Wno-pointer-sign
-lib_LTLIBRARIES= libucl.la
-libucl_la_SOURCES= ucl_emitter.c \
- ucl_emitter_streamline.c \
- ucl_emitter_utils.c \
- ucl_hash.c \
- ucl_parser.c \
- ucl_schema.c \
- ucl_util.c \
- ucl_msgpack.c \
- ucl_sexp.c
-libucl_la_CFLAGS= $(libucl_common_cflags) \
- @CURL_CFLAGS@
-libucl_la_LDFLAGS = -version-info @SO_VERSION@
-libucl_la_LIBADD= @LIBFETCH_LIBS@ \
- @LIBCRYPTO_LIB@ \
- @LIBREGEX_LIB@ \
- @CURL_LIBS@
-
-include_HEADERS= $(top_srcdir)/include/ucl.h \
- $(top_srcdir)/include/ucl++.h
-noinst_HEADERS= ucl_internal.h \
- mum.h \
- ucl_hash.h \
- ucl_chartable.h \
- tree.h
diff --git a/contrib/libucl/src/mum.h b/contrib/libucl/src/mum.h
index 148161a3ec58..318efea50268 100644
--- a/contrib/libucl/src/mum.h
+++ b/contrib/libucl/src/mum.h
@@ -69,11 +69,9 @@ typedef unsigned __int64 uint64_t;
#endif
#endif
-#if 0
#if defined(__GNUC__) && ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 9) || (__GNUC__ > 4))
#define _MUM_FRESH_GCC
#endif
-#endif
#if defined(__GNUC__) && !defined(__llvm__) && defined(_MUM_FRESH_GCC)
#define _MUM_ATTRIBUTE_UNUSED __attribute__((unused))
diff --git a/contrib/libucl/src/ucl_emitter.c b/contrib/libucl/src/ucl_emitter.c
index 4f4465dfbf4a..97d8f618021f 100644
--- a/contrib/libucl/src/ucl_emitter.c
+++ b/contrib/libucl/src/ucl_emitter.c
@@ -47,9 +47,9 @@ static void ucl_emitter_common_elt (struct ucl_emitter_context *ctx,
static void ucl_emit_ ## type ## _elt (struct ucl_emitter_context *ctx, \
const ucl_object_t *obj, bool first, bool print_key); \
static void ucl_emit_ ## type ## _start_obj (struct ucl_emitter_context *ctx, \
- const ucl_object_t *obj, bool print_key); \
+ const ucl_object_t *obj, bool first, bool print_key); \
static void ucl_emit_ ## type## _start_array (struct ucl_emitter_context *ctx, \
- const ucl_object_t *obj, bool print_key); \
+ const ucl_object_t *obj, bool first, bool print_key); \
static void ucl_emit_ ##type## _end_object (struct ucl_emitter_context *ctx, \
const ucl_object_t *obj); \
static void ucl_emit_ ##type## _end_array (struct ucl_emitter_context *ctx, \
@@ -248,12 +248,26 @@ ucl_emitter_common_end_array (struct ucl_emitter_context *ctx,
*/
static void
ucl_emitter_common_start_array (struct ucl_emitter_context *ctx,
- const ucl_object_t *obj, bool print_key, bool compact)
+ const ucl_object_t *obj, bool first, bool print_key, bool compact)
{
const ucl_object_t *cur;
ucl_object_iter_t iter = NULL;
const struct ucl_emitter_functions *func = ctx->func;
- bool first = true;
+ bool first_key = true;
+
+ if (ctx->id != UCL_EMIT_CONFIG && !first) {
+ if (compact) {
+ func->ucl_emitter_append_character (',', 1, func->ud);
+ }
+ else {
+ if (ctx->id == UCL_EMIT_YAML && ctx->indent == 0) {
+ func->ucl_emitter_append_len ("\n", 1, func->ud);
+ } else {
+ func->ucl_emitter_append_len (",\n", 2, func->ud);
+ }
+ }
+ ucl_add_tabs (func, ctx->indent, compact);
+ }
ucl_emitter_print_key (print_key, ctx, obj, compact);
@@ -269,16 +283,16 @@ ucl_emitter_common_start_array (struct ucl_emitter_context *ctx,
if (obj->type == UCL_ARRAY) {
/* explicit array */
while ((cur = ucl_object_iterate (obj, &iter, true)) != NULL) {
- ucl_emitter_common_elt (ctx, cur, first, false, compact);
- first = false;
+ ucl_emitter_common_elt (ctx, cur, first_key, false, compact);
+ first_key = false;
}
}
else {
/* implicit array */
cur = obj;
while (cur) {
- ucl_emitter_common_elt (ctx, cur, first, false, compact);
- first = false;
+ ucl_emitter_common_elt (ctx, cur, first_key, false, compact);
+ first_key = false;
cur = cur->next;
}
}
@@ -294,12 +308,26 @@ ucl_emitter_common_start_array (struct ucl_emitter_context *ctx,
*/
static void
ucl_emitter_common_start_object (struct ucl_emitter_context *ctx,
- const ucl_object_t *obj, bool print_key, bool compact)
+ const ucl_object_t *obj, bool first, bool print_key, bool compact)
{
ucl_hash_iter_t it = NULL;
const ucl_object_t *cur, *elt;
const struct ucl_emitter_functions *func = ctx->func;
- bool first = true;
+ bool first_key = true;
+
+ if (ctx->id != UCL_EMIT_CONFIG && !first) {
+ if (compact) {
+ func->ucl_emitter_append_character (',', 1, func->ud);
+ }
+ else {
+ if (ctx->id == UCL_EMIT_YAML && ctx->indent == 0) {
+ func->ucl_emitter_append_len ("\n", 1, func->ud);
+ } else {
+ func->ucl_emitter_append_len (",\n", 2, func->ud);
+ }
+ }
+ ucl_add_tabs (func, ctx->indent, compact);
+ }
ucl_emitter_print_key (print_key, ctx, obj, compact);
/*
@@ -320,13 +348,13 @@ ucl_emitter_common_start_object (struct ucl_emitter_context *ctx,
if (ctx->id == UCL_EMIT_CONFIG) {
LL_FOREACH (cur, elt) {
- ucl_emitter_common_elt (ctx, elt, first, true, compact);
+ ucl_emitter_common_elt (ctx, elt, first_key, true, compact);
}
}
else {
/* Expand implicit arrays */
if (cur->next != NULL) {
- if (!first) {
+ if (!first_key) {
if (compact) {
func->ucl_emitter_append_character (',', 1, func->ud);
}
@@ -335,15 +363,15 @@ ucl_emitter_common_start_object (struct ucl_emitter_context *ctx,
}
}
ucl_add_tabs (func, ctx->indent, compact);
- ucl_emitter_common_start_array (ctx, cur, true, compact);
+ ucl_emitter_common_start_array (ctx, cur, first_key, true, compact);
ucl_emitter_common_end_array (ctx, cur, compact);
}
else {
- ucl_emitter_common_elt (ctx, cur, first, true, compact);
+ ucl_emitter_common_elt (ctx, cur, first_key, true, compact);
}
}
- first = false;
+ first_key = false;
}
}
@@ -446,11 +474,11 @@ ucl_emitter_common_elt (struct ucl_emitter_context *ctx,
ucl_emitter_finish_object (ctx, obj, compact, !print_key);
break;
case UCL_OBJECT:
- ucl_emitter_common_start_object (ctx, obj, print_key, compact);
+ ucl_emitter_common_start_object (ctx, obj, true, print_key, compact);
ucl_emitter_common_end_object (ctx, obj, compact);
break;
case UCL_ARRAY:
- ucl_emitter_common_start_array (ctx, obj, print_key, compact);
+ ucl_emitter_common_start_array (ctx, obj, true, print_key, compact);
ucl_emitter_common_end_array (ctx, obj, compact);
break;
case UCL_USERDATA:
@@ -490,12 +518,12 @@ ucl_emitter_common_elt (struct ucl_emitter_context *ctx,
ucl_emitter_common_elt (ctx, obj, first, print_key, (compact)); \
} \
static void ucl_emit_ ## type ## _start_obj (struct ucl_emitter_context *ctx, \
- const ucl_object_t *obj, bool print_key) { \
- ucl_emitter_common_start_object (ctx, obj, print_key, (compact)); \
+ const ucl_object_t *obj, bool first, bool print_key) { \
+ ucl_emitter_common_start_object (ctx, obj, first, print_key, (compact)); \
} \
static void ucl_emit_ ## type## _start_array (struct ucl_emitter_context *ctx, \
- const ucl_object_t *obj, bool print_key) { \
- ucl_emitter_common_start_array (ctx, obj, print_key, (compact)); \
+ const ucl_object_t *obj, bool first, bool print_key) { \
+ ucl_emitter_common_start_array (ctx, obj, first, print_key, (compact)); \
} \
static void ucl_emit_ ##type## _end_object (struct ucl_emitter_context *ctx, \
const ucl_object_t *obj) { \
@@ -513,7 +541,7 @@ UCL_EMIT_TYPE_IMPL(yaml, false)
static void
ucl_emit_msgpack_elt (struct ucl_emitter_context *ctx,
- const ucl_object_t *obj, bool first, bool print_key)
+ const ucl_object_t *obj, bool _first, bool print_key)
{
ucl_object_iter_t it;
struct ucl_object_userdata *ud;
@@ -556,7 +584,7 @@ ucl_emit_msgpack_elt (struct ucl_emitter_context *ctx,
case UCL_OBJECT:
ucl_emitter_print_key_msgpack (print_key, ctx, obj);
- ucl_emit_msgpack_start_obj (ctx, obj, print_key);
+ ucl_emit_msgpack_start_obj (ctx, obj, false, print_key);
it = NULL;
while ((cur = ucl_object_iterate (obj, &it, true)) != NULL) {
@@ -575,7 +603,7 @@ ucl_emit_msgpack_elt (struct ucl_emitter_context *ctx,
case UCL_ARRAY:
ucl_emitter_print_key_msgpack (print_key, ctx, obj);
- ucl_emit_msgpack_start_array (ctx, obj, print_key);
+ ucl_emit_msgpack_start_array (ctx, obj, false, print_key);
it = NULL;
while ((cur = ucl_object_iterate (obj, &it, true)) != NULL) {
@@ -601,14 +629,14 @@ ucl_emit_msgpack_elt (struct ucl_emitter_context *ctx,
static void
ucl_emit_msgpack_start_obj (struct ucl_emitter_context *ctx,
- const ucl_object_t *obj, bool print_key)
+ const ucl_object_t *obj, bool _first, bool _print_key)
{
ucl_emitter_print_object_msgpack (ctx, obj->len);
}
static void
ucl_emit_msgpack_start_array (struct ucl_emitter_context *ctx,
- const ucl_object_t *obj, bool print_key)
+ const ucl_object_t *obj, bool _first, bool _print_key)
{
ucl_emitter_print_array_msgpack (ctx, obj->len);
}
diff --git a/contrib/libucl/src/ucl_emitter_streamline.c b/contrib/libucl/src/ucl_emitter_streamline.c
index a7178c5d74b0..8ca86fa081c9 100644
--- a/contrib/libucl/src/ucl_emitter_streamline.c
+++ b/contrib/libucl/src/ucl_emitter_streamline.c
@@ -103,18 +103,19 @@ ucl_object_emit_streamline_start_container (struct ucl_emitter_context *ctx,
top = sctx->containers;
st = malloc (sizeof (*st));
if (st != NULL) {
- if (top != NULL && !top->is_array) {
+ st->empty = true;
+ if (top && !top->is_array) {
print_key = true;
}
- st->empty = true;
+
st->obj = obj;
if (obj != NULL && obj->type == UCL_ARRAY) {
st->is_array = true;
- sctx->ops->ucl_emitter_start_array (ctx, obj, print_key);
+ sctx->ops->ucl_emitter_start_array (ctx, obj, top == NULL, print_key);
}
else {
st->is_array = false;
- sctx->ops->ucl_emitter_start_object (ctx, obj, print_key);
+ sctx->ops->ucl_emitter_start_object (ctx, obj, top == NULL, print_key);
}
LL_PREPEND (sctx->containers, st);
}
diff --git a/contrib/libucl/src/ucl_hash.c b/contrib/libucl/src/ucl_hash.c
index a74dfcdee68e..0208cfd29c9a 100644
--- a/contrib/libucl/src/ucl_hash.c
+++ b/contrib/libucl/src/ucl_hash.c
@@ -32,12 +32,12 @@
struct ucl_hash_elt {
const ucl_object_t *obj;
- size_t ar_idx;
+ struct ucl_hash_elt *prev, *next;
};
struct ucl_hash_struct {
void *hash;
- kvec_t(const ucl_object_t *) ar;
+ struct ucl_hash_elt *head;
bool caseless;
};
@@ -45,7 +45,6 @@ static uint64_t
ucl_hash_seed (void)
{
static uint64_t seed;
-
if (seed == 0) {
#ifdef UCL_RANDOM_FUNCTION
seed = UCL_RANDOM_FUNCTION;
@@ -115,7 +114,7 @@ ucl_hash_equal (const ucl_object_t *k1, const ucl_object_t *k2)
return 0;
}
-KHASH_INIT (ucl_hash_node, const ucl_object_t *, struct ucl_hash_elt, 1,
+KHASH_INIT (ucl_hash_node, const ucl_object_t *, struct ucl_hash_elt *, 1,
ucl_hash_func, ucl_hash_equal)
static inline uint32_t
@@ -227,7 +226,7 @@ ucl_hash_caseless_equal (const ucl_object_t *k1, const ucl_object_t *k2)
return 0;
}
-KHASH_INIT (ucl_hash_caseless_node, const ucl_object_t *, struct ucl_hash_elt, 1,
+KHASH_INIT (ucl_hash_caseless_node, const ucl_object_t *, struct ucl_hash_elt *, 1,
ucl_hash_caseless_func, ucl_hash_caseless_equal)
ucl_hash_t*
@@ -238,8 +237,7 @@ ucl_hash_create (bool ignore_case)
new = UCL_ALLOC (sizeof (ucl_hash_t));
if (new != NULL) {
void *h;
- kv_init (new->ar);
-
+ new->head = NULL;
new->caseless = ignore_case;
if (ignore_case) {
h = (void *)kh_init (ucl_hash_caseless_node);
@@ -258,7 +256,6 @@ ucl_hash_create (bool ignore_case)
void ucl_hash_destroy (ucl_hash_t* hashlin, ucl_hash_free_func func)
{
- const ucl_object_t *cur, *tmp;
if (hashlin == NULL) {
return;
@@ -269,10 +266,11 @@ void ucl_hash_destroy (ucl_hash_t* hashlin, ucl_hash_free_func func)
khash_t(ucl_hash_node) *h = (khash_t(ucl_hash_node) *)
hashlin->hash;
khiter_t k;
+ const ucl_object_t *cur, *tmp;
for (k = kh_begin (h); k != kh_end (h); ++k) {
if (kh_exist (h, k)) {
- cur = (kh_value (h, k)).obj;
+ cur = (kh_value (h, k))->obj;
while (cur != NULL) {
tmp = cur->next;
func (__DECONST (ucl_object_t *, cur));
@@ -293,7 +291,12 @@ void ucl_hash_destroy (ucl_hash_t* hashlin, ucl_hash_free_func func)
kh_destroy (ucl_hash_node, h);
}
- kv_destroy (hashlin->ar);
+ struct ucl_hash_elt *cur, *tmp;
+
+ DL_FOREACH_SAFE(hashlin->head, cur, tmp) {
+ UCL_FREE(sizeof(*cur), cur);
+ }
+
UCL_FREE (sizeof (*hashlin), hashlin);
}
@@ -303,7 +306,7 @@ ucl_hash_insert (ucl_hash_t* hashlin, const ucl_object_t *obj,
{
khiter_t k;
int ret;
- struct ucl_hash_elt *elt;
+ struct ucl_hash_elt **pelt, *elt;
if (hashlin == NULL) {
return false;
@@ -314,10 +317,14 @@ ucl_hash_insert (ucl_hash_t* hashlin, const ucl_object_t *obj,
hashlin->hash;
k = kh_put (ucl_hash_caseless_node, h, obj, &ret);
if (ret > 0) {
- elt = &kh_value (h, k);
- kv_push_safe (const ucl_object_t *, hashlin->ar, obj, e0);
+ elt = UCL_ALLOC(sizeof(*elt));
+ pelt = &kh_value (h, k);
+ *pelt = elt;
+ DL_APPEND(hashlin->head, elt);
elt->obj = obj;
- elt->ar_idx = kv_size (hashlin->ar) - 1;
+ }
+ else if (ret < 0) {
+ goto e0;
}
}
else {
@@ -325,10 +332,11 @@ ucl_hash_insert (ucl_hash_t* hashlin, const ucl_object_t *obj,
hashlin->hash;
k = kh_put (ucl_hash_node, h, obj, &ret);
if (ret > 0) {
- elt = &kh_value (h, k);
- kv_push_safe (const ucl_object_t *, hashlin->ar, obj, e0);
+ elt = UCL_ALLOC(sizeof(*elt));
+ pelt = &kh_value (h, k);
+ *pelt = elt;
+ DL_APPEND(hashlin->head, elt);
elt->obj = obj;
- elt->ar_idx = kv_size (hashlin->ar) - 1;
} else if (ret < 0) {
goto e0;
}
@@ -343,7 +351,7 @@ void ucl_hash_replace (ucl_hash_t* hashlin, const ucl_object_t *old,
{
khiter_t k;
int ret;
- struct ucl_hash_elt elt, *pelt;
+ struct ucl_hash_elt *elt, *nelt;
if (hashlin == NULL) {
return;
@@ -354,13 +362,14 @@ void ucl_hash_replace (ucl_hash_t* hashlin, const ucl_object_t *old,
hashlin->hash;
k = kh_put (ucl_hash_caseless_node, h, old, &ret);
if (ret == 0) {
- elt = kh_value (h, k);
+ elt = kh_value(h, k);
kh_del (ucl_hash_caseless_node, h, k);
k = kh_put (ucl_hash_caseless_node, h, new, &ret);
- pelt = &kh_value (h, k);
- pelt->obj = new;
- pelt->ar_idx = elt.ar_idx;
- kv_A (hashlin->ar, elt.ar_idx) = new;
+ nelt = UCL_ALLOC(sizeof(*nelt));
+ nelt->obj = new;
+ kh_value(h, k) = nelt;
+ DL_REPLACE_ELEM(hashlin->head, elt, nelt);
+ UCL_FREE(sizeof(*elt), elt);
}
}
else {
@@ -371,17 +380,17 @@ void ucl_hash_replace (ucl_hash_t* hashlin, const ucl_object_t *old,
elt = kh_value (h, k);
kh_del (ucl_hash_node, h, k);
k = kh_put (ucl_hash_node, h, new, &ret);
- pelt = &kh_value (h, k);
- pelt->obj = new;
- pelt->ar_idx = elt.ar_idx;
- kv_A (hashlin->ar, elt.ar_idx) = new;
+ nelt = UCL_ALLOC(sizeof(*nelt));
+ nelt->obj = new;
+ kh_value(h, k) = nelt;
+ DL_REPLACE_ELEM(hashlin->head, elt, nelt);
+ UCL_FREE(sizeof(*elt), elt);
}
}
}
struct ucl_hash_real_iter {
- const ucl_object_t **cur;
- const ucl_object_t **end;
+ const struct ucl_hash_elt *cur;
};
#define UHI_SETERR(ep, ern) {if (ep != NULL) *ep = (ern);}
@@ -405,13 +414,13 @@ ucl_hash_iterate2 (ucl_hash_t *hashlin, ucl_hash_iter_t *iter, int *ep)
return NULL;
}
- it->cur = &hashlin->ar.a[0];
- it->end = it->cur + hashlin->ar.n;
+ it->cur = hashlin->head;
}
UHI_SETERR(ep, 0);
- if (it->cur < it->end) {
- ret = *it->cur++;
+ if (it->cur) {
+ ret = it->cur->obj;
+ it->cur = it->cur->next;
}
else {
UCL_FREE (sizeof (*it), it);
@@ -429,7 +438,7 @@ ucl_hash_iter_has_next (ucl_hash_t *hashlin, ucl_hash_iter_t iter)
{
struct ucl_hash_real_iter *it = (struct ucl_hash_real_iter *)(iter);
- return it->cur < it->end - 1;
+ return it->cur != NULL;
}
@@ -454,7 +463,7 @@ ucl_hash_search (ucl_hash_t* hashlin, const char *key, unsigned keylen)
k = kh_get (ucl_hash_caseless_node, h, &search);
if (k != kh_end (h)) {
- elt = &kh_value (h, k);
+ elt = kh_value (h, k);
ret = elt->obj;
}
}
@@ -463,7 +472,7 @@ ucl_hash_search (ucl_hash_t* hashlin, const char *key, unsigned keylen)
hashlin->hash;
k = kh_get (ucl_hash_node, h, &search);
if (k != kh_end (h)) {
- elt = &kh_value (h, k);
+ elt = kh_value (h, k);
ret = elt->obj;
}
}
@@ -476,7 +485,6 @@ ucl_hash_delete (ucl_hash_t* hashlin, const ucl_object_t *obj)
{
khiter_t k;
struct ucl_hash_elt *elt;
- size_t i;
if (hashlin == NULL) {
return;
@@ -488,16 +496,10 @@ ucl_hash_delete (ucl_hash_t* hashlin, const ucl_object_t *obj)
k = kh_get (ucl_hash_caseless_node, h, obj);
if (k != kh_end (h)) {
- elt = &kh_value (h, k);
- i = elt->ar_idx;
- kv_del (const ucl_object_t *, hashlin->ar, elt->ar_idx);
+ elt = kh_value (h, k);
+ DL_DELETE(hashlin->head, elt);
kh_del (ucl_hash_caseless_node, h, k);
-
- /* Update subsequent elts */
- for (; i < hashlin->ar.n; i ++) {
- elt = &kh_value (h, i);
- elt->ar_idx --;
- }
+ UCL_FREE(sizeof(*elt), elt);
}
}
else {
@@ -505,16 +507,10 @@ ucl_hash_delete (ucl_hash_t* hashlin, const ucl_object_t *obj)
hashlin->hash;
k = kh_get (ucl_hash_node, h, obj);
if (k != kh_end (h)) {
- elt = &kh_value (h, k);
- i = elt->ar_idx;
- kv_del (const ucl_object_t *, hashlin->ar, elt->ar_idx);
+ elt = kh_value (h, k);
+ DL_DELETE(hashlin->head, elt);
kh_del (ucl_hash_node, h, k);
-
- /* Update subsequent elts */
- for (; i < hashlin->ar.n; i ++) {
- elt = &kh_value (h, i);
- elt->ar_idx --;
- }
+ UCL_FREE(sizeof(*elt), elt);
}
}
}
@@ -525,9 +521,7 @@ bool ucl_hash_reserve (ucl_hash_t *hashlin, size_t sz)
return false;
}
- if (sz > hashlin->ar.m) {
- kv_resize_safe (const ucl_object_t *, hashlin->ar, sz, e0);
-
+ if (sz > kh_size((khash_t(ucl_hash_node) *)hashlin->hash)) {
if (hashlin->caseless) {
khash_t(ucl_hash_caseless_node) *h = (khash_t(
ucl_hash_caseless_node) *)
@@ -540,8 +534,6 @@ bool ucl_hash_reserve (ucl_hash_t *hashlin, size_t sz)
}
}
return true;
-e0:
- return false;
}
static int
@@ -591,27 +583,27 @@ ucl_lc_cmp (const char *s, const char *d, size_t l)
static int
ucl_hash_cmp_icase (const void *a, const void *b)
{
- const ucl_object_t *oa = *(const ucl_object_t **)a,
- *ob = *(const ucl_object_t **)b;
+ const struct ucl_hash_elt *oa = (const struct ucl_hash_elt *)a,
+ *ob = (const struct ucl_hash_elt *)b;
- if (oa->keylen == ob->keylen) {
- return ucl_lc_cmp (oa->key, ob->key, oa->keylen);
+ if (oa->obj->keylen == ob->obj->keylen) {
+ return ucl_lc_cmp (oa->obj->key, ob->obj->key, oa->obj->keylen);
}
- return ((int)(oa->keylen)) - ob->keylen;
+ return ((int)(oa->obj->keylen)) - ob->obj->keylen;
}
static int
ucl_hash_cmp_case_sens (const void *a, const void *b)
{
- const ucl_object_t *oa = *(const ucl_object_t **)a,
- *ob = *(const ucl_object_t **)b;
+ const struct ucl_hash_elt *oa = (const struct ucl_hash_elt *)a,
+ *ob = (const struct ucl_hash_elt *)b;
- if (oa->keylen == ob->keylen) {
- return memcmp (oa->key, ob->key, oa->keylen);
+ if (oa->obj->keylen == ob->obj->keylen) {
+ return memcmp (oa->obj->key, ob->obj->key, oa->obj->keylen);
}
- return ((int)(oa->keylen)) - ob->keylen;
+ return ((int)(oa->obj->keylen)) - ob->obj->keylen;
}
void
@@ -619,18 +611,18 @@ ucl_hash_sort (ucl_hash_t *hashlin, enum ucl_object_keys_sort_flags fl)
{
if (fl & UCL_SORT_KEYS_ICASE) {
- qsort (hashlin->ar.a, hashlin->ar.n, sizeof (ucl_object_t *),
- ucl_hash_cmp_icase);
+ DL_SORT(hashlin->head, ucl_hash_cmp_icase);
}
else {
- qsort (hashlin->ar.a, hashlin->ar.n, sizeof (ucl_object_t *),
- ucl_hash_cmp_case_sens);
+ DL_SORT(hashlin->head, ucl_hash_cmp_case_sens);
}
if (fl & UCL_SORT_KEYS_RECURSIVE) {
- for (size_t i = 0; i < hashlin->ar.n; i ++) {
- if (ucl_object_type (hashlin->ar.a[i]) == UCL_OBJECT) {
- ucl_hash_sort (hashlin->ar.a[i]->value.ov, fl);
+ struct ucl_hash_elt *elt;
+
+ DL_FOREACH(hashlin->head, elt) {
+ if (ucl_object_type (elt->obj) == UCL_OBJECT) {
+ ucl_hash_sort (elt->obj->value.ov, fl);
}
}
}
diff --git a/contrib/libucl/src/ucl_msgpack.c b/contrib/libucl/src/ucl_msgpack.c
index 08e690a4728a..628ed2be993d 100644
--- a/contrib/libucl/src/ucl_msgpack.c
+++ b/contrib/libucl/src/ucl_msgpack.c
@@ -1246,8 +1246,8 @@ ucl_msgpack_consume (struct ucl_parser *parser)
/* Empty container at the end */
if (len != 0) {
ucl_create_err (&parser->err,
- "invalid non-empty container at the end; len=%zu",
- (size_t)len);
+ "invalid non-empty container at the end; len=%ju",
+ (uintmax_t)len);
return false;
}
diff --git a/contrib/libucl/src/ucl_parser.c b/contrib/libucl/src/ucl_parser.c
index 23f5bce3056f..728cd6381056 100644
--- a/contrib/libucl/src/ucl_parser.c
+++ b/contrib/libucl/src/ucl_parser.c
@@ -47,6 +47,9 @@ struct ucl_parser_saved_state {
*/
#define ucl_chunk_skipc(chunk, p) \
do { \
+ if (p == chunk->end) { \
+ break; \
+ } \
if (*(p) == '\n') { \
(chunk)->line ++; \
(chunk)->column = 0; \
@@ -161,51 +164,50 @@ start:
}
}
}
- else if (chunk->remain >= 2 && *p == '/') {
- if (p[1] == '*') {
- beg = p;
- ucl_chunk_skipc (chunk, p);
- comments_nested ++;
- ucl_chunk_skipc (chunk, p);
-
- while (p < chunk->end) {
- if (*p == '"' && *(p - 1) != '\\') {
- quoted = !quoted;
- }
-
- if (!quoted) {
- if (*p == '*') {
- ucl_chunk_skipc (chunk, p);
- if (*p == '/') {
- comments_nested --;
- if (comments_nested == 0) {
- if (parser->flags & UCL_PARSER_SAVE_COMMENTS) {
- ucl_save_comment (parser, beg, p - beg + 1);
- beg = NULL;
- }
-
- ucl_chunk_skipc (chunk, p);
- goto start;
- }
- }
- ucl_chunk_skipc (chunk, p);
- }
- else if (p[0] == '/' && chunk->remain >= 2 && p[1] == '*') {
- comments_nested ++;
- ucl_chunk_skipc (chunk, p);
- ucl_chunk_skipc (chunk, p);
- continue;
+ else if (chunk->remain >= 2 && *p == '/' && p[1] == '*') {
+ beg = p;
+ comments_nested ++;
+ ucl_chunk_skipc (chunk, p);
+ ucl_chunk_skipc (chunk, p);
+ while (p < chunk->end) {
+ if (*p == '"' && *(p - 1) != '\\') {
+ /* begin or end double-quoted string */
+ quoted = !quoted;
+ ucl_chunk_skipc (chunk, p);
+ }
+ else if (quoted) {
+ /* quoted character */
+ ucl_chunk_skipc (chunk, p);
+ }
+ else if (chunk->remain >= 2 && *p == '*' && p[1] == '/') {
+ /* end of comment */
+ ucl_chunk_skipc (chunk, p);
+ ucl_chunk_skipc (chunk, p);
+ comments_nested --;
+ if (comments_nested == 0) {
+ if (parser->flags & UCL_PARSER_SAVE_COMMENTS) {
+ ucl_save_comment (parser, beg, p - beg + 1);
+ beg = NULL;
}
+ goto start;
}
-
+ }
+ else if (chunk->remain >= 2 && *p == '/' && p[1] == '*') {
+ /* start of nested comment */
+ comments_nested ++;
+ ucl_chunk_skipc (chunk, p);
ucl_chunk_skipc (chunk, p);
}
- if (comments_nested != 0) {
- ucl_set_err (parser, UCL_ENESTED,
- "unfinished multiline comment", &parser->err);
- return false;
+ else {
+ /* anything else */
+ ucl_chunk_skipc (chunk, p);
}
}
+ if (comments_nested != 0) {
+ ucl_set_err (parser, UCL_ENESTED,
+ "unfinished multiline comment", &parser->err);
+ return false;
+ }
}
if (beg && p > beg && (parser->flags & UCL_PARSER_SAVE_COMMENTS)) {
@@ -345,8 +347,9 @@ ucl_check_variable_safe (struct ucl_parser *parser, const char *ptr, size_t rema
/* Call generic handler */
if (parser->var_handler (ptr, remain, &dst, &dstlen, &need_free,
parser->var_data)) {
- *out_len = dstlen;
*found = true;
+ *out_len = dstlen;
+
if (need_free) {
free (dst);
}
@@ -395,6 +398,9 @@ ucl_check_variable (struct ucl_parser *parser, const char *ptr,
}
p ++;
}
+ if(p == end) {
+ (*out_len) ++;
+ }
}
else if (*ptr != '$') {
/* Not count escaped dollar sign */
@@ -418,13 +424,14 @@ ucl_check_variable (struct ucl_parser *parser, const char *ptr,
* Expand a single variable
* @param parser
* @param ptr
- * @param remain
+ * @param in_len
* @param dest
+ * @param out_len
* @return
*/
static const char *
ucl_expand_single_variable (struct ucl_parser *parser, const char *ptr,
- size_t remain, unsigned char **dest)
+ size_t in_len, unsigned char **dest, size_t out_len)
{
unsigned char *d = *dest, *dst;
const char *p = ptr + 1, *ret;
@@ -435,7 +442,8 @@ ucl_expand_single_variable (struct ucl_parser *parser, const char *ptr,
bool strict = false;
ret = ptr + 1;
- remain --;
+ /* For the $ sign */
+ in_len --;
if (*p == '$') {
*d++ = *p++;
@@ -444,39 +452,53 @@ ucl_expand_single_variable (struct ucl_parser *parser, const char *ptr,
}
else if (*p == '{') {
p ++;
+ in_len --;
strict = true;
ret += 2;
- remain -= 2;
}
LL_FOREACH (parser->variables, var) {
- if (remain >= var->var_len) {
+ if (out_len >= var->value_len && in_len >= (var->var_len + (strict ? 1 : 0))) {
if (memcmp (p, var->var, var->var_len) == 0) {
- memcpy (d, var->value, var->value_len);
- ret += var->var_len;
- d += var->value_len;
- found = true;
- break;
+ if (!strict || p[var->var_len] == '}') {
+ memcpy (d, var->value, var->value_len);
+ ret += var->var_len;
+ d += var->value_len;
+ found = true;
+ break;
+ }
}
}
}
+
if (!found) {
if (strict && parser->var_handler != NULL) {
- if (parser->var_handler (p, remain, &dst, &dstlen, &need_free,
+ dstlen = out_len;
+
+ if (parser->var_handler (p, in_len, &dst, &dstlen, &need_free,
parser->var_data)) {
- memcpy (d, dst, dstlen);
- ret += remain;
- d += dstlen;
- found = true;
- if (need_free) {
- free (dst);
+ if (dstlen > out_len) {
+ /* We do not have enough space! */
+ if (need_free) {
+ free (dst);
+ }
+ }
+ else {
+ memcpy(d, dst, dstlen);
+ ret += in_len;
+ d += dstlen;
+ found = true;
+
+ if (need_free) {
+ free(dst);
+ }
}
}
}
- /* Leave variable as is */
+ /* Leave variable as is, in this case we use dest */
if (!found) {
- if (strict) {
+ if (strict && out_len >= 2) {
/* Copy '${' */
memcpy (d, ptr, 2);
d += 2;
@@ -506,7 +528,7 @@ ucl_expand_variable (struct ucl_parser *parser, unsigned char **dst,
const char *src, size_t in_len)
{
const char *p, *end = src + in_len;
- unsigned char *d;
+ unsigned char *d, *d_end;
size_t out_len = 0;
bool vars_found = false;
@@ -517,7 +539,7 @@ ucl_expand_variable (struct ucl_parser *parser, unsigned char **dst,
p = src;
while (p != end) {
- if (*p == '$') {
+ if (*p == '$' && p + 1 != end) {
p = ucl_check_variable (parser, p + 1, end - p - 1, &out_len, &vars_found);
}
else {
@@ -538,10 +560,11 @@ ucl_expand_variable (struct ucl_parser *parser, unsigned char **dst,
}
d = *dst;
+ d_end = d + out_len;
p = src;
- while (p != end) {
- if (*p == '$') {
- p = ucl_expand_single_variable (parser, p, end - p, &d);
+ while (p != end && d != d_end) {
+ if (*p == '$' && p + 1 != end) {
+ p = ucl_expand_single_variable (parser, p, end - p, &d, d_end - d);
}
else {
*d++ = *p++;
@@ -686,6 +709,8 @@ ucl_parser_add_container (ucl_object_t *obj, struct ucl_parser *parser,
ucl_object_unref (obj);
}
+ UCL_FREE(sizeof (struct ucl_stack), st);
+
return NULL;
}
@@ -722,13 +747,13 @@ ucl_maybe_parse_number (ucl_object_t *obj,
const char *p = start, *c = start;
char *endptr;
bool got_dot = false, got_exp = false, need_double = false,
- is_time = false, valid_start = false, is_hex = false,
- is_neg = false;
+ is_time = false, valid_start = false, is_hex = false;
+ int is_neg = 0;
double dv = 0;
int64_t lv = 0;
if (*p == '-') {
- is_neg = true;
+ is_neg = 1;
c ++;
p ++;
}
@@ -744,6 +769,7 @@ ucl_maybe_parse_number (ucl_object_t *obj,
is_hex = true;
allow_double = false;
c = p + 1;
+ p ++;
}
else if (allow_double) {
if (p == c) {
@@ -792,26 +818,46 @@ ucl_maybe_parse_number (ucl_object_t *obj,
break;
}
}
+ else if (!allow_double && *p == '.') {
+ /* Unexpected dot */
+ *pos = start;
+ return EINVAL;
+ }
else {
break;
}
}
- if (!valid_start) {
+ if (!valid_start || p == c) {
*pos = start;
return EINVAL;
}
+ char numbuf[128];
+
+ if ((size_t)(p - c + 1) >= sizeof(numbuf)) {
+ *pos = start;
+ return EINVAL;
+ }
+
+ if (is_neg) {
+ numbuf[0] = '-';
+ ucl_strlcpy (&numbuf[1], c, p - c + 1);
+ }
+ else {
+ ucl_strlcpy (numbuf, c, p - c + 1);
+ }
+
errno = 0;
if (need_double) {
- dv = strtod (c, &endptr);
+ dv = strtod (numbuf, &endptr);
}
else {
if (is_hex) {
- lv = strtoimax (c, &endptr, 16);
+ lv = strtoimax (numbuf, &endptr, 16);
}
else {
- lv = strtoimax (c, &endptr, 10);
+ lv = strtoimax (numbuf, &endptr, 10);
}
}
if (errno == ERANGE) {
@@ -819,7 +865,15 @@ ucl_maybe_parse_number (ucl_object_t *obj,
return ERANGE;
}
- /* Now check endptr */
+ /* Now check endptr and move it from numbuf to the real ending */
+ if (endptr != NULL) {
+ long shift = endptr - numbuf - is_neg;
+ endptr = (char *)c + shift;
+ }
+ if (endptr >= end) {
+ p = end;
+ goto set_obj;
+ }
if (endptr == NULL || ucl_lex_is_atom_end (*endptr) || *endptr == '\0') {
p = endptr;
goto set_obj;
@@ -849,6 +903,10 @@ ucl_maybe_parse_number (ucl_object_t *obj,
dv *= ucl_lex_num_multiplier (*p, false);
}
p += 2;
+ if (end - p > 0 && !ucl_lex_is_atom_end (*p)) {
+ *pos = start;
+ return EINVAL;
+ }
goto set_obj;
}
else if (number_bytes || (p[1] == 'b' || p[1] == 'B')) {
@@ -859,6 +917,10 @@ ucl_maybe_parse_number (ucl_object_t *obj,
}
lv *= ucl_lex_num_multiplier (*p, true);
p += 2;
+ if (end - p > 0 && !ucl_lex_is_atom_end (*p)) {
+ *pos = start;
+ return EINVAL;
+ }
goto set_obj;
}
else if (ucl_lex_is_atom_end (p[1])) {
@@ -883,6 +945,10 @@ ucl_maybe_parse_number (ucl_object_t *obj,
is_time = true;
dv *= 60.;
p += 3;
+ if (end - p > 0 && !ucl_lex_is_atom_end (*p)) {
+ *pos = start;
+ return EINVAL;
+ }
goto set_obj;
}
}
@@ -895,6 +961,10 @@ ucl_maybe_parse_number (ucl_object_t *obj,
lv *= ucl_lex_num_multiplier (*p, number_bytes);
}
p ++;
+ if (end - p > 0 && !ucl_lex_is_atom_end (*p)) {
+ *pos = start;
+ return EINVAL;
+ }
goto set_obj;
}
break;
@@ -943,7 +1013,7 @@ ucl_maybe_parse_number (ucl_object_t *obj,
}
else if (endptr == end) {
/* Just a number at the end of chunk */
- p = endptr;
+ p = end;
goto set_obj;
}
@@ -959,11 +1029,11 @@ set_obj:
else {
obj->type = UCL_TIME;
}
- obj->value.dv = is_neg ? (-dv) : dv;
+ obj->value.dv = dv;
}
else {
obj->type = UCL_INT;
- obj->value.iv = is_neg ? (-lv) : lv;
+ obj->value.iv = lv;
}
}
*pos = p;
@@ -1037,13 +1107,13 @@ ucl_lex_json_string (struct ucl_parser *parser,
}
else if (c == '\\') {
ucl_chunk_skipc (chunk, p);
- c = *p;
if (p >= chunk->end) {
ucl_set_err (parser, UCL_ESYNTAX, "unfinished escape character",
&parser->err);
return false;
}
- else if (ucl_test_character (c, UCL_CHARACTER_ESCAPE)) {
+ c = *p;
+ if (ucl_test_character (c, UCL_CHARACTER_ESCAPE)) {
if (c == 'u') {
ucl_chunk_skipc (chunk, p);
for (i = 0; i < 4 && p < chunk->end; i ++) {
@@ -1289,24 +1359,20 @@ ucl_parser_process_object_element (struct ucl_parser *parser, ucl_object_t *nobj
*/
static bool
ucl_parse_key (struct ucl_parser *parser, struct ucl_chunk *chunk,
- bool *next_key, bool *end_of_object)
+ bool *next_key, bool *end_of_object, bool *got_content)
{
const unsigned char *p, *c = NULL, *end, *t;
const char *key = NULL;
bool got_quote = false, got_eq = false, got_semicolon = false,
need_unescape = false, ucl_escape = false, var_expand = false,
- got_content = false, got_sep = false;
+ got_sep = false;
ucl_object_t *nobj;
ssize_t keylen;
p = chunk->pos;
- if (*p == '.') {
- /* It is macro actually */
- if (!(parser->flags & UCL_PARSER_DISABLE_MACRO)) {
- ucl_chunk_skipc (chunk, p);
- }
-
+ if (*p == '.' && !(parser->flags & UCL_PARSER_DISABLE_MACRO)) {
+ ucl_chunk_skipc (chunk, p);
parser->prev_state = parser->state;
parser->state = UCL_STATE_MACRO_NAME;
*end_of_object = false;
@@ -1330,13 +1396,13 @@ ucl_parse_key (struct ucl_parser *parser, struct ucl_chunk *chunk,
/* The first symbol */
c = p;
ucl_chunk_skipc (chunk, p);
- got_content = true;
+ *got_content = true;
}
else if (*p == '"') {
/* JSON style key */
c = p + 1;
got_quote = true;
- got_content = true;
+ *got_content = true;
ucl_chunk_skipc (chunk, p);
}
else if (*p == '}') {
@@ -1344,7 +1410,7 @@ ucl_parse_key (struct ucl_parser *parser, struct ucl_chunk *chunk,
*end_of_object = true;
return true;
}
- else if (*p == '.') {
+ else if (*p == '.' && !(parser->flags & UCL_PARSER_DISABLE_MACRO)) {
ucl_chunk_skipc (chunk, p);
parser->prev_state = parser->state;
parser->state = UCL_STATE_MACRO_NAME;
@@ -1361,7 +1427,7 @@ ucl_parse_key (struct ucl_parser *parser, struct ucl_chunk *chunk,
/* Parse the body of a key */
if (!got_quote) {
if (ucl_test_character (*p, UCL_CHARACTER_KEY)) {
- got_content = true;
+ *got_content = true;
ucl_chunk_skipc (chunk, p);
}
else if (ucl_test_character (*p, UCL_CHARACTER_KEY_SEP)) {
@@ -1387,11 +1453,11 @@ ucl_parse_key (struct ucl_parser *parser, struct ucl_chunk *chunk,
}
}
- if (p >= chunk->end && got_content) {
+ if (p >= chunk->end && *got_content) {
ucl_set_err (parser, UCL_ESYNTAX, "unfinished key", &parser->err);
return false;
}
- else if (!got_content) {
+ else if (!*got_content) {
return true;
}
*end_of_object = false;
@@ -1752,6 +1818,9 @@ ucl_parse_value (struct ucl_parser *parser, struct ucl_chunk *chunk)
case '{':
obj = ucl_parser_get_container (parser);
if (obj == NULL) {
+ parser->state = UCL_STATE_ERROR;
+ ucl_set_err(parser, UCL_ESYNTAX, "object value must be a part of an object",
+ &parser->err);
return false;
}
/* We have a new object */
@@ -1773,6 +1842,9 @@ ucl_parse_value (struct ucl_parser *parser, struct ucl_chunk *chunk)
case '[':
obj = ucl_parser_get_container (parser);
if (obj == NULL) {
+ parser->state = UCL_STATE_ERROR;
+ ucl_set_err(parser, UCL_ESYNTAX, "array value must be a part of an object",
+ &parser->err);
return false;
}
/* We have a new array */
@@ -1804,6 +1876,12 @@ ucl_parse_value (struct ucl_parser *parser, struct ucl_chunk *chunk)
break;
case '<':
obj = ucl_parser_get_container (parser);
+ if (obj == NULL) {
+ parser->state = UCL_STATE_ERROR;
+ ucl_set_err(parser, UCL_ESYNTAX, "multiline value must be a part of an object",
+ &parser->err);
+ return false;
+ }
/* We have something like multiline value, which must be <<[A-Z]+\n */
if (chunk->end - p > 3) {
if (memcmp (p, "<<", 2) == 0) {
@@ -1812,6 +1890,11 @@ ucl_parse_value (struct ucl_parser *parser, struct ucl_chunk *chunk)
while (p < chunk->end && *p >= 'A' && *p <= 'Z') {
p ++;
}
+ if(p == chunk->end) {
+ ucl_set_err (parser, UCL_ESYNTAX,
+ "unterminated multiline value", &parser->err);
+ return false;
+ }
if (*p =='\n') {
/* Set chunk positions and start multiline parsing */
chunk->remain -= p - c + 1;
@@ -1850,6 +1933,13 @@ parse_string:
obj = ucl_parser_get_container (parser);
}
+ if (obj == NULL) {
+ parser->state = UCL_STATE_ERROR;
+ ucl_set_err(parser, UCL_ESYNTAX, "value must be a part of an object",
+ &parser->err);
+ return false;
+ }
+
/* Parse atom */
if (ucl_test_character (*p, UCL_CHARACTER_VALUE_DIGIT_START)) {
if (!ucl_lex_number (parser, chunk, obj)) {
@@ -2339,7 +2429,7 @@ ucl_state_machine (struct ucl_parser *parser)
unsigned char *macro_escaped;
size_t macro_len = 0;
struct ucl_macro *macro = NULL;
- bool next_key = false, end_of_object = false, ret;
+ bool next_key = false, end_of_object = false, got_content = false, ret;
if (parser->top_obj == NULL) {
parser->state = UCL_STATE_INIT;
@@ -2428,7 +2518,10 @@ ucl_state_machine (struct ucl_parser *parser)
parser->state = UCL_STATE_ERROR;
return false;
}
- if (!ucl_parse_key (parser, chunk, &next_key, &end_of_object)) {
+
+ got_content = false;
+
+ if (!ucl_parse_key (parser, chunk, &next_key, &end_of_object, &got_content)) {
parser->prev_state = parser->state;
parser->state = UCL_STATE_ERROR;
return false;
@@ -2451,7 +2544,8 @@ ucl_state_machine (struct ucl_parser *parser)
return false;
}
}
- else {
+ else if (got_content) {
+ /* Do not switch state if we have not read any content */
parser->state = UCL_STATE_VALUE;
}
}
@@ -2617,6 +2711,9 @@ ucl_state_machine (struct ucl_parser *parser)
return false;
}
break;
+ case UCL_STATE_ERROR:
+ /* Already in the error state */
+ return false;
default:
ucl_set_err (parser, UCL_EINTERNAL,
"internal error: parser is in an unknown state", &parser->err);
@@ -2889,7 +2986,9 @@ ucl_parser_add_chunk_full (struct ucl_parser *parser, const unsigned char *data,
if (!special_handler->handler (parser, data, len, &ndata, &nlen,
special_handler->user_data)) {
+ UCL_FREE(sizeof (struct ucl_chunk), chunk);
ucl_create_err (&parser->err, "call for external handler failed");
+
return false;
}
@@ -2909,7 +3008,7 @@ ucl_parser_add_chunk_full (struct ucl_parser *parser, const unsigned char *data,
if (parse_type == UCL_PARSE_AUTO && len > 0) {
/* We need to detect parse type by the first symbol */
- if ((*data & 0x80) == 0x80 && (*data >= 0xdc && *data <= 0xdf)) {
+ if ((*data & 0x80) == 0x80) {
parse_type = UCL_PARSE_MSGPACK;
}
else if (*data == '(') {
diff --git a/contrib/libucl/src/ucl_schema.c b/contrib/libucl/src/ucl_schema.c
index 68f01187e375..f4ec0ed3284a 100644
--- a/contrib/libucl/src/ucl_schema.c
+++ b/contrib/libucl/src/ucl_schema.c
@@ -39,6 +39,7 @@
#ifdef HAVE_MATH_H
#include <math.h>
#endif
+#include <inttypes.h>
static bool ucl_schema_validate (const ucl_object_t *schema,
const ucl_object_t *obj, bool try_array,
diff --git a/contrib/libucl/src/ucl_util.c b/contrib/libucl/src/ucl_util.c
index b00a34787e5a..8f97c20db503 100644
--- a/contrib/libucl/src/ucl_util.c
+++ b/contrib/libucl/src/ucl_util.c
@@ -67,7 +67,7 @@ typedef kvec_t(ucl_object_t *) ucl_array_t;
#include <fetch.h>
#endif
-#if defined(_MSC_VER)
+#if defined(_WIN32)
#include <windows.h>
#include <io.h>
#include <direct.h>
@@ -889,44 +889,49 @@ ucl_fetch_file (const unsigned char *filename, unsigned char **buf, size_t *bufl
{
int fd;
struct stat st;
+ if ((fd = open (filename, O_RDONLY)) == -1) {
+ ucl_create_err (err, "cannot open file %s: %s",
+ filename, strerror (errno));
+ return false;
+ }
- if (stat (filename, &st) == -1) {
+ if (fstat (fd, &st) == -1) {
if (must_exist || errno == EPERM) {
ucl_create_err (err, "cannot stat file %s: %s",
filename, strerror (errno));
}
+ close (fd);
+
return false;
}
if (!S_ISREG (st.st_mode)) {
if (must_exist) {
ucl_create_err (err, "file %s is not a regular file", filename);
}
+ close (fd);
return false;
}
+
if (st.st_size == 0) {
/* Do not map empty files */
*buf = NULL;
*buflen = 0;
}
else {
- if ((fd = open (filename, O_RDONLY)) == -1) {
- ucl_create_err (err, "cannot open file %s: %s",
- filename, strerror (errno));
- return false;
- }
- if ((*buf = ucl_mmap (NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
- close (fd);
- ucl_create_err (err, "cannot mmap file %s: %s",
- filename, strerror (errno));
+ if ((*buf = ucl_mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) {
+ close(fd);
+ ucl_create_err(err, "cannot mmap file %s: %s",
+ filename, strerror(errno));
*buf = NULL;
return false;
}
*buflen = st.st_size;
- close (fd);
}
+ close (fd);
+
return true;
}
@@ -1017,6 +1022,9 @@ ucl_include_url (const unsigned char *data, size_t len,
snprintf (urlbuf, sizeof (urlbuf), "%.*s", (int)len, data);
if (!ucl_fetch_url (urlbuf, &buf, &buflen, &parser->err, params->must_exist)) {
+ if (!params->must_exist) {
+ ucl_parser_clear_error (parser);
+ }
return !params->must_exist;
}
@@ -1092,6 +1100,11 @@ ucl_include_file_single (const unsigned char *data, size_t len,
ucl_hash_t *container = NULL;
struct ucl_stack *st = NULL;
+ if (parser->state == UCL_STATE_ERROR) {
+ /* Return immediately if we are in the error state... */
+ return false;
+ }
+
snprintf (filebuf, sizeof (filebuf), "%.*s", (int)len, data);
if (ucl_realpath (filebuf, realbuf) == NULL) {
if (params->soft_fail) {
@@ -1128,6 +1141,8 @@ ucl_include_file_single (const unsigned char *data, size_t len,
return false;
}
+ ucl_parser_clear_error (parser);
+
return true;
}
@@ -1138,6 +1153,10 @@ ucl_include_file_single (const unsigned char *data, size_t len,
/* We need to check signature first */
snprintf (filebuf, sizeof (filebuf), "%s.sig", realbuf);
if (!ucl_fetch_file (filebuf, &sigbuf, &siglen, &parser->err, true)) {
+ if (buf) {
+ ucl_munmap (buf, buflen);
+ }
+
return false;
}
if (!ucl_sig_check (buf, buflen, sigbuf, siglen, parser)) {
@@ -1147,8 +1166,13 @@ ucl_include_file_single (const unsigned char *data, size_t len,
if (sigbuf) {
ucl_munmap (sigbuf, siglen);
}
+ if (buf) {
+ ucl_munmap (buf, buflen);
+ }
+
return false;
}
+
if (sigbuf) {
ucl_munmap (sigbuf, siglen);
}
@@ -1257,6 +1281,8 @@ ucl_include_file_single (const unsigned char *data, size_t len,
ucl_munmap (buf, buflen);
}
+ ucl_object_unref (new_obj);
+
return false;
}
nest_obj->prev = nest_obj;
@@ -1576,11 +1602,6 @@ ucl_include_common (const unsigned char *data, size_t len,
else if (param->type == UCL_INT) {
if (strncmp (param->key, "priority", param->keylen) == 0) {
params.priority = ucl_object_toint (param);
- if (params.priority > UCL_PRIORITY_MAX) {
- ucl_create_err (&parser->err, "Invalid priority value in macro: %d",
- params.priority);
- return false;
- }
}
}
}
@@ -1719,9 +1740,8 @@ ucl_priority_handler (const unsigned char *data, size_t len,
if (len > 0) {
value = malloc(len + 1);
ucl_strlcpy(value, (const char *)data, len + 1);
- errno = 0;
- priority = strtoul(value, &leftover, 10);
- if (errno != 0 || *leftover != '\0' || priority > UCL_PRIORITY_MAX) {
+ priority = strtol(value, &leftover, 10);
+ if (*leftover != '\0') {
ucl_create_err (&parser->err, "Invalid priority value in macro: %s",
value);
free(value);
@@ -1842,6 +1862,10 @@ ucl_load_handler (const unsigned char *data, size_t len,
!try_load)) {
free (load_file);
+ if (try_load) {
+ ucl_parser_clear_error (parser);
+ }
+
return (try_load || false);
}
@@ -1919,7 +1943,7 @@ ucl_inherit_handler (const unsigned char *data, size_t len,
/* Some sanity checks */
if (parent == NULL || ucl_object_type (parent) != UCL_OBJECT) {
- ucl_create_err (&parser->err, "Unable to find inherited object %*.s",
+ ucl_create_err (&parser->err, "Unable to find inherited object %.*s",
(int)len, data);
return false;
}
@@ -2177,7 +2201,7 @@ ucl_strnstr (const char *s, const char *find, int len)
mlen = strlen (find);
do {
do {
- if ((sc = *s++) == 0 || len-- == 0)
+ if ((sc = *s++) == 0 || len-- < mlen)
return (NULL);
} while (sc != c);
} while (strncmp (s, find, mlen) != 0);
@@ -2596,6 +2620,7 @@ ucl_object_merge (ucl_object_t *top, ucl_object_t *elt, bool copy)
if (!ucl_object_merge (found, cp, copy)) {
return false;
}
+ ucl_object_unref (cp);
}
else {
ucl_hash_replace (top->value.ov, found, cp);
@@ -2627,6 +2652,7 @@ ucl_object_merge (ucl_object_t *top, ucl_object_t *elt, bool copy)
if (!ucl_object_merge (found, cp, copy)) {
return false;
}
+ ucl_object_unref (cp);
}
else {
ucl_hash_replace (top->value.ov, found, cp);
@@ -3068,13 +3094,13 @@ ucl_object_type (const ucl_object_t *obj)
ucl_object_t*
ucl_object_fromstring (const char *str)
{
- return ucl_object_fromstring_common (str, 0, UCL_STRING_ESCAPE);
+ return ucl_object_fromstring_common (str, 0, UCL_STRING_RAW);
}
ucl_object_t *
ucl_object_fromlstring (const char *str, size_t len)
{
- return ucl_object_fromstring_common (str, len, UCL_STRING_ESCAPE);
+ return ucl_object_fromstring_common (str, len, UCL_STRING_RAW);
}
ucl_object_t *
@@ -3594,9 +3620,11 @@ ucl_object_copy_internal (const ucl_object_t *other, bool allow_array)
/* deep copy of values stored */
if (other->trash_stack[UCL_TRASH_KEY] != NULL) {
- new->trash_stack[UCL_TRASH_KEY] =
- strdup (other->trash_stack[UCL_TRASH_KEY]);
+ new->trash_stack[UCL_TRASH_KEY] = NULL;
if (other->key == (const char *)other->trash_stack[UCL_TRASH_KEY]) {
+ new->trash_stack[UCL_TRASH_KEY] = malloc(other->keylen + 1);
+ memcpy(new->trash_stack[UCL_TRASH_KEY], other->trash_stack[UCL_TRASH_KEY], other->keylen);
+ new->trash_stack[UCL_TRASH_KEY][other->keylen] = '\0';
new->key = new->trash_stack[UCL_TRASH_KEY];
}
}
@@ -3666,13 +3694,6 @@ ucl_object_compare (const ucl_object_t *o1, const ucl_object_t *o2)
ucl_object_iter_t iter = NULL;
int ret = 0;
- // Must check for NULL or code will segfault
- if ((o1 == NULL) || (o2 == NULL))
- {
- // The only way this could be true is of both are NULL
- return (o1 == NULL) && (o2 == NULL);
- }
-
if (o1->type != o2->type) {
return (o1->type) - (o2->type);
}
diff --git a/contrib/libucl/stamp-h.in b/contrib/libucl/stamp-h.in
deleted file mode 100644
index 9788f70238c9..000000000000
--- a/contrib/libucl/stamp-h.in
+++ /dev/null
@@ -1 +0,0 @@
-timestamp
diff --git a/contrib/libucl/tests/.gitignore b/contrib/libucl/tests/.gitignore
new file mode 100644
index 000000000000..464482434f22
--- /dev/null
+++ b/contrib/libucl/tests/.gitignore
@@ -0,0 +1,10 @@
+*.log
+*.trs
+*.plist
+
+test_basic
+test_generate
+test_msgpack
+test_schema
+test_speed
+test_streamline
diff --git a/contrib/libucl/tests/Makefile.am b/contrib/libucl/tests/Makefile.am
deleted file mode 100644
index 055eb8bd85b0..000000000000
--- a/contrib/libucl/tests/Makefile.am
+++ /dev/null
@@ -1,45 +0,0 @@
-EXTRA_DIST = $(TESTS) basic schema generate.res \
- streamline.res rcl_test.json.xz
-
-TESTS = basic.test \
- generate.test \
- schema.test \
- msgpack.test \
- speed.test \
- msgpack.test
-TESTS_ENVIRONMENT = $(SH) \
- TEST_DIR=$(top_srcdir)/tests \
- TEST_OUT_DIR=$(top_builddir)/tests \
- TEST_BINARY_DIR=$(top_builddir)/tests
-
-common_test_cflags = -I$(top_srcdir)/include \
- -I$(top_srcdir)/src \
- -I$(top_srcdir)/uthash
-common_test_ldadd = $(top_builddir)/src/libucl.la
-
-test_basic_SOURCES = test_basic.c
-test_basic_LDADD = $(common_test_ldadd)
-test_basic_CFLAGS = $(common_test_cflags)
-
-test_speed_SOURCES = test_speed.c
-test_speed_LDADD = $(common_test_ldadd)
-test_speed_CFLAGS = $(common_test_cflags)
-
-test_generate_SOURCES = test_generate.c
-test_generate_LDADD = $(common_test_ldadd)
-test_generate_CFLAGS = $(common_test_cflags)
-
-test_schema_SOURCES = test_schema.c
-test_schema_LDADD = $(common_test_ldadd)
-test_schema_CFLAGS = $(common_test_cflags)
-
-test_streamline_SOURCES = test_streamline.c
-test_streamline_LDADD = $(common_test_ldadd)
-test_streamline_CFLAGS = $(common_test_cflags)
-
-test_msgpack_SOURCES = test_msgpack.c
-test_msgpack_LDADD = $(common_test_ldadd)
-test_msgpack_CFLAGS = $(common_test_cflags)
-
-check_PROGRAMS = test_basic test_speed test_generate test_schema test_streamline \
- test_msgpack \ No newline at end of file
diff --git a/contrib/libucl/tests/schema/definitions.json b/contrib/libucl/tests/schema/definitions.json
deleted file mode 100644
index 1ab9b2163c44..000000000000
--- a/contrib/libucl/tests/schema/definitions.json
+++ /dev/null
@@ -1,32 +0,0 @@
-[
- {
- "description": "valid definition",
- "schema": {"$ref": "http://highsecure.ru/ucl-schema/schema#"},
- "tests": [
- {
- "description": "valid definition schema",
- "data": {
- "definitions": {
- "foo": {"type": "integer"}
- }
- },
- "valid": true
- }
- ]
- },
- {
- "description": "invalid definition",
- "schema": {"$ref": "http://highsecure.ru/ucl-schema/schema#"},
- "tests": [
- {
- "description": "invalid definition schema",
- "data": {
- "definitions": {
- "foo": {"type": 1}
- }
- },
- "valid": false
- }
- ]
- }
-]
diff --git a/contrib/libucl/tests/schema/ref.json b/contrib/libucl/tests/schema/ref.json
index 1767769cd845..d8214bc2b30c 100644
--- a/contrib/libucl/tests/schema/ref.json
+++ b/contrib/libucl/tests/schema/ref.json
@@ -124,21 +124,5 @@
"valid": false
}
]
- },
- {
- "description": "remote ref, containing refs itself",
- "schema": {"$ref": "http://highsecure.ru/ucl-schema/schema#"},
- "tests": [
- {
- "description": "remote ref valid",
- "data": {"minLength": 1},
- "valid": true
- },
- {
- "description": "remote ref invalid",
- "data": {"minLength": -1},
- "valid": false
- }
- ]
}
]
diff --git a/contrib/libucl/tests/schema/refRemote.json b/contrib/libucl/tests/schema/refRemote.json
deleted file mode 100644
index 067c666b0ec8..000000000000
--- a/contrib/libucl/tests/schema/refRemote.json
+++ /dev/null
@@ -1,76 +0,0 @@
-[
- {
- "description": "remote ref",
- "schema": {"$ref": "http://highsecure.ru/ucl-schema/remotes/integer.json"},
- "tests": [
- {
- "description": "remote ref valid",
- "data": 1,
- "valid": true
- },
- {
- "description": "remote ref invalid",
- "data": "a",
- "valid": false
- }
- ]
- },
- {
- "description": "fragment within remote ref",
- "schema": {"$ref": "http://highsecure.ru/ucl-schema/remotes/subSchemas.json#/integer"},
- "tests": [
- {
- "description": "remote fragment valid",
- "data": 1,
- "valid": true
- },
- {
- "description": "remote fragment invalid",
- "data": "a",
- "valid": false
- }
- ]
- },
- {
- "description": "ref within remote ref",
- "schema": {
- "$ref": "http://highsecure.ru/ucl-schema/remotes/subSchemas.json#/refToInteger"
- },
- "tests": [
- {
- "description": "ref within ref valid",
- "data": 1,
- "valid": true
- },
- {
- "description": "ref within ref invalid",
- "data": "a",
- "valid": false
- }
- ]
- }
-/*
- {
- "description": "change resolution scope",
- "schema": {
- "id": "http://highsecure.ru/ucl-schema/remotes/",
- "items": {
- "id": "folder/",
- "items": {"$ref": "folderInteger.json"}
- }
- },
- "tests": [
- {
- "description": "changed scope ref valid",
- "data": [[1]],
- "valid": true
- },
- {
- "description": "changed scope ref invalid",
- "data": [["a"]],
- "valid": false
- }
- ]
- }
-*/
-]
diff --git a/contrib/libucl/tests/test_speed.c b/contrib/libucl/tests/test_speed.c
index 56f2e5abc6c7..51476c94940b 100644
--- a/contrib/libucl/tests/test_speed.c
+++ b/contrib/libucl/tests/test_speed.c
@@ -44,7 +44,7 @@ get_ticks (void)
{
double res;
-#ifdef __APPLE__
+#if defined(__APPLE__) && defined(HAVE_MACH_MACH_TIME_H)
res = mach_absolute_time () / 1000000000.;
#else
struct timespec ts;
diff --git a/contrib/libucl/tests/test_streamline.c b/contrib/libucl/tests/test_streamline.c
index 4c56c4cdcffd..37fe14f9fb97 100644
--- a/contrib/libucl/tests/test_streamline.c
+++ b/contrib/libucl/tests/test_streamline.c
@@ -26,6 +26,10 @@
#include <assert.h>
#include "ucl.h"
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+
int
main (int argc, char **argv)
{
@@ -34,7 +38,28 @@ main (int argc, char **argv)
const char *fname_out = NULL;
struct ucl_emitter_context *ctx;
struct ucl_emitter_functions *f;
- int ret = 0;
+ int ret = 0, opt, json = 0, compact = 0, yaml = 0;
+
+ while ((opt = getopt(argc, argv, "jcy")) != -1) {
+ switch (opt) {
+ case 'j':
+ json = 1;
+ break;
+ case 'c':
+ compact = 1;
+ break;
+ case 'y':
+ yaml = 1;
+ break;
+ default: /* '?' */
+ fprintf (stderr, "Usage: %s [-jcy] [out]\n",
+ argv[0]);
+ exit (EXIT_FAILURE);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
switch (argc) {
case 2:
@@ -63,7 +88,21 @@ main (int argc, char **argv)
ucl_object_insert_key (obj, cur, "key3", 0, false);
f = ucl_object_emit_file_funcs (out);
- ctx = ucl_object_emit_streamline_new (obj, UCL_EMIT_CONFIG, f);
+
+ if (yaml) {
+ ctx = ucl_object_emit_streamline_new (obj, UCL_EMIT_YAML, f);
+ }
+ else if (json) {
+ if (compact) {
+ ctx = ucl_object_emit_streamline_new (obj, UCL_EMIT_JSON_COMPACT, f);
+ }
+ else {
+ ctx = ucl_object_emit_streamline_new (obj, UCL_EMIT_JSON, f);
+ }
+ }
+ else {
+ ctx = ucl_object_emit_streamline_new (obj, UCL_EMIT_CONFIG, f);
+ }
assert (ctx != NULL);
diff --git a/contrib/libucl/uthash/utlist.h b/contrib/libucl/uthash/utlist.h
index c82dd916e2ed..7cda1ca0ecac 100644
--- a/contrib/libucl/uthash/utlist.h
+++ b/contrib/libucl/uthash/utlist.h
@@ -1,5 +1,5 @@
/*
-Copyright (c) 2007-2013, Troy D. Hanson http://troydhanson.github.com/uthash/
+Copyright (c) 2007-2022, Troy D. Hanson https://troydhanson.github.io/uthash/
All rights reserved.
Redistribution and use in source and binary forms, with or without
@@ -24,11 +24,11 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#ifndef UTLIST_H
#define UTLIST_H
-#define UTLIST_VERSION 1.9.8
+#define UTLIST_VERSION 2.3.0
#include <assert.h>
-/*
+/*
* This file contains macros to manipulate singly and doubly-linked lists.
*
* 1. LL_ macros: singly-linked lists.
@@ -38,7 +38,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* To use singly-linked lists, your structure must have a "next" pointer.
* To use doubly-linked lists, your structure must "prev" and "next" pointers.
* Either way, the pointer to the head of the list must be initialized to NULL.
- *
+ *
* ----------------.EXAMPLE -------------------------
* struct item {
* int id;
@@ -61,41 +61,46 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/* These macros use decltype or the earlier __typeof GNU extension.
As decltype is only available in newer compilers (VS2010 or gcc 4.3+
- when compiling c++ code), this code uses whatever method is needed
+ when compiling c++ source) this code uses whatever method is needed
or, for VS2008 where neither is available, uses casting workarounds. */
-#ifdef _MSC_VER /* MS compiler */
+#if !defined(LDECLTYPE) && !defined(NO_DECLTYPE)
+#if defined(_MSC_VER) /* MS compiler */
#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */
#define LDECLTYPE(x) decltype(x)
-#else /* VS2008 or older (or VS2010 in C mode) */
+#else /* VS2008 or older (or VS2010 in C mode) */
#define NO_DECLTYPE
-#define LDECLTYPE(x) char*
#endif
-#elif defined(__ICCARM__)
+#elif defined(__MCST__) /* Elbrus C Compiler */
+#define LDECLTYPE(x) __typeof(x)
+#elif defined(__BORLANDC__) || defined(__ICCARM__) || defined(__LCC__) || defined(__WATCOMC__)
#define NO_DECLTYPE
-#define LDECLTYPE(x) char*
-#else /* GNU, Sun and other compilers */
+#else /* GNU, Sun and other compilers */
#define LDECLTYPE(x) __typeof(x)
#endif
+#endif
/* for VS2008 we use some workarounds to get around the lack of decltype,
* namely, we always reassign our tmp variable to the list head if we need
* to dereference its prev/next pointers, and save/restore the real head.*/
#ifdef NO_DECLTYPE
-#define _SV(elt,list) _tmp = (char*)(list); {char **_alias = (char**)&(list); *_alias = (elt); }
-#define _NEXT(elt,list,next) ((char*)((list)->next))
-#define _NEXTASGN(elt,list,to,next) { char **_alias = (char**)&((list)->next); *_alias=(char*)(to); }
-/* #define _PREV(elt,list,prev) ((char*)((list)->prev)) */
-#define _PREVASGN(elt,list,to,prev) { char **_alias = (char**)&((list)->prev); *_alias=(char*)(to); }
-#define _RS(list) { char **_alias = (char**)&(list); *_alias=_tmp; }
-#define _CASTASGN(a,b) { char **_alias = (char**)&(a); *_alias=(char*)(b); }
-#else
-#define _SV(elt,list)
-#define _NEXT(elt,list,next) ((elt)->next)
-#define _NEXTASGN(elt,list,to,next) ((elt)->next)=(to)
-/* #define _PREV(elt,list,prev) ((elt)->prev) */
-#define _PREVASGN(elt,list,to,prev) ((elt)->prev)=(to)
-#define _RS(list)
-#define _CASTASGN(a,b) (a)=(b)
+#define IF_NO_DECLTYPE(x) x
+#define LDECLTYPE(x) char*
+#define UTLIST_SV(elt,list) _tmp = (char*)(list); {char **_alias = (char**)&(list); *_alias = (elt); }
+#define UTLIST_NEXT(elt,list,next) ((char*)((list)->next))
+#define UTLIST_NEXTASGN(elt,list,to,next) { char **_alias = (char**)&((list)->next); *_alias=(char*)(to); }
+/* #define UTLIST_PREV(elt,list,prev) ((char*)((list)->prev)) */
+#define UTLIST_PREVASGN(elt,list,to,prev) { char **_alias = (char**)&((list)->prev); *_alias=(char*)(to); }
+#define UTLIST_RS(list) { char **_alias = (char**)&(list); *_alias=_tmp; }
+#define UTLIST_CASTASGN(a,b) { char **_alias = (char**)&(a); *_alias=(char*)(b); }
+#else
+#define IF_NO_DECLTYPE(x)
+#define UTLIST_SV(elt,list)
+#define UTLIST_NEXT(elt,list,next) ((elt)->next)
+#define UTLIST_NEXTASGN(elt,list,to,next) ((elt)->next)=(to)
+/* #define UTLIST_PREV(elt,list,prev) ((elt)->prev) */
+#define UTLIST_PREVASGN(elt,list,to,prev) ((elt)->prev)=(to)
+#define UTLIST_RS(list)
+#define UTLIST_CASTASGN(a,b) (a)=(b)
#endif
/******************************************************************************
@@ -111,13 +116,14 @@ do {
LDECLTYPE(list) _ls_q; \
LDECLTYPE(list) _ls_e; \
LDECLTYPE(list) _ls_tail; \
+ IF_NO_DECLTYPE(LDECLTYPE(list) _tmp;) \
int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \
if (list) { \
_ls_insize = 1; \
_ls_looping = 1; \
while (_ls_looping) { \
- _CASTASGN(_ls_p,list); \
- list = NULL; \
+ UTLIST_CASTASGN(_ls_p,list); \
+ (list) = NULL; \
_ls_tail = NULL; \
_ls_nmerges = 0; \
while (_ls_p) { \
@@ -126,35 +132,35 @@ do {
_ls_psize = 0; \
for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \
_ls_psize++; \
- _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list,next); _RS(list); \
+ UTLIST_SV(_ls_q,list); _ls_q = UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); \
if (!_ls_q) break; \
} \
_ls_qsize = _ls_insize; \
while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \
if (_ls_psize == 0) { \
- _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \
- _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \
+ _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \
+ UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \
} else if (_ls_qsize == 0 || !_ls_q) { \
- _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \
- _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \
+ _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \
+ UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \
} else if (cmp(_ls_p,_ls_q) <= 0) { \
- _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \
- _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \
+ _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \
+ UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \
} else { \
- _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \
- _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \
+ _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \
+ UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \
} \
if (_ls_tail) { \
- _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e,next); _RS(list); \
+ UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,_ls_e,next); UTLIST_RS(list); \
} else { \
- _CASTASGN(list,_ls_e); \
+ UTLIST_CASTASGN(list,_ls_e); \
} \
_ls_tail = _ls_e; \
} \
_ls_p = _ls_q; \
} \
if (_ls_tail) { \
- _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,NULL,next); _RS(list); \
+ UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,NULL,next); UTLIST_RS(list); \
} \
if (_ls_nmerges <= 1) { \
_ls_looping=0; \
@@ -174,13 +180,14 @@ do {
LDECLTYPE(list) _ls_q; \
LDECLTYPE(list) _ls_e; \
LDECLTYPE(list) _ls_tail; \
+ IF_NO_DECLTYPE(LDECLTYPE(list) _tmp;) \
int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \
if (list) { \
_ls_insize = 1; \
_ls_looping = 1; \
while (_ls_looping) { \
- _CASTASGN(_ls_p,list); \
- list = NULL; \
+ UTLIST_CASTASGN(_ls_p,list); \
+ (list) = NULL; \
_ls_tail = NULL; \
_ls_nmerges = 0; \
while (_ls_p) { \
@@ -189,36 +196,36 @@ do {
_ls_psize = 0; \
for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \
_ls_psize++; \
- _SV(_ls_q,list); _ls_q = _NEXT(_ls_q,list,next); _RS(list); \
+ UTLIST_SV(_ls_q,list); _ls_q = UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); \
if (!_ls_q) break; \
} \
_ls_qsize = _ls_insize; \
- while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \
+ while ((_ls_psize > 0) || ((_ls_qsize > 0) && _ls_q)) { \
if (_ls_psize == 0) { \
- _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \
- _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \
- } else if (_ls_qsize == 0 || !_ls_q) { \
- _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \
- _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \
+ _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \
+ UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \
+ } else if ((_ls_qsize == 0) || (!_ls_q)) { \
+ _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \
+ UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \
} else if (cmp(_ls_p,_ls_q) <= 0) { \
- _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \
- _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \
+ _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \
+ UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \
} else { \
- _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \
- _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \
+ _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \
+ UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \
} \
if (_ls_tail) { \
- _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e,next); _RS(list); \
+ UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,_ls_e,next); UTLIST_RS(list); \
} else { \
- _CASTASGN(list,_ls_e); \
+ UTLIST_CASTASGN(list,_ls_e); \
} \
- _SV(_ls_e,list); _PREVASGN(_ls_e,list,_ls_tail,prev); _RS(list); \
+ UTLIST_SV(_ls_e,list); UTLIST_PREVASGN(_ls_e,list,_ls_tail,prev); UTLIST_RS(list); \
_ls_tail = _ls_e; \
} \
_ls_p = _ls_q; \
} \
- _CASTASGN(list->prev, _ls_tail); \
- _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,NULL,next); _RS(list); \
+ UTLIST_CASTASGN((list)->prev, _ls_tail); \
+ UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,NULL,next); UTLIST_RS(list); \
if (_ls_nmerges <= 1) { \
_ls_looping=0; \
} \
@@ -243,9 +250,9 @@ do {
_ls_insize = 1; \
_ls_looping = 1; \
while (_ls_looping) { \
- _CASTASGN(_ls_p,list); \
- _CASTASGN(_ls_oldhead,list); \
- list = NULL; \
+ UTLIST_CASTASGN(_ls_p,list); \
+ UTLIST_CASTASGN(_ls_oldhead,list); \
+ (list) = NULL; \
_ls_tail = NULL; \
_ls_nmerges = 0; \
while (_ls_p) { \
@@ -254,47 +261,47 @@ do {
_ls_psize = 0; \
for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \
_ls_psize++; \
- _SV(_ls_q,list); \
- if (_NEXT(_ls_q,list,next) == _ls_oldhead) { \
+ UTLIST_SV(_ls_q,list); \
+ if (UTLIST_NEXT(_ls_q,list,next) == _ls_oldhead) { \
_ls_q = NULL; \
} else { \
- _ls_q = _NEXT(_ls_q,list,next); \
+ _ls_q = UTLIST_NEXT(_ls_q,list,next); \
} \
- _RS(list); \
+ UTLIST_RS(list); \
if (!_ls_q) break; \
} \
_ls_qsize = _ls_insize; \
while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \
if (_ls_psize == 0) { \
- _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \
- _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \
+ _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \
+ UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \
if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \
} else if (_ls_qsize == 0 || !_ls_q) { \
- _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \
- _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \
+ _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \
+ UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \
if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \
} else if (cmp(_ls_p,_ls_q) <= 0) { \
- _ls_e = _ls_p; _SV(_ls_p,list); _ls_p = \
- _NEXT(_ls_p,list,next); _RS(list); _ls_psize--; \
+ _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \
+ UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \
if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \
} else { \
- _ls_e = _ls_q; _SV(_ls_q,list); _ls_q = \
- _NEXT(_ls_q,list,next); _RS(list); _ls_qsize--; \
+ _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \
+ UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \
if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \
} \
if (_ls_tail) { \
- _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_ls_e,next); _RS(list); \
+ UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,_ls_e,next); UTLIST_RS(list); \
} else { \
- _CASTASGN(list,_ls_e); \
+ UTLIST_CASTASGN(list,_ls_e); \
} \
- _SV(_ls_e,list); _PREVASGN(_ls_e,list,_ls_tail,prev); _RS(list); \
+ UTLIST_SV(_ls_e,list); UTLIST_PREVASGN(_ls_e,list,_ls_tail,prev); UTLIST_RS(list); \
_ls_tail = _ls_e; \
} \
_ls_p = _ls_q; \
} \
- _CASTASGN(list->prev,_ls_tail); \
- _CASTASGN(_tmp,list); \
- _SV(_ls_tail,list); _NEXTASGN(_ls_tail,list,_tmp,next); _RS(list); \
+ UTLIST_CASTASGN((list)->prev,_ls_tail); \
+ UTLIST_CASTASGN(_tmp,list); \
+ UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,_tmp,next); UTLIST_RS(list); \
if (_ls_nmerges <= 1) { \
_ls_looping=0; \
} \
@@ -311,8 +318,8 @@ do {
#define LL_PREPEND2(head,add,next) \
do { \
- (add)->next = head; \
- head = add; \
+ (add)->next = (head); \
+ (head) = (add); \
} while (0)
#define LL_CONCAT(head1,head2) \
@@ -322,7 +329,7 @@ do {
do { \
LDECLTYPE(head1) _tmp; \
if (head1) { \
- _tmp = head1; \
+ _tmp = (head1); \
while (_tmp->next) { _tmp = _tmp->next; } \
_tmp->next=(head2); \
} else { \
@@ -338,7 +345,7 @@ do {
LDECLTYPE(head) _tmp; \
(add)->next=NULL; \
if (head) { \
- _tmp = head; \
+ _tmp = (head); \
while (_tmp->next) { _tmp = _tmp->next; } \
_tmp->next=(add); \
} else { \
@@ -346,96 +353,76 @@ do {
} \
} while (0)
-#define LL_DELETE(head,del) \
- LL_DELETE2(head,del,next)
+#define LL_INSERT_INORDER(head,add,cmp) \
+ LL_INSERT_INORDER2(head,add,cmp,next)
-#define LL_DELETE2(head,del,next) \
+#define LL_INSERT_INORDER2(head,add,cmp,next) \
do { \
LDECLTYPE(head) _tmp; \
- if ((head) == (del)) { \
- (head)=(head)->next; \
+ if (head) { \
+ LL_LOWER_BOUND2(head, _tmp, add, cmp, next); \
+ LL_APPEND_ELEM2(head, _tmp, add, next); \
} else { \
- _tmp = head; \
- while (_tmp->next && (_tmp->next != (del))) { \
- _tmp = _tmp->next; \
- } \
- if (_tmp->next) { \
- _tmp->next = ((del)->next); \
- } \
+ (head) = (add); \
+ (head)->next = NULL; \
} \
} while (0)
-/* Here are VS2008 replacements for LL_APPEND and LL_DELETE */
-#define LL_APPEND_VS2008(head,add) \
- LL_APPEND2_VS2008(head,add,next)
+#define LL_LOWER_BOUND(head,elt,like,cmp) \
+ LL_LOWER_BOUND2(head,elt,like,cmp,next)
-#define LL_APPEND2_VS2008(head,add,next) \
-do { \
- if (head) { \
- (add)->next = head; /* use add->next as a temp variable */ \
- while ((add)->next->next) { (add)->next = (add)->next->next; } \
- (add)->next->next=(add); \
- } else { \
- (head)=(add); \
- } \
- (add)->next=NULL; \
-} while (0)
+#define LL_LOWER_BOUND2(head,elt,like,cmp,next) \
+ do { \
+ if ((head) == NULL || (cmp(head, like)) >= 0) { \
+ (elt) = NULL; \
+ } else { \
+ for ((elt) = (head); (elt)->next != NULL; (elt) = (elt)->next) { \
+ if (cmp((elt)->next, like) >= 0) { \
+ break; \
+ } \
+ } \
+ } \
+ } while (0)
-#define LL_DELETE_VS2008(head,del) \
- LL_DELETE2_VS2008(head,del,next)
+#define LL_DELETE(head,del) \
+ LL_DELETE2(head,del,next)
-#define LL_DELETE2_VS2008(head,del,next) \
+#define LL_DELETE2(head,del,next) \
do { \
+ LDECLTYPE(head) _tmp; \
if ((head) == (del)) { \
(head)=(head)->next; \
} else { \
- char *_tmp = (char*)(head); \
- while ((head)->next && ((head)->next != (del))) { \
- head = (head)->next; \
- } \
- if ((head)->next) { \
- (head)->next = ((del)->next); \
+ _tmp = (head); \
+ while (_tmp->next && (_tmp->next != (del))) { \
+ _tmp = _tmp->next; \
} \
- { \
- char **_head_alias = (char**)&(head); \
- *_head_alias = _tmp; \
+ if (_tmp->next) { \
+ _tmp->next = (del)->next; \
} \
} \
} while (0)
-#ifdef NO_DECLTYPE
-#undef LL_APPEND
-#define LL_APPEND LL_APPEND_VS2008
-#undef LL_DELETE
-#define LL_DELETE LL_DELETE_VS2008
-#undef LL_DELETE2
-#define LL_DELETE2 LL_DELETE2_VS2008
-#undef LL_APPEND2
-#define LL_APPEND2 LL_APPEND2_VS2008
-#undef LL_CONCAT /* no LL_CONCAT_VS2008 */
-#undef DL_CONCAT /* no DL_CONCAT_VS2008 */
-#endif
-/* end VS2008 replacements */
#define LL_COUNT(head,el,counter) \
LL_COUNT2(head,el,counter,next) \
#define LL_COUNT2(head,el,counter,next) \
-{ \
- counter = 0; \
- LL_FOREACH2(head,el,next){ ++counter; } \
-}
+do { \
+ (counter) = 0; \
+ LL_FOREACH2(head,el,next) { ++(counter); } \
+} while (0)
#define LL_FOREACH(head,el) \
LL_FOREACH2(head,el,next)
#define LL_FOREACH2(head,el,next) \
- for(el=head;el;el=(el)->next)
+ for ((el) = (head); el; (el) = (el)->next)
#define LL_FOREACH_SAFE(head,el,tmp) \
LL_FOREACH_SAFE2(head,el,tmp,next)
#define LL_FOREACH_SAFE2(head,el,tmp,next) \
- for((el)=(head);(el) && (tmp = (el)->next, 1); (el) = tmp)
+ for ((el) = (head); (el) && ((tmp) = (el)->next, 1); (el) = (tmp))
#define LL_SEARCH_SCALAR(head,out,field,val) \
LL_SEARCH_SCALAR2(head,out,field,val,next)
@@ -445,7 +432,7 @@ do {
LL_FOREACH2(head,out,next) { \
if ((out)->field == (val)) break; \
} \
-} while(0)
+} while (0)
#define LL_SEARCH(head,out,elt,cmp) \
LL_SEARCH2(head,out,elt,cmp,next)
@@ -455,19 +442,19 @@ do {
LL_FOREACH2(head,out,next) { \
if ((cmp(out,elt))==0) break; \
} \
-} while(0)
+} while (0)
-#define LL_REPLACE_ELEM(head, el, add) \
+#define LL_REPLACE_ELEM2(head, el, add, next) \
do { \
LDECLTYPE(head) _tmp; \
- assert(head != NULL); \
- assert(el != NULL); \
- assert(add != NULL); \
+ assert((head) != NULL); \
+ assert((el) != NULL); \
+ assert((add) != NULL); \
(add)->next = (el)->next; \
if ((head) == (el)) { \
(head) = (add); \
} else { \
- _tmp = head; \
+ _tmp = (head); \
while (_tmp->next && (_tmp->next != (el))) { \
_tmp = _tmp->next; \
} \
@@ -477,26 +464,158 @@ do {
} \
} while (0)
+#define LL_REPLACE_ELEM(head, el, add) \
+ LL_REPLACE_ELEM2(head, el, add, next)
+
+#define LL_PREPEND_ELEM2(head, el, add, next) \
+do { \
+ if (el) { \
+ LDECLTYPE(head) _tmp; \
+ assert((head) != NULL); \
+ assert((add) != NULL); \
+ (add)->next = (el); \
+ if ((head) == (el)) { \
+ (head) = (add); \
+ } else { \
+ _tmp = (head); \
+ while (_tmp->next && (_tmp->next != (el))) { \
+ _tmp = _tmp->next; \
+ } \
+ if (_tmp->next) { \
+ _tmp->next = (add); \
+ } \
+ } \
+ } else { \
+ LL_APPEND2(head, add, next); \
+ } \
+} while (0) \
+
#define LL_PREPEND_ELEM(head, el, add) \
+ LL_PREPEND_ELEM2(head, el, add, next)
+
+#define LL_APPEND_ELEM2(head, el, add, next) \
do { \
- LDECLTYPE(head) _tmp; \
- assert(head != NULL); \
- assert(el != NULL); \
- assert(add != NULL); \
- (add)->next = (el); \
- if ((head) == (el)) { \
- (head) = (add); \
+ if (el) { \
+ assert((head) != NULL); \
+ assert((add) != NULL); \
+ (add)->next = (el)->next; \
+ (el)->next = (add); \
} else { \
- _tmp = head; \
- while (_tmp->next && (_tmp->next != (el))) { \
- _tmp = _tmp->next; \
+ LL_PREPEND2(head, add, next); \
+ } \
+} while (0) \
+
+#define LL_APPEND_ELEM(head, el, add) \
+ LL_APPEND_ELEM2(head, el, add, next)
+
+#ifdef NO_DECLTYPE
+/* Here are VS2008 / NO_DECLTYPE replacements for a few functions */
+
+#undef LL_CONCAT2
+#define LL_CONCAT2(head1,head2,next) \
+do { \
+ char *_tmp; \
+ if (head1) { \
+ _tmp = (char*)(head1); \
+ while ((head1)->next) { (head1) = (head1)->next; } \
+ (head1)->next = (head2); \
+ UTLIST_RS(head1); \
+ } else { \
+ (head1)=(head2); \
} \
- if (_tmp->next) { \
- _tmp->next = (add); \
+} while (0)
+
+#undef LL_APPEND2
+#define LL_APPEND2(head,add,next) \
+do { \
+ if (head) { \
+ (add)->next = head; /* use add->next as a temp variable */ \
+ while ((add)->next->next) { (add)->next = (add)->next->next; } \
+ (add)->next->next=(add); \
+ } else { \
+ (head)=(add); \
+ } \
+ (add)->next=NULL; \
+} while (0)
+
+#undef LL_INSERT_INORDER2
+#define LL_INSERT_INORDER2(head,add,cmp,next) \
+do { \
+ if ((head) == NULL || (cmp(head, add)) >= 0) { \
+ (add)->next = (head); \
+ (head) = (add); \
+ } else { \
+ char *_tmp = (char*)(head); \
+ while ((head)->next != NULL && (cmp((head)->next, add)) < 0) { \
+ (head) = (head)->next; \
+ } \
+ (add)->next = (head)->next; \
+ (head)->next = (add); \
+ UTLIST_RS(head); \
+ } \
+} while (0)
+
+#undef LL_DELETE2
+#define LL_DELETE2(head,del,next) \
+do { \
+ if ((head) == (del)) { \
+ (head)=(head)->next; \
+ } else { \
+ char *_tmp = (char*)(head); \
+ while ((head)->next && ((head)->next != (del))) { \
+ (head) = (head)->next; \
+ } \
+ if ((head)->next) { \
+ (head)->next = ((del)->next); \
+ } \
+ UTLIST_RS(head); \
+ } \
+} while (0)
+
+#undef LL_REPLACE_ELEM2
+#define LL_REPLACE_ELEM2(head, el, add, next) \
+do { \
+ assert((head) != NULL); \
+ assert((el) != NULL); \
+ assert((add) != NULL); \
+ if ((head) == (el)) { \
+ (head) = (add); \
+ } else { \
+ (add)->next = head; \
+ while ((add)->next->next && ((add)->next->next != (el))) { \
+ (add)->next = (add)->next->next; \
+ } \
+ if ((add)->next->next) { \
+ (add)->next->next = (add); \
+ } \
+ } \
+ (add)->next = (el)->next; \
+} while (0)
+
+#undef LL_PREPEND_ELEM2
+#define LL_PREPEND_ELEM2(head, el, add, next) \
+do { \
+ if (el) { \
+ assert((head) != NULL); \
+ assert((add) != NULL); \
+ if ((head) == (el)) { \
+ (head) = (add); \
+ } else { \
+ (add)->next = (head); \
+ while ((add)->next->next && ((add)->next->next != (el))) { \
+ (add)->next = (add)->next->next; \
+ } \
+ if ((add)->next->next) { \
+ (add)->next->next = (add); \
+ } \
+ } \
+ (add)->next = (el); \
+ } else { \
+ LL_APPEND2(head, add, next); \
} \
- } \
} while (0) \
+#endif /* NO_DECLTYPE */
/******************************************************************************
* doubly linked list macros (non-circular) *
@@ -506,7 +625,7 @@ do {
#define DL_PREPEND2(head,add,prev,next) \
do { \
- (add)->next = head; \
+ (add)->next = (head); \
if (head) { \
(add)->prev = (head)->prev; \
(head)->prev = (add); \
@@ -531,7 +650,39 @@ do {
(head)->prev = (head); \
(head)->next = NULL; \
} \
-} while (0)
+} while (0)
+
+#define DL_INSERT_INORDER(head,add,cmp) \
+ DL_INSERT_INORDER2(head,add,cmp,prev,next)
+
+#define DL_INSERT_INORDER2(head,add,cmp,prev,next) \
+do { \
+ LDECLTYPE(head) _tmp; \
+ if (head) { \
+ DL_LOWER_BOUND2(head, _tmp, add, cmp, next); \
+ DL_APPEND_ELEM2(head, _tmp, add, prev, next); \
+ } else { \
+ (head) = (add); \
+ (head)->prev = (head); \
+ (head)->next = NULL; \
+ } \
+} while (0)
+
+#define DL_LOWER_BOUND(head,elt,like,cmp) \
+ DL_LOWER_BOUND2(head,elt,like,cmp,next)
+
+#define DL_LOWER_BOUND2(head,elt,like,cmp,next) \
+do { \
+ if ((head) == NULL || (cmp(head, like)) >= 0) { \
+ (elt) = NULL; \
+ } else { \
+ for ((elt) = (head); (elt)->next != NULL; (elt) = (elt)->next) { \
+ if ((cmp((elt)->next, like)) >= 0) { \
+ break; \
+ } \
+ } \
+ } \
+} while (0)
#define DL_CONCAT(head1,head2) \
DL_CONCAT2(head1,head2,prev,next)
@@ -541,25 +692,27 @@ do {
LDECLTYPE(head1) _tmp; \
if (head2) { \
if (head1) { \
- _tmp = (head2)->prev; \
+ UTLIST_CASTASGN(_tmp, (head2)->prev); \
(head2)->prev = (head1)->prev; \
(head1)->prev->next = (head2); \
- (head1)->prev = _tmp; \
+ UTLIST_CASTASGN((head1)->prev, _tmp); \
} else { \
(head1)=(head2); \
} \
} \
-} while (0)
+} while (0)
#define DL_DELETE(head,del) \
DL_DELETE2(head,del,prev,next)
#define DL_DELETE2(head,del,prev,next) \
do { \
+ assert((head) != NULL); \
assert((del)->prev != NULL); \
if ((del)->prev == (del)) { \
(head)=NULL; \
- } else if ((del)==(head)) { \
+ } else if ((del) == (head)) { \
+ assert((del)->next != NULL); \
(del)->next->prev = (del)->prev; \
(head) = (del)->next; \
} else { \
@@ -570,29 +723,29 @@ do {
(head)->prev = (del)->prev; \
} \
} \
-} while (0)
+} while (0)
#define DL_COUNT(head,el,counter) \
DL_COUNT2(head,el,counter,next) \
#define DL_COUNT2(head,el,counter,next) \
-{ \
- counter = 0; \
- DL_FOREACH2(head,el,next){ ++counter; } \
-}
+do { \
+ (counter) = 0; \
+ DL_FOREACH2(head,el,next) { ++(counter); } \
+} while (0)
#define DL_FOREACH(head,el) \
DL_FOREACH2(head,el,next)
#define DL_FOREACH2(head,el,next) \
- for(el=head;el;el=(el)->next)
+ for ((el) = (head); el; (el) = (el)->next)
/* this version is safe for deleting the elements during iteration */
#define DL_FOREACH_SAFE(head,el,tmp) \
DL_FOREACH_SAFE2(head,el,tmp,next)
#define DL_FOREACH_SAFE2(head,el,tmp,next) \
- for((el)=(head);(el) && (tmp = (el)->next, 1); (el) = tmp)
+ for ((el) = (head); (el) && ((tmp) = (el)->next, 1); (el) = (tmp))
/* these are identical to their singly-linked list counterparts */
#define DL_SEARCH_SCALAR LL_SEARCH_SCALAR
@@ -600,11 +753,11 @@ do {
#define DL_SEARCH_SCALAR2 LL_SEARCH_SCALAR2
#define DL_SEARCH2 LL_SEARCH2
-#define DL_REPLACE_ELEM(head, el, add) \
+#define DL_REPLACE_ELEM2(head, el, add, prev, next) \
do { \
- assert(head != NULL); \
- assert(el != NULL); \
- assert(add != NULL); \
+ assert((head) != NULL); \
+ assert((el) != NULL); \
+ assert((add) != NULL); \
if ((head) == (el)) { \
(head) = (add); \
(add)->next = (el)->next; \
@@ -626,25 +779,104 @@ do {
} \
} while (0)
+#define DL_REPLACE_ELEM(head, el, add) \
+ DL_REPLACE_ELEM2(head, el, add, prev, next)
+
+#define DL_PREPEND_ELEM2(head, el, add, prev, next) \
+do { \
+ if (el) { \
+ assert((head) != NULL); \
+ assert((add) != NULL); \
+ (add)->next = (el); \
+ (add)->prev = (el)->prev; \
+ (el)->prev = (add); \
+ if ((head) == (el)) { \
+ (head) = (add); \
+ } else { \
+ (add)->prev->next = (add); \
+ } \
+ } else { \
+ DL_APPEND2(head, add, prev, next); \
+ } \
+} while (0) \
+
#define DL_PREPEND_ELEM(head, el, add) \
+ DL_PREPEND_ELEM2(head, el, add, prev, next)
+
+#define DL_APPEND_ELEM2(head, el, add, prev, next) \
do { \
- assert(head != NULL); \
- assert(el != NULL); \
- assert(add != NULL); \
- (add)->next = (el); \
- (add)->prev = (el)->prev; \
- (el)->prev = (add); \
- if ((head) == (el)) { \
- (head) = (add); \
+ if (el) { \
+ assert((head) != NULL); \
+ assert((add) != NULL); \
+ (add)->next = (el)->next; \
+ (add)->prev = (el); \
+ (el)->next = (add); \
+ if ((add)->next) { \
+ (add)->next->prev = (add); \
+ } else { \
+ (head)->prev = (add); \
+ } \
} else { \
- (add)->prev->next = (add); \
+ DL_PREPEND2(head, add, prev, next); \
} \
} while (0) \
+#define DL_APPEND_ELEM(head, el, add) \
+ DL_APPEND_ELEM2(head, el, add, prev, next)
+
+#ifdef NO_DECLTYPE
+/* Here are VS2008 / NO_DECLTYPE replacements for a few functions */
+
+#undef DL_INSERT_INORDER2
+#define DL_INSERT_INORDER2(head,add,cmp,prev,next) \
+do { \
+ if ((head) == NULL) { \
+ (add)->prev = (add); \
+ (add)->next = NULL; \
+ (head) = (add); \
+ } else if ((cmp(head, add)) >= 0) { \
+ (add)->prev = (head)->prev; \
+ (add)->next = (head); \
+ (head)->prev = (add); \
+ (head) = (add); \
+ } else { \
+ char *_tmp = (char*)(head); \
+ while ((head)->next && (cmp((head)->next, add)) < 0) { \
+ (head) = (head)->next; \
+ } \
+ (add)->prev = (head); \
+ (add)->next = (head)->next; \
+ (head)->next = (add); \
+ UTLIST_RS(head); \
+ if ((add)->next) { \
+ (add)->next->prev = (add); \
+ } else { \
+ (head)->prev = (add); \
+ } \
+ } \
+} while (0)
+#endif /* NO_DECLTYPE */
/******************************************************************************
* circular doubly linked list macros *
*****************************************************************************/
+#define CDL_APPEND(head,add) \
+ CDL_APPEND2(head,add,prev,next)
+
+#define CDL_APPEND2(head,add,prev,next) \
+do { \
+ if (head) { \
+ (add)->prev = (head)->prev; \
+ (add)->next = (head); \
+ (head)->prev = (add); \
+ (add)->prev->next = (add); \
+ } else { \
+ (add)->prev = (add); \
+ (add)->next = (add); \
+ (head) = (add); \
+ } \
+} while (0)
+
#define CDL_PREPEND(head,add) \
CDL_PREPEND2(head,add,prev,next)
@@ -659,7 +891,39 @@ do {
(add)->prev = (add); \
(add)->next = (add); \
} \
-(head)=(add); \
+ (head) = (add); \
+} while (0)
+
+#define CDL_INSERT_INORDER(head,add,cmp) \
+ CDL_INSERT_INORDER2(head,add,cmp,prev,next)
+
+#define CDL_INSERT_INORDER2(head,add,cmp,prev,next) \
+do { \
+ LDECLTYPE(head) _tmp; \
+ if (head) { \
+ CDL_LOWER_BOUND2(head, _tmp, add, cmp, next); \
+ CDL_APPEND_ELEM2(head, _tmp, add, prev, next); \
+ } else { \
+ (head) = (add); \
+ (head)->next = (head); \
+ (head)->prev = (head); \
+ } \
+} while (0)
+
+#define CDL_LOWER_BOUND(head,elt,like,cmp) \
+ CDL_LOWER_BOUND2(head,elt,like,cmp,next)
+
+#define CDL_LOWER_BOUND2(head,elt,like,cmp,next) \
+do { \
+ if ((head) == NULL || (cmp(head, like)) >= 0) { \
+ (elt) = NULL; \
+ } else { \
+ for ((elt) = (head); (elt)->next != (head); (elt) = (elt)->next) { \
+ if ((cmp((elt)->next, like)) >= 0) { \
+ break; \
+ } \
+ } \
+ } \
} while (0)
#define CDL_DELETE(head,del) \
@@ -667,37 +931,37 @@ do {
#define CDL_DELETE2(head,del,prev,next) \
do { \
- if ( ((head)==(del)) && ((head)->next == (head))) { \
- (head) = 0L; \
+ if (((head)==(del)) && ((head)->next == (head))) { \
+ (head) = NULL; \
} else { \
(del)->next->prev = (del)->prev; \
(del)->prev->next = (del)->next; \
if ((del) == (head)) (head)=(del)->next; \
} \
-} while (0)
+} while (0)
#define CDL_COUNT(head,el,counter) \
CDL_COUNT2(head,el,counter,next) \
#define CDL_COUNT2(head, el, counter,next) \
-{ \
- counter = 0; \
- CDL_FOREACH2(head,el,next){ ++counter; } \
-}
+do { \
+ (counter) = 0; \
+ CDL_FOREACH2(head,el,next) { ++(counter); } \
+} while (0)
#define CDL_FOREACH(head,el) \
CDL_FOREACH2(head,el,next)
#define CDL_FOREACH2(head,el,next) \
- for(el=head;el;el=((el)->next==head ? 0L : (el)->next))
+ for ((el)=(head);el;(el)=(((el)->next==(head)) ? NULL : (el)->next))
#define CDL_FOREACH_SAFE(head,el,tmp1,tmp2) \
CDL_FOREACH_SAFE2(head,el,tmp1,tmp2,prev,next)
#define CDL_FOREACH_SAFE2(head,el,tmp1,tmp2,prev,next) \
- for((el)=(head), ((tmp1)=(head)?((head)->prev):NULL); \
- (el) && ((tmp2)=(el)->next, 1); \
- ((el) = (((el)==(tmp1)) ? 0L : (tmp2))))
+ for ((el) = (head), (tmp1) = (head) ? (head)->prev : NULL; \
+ (el) && ((tmp2) = (el)->next, 1); \
+ (el) = ((el) == (tmp1) ? NULL : (tmp2)))
#define CDL_SEARCH_SCALAR(head,out,field,val) \
CDL_SEARCH_SCALAR2(head,out,field,val,next)
@@ -707,7 +971,7 @@ do {
CDL_FOREACH2(head,out,next) { \
if ((out)->field == (val)) break; \
} \
-} while(0)
+} while (0)
#define CDL_SEARCH(head,out,elt,cmp) \
CDL_SEARCH2(head,out,elt,cmp,next)
@@ -717,13 +981,13 @@ do {
CDL_FOREACH2(head,out,next) { \
if ((cmp(out,elt))==0) break; \
} \
-} while(0)
+} while (0)
-#define CDL_REPLACE_ELEM(head, el, add) \
+#define CDL_REPLACE_ELEM2(head, el, add, prev, next) \
do { \
- assert(head != NULL); \
- assert(el != NULL); \
- assert(add != NULL); \
+ assert((head) != NULL); \
+ assert((el) != NULL); \
+ assert((add) != NULL); \
if ((el)->next == (el)) { \
(add)->next = (add); \
(add)->prev = (add); \
@@ -739,19 +1003,74 @@ do {
} \
} while (0)
+#define CDL_REPLACE_ELEM(head, el, add) \
+ CDL_REPLACE_ELEM2(head, el, add, prev, next)
+
+#define CDL_PREPEND_ELEM2(head, el, add, prev, next) \
+do { \
+ if (el) { \
+ assert((head) != NULL); \
+ assert((add) != NULL); \
+ (add)->next = (el); \
+ (add)->prev = (el)->prev; \
+ (el)->prev = (add); \
+ (add)->prev->next = (add); \
+ if ((head) == (el)) { \
+ (head) = (add); \
+ } \
+ } else { \
+ CDL_APPEND2(head, add, prev, next); \
+ } \
+} while (0)
+
#define CDL_PREPEND_ELEM(head, el, add) \
+ CDL_PREPEND_ELEM2(head, el, add, prev, next)
+
+#define CDL_APPEND_ELEM2(head, el, add, prev, next) \
do { \
- assert(head != NULL); \
- assert(el != NULL); \
- assert(add != NULL); \
- (add)->next = (el); \
- (add)->prev = (el)->prev; \
- (el)->prev = (add); \
- (add)->prev->next = (add); \
- if ((head) == (el)) { \
- (head) = (add); \
+ if (el) { \
+ assert((head) != NULL); \
+ assert((add) != NULL); \
+ (add)->next = (el)->next; \
+ (add)->prev = (el); \
+ (el)->next = (add); \
+ (add)->next->prev = (add); \
+ } else { \
+ CDL_PREPEND2(head, add, prev, next); \
} \
-} while (0) \
+} while (0)
-#endif /* UTLIST_H */
+#define CDL_APPEND_ELEM(head, el, add) \
+ CDL_APPEND_ELEM2(head, el, add, prev, next)
+
+#ifdef NO_DECLTYPE
+/* Here are VS2008 / NO_DECLTYPE replacements for a few functions */
+#undef CDL_INSERT_INORDER2
+#define CDL_INSERT_INORDER2(head,add,cmp,prev,next) \
+do { \
+ if ((head) == NULL) { \
+ (add)->prev = (add); \
+ (add)->next = (add); \
+ (head) = (add); \
+ } else if ((cmp(head, add)) >= 0) { \
+ (add)->prev = (head)->prev; \
+ (add)->next = (head); \
+ (add)->prev->next = (add); \
+ (head)->prev = (add); \
+ (head) = (add); \
+ } else { \
+ char *_tmp = (char*)(head); \
+ while ((char*)(head)->next != _tmp && (cmp((head)->next, add)) < 0) { \
+ (head) = (head)->next; \
+ } \
+ (add)->prev = (head); \
+ (add)->next = (head)->next; \
+ (add)->next->prev = (add); \
+ (head)->next = (add); \
+ UTLIST_RS(head); \
+ } \
+} while (0)
+#endif /* NO_DECLTYPE */
+
+#endif /* UTLIST_H */
diff --git a/contrib/libucl/utils/CMakeLists.txt b/contrib/libucl/utils/CMakeLists.txt
deleted file mode 100644
index 4de95fd7b4b0..000000000000
--- a/contrib/libucl/utils/CMakeLists.txt
+++ /dev/null
@@ -1,12 +0,0 @@
-
-PROJECT(libucl-utils C)
-
-FUNCTION(MAKE_UTIL UTIL_NAME UTIL_SRCS)
- ADD_EXECUTABLE(${UTIL_NAME} ${UTIL_SRCS})
- TARGET_LINK_LIBRARIES(${UTIL_NAME} ucl)
- INSTALL(TARGETS ${UTIL_NAME} DESTINATION bin)
-ENDFUNCTION()
-
-MAKE_UTIL(ucl_chargen chargen.c)
-MAKE_UTIL(ucl_objdump objdump.c)
-MAKE_UTIL(ucl_tool ucl-tool.c)
diff --git a/contrib/libucl/utils/Makefile.am b/contrib/libucl/utils/Makefile.am
deleted file mode 100644
index ec85aaa5e372..000000000000
--- a/contrib/libucl/utils/Makefile.am
+++ /dev/null
@@ -1,23 +0,0 @@
-common_utils_cflags = -I$(top_srcdir)/include \
- -I$(top_srcdir)/src \
- -I$(top_srcdir)/uthash
-common_utils_ldadd = $(top_builddir)/src/libucl.la
-
-ucl_chargen_SOURCES = chargen.c
-ucl_chargen_LDADD = $(common_utils_ldadd)
-ucl_chargen_CFLAGS = $(common_utils_cflags)
-
-ucl_objdump_SOURCES = objdump.c
-ucl_objdump_LDADD = $(common_utils_ldadd)
-ucl_objdump_CFLAGS = $(common_utils_cflags)
-
-ucl_tool_SOURCES = ucl-tool.c
-ucl_tool_LDADD = $(common_utils_ldadd)
-ucl_tool_CFLAGS = $(common_utils_cflags)
-
-if UTILS
-UTL = ucl_chargen ucl_objdump ucl_tool
-else
-UTL =
-endif
-bin_PROGRAMS = $(UTL)
diff --git a/contrib/libucl/utils/chargen.c b/contrib/libucl/utils/chargen.c
deleted file mode 100644
index 398134054c21..000000000000
--- a/contrib/libucl/utils/chargen.c
+++ /dev/null
@@ -1,128 +0,0 @@
-/* Copyright (c) 2013, Vsevolod Stakhov
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-/**
- * @file this utility generates character table for ucl
- */
-
-#include <stdio.h>
-#include <ctype.h>
-#include <stdbool.h>
-
-static inline int
-print_flag (const char *flag, bool *need_or, char *val)
-{
- int res;
- res = sprintf (val, "%s%s", *need_or ? "|" : "", flag);
-
- *need_or |= true;
-
- return res;
-}
-
-int
-main (int argc, char **argv)
-{
- int i, col, r;
- const char *name = "ucl_chartable";
- bool need_or;
- char valbuf[2048];
-
- col = 0;
-
- if (argc > 1) {
- name = argv[1];
- }
-
- printf ("static const unsigned int %s[256] = {\n", name);
-
- for (i = 0; i < 256; i ++) {
- need_or = false;
- r = 0;
- /* UCL_CHARACTER_VALUE_END */
-
- if (i == ' ' || i == '\t') {
- r += print_flag ("UCL_CHARACTER_WHITESPACE", &need_or, valbuf + r);
- }
- if (isspace (i)) {
- r += print_flag ("UCL_CHARACTER_WHITESPACE_UNSAFE", &need_or, valbuf + r);
- }
- if (isalnum (i) || i >= 0x80 || i == '/' || i == '_') {
- r += print_flag ("UCL_CHARACTER_KEY_START", &need_or, valbuf + r);
- }
- if (isalnum (i) || i == '-' || i == '_' || i == '/' || i == '.' || i >= 0x80) {
- r += print_flag ("UCL_CHARACTER_KEY", &need_or, valbuf + r);
- }
- if (i == 0 || i == '\r' || i == '\n' || i == ']' || i == '}' || i == ';' || i == ',' || i == '#') {
- r += print_flag ("UCL_CHARACTER_VALUE_END", &need_or, valbuf + r);
- }
- else {
- if (isprint (i) || i >= 0x80) {
- r += print_flag ("UCL_CHARACTER_VALUE_STR", &need_or, valbuf + r);
- }
- if (isdigit (i) || i == '-') {
- r += print_flag ("UCL_CHARACTER_VALUE_DIGIT_START", &need_or, valbuf + r);
- }
- if (isalnum (i) || i == '.' || i == '-' || i == '+') {
- r += print_flag ("UCL_CHARACTER_VALUE_DIGIT", &need_or, valbuf + r);
- }
- }
- if (i == '"' || i == '\\' || i == '/' || i == 'b' ||
- i == 'f' || i == 'n' || i == 'r' || i == 't' || i == 'u') {
- r += print_flag ("UCL_CHARACTER_ESCAPE", &need_or, valbuf + r);
- }
- if (i == ' ' || i == '\t' || i == ':' || i == '=') {
- r += print_flag ("UCL_CHARACTER_KEY_SEP", &need_or, valbuf + r);
- }
- if (i == '\n' || i == '\r' || i == '\\' || i == '\b' || i == '\t' ||
- i == '"' || i == '\f') {
- r += print_flag ("UCL_CHARACTER_JSON_UNSAFE", &need_or, valbuf + r);
- }
- if (i == '\n' || i == '\r' || i == '\\' || i == '\b' || i == '\t' ||
- i == '"' || i == '\f' || i == '=' || i == ':' || i == '{' || i == '[' || i == ' ') {
- r += print_flag ("UCL_CHARACTER_UCL_UNSAFE", &need_or, valbuf + r);
- }
-
- if (!need_or) {
- r += print_flag ("UCL_CHARACTER_DENIED", &need_or, valbuf + r);
- }
-
- if (isprint (i)) {
- r += sprintf (valbuf + r, " /* %c */", i);
- }
- if (i != 255) {
- r += sprintf (valbuf + r, ", ");
- }
- col += r;
- if (col > 80) {
- printf ("\n%s", valbuf);
- col = r;
- }
- else {
- printf ("%s", valbuf);
- }
- }
- printf ("\n}\n");
-
- return 0;
-}
diff --git a/contrib/libucl/utils/objdump.c b/contrib/libucl/utils/objdump.c
deleted file mode 100644
index 416eca7c87e0..000000000000
--- a/contrib/libucl/utils/objdump.c
+++ /dev/null
@@ -1,185 +0,0 @@
-/* Copyright (c) 2013, Dmitriy V. Reshetnikov
- * Copyright (c) 2013, Vsevolod Stakhov
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#if defined(_MSC_VER)
- #include <BaseTsd.h>
-
- typedef SSIZE_T ssize_t;
-#endif
-
-#include "ucl.h"
-
-void
-ucl_obj_dump (const ucl_object_t *obj, unsigned int shift)
-{
- int num = shift * 4 + 5;
- char *pre = (char *) malloc (num * sizeof(char));
- const ucl_object_t *cur, *tmp;
- ucl_object_iter_t it = NULL, it_obj = NULL;
-
- pre[--num] = 0x00;
- while (num--)
- pre[num] = 0x20;
-
- tmp = obj;
-
- while ((obj = ucl_object_iterate (tmp, &it, false))) {
- printf ("%sucl object address: %p\n", pre + 4, obj);
- if (obj->key != NULL) {
- printf ("%skey: \"%s\"\n", pre, ucl_object_key (obj));
- }
- printf ("%sref: %u\n", pre, obj->ref);
- printf ("%slen: %u\n", pre, obj->len);
- printf ("%sprev: %p\n", pre, obj->prev);
- printf ("%snext: %p\n", pre, obj->next);
- if (obj->type == UCL_OBJECT) {
- printf ("%stype: UCL_OBJECT\n", pre);
- printf ("%svalue: %p\n", pre, obj->value.ov);
- it_obj = NULL;
- while ((cur = ucl_object_iterate (obj, &it_obj, true))) {
- ucl_obj_dump (cur, shift + 2);
- }
- }
- else if (obj->type == UCL_ARRAY) {
- printf ("%stype: UCL_ARRAY\n", pre);
- printf ("%svalue: %p\n", pre, obj->value.av);
- it_obj = NULL;
- while ((cur = ucl_object_iterate (obj, &it_obj, true))) {
- ucl_obj_dump (cur, shift + 2);
- }
- }
- else if (obj->type == UCL_INT) {
- printf ("%stype: UCL_INT\n", pre);
- printf ("%svalue: %jd\n", pre, (intmax_t)ucl_object_toint (obj));
- }
- else if (obj->type == UCL_FLOAT) {
- printf ("%stype: UCL_FLOAT\n", pre);
- printf ("%svalue: %f\n", pre, ucl_object_todouble (obj));
- }
- else if (obj->type == UCL_STRING) {
- printf ("%stype: UCL_STRING\n", pre);
- printf ("%svalue: \"%s\"\n", pre, ucl_object_tostring (obj));
- }
- else if (obj->type == UCL_BOOLEAN) {
- printf ("%stype: UCL_BOOLEAN\n", pre);
- printf ("%svalue: %s\n", pre, ucl_object_tostring_forced (obj));
- }
- else if (obj->type == UCL_TIME) {
- printf ("%stype: UCL_TIME\n", pre);
- printf ("%svalue: %f\n", pre, ucl_object_todouble (obj));
- }
- else if (obj->type == UCL_USERDATA) {
- printf ("%stype: UCL_USERDATA\n", pre);
- printf ("%svalue: %p\n", pre, obj->value.ud);
- }
- }
-
- free (pre);
-}
-
-int
-main(int argc, char **argv)
-{
- const char *fn = NULL;
- unsigned char *inbuf;
- struct ucl_parser *parser;
- int k, ret = 0;
- ssize_t bufsize, r = 0;
- ucl_object_t *obj = NULL;
- const ucl_object_t *par;
- FILE *in;
-
- if (argc > 1) {
- fn = argv[1];
- }
-
- if (fn != NULL) {
- in = fopen (fn, "r");
- if (in == NULL) {
- exit (EXIT_FAILURE);
- }
- }
- else {
- in = stdin;
- }
-
- parser = ucl_parser_new (0);
- inbuf = malloc (BUFSIZ);
- bufsize = BUFSIZ;
- r = 0;
-
- while (!feof (in) && !ferror (in)) {
- if (r == bufsize) {
- inbuf = realloc (inbuf, bufsize * 2);
- bufsize *= 2;
- if (inbuf == NULL) {
- perror ("realloc");
- exit (EXIT_FAILURE);
- }
- }
- r += fread (inbuf + r, 1, bufsize - r, in);
- }
-
- if (ferror (in)) {
- fprintf (stderr, "Failed to read the input file.\n");
- exit (EXIT_FAILURE);
- }
-
- ucl_parser_add_chunk (parser, inbuf, r);
- fclose (in);
- if (ucl_parser_get_error(parser)) {
- printf ("Error occurred: %s\n", ucl_parser_get_error(parser));
- ret = 1;
- goto end;
- }
-
- obj = ucl_parser_get_object (parser);
- if (ucl_parser_get_error (parser)) {
- printf ("Error occurred: %s\n", ucl_parser_get_error(parser));
- ret = 1;
- goto end;
- }
-
- if (argc > 2) {
- for (k = 2; k < argc; k++) {
- printf ("search for \"%s\"... ", argv[k]);
- par = ucl_object_lookup (obj, argv[k]);
- printf ("%sfound\n", (par == NULL )?"not ":"");
- ucl_obj_dump (par, 0);
- }
- }
- else {
- ucl_obj_dump (obj, 0);
- }
-
-end:
- if (parser != NULL) {
- ucl_parser_free (parser);
- }
- if (obj != NULL) {
- ucl_object_unref (obj);
- }
-
- return ret;
-}
diff --git a/contrib/libucl/utils/ucl-tool.c b/contrib/libucl/utils/ucl-tool.c
deleted file mode 100644
index 9b807d35c092..000000000000
--- a/contrib/libucl/utils/ucl-tool.c
+++ /dev/null
@@ -1,170 +0,0 @@
-/* Copyright (c) 2015, Cesanta Software
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * * Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
- * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
- * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "ucl.h"
-
-void usage(const char *name, FILE *out) {
- fprintf(out, "Usage: %s [--help] [-i|--in file] [-o|--out file]\n", name);
- fprintf(out, " [-s|--schema file] [-f|--format format]\n\n");
- fprintf(out, " --help - print this message and exit\n");
- fprintf(out, " --in - specify input filename "
- "(default: standard input)\n");
- fprintf(out, " --out - specify output filename "
- "(default: standard output)\n");
- fprintf(out, " --schema - specify schema file for validation\n");
- fprintf(out, " --format - output format. Options: ucl (default), "
- "json, compact_json, yaml, msgpack\n");
-}
-
-int main(int argc, char **argv) {
- int i;
- char ch;
- FILE *in = stdin, *out = stdout;
- const char *schema = NULL, *parm, *val;
- unsigned char *buf = NULL;
- size_t size = 0, r = 0;
- struct ucl_parser *parser = NULL;
- ucl_object_t *obj = NULL;
- ucl_emitter_t emitter = UCL_EMIT_CONFIG;
-
- for (i = 1; i < argc; ++i) {
- parm = argv[i];
- val = ((i + 1) < argc) ? argv[++i] : NULL;
-
- if ((strcmp(parm, "--help") == 0) || (strcmp(parm, "-h") == 0)) {
- usage(argv[0], stdout);
- exit(0);
-
- } else if ((strcmp(parm, "--in") == 0) || (strcmp(parm, "-i") == 0)) {
- if (!val)
- goto err_val;
-
- in = fopen(val, "r");
- if (in == NULL) {
- perror("fopen on input file");
- exit(EXIT_FAILURE);
- }
- } else if ((strcmp(parm, "--out") == 0) || (strcmp(parm, "-o") == 0)) {
- if (!val)
- goto err_val;
-
- out = fopen(val, "w");
- if (out == NULL) {
- perror("fopen on output file");
- exit(EXIT_FAILURE);
- }
- } else if ((strcmp(parm, "--schema") == 0) || (strcmp(parm, "-s") == 0)) {
- if (!val)
- goto err_val;
- schema = val;
-
- } else if ((strcmp(parm, "--format") == 0) || (strcmp(parm, "-f") == 0)) {
- if (!val)
- goto err_val;
-
- if (strcmp(val, "ucl") == 0) {
- emitter = UCL_EMIT_CONFIG;
- } else if (strcmp(val, "json") == 0) {
- emitter = UCL_EMIT_JSON;
- } else if (strcmp(val, "yaml") == 0) {
- emitter = UCL_EMIT_YAML;
- } else if (strcmp(val, "compact_json") == 0) {
- emitter = UCL_EMIT_JSON_COMPACT;
- } else if (strcmp(val, "msgpack") == 0) {
- emitter = UCL_EMIT_MSGPACK;
- } else {
- fprintf(stderr, "Unknown output format: %s\n", val);
- exit(EXIT_FAILURE);
- }
- } else {
- usage(argv[0], stderr);
- exit(EXIT_FAILURE);
- }
- }
-
- parser = ucl_parser_new(0);
- buf = malloc(BUFSIZ);
- size = BUFSIZ;
- while (!feof(in) && !ferror(in)) {
- if (r == size) {
- buf = realloc(buf, size*2);
- size *= 2;
- if (buf == NULL) {
- perror("realloc");
- exit(EXIT_FAILURE);
- }
- }
- r += fread(buf + r, 1, size - r, in);
- }
- if (ferror(in)) {
- fprintf(stderr, "Failed to read the input file.\n");
- exit(EXIT_FAILURE);
- }
- fclose(in);
- if (!ucl_parser_add_chunk(parser, buf, r)) {
- fprintf(stderr, "Failed to parse input file: %s\n",
- ucl_parser_get_error(parser));
- exit(EXIT_FAILURE);
- }
- if ((obj = ucl_parser_get_object(parser)) == NULL) {
- fprintf(stderr, "Failed to get root object: %s\n",
- ucl_parser_get_error(parser));
- exit(EXIT_FAILURE);
- }
- if (schema != NULL) {
- struct ucl_parser *schema_parser = ucl_parser_new(0);
- ucl_object_t *schema_obj = NULL;
- struct ucl_schema_error error;
-
- if (!ucl_parser_add_file(schema_parser, schema)) {
- fprintf(stderr, "Failed to parse schema file: %s\n",
- ucl_parser_get_error(schema_parser));
- exit(EXIT_FAILURE);
- }
- if ((schema_obj = ucl_parser_get_object(schema_parser)) == NULL) {
- fprintf(stderr, "Failed to get root object: %s\n",
- ucl_parser_get_error(schema_parser));
- exit(EXIT_FAILURE);
- }
- if (!ucl_object_validate(schema_obj, obj, &error)) {
- fprintf(stderr, "Validation failed: %s\n", error.msg);
- exit(EXIT_FAILURE);
- }
- }
-
- if (emitter != UCL_EMIT_MSGPACK) {
- fprintf(out, "%s\n", ucl_object_emit(obj, emitter));
- } else {
- size_t len;
- unsigned char *res;
-
- res = ucl_object_emit_len(obj, emitter, &len);
- fwrite(res, 1, len, out);
- }
-
- return 0;
-
-err_val:
- fprintf(stderr, "Parameter %s is missing mandatory value\n", parm);
- usage(argv[0], stderr);
- exit(EXIT_FAILURE);
-}
diff --git a/contrib/libxo/libxo/xo.h b/contrib/libxo/libxo/xo.h
index 6a61a16c7cae..7f37b469b54e 100644
--- a/contrib/libxo/libxo/xo.h
+++ b/contrib/libxo/libxo/xo.h
@@ -27,6 +27,10 @@
#include <stdlib.h>
#include <errno.h>
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
#ifdef __dead2
#define NORETURN __dead2
#else
@@ -699,4 +703,8 @@ xo_retain_clear_all (void);
void
xo_retain_clear (const char *fmt);
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
#endif /* INCLUDE_XO_H */
diff --git a/contrib/libxo/libxo/xo_encoder.h b/contrib/libxo/libxo/xo_encoder.h
index 099248ae13a6..bb57194ab030 100644
--- a/contrib/libxo/libxo/xo_encoder.h
+++ b/contrib/libxo/libxo/xo_encoder.h
@@ -20,6 +20,10 @@
#include <string.h>
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
/*
* Expose libxo's memory allocation functions
*/
@@ -167,4 +171,8 @@ xo_encoder_op_name (xo_encoder_op_t op);
void
xo_failure (xo_handle_t *xop, const char *fmt, ...);
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
#endif /* XO_ENCODER_H */
diff --git a/contrib/llvm-project/libcxx/include/__functional/binary_function.h b/contrib/llvm-project/libcxx/include/__functional/binary_function.h
index ddee3b170311..18879f65112b 100644
--- a/contrib/llvm-project/libcxx/include/__functional/binary_function.h
+++ b/contrib/llvm-project/libcxx/include/__functional/binary_function.h
@@ -39,11 +39,10 @@ struct __binary_function_keep_layout_base {
};
#if _LIBCPP_STD_VER <= 14 || defined(_LIBCPP_ENABLE_CXX17_REMOVED_UNARY_BINARY_FUNCTION)
-_LIBCPP_DIAGNOSTIC_PUSH
-_LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wdeprecated-declarations")
+_LIBCPP_SUPPRESS_DEPRECATED_PUSH
template <class _Arg1, class _Arg2, class _Result>
using __binary_function = binary_function<_Arg1, _Arg2, _Result>;
-_LIBCPP_DIAGNOSTIC_POP
+_LIBCPP_SUPPRESS_DEPRECATED_POP
#else
template <class _Arg1, class _Arg2, class _Result>
using __binary_function = __binary_function_keep_layout_base<_Arg1, _Arg2, _Result>;
diff --git a/contrib/llvm-project/libcxx/include/__functional/unary_function.h b/contrib/llvm-project/libcxx/include/__functional/unary_function.h
index 69b1bc94220a..d46df2e86fbd 100644
--- a/contrib/llvm-project/libcxx/include/__functional/unary_function.h
+++ b/contrib/llvm-project/libcxx/include/__functional/unary_function.h
@@ -36,11 +36,10 @@ struct __unary_function_keep_layout_base {
};
#if _LIBCPP_STD_VER <= 14 || defined(_LIBCPP_ENABLE_CXX17_REMOVED_UNARY_BINARY_FUNCTION)
-_LIBCPP_DIAGNOSTIC_PUSH
-_LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wdeprecated-declarations")
+_LIBCPP_SUPPRESS_DEPRECATED_PUSH
template <class _Arg, class _Result>
using __unary_function = unary_function<_Arg, _Result>;
-_LIBCPP_DIAGNOSTIC_POP
+_LIBCPP_SUPPRESS_DEPRECATED_POP
#else
template <class _Arg, class _Result>
using __unary_function = __unary_function_keep_layout_base<_Arg, _Result>;
diff --git a/contrib/llvm-project/libcxx/include/__functional/weak_result_type.h b/contrib/llvm-project/libcxx/include/__functional/weak_result_type.h
index ad7a8395186c..488fec9dac21 100644
--- a/contrib/llvm-project/libcxx/include/__functional/weak_result_type.h
+++ b/contrib/llvm-project/libcxx/include/__functional/weak_result_type.h
@@ -77,6 +77,7 @@ struct __maybe_derive_from_unary_function // bool is true
template <class _Tp>
struct __maybe_derive_from_unary_function<_Tp, false> {};
+_LIBCPP_SUPPRESS_DEPRECATED_PUSH
template <class _Tp, bool = __derives_from_binary_function<_Tp>::value>
struct __maybe_derive_from_binary_function // bool is true
: public __derives_from_binary_function<_Tp>::type {};
@@ -99,6 +100,7 @@ struct __weak_result_type_imp<_Tp, false>
template <class _Tp>
struct __weak_result_type : public __weak_result_type_imp<_Tp> {};
+_LIBCPP_SUPPRESS_DEPRECATED_POP
// 0 argument case
diff --git a/contrib/llvm-project/libcxx/include/__memory/allocator_traits.h b/contrib/llvm-project/libcxx/include/__memory/allocator_traits.h
index c5fcc89327b8..f3e327edda12 100644
--- a/contrib/llvm-project/libcxx/include/__memory/allocator_traits.h
+++ b/contrib/llvm-project/libcxx/include/__memory/allocator_traits.h
@@ -40,6 +40,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
template <class _Tp> \
struct NAME<_Tp, __void_t<typename _Tp::PROPERTY > > : true_type {}
+_LIBCPP_SUPPRESS_DEPRECATED_PUSH
// __pointer
template <class _Tp,
class _Alloc,
@@ -67,6 +68,7 @@ struct __const_pointer<_Tp, _Ptr, _Alloc, false> {
using type _LIBCPP_NODEBUG = typename pointer_traits<_Ptr>::template rebind<const _Tp>;
#endif
};
+_LIBCPP_SUPPRESS_DEPRECATED_POP
// __void_pointer
_LIBCPP_ALLOCATOR_TRAITS_HAS_XXX(__has_void_pointer, void_pointer);
diff --git a/contrib/llvm-project/libcxx/include/__memory/uninitialized_algorithms.h b/contrib/llvm-project/libcxx/include/__memory/uninitialized_algorithms.h
index 7475ef5cf85d..79cab80dcf73 100644
--- a/contrib/llvm-project/libcxx/include/__memory/uninitialized_algorithms.h
+++ b/contrib/llvm-project/libcxx/include/__memory/uninitialized_algorithms.h
@@ -642,7 +642,8 @@ __uninitialized_allocator_relocate(_Alloc& __alloc, _Tp* __first, _Tp* __last, _
__guard.__complete();
std::__allocator_destroy(__alloc, __first, __last);
} else {
- __builtin_memcpy(const_cast<__remove_const_t<_Tp>*>(__result), __first, sizeof(_Tp) * (__last - __first));
+ // Casting to void* to suppress clang complaining that this is technically UB.
+ __builtin_memcpy(static_cast<void*>(const_cast<__remove_const_t<_Tp>*>(__result)), __first, sizeof(_Tp) * (__last - __first));
}
}
diff --git a/contrib/llvm-project/libcxx/include/__type_traits/is_trivially_relocatable.h b/contrib/llvm-project/libcxx/include/__type_traits/is_trivially_relocatable.h
index c0871731cc00..9b0e240de55f 100644
--- a/contrib/llvm-project/libcxx/include/__type_traits/is_trivially_relocatable.h
+++ b/contrib/llvm-project/libcxx/include/__type_traits/is_trivially_relocatable.h
@@ -11,7 +11,6 @@
#include <__config>
#include <__type_traits/enable_if.h>
-#include <__type_traits/integral_constant.h>
#include <__type_traits/is_same.h>
#include <__type_traits/is_trivially_copyable.h>
@@ -23,8 +22,11 @@ _LIBCPP_BEGIN_NAMESPACE_STD
// A type is trivially relocatable if a move construct + destroy of the original object is equivalent to
// `memcpy(dst, src, sizeof(T))`.
-
-#if __has_builtin(__is_trivially_relocatable)
+//
+// Note that we don't use the __is_trivially_relocatable Clang builtin right now because it does not
+// implement the semantics of any current or future trivial relocation proposal and it can lead to
+// incorrect optimizations on some platforms (Windows) and supported compilers (AppleClang).
+#if __has_builtin(__is_trivially_relocatable) && 0
template <class _Tp, class = void>
struct __libcpp_is_trivially_relocatable : integral_constant<bool, __is_trivially_relocatable(_Tp)> {};
#else
diff --git a/contrib/llvm-project/libcxx/include/tuple b/contrib/llvm-project/libcxx/include/tuple
index 081b90c7bbec..1d39974d5a6b 100644
--- a/contrib/llvm-project/libcxx/include/tuple
+++ b/contrib/llvm-project/libcxx/include/tuple
@@ -302,7 +302,9 @@ class __tuple_leaf {
template <class _Tp>
static _LIBCPP_HIDE_FROM_ABI constexpr bool __can_bind_reference() {
-# if __has_keyword(__reference_binds_to_temporary)
+# if __has_keyword(__reference_constructs_from_temporary)
+ return !__reference_constructs_from_temporary(_Hp, _Tp);
+# elif __has_keyword(__reference_binds_to_temporary)
return !__reference_binds_to_temporary(_Hp, _Tp);
# else
return true;
diff --git a/contrib/llvm-project/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp b/contrib/llvm-project/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp
index 075455c03415..7de9b4dd2ea1 100644
--- a/contrib/llvm-project/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp
+++ b/contrib/llvm-project/llvm/lib/ObjCopy/ELF/ELFObjcopy.cpp
@@ -368,7 +368,7 @@ static Error updateAndRemoveSymbols(const CommonConfig &Config,
// (like GroupSection or RelocationSection). This way, we know which
// symbols are still 'needed' and which are not.
if (Config.StripUnneeded || !Config.UnneededSymbolsToRemove.empty() ||
- !Config.OnlySection.empty()) {
+ !Config.OnlySection.empty() || Config.DiscardMode != DiscardType::None) {
for (SectionBase &Sec : Obj.sections())
Sec.markSymbols();
}
@@ -390,22 +390,23 @@ static Error updateAndRemoveSymbols(const CommonConfig &Config,
if (Config.StripDebug && Sym.Type == STT_FILE)
return true;
- if ((Config.DiscardMode == DiscardType::All ||
- (Config.DiscardMode == DiscardType::Locals &&
- StringRef(Sym.Name).starts_with(".L"))) &&
- Sym.Binding == STB_LOCAL && Sym.getShndx() != SHN_UNDEF &&
- Sym.Type != STT_FILE && Sym.Type != STT_SECTION)
- return true;
-
if ((Config.StripUnneeded ||
Config.UnneededSymbolsToRemove.matches(Sym.Name)) &&
(!Obj.isRelocatable() || isUnneededSymbol(Sym)))
return true;
- // We want to remove undefined symbols if all references have been stripped.
- if (!Config.OnlySection.empty() && !Sym.Referenced &&
- Sym.getShndx() == SHN_UNDEF)
- return true;
+ if (!Sym.Referenced) {
+ if ((Config.DiscardMode == DiscardType::All ||
+ (Config.DiscardMode == DiscardType::Locals &&
+ StringRef(Sym.Name).starts_with(".L"))) &&
+ Sym.Binding == STB_LOCAL && Sym.getShndx() != SHN_UNDEF &&
+ Sym.Type != STT_FILE && Sym.Type != STT_SECTION)
+ return true;
+ // We want to remove undefined symbols if all references have been
+ // stripped.
+ if (!Config.OnlySection.empty() && Sym.getShndx() == SHN_UNDEF)
+ return true;
+ }
return false;
};
diff --git a/contrib/llvm-project/llvm/lib/Target/PowerPC/PPCMergeStringPool.cpp b/contrib/llvm-project/llvm/lib/Target/PowerPC/PPCMergeStringPool.cpp
index 309938accdf4..daf6a0e65d54 100644
--- a/contrib/llvm-project/llvm/lib/Target/PowerPC/PPCMergeStringPool.cpp
+++ b/contrib/llvm-project/llvm/lib/Target/PowerPC/PPCMergeStringPool.cpp
@@ -170,8 +170,9 @@ void PPCMergeStringPool::collectCandidateConstants(Module &M) {
LLVM_DEBUG(dbgs() << "hasInitializer() " << Global.hasInitializer()
<< "\n");
- // We can only pool constants.
- if (!Global.isConstant() || !Global.hasInitializer())
+ // We can only pool non-thread-local constants.
+ if (!Global.isConstant() || !Global.hasInitializer() ||
+ Global.isThreadLocal())
continue;
// If a global constant has a section we do not try to pool it because
diff --git a/contrib/lua/Makefile b/contrib/lua/Makefile
index 8efa2eb3fdd6..6e21588476df 100644
--- a/contrib/lua/Makefile
+++ b/contrib/lua/Makefile
@@ -46,7 +46,7 @@ TO_MAN= lua.1 luac.1
# Lua version and release.
V= 5.4
-R= $V.6
+R= $V.8
# Targets start here.
all: $(PLAT)
diff --git a/contrib/lua/README b/contrib/lua/README
index 1ae97165babe..b251d296f687 100644
--- a/contrib/lua/README
+++ b/contrib/lua/README
@@ -1,5 +1,5 @@
-This is Lua 5.4.6, released on 02 May 2023.
+This is Lua 5.4.8, released on 21 May 2025.
For installation instructions, license details, and
further information about Lua, see doc/readme.html.
diff --git a/contrib/lua/doc/contents.html b/contrib/lua/doc/contents.html
index 1231e6d2481d..18b677dbac8f 100644
--- a/contrib/lua/doc/contents.html
+++ b/contrib/lua/doc/contents.html
@@ -10,7 +10,7 @@
<BODY>
<H1>
-<A HREF="http://www.lua.org/"><IMG SRC="logo.gif" ALT="Lua"></A>
+<A HREF="https://www.lua.org/"><IMG SRC="logo.gif" ALT="Lua"></A>
Lua 5.4 Reference Manual
</H1>
@@ -18,7 +18,7 @@ Lua 5.4 Reference Manual
The reference manual is the official definition of the Lua language.
<BR>
For a complete introduction to Lua programming, see the book
-<A HREF="http://www.lua.org/pil/">Programming in Lua</A>.
+<A HREF="https://www.lua.org/pil/">Programming in Lua</A>.
<DIV CLASS="menubar">
<A HREF="manual.html">start</A>
@@ -27,14 +27,14 @@ For a complete introduction to Lua programming, see the book
&middot;
<A HREF="#index">index</A>
&middot;
-<A HREF="http://www.lua.org/manual/">other versions</A>
+<A HREF="https://www.lua.org/manual/">other versions</A>
</DIV>
<P>
<SMALL>
-Copyright &copy; 2020&ndash;2023 Lua.org, PUC-Rio.
+Copyright &copy; 2020&ndash;2025 Lua.org, PUC-Rio.
Freely available under the terms of the
-<A HREF="http://www.lua.org/license.html">Lua license</A>.
+<A HREF="https://www.lua.org/license.html">Lua license</A>.
</SMALL>
<H2><A NAME="contents">Contents</A></H2>
@@ -668,10 +668,10 @@ Freely available under the terms of the
<P CLASS="footer">
Last update:
-Sat Apr 1 17:57:05 UTC 2023
+Wed May 21 21:11:33 UTC 2025
</P>
<!--
-Last change: revised for Lua 5.4.5
+Last change: revised for Lua 5.4.8
-->
</BODY>
diff --git a/contrib/lua/doc/lua.1 b/contrib/lua/doc/lua.1
index 3f472fd81f62..3c9e000234e3 100644
--- a/contrib/lua/doc/lua.1
+++ b/contrib/lua/doc/lua.1
@@ -1,5 +1,5 @@
-.\" $Id: lua.man,v 1.14 2022/09/23 09:06:36 lhf Exp $
-.TH LUA 1 "$Date: 2022/09/23 09:06:36 $"
+.\" $Id: lua.man,v 1.14 2024/05/08 18:48:27 lhf Exp $
+.TH LUA 1 "$Date: 2024/05/08 18:48:27 $"
.SH NAME
lua \- Lua interpreter
.SH SYNOPSIS
@@ -123,7 +123,7 @@ and the version-neutral variants are ignored.
Code to be executed before command line options and scripts.
.TP
.B LUA_PATH, LUA_PATH_5_4
-Initial value of package.cpath,
+Initial value of package.path,
the path used by require to search for Lua loaders.
.TP
.B LUA_CPATH, LUA_CPATH_5_4
diff --git a/contrib/lua/doc/lua.css b/contrib/lua/doc/lua.css
index cbd0799d1525..9013b445c654 100644
--- a/contrib/lua/doc/lua.css
+++ b/contrib/lua/doc/lua.css
@@ -143,6 +143,7 @@ table.book td.cover {
table.book img {
border: solid #000080 1px ;
+ border-radius: 2px ;
}
table.book span {
diff --git a/contrib/lua/doc/manual.html b/contrib/lua/doc/manual.html
index 0af688b343c7..8239bc2a964f 100644
--- a/contrib/lua/doc/manual.html
+++ b/contrib/lua/doc/manual.html
@@ -10,7 +10,7 @@
<BODY>
<H1>
-<A HREF="http://www.lua.org/"><IMG SRC="logo.gif" ALT="Lua"></A>
+<A HREF="https://www.lua.org/"><IMG SRC="logo.gif" ALT="Lua"></A>
Lua 5.4 Reference Manual
</H1>
@@ -19,9 +19,9 @@ by Roberto Ierusalimschy, Luiz Henrique de Figueiredo, Waldemar Celes
<P>
<SMALL>
-Copyright &copy; 2020&ndash;2023 Lua.org, PUC-Rio.
+Copyright &copy; 2020&ndash;2025 Lua.org, PUC-Rio.
Freely available under the terms of the
-<a href="http://www.lua.org/license.html">Lua license</a>.
+<a href="https://www.lua.org/license.html">Lua license</a>.
</SMALL>
<DIV CLASS="menubar">
@@ -29,7 +29,7 @@ Freely available under the terms of the
&middot;
<A HREF="contents.html#index">index</A>
&middot;
-<A HREF="http://www.lua.org/manual/">other versions</A>
+<A HREF="https://www.lua.org/manual/">other versions</A>
</DIV>
<!-- ====================================================================== -->
@@ -391,7 +391,7 @@ Whenever there is an error,
an <em>error object</em>
is propagated with information about the error.
Lua itself only generates errors whose error object is a string,
-but programs may generate errors with
+but programs can generate errors with
any value as the error object.
It is up to the Lua program or its host to handle such error objects.
For historical reasons,
@@ -401,7 +401,7 @@ even though it does not have to be a string.
<p>
When you use <a href="#pdf-xpcall"><code>xpcall</code></a> (or <a href="#lua_pcall"><code>lua_pcall</code></a>, in C)
-you may give a <em>message handler</em>
+you can give a <em>message handler</em>
to be called in case of errors.
This function is called with the original error object
and returns a new error object.
@@ -453,7 +453,7 @@ which is then called a <em>metamethod</em>.
In the previous example, the key is the string "<code>__add</code>"
and the metamethod is the function that performs the addition.
Unless stated otherwise,
-a metamethod may in fact be any callable value,
+a metamethod can in fact be any callable value,
which is either a function or a value with a <code>__call</code> metamethod.
@@ -1725,7 +1725,7 @@ labels in Lua are considered statements too:
<p>
A label is visible in the entire block where it is defined,
except inside nested functions.
-A goto may jump to any visible label as long as it does not
+A goto can jump to any visible label as long as it does not
enter into the scope of a local variable.
A label should not be declared
where a label with the same name is visible,
@@ -5571,7 +5571,7 @@ otherwise, returns <code>NULL</code>.
<hr><h3><a name="lua_toclose"><code>lua_toclose</code></a></h3><p>
-<span class="apii">[-0, +0, <em>m</em>]</span>
+<span class="apii">[-0, +0, <em>v</em>]</span>
<pre>void lua_toclose (lua_State *L, int index);</pre>
<p>
@@ -5592,6 +5592,11 @@ unless previously deactivated by <a href="#lua_closeslot"><code>lua_closeslot</c
<p>
+This function raises an error if the value at the given slot
+neither has a <code>__close</code> metamethod nor is a false value.
+
+
+<p>
This function should not be called for an index
that is equal to or below an active to-be-closed slot.
@@ -5664,6 +5669,12 @@ after its last character (as in&nbsp;C),
but can contain other zeros in its body.
+<p>
+This function can raise memory errors only
+when converting a number to a string
+(as then it may create a new string).
+
+
@@ -11276,13 +11287,13 @@ The returned table can contain all the fields returned by <a href="#lua_getinfo"
with the string <code>what</code> describing which fields to fill in.
The default for <code>what</code> is to get all information available,
except the table of valid lines.
-If present,
-the option '<code>f</code>'
+The option '<code>f</code>'
adds a field named <code>func</code> with the function itself.
-If present,
-the option '<code>L</code>'
-adds a field named <code>activelines</code> with the table of
-valid lines.
+The option '<code>L</code>' adds a field named <code>activelines</code>
+with the table of valid lines,
+provided the function is a Lua function.
+If the function has no debug information,
+the table is empty.
<p>
@@ -11619,6 +11630,10 @@ Lua does not consult any environment variables.
In particular,
the values of <a href="#pdf-package.path"><code>package.path</code></a> and <a href="#pdf-package.cpath"><code>package.cpath</code></a>
are set with the default paths defined in <code>luaconf.h</code>.
+To signal to the libraries that this option is on,
+the stand-alone interpreter sets the field
+<code>"LUA_NOENV"</code> in the registry to a true value.
+Other libraries may consult this field for the same purpose.
<p>
@@ -12033,13 +12048,12 @@ and LiteralString, see <a href="#3.1">&sect;3.1</a>.)
-
<P CLASS="footer">
Last update:
-Tue May 2 20:09:38 UTC 2023
+Wed May 21 21:09:59 UTC 2025
</P>
<!--
-Last change: revised for Lua 5.4.6
+Last change: revised for Lua 5.4.8
-->
</body></html>
diff --git a/contrib/lua/doc/readme.html b/contrib/lua/doc/readme.html
index 918ec8ed9378..a4eb59dd38c6 100644
--- a/contrib/lua/doc/readme.html
+++ b/contrib/lua/doc/readme.html
@@ -29,7 +29,7 @@ tt, kbd, code {
<BODY>
<H1>
-<A HREF="http://www.lua.org/"><IMG SRC="logo.gif" ALT="Lua"></A>
+<A HREF="https://www.lua.org/"><IMG SRC="logo.gif" ALT="Lua"></A>
Welcome to Lua 5.4
</H1>
@@ -49,29 +49,31 @@ Welcome to Lua 5.4
<P>
Lua is a powerful, efficient, lightweight, embeddable scripting language
developed by a
-<A HREF="http://www.lua.org/authors.html">team</A>
+<A HREF="https://www.lua.org/authors.html">team</A>
at
-<A HREF="http://www.puc-rio.br/">PUC-Rio</A>,
+<A HREF="https://www.puc-rio.br/">PUC-Rio</A>,
the Pontifical Catholic University of Rio de Janeiro in Brazil.
Lua is
<A HREF="#license">free software</A>
used in
-<A HREF="http://www.lua.org/uses.html">many products and projects</A>
+<A HREF="https://www.lua.org/uses.html">many products and projects</A>
around the world.
<P>
Lua's
-<A HREF="http://www.lua.org/">official web site</A>
+<A HREF="https://www.lua.org/">official website</A>
provides complete information
about Lua,
including
an
-<A HREF="http://www.lua.org/about.html">executive summary</A>
+<A HREF="https://www.lua.org/about.html">executive summary</A>,
+tips on
+<A HREF="https://www.lua.org/start.html">getting started</A>,
and
updated
-<A HREF="http://www.lua.org/docs.html">documentation</A>,
+<A HREF="https://www.lua.org/docs.html">documentation</A>,
especially the
-<A HREF="http://www.lua.org/manual/5.4/">reference manual</A>,
+<A HREF="https://www.lua.org/manual/5.4/">reference manual</A>,
which may differ slightly from the
<A HREF="contents.html">local copy</A>
distributed in this package.
@@ -79,7 +81,7 @@ distributed in this package.
<H2><A NAME="install">Installing Lua</A></H2>
<P>
Lua is distributed in
-<A HREF="http://www.lua.org/ftp/">source</A>
+<A HREF="https://www.lua.org/ftp/">source</A>
form.
You need to build it before using it.
Building Lua should be straightforward
@@ -88,7 +90,7 @@ Lua is implemented in pure ANSI C and compiles unmodified in all known
platforms that have an ANSI C compiler.
Lua also compiles unmodified as C++.
The instructions given below for building Lua are for Unix-like platforms,
-such as Linux and Mac OS X.
+such as Linux and macOS.
See also
<A HREF="#other">instructions for other systems</A>
and
@@ -97,7 +99,7 @@ and
<P>
If you don't have the time or the inclination to compile Lua yourself,
get a binary from
-<A HREF="http://lua-users.org/wiki/LuaBinaries">LuaBinaries</A>.
+<A HREF="https://luabinaries.sourceforge.net">LuaBinaries</A>.
<H3>Building Lua</H3>
<P>
@@ -107,7 +109,7 @@ Here are the details.
<OL>
<LI>
Open a terminal window and move to
-the top-level directory, which is named <TT>lua-5.4.6</TT>.
+the top-level directory, which is named <TT>lua-5.4.8</TT>.
The <TT>Makefile</TT> there controls both the build process and the installation process.
<P>
<LI>
@@ -211,8 +213,8 @@ then try "<KBD>make linux-readline MYLIBS=-ltermcap</KBD>".
record the changes you've made.
<P>
- On the other hand, if you need to customize some Lua features, you'll need
- to edit <TT>src/luaconf.h</TT> before building and installing Lua.
+ On the other hand, if you need to customize some Lua features,
+ edit <TT>src/luaconf.h</TT> before building and installing Lua.
The edited file will be the one installed, and
it will be used by any Lua clients that you build, to ensure consistency.
Further customization is available to experts by editing the Lua sources.
@@ -241,7 +243,7 @@ compiler:
</DL>
<P>
- To use Lua as a library in your own programs, you'll need to know how to
+ To use Lua as a library in your own programs, you need to know how to
create and use libraries with your compiler. Moreover, to dynamically load
C libraries for Lua, you'll need to know how to create dynamic libraries
and you'll need to make sure that the Lua API functions are accessible to
@@ -284,11 +286,11 @@ lists the
<H2><A NAME="license">License</A></H2>
<P>
-<A HREF="http://www.opensource.org/docs/definition.php">
-<IMG SRC="osi-certified-72x60.png" ALIGN="right" ALT="[osi certified]" STYLE="padding-left: 30px ;">
+<A HREF="https://opensource.org/osd">
+<IMG SRC="OSIApproved_100X125.png" ALIGN="right" ALT="[Open Source Initiative Approved License]" STYLE="padding-left: 1em" WIDTH=50>
</A>
Lua is free software distributed under the terms of the
-<A HREF="http://www.opensource.org/licenses/mit-license.html">MIT license</A>
+<A HREF="https://opensource.org/license/mit">MIT license</A>
reproduced below;
it may be used for any purpose, including commercial purposes,
at absolutely no cost without having to ask us.
@@ -296,11 +298,11 @@ at absolutely no cost without having to ask us.
The only requirement is that if you do use Lua,
then you should give us credit by including the appropriate copyright notice somewhere in your product or its documentation.
-For details, see
-<A HREF="http://www.lua.org/license.html">this</A>.
+For details, see the
+<A HREF="https://www.lua.org/license.html">license page</A>.
<BLOCKQUOTE STYLE="padding-bottom: 0em">
-Copyright &copy; 1994&ndash;2023 Lua.org, PUC-Rio.
+Copyright &copy; 1994&ndash;2025 Lua.org, PUC-Rio.
<P>
Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -327,10 +329,10 @@ THE SOFTWARE.
<P CLASS="footer">
Last update:
-Tue May 2 20:08:55 UTC 2023
+Wed May 21 21:12:01 UTC 2025
</P>
<!--
-Last change: revised for Lua 5.4.6
+Last change: revised for Lua 5.4.8
-->
</BODY>
diff --git a/contrib/lua/src/lapi.c b/contrib/lua/src/lapi.c
index 34e64af1428c..04e09cff7e0d 100644
--- a/contrib/lua/src/lapi.c
+++ b/contrib/lua/src/lapi.c
@@ -417,9 +417,9 @@ LUA_API const char *lua_tolstring (lua_State *L, int idx, size_t *len) {
o = index2value(L, idx); /* previous call may reallocate the stack */
}
if (len != NULL)
- *len = vslen(o);
+ *len = tsslen(tsvalue(o));
lua_unlock(L);
- return svalue(o);
+ return getstr(tsvalue(o));
}
@@ -1343,7 +1343,7 @@ void lua_warning (lua_State *L, const char *msg, int tocont) {
LUA_API void *lua_newuserdatauv (lua_State *L, size_t size, int nuvalue) {
Udata *u;
lua_lock(L);
- api_check(L, 0 <= nuvalue && nuvalue < USHRT_MAX, "invalid value");
+ api_check(L, 0 <= nuvalue && nuvalue < SHRT_MAX, "invalid value");
u = luaS_newudata(L, size, nuvalue);
setuvalue(L, s2v(L->top.p), u);
api_incr_top(L);
diff --git a/contrib/lua/src/lauxlib.c b/contrib/lua/src/lauxlib.c
index 4ca6c6548899..923105ed3176 100644
--- a/contrib/lua/src/lauxlib.c
+++ b/contrib/lua/src/lauxlib.c
@@ -80,6 +80,7 @@ static int pushglobalfuncname (lua_State *L, lua_Debug *ar) {
int top = lua_gettop(L);
lua_getinfo(L, "f", ar); /* push function */
lua_getfield(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE);
+ luaL_checkstack(L, 6, "not enough stack"); /* slots for 'findfield' */
if (findfield(L, top + 1, 2)) {
const char *name = lua_tostring(L, -1);
if (strncmp(name, LUA_GNAME ".", 3) == 0) { /* name start with '_G.'? */
@@ -249,11 +250,13 @@ LUALIB_API int luaL_fileresult (lua_State *L, int stat, const char *fname) {
return 1;
}
else {
+ const char *msg;
luaL_pushfail(L);
+ msg = (en != 0) ? strerror(en) : "(no extra info)";
if (fname)
- lua_pushfstring(L, "%s: %s", fname, strerror(en));
+ lua_pushfstring(L, "%s: %s", fname, msg);
else
- lua_pushstring(L, strerror(en));
+ lua_pushstring(L, msg);
lua_pushinteger(L, en);
return 3;
}
@@ -732,9 +735,12 @@ static const char *getF (lua_State *L, void *ud, size_t *size) {
static int errfile (lua_State *L, const char *what, int fnameindex) {
- const char *serr = strerror(errno);
+ int err = errno;
const char *filename = lua_tostring(L, fnameindex) + 1;
- lua_pushfstring(L, "cannot %s %s: %s", what, filename, serr);
+ if (err != 0)
+ lua_pushfstring(L, "cannot %s %s: %s", what, filename, strerror(err));
+ else
+ lua_pushfstring(L, "cannot %s %s", what, filename);
lua_remove(L, fnameindex);
return LUA_ERRFILE;
}
@@ -787,6 +793,7 @@ LUALIB_API int luaL_loadfilex (lua_State *L, const char *filename,
}
else {
lua_pushfstring(L, "@%s", filename);
+ errno = 0;
lf.f = fopen(filename, "r");
if (lf.f == NULL) return errfile(L, "open", fnameindex);
}
@@ -796,6 +803,7 @@ LUALIB_API int luaL_loadfilex (lua_State *L, const char *filename,
if (c == LUA_SIGNATURE[0]) { /* binary file? */
lf.n = 0; /* remove possible newline */
if (filename) { /* "real" file? */
+ errno = 0;
lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */
if (lf.f == NULL) return errfile(L, "reopen", fnameindex);
skipcomment(lf.f, &c); /* re-read initial portion */
@@ -803,6 +811,7 @@ LUALIB_API int luaL_loadfilex (lua_State *L, const char *filename,
}
if (c != EOF)
lf.buff[lf.n++] = c; /* 'c' is the first character of the stream */
+ errno = 0;
status = lua_load(L, getF, &lf, lua_tostring(L, -1), mode);
readstatus = ferror(lf.f);
if (filename) fclose(lf.f); /* close file (even in case of errors) */
@@ -933,7 +942,7 @@ LUALIB_API const char *luaL_tolstring (lua_State *L, int idx, size_t *len) {
LUALIB_API void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) {
luaL_checkstack(L, nup, "too many upvalues");
for (; l->name != NULL; l++) { /* fill the table with given functions */
- if (l->func == NULL) /* place holder? */
+ if (l->func == NULL) /* placeholder? */
lua_pushboolean(L, 0);
else {
int i;
@@ -1025,9 +1034,14 @@ static void *l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) {
}
+/*
+** Standard panic funcion just prints an error message. The test
+** with 'lua_type' avoids possible memory errors in 'lua_tostring'.
+*/
static int panic (lua_State *L) {
- const char *msg = lua_tostring(L, -1);
- if (msg == NULL) msg = "error object is not a string";
+ const char *msg = (lua_type(L, -1) == LUA_TSTRING)
+ ? lua_tostring(L, -1)
+ : "error object is not a string";
lua_writestringerror("PANIC: unprotected error in call to Lua API (%s)\n",
msg);
return 0; /* return to Lua to abort */
diff --git a/contrib/lua/src/lcode.c b/contrib/lua/src/lcode.c
index 8d6ce8c08bd2..85466a82ee1d 100644
--- a/contrib/lua/src/lcode.c
+++ b/contrib/lua/src/lcode.c
@@ -35,6 +35,7 @@
#define MAXREGS 255
+/* (note that expressions VJMP also have jumps.) */
#define hasjumps(e) ((e)->t != (e)->f)
@@ -415,7 +416,7 @@ int luaK_codeABx (FuncState *fs, OpCode o, int a, unsigned int bc) {
/*
** Format and emit an 'iAsBx' instruction.
*/
-int luaK_codeAsBx (FuncState *fs, OpCode o, int a, int bc) {
+static int codeAsBx (FuncState *fs, OpCode o, int a, int bc) {
unsigned int b = bc + OFFSET_sBx;
lua_assert(getOpMode(o) == iAsBx);
lua_assert(a <= MAXARG_A && b <= MAXARG_Bx);
@@ -678,7 +679,7 @@ static int fitsBx (lua_Integer i) {
void luaK_int (FuncState *fs, int reg, lua_Integer i) {
if (fitsBx(i))
- luaK_codeAsBx(fs, OP_LOADI, reg, cast_int(i));
+ codeAsBx(fs, OP_LOADI, reg, cast_int(i));
else
luaK_codek(fs, reg, luaK_intK(fs, i));
}
@@ -687,7 +688,7 @@ void luaK_int (FuncState *fs, int reg, lua_Integer i) {
static void luaK_float (FuncState *fs, int reg, lua_Number f) {
lua_Integer fi;
if (luaV_flttointeger(f, &fi, F2Ieq) && fitsBx(fi))
- luaK_codeAsBx(fs, OP_LOADF, reg, cast_int(fi));
+ codeAsBx(fs, OP_LOADF, reg, cast_int(fi));
else
luaK_codek(fs, reg, luaK_numberK(fs, f));
}
@@ -783,7 +784,8 @@ void luaK_dischargevars (FuncState *fs, expdesc *e) {
break;
}
case VLOCAL: { /* already in a register */
- e->u.info = e->u.var.ridx;
+ int temp = e->u.var.ridx;
+ e->u.info = temp; /* (can't do a direct assignment; values overlap) */
e->k = VNONRELOC; /* becomes a non-relocatable value */
break;
}
@@ -991,7 +993,7 @@ void luaK_exp2anyregup (FuncState *fs, expdesc *e) {
** or it is a constant.
*/
void luaK_exp2val (FuncState *fs, expdesc *e) {
- if (hasjumps(e))
+ if (e->k == VJMP || hasjumps(e))
luaK_exp2anyreg(fs, e);
else
luaK_dischargevars(fs, e);
@@ -1032,7 +1034,7 @@ static int luaK_exp2K (FuncState *fs, expdesc *e) {
** in the range of R/K indices).
** Returns 1 iff expression is K.
*/
-int luaK_exp2RK (FuncState *fs, expdesc *e) {
+static int exp2RK (FuncState *fs, expdesc *e) {
if (luaK_exp2K(fs, e))
return 1;
else { /* not a constant in the right range: put it in a register */
@@ -1044,7 +1046,7 @@ int luaK_exp2RK (FuncState *fs, expdesc *e) {
static void codeABRK (FuncState *fs, OpCode o, int a, int b,
expdesc *ec) {
- int k = luaK_exp2RK(fs, ec);
+ int k = exp2RK(fs, ec);
luaK_codeABCk(fs, o, a, b, ec->u.info, k);
}
@@ -1222,7 +1224,7 @@ static void codenot (FuncState *fs, expdesc *e) {
/*
-** Check whether expression 'e' is a small literal string
+** Check whether expression 'e' is a short literal string
*/
static int isKstr (FuncState *fs, expdesc *e) {
return (e->k == VK && !hasjumps(e) && e->u.info <= MAXARG_B &&
@@ -1232,7 +1234,7 @@ static int isKstr (FuncState *fs, expdesc *e) {
/*
** Check whether expression 'e' is a literal integer.
*/
-int luaK_isKint (expdesc *e) {
+static int isKint (expdesc *e) {
return (e->k == VKINT && !hasjumps(e));
}
@@ -1242,7 +1244,7 @@ int luaK_isKint (expdesc *e) {
** proper range to fit in register C
*/
static int isCint (expdesc *e) {
- return luaK_isKint(e) && (l_castS2U(e->u.ival) <= l_castS2U(MAXARG_C));
+ return isKint(e) && (l_castS2U(e->u.ival) <= l_castS2U(MAXARG_C));
}
@@ -1251,7 +1253,7 @@ static int isCint (expdesc *e) {
** proper range to fit in register sC
*/
static int isSCint (expdesc *e) {
- return luaK_isKint(e) && fitsC(e->u.ival);
+ return isKint(e) && fitsC(e->u.ival);
}
@@ -1290,15 +1292,17 @@ void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) {
if (t->k == VUPVAL && !isKstr(fs, k)) /* upvalue indexed by non 'Kstr'? */
luaK_exp2anyreg(fs, t); /* put it in a register */
if (t->k == VUPVAL) {
- t->u.ind.t = t->u.info; /* upvalue index */
- t->u.ind.idx = k->u.info; /* literal string */
+ int temp = t->u.info; /* upvalue index */
+ lua_assert(isKstr(fs, k));
+ t->u.ind.t = temp; /* (can't do a direct assignment; values overlap) */
+ t->u.ind.idx = k->u.info; /* literal short string */
t->k = VINDEXUP;
}
else {
/* register index of the table */
t->u.ind.t = (t->k == VLOCAL) ? t->u.var.ridx: t->u.info;
if (isKstr(fs, k)) {
- t->u.ind.idx = k->u.info; /* literal string */
+ t->u.ind.idx = k->u.info; /* literal short string */
t->k = VINDEXSTR;
}
else if (isCint(k)) {
@@ -1466,7 +1470,7 @@ static void codebinK (FuncState *fs, BinOpr opr,
*/
static int finishbinexpneg (FuncState *fs, expdesc *e1, expdesc *e2,
OpCode op, int line, TMS event) {
- if (!luaK_isKint(e2))
+ if (!isKint(e2))
return 0; /* not an integer constant */
else {
lua_Integer i2 = e2->u.ival;
@@ -1599,7 +1603,7 @@ static void codeeq (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2) {
op = OP_EQI;
r2 = im; /* immediate operand */
}
- else if (luaK_exp2RK(fs, e2)) { /* 2nd expression is constant? */
+ else if (exp2RK(fs, e2)) { /* 2nd expression is constant? */
op = OP_EQK;
r2 = e2->u.info; /* constant index */
}
@@ -1665,7 +1669,7 @@ void luaK_infix (FuncState *fs, BinOpr op, expdesc *v) {
}
case OPR_EQ: case OPR_NE: {
if (!tonumeral(v, NULL))
- luaK_exp2RK(fs, v);
+ exp2RK(fs, v);
/* else keep numeral, which may be an immediate operand */
break;
}
diff --git a/contrib/lua/src/lcode.h b/contrib/lua/src/lcode.h
index 326582445263..0b971fc4359b 100644
--- a/contrib/lua/src/lcode.h
+++ b/contrib/lua/src/lcode.h
@@ -61,10 +61,8 @@ typedef enum UnOpr { OPR_MINUS, OPR_BNOT, OPR_NOT, OPR_LEN, OPR_NOUNOPR } UnOpr;
LUAI_FUNC int luaK_code (FuncState *fs, Instruction i);
LUAI_FUNC int luaK_codeABx (FuncState *fs, OpCode o, int A, unsigned int Bx);
-LUAI_FUNC int luaK_codeAsBx (FuncState *fs, OpCode o, int A, int Bx);
LUAI_FUNC int luaK_codeABCk (FuncState *fs, OpCode o, int A,
int B, int C, int k);
-LUAI_FUNC int luaK_isKint (expdesc *e);
LUAI_FUNC int luaK_exp2const (FuncState *fs, const expdesc *e, TValue *v);
LUAI_FUNC void luaK_fixline (FuncState *fs, int line);
LUAI_FUNC void luaK_nil (FuncState *fs, int from, int n);
@@ -76,7 +74,6 @@ LUAI_FUNC int luaK_exp2anyreg (FuncState *fs, expdesc *e);
LUAI_FUNC void luaK_exp2anyregup (FuncState *fs, expdesc *e);
LUAI_FUNC void luaK_exp2nextreg (FuncState *fs, expdesc *e);
LUAI_FUNC void luaK_exp2val (FuncState *fs, expdesc *e);
-LUAI_FUNC int luaK_exp2RK (FuncState *fs, expdesc *e);
LUAI_FUNC void luaK_self (FuncState *fs, expdesc *e, expdesc *key);
LUAI_FUNC void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k);
LUAI_FUNC void luaK_goiftrue (FuncState *fs, expdesc *e);
diff --git a/contrib/lua/src/ldebug.c b/contrib/lua/src/ldebug.c
index 28b1caabf77e..7264fce8a55c 100644
--- a/contrib/lua/src/ldebug.c
+++ b/contrib/lua/src/ldebug.c
@@ -31,12 +31,15 @@
-#define noLuaClosure(f) ((f) == NULL || (f)->c.tt == LUA_VCCL)
+#define LuaClosure(f) ((f) != NULL && (f)->c.tt == LUA_VLCL)
static const char *funcnamefromcall (lua_State *L, CallInfo *ci,
const char **name);
+static const char strlocal[] = "local";
+static const char strupval[] = "upvalue";
+
static int currentpc (CallInfo *ci) {
lua_assert(isLua(ci));
@@ -254,7 +257,7 @@ LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n) {
static void funcinfo (lua_Debug *ar, Closure *cl) {
- if (noLuaClosure(cl)) {
+ if (!LuaClosure(cl)) {
ar->source = "=[C]";
ar->srclen = LL("=[C]");
ar->linedefined = -1;
@@ -288,29 +291,31 @@ static int nextline (const Proto *p, int currentline, int pc) {
static void collectvalidlines (lua_State *L, Closure *f) {
- if (noLuaClosure(f)) {
+ if (!LuaClosure(f)) {
setnilvalue(s2v(L->top.p));
api_incr_top(L);
}
else {
- int i;
- TValue v;
const Proto *p = f->l.p;
int currentline = p->linedefined;
Table *t = luaH_new(L); /* new table to store active lines */
sethvalue2s(L, L->top.p, t); /* push it on stack */
api_incr_top(L);
- setbtvalue(&v); /* boolean 'true' to be the value of all indices */
- if (!p->is_vararg) /* regular function? */
- i = 0; /* consider all instructions */
- else { /* vararg function */
- lua_assert(GET_OPCODE(p->code[0]) == OP_VARARGPREP);
- currentline = nextline(p, currentline, 0);
- i = 1; /* skip first instruction (OP_VARARGPREP) */
- }
- for (; i < p->sizelineinfo; i++) { /* for each instruction */
- currentline = nextline(p, currentline, i); /* get its line */
- luaH_setint(L, t, currentline, &v); /* table[line] = true */
+ if (p->lineinfo != NULL) { /* proto with debug information? */
+ int i;
+ TValue v;
+ setbtvalue(&v); /* boolean 'true' to be the value of all indices */
+ if (!p->is_vararg) /* regular function? */
+ i = 0; /* consider all instructions */
+ else { /* vararg function */
+ lua_assert(GET_OPCODE(p->code[0]) == OP_VARARGPREP);
+ currentline = nextline(p, currentline, 0);
+ i = 1; /* skip first instruction (OP_VARARGPREP) */
+ }
+ for (; i < p->sizelineinfo; i++) { /* for each instruction */
+ currentline = nextline(p, currentline, i); /* get its line */
+ luaH_setint(L, t, currentline, &v); /* table[line] = true */
+ }
}
}
}
@@ -339,7 +344,7 @@ static int auxgetinfo (lua_State *L, const char *what, lua_Debug *ar,
}
case 'u': {
ar->nups = (f == NULL) ? 0 : f->c.nupvalues;
- if (noLuaClosure(f)) {
+ if (!LuaClosure(f)) {
ar->isvararg = 1;
ar->nparams = 0;
}
@@ -417,40 +422,6 @@ LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar) {
** =======================================================
*/
-static const char *getobjname (const Proto *p, int lastpc, int reg,
- const char **name);
-
-
-/*
-** Find a "name" for the constant 'c'.
-*/
-static void kname (const Proto *p, int c, const char **name) {
- TValue *kvalue = &p->k[c];
- *name = (ttisstring(kvalue)) ? svalue(kvalue) : "?";
-}
-
-
-/*
-** Find a "name" for the register 'c'.
-*/
-static void rname (const Proto *p, int pc, int c, const char **name) {
- const char *what = getobjname(p, pc, c, name); /* search for 'c' */
- if (!(what && *what == 'c')) /* did not find a constant name? */
- *name = "?";
-}
-
-
-/*
-** Find a "name" for a 'C' value in an RK instruction.
-*/
-static void rkname (const Proto *p, int pc, Instruction i, const char **name) {
- int c = GETARG_C(i); /* key index */
- if (GETARG_k(i)) /* is 'c' a constant? */
- kname(p, c, name);
- else /* 'c' is a register */
- rname(p, pc, c, name);
-}
-
static int filterpc (int pc, int jmptarget) {
if (pc < jmptarget) /* is code conditional (inside a jump)? */
@@ -509,28 +480,29 @@ static int findsetreg (const Proto *p, int lastpc, int reg) {
/*
-** Check whether table being indexed by instruction 'i' is the
-** environment '_ENV'
+** Find a "name" for the constant 'c'.
*/
-static const char *gxf (const Proto *p, int pc, Instruction i, int isup) {
- int t = GETARG_B(i); /* table index */
- const char *name; /* name of indexed variable */
- if (isup) /* is an upvalue? */
- name = upvalname(p, t);
- else
- getobjname(p, pc, t, &name);
- return (name && strcmp(name, LUA_ENV) == 0) ? "global" : "field";
+static const char *kname (const Proto *p, int index, const char **name) {
+ TValue *kvalue = &p->k[index];
+ if (ttisstring(kvalue)) {
+ *name = getstr(tsvalue(kvalue));
+ return "constant";
+ }
+ else {
+ *name = "?";
+ return NULL;
+ }
}
-static const char *getobjname (const Proto *p, int lastpc, int reg,
- const char **name) {
- int pc;
- *name = luaF_getlocalname(p, reg + 1, lastpc);
+static const char *basicgetobjname (const Proto *p, int *ppc, int reg,
+ const char **name) {
+ int pc = *ppc;
+ *name = luaF_getlocalname(p, reg + 1, pc);
if (*name) /* is a local? */
- return "local";
+ return strlocal;
/* else try symbolic execution */
- pc = findsetreg(p, lastpc, reg);
+ *ppc = pc = findsetreg(p, pc, reg);
if (pc != -1) { /* could find instruction? */
Instruction i = p->code[pc];
OpCode op = GET_OPCODE(i);
@@ -538,18 +510,86 @@ static const char *getobjname (const Proto *p, int lastpc, int reg,
case OP_MOVE: {
int b = GETARG_B(i); /* move from 'b' to 'a' */
if (b < GETARG_A(i))
- return getobjname(p, pc, b, name); /* get name for 'b' */
+ return basicgetobjname(p, ppc, b, name); /* get name for 'b' */
break;
}
+ case OP_GETUPVAL: {
+ *name = upvalname(p, GETARG_B(i));
+ return strupval;
+ }
+ case OP_LOADK: return kname(p, GETARG_Bx(i), name);
+ case OP_LOADKX: return kname(p, GETARG_Ax(p->code[pc + 1]), name);
+ default: break;
+ }
+ }
+ return NULL; /* could not find reasonable name */
+}
+
+
+/*
+** Find a "name" for the register 'c'.
+*/
+static void rname (const Proto *p, int pc, int c, const char **name) {
+ const char *what = basicgetobjname(p, &pc, c, name); /* search for 'c' */
+ if (!(what && *what == 'c')) /* did not find a constant name? */
+ *name = "?";
+}
+
+
+/*
+** Find a "name" for a 'C' value in an RK instruction.
+*/
+static void rkname (const Proto *p, int pc, Instruction i, const char **name) {
+ int c = GETARG_C(i); /* key index */
+ if (GETARG_k(i)) /* is 'c' a constant? */
+ kname(p, c, name);
+ else /* 'c' is a register */
+ rname(p, pc, c, name);
+}
+
+
+/*
+** Check whether table being indexed by instruction 'i' is the
+** environment '_ENV'. If the table is an upvalue, get its name;
+** otherwise, find some "name" for the table and check whether
+** that name is the name of a local variable (and not, for instance,
+** a string). Then check that, if there is a name, it is '_ENV'.
+*/
+static const char *isEnv (const Proto *p, int pc, Instruction i, int isup) {
+ int t = GETARG_B(i); /* table index */
+ const char *name; /* name of indexed variable */
+ if (isup) /* is 't' an upvalue? */
+ name = upvalname(p, t);
+ else { /* 't' is a register */
+ const char *what = basicgetobjname(p, &pc, t, &name);
+ if (what != strlocal && what != strupval)
+ name = NULL; /* cannot be the variable _ENV */
+ }
+ return (name && strcmp(name, LUA_ENV) == 0) ? "global" : "field";
+}
+
+
+/*
+** Extend 'basicgetobjname' to handle table accesses
+*/
+static const char *getobjname (const Proto *p, int lastpc, int reg,
+ const char **name) {
+ const char *kind = basicgetobjname(p, &lastpc, reg, name);
+ if (kind != NULL)
+ return kind;
+ else if (lastpc != -1) { /* could find instruction? */
+ Instruction i = p->code[lastpc];
+ OpCode op = GET_OPCODE(i);
+ switch (op) {
case OP_GETTABUP: {
int k = GETARG_C(i); /* key index */
kname(p, k, name);
- return gxf(p, pc, i, 1);
+ return isEnv(p, lastpc, i, 1);
}
case OP_GETTABLE: {
int k = GETARG_C(i); /* key index */
- rname(p, pc, k, name);
- return gxf(p, pc, i, 0);
+ rname(p, lastpc, k, name);
+ return isEnv(p, lastpc, i, 0);
}
case OP_GETI: {
*name = "integer index";
@@ -558,24 +598,10 @@ static const char *getobjname (const Proto *p, int lastpc, int reg,
case OP_GETFIELD: {
int k = GETARG_C(i); /* key index */
kname(p, k, name);
- return gxf(p, pc, i, 0);
- }
- case OP_GETUPVAL: {
- *name = upvalname(p, GETARG_B(i));
- return "upvalue";
- }
- case OP_LOADK:
- case OP_LOADKX: {
- int b = (op == OP_LOADK) ? GETARG_Bx(i)
- : GETARG_Ax(p->code[pc + 1]);
- if (ttisstring(&p->k[b])) {
- *name = svalue(&p->k[b]);
- return "constant";
- }
- break;
+ return isEnv(p, lastpc, i, 0);
}
case OP_SELF: {
- rkname(p, pc, i, name);
+ rkname(p, lastpc, i, name);
return "method";
}
default: break; /* go through to return NULL */
@@ -627,7 +653,7 @@ static const char *funcnamefromcode (lua_State *L, const Proto *p,
default:
return NULL; /* cannot find a reasonable name */
}
- *name = getstr(G(L)->tmname[tm]) + 2;
+ *name = getshrstr(G(L)->tmname[tm]) + 2;
return "metamethod";
}
@@ -684,7 +710,7 @@ static const char *getupvalname (CallInfo *ci, const TValue *o,
for (i = 0; i < c->nupvalues; i++) {
if (c->upvals[i]->v.p == o) {
*name = upvalname(c->p, i);
- return "upvalue";
+ return strupval;
}
}
return NULL;
@@ -866,6 +892,28 @@ static int changedline (const Proto *p, int oldpc, int newpc) {
/*
+** Traces Lua calls. If code is running the first instruction of a function,
+** and function is not vararg, and it is not coming from an yield,
+** calls 'luaD_hookcall'. (Vararg functions will call 'luaD_hookcall'
+** after adjusting its variable arguments; otherwise, they could call
+** a line/count hook before the call hook. Functions coming from
+** an yield already called 'luaD_hookcall' before yielding.)
+*/
+int luaG_tracecall (lua_State *L) {
+ CallInfo *ci = L->ci;
+ Proto *p = ci_func(ci)->p;
+ ci->u.l.trap = 1; /* ensure hooks will be checked */
+ if (ci->u.l.savedpc == p->code) { /* first instruction (not resuming)? */
+ if (p->is_vararg)
+ return 0; /* hooks will start at VARARGPREP instruction */
+ else if (!(ci->callstatus & CIST_HOOKYIELD)) /* not yieded? */
+ luaD_hookcall(L, ci); /* check 'call' hook */
+ }
+ return 1; /* keep 'trap' on */
+}
+
+
+/*
** Traces the execution of a Lua function. Called before the execution
** of each opcode, when debug is on. 'L->oldpc' stores the last
** instruction traced, to detect line changes. When entering a new
@@ -888,12 +936,12 @@ int luaG_traceexec (lua_State *L, const Instruction *pc) {
}
pc++; /* reference is always next instruction */
ci->u.l.savedpc = pc; /* save 'pc' */
- counthook = (--L->hookcount == 0 && (mask & LUA_MASKCOUNT));
+ counthook = (mask & LUA_MASKCOUNT) && (--L->hookcount == 0);
if (counthook)
resethookcount(L); /* reset count */
else if (!(mask & LUA_MASKLINE))
return 1; /* no line hook and count != 0; nothing to be done now */
- if (ci->callstatus & CIST_HOOKYIELD) { /* called hook last time? */
+ if (ci->callstatus & CIST_HOOKYIELD) { /* hook yielded last time? */
ci->callstatus &= ~CIST_HOOKYIELD; /* erase mark */
return 1; /* do not call hook again (VM yielded, so it did not move) */
}
@@ -915,7 +963,6 @@ int luaG_traceexec (lua_State *L, const Instruction *pc) {
if (L->status == LUA_YIELD) { /* did hook yield? */
if (counthook)
L->hookcount = 1; /* undo decrement to zero */
- ci->u.l.savedpc--; /* undo increment (resume will increment it again) */
ci->callstatus |= CIST_HOOKYIELD; /* mark that it yielded */
luaD_throw(L, LUA_YIELD);
}
diff --git a/contrib/lua/src/ldebug.h b/contrib/lua/src/ldebug.h
index 2c3074c61b6f..2bfce3cb5e77 100644
--- a/contrib/lua/src/ldebug.h
+++ b/contrib/lua/src/ldebug.h
@@ -58,6 +58,7 @@ LUAI_FUNC const char *luaG_addinfo (lua_State *L, const char *msg,
TString *src, int line);
LUAI_FUNC l_noret luaG_errormsg (lua_State *L);
LUAI_FUNC int luaG_traceexec (lua_State *L, const Instruction *pc);
+LUAI_FUNC int luaG_tracecall (lua_State *L);
#endif
diff --git a/contrib/lua/src/ldo.c b/contrib/lua/src/ldo.c
index 2a0017ca62a3..c92573d6e699 100644
--- a/contrib/lua/src/ldo.c
+++ b/contrib/lua/src/ldo.c
@@ -94,10 +94,6 @@ void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop) {
setsvalue2s(L, oldtop, G(L)->memerrmsg); /* reuse preregistered msg. */
break;
}
- case LUA_ERRERR: {
- setsvalue2s(L, oldtop, luaS_newliteral(L, "error in error handling"));
- break;
- }
case LUA_OK: { /* special case only for closing upvalues */
setnilvalue(s2v(oldtop)); /* no error message */
break;
@@ -120,6 +116,7 @@ l_noret luaD_throw (lua_State *L, int errcode) {
else { /* thread has no error handler */
global_State *g = G(L);
errcode = luaE_resetthread(L, errcode); /* close all upvalues */
+ L->status = errcode;
if (g->mainthread->errorJmp) { /* main thread has a handler? */
setobjs2s(L, g->mainthread->top.p++, L->top.p - 1); /* copy error obj. */
luaD_throw(g->mainthread, errcode); /* re-throw in main thread */
@@ -198,6 +195,16 @@ static void correctstack (lua_State *L) {
/* some space for error handling */
#define ERRORSTACKSIZE (LUAI_MAXSTACK + 200)
+
+/* raise an error while running the message handler */
+l_noret luaD_errerr (lua_State *L) {
+ TString *msg = luaS_newliteral(L, "error in error handling");
+ setsvalue2s(L, L->top.p, msg);
+ L->top.p++; /* assume EXTRA_STACK */
+ luaD_throw(L, LUA_ERRERR);
+}
+
+
/*
** Reallocate the stack to a new size, correcting all pointers into it.
** In ISO C, any pointer use after the pointer has been deallocated is
@@ -247,7 +254,7 @@ int luaD_growstack (lua_State *L, int n, int raiseerror) {
a stack error; cannot grow further than that. */
lua_assert(stacksize(L) == ERRORSTACKSIZE);
if (raiseerror)
- luaD_throw(L, LUA_ERRERR); /* error inside message handler */
+ luaD_errerr(L); /* error inside message handler */
return 0; /* if not 'raiseerror', just signal it */
}
else if (n < LUAI_MAXSTACK) { /* avoids arithmetic overflows */
@@ -409,7 +416,7 @@ static void rethook (lua_State *L, CallInfo *ci, int nres) {
** stack, below original 'func', so that 'luaD_precall' can call it. Raise
** an error if there is no '__call' metafield.
*/
-StkId luaD_tryfuncTM (lua_State *L, StkId func) {
+static StkId tryfuncTM (lua_State *L, StkId func) {
const TValue *tm;
StkId p;
checkstackGCp(L, 1, func); /* space for metamethod */
@@ -568,7 +575,7 @@ int luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func,
return -1;
}
default: { /* not a function */
- func = luaD_tryfuncTM(L, func); /* try to get '__call' metamethod */
+ func = tryfuncTM(L, func); /* try to get '__call' metamethod */
/* return luaD_pretailcall(L, ci, func, narg1 + 1, delta); */
narg1++;
goto retry; /* try again */
@@ -609,7 +616,7 @@ CallInfo *luaD_precall (lua_State *L, StkId func, int nresults) {
return ci;
}
default: { /* not a function */
- func = luaD_tryfuncTM(L, func); /* try to get '__call' metamethod */
+ func = tryfuncTM(L, func); /* try to get '__call' metamethod */
/* return luaD_precall(L, func, nresults); */
goto retry; /* try again with metamethod */
}
@@ -792,6 +799,10 @@ static void resume (lua_State *L, void *ud) {
lua_assert(L->status == LUA_YIELD);
L->status = LUA_OK; /* mark that it is running (again) */
if (isLua(ci)) { /* yielded inside a hook? */
+ /* undo increment made by 'luaG_traceexec': instruction was not
+ executed yet */
+ lua_assert(ci->callstatus & CIST_HOOKYIELD);
+ ci->u.l.savedpc--;
L->top.p = firstArg; /* discard arguments */
luaV_execute(L, ci); /* just continue running Lua code */
}
diff --git a/contrib/lua/src/ldo.h b/contrib/lua/src/ldo.h
index 1aa446ad09e9..4de9540ec807 100644
--- a/contrib/lua/src/ldo.h
+++ b/contrib/lua/src/ldo.h
@@ -60,6 +60,7 @@
/* type of protected functions, to be ran by 'runprotected' */
typedef void (*Pfunc) (lua_State *L, void *ud);
+LUAI_FUNC l_noret luaD_errerr (lua_State *L);
LUAI_FUNC void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop);
LUAI_FUNC int luaD_protectedparser (lua_State *L, ZIO *z, const char *name,
const char *mode);
@@ -71,7 +72,6 @@ LUAI_FUNC int luaD_pretailcall (lua_State *L, CallInfo *ci, StkId func,
LUAI_FUNC CallInfo *luaD_precall (lua_State *L, StkId func, int nResults);
LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults);
LUAI_FUNC void luaD_callnoyield (lua_State *L, StkId func, int nResults);
-LUAI_FUNC StkId luaD_tryfuncTM (lua_State *L, StkId func);
LUAI_FUNC int luaD_closeprotected (lua_State *L, ptrdiff_t level, int status);
LUAI_FUNC int luaD_pcall (lua_State *L, Pfunc func, void *u,
ptrdiff_t oldtop, ptrdiff_t ef);
diff --git a/contrib/lua/src/lgc.c b/contrib/lua/src/lgc.c
index a3094ff57126..5817f9eec35a 100644
--- a/contrib/lua/src/lgc.c
+++ b/contrib/lua/src/lgc.c
@@ -542,10 +542,12 @@ static void traversestrongtable (global_State *g, Table *h) {
static lu_mem traversetable (global_State *g, Table *h) {
const char *weakkey, *weakvalue;
const TValue *mode = gfasttm(g, h->metatable, TM_MODE);
+ TString *smode;
markobjectN(g, h->metatable);
- if (mode && ttisstring(mode) && /* is there a weak mode? */
- (cast_void(weakkey = strchr(svalue(mode), 'k')),
- cast_void(weakvalue = strchr(svalue(mode), 'v')),
+ if (mode && ttisshrstring(mode) && /* is there a weak mode? */
+ (cast_void(smode = tsvalue(mode)),
+ cast_void(weakkey = strchr(getshrstr(smode), 'k')),
+ cast_void(weakvalue = strchr(getshrstr(smode), 'v')),
(weakkey || weakvalue))) { /* is really weak? */
if (!weakkey) /* strong keys? */
traverseweakvalue(g, h);
@@ -638,7 +640,9 @@ static int traversethread (global_State *g, lua_State *th) {
for (uv = th->openupval; uv != NULL; uv = uv->u.open.next)
markobject(g, uv); /* open upvalues cannot be collected */
if (g->gcstate == GCSatomic) { /* final traversal? */
- for (; o < th->stack_last.p + EXTRA_STACK; o++)
+ if (!g->gcemergency)
+ luaD_shrinkstack(th); /* do not change stack in emergency cycle */
+ for (o = th->top.p; o < th->stack_last.p + EXTRA_STACK; o++)
setnilvalue(s2v(o)); /* clear dead stack slice */
/* 'remarkupvals' may have removed thread from 'twups' list */
if (!isintwups(th) && th->openupval != NULL) {
@@ -646,8 +650,6 @@ static int traversethread (global_State *g, lua_State *th) {
g->twups = th;
}
}
- else if (!g->gcemergency)
- luaD_shrinkstack(th); /* do not change stack in emergency cycle */
return 1 + stacksize(th);
}
@@ -1409,7 +1411,7 @@ static void stepgenfull (lua_State *L, global_State *g) {
setminordebt(g);
}
else { /* another bad collection; stay in incremental mode */
- g->GCestimate = gettotalbytes(g); /* first estimate */;
+ g->GCestimate = gettotalbytes(g); /* first estimate */
entersweep(L);
luaC_runtilstate(L, bitmask(GCSpause)); /* finish collection */
setpause(g);
@@ -1604,7 +1606,7 @@ static lu_mem singlestep (lua_State *L) {
case GCSenteratomic: {
work = atomic(L); /* work is what was traversed by 'atomic' */
entersweep(L);
- g->GCestimate = gettotalbytes(g); /* first estimate */;
+ g->GCestimate = gettotalbytes(g); /* first estimate */
break;
}
case GCSswpallgc: { /* sweep "regular" objects */
@@ -1710,6 +1712,8 @@ static void fullinc (lua_State *L, global_State *g) {
entersweep(L); /* sweep everything to turn them back to white */
/* finish any pending sweep phase to start a new cycle */
luaC_runtilstate(L, bitmask(GCSpause));
+ luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */
+ g->gcstate = GCSenteratomic; /* go straight to atomic phase */
luaC_runtilstate(L, bitmask(GCScallfin)); /* run up to finalizers */
/* estimate must be correct after a full GC cycle */
lua_assert(g->GCestimate == gettotalbytes(g));
diff --git a/contrib/lua/src/liolib.c b/contrib/lua/src/liolib.c
index b08397da45da..c5075f3e78a9 100644
--- a/contrib/lua/src/liolib.c
+++ b/contrib/lua/src/liolib.c
@@ -245,8 +245,8 @@ static int f_gc (lua_State *L) {
*/
static int io_fclose (lua_State *L) {
LStream *p = tolstream(L);
- int res = fclose(p->f);
- return luaL_fileresult(L, (res == 0), NULL);
+ errno = 0;
+ return luaL_fileresult(L, (fclose(p->f) == 0), NULL);
}
@@ -272,6 +272,7 @@ static int io_open (lua_State *L) {
LStream *p = newfile(L);
const char *md = mode; /* to traverse/check mode */
luaL_argcheck(L, l_checkmode(md), 2, "invalid mode");
+ errno = 0;
p->f = fopen(filename, mode);
return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1;
}
@@ -292,6 +293,7 @@ static int io_popen (lua_State *L) {
const char *mode = luaL_optstring(L, 2, "r");
LStream *p = newprefile(L);
luaL_argcheck(L, l_checkmodep(mode), 2, "invalid mode");
+ errno = 0;
p->f = l_popen(L, filename, mode);
p->closef = &io_pclose;
return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1;
@@ -300,6 +302,7 @@ static int io_popen (lua_State *L) {
static int io_tmpfile (lua_State *L) {
LStream *p = newfile(L);
+ errno = 0;
p->f = tmpfile();
return (p->f == NULL) ? luaL_fileresult(L, 0, NULL) : 1;
}
@@ -567,6 +570,7 @@ static int g_read (lua_State *L, FILE *f, int first) {
int nargs = lua_gettop(L) - 1;
int n, success;
clearerr(f);
+ errno = 0;
if (nargs == 0) { /* no arguments? */
success = read_line(L, f, 1);
n = first + 1; /* to return 1 result */
@@ -660,6 +664,7 @@ static int io_readline (lua_State *L) {
static int g_write (lua_State *L, FILE *f, int arg) {
int nargs = lua_gettop(L) - arg;
int status = 1;
+ errno = 0;
for (; nargs--; arg++) {
if (lua_type(L, arg) == LUA_TNUMBER) {
/* optimization: could be done exactly as for strings */
@@ -678,7 +683,8 @@ static int g_write (lua_State *L, FILE *f, int arg) {
}
if (l_likely(status))
return 1; /* file handle already on stack top */
- else return luaL_fileresult(L, status, NULL);
+ else
+ return luaL_fileresult(L, status, NULL);
}
@@ -703,6 +709,7 @@ static int f_seek (lua_State *L) {
l_seeknum offset = (l_seeknum)p3;
luaL_argcheck(L, (lua_Integer)offset == p3, 3,
"not an integer in proper range");
+ errno = 0;
op = l_fseek(f, offset, mode[op]);
if (l_unlikely(op))
return luaL_fileresult(L, 0, NULL); /* error */
@@ -719,19 +726,25 @@ static int f_setvbuf (lua_State *L) {
FILE *f = tofile(L);
int op = luaL_checkoption(L, 2, NULL, modenames);
lua_Integer sz = luaL_optinteger(L, 3, LUAL_BUFFERSIZE);
- int res = setvbuf(f, NULL, mode[op], (size_t)sz);
+ int res;
+ errno = 0;
+ res = setvbuf(f, NULL, mode[op], (size_t)sz);
return luaL_fileresult(L, res == 0, NULL);
}
static int io_flush (lua_State *L) {
- return luaL_fileresult(L, fflush(getiofile(L, IO_OUTPUT)) == 0, NULL);
+ FILE *f = getiofile(L, IO_OUTPUT);
+ errno = 0;
+ return luaL_fileresult(L, fflush(f) == 0, NULL);
}
static int f_flush (lua_State *L) {
- return luaL_fileresult(L, fflush(tofile(L)) == 0, NULL);
+ FILE *f = tofile(L);
+ errno = 0;
+ return luaL_fileresult(L, fflush(f) == 0, NULL);
}
@@ -773,7 +786,7 @@ static const luaL_Reg meth[] = {
** metamethods for file handles
*/
static const luaL_Reg metameth[] = {
- {"__index", NULL}, /* place holder */
+ {"__index", NULL}, /* placeholder */
{"__gc", f_gc},
{"__close", f_gc},
{"__tostring", f_tostring},
diff --git a/contrib/lua/src/lmathlib.c b/contrib/lua/src/lmathlib.c
index d0b1e1e5d6f5..438106348084 100644
--- a/contrib/lua/src/lmathlib.c
+++ b/contrib/lua/src/lmathlib.c
@@ -249,6 +249,15 @@ static int math_type (lua_State *L) {
** ===================================================================
*/
+/*
+** This code uses lots of shifts. ANSI C does not allow shifts greater
+** than or equal to the width of the type being shifted, so some shifts
+** are written in convoluted ways to match that restriction. For
+** preprocessor tests, it assumes a width of 32 bits, so the maximum
+** shift there is 31 bits.
+*/
+
+
/* number of binary digits in the mantissa of a float */
#define FIGS l_floatatt(MANT_DIG)
@@ -271,16 +280,19 @@ static int math_type (lua_State *L) {
/* 'long' has at least 64 bits */
#define Rand64 unsigned long
+#define SRand64 long
#elif !defined(LUA_USE_C89) && defined(LLONG_MAX)
/* there is a 'long long' type (which must have at least 64 bits) */
#define Rand64 unsigned long long
+#define SRand64 long long
#elif ((LUA_MAXUNSIGNED >> 31) >> 31) >= 3
/* 'lua_Unsigned' has at least 64 bits */
#define Rand64 lua_Unsigned
+#define SRand64 lua_Integer
#endif
@@ -319,23 +331,30 @@ static Rand64 nextrand (Rand64 *state) {
}
-/* must take care to not shift stuff by more than 63 slots */
-
-
/*
** Convert bits from a random integer into a float in the
** interval [0,1), getting the higher FIG bits from the
** random unsigned integer and converting that to a float.
+** Some old Microsoft compilers cannot cast an unsigned long
+** to a floating-point number, so we use a signed long as an
+** intermediary. When lua_Number is float or double, the shift ensures
+** that 'sx' is non negative; in that case, a good compiler will remove
+** the correction.
*/
/* must throw out the extra (64 - FIGS) bits */
#define shift64_FIG (64 - FIGS)
-/* to scale to [0, 1), multiply by scaleFIG = 2^(-FIGS) */
+/* 2^(-FIGS) == 2^-1 / 2^(FIGS-1) */
#define scaleFIG (l_mathop(0.5) / ((Rand64)1 << (FIGS - 1)))
static lua_Number I2d (Rand64 x) {
- return (lua_Number)(trim64(x) >> shift64_FIG) * scaleFIG;
+ SRand64 sx = (SRand64)(trim64(x) >> shift64_FIG);
+ lua_Number res = (lua_Number)(sx) * scaleFIG;
+ if (sx < 0)
+ res += l_mathop(1.0); /* correct the two's complement if negative */
+ lua_assert(0 <= res && res < 1);
+ return res;
}
/* convert a 'Rand64' to a 'lua_Unsigned' */
@@ -471,8 +490,6 @@ static lua_Number I2d (Rand64 x) {
#else /* 32 < FIGS <= 64 */
-/* must take care to not shift stuff by more than 31 slots */
-
/* 2^(-FIGS) = 1.0 / 2^30 / 2^3 / 2^(FIGS-33) */
#define scaleFIG \
(l_mathop(1.0) / (UONE << 30) / l_mathop(8.0) / (UONE << (FIGS - 33)))
diff --git a/contrib/lua/src/loadlib.c b/contrib/lua/src/loadlib.c
index d792dffaa03b..6d289fcebb8c 100644
--- a/contrib/lua/src/loadlib.c
+++ b/contrib/lua/src/loadlib.c
@@ -25,15 +25,6 @@
/*
-** LUA_IGMARK is a mark to ignore all before it when building the
-** luaopen_ function name.
-*/
-#if !defined (LUA_IGMARK)
-#define LUA_IGMARK "-"
-#endif
-
-
-/*
** LUA_CSUBSEP is the character that replaces dots in submodule names
** when searching for a C loader.
** LUA_LSUBSEP is the character that replaces dots in submodule names
diff --git a/contrib/lua/src/lobject.c b/contrib/lua/src/lobject.c
index f73ffc6d92bd..9cfa5227eb46 100644
--- a/contrib/lua/src/lobject.c
+++ b/contrib/lua/src/lobject.c
@@ -542,7 +542,7 @@ const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) {
addstr2buff(&buff, fmt, strlen(fmt)); /* rest of 'fmt' */
clearbuff(&buff); /* empty buffer into the stack */
lua_assert(buff.pushed == 1);
- return svalue(s2v(L->top.p - 1));
+ return getstr(tsvalue(s2v(L->top.p - 1)));
}
diff --git a/contrib/lua/src/lobject.h b/contrib/lua/src/lobject.h
index 556608e4aa21..980e42f8c27a 100644
--- a/contrib/lua/src/lobject.h
+++ b/contrib/lua/src/lobject.h
@@ -386,7 +386,7 @@ typedef struct GCObject {
typedef struct TString {
CommonHeader;
lu_byte extra; /* reserved words for short strings; "has hash" for longs */
- lu_byte shrlen; /* length for short strings */
+ lu_byte shrlen; /* length for short strings, 0xFF for long strings */
unsigned int hash;
union {
size_t lnglen; /* length for long strings */
@@ -398,19 +398,17 @@ typedef struct TString {
/*
-** Get the actual string (array of bytes) from a 'TString'.
+** Get the actual string (array of bytes) from a 'TString'. (Generic
+** version and specialized versions for long and short strings.)
*/
-#define getstr(ts) ((ts)->contents)
+#define getstr(ts) ((ts)->contents)
+#define getlngstr(ts) check_exp((ts)->shrlen == 0xFF, (ts)->contents)
+#define getshrstr(ts) check_exp((ts)->shrlen != 0xFF, (ts)->contents)
-/* get the actual string (array of bytes) from a Lua value */
-#define svalue(o) getstr(tsvalue(o))
-
/* get string length from 'TString *s' */
-#define tsslen(s) ((s)->tt == LUA_VSHRSTR ? (s)->shrlen : (s)->u.lnglen)
-
-/* get string length from 'TValue *o' */
-#define vslen(o) tsslen(tsvalue(o))
+#define tsslen(s) \
+ ((s)->shrlen != 0xFF ? (s)->shrlen : (s)->u.lnglen)
/* }================================================================== */
diff --git a/contrib/lua/src/lopcodes.h b/contrib/lua/src/lopcodes.h
index 4c55145399ff..46911cac14e0 100644
--- a/contrib/lua/src/lopcodes.h
+++ b/contrib/lua/src/lopcodes.h
@@ -210,15 +210,15 @@ OP_LOADNIL,/* A B R[A], R[A+1], ..., R[A+B] := nil */
OP_GETUPVAL,/* A B R[A] := UpValue[B] */
OP_SETUPVAL,/* A B UpValue[B] := R[A] */
-OP_GETTABUP,/* A B C R[A] := UpValue[B][K[C]:string] */
+OP_GETTABUP,/* A B C R[A] := UpValue[B][K[C]:shortstring] */
OP_GETTABLE,/* A B C R[A] := R[B][R[C]] */
OP_GETI,/* A B C R[A] := R[B][C] */
-OP_GETFIELD,/* A B C R[A] := R[B][K[C]:string] */
+OP_GETFIELD,/* A B C R[A] := R[B][K[C]:shortstring] */
-OP_SETTABUP,/* A B C UpValue[A][K[B]:string] := RK(C) */
+OP_SETTABUP,/* A B C UpValue[A][K[B]:shortstring] := RK(C) */
OP_SETTABLE,/* A B C R[A][R[B]] := RK(C) */
OP_SETI,/* A B C R[A][B] := RK(C) */
-OP_SETFIELD,/* A B C R[A][K[B]:string] := RK(C) */
+OP_SETFIELD,/* A B C R[A][K[B]:shortstring] := RK(C) */
OP_NEWTABLE,/* A B C k R[A] := {} */
diff --git a/contrib/lua/src/loslib.c b/contrib/lua/src/loslib.c
index ad5a92768852..ba80d72c4575 100644
--- a/contrib/lua/src/loslib.c
+++ b/contrib/lua/src/loslib.c
@@ -155,6 +155,7 @@ static int os_execute (lua_State *L) {
static int os_remove (lua_State *L) {
const char *filename = luaL_checkstring(L, 1);
+ errno = 0;
return luaL_fileresult(L, remove(filename) == 0, filename);
}
@@ -162,6 +163,7 @@ static int os_remove (lua_State *L) {
static int os_rename (lua_State *L) {
const char *fromname = luaL_checkstring(L, 1);
const char *toname = luaL_checkstring(L, 2);
+ errno = 0;
return luaL_fileresult(L, rename(fromname, toname) == 0, NULL);
}
diff --git a/contrib/lua/src/lparser.c b/contrib/lua/src/lparser.c
index b745f236f068..1ac82990e0c3 100644
--- a/contrib/lua/src/lparser.c
+++ b/contrib/lua/src/lparser.c
@@ -198,7 +198,7 @@ static int new_localvar (LexState *ls, TString *name) {
checklimit(fs, dyd->actvar.n + 1 - fs->firstlocal,
MAXVARS, "local variables");
luaM_growvector(L, dyd->actvar.arr, dyd->actvar.n + 1,
- dyd->actvar.size, Vardesc, USHRT_MAX, "local variables");
+ dyd->actvar.size, Vardesc, SHRT_MAX, "local variables");
var = &dyd->actvar.arr[dyd->actvar.n++];
var->vd.kind = VDKREG; /* default */
var->vd.name = name;
@@ -849,12 +849,11 @@ static void recfield (LexState *ls, ConsControl *cc) {
FuncState *fs = ls->fs;
int reg = ls->fs->freereg;
expdesc tab, key, val;
- if (ls->t.token == TK_NAME) {
- checklimit(fs, cc->nh, MAX_INT, "items in a constructor");
+ if (ls->t.token == TK_NAME)
codename(ls, &key);
- }
else /* ls->t.token == '[' */
yindex(ls, &key);
+ checklimit(fs, cc->nh, MAX_INT, "items in a constructor");
cc->nh++;
checknext(ls, '=');
tab = *cc->t;
@@ -1022,10 +1021,11 @@ static int explist (LexState *ls, expdesc *v) {
}
-static void funcargs (LexState *ls, expdesc *f, int line) {
+static void funcargs (LexState *ls, expdesc *f) {
FuncState *fs = ls->fs;
expdesc args;
int base, nparams;
+ int line = ls->linenumber;
switch (ls->t.token) {
case '(': { /* funcargs -> '(' [ explist ] ')' */
luaX_next(ls);
@@ -1063,8 +1063,8 @@ static void funcargs (LexState *ls, expdesc *f, int line) {
}
init_exp(f, VCALL, luaK_codeABC(fs, OP_CALL, base, nparams+1, 2));
luaK_fixline(fs, line);
- fs->freereg = base+1; /* call remove function and arguments and leaves
- (unless changed) one result */
+ fs->freereg = base+1; /* call removes function and arguments and leaves
+ one result (unless changed later) */
}
@@ -1103,7 +1103,6 @@ static void suffixedexp (LexState *ls, expdesc *v) {
/* suffixedexp ->
primaryexp { '.' NAME | '[' exp ']' | ':' NAME funcargs | funcargs } */
FuncState *fs = ls->fs;
- int line = ls->linenumber;
primaryexp(ls, v);
for (;;) {
switch (ls->t.token) {
@@ -1123,12 +1122,12 @@ static void suffixedexp (LexState *ls, expdesc *v) {
luaX_next(ls);
codename(ls, &key);
luaK_self(fs, v, &key);
- funcargs(ls, v, line);
+ funcargs(ls, v);
break;
}
case '(': case TK_STRING: case '{': { /* funcargs */
luaK_exp2nextreg(fs, v);
- funcargs(ls, v, line);
+ funcargs(ls, v);
break;
}
default: return;
diff --git a/contrib/lua/src/lstate.c b/contrib/lua/src/lstate.c
index 1e925e5ad4cb..f3f2ccfdd5fb 100644
--- a/contrib/lua/src/lstate.c
+++ b/contrib/lua/src/lstate.c
@@ -119,7 +119,7 @@ CallInfo *luaE_extendCI (lua_State *L) {
/*
** free all CallInfo structures not in use by a thread
*/
-void luaE_freeCI (lua_State *L) {
+static void freeCI (lua_State *L) {
CallInfo *ci = L->ci;
CallInfo *next = ci->next;
ci->next = NULL;
@@ -166,7 +166,7 @@ void luaE_checkcstack (lua_State *L) {
if (getCcalls(L) == LUAI_MAXCCALLS)
luaG_runerror(L, "C stack overflow");
else if (getCcalls(L) >= (LUAI_MAXCCALLS / 10 * 11))
- luaD_throw(L, LUA_ERRERR); /* error while handling stack error */
+ luaD_errerr(L); /* error while handling stack error */
}
@@ -204,7 +204,7 @@ static void freestack (lua_State *L) {
if (L->stack.p == NULL)
return; /* stack not completely built yet */
L->ci = &L->base_ci; /* free the entire 'ci' list */
- luaE_freeCI(L);
+ freeCI(L);
lua_assert(L->nci == 0);
luaM_freearray(L, L->stack.p, stacksize(L) + EXTRA_STACK); /* free stack */
}
@@ -272,7 +272,9 @@ static void close_state (lua_State *L) {
luaC_freeallobjects(L); /* just collect its objects */
else { /* closing a fully built state */
L->ci = &L->base_ci; /* unwind CallInfo list */
+ L->errfunc = 0; /* stack unwind can "throw away" the error function */
luaD_closeprotected(L, 1, LUA_OK); /* close all upvalues */
+ L->top.p = L->stack.p + 1; /* empty the stack to run finalizers */
luaC_freeallobjects(L); /* collect all objects */
luai_userstateclose(L);
}
@@ -328,6 +330,7 @@ int luaE_resetthread (lua_State *L, int status) {
if (status == LUA_YIELD)
status = LUA_OK;
L->status = LUA_OK; /* so it can run __close metamethods */
+ L->errfunc = 0; /* stack unwind can "throw away" the error function */
status = luaD_closeprotected(L, 1, status);
if (status != LUA_OK) /* errors? */
luaD_seterrorobj(L, status, L->stack.p + 1);
@@ -433,7 +436,7 @@ void luaE_warning (lua_State *L, const char *msg, int tocont) {
void luaE_warnerror (lua_State *L, const char *where) {
TValue *errobj = s2v(L->top.p - 1); /* error object */
const char *msg = (ttisstring(errobj))
- ? svalue(errobj)
+ ? getstr(tsvalue(errobj))
: "error object is not a string";
/* produce warning "error in %s (%s)" (where, msg) */
luaE_warning(L, "error in ", 1);
diff --git a/contrib/lua/src/lstate.h b/contrib/lua/src/lstate.h
index 8bf6600e3441..007704c826be 100644
--- a/contrib/lua/src/lstate.h
+++ b/contrib/lua/src/lstate.h
@@ -181,7 +181,7 @@ struct CallInfo {
union {
struct { /* only for Lua functions */
const Instruction *savedpc;
- volatile l_signalT trap;
+ volatile l_signalT trap; /* function is tracing lines/counts */
int nextraargs; /* # of extra arguments in vararg functions */
} l;
struct { /* only for C functions */
@@ -396,7 +396,6 @@ union GCUnion {
LUAI_FUNC void luaE_setdebt (global_State *g, l_mem debt);
LUAI_FUNC void luaE_freethread (lua_State *L, lua_State *L1);
LUAI_FUNC CallInfo *luaE_extendCI (lua_State *L);
-LUAI_FUNC void luaE_freeCI (lua_State *L);
LUAI_FUNC void luaE_shrinkCI (lua_State *L);
LUAI_FUNC void luaE_checkcstack (lua_State *L);
LUAI_FUNC void luaE_incCstack (lua_State *L);
diff --git a/contrib/lua/src/lstring.c b/contrib/lua/src/lstring.c
index 13dcaf4259bc..97757355c0b6 100644
--- a/contrib/lua/src/lstring.c
+++ b/contrib/lua/src/lstring.c
@@ -36,7 +36,7 @@ int luaS_eqlngstr (TString *a, TString *b) {
lua_assert(a->tt == LUA_VLNGSTR && b->tt == LUA_VLNGSTR);
return (a == b) || /* same instance or... */
((len == b->u.lnglen) && /* equal length and ... */
- (memcmp(getstr(a), getstr(b), len) == 0)); /* equal contents */
+ (memcmp(getlngstr(a), getlngstr(b), len) == 0)); /* equal contents */
}
@@ -52,7 +52,7 @@ unsigned int luaS_hashlongstr (TString *ts) {
lua_assert(ts->tt == LUA_VLNGSTR);
if (ts->extra == 0) { /* no hash? */
size_t len = ts->u.lnglen;
- ts->hash = luaS_hash(getstr(ts), len, ts->hash);
+ ts->hash = luaS_hash(getlngstr(ts), len, ts->hash);
ts->extra = 1; /* now it has its hash */
}
return ts->hash;
@@ -157,6 +157,7 @@ static TString *createstrobj (lua_State *L, size_t l, int tag, unsigned int h) {
TString *luaS_createlngstrobj (lua_State *L, size_t l) {
TString *ts = createstrobj(L, l, LUA_VLNGSTR, G(L)->seed);
ts->u.lnglen = l;
+ ts->shrlen = 0xFF; /* signals that it is a long string */
return ts;
}
@@ -193,7 +194,7 @@ static TString *internshrstr (lua_State *L, const char *str, size_t l) {
TString **list = &tb->hash[lmod(h, tb->size)];
lua_assert(str != NULL); /* otherwise 'memcmp'/'memcpy' are undefined */
for (ts = *list; ts != NULL; ts = ts->u.hnext) {
- if (l == ts->shrlen && (memcmp(str, getstr(ts), l * sizeof(char)) == 0)) {
+ if (l == ts->shrlen && (memcmp(str, getshrstr(ts), l * sizeof(char)) == 0)) {
/* found! */
if (isdead(g, ts)) /* dead (but not collected yet)? */
changewhite(ts); /* resurrect it */
@@ -206,8 +207,8 @@ static TString *internshrstr (lua_State *L, const char *str, size_t l) {
list = &tb->hash[lmod(h, tb->size)]; /* rehash with new size */
}
ts = createstrobj(L, l, LUA_VSHRSTR, h);
- memcpy(getstr(ts), str, l * sizeof(char));
ts->shrlen = cast_byte(l);
+ memcpy(getshrstr(ts), str, l * sizeof(char));
ts->u.hnext = *list;
*list = ts;
tb->nuse++;
@@ -223,10 +224,10 @@ TString *luaS_newlstr (lua_State *L, const char *str, size_t l) {
return internshrstr(L, str, l);
else {
TString *ts;
- if (l_unlikely(l >= (MAX_SIZE - sizeof(TString))/sizeof(char)))
+ if (l_unlikely(l * sizeof(char) >= (MAX_SIZE - sizeof(TString))))
luaM_toobig(L);
ts = luaS_createlngstrobj(L, l);
- memcpy(getstr(ts), str, l * sizeof(char));
+ memcpy(getlngstr(ts), str, l * sizeof(char));
return ts;
}
}
diff --git a/contrib/lua/src/ltable.c b/contrib/lua/src/ltable.c
index 3c690c5f1751..3353c047939a 100644
--- a/contrib/lua/src/ltable.c
+++ b/contrib/lua/src/ltable.c
@@ -252,7 +252,7 @@ LUAI_FUNC unsigned int luaH_realasize (const Table *t) {
return t->alimit; /* this is the size */
else {
unsigned int size = t->alimit;
- /* compute the smallest power of 2 not smaller than 'n' */
+ /* compute the smallest power of 2 not smaller than 'size' */
size |= (size >> 1);
size |= (size >> 2);
size |= (size >> 4);
@@ -662,7 +662,8 @@ static Node *getfreepos (Table *t) {
** put new key in its main position; otherwise (colliding node is in its main
** position), new key goes to an empty position.
*/
-void luaH_newkey (lua_State *L, Table *t, const TValue *key, TValue *value) {
+static void luaH_newkey (lua_State *L, Table *t, const TValue *key,
+ TValue *value) {
Node *mp;
TValue aux;
if (l_unlikely(ttisnil(key)))
@@ -721,22 +722,36 @@ void luaH_newkey (lua_State *L, Table *t, const TValue *key, TValue *value) {
/*
** Search function for integers. If integer is inside 'alimit', get it
-** directly from the array part. Otherwise, if 'alimit' is not equal to
-** the real size of the array, key still can be in the array part. In
-** this case, try to avoid a call to 'luaH_realasize' when key is just
-** one more than the limit (so that it can be incremented without
-** changing the real size of the array).
+** directly from the array part. Otherwise, if 'alimit' is not
+** the real size of the array, the key still can be in the array part.
+** In this case, do the "Xmilia trick" to check whether 'key-1' is
+** smaller than the real size.
+** The trick works as follow: let 'p' be an integer such that
+** '2^(p+1) >= alimit > 2^p', or '2^(p+1) > alimit-1 >= 2^p'.
+** That is, 2^(p+1) is the real size of the array, and 'p' is the highest
+** bit on in 'alimit-1'. What we have to check becomes 'key-1 < 2^(p+1)'.
+** We compute '(key-1) & ~(alimit-1)', which we call 'res'; it will
+** have the 'p' bit cleared. If the key is outside the array, that is,
+** 'key-1 >= 2^(p+1)', then 'res' will have some bit on higher than 'p',
+** therefore it will be larger or equal to 'alimit', and the check
+** will fail. If 'key-1 < 2^(p+1)', then 'res' has no bit on higher than
+** 'p', and as the bit 'p' itself was cleared, 'res' will be smaller
+** than 2^p, therefore smaller than 'alimit', and the check succeeds.
+** As special cases, when 'alimit' is 0 the condition is trivially false,
+** and when 'alimit' is 1 the condition simplifies to 'key-1 < alimit'.
+** If key is 0 or negative, 'res' will have its higher bit on, so that
+** if cannot be smaller than alimit.
*/
const TValue *luaH_getint (Table *t, lua_Integer key) {
- if (l_castS2U(key) - 1u < t->alimit) /* 'key' in [1, t->alimit]? */
+ lua_Unsigned alimit = t->alimit;
+ if (l_castS2U(key) - 1u < alimit) /* 'key' in [1, t->alimit]? */
return &t->array[key - 1];
- else if (!limitequalsasize(t) && /* key still may be in the array part? */
- (l_castS2U(key) == t->alimit + 1 ||
- l_castS2U(key) - 1u < luaH_realasize(t))) {
+ else if (!isrealasize(t) && /* key still may be in the array part? */
+ (((l_castS2U(key) - 1u) & ~(alimit - 1u)) < alimit)) {
t->alimit = cast_uint(key); /* probably '#t' is here now */
return &t->array[key - 1];
}
- else {
+ else { /* key is not in the array part; check the hash */
Node *n = hashint(t, key);
for (;;) { /* check whether 'key' is somewhere in the chain */
if (keyisinteger(n) && keyival(n) == key)
diff --git a/contrib/lua/src/ltable.h b/contrib/lua/src/ltable.h
index 75dd9e26e015..8e6890342348 100644
--- a/contrib/lua/src/ltable.h
+++ b/contrib/lua/src/ltable.h
@@ -41,8 +41,6 @@ LUAI_FUNC void luaH_setint (lua_State *L, Table *t, lua_Integer key,
LUAI_FUNC const TValue *luaH_getshortstr (Table *t, TString *key);
LUAI_FUNC const TValue *luaH_getstr (Table *t, TString *key);
LUAI_FUNC const TValue *luaH_get (Table *t, const TValue *key);
-LUAI_FUNC void luaH_newkey (lua_State *L, Table *t, const TValue *key,
- TValue *value);
LUAI_FUNC void luaH_set (lua_State *L, Table *t, const TValue *key,
TValue *value);
LUAI_FUNC void luaH_finishset (lua_State *L, Table *t, const TValue *key,
diff --git a/contrib/lua/src/ltm.h b/contrib/lua/src/ltm.h
index c309e2ae10e3..73b833c605da 100644
--- a/contrib/lua/src/ltm.h
+++ b/contrib/lua/src/ltm.h
@@ -9,7 +9,6 @@
#include "lobject.h"
-#include "lstate.h"
/*
@@ -96,8 +95,8 @@ LUAI_FUNC int luaT_callorderiTM (lua_State *L, const TValue *p1, int v2,
int inv, int isfloat, TMS event);
LUAI_FUNC void luaT_adjustvarargs (lua_State *L, int nfixparams,
- CallInfo *ci, const Proto *p);
-LUAI_FUNC void luaT_getvarargs (lua_State *L, CallInfo *ci,
+ struct CallInfo *ci, const Proto *p);
+LUAI_FUNC void luaT_getvarargs (lua_State *L, struct CallInfo *ci,
StkId where, int wanted);
diff --git a/contrib/lua/src/lua.c b/contrib/lua/src/lua.c
index 0ff884545304..4a90e55dd94b 100644
--- a/contrib/lua/src/lua.c
+++ b/contrib/lua/src/lua.c
@@ -115,12 +115,13 @@ static void l_message (const char *pname, const char *msg) {
/*
** Check whether 'status' is not OK and, if so, prints the error
-** message on the top of the stack. It assumes that the error object
-** is a string, as it was either generated by Lua or by 'msghandler'.
+** message on the top of the stack.
*/
static int report (lua_State *L, int status) {
if (status != LUA_OK) {
const char *msg = lua_tostring(L, -1);
+ if (msg == NULL)
+ msg = "(error message not a string)";
l_message(progname, msg);
lua_pop(L, 1); /* remove message */
}
@@ -210,12 +211,17 @@ static int dostring (lua_State *L, const char *s, const char *name) {
/*
** Receives 'globname[=modname]' and runs 'globname = require(modname)'.
+** If there is no explicit modname and globname contains a '-', cut
+** the suffix after '-' (the "version") to make the global name.
*/
static int dolibrary (lua_State *L, char *globname) {
int status;
+ char *suffix = NULL;
char *modname = strchr(globname, '=');
- if (modname == NULL) /* no explicit name? */
+ if (modname == NULL) { /* no explicit name? */
modname = globname; /* module name is equal to global name */
+ suffix = strchr(modname, *LUA_IGMARK); /* look for a suffix mark */
+ }
else {
*modname = '\0'; /* global name ends here */
modname++; /* module name starts after the '=' */
@@ -223,8 +229,11 @@ static int dolibrary (lua_State *L, char *globname) {
lua_getglobal(L, "require");
lua_pushstring(L, modname);
status = docall(L, 1, 1); /* call 'require(modname)' */
- if (status == LUA_OK)
+ if (status == LUA_OK) {
+ if (suffix != NULL) /* is there a suffix mark? */
+ *suffix = '\0'; /* remove suffix from global name */
lua_setglobal(L, globname); /* globname = require(modname) */
+ }
return report(L, status);
}
@@ -481,10 +490,8 @@ static int incomplete (lua_State *L, int status) {
if (status == LUA_ERRSYNTAX) {
size_t lmsg;
const char *msg = lua_tolstring(L, -1, &lmsg);
- if (lmsg >= marklen && strcmp(msg + lmsg - marklen, EOFMARK) == 0) {
- lua_pop(L, 1);
+ if (lmsg >= marklen && strcmp(msg + lmsg - marklen, EOFMARK) == 0)
return 1;
- }
}
return 0; /* else... */
}
@@ -499,9 +506,9 @@ static int pushline (lua_State *L, int firstline) {
size_t l;
const char *prmt = get_prompt(L, firstline);
int readstatus = lua_readline(L, b, prmt);
- if (readstatus == 0)
- return 0; /* no input (prompt will be popped by caller) */
lua_pop(L, 1); /* remove prompt */
+ if (readstatus == 0)
+ return 0; /* no input */
l = strlen(b);
if (l > 0 && b[l-1] == '\n') /* line ends with newline? */
b[--l] = '\0'; /* remove it */
@@ -543,8 +550,9 @@ static int multiline (lua_State *L) {
int status = luaL_loadbuffer(L, line, len, "=stdin"); /* try it */
if (!incomplete(L, status) || !pushline(L, 0)) {
lua_saveline(L, line); /* keep history */
- return status; /* cannot or should not try to add continuation line */
+ return status; /* should not or cannot try to add continuation line */
}
+ lua_remove(L, -2); /* remove error message (from incomplete line) */
lua_pushliteral(L, "\n"); /* add newline... */
lua_insert(L, -2); /* ...between the two lines */
lua_concat(L, 3); /* join them */
diff --git a/contrib/lua/src/lua.h b/contrib/lua/src/lua.h
index fd16cf8050b8..f3ea590d9cd6 100644
--- a/contrib/lua/src/lua.h
+++ b/contrib/lua/src/lua.h
@@ -18,14 +18,14 @@
#define LUA_VERSION_MAJOR "5"
#define LUA_VERSION_MINOR "4"
-#define LUA_VERSION_RELEASE "6"
+#define LUA_VERSION_RELEASE "8"
#define LUA_VERSION_NUM 504
-#define LUA_VERSION_RELEASE_NUM (LUA_VERSION_NUM * 100 + 6)
+#define LUA_VERSION_RELEASE_NUM (LUA_VERSION_NUM * 100 + 8)
#define LUA_VERSION "Lua " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR
#define LUA_RELEASE LUA_VERSION "." LUA_VERSION_RELEASE
-#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2023 Lua.org, PUC-Rio"
+#define LUA_COPYRIGHT LUA_RELEASE " Copyright (C) 1994-2025 Lua.org, PUC-Rio"
#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo, W. Celes"
@@ -497,7 +497,7 @@ struct lua_Debug {
/******************************************************************************
-* Copyright (C) 1994-2023 Lua.org, PUC-Rio.
+* Copyright (C) 1994-2025 Lua.org, PUC-Rio.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
diff --git a/contrib/lua/src/luaconf.h b/contrib/lua/src/luaconf.h
index e517fb4d2e75..c7ca969e321f 100644
--- a/contrib/lua/src/luaconf.h
+++ b/contrib/lua/src/luaconf.h
@@ -257,6 +257,15 @@
#endif
+
+/*
+** LUA_IGMARK is a mark to ignore all after it when building the
+** module name (e.g., used to build the luaopen_ function name).
+** Typically, the suffix after the mark is the module version,
+** as in "mod-v1.2.so".
+*/
+#define LUA_IGMARK "-"
+
/* }================================================================== */
diff --git a/contrib/lua/src/lundump.c b/contrib/lua/src/lundump.c
index 02aed64fb622..e8d92a8534ff 100644
--- a/contrib/lua/src/lundump.c
+++ b/contrib/lua/src/lundump.c
@@ -81,7 +81,7 @@ static size_t loadUnsigned (LoadState *S, size_t limit) {
static size_t loadSize (LoadState *S) {
- return loadUnsigned(S, ~(size_t)0);
+ return loadUnsigned(S, MAX_SIZET);
}
@@ -122,7 +122,7 @@ static TString *loadStringN (LoadState *S, Proto *p) {
ts = luaS_createlngstrobj(L, size); /* create string */
setsvalue2s(L, L->top.p, ts); /* anchor it ('loadVector' can GC) */
luaD_inctop(L);
- loadVector(S, getstr(ts), size); /* load directly in final place */
+ loadVector(S, getlngstr(ts), size); /* load directly in final place */
L->top.p--; /* pop string */
}
luaC_objbarrier(L, p, ts);
diff --git a/contrib/lua/src/lundump.h b/contrib/lua/src/lundump.h
index f3748a998075..a97676ca1852 100644
--- a/contrib/lua/src/lundump.h
+++ b/contrib/lua/src/lundump.h
@@ -21,8 +21,7 @@
/*
** Encode major-minor version in one byte, one nibble for each
*/
-#define MYINT(s) (s[0]-'0') /* assume one-digit numerals */
-#define LUAC_VERSION (MYINT(LUA_VERSION_MAJOR)*16+MYINT(LUA_VERSION_MINOR))
+#define LUAC_VERSION (((LUA_VERSION_NUM / 100) * 16) + LUA_VERSION_NUM % 100)
#define LUAC_FORMAT 0 /* this is the official format */
diff --git a/contrib/lua/src/lvm.c b/contrib/lua/src/lvm.c
index 9d1bdfb0bd6e..45b47e7c8793 100644
--- a/contrib/lua/src/lvm.c
+++ b/contrib/lua/src/lvm.c
@@ -95,8 +95,10 @@ static int l_strton (const TValue *obj, TValue *result) {
lua_assert(obj != result);
if (!cvt2num(obj)) /* is object not a string? */
return 0;
- else
- return (luaO_str2num(svalue(obj), result) == vslen(obj) + 1);
+ else {
+ TString *st = tsvalue(obj);
+ return (luaO_str2num(getstr(st), result) == tsslen(st) + 1);
+ }
}
@@ -341,7 +343,10 @@ void luaV_finishset (lua_State *L, const TValue *t, TValue *key,
lua_assert(isempty(slot)); /* slot must be empty */
tm = fasttm(L, h->metatable, TM_NEWINDEX); /* get metamethod */
if (tm == NULL) { /* no metamethod? */
+ sethvalue2s(L, L->top.p, h); /* anchor 't' */
+ L->top.p++; /* assume EXTRA_STACK */
luaH_finishset(L, h, key, slot, val); /* set new value */
+ L->top.p--;
invalidateTMcache(h);
luaC_barrierback(L, obj2gco(h), val);
return;
@@ -370,30 +375,32 @@ void luaV_finishset (lua_State *L, const TValue *t, TValue *key,
/*
-** Compare two strings 'ls' x 'rs', returning an integer less-equal-
-** -greater than zero if 'ls' is less-equal-greater than 'rs'.
+** Compare two strings 'ts1' x 'ts2', returning an integer less-equal-
+** -greater than zero if 'ts1' is less-equal-greater than 'ts2'.
** The code is a little tricky because it allows '\0' in the strings
-** and it uses 'strcoll' (to respect locales) for each segments
-** of the strings.
+** and it uses 'strcoll' (to respect locales) for each segment
+** of the strings. Note that segments can compare equal but still
+** have different lengths.
*/
-static int l_strcmp (const TString *ls, const TString *rs) {
- const char *l = getstr(ls);
- size_t ll = tsslen(ls);
- const char *r = getstr(rs);
- size_t lr = tsslen(rs);
+static int l_strcmp (const TString *ts1, const TString *ts2) {
+ const char *s1 = getstr(ts1);
+ size_t rl1 = tsslen(ts1); /* real length */
+ const char *s2 = getstr(ts2);
+ size_t rl2 = tsslen(ts2);
for (;;) { /* for each segment */
- int temp = strcoll(l, r);
+ int temp = strcoll(s1, s2);
if (temp != 0) /* not equal? */
return temp; /* done */
else { /* strings are equal up to a '\0' */
- size_t len = strlen(l); /* index of first '\0' in both strings */
- if (len == lr) /* 'rs' is finished? */
- return (len == ll) ? 0 : 1; /* check 'ls' */
- else if (len == ll) /* 'ls' is finished? */
- return -1; /* 'ls' is less than 'rs' ('rs' is not finished) */
- /* both strings longer than 'len'; go on comparing after the '\0' */
- len++;
- l += len; ll -= len; r += len; lr -= len;
+ size_t zl1 = strlen(s1); /* index of first '\0' in 's1' */
+ size_t zl2 = strlen(s2); /* index of first '\0' in 's2' */
+ if (zl2 == rl2) /* 's2' is finished? */
+ return (zl1 == rl1) ? 0 : 1; /* check 's1' */
+ else if (zl1 == rl1) /* 's1' is finished? */
+ return -1; /* 's1' is less than 's2' ('s2' is not finished) */
+ /* both strings longer than 'zl'; go on comparing after the '\0' */
+ zl1++; zl2++;
+ s1 += zl1; rl1 -= zl1; s2 += zl2; rl2 -= zl2;
}
}
}
@@ -628,8 +635,9 @@ int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2) {
static void copy2buff (StkId top, int n, char *buff) {
size_t tl = 0; /* size already copied */
do {
- size_t l = vslen(s2v(top - n)); /* length of string being copied */
- memcpy(buff + tl, svalue(s2v(top - n)), l * sizeof(char));
+ TString *st = tsvalue(s2v(top - n));
+ size_t l = tsslen(st); /* length of string being copied */
+ memcpy(buff + tl, getstr(st), l * sizeof(char));
tl += l;
} while (--n > 0);
}
@@ -655,12 +663,12 @@ void luaV_concat (lua_State *L, int total) {
}
else {
/* at least two non-empty string values; get as many as possible */
- size_t tl = vslen(s2v(top - 1));
+ size_t tl = tsslen(tsvalue(s2v(top - 1)));
TString *ts;
/* collect total length and number of strings */
for (n = 1; n < total && tostring(L, s2v(top - n - 1)); n++) {
- size_t l = vslen(s2v(top - n - 1));
- if (l_unlikely(l >= (MAX_SIZE/sizeof(char)) - tl)) {
+ size_t l = tsslen(tsvalue(s2v(top - n - 1)));
+ if (l_unlikely(l >= MAX_SIZE - sizeof(TString) - tl)) {
L->top.p = top - total; /* pop strings to avoid wasting stack */
luaG_runerror(L, "string length overflow");
}
@@ -673,7 +681,7 @@ void luaV_concat (lua_State *L, int total) {
}
else { /* long string; copy strings directly to final result */
ts = luaS_createlngstrobj(L, tl);
- copy2buff(top, n, getstr(ts));
+ copy2buff(top, n, getlngstr(ts));
}
setsvalue2s(L, top - n, ts); /* create result */
}
@@ -1159,18 +1167,11 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
startfunc:
trap = L->hookmask;
returning: /* trap already set */
- cl = clLvalue(s2v(ci->func.p));
+ cl = ci_func(ci);
k = cl->p->k;
pc = ci->u.l.savedpc;
- if (l_unlikely(trap)) {
- if (pc == cl->p->code) { /* first instruction (not resuming)? */
- if (cl->p->is_vararg)
- trap = 0; /* hooks will start after VARARGPREP instruction */
- else /* check 'call' hook */
- luaD_hookcall(L, ci);
- }
- ci->u.l.trap = 1; /* assume trap is on, for now */
- }
+ if (l_unlikely(trap))
+ trap = luaG_tracecall(L);
base = ci->func.p + 1;
/* main loop of interpreter */
for (;;) {
@@ -1257,7 +1258,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
const TValue *slot;
TValue *upval = cl->upvals[GETARG_B(i)]->v.p;
TValue *rc = KC(i);
- TString *key = tsvalue(rc); /* key must be a string */
+ TString *key = tsvalue(rc); /* key must be a short string */
if (luaV_fastget(L, upval, key, slot, luaH_getshortstr)) {
setobj2s(L, ra, slot);
}
@@ -1300,7 +1301,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
const TValue *slot;
TValue *rb = vRB(i);
TValue *rc = KC(i);
- TString *key = tsvalue(rc); /* key must be a string */
+ TString *key = tsvalue(rc); /* key must be a short string */
if (luaV_fastget(L, rb, key, slot, luaH_getshortstr)) {
setobj2s(L, ra, slot);
}
@@ -1313,7 +1314,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
TValue *upval = cl->upvals[GETARG_A(i)]->v.p;
TValue *rb = KB(i);
TValue *rc = RKC(i);
- TString *key = tsvalue(rb); /* key must be a string */
+ TString *key = tsvalue(rb); /* key must be a short string */
if (luaV_fastget(L, upval, key, slot, luaH_getshortstr)) {
luaV_finishfastset(L, upval, slot, rc);
}
@@ -1356,7 +1357,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
const TValue *slot;
TValue *rb = KB(i);
TValue *rc = RKC(i);
- TString *key = tsvalue(rb); /* key must be a string */
+ TString *key = tsvalue(rb); /* key must be a short string */
if (luaV_fastget(L, s2v(ra), key, slot, luaH_getshortstr)) {
luaV_finishfastset(L, s2v(ra), slot, rc);
}
diff --git a/contrib/lyaml/ext/yaml/yaml.c b/contrib/lyaml/ext/yaml/yaml.c
index 54478610134f..6a5ddc605e0f 100644
--- a/contrib/lyaml/ext/yaml/yaml.c
+++ b/contrib/lyaml/ext/yaml/yaml.c
@@ -35,6 +35,8 @@
#include "lyaml.h"
+#include "bootstrap.h"
+
#define MYNAME "yaml"
#define MYVERSION MYNAME " library for " LUA_VERSION " / " VERSION
@@ -64,3 +66,5 @@ luaopen_yaml (lua_State *L)
return 1;
}
+
+FLUA_MODULE(yaml);
diff --git a/contrib/mandoc/Makefile b/contrib/mandoc/Makefile
index 7ec34a560504..d4a2c794b437 100644
--- a/contrib/mandoc/Makefile
+++ b/contrib/mandoc/Makefile
@@ -15,7 +15,7 @@
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-VERSION = 1.14.6s20250613
+VERSION = 1.14.6s20250926
# === LIST OF FILES ====================================================
diff --git a/contrib/mandoc/TODO b/contrib/mandoc/TODO
index 3f5a449af68f..5d582b85b154 100644
--- a/contrib/mandoc/TODO
+++ b/contrib/mandoc/TODO
@@ -1,6 +1,6 @@
************************************************************************
* Official mandoc TODO.
-* $Id: TODO,v 1.337 2025/04/08 21:53:14 schwarze Exp $
+* $Id: TODO,v 1.338 2025/07/22 13:36:54 schwarze Exp $
************************************************************************
Many issues are annotated for difficulty as follows:
@@ -505,6 +505,15 @@ are mere guesses, and some may be wrong.
re-reported by tb@ Mon, 16 Mar 2015 16:47:21 +0100
loc ** exist ** algo ** size * imp **
+- Check for bad line breaks caused by PostScript and PDF using variable-
+ width fonts, for example in .Bl -width "string". The difficulty line
+ below describes a naive solution by simply scaling up widths internally
+ or adding default spacing (like in terminal output). If fixes are
+ needed in width measurements, it might be a bit harder, but likely
+ not unreasonably so.
+ reported by Jan Stary 20 May 2024 10:19:01 +0200
+ loc * exist * algo ** size * imp **
+
--- HTML issues --------------------------------------------------------
- support the idiom .TP .IP .TP for multi-paragraph list item bodies
diff --git a/contrib/mandoc/catman.8 b/contrib/mandoc/catman.8
index 903fa1fa82c9..c0f14872afc6 100644
--- a/contrib/mandoc/catman.8
+++ b/contrib/mandoc/catman.8
@@ -1,6 +1,6 @@
-.\" $Id: catman.8,v 1.8 2017/03/18 19:56:01 schwarze Exp $
+.\" $Id: catman.8,v 1.15 2025/07/13 14:15:26 schwarze Exp $
.\"
-.\" Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org>
+.\" Copyright (c) 2017, 2025 Ingo Schwarze <schwarze@openbsd.org>
.\"
.\" Permission to use, copy, modify, and distribute this software for any
.\" purpose with or without fee is hereby granted, provided that the above
@@ -14,7 +14,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
-.Dd $Mdocdate: March 18 2017 $
+.Dd $Mdocdate: July 13 2025 $
.Dt CATMAN 8
.Os
.Sh NAME
@@ -37,9 +37,9 @@ and
format and formats all of them, storing the formatted versions in
the same relative paths below
.Ar dstdir .
-Subdirectories of
+Unless they already exist,
.Ar dstdir
-are created as needed.
+itself and any required subdirectories are created.
Existing files are not explicitly deleted, but possibly overwritten.
.Pp
The options are as follows:
@@ -71,6 +71,18 @@ output mode, the
.Cm fragment
output option is implied.
Other output options are not supported.
+.It Fl v
+Verbose mode, printing additional information to standard error output.
+Specifying this once prints a summary about the number of files
+and directories processed at the end of the iteration.
+Specifying it twice additionally prints debugging information
+about the backchannel from
+.Xr mandocd 8
+to
+.Nm
+that is used to limit the number of files in flight at any given time.
+For details, see
+.Sx DIAGNOSTICS .
.El
.Sh IMPLEMENTATION NOTES
Since this version avoids
@@ -87,33 +99,209 @@ implementations.
.Sh EXIT STATUS
.Ex -std
.Pp
-Possible errors include:
-.Bl -bullet
-.It
-missing, invalid, or excessive command line arguments
-.It
-failure to change the current working directory to
+Failures while trying to open individual manual pages for reading,
+to save individual formatted files to the file system,
+or even to read or create subdirectories do not cause
+.Nm
+to return an error exit status.
+In such cases,
+.Nm
+simply continues with the next file or subdirectory.
+.Sh DIAGNOSTICS
+Some fatal errors cause
+.Nm
+to exit before the iteration over input files is even started:
+.Bl -tag -width Ds -offset indent
+.It unknown option \-\- Ar option
+An invalid option was passed on the command line.
+.It missing arguments: srcdir and dstdir
+No argument was provided.
+Both
.Ar srcdir
-.It
-failure to open
+and
.Ar dstdir
-.It
-communication failure with
+are mandatory.
+.It missing argument: dstdir
+Only one argument was provided.
+The second argument,
+.Ar dstdir ,
+is mandatory, too.
+.It too many arguments: Ar third argument
+Three or more arguments were provided, but only two are supported.
+.It Sy socketpair : Ar reason
+The sockets needed for communication with
+.Xr mandocd 8
+could not be created, for example due to file descriptor or memory exhaustion.
+.It Sy fork : Ar reason
+The new process needed to run
.Xr mandocd 8
-.It
-resource exhaustion, for example file descriptor, process table,
-or memory exhaustion
+could not be created, for example due to process table exhaustion
+or system resource limits.
+.It Sy exec Ns Po Sy mandocd Pc : Ar reason
+The
+.Xr mandocd 8
+child program could not be started, for example because it is not in the
+.Ev PATH
+or has no execute permission.
+.It Sy mkdir No destination Ar dstdir : reason
+The
+.Ar dstdir
+does not exist and could not be created, for example because
+the parent directory does not exist or permission is denied.
+.It Sy open No destination Ar dstdir : reason
+The
+.Ar dstdir
+could not be opened for reading, for example because
+it is not a directory or permission is denied.
+.It Sy chdir No to source Ar srcdir : reason
+The current working directory could not be changed to
+.Ar srcdir ,
+for example because it does not exist, it is not a directory,
+or permission is denied.
+.It Sy fts_open : Ar reason
+Starting the iteration was attempted but failed,
+for example due to memory exhaustion.
.El
.Pp
-Except for memory exhaustion and similar system-level failures,
-failures while trying to open, read, parse, or format individual
-manual pages, to save individual formatted files to the file system,
-or even to create directories do not cause
+Some fatal errors cause the iteration over input files to be aborted
+prematurely:
+.Bl -tag -width Ds -offset indent
+.It FATAL: Sy fts_read : Ar reason
+A call to
+.Xr fts_read 3
+returned
+.Dv NULL ,
+meaning that the iteration failed before being complete.
+.It FATAL: mandocd child died: got Ar SIGNAME
+This message appears if
.Nm
-to return an error exit status.
-In such cases,
+gets the
+.Dv SIGCHLD
+or
+.Dv SIGPIPE
+signal, most likely due to a fatal bug in
+.Xr mandocd 8 .
+.It FATAL: Sy sendmsg : Ar reason
+The file descriptors needed to process one of the manual pages
+could not be sent to
+.Xr mandocd 8 ,
+for example because
+.Xr mandocd 8
+could not be started or died unexpectedly.
+.It FATAL: Sy recv : Ar reason
+Trying to read a reply message from
+.Xr mandocd 8
+failed, most likely because
+.Xr mandocd 8
+unexpectedly died or closed the socket.
+.It FATAL: signal Ar SIGNAME
+This message appears if
+.Nm
+gets a
+.Dv SIGHUP ,
+.Dv SIGINT ,
+or
+.Dv SIGTERM
+signal, for example because the user deliberately killed it.
+.El
+.Pp
+Some non-fatal errors cause a single subdirectory to be skipped.
+The iteration is not aborted but continues with the next subdirectory,
+and the exit status is unaffected:
+.Bl -tag -width Ds -offset indent
+.It directory Ar subdirectory No unreadable : Ar reason
+A directory below
+.Ar srcdir
+could not be read and is skipped.
+.It directory Ar subdirectory No causes cycle
+A directory below
+.Ar srcdir
+is skipped because it would cause cyclic processing.
+.It Sy mkdirat Ar subdirectory : reason
+A required directory below
+.Ar dstdir
+does not exist and could not be created.
+The corresponding subdirectory below
+.Ar srcdir
+is skipped.
+.El
+.Pp
+Some non-fatal errors cause a single source file to be skipped.
+The iteration is not aborted but continues with the next file,
+and the exit status is unaffected:
+.Bl -tag -width Ds -offset indent
+.It file Ar filename : reason
+The function
+.Xr fts_read 3
+reported a non-fatal error with respect to
+.Ar filename .
+.It file Ar filename : No not a regular file
+For example, it might be a symbolic link or a device file.
+.It Sy open Ar filename No for reading : Ar reason
+A file below
+.Ar srcdir
+could not be read, for example due to permission problems.
+.It Sy openat Ar filename No for writing : Ar reason
+A file below
+.Ar dstdir
+could not be created or truncated, for example due to permission problems.
+.El
+.Pp
+If errors occur, the applicable summary messages appear
+after the end of the iteration:
+.Pp
+.Bl -tag -width Ds -offset indent -compact
+.It skipped Ar number No directories due to errors
+.It skipped Ar number No files due to errors
+.It processing aborted due to fatal error
+.El
+.Pp
+If the
+.Fl v
+flag is specified, the following summary message also appears:
+.Bl -tag -width Ds -offset indent
+.It processed Ar nfiles No files in Ar ndirs No directories
+A file is counted if it could be opened for reading and the
+corresponding output file could be opened for writing;
+this does not necessarily mean that it is a useful manual page.
+A directory is counted if it could be opened for reading and the
+corresponding output directory existed or could be created;
+this does not necessarily mean that any files could be
+processed inside.
+.El
+.Pp
+If the
+.Fl v
+flag is specified twice, the following messages also appear:
+.Bl -tag -width Ds -offset indent
+.It allowing up to Ar number No files in flight
+This is printed at the beginning of the iteration,
+showing the maximum number of files that
+.Nm
+allows to be in flight at any given time.
+.It files in flight: Ar old No \- Ar decrement No = Ar new
+This message is printed when
+.Nm
+learns about
+.Xr mandocd 8
+accepting more than one file at the same time.
+The three numbers printed are the old number of files in flight,
+the amount this number is being reduced, and the resulting
+new number of files in flight.
+.It waiting for Ar number No files in flight
+This message is printed at the end of the iteration, after
+.Nm
+has submitted all files to
+.Xr mandocd 8
+that it intends to.
+THe message informs about the number of files still in flight
+at this point.
+The
.Nm
-will simply continue with the next file or subdirectory.
+program then waits until
+.Xr mandocd 8
+has accepted them all or until an error occurs.
+.El
.Sh SEE ALSO
.Xr mandoc 1 ,
.Xr mandocd 8
diff --git a/contrib/mandoc/catman.c b/contrib/mandoc/catman.c
index e46613eb0e8c..c9eda18bf71c 100644
--- a/contrib/mandoc/catman.c
+++ b/contrib/mandoc/catman.c
@@ -1,7 +1,7 @@
-/* $Id: catman.c,v 1.23 2021/10/15 15:04:02 schwarze Exp $ */
+/* $Id: catman.c,v 1.30 2025/07/13 14:15:26 schwarze Exp $ */
/*
+ * Copyright (c) 2017, 2025 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2017 Michael Stapelberg <stapelberg@debian.org>
- * Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -25,6 +25,7 @@
#include <sys/socket.h>
#include <sys/stat.h>
+#include <assert.h>
#if HAVE_ERR
#include <err.h>
#endif
@@ -35,26 +36,44 @@
#else
#include "compat_fts.h"
#endif
+#include <signal.h>
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
+int verbose_flag = 0;
+sig_atomic_t got_signal = 0;
+
int process_manpage(int, int, const char *);
int process_tree(int, int);
void run_mandocd(int, const char *, const char *)
__attribute__((__noreturn__));
+void signal_handler(int);
ssize_t sock_fd_write(int, int, int, int);
void usage(void) __attribute__((__noreturn__));
void
+signal_handler(int signum)
+{
+ got_signal = signum;
+}
+
+void
run_mandocd(int sockfd, const char *outtype, const char* defos)
{
char sockfdstr[10];
+ int len;
- if (snprintf(sockfdstr, sizeof(sockfdstr), "%d", sockfd) == -1)
+ len = snprintf(sockfdstr, sizeof(sockfdstr), "%d", sockfd);
+ if (len >= (int)sizeof(sockfdstr)) {
+ errno = EOVERFLOW;
+ len = -1;
+ }
+ if (len < 0)
err(1, "snprintf");
if (defos == NULL)
execlp("mandocd", "mandocd", "-T", outtype,
@@ -109,10 +128,11 @@ sock_fd_write(int fd, int fd0, int fd1, int fd2)
* to neither cause more than a handful of retries
* in normal operation nor unnecessary delays.
*/
- for (;;) {
- if ((sz = sendmsg(fd, &msg, 0)) != -1 ||
- errno != EAGAIN)
+ while ((sz = sendmsg(fd, &msg, 0)) == -1) {
+ if (errno != EAGAIN) {
+ warn("FATAL: sendmsg");
break;
+ }
nanosleep(&timeout, NULL);
}
return sz;
@@ -125,14 +145,16 @@ process_manpage(int srv_fd, int dstdir_fd, const char *path)
int irc;
if ((in_fd = open(path, O_RDONLY)) == -1) {
- warn("open(%s)", path);
+ warn("open %s for reading", path);
+ fflush(stderr);
return 0;
}
if ((out_fd = openat(dstdir_fd, path,
O_WRONLY | O_NOFOLLOW | O_CREAT | O_TRUNC,
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1) {
- warn("openat(%s)", path);
+ warn("openat %s for writing", path);
+ fflush(stderr);
close(in_fd);
return 0;
}
@@ -142,20 +164,22 @@ process_manpage(int srv_fd, int dstdir_fd, const char *path)
close(in_fd);
close(out_fd);
- if (irc < 0) {
- warn("sendmsg");
- return -1;
- }
- return 0;
+ return irc;
}
int
process_tree(int srv_fd, int dstdir_fd)
{
+ const struct timespec timeout = { 0, 10000000 }; /* 0.01 s */
+ const int max_inflight = 16;
+
FTS *ftsp;
FTSENT *entry;
const char *argv[2];
const char *path;
+ int inflight, irc, decr, fatal;
+ int gooddirs, baddirs, goodfiles, badfiles;
+ char dummy[1];
argv[0] = ".";
argv[1] = (char *)NULL;
@@ -166,13 +190,59 @@ process_tree(int srv_fd, int dstdir_fd)
return -1;
}
- while ((entry = fts_read(ftsp)) != NULL) {
+ if (verbose_flag >= 2) {
+ warnx("allowing up to %d files in flight", max_inflight);
+ fflush(stderr);
+ }
+ inflight = fatal = gooddirs = baddirs = goodfiles = badfiles = 0;
+ while (fatal == 0 && got_signal == 0 &&
+ (entry = fts_read(ftsp)) != NULL) {
+ if (inflight >= max_inflight) {
+ while (recv(srv_fd, dummy, sizeof(dummy), 0) == -1) {
+ if (errno != EAGAIN) {
+ warn("FATAL: recv");
+ fatal = errno;
+ break;
+ }
+ nanosleep(&timeout, NULL);
+ }
+ if (fatal != 0)
+ break;
+ decr = 1;
+ while ((irc = recv(srv_fd, dummy, sizeof(dummy),
+ MSG_DONTWAIT)) > 0)
+ decr++;
+ assert(inflight >= decr);
+ if (verbose_flag >= 2 && decr > 1) {
+ warnx("files in flight: %d - %d = %d",
+ inflight, decr, inflight - decr);
+ fflush(stderr);
+ }
+ inflight -= decr;
+ if (irc == 0) {
+ errno = ECONNRESET;
+ inflight = -1;
+ }
+ if (errno != EAGAIN) {
+ warn("FATAL: recv");
+ fatal = errno;
+ break;
+ }
+ }
path = entry->fts_path + 2;
switch (entry->fts_info) {
case FTS_F:
- if (process_manpage(srv_fd, dstdir_fd, path) == -1) {
- fts_close(ftsp);
- return -1;
+ switch (process_manpage(srv_fd, dstdir_fd, path)) {
+ case -1:
+ fatal = errno;
+ break;
+ case 0:
+ badfiles++;
+ break;
+ default:
+ goodfiles++;
+ inflight++;
+ break;
}
break;
case FTS_D:
@@ -180,25 +250,96 @@ process_tree(int srv_fd, int dstdir_fd)
mkdirat(dstdir_fd, path, S_IRWXU | S_IRGRP |
S_IXGRP | S_IROTH | S_IXOTH) == -1 &&
errno != EEXIST) {
- warn("mkdirat(%s)", path);
+ warn("mkdirat %s", path);
+ fflush(stderr);
(void)fts_set(ftsp, entry, FTS_SKIP);
- }
+ baddirs++;
+ } else
+ gooddirs++;
break;
case FTS_DP:
break;
+ case FTS_DNR:
+ warnx("directory %s unreadable: %s",
+ path, strerror(entry->fts_errno));
+ fflush(stderr);
+ baddirs++;
+ break;
+ case FTS_DC:
+ warnx("directory %s causes cycle", path);
+ fflush(stderr);
+ baddirs++;
+ break;
+ case FTS_ERR:
+ case FTS_NS:
+ warnx("file %s: %s",
+ path, strerror(entry->fts_errno));
+ fflush(stderr);
+ badfiles++;
+ break;
default:
- warnx("%s: not a regular file", path);
+ warnx("file %s: not a regular file", path);
+ fflush(stderr);
+ badfiles++;
break;
}
}
+ if (got_signal != 0) {
+ switch (got_signal) {
+ case SIGCHLD:
+ warnx("FATAL: mandocd child died: got SIGCHLD");
+ break;
+ case SIGPIPE:
+ warnx("FATAL: mandocd child died: got SIGPIPE");
+ break;
+ default:
+ warnx("FATAL: signal SIG%s", sys_signame[got_signal]);
+ break;
+ }
+ inflight = -1;
+ fatal = 1;
+ } else if (fatal == 0 && (fatal = errno) != 0)
+ warn("FATAL: fts_read");
fts_close(ftsp);
- return 0;
+ if (verbose_flag >= 2 && inflight > 0) {
+ warnx("waiting for %d files in flight", inflight);
+ fflush(stderr);
+ }
+ while (inflight > 0) {
+ irc = recv(srv_fd, dummy, sizeof(dummy), 0);
+ if (irc > 0)
+ inflight--;
+ else if (irc == -1 && errno == EAGAIN)
+ nanosleep(&timeout, NULL);
+ else {
+ if (irc == 0)
+ errno = ECONNRESET;
+ warn("recv");
+ inflight = -1;
+ }
+ }
+ if (verbose_flag)
+ warnx("processed %d files in %d directories",
+ goodfiles, gooddirs);
+ if (baddirs > 0)
+ warnx("skipped %d %s due to errors", baddirs,
+ baddirs == 1 ? "directory" : "directories");
+ if (badfiles > 0)
+ warnx("skipped %d %s due to errors", badfiles,
+ badfiles == 1 ? "file" : "files");
+ if (fatal != 0) {
+ warnx("processing aborted due to fatal error, "
+ "results are probably incomplete");
+ inflight = -1;
+ }
+ return inflight;
}
int
main(int argc, char **argv)
{
+ struct sigaction sa;
const char *defos, *outtype;
int srv_fds[2];
int dstdir_fd;
@@ -207,7 +348,7 @@ main(int argc, char **argv)
defos = NULL;
outtype = "ascii";
- while ((opt = getopt(argc, argv, "I:T:")) != -1) {
+ while ((opt = getopt(argc, argv, "I:T:v")) != -1) {
switch (opt) {
case 'I':
defos = optarg;
@@ -215,6 +356,9 @@ main(int argc, char **argv)
case 'T':
outtype = optarg;
break;
+ case 'v':
+ verbose_flag += 1;
+ break;
default:
usage();
}
@@ -224,8 +368,36 @@ main(int argc, char **argv)
argc -= optind;
argv += optind;
}
- if (argc != 2)
+ if (argc != 2) {
+ switch (argc) {
+ case 0:
+ warnx("missing arguments: srcdir and dstdir");
+ break;
+ case 1:
+ warnx("missing argument: dstdir");
+ break;
+ default:
+ warnx("too many arguments: %s", argv[2]);
+ break;
+ }
usage();
+ }
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = &signal_handler;
+ sa.sa_flags = SA_NOCLDWAIT;
+ if (sigfillset(&sa.sa_mask) == -1)
+ err(1, "sigfillset");
+ if (sigaction(SIGHUP, &sa, NULL) == -1)
+ err(1, "sigaction(SIGHUP)");
+ if (sigaction(SIGINT, &sa, NULL) == -1)
+ err(1, "sigaction(SIGINT)");
+ if (sigaction(SIGPIPE, &sa, NULL) == -1)
+ err(1, "sigaction(SIGPIPE)");
+ if (sigaction(SIGTERM, &sa, NULL) == -1)
+ err(1, "sigaction(SIGTERM)");
+ if (sigaction(SIGCHLD, &sa, NULL) == -1)
+ err(1, "sigaction(SIGCHLD)");
if (socketpair(AF_LOCAL, SOCK_STREAM, AF_UNSPEC, srv_fds) == -1)
err(1, "socketpair");
@@ -242,11 +414,18 @@ main(int argc, char **argv)
}
close(srv_fds[1]);
- if ((dstdir_fd = open(argv[1], O_RDONLY | O_DIRECTORY)) == -1)
- err(1, "open(%s)", argv[1]);
+ if ((dstdir_fd = open(argv[1], O_RDONLY | O_DIRECTORY)) == -1) {
+ if (errno != ENOENT)
+ err(1, "open destination %s", argv[1]);
+ if (mkdir(argv[1], S_IRWXU |
+ S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == -1)
+ err(1, "mkdir destination %s", argv[1]);
+ if ((dstdir_fd = open(argv[1], O_RDONLY | O_DIRECTORY)) == -1)
+ err(1, "open destination %s", argv[1]);
+ }
if (chdir(argv[0]) == -1)
- err(1, "chdir(%s)", argv[0]);
+ err(1, "chdir to source %s", argv[0]);
return process_tree(srv_fds[0], dstdir_fd) == -1 ? 1 : 0;
}
diff --git a/contrib/mandoc/dba.c b/contrib/mandoc/dba.c
index ee43933de3bf..ab40798e8eed 100644
--- a/contrib/mandoc/dba.c
+++ b/contrib/mandoc/dba.c
@@ -1,6 +1,6 @@
-/* $Id: dba.c,v 1.10 2017/02/17 14:43:54 schwarze Exp $ */
+/* $Id: dba.c,v 1.11 2025/09/24 13:13:30 schwarze Exp $ */
/*
- * Copyright (c) 2016, 2017 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2016, 2017, 2025 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -318,7 +318,8 @@ compare_names(const void *vp1, const void *vp2)
cp1 = *(const char * const *)vp1;
cp2 = *(const char * const *)vp2;
return (diff = *cp2 - *cp1) ? diff :
- strcasecmp(cp1 + 1, cp2 + 1);
+ (diff = strcasecmp(cp1 + 1, cp2 + 1)) ? diff :
+ strcmp(cp1 + 1, cp2 + 1);
}
static int
diff --git a/contrib/mandoc/gmdiff b/contrib/mandoc/gmdiff
index 69431f703aaf..54025e4cd450 100644
--- a/contrib/mandoc/gmdiff
+++ b/contrib/mandoc/gmdiff
@@ -45,8 +45,8 @@ while [ -n "$1" ]; do
file=$1
shift
echo " ========== $file ========== "
- $ROFF -mandoc $file | $COLPIPE 2> /tmp/roff.err > /tmp/roff.out
- ${MANDOC:=mandoc} $MOPT $file | $COLPIPE \
+ ($ROFF -mandoc $file | $COLPIPE) 2> /tmp/roff.err > /tmp/roff.out
+ (${MANDOC:=mandoc} $MOPT $file | $COLPIPE) \
2> /tmp/mandoc.err > /tmp/mandoc.out
for i in roff mandoc; do
[ -s /tmp/$i.err ] && echo "$i errors:" && cat /tmp/$i.err
diff --git a/contrib/mandoc/main.c b/contrib/mandoc/main.c
index 57b06c9b9e66..d70ff1ce77b4 100644
--- a/contrib/mandoc/main.c
+++ b/contrib/mandoc/main.c
@@ -1,6 +1,6 @@
-/* $Id: main.c,v 1.361 2022/04/14 16:43:43 schwarze Exp $ */
+/* $Id: main.c,v 1.364 2025/09/24 21:30:20 schwarze Exp $ */
/*
- * Copyright (c) 2010-2012, 2014-2021 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2010-2012,2014-2021,2025 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org>
*
@@ -109,7 +109,7 @@ static void parse(struct mparse *, int, const char *,
struct outstate *, struct manconf *);
static void passthrough(int, int);
static void process_onefile(struct mparse *, struct manpage *,
- int, struct outstate *, struct manconf *);
+ struct outstate *, struct manconf *);
static void run_pager(struct outstate *, char *);
static pid_t spawn_pager(struct outstate *, char *);
static void usage(enum argmode) __attribute__((__noreturn__));
@@ -144,7 +144,6 @@ main(int argc, char *argv[])
int options; /* Parser options. */
int show_usage; /* Invalid argument: give up. */
int prio, best_prio;
- int startdir;
int c;
enum mandoc_os os_e; /* Check base system conventions. */
enum outmode outmode; /* According to command line. */
@@ -514,7 +513,7 @@ main(int argc, char *argv[])
if (resnsz == 0)
(void)fs_search(&search, &conf.manpath,
*argv, &resn, &resnsz);
- if (resnsz == 0 && strchr(*argv, '/') == NULL) {
+ if (resnsz == 0) {
if (search.arch != NULL &&
arch_valid(search.arch, OSENUM) == 0)
warnx("Unknown architecture \"%s\".",
@@ -529,20 +528,6 @@ main(int argc, char *argv[])
mandoc_msg_setrc(MANDOCLEVEL_BADARG);
continue;
}
- if (resnsz == 0) {
- if (access(*argv, R_OK) == -1) {
- mandoc_msg_setinfilename(*argv);
- mandoc_msg(MANDOCERR_BADARG_BAD,
- 0, 0, "%s", strerror(errno));
- mandoc_msg_setinfilename(NULL);
- continue;
- }
- resnsz = 1;
- resn = mandoc_calloc(resnsz, sizeof(*res));
- resn->file = mandoc_strdup(*argv);
- resn->ipath = SIZE_MAX;
- resn->form = FORM_SRC;
- }
if (outmode != OUTMODE_ONE || resnsz == 1) {
res = mandoc_reallocarray(res,
ressz + resnsz, sizeof(*res));
@@ -559,7 +544,8 @@ main(int argc, char *argv[])
best_prio = 40;
for (ib = i = 0; i < resnsz; i++) {
- sec = resn[i].file;
+ sec = resn[i].file +
+ strlen(conf.manpath.paths[resn[i].ipath]);
sec += strcspn(sec, "123456789");
if (sec[0] == '\0')
continue; /* No section at all. */
@@ -647,23 +633,13 @@ main(int argc, char *argv[])
mchars_alloc();
mp = mparse_alloc(options, os_e, os_s);
- /*
- * Remember the original working directory, if possible.
- * This will be needed if some names on the command line
- * are page names and some are relative file names.
- * Do not error out if the current directory is not
- * readable: Maybe it won't be needed after all.
- */
- startdir = open(".", O_RDONLY | O_DIRECTORY);
for (i = 0; i < ressz; i++) {
- process_onefile(mp, res + i, startdir, &outst, &conf);
+ if (i > 0)
+ mparse_reset(mp);
+ process_onefile(mp, res + i, &outst, &conf);
if (outst.wstop && mandoc_msg_getrc() != MANDOCLEVEL_OK)
break;
}
- if (startdir != -1) {
- (void)fchdir(startdir);
- close(startdir);
- }
if (conf.output.tag != NULL && conf.output.tag_found == 0) {
mandoc_msg(MANDOCERR_TAG, 0, 0, "%s", conf.output.tag);
conf.output.tag = NULL;
@@ -909,7 +885,7 @@ fs_search(const struct mansearch *cfg, const struct manpaths *paths,
}
static void
-process_onefile(struct mparse *mp, struct manpage *resp, int startdir,
+process_onefile(struct mparse *mp, struct manpage *resp,
struct outstate *outst, struct manconf *conf)
{
int fd;
@@ -921,8 +897,6 @@ process_onefile(struct mparse *mp, struct manpage *resp, int startdir,
*/
if (resp->ipath != SIZE_MAX)
(void)chdir(conf->manpath.paths[resp->ipath]);
- else if (startdir != -1)
- (void)fchdir(startdir);
mandoc_msg_setinfilename(resp->file);
if (resp->file != NULL) {
@@ -982,18 +956,12 @@ parse(struct mparse *mp, int fd, const char *file,
struct outstate *outst, struct manconf *conf)
{
static struct manpaths basepaths;
- static int previous;
struct roff_meta *meta;
assert(fd >= 0);
if (file == NULL)
file = "<stdin>";
- if (previous)
- mparse_reset(mp);
- else
- previous = 1;
-
mparse_readfd(mp, fd, file);
if (fd != STDIN_FILENO)
close(fd);
diff --git a/contrib/mandoc/man.7 b/contrib/mandoc/man.7
index 4d27c76ba110..91eafbb35f70 100644
--- a/contrib/mandoc/man.7
+++ b/contrib/mandoc/man.7
@@ -1,7 +1,8 @@
-.\" $Id: man.7,v 1.150 2023/10/23 22:57:54 schwarze Exp $
+.\" $Id: man.7,v 1.154 2025/08/05 21:16:20 schwarze Exp $
.\"
+.\" Copyright (c) 2011-2015, 2017-2020, 2023, 2025
+.\" Ingo Schwarze <schwarze@openbsd.org>
.\" Copyright (c) 2009, 2010, 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
-.\" Copyright (c) 2011-2015,2017-2020,2023 Ingo Schwarze <schwarze@openbsd.org>
.\" Copyright (c) 2017 Anthony Bentley <bentley@openbsd.org>
.\" Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org>
.\"
@@ -17,7 +18,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
-.Dd $Mdocdate: October 23 2023 $
+.Dd $Mdocdate: August 5 2025 $
.Dt MAN 7
.Os
.Sh NAME
@@ -89,7 +90,7 @@ but can be found in the alphabetical reference below.
.Ss Page header and footer meta-data
.Bl -column "RS, RE" description
.It Ic TH Ta set the title: Ar name section date Op Ar source Op Ar volume
-.It Ic AT Ta display AT&T UNIX version in the page footer (<= 1 argument)
+.It Ic AT Ta display AT&T UNIX version in the page footer (<= 2 arguments)
.It Ic UC Ta display BSD version in the page footer (<= 1 argument)
.El
.Ss Sections and paragraphs
@@ -99,6 +100,7 @@ but can be found in the alphabetical reference below.
.It Ic PP Ta start an undecorated paragraph (no arguments)
.It Ic IP Ta indented paragraph: Op Ar head Op Ar width
.It Ic TP Ta tagged paragraph: Op Ar width
+.It Ic HP Ta hanged paragraph: Op Ar width
.It Ic PD Ta set vertical paragraph distance: Op Ar height
.It Ic EX , EE Ta display an example (no arguments)
.It Ic RS , RE Ta reset the left margin: Op Ar width
@@ -198,11 +200,6 @@ argument is a
scaling width.
If specified, it's saved for later paragraph left margins;
if unspecified, the saved or default width is used.
-.Pp
-This macro is portable, but deprecated
-because it has no good representation in HTML output,
-usually ending up indistinguishable from
-.Ic PP .
.It Ic I
Text is rendered in italics.
.It Ic IB
@@ -239,6 +236,17 @@ A synonym for
End a mailto block started with
.Ic MT .
This is a GNU extension.
+.It Ic MR
+Reference another manual page.
+This is a Plan 9 extension also supported by GNU.
+It has the following syntax:
+.Pp
+.D1 Pf . Ic MR Ar name section Op Ar suffix
+.Pp
+The optional, single
+.Ar suffix
+argument is appended without preceding whitespace
+and typically used for trailing punctuation.
.It Ic MT
Begin a mailto block.
This is a GNU extension.
@@ -250,8 +258,12 @@ link description to be shown
.Ed
.It Ic OP
Optional command-line argument.
-This is a rarely used DWB extension.
-It has the following syntax:
+This is a deprecated GNU extension.
+The name and purpose of the macro match an earlier DWB extension,
+but both the syntax and semantics are incompatible.
+In GNU and
+.Xr mandoc 1 ,
+it has the following syntax:
.Pp
.D1 Pf . Ic OP Ar key Op Ar value
.Pp
@@ -503,43 +515,56 @@ raised.
.Pp
The syntax is as follows:
.Bd -literal -offset indent
-\&.YO \(lBbody...\(rB
-\(lBbody...\(rB
+\&.\e" current-line syntax
+\&.YO \(lBbody ...\(rB
+
+\&.\e" next-line syntax
+\&.YO
+body ...
.Ed
-.Bl -column "MacroX" "ArgumentsX" "ScopeXXXXX" "CompatX" -offset indent
-.It Em Macro Ta Em Arguments Ta Em Scope Ta Em Notes
-.It Ic AT Ta <=1 Ta current Ta \&
-.It Ic B Ta n Ta next-line Ta \&
-.It Ic BI Ta n Ta current Ta \&
-.It Ic BR Ta n Ta current Ta \&
-.It Ic DT Ta 0 Ta current Ta \&
-.It Ic EE Ta 0 Ta current Ta Version 9 At
-.It Ic EX Ta 0 Ta current Ta Version 9 At
-.It Ic I Ta n Ta next-line Ta \&
-.It Ic IB Ta n Ta current Ta \&
-.It Ic IR Ta n Ta current Ta \&
-.It Ic OP Ta >=1 Ta current Ta DWB
-.It Ic PD Ta 1 Ta current Ta \&
-.It Ic RB Ta n Ta current Ta \&
-.It Ic RI Ta n Ta current Ta \&
-.It Ic SB Ta n Ta next-line Ta \&
-.It Ic SM Ta n Ta next-line Ta \&
-.It Ic TH Ta >1, <6 Ta current Ta \&
-.It Ic UC Ta <=1 Ta current Ta \&
-.It Ic in Ta 1 Ta current Ta Xr roff 7
+.Bl -column -offset indent\
+ "Macro" "Arguments" "curr and next" "Version 9 AT&T UNIX"
+.It Em Macro Ta Em Arguments Ta Em Line Scope Ta Em Notes
+.It Ic AT Ta 0 to 2 Ta current Ta \&
+.It Ic B Ta 1 or more Ta curr or next Ta \&
+.It Ic BI Ta 2 or more Ta current Ta \&
+.It Ic BR Ta 2 or more Ta current Ta \&
+.It Ic DT Ta 0 Ta none Ta \&
+.It Ic EE Ta 0 Ta none Ta Version 9 At
+.It Ic EX Ta 0 Ta none Ta Version 9 At
+.It Ic I Ta 1 or more Ta curr or next Ta \&
+.It Ic IB Ta 2 or more Ta current Ta \&
+.It Ic IR Ta 2 or more Ta current Ta \&
+.It Ic MR Ta 2 or 3 Ta current Ta Plan 9
+.It Ic OP Ta 1 or 2 Ta current Ta GNU
+.It Ic PD Ta 0 or 1 Ta current Ta \&
+.It Ic RB Ta 2 or more Ta current Ta \&
+.It Ic RI Ta 2 or more Ta current Ta \&
+.It Ic SB Ta 1 or more Ta curr or next Ta \&
+.It Ic SM Ta 1 or more Ta curr or next Ta \&
+.It Ic TH Ta 3 to 5 Ta current Ta \&
+.It Ic UC Ta 0 or 1 Ta current Ta \&
+.It Ic in Ta 0 or 1 Ta current Ta Xr roff 7
.El
.Ss Block Macros
Block macros comprise a head and body.
-As with in-line macros, the head is scoped to the current line and, in
-one circumstance, the next line (the next-line stipulations as in
+As with in-line macros, the head is scoped to the current line or,
+for some macros, to the next line (the next-line stipulations as in
.Sx Line Macros
apply here as well).
.Pp
The syntax is as follows:
.Bd -literal -offset indent
-\&.YO \(lBhead...\(rB
-\(lBhead...\(rB
-\(lBbody...\(rB
+\&.\e" current-line syntax
+\&.YO \(lBhead ...\(rB
+body ...
+\&...
+
+\&.\e" next-line syntax
+\&.YO \(lBhead\(rB
+head ...
+body ...
+\&...
.Ed
.Pp
The closure of body scope may be to the section, where a macro is closed
@@ -547,40 +572,42 @@ by
.Ic SH ;
sub-section, closed by a section or
.Ic SS ;
-or paragraph, closed by a section, sub-section,
+paragraph, closed by a section, sub-section,
.Ic HP ,
.Ic IP ,
.Ic LP ,
.Ic P ,
.Ic PP ,
-.Ic RE ,
+.Ic RS ,
.Ic SY ,
+.Ic TP ,
or
-.Ic TP .
-No closure refers to an explicit block closing macro.
+.Ic TQ ;
+or to an explicit block closing macro.
.Pp
As a rule, block macros may not be nested; thus, calling a block macro
while another block macro scope is open, and the open scope is not
implicitly closed, is syntactically incorrect.
-.Bl -column "MacroX" "ArgumentsX" "Head ScopeX" "sub-sectionX" "compatX" -offset indent
-.It Em Macro Ta Em Arguments Ta Em Head Scope Ta Em Body Scope Ta Em Notes
-.It Ic HP Ta <2 Ta current Ta paragraph Ta \&
-.It Ic IP Ta <3 Ta current Ta paragraph Ta \&
-.It Ic LP Ta 0 Ta current Ta paragraph Ta \&
-.It Ic ME Ta 0 Ta none Ta none Ta GNU
-.It Ic MT Ta 1 Ta current Ta to \&ME Ta GNU
-.It Ic P Ta 0 Ta current Ta paragraph Ta \&
-.It Ic PP Ta 0 Ta current Ta paragraph Ta \&
-.It Ic RE Ta <=1 Ta current Ta none Ta \&
-.It Ic RS Ta 1 Ta current Ta to \&RE Ta \&
-.It Ic SH Ta >0 Ta next-line Ta section Ta \&
-.It Ic SS Ta >0 Ta next-line Ta sub-section Ta \&
-.It Ic SY Ta 1 Ta current Ta to \&YS Ta GNU
-.It Ic TP Ta n Ta next-line Ta paragraph Ta \&
-.It Ic TQ Ta n Ta next-line Ta paragraph Ta GNU
-.It Ic UE Ta 0 Ta current Ta none Ta GNU
-.It Ic UR Ta 1 Ta current Ta part Ta GNU
-.It Ic YS Ta 0 Ta none Ta none Ta GNU
+.Bl -column -offset indent\
+ "Macro" "Arguments" "curr and next" "sub-section" "Notes"
+.It Em Macro Ta Em Arguments Ta Em Head Scope Ta Em Body Scope Ta Em Notes
+.It Ic HP Ta 0 or 1 Ta current Ta paragraph Ta \&
+.It Ic IP Ta 0 to 2 Ta current Ta paragraph Ta \&
+.It Ic LP Ta 0 Ta none Ta paragraph Ta \&
+.It Ic ME Ta 0 or 1 Ta current Ta none Ta GNU
+.It Ic MT Ta 1 Ta current Ta to \&ME Ta GNU
+.It Ic P Ta 0 Ta none Ta paragraph Ta \&
+.It Ic PP Ta 0 Ta none Ta paragraph Ta \&
+.It Ic RE Ta 0 or 1 Ta current Ta none Ta \&
+.It Ic RS Ta 0 or 1 Ta current Ta to \&RE Ta \&
+.It Ic SH Ta 1 or more Ta curr or next Ta section Ta \&
+.It Ic SS Ta 1 or more Ta curr or next Ta sub-section Ta \&
+.It Ic SY Ta 1 Ta current Ta to \&YS Ta GNU
+.It Ic TP Ta 0 or 1 Ta curr and next Ta paragraph Ta \&
+.It Ic TQ Ta 0 or 1 Ta curr and next Ta paragraph Ta GNU
+.It Ic UE Ta 0 or 1 Ta current Ta none Ta GNU
+.It Ic UR Ta 1 Ta current Ta to \&UE Ta GNU
+.It Ic YS Ta 0 Ta none Ta none Ta GNU
.El
.Pp
If a block macro is next-line scoped, it may only be followed by in-line
diff --git a/contrib/mandoc/man.c b/contrib/mandoc/man.c
index f651efe3de8b..26846f1cf243 100644
--- a/contrib/mandoc/man.c
+++ b/contrib/mandoc/man.c
@@ -1,4 +1,4 @@
-/* $Id: man.c,v 1.189 2022/08/16 23:01:09 schwarze Exp $ */
+/* $Id: man.c,v 1.190 2025/08/22 13:17:06 schwarze Exp $ */
/*
* Copyright (c) 2013-2015,2017-2019,2022 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
@@ -134,6 +134,7 @@ man_ptext(struct roff_man *man, int line, char *buf, int offs)
*ep = '\0';
return 1;
}
+ mandoc_msg(MANDOCERR_FI_BLANK, line, i, NULL);
roff_elem_alloc(man, line, offs, ROFF_sp);
man->next = ROFF_NEXT_SIBLING;
return 1;
diff --git a/contrib/mandoc/man.options.1 b/contrib/mandoc/man.options.1
index d8c790f4fa04..be101d4b5b62 100644
--- a/contrib/mandoc/man.options.1
+++ b/contrib/mandoc/man.options.1
@@ -1,6 +1,6 @@
-.\" $Id: man.options.1,v 1.7 2017/07/04 23:40:01 schwarze Exp $
+.\" $Id: man.options.1,v 1.9 2025/08/28 13:46:57 schwarze Exp $
.\"
-.\" Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org>
+.\" Copyright (c) 2017, 2025 Ingo Schwarze <schwarze@openbsd.org>
.\"
.\" Permission to use, copy, modify, and distribute this software for any
.\" purpose with or without fee is hereby granted, provided that the above
@@ -14,7 +14,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
-.Dd $Mdocdate: July 4 2017 $
+.Dd $Mdocdate: August 28 2025 $
.Dt MAN.OPTIONS 1
.Os
.Sh NAME
@@ -39,9 +39,18 @@
.de At5
.At V Pq January 1983 \\$1
..
+.de At8
+.No Version 8 At Pq February 1985 \\$1
+..
.de Bx43
.Bx 4.3 Pq June 1986 \\$1
..
+.de At9
+.No Version 9 At Pq September 1986 \\$1
+..
+.de At10
+.No Version 10 At Pq October 1989 \\$1
+..
.\" option was present in groff-1.01 as contained in 4.3BSD-Net/2
.\" and no mention of it could be found in the ChangeLog,
.\" so it's probably older than groff-0.4, where the log started
@@ -49,7 +58,7 @@
.No probably before groff-0.4 Pq before July 14, 1990 \\$1
..
.de Eaton
-.No Eaton Pq before July 7, 1993; 1990/91? \\$1
+.No Eaton Pq before July 7, 1993; 1990/91?\& \\$1
..
.\" man-1.5e was released on July 11, 1998.
.de man15e
@@ -399,6 +408,12 @@ do not feed out paper nor stop phototypesetter
.br
.Nm troff :
.At7
+.Pp
+.Bq superseded by Fl l
+interpret arguments as file names
+.br
+.Nm man :
+.At10
.It Fl G
preprocess with
.Xr grap 1
@@ -549,6 +564,7 @@ mode
.br
.Nm man :
.Bx4 ,
+.At10 ,
.Eaton ;
.No POSIX , Ox , Fx , Nx , No man-db , man-1.6 , illumos , Solaris 9-11
.br
@@ -720,7 +736,9 @@ specify a page number for the first page
output mode
.br
.Nm man :
-.At7
+.At7 ,
+.At8 ,
+.At10
.Pp
do not create the
.Xr whatis 1
@@ -852,6 +870,12 @@ invoke the simultaneous input-output mode of the .rd request
.Nm nroff , troff :
.At7
.Pp
+quick mode: prefer preformatted page if available
+.br
+.Nm man :
+.At8 ,
+.At10
+.Pp
issue no warnings
.br
.Nm manpath :
@@ -1046,6 +1070,8 @@ output mode
.Bx 2 Pq May 10, 1979 ,
.At3 ,
.At5 ,
+.At8 ,
+.At10 ,
.Eaton ;
.Fx , No man-db , man-1.6 , illumos , Solaris 9-11
.br
@@ -1113,7 +1139,8 @@ print version number
verbose mode
.br
.Nm catman :
-.Fx Pq March 15, 1995
+.Fx Pq March 15, 1995 ,
+.No mandoc Pq June 30, 2025
.br
.Nm makewhatis :
.man15g
@@ -1161,6 +1188,7 @@ list pathnames
.At7 ,
.At3 ,
.At5 ,
+.At8 ,
.Eaton ;
.Ox , Fx , Nx , No man-db , man-1.6
.br
diff --git a/contrib/mandoc/man_html.c b/contrib/mandoc/man_html.c
index 6784171af1e6..fc593be1112c 100644
--- a/contrib/mandoc/man_html.c
+++ b/contrib/mandoc/man_html.c
@@ -1,6 +1,6 @@
-/* $Id: man_html.c,v 1.187 2023/10/24 20:53:12 schwarze Exp $ */
+/* $Id: man_html.c,v 1.188 2025/06/26 17:06:34 schwarze Exp $ */
/*
- * Copyright (c) 2013-15,2017-20,2022-23 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2013-2020,2022-2023,2025 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2008-2012, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
*
* Permission to use, copy, modify, and distribute this software for any
@@ -293,21 +293,30 @@ static void
man_root_post(const struct roff_meta *man, struct html *h)
{
struct tag *t;
+ char *title;
+
+ assert(man->title != NULL);
+ if (man->msec == NULL)
+ title = mandoc_strdup(man->title);
+ else
+ mandoc_asprintf(&title, "%s(%s)", man->title, man->msec);
t = print_otag(h, TAG_DIV, "cr?", "foot", "doc-pagefooter",
"aria-label", "Manual footer line");
print_otag(h, TAG_SPAN, "c", "foot-left");
+ if (man->os != NULL)
+ print_text(h, man->os);
print_stagq(h, t);
print_otag(h, TAG_SPAN, "c", "foot-date");
print_text(h, man->date);
print_stagq(h, t);
- print_otag(h, TAG_SPAN, "c", "foot-os");
- if (man->os != NULL)
- print_text(h, man->os);
+ print_otag(h, TAG_SPAN, "c", "foot-right");
+ print_text(h, title);
print_tagq(h, t);
+ free(title);
}
static int
diff --git a/contrib/mandoc/man_term.c b/contrib/mandoc/man_term.c
index 706fab8cd4d1..ac75c2c5ef40 100644
--- a/contrib/mandoc/man_term.c
+++ b/contrib/mandoc/man_term.c
@@ -1,6 +1,6 @@
-/* $Id: man_term.c,v 1.244 2023/11/13 19:13:01 schwarze Exp $ */
+/* $Id: man_term.c,v 1.248 2025/07/27 15:27:28 schwarze Exp $ */
/*
- * Copyright (c) 2010-15,2017-20,2022-23 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2010-2020,2022-23,2025 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
*
* Permission to use, copy, modify, and distribute this software for any
@@ -38,14 +38,14 @@
#include "term_tag.h"
#include "main.h"
-#define MAXMARGINS 64 /* maximum number of indented scopes */
+#define MAXMARGINS 64 /* Maximum number of indented scopes. */
struct mtermp {
- int lmargin[MAXMARGINS]; /* margins (incl. vis. page) */
- int lmargincur; /* index of current margin */
- int lmarginsz; /* actual number of nested margins */
- size_t offset; /* default offset to visible page */
- int pardist; /* vert. space before par., unit: [v] */
+ int lmargin[MAXMARGINS]; /* Margins in basic units. */
+ int lmargincur; /* Index of current margin. */
+ int lmarginsz; /* Actual number of nested margins. */
+ size_t offset; /* Default offset in basic units. */
+ int pardist; /* Vert. space before par., unit: [v]. */
};
#define DECL_ARGS struct termp *p, \
@@ -194,12 +194,10 @@ terminal_man(void *arg, const struct roff_meta *man)
}
/*
- * Printing leading vertical space before a block.
- * This is used for the paragraph macros.
- * The rules are pretty simple, since there's very little nesting going
- * on here. Basically, if we're the first within another block (SS/SH),
- * then don't emit vertical space. If we are (RS), then do. If not the
- * first, print it.
+ * Print leading vertical space before a paragraph, unless
+ * it is the first paragraph in a section or subsection.
+ * If it is the first paragraph in an .RS block, consider
+ * that .RS block instead of the paragraph, recursively.
*/
static void
print_bvspace(struct termp *p, struct roff_node *n, int pardist)
@@ -214,9 +212,13 @@ print_bvspace(struct termp *p, struct roff_node *n, int pardist)
nch->type == ROFFT_TBL)
return;
- if (n->parent->tok != MAN_RS && roff_node_prev(n) == NULL)
- return;
-
+ while (roff_node_prev(n) == NULL) {
+ n = n->parent;
+ if (n->tok != MAN_RS)
+ return;
+ if (n->type == ROFFT_BODY)
+ n = n->parent;
+ }
for (i = 0; i < pardist; i++)
term_vspace(p);
}
@@ -372,8 +374,8 @@ static int
pre_in(DECL_ARGS)
{
struct roffsu su;
- const char *cp;
- size_t v;
+ const char *cp; /* Request argument. */
+ size_t v; /* Indentation in basic units. */
int less;
term_newln(p);
@@ -386,17 +388,18 @@ pre_in(DECL_ARGS)
cp = n->child->string;
less = 0;
- if (*cp == '-')
+ if (*cp == '-') {
less = -1;
- else if (*cp == '+')
+ cp++;
+ } else if (*cp == '+') {
less = 1;
- else
- cp--;
+ cp++;
+ }
- if (a2roffsu(++cp, &su, SCALE_EN) == NULL)
+ if (a2roffsu(cp, &su, SCALE_EN) == NULL)
return 0;
- v = term_hen(p, &su);
+ v = term_hspan(p, &su);
if (less < 0)
p->tcol->offset -= p->tcol->offset > v ? v : p->tcol->offset;
@@ -424,7 +427,7 @@ pre_HP(DECL_ARGS)
{
struct roffsu su;
const struct roff_node *nn;
- int len;
+ int len; /* Indentation in basic units. */
switch (n->type) {
case ROFFT_BLOCK:
@@ -450,7 +453,7 @@ pre_HP(DECL_ARGS)
if ((nn = n->parent->head->child) != NULL &&
a2roffsu(nn->string, &su, SCALE_EN) != NULL) {
- len = term_hen(p, &su);
+ len = term_hspan(p, &su);
if (len < 0 && (size_t)(-len) > mt->offset)
len = -mt->offset;
else if (len > SHRT_MAX)
@@ -518,7 +521,7 @@ pre_IP(DECL_ARGS)
{
struct roffsu su;
const struct roff_node *nn;
- int len;
+ int len; /* Indentation in basic units. */
switch (n->type) {
case ROFFT_BLOCK:
@@ -539,7 +542,7 @@ pre_IP(DECL_ARGS)
if ((nn = n->parent->head->child) != NULL &&
(nn = nn->next) != NULL &&
a2roffsu(nn->string, &su, SCALE_EN) != NULL) {
- len = term_hen(p, &su);
+ len = term_hspan(p, &su);
if (len < 0 && (size_t)(-len) > mt->offset)
len = -mt->offset;
else if (len > SHRT_MAX)
@@ -591,7 +594,7 @@ pre_TP(DECL_ARGS)
{
struct roffsu su;
struct roff_node *nn;
- int len;
+ int len; /* Indentation in basic units. */
switch (n->type) {
case ROFFT_BLOCK:
@@ -614,7 +617,7 @@ pre_TP(DECL_ARGS)
if ((nn = n->parent->head->child) != NULL &&
nn->string != NULL && ! (NODE_LINE & nn->flags) &&
a2roffsu(nn->string, &su, SCALE_EN) != NULL) {
- len = term_hen(p, &su);
+ len = term_hspan(p, &su);
if (len < 0 && (size_t)(-len) > mt->offset)
len = -mt->offset;
else if (len > SHRT_MAX)
@@ -691,10 +694,11 @@ pre_SS(DECL_ARGS)
term_vspace(p);
break;
case ROFFT_HEAD:
+ p->fontibi = 1;
term_fontrepl(p, TERMFONT_BOLD);
- p->tcol->offset = term_len(p, 3);
+ p->tcol->offset = term_len(p, p->defindent) / 2 + 1;
p->tcol->rmargin = mt->offset;
- p->trailspace = mt->offset;
+ p->trailspace = mt->offset / term_len(p, 1);
p->flags |= TERMP_NOBREAK | TERMP_BRIND;
break;
case ROFFT_BODY:
@@ -732,10 +736,11 @@ pre_SH(DECL_ARGS)
term_vspace(p);
break;
case ROFFT_HEAD:
+ p->fontibi = 1;
term_fontrepl(p, TERMFONT_BOLD);
p->tcol->offset = 0;
p->tcol->rmargin = mt->offset;
- p->trailspace = mt->offset;
+ p->trailspace = mt->offset / term_len(p, 1);
p->flags |= TERMP_NOBREAK | TERMP_BRIND;
break;
case ROFFT_BODY:
@@ -757,6 +762,8 @@ post_SH(DECL_ARGS)
case ROFFT_BLOCK:
break;
case ROFFT_HEAD:
+ p->fontibi = 0;
+ /* FALLTHROUGH */
case ROFFT_BODY:
term_newln(p);
break;
@@ -787,7 +794,7 @@ pre_RS(DECL_ARGS)
if (n->child == NULL)
n->aux = mt->lmargin[mt->lmargincur];
else if (a2roffsu(n->child->string, &su, SCALE_EN) != NULL)
- n->aux = term_hen(p, &su);
+ n->aux = term_hspan(p, &su);
if (n->aux < 0 && (size_t)(-n->aux) > mt->offset)
n->aux = -mt->offset;
else if (n->aux > SHRT_MAX)
@@ -827,7 +834,7 @@ static int
pre_SY(DECL_ARGS)
{
const struct roff_node *nn;
- int len;
+ int len; /* Indentation in basic units. */
switch (n->type) {
case ROFFT_BLOCK:
@@ -842,7 +849,9 @@ pre_SY(DECL_ARGS)
}
nn = n->parent->head->child;
- len = nn == NULL ? 1 : term_strlen(p, nn->string) + 1;
+ len = term_len(p, 1);
+ if (nn != NULL)
+ len += term_strlen(p, nn->string);
switch (n->type) {
case ROFFT_HEAD:
@@ -1015,40 +1024,26 @@ static void
print_man_foot(struct termp *p, const struct roff_meta *meta)
{
char *title;
- size_t datelen, titlen;
+ size_t datelen, titlen; /* In basic units. */
- assert(meta->title);
- assert(meta->msec);
- assert(meta->date);
+ assert(meta->title != NULL);
+ assert(meta->msec != NULL);
term_fontrepl(p, TERMFONT_NONE);
-
if (meta->hasbody)
term_vspace(p);
- /*
- * Temporary, undocumented option to imitate mdoc(7) output.
- * In the bottom right corner, use the operating system
- * instead of the title.
- */
-
- if ( ! p->mdocstyle) {
- mandoc_asprintf(&title, "%s(%s)",
- meta->title, meta->msec);
- } else if (meta->os != NULL) {
- title = mandoc_strdup(meta->os);
- } else {
- title = mandoc_strdup("");
- }
datelen = term_strlen(p, meta->date);
+ mandoc_asprintf(&title, "%s(%s)", meta->title, meta->msec);
+ titlen = term_strlen(p, title);
/* Bottom left corner: operating system. */
- p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
- p->trailspace = 1;
p->tcol->offset = 0;
p->tcol->rmargin = p->maxrmargin > datelen ?
(p->maxrmargin + term_len(p, 1) - datelen) / 2 : 0;
+ p->trailspace = 1;
+ p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
if (meta->os)
term_word(p, meta->os);
@@ -1057,7 +1052,6 @@ print_man_foot(struct termp *p, const struct roff_meta *meta)
/* At the bottom in the middle: manual date. */
p->tcol->offset = p->tcol->rmargin;
- titlen = term_strlen(p, title);
p->tcol->rmargin = p->maxrmargin > titlen ?
p->maxrmargin - titlen : 0;
p->flags |= TERMP_NOSPACE;
@@ -1067,11 +1061,11 @@ print_man_foot(struct termp *p, const struct roff_meta *meta)
/* Bottom right corner: manual title and section. */
- p->flags &= ~TERMP_NOBREAK;
- p->flags |= TERMP_NOSPACE;
- p->trailspace = 0;
p->tcol->offset = p->tcol->rmargin;
p->tcol->rmargin = p->maxrmargin;
+ p->trailspace = 0;
+ p->flags &= ~TERMP_NOBREAK;
+ p->flags |= TERMP_NOSPACE;
term_word(p, title);
term_flushln(p);
@@ -1086,7 +1080,6 @@ print_man_foot(struct termp *p, const struct roff_meta *meta)
p->tcol->offset = 0;
p->flags = 0;
-
free(title);
}
@@ -1095,7 +1088,7 @@ print_man_head(struct termp *p, const struct roff_meta *meta)
{
const char *volume;
char *title;
- size_t vollen, titlen;
+ size_t vollen, titlen; /* In basic units. */
assert(meta->title);
assert(meta->msec);
@@ -1111,7 +1104,8 @@ print_man_head(struct termp *p, const struct roff_meta *meta)
p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
p->trailspace = 1;
p->tcol->offset = 0;
- p->tcol->rmargin = 2 * (titlen+1) + vollen < p->maxrmargin ?
+ p->tcol->rmargin =
+ titlen * 2 + term_len(p, 2) + vollen < p->maxrmargin ?
(p->maxrmargin - vollen + term_len(p, 1)) / 2 :
vollen < p->maxrmargin ? p->maxrmargin - vollen : 0;
@@ -1123,7 +1117,7 @@ print_man_head(struct termp *p, const struct roff_meta *meta)
p->flags |= TERMP_NOSPACE;
p->tcol->offset = p->tcol->rmargin;
p->tcol->rmargin = p->tcol->offset + vollen + titlen <
- p->maxrmargin ? p->maxrmargin - titlen : p->maxrmargin;
+ p->maxrmargin ? p->maxrmargin - titlen : p->maxrmargin;
term_word(p, volume);
term_flushln(p);
@@ -1143,13 +1137,6 @@ print_man_head(struct termp *p, const struct roff_meta *meta)
p->flags &= ~TERMP_NOSPACE;
p->tcol->offset = 0;
p->tcol->rmargin = p->maxrmargin;
-
- /*
- * Groff prints three blank lines before the content.
- * Do the same, except in the temporary, undocumented
- * mode imitating mdoc(7) output.
- */
-
term_vspace(p);
free(title);
}
diff --git a/contrib/mandoc/man_validate.c b/contrib/mandoc/man_validate.c
index 857adba2798f..57ac9327afd4 100644
--- a/contrib/mandoc/man_validate.c
+++ b/contrib/mandoc/man_validate.c
@@ -1,6 +1,6 @@
-/* $Id: man_validate.c,v 1.159 2023/10/24 20:53:12 schwarze Exp $ */
+/* $Id: man_validate.c,v 1.161 2025/07/09 12:51:06 schwarze Exp $ */
/*
- * Copyright (c) 2010, 2012-2020, 2023 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2010-2020, 2023, 2025 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
*
* Permission to use, copy, modify, and distribute this software for any
@@ -299,6 +299,14 @@ post_SH(CHKARGS)
nc = n->child;
switch (n->type) {
+ case ROFFT_BLOCK:
+ if ((nc = n->prev) != NULL && nc->tok == ROFF_br) {
+ mandoc_msg(MANDOCERR_PAR_SKIP, nc->line, nc->pos,
+ "%s before first %s", roff_name[nc->tok],
+ roff_name[n->tok]);
+ roff_node_delete(man, nc);
+ }
+ return;
case ROFFT_HEAD:
tag = NULL;
deroff(&tag, n);
@@ -473,7 +481,7 @@ post_TH(CHKARGS)
/* ->TITLE<- MSEC DATE OS VOL */
n = n->child;
- if (n != NULL && n->string != NULL) {
+ if (n != NULL && n->string != NULL && *n->string != '\0') {
for (p = n->string; *p != '\0'; p++) {
/* Only warn about this once... */
if (isalpha((unsigned char)*p) &&
@@ -486,8 +494,8 @@ post_TH(CHKARGS)
}
man->meta.title = mandoc_strdup(n->string);
} else {
- man->meta.title = mandoc_strdup("");
- mandoc_msg(MANDOCERR_TH_NOTITLE, nb->line, nb->pos, "TH");
+ man->meta.title = mandoc_strdup("UNTITLED");
+ mandoc_msg(MANDOCERR_DT_NOTITLE, nb->line, nb->pos, "TH");
}
/* TITLE ->MSEC<- DATE OS VOL */
diff --git a/contrib/mandoc/mandoc.1 b/contrib/mandoc/mandoc.1
index 32a3e2811513..0f83bcd53f1b 100644
--- a/contrib/mandoc/mandoc.1
+++ b/contrib/mandoc/mandoc.1
@@ -1,4 +1,4 @@
-.\" $Id: mandoc.1,v 1.270 2025/03/03 14:07:51 schwarze Exp $
+.\" $Id: mandoc.1,v 1.273 2025/08/22 13:17:06 schwarze Exp $
.\"
.\" Copyright (c) 2012, 2014-2023, 2025 Ingo Schwarze <schwarze@openbsd.org>
.\" Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
@@ -15,7 +15,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
-.Dd $Mdocdate: March 3 2025 $
+.Dd $Mdocdate: August 22 2025 $
.Dt MANDOC 1
.Os
.Sh NAME
@@ -292,19 +292,6 @@ Increasing this is not recommended; it may result in degraded formatting,
for example overfull lines or ugly line breaks.
When output is to a pager on a terminal that is less than 66 columns
wide, the default is reduced to three columns.
-.It Cm mdoc
-Format
-.Xr man 7
-input files in
-.Xr mdoc 7
-output style.
-This prints the operating system name rather than the page title
-on the right side of the footer line.
-One useful application is for checking that
-.Fl T Cm man
-output formats in the same way as the
-.Xr mdoc 7
-source it was generated from.
.It Cm tag Ns Op = Ns Ar term
If the formatted manual page is opened in a pager,
go to the definition of the
@@ -1015,14 +1002,14 @@ macro that could be represented using
or
.Ic \&Dx .
.It Sy "errnos out of order"
-.Pq mdoc, Nx
+.Pq mdoc , Nx
The
.Ic \&Er
items in a
.Ic \&Bl
list are not in alphabetical order.
.It Sy "duplicate errno"
-.Pq mdoc, Nx
+.Pq mdoc , Nx
A
.Ic \&Bl
list contains two consecutive
@@ -1121,17 +1108,21 @@ but leaving out the backslash might not be portable.
.Ss Warnings related to the document prologue
.Bl -ohang
.It Sy "missing manual title, using UNTITLED"
-.Pq mdoc
+.Pq mdoc , man
A
.Ic \&Dt
-macro has no arguments, or there is no
+or
+.Ic \&TH
+macro has no arguments, its first argument is an empty string, or there is no
.Ic \&Dt
-macro before the first non-prologue macro.
+macro before the first non-prologue
+.Xr mdoc 7
+macro.
.It Sy "missing manual title, using \(dq\(dq"
.Pq man
-There is no
+An input document does not contain any
.Ic \&TH
-macro, or it has no arguments.
+macro.
.It Sy "missing manual section, using \(dq\(dq"
.Pq mdoc , man
A
@@ -1159,7 +1150,7 @@ The
argument is used as provided anyway.
Consider checking whether the file name or the argument need a correction.
.It Sy "missing date, using \(dq\(dq"
-.Pq mdoc, man
+.Pq mdoc , man
The document was parsed as
.Xr mdoc 7
and it has no
@@ -1741,7 +1732,7 @@ or
macro contains an opening or closing parenthesis; that's probably wrong,
parentheses are added automatically.
.It Sy "unknown library name"
-.Pq mdoc, not on Ox
+.Pq mdoc , not on Ox
An
.Ic \&Lb
macro has an unknown name argument and will be rendered as
@@ -1799,7 +1790,7 @@ The last character is mapped to the blank character.
.Ss "Warnings related to plain text"
.Bl -ohang
.It Sy "blank line in fill mode, using .sp"
-.Pq mdoc
+.Pq mdoc , man
The meaning of blank input lines is only well-defined in non-fill mode:
In fill mode, line breaks of text input lines are not supposed to be
significant.
@@ -1809,6 +1800,8 @@ are formatted like
requests.
To request a paragraph break, use
.Ic \&Pp
+or
+.Ic \&PP
instead of a blank line.
.It Sy "tab in filled text"
.Pq mdoc , man
diff --git a/contrib/mandoc/mandoc.css b/contrib/mandoc/mandoc.css
index 88432b9322b7..46e03a386ae0 100644
--- a/contrib/mandoc/mandoc.css
+++ b/contrib/mandoc/mandoc.css
@@ -1,4 +1,4 @@
-/* $Id: mandoc.css,v 1.54 2025/01/25 03:18:55 schwarze Exp $ */
+/* $Id: mandoc.css,v 1.55 2025/06/26 17:06:34 schwarze Exp $ */
/*
* Standard style sheet for mandoc(1) -Thtml and man.cgi(8).
*
@@ -73,7 +73,7 @@ div[role=doc-pagefooter] {
.foot-left { flex: 1; }
.foot-date { flex: 0 1 auto;
text-align: center; }
-.foot-os { flex: 1;
+.foot-right { flex: 1;
text-align: right; }
/* Sections and paragraphs. */
diff --git a/contrib/mandoc/mandocd.8 b/contrib/mandoc/mandocd.8
index d679deb1b9e4..aaf4e3dede70 100644
--- a/contrib/mandoc/mandocd.8
+++ b/contrib/mandoc/mandocd.8
@@ -1,6 +1,6 @@
-.\" $Id: mandocd.8,v 1.3 2021/09/28 15:41:41 schwarze Exp $
+.\" $Id: mandocd.8,v 1.5 2025/06/30 15:07:38 schwarze Exp $
.\"
-.\" Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org>
+.\" Copyright (c) 2017, 2025 Ingo Schwarze <schwarze@openbsd.org>
.\"
.\" Permission to use, copy, modify, and distribute this software for any
.\" purpose with or without fee is hereby granted, provided that the above
@@ -14,7 +14,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
-.Dd $Mdocdate: September 28 2021 $
+.Dd $Mdocdate: June 30 2025 $
.Dt MANDOCD 8
.Os
.Sh NAME
@@ -78,6 +78,19 @@ or
input, the second one for formatted output, and the third one
for error output.
.Pp
+After accepting each message,
+.Nm
+replies with a one-byte message of its own,
+such that the parent process can keep track of how many messages
+.Nm
+has already accepted and how many file descriptors
+consequently are still in flight, such that the parent process
+can limit the number of file descriptors in flight at any given time
+in order to prevent
+.Er EMFILE
+failure of
+.Xr sendmsg 2 .
+.Pp
The options are as follows:
.Bl -tag -width Ds
.It Fl I Cm os Ns = Ns Ar name
@@ -112,7 +125,7 @@ Other output options are not supported.
After exhausting one input file descriptor, all three file descriptors
are closed before reading the next dummy byte and control message.
.Pp
-When a zero-byte message is read, when the
+When a zero-byte message or a misformatted message is read, when the
.Ar socket_fd
is closed by the parent process,
or when an error occurs,
@@ -131,9 +144,10 @@ missing, invalid, or excessive
.Xr exec 3
arguments
.It
+communication failure with the parent, for example failure in
.Xr recvmsg 2
-failure, for example due to
-.Er EMSGSIZE
+or
+.Xr send 2
.It
missing or unexpected control data, in particular a
.Fa cmsg_level
diff --git a/contrib/mandoc/mandocd.c b/contrib/mandoc/mandocd.c
index ccc846bd0310..52ba0cc613fa 100644
--- a/contrib/mandoc/mandocd.c
+++ b/contrib/mandoc/mandocd.c
@@ -1,7 +1,7 @@
-/* $Id: mandocd.c,v 1.13 2022/04/14 16:43:44 schwarze Exp $ */
+/* $Id: mandocd.c,v 1.15 2025/06/30 15:04:57 schwarze Exp $ */
/*
+ * Copyright (c) 2017-2019, 2022, 2025 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2017 Michael Stapelberg <stapelberg@debian.org>
- * Copyright (c) 2017, 2019, 2021 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -27,11 +27,14 @@
#if HAVE_ERR
#include <err.h>
#endif
+#include <errno.h>
#include <limits.h>
+#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <time.h>
#include <unistd.h>
#include "mandoc.h"
@@ -61,6 +64,7 @@ static void usage(void) __attribute__((__noreturn__));
static int
read_fds(int clientfd, int *fds)
{
+ const struct timespec timeout = { 0, 10000000 }; /* 0.01 s */
struct msghdr msg;
struct iovec iov[1];
unsigned char dummy[1];
@@ -98,6 +102,15 @@ read_fds(int clientfd, int *fds)
break;
}
+ *dummy = '\0';
+ while (send(clientfd, dummy, sizeof(dummy), 0) == -1) {
+ if (errno != EAGAIN) {
+ warn("send");
+ return -1;
+ }
+ nanosleep(&timeout, NULL);
+ }
+
if ((cmsg = CMSG_FIRSTHDR(&msg)) == NULL) {
warnx("CMSG_FIRSTHDR: missing control message");
return -1;
@@ -120,6 +133,7 @@ read_fds(int clientfd, int *fds)
int
main(int argc, char *argv[])
{
+ struct sigaction sa;
struct manoutput options;
struct mparse *parser;
void *formatter;
@@ -170,13 +184,25 @@ main(int argc, char *argv[])
argc -= optind;
argv += optind;
}
- if (argc != 1)
+ if (argc != 1) {
+ if (argc == 0)
+ warnx("missing argument: socket_fd");
+ else
+ warnx("too many arguments: %s", argv[1]);
usage();
+ }
errstr = NULL;
clientfd = strtonum(argv[0], 3, INT_MAX, &errstr);
if (errstr)
- errx(1, "file descriptor %s %s", argv[1], errstr);
+ errx(1, "file descriptor %s is %s", argv[0], errstr);
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = SIG_IGN;
+ if (sigfillset(&sa.sa_mask) == -1)
+ err(1, "sigfillset");
+ if (sigaction(SIGPIPE, &sa, NULL) == -1)
+ err(1, "sigaction(SIGPIPE)");
mchars_alloc();
parser = mparse_alloc(MPARSE_SO | MPARSE_UTF8 | MPARSE_LATIN1 |
diff --git a/contrib/mandoc/manpath.c b/contrib/mandoc/manpath.c
index 3760e2293c3a..f744368b5a38 100644
--- a/contrib/mandoc/manpath.c
+++ b/contrib/mandoc/manpath.c
@@ -1,6 +1,6 @@
-/* $Id: manpath.c,v 1.44 2021/11/05 18:03:08 schwarze Exp $ */
+/* $Id: manpath.c,v 1.45 2025/06/26 17:26:23 schwarze Exp $ */
/*
- * Copyright (c) 2011,2014,2015,2017-2019 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2011,2014,2015,2017-2021 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
*
* Permission to use, copy, modify, and distribute this software for any
@@ -218,7 +218,7 @@ manconf_output(struct manoutput *conf, const char *cp, int fromfile)
/* Token taking an optional argument. */
"tag",
/* Tokens not taking arguments. */
- "fragment", "mdoc", "noval", "toc"
+ "fragment", "noval", "toc"
};
const size_t ntoks = sizeof(toks) / sizeof(toks[0]);
@@ -328,12 +328,9 @@ manconf_output(struct manoutput *conf, const char *cp, int fromfile)
conf->fragment = 1;
return 0;
case 10:
- conf->mdoc = 1;
- return 0;
- case 11:
conf->noval = 1;
return 0;
- case 12:
+ case 11:
conf->toc = 1;
return 0;
default:
diff --git a/contrib/mandoc/mdoc.7 b/contrib/mandoc/mdoc.7
index 55cc7fae688d..6c2d3568baa6 100644
--- a/contrib/mandoc/mdoc.7
+++ b/contrib/mandoc/mdoc.7
@@ -1,4 +1,4 @@
-.\" $Id: mdoc.7,v 1.299 2025/06/13 16:18:28 schwarze Exp $
+.\" $Id: mdoc.7,v 1.300 2025/08/19 14:08:59 schwarze Exp $
.\"
.\" Copyright (c) 2010-2021, 2024, 2025 Ingo Schwarze <schwarze@openbsd.org>
.\" Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
@@ -15,7 +15,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
-.Dd $Mdocdate: June 13 2025 $
+.Dd $Mdocdate: August 19 2025 $
.Dt MDOC 7
.Os
.Sh NAME
@@ -979,7 +979,7 @@ Unless the
.Fl compact
argument is specified, list entries are separated by vertical space.
.Pp
-A list must specify one of the following list types:
+The following list types are commonly used:
.Bl -tag -width 12n -offset indent
.It Fl bullet
No item heads can be specified, but a bullet will be printed at the head
@@ -994,7 +994,14 @@ The
.Fl width
argument has no effect; instead, the string length of each argument
specifies the width of one column.
-If the first line of the body of a
+The width specification for the last column does not affect formatting.
+.Pp
+For two-column lists, using
+.Fl tag
+often results in simpler code, identical terminal output, and better HTML
+output, especially when the first column contains short identifiers.
+.Pp
+For compatibility with legacy documents, if the first line of the body of a
.Fl column
list is not an
.Ic \&It
@@ -1016,7 +1023,7 @@ Like
except that item heads are not parsed for macro invocations.
Most often used in the
.Em DIAGNOSTICS
-section with error constants in the item heads.
+section with error messages in the item heads.
.It Fl enum
A numbered list.
No item heads can be specified.
@@ -1024,6 +1031,17 @@ Formatted like
.Fl bullet ,
except that ordinal numbers are used in place of bullets,
starting at 1.
+.It Fl tag
+Item bodies are indented according to the
+.Fl width
+argument.
+When an item head fits inside the indentation, the item body follows
+this head on the same output line.
+Otherwise, the body starts on the output line following the head.
+.El
+.Pp
+The following list types are rarely useful:
+.Bl -tag -width 12n -offset indent
.It Fl hang
Like
.Fl tag ,
@@ -1050,13 +1068,6 @@ Item bodies start on the line following item heads and are not indented.
The
.Fl width
argument is ignored.
-.It Fl tag
-Item bodies are indented according to the
-.Fl width
-argument.
-When an item head fits inside the indentation, the item body follows
-this head on the same output line.
-Otherwise, the body starts on the output line following the head.
.El
.Pp
Lists may be nested within lists and displays.
diff --git a/contrib/mandoc/mdoc_html.c b/contrib/mandoc/mdoc_html.c
index b67eac4be233..8ac3884c7225 100644
--- a/contrib/mandoc/mdoc_html.c
+++ b/contrib/mandoc/mdoc_html.c
@@ -1,4 +1,4 @@
-/* $Id: mdoc_html.c,v 1.353 2025/01/25 00:22:28 schwarze Exp $ */
+/* $Id: mdoc_html.c,v 1.354 2025/06/26 17:06:34 schwarze Exp $ */
/*
* Copyright (c) 2014-2022, 2025 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2008-2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
@@ -454,20 +454,29 @@ static void
mdoc_root_post(const struct roff_meta *meta, struct html *h)
{
struct tag *t;
+ char *title;
+
+ assert(meta->title != NULL);
+ if (meta->msec == NULL)
+ title = mandoc_strdup(meta->title);
+ else
+ mandoc_asprintf(&title, "%s(%s)", meta->title, meta->msec);
t = print_otag(h, TAG_DIV, "cr?", "foot", "doc-pagefooter",
"aria-label", "Manual footer line");
print_otag(h, TAG_SPAN, "c", "foot-left");
+ print_text(h, meta->os);
print_stagq(h, t);
print_otag(h, TAG_SPAN, "c", "foot-date");
print_text(h, meta->date);
print_stagq(h, t);
- print_otag(h, TAG_SPAN, "c", "foot-os");
- print_text(h, meta->os);
+ print_otag(h, TAG_SPAN, "c", "foot-right");
+ print_text(h, title);
print_tagq(h, t);
+ free(title);
}
static int
diff --git a/contrib/mandoc/mdoc_man.c b/contrib/mandoc/mdoc_man.c
index 5438b2ba5941..99693b5d81dd 100644
--- a/contrib/mandoc/mdoc_man.c
+++ b/contrib/mandoc/mdoc_man.c
@@ -1,4 +1,4 @@
-/* $Id: mdoc_man.c,v 1.139 2025/01/24 22:37:24 schwarze Exp $ */
+/* $Id: mdoc_man.c,v 1.141 2025/07/02 19:57:48 schwarze Exp $ */
/*
* Copyright (c) 2011-2021, 2025 Ingo Schwarze <schwarze@openbsd.org>
*
@@ -494,6 +494,7 @@ print_offs(const char *v, int keywords)
const char *end;
int sz;
+ outflags &= ~MMAN_PP;
print_line(".RS", MMAN_Bk_susp);
/* Convert v into a number (of characters). */
@@ -1616,9 +1617,7 @@ pre_lk(DECL_ARGS)
}
/* Link target. */
- font_push('B');
print_word(link->string);
- font_pop();
/* Trailing punctuation. */
while (punct != NULL) {
diff --git a/contrib/mandoc/mdoc_markdown.c b/contrib/mandoc/mdoc_markdown.c
index 06ca839a58b8..eaa22626c99c 100644
--- a/contrib/mandoc/mdoc_markdown.c
+++ b/contrib/mandoc/mdoc_markdown.c
@@ -1,4 +1,4 @@
-/* $Id: mdoc_markdown.c,v 1.39 2025/01/20 07:01:17 schwarze Exp $ */
+/* $Id: mdoc_markdown.c,v 1.40 2025/06/26 17:06:34 schwarze Exp $ */
/*
* Copyright (c) 2017, 2018, 2020, 2025 Ingo Schwarze <schwarze@openbsd.org>
*
@@ -292,6 +292,14 @@ markdown_mdoc(void *arg, const struct roff_meta *mdoc)
md_word(mdoc->os);
md_word("-");
md_word(mdoc->date);
+ md_word("-");
+ md_word(mdoc->title);
+ if (mdoc->msec != NULL) {
+ outflags &= ~MD_spc;
+ md_word("(");
+ md_word(mdoc->msec);
+ md_word(")");
+ }
putchar('\n');
}
diff --git a/contrib/mandoc/mdoc_term.c b/contrib/mandoc/mdoc_term.c
index 931bc384a002..b0544de0304e 100644
--- a/contrib/mandoc/mdoc_term.c
+++ b/contrib/mandoc/mdoc_term.c
@@ -1,6 +1,6 @@
-/* $Id: mdoc_term.c,v 1.383 2023/11/13 19:13:01 schwarze Exp $ */
+/* $Id: mdoc_term.c,v 1.387 2025/07/27 15:27:28 schwarze Exp $ */
/*
- * Copyright (c) 2010, 2012-2020, 2022 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2010,2012-2020,2022,2025 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2013 Franco Fichtner <franco@lastsummer.de>
*
@@ -302,7 +302,7 @@ print_mdoc_node(DECL_ARGS)
{
const struct mdoc_term_act *act;
struct termpair npair;
- size_t offset, rmargin;
+ size_t offset, rmargin; /* In basic units. */
int chld;
/*
@@ -441,70 +441,62 @@ print_mdoc_node(DECL_ARGS)
static void
print_mdoc_foot(struct termp *p, const struct roff_meta *meta)
{
- size_t sz;
+ char *title;
+ size_t datelen, titlen; /* In basic units. */
- term_fontrepl(p, TERMFONT_NONE);
-
- /*
- * Output the footer in new-groff style, that is, three columns
- * with the middle being the manual date and flanking columns
- * being the operating system:
- *
- * SYSTEM DATE SYSTEM
- */
+ assert(meta->title != NULL);
+ datelen = term_strlen(p, meta->date);
+ if (meta->msec == NULL)
+ title = mandoc_strdup(meta->title);
+ else
+ mandoc_asprintf(&title, "%s(%s)", meta->title, meta->msec);
+ titlen = term_strlen(p, title);
+ term_fontrepl(p, TERMFONT_NONE);
term_vspace(p);
+ /* Bottom left corner: operating system. */
+
p->tcol->offset = 0;
- sz = term_strlen(p, meta->date);
- p->tcol->rmargin = p->maxrmargin > sz ?
- (p->maxrmargin + term_len(p, 1) - sz) / 2 : 0;
+ p->tcol->rmargin = p->maxrmargin > datelen ?
+ (p->maxrmargin + term_len(p, 1) - datelen) / 2 : 0;
p->trailspace = 1;
p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
term_word(p, meta->os);
term_flushln(p);
+ /* At the bottom in the middle: manual date. */
+
p->tcol->offset = p->tcol->rmargin;
- sz = term_strlen(p, meta->os);
- p->tcol->rmargin = p->maxrmargin > sz ? p->maxrmargin - sz : 0;
+ p->tcol->rmargin = p->maxrmargin > titlen ?
+ p->maxrmargin - titlen : 0;
p->flags |= TERMP_NOSPACE;
term_word(p, meta->date);
term_flushln(p);
+ /* Bottom right corner: manual title and section. */
+
p->tcol->offset = p->tcol->rmargin;
p->tcol->rmargin = p->maxrmargin;
p->trailspace = 0;
p->flags &= ~TERMP_NOBREAK;
p->flags |= TERMP_NOSPACE;
- term_word(p, meta->os);
+ term_word(p, title);
term_flushln(p);
p->tcol->offset = 0;
- p->tcol->rmargin = p->maxrmargin;
p->flags = 0;
+ free(title);
}
static void
print_mdoc_head(struct termp *p, const struct roff_meta *meta)
{
char *volume, *title;
- size_t vollen, titlen;
-
- /*
- * The header is strange. It has three components, which are
- * really two with the first duplicated. It goes like this:
- *
- * IDENTIFIER TITLE IDENTIFIER
- *
- * The IDENTIFIER is NAME(SECTION), which is the command-name
- * (if given, or "unknown" if not) followed by the manual page
- * section. These are given in `Dt'. The TITLE is a free-form
- * string depending on the manual volume. If not specified, it
- * switches on the manual section.
- */
+ size_t vollen, titlen; /* In basic units. */
assert(meta->vol);
if (NULL == meta->arch)
@@ -514,6 +506,8 @@ print_mdoc_head(struct termp *p, const struct roff_meta *meta)
meta->vol, meta->arch);
vollen = term_strlen(p, volume);
+ /* Top left corner: manual title and section. */
+
if (NULL == meta->msec)
title = mandoc_strdup(meta->title);
else
@@ -524,13 +518,16 @@ print_mdoc_head(struct termp *p, const struct roff_meta *meta)
p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
p->trailspace = 1;
p->tcol->offset = 0;
- p->tcol->rmargin = 2 * (titlen+1) + vollen < p->maxrmargin ?
+ p->tcol->rmargin =
+ titlen * 2 + term_len(p, 2) + vollen < p->maxrmargin ?
(p->maxrmargin - vollen + term_len(p, 1)) / 2 :
- vollen < p->maxrmargin ? p->maxrmargin - vollen : 0;
+ vollen < p->maxrmargin ? p->maxrmargin - vollen : 0;
term_word(p, title);
term_flushln(p);
+ /* At the top in the middle: manual volume. */
+
p->flags |= TERMP_NOSPACE;
p->tcol->offset = p->tcol->rmargin;
p->tcol->rmargin = p->tcol->offset + vollen + titlen <
@@ -539,6 +536,8 @@ print_mdoc_head(struct termp *p, const struct roff_meta *meta)
term_word(p, volume);
term_flushln(p);
+ /* Top right corner: title and section, again. */
+
p->flags &= ~TERMP_NOBREAK;
p->trailspace = 0;
if (p->tcol->rmargin + titlen <= p->maxrmargin) {
@@ -556,6 +555,11 @@ print_mdoc_head(struct termp *p, const struct roff_meta *meta)
free(volume);
}
+/*
+ * Interpret the string v as a scaled width or, if the syntax is invalid,
+ * measure how much width it takes up when printed. In both cases,
+ * return the width in basic units.
+ */
static int
a2width(const struct termp *p, const char *v)
{
@@ -564,10 +568,10 @@ a2width(const struct termp *p, const char *v)
end = a2roffsu(v, &su, SCALE_MAX);
if (end == NULL || *end != '\0') {
- su.unit = SCALE_EN;
- su.scale = term_strlen(p, v) / term_strlen(p, "0");
+ su.unit = SCALE_BU;
+ su.scale = term_strlen(p, v);
}
- return term_hen(p, &su);
+ return term_hspan(p, &su);
}
/*
@@ -623,8 +627,11 @@ termp_it_pre(DECL_ARGS)
struct roffsu su;
char buf[24];
const struct roff_node *bl, *nn;
- size_t ncols, dcol;
- int i, offset, width;
+ size_t ncols; /* Number of columns in .Bl -column. */
+ size_t dcol; /* Column spacing in basic units. */
+ int i; /* Zero-based column index. */
+ int offset; /* Start of column in basic units. */
+ int width; /* Column width in basic units. */
enum mdoc_list type;
if (n->type == ROFFT_BLOCK) {
@@ -701,10 +708,9 @@ termp_it_pre(DECL_ARGS)
for (i = 0, nn = n->prev;
nn->prev && i < (int)ncols;
nn = nn->prev, i++) {
- su.unit = SCALE_EN;
- su.scale = term_strlen(p, bl->norm->Bl.cols[i]) /
- term_strlen(p, "0");
- offset += term_hen(p, &su) + dcol;
+ su.unit = SCALE_BU;
+ su.scale = term_strlen(p, bl->norm->Bl.cols[i]);
+ offset += term_hspan(p, &su) + dcol;
}
/*
@@ -720,10 +726,9 @@ termp_it_pre(DECL_ARGS)
* Use the declared column widths, extended as explained
* in the preceding paragraph.
*/
- su.unit = SCALE_EN;
- su.scale = term_strlen(p, bl->norm->Bl.cols[i]) /
- term_strlen(p, "0");
- width = term_hen(p, &su) + dcol;
+ su.unit = SCALE_BU;
+ su.scale = term_strlen(p, bl->norm->Bl.cols[i]);
+ width = term_hspan(p, &su) + dcol;
break;
default:
if (NULL == bl->norm->Bl.width)
@@ -1274,6 +1279,7 @@ termp_sh_pre(DECL_ARGS)
term_vspace(p);
break;
case ROFFT_HEAD:
+ p->fontibi = 1;
return termp_bold_pre(p, pair, meta, n);
case ROFFT_BODY:
p->tcol->offset = term_len(p, p->defindent);
@@ -1294,6 +1300,7 @@ termp_sh_post(DECL_ARGS)
{
switch (n->type) {
case ROFFT_HEAD:
+ p->fontibi = 0;
term_newln(p);
break;
case ROFFT_BODY:
@@ -1421,7 +1428,7 @@ termp_fa_pre(DECL_ARGS)
static int
termp_bd_pre(DECL_ARGS)
{
- int offset;
+ int offset; /* In basic units. */
if (n->type == ROFFT_BLOCK) {
print_bvspace(p, n, n);
@@ -1509,7 +1516,8 @@ termp_ss_pre(DECL_ARGS)
term_vspace(p);
break;
case ROFFT_HEAD:
- p->tcol->offset = term_len(p, (p->defindent+1)/2);
+ p->tcol->offset = term_len(p, p->defindent) / 2 + 1;
+ p->fontibi = 1;
return termp_bold_pre(p, pair, meta, n);
case ROFFT_BODY:
p->tcol->offset = term_len(p, p->defindent);
@@ -1526,8 +1534,16 @@ termp_ss_pre(DECL_ARGS)
static void
termp_ss_post(DECL_ARGS)
{
- if (n->type == ROFFT_HEAD || n->type == ROFFT_BODY)
+ switch (n->type) {
+ case ROFFT_HEAD:
+ p->fontibi = 0;
+ /* FALLTHROUGH */
+ case ROFFT_BODY:
term_newln(p);
+ break;
+ default:
+ break;
+ }
}
static int
@@ -1888,9 +1904,7 @@ termp_lk_pre(DECL_ARGS)
}
/* Link target. */
- term_fontpush(p, TERMFONT_BOLD);
term_word(p, link->string);
- term_fontpop(p);
/* Trailing punctuation. */
while (punct != NULL) {
diff --git a/contrib/mandoc/mdoc_validate.c b/contrib/mandoc/mdoc_validate.c
index 4ca1253e4b70..ac265b88f484 100644
--- a/contrib/mandoc/mdoc_validate.c
+++ b/contrib/mandoc/mdoc_validate.c
@@ -1,4 +1,4 @@
-/* $Id: mdoc_validate.c,v 1.393 2025/06/05 12:38:26 schwarze Exp $ */
+/* $Id: mdoc_validate.c,v 1.396 2025/07/26 12:23:16 schwarze Exp $ */
/*
* Copyright (c) 2010-2022, 2025 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
@@ -1714,7 +1714,7 @@ post_xx(POST_ARGS)
os = "OpenBSD";
break;
case MDOC_Ux:
- os = "UNIX";
+ os = "Unix";
break;
default:
abort();
@@ -2777,7 +2777,7 @@ post_dd(POST_ARGS)
mandoc_msg(MANDOCERR_PROLOG_ORDER,
n->line, n->pos, "Dd after Os");
- if (mdoc->quick && n != NULL)
+ if (mdoc->quick)
mdoc->meta.date = mandoc_strdup("");
else
mdoc->meta.date = mandoc_normdate(n->child, n);
@@ -2842,8 +2842,7 @@ post_dt(POST_ARGS)
if (nn == NULL) {
mandoc_msg(MANDOCERR_MSEC_MISSING, n->line, n->pos,
"Dt %s", mdoc->meta.title);
- mdoc->meta.vol = mandoc_strdup("LOCAL");
- return; /* msec and arch remain NULL. */
+ return; /* msec, vol, and arch remain NULL. */
}
mdoc->meta.msec = mandoc_strdup(nn->string);
@@ -2854,7 +2853,6 @@ post_dt(POST_ARGS)
if (cp == NULL) {
mandoc_msg(MANDOCERR_MSEC_BAD,
nn->line, nn->pos, "Dt ... %s", nn->string);
- mdoc->meta.vol = mandoc_strdup(nn->string);
} else {
mdoc->meta.vol = mandoc_strdup(cp);
if (mdoc->filesec != '\0' &&
diff --git a/contrib/mandoc/out.c b/contrib/mandoc/out.c
index f6f5859a1629..21c282b2141b 100644
--- a/contrib/mandoc/out.c
+++ b/contrib/mandoc/out.c
@@ -1,8 +1,8 @@
-/* $Id: out.c,v 1.86 2025/01/05 18:14:39 schwarze Exp $ */
+/* $Id: out.c,v 1.87 2025/07/16 14:33:08 schwarze Exp $ */
/*
- * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
- * Copyright (c) 2011, 2014, 2015, 2017, 2018, 2019, 2021
+ * Copyright (c) 2011, 2014, 2015, 2017, 2018, 2019, 2021, 2025
* Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -122,9 +122,23 @@ tblcalc(struct rofftbl *tbl, const struct tbl_span *sp_first,
const struct tbl_dat *dp;
struct roffcol *col;
struct tbl_colgroup *first_group, **gp, *g;
- size_t *colwidth;
- size_t ewidth, min1, min2, wanted, width, xwidth;
- int done, icol, maxcol, necol, nxcol, quirkcol;
+
+ /* Widths in basic units. */
+ size_t *colwidth; /* Widths of all columns. */
+ size_t min1; /* Width of the narrowest column. */
+ size_t min2; /* Width of the second narrowest column. */
+ size_t wanted; /* For any of the narrowest columns. */
+ size_t xwidth; /* Total width of columns not to expand. */
+ size_t ewidth; /* Width of widest column to equalize. */
+ size_t width; /* Width of the data in basic units. */
+ size_t enw; /* Width of one EN unit. */
+
+ int icol; /* Column number, starting at zero. */
+ int maxcol; /* Number of last column. */
+ int necol; /* Number of columns to equalize. */
+ int nxcol; /* Number of columns to expand. */
+ int done; /* Boolean: this group is wide enough. */
+ int quirkcol;
/*
* Allocate the master column specifiers. These will hold the
@@ -139,6 +153,7 @@ tblcalc(struct rofftbl *tbl, const struct tbl_span *sp_first,
maxcol = -1;
first_group = NULL;
+ enw = (*tbl->len)(1, tbl->arg);
for (sp = sp_first; sp != NULL; sp = sp->next) {
if (sp->pos != TBL_SPAN_DATA)
continue;
@@ -175,8 +190,8 @@ tblcalc(struct rofftbl *tbl, const struct tbl_span *sp_first,
opts, dp,
dp->block == 0 ? 0 :
dp->layout->width ? dp->layout->width :
- rmargin ? (rmargin + sp->opts->cols / 2)
- / (sp->opts->cols + 1) : 0);
+ rmargin ? (rmargin / enw + sp->opts->cols / 2) /
+ (sp->opts->cols + 1) * enw : 0);
if (dp->hspans == 0)
continue;
@@ -211,8 +226,8 @@ tblcalc(struct rofftbl *tbl, const struct tbl_span *sp_first,
maxcol = sp_first->opts->cols - 1;
for (icol = 0; icol <= maxcol; icol++) {
col = tbl->cols + icol;
- if (col->width < 1)
- col->width = 1;
+ if (col->width < enw)
+ col->width = enw;
/*
* Column spacings are needed for span width
@@ -234,7 +249,8 @@ tblcalc(struct rofftbl *tbl, const struct tbl_span *sp_first,
for (icol = g->startcol; icol <= g->endcol; icol++) {
width = tbl->cols[icol].width;
if (icol < g->endcol)
- width += tbl->cols[icol].spacing;
+ width += (*tbl->len)(tbl->cols[icol].spacing,
+ tbl->arg);
if (g->wanted <= width) {
done = 1;
break;
@@ -372,9 +388,9 @@ tblcalc(struct rofftbl *tbl, const struct tbl_span *sp_first,
*/
if (nxcol && rmargin) {
- xwidth += 3*maxcol +
+ xwidth += (*tbl->len)(3 * maxcol +
(opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX) ?
- 2 : !!opts->lvert + !!opts->rvert);
+ 2 : !!opts->lvert + !!opts->rvert), tbl->arg);
if (rmargin <= offset + xwidth)
return;
xwidth = rmargin - offset - xwidth;
@@ -387,7 +403,7 @@ tblcalc(struct rofftbl *tbl, const struct tbl_span *sp_first,
*/
if (nxcol == 5) {
- quirkcol = xwidth % nxcol + 2;
+ quirkcol = xwidth / enw % nxcol + 2;
if (quirkcol != 3 && quirkcol != 4)
quirkcol = -1;
} else
@@ -402,7 +418,7 @@ tblcalc(struct rofftbl *tbl, const struct tbl_span *sp_first,
col->width = (double)xwidth * ++necol / nxcol
- ewidth + 0.4995;
if (necol == quirkcol)
- col->width--;
+ col->width -= enw;
ewidth += col->width;
}
}
@@ -444,9 +460,12 @@ tblcalc_literal(struct rofftbl *tbl, struct roffcol *col,
const char *str; /* Beginning of the first line. */
const char *beg; /* Beginning of the current line. */
char *end; /* End of the current line. */
- size_t lsz; /* Length of the current line. */
- size_t wsz; /* Length of the current word. */
- size_t msz; /* Length of the longest line. */
+
+ /* Widths in basic units. */
+ size_t lsz; /* Of the current line. */
+ size_t wsz; /* Of the current word. */
+ size_t msz; /* Of the longest line. */
+ size_t enw; /* Of one EN unit. */
if (dp->string == NULL || *dp->string == '\0')
return 0;
@@ -460,8 +479,9 @@ tblcalc_literal(struct rofftbl *tbl, struct roffcol *col,
end++;
}
wsz = (*tbl->slen)(beg, tbl->arg);
- if (mw && lsz && lsz + 1 + wsz <= mw)
- lsz += 1 + wsz;
+ enw = (*tbl->len)(1, tbl->arg);
+ if (mw && lsz && lsz + enw + wsz <= mw)
+ lsz += enw + wsz;
else
lsz = wsz;
if (msz < lsz)
@@ -479,7 +499,8 @@ tblcalc_number(struct rofftbl *tbl, struct roffcol *col,
const struct tbl_opts *opts, const struct tbl_dat *dp)
{
const char *cp, *lastdigit, *lastpoint;
- size_t intsz, totsz;
+ size_t totsz; /* Total width of the number in basic units. */
+ size_t intsz; /* Width of the integer part in basic units. */
char buf[2];
if (dp->string == NULL || *dp->string == '\0')
diff --git a/contrib/mandoc/out.h b/contrib/mandoc/out.h
index f746e4486958..a3b49b70460d 100644
--- a/contrib/mandoc/out.h
+++ b/contrib/mandoc/out.h
@@ -1,7 +1,7 @@
-/* $Id: out.h,v 1.35 2022/09/11 09:13:48 schwarze Exp $ */
+/* $Id: out.h,v 1.36 2025/07/16 14:33:08 schwarze Exp $ */
/*
+ * Copyright (c) 2011,2014,2017,2018,2025 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
- * Copyright (c) 2014, 2017, 2018 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -33,11 +33,11 @@ enum roffscale {
};
struct roffcol {
- size_t width; /* width of cell */
- size_t nwidth; /* max. width of number in cell */
- size_t decimal; /* decimal position in cell */
- size_t spacing; /* spacing after the column */
- int flags; /* layout flags, see tbl_cell */
+ size_t width; /* Width of cell [BU]. */
+ size_t nwidth; /* Maximum width of number [BU]. */
+ size_t decimal; /* Decimal position [BU]. */
+ size_t spacing; /* Spacing after the column [EN]. */
+ int flags; /* Layout flags, see tbl_cell. */
};
struct roffsu {
@@ -45,16 +45,14 @@ struct roffsu {
double scale;
};
-typedef size_t (*tbl_sulen)(const struct roffsu *, void *);
typedef size_t (*tbl_strlen)(const char *, void *);
typedef size_t (*tbl_len)(size_t, void *);
struct rofftbl {
- tbl_sulen sulen; /* calculate scaling unit length */
- tbl_strlen slen; /* calculate string length */
- tbl_len len; /* produce width of empty space */
- struct roffcol *cols; /* master column specifiers */
- void *arg; /* passed to sulen, slen, and len */
+ tbl_strlen slen; /* Calculate string length [BU]. */
+ tbl_len len; /* Produce width of empty space [BU]. */
+ struct roffcol *cols; /* Master column specifiers. */
+ void *arg; /* Passed to slen() and len(). */
};
diff --git a/contrib/mandoc/roff.7 b/contrib/mandoc/roff.7
index 27f83853e75b..adb5852e069b 100644
--- a/contrib/mandoc/roff.7
+++ b/contrib/mandoc/roff.7
@@ -1,6 +1,6 @@
-.\" $Id: roff.7,v 1.121 2023/10/23 20:25:02 schwarze Exp $
+.\" $Id: roff.7,v 1.123 2025/08/04 23:12:08 schwarze Exp $
.\"
-.\" Copyright (c) 2010-2019, 2022-2023 Ingo Schwarze <schwarze@openbsd.org>
+.\" Copyright (c) 2010-2019,2022-2023,2025 Ingo Schwarze <schwarze@openbsd.org>
.\" Copyright (c) 2010, 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
.\"
.\" Permission to use, copy, modify, and distribute this software for any
@@ -15,7 +15,7 @@
.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
-.Dd $Mdocdate: October 23 2023 $
+.Dd $Mdocdate: August 4 2025 $
.Dt ROFF 7
.Os
.Sh NAME
@@ -79,8 +79,10 @@ They provide free-form text to be printed; the formatting of the text
depends on the respective processing context.
.Sh LANGUAGE SYNTAX
.Nm
-documents may contain only graphable 7-bit ASCII characters, the space
-character, and, in certain circumstances, the tab character.
+documents are text files containing only printable
+.Xr ascii 7
+characters, the space character,
+and, in certain circumstances, the tab character.
The backslash character
.Sq \e
indicates the start of an escape sequence, used for example for
@@ -180,7 +182,7 @@ Italic font.
Return to the previous font.
If a macro caused a font change since the last
.Ic \ef
-eascape sequence or
+escape sequence or
.Ic \&ft
request, this returns to the font before the last font change in
the macro rather than to the font before the last manual font change.
@@ -267,7 +269,7 @@ width of rendered
.Pq en
character
.It u
-default horizontal span for the terminal
+device-dependent basic units
.It M
mini-em (1/100 em)
.El
@@ -289,7 +291,7 @@ for vertical spaces and
for horizontal ones.
.Pp
Examples:
-.Bl -tag -width ".Bl -tag -width 2i" -offset indent -compact
+.Bl -tag -width "xBl -tag -width 2i" -offset indent -compact
.It Li \&.Bl -tag -width 2i
two-inch tagged list indentation in
.Xr mdoc 7
@@ -1319,10 +1321,16 @@ among others because it overrides the
.Xr mandoc 1
.Fl O Cm width
command line option.
-.It Ic \&lnr Ar register Oo Cm + Ns | Ns Cm - Oc Ns Ar value Op Ar increment
+.It Ic \&lnr Ar registername Xo
+.Oo Cm + Ns | Ns Cm \- Oc Ns Ar value
+.Op Ar increment
+.Xc
Set local number register.
This is a Heirloom extension and currently unsupported.
-.It Ic \&lnrf Ar register Oo Cm + Ns | Ns Cm - Oc Ns Ar value Op Ar increment
+.It Ic \&lnrf Ar registername Xo
+.Oo Cm + Ns | Ns Cm \- Oc Ns Ar value
+.Op Ar increment
+.Xc
Set local floating-point register.
This is a Heirloom extension and currently unsupported.
.It Ic \&lpfx Ar string
@@ -1398,10 +1406,13 @@ skipping the
.Ic \&nop
request and any space characters immediately following it.
This is mostly used to indent text lines inside macro definitions.
-.It Ic \&nr Ar register Oo Cm + Ns | Ns Cm - Oc Ns Ar expression Op Ar stepsize
-Define or change a register.
-A register is an arbitrary string value that defines some sort of state,
-which influences parsing and/or formatting.
+.It Ic \&nr Ar registername Xo
+.Oo Cm + Ns | Ns Cm \- Oc Ns Ar expression
+.Op Ar stepsize
+.Xc
+Define or change the number register with the given
+.Ar registername .
+A register can store an integer number.
For the syntax of
.Ar expression ,
see
@@ -1410,6 +1421,9 @@ below.
If it is prefixed by a sign, the register will be
incremented or decremented instead of assigned to.
.Pp
+Once set, the value of a number register can be interpolated using the
+.Ic \en
+escape sequence.
The
.Ar stepsize
is used by the
@@ -1418,29 +1432,13 @@ auto-increment feature.
It remains unchanged when omitted while changing an existing register,
and it defaults to 0 when defining a new register.
.Pp
-The following
-.Ar register
-is handled specially:
-.Bl -tag -width Ds
-.It Cm nS
-If set to a positive integer value, certain
-.Xr mdoc 7
-macros will behave in the same way as in the
-.Em SYNOPSIS
-section.
-If set to 0, these macros will behave in the same way as outside the
-.Em SYNOPSIS
-section, even when called within the
-.Em SYNOPSIS
-section itself.
-Note that starting a new
-.Xr mdoc 7
-section with the
-.Ic \&Sh
-macro will reset this register.
-.El
+Some number registers can be read to inspect parser state,
+and some can be changed to influence formatting.
+For details about individual registers, see the
+.Sx NUMBER REGISTER REFERENCE
+below.
.It Xo
-.Ic \&nrf Ar register Oo Cm + Ns | Ns Cm - Oc Ns Ar expression
+.Ic \&nrf Ar registername Oo Cm + Ns | Ns Cm \- Oc Ns Ar expression
.Op Ar increment
.Xc
Define or change a floating-point register.
@@ -1569,7 +1567,7 @@ requests is not supported, and diversions are not implemented at all.
Rename a number register.
Currently unsupported.
.It Ic \&rr Ar register
-Remove a register.
+Remove a number register.
.It Ic \&rs
End no-space mode.
Currently ignored.
@@ -2152,6 +2150,8 @@ on the current font.
.It Ic \en Ns Oo +|- Oc Ns Ic \&[ Ns Ar name Ns Ic \&]
Interpolate the number register
.Ar name .
+If the register is not yet defined,
+it is automatically initialised to zero before interpolation.
For short names, there are variants
.Ic \en Ns Ar c
and
@@ -2162,6 +2162,9 @@ the register is first incremented or decremented by the
that was specified in the relevant
.Ic \&nr
request, and the changed value is interpolated.
+For the names of predefined registers, see the
+.Sx NUMBER REGISTER REFERENCE
+below.
.It Ic \eO Ns Ar digit , Ic \eO[5 Ns arguments Ns Ic \&]
Suppress output.
This is a groff extension and currently unsupported.
@@ -2250,6 +2253,83 @@ with zero width and height; ignored by
.It Ic \ez
Output the next character without advancing the cursor position.
.El
+.Sh NUMBER REGISTER REFERENCE
+In
+.Xr mdoc 7
+and
+.Xr man 7
+documents, using registers is discouraged.
+For compatibility with legacy documents, the
+.Xr mandoc 1
+.Nm
+parser recognises the following names of read-only registers:
+.Bl -tag -width Ds
+.It Cm .$
+The number of arguments of the innermost user-defined macro
+currently being called, or 0 by default.
+The
+.Ic shift
+request decrements the value of this register.
+.It Cm .A
+Whether ASCII approximation mode is on;
+.Xr mandoc 1
+always returns 0, meaning off.
+.It Cm .g
+Whether the formatter claims groff compatibility;
+.Xr mandoc 1
+always returns 1, meaning yes.
+.It Cm .H
+The minimum horizontal movement in basic units;
+.Xr mandoc 1
+always returns 24, corresponding to one character position.
+.It Cm .j
+The current line adjustment mode;
+.Xr mandoc 1
+always returns 0, meaning flush left.
+.It Cm .l
+The line length in basic units;
+.Xr mandoc 1
+always returns 78 * 24, corresponding to 78 characters per line.
+.It Cm \&.T
+Whether an output device has been selected;
+.Xr mandoc 1
+always returns 1, meaning yes.
+.It Cm .V
+The minimum vertical movement in basic units;
+.Xr mandoc 1
+always returns 40, corresponding to one line height.
+.El
+.Pp
+The
+.Cm nS
+register is handled specially.
+If set to a positive integer value, certain
+.Xr mdoc 7
+macros behave in the same way as in the
+.Em SYNOPSIS
+section.
+If set to 0, these macros behave in the same way as outside the
+.Em SYNOPSIS
+section, even when called within the
+.Em SYNOPSIS
+section itself.
+Starting a new
+.Xr mdoc 7
+section with the
+.Ic \&Sh
+macro resets this register.
+.Pp
+Full
+.Nm
+implementations support large numbers of additional predefined registers.
+While the
+.Ic \&nr
+request supports setting and the
+.Ic \en
+escape sequence supports inspecting arbitrary registers,
+.Xr mandoc 1
+only defines the few registers listed above by default.
+All other registers are undefined by default and yield 0 when interpolated.
.Sh COMPATIBILITY
The
.Xr mandoc 1
@@ -2266,17 +2346,29 @@ never reads or writes external files except via
.Ic \&so
requests with safe relative paths.
.It
-There is no automatic hyphenation, no adjustment to the right margin,
-and very limited support for centering; the output is always set flush-left.
-.It
-Support for setting tabulator and leader characters is missing,
-and support for manually changing indentation is limited.
+There is no automatic hyphenation and no support for the
+.Ic \&ad
+line adjustment request.
+Except when the
+.Ic \&ce
+or
+.Ic \&rj
+requests or the
+.Xr tbl 7
+cell specifications
+.Cm c ,
+.Cm n ,
+or
+.Cm r
+or the table option
+.Cm center
+are used, output is always set flush-left.
.It
-The
-.Sq u
-scaling unit is the default terminal unit.
-In traditional troff systems, this unit changes depending on the
-output media.
+Support for setting tabulator and leader characters is missing, and the
+.Ic \&in
+indentation request is not supported in
+.Xr mdoc 7
+input files.
.It
Width measurements are implemented in a crude way
and often yield wrong results.
@@ -2336,6 +2428,15 @@ implementations.
.%D September 17, 2007
.%U http://heirloom.sourceforge.net/doctools/troff.pdf
.Re
+.Rs
+.%A James Clark
+.%A Werner Lemberg
+.%A G. Branden Robinson
+.%I Free Software Foundation, Inc.
+.%T The GNU Troff Manual
+.%D 1999\(en2023
+.%U https://www.gnu.org/software/groff/manual/
+.Re
.Sh HISTORY
The RUNOFF typesetting system, whose input forms the basis for
.Nm ,
diff --git a/contrib/mandoc/roff_term.c b/contrib/mandoc/roff_term.c
index f696898ebd5a..38321c830013 100644
--- a/contrib/mandoc/roff_term.c
+++ b/contrib/mandoc/roff_term.c
@@ -1,6 +1,7 @@
-/* $Id: roff_term.c,v 1.25 2023/04/28 19:11:04 schwarze Exp $ */
+/* $Id: roff_term.c,v 1.27 2025/08/21 15:38:51 schwarze Exp $ */
/*
- * Copyright (c) 2010,2014,2015,2017-2020 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2010, 2014, 2015, 2017-2021, 2025
+ * Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -159,8 +160,14 @@ static void
roff_term_pre_po(ROFF_TERM_ARGS)
{
struct roffsu su;
- static int po, pouse, polast;
- int ponew;
+
+ /* Page offsets in basic units. */
+ static int polast; /* Previously requested. */
+ static int po; /* Currently requested. */
+ static int pouse; /* Currently used. */
+ int pomin; /* Minimum to be used. */
+ int pomax; /* Maximum to be used. */
+ int ponew; /* Newly requested. */
/* Revert the currently active page offset. */
p->tcol->offset -= pouse;
@@ -168,7 +175,7 @@ roff_term_pre_po(ROFF_TERM_ARGS)
/* Determine the requested page offset. */
if (n->child != NULL &&
a2roffsu(n->child->string, &su, SCALE_EM) != NULL) {
- ponew = term_hen(p, &su);
+ ponew = term_hspan(p, &su);
if (*n->child->string == '+' ||
*n->child->string == '-')
ponew += po;
@@ -180,8 +187,9 @@ roff_term_pre_po(ROFF_TERM_ARGS)
po = ponew;
/* Truncate to the range [-offset, 60], remember, and apply it. */
- pouse = po >= 60 ? 60 :
- po < -(int)p->tcol->offset ? -(int)p->tcol->offset : po;
+ pomin = -p->tcol->offset;
+ pomax = term_len(p, 60);
+ pouse = po > pomax ? pomax : po < pomin ? pomin : po;
p->tcol->offset += pouse;
}
@@ -219,9 +227,10 @@ static void
roff_term_pre_ti(ROFF_TERM_ARGS)
{
struct roffsu su;
- const char *cp;
- const size_t maxoff = 72;
- int len, sign;
+ const char *cp; /* Request argument. */
+ size_t maxoff; /* Maximum indentation in basic units. */
+ int len; /* Request argument in basic units. */
+ int sign;
roff_term_pre_br(p, n);
@@ -239,7 +248,8 @@ roff_term_pre_ti(ROFF_TERM_ARGS)
if (a2roffsu(cp, &su, SCALE_EM) == NULL)
return;
- len = term_hen(p, &su);
+ len = term_hspan(p, &su);
+ maxoff = term_len(p, 72);
switch (sign) {
case 1:
diff --git a/contrib/mandoc/tbl.h b/contrib/mandoc/tbl.h
index 5e98735d6f97..c7566c110f42 100644
--- a/contrib/mandoc/tbl.h
+++ b/contrib/mandoc/tbl.h
@@ -1,7 +1,7 @@
-/* $Id: tbl.h,v 1.3 2025/01/05 18:14:39 schwarze Exp $ */
+/* $Id: tbl.h,v 1.4 2025/07/16 14:33:08 schwarze Exp $ */
/*
+ * Copyright (c) 2014-2018, 2021, 2025 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
- * Copyright (c) 2014,2015,2017,2018,2021 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -27,8 +27,8 @@ struct tbl_opts {
#define TBL_OPT_NOSPACE (1 << 6) /* Option "nospaces". */
#define TBL_OPT_NOWARN (1 << 7) /* Option "nowarn". */
int cols; /* Number of columns. */
- int lvert; /* Width of left vertical line. */
- int rvert; /* Width of right vertical line. */
+ int lvert; /* Width of left vertical line in EN. */
+ int rvert; /* Width of right vertical line in EN. */
char tab; /* Option "tab": cell separator. */
char decimal; /* Option "decimalpoint". */
};
@@ -51,9 +51,9 @@ enum tbl_cellt {
*/
struct tbl_cell {
struct tbl_cell *next; /* Layout cell to the right. */
- size_t width; /* Minimum column width. */
- size_t spacing; /* To the right of the column. */
- int vert; /* Width of subsequent vertical line. */
+ size_t width; /* Minimum column width in basic units. */
+ size_t spacing; /* To the right of the column in EN. */
+ int vert; /* Width of subseq. vertical line in EN. */
int col; /* Column number, starting from 0. */
int flags;
#define TBL_CELL_TALIGN (1 << 2) /* t, T */
@@ -73,7 +73,7 @@ struct tbl_row {
struct tbl_row *next; /* Layout row below. */
struct tbl_cell *first; /* Leftmost layout cell. */
struct tbl_cell *last; /* Rightmost layout cell. */
- int vert; /* Width of left vertical line. */
+ int vert; /* Width of left vertical line in EN. */
};
enum tbl_datt {
diff --git a/contrib/mandoc/tbl_html.c b/contrib/mandoc/tbl_html.c
index 57d90c4c2d67..56ea3c08eef4 100644
--- a/contrib/mandoc/tbl_html.c
+++ b/contrib/mandoc/tbl_html.c
@@ -1,4 +1,4 @@
-/* $Id: tbl_html.c,v 1.41 2022/04/23 14:02:17 schwarze Exp $ */
+/* $Id: tbl_html.c,v 1.42 2025/07/16 14:33:08 schwarze Exp $ */
/*
* Copyright (c) 2014, 2015, 2017, 2018, 2021, 2022
* Ingo Schwarze <schwarze@openbsd.org>
@@ -37,7 +37,6 @@
static void html_tblopen(struct html *, const struct tbl_span *);
static size_t html_tbl_len(size_t, void *);
static size_t html_tbl_strlen(const char *, void *);
-static size_t html_tbl_sulen(const struct roffsu *, void *);
static size_t
@@ -52,36 +51,6 @@ html_tbl_strlen(const char *p, void *arg)
return strlen(p);
}
-static size_t
-html_tbl_sulen(const struct roffsu *su, void *arg)
-{
- if (su->scale < 0.0)
- return 0;
-
- switch (su->unit) {
- case SCALE_FS: /* 2^16 basic units */
- return su->scale * 65536.0 / 24.0;
- case SCALE_IN: /* 10 characters per inch */
- return su->scale * 10.0;
- case SCALE_CM: /* 2.54 cm per inch */
- return su->scale * 10.0 / 2.54;
- case SCALE_PC: /* 6 pica per inch */
- case SCALE_VS:
- return su->scale * 10.0 / 6.0;
- case SCALE_EN:
- case SCALE_EM:
- return su->scale;
- case SCALE_PT: /* 12 points per pica */
- return su->scale * 10.0 / 6.0 / 12.0;
- case SCALE_BU: /* 24 basic units per character */
- return su->scale / 24.0;
- case SCALE_MM: /* 1/1000 inch */
- return su->scale / 100.0;
- default:
- abort();
- }
-}
-
static void
html_tblopen(struct html *h, const struct tbl_span *sp)
{
@@ -89,7 +58,6 @@ html_tblopen(struct html *h, const struct tbl_span *sp)
if (h->tbl.cols == NULL) {
h->tbl.len = html_tbl_len;
h->tbl.slen = html_tbl_strlen;
- h->tbl.sulen = html_tbl_sulen;
tblcalc(&h->tbl, sp, 0, 0);
}
assert(NULL == h->tblt);
diff --git a/contrib/mandoc/tbl_layout.c b/contrib/mandoc/tbl_layout.c
index 3b7e64580fd5..aa054dee9411 100644
--- a/contrib/mandoc/tbl_layout.c
+++ b/contrib/mandoc/tbl_layout.c
@@ -1,4 +1,4 @@
-/* $Id: tbl_layout.c,v 1.51 2025/01/05 18:14:39 schwarze Exp $ */
+/* $Id: tbl_layout.c,v 1.52 2025/07/16 14:33:08 schwarze Exp $ */
/*
* Copyright (c) 2012, 2014, 2015, 2017, 2020, 2021, 2025
* Ingo Schwarze <schwarze@openbsd.org>
@@ -66,8 +66,8 @@ mods(struct tbl_node *tbl, struct tbl_cell *cp,
int ln, const char *p, int *pos)
{
char *endptr;
- unsigned long spacing;
- int isz;
+ unsigned long spacing; /* Column spacing in EN units. */
+ int isz; /* Width in basic units. */
enum mandoc_esc fontesc;
mod:
@@ -145,8 +145,7 @@ mod:
mandoc_msg(MANDOCERR_TBLLAYOUT_WIDTH,
ln, *pos, "%s", p + *pos);
else {
- /* Convert from BU to EN and round. */
- cp->width = (isz + 11) /24;
+ cp->width = isz;
(*pos)++;
}
} else {
@@ -155,6 +154,7 @@ mod:
cp->width *= 10;
cp->width += p[(*pos)++] - '0';
}
+ cp->width *= 24;
if (cp->width == 0)
mandoc_msg(MANDOCERR_TBLLAYOUT_WIDTH,
ln, *pos, "%s", p + *pos);
diff --git a/contrib/mandoc/tbl_term.c b/contrib/mandoc/tbl_term.c
index e92349514d9f..a7f057084266 100644
--- a/contrib/mandoc/tbl_term.c
+++ b/contrib/mandoc/tbl_term.c
@@ -1,6 +1,6 @@
-/* $Id: tbl_term.c,v 1.79 2022/08/28 10:58:31 schwarze Exp $ */
+/* $Id: tbl_term.c,v 1.81 2025/07/24 17:54:48 schwarze Exp $ */
/*
- * Copyright (c) 2011-2022 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2011-2022, 2025 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2009, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
*
* Permission to use, copy, modify, and distribute this software for any
@@ -39,23 +39,23 @@
static size_t term_tbl_len(size_t, void *);
static size_t term_tbl_strlen(const char *, void *);
-static size_t term_tbl_sulen(const struct roffsu *, void *);
static void tbl_data(struct termp *, const struct tbl_opts *,
const struct tbl_cell *,
const struct tbl_dat *,
- const struct roffcol *);
+ const struct roffcol *, size_t *);
static void tbl_direct_border(struct termp *, int, size_t);
-static void tbl_fill_border(struct termp *, int, size_t);
-static void tbl_fill_char(struct termp *, char, size_t);
-static void tbl_fill_string(struct termp *, const char *, size_t);
+static void tbl_fill_border(struct termp *, int, size_t *, size_t);
+static void tbl_fill_char(struct termp *, char, size_t *, size_t);
+static void tbl_fill_string(struct termp *, const char *,
+ size_t *, size_t);
static void tbl_hrule(struct termp *, const struct tbl_span *,
const struct tbl_span *, const struct tbl_span *,
int);
static void tbl_literal(struct termp *, const struct tbl_dat *,
- const struct roffcol *);
+ const struct roffcol *, size_t *);
static void tbl_number(struct termp *, const struct tbl_opts *,
const struct tbl_dat *,
- const struct roffcol *);
+ const struct roffcol *, size_t *);
static void tbl_word(struct termp *, const struct tbl_dat *);
@@ -140,15 +140,6 @@ static const int *borders_locale;
static size_t
-term_tbl_sulen(const struct roffsu *su, void *arg)
-{
- int i;
-
- i = term_hen((const struct termp *)arg, su);
- return i > 0 ? i : 0;
-}
-
-static size_t
term_tbl_strlen(const char *p, void *arg)
{
return term_strlen((const struct termp *)arg, p);
@@ -166,16 +157,29 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
{
const struct tbl_cell *cp, *cpn, *cpp, *cps;
const struct tbl_dat *dp;
- static size_t offset;
- size_t save_offset;
- size_t coloff, tsz;
- int hspans, ic, more;
- int dvert, fc, horiz, lhori, rhori, uvert;
+
+ /* Positions and widths in basic units. */
+ static size_t offset; /* Of the table as a whole. */
+ size_t save_offset; /* Of the surrounding text. */
+ size_t coloff; /* Of this cell. */
+ size_t tsz; /* Total width of the table. */
+ size_t enw; /* Width of one EN unit. */
+
+ int ic; /* Column number. */
+ int hspans; /* Number of spans following this cell. */
+ int horiz; /* Boolean: this row only contains a line. */
+ int lhori; /* Number of horizontal lines pointing left. */
+ int rhori; /* Number of horizontal lines pointing right. */
+ int dvert; /* Number of vertical lines pointing down. */
+ int uvert; /* Number of vertical lines pointing up. */
+ int fc; /* Frame character index in borders_locale[]. */
+ int more; /* Boolean: there are more columns to print. */
/* Inhibit printing of spaces: we do padding ourselves. */
tp->flags |= TERMP_NOSPACE | TERMP_NONOSPACE;
save_offset = tp->tcol->offset;
+ enw = term_len(tp, 1);
/*
* The first time we're invoked for a given table block,
@@ -188,7 +192,6 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
tp->tbl.len = term_tbl_len;
tp->tbl.slen = term_tbl_strlen;
- tp->tbl.sulen = term_tbl_sulen;
tp->tbl.arg = tp;
tblcalc(&tp->tbl, sp, tp->tcol->offset, tp->tcol->rmargin);
@@ -197,17 +200,36 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
offset = tp->tcol->offset;
if (sp->opts->opts & TBL_OPT_CENTRE) {
- tsz = sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX)
- ? 2 : !!sp->opts->lvert + !!sp->opts->rvert;
+
+ /*
+ * Vertical lines on the edges of the table make the
+ * table wider; take that into account for centering.
+ * The following assignment essentially says that a
+ * line on the right side occupies two columns (which
+ * matches reality) and a line on the left side three
+ * columns (which does not match reality; in fact,
+ * it only occupies two columns). But this is how
+ * groff does centering, so for compatibility, use
+ * the same numbers as groff.
+ */
+
+ tsz = term_len(tp,
+ sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX) ?
+ 5 : 3 * !!sp->opts->lvert + 2 * !!sp->opts->rvert);
+
+ /* Column widths and column spacing. */
+
for (ic = 0; ic + 1 < sp->opts->cols; ic++)
tsz += tp->tbl.cols[ic].width +
- tp->tbl.cols[ic].spacing;
+ term_len(tp, tp->tbl.cols[ic].spacing);
if (sp->opts->cols)
tsz += tp->tbl.cols[sp->opts->cols - 1].width;
+
if (offset + tsz > tp->tcol->rmargin)
- tsz -= 1;
+ tsz -= enw;
offset = offset + tp->tcol->rmargin > tsz ?
- (offset + tp->tcol->rmargin - tsz) / 2 : 0;
+ ((offset + tp->tcol->rmargin - tsz) / enw / 2) *
+ enw : 0;
tp->tcol->offset = offset;
}
@@ -239,7 +261,7 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
if (sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX) ||
sp->opts->lvert)
- coloff++;
+ coloff += enw * 2;
tp->tcol->rmargin = coloff;
/* Set up the data columns. */
@@ -254,7 +276,8 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
coloff += tp->tbl.cols[ic].width;
tp->tcol->rmargin = coloff;
if (ic + 1 < sp->opts->cols)
- coloff += tp->tbl.cols[ic].spacing;
+ coloff += term_len(tp,
+ tp->tbl.cols[ic].spacing);
if (hspans) {
hspans--;
continue;
@@ -269,7 +292,7 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
/* Set up a column for a right vertical frame. */
tp->tcol++;
- tp->tcol->offset = coloff + 1;
+ tp->tcol->offset = coloff + enw;
tp->tcol->rmargin = tp->maxrmargin;
/* Spans may have reduced the number of columns. */
@@ -279,6 +302,7 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
/* Fill the buffers for all data columns. */
tp->tcol = tp->tcols;
+ coloff = tp->tcols[1].offset;
cp = cpn = sp->layout->first;
dp = sp->first;
hspans = 0;
@@ -294,7 +318,9 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
tp->tcol++;
tp->col = 0;
tp->flags &= ~(TERMP_BACKAFTER | TERMP_BACKBEFORE);
- tbl_data(tp, sp->opts, cp, dp, tp->tbl.cols + ic);
+ tbl_data(tp, sp->opts, cp, dp, tp->tbl.cols + ic,
+ &coloff);
+ coloff += term_len(tp, tp->tbl.cols[ic].spacing);
if (dp != NULL &&
(ic || sp->layout->first->pos != TBL_CELL_SPAN)) {
hspans = dp->hspans;
@@ -328,8 +354,8 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
fc = BUP * uvert + BDOWN * dvert + BRIGHT * rhori;
if (uvert > 0 || dvert > 0 || (horiz && sp->opts->lvert)) {
(*tp->advance)(tp, tp->tcols->offset);
- tp->viscol = tp->tcol->offset;
- tbl_direct_border(tp, fc, 1);
+ tbl_direct_border(tp, fc, enw);
+ tbl_direct_border(tp, BHORIZ * rhori, enw);
}
/* Print the data cells. */
@@ -348,6 +374,7 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
for (ic = 0; ic < sp->opts->cols; ic++) {
/*
+ * Handle horizontal alignment.
* Figure out whether to print a
* vertical line after this cell
* and advance to next layout cell.
@@ -362,6 +389,16 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
if (sp->pos == TBL_SPAN_DATA)
uvert = dvert = cps->vert;
switch (cp->pos) {
+ case TBL_CELL_CENTRE:
+ tp->flags |= TERMP_CENTER;
+ break;
+ case TBL_CELL_RIGHT:
+ tp->flags |= TERMP_RIGHT;
+ break;
+ case TBL_CELL_LONG:
+ if (hspans == 0)
+ tp->tcol->offset += enw;
+ break;
case TBL_CELL_HORIZ:
fc = BHORIZ;
break;
@@ -432,6 +469,9 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
tp->tcol++;
if (tp->tcol->col < tp->tcol->lastcol)
term_flushln(tp);
+ tp->flags &= ~(TERMP_CENTER | TERMP_RIGHT);
+ if (cp != NULL && cp->pos == TBL_CELL_LONG)
+ tp->tcol->offset -= enw;
if (tp->tcol->col < tp->tcol->lastcol)
more = 1;
@@ -451,15 +491,13 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
continue;
}
- if (tp->viscol < tp->tcol->rmargin) {
- (*tp->advance)(tp, tp->tcol->rmargin
- - tp->viscol);
- tp->viscol = tp->tcol->rmargin;
- }
+ if (tp->viscol < tp->tcol->rmargin)
+ (*tp->advance)(tp,
+ tp->tcol->rmargin - tp->viscol);
while (tp->viscol < tp->tcol->rmargin +
- tp->tbl.cols[ic].spacing / 2)
+ term_len(tp, tp->tbl.cols[ic].spacing / 2))
tbl_direct_border(tp,
- BHORIZ * lhori, 1);
+ BHORIZ * lhori, enw);
if (tp->tcol + 1 == tp->tcols + tp->lasttcol)
continue;
@@ -479,7 +517,7 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
if (tp->tbl.cols[ic].spacing)
tbl_direct_border(tp,
BLEFT * lhori + BRIGHT * rhori +
- BUP * uvert + BDOWN * dvert, 1);
+ BUP * uvert + BDOWN * dvert, enw);
if (tp->enc == TERMENC_UTF8)
uvert = dvert = 0;
@@ -489,7 +527,7 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
tbl_direct_border(tp,
BHORIZ * rhori +
BUP * (uvert > 1) +
- BDOWN * (dvert > 1), 1);
+ BDOWN * (dvert > 1), enw);
}
}
@@ -528,15 +566,14 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
if (horiz == 0 && (IS_HORIZ(sp->layout->last) == 0 ||
sp->layout->last->col + 1 < sp->opts->cols)) {
tp->tcol++;
- do {
+ if (tp->tcol->offset > tp->viscol)
tbl_direct_border(tp,
- BHORIZ * lhori, 1);
- } while (tp->viscol < tp->tcol->offset);
+ BHORIZ * lhori,
+ tp->tcol->offset - tp->viscol);
}
- tbl_direct_border(tp, fc, 1);
+ tbl_direct_border(tp, fc, enw);
}
(*tp->endline)(tp);
- tp->viscol = 0;
} while (more);
/*
@@ -575,6 +612,7 @@ tbl_hrule(struct termp *tp, const struct tbl_span *spp,
const struct tbl_cell *cpn; /* Layout cell below this line. */
const struct tbl_dat *dpn; /* Data cell below this line. */
const struct roffcol *col; /* Contains width and spacing. */
+ size_t enw; /* Width of one EN unit. */
int opts; /* For the table as a whole. */
int bw; /* Box line width. */
int hw; /* Horizontal line width. */
@@ -599,16 +637,19 @@ tbl_hrule(struct termp *tp, const struct tbl_span *spp,
/* Print the left end of the line. */
- if (tp->viscol == 0) {
+ enw = term_len(tp, 1);
+ if (tp->viscol == 0)
(*tp->advance)(tp, tp->tcols->offset);
- tp->viscol = tp->tcols->offset;
- }
- if (flags != 0)
+ if (flags != 0) {
tbl_direct_border(tp,
(spp == NULL ? 0 : BUP * bw) +
(spn == NULL ? 0 : BDOWN * bw) +
(spp == NULL || cpn == NULL ||
- cpn->pos != TBL_CELL_DOWN ? BRIGHT * hw : 0), 1);
+ cpn->pos != TBL_CELL_DOWN ? BRIGHT * hw : 0), enw);
+ tbl_direct_border(tp,
+ (spp == NULL || cpn == NULL ||
+ cpn->pos != TBL_CELL_DOWN ? BHORIZ * hw : 0), enw);
+ }
col = tp->tbl.cols;
for (;;) {
@@ -625,7 +666,7 @@ tbl_hrule(struct termp *tp, const struct tbl_span *spp,
strcmp(dpn->string, "\\^") != 0))
? hw : 0;
tbl_direct_border(tp, BHORIZ * lw,
- col->width + col->spacing / 2);
+ col->width + term_len(tp, col->spacing / 2));
/*
* Figure out whether a vertical line is crossing
@@ -678,7 +719,7 @@ tbl_hrule(struct termp *tp, const struct tbl_span *spp,
if (col->spacing)
tbl_direct_border(tp, BLEFT * lw +
- BRIGHT * rw + BUP * uw + BDOWN * dw, 1);
+ BRIGHT * rw + BUP * uw + BDOWN * dw, enw);
/*
* In ASCII output, a crossing may print two characters.
@@ -688,13 +729,14 @@ tbl_hrule(struct termp *tp, const struct tbl_span *spp,
uw = dw = 0;
if (col->spacing > 2)
tbl_direct_border(tp,
- BHORIZ * rw + BUP * uw + BDOWN * dw, 1);
+ BHORIZ * rw + BUP * uw + BDOWN * dw, enw);
/* Padding before the start of the next column. */
if (col->spacing > 4)
tbl_direct_border(tp,
- BHORIZ * rw, (col->spacing - 3) / 2);
+ BHORIZ * rw,
+ term_len(tp, (col->spacing - 3) / 2));
}
/* Print the right end of the line. */
@@ -705,23 +747,22 @@ tbl_hrule(struct termp *tp, const struct tbl_span *spp,
(spn == NULL ? 0 : BDOWN * bw) +
(spp == NULL || spn == NULL ||
spn->layout->last->pos != TBL_CELL_DOWN ?
- BLEFT * hw : 0), 1);
+ BLEFT * hw : 0), enw);
(*tp->endline)(tp);
- tp->viscol = 0;
}
}
static void
tbl_data(struct termp *tp, const struct tbl_opts *opts,
const struct tbl_cell *cp, const struct tbl_dat *dp,
- const struct roffcol *col)
+ const struct roffcol *col, size_t *coloff)
{
switch (cp->pos) {
case TBL_CELL_HORIZ:
- tbl_fill_border(tp, BHORIZ, col->width);
+ tbl_fill_border(tp, BHORIZ, coloff, col->width);
return;
case TBL_CELL_DHORIZ:
- tbl_fill_border(tp, BHORIZ * 2, col->width);
+ tbl_fill_border(tp, BHORIZ * 2, coloff, col->width);
return;
default:
break;
@@ -735,11 +776,11 @@ tbl_data(struct termp *tp, const struct tbl_opts *opts,
return;
case TBL_DATA_HORIZ:
case TBL_DATA_NHORIZ:
- tbl_fill_border(tp, BHORIZ, col->width);
+ tbl_fill_border(tp, BHORIZ, coloff, col->width);
return;
case TBL_DATA_NDHORIZ:
case TBL_DATA_DHORIZ:
- tbl_fill_border(tp, BHORIZ * 2, col->width);
+ tbl_fill_border(tp, BHORIZ * 2, coloff, col->width);
return;
default:
break;
@@ -750,10 +791,10 @@ tbl_data(struct termp *tp, const struct tbl_opts *opts,
case TBL_CELL_CENTRE:
case TBL_CELL_LEFT:
case TBL_CELL_RIGHT:
- tbl_literal(tp, dp, col);
+ tbl_literal(tp, dp, col, coloff);
break;
case TBL_CELL_NUMBER:
- tbl_number(tp, opts, dp, col);
+ tbl_number(tp, opts, dp, col, coloff);
break;
case TBL_CELL_DOWN:
case TBL_CELL_SPAN:
@@ -763,46 +804,72 @@ tbl_data(struct termp *tp, const struct tbl_opts *opts,
}
}
+/*
+ * Print multiple copies of the string cp to advance to
+ * len basic units from the left edge of the current column.
+ */
static void
-tbl_fill_string(struct termp *tp, const char *cp, size_t len)
+tbl_fill_string(struct termp *tp, const char *cp, size_t *coloff, size_t len)
{
- size_t i, sz;
+ size_t sz; /* Width of the string cp in basic units. */
+ size_t target; /* Distance from the left margin in basic units. */
+ if (len == 0)
+ return;
sz = term_strlen(tp, cp);
- for (i = 0; i < len; i += sz)
+ target = tp->tcol->offset + len;
+ while (*coloff < target) {
term_word(tp, cp);
+ *coloff += sz;
+ }
}
+/*
+ * Print multiple copies of the ASCII character c to advance to
+ * len basic units from the left edge of the current column.
+ */
static void
-tbl_fill_char(struct termp *tp, char c, size_t len)
+tbl_fill_char(struct termp *tp, char c, size_t *coloff, size_t len)
{
char cp[2];
cp[0] = c;
cp[1] = '\0';
- tbl_fill_string(tp, cp, len);
+ tbl_fill_string(tp, cp, coloff, len);
}
+/*
+ * Print multiple copies of the border c to fill len basic units.
+ * Used for horizontal lines inside table cells.
+ */
static void
-tbl_fill_border(struct termp *tp, int c, size_t len)
+tbl_fill_border(struct termp *tp, int c, size_t *coloff, size_t len)
{
char buf[12];
if ((c = borders_locale[c]) > 127) {
(void)snprintf(buf, sizeof(buf), "\\[u%04x]", c);
- tbl_fill_string(tp, buf, len);
+ tbl_fill_string(tp, buf, coloff, len);
} else
- tbl_fill_char(tp, c, len);
+ tbl_fill_char(tp, c, coloff, len);
}
+/*
+ * The same, but bypassing term_flushln().
+ * Used for horizontal and vertical lines at the edges of table cells.
+ */
static void
tbl_direct_border(struct termp *tp, int c, size_t len)
{
- size_t i, sz;
+ size_t sz; /* Width of the character in basic units. */
+ size_t enw2; /* Width of half an EN in basic units. */
+ size_t target; /* Distance from the left margin in basic units. */
c = borders_locale[c];
- sz = (*tp->width)(tp, c);
- for (i = 0; i < len; i += sz) {
+ sz = (*tp->getwidth)(tp, c);
+ enw2 = (*tp->getwidth)(tp, ' ') / 2;
+ target = tp->viscol + len;
+ while (tp->viscol + enw2 < target) {
(*tp->letter)(tp, c);
tp->viscol += sz;
}
@@ -810,56 +877,36 @@ tbl_direct_border(struct termp *tp, int c, size_t len)
static void
tbl_literal(struct termp *tp, const struct tbl_dat *dp,
- const struct roffcol *col)
+ const struct roffcol *col, size_t *coloff)
{
- size_t len, padl, padr, width;
- int ic, hspans;
+ size_t width; /* Of the cell including following spans [BU]. */
+ int ic; /* Column number of the cell. */
+ int hspans; /* Number of horizontal spans that follow. */
- assert(dp->string);
- len = term_strlen(tp, dp->string);
width = col->width;
ic = dp->layout->col;
hspans = dp->hspans;
while (hspans--) {
- width += tp->tbl.cols[ic].spacing;
+ width += term_len(tp, tp->tbl.cols[ic].spacing);
ic++;
width += tp->tbl.cols[ic].width;
}
-
- padr = width > len ? width - len : 0;
- padl = 0;
-
- switch (dp->layout->pos) {
- case TBL_CELL_LONG:
- padl = term_len(tp, 1);
- padr = padr > padl ? padr - padl : 0;
- break;
- case TBL_CELL_CENTRE:
- if (2 > padr)
- break;
- padl = padr / 2;
- padr -= padl;
- break;
- case TBL_CELL_RIGHT:
- padl = padr;
- padr = 0;
- break;
- default:
- break;
- }
-
- tbl_fill_char(tp, ASCII_NBRSP, padl);
tbl_word(tp, dp);
- tbl_fill_char(tp, ASCII_NBRSP, padr);
+ *coloff += width;
}
static void
tbl_number(struct termp *tp, const struct tbl_opts *opts,
const struct tbl_dat *dp,
- const struct roffcol *col)
+ const struct roffcol *col, size_t *coloff)
{
const char *cp, *lastdigit, *lastpoint;
- size_t intsz, padl, totsz;
+
+ /* Widths in basic units. */
+ size_t pad; /* Padding before the number. */
+ size_t totsz; /* Of the number to be printed. */
+ size_t intsz; /* Of the integer part. */
+
char buf[2];
/*
@@ -883,7 +930,7 @@ tbl_number(struct termp *tp, const struct tbl_opts *opts,
/* Then measure both widths. */
- padl = 0;
+ pad = 0;
totsz = term_strlen(tp, dp->string);
if (lastdigit != NULL) {
if (lastpoint == NULL)
@@ -901,23 +948,19 @@ tbl_number(struct termp *tp, const struct tbl_opts *opts,
*/
if (col->decimal > intsz && col->width > totsz) {
- padl = col->decimal - intsz;
- if (padl + totsz > col->width)
- padl = col->width - totsz;
+ pad = col->decimal - intsz;
+ if (pad + totsz > col->width)
+ pad = col->width - totsz;
}
/* If it is not a number, simply center the string. */
} else if (col->width > totsz)
- padl = (col->width - totsz) / 2;
+ pad = (col->width - totsz) / 2;
- tbl_fill_char(tp, ASCII_NBRSP, padl);
+ tbl_fill_char(tp, ASCII_NBRSP, coloff, pad);
tbl_word(tp, dp);
-
- /* Pad right to fill the column. */
-
- if (col->width > padl + totsz)
- tbl_fill_char(tp, ASCII_NBRSP, col->width - padl - totsz);
+ *coloff += col->width;
}
static void
diff --git a/contrib/mandoc/term.c b/contrib/mandoc/term.c
index 58d9d9bf9240..4dde60d1e45c 100644
--- a/contrib/mandoc/term.c
+++ b/contrib/mandoc/term.c
@@ -1,6 +1,6 @@
-/* $Id: term.c,v 1.291 2023/04/28 19:11:04 schwarze Exp $ */
+/* $Id: term.c,v 1.294 2025/08/01 14:59:39 schwarze Exp $ */
/*
- * Copyright (c) 2010-2022 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2010-2022, 2025 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
*
* Permission to use, copy, modify, and distribute this software for any
@@ -94,12 +94,15 @@ term_end(struct termp *p)
void
term_flushln(struct termp *p)
{
- size_t vbl; /* Number of blanks to prepend to the output. */
+ /* Widths in basic units. */
+ size_t vbl; /* Whitespace to prepend to the output. */
size_t vbr; /* Actual visual position of the end of field. */
size_t vfield; /* Desired visual field width. */
size_t vtarget; /* Desired visual position of the right margin. */
- size_t ic; /* Character position in the input buffer. */
- size_t nbr; /* Number of characters to print in this field. */
+
+ /* Bytes. */
+ size_t ic; /* Byte index in the input buffer. */
+ size_t nbr; /* Number of bytes to print in this field. */
/*
* Normally, start writing at the left margin, but with the
@@ -108,8 +111,8 @@ term_flushln(struct termp *p)
vbl = (p->flags & TERMP_NOPAD) || p->tcol->offset < p->viscol ?
0 : p->tcol->offset - p->viscol;
- if (p->minbl && vbl < p->minbl)
- vbl = p->minbl;
+ if (p->minbl > 0 && vbl < term_len(p, p->minbl))
+ vbl = term_len(p, p->minbl);
if ((p->flags & TERMP_MULTICOL) == 0)
p->tcol->col = 0;
@@ -137,7 +140,7 @@ term_flushln(struct termp *p)
*/
term_fill(p, &nbr, &vbr,
- p->flags & TERMP_BRNEVER ? SIZE_MAX : vtarget);
+ p->flags & TERMP_BRNEVER ? SIZE_MAX / 2 : vtarget);
if (nbr == 0)
break;
@@ -161,7 +164,7 @@ term_flushln(struct termp *p)
p->tcol->taboff += vbr;
else
p->tcol->taboff += vtarget;
- p->tcol->taboff += (*p->width)(p, ' ');
+ p->tcol->taboff += term_len(p, 1);
/*
* If there is no text left in the field, exit the loop.
@@ -178,7 +181,7 @@ term_flushln(struct termp *p)
continue;
case ' ':
if (p->flags & TERMP_BRTRSP)
- vbr += (*p->width)(p, ' ');
+ vbr += term_len(p, 1);
continue;
case '\n':
case ASCII_NBRZW:
@@ -245,13 +248,13 @@ term_flushln(struct termp *p)
if ((p->flags & TERMP_HANG) == 0 &&
((p->flags & TERMP_NOBREAK) == 0 ||
- vbr + term_len(p, p->trailspace) > vfield))
+ vbr + term_len(p, p->trailspace) > vfield + term_len(p, 1) / 2))
endline(p);
}
/*
- * Store the number of input characters to print in this field in *nbr
- * and their total visual width to print in *vbr.
+ * Store the number of input bytes to print in this field in *nbr
+ * and their total visual width in basic units in *vbr.
* If there is only whitespace in the field, both remain zero.
* The desired visual width of the field is provided by vtarget.
* If the first word is longer, the field will be overrun.
@@ -259,28 +262,33 @@ term_flushln(struct termp *p)
static void
term_fill(struct termp *p, size_t *nbr, size_t *vbr, size_t vtarget)
{
- size_t ic; /* Character position in the input buffer. */
+ /* Widths in basic units. */
size_t vis; /* Visual position of the current character. */
size_t vn; /* Visual position of the next character. */
+ size_t enw; /* Width of an EN unit. */
+ int taboff; /* Temporary offset for literal tabs. */
+
+ size_t ic; /* Byte index in the input buffer. */
int breakline; /* Break at the end of this word. */
int graph; /* Last character was non-blank. */
- int taboff; /* Temporary offset for literal tabs. */
*nbr = *vbr = vis = 0;
breakline = graph = 0;
taboff = p->tcol->taboff;
+ enw = (*p->getwidth)(p, ' ');
+ vtarget += enw / 2;
for (ic = p->tcol->col; ic < p->tcol->lastcol; ic++) {
switch (p->tcol->buf[ic]) {
case '\b': /* Escape \o (overstrike) or backspace markup. */
assert(ic > 0);
- vis -= (*p->width)(p, p->tcol->buf[ic - 1]);
+ vis -= (*p->getwidth)(p, p->tcol->buf[ic - 1]);
continue;
case ' ':
case ASCII_BREAK: /* Escape \: (breakpoint). */
vn = vis;
if (p->tcol->buf[ic] == ' ')
- vn += (*p->width)(p, ' ');
+ vn += enw;
/* Can break at the end of a word. */
if (breakline || vn > vtarget)
break;
@@ -305,7 +313,7 @@ term_fill(struct termp *p, size_t *nbr, size_t *vbr, size_t vtarget)
* hyphen such that we get the correct width.
*/
p->tcol->buf[ic] = '-';
- vis += (*p->width)(p, '-');
+ vis += (*p->getwidth)(p, '-');
if (vis > vtarget) {
ic++;
break;
@@ -315,7 +323,7 @@ term_fill(struct termp *p, size_t *nbr, size_t *vbr, size_t vtarget)
continue;
case ASCII_TABREF:
- taboff = -vis - (*p->width)(p, ' ');
+ taboff = -vis - enw;
continue;
default:
@@ -334,7 +342,7 @@ term_fill(struct termp *p, size_t *nbr, size_t *vbr, size_t vtarget)
p->tcol->buf[ic] = ' ';
/* FALLTHROUGH */
default: /* Printable character. */
- vis += (*p->width)(p, p->tcol->buf[ic]);
+ vis += (*p->getwidth)(p, p->tcol->buf[ic]);
break;
}
graph = 1;
@@ -359,18 +367,20 @@ term_fill(struct termp *p, size_t *nbr, size_t *vbr, size_t vtarget)
/*
* Print the contents of one field
- * with an indentation of vbl visual columns,
- * and an input string length of nbr characters.
+ * with an indentation of vbl basic units
+ * and an input string length of nbr bytes.
*/
static void
term_field(struct termp *p, size_t vbl, size_t nbr)
{
- size_t ic; /* Character position in the input buffer. */
+ /* Widths in basic units. */
size_t vis; /* Visual position of the current character. */
size_t vt; /* Visual position including tab offset. */
size_t dv; /* Visual width of the current character. */
int taboff; /* Temporary offset for literal tabs. */
+ size_t ic; /* Byte position in the input buffer. */
+
vis = 0;
taboff = p->tcol->taboff;
for (ic = p->tcol->col; ic < nbr; ic++) {
@@ -386,7 +396,7 @@ term_field(struct termp *p, size_t vbl, size_t nbr)
case ASCII_NBRZW:
continue;
case ASCII_TABREF:
- taboff = -vis - (*p->width)(p, ' ');
+ taboff = -vis - (*p->getwidth)(p, ' ');
continue;
case '\t':
case ' ':
@@ -398,7 +408,7 @@ term_field(struct termp *p, size_t vbl, size_t nbr)
vt = vis + taboff;
dv = term_tab_next(vt) - vt;
} else
- dv = (*p->width)(p, ' ');
+ dv = (*p->getwidth)(p, ' ');
vbl += dv;
vis += dv;
continue;
@@ -413,7 +423,6 @@ term_field(struct termp *p, size_t vbl, size_t nbr)
if (vbl > 0) {
(*p->advance)(p, vbl);
- p->viscol += vbl;
vbl = 0;
}
@@ -421,11 +430,11 @@ term_field(struct termp *p, size_t vbl, size_t nbr)
(*p->letter)(p, p->tcol->buf[ic]);
if (p->tcol->buf[ic] == '\b') {
- dv = (*p->width)(p, p->tcol->buf[ic - 1]);
+ dv = (*p->getwidth)(p, p->tcol->buf[ic - 1]);
p->viscol -= dv;
vis -= dv;
} else {
- dv = (*p->width)(p, p->tcol->buf[ic]);
+ dv = (*p->getwidth)(p, p->tcol->buf[ic]);
p->viscol += dv;
vis += dv;
}
@@ -433,6 +442,10 @@ term_field(struct termp *p, size_t vbl, size_t nbr)
p->tcol->col = nbr;
}
+/*
+ * Print the margin character, if one is configured,
+ * and end the output line.
+ */
static void
endline(struct termp *p)
{
@@ -441,14 +454,13 @@ endline(struct termp *p)
p->flags &= ~TERMP_ENDMC;
}
if (p->mc != NULL) {
- if (p->viscol && p->maxrmargin >= p->viscol)
- (*p->advance)(p, p->maxrmargin - p->viscol + 1);
+ if (p->viscol > 0 && p->viscol <= p->maxrmargin)
+ (*p->advance)(p,
+ p->maxrmargin - p->viscol + term_len(p, 1));
p->flags |= TERMP_NOBUF | TERMP_NOSPACE;
term_word(p, p->mc);
p->flags &= ~(TERMP_NOBUF | TERMP_NEWMC);
}
- p->viscol = 0;
- p->minbl = 0;
(*p->endline)(p);
}
@@ -477,8 +489,6 @@ term_vspace(struct termp *p)
{
term_newln(p);
- p->viscol = 0;
- p->minbl = 0;
if (0 < p->skipvsp)
p->skipvsp--;
else
@@ -496,34 +506,36 @@ term_fontlast(struct termp *p)
p->fontq[p->fonti] = f;
}
-/* Set font, save current, discard previous; for \f, .ft, .B etc. */
+/* Set font, save current, discard previous; for \f, .ft, and man(7). */
void
term_fontrepl(struct termp *p, enum termfont f)
{
-
p->fontl = p->fontq[p->fonti];
+ if (p->fontibi && f == TERMFONT_UNDER)
+ f = TERMFONT_BI;
p->fontq[p->fonti] = f;
}
-/* Set font, save previous. */
+/* Set font, save previous; for mdoc(7), eqn(7), and tbl(7). */
void
term_fontpush(struct termp *p, enum termfont f)
{
+ enum termfont fl;
- p->fontl = p->fontq[p->fonti];
+ fl = p->fontq[p->fonti];
if (++p->fonti == p->fontsz) {
p->fontsz += 8;
p->fontq = mandoc_reallocarray(p->fontq,
p->fontsz, sizeof(*p->fontq));
}
- p->fontq[p->fonti] = f;
+ p->fontq[p->fonti] = fl;
+ term_fontrepl(p, f);
}
/* Flush to make the saved pointer current again. */
void
term_fontpopq(struct termp *p, int i)
{
-
assert(i >= 0);
if (p->fonti > i)
p->fonti = i;
@@ -533,8 +545,7 @@ term_fontpopq(struct termp *p, int i)
void
term_fontpop(struct termp *p)
{
-
- assert(p->fonti);
+ assert(p->fonti > 0);
p->fonti--;
}
@@ -548,9 +559,14 @@ term_word(struct termp *p, const char *word)
{
struct roffsu su;
const char nbrsp[2] = { ASCII_NBRSP, 0 };
- const char *seq, *cp;
- int sz, uc;
- size_t csz, lsz, ssz;
+ const char *seq; /* Escape sequence argument. */
+ const char *cp; /* String to be printed. */
+ size_t csz; /* String length in basic units. */
+ size_t lsz; /* Line width in basic units. */
+ size_t ssz; /* Substring length in bytes. */
+ int sz; /* Argument length in bytes. */
+ int uc; /* Unicode codepoint number. */
+ int bu; /* Width in basic units. */
enum mandoc_esc esc;
if ((p->flags & TERMP_NOBUF) == 0) {
@@ -663,15 +679,15 @@ term_word(struct termp *p, const char *word)
}
if (*seq == '|') {
seq++;
- uc = -p->col;
+ bu = -term_len(p, p->col);
} else
- uc = 0;
+ bu = 0;
if (a2roffsu(seq, &su, SCALE_EM) == NULL)
continue;
- uc += term_hen(p, &su);
- if (uc >= 0) {
- while (uc > 0) {
- uc -= term_len(p, 1);
+ bu += term_hspan(p, &su);
+ if (bu >= 0) {
+ while (bu > 0) {
+ bu -= term_len(p, 1);
if (p->flags & TERMP_BACKBEFORE)
p->flags &= ~TERMP_BACKBEFORE;
else
@@ -681,17 +697,17 @@ term_word(struct termp *p, const char *word)
}
if (p->flags & TERMP_BACKBEFORE) {
p->flags &= ~TERMP_BACKBEFORE;
- assert(p->col > 0);
+ assert(p->col > 1);
p->col--;
}
- if (p->col >= (size_t)(-uc)) {
- p->col += uc;
+ if (term_len(p, p->col) >= (size_t)(-bu)) {
+ p->col -= -bu / term_len(p, 1);
} else {
- uc += p->col;
+ bu += term_len(p, p->col);
p->col = 0;
- if (p->tcol->offset > (size_t)(-uc)) {
- p->ti += uc;
- p->tcol->offset += uc;
+ if (p->tcol->offset > (size_t)(-bu)) {
+ p->ti += bu;
+ p->tcol->offset += bu;
} else {
p->ti -= p->tcol->offset;
p->tcol->offset = 0;
@@ -701,13 +717,13 @@ term_word(struct termp *p, const char *word)
case ESCAPE_HLINE:
if ((cp = a2roffsu(seq, &su, SCALE_EM)) == NULL)
continue;
- uc = term_hen(p, &su);
- if (uc <= 0) {
+ bu = term_hspan(p, &su);
+ if (bu <= 0) {
if (p->tcol->rmargin <= p->tcol->offset)
continue;
lsz = p->tcol->rmargin - p->tcol->offset;
} else
- lsz = uc;
+ lsz = bu;
if (*cp == seq[-1])
uc = -1;
else if (*cp == '\\') {
@@ -739,13 +755,16 @@ term_word(struct termp *p, const char *word)
csz = term_strlen(p, cp);
ssz = strlen(cp);
} else
- csz = (*p->width)(p, uc);
- while (lsz >= csz) {
+ csz = (*p->getwidth)(p, uc);
+ while (lsz > 0) {
if (p->enc == TERMENC_ASCII)
encode(p, cp, ssz);
else
encode1(p, uc);
- lsz -= csz;
+ if (lsz > csz)
+ lsz -= csz;
+ else
+ lsz = 0;
}
continue;
case ESCAPE_SKIPCHAR:
@@ -954,28 +973,39 @@ term_setwidth(struct termp *p, const char *wstr)
size_t
term_len(const struct termp *p, size_t sz)
{
-
- return (*p->width)(p, ' ') * sz;
+ return (*p->getwidth)(p, ' ') * sz;
}
static size_t
cond_width(const struct termp *p, int c, int *skip)
{
-
if (*skip) {
(*skip) = 0;
return 0;
} else
- return (*p->width)(p, c);
+ return (*p->getwidth)(p, c);
}
size_t
term_strlen(const struct termp *p, const char *cp)
{
- size_t sz, rsz, i;
- int ssz, skip, uc;
- const char *seq, *rhs;
+ const char *seq; /* Escape sequence argument. */
+ const char *rhs; /* String to be printed. */
+
+ /* Widths in basic units. */
+ size_t sz; /* Return value. */
+ size_t this_sz; /* Individual char for overstrike. */
+ size_t max_sz; /* Result of overstrike. */
+
+ /* Numbers of bytes. */
+ size_t rsz; /* Substring length in bytes. */
+ size_t i; /* Byte index in substring. */
+ int ssz; /* Argument length in bytes. */
+ int skip; /* Number of bytes to skip. */
+
+ int uc; /* Unicode codepoint number. */
enum mandoc_esc esc;
+
static const char rej[] = { '\\', ASCII_NBRSP, ASCII_NBRZW,
ASCII_BREAK, ASCII_HYPH, ASCII_TABREF, '\0' };
@@ -1039,18 +1069,18 @@ term_strlen(const struct termp *p, const char *cp)
skip = 1;
continue;
case ESCAPE_OVERSTRIKE:
- rsz = 0;
+ max_sz = 0;
rhs = seq + ssz;
while (seq < rhs) {
if (*seq == '\\') {
mandoc_escape(&seq, NULL, NULL);
continue;
}
- i = (*p->width)(p, *seq++);
- if (rsz < i)
- rsz = i;
+ this_sz = (*p->getwidth)(p, *seq++);
+ if (max_sz < this_sz)
+ max_sz = this_sz;
}
- sz += rsz;
+ sz += max_sz;
continue;
default:
continue;
@@ -1085,7 +1115,7 @@ term_strlen(const struct termp *p, const char *cp)
*/
for (i = 0; i < rsz; i++)
- sz += (*p->width)(p, *rhs++);
+ sz += (*p->getwidth)(p, *rhs++);
break;
case ASCII_NBRSP:
sz += cond_width(p, ' ', &skip);
@@ -1146,25 +1176,10 @@ term_vspan(const struct termp *p, const struct roffsu *su)
}
/*
- * Convert a scaling width to basic units, rounding towards 0.
+ * Convert a scaling width to basic units.
*/
int
term_hspan(const struct termp *p, const struct roffsu *su)
{
-
return (*p->hspan)(p, su);
}
-
-/*
- * Convert a scaling width to basic units, rounding to closest.
- */
-int
-term_hen(const struct termp *p, const struct roffsu *su)
-{
- int bu;
-
- if ((bu = (*p->hspan)(p, su)) >= 0)
- return (bu + 11) / 24;
- else
- return -((-bu + 11) / 24);
-}
diff --git a/contrib/mandoc/term.h b/contrib/mandoc/term.h
index 3b3a79527eeb..1e4659734fc5 100644
--- a/contrib/mandoc/term.h
+++ b/contrib/mandoc/term.h
@@ -1,6 +1,7 @@
-/* $Id: term.h,v 1.134 2022/08/16 17:45:55 schwarze Exp $ */
+/* $Id: term.h,v 1.138 2025/07/27 15:27:28 schwarze Exp $ */
/*
- * Copyright (c) 2011-2015,2017,2019,2022 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2011-2015, 2017, 2019, 2021, 2022, 2025
+ * Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
*
* Permission to use, copy, modify, and distribute this software for any
@@ -44,19 +45,14 @@ struct termp;
typedef void (*term_margin)(struct termp *, const struct roff_meta *);
-struct termp_tbl {
- int width; /* width in fixed chars */
- int decimal; /* decimal point position */
-};
-
struct termp_col {
int *buf; /* Output buffer. */
size_t maxcols; /* Allocated bytes in buf. */
size_t lastcol; /* Last byte in buf. */
size_t col; /* Byte in buf to be written. */
- size_t rmargin; /* Current right margin. */
- size_t offset; /* Current left margin. */
- size_t taboff; /* Offset for literal tabs. */
+ size_t rmargin; /* Current right margin [BU]. */
+ size_t offset; /* Current left margin [BU]. */
+ size_t taboff; /* Offset for literal tabs [BU]. */
};
struct termp {
@@ -66,17 +62,16 @@ struct termp {
size_t maxtcol; /* Allocated table columns. */
size_t lasttcol; /* Last column currently used. */
size_t line; /* Current output line number. */
- size_t defindent; /* Default indent for text. */
- size_t defrmargin; /* Right margin of the device. */
- size_t lastrmargin; /* Right margin before the last ll. */
- size_t maxrmargin; /* Max right margin. */
+ size_t defindent; /* Default indent for text [EN]. */
+ size_t defrmargin; /* Right margin of the device [BU]. */
+ size_t lastrmargin; /* Right margin before last ll [BU]. */
+ size_t maxrmargin; /* Maximum right margin [BU]. */
size_t col; /* Byte position in buf. */
- size_t viscol; /* Chars on current line. */
- size_t trailspace; /* See term_flushln(). */
- size_t minbl; /* Minimum blanks before next field. */
+ size_t viscol; /* Width of the current line [BU]. */
+ size_t trailspace; /* Whitespace after field [EN]. */
+ size_t minbl; /* Whitespace before field [EN]. */
int synopsisonly; /* Print the synopsis only. */
- int mdocstyle; /* Imitate mdoc(7) output. */
- int ti; /* Temporary indent for one line. */
+ int ti; /* Temporary indent for line [BU]. */
int skipvsp; /* Vertical space to skip. */
int flags;
#define TERMP_SENTENCE (1 << 0) /* Space before a sentence. */
@@ -108,6 +103,7 @@ struct termp {
enum termfont *fontq; /* Symmetric fonts. */
int fontsz; /* Allocated size of font stack */
int fonti; /* Index of font stack. */
+ int fontibi; /* Map font I to BI. */
term_margin headf; /* invoked to print head */
term_margin footf; /* invoked to print foot */
void (*letter)(struct termp *, int);
@@ -116,7 +112,7 @@ struct termp {
void (*endline)(struct termp *);
void (*advance)(struct termp *, size_t);
void (*setwidth)(struct termp *, int, int);
- size_t (*width)(const struct termp *, int);
+ size_t (*getwidth)(const struct termp *, int);
int (*hspan)(const struct termp *,
const struct roffsu *);
const void *argf; /* arg for headf/footf */
@@ -143,13 +139,11 @@ void term_end(struct termp *);
void term_setwidth(struct termp *, const char *);
int term_hspan(const struct termp *, const struct roffsu *);
-int term_hen(const struct termp *, const struct roffsu *);
int term_vspan(const struct termp *, const struct roffsu *);
size_t term_strlen(const struct termp *, const char *);
size_t term_len(const struct termp *, size_t);
void term_tab_set(const struct termp *, const char *);
-void term_tab_iset(size_t);
void term_tab_ref(struct termp *);
size_t term_tab_next(size_t);
void term_tab_free(void);
diff --git a/contrib/mandoc/term_ascii.c b/contrib/mandoc/term_ascii.c
index 3942dc757953..990833c8a021 100644
--- a/contrib/mandoc/term_ascii.c
+++ b/contrib/mandoc/term_ascii.c
@@ -1,7 +1,7 @@
-/* $Id: term_ascii.c,v 1.69 2023/11/13 19:13:01 schwarze Exp $ */
+/* $Id: term_ascii.c,v 1.71 2025/07/16 14:33:08 schwarze Exp $ */
/*
+ * Copyright (c) 2014,2015,2017-2020,2025 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
- * Copyright (c) 2014,2015,2017,2018,2020 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -43,7 +43,7 @@
static struct termp *ascii_init(enum termenc, const struct manoutput *);
static int ascii_hspan(const struct termp *,
const struct roffsu *);
-static size_t ascii_width(const struct termp *, int);
+static size_t ascii_getwidth(const struct termp *, int);
static void ascii_advance(struct termp *, size_t);
static void ascii_begin(struct termp *);
static void ascii_end(struct termp *);
@@ -55,7 +55,7 @@ static void ascii_setwidth(struct termp *, int, int);
static void locale_advance(struct termp *, size_t);
static void locale_endline(struct termp *);
static void locale_letter(struct termp *, int);
-static size_t locale_width(const struct termp *, int);
+static size_t locale_getwidth(const struct termp *, int);
#endif
@@ -73,7 +73,6 @@ ascii_init(enum termenc enc, const struct manoutput *outopts)
p->line = 1;
p->defindent = 5;
- p->defrmargin = p->lastrmargin = 78;
p->fontq = mandoc_reallocarray(NULL,
(p->fontsz = 8), sizeof(*p->fontq));
p->fontq[0] = p->fontl = TERMFONT_NONE;
@@ -82,13 +81,12 @@ ascii_init(enum termenc enc, const struct manoutput *outopts)
p->end = ascii_end;
p->hspan = ascii_hspan;
p->type = TERMTYPE_CHAR;
-
p->enc = TERMENC_ASCII;
p->advance = ascii_advance;
p->endline = ascii_endline;
p->letter = ascii_letter;
p->setwidth = ascii_setwidth;
- p->width = ascii_width;
+ p->getwidth = ascii_getwidth;
#if HAVE_WCHAR
if (enc != TERMENC_ASCII) {
@@ -118,17 +116,15 @@ ascii_init(enum termenc enc, const struct manoutput *outopts)
p->advance = locale_advance;
p->endline = locale_endline;
p->letter = locale_letter;
- p->width = locale_width;
+ p->getwidth = locale_getwidth;
}
}
#endif
+ p->defrmargin = term_len(p, outopts->width ? outopts->width : 78);
+ p->lastrmargin = p->defrmargin;
- if (outopts->mdoc)
- p->mdocstyle = 1;
if (outopts->indent)
p->defindent = outopts->indent;
- if (outopts->width)
- p->defrmargin = outopts->width;
if (outopts->synopsisonly)
p->synopsisonly = 1;
@@ -140,29 +136,24 @@ ascii_init(enum termenc enc, const struct manoutput *outopts)
void *
ascii_alloc(const struct manoutput *outopts)
{
-
return ascii_init(TERMENC_ASCII, outopts);
}
void *
utf8_alloc(const struct manoutput *outopts)
{
-
return ascii_init(TERMENC_UTF8, outopts);
}
void *
locale_alloc(const struct manoutput *outopts)
{
-
return ascii_init(TERMENC_LOCALE, outopts);
}
static void
ascii_setwidth(struct termp *p, int iop, int width)
{
-
- width /= 24;
p->tcol->rmargin = p->defrmargin;
if (iop > 0)
p->defrmargin += width;
@@ -172,8 +163,8 @@ ascii_setwidth(struct termp *p, int iop, int width)
p->defrmargin -= width;
else
p->defrmargin = 0;
- if (p->defrmargin > 1000)
- p->defrmargin = 1000;
+ if (p->defrmargin > term_len(p, 1000))
+ p->defrmargin = term_len(p, 1000);
p->lastrmargin = p->tcol->rmargin;
p->tcol->rmargin = p->maxrmargin = p->defrmargin;
}
@@ -182,67 +173,76 @@ void
terminal_sepline(void *arg)
{
struct termp *p;
- size_t i;
+ size_t i; /* Printed width in basic units. */
+ size_t sz; /* Width of a dash in basic units. */
p = (struct termp *)arg;
(*p->endline)(p);
- for (i = 0; i < p->defrmargin; i++)
+ sz = (*p->getwidth)(p, '-');
+ for (i = 0; i < p->defrmargin; i += sz)
(*p->letter)(p, '-');
(*p->endline)(p);
(*p->endline)(p);
}
static size_t
-ascii_width(const struct termp *p, int c)
+ascii_getwidth(const struct termp *p, int c)
{
- return c != ASCII_BREAK && c != ASCII_NBRZW && c != ASCII_TABREF;
+ switch (c) {
+ case ASCII_BREAK:
+ case ASCII_NBRZW:
+ case ASCII_TABREF:
+ return 0;
+ default:
+ return 24;
+ }
}
void
ascii_free(void *arg)
{
-
term_free((struct termp *)arg);
}
static void
ascii_letter(struct termp *p, int c)
{
-
putchar(c);
}
static void
ascii_begin(struct termp *p)
{
-
(*p->headf)(p, p->argf);
}
static void
ascii_end(struct termp *p)
{
-
(*p->footf)(p, p->argf);
}
static void
ascii_endline(struct termp *p)
{
-
p->line++;
if ((int)p->tcol->offset > p->ti)
p->tcol->offset -= p->ti;
else
p->tcol->offset = 0;
p->ti = 0;
+ p->minbl = 0;
+ p->viscol = 0;
putchar('\n');
}
static void
ascii_advance(struct termp *p, size_t len)
{
- size_t i;
+ size_t dst; /* Destination column in basic units. */
+ size_t sz; /* Width of a space in basic units. */
+
+ sz = (*p->getwidth)(p, ' ');
/*
* XXX We used to have "assert(len < UINT16_MAX)" here.
@@ -250,10 +250,14 @@ ascii_advance(struct termp *p, size_t len)
* can trigger that by merely providing large input.
* For now, simply truncate.
*/
- if (len > 256)
- len = 256;
- for (i = 0; i < len; i++)
+ if (len > 256 * sz)
+ len = 256 * sz;
+
+ dst = p->viscol + len;
+ while (p->viscol + sz / 2 < dst) {
putchar(' ');
+ p->viscol += sz;
+ }
}
static int
@@ -372,7 +376,7 @@ ascii_uc2str(int uc)
#if HAVE_WCHAR
static size_t
-locale_width(const struct termp *p, int c)
+locale_getwidth(const struct termp *p, int c)
{
int rc;
@@ -381,13 +385,16 @@ locale_width(const struct termp *p, int c)
rc = wcwidth(c);
if (rc < 0)
rc = 0;
- return rc;
+ return rc * 24;
}
static void
locale_advance(struct termp *p, size_t len)
{
- size_t i;
+ size_t dst; /* Destination column in basic units. */
+ size_t sz; /* Width of a space in basic units. */
+
+ sz = (*p->getwidth)(p, ' ');
/*
* XXX We used to have "assert(len < UINT16_MAX)" here.
@@ -395,29 +402,33 @@ locale_advance(struct termp *p, size_t len)
* can trigger that by merely providing large input.
* For now, simply truncate.
*/
- if (len > 256)
- len = 256;
- for (i = 0; i < len; i++)
+ if (len > 256 * sz)
+ len = 256 * sz;
+
+ dst = p->viscol + len;
+ while (p->viscol + sz / 2 < dst) {
putwchar(L' ');
+ p->viscol += sz;
+ }
}
static void
locale_endline(struct termp *p)
{
-
p->line++;
if ((int)p->tcol->offset > p->ti)
p->tcol->offset -= p->ti;
else
p->tcol->offset = 0;
p->ti = 0;
+ p->minbl = 0;
+ p->viscol = 0;
putwchar(L'\n');
}
static void
locale_letter(struct termp *p, int c)
{
-
putwchar(c);
}
#endif
diff --git a/contrib/mandoc/term_ps.c b/contrib/mandoc/term_ps.c
index 374d3d9a6abd..91124152d55a 100644
--- a/contrib/mandoc/term_ps.c
+++ b/contrib/mandoc/term_ps.c
@@ -1,7 +1,7 @@
-/* $Id: term_ps.c,v 1.92 2020/09/06 14:45:22 schwarze Exp $ */
+/* $Id: term_ps.c,v 1.95 2025/09/26 12:17:12 schwarze Exp $ */
/*
+ * Copyright (c) 2014-2017, 2020, 2025 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
- * Copyright (c) 2014,2015,2016,2017,2020 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2017 Marc Espie <espie@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
@@ -95,7 +95,7 @@ struct termp_ps {
static int ps_hspan(const struct termp *,
const struct roffsu *);
-static size_t ps_width(const struct termp *, int);
+static size_t ps_getwidth(const struct termp *, int);
static void ps_advance(struct termp *, size_t);
static void ps_begin(struct termp *);
static void ps_closepage(struct termp *);
@@ -549,7 +549,7 @@ pspdf_alloc(const struct manoutput *outopts, enum termtype type)
p->hspan = ps_hspan;
p->letter = ps_letter;
p->setwidth = ps_setwidth;
- p->width = ps_width;
+ p->getwidth = ps_getwidth;
/* Default to US letter (millimetres). */
@@ -1211,6 +1211,7 @@ ps_advance(struct termp *p, size_t len)
ps_plast(p);
ps_pclose(p);
p->ps->pscol += len;
+ p->viscol += len;
}
static void
@@ -1221,6 +1222,8 @@ ps_endline(struct termp *p)
ps_plast(p);
ps_pclose(p);
+ p->viscol = 0;
+ p->minbl = 0;
/*
* If we're in the margin, don't try to recalculate our current
@@ -1282,7 +1285,7 @@ ps_setfont(struct termp *p, enum termfont f)
}
static size_t
-ps_width(const struct termp *p, int c)
+ps_getwidth(const struct termp *p, int c)
{
if (c <= 32 || c - 32 >= MAXCHAR)
@@ -1311,7 +1314,7 @@ ps_hspan(const struct termp *p, const struct roffsu *su)
* scaling unit so that output is the same regardless
* the media.
*/
- r = PNT2AFM(p, su->scale * 72.0 / 240.0);
+ r = PNT2AFM(p, su->scale * 72.0 / 10.0);
break;
case SCALE_CM:
r = PNT2AFM(p, su->scale * 72.0 / 2.54);
@@ -1344,8 +1347,7 @@ ps_hspan(const struct termp *p, const struct roffsu *su)
r = su->scale;
break;
}
-
- return r * 24.0;
+ return r;
}
static void
diff --git a/contrib/mandoc/term_tab.c b/contrib/mandoc/term_tab.c
index a2d1074159b9..dd1b6bcdc696 100644
--- a/contrib/mandoc/term_tab.c
+++ b/contrib/mandoc/term_tab.c
@@ -1,6 +1,6 @@
-/* $Id: term_tab.c,v 1.7 2021/10/04 18:56:31 schwarze Exp $ */
+/* $Id: term_tab.c,v 1.9 2025/07/16 14:33:08 schwarze Exp $ */
/*
- * Copyright (c) 2017, 2021 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2017, 2021, 2025 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -27,7 +27,7 @@
#include "term.h"
struct tablist {
- size_t *t; /* Allocated array of tab positions. */
+ size_t *t; /* Allocated array of tab positions [BU]. */
size_t s; /* Allocated number of positions. */
size_t n; /* Currently used number of positions. */
};
@@ -36,7 +36,7 @@ static struct {
struct tablist a; /* All tab positions for lookup. */
struct tablist p; /* Periodic tab positions to add. */
struct tablist *r; /* Tablist currently being recorded. */
- size_t d; /* Default tab width in units of n. */
+ size_t d; /* Default tab width in basic units. */
} tabs;
@@ -55,7 +55,7 @@ term_tab_set(const struct termp *p, const char *arg)
tabs.r = &tabs.a;
if (tabs.d == 0) {
a2roffsu(".8i", &su, SCALE_IN);
- tabs.d = term_hen(p, &su);
+ tabs.d = term_hspan(p, &su);
}
return;
}
@@ -84,28 +84,13 @@ term_tab_set(const struct termp *p, const char *arg)
/* Append the new position. */
- pos = term_hen(p, &su);
+ pos = term_hspan(p, &su);
tl->t[tl->n] = pos;
if (add && tl->n)
tl->t[tl->n] += tl->t[tl->n - 1];
tl->n++;
}
-/*
- * Simplified version without a parser,
- * never incremental, never periodic, for use by tbl(7).
- */
-void
-term_tab_iset(size_t inc)
-{
- if (tabs.a.n >= tabs.a.s) {
- tabs.a.s += 8;
- tabs.a.t = mandoc_reallocarray(tabs.a.t, tabs.a.s,
- sizeof(*tabs.a.t));
- }
- tabs.a.t[tabs.a.n++] = inc;
-}
-
size_t
term_tab_next(size_t prev)
{
diff --git a/contrib/ofed/libcxgb4/dev.c b/contrib/ofed/libcxgb4/dev.c
index d3c289dad9f2..db728f5627da 100644
--- a/contrib/ofed/libcxgb4/dev.c
+++ b/contrib/ofed/libcxgb4/dev.c
@@ -144,6 +144,8 @@ static struct ibv_context *c4iw_alloc_context(struct ibv_device *ibdev,
context->ibv_ctx.ops = c4iw_ctx_ops;
switch (rhp->chip_version) {
+ case CHELSIO_T7:
+ PDBG("%s T7/T6/T5/T4 device\n", __FUNCTION__);
case CHELSIO_T6:
PDBG("%s T6/T5/T4 device\n", __FUNCTION__);
case CHELSIO_T5:
@@ -429,6 +431,8 @@ static struct verbs_device *cxgb4_driver_init(const char *uverbs_sys_path,
strstr(&ibdev[2], "nex") && devnum >= 0) {
snprintf(dev_str, sizeof(dev_str), "/dev/t%cnex/%d", ibdev[1],
devnum);
+ } else if (strstr(&ibdev[0], "chnex") && devnum >= 0) {
+ snprintf(dev_str, sizeof(dev_str), "/dev/chnex/%d", devnum);
} else
return NULL;
@@ -523,7 +527,10 @@ found:
goto err;
dev->ibv_dev.ops = &c4iw_dev_ops;
- dev->chip_version = CHELSIO_CHIP_VERSION(hca_table[i].device >> 8);
+ if (hca_table[i].device == 0xd000)
+ dev->chip_version = CHELSIO_T7;
+ else
+ dev->chip_version = CHELSIO_CHIP_VERSION(hca_table[i].device >> 8);
dev->abi_version = abi_version;
PDBG("%s device claimed\n", __FUNCTION__);
diff --git a/contrib/ofed/libcxgb4/libcxgb4.h b/contrib/ofed/libcxgb4/libcxgb4.h
index b891e8b1df97..216eee05a9d8 100644
--- a/contrib/ofed/libcxgb4/libcxgb4.h
+++ b/contrib/ofed/libcxgb4/libcxgb4.h
@@ -64,6 +64,11 @@ struct c4iw_dev {
int abi_version;
};
+static inline int dev_is_t7(struct c4iw_dev *dev)
+{
+ return dev->chip_version == CHELSIO_T7;
+}
+
static inline int dev_is_t6(struct c4iw_dev *dev)
{
return dev->chip_version == CHELSIO_T6;
diff --git a/contrib/ofed/libcxgb4/t4_chip_type.h b/contrib/ofed/libcxgb4/t4_chip_type.h
index 54b718111e3f..ae3b760c51a1 100644
--- a/contrib/ofed/libcxgb4/t4_chip_type.h
+++ b/contrib/ofed/libcxgb4/t4_chip_type.h
@@ -37,6 +37,7 @@
#define CHELSIO_T4 0x4
#define CHELSIO_T5 0x5
#define CHELSIO_T6 0x6
+#define CHELSIO_T7 0x7
/* We code the Chelsio T4 Family "Chip Code" as a tuple:
*
@@ -65,6 +66,10 @@ enum chip_type {
T6_A0 = CHELSIO_CHIP_CODE(CHELSIO_T6, 0),
T6_FIRST_REV = T6_A0,
T6_LAST_REV = T6_A0,
+
+ T7_A1 = CHELSIO_CHIP_CODE(CHELSIO_T7, 1),
+ T7_FIRST_REV = T7_A1,
+ T7_LAST_REV = T7_A1,
};
static inline int is_t4(enum chip_type chip)
@@ -82,4 +87,9 @@ static inline int is_t6(enum chip_type chip)
return (CHELSIO_CHIP_VERSION(chip) == CHELSIO_T6);
}
+static inline int is_t7(enum chip_type chip)
+{
+ return (CHELSIO_CHIP_VERSION(chip) == CHELSIO_T7);
+}
+
#endif /* __T4_CHIP_TYPE_H__ */
diff --git a/contrib/ofed/libcxgb4/t4_pci_id_tbl.h b/contrib/ofed/libcxgb4/t4_pci_id_tbl.h
index 50812a1d67bd..0ff7e689dc84 100644
--- a/contrib/ofed/libcxgb4/t4_pci_id_tbl.h
+++ b/contrib/ofed/libcxgb4/t4_pci_id_tbl.h
@@ -92,8 +92,7 @@
#endif
CH_PCI_DEVICE_ID_TABLE_DEFINE_BEGIN
- /* T4 adapters:
- */
+ /* T4 adapters */
CH_PCI_ID_TABLE_FENTRY(0x4000), /* T440-dbg */
CH_PCI_ID_TABLE_FENTRY(0x4001), /* T420-cr */
CH_PCI_ID_TABLE_FENTRY(0x4002), /* T422-cr */
@@ -119,8 +118,7 @@ CH_PCI_DEVICE_ID_TABLE_DEFINE_BEGIN
CH_PCI_ID_TABLE_FENTRY(0x4087), /* Custom T440-cr */
CH_PCI_ID_TABLE_FENTRY(0x4088), /* Custom T440 2-xaui, 2-xfi */
- /* T5 adapters:
- */
+ /* T5 adapters */
CH_PCI_ID_TABLE_FENTRY(0x5000), /* T580-dbg */
CH_PCI_ID_TABLE_FENTRY(0x5001), /* T520-cr */
CH_PCI_ID_TABLE_FENTRY(0x5002), /* T522-cr */
@@ -169,8 +167,7 @@ CH_PCI_DEVICE_ID_TABLE_DEFINE_BEGIN
CH_PCI_ID_TABLE_FENTRY(0x509b), /* Custom T540-CR LOM */
CH_PCI_ID_TABLE_FENTRY(0x509c), /* Custom T520-CR*/
- /* T6 adapters:
- */
+ /* T6 adapters */
CH_PCI_ID_TABLE_FENTRY(0x6001),
CH_PCI_ID_TABLE_FENTRY(0x6002),
CH_PCI_ID_TABLE_FENTRY(0x6003),
@@ -184,6 +181,24 @@ CH_PCI_DEVICE_ID_TABLE_DEFINE_BEGIN
CH_PCI_ID_TABLE_FENTRY(0x6011),
CH_PCI_ID_TABLE_FENTRY(0x6014),
CH_PCI_ID_TABLE_FENTRY(0x6015),
+ CH_PCI_ID_TABLE_FENTRY(0x6081),
+
+ /* T7 adapters */
+ { .vendor = 0x1425, .device = 0xd000 }, /* T7 FPGA */
+ CH_PCI_ID_TABLE_FENTRY(0x7000), /* T7-DBG */
+ CH_PCI_ID_TABLE_FENTRY(0x7001), /* T7250 */
+ CH_PCI_ID_TABLE_FENTRY(0x7002), /* S7250 */
+ CH_PCI_ID_TABLE_FENTRY(0x7003), /* T7450 */
+ CH_PCI_ID_TABLE_FENTRY(0x7004), /* S7450 */
+ CH_PCI_ID_TABLE_FENTRY(0x7005), /* T72200 */
+ CH_PCI_ID_TABLE_FENTRY(0x7006), /* S72200 */
+ CH_PCI_ID_TABLE_FENTRY(0x7007), /* T72200-FH */
+ CH_PCI_ID_TABLE_FENTRY(0x7008), /* T71400 */
+ CH_PCI_ID_TABLE_FENTRY(0x7009), /* S7210-BT */
+ CH_PCI_ID_TABLE_FENTRY(0x700a), /* T7450-RC */
+ CH_PCI_ID_TABLE_FENTRY(0x700b), /* T72200-RC */
+ CH_PCI_ID_TABLE_FENTRY(0x700c), /* T72200-FH-RC */
+ CH_PCI_ID_TABLE_FENTRY(0x7080), /* Custom */
CH_PCI_DEVICE_ID_TABLE_DEFINE_END;
#endif /* __T4_PCI_ID_TBL_H__ */
diff --git a/contrib/one-true-awk/FIXES b/contrib/one-true-awk/FIXES
index b3bf38f0aa1c..b876b9ec5ec9 100644
--- a/contrib/one-true-awk/FIXES
+++ b/contrib/one-true-awk/FIXES
@@ -25,6 +25,14 @@ THIS SOFTWARE.
This file lists all bug fixes, changes, etc., made since the
second edition of the AWK book was published in September 2023.
+Aug 04, 2025
+ Fix incorrect divisor in rand() - it was returning
+ even random numbers only. Thanks to Ozan Yigit.
+
+ Fix a syntax issue with /= that caused constants to
+ turn into variables [eg. 42 /= 7]. Thanks to Arnold
+ Robbins.
+
Jan 14, 2025
Fix incorrect error line number issues. unput has
no business managing lineno. Thanks to Ozan Yigit.
diff --git a/contrib/one-true-awk/main.c b/contrib/one-true-awk/main.c
index 361c23e70861..b8053af34b05 100644
--- a/contrib/one-true-awk/main.c
+++ b/contrib/one-true-awk/main.c
@@ -22,7 +22,7 @@ ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
****************************************************************/
-const char *version = "version 20250116";
+const char *version = "version 20250804";
#define DEBUG
#include <stdio.h>
diff --git a/contrib/one-true-awk/run.c b/contrib/one-true-awk/run.c
index eaddfdecbdd3..9bc07a517372 100644
--- a/contrib/one-true-awk/run.c
+++ b/contrib/one-true-awk/run.c
@@ -1567,6 +1567,8 @@ Cell *assign(Node **a, int n) /* a[0] = a[1], a[0] += a[1], etc. */
xf *= yf;
break;
case DIVEQ:
+ if ((x->tval & CON) != 0)
+ FATAL("non-constant required for left side of /=");
if (yf == 0)
FATAL("division by zero in /=");
xf /= yf;
@@ -2188,7 +2190,7 @@ Cell *bltin(Node **a, int n) /* builtin functions. a[0] is type, a[1] is arg lis
/* random() returns numbers in [0..2^31-1]
* in order to get a number in [0, 1), divide it by 2^31
*/
- u = (Awkfloat) random() / (0x7fffffffL + 0x1UL);
+ u = (Awkfloat) random() / RAND_MAX;
break;
case FSRAND:
if (isrec(x)) /* no argument provided */
diff --git a/contrib/tcpdump/print-pfsync.c b/contrib/tcpdump/print-pfsync.c
index 6bf9abaf3903..e7885c4a9e00 100644
--- a/contrib/tcpdump/print-pfsync.c
+++ b/contrib/tcpdump/print-pfsync.c
@@ -53,8 +53,8 @@
static void pfsync_print(netdissect_options *, struct pfsync_header *,
const u_char *, u_int);
static void print_src_dst(netdissect_options *,
- const struct pfsync_state_peer *,
- const struct pfsync_state_peer *, uint8_t);
+ const struct pf_state_peer_export *,
+ const struct pf_state_peer_export *, uint8_t);
static void print_state(netdissect_options *, union pfsync_state_union *, int);
void
@@ -102,6 +102,7 @@ struct pfsync_actions {
static void pfsync_print_clr(netdissect_options *, const void *);
static void pfsync_print_state_1301(netdissect_options *, const void *);
static void pfsync_print_state_1400(netdissect_options *, const void *);
+static void pfsync_print_state_1500(netdissect_options *, const void *);
static void pfsync_print_ins_ack(netdissect_options *, const void *);
static void pfsync_print_upd_c(netdissect_options *, const void *);
static void pfsync_print_upd_req(netdissect_options *, const void *);
@@ -131,6 +132,8 @@ struct pfsync_actions actions[] = {
{ "eof", 0, NULL },
{ "insert", sizeof(struct pfsync_state_1400), pfsync_print_state_1400 },
{ "update", sizeof(struct pfsync_state_1400), pfsync_print_state_1400 },
+ { "insert", sizeof(struct pfsync_state_1500), pfsync_print_state_1500 },
+ { "update", sizeof(struct pfsync_state_1500), pfsync_print_state_1500 },
};
static void
@@ -228,13 +231,22 @@ pfsync_print_state_1301(netdissect_options *ndo, const void *bp)
static void
pfsync_print_state_1400(netdissect_options *ndo, const void *bp)
{
- struct pfsync_state_1301 *st = (struct pfsync_state_1301 *)bp;
+ struct pfsync_state_1400 *st = (struct pfsync_state_1400 *)bp;
fn_print_char(ndo, '\n');
print_state(ndo, (union pfsync_state_union *)st, PFSYNC_MSG_VERSION_1400);
}
static void
+pfsync_print_state_1500(netdissect_options *ndo, const void *bp)
+{
+ struct pfsync_state_1500 *st = (struct pfsync_state_1500 *)bp;
+
+ fn_print_char(ndo, '\n');
+ print_state(ndo, (union pfsync_state_union *)st, PFSYNC_MSG_VERSION_1500);
+}
+
+static void
pfsync_print_ins_ack(netdissect_options *ndo, const void *bp)
{
const struct pfsync_ins_ack *iack = bp;
@@ -330,7 +342,7 @@ print_host(netdissect_options *ndo, struct pf_addr *addr, uint16_t port,
}
static void
-print_seq(netdissect_options *ndo, const struct pfsync_state_peer *p)
+print_seq(netdissect_options *ndo, const struct pf_state_peer_export *p)
{
if (p->seqdiff)
ND_PRINT("[%u + %u](+%u)", ntohl(p->seqlo),
@@ -341,8 +353,8 @@ print_seq(netdissect_options *ndo, const struct pfsync_state_peer *p)
}
static void
-print_src_dst(netdissect_options *ndo, const struct pfsync_state_peer *src,
- const struct pfsync_state_peer *dst, uint8_t proto)
+print_src_dst(netdissect_options *ndo, const struct pf_state_peer_export *src,
+ const struct pf_state_peer_export *dst, uint8_t proto)
{
if (proto == IPPROTO_TCP) {
@@ -390,7 +402,7 @@ print_src_dst(netdissect_options *ndo, const struct pfsync_state_peer *src,
static void
print_state(netdissect_options *ndo, union pfsync_state_union *s, int version)
{
- struct pfsync_state_peer *src, *dst;
+ struct pf_state_peer_export *src, *dst;
struct pfsync_state_key *sk, *nk;
int min, sec;
diff --git a/contrib/tnftp/src/ftp.1 b/contrib/tnftp/src/ftp.1
index 34a88c23520b..e9f09ddca8cc 100644
--- a/contrib/tnftp/src/ftp.1
+++ b/contrib/tnftp/src/ftp.1
@@ -58,7 +58,7 @@
.\"
.\" @(#)ftp.1 8.3 (Berkeley) 10/9/94
.\"
-.Dd May 10, 2008
+.Dd September 25, 2025
.Dt FTP 1
.Os
.Sh NAME
@@ -2325,8 +2325,7 @@ for an example of how to make this automatic.
.Sh SEE ALSO
.Xr getservbyname 3 ,
.Xr editrc 5 ,
-.Xr services 5 ,
-.Xr ftpd 8
+.Xr services 5
.Sh STANDARDS
.Nm
attempts to be compliant with:
diff --git a/contrib/tzcode/Makefile b/contrib/tzcode/Makefile
index 0087b4596515..2130582c2deb 100644
--- a/contrib/tzcode/Makefile
+++ b/contrib/tzcode/Makefile
@@ -137,7 +137,7 @@ TIME_T_ALTERNATIVES_TAIL = int_least32_t.ck uint_least32_t.ck \
uint_least64_t.ck
# What kind of TZif data files to generate. (TZif is the binary time
-# zone data format that zic generates; see Internet RFC 8536.)
+# zone data format that zic generates; see Internet RFC 9636.)
# If you want only POSIX time, with time values interpreted as
# seconds since the epoch (not counting leap seconds), use
# REDO= posix_only
@@ -255,6 +255,7 @@ LDLIBS=
# -DHAVE_UNISTD_H=0 if <unistd.h> does not work*
# -DHAVE_UTMPX_H=0 if <utmpx.h> does not work*
# -Dlocale_t=XXX if your system uses XXX instead of locale_t
+# -DMKTIME_MIGHT_OVERFLOW if mktime might fail due to time_t overflow
# -DPORT_TO_C89 if tzcode should also run on mostly-C89 platforms+
# Typically it is better to use a later standard. For example,
# with GCC 4.9.4 (2016), prefer '-std=gnu11' to '-DPORT_TO_C89'.
@@ -262,7 +263,7 @@ LDLIBS=
# feature (integers at least 64 bits wide) and maybe more.
# -DRESERVE_STD_EXT_IDS if your platform reserves standard identifiers
# with external linkage, e.g., applications cannot define 'localtime'.
-# -Dssize_t=long on hosts like MS-Windows that lack ssize_t
+# -Dssize_t=int on hosts like MS-Windows that lack ssize_t
# -DSUPPORT_C89=0 if the tzcode library should not support C89 callers
# Although -DSUPPORT_C89=0 might work around latent bugs in callers,
# it does not conform to POSIX.
@@ -285,7 +286,7 @@ LDLIBS=
# This mishandles some past timestamps, as US DST rules have changed.
# It also mishandles settings like TZ='EET-2EEST' for eastern Europe,
# as Europe and US DST rules differ.
-# -DTZNAME_MAXIMUM=N to limit time zone abbreviations to N bytes (default 255)
+# -DTZNAME_MAXIMUM=N to limit time zone abbreviations to N bytes (default 254)
# -DUNINIT_TRAP if reading uninitialized storage can cause problems
# other than simply getting garbage data
# -DUSE_LTZ=0 to build zdump with the system time zone library
@@ -319,7 +320,8 @@ GCC_DEBUG_FLAGS = -DGCC_LINT -g3 -O3 \
$(GCC_INSTRUMENT) \
-Wall -Wextra \
-Walloc-size-larger-than=100000 -Warray-bounds=2 \
- -Wbad-function-cast -Wbidi-chars=any,ucn -Wcast-align=strict -Wdate-time \
+ -Wbad-function-cast -Wbidi-chars=any,ucn -Wcast-align=strict -Wcast-qual \
+ -Wdate-time \
-Wdeclaration-after-statement -Wdouble-promotion \
-Wduplicated-branches -Wduplicated-cond -Wflex-array-member-not-at-end \
-Wformat=2 -Wformat-overflow=2 -Wformat-signedness -Wformat-truncation \
@@ -336,7 +338,7 @@ GCC_DEBUG_FLAGS = -DGCC_LINT -g3 -O3 \
-Wsuggest-attribute=noreturn -Wsuggest-attribute=pure \
-Wtrampolines -Wundef -Wunused-macros -Wuse-after-free=3 \
-Wvariadic-macros -Wvla -Wwrite-strings \
- -Wno-format-nonliteral -Wno-sign-compare
+ -Wno-format-nonliteral -Wno-sign-compare -Wno-type-limits
#
# If your system has a "GMT offset" field in its "struct tm"s
# (or if you decide to add such a field in your system's "time.h" file),
@@ -614,8 +616,8 @@ TZS_YEAR= 2050
TZS_CUTOFF_FLAG= -c $(TZS_YEAR)
TZS= to$(TZS_YEAR).tzs
TZS_NEW= to$(TZS_YEAR)new.tzs
-TZS_DEPS= $(YDATA) asctime.c localtime.c \
- private.h tzfile.h zdump.c zic.c
+TZS_DEPS= $(YDATA) localtime.c private.h \
+ strftime.c tzfile.h zdump.c zic.c
TZDATA_DIST = $(COMMON) $(DATA) $(MISC)
# EIGHT_YARDS is just a yard short of the whole ENCHILADA.
EIGHT_YARDS = $(TZDATA_DIST) $(DOCS) $(SOURCES) tzdata.zi
@@ -855,10 +857,10 @@ tzselect: tzselect.ksh version
chmod +x $@.out
mv $@.out $@
-check: check_mild back.ck
+check: check_mild back.ck now.ck
check_mild: check_web check_zishrink \
character-set.ck white-space.ck links.ck mainguard.ck \
- name-lengths.ck now.ck slashed-abbrs.ck sorted.ck \
+ name-lengths.ck slashed-abbrs.ck sorted.ck \
tables.ck ziguard.ck tzs.ck
# True if UTF8_LOCALE does not work;
@@ -1103,7 +1105,7 @@ set-timestamps.out: $(EIGHT_YARDS)
touch -md @1 test.out; then \
rm -f test.out && \
for file in $$files; do \
- if git diff --quiet $$file; then \
+ if git diff --quiet HEAD $$file; then \
time=$$(TZ=UTC0 git log -1 \
--format='tformat:%cd' \
--date='format:%Y-%m-%dT%H:%M:%SZ' \
@@ -1354,13 +1356,13 @@ long-long.ck unsigned.ck: $(VERSION_DEPS)
zonenames: tzdata.zi
@$(AWK) '/^Z/ { print $$2 } /^L/ { print $$3 }' tzdata.zi
-asctime.o: private.h tzfile.h
+asctime.o: private.h
date.o: private.h
difftime.o: private.h
-localtime.o: private.h tzfile.h tzdir.h
-strftime.o: private.h tzfile.h
-zdump.o: version.h
-zic.o: private.h tzfile.h tzdir.h version.h
+localtime.o: private.h tzdir.h tzfile.h
+strftime.o: localtime.c private.h tzdir.h tzfile.h
+zdump.o: private.h version.h
+zic.o: private.h tzdir.h tzfile.h version.h
.PHONY: ALL INSTALL all
.PHONY: check check_mild check_time_t_alternatives
diff --git a/contrib/tzcode/NEWS b/contrib/tzcode/NEWS
index 83b8b8c8d39c..8c0771641ef0 100644
--- a/contrib/tzcode/NEWS
+++ b/contrib/tzcode/NEWS
@@ -1,5 +1,108 @@
News for the tz database
+Release 2025b - 2025-03-22 13:40:46 -0700
+
+ Briefly:
+ New zone for Aysén Region in Chile which moves from -04/-03 to -03.
+
+ Changes to future timestamps
+
+ Chile's Aysén Region moves from -04/-03 to -03 year-round, joining
+ Magallanes Region. The region will not change its clocks on
+ 2025-04-05 at 24:00, diverging from America/Santiago and creating a
+ new zone America/Coyhaique. (Thanks to Yonathan Dossow.) Model
+ this as a change to standard offset effective 2025-03-20.
+
+ Changes to past timestamps
+
+ Iran switched from +04 to +0330 on 1978-11-10 at 24:00, not at
+ year end. (Thanks to Roozbeh Pournader.)
+
+ Changes to code
+
+ 'zic -l TIMEZONE -d . -l /some/other/file/system' no longer
+ attempts to create an incorrect symlink, and no longer has a
+ read buffer underflow. (Problem reported by Evgeniy Gorbanev.)
+
+
+Release 2025a - 2025-01-15 10:47:24 -0800
+
+ Briefly:
+ Paraguay adopted permanent -03 starting spring 2024.
+ Improve pre-1991 data for the Philippines.
+ Etc/Unknown is now reserved.
+
+ Changes to future timestamps
+
+ Paraguay stopped changing its clocks after the spring-forward
+ transition on 2024-10-06, so it is now permanently at -03.
+ (Thanks to Heitor David Pinto and Even Scharning.)
+ This affects timestamps starting 2025-03-22, as well as the
+ obsolescent tm_isdst flags starting 2024-10-15.
+
+ Changes to past timestamps
+
+ Correct timestamps for the Philippines before 1900, and from 1937
+ through 1990. (Thanks to P Chan for the heads-up and citations.)
+ This includes adjusting local mean time before 1899; fixing
+ transitions in September 1899, January 1937, and June 1954; adding
+ transitions in December 1941, November 1945, March and September
+ 1977, and May and July 1990; and removing incorrect transitions in
+ March and September 1978.
+
+ Changes to data
+
+ Add zone1970.tab lines for the Concordia and Eyre Bird Observatory
+ research stations. (Thanks to Derick Rethans and Jule Dabars.)
+
+ Changes to code
+
+ strftime %s now generates the correct numeric string even when the
+ represented number does not fit into time_t. This is better than
+ generating the numeric equivalent of (time_t) -1, as strftime did
+ in TZDB releases 96a (when %s was introduced) through 2020a and in
+ releases 2022b through 2024b. It is also better than failing and
+ returning 0, as strftime did in releases 2020b through 2022a.
+
+ strftime now outputs an invalid conversion specifier as-is,
+ instead of eliding the leading '%', which confused debugging.
+
+ An invalid TZ now generates the time zone abbreviation "-00", not
+ "UTC", to help the user see that an error has occurred. (Thanks
+ to Arthur David Olson for suggesting a "wrong result".)
+
+ mktime and timeoff no longer incorrectly fail merely because a
+ struct tm component near INT_MIN or INT_MAX overflows when a
+ lower-order component carries into it.
+
+ TZNAME_MAXIMUM, the maximum number of bytes in a proleptic TZ
+ string's time zone abbreviation, now defaults to 254 not 255.
+ This helps reduce the size of internal state from 25480 to 21384
+ on common platforms. This change should not be a problem, as
+ nobody uses such long "abbreviations" and the longstanding tzcode
+ maximum was 16 until release 2023a. For those who prefer no
+ arbitrary limits, you can now specify TZNAME_MAXIMUM values up to
+ PTRDIFF_MAX, a limit forced by C anyway; formerly tzcode silently
+ misbehaved unless TZNAME_MAXIMUM was less than INT_MAX.
+
+ tzset and related functions no longer leak a file descriptor if
+ another thread forks or execs at about the same time and if the
+ platform has O_CLOFORK and O_CLOEXEC respectively. Also, the
+ functions no longer let a TZif file become a controlling terminal.
+
+ 'zdump -' now reads TZif data from /dev/stdin.
+ (From a question by Arthur David Olson.)
+
+ Changes to documentation
+
+ The name Etc/Unknown is now reserved: it will not be used by TZDB.
+ This is for compatibility with CLDR, which uses the string
+ "Etc/Unknown" for an unknown or invalid timezone. (Thanks to
+ Justin Grant, Mark Davis, and Guy Harris.)
+
+ Cite Internet RFC 9636, which obsoletes RFC 8536 for TZif format.
+
+
Release 2024b - 2024-09-04 12:27:47 -0700
Briefly:
@@ -116,7 +219,7 @@ Release 2024b - 2024-09-04 12:27:47 -0700
Changes to commentary
Commentary about historical transitions in Portugal and her former
- colonies has been expanded with links to many relevant legislation.
+ colonies has been expanded with links to relevant legislation.
(Thanks to Tim Parenti.)
@@ -204,10 +307,10 @@ Release 2023d - 2023-12-21 20:02:24 -0800
changing its time zone from -01/+00 to -02/-01 at the same moment
as the spring-forward transition. Its clocks will therefore not
spring forward as previously scheduled. The time zone change
- reverts to its common practice before 1981.
+ reverts to its common practice before 1981. (Thanks to Jule Dabars.)
Fix predictions for DST transitions in Palestine in 2072-2075,
- correcting a typo introduced in 2023a.
+ correcting a typo introduced in 2023a. (Thanks to Jule Dabars.)
Changes to past and future timestamps
diff --git a/contrib/tzcode/asctime.c b/contrib/tzcode/asctime.c
index ebb90a1cc84d..491d23bf73ac 100644
--- a/contrib/tzcode/asctime.c
+++ b/contrib/tzcode/asctime.c
@@ -7,38 +7,16 @@
/*
** Avoid the temptation to punt entirely to strftime;
+** strftime can behave badly when tm components are out of range, and
** the output of strftime is supposed to be locale specific
** whereas the output of asctime is supposed to be constant.
*/
/*LINTLIBRARY*/
-#include "namespace.h"
#include "private.h"
-#include "un-namespace.h"
#include <stdio.h>
-/*
-** All years associated with 32-bit time_t values are exactly four digits long;
-** some years associated with 64-bit time_t values are not.
-** Vintage programs are coded for years that are always four digits long
-** and may assume that the newline always lands in the same place.
-** For years that are less than four digits, we pad the output with
-** leading zeroes to get the newline in the traditional place.
-** The -4 ensures that we get four characters of output even if
-** we call a strftime variant that produces fewer characters for some years.
-** This conforms to recent ISO C and POSIX standards, which say behavior
-** is undefined when the year is less than 1000 or greater than 9999.
-*/
-static char const ASCTIME_FMT[] = "%s %s%3d %.2d:%.2d:%.2d %-4s\n";
-/*
-** For years that are more than four digits we put extra spaces before the year
-** so that code trying to overwrite the newline won't end up overwriting
-** a digit within a year and truncating the year (operating on the assumption
-** that no output is better than wrong output).
-*/
-static char const ASCTIME_FMT_B[] = "%s %s%3d %.2d:%.2d:%.2d %s\n";
-
enum { STD_ASCTIME_BUF_SIZE = 26 };
/*
** Big enough for something such as
@@ -52,14 +30,24 @@ enum { STD_ASCTIME_BUF_SIZE = 26 };
*/
static char buf_asctime[2*3 + 5*INT_STRLEN_MAXIMUM(int) + 7 + 2 + 1 + 1];
-/* A similar buffer for ctime.
- C89 requires that they be the same buffer.
- This requirement was removed in C99, so support it only if requested,
- as support is more likely to lead to bugs in badly written programs. */
-#if SUPPORT_C89
-# define buf_ctime buf_asctime
-#else
-static char buf_ctime[sizeof buf_asctime];
+/* On pre-C99 platforms, a snprintf substitute good enough for us. */
+#if !HAVE_SNPRINTF
+# include <stdarg.h>
+ATTRIBUTE_FORMAT((printf, 3, 4)) static int
+my_snprintf(char *s, size_t size, char const *format, ...)
+{
+ int n;
+ va_list args;
+ char stackbuf[sizeof buf_asctime];
+ va_start(args, format);
+ n = vsprintf(stackbuf, format, args);
+ va_end (args);
+ if (0 <= n && n < size)
+ memcpy (s, stackbuf, n + 1);
+ return n;
+}
+# undef snprintf
+# define snprintf my_snprintf
#endif
/* Publish asctime_r and ctime_r only when supporting older POSIX. */
@@ -84,14 +72,19 @@ asctime_r(struct tm const *restrict timeptr, char *restrict buf)
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
- const char * wn;
- const char * mn;
- char year[INT_STRLEN_MAXIMUM(int) + 2];
- char result[sizeof buf_asctime];
+ register const char * wn;
+ register const char * mn;
+ int year, mday, hour, min, sec;
+ long long_TM_YEAR_BASE = TM_YEAR_BASE;
+ size_t bufsize = (buf == buf_asctime
+ ? sizeof buf_asctime : STD_ASCTIME_BUF_SIZE);
if (timeptr == NULL) {
+ strcpy(buf, "??? ??? ?? ??:??:?? ????\n");
+ /* Set errno now, since strcpy might change it in
+ POSIX.1-2017 and earlier. */
errno = EINVAL;
- return strcpy(buf, "??? ??? ?? ??:??:?? ????\n");
+ return buf;
}
if (timeptr->tm_wday < 0 || timeptr->tm_wday >= DAYSPERWEEK)
wn = "???";
@@ -99,25 +92,41 @@ asctime_r(struct tm const *restrict timeptr, char *restrict buf)
if (timeptr->tm_mon < 0 || timeptr->tm_mon >= MONSPERYEAR)
mn = "???";
else mn = mon_name[timeptr->tm_mon];
- /*
- ** Use strftime's %Y to generate the year, to avoid overflow problems
- ** when computing timeptr->tm_year + TM_YEAR_BASE.
- ** Assume that strftime is unaffected by other out-of-range members
- ** (e.g., timeptr->tm_mday) when processing "%Y".
- */
- strftime(year, sizeof year, "%Y", timeptr);
- /*
- ** We avoid using snprintf since it's not available on all systems.
- */
- sprintf(result,
- ((strlen(year) <= 4) ? ASCTIME_FMT : ASCTIME_FMT_B),
- wn, mn,
- timeptr->tm_mday, timeptr->tm_hour,
- timeptr->tm_min, timeptr->tm_sec,
- year);
- if (strlen(result) < STD_ASCTIME_BUF_SIZE
- || buf == buf_ctime || buf == buf_asctime)
- return strcpy(buf, result);
+
+ year = timeptr->tm_year;
+ mday = timeptr->tm_mday;
+ hour = timeptr->tm_hour;
+ min = timeptr->tm_min;
+ sec = timeptr->tm_sec;
+
+ /* Vintage programs are coded for years that are always four bytes long
+ and may assume that the newline always lands in the same place.
+ For years that are less than four bytes, pad the output with
+ leading zeroes to get the newline in the traditional place.
+ For years longer than four bytes, put extra spaces before the year
+ so that vintage code trying to overwrite the newline
+ won't overwrite a digit within a year and truncate the year,
+ using the principle that no output is better than wrong output.
+ This conforms to ISO C and POSIX standards, which say behavior
+ is undefined when the year is less than 1000 or greater than 9999.
+
+ Also, avoid overflow when formatting tm_year + TM_YEAR_BASE. */
+
+ if ((year <= LONG_MAX - TM_YEAR_BASE
+ ? snprintf (buf, bufsize,
+ ((-999 - TM_YEAR_BASE <= year
+ && year <= 9999 - TM_YEAR_BASE)
+ ? "%s %s%3d %.2d:%.2d:%.2d %04ld\n"
+ : "%s %s%3d %.2d:%.2d:%.2d %ld\n"),
+ wn, mn, mday, hour, min, sec,
+ year + long_TM_YEAR_BASE)
+ : snprintf (buf, bufsize,
+ "%s %s%3d %.2d:%.2d:%.2d %d%d\n",
+ wn, mn, mday, hour, min, sec,
+ year / 10 + TM_YEAR_BASE / 10,
+ year % 10))
+ < bufsize)
+ return buf;
else {
errno = EOVERFLOW;
return NULL;
@@ -142,5 +151,8 @@ ctime_r(const time_t *timep, char *buf)
char *
ctime(const time_t *timep)
{
- return ctime_r(timep, buf_ctime);
+ /* Do not call localtime_r, as C23 requires ctime to initialize the
+ static storage that localtime updates. */
+ struct tm *tmp = localtime(timep);
+ return tmp ? asctime(tmp) : NULL;
}
diff --git a/contrib/tzcode/date.1 b/contrib/tzcode/date.1
index 01907bc76e2c..3a02e7c2e08a 100644
--- a/contrib/tzcode/date.1
+++ b/contrib/tzcode/date.1
@@ -6,15 +6,13 @@ date \- show and set date and time
.SH SYNOPSIS
.if n .nh
.if n .na
-.ie \n(.g .ds - \f(CR-\fP
-.el .ds - \-
.B date
[
-.B \*-u
+.B \-u
] [
-.B \*-c
+.B \-c
] [
-.B \*-r
+.B \-r
.I seconds
] [
.BI + format
@@ -35,7 +33,7 @@ command
without arguments writes the date and time to the standard output in
the form
.ce 1
-Wed Mar 8 14:54:40 EST 1989
+Sat Mar 8 14:54:40 EST 2025
.br
with
.B EST
@@ -49,99 +47,24 @@ If a command-line argument starts with a plus sign (\c
.q "\fB+\fP" ),
the rest of the argument is used as a
.I format
-that controls what appears in the output.
-In the format, when a percent sign (\c
-.q "\fB%\fP"
-appears,
-it and the character after it are not output,
-but rather identify part of the date or time
-to be output in a particular way
-(or identify a special character to output):
-.nf
-.sp
-.if t .in +.5i
-.if n .in +2
-.ta \w'%M\0\0'u +\w'Wed Mar 8 14:54:40 EST 1989\0\0'u
- Sample output Explanation
-%a Wed Abbreviated weekday name*
-%A Wednesday Full weekday name*
-%b Mar Abbreviated month name*
-%B March Full month name*
-%c Wed Mar 08 14:54:40 1989 Date and time*
-%C 19 Century
-%d 08 Day of month (always two digits)
-%D 03/08/89 Month/day/year (eight characters)
-%e 8 Day of month (leading zero blanked)
-%h Mar Abbreviated month name*
-%H 14 24-hour-clock hour (two digits)
-%I 02 12-hour-clock hour (two digits)
-%j 067 Julian day number (three digits)
-%k 2 12-hour-clock hour (leading zero blanked)
-%l 14 24-hour-clock hour (leading zero blanked)
-%m 03 Month number (two digits)
-%M 54 Minute (two digits)
-%n \\n newline character
-%p PM AM/PM designation
-%r 02:54:40 PM Hour:minute:second AM/PM designation
-%R 14:54 Hour:minute
-%S 40 Second (two digits)
-%t \\t tab character
-%T 14:54:40 Hour:minute:second
-%U 10 Sunday-based week number (two digits)
-%w 3 Day number (one digit, Sunday is 0)
-%W 10 Monday-based week number (two digits)
-%x 03/08/89 Date*
-%X 14:54:40 Time*
-%y 89 Last two digits of year
-%Y 1989 Year in full
-%z -0500 Numeric time zone
-%Z EST Time zone abbreviation
-%+ Wed Mar 8 14:54:40 EST 1989 Default output format*
-.if t .in -.5i
-.if n .in -2
-* The exact output depends on the locale.
-.sp
-.fi
-If a character other than one of those shown above appears after
-a percent sign in the format,
-that following character is output.
-All other characters in the format are copied unchanged to the output;
-a newline character is always added at the end of the output.
-.PP
-In Sunday-based week numbering,
-the first Sunday of the year begins week 1;
-days preceding it are part of
-.q "week 0" .
-In Monday-based week numbering,
-the first Monday of the year begins week 1.
-.PP
-To set the date, use a command line argument with one of the following forms:
-.nf
-.if t .in +.5i
-.if n .in +2
-.ta \w'198903081454\0'u
-1454 24-hour-clock hours (first two digits) and minutes
-081454 Month day (first two digits), hours, and minutes
-03081454 Month (two digits, January is 01), month day, hours, minutes
-8903081454 Year, month, month day, hours, minutes
-0308145489 Month, month day, hours, minutes, year
- (on System V-compatible systems)
-030814541989 Month, month day, hours, minutes, four-digit year
-198903081454 Four-digit year, month, month day, hours, minutes
-.if t .in -.5i
-.if n .in -2
-.fi
-If the century, year, month, or month day is not given,
-the current value is used.
-Any of the above forms may be followed by a period and two digits that give
-the seconds part of the new time; if no seconds are given, zero is assumed.
+that is processed by
+.BR strftime (3)
+to determine what to output;
+a newline character is appended.
+For example, the shell command:
+.ce 1
+date +"%Y\-%m\-%d %H:%M:%S %z"
+.br
+outputs a line like
+.q "2025\-03\-08 14:54:40 \-0500"
+instead.
.PP
These options are available:
.TP
-.BR \*-u " or " \*-c
+.BR \-u " or " \-c
Use Universal Time when setting and showing the date and time.
.TP
-.BI "\*-r " seconds
+.BI "\-r " seconds
Output the date that corresponds to
.I seconds
past the epoch of 1970-01-01 00:00:00 UTC, where
@@ -149,16 +72,13 @@ past the epoch of 1970-01-01 00:00:00 UTC, where
should be an integer, either decimal, octal (leading 0), or
hexadecimal (leading 0x), preceded by an optional sign.
.SH FILES
-.ta \w'/usr/share/zoneinfo/posixrules\0\0'u
+.ta \w'/usr/share/zoneinfo/Etc/UTC\0\0'u
/etc/localtime local timezone file
.br
/usr/lib/locale/\f2L\fP/LC_TIME description of time locale \f2L\fP
.br
/usr/share/zoneinfo timezone directory
.br
-/usr/share/zoneinfo/posixrules default DST rules (obsolete)
-.br
-/usr/share/zoneinfo/GMT for UTC leap seconds
-.PP
-If /usr/share/zoneinfo/GMT is absent,
-UTC leap seconds are loaded from /usr/share/zoneinfo/GMT0 if present.
+/usr/share/zoneinfo/Etc/UTC for UTC leap seconds
+.SH SEE ALSO
+.BR strftime (3).
diff --git a/contrib/tzcode/difftime.c b/contrib/tzcode/difftime.c
index 43af402cdc12..ff78f03c5705 100644
--- a/contrib/tzcode/difftime.c
+++ b/contrib/tzcode/difftime.c
@@ -7,9 +7,7 @@
/*LINTLIBRARY*/
-#include "namespace.h"
#include "private.h" /* for time_t and TYPE_SIGNED */
-#include "un-namespace.h"
/* Return -X as a double. Using this avoids casting to 'double'. */
static double
diff --git a/contrib/tzcode/localtime.c b/contrib/tzcode/localtime.c
index a6ec3d8e4e21..1668475ea646 100644
--- a/contrib/tzcode/localtime.c
+++ b/contrib/tzcode/localtime.c
@@ -13,42 +13,114 @@
/*LINTLIBRARY*/
#define LOCALTIME_IMPLEMENTATION
-#include "namespace.h"
+#ifdef __FreeBSD__
+#include <pthread.h>
+#endif /* __FreeBSD__ */
#ifdef DETECT_TZ_CHANGES
-#ifndef DETECT_TZ_CHANGES_INTERVAL
-#define DETECT_TZ_CHANGES_INTERVAL 61
-#endif
+# ifndef DETECT_TZ_CHANGES_INTERVAL
+# define DETECT_TZ_CHANGES_INTERVAL 61
+# endif
int __tz_change_interval = DETECT_TZ_CHANGES_INTERVAL;
-#include <sys/stat.h>
-#endif
-#include <fcntl.h>
-#if THREAD_SAFE
-#include <pthread.h>
-#endif
+# include <sys/stat.h>
+#endif /* DETECT_TZ_CHANGES */
#include "private.h"
-#include "un-namespace.h"
#include "tzdir.h"
#include "tzfile.h"
-
+#include <fcntl.h>
+#ifdef __FreeBSD__
#include "libc_private.h"
+#endif /* __FreeBSD__ */
+
+#if HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
+#if !defined S_ISREG && defined S_IFREG
+/* Ancient UNIX or recent MS-Windows. */
+# define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG)
+#endif
#if defined THREAD_SAFE && THREAD_SAFE
+# include <pthread.h>
+#ifdef __FreeBSD__
+# define pthread_mutex_lock(l) (__isthreaded ? pthread_mutex_lock(l) : 0)
+# define pthread_mutex_unlock(l) (__isthreaded ? pthread_mutex_unlock(l) : 0)
+#endif /* __FreeBSD__ */
static pthread_mutex_t locallock = PTHREAD_MUTEX_INITIALIZER;
-static int lock(void) {
- if (__isthreaded)
- return _pthread_mutex_lock(&locallock);
- return 0;
-}
-static void unlock(void) {
- if (__isthreaded)
- _pthread_mutex_unlock(&locallock);
-}
+static int lock(void) { return pthread_mutex_lock(&locallock); }
+static void unlock(void) { pthread_mutex_unlock(&locallock); }
#else
static int lock(void) { return 0; }
static void unlock(void) { }
#endif
+/* Unless intptr_t is missing, pacify gcc -Wcast-qual on char const * exprs.
+ Use this carefully, as the casts disable type checking.
+ This is a macro so that it can be used in static initializers. */
+#ifdef INTPTR_MAX
+# define UNCONST(a) ((char *) (intptr_t) (a))
+#else
+# define UNCONST(a) ((char *) (a))
+#endif
+
+/* A signed type wider than int, so that we can add 1900 + tm_mon/12 to tm_year
+ without overflow. The static_assert checks that it is indeed wider
+ than int; if this fails on your platform please let us know. */
+#if INT_MAX < LONG_MAX
+typedef long iinntt;
+# define IINNTT_MIN LONG_MIN
+# define IINNTT_MAX LONG_MAX
+#elif INT_MAX < LLONG_MAX
+typedef long long iinntt;
+# define IINNTT_MIN LLONG_MIN
+# define IINNTT_MAX LLONG_MAX
+#else
+typedef intmax_t iinntt;
+# define IINNTT_MIN INTMAX_MIN
+# define IINNTT_MAX INTMAX_MAX
+#endif
+static_assert(IINNTT_MIN < INT_MIN && INT_MAX < IINNTT_MAX);
+
+/* On platforms where offtime or mktime might overflow,
+ strftime.c defines USE_TIMEX_T to be true and includes us.
+ This tells us to #define time_t to an internal type timex_t that is
+ wide enough so that strftime %s never suffers from integer overflow,
+ and to #define offtime (if TM_GMTOFF is defined) or mktime (otherwise)
+ to a static function that returns the redefined time_t.
+ It also tells us to define only data and code needed
+ to support the offtime or mktime variant. */
+#ifndef USE_TIMEX_T
+# define USE_TIMEX_T false
+#endif
+#if USE_TIMEX_T
+# undef TIME_T_MIN
+# undef TIME_T_MAX
+# undef time_t
+# define time_t timex_t
+# if MKTIME_FITS_IN(LONG_MIN, LONG_MAX)
+typedef long timex_t;
+# define TIME_T_MIN LONG_MIN
+# define TIME_T_MAX LONG_MAX
+# elif MKTIME_FITS_IN(LLONG_MIN, LLONG_MAX)
+typedef long long timex_t;
+# define TIME_T_MIN LLONG_MIN
+# define TIME_T_MAX LLONG_MAX
+# else
+typedef intmax_t timex_t;
+# define TIME_T_MIN INTMAX_MIN
+# define TIME_T_MAX INTMAX_MAX
+# endif
+
+# ifdef TM_GMTOFF
+# undef timeoff
+# define timeoff timex_timeoff
+# undef EXTERN_TIMEOFF
+# else
+# undef mktime
+# define mktime timex_mktime
+# endif
+#endif
+
#ifndef TZ_ABBR_CHAR_SET
# define TZ_ABBR_CHAR_SET \
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 :+-._"
@@ -58,12 +130,23 @@ static void unlock(void) { }
# define TZ_ABBR_ERR_CHAR '_'
#endif /* !defined TZ_ABBR_ERR_CHAR */
-/*
-** Support non-POSIX platforms that distinguish between text and binary files.
-*/
+/* Port to platforms that lack some O_* flags. Unless otherwise
+ specified, the flags are standardized by POSIX. */
#ifndef O_BINARY
-# define O_BINARY 0
+# define O_BINARY 0 /* MS-Windows */
+#endif
+#ifndef O_CLOEXEC
+# define O_CLOEXEC 0
+#endif
+#ifndef O_CLOFORK
+# define O_CLOFORK 0
+#endif
+#ifndef O_IGNORE_CTTY
+# define O_IGNORE_CTTY 0 /* GNU/Hurd */
+#endif
+#ifndef O_NOCTTY
+# define O_NOCTTY 0
#endif
#ifndef WILDABBR
@@ -92,7 +175,10 @@ static void unlock(void) { }
static const char wildabbr[] = WILDABBR;
static char const etc_utc[] = "Etc/UTC";
+
+#if !USE_TIMEX_T || defined TM_ZONE || !defined TM_GMTOFF
static char const *utc = etc_utc + sizeof "Etc/" - 1;
+#endif
/*
** The DST rules to use if TZ has no rules and we can't load TZDEFRULES.
@@ -104,10 +190,31 @@ static char const *utc = etc_utc + sizeof "Etc/" - 1;
# define TZDEFRULESTRING ",M3.2.0,M11.1.0"
#endif
+/* Limit to time zone abbreviation length in proleptic TZ strings.
+ This is distinct from TZ_MAX_CHARS, which limits TZif file contents.
+ It defaults to 254, not 255, so that desigidx_type can be an unsigned char.
+ unsigned char suffices for TZif files, so the only reason to increase
+ TZNAME_MAXIMUM is to support TZ strings specifying abbreviations
+ longer than 254 bytes. There is little reason to do that, though,
+ as strings that long are hardly "abbreviations". */
+#ifndef TZNAME_MAXIMUM
+# define TZNAME_MAXIMUM 254
+#endif
+
+#if TZNAME_MAXIMUM < UCHAR_MAX
+typedef unsigned char desigidx_type;
+#elif TZNAME_MAXIMUM < INT_MAX
+typedef int desigidx_type;
+#elif TZNAME_MAXIMUM < PTRDIFF_MAX
+typedef ptrdiff_t desigidx_type;
+#else
+# error "TZNAME_MAXIMUM too large"
+#endif
+
struct ttinfo { /* time type information */
- int_fast32_t tt_utoff; /* UT offset in seconds */
+ int_least32_t tt_utoff; /* UT offset in seconds */
+ desigidx_type tt_desigidx; /* abbreviation list index */
bool tt_isdst; /* used to set tm_isdst */
- int tt_desigidx; /* abbreviation list index */
bool tt_ttisstd; /* transition is std time */
bool tt_ttisut; /* transition is UT */
};
@@ -126,12 +233,6 @@ static char const UNSPEC[] = "-00";
for ttunspecified to work without crashing. */
enum { CHARS_EXTRA = max(sizeof UNSPEC, 2) - 1 };
-/* Limit to time zone abbreviation length in proleptic TZ strings.
- This is distinct from TZ_MAX_CHARS, which limits TZif file contents. */
-#ifndef TZNAME_MAXIMUM
-# define TZNAME_MAXIMUM 255
-#endif
-
/* A representation of the contents of a TZif file. Ideally this
would have no size limits; the following sizes should suffice for
practical use. This struct should not be too large, as instances
@@ -166,12 +267,14 @@ struct rule {
int_fast32_t r_time; /* transition time of rule */
};
+#ifdef __FreeBSD__
+static void tzset_unlocked_name(char const *);
+#endif /* __FreeBSD__ */
static struct tm *gmtsub(struct state const *, time_t const *, int_fast32_t,
struct tm *);
static bool increment_overflow(int *, int);
static bool increment_overflow_time(time_t *, int_fast32_t);
static int_fast32_t leapcorr(struct state const *, time_t);
-static bool normalize_overflow32(int_fast32_t *, int *, int);
static struct tm *timesub(time_t const *, int_fast32_t, struct state const *,
struct tm *);
static bool tzparse(char const *, struct state *, struct state const *);
@@ -192,9 +295,11 @@ static struct state *const gmtptr = &gmtmem;
# define TZ_STRLEN_MAX 255
#endif /* !defined TZ_STRLEN_MAX */
+#if !USE_TIMEX_T || !defined TM_GMTOFF
static char lcl_TZname[TZ_STRLEN_MAX + 1];
static int lcl_is_set;
-
+#endif
+#ifdef __FreeBSD__
static pthread_once_t gmt_once = PTHREAD_ONCE_INIT;
static pthread_once_t gmtime_once = PTHREAD_ONCE_INIT;
static pthread_key_t gmtime_key;
@@ -205,6 +310,7 @@ static int offtime_key_error;
static pthread_once_t localtime_once = PTHREAD_ONCE_INIT;
static pthread_key_t localtime_key;
static int localtime_key_error;
+#endif /* __FreeBSD__ */
/*
** Section 4.12.3 of X3.159-1989 requires that
@@ -218,27 +324,29 @@ static int localtime_key_error;
** trigger latent bugs in programs.
*/
-#if SUPPORT_C89
+#if !USE_TIMEX_T
+
+# if SUPPORT_C89
static struct tm tm;
#endif
-#if 2 <= HAVE_TZNAME + TZ_TIME_T
-char * tzname[2] = {
- (char *) wildabbr,
- (char *) wildabbr
-};
-#endif
-#if 2 <= USG_COMPAT + TZ_TIME_T
+# if 2 <= HAVE_TZNAME + TZ_TIME_T
+char *tzname[2] = { UNCONST(wildabbr), UNCONST(wildabbr) };
+# endif
+# if 2 <= USG_COMPAT + TZ_TIME_T
long timezone;
int daylight;
-#endif
-#if 2 <= ALTZONE + TZ_TIME_T
+# endif
+# if 2 <= ALTZONE + TZ_TIME_T
long altzone;
+# endif
+
#endif
/* Initialize *S to a value based on UTOFF, ISDST, and DESIGIDX. */
static void
-init_ttinfo(struct ttinfo *s, int_fast32_t utoff, bool isdst, int desigidx)
+init_ttinfo(struct ttinfo *s, int_fast32_t utoff, bool isdst,
+ desigidx_type desigidx)
{
s->tt_utoff = utoff;
s->tt_isdst = isdst;
@@ -302,20 +410,22 @@ detzcode64(const char *const codep)
return result;
}
+#if !USE_TIMEX_T || !defined TM_GMTOFF
+
static void
update_tzname_etc(struct state const *sp, struct ttinfo const *ttisp)
{
-#if HAVE_TZNAME
- tzname[ttisp->tt_isdst] = (char *) &sp->chars[ttisp->tt_desigidx];
-#endif
-#if USG_COMPAT
+# if HAVE_TZNAME
+ tzname[ttisp->tt_isdst] = UNCONST(&sp->chars[ttisp->tt_desigidx]);
+# endif
+# if USG_COMPAT
if (!ttisp->tt_isdst)
timezone = - ttisp->tt_utoff;
-#endif
-#if ALTZONE
+# endif
+# if ALTZONE
if (ttisp->tt_isdst)
altzone = - ttisp->tt_utoff;
-#endif
+# endif
}
/* If STDDST_MASK indicates that SP's TYPE provides useful info,
@@ -346,18 +456,18 @@ settzname(void)
When STDDST_MASK becomes zero we can stop looking. */
int stddst_mask = 0;
-#if HAVE_TZNAME
- tzname[0] = tzname[1] = (char *) (sp ? wildabbr : utc);
+# if HAVE_TZNAME
+ tzname[0] = tzname[1] = UNCONST(sp ? wildabbr : utc);
stddst_mask = 3;
-#endif
-#if USG_COMPAT
+# endif
+# if USG_COMPAT
timezone = 0;
stddst_mask = 3;
-#endif
-#if ALTZONE
+# endif
+# if ALTZONE
altzone = 0;
stddst_mask |= 2;
-#endif
+# endif
/*
** And to get the latest time zone abbreviations into tzname. . .
*/
@@ -367,9 +477,9 @@ settzname(void)
for (i = sp->typecnt - 1; stddst_mask && 0 <= i; i--)
stddst_mask = may_update_tzname_etc(stddst_mask, sp, i);
}
-#if USG_COMPAT
+# if USG_COMPAT
daylight = stddst_mask >> 1 ^ 1;
-#endif
+# endif
}
/* Replace bogus characters in time zone abbreviations.
@@ -396,21 +506,24 @@ scrub_abbrs(struct state *sp)
return 0;
}
+#endif
+
#ifdef DETECT_TZ_CHANGES
/*
- * Determine if there's a change in the timezone since the last time we checked.
+ * Check whether either the time zone name or the file it refers to has
+ * changed since the last time we checked.
* Returns: -1 on error
- * 0 if the timezone has not changed
- * 1 if the timezone has changed
+ * 0 if the time zone has not changed
+ * 1 if the time zone has changed
*/
static int
-change_in_tz(const char *name)
+tzfile_changed(const char *name, int fd)
{
static char old_name[PATH_MAX];
static struct stat old_sb;
struct stat sb;
- if (stat(name, &sb) != 0)
+ if (fstat(fd, &sb) != 0)
return -1;
if (strcmp(name, old_name) != 0) {
@@ -429,9 +542,7 @@ change_in_tz(const char *name)
return 0;
}
-#else /* !DETECT_TZ_CHANGES */
-#define change_in_tz(X) 1
-#endif /* !DETECT_TZ_CHANGES */
+#endif /* DETECT_TZ_CHANGES */
/* Input buffer for data read from a compiled tz file. */
union input_buffer {
@@ -444,8 +555,10 @@ union input_buffer {
+ 4 * TZ_MAX_TIMES];
};
+#ifndef __FreeBSD__
/* TZDIR with a trailing '/' rather than a trailing '\0'. */
static char const tzdirslash[sizeof TZDIR] = TZDIR "/";
+#endif /* !__FreeBSD__ */
/* Local storage needed for 'tzloadbody'. */
union local_storage {
@@ -458,6 +571,7 @@ union local_storage {
struct state st;
} u;
+#ifndef __FreeBSD__
/* The name of the file to be opened. Ideally this would have no
size limits, to support arbitrarily long Zone names.
Limiting Zone names to 1024 bytes should suffice for practical use.
@@ -465,19 +579,31 @@ union local_storage {
file_analysis as that struct is allocated anyway, as the other
union member. */
char fullname[max(sizeof(struct file_analysis), sizeof tzdirslash + 1024)];
+#endif /* !__FreeBSD__ */
};
-/* Load tz data from the file named NAME into *SP. Read extended
- format if DOEXTEND. Use *LSP for temporary storage. Return 0 on
+/* These tzload flags can be ORed together, and fit into 'char'. */
+enum { TZLOAD_FROMENV = 1 }; /* The TZ string came from the environment. */
+enum { TZLOAD_TZSTRING = 2 }; /* Read any newline-surrounded TZ string. */
+
+/* Load tz data from the file named NAME into *SP. Respect TZLOADFLAGS.
+ Use *LSP for temporary storage. Return 0 on
success, an errno value on failure. */
static int
-tzloadbody(char const *name, struct state *sp, bool doextend,
+tzloadbody(char const *name, struct state *sp, char tzloadflags,
union local_storage *lsp)
{
register int i;
register int fid;
register int stored;
register ssize_t nread;
+#ifdef __FreeBSD__
+ struct stat sb;
+ const char *relname;
+ int dd, serrno;
+#else /* !__FreeBSD__ */
+ register bool doaccess;
+#endif /* !__FreeBSD__ */
register union input_buffer *up = &lsp->u.u;
register int tzheadsize = sizeof(struct tzhead);
@@ -487,11 +613,21 @@ tzloadbody(char const *name, struct state *sp, bool doextend,
name = TZDEFAULT;
if (! name)
return EINVAL;
+ tzloadflags &= ~TZLOAD_FROMENV;
}
if (name[0] == ':')
++name;
- if (name[0] != '/') {
+#ifndef __FreeBSD__
+#ifdef SUPPRESS_TZDIR
+ /* Do not prepend TZDIR. This is intended for specialized
+ applications only, due to its security implications. */
+ doaccess = true;
+#else
+ doaccess = name[0] == '/';
+#endif
+ if (!doaccess) {
+ char const *dot;
if (sizeof lsp->fullname - sizeof tzdirslash <= strlen(name))
return ENAMETOOLONG;
@@ -501,34 +637,101 @@ tzloadbody(char const *name, struct state *sp, bool doextend,
memcpy(lsp->fullname, tzdirslash, sizeof tzdirslash);
strcpy(lsp->fullname + sizeof tzdirslash, name);
+ /* Set doaccess if NAME contains a ".." file name
+ component, as such a name could read a file outside
+ the TZDIR virtual subtree. */
+ for (dot = name; (dot = strchr(dot, '.')); dot++)
+ if ((dot == name || dot[-1] == '/') && dot[1] == '.'
+ && (dot[2] == '/' || !dot[2])) {
+ doaccess = true;
+ break;
+ }
+
name = lsp->fullname;
}
- if (doextend) {
- /*
- * Detect if the timezone file has changed. Check
- * 'doextend' to ignore TZDEFRULES; the change_in_tz()
- * function can only keep state for a single file.
- */
- switch (change_in_tz(name)) {
- case -1:
- return errno;
- case 0:
- return 0;
- case 1:
- break;
- }
+ if (doaccess && (tzloadflags & TZLOAD_FROMENV)) {
+ /* Check for security violations and for devices whose mere
+ opening could have unwanted side effects. Although these
+ checks are racy, they're better than nothing and there is
+ no portable way to fix the races. */
+ if (access(name, R_OK) < 0)
+ return errno;
+#ifdef S_ISREG
+ {
+ struct stat st;
+ if (stat(name, &st) < 0)
+ return errno;
+ if (!S_ISREG(st.st_mode))
+ return EINVAL;
+ }
+#endif
+ }
+ fid = open(name, (O_RDONLY | O_BINARY | O_CLOEXEC | O_CLOFORK
+ | O_IGNORE_CTTY | O_NOCTTY));
+#else /* __FreeBSD__ */
+ if ((tzloadflags & TZLOAD_FROMENV) && strcmp(name, TZDEFAULT) == 0)
+ tzloadflags &= ~TZLOAD_FROMENV;
+ relname = name;
+ if (strncmp(relname, TZDIR "/", strlen(TZDIR) + 1) == 0) {
+ relname += strlen(TZDIR) + 1;
+ while (*relname == '/')
+ relname++;
+ }
+ dd = open(TZDIR, O_DIRECTORY | O_SEARCH | O_CLOEXEC);
+ if ((tzloadflags & TZLOAD_FROMENV) && issetugid()) {
+ if (dd < 0)
+ return errno;
+ if (fstatat(dd, relname, &sb, AT_RESOLVE_BENEATH) < 0) {
+ fid = -1;
+ } else if (!S_ISREG(sb.st_mode)) {
+ fid = -1;
+ errno = EINVAL;
+ } else {
+ fid = openat(dd, relname, O_RDONLY | O_CLOEXEC | O_RESOLVE_BENEATH);
+ }
+ } else {
+ if (dd < 0) {
+ relname = name;
+ dd = AT_FDCWD;
+ }
+ fid = openat(dd, relname, O_RDONLY | O_CLOEXEC);
}
- fid = _open(name, O_RDONLY | O_BINARY);
+ if (dd != AT_FDCWD && dd >= 0) {
+ serrno = errno;
+ close(dd);
+ errno = serrno;
+ }
+#endif /* __FreeBSD__ */
if (fid < 0)
return errno;
- nread = _read(fid, up->buf, sizeof up->buf);
+#ifdef DETECT_TZ_CHANGES
+ if (tzloadflags) {
+ /*
+ * Detect if the timezone file has changed. Check tzloadflags
+ * to ignore TZDEFRULES; the tzfile_changed() function can only
+ * keep state for a single file.
+ */
+ switch (tzfile_changed(name, fid)) {
+ case -1:
+ serrno = errno;
+ close(fid);
+ return serrno;
+ case 0:
+ close(fid);
+ return 0;
+ case 1:
+ break;
+ }
+ }
+#endif /* DETECT_TZ_CHANGES */
+ nread = read(fid, up->buf, sizeof up->buf);
if (nread < tzheadsize) {
int err = nread < 0 ? errno : EINVAL;
- _close(fid);
+ close(fid);
return err;
}
- if (_close(fid) < 0)
+ if (close(fid) < 0)
return errno;
for (stored = 4; stored <= 8; stored *= 2) {
char version = up->tzhead.tzh_version[0];
@@ -645,7 +848,7 @@ tzloadbody(char const *name, struct state *sp, bool doextend,
correction must differ from the previous one's by 1
second or less, except that the first correction can be
any value; these requirements are more generous than
- RFC 8536, to allow future RFC extensions. */
+ RFC 9636, to allow future RFC extensions. */
if (! (i == 0
|| (prevcorr < corr
? corr == prevcorr + 1
@@ -696,7 +899,7 @@ tzloadbody(char const *name, struct state *sp, bool doextend,
if (!version)
break;
}
- if (doextend && nread > 2 &&
+ if ((tzloadflags & TZLOAD_TZSTRING) && nread > 2 &&
up->buf[0] == '\n' && up->buf[nread - 1] == '\n' &&
sp->typecnt + 2 <= TZ_MAX_TYPES) {
struct state *ts = &lsp->u.st;
@@ -771,23 +974,23 @@ tzloadbody(char const *name, struct state *sp, bool doextend,
return 0;
}
-/* Load tz data from the file named NAME into *SP. Read extended
- format if DOEXTEND. Return 0 on success, an errno value on failure. */
+/* Load tz data from the file named NAME into *SP. Respect TZLOADFLAGS.
+ Return 0 on success, an errno value on failure. */
static int
-tzload(char const *name, struct state *sp, bool doextend)
+tzload(char const *name, struct state *sp, char tzloadflags)
{
#ifdef ALL_STATE
union local_storage *lsp = malloc(sizeof *lsp);
if (!lsp) {
return HAVE_MALLOC_ERRNO ? errno : ENOMEM;
} else {
- int err = tzloadbody(name, sp, doextend, lsp);
+ int err = tzloadbody(name, sp, tzloadflags, lsp);
free(lsp);
return err;
}
#else
union local_storage ls;
- return tzloadbody(name, sp, doextend, &ls);
+ return tzloadbody(name, sp, tzloadflags, &ls);
#endif
}
@@ -1128,7 +1331,7 @@ tzparse(const char *name, struct state *sp, struct state const *basep)
sp->leapcnt = basep->leapcnt;
memcpy(sp->lsis, basep->lsis, sp->leapcnt * sizeof *sp->lsis);
} else {
- load_ok = tzload(TZDEFRULES, sp, false) == 0;
+ load_ok = tzload(TZDEFRULES, sp, 0) == 0;
if (!load_ok)
sp->leapcnt = 0; /* So, we're off a little. */
}
@@ -1365,13 +1568,16 @@ tzparse(const char *name, struct state *sp, struct state const *basep)
static void
gmtload(struct state *const sp)
{
- if (tzload(etc_utc, sp, true) != 0)
+ if (tzload(etc_utc, sp, TZLOAD_TZSTRING) != 0)
tzparse("UTC0", sp, NULL);
}
#ifdef DETECT_TZ_CHANGES
+/*
+ * Check if the time zone data we have is still fresh.
+ */
static int
-recheck_tzdata()
+tzdata_is_fresh(void)
{
static time_t last_checked;
struct timespec now;
@@ -1387,14 +1593,15 @@ recheck_tzdata()
return 0;
}
-#else /* !DETECT_TZ_CHANGES */
-#define recheck_tzdata() 0
-#endif /* !DETECT_TZ_CHANGES */
+#endif /* DETECT_TZ_CHANGES */
+
+#if !USE_TIMEX_T || !defined TM_GMTOFF
/* Initialize *SP to a value appropriate for the TZ setting NAME.
+ Respect TZLOADFLAGS.
Return 0 on success, an errno value on failure. */
static int
-zoneinit(struct state *sp, char const *name)
+zoneinit(struct state *sp, char const *name, char tzloadflags)
{
if (name && ! name[0]) {
/*
@@ -1409,7 +1616,7 @@ zoneinit(struct state *sp, char const *name)
strcpy(sp->chars, utc);
return 0;
} else {
- int err = tzload(name, sp, true);
+ int err = tzload(name, sp, tzloadflags);
if (err != 0 && name && name[0] != ':' && tzparse(name, sp, NULL))
err = 0;
if (err == 0)
@@ -1419,22 +1626,38 @@ zoneinit(struct state *sp, char const *name)
}
static void
+tzset_unlocked(void)
+{
+ char const *name = getenv("TZ");
+#ifdef __FreeBSD__
+ tzset_unlocked_name(name);
+}
+static void
tzset_unlocked_name(char const *name)
{
+#endif
struct state *sp = lclptr;
int lcl = name ? strlen(name) < sizeof lcl_TZname : -1;
if (lcl < 0
? lcl_is_set < 0
: 0 < lcl_is_set && strcmp(lcl_TZname, name) == 0)
- if (recheck_tzdata() == 0)
- return;
-#ifdef ALL_STATE
+#ifdef DETECT_TZ_CHANGES
+ if (tzdata_is_fresh() == 0)
+#endif /* DETECT_TZ_CHANGES */
+ return;
+# ifdef ALL_STATE
if (! sp)
lclptr = sp = malloc(sizeof *lclptr);
-#endif /* defined ALL_STATE */
+# endif
if (sp) {
- if (zoneinit(sp, name) != 0)
- zoneinit(sp, "");
+ int err = zoneinit(sp, name, TZLOAD_FROMENV | TZLOAD_TZSTRING);
+ if (err != 0) {
+ zoneinit(sp, "", 0);
+ /* Abbreviate with "-00" if there was an error.
+ Do not treat a missing TZDEFAULT file as an error. */
+ if (name || err != ENOENT)
+ strcpy(sp->chars, UNSPEC);
+ }
if (0 < lcl)
strcpy(lcl_TZname, name);
}
@@ -1442,12 +1665,9 @@ tzset_unlocked_name(char const *name)
lcl_is_set = lcl;
}
-static void
-tzset_unlocked(void)
-{
- tzset_unlocked_name(getenv("TZ"));
-}
+#endif
+#if !USE_TIMEX_T
void
tzset(void)
{
@@ -1456,7 +1676,9 @@ tzset(void)
tzset_unlocked();
unlock();
}
+#endif
+#ifdef __FreeBSD__
void
freebsd13_tzsetwall(void)
{
@@ -1468,7 +1690,7 @@ freebsd13_tzsetwall(void)
__sym_compat(tzsetwall, freebsd13_tzsetwall, FBSD_1.0);
__warn_references(tzsetwall,
"warning: tzsetwall() is deprecated, use tzset() instead.");
-
+#endif /* __FreeBSD__ */
static void
gmtcheck(void)
{
@@ -1485,15 +1707,18 @@ gmtcheck(void)
}
unlock();
}
+#ifdef __FreeBSD__
+#define gmtcheck() _once(&gmt_once, gmtcheck)
+#endif
-#if NETBSD_INSPIRED
+#if NETBSD_INSPIRED && !USE_TIMEX_T
timezone_t
tzalloc(char const *name)
{
timezone_t sp = malloc(sizeof *sp);
if (sp) {
- int err = zoneinit(sp, name);
+ int err = zoneinit(sp, name, TZLOAD_TZSTRING);
if (err != 0) {
free(sp);
errno = err;
@@ -1522,6 +1747,8 @@ tzfree(timezone_t sp)
#endif
+#if !USE_TIMEX_T || !defined TM_GMTOFF
+
/*
** The easy way to behave "as if no library function calls" localtime
** is to not call it, so we drop its guts into "localsub", which can be
@@ -1576,14 +1803,14 @@ localsub(struct state const *sp, time_t const *timep, int_fast32_t setname,
return NULL; /* "cannot happen" */
result = localsub(sp, &newt, setname, tmp);
if (result) {
-#if defined ckd_add && defined ckd_sub
+# if defined ckd_add && defined ckd_sub
if (t < sp->ats[0]
? ckd_sub(&result->tm_year,
result->tm_year, years)
: ckd_add(&result->tm_year,
result->tm_year, years))
return NULL;
-#else
+# else
register int_fast64_t newy;
newy = result->tm_year;
@@ -1593,7 +1820,7 @@ localsub(struct state const *sp, time_t const *timep, int_fast32_t setname,
if (! (INT_MIN <= newy && newy <= INT_MAX))
return NULL;
result->tm_year = newy;
-#endif
+# endif
}
return result;
}
@@ -1622,25 +1849,26 @@ localsub(struct state const *sp, time_t const *timep, int_fast32_t setname,
result = timesub(&t, ttisp->tt_utoff, sp, tmp);
if (result) {
result->tm_isdst = ttisp->tt_isdst;
-#ifdef TM_ZONE
- result->TM_ZONE = (char *) &sp->chars[ttisp->tt_desigidx];
-#endif /* defined TM_ZONE */
+# ifdef TM_ZONE
+ result->TM_ZONE = UNCONST(&sp->chars[ttisp->tt_desigidx]);
+# endif
if (setname)
update_tzname_etc(sp, ttisp);
}
return result;
}
+#endif
-#if NETBSD_INSPIRED
+#if !USE_TIMEX_T
+# if NETBSD_INSPIRED
struct tm *
localtime_rz(struct state *restrict sp, time_t const *restrict timep,
struct tm *restrict tmp)
{
return localsub(sp, timep, 0, tmp);
}
-
-#endif
+# endif
static struct tm *
localtime_tzset(time_t const *timep, struct tm *tmp, bool setname)
@@ -1652,45 +1880,47 @@ localtime_tzset(time_t const *timep, struct tm *tmp, bool setname)
}
#ifndef DETECT_TZ_CHANGES
if (setname || !lcl_is_set)
-#endif
+#endif /* DETECT_TZ_CHANGES */
tzset_unlocked();
tmp = localsub(lclptr, timep, setname, tmp);
unlock();
return tmp;
}
+#ifdef __FreeBSD__
static void
localtime_key_init(void)
{
-
- localtime_key_error = _pthread_key_create(&localtime_key, free);
+ localtime_key_error = pthread_key_create(&localtime_key, free);
}
-
+#endif /* __FreeBSD__ */
struct tm *
localtime(const time_t *timep)
{
-#if !SUPPORT_C89
- static struct tm tm;
-#endif
- struct tm *p_tm = &tm;
-
- if (__isthreaded != 0) {
- _pthread_once(&localtime_once, localtime_key_init);
- if (localtime_key_error != 0) {
- errno = localtime_key_error;
- return (NULL);
- }
- if ((p_tm = _pthread_getspecific(localtime_key)) == NULL) {
- if ((p_tm = malloc(sizeof(*p_tm))) == NULL) {
- return (NULL);
- }
- if (_pthread_setspecific(localtime_key, p_tm) != 0) {
- free(p_tm);
- return (NULL);
- }
- }
- }
- return localtime_tzset(timep, p_tm, true);
+# if !SUPPORT_C89
+ static struct tm tm;
+# endif
+#ifdef __FreeBSD__
+ struct tm *p_tm = &tm;
+
+ if (__isthreaded != 0) {
+ pthread_once(&localtime_once, localtime_key_init);
+ if (localtime_key_error != 0) {
+ errno = localtime_key_error;
+ return (NULL);
+ }
+ if ((p_tm = pthread_getspecific(localtime_key)) == NULL) {
+ if ((p_tm = malloc(sizeof(*p_tm))) == NULL) {
+ return (NULL);
+ }
+ if (pthread_setspecific(localtime_key, p_tm) != 0) {
+ free(p_tm);
+ return (NULL);
+ }
+ }
+ }
+#endif /* __FreeBSD__ */
+ return localtime_tzset(timep, p_tm, true);
}
struct tm *
@@ -1698,6 +1928,7 @@ localtime_r(const time_t *restrict timep, struct tm *restrict tmp)
{
return localtime_tzset(timep, tmp, false);
}
+#endif
/*
** gmtsub is to gmtime as localsub is to localtime.
@@ -1716,12 +1947,14 @@ gmtsub(ATTRIBUTE_MAYBE_UNUSED struct state const *sp, time_t const *timep,
** "+xx" or "-xx" if offset is non-zero,
** but this is no time for a treasure hunt.
*/
- tmp->TM_ZONE = ((char *)
- (offset ? wildabbr : gmtptr ? gmtptr->chars : utc));
+ tmp->TM_ZONE = UNCONST(offset ? wildabbr
+ : gmtptr ? gmtptr->chars : utc);
#endif /* defined TM_ZONE */
return result;
}
+#if !USE_TIMEX_T
+
/*
* Re-entrant version of gmtime.
*/
@@ -1729,45 +1962,47 @@ gmtsub(ATTRIBUTE_MAYBE_UNUSED struct state const *sp, time_t const *timep,
struct tm *
gmtime_r(time_t const *restrict timep, struct tm *restrict tmp)
{
- _once(&gmt_once, gmtcheck);
- return gmtsub(gmtptr, timep, 0, tmp);
+ gmtcheck();
+ return gmtsub(gmtptr, timep, 0, tmp);
}
+#ifdef __FreeBSD__
static void
gmtime_key_init(void)
{
-
- gmtime_key_error = _pthread_key_create(&gmtime_key, free);
+ gmtime_key_error = pthread_key_create(&gmtime_key, free);
}
-
+#endif /* __FreeBSD__ */
struct tm *
gmtime(const time_t *timep)
{
-#if !SUPPORT_C89
- static struct tm tm;
-#endif
- struct tm *p_tm = &tm;
-
- if (__isthreaded != 0) {
- _pthread_once(&gmtime_once, gmtime_key_init);
- if (gmtime_key_error != 0) {
- errno = gmtime_key_error;
- return (NULL);
- }
- if ((p_tm = _pthread_getspecific(gmtime_key)) == NULL) {
- if ((p_tm = malloc(sizeof(*p_tm))) == NULL) {
- return (NULL);
- }
- if (_pthread_setspecific(gmtime_key, p_tm) != 0) {
- free(p_tm);
- return (NULL);
- }
- }
- }
- return gmtime_r(timep, p_tm);
+# if !SUPPORT_C89
+ static struct tm tm;
+# endif
+#ifdef __FreeBSD__
+ struct tm *p_tm = &tm;
+
+ if (__isthreaded != 0) {
+ pthread_once(&gmtime_once, gmtime_key_init);
+ if (gmtime_key_error != 0) {
+ errno = gmtime_key_error;
+ return (NULL);
+ }
+ if ((p_tm = pthread_getspecific(gmtime_key)) == NULL) {
+ if ((p_tm = malloc(sizeof(*p_tm))) == NULL) {
+ return (NULL);
+ }
+ if (pthread_setspecific(gmtime_key, p_tm) != 0) {
+ free(p_tm);
+ return (NULL);
+ }
+ }
+ }
+#endif /* __FreeBSD__ */
+ return gmtime_r(timep, p_tm);
}
-#if STD_INSPIRED
+# if STD_INSPIRED
/* This function is obsolescent and may disappear in future releases.
Callers can instead use localtime_rz with a fixed-offset zone. */
@@ -1775,44 +2010,47 @@ gmtime(const time_t *timep)
struct tm *
offtime_r(time_t const *restrict timep, long offset, struct tm *restrict tmp)
{
- _once(&gmt_once, gmtcheck);
- return gmtsub(gmtptr, timep, offset, tmp);
+ gmtcheck();
+ return gmtsub(gmtptr, timep, offset, tmp);
}
+#ifdef __FreeBSD__
static void
offtime_key_init(void)
{
-
- offtime_key_error = _pthread_key_create(&offtime_key, free);
+ offtime_key_error = pthread_key_create(&offtime_key, free);
}
-
+#endif /* __FreeBSD__ */
struct tm *
-offtime(const time_t *timep, long offset)
-{
-#if !SUPPORT_C89
- static struct tm tm;
+offtime(time_t const *timep, long offset)
+{
+# if !SUPPORT_C89
+ static struct tm tm;
+# endif
+#ifdef __FreeBSD__
+ struct tm *p_tm = &tm;
+
+ if (__isthreaded != 0) {
+ pthread_once(&offtime_once, offtime_key_init);
+ if (offtime_key_error != 0) {
+ errno = offtime_key_error;
+ return (NULL);
+ }
+ if ((p_tm = pthread_getspecific(offtime_key)) == NULL) {
+ if ((p_tm = malloc(sizeof(*p_tm))) == NULL) {
+ return (NULL);
+ }
+ if (pthread_setspecific(offtime_key, p_tm) != 0) {
+ free(p_tm);
+ return (NULL);
+ }
+ }
+ }
#endif
- struct tm *p_tm = &tm;
-
- if (__isthreaded != 0) {
- _pthread_once(&offtime_once, offtime_key_init);
- if (offtime_key_error != 0) {
- errno = offtime_key_error;
- return (NULL);
- }
- if ((p_tm = _pthread_getspecific(offtime_key)) == NULL) {
- if ((p_tm = malloc(sizeof(*p_tm))) == NULL) {
- return (NULL);
- }
- if (_pthread_setspecific(offtime_key, p_tm) != 0) {
- free(p_tm);
- return (NULL);
- }
- }
- }
- return offtime_r(timep, offset, p_tm);
+ return offtime_r(timep, offset, p_tm);
}
+# endif
#endif
/*
@@ -1871,7 +2109,7 @@ timesub(const time_t *timep, int_fast32_t offset,
dayoff = offset / SECSPERDAY - corr / SECSPERDAY + rem / SECSPERDAY - 3;
rem %= SECSPERDAY;
/* y = (EPOCH_YEAR
- + floor((tdays + dayoff) / DAYSPERREPEAT) * YEARSPERREPEAT),
+ + floor((tdays + dayoff) / DAYSPERREPEAT) * YEARSPERREPEAT),
sans overflow. But calculate against 1570 (EPOCH_YEAR -
YEARSPERREPEAT) instead of against 1970 so that things work
for localtime values before 1970 when time_t is unsigned. */
@@ -1988,17 +2226,17 @@ increment_overflow(int *ip, int j)
}
static bool
-increment_overflow32(int_fast32_t *const lp, int const m)
+increment_overflow_time_iinntt(time_t *tp, iinntt j)
{
#ifdef ckd_add
- return ckd_add(lp, *lp, m);
+ return ckd_add(tp, *tp, j);
#else
- register int_fast32_t const l = *lp;
-
- if ((l >= 0) ? (m > INT_FAST32_MAX - l) : (m < INT_FAST32_MIN - l))
- return true;
- *lp += m;
- return false;
+ if (j < 0
+ ? (TYPE_SIGNED(time_t) ? *tp < TIME_T_MIN - j : *tp <= -1 - j)
+ : TIME_T_MAX - j < *tp)
+ return true;
+ *tp += j;
+ return false;
#endif
}
@@ -2022,30 +2260,6 @@ increment_overflow_time(time_t *tp, int_fast32_t j)
#endif
}
-static bool
-normalize_overflow(int *const tensptr, int *const unitsptr, const int base)
-{
- register int tensdelta;
-
- tensdelta = (*unitsptr >= 0) ?
- (*unitsptr / base) :
- (-1 - (-1 - *unitsptr) / base);
- *unitsptr -= tensdelta * base;
- return increment_overflow(tensptr, tensdelta);
-}
-
-static bool
-normalize_overflow32(int_fast32_t *tensptr, int *unitsptr, int base)
-{
- register int tensdelta;
-
- tensdelta = (*unitsptr >= 0) ?
- (*unitsptr / base) :
- (-1 - (-1 - *unitsptr) / base);
- *unitsptr -= tensdelta * base;
- return increment_overflow32(tensptr, tensdelta);
-}
-
static int
tmcomp(register const struct tm *const atmp,
register const struct tm *const btmp)
@@ -2090,11 +2304,9 @@ time2sub(struct tm *const tmp,
{
register int dir;
register int i, j;
- register int saved_seconds;
- register int_fast32_t li;
register time_t lo;
register time_t hi;
- int_fast32_t y;
+ iinntt y, mday, hour, min, saved_seconds;
time_t newt;
time_t t;
struct tm yourtm, mytm;
@@ -2102,36 +2314,57 @@ time2sub(struct tm *const tmp,
*okayp = false;
mktmcpy(&yourtm, tmp);
+ min = yourtm.tm_min;
if (do_norm_secs) {
- if (normalize_overflow(&yourtm.tm_min, &yourtm.tm_sec,
- SECSPERMIN))
- return WRONG;
+ min += yourtm.tm_sec / SECSPERMIN;
+ yourtm.tm_sec %= SECSPERMIN;
+ if (yourtm.tm_sec < 0) {
+ yourtm.tm_sec += SECSPERMIN;
+ min--;
+ }
}
- if (normalize_overflow(&yourtm.tm_hour, &yourtm.tm_min, MINSPERHOUR))
- return WRONG;
- if (normalize_overflow(&yourtm.tm_mday, &yourtm.tm_hour, HOURSPERDAY))
- return WRONG;
+
+ hour = yourtm.tm_hour;
+ hour += min / MINSPERHOUR;
+ yourtm.tm_min = min % MINSPERHOUR;
+ if (yourtm.tm_min < 0) {
+ yourtm.tm_min += MINSPERHOUR;
+ hour--;
+ }
+
+ mday = yourtm.tm_mday;
+ mday += hour / HOURSPERDAY;
+ yourtm.tm_hour = hour % HOURSPERDAY;
+ if (yourtm.tm_hour < 0) {
+ yourtm.tm_hour += HOURSPERDAY;
+ mday--;
+ }
+
y = yourtm.tm_year;
- if (normalize_overflow32(&y, &yourtm.tm_mon, MONSPERYEAR))
- return WRONG;
+ y += yourtm.tm_mon / MONSPERYEAR;
+ yourtm.tm_mon %= MONSPERYEAR;
+ if (yourtm.tm_mon < 0) {
+ yourtm.tm_mon += MONSPERYEAR;
+ y--;
+ }
+
/*
** Turn y into an actual year number for now.
** It is converted back to an offset from TM_YEAR_BASE later.
*/
- if (increment_overflow32(&y, TM_YEAR_BASE))
- return WRONG;
- while (yourtm.tm_mday <= 0) {
- if (increment_overflow32(&y, -1))
- return WRONG;
- li = y + (1 < yourtm.tm_mon);
- yourtm.tm_mday += year_lengths[isleap(li)];
+ y += TM_YEAR_BASE;
+
+ while (mday <= 0) {
+ iinntt li = y - (yourtm.tm_mon <= 1);
+ mday += year_lengths[isleap(li)];
+ y--;
}
- while (yourtm.tm_mday > DAYSPERLYEAR) {
- li = y + (1 < yourtm.tm_mon);
- yourtm.tm_mday -= year_lengths[isleap(li)];
- if (increment_overflow32(&y, 1))
- return WRONG;
+ while (DAYSPERLYEAR < mday) {
+ iinntt li = y + (1 < yourtm.tm_mon);
+ mday -= year_lengths[isleap(li)];
+ y++;
}
+ yourtm.tm_mday = mday;
for ( ; ; ) {
i = mon_lengths[isleap(y)][yourtm.tm_mon];
if (yourtm.tm_mday <= i)
@@ -2139,16 +2372,14 @@ time2sub(struct tm *const tmp,
yourtm.tm_mday -= i;
if (++yourtm.tm_mon >= MONSPERYEAR) {
yourtm.tm_mon = 0;
- if (increment_overflow32(&y, 1))
- return WRONG;
+ y++;
}
}
#ifdef ckd_add
if (ckd_add(&yourtm.tm_year, y, -TM_YEAR_BASE))
return WRONG;
#else
- if (increment_overflow32(&y, -TM_YEAR_BASE))
- return WRONG;
+ y -= TM_YEAR_BASE;
if (! (INT_MIN <= y && y <= INT_MAX))
return WRONG;
yourtm.tm_year = y;
@@ -2164,9 +2395,8 @@ time2sub(struct tm *const tmp,
** not in the same minute that a leap second was deleted from,
** which is a safer assumption than using 58 would be.
*/
- if (increment_overflow(&yourtm.tm_sec, 1 - SECSPERMIN))
- return WRONG;
saved_seconds = yourtm.tm_sec;
+ saved_seconds -= SECSPERMIN - 1;
yourtm.tm_sec = SECSPERMIN - 1;
} else {
saved_seconds = yourtm.tm_sec;
@@ -2275,10 +2505,8 @@ time2sub(struct tm *const tmp,
return WRONG;
}
label:
- newt = t + saved_seconds;
- if ((newt < t) != (saved_seconds < 0))
+ if (increment_overflow_time_iinntt(&t, saved_seconds))
return WRONG;
- t = newt;
if (funcp(sp, &t, offset, tmp))
*okayp = true;
return t;
@@ -2323,7 +2551,6 @@ time1(struct tm *const tmp,
errno = EINVAL;
return WRONG;
}
-
if (tmp->tm_isdst > 1)
tmp->tm_isdst = 1;
t = time2(tmp, funcp, sp, offset, &okay);
@@ -2376,27 +2603,22 @@ time1(struct tm *const tmp,
return WRONG;
}
+#if !defined TM_GMTOFF || !USE_TIMEX_T
+
static time_t
mktime_tzname(struct state *sp, struct tm *tmp, bool setname)
{
if (sp)
return time1(tmp, localsub, sp, setname);
else {
- _once(&gmt_once, gmtcheck);
+ gmtcheck();
return time1(tmp, gmtsub, gmtptr, 0);
}
}
-#if NETBSD_INSPIRED
-
-time_t
-mktime_z(struct state *restrict sp, struct tm *restrict tmp)
-{
- return mktime_tzname(sp, tmp, false);
-}
-
-#endif
-
+# if USE_TIMEX_T
+static
+# endif
time_t
mktime(struct tm *tmp)
{
@@ -2412,7 +2634,17 @@ mktime(struct tm *tmp)
return t;
}
-#if STD_INSPIRED
+#endif
+
+#if NETBSD_INSPIRED && !USE_TIMEX_T
+time_t
+mktime_z(struct state *restrict sp, struct tm *restrict tmp)
+{
+ return mktime_tzname(sp, tmp, false);
+}
+#endif
+
+#if STD_INSPIRED && !USE_TIMEX_T
/* This function is obsolescent and may disappear in future releases.
Callers can instead use mktime. */
time_t
@@ -2424,12 +2656,14 @@ timelocal(struct tm *tmp)
}
#endif
-#ifndef EXTERN_TIMEOFF
-# ifndef timeoff
-# define timeoff my_timeoff /* Don't collide with OpenBSD 7.4 <time.h>. */
+#if defined TM_GMTOFF || !USE_TIMEX_T
+
+# ifndef EXTERN_TIMEOFF
+# ifndef timeoff
+# define timeoff my_timeoff /* Don't collide with OpenBSD 7.4 <time.h>. */
+# endif
+# define EXTERN_TIMEOFF static
# endif
-# define EXTERN_TIMEOFF static
-#endif
/* This function is obsolescent and may disappear in future releases.
Callers can instead use mktime_z with a fixed-offset zone. */
@@ -2438,10 +2672,12 @@ timeoff(struct tm *tmp, long offset)
{
if (tmp)
tmp->tm_isdst = 0;
- _once(&gmt_once, gmtcheck);
+ gmtcheck();
return time1(tmp, gmtsub, gmtptr, offset);
}
+#endif
+#if !USE_TIMEX_T
time_t
timegm(struct tm *tmp)
{
@@ -2454,6 +2690,7 @@ timegm(struct tm *tmp)
*tmp = tmcpy;
return t;
}
+#endif
static int_fast32_t
leapcorr(struct state const *sp, time_t t)
@@ -2474,15 +2711,16 @@ leapcorr(struct state const *sp, time_t t)
** XXX--is the below the right way to conditionalize??
*/
-#if STD_INSPIRED
+#if !USE_TIMEX_T
+# if STD_INSPIRED
/* NETBSD_INSPIRED_EXTERN functions are exported to callers if
NETBSD_INSPIRED is defined, and are private otherwise. */
-# if NETBSD_INSPIRED
-# define NETBSD_INSPIRED_EXTERN
-# else
-# define NETBSD_INSPIRED_EXTERN static
-# endif
+# if NETBSD_INSPIRED
+# define NETBSD_INSPIRED_EXTERN
+# else
+# define NETBSD_INSPIRED_EXTERN static
+# endif
/*
** IEEE Std 1003.1 (POSIX) says that 536457599
@@ -2508,7 +2746,7 @@ time2posix(time_t t)
}
#ifndef DETECT_TZ_CHANGES
if (!lcl_is_set)
-#endif
+#endif /* DETECT_TZ_CHANGES */
tzset_unlocked();
if (lclptr)
t = time2posix_z(lclptr, t);
@@ -2555,7 +2793,7 @@ posix2time(time_t t)
}
#ifndef DETECT_TZ_CHANGES
if (!lcl_is_set)
-#endif
+#endif /* DETECT_TZ_CHANGES */
tzset_unlocked();
if (lclptr)
t = posix2time_z(lclptr, t);
@@ -2563,17 +2801,13 @@ posix2time(time_t t)
return t;
}
-#endif /* STD_INSPIRED */
+# endif /* STD_INSPIRED */
-#if TZ_TIME_T
+# if TZ_TIME_T
-# if !USG_COMPAT
-# define daylight 0
-# define timezone 0
-# endif
-# if !ALTZONE
-# define altzone 0
-# endif
+# if !USG_COMPAT
+# define timezone 0
+# endif
/* Convert from the underlying system's time_t to the ersatz time_tz,
which is called 'time_t' in this file. Typically, this merely
@@ -2591,9 +2825,9 @@ time(time_t *p)
{
time_t r = sys_time(0);
if (r != (time_t) -1) {
- int_fast32_t offset = EPOCH_LOCAL ? (daylight ? timezone : altzone) : 0;
- if (increment_overflow32(&offset, -EPOCH_OFFSET)
- || increment_overflow_time(&r, offset)) {
+ iinntt offset = EPOCH_LOCAL ? timezone : 0;
+ if (offset < IINNTT_MIN + EPOCH_OFFSET
+ || increment_overflow_time_iinntt(&r, offset - EPOCH_OFFSET)) {
errno = EOVERFLOW;
r = -1;
}
@@ -2603,4 +2837,5 @@ time(time_t *p)
return r;
}
+# endif
#endif
diff --git a/contrib/tzcode/newctime.3 b/contrib/tzcode/newctime.3
index d19fd25b7926..9d09e5a55bd9 100644
--- a/contrib/tzcode/newctime.3
+++ b/contrib/tzcode/newctime.3
@@ -5,43 +5,34 @@
asctime, ctime, difftime, gmtime, localtime, mktime \- convert date and time
.SH SYNOPSIS
.nf
-.ie \n(.g .ds - \f(CR-\fP
-.el .ds - \-
.B #include <time.h>
.PP
-.B [[deprecated]] char *ctime(time_t const *clock);
-.PP
-/* Only in POSIX.1-2017 and earlier. */
-.B char *ctime_r(time_t const *clock, char *buf);
-.PP
-.B double difftime(time_t time1, time_t time0);
-.PP
-.B [[deprecated]] char *asctime(struct tm const *tm);
-.PP
-/* Only in POSIX.1-2017 and earlier. */
-.B "char *asctime_r(struct tm const *restrict tm,"
-.B " char *restrict result);"
-.PP
.B struct tm *localtime(time_t const *clock);
-.PP
.B "struct tm *localtime_r(time_t const *restrict clock,"
.B " struct tm *restrict result);"
-.PP
.B "struct tm *localtime_rz(timezone_t restrict zone,"
.B " time_t const *restrict clock,"
.B " struct tm *restrict result);"
.PP
.B struct tm *gmtime(time_t const *clock);
-.PP
.B "struct tm *gmtime_r(time_t const *restrict clock,"
.B " struct tm *restrict result);"
.PP
.B time_t mktime(struct tm *tm);
-.PP
.B "time_t mktime_z(timezone_t restrict zone,"
.B " struct tm *restrict tm);"
.PP
-.B cc ... \*-ltz
+.B double difftime(time_t time1, time_t time0);
+.PP
+.B [[deprecated]] char *asctime(struct tm const *tm);
+.B [[deprecated]] char *ctime(time_t const *clock);
+.PP
+/* Only in POSIX.1-2017 and earlier. */
+.B char *ctime_r(time_t const *clock, char *buf);
+.B "char *asctime_r(struct tm const *restrict tm,"
+.B " char *restrict result);"
+.PP
+.B cc ... \-ltz
.fi
.SH DESCRIPTION
.ie '\(en'' .ds en \-
@@ -54,82 +45,37 @@ asctime, ctime, difftime, gmtime, localtime, mktime \- convert date and time
\\$3\*(lq\\$1\*(rq\\$2
..
The
-.B ctime
-function
-converts a long integer, pointed to by
-.IR clock ,
-and returns a pointer to a
-string of the form
-.br
-.ce
-.eo
-Thu Nov 24 18:22:48 1986\n\0
-.br
-.ec
-Years requiring fewer than four characters are padded with leading zeroes.
-For years longer than four characters, the string is of the form
-.br
-.ce
-.eo
-Thu Nov 24 18:22:48 81986\n\0
-.ec
-.br
-with five spaces before the year.
-These unusual formats are designed to make it less likely that older
-software that expects exactly 26 bytes of output will mistakenly output
-misleading values for out-of-range years.
-.PP
-The
-.BI * clock
-timestamp represents the time in seconds since 1970-01-01 00:00:00
-Coordinated Universal Time (UTC).
-The POSIX standard says that timestamps must be nonnegative
-and must ignore leap seconds.
-Many implementations extend POSIX by allowing negative timestamps,
-and can therefore represent timestamps that predate the
-introduction of UTC and are some other flavor of Universal Time (UT).
-Some implementations support leap seconds, in contradiction to POSIX.
-.PP
-The
-.B ctime
-function is deprecated starting in C23.
-Callers can use
-.B localtime_r
-and
-.B strftime
-instead.
-.PP
-The
.B localtime
and
.B gmtime
functions
+convert an integer, pointed to by
+.IR clock ,
+and
return pointers to
.q "tm"
structures, described below.
+If the integer is out of range for conversion,
+these functions return a null pointer.
The
.B localtime
function
corrects for the time zone and any time zone adjustments
(such as Daylight Saving Time in the United States).
-.PP
The
.B gmtime
-function
-converts to Coordinated Universal Time.
+function converts to Coordinated Universal Time.
.PP
The
-.B asctime
-function
-converts a time value contained in a
-.q "tm"
-structure to a string,
-as shown in the above example,
-and returns a pointer to the string.
-This function is deprecated starting in C23.
-Callers can use
-.B strftime
-instead.
+.BI * clock
+timestamp represents the time in seconds since 1970-01-01 00:00:00
+Coordinated Universal Time (UTC).
+The POSIX standard says that timestamps must be nonnegative
+and must ignore leap seconds.
+Many implementations extend POSIX by allowing negative timestamps,
+and can therefore represent timestamps that predate the
+introduction of UTC and are some other flavor of Universal Time (UT).
+Some implementations support leap seconds, in contradiction to POSIX.
.PP
The
.B mktime
@@ -204,6 +150,52 @@ returns the difference between two calendar times,
expressed in seconds.
.PP
The
+.B asctime
+function
+converts a time value contained in a
+.q "tm"
+structure to a pointer to a
+string of the form
+.br
+.ce
+.eo
+Thu Nov 24 18:22:48 1986\n\0
+.br
+.ec
+Years requiring fewer than four characters are padded with leading zeroes.
+For years longer than four characters, the string is of the form
+.br
+.ce
+.eo
+Thu Nov 24 18:22:48 81986\n\0
+.ec
+.br
+with five spaces before the year.
+This unusual format is designed to make it less likely that older
+software that expects exactly 26 bytes of output will mistakenly output
+misleading values for out-of-range years.
+This function is deprecated starting in C23.
+Callers can use
+.B strftime
+instead.
+.PP
+The
+.B ctime
+function is equivalent to calliing
+.B localtime
+and then calling
+.B asctime
+on the result.
+Like
+.BR asctime ,
+this function is deprecated starting in C23.
+Callers can use
+.B localtime
+and
+.B strftime
+instead.
+.PP
+The
.BR ctime_r ,
.BR localtime_r ,
.BR gmtime_r ,
diff --git a/contrib/tzcode/newstrftime.3 b/contrib/tzcode/newstrftime.3
index a9997a092d0a..e9a382240ed2 100644
--- a/contrib/tzcode/newstrftime.3
+++ b/contrib/tzcode/newstrftime.3
@@ -40,8 +40,6 @@
strftime \- format date and time
.SH SYNOPSIS
.nf
-.ie \n(.g .ds - \f(CR-\fP
-.el .ds - \-
.B #include <time.h>
.PP
.B "size_t strftime(char *restrict buf, size_t maxsize,"
@@ -93,7 +91,7 @@ If a bracketed member name is followed by
.B strftime
can use the named member even though POSIX.1-2024 does not list it;
if the name is followed by
-.q \*- ,
+.q \- ,
.B strftime
ignores the member even though POSIX.1-2024 lists it
which means portable code should set it.
@@ -139,11 +137,14 @@ is replaced by the locale's appropriate date and time representation.
.IR tm_sec ,
.IR tm_gmtoff ,
.IR tm_zone ,
-.IR tm_isdst \*-].
+.IR tm_isdst \-].
.TP
%D
is equivalent to
.c %m/%d/%y .
+Although used in the United States for current dates,
+this format is ambiguous elsewhere
+and for dates that might involve other centuries.
.RI [ tm_year ,
.IR tm_mon ,
.IR tm_mday ]
@@ -167,6 +168,8 @@ is equivalent to
.TP
%G
is replaced by the ISO 8601 year with century as a decimal number.
+This is the year that includes the greater part of the week.
+(Monday as the first day of a week).
See also the
.c %V
conversion specification.
@@ -176,11 +179,7 @@ conversion specification.
.TP
%g
is replaced by the ISO 8601 year without century as a decimal number [00,99].
-This is the year that includes the greater part of the week.
-(Monday as the first day of a week).
-See also the
-.c %V
-conversion specification.
+Since it omits the century, it is ambiguous for dates.
.RI [ tm_year ,
.IR tm_yday ,
.IR tm_wday ]
@@ -249,9 +248,22 @@ of leap seconds.
is replaced by the number of seconds since the Epoch (see
.BR ctime (3)).
Although %s is reliable in this implementation,
-it can have glitches on other platforms (notably platforms lacking
-.IR tm_gmtoff ),
-so portable code should format a
+it can have glitches on other platforms
+(notably obsolescent platforms lacking
+.I tm_gmtoff
+or where
+.B time_t
+is no wider than int), and POSIX allows
+.B strftime
+to set
+.B errno
+to
+.B EINVAL
+or
+.B EOVERFLOW
+and return 0 if the number of seconds would be negative or out of range for
+.BR time_t .
+Portable code should therefore format a
.B time_t
value directly via something like
.B sprintf
@@ -267,7 +279,7 @@ with "%s".
.IR tm_min ,
.IR tm_sec ,
.IR tm_gmtoff +,
-.IR tm_isdst \*-].
+.IR tm_isdst \-].
.TP
%T
is replaced by the time in the format
@@ -284,7 +296,7 @@ is replaced by the week number of the year (Sunday as the first day of
the week) as a decimal number [00,53].
.RI [ tm_wday ,
.IR tm_yday ,
-.IR tm_year \*-]
+.IR tm_year \-]
.TP
%u
is replaced by the weekday (Monday as the first day of the week)
@@ -318,31 +330,33 @@ as a decimal number [0,6].
.TP
%X
is replaced by the locale's appropriate time representation.
-.RI [ tm_year \*-,
-.IR tm_yday \*-,
-.IR tm_mon \*-,
-.IR tm_mday \*-,
-.IR tm_wday \*-,
+.RI [ tm_year \-,
+.IR tm_yday \-,
+.IR tm_mon \-,
+.IR tm_mday \-,
+.IR tm_wday \-,
.IR tm_hour ,
.IR tm_min ,
.IR tm_sec ,
.IR tm_gmtoff ,
.IR tm_zone ,
-.IR tm_isdst \*-].
+.IR tm_isdst \-].
.TP
%x
is replaced by the locale's appropriate date representation.
+This format can be ambiguous for dates, e.g.,
+it can generate "01/02/03" in the C locale.
.RI [ tm_year ,
.IR tm_yday ,
.IR tm_mon ,
.IR tm_mday ,
.IR tm_wday ,
-.IR tm_hour \*-,
-.IR tm_min \*-,
-.IR tm_sec \*-,
-.IR tm_gmtoff \*-,
-.IR tm_zone \*-,
-.IR tm_isdst \*-].
+.IR tm_hour \-,
+.IR tm_min \-,
+.IR tm_sec \-,
+.IR tm_gmtoff \-,
+.IR tm_zone \-,
+.IR tm_isdst \-].
.TP
%Y
is replaced by the year with century as a decimal number.
@@ -350,28 +364,29 @@ is replaced by the year with century as a decimal number.
.TP
%y
is replaced by the year without century as a decimal number [00,99].
+Since it omits the century, it is ambiguous for dates.
.RI [ tm_year ]
.TP
%Z
is replaced by the time zone abbreviation,
or by the empty string if this is not determinable.
.RI [ tm_zone ,
-.IR tm_isdst \*-]
+.IR tm_isdst \-]
.TP
%z
is replaced by the offset from the Prime Meridian
-in the format +HHMM or \*-HHMM (ISO 8601) as appropriate,
+in the format +HHMM or \-HHMM (ISO 8601) as appropriate,
with positive values representing locations east of Greenwich,
or by the empty string if this is not determinable.
-The numeric time zone abbreviation \*-0000 is used when the time is
+The numeric time zone abbreviation \-0000 is used when the time is
Universal Time
but local time is indeterminate; by convention this is used for
locations while uninhabited, and corresponds to a zero offset when the
time zone abbreviation begins with
-.q "\*-" .
+.q "\-" .
.RI [ tm_gmtoff ,
.IR tm_zone +,
-.IR tm_isdst \*-]
+.IR tm_isdst \-]
.TP
%%
is replaced by a single %.
@@ -418,15 +433,6 @@ This function fails if:
The total number of resulting bytes, including the terminating
NUL character, is more than
.IR maxsize .
-.PP
-This function may fail if:
-.TP
-[EOVERFLOW]
-The format includes an
-.c %s
-conversion and the number of seconds since the Epoch cannot be represented
-in a
-.c time_t .
.SH SEE ALSO
.BR date (1),
.BR getenv (3),
diff --git a/contrib/tzcode/newtzset.3 b/contrib/tzcode/newtzset.3
index 661fb25be098..db6bfa7fa64c 100644
--- a/contrib/tzcode/newtzset.3
+++ b/contrib/tzcode/newtzset.3
@@ -5,8 +5,6 @@
tzset \- initialize time conversion information
.SH SYNOPSIS
.nf
-.ie \n(.g .ds - \f(CR-\fP
-.el .ds - \-
.B #include <time.h>
.PP
.BI "timezone_t tzalloc(char const *" TZ );
@@ -23,7 +21,7 @@ tzset \- initialize time conversion information
.br
.B extern int daylight;
.PP
-.B cc ... \*-ltz
+.B cc ... \-ltz
.fi
.SH DESCRIPTION
.ie '\(en'' .ds en \-
@@ -110,7 +108,7 @@ except a leading colon
digits, comma
.RB ( , ),
ASCII minus
-.RB ( \*- ),
+.RB ( \- ),
ASCII plus
.RB ( + ),
and NUL bytes are allowed.
@@ -150,7 +148,7 @@ daylight saving time is assumed to be one hour ahead of standard time. One or
more digits may be used; the value is always interpreted as a decimal
number. The hour must be between zero and 24, and the minutes (and
seconds) \*(en if present \*(en between zero and 59. If preceded by a
-.q "\*-" ,
+.q "\-" ,
the time zone shall be east of the Prime Meridian; otherwise it shall be
west (which may be indicated by an optional preceding
.q "+" .
@@ -239,7 +237,7 @@ values that directly specify the timezone.
stands for US Eastern Standard
Time (EST), 5 hours behind UT, without daylight saving.
.TP
-.B <+12>\*-12<+13>,M11.1.0,M1.2.1/147
+.B <+12>\-12<+13>,M11.1.0,M1.2.1/147
stands for Fiji time, 12 hours ahead
of UT, springing forward on November's first Sunday at 02:00, and
falling back on January's second Monday at 147:00 (i.e., 03:00 on the
@@ -249,34 +247,34 @@ and daylight saving time are
and
.q "+13".
.TP
-.B IST\*-2IDT,M3.4.4/26,M10.5.0
+.B IST\-2IDT,M3.4.4/26,M10.5.0
stands for Israel Standard Time (IST) and Israel Daylight Time (IDT),
2 hours ahead of UT, springing forward on March's fourth
Thursday at 26:00 (i.e., 02:00 on the first Friday on or after March
23), and falling back on October's last Sunday at 02:00.
.TP
-.B <\*-04>4<\*-03>,J1/0,J365/25
+.B <\-04>4<\-03>,J1/0,J365/25
stands for permanent daylight saving time, 3 hours behind UT with
abbreviation
-.q "\*-03".
+.q "\-03".
There is a dummy fall-back transition on December 31 at 25:00 daylight
saving time (i.e., 24:00 standard time, equivalent to January 1 at
00:00 standard time), and a simultaneous spring-forward transition on
January 1 at 00:00 standard time, so daylight saving time is in effect
all year and the initial
-.B <\*-04>
+.B <\-04>
is a placeholder.
.TP
-.B <\*-03>3<\*-02>,M3.5.0/\*-2,M10.5.0/\*-1
+.B <\-03>3<\-02>,M3.5.0/\-2,M10.5.0/\-1
stands for time in western Greenland, 3 hours behind UT, where clocks
follow the EU rules of
springing forward on March's last Sunday at 01:00 UT (\-02:00 local
time, i.e., 22:00 the previous day) and falling back on October's last
Sunday at 01:00 UT (\-01:00 local time, i.e., 23:00 the previous day).
The abbreviations for standard and daylight saving time are
-.q "\*-03"
+.q "\-03"
and
-.q "\*-02".
+.q "\-02".
.PP
If
.I TZ
diff --git a/contrib/tzcode/private.h b/contrib/tzcode/private.h
index 9c5edff252fd..e6b206690b73 100644
--- a/contrib/tzcode/private.h
+++ b/contrib/tzcode/private.h
@@ -37,6 +37,38 @@
# define SUPPORT_C89 1
#endif
+
+/* The following feature-test macros should be defined before
+ any #include of a system header. */
+
+/* Enable tm_gmtoff, tm_zone, and environ on GNUish systems. */
+#define _GNU_SOURCE 1
+/* Fix asctime_r on Solaris 11. */
+#define _POSIX_PTHREAD_SEMANTICS 1
+/* Enable strtoimax on pre-C99 Solaris 11. */
+#define __EXTENSIONS__ 1
+/* Cause MS-Windows headers to define POSIX names. */
+#define _CRT_DECLARE_NONSTDC_NAMES 1
+/* Prevent MS-Windows headers from defining min and max. */
+#define NOMINMAX 1
+
+/* On GNUish systems where time_t might be 32 or 64 bits, use 64.
+ On these platforms _FILE_OFFSET_BITS must also be 64; otherwise
+ setting _TIME_BITS to 64 does not work. The code does not
+ otherwise rely on _FILE_OFFSET_BITS being 64, since it does not
+ use off_t or functions like 'stat' that depend on off_t. */
+#ifndef _TIME_BITS
+# ifndef _FILE_OFFSET_BITS
+# define _FILE_OFFSET_BITS 64
+# endif
+# if _FILE_OFFSET_BITS == 64
+# define _TIME_BITS 64
+# endif
+#endif
+
+/* End of feature-test macro definitions. */
+
+
#ifndef __STDC_VERSION__
# define __STDC_VERSION__ 0
#endif
@@ -51,6 +83,7 @@
#endif
#if __STDC_VERSION__ < 202311
+# undef static_assert
# define static_assert(cond) extern int static_assert_check[(cond) ? 1 : -1]
#endif
@@ -87,11 +120,11 @@
#if !defined HAVE_GETTEXT && defined __has_include
# if __has_include(<libintl.h>)
-# define HAVE_GETTEXT true
+# define HAVE_GETTEXT 1
# endif
#endif
#ifndef HAVE_GETTEXT
-# define HAVE_GETTEXT false
+# define HAVE_GETTEXT 0
#endif
#ifndef HAVE_INCOMPATIBLE_CTIME_R
@@ -124,20 +157,20 @@
#if !defined HAVE_SYS_STAT_H && defined __has_include
# if !__has_include(<sys/stat.h>)
-# define HAVE_SYS_STAT_H false
+# define HAVE_SYS_STAT_H 0
# endif
#endif
#ifndef HAVE_SYS_STAT_H
-# define HAVE_SYS_STAT_H true
+# define HAVE_SYS_STAT_H 1
#endif
#if !defined HAVE_UNISTD_H && defined __has_include
# if !__has_include(<unistd.h>)
-# define HAVE_UNISTD_H false
+# define HAVE_UNISTD_H 0
# endif
#endif
#ifndef HAVE_UNISTD_H
-# define HAVE_UNISTD_H true
+# define HAVE_UNISTD_H 1
#endif
#ifndef NETBSD_INSPIRED
@@ -149,25 +182,6 @@
# define ctime_r _incompatible_ctime_r
#endif /* HAVE_INCOMPATIBLE_CTIME_R */
-/* Enable tm_gmtoff, tm_zone, and environ on GNUish systems. */
-#define _GNU_SOURCE 1
-/* Fix asctime_r on Solaris 11. */
-#define _POSIX_PTHREAD_SEMANTICS 1
-/* Enable strtoimax on pre-C99 Solaris 11. */
-#define __EXTENSIONS__ 1
-
-/* On GNUish systems where time_t might be 32 or 64 bits, use 64.
- On these platforms _FILE_OFFSET_BITS must also be 64; otherwise
- setting _TIME_BITS to 64 does not work. The code does not
- otherwise rely on _FILE_OFFSET_BITS being 64, since it does not
- use off_t or functions like 'stat' that depend on off_t. */
-#ifndef _FILE_OFFSET_BITS
-# define _FILE_OFFSET_BITS 64
-#endif
-#if !defined _TIME_BITS && _FILE_OFFSET_BITS == 64
-# define _TIME_BITS 64
-#endif
-
/*
** Nested includes
*/
@@ -260,6 +274,10 @@
# endif
#endif
+#ifndef HAVE_SNPRINTF
+# define HAVE_SNPRINTF (!PORT_TO_C89 || 199901 <= __STDC_VERSION__)
+#endif
+
#ifndef HAVE_STRFTIME_L
# if _POSIX_VERSION < 200809
# define HAVE_STRFTIME_L 0
@@ -305,7 +323,7 @@
** stdint.h, even with pre-C99 compilers.
*/
#if !defined HAVE_STDINT_H && defined __has_include
-# define HAVE_STDINT_H true /* C23 __has_include implies C99 stdint.h. */
+# define HAVE_STDINT_H 1 /* C23 __has_include implies C99 stdint.h. */
#endif
#ifndef HAVE_STDINT_H
# define HAVE_STDINT_H \
@@ -375,11 +393,15 @@ typedef int int_fast32_t;
# endif
#endif
+#ifndef INT_LEAST32_MAX
+typedef int_fast32_t int_least32_t;
+#endif
+
#ifndef INTMAX_MAX
# ifdef LLONG_MAX
typedef long long intmax_t;
# ifndef HAVE_STRTOLL
-# define HAVE_STRTOLL true
+# define HAVE_STRTOLL 1
# endif
# if HAVE_STRTOLL
# define strtoimax strtoll
@@ -459,7 +481,7 @@ typedef unsigned long uintmax_t;
hosts, unless compiled with -DHAVE_STDCKDINT_H=0 or with pre-C23 EDG. */
#if !defined HAVE_STDCKDINT_H && defined __has_include
# if __has_include(<stdckdint.h>)
-# define HAVE_STDCKDINT_H true
+# define HAVE_STDCKDINT_H 1
# endif
#endif
#ifdef HAVE_STDCKDINT_H
@@ -554,13 +576,26 @@ typedef unsigned long uintmax_t;
# define ATTRIBUTE_REPRODUCIBLE /* empty */
#endif
+#if HAVE___HAS_C_ATTRIBUTE
+# if __has_c_attribute(unsequenced)
+# define ATTRIBUTE_UNSEQUENCED [[unsequenced]]
+# endif
+#endif
+#ifndef ATTRIBUTE_UNSEQUENCED
+# define ATTRIBUTE_UNSEQUENCED /* empty */
+#endif
+
/* GCC attributes that are useful in tzcode.
+ __attribute__((const)) is stricter than [[unsequenced]],
+ so the latter is an adequate substitute in non-GCC C23 platforms.
__attribute__((pure)) is stricter than [[reproducible]],
so the latter is an adequate substitute in non-GCC C23 platforms. */
#if __GNUC__ < 3
+# define ATTRIBUTE_CONST ATTRIBUTE_UNSEQUENCED
# define ATTRIBUTE_FORMAT(spec) /* empty */
# define ATTRIBUTE_PURE ATTRIBUTE_REPRODUCIBLE
#else
+# define ATTRIBUTE_CONST __attribute__((const))
# define ATTRIBUTE_FORMAT(spec) __attribute__((format spec))
# define ATTRIBUTE_PURE __attribute__((pure))
#endif
@@ -593,6 +628,12 @@ typedef unsigned long uintmax_t;
# define RESERVE_STD_EXT_IDS 0
#endif
+#ifdef time_tz
+# define defined_time_tz true
+#else
+# define defined_time_tz false
+#endif
+
/* If standard C identifiers with external linkage (e.g., localtime)
are reserved and are not already being renamed anyway, rename them
as if compiling with '-Dtime_tz=time_t'. */
@@ -608,9 +649,9 @@ typedef unsigned long uintmax_t;
** typical platforms.
*/
#if defined time_tz || EPOCH_LOCAL || EPOCH_OFFSET != 0
-# define TZ_TIME_T 1
+# define TZ_TIME_T true
#else
-# define TZ_TIME_T 0
+# define TZ_TIME_T false
#endif
#if defined LOCALTIME_IMPLEMENTATION && TZ_TIME_T
@@ -707,7 +748,7 @@ DEPRECATED_IN_C23 char *ctime(time_t const *);
char *asctime_r(struct tm const *restrict, char *restrict);
char *ctime_r(time_t const *, char *);
#endif
-double difftime(time_t, time_t);
+ATTRIBUTE_CONST double difftime(time_t, time_t);
size_t strftime(char *restrict, size_t, char const *restrict,
struct tm const *restrict);
# if HAVE_STRFTIME_L
@@ -729,9 +770,9 @@ void tzset(void);
|| defined __GLIBC__ || defined __tm_zone /* musl */ \
|| defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ \
|| (defined __APPLE__ && defined __MACH__))
-# define HAVE_DECL_TIMEGM true
+# define HAVE_DECL_TIMEGM 1
# else
-# define HAVE_DECL_TIMEGM false
+# define HAVE_DECL_TIMEGM 0
# endif
#endif
#if !HAVE_DECL_TIMEGM && !defined timegm
@@ -771,14 +812,18 @@ extern long altzone;
*/
#ifndef STD_INSPIRED
-# define STD_INSPIRED 0
+# ifdef __NetBSD__
+# define STD_INSPIRED 1
+# else
+# define STD_INSPIRED 0
+# endif
#endif
#if STD_INSPIRED
# if TZ_TIME_T || !defined offtime
struct tm *offtime(time_t const *, long);
# endif
# if TZ_TIME_T || !defined offtime_r
-struct tm *offtime_r(time_t const *, long, struct tm *);
+struct tm *offtime_r(time_t const *restrict, long, struct tm *restrict);
# endif
# if TZ_TIME_T || !defined timelocal
time_t timelocal(struct tm *);
@@ -880,7 +925,7 @@ ATTRIBUTE_PURE time_t time2posix_z(timezone_t, time_t);
default: TIME_T_MAX_NO_PADDING) \
: (time_t) -1)
enum { SIGNED_PADDING_CHECK_NEEDED
- = _Generic((time_t) 0,
+ = _Generic((time_t) 0,
signed char: false, short: false,
int: false, long: false, long long: false,
default: true) };
@@ -927,8 +972,8 @@ static_assert(! TYPE_SIGNED(time_t) || ! SIGNED_PADDING_CHECK_NEEDED
# define UNINIT_TRAP 0
#endif
-/* localtime.c sometimes needs access to timeoff if it is not already public.
- tz_private_timeoff should be used only by localtime.c. */
+/* strftime.c sometimes needs access to timeoff if it is not already public.
+ tz_private_timeoff should be used only by localtime.c and strftime.c. */
#if (!defined EXTERN_TIMEOFF \
&& defined TM_GMTOFF && (200809 < _POSIX_VERSION || ! UNINIT_TRAP))
# ifndef timeoff
diff --git a/contrib/tzcode/strftime.c b/contrib/tzcode/strftime.c
index 755d341fa6d9..487a5234cbc5 100644
--- a/contrib/tzcode/strftime.c
+++ b/contrib/tzcode/strftime.c
@@ -39,8 +39,63 @@
#include <locale.h>
#include <stdio.h>
+/* If true, the value returned by an idealized unlimited-range mktime
+ always fits into an integer type with bounds MIN and MAX.
+ If false, the value might not fit.
+ This macro is usable in #if if its arguments are.
+ Add or subtract 2**31 - 1 for the maximum UT offset allowed in a TZif file,
+ divide by the maximum number of non-leap seconds in a year,
+ divide again by two just to be safe,
+ and account for the tm_year origin (1900) and time_t origin (1970). */
+#define MKTIME_FITS_IN(min, max) \
+ ((min) < 0 \
+ && ((min) + 0x7fffffff) / 366 / 24 / 60 / 60 / 2 + 1970 - 1900 < INT_MIN \
+ && INT_MAX < ((max) - 0x7fffffff) / 366 / 24 / 60 / 60 / 2 + 1970 - 1900)
+
+/* MKTIME_MIGHT_OVERFLOW is true if mktime can fail due to time_t overflow
+ or if it is not known whether mktime can fail,
+ and is false if mktime definitely cannot fail.
+ This macro is usable in #if, and so does not use TIME_T_MAX or sizeof.
+ If the builder has not configured this macro, guess based on what
+ known platforms do. When in doubt, guess true. */
+#ifndef MKTIME_MIGHT_OVERFLOW
+# if defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__
+# include <sys/param.h>
+# endif
+# if ((/* The following heuristics assume native time_t. */ \
+ defined_time_tz) \
+ || ((/* Traditional time_t is 'long', so if 'long' is not wide enough \
+ assume overflow unless we're on a known-safe host. */ \
+ !MKTIME_FITS_IN(LONG_MIN, LONG_MAX)) \
+ && (/* GNU C Library 2.29 (2019-02-01) and later has 64-bit time_t \
+ if __TIMESIZE is 64. */ \
+ !defined __TIMESIZE || __TIMESIZE < 64) \
+ && (/* FreeBSD 12 r320347 (__FreeBSD_version 1200036; 2017-06-26), \
+ and later has 64-bit time_t on all platforms but i386 which \
+ is currently scheduled for end-of-life on 2028-11-30. */ \
+ !defined __FreeBSD_version || __FreeBSD_version < 1200036 \
+ || defined __i386) \
+ && (/* NetBSD 6.0 (2012-10-17) and later has 64-bit time_t. */ \
+ !defined __NetBSD_Version__ || __NetBSD_Version__ < 600000000) \
+ && (/* OpenBSD 5.5 (2014-05-01) and later has 64-bit time_t. */ \
+ !defined OpenBSD || OpenBSD < 201405)))
+# define MKTIME_MIGHT_OVERFLOW 1
+# else
+# define MKTIME_MIGHT_OVERFLOW 0
+# endif
+#endif
+/* Check that MKTIME_MIGHT_OVERFLOW is consistent with time_t's range. */
+static_assert(MKTIME_MIGHT_OVERFLOW
+ || MKTIME_FITS_IN(TIME_T_MIN, TIME_T_MAX));
+
+#if MKTIME_MIGHT_OVERFLOW
+/* Do this after system includes as it redefines time_t, mktime, timeoff. */
+# define USE_TIMEX_T true
+# include "localtime.c"
+#endif
+
#ifndef DEPRECATE_TWO_DIGIT_YEARS
-# define DEPRECATE_TWO_DIGIT_YEARS false
+# define DEPRECATE_TWO_DIGIT_YEARS 0
#endif
struct lc_time_T {
@@ -135,10 +190,6 @@ strftime(char *restrict s, size_t maxsize, char const *restrict format,
tzset();
p = _fmt(format, t, s, s + maxsize, &warn);
- if (!p) {
- errno = EOVERFLOW;
- return 0;
- }
if (DEPRECATE_TWO_DIGIT_YEARS
&& warn != IN_NONE && getenv(YEAR_2000_NAME)) {
fprintf(stderr, "\n");
@@ -170,7 +221,12 @@ _fmt(const char *format, const struct tm *t, char *pt,
if (*format == '%') {
label:
switch (*++format) {
- case '\0':
+ default:
+ /* Output unknown conversion specifiers as-is,
+ to aid debugging. This includes '%' at
+ format end. This conforms to C23 section
+ 7.29.3.5 paragraph 6, which says behavior
+ is undefined here. */
--format;
break;
case 'A':
@@ -327,16 +383,17 @@ label:
tm.tm_mday = t->tm_mday;
tm.tm_mon = t->tm_mon;
tm.tm_year = t->tm_year;
+
+ /* Get the time_t value for TM.
+ Native time_t, or its redefinition
+ by localtime.c above, is wide enough
+ so that this cannot overflow. */
#ifdef TM_GMTOFF
mkt = timeoff(&tm, t->TM_GMTOFF);
#else
tm.tm_isdst = t->tm_isdst;
mkt = mktime(&tm);
#endif
- /* If mktime fails, %s expands to the
- value of (time_t) -1 as a failure
- marker; this is better in practice
- than strftime failing. */
if (TYPE_SIGNED(time_t)) {
intmax_t n = mkt;
sprintf(buf, "%"PRIdMAX, n);
@@ -590,12 +647,6 @@ label:
warnp);
continue;
case '%':
- /*
- ** X311J/88-090 (4.12.3.5): if conversion char is
- ** undefined, behavior is undefined. Print out the
- ** character itself as printf(3) also does.
- */
- default:
break;
}
}
diff --git a/contrib/tzcode/theory.html b/contrib/tzcode/theory.html
index d3573ede0dfb..352a3d87078f 100644
--- a/contrib/tzcode/theory.html
+++ b/contrib/tzcode/theory.html
@@ -123,8 +123,9 @@ If geolocation information is available, a selection interface can
locate the user on a timezone map or prioritize names that are
geographically close. For an example selection interface, see the
<code>tzselect</code> program in the <code><abbr>tz</abbr></code> code.
-The <a href="https://cldr.unicode.org">Unicode Common Locale Data
-Repository</a> contains data that may be useful for other selection
+Unicode's <a href="https://cldr.unicode.org">Common Locale Data
+Repository (<abbr>CLDR</abbr>)</a>
+contains data that may be useful for other selection
interfaces; it maps timezone names like <code>Europe/Prague</code> to
locale-dependent strings like "Prague", "Praha", "Прага", and "布拉格".
</p>
@@ -200,6 +201,8 @@ in decreasing order of importance:
<li>
A name must not be empty, or contain '<code>//</code>', or
start or end with '<code>/</code>'.
+ Also, a name must not be '<code>Etc/Unknown</code>', as
+ <abbr>CLDR</abbr> uses that string for an unknown or invalid timezone.
</li>
<li>
Do not use names that differ only in case.
@@ -220,10 +223,18 @@ in decreasing order of importance:
do not need locations, since local time is not defined there.
</li>
<li>
- If all the clocks in a timezone have agreed since 1970,
- do not bother to include more than one timezone
- even if some of the clocks disagreed before 1970.
+ If all clocks in a region have agreed since 1970,
+ give them just one name even if some of the clocks disagreed before 1970,
+ or reside in different countries or in notable or faraway locations.
Otherwise these tables would become annoyingly large.
+ For example, do not create a name <code>Indian/Crozet</code>
+ as a near-duplicate or alias of <code>Asia/Dubai</code>
+ merely because they are different countries or territories,
+ or their clocks disagreed before 1970, or the
+ <a href="https://en.wikipedia.org/wiki/Crozet_Islands">Crozet Islands</a>
+ are notable in their own right,
+ or the Crozet Islands are not adjacent to other locations
+ that use <code>Asia/Dubai</code>.
</li>
<li>
If boundaries between regions are fluid, such as during a war or
@@ -579,10 +590,10 @@ in decreasing order of importance:
locations while uninhabited.
The leading '<code>-</code>' is a flag that the <abbr>UT</abbr> offset is in
some sense undefined; this notation is derived
- from <a href="https://datatracker.ietf.org/doc/html/rfc3339">Internet
+ from <a href="https://www.rfc-editor.org/rfc/rfc3339">Internet
<abbr title="Request For Comments">RFC</abbr> 3339</a>.
(The abbreviation 'Z' that
- <a href="https://datatracker.ietf.org/doc/html/rfc9557">Internet
+ <a href="https://www.rfc-editor.org/rfc/rfc9557">Internet
<abbr>RFC</abbr> 9557</a> uses for this concept
would violate the POSIX requirement
of at least three characters in an abbreviation.)
@@ -1115,8 +1126,8 @@ However POSIX.1-2024, like earlier POSIX editions, has some limitations:
the name of a file from which time-related information is read.
The file's format is <dfn><abbr>TZif</abbr></dfn>,
a timezone information format that contains binary data; see
- <a href="https://datatracker.ietf.org/doc/html/8536">Internet
- <abbr>RFC</abbr> 8536</a>.
+ <a href="https://www.rfc-editor.org/rfc/9636">Internet
+ <abbr>RFC</abbr> 9636</a>.
The daylight saving time rules to be used for a
particular timezone are encoded in the
<abbr>TZif</abbr> file; the format of the file allows <abbr>US</abbr>,
@@ -1201,12 +1212,15 @@ The vestigial <abbr>API</abbr>s are:
The <code>tm_isdst</code> member is almost never needed and most of
its uses should be discouraged in favor of the abovementioned
<abbr>API</abbr>s.
+ It was intended as an index into the <code>tzname</code> variable,
+ but as mentioned previously that usage is obsolete.
Although it can still be used in arguments to
<code>mktime</code> to disambiguate timestamps near
a <abbr>DST</abbr> transition when the clock jumps back on
platforms lacking <code>tm_gmtoff</code>, this
- disambiguation does not work when standard time itself jumps back,
- which can occur when a location changes to a time zone with a
+ disambiguation works only for proleptic <code>TZ</code> strings;
+ it does not work in general for geographical timezones,
+ such as when a location changes to a time zone with a
lesser <abbr>UT</abbr> offset.
</li>
</ul>
@@ -1223,8 +1237,8 @@ The vestigial <abbr>API</abbr>s are:
Programs that in the past used the <code>timezone</code> function
may now examine <code>localtime(&amp;clock)-&gt;tm_zone</code>
(if <code>TM_ZONE</code> is defined) or
- <code>tzname[localtime(&amp;clock)-&gt;tm_isdst]</code>
- (if <code>HAVE_TZNAME</code> is nonzero) to learn the correct time
+ use <code>strftime</code> with a <code>%Z</code> conversion specification
+ to learn the correct time
zone abbreviation to use.
</li>
<li>
diff --git a/contrib/tzcode/tz-link.html b/contrib/tzcode/tz-link.html
index be2aae5489d6..ad2cc972a4f9 100644
--- a/contrib/tzcode/tz-link.html
+++ b/contrib/tzcode/tz-link.html
@@ -194,9 +194,9 @@ After obtaining the code and data files, see the
The code lets you compile the <code><abbr>tz</abbr></code> source files into
machine-readable binary files, one for each location. The binary files
are in a special format specified by
-<a href="https://datatracker.ietf.org/doc/html/8536">The
+<a href="https://www.rfc-editor.org/rfc/9636">The
Time Zone Information Format (<abbr>TZif</abbr>)</a>
-(Internet <abbr title="Request For Comments">RFC</abbr> 8536).
+(Internet <abbr title="Request For Comments">RFC</abbr> 9636).
The code also lets
you read a <abbr>TZif</abbr> file and interpret timestamps for that
location.</p>
@@ -260,7 +260,7 @@ Studio Code</a>.
</p>
<p>
For further information about updates, please see
-<a href="https://datatracker.ietf.org/doc/html/rfc6557">Procedures for
+<a href="https://www.rfc-editor.org/rfc/rfc6557">Procedures for
Maintaining the Time Zone Database</a> (Internet <abbr>RFC</abbr> 6557).
More detail can be
found in <a href="theory.html">Theory and pragmatics of the
@@ -379,26 +379,26 @@ calculates the current time difference between locations.</li>
<li>The <a href="https://www.ietf.org">Internet Engineering Task Force</a>'s
<a href="https://datatracker.ietf.org/wg/tzdist/charter/">Time Zone Data
Distribution Service (tzdist) working group</a> defined <a
-href="https://datatracker.ietf.org/doc/html/rfc7808">TZDIST</a>
+href="https://www.rfc-editor.org/rfc/rfc7808">TZDIST</a>
(Internet <abbr>RFC</abbr> 7808), a time zone data distribution service,
-along with <a href="https://datatracker.ietf.org/doc/html/rfc7809">CalDAV</a>
+along with <a href="https://www.rfc-editor.org/rfc/rfc7809">CalDAV</a>
(Internet <abbr>RFC</abbr> 7809), a calendar access protocol for
transferring time zone data by reference.
<a href="https://devguide.calconnect.org/Time-Zones/TZDS/">TZDIST
implementations</a> are available.
The <a href="https://www.ietf.org/mailman/listinfo/tzdist-bis">tzdist-bis
mailing list</a> discusses possible extensions.</li>
-<li>The <a href="https://datatracker.ietf.org/doc/html/rfc5545">
+<li>The <a href="https://www.rfc-editor.org/rfc/rfc5545">
Internet Calendaring and Scheduling Core Object Specification
(iCalendar)</a> (Internet <abbr>RFC</abbr> 5445)
covers time zone
data; see its VTIMEZONE calendar component.
The iCalendar format requires specialized parsers and generators; a
-variant <a href="https://datatracker.ietf.org/doc/html/rfc6321">xCal</a>
+variant <a href="https://www.rfc-editor.org/rfc/rfc6321">xCal</a>
(Internet <abbr>RFC</abbr> 6321) uses
<a href="https://www.w3.org/XML/"><abbr
title="Extensible Markup Language">XML</abbr></a> format, and a variant
-<a href="https://datatracker.ietf.org/doc/html/rfc7265">jCal</a>
+<a href="https://www.rfc-editor.org/rfc/rfc7265">jCal</a>
(Internet <abbr>RFC</abbr> 7265)
uses <a href="https://www.json.org/json-en.html"><abbr
title="JavaScript Object Notation">JSON</abbr></a> format.</li>
@@ -935,7 +935,13 @@ with perhaps the best-documented history of clock adjustments.</dd>
<dt>United States</dt>
<dd>The Department of Transportation's <a
href="https://www.transportation.gov/regulations/recent-time-zone-proceedings">Recent
-Time Zone Proceedings</a> lists changes to time zone boundaries.</dd>
+Time Zone Proceedings</a> lists changes to
+official written time zone boundaries, and its <a
+href="https://geodata.bts.gov/datasets/usdot::time-zones/about">Time
+Zones dataset</a> maps current boundaries.
+These boundaries are only for standard time, so the current map puts
+all of Arizona in one time zone even though part of Arizona
+observes <abbr>DST</abbr> and part does not.</dd>
<dt>Uruguay</dt>
<dd>The Oceanography, Hydrography, and Meteorology Service of the Uruguayan
Navy (SOHMA) publishes an annual <a
@@ -979,6 +985,14 @@ a Sleep Research Society position statement</a>.
doi:<a href="https://doi.org/10.1093/sleep/zsac236">10.1093/sleep/zsac236</a>.
After reviewing the scientific literature, the Sleep Research Society
advocates permanent standard time due to its health benefits.
+<li>Neumann P, von Blanckenburg K. <a
+href="https://journals.sagepub.com/doi/full/10.1177/0961463X241310562">What
+time will it be? A comprehensive literature review on daylight saving time</a>.
+<em>Time Soc</em>. 2025-01-21.
+doi:<a href="https://doi.org/10.1177/0961463X241310562">10.1177/0961463X241310562</a>.
+This reviews DST's effects on electricity, health, crime, road safety,
+and the economy, focusing on research since 2010, and concludes that
+year-round standard time is preferable overall.
<li>Rishi MA, Cheng JY, Strang AR <em>et al</em>.
<a href="https://jcsm.aasm.org/doi/10.5664/jcsm.10898">Permanent standard time
is the optimal choice for health and safety:
@@ -994,7 +1008,8 @@ should we abolish Daylight Saving Time?</a>
<em>J Biol Rhythms.</em> 2019;34(3):227&ndash;230.
doi:<a href="https://doi.org/10.1177/0748730419854197">10.1177/0748730419854197</a>.
The Society for Research on Biological Rhythms
-opposes DST changes and permanent DST, and advocates that governments adopt
+opposes <abbr>DST</abbr> changes and permanent <abbr>DST</abbr>,
+and advocates that governments adopt
"permanent Standard Time for the health and safety of their citizens".</li>
</ul>
</section>
@@ -1023,7 +1038,7 @@ title="Institute of Electrical and Electronics Engineers">IEEE</abbr> 1588)
can achieve submicrosecond clock accuracy on a local area network
with special-purpose hardware.</li>
<li><a
-href="https://datatracker.ietf.org/doc/html/rfc4833">Timezone
+href="https://www.rfc-editor.org/rfc/rfc4833">Timezone
Options for <abbr title="Dynamic Host Configuration Protocol">DHCP</abbr></a>
(Internet <abbr>RFC</abbr> 4833)
specifies a <a
@@ -1105,7 +1120,7 @@ the abovementioned <abbr>NTP</abbr> implementations, <a
href="https://github.com/google/unsmear">supports</a> conversion between
<abbr>UTC</abbr> and smeared <abbr>POSIX</abbr> timestamps, and is used by major
cloud service providers. However, according to
-<a href="https://datatracker.ietf.org/doc/html/rfc8633#section-3.7.1">&sect;3.7.1 of
+<a href="https://www.rfc-editor.org/rfc/rfc8633#section-3.7.1">&sect;3.7.1 of
Network Time Protocol Best Current Practices</a>
(Internet <abbr>RFC</abbr> 8633), leap smearing is not suitable for
applications requiring accurate <abbr>UTC</abbr> or civil time,
@@ -1165,16 +1180,16 @@ interchange &ndash; Part 1: Basic rules</em></a>.</li>
<a href="https://www.w3.org/TR/xmlschema/#dateTime"><abbr>XML</abbr>
Schema: Datatypes &ndash; dateTime</a> specifies a format inspired by
<abbr>ISO</abbr> 8601 that is in common use in <abbr>XML</abbr> data.</li>
-<li><a href="https://datatracker.ietf.org/doc/html/rfc5322#section-3.3">&sect;3.3 of
+<li><a href="https://www.rfc-editor.org/rfc/rfc5322#section-3.3">&sect;3.3 of
Internet Message Format</a> (Internet <abbr>RFC</abbr> 5322)
specifies the time notation used in email and <a
href="https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol"><abbr>HTTP</abbr></a>
headers.</li>
<li>
-<a href="https://datatracker.ietf.org/doc/html/rfc3339">Date and Time
+<a href="https://www.rfc-editor.org/rfc/rfc3339">Date and Time
on the Internet: Timestamps</a> (Internet <abbr>RFC</abbr> 3339)
specifies an <abbr>ISO</abbr> 8601 profile for use in new Internet protocols.
-An extension, <a href="https://datatracker.ietf.org/doc/html/rfc9557">Date
+An extension, <a href="https://www.rfc-editor.org/rfc/rfc9557">Date
and Time on the Internet: Timestamps with Additional Information</a>
(Internet <abbr>RFC</abbr> 9557) extends this profile
to let you specify the <code><abbr>tzdb</abbr></code> timezone of a timestamp
diff --git a/contrib/tzcode/tzfile.5 b/contrib/tzcode/tzfile.5
index 63bda4114388..66d169fc5302 100644
--- a/contrib/tzcode/tzfile.5
+++ b/contrib/tzcode/tzfile.5
@@ -11,7 +11,7 @@ The timezone information files used by
.Xr tzset 3
are found under
.Pa /usr/share/zoneinfo .
-These files use the format described in Internet RFC 8536.
+These files use the format described in Internet RFC 9636.
Each file is a sequence of 8-bit bytes.
In a file, a binary integer is represented by a sequence of one or
more bytes in network order (bigendian, or high-order byte first),
@@ -107,7 +107,7 @@ and
serves as an index into the array of time zone abbreviation bytes
that follow the
.Vt ttinfo
-entries in the file; if the designated string is "\*-00", the
+entries in the file; if the designated string is "\-00", the
.Vt ttinfo
entry is a placeholder indicating that local time is unspecified.
The
@@ -128,7 +128,7 @@ The byte strings can overlap if one is a suffix of the other.
The encoding of these strings is not specified.
.It Va tzh_leapcnt
pairs of four-byte values, written in network byte order;
-the first value of each pair gives the nonnegative time
+the first value of each pair gives the non-negative time
(as returned by
.Xr time 3 )
at which a leap second occurs or at which the leap second table expires;
@@ -141,7 +141,7 @@ Each pair denotes one leap second, either positive or negative,
except that if the last pair has the same correction as the previous one,
the last pair denotes the leap second table's expiration time.
Each leap second is at the end of a UTC calendar month.
-The first leap second has a nonnegative occurrence time,
+The first leap second has a non-negative occurrence time,
and is a positive leap second if and only if its correction is positive;
the correction for each leap second after the first differs
from the previous leap second by either 1 for a positive leap second,
@@ -168,7 +168,7 @@ The standard/wall and UT/local indicators were designed for
transforming a TZif file's transition times into transitions appropriate
for another time zone specified via
a proleptic TZ string that lacks rules.
-For example, when TZ="EET\*-2EEST" and there is no TZif file "EET\*-2EEST",
+For example, when TZ="EET\-2EEST" and there is no TZif file "EET\-2EEST",
the idea was to adapt the transition times from a TZif file with the
well-known name "posixrules" that is present only for this purpose and
is a copy of the file "Europe/Brussels", a file with a different UT offset.
@@ -177,7 +177,7 @@ the default rules are installation-dependent, and no implementation
is known to support this feature for timestamps past 2037,
so users desiring (say) Greek time should instead specify
TZ="Europe/Athens" for better historical coverage, falling back on
-TZ="EET\*-2EEST,M3.5.0/3,M10.5.0/4" if POSIX conformance is required
+TZ="EET\-2EEST,M3.5.0/3,M10.5.0/4" if POSIX conformance is required
and older timestamps need not be handled accurately.
.Pp
The
@@ -203,7 +203,7 @@ after the last transition time stored in the file
or for all instants if the file has no transitions.
The TZ string is empty (i.e., nothing between the newlines)
if there is no proleptic representation for such instants.
-If nonempty, the TZ string must agree with the local time
+If non-empty, the TZ string must agree with the local time
type after the last transition time if present in the eight-byte data;
for example, given the string
.Dq "WET0WEST,M3.5.0/1,M10.5.0"
@@ -218,7 +218,7 @@ the earliest transition time.
For version-3-format timezone files, a TZ string (see
.Xr newtzset 3 )
may use the following POSIX.1-2024 extensions to POSIX.1-2017:
-First, as in TZ="<\*-02>2<\*-01>,M3.5.0/\*-1,M10.5.0/0",
+First, as in TZ="<\-02>2<\-01>,M3.5.0/\-1,M10.5.0/0",
the hours part of its transition times may be signed and range from
\-167 through 167 instead of being limited to unsigned values
from 0 through 24.
@@ -275,7 +275,7 @@ time did not exist (possibly with an error indication).
Time zone designations should consist of at least three (3)
and no more than six (6) ASCII characters from the set of
alphanumerics,
-.Dq "\*-" ,
+.Dq "\-" ,
and
.Dq "+" .
This is for compatibility with POSIX requirements for
@@ -300,16 +300,16 @@ through 60 instead of the usual 59; the UTC offset is unaffected.
This section documents common problems in reading or writing TZif files.
Most of these are problems in generating TZif files for use by
older readers.
-The goals of this section are:
+The goals of this section are to help:
.Bl -bullet
.It
-to help TZif writers output files that avoid common
+TZif writers output files that avoid common
pitfalls in older or buggy TZif readers,
.It
-to help TZif readers avoid common pitfalls when reading
+TZif readers avoid common pitfalls when reading
files generated by future TZif writers, and
.It
-to help any future specification authors see what sort of
+any future specification authors see what sort of
problems arise when the TZif format is changed.
.El
.Pp
@@ -320,9 +320,9 @@ reader was designed for.
When complete compatibility was not achieved, an attempt was
made to limit glitches to rarely used timestamps and allow
simple partial workarounds in writers designed to generate
-new-version data useful even for older-version readers.
+newer-version data useful even for older-version readers.
This section attempts to document these compatibility issues and
-workarounds, as well as to document other common bugs in
+workarounds as well as documenting other common bugs in
readers.
.Pp
Interoperability problems with TZif include the following:
@@ -355,15 +355,15 @@ for two time zones east, e.g.,
for a time zone with a never-used standard time (XXX, \-03)
and negative daylight saving time (EDT, \-04) all year.
Alternatively,
-as a partial workaround a writer can substitute standard time
+as a partial workaround, a writer can substitute standard time
for the next time zone east \(en e.g.,
.Dq "AST4"
for permanent
Atlantic Standard Time (\-04).
.It
-Some readers designed for version 2 or 3, and that require strict
-conformance to RFC 8536, reject version 4 files whose leap second
-tables are truncated at the start or that end in expiration times.
+Some readers designed for version 2 or 3 and that require strict
+conformance to RFC 9636 reject version 4 files whose leap second
+tables are truncated at the start or end in expiration times.
.It
Some readers ignore the footer, and instead predict future
timestamps from the time type of the last transition.
@@ -378,7 +378,7 @@ and even for current timestamps it can fail for settings like
TZ="Africa/Casablanca". This corresponds to a TZif file
containing explicit transitions through the year 2087,
followed by a footer containing the TZ string
-.Dq <+01>\*-1 ,
+.Dq <+01>\-1 ,
which should be used only for timestamps after the last
explicit transition.
.It
@@ -389,7 +389,7 @@ As a partial workaround, a writer can output a dummy (no-op)
first transition at an early time.
.It
Some readers mishandle timestamps before the first
-transition that has a timestamp not less than \-2**31.
+transition that has a timestamp that is not less than \-2**31.
Readers that support only 32-bit timestamps are likely to be
more prone to this problem, for example, when they process
64-bit transitions only some of which are representable in 32
@@ -401,7 +401,7 @@ Some readers mishandle a transition if its timestamp has
the minimum possible signed 64-bit value.
Timestamps less than \-2**59 are not recommended.
.It
-Some readers mishandle TZ strings that
+Some readers mishandle proleptic TZ strings that
contain
.Dq "<"
or
@@ -418,9 +418,9 @@ non-ASCII characters.
These characters are not recommended.
.It
Some readers may mishandle time zone abbreviations that
-contain fewer than 3 or more than 6 characters, or that
+contain fewer than 3 or more than 6 characters or that
contain ASCII characters other than alphanumerics,
-.Dq "\*-",
+.Dq "\-",
and
.Dq "+".
These abbreviations are not recommended.
@@ -430,7 +430,7 @@ daylight-saving time UT offsets that are less than the UT
offsets for the corresponding standard time.
These readers do not support locations like Ireland, which
uses the equivalent of the TZ string
-.Dq "IST\*-1GMT0,M10.5.0,M3.5.0/1",
+.Dq "IST\-1GMT0,M10.5.0,M3.5.0/1",
observing standard time
(IST, +01) in summer and daylight saving time (GMT, +00) in winter.
As a partial workaround, a writer can output data for the
@@ -443,7 +443,7 @@ abbreviations correctly.
.It
Some readers generate ambiguous timestamps for positive leap seconds
that occur when the UTC offset is not a multiple of 60 seconds.
-For example, in a timezone with UTC offset +01:23:45 and with
+For example, with UTC offset +01:23:45 and
a positive leap second 78796801 (1972-06-30 23:59:60 UTC), some readers will
map both 78796800 and 78796801 to 01:23:45 local time the next day
instead of mapping the latter to 01:23:46, and they will map 78796815 to
@@ -462,15 +462,15 @@ Developers of distributed applications should keep this
in mind if they need to deal with pre-1970 data.
.It
Some readers mishandle timestamps before the first
-transition that has a nonnegative timestamp.
+transition that has a non-negative timestamp.
Readers that do not support negative timestamps are likely to
be more prone to this problem.
.It
Some readers mishandle time zone abbreviations like
-.Dq "\*-08"
+.Dq "\-08"
that contain
-.Dq "+" ,
-.Dq "\*-" ,
+.Dq "+",
+.Dq "\-",
or digits.
.It
Some readers mishandle UT offsets that are out of the
@@ -479,7 +479,7 @@ support locations like Kiritimati that are outside this
range.
.It
Some readers mishandle UT offsets in the range [\-3599, \-1]
-seconds from UT, because they integer-divide the offset by
+seconds from UT because they integer-divide the offset by
3600 to get 0 and then display the hour part as
.Dq "+00" .
.It
@@ -498,8 +498,8 @@ of one hour, or of 15 minutes, or of 1 minute.
.%A P. Eggert
.%A K. Murchison
.%T "The Time Zone Information Format (TZif)"
-.%R RFC 8536
-.%D February 2019
-.%U https://datatracker.ietf.org/doc/html/rfc8536
-.%U https://doi.org/10.17487/RFC8536
+.%R RFC 9636
+.%D October 2024
+.%U https://datatracker.ietf.org/doc/html/rfc9636
+.%U https://doi.org/10.17487/RFC9636
.Re
diff --git a/contrib/tzcode/tzfile.h b/contrib/tzcode/tzfile.h
index c09f39465914..55867b5c260c 100644
--- a/contrib/tzcode/tzfile.h
+++ b/contrib/tzcode/tzfile.h
@@ -28,7 +28,7 @@
#endif /* !defined TZDEFRULES */
-/* See Internet RFC 8536 for more details about the following format. */
+/* See Internet RFC 9636 for more details about the following format. */
/*
** Each file begins with. . .
diff --git a/contrib/tzcode/tzselect.8 b/contrib/tzcode/tzselect.8
index ee031614f3ed..b83f702d5e0d 100644
--- a/contrib/tzcode/tzselect.8
+++ b/contrib/tzcode/tzselect.8
@@ -4,8 +4,6 @@
.SH NAME
tzselect \- select a timezone
.SH SYNOPSIS
-.ie \n(.g .ds - \f(CR-\fP
-.el .ds - \-
.ds d " degrees
.ds m " minutes
.ds s " seconds
@@ -20,15 +18,15 @@ tzselect \- select a timezone
.\}
.B tzselect
[
-.B \*-c
+.B \-c
.I coord
] [
-.B \*-n
+.B \-n
.I limit
] [
-.B \*-\*-help
+.B \-\-help
] [
-.B \*-\*-version
+.B \-\-version
]
.SH DESCRIPTION
The
@@ -40,7 +38,7 @@ The output is suitable as a value for the TZ environment variable.
All interaction with the user is done via standard input and standard error.
.SH OPTIONS
.TP
-.BI "\*-c " coord
+.BI "\-c " coord
Instead of asking for continent and then country and then city,
ask for selection from time zones whose largest cities
are closest to the location with geographical coordinates
@@ -70,27 +68,27 @@ seconds, with any trailing fractions represent fractional minutes or
.I SS
is present) seconds. The decimal point is that of the current locale.
For example, in the (default) C locale,
-.B "\*-c\ +40.689\*-074.045"
+.B "\-c\ +40.689\-074.045"
specifies 40.689\*d\*_N, 74.045\*d\*_W,
-.B "\*-c\ +4041.4\*-07402.7"
+.B "\-c\ +4041.4\-07402.7"
specifies 40\*d\*_41.4\*m\*_N, 74\*d\*_2.7\*m\*_W, and
-.B "\*-c\ +404121\*-0740240"
+.B "\-c\ +404121\-0740240"
specifies 40\*d\*_41\*m\*_21\*s\*_N, 74\*d\*_2\*m\*_40\*s\*_W.
If
.I coord
is not one of the documented forms, the resulting behavior is unspecified.
.TP
-.BI "\*-n " limit
+.BI "\-n " limit
When
-.B \*-c
+.B \-c
is used, display the closest
.I limit
locations (default 10).
.TP
-.B "\*-\*-help"
+.B "\-\-help"
Output help information and exit.
.TP
-.B "\*-\*-version"
+.B "\-\-version"
Output version information and exit.
.SH "ENVIRONMENT VARIABLES"
.TP
diff --git a/contrib/tzcode/version b/contrib/tzcode/version
index 699e50d4d38e..ef468adcecf9 100644
--- a/contrib/tzcode/version
+++ b/contrib/tzcode/version
@@ -1 +1 @@
-2024b
+2025b
diff --git a/contrib/tzcode/zdump.8 b/contrib/tzcode/zdump.8
index 7a78f6b9c040..c5cb092db16f 100644
--- a/contrib/tzcode/zdump.8
+++ b/contrib/tzcode/zdump.8
@@ -28,6 +28,14 @@ The
program prints the current time in each
.Ar timezone
named on the command line.
+A
+.Ar timezone
+of
+.Li -
+is treated as if it were
+.Pa /dev/stdin ;
+this can be used to pipe TZif data into
+.Nm .
.Pp
The following options are available:
.Bl -tag -width indent
@@ -106,7 +114,7 @@ then a line
where
.Ar string
is a double-quoted string giving the timezone, a second line
-.Dq "\*- \*- \fIinterval\fP"
+.Dq "\- \- \fIinterval\fP"
describing the time interval before the first transition if any, and
zero or more following lines
.Dq "\fIdate time interval\fP",
@@ -138,11 +146,11 @@ the seconds are omitted if they are zero, and
the minutes are also omitted if they are also zero.
Positive UT
offsets are east of Greenwich.
-The UT offset \*-00 denotes a UT
+The UT offset \-00 denotes a UT
placeholder in areas where the actual offset is unspecified; by
convention, this occurs when the UT offset is zero and the time zone
abbreviation begins with
-.Dq "-"
+.Dq "\-"
or is
.Dq "zzz".
.Pp
diff --git a/contrib/tzcode/zdump.c b/contrib/tzcode/zdump.c
index a42c337e6d44..a8a8f5aa42ca 100644
--- a/contrib/tzcode/zdump.c
+++ b/contrib/tzcode/zdump.c
@@ -14,10 +14,6 @@
#include "private.h"
#include <stdio.h>
-#ifndef HAVE_SNPRINTF
-# define HAVE_SNPRINTF (!PORT_TO_C89 || 199901 <= __STDC_VERSION__)
-#endif
-
#ifndef HAVE_LOCALTIME_R
# define HAVE_LOCALTIME_R 1
#endif
@@ -148,17 +144,6 @@ sumsize(ptrdiff_t a, ptrdiff_t b)
size_overflow();
}
-/* Return the size of of the string STR, including its trailing NUL.
- Report an error and exit if this would exceed INDEX_MAX which means
- pointer subtraction wouldn't work. */
-static ptrdiff_t
-xstrsize(char const *str)
-{
- size_t len = strlen(str);
- if (len < INDEX_MAX)
- return len + 1;
- size_overflow();
-}
/* Return a pointer to a newly allocated buffer of size SIZE, exiting
on failure. SIZE should be positive. */
@@ -266,7 +251,7 @@ tzalloc(char const *val)
static ptrdiff_t fakeenv0size;
void *freeable = NULL;
char **env = fakeenv, **initial_environ;
- ptrdiff_t valsize = xstrsize(val);
+ ptrdiff_t valsize = strlen(val) + 1;
if (fakeenv0size < valsize) {
char **e = environ, **to;
ptrdiff_t initial_nenvptrs = 1; /* Counting the trailing NULL pointer. */
@@ -427,7 +412,7 @@ saveabbr(char **buf, ptrdiff_t *bufalloc, struct tm const *tmp)
if (HAVE_LOCALTIME_RZ)
return ab;
else {
- ptrdiff_t absize = xstrsize(ab);
+ ptrdiff_t absize = strlen(ab) + 1;
if (*bufalloc < absize) {
free(*buf);
@@ -489,6 +474,7 @@ main(int argc, char *argv[])
register time_t cuthitime;
time_t now;
bool iflag = false;
+ size_t arglenmax = 0;
cutlotime = absolute_min_time;
cuthitime = absolute_max_time;
@@ -588,15 +574,21 @@ main(int argc, char *argv[])
now = time(NULL);
now |= !now;
}
- longest = 0;
for (i = optind; i < argc; i++) {
size_t arglen = strlen(argv[i]);
- if (longest < arglen)
- longest = min(arglen, INT_MAX);
+ if (arglenmax < arglen)
+ arglenmax = arglen;
}
+ if (!HAVE_SETENV && INDEX_MAX <= arglenmax)
+ size_overflow();
+ longest = min(arglenmax, INT_MAX - 2);
for (i = optind; i < argc; ++i) {
- timezone_t tz = tzalloc(argv[i]);
+ /* Treat "-" as standard input on platforms with /dev/stdin.
+ It's not worth the bother of supporting "-" on other
+ platforms, as that would need temp files. */
+ timezone_t tz = tzalloc(strcmp(argv[i], "-") == 0
+ ? "/dev/stdin" : argv[i]);
char const *ab;
time_t t;
struct tm tm, newtm;
@@ -697,7 +689,7 @@ yeartot(intmax_t y)
return absolute_max_time;
seconds = diff400 * SECSPER400YEARS;
years = diff400 * 400;
- } else {
+ } else {
seconds = isleap(myy) ? SECSPERLYEAR : SECSPERNYEAR;
years = 1;
}
@@ -928,13 +920,10 @@ showextrema(timezone_t tz, char *zone, time_t lo, struct tm *lotmp, time_t hi)
}
}
-#if HAVE_SNPRINTF
-# define my_snprintf snprintf
-#else
+/* On pre-C99 platforms, a snprintf substitute good enough for us. */
+#if !HAVE_SNPRINTF
# include <stdarg.h>
-
-/* A substitute for snprintf that is good enough for zdump. */
-static int
+ATTRIBUTE_FORMAT((printf, 3, 4)) static int
my_snprintf(char *s, size_t size, char const *format, ...)
{
int n;
@@ -962,6 +951,7 @@ my_snprintf(char *s, size_t size, char const *format, ...)
va_end(args);
return n;
}
+# define snprintf my_snprintf
#endif
/* Store into BUF, of size SIZE, a formatted local time taken from *TM.
@@ -976,10 +966,10 @@ format_local_time(char *buf, ptrdiff_t size, struct tm const *tm)
{
int ss = tm->tm_sec, mm = tm->tm_min, hh = tm->tm_hour;
return (ss
- ? my_snprintf(buf, size, "%02d:%02d:%02d", hh, mm, ss)
+ ? snprintf(buf, size, "%02d:%02d:%02d", hh, mm, ss)
: mm
- ? my_snprintf(buf, size, "%02d:%02d", hh, mm)
- : my_snprintf(buf, size, "%02d", hh));
+ ? snprintf(buf, size, "%02d:%02d", hh, mm)
+ : snprintf(buf, size, "%02d", hh));
}
/* Store into BUF, of size SIZE, a formatted UT offset for the
@@ -1014,10 +1004,10 @@ format_utc_offset(char *buf, ptrdiff_t size, struct tm const *tm, time_t t)
mm = off / 60 % 60;
hh = off / 60 / 60;
return (ss || 100 <= hh
- ? my_snprintf(buf, size, "%c%02ld%02d%02d", sign, hh, mm, ss)
+ ? snprintf(buf, size, "%c%02ld%02d%02d", sign, hh, mm, ss)
: mm
- ? my_snprintf(buf, size, "%c%02ld%02d", sign, hh, mm)
- : my_snprintf(buf, size, "%c%02ld", sign, hh));
+ ? snprintf(buf, size, "%c%02ld%02d", sign, hh, mm)
+ : snprintf(buf, size, "%c%02ld", sign, hh));
}
/* Store into BUF (of size SIZE) a quoted string representation of P.
@@ -1120,7 +1110,7 @@ istrftime(char *buf, ptrdiff_t size, char const *time_fmt,
for (abp = ab; is_alpha(*abp); abp++)
continue;
len = (!*abp && *ab
- ? my_snprintf(b, s, "%s", ab)
+ ? snprintf(b, s, "%s", ab)
: format_quoted_string(b, s, ab));
if (s <= len)
return false;
@@ -1128,7 +1118,7 @@ istrftime(char *buf, ptrdiff_t size, char const *time_fmt,
}
formatted_len
= (tm->tm_isdst
- ? my_snprintf(b, s, &"\t\t%d"[show_abbr], tm->tm_isdst)
+ ? snprintf(b, s, &"\t\t%d"[show_abbr], tm->tm_isdst)
: 0);
}
break;
diff --git a/contrib/tzcode/zic.8 b/contrib/tzcode/zic.8
index 3804096dd6f6..d83ff7c4a0b1 100644
--- a/contrib/tzcode/zic.8
+++ b/contrib/tzcode/zic.8
@@ -112,13 +112,13 @@ Link \fItimezone\fP posixrules
If
.Ar timezone
is
-.Dq "\*-"
+.Dq "\-"
(the default), any already-existing link is removed.
.Pp
Unless
.Ar timezone
is
-.Dq "\*-" ,
+.Dq "\-" ,
this option is obsolete and poorly supported.
Among other things it should not be used for timestamps after the year 2037,
and it should not be combined with
@@ -148,6 +148,10 @@ omits data intended for negative timestamps (i.e., before the Epoch), and
.Fl r @0/@2147483648
outputs data intended only for nonnegative timestamps that fit into
31-bit signed integers.
+On platforms with GNU
+.Nm date ,
+.Dq "zic \-r @$(date +%s)"
+omits data intended for past timestamps.
Although this option typically reduces the output file's size,
the size can increase due to the need to represent the timestamp range
boundaries, particularly if
@@ -366,7 +370,15 @@ separate script to further restrict in which
of years the rule would apply.
.It IN
Names the month in which the rule takes effect.
-Month names may be abbreviated.
+Month names may be abbreviated as mentioned previously;
+for example, January can appear as
+.Dq January ,
+.Dq JANU
+or
+.Dq Ja ,
+but not as
+.Dq j
+which would be ambiguous with both June and July.
.It ON
Gives the day on which the rule takes effect.
Recognized forms include:
@@ -389,7 +401,12 @@ or a weekday name preceded by
.Dq "last"
(e.g.,
.Ql "lastSunday" )
-may be abbreviated or spelled out in full.
+may be abbreviated as mentioned previously,
+e.g.,
+.Dq Su
+for Sunday and
+.Dq lastsa
+for the last Saturday.
There must be no white space characters within the
.Ar ON
field.
@@ -540,7 +557,7 @@ field,
giving the amount of time to be added to local standard time
and whether the resulting time is standard or daylight saving.
Standard time applies if this field is
-.Ql \*-
+.Ql \-
or for timestamps occurring before any rule takes effect.
When an amount of time is given, only the sum of standard time and
this amount matters.
diff --git a/contrib/tzcode/zic.c b/contrib/tzcode/zic.c
index 2605f65dcd23..80902ce534a1 100644
--- a/contrib/tzcode/zic.c
+++ b/contrib/tzcode/zic.c
@@ -526,19 +526,19 @@ memcheck(void *ptr)
}
static void *
-emalloc(size_t size)
+xmalloc(size_t size)
{
return memcheck(malloc(size));
}
static void *
-erealloc(void *ptr, size_t size)
+xrealloc(void *ptr, size_t size)
{
return memcheck(realloc(ptr, size));
}
static char *
-estrdup(char const *str)
+xstrdup(char const *str)
{
return memcheck(strdup(str));
}
@@ -567,7 +567,7 @@ growalloc(void *ptr, ptrdiff_t itemsize, ptrdiff_t nitems,
{
return (nitems < *nitems_alloc
? ptr
- : erealloc(ptr, grow_nitems_alloc(nitems_alloc, itemsize)));
+ : xrealloc(ptr, grow_nitems_alloc(nitems_alloc, itemsize)));
}
/*
@@ -654,6 +654,8 @@ close_file(FILE *stream, char const *dir, char const *name,
char const *e = (ferror(stream) ? _("I/O error")
: fclose(stream) != 0 ? strerror(errno) : NULL);
if (e) {
+ if (name && *name == '/')
+ dir = NULL;
fprintf(stderr, "%s: %s%s%s%s%s\n", progname,
dir ? dir : "", dir ? "/" : "",
name ? name : "", name ? ": " : "",
@@ -961,6 +963,9 @@ static mode_t mflag = (S_IRUSR | S_IRGRP | S_IROTH
| S_IWUSR);
static const char * tzdefault;
+/* True if DIRECTORY ends in '/'. */
+static bool directory_ends_in_slash;
+
/* -1 if the TZif output file should be slim, 0 if default, 1 if the
output should be fat for backward compatibility. ZIC_BLOAT_DEFAULT
determines the default. */
@@ -1166,6 +1171,7 @@ _("invalid file mode"));
return EXIT_FAILURE;
associate();
change_directory(directory);
+ directory_ends_in_slash = directory[strlen(directory) - 1] == '/';
catch_signals();
for (i = 0; i < nzones; i = j) {
/*
@@ -1353,7 +1359,7 @@ random_dirent(char const **name, char **namealloc)
uint_fast64_t unfair_min = - ((UINTMAX_MAX % base__6 + 1) % base__6);
if (!dst) {
- dst = emalloc(size_sum(dirlen, prefixlen + suffixlen + 1));
+ dst = xmalloc(size_sum(dirlen, prefixlen + suffixlen + 1));
memcpy(dst, src, dirlen);
memcpy(dst + dirlen, prefix, prefixlen);
dst[dirlen + prefixlen + suffixlen] = '\0';
@@ -1370,6 +1376,20 @@ random_dirent(char const **name, char **namealloc)
}
}
+/* For diagnostics the directory, and file name relative to that
+ directory, respectively. A diagnostic routine can name FILENAME by
+ outputting diagdir(FILENAME), then diagslash(FILENAME), then FILENAME. */
+static char const *
+diagdir(char const *filename)
+{
+ return *filename == '/' ? "" : directory;
+}
+static char const *
+diagslash(char const *filename)
+{
+ return &"/"[*filename == '/' || directory_ends_in_slash];
+}
+
/* Prepare to write to the file *OUTNAME, using *TEMPNAME to store the
name of the temporary file that will eventually be renamed to
*OUTNAME. Assign the temporary file's name to both *OUTNAME and
@@ -1406,8 +1426,9 @@ open_outfile(char const **outname, char **tempname)
} else if (fopen_errno == EEXIST)
random_dirent(outname, tempname);
else {
- fprintf(stderr, _("%s: Can't create %s/%s: %s\n"),
- progname, directory, *outname, strerror(fopen_errno));
+ fprintf(stderr, _("%s: Can't create %s%s%s: %s\n"),
+ progname, diagdir(*outname), diagslash(*outname), *outname,
+ strerror(fopen_errno));
exit(EXIT_FAILURE);
}
}
@@ -1424,9 +1445,10 @@ rename_dest(char *tempname, char const *name)
if (tempname) {
if (rename(tempname, name) != 0) {
int rename_errno = errno;
- (void)remove(tempname);
- fprintf(stderr, _("%s: rename to %s/%s: %s\n"),
- progname, directory, name, strerror(rename_errno));
+ remove(tempname);
+ fprintf(stderr, _("%s: rename to %s%s%s: %s\n"),
+ progname, diagdir(name), diagslash(name), name,
+ strerror(rename_errno));
exit(EXIT_FAILURE);
}
free(tempname);
@@ -1436,7 +1458,8 @@ rename_dest(char *tempname, char const *name)
/* Create symlink contents suitable for symlinking TARGET to LINKNAME, as a
freshly allocated string. TARGET should be a relative file name, and
is relative to the global variable DIRECTORY. LINKNAME can be either
- relative or absolute. */
+ relative or absolute. Return a null pointer if the symlink contents
+ was not computed because LINKNAME is absolute but DIRECTORY is not. */
static char *
relname(char const *target, char const *linkname)
{
@@ -1449,8 +1472,10 @@ relname(char const *target, char const *linkname)
size_t len = strlen(directory);
size_t lenslash = len + (len && directory[len - 1] != '/');
size_t targetsize = strlen(target) + 1;
+ if (*directory != '/')
+ return NULL;
linksize = size_sum(lenslash, targetsize);
- f = result = emalloc(linksize);
+ f = result = xmalloc(linksize);
memcpy(result, directory, len);
result[len] = '/';
memcpy(result + lenslash, target, targetsize);
@@ -1464,7 +1489,7 @@ relname(char const *target, char const *linkname)
dotdotetcsize = size_sum(size_product(dotdots, 3), taillen + 1);
if (dotdotetcsize <= linksize) {
if (!result)
- result = emalloc(dotdotetcsize);
+ result = xmalloc(dotdotetcsize);
for (i = 0; i < dotdots; i++)
memcpy(result + 3 * i, "../", 3);
memmove(result + 3 * dotdots, f + dir_len, taillen + 1);
@@ -1500,8 +1525,9 @@ dolink(char const *target, char const *linkname, bool staysymlink)
return;
else {
char const *e = strerror(errno);
- fprintf(stderr, _("%s: Can't remove %s/%s: %s\n"),
- progname, directory, linkname, e);
+ fprintf(stderr, _("%s: Can't remove %s%s%s: %s\n"),
+ progname, diagdir(linkname), diagslash(linkname), linkname,
+ e);
exit(EXIT_FAILURE);
}
}
@@ -1544,8 +1570,9 @@ dolink(char const *target, char const *linkname, bool staysymlink)
mkdirs(linkname, true);
linkdirs_made = true;
} else {
- fprintf(stderr, _("%s: Can't link %s/%s to %s/%s: %s\n"),
- progname, directory, target, directory, outname,
+ fprintf(stderr, _("%s: Can't link %s%s%s to %s%s%s: %s\n"),
+ progname, diagdir(target), diagslash(target), target,
+ diagdir(outname), diagslash(outname), outname,
strerror(link_errno));
exit(EXIT_FAILURE);
}
@@ -1554,21 +1581,23 @@ dolink(char const *target, char const *linkname, bool staysymlink)
bool absolute = *target == '/';
char *linkalloc = absolute ? NULL : relname(target, linkname);
char const *contents = absolute ? target : linkalloc;
- int symlink_errno;
+ int symlink_errno = -1;
- while (true) {
- if (symlink(contents, outname) == 0) {
- symlink_errno = 0;
- break;
+ if (contents) {
+ while (true) {
+ if (symlink(contents, outname) == 0) {
+ symlink_errno = 0;
+ break;
+ }
+ symlink_errno = errno;
+ if (symlink_errno == EEXIST)
+ random_dirent(&outname, &tempname);
+ else if (symlink_errno == ENOENT && !linkdirs_made) {
+ mkdirs(linkname, true);
+ linkdirs_made = true;
+ } else
+ break;
}
- symlink_errno = errno;
- if (symlink_errno == EEXIST)
- random_dirent(&outname, &tempname);
- else if (symlink_errno == ENOENT && !linkdirs_made) {
- mkdirs(linkname, true);
- linkdirs_made = true;
- } else
- break;
}
free(linkalloc);
if (symlink_errno == 0) {
@@ -1581,8 +1610,8 @@ dolink(char const *target, char const *linkname, bool staysymlink)
fp = fopen(target, "rb");
if (!fp) {
char const *e = strerror(errno);
- fprintf(stderr, _("%s: Can't read %s/%s: %s\n"),
- progname, directory, target, e);
+ fprintf(stderr, _("%s: Can't read %s%s%s: %s\n"),
+ progname, diagdir(target), diagslash(target), target, e);
exit(EXIT_FAILURE);
}
tp = open_outfile(&outname, &tempname);
@@ -1593,6 +1622,8 @@ dolink(char const *target, char const *linkname, bool staysymlink)
if (link_errno != ENOTSUP)
warning(_("copy used because hard link failed: %s"),
strerror(link_errno));
+ else if (symlink_errno < 0)
+ warning(_("copy used because symbolic link not obvious"));
else if (symlink_errno != ENOTSUP)
warning(_("copy used because symbolic link failed: %s"),
strerror(symlink_errno));
@@ -1906,8 +1937,8 @@ inrule(char **fields, int nfields)
fields[RF_COMMAND], fields[RF_MONTH], fields[RF_DAY],
fields[RF_TOD]))
return;
- r.r_name = estrdup(fields[RF_NAME]);
- r.r_abbrvar = estrdup(fields[RF_ABBRVAR]);
+ r.r_name = xstrdup(fields[RF_NAME]);
+ r.r_abbrvar = xstrdup(fields[RF_ABBRVAR]);
if (max_abbrvar_len < strlen(r.r_abbrvar))
max_abbrvar_len = strlen(r.r_abbrvar);
rules = growalloc(rules, sizeof *rules, nrules, &nrules_alloc);
@@ -1990,7 +2021,8 @@ inzsub(char **fields, int nfields, bool iscont)
z.z_filenum = filenum;
z.z_linenum = linenum;
z.z_stdoff = gethms(fields[i_stdoff], _("invalid UT offset"));
- if ((cp = strchr(fields[i_format], '%')) != 0) {
+ cp = strchr(fields[i_format], '%');
+ if (cp) {
if ((*++cp != 's' && *cp != 'z') || strchr(cp, '%')
|| strchr(fields[i_format], '/')) {
error(_("invalid abbreviation format"));
@@ -2028,9 +2060,9 @@ inzsub(char **fields, int nfields, bool iscont)
return false;
}
}
- z.z_name = iscont ? NULL : estrdup(fields[ZF_NAME]);
- z.z_rule = estrdup(fields[i_rule]);
- z.z_format = cp1 = estrdup(fields[i_format]);
+ z.z_name = iscont ? NULL : xstrdup(fields[ZF_NAME]);
+ z.z_rule = xstrdup(fields[i_rule]);
+ z.z_format = cp1 = xstrdup(fields[i_format]);
if (z.z_format_specifier == 'z') {
cp1[cp - fields[i_format]] = 's';
if (noise)
@@ -2173,8 +2205,8 @@ inlink(char **fields, int nfields)
return;
l.l_filenum = filenum;
l.l_linenum = linenum;
- l.l_target = estrdup(fields[LF_TARGET]);
- l.l_linkname = estrdup(fields[LF_LINKNAME]);
+ l.l_target = xstrdup(fields[LF_TARGET]);
+ l.l_linkname = xstrdup(fields[LF_LINKNAME]);
links = growalloc(links, sizeof *links, nlinks, &nlinks_alloc);
links[nlinks++] = l;
}
@@ -2197,7 +2229,7 @@ rulesub(struct rule *rp, const char *loyearp, const char *hiyearp,
rp->r_month = lp->l_value;
rp->r_todisstd = false;
rp->r_todisut = false;
- dp = estrdup(timep);
+ dp = xstrdup(timep);
if (*dp != '\0') {
ep = dp + strlen(dp) - 1;
switch (lowerit(*ep)) {
@@ -2272,19 +2304,23 @@ rulesub(struct rule *rp, const char *loyearp, const char *hiyearp,
** Sun<=20
** Sun>=7
*/
- dp = estrdup(dayp);
+ dp = xstrdup(dayp);
if ((lp = byword(dp, lasts)) != NULL) {
rp->r_dycode = DC_DOWLEQ;
rp->r_wday = lp->l_value;
rp->r_dayofmonth = len_months[1][rp->r_month];
} else {
- if ((ep = strchr(dp, '<')) != 0)
- rp->r_dycode = DC_DOWLEQ;
- else if ((ep = strchr(dp, '>')) != 0)
- rp->r_dycode = DC_DOWGEQ;
+ ep = strchr(dp, '<');
+ if (ep)
+ rp->r_dycode = DC_DOWLEQ;
else {
+ ep = strchr(dp, '>');
+ if (ep)
+ rp->r_dycode = DC_DOWGEQ;
+ else {
ep = dp;
rp->r_dycode = DC_DOM;
+ }
}
if (rp->r_dycode != DC_DOM) {
*ep++ = 0;
@@ -2427,7 +2463,7 @@ writezone(const char *const name, const char *const string, char version,
/* Allocate the ATS and TYPES arrays via a single malloc,
as this is a bit faster. Do not malloc(0) if !timecnt,
as that might return NULL even on success. */
- zic_t *ats = emalloc(align_to(size_product(timecnt + !timecnt,
+ zic_t *ats = xmalloc(align_to(size_product(timecnt + !timecnt,
sizeof *ats + 1),
alignof(zic_t)));
void *typesptr = ats + timecnt;
@@ -2802,7 +2838,7 @@ writezone(const char *const name, const char *const string, char version,
if (thisleapexpiry) {
/* Append a no-op leap correction indicating when the leap
second table expires. Although this does not conform to
- Internet RFC 8536, most clients seem to accept this and
+ Internet RFC 9636, most clients seem to accept this and
the plan is to amend the RFC to allow this in version 4
TZif files. */
puttzcodepass(leapexpires, fp, pass);
@@ -3059,7 +3095,7 @@ stringzone(char *result, struct zone const *zpfirst, ptrdiff_t zonecount)
result[0] = '\0';
- /* Internet RFC 8536 section 5.1 says to use an empty TZ string if
+ /* Internet RFC 9636 section 6.1 says to use an empty TZ string if
future timestamps are truncated. */
if (hi_time < max_time)
return -1;
@@ -3187,9 +3223,9 @@ outzone(const struct zone *zpfirst, ptrdiff_t zonecount)
max_abbr_len = 2 + max_format_len + max_abbrvar_len;
max_envvar_len = 2 * max_abbr_len + 5 * 9;
- startbuf = emalloc(max_abbr_len + 1);
- ab = emalloc(max_abbr_len + 1);
- envvar = emalloc(max_envvar_len + 1);
+ startbuf = xmalloc(max_abbr_len + 1);
+ ab = xmalloc(max_abbr_len + 1);
+ envvar = xmalloc(max_envvar_len + 1);
INITIALIZE(untiltime);
INITIALIZE(starttime);
/*
@@ -3972,7 +4008,7 @@ mkdirs(char const *argname, bool ancestors)
if (Dflag)
return;
- char *name = estrdup(argname);
+ char *name = xstrdup(argname);
char *cp = name;
/* On MS-Windows systems, do not worry about drive letters or
diff --git a/contrib/unbound/config.guess b/contrib/unbound/config.guess
index 48a684601bd2..a9d01fde4617 100755
--- a/contrib/unbound/config.guess
+++ b/contrib/unbound/config.guess
@@ -1,10 +1,10 @@
#! /bin/sh
# Attempt to guess a canonical system name.
-# Copyright 1992-2024 Free Software Foundation, Inc.
+# Copyright 1992-2025 Free Software Foundation, Inc.
# shellcheck disable=SC2006,SC2268 # see below for rationale
-timestamp='2024-07-27'
+timestamp='2025-07-10'
# This file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
@@ -60,7 +60,7 @@ version="\
GNU config.guess ($timestamp)
Originally written by Per Bothner.
-Copyright 1992-2024 Free Software Foundation, Inc.
+Copyright 1992-2025 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
@@ -1597,8 +1597,11 @@ EOF
*:Unleashed:*:*)
GUESS=$UNAME_MACHINE-unknown-unleashed$UNAME_RELEASE
;;
- *:Ironclad:*:*)
- GUESS=$UNAME_MACHINE-unknown-ironclad
+ x86_64:[Ii]ronclad:*:*|i?86:[Ii]ronclad:*:*)
+ GUESS=$UNAME_MACHINE-pc-ironclad-mlibc
+ ;;
+ *:[Ii]ronclad:*:*)
+ GUESS=$UNAME_MACHINE-unknown-ironclad-mlibc
;;
esac
@@ -1808,8 +1811,8 @@ fi
exit 1
# Local variables:
-# eval: (add-hook 'before-save-hook 'time-stamp)
+# eval: (add-hook 'before-save-hook 'time-stamp nil t)
# time-stamp-start: "timestamp='"
-# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-format: "%Y-%02m-%02d"
# time-stamp-end: "'"
# End:
diff --git a/contrib/unbound/config.h.in b/contrib/unbound/config.h.in
index f2dc8c8b92b3..584810398b91 100644
--- a/contrib/unbound/config.h.in
+++ b/contrib/unbound/config.h.in
@@ -48,13 +48,13 @@
internal symbols */
#undef EXPORT_ALL_SYMBOLS
-/* Define to 1 if you have the `accept4' function. */
+/* Define to 1 if you have the 'accept4' function. */
#undef HAVE_ACCEPT4
-/* Define to 1 if you have the `arc4random' function. */
+/* Define to 1 if you have the 'arc4random' function. */
#undef HAVE_ARC4RANDOM
-/* Define to 1 if you have the `arc4random_uniform' function. */
+/* Define to 1 if you have the 'arc4random_uniform' function. */
#undef HAVE_ARC4RANDOM_UNIFORM
/* Define to 1 if you have the <arpa/inet.h> header file. */
@@ -78,7 +78,7 @@
/* If we have be64toh */
#undef HAVE_BE64TOH
-/* Define to 1 if you have the `BIO_set_callback_ex' function. */
+/* Define to 1 if you have the 'BIO_set_callback_ex' function. */
#undef HAVE_BIO_SET_CALLBACK_EX
/* Define to 1 if you have the <bsd/stdlib.h> header file. */
@@ -87,241 +87,241 @@
/* Define to 1 if you have the <bsd/string.h> header file. */
#undef HAVE_BSD_STRING_H
-/* Define to 1 if you have the `chown' function. */
+/* Define to 1 if you have the 'chown' function. */
#undef HAVE_CHOWN
-/* Define to 1 if you have the `chroot' function. */
+/* Define to 1 if you have the 'chroot' function. */
#undef HAVE_CHROOT
-/* Define to 1 if you have the `CRYPTO_cleanup_all_ex_data' function. */
+/* Define to 1 if you have the 'CRYPTO_cleanup_all_ex_data' function. */
#undef HAVE_CRYPTO_CLEANUP_ALL_EX_DATA
-/* Define to 1 if you have the `CRYPTO_THREADID_set_callback' function. */
+/* Define to 1 if you have the 'CRYPTO_THREADID_set_callback' function. */
#undef HAVE_CRYPTO_THREADID_SET_CALLBACK
-/* Define to 1 if you have the `ctime_r' function. */
+/* Define to 1 if you have the 'ctime_r' function. */
#undef HAVE_CTIME_R
-/* Define to 1 if you have the `daemon' function. */
+/* Define to 1 if you have the 'daemon' function. */
#undef HAVE_DAEMON
-/* Define to 1 if you have the declaration of `arc4random', and to 0 if you
+/* Define to 1 if you have the declaration of 'arc4random', and to 0 if you
don't. */
#undef HAVE_DECL_ARC4RANDOM
-/* Define to 1 if you have the declaration of `arc4random_uniform', and to 0
+/* Define to 1 if you have the declaration of 'arc4random_uniform', and to 0
if you don't. */
#undef HAVE_DECL_ARC4RANDOM_UNIFORM
-/* Define to 1 if you have the declaration of `evsignal_assign', and to 0 if
+/* Define to 1 if you have the declaration of 'evsignal_assign', and to 0 if
you don't. */
#undef HAVE_DECL_EVSIGNAL_ASSIGN
-/* Define to 1 if you have the declaration of `inet_ntop', and to 0 if you
+/* Define to 1 if you have the declaration of 'inet_ntop', and to 0 if you
don't. */
#undef HAVE_DECL_INET_NTOP
-/* Define to 1 if you have the declaration of `inet_pton', and to 0 if you
+/* Define to 1 if you have the declaration of 'inet_pton', and to 0 if you
don't. */
#undef HAVE_DECL_INET_PTON
-/* Define to 1 if you have the declaration of `nghttp2_session_server_new',
+/* Define to 1 if you have the declaration of 'nghttp2_session_server_new',
and to 0 if you don't. */
#undef HAVE_DECL_NGHTTP2_SESSION_SERVER_NEW
-/* Define to 1 if you have the declaration of `ngtcp2_conn_server_new', and to
+/* Define to 1 if you have the declaration of 'ngtcp2_conn_server_new', and to
0 if you don't. */
#undef HAVE_DECL_NGTCP2_CONN_SERVER_NEW
-/* Define to 1 if you have the declaration of `ngtcp2_crypto_encrypt_cb', and
+/* Define to 1 if you have the declaration of 'ngtcp2_crypto_encrypt_cb', and
to 0 if you don't. */
#undef HAVE_DECL_NGTCP2_CRYPTO_ENCRYPT_CB
-/* Define to 1 if you have the declaration of `NID_ED25519', and to 0 if you
+/* Define to 1 if you have the declaration of 'NID_ED25519', and to 0 if you
don't. */
#undef HAVE_DECL_NID_ED25519
-/* Define to 1 if you have the declaration of `NID_ED448', and to 0 if you
+/* Define to 1 if you have the declaration of 'NID_ED448', and to 0 if you
don't. */
#undef HAVE_DECL_NID_ED448
-/* Define to 1 if you have the declaration of `NID_secp384r1', and to 0 if you
+/* Define to 1 if you have the declaration of 'NID_secp384r1', and to 0 if you
don't. */
#undef HAVE_DECL_NID_SECP384R1
-/* Define to 1 if you have the declaration of `NID_X9_62_prime256v1', and to 0
+/* Define to 1 if you have the declaration of 'NID_X9_62_prime256v1', and to 0
if you don't. */
#undef HAVE_DECL_NID_X9_62_PRIME256V1
-/* Define to 1 if you have the declaration of `reallocarray', and to 0 if you
+/* Define to 1 if you have the declaration of 'reallocarray', and to 0 if you
don't. */
#undef HAVE_DECL_REALLOCARRAY
-/* Define to 1 if you have the declaration of `redisConnect', and to 0 if you
+/* Define to 1 if you have the declaration of 'redisConnect', and to 0 if you
don't. */
#undef HAVE_DECL_REDISCONNECT
-/* Define to 1 if you have the declaration of `sk_SSL_COMP_pop_free', and to 0
+/* Define to 1 if you have the declaration of 'sk_SSL_COMP_pop_free', and to 0
if you don't. */
#undef HAVE_DECL_SK_SSL_COMP_POP_FREE
/* Define to 1 if you have the declaration of
- `SSL_COMP_get_compression_methods', and to 0 if you don't. */
+ 'SSL_COMP_get_compression_methods', and to 0 if you don't. */
#undef HAVE_DECL_SSL_COMP_GET_COMPRESSION_METHODS
-/* Define to 1 if you have the declaration of `SSL_CTX_set_ecdh_auto', and to
+/* Define to 1 if you have the declaration of 'SSL_CTX_set_ecdh_auto', and to
0 if you don't. */
#undef HAVE_DECL_SSL_CTX_SET_ECDH_AUTO
-/* Define to 1 if you have the declaration of `strlcat', and to 0 if you
+/* Define to 1 if you have the declaration of 'strlcat', and to 0 if you
don't. */
#undef HAVE_DECL_STRLCAT
-/* Define to 1 if you have the declaration of `strlcpy', and to 0 if you
+/* Define to 1 if you have the declaration of 'strlcpy', and to 0 if you
don't. */
#undef HAVE_DECL_STRLCPY
-/* Define to 1 if you have the declaration of `XML_StopParser', and to 0 if
+/* Define to 1 if you have the declaration of 'XML_StopParser', and to 0 if
you don't. */
#undef HAVE_DECL_XML_STOPPARSER
/* Define to 1 if you have the <dlfcn.h> header file. */
#undef HAVE_DLFCN_H
-/* Define to 1 if you have the `DSA_SIG_set0' function. */
+/* Define to 1 if you have the 'DSA_SIG_set0' function. */
#undef HAVE_DSA_SIG_SET0
/* Define to 1 if you have the <endian.h> header file. */
#undef HAVE_ENDIAN_H
-/* Define to 1 if you have the `endprotoent' function. */
+/* Define to 1 if you have the 'endprotoent' function. */
#undef HAVE_ENDPROTOENT
-/* Define to 1 if you have the `endpwent' function. */
+/* Define to 1 if you have the 'endpwent' function. */
#undef HAVE_ENDPWENT
-/* Define to 1 if you have the `endservent' function. */
+/* Define to 1 if you have the 'endservent' function. */
#undef HAVE_ENDSERVENT
-/* Define to 1 if you have the `ENGINE_cleanup' function. */
+/* Define to 1 if you have the 'ENGINE_cleanup' function. */
#undef HAVE_ENGINE_CLEANUP
-/* Define to 1 if you have the `ERR_free_strings' function. */
+/* Define to 1 if you have the 'ERR_free_strings' function. */
#undef HAVE_ERR_FREE_STRINGS
-/* Define to 1 if you have the `ERR_load_crypto_strings' function. */
+/* Define to 1 if you have the 'ERR_load_crypto_strings' function. */
#undef HAVE_ERR_LOAD_CRYPTO_STRINGS
-/* Define to 1 if you have the `event_assign' function. */
+/* Define to 1 if you have the 'event_assign' function. */
#undef HAVE_EVENT_ASSIGN
-/* Define to 1 if you have the `event_base_free' function. */
+/* Define to 1 if you have the 'event_base_free' function. */
#undef HAVE_EVENT_BASE_FREE
-/* Define to 1 if you have the `event_base_get_method' function. */
+/* Define to 1 if you have the 'event_base_get_method' function. */
#undef HAVE_EVENT_BASE_GET_METHOD
-/* Define to 1 if you have the `event_base_new' function. */
+/* Define to 1 if you have the 'event_base_new' function. */
#undef HAVE_EVENT_BASE_NEW
-/* Define to 1 if you have the `event_base_once' function. */
+/* Define to 1 if you have the 'event_base_once' function. */
#undef HAVE_EVENT_BASE_ONCE
/* Define to 1 if you have the <event.h> header file. */
#undef HAVE_EVENT_H
-/* Define to 1 if you have the `EVP_aes_256_cbc' function. */
+/* Define to 1 if you have the 'EVP_aes_256_cbc' function. */
#undef HAVE_EVP_AES_256_CBC
-/* Define to 1 if you have the `EVP_cleanup' function. */
+/* Define to 1 if you have the 'EVP_cleanup' function. */
#undef HAVE_EVP_CLEANUP
-/* Define to 1 if you have the `EVP_default_properties_is_fips_enabled'
+/* Define to 1 if you have the 'EVP_default_properties_is_fips_enabled'
function. */
#undef HAVE_EVP_DEFAULT_PROPERTIES_IS_FIPS_ENABLED
-/* Define to 1 if you have the `EVP_DigestVerify' function. */
+/* Define to 1 if you have the 'EVP_DigestVerify' function. */
#undef HAVE_EVP_DIGESTVERIFY
-/* Define to 1 if you have the `EVP_dss1' function. */
+/* Define to 1 if you have the 'EVP_dss1' function. */
#undef HAVE_EVP_DSS1
-/* Define to 1 if you have the `EVP_EncryptInit_ex' function. */
+/* Define to 1 if you have the 'EVP_EncryptInit_ex' function. */
#undef HAVE_EVP_ENCRYPTINIT_EX
-/* Define to 1 if you have the `EVP_MAC_CTX_set_params' function. */
+/* Define to 1 if you have the 'EVP_MAC_CTX_set_params' function. */
#undef HAVE_EVP_MAC_CTX_SET_PARAMS
-/* Define to 1 if you have the `EVP_MD_CTX_new' function. */
+/* Define to 1 if you have the 'EVP_MD_CTX_new' function. */
#undef HAVE_EVP_MD_CTX_NEW
-/* Define to 1 if you have the `EVP_sha1' function. */
+/* Define to 1 if you have the 'EVP_sha1' function. */
#undef HAVE_EVP_SHA1
-/* Define to 1 if you have the `EVP_sha256' function. */
+/* Define to 1 if you have the 'EVP_sha256' function. */
#undef HAVE_EVP_SHA256
-/* Define to 1 if you have the `EVP_sha512' function. */
+/* Define to 1 if you have the 'EVP_sha512' function. */
#undef HAVE_EVP_SHA512
-/* Define to 1 if you have the `ev_default_loop' function. */
+/* Define to 1 if you have the 'ev_default_loop' function. */
#undef HAVE_EV_DEFAULT_LOOP
-/* Define to 1 if you have the `ev_loop' function. */
+/* Define to 1 if you have the 'ev_loop' function. */
#undef HAVE_EV_LOOP
/* Define to 1 if you have the <expat.h> header file. */
#undef HAVE_EXPAT_H
-/* Define to 1 if you have the `explicit_bzero' function. */
+/* Define to 1 if you have the 'explicit_bzero' function. */
#undef HAVE_EXPLICIT_BZERO
-/* Define to 1 if you have the `fcntl' function. */
+/* Define to 1 if you have the 'fcntl' function. */
#undef HAVE_FCNTL
-/* Define to 1 if you have the `FIPS_mode' function. */
+/* Define to 1 if you have the 'FIPS_mode' function. */
#undef HAVE_FIPS_MODE
-/* Define to 1 if you have the `fork' function. */
+/* Define to 1 if you have the 'fork' function. */
#undef HAVE_FORK
-/* Define to 1 if fseeko (and presumably ftello) exists and is declared. */
+/* Define to 1 if fseeko (and ftello) are declared in stdio.h. */
#undef HAVE_FSEEKO
-/* Define to 1 if you have the `fsync' function. */
+/* Define to 1 if you have the 'fsync' function. */
#undef HAVE_FSYNC
/* Whether getaddrinfo is available */
#undef HAVE_GETADDRINFO
-/* Define to 1 if you have the `getauxval' function. */
+/* Define to 1 if you have the 'getauxval' function. */
#undef HAVE_GETAUXVAL
-/* Define to 1 if you have the `getentropy' function. */
+/* Define to 1 if you have the 'getentropy' function. */
#undef HAVE_GETENTROPY
-/* Define to 1 if you have the `getifaddrs' function. */
+/* Define to 1 if you have the 'getifaddrs' function. */
#undef HAVE_GETIFADDRS
/* Define to 1 if you have the <getopt.h> header file. */
#undef HAVE_GETOPT_H
-/* Define to 1 if you have the `getpwnam' function. */
+/* Define to 1 if you have the 'getpwnam' function. */
#undef HAVE_GETPWNAM
-/* Define to 1 if you have the `getrlimit' function. */
+/* Define to 1 if you have the 'getrlimit' function. */
#undef HAVE_GETRLIMIT
-/* Define to 1 if you have the `gettid' function. */
+/* Define to 1 if you have the 'gettid' function. */
#undef HAVE_GETTID
-/* Define to 1 if you have the `glob' function. */
+/* Define to 1 if you have the 'glob' function. */
#undef HAVE_GLOB
/* Define to 1 if you have the <glob.h> header file. */
#undef HAVE_GLOB_H
-/* Define to 1 if you have the `gmtime_r' function. */
+/* Define to 1 if you have the 'gmtime_r' function. */
#undef HAVE_GMTIME_R
/* Define to 1 if you have the <grp.h> header file. */
@@ -330,7 +330,7 @@
/* Define to 1 if you have the <hiredis/hiredis.h> header file. */
#undef HAVE_HIREDIS_HIREDIS_H
-/* Define to 1 if you have the `HMAC_Init_ex' function. */
+/* Define to 1 if you have the 'HMAC_Init_ex' function. */
#undef HAVE_HMAC_INIT_EX
/* If we have htobe64 */
@@ -339,19 +339,19 @@
/* Define to 1 if you have the <ifaddrs.h> header file. */
#undef HAVE_IFADDRS_H
-/* Define to 1 if you have the `if_nametoindex' function. */
+/* Define to 1 if you have the 'if_nametoindex' function. */
#undef HAVE_IF_NAMETOINDEX
-/* Define to 1 if you have the `inet_aton' function. */
+/* Define to 1 if you have the 'inet_aton' function. */
#undef HAVE_INET_ATON
-/* Define to 1 if you have the `inet_ntop' function. */
+/* Define to 1 if you have the 'inet_ntop' function. */
#undef HAVE_INET_NTOP
-/* Define to 1 if you have the `inet_pton' function. */
+/* Define to 1 if you have the 'inet_pton' function. */
#undef HAVE_INET_PTON
-/* Define to 1 if you have the `initgroups' function. */
+/* Define to 1 if you have the 'initgroups' function. */
#undef HAVE_INITGROUPS
/* Define to 1 if you have the <inttypes.h> header file. */
@@ -363,10 +363,10 @@
/* Define to 1 if you have the <iphlpapi.h> header file. */
#undef HAVE_IPHLPAPI_H
-/* Define to 1 if you have the `isblank' function. */
+/* Define to 1 if you have the 'isblank' function. */
#undef HAVE_ISBLANK
-/* Define to 1 if you have the `kill' function. */
+/* Define to 1 if you have the 'kill' function. */
#undef HAVE_KILL
/* Use portable libbsd functions */
@@ -384,7 +384,7 @@
/* Define to 1 if you have the <linux/net_tstamp.h> header file. */
#undef HAVE_LINUX_NET_TSTAMP_H
-/* Define to 1 if you have the `localtime_r' function. */
+/* Define to 1 if you have the 'localtime_r' function. */
#undef HAVE_LOCALTIME_R
/* Define to 1 if you have the <login_cap.h> header file. */
@@ -393,7 +393,7 @@
/* If have GNU libc compatible malloc */
#undef HAVE_MALLOC
-/* Define to 1 if you have the `memmove' function. */
+/* Define to 1 if you have the 'memmove' function. */
#undef HAVE_MEMMOVE
/* Define to 1 if you have the <minix/config.h> header file. */
@@ -435,49 +435,49 @@
/* Define this to use ngtcp2. */
#undef HAVE_NGTCP2
-/* Define to 1 if you have the `ngtcp2_ccerr_default' function. */
+/* Define to 1 if you have the 'ngtcp2_ccerr_default' function. */
#undef HAVE_NGTCP2_CCERR_DEFAULT
-/* Define to 1 if you have the `ngtcp2_conn_encode_0rtt_transport_params'
+/* Define to 1 if you have the 'ngtcp2_conn_encode_0rtt_transport_params'
function. */
#undef HAVE_NGTCP2_CONN_ENCODE_0RTT_TRANSPORT_PARAMS
-/* Define to 1 if you have the `ngtcp2_conn_get_max_local_streams_uni'
+/* Define to 1 if you have the 'ngtcp2_conn_get_max_local_streams_uni'
function. */
#undef HAVE_NGTCP2_CONN_GET_MAX_LOCAL_STREAMS_UNI
-/* Define to 1 if you have the `ngtcp2_conn_get_num_scid' function. */
+/* Define to 1 if you have the 'ngtcp2_conn_get_num_scid' function. */
#undef HAVE_NGTCP2_CONN_GET_NUM_SCID
-/* Define to 1 if you have the `ngtcp2_conn_in_closing_period' function. */
+/* Define to 1 if you have the 'ngtcp2_conn_in_closing_period' function. */
#undef HAVE_NGTCP2_CONN_IN_CLOSING_PERIOD
-/* Define to 1 if you have the `ngtcp2_conn_in_draining_period' function. */
+/* Define to 1 if you have the 'ngtcp2_conn_in_draining_period' function. */
#undef HAVE_NGTCP2_CONN_IN_DRAINING_PERIOD
/* Define if ngtcp2_conn_shutdown_stream has 4 arguments. */
#undef HAVE_NGTCP2_CONN_SHUTDOWN_STREAM4
-/* Define to 1 if you have the `ngtcp2_conn_tls_early_data_rejected' function.
+/* Define to 1 if you have the 'ngtcp2_conn_tls_early_data_rejected' function.
*/
#undef HAVE_NGTCP2_CONN_TLS_EARLY_DATA_REJECTED
-/* Define to 1 if you have the `ngtcp2_crypto_encrypt_cb' function. */
+/* Define to 1 if you have the 'ngtcp2_crypto_encrypt_cb' function. */
#undef HAVE_NGTCP2_CRYPTO_ENCRYPT_CB
/* Define to 1 if you have the
- `ngtcp2_crypto_quictls_configure_client_context' function. */
+ 'ngtcp2_crypto_quictls_configure_client_context' function. */
#undef HAVE_NGTCP2_CRYPTO_QUICTLS_CONFIGURE_CLIENT_CONTEXT
/* Define to 1 if you have the
- `ngtcp2_crypto_quictls_configure_server_context' function. */
+ 'ngtcp2_crypto_quictls_configure_server_context' function. */
#undef HAVE_NGTCP2_CRYPTO_QUICTLS_CONFIGURE_SERVER_CONTEXT
/* Define to 1 if you have the
- `ngtcp2_crypto_quictls_from_ossl_encryption_level' function. */
+ 'ngtcp2_crypto_quictls_from_ossl_encryption_level' function. */
#undef HAVE_NGTCP2_CRYPTO_QUICTLS_FROM_OSSL_ENCRYPTION_LEVEL
-/* Define to 1 if the system has the type `ngtcp2_encryption_level'. */
+/* Define to 1 if the system has the type 'ngtcp2_encryption_level'. */
#undef HAVE_NGTCP2_ENCRYPTION_LEVEL
/* Define to 1 if you have the <ngtcp2/ngtcp2_crypto_openssl.h> header file.
@@ -494,13 +494,13 @@
/* Use libnss for crypto */
#undef HAVE_NSS
-/* Define to 1 if you have the `OpenSSL_add_all_digests' function. */
+/* Define to 1 if you have the 'OpenSSL_add_all_digests' function. */
#undef HAVE_OPENSSL_ADD_ALL_DIGESTS
/* Define to 1 if you have the <openssl/bn.h> header file. */
#undef HAVE_OPENSSL_BN_H
-/* Define to 1 if you have the `OPENSSL_config' function. */
+/* Define to 1 if you have the 'OPENSSL_config' function. */
#undef HAVE_OPENSSL_CONFIG
/* Define to 1 if you have the <openssl/conf.h> header file. */
@@ -521,10 +521,10 @@
/* Define to 1 if you have the <openssl/err.h> header file. */
#undef HAVE_OPENSSL_ERR_H
-/* Define to 1 if you have the `OPENSSL_init_crypto' function. */
+/* Define to 1 if you have the 'OPENSSL_init_crypto' function. */
#undef HAVE_OPENSSL_INIT_CRYPTO
-/* Define to 1 if you have the `OPENSSL_init_ssl' function. */
+/* Define to 1 if you have the 'OPENSSL_init_ssl' function. */
#undef HAVE_OPENSSL_INIT_SSL
/* Define to 1 if you have the <openssl/param_build.h> header file. */
@@ -539,10 +539,10 @@
/* Define to 1 if you have the <openssl/ssl.h> header file. */
#undef HAVE_OPENSSL_SSL_H
-/* Define to 1 if you have the `OSSL_PARAM_BLD_new' function. */
+/* Define to 1 if you have the 'OSSL_PARAM_BLD_new' function. */
#undef HAVE_OSSL_PARAM_BLD_NEW
-/* Define to 1 if you have the `poll' function. */
+/* Define to 1 if you have the 'poll' function. */
#undef HAVE_POLL
/* Define to 1 if you have the <poll.h> header file. */
@@ -554,10 +554,10 @@
/* Have PTHREAD_PRIO_INHERIT. */
#undef HAVE_PTHREAD_PRIO_INHERIT
-/* Define to 1 if the system has the type `pthread_rwlock_t'. */
+/* Define to 1 if the system has the type 'pthread_rwlock_t'. */
#undef HAVE_PTHREAD_RWLOCK_T
-/* Define to 1 if the system has the type `pthread_spinlock_t'. */
+/* Define to 1 if the system has the type 'pthread_spinlock_t'. */
#undef HAVE_PTHREAD_SPINLOCK_T
/* Define to 1 if you have the <pwd.h> header file. */
@@ -566,101 +566,101 @@
/* Define if you have Python libraries and header files. */
#undef HAVE_PYTHON
-/* Define to 1 if you have the `random' function. */
+/* Define to 1 if you have the 'random' function. */
#undef HAVE_RANDOM
-/* Define to 1 if you have the `RAND_cleanup' function. */
+/* Define to 1 if you have the 'RAND_cleanup' function. */
#undef HAVE_RAND_CLEANUP
/* If we have reallocarray(3) */
#undef HAVE_REALLOCARRAY
-/* Define to 1 if you have the `recvmsg' function. */
+/* Define to 1 if you have the 'recvmsg' function. */
#undef HAVE_RECVMSG
-/* Define to 1 if you have the `sendmsg' function. */
+/* Define to 1 if you have the 'sendmsg' function. */
#undef HAVE_SENDMSG
-/* Define to 1 if you have the `setregid' function. */
+/* Define to 1 if you have the 'setregid' function. */
#undef HAVE_SETREGID
-/* Define to 1 if you have the `setresgid' function. */
+/* Define to 1 if you have the 'setresgid' function. */
#undef HAVE_SETRESGID
-/* Define to 1 if you have the `setresuid' function. */
+/* Define to 1 if you have the 'setresuid' function. */
#undef HAVE_SETRESUID
-/* Define to 1 if you have the `setreuid' function. */
+/* Define to 1 if you have the 'setreuid' function. */
#undef HAVE_SETREUID
-/* Define to 1 if you have the `setrlimit' function. */
+/* Define to 1 if you have the 'setrlimit' function. */
#undef HAVE_SETRLIMIT
-/* Define to 1 if you have the `setsid' function. */
+/* Define to 1 if you have the 'setsid' function. */
#undef HAVE_SETSID
-/* Define to 1 if you have the `setusercontext' function. */
+/* Define to 1 if you have the 'setusercontext' function. */
#undef HAVE_SETUSERCONTEXT
-/* Define to 1 if you have the `SHA512_Update' function. */
+/* Define to 1 if you have the 'SHA512_Update' function. */
#undef HAVE_SHA512_UPDATE
-/* Define to 1 if you have the `shmget' function. */
+/* Define to 1 if you have the 'shmget' function. */
#undef HAVE_SHMGET
-/* Define to 1 if you have the `sigprocmask' function. */
+/* Define to 1 if you have the 'sigprocmask' function. */
#undef HAVE_SIGPROCMASK
-/* Define to 1 if you have the `sleep' function. */
+/* Define to 1 if you have the 'sleep' function. */
#undef HAVE_SLEEP
-/* Define to 1 if you have the `snprintf' function. */
+/* Define to 1 if you have the 'snprintf' function. */
#undef HAVE_SNPRINTF
-/* Define to 1 if you have the `socketpair' function. */
+/* Define to 1 if you have the 'socketpair' function. */
#undef HAVE_SOCKETPAIR
/* Using Solaris threads */
#undef HAVE_SOLARIS_THREADS
-/* Define to 1 if you have the `srandom' function. */
+/* Define to 1 if you have the 'srandom' function. */
#undef HAVE_SRANDOM
/* Define if you have the SSL libraries installed. */
#undef HAVE_SSL
-/* Define to 1 if you have the `SSL_CTX_set_alpn_protos' function. */
+/* Define to 1 if you have the 'SSL_CTX_set_alpn_protos' function. */
#undef HAVE_SSL_CTX_SET_ALPN_PROTOS
-/* Define to 1 if you have the `SSL_CTX_set_alpn_select_cb' function. */
+/* Define to 1 if you have the 'SSL_CTX_set_alpn_select_cb' function. */
#undef HAVE_SSL_CTX_SET_ALPN_SELECT_CB
-/* Define to 1 if you have the `SSL_CTX_set_ciphersuites' function. */
+/* Define to 1 if you have the 'SSL_CTX_set_ciphersuites' function. */
#undef HAVE_SSL_CTX_SET_CIPHERSUITES
-/* Define to 1 if you have the `SSL_CTX_set_security_level' function. */
+/* Define to 1 if you have the 'SSL_CTX_set_security_level' function. */
#undef HAVE_SSL_CTX_SET_SECURITY_LEVEL
-/* Define to 1 if you have the `SSL_CTX_set_tlsext_ticket_key_evp_cb'
+/* Define to 1 if you have the 'SSL_CTX_set_tlsext_ticket_key_evp_cb'
function. */
#undef HAVE_SSL_CTX_SET_TLSEXT_TICKET_KEY_EVP_CB
-/* Define to 1 if you have the `SSL_CTX_set_tmp_ecdh' function. */
+/* Define to 1 if you have the 'SSL_CTX_set_tmp_ecdh' function. */
#undef HAVE_SSL_CTX_SET_TMP_ECDH
-/* Define to 1 if you have the `SSL_get0_alpn_selected' function. */
+/* Define to 1 if you have the 'SSL_get0_alpn_selected' function. */
#undef HAVE_SSL_GET0_ALPN_SELECTED
-/* Define to 1 if you have the `SSL_get0_peername' function. */
+/* Define to 1 if you have the 'SSL_get0_peername' function. */
#undef HAVE_SSL_GET0_PEERNAME
-/* Define to 1 if you have the `SSL_get1_peer_certificate' function. */
+/* Define to 1 if you have the 'SSL_get1_peer_certificate' function. */
#undef HAVE_SSL_GET1_PEER_CERTIFICATE
-/* Define to 1 if you have the `SSL_is_quic' function. */
+/* Define to 1 if you have the 'SSL_is_quic' function. */
#undef HAVE_SSL_IS_QUIC
-/* Define to 1 if you have the `SSL_set1_host' function. */
+/* Define to 1 if you have the 'SSL_set1_host' function. */
#undef HAVE_SSL_SET1_HOST
/* Define to 1 if you have the <stdarg.h> header file. */
@@ -681,7 +681,7 @@
/* Define to 1 if you have the <stdlib.h> header file. */
#undef HAVE_STDLIB_H
-/* Define to 1 if you have the `strftime' function. */
+/* Define to 1 if you have the 'strftime' function. */
#undef HAVE_STRFTIME
/* Define to 1 if you have the <strings.h> header file. */
@@ -690,39 +690,39 @@
/* Define to 1 if you have the <string.h> header file. */
#undef HAVE_STRING_H
-/* Define to 1 if you have the `strlcat' function. */
+/* Define to 1 if you have the 'strlcat' function. */
#undef HAVE_STRLCAT
-/* Define to 1 if you have the `strlcpy' function. */
+/* Define to 1 if you have the 'strlcpy' function. */
#undef HAVE_STRLCPY
-/* Define to 1 if you have the `strptime' function. */
+/* Define to 1 if you have the 'strptime' function. */
#undef HAVE_STRPTIME
-/* Define to 1 if you have the `strsep' function. */
+/* Define to 1 if you have the 'strsep' function. */
#undef HAVE_STRSEP
-/* Define to 1 if `ipi_spec_dst' is a member of `struct in_pktinfo'. */
+/* Define to 1 if 'ipi_spec_dst' is a member of 'struct in_pktinfo'. */
#undef HAVE_STRUCT_IN_PKTINFO_IPI_SPEC_DST
-/* Define to 1 if `tokenlen' is a member of `struct ngtcp2_pkt_hd'. */
+/* Define to 1 if 'tokenlen' is a member of 'struct ngtcp2_pkt_hd'. */
#undef HAVE_STRUCT_NGTCP2_PKT_HD_TOKENLEN
-/* Define to 1 if `max_tx_udp_payload_size' is a member of `struct
+/* Define to 1 if 'max_tx_udp_payload_size' is a member of 'struct
ngtcp2_settings'. */
#undef HAVE_STRUCT_NGTCP2_SETTINGS_MAX_TX_UDP_PAYLOAD_SIZE
-/* Define to 1 if `tokenlen' is a member of `struct ngtcp2_settings'. */
+/* Define to 1 if 'tokenlen' is a member of 'struct ngtcp2_settings'. */
#undef HAVE_STRUCT_NGTCP2_SETTINGS_TOKENLEN
-/* Define to 1 if `original_dcid_present' is a member of `struct
+/* Define to 1 if 'original_dcid_present' is a member of 'struct
ngtcp2_transport_params'. */
#undef HAVE_STRUCT_NGTCP2_TRANSPORT_PARAMS_ORIGINAL_DCID_PRESENT
-/* Define to 1 if the system has the type `struct ngtcp2_version_cid'. */
+/* Define to 1 if the system has the type 'struct ngtcp2_version_cid'. */
#undef HAVE_STRUCT_NGTCP2_VERSION_CID
-/* Define to 1 if `sun_len' is a member of `struct sockaddr_un'. */
+/* Define to 1 if 'sun_len' is a member of 'struct sockaddr_un'. */
#undef HAVE_STRUCT_SOCKADDR_UN_SUN_LEN
/* Define if you have Swig libraries and header files. */
@@ -782,16 +782,16 @@
/* Define to 1 if you have the <time.h> header file. */
#undef HAVE_TIME_H
-/* Define to 1 if you have the `tzset' function. */
+/* Define to 1 if you have the 'tzset' function. */
#undef HAVE_TZSET
/* Define to 1 if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H
-/* Define to 1 if you have the `usleep' function. */
+/* Define to 1 if you have the 'usleep' function. */
#undef HAVE_USLEEP
-/* Define to 1 if you have the `vfork' function. */
+/* Define to 1 if you have the 'vfork' function. */
#undef HAVE_VFORK
/* Define to 1 if you have the <vfork.h> header file. */
@@ -809,22 +809,22 @@
/* Define to 1 if you have the <winsock2.h> header file. */
#undef HAVE_WINSOCK2_H
-/* Define to 1 if `fork' works. */
+/* Define to 1 if 'fork' works. */
#undef HAVE_WORKING_FORK
-/* Define to 1 if `vfork' works. */
+/* Define to 1 if 'vfork' works. */
#undef HAVE_WORKING_VFORK
-/* Define to 1 if you have the `writev' function. */
+/* Define to 1 if you have the 'writev' function. */
#undef HAVE_WRITEV
/* Define to 1 if you have the <ws2tcpip.h> header file. */
#undef HAVE_WS2TCPIP_H
-/* Define to 1 if you have the `X509_VERIFY_PARAM_set1_host' function. */
+/* Define to 1 if you have the 'X509_VERIFY_PARAM_set1_host' function. */
#undef HAVE_X509_VERIFY_PARAM_SET1_HOST
-/* Define to 1 if you have the `_beginthreadex' function. */
+/* Define to 1 if you have the '_beginthreadex' function. */
#undef HAVE__BEGINTHREADEX
/* If HMAC_Init_ex() returns void */
@@ -923,16 +923,16 @@
/* Shared data */
#undef SHARE_DIR
-/* The size of `pthread_t', as computed by sizeof. */
+/* The size of 'pthread_t', as computed by sizeof. */
#undef SIZEOF_PTHREAD_T
-/* The size of `size_t', as computed by sizeof. */
+/* The size of 'size_t', as computed by sizeof. */
#undef SIZEOF_SIZE_T
-/* The size of `time_t', as computed by sizeof. */
+/* The size of 'time_t', as computed by sizeof. */
#undef SIZEOF_TIME_T
-/* The size of `unsigned long', as computed by sizeof. */
+/* The size of 'unsigned long', as computed by sizeof. */
#undef SIZEOF_UNSIGNED_LONG
/* define if (v)snprintf does not return length needed, (but length used) */
@@ -941,7 +941,7 @@
/* Define to 1 if libsodium supports sodium_set_misuse_handler */
#undef SODIUM_MISUSE_HANDLER
-/* Define to 1 if all of the C90 standard headers exist (not just the ones
+/* Define to 1 if all of the C89 standard headers exist (not just the ones
required in a freestanding environment). This macro is provided for
backward compatibility; new code need not use it. */
#undef STDC_HEADERS
@@ -1035,7 +1035,7 @@
/* Define this to enable SHA256 and SHA512 support. */
#undef USE_SHA2
-/* Enable extensions on AIX 3, Interix. */
+/* Enable extensions on AIX, Interix, z/OS. */
#ifndef _ALL_SOURCE
# undef _ALL_SOURCE
#endif
@@ -1096,11 +1096,15 @@
#ifndef __STDC_WANT_IEC_60559_DFP_EXT__
# undef __STDC_WANT_IEC_60559_DFP_EXT__
#endif
+/* Enable extensions specified by C23 Annex F. */
+#ifndef __STDC_WANT_IEC_60559_EXT__
+# undef __STDC_WANT_IEC_60559_EXT__
+#endif
/* Enable extensions specified by ISO/IEC TS 18661-4:2015. */
#ifndef __STDC_WANT_IEC_60559_FUNCS_EXT__
# undef __STDC_WANT_IEC_60559_FUNCS_EXT__
#endif
-/* Enable extensions specified by ISO/IEC TS 18661-3:2015. */
+/* Enable extensions specified by C23 Annex H and ISO/IEC TS 18661-3:2015. */
#ifndef __STDC_WANT_IEC_60559_TYPES_EXT__
# undef __STDC_WANT_IEC_60559_TYPES_EXT__
#endif
@@ -1141,30 +1145,36 @@
/* Define if you want PyUnbound. */
#undef WITH_PYUNBOUND
-/* Define to 1 if `lex' declares `yytext' as a `char *' by default, not a
- `char[]'. */
+/* Define to 1 if 'lex' declares 'yytext' as a 'char *' by default, not a
+ 'char[]'. */
#undef YYTEXT_POINTER
/* Number of bits in a file offset, on hosts where this is settable. */
#undef _FILE_OFFSET_BITS
-/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */
+/* Define to 1 if necessary to make fseeko visible. */
#undef _LARGEFILE_SOURCE
-/* Define for large files, on AIX-style hosts. */
+/* Define to 1 on platforms where this makes off_t a 64-bit type. */
#undef _LARGE_FILES
/* Enable for compile on Minix */
#undef _NETBSD_SOURCE
+/* Number of bits in time_t, on hosts where this is settable. */
+#undef _TIME_BITS
+
+/* Define to 1 on platforms where this makes time_t a 64-bit type. */
+#undef __MINGW_USE_VC2005_COMPAT
+
/* defined to use gcc ansi snprintf and sscanf that understands %lld when
compiled for windows. */
#undef __USE_MINGW_ANSI_STDIO
-/* Define to empty if `const' does not conform to ANSI C. */
+/* Define to empty if 'const' does not conform to ANSI C. */
#undef const
-/* Define to `int' if <sys/types.h> doesn't define. */
+/* Define as 'int' if <sys/types.h> doesn't define. */
#undef gid_t
/* in_addr_t */
@@ -1173,28 +1183,28 @@
/* in_port_t */
#undef in_port_t
-/* Define to `__inline__' or `__inline' if that's what the C compiler
+/* Define to '__inline__' or '__inline' if that's what the C compiler
calls it, or to nothing if 'inline' is not supported under any name. */
#ifndef __cplusplus
#undef inline
#endif
-/* Define to `short' if <sys/types.h> does not define. */
+/* Define to 'short' if <sys/types.h> does not define. */
#undef int16_t
-/* Define to `int' if <sys/types.h> does not define. */
+/* Define to 'int' if <sys/types.h> does not define. */
#undef int32_t
-/* Define to `long long' if <sys/types.h> does not define. */
+/* Define to 'long long' if <sys/types.h> does not define. */
#undef int64_t
-/* Define to `signed char' if <sys/types.h> does not define. */
+/* Define to 'signed char' if <sys/types.h> does not define. */
#undef int8_t
/* Define if replacement function should be used. */
#undef malloc
-/* Define to `long int' if <sys/types.h> does not define. */
+/* Define to 'long int' if <sys/types.h> does not define. */
#undef off_t
/* Define as a signed integer type capable of holding a process identifier. */
@@ -1203,34 +1213,34 @@
/* Define to 'int' if not defined */
#undef rlim_t
-/* Define to `unsigned int' if <sys/types.h> does not define. */
+/* Define as 'unsigned int' if <stddef.h> doesn't define. */
#undef size_t
/* Define to 'int' if not defined */
#undef socklen_t
-/* Define to `int' if <sys/types.h> does not define. */
+/* Define to 'int' if <sys/types.h> does not define. */
#undef ssize_t
/* Define to 'unsigned char if not defined */
#undef u_char
-/* Define to `int' if <sys/types.h> doesn't define. */
+/* Define as 'int' if <sys/types.h> doesn't define. */
#undef uid_t
-/* Define to `unsigned short' if <sys/types.h> does not define. */
+/* Define to 'unsigned short' if <sys/types.h> does not define. */
#undef uint16_t
-/* Define to `unsigned int' if <sys/types.h> does not define. */
+/* Define to 'unsigned int' if <sys/types.h> does not define. */
#undef uint32_t
-/* Define to `unsigned long long' if <sys/types.h> does not define. */
+/* Define to 'unsigned long long' if <sys/types.h> does not define. */
#undef uint64_t
-/* Define to `unsigned char' if <sys/types.h> does not define. */
+/* Define to 'unsigned char' if <sys/types.h> does not define. */
#undef uint8_t
-/* Define as `fork' if `vfork' does not work. */
+/* Define as 'fork' if 'vfork' does not work. */
#undef vfork
#if defined(OMITTED__D_GNU_SOURCE) && !defined(_GNU_SOURCE)
diff --git a/contrib/unbound/config.sub b/contrib/unbound/config.sub
index 4aaae46f6f74..3d35cde174de 100755
--- a/contrib/unbound/config.sub
+++ b/contrib/unbound/config.sub
@@ -1,10 +1,10 @@
#! /bin/sh
# Configuration validation subroutine script.
-# Copyright 1992-2024 Free Software Foundation, Inc.
+# Copyright 1992-2025 Free Software Foundation, Inc.
# shellcheck disable=SC2006,SC2268,SC2162 # see below for rationale
-timestamp='2024-05-27'
+timestamp='2025-07-10'
# This file is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
@@ -76,7 +76,7 @@ Report bugs and patches to <config-patches@gnu.org>."
version="\
GNU config.sub ($timestamp)
-Copyright 1992-2024 Free Software Foundation, Inc.
+Copyright 1992-2025 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
@@ -145,6 +145,7 @@ case $1 in
| kfreebsd*-gnu* \
| knetbsd*-gnu* \
| kopensolaris*-gnu* \
+ | ironclad-* \
| linux-* \
| managarm-* \
| netbsd*-eabi* \
@@ -242,7 +243,6 @@ case $1 in
| rombug \
| semi \
| sequent* \
- | siemens \
| sgi* \
| siemens \
| sim \
@@ -261,7 +261,7 @@ case $1 in
basic_machine=$field1-$field2
basic_os=
;;
- zephyr*)
+ tock* | zephyr*)
basic_machine=$field1-unknown
basic_os=$field2
;;
@@ -1194,7 +1194,7 @@ case $cpu-$vendor in
xscale-* | xscalee[bl]-*)
cpu=`echo "$cpu" | sed 's/^xscale/arm/'`
;;
- arm64-* | aarch64le-*)
+ arm64-* | aarch64le-* | arm64_32-*)
cpu=aarch64
;;
@@ -1321,6 +1321,7 @@ case $cpu-$vendor in
| i960 \
| ia16 \
| ia64 \
+ | intelgt \
| ip2k \
| iq2000 \
| javascript \
@@ -1522,6 +1523,10 @@ EOF
kernel=nto
os=`echo "$basic_os" | sed -e 's|nto|qnx|'`
;;
+ ironclad*)
+ kernel=ironclad
+ os=`echo "$basic_os" | sed -e 's|ironclad|mlibc|'`
+ ;;
linux*)
kernel=linux
os=`echo "$basic_os" | sed -e 's|linux|gnu|'`
@@ -1976,6 +1981,7 @@ case $os in
| atheos* \
| auroraux* \
| aux* \
+ | banan_os* \
| beos* \
| bitrig* \
| bme* \
@@ -2022,7 +2028,6 @@ case $os in
| ios* \
| iris* \
| irix* \
- | ironclad* \
| isc* \
| its* \
| l4re* \
@@ -2118,6 +2123,7 @@ case $os in
| sysv* \
| tenex* \
| tirtos* \
+ | tock* \
| toppers* \
| tops10* \
| tops20* \
@@ -2214,6 +2220,8 @@ case $kernel-$os-$obj in
;;
uclinux-uclibc*- | uclinux-gnu*- )
;;
+ ironclad-mlibc*-)
+ ;;
managarm-mlibc*- | managarm-kernel*- )
;;
windows*-msvc*-)
@@ -2249,6 +2257,8 @@ case $kernel-$os-$obj in
;;
*-eabi*- | *-gnueabi*-)
;;
+ ios*-simulator- | tvos*-simulator- | watchos*-simulator- )
+ ;;
none--*)
# None (no kernel, i.e. freestanding / bare metal),
# can be paired with an machine code file format
@@ -2347,8 +2357,8 @@ echo "$cpu-$vendor${kernel:+-$kernel}${os:+-$os}${obj:+-$obj}"
exit
# Local variables:
-# eval: (add-hook 'before-save-hook 'time-stamp)
+# eval: (add-hook 'before-save-hook 'time-stamp nil t)
# time-stamp-start: "timestamp='"
-# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-format: "%Y-%02m-%02d"
# time-stamp-end: "'"
# End:
diff --git a/contrib/unbound/configure b/contrib/unbound/configure
index 0b78d97b16e9..c4c5de99d85d 100755
--- a/contrib/unbound/configure
+++ b/contrib/unbound/configure
@@ -1,11 +1,11 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.71 for unbound 1.23.0.
+# Generated by GNU Autoconf 2.72 for unbound 1.23.1.
#
# Report bugs to <unbound-bugs@nlnetlabs.nl or https://github.com/NLnetLabs/unbound/issues>.
#
#
-# Copyright (C) 1992-1996, 1998-2017, 2020-2021 Free Software Foundation,
+# Copyright (C) 1992-1996, 1998-2017, 2020-2023 Free Software Foundation,
# Inc.
#
#
@@ -17,7 +17,6 @@
# Be more Bourne compatible
DUALCASE=1; export DUALCASE # for MKS sh
-as_nop=:
if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1
then :
emulate sh
@@ -26,12 +25,13 @@ then :
# is contrary to our usage. Disable this feature.
alias -g '${1+"$@"}'='"$@"'
setopt NO_GLOB_SUBST
-else $as_nop
- case `(set -o) 2>/dev/null` in #(
+else case e in #(
+ e) case `(set -o) 2>/dev/null` in #(
*posix*) :
set -o posix ;; #(
*) :
;;
+esac ;;
esac
fi
@@ -103,7 +103,7 @@ IFS=$as_save_IFS
;;
esac
-# We did not find ourselves, most probably we were run as `sh COMMAND'
+# We did not find ourselves, most probably we were run as 'sh COMMAND'
# in which case we are not to be found in the path.
if test "x$as_myself" = x; then
as_myself=$0
@@ -133,15 +133,14 @@ case $- in # ((((
esac
exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
# Admittedly, this is quite paranoid, since all the known shells bail
-# out after a failed `exec'.
+# out after a failed 'exec'.
printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2
exit 255
fi
# We don't want this to propagate to other subprocesses.
{ _as_can_reexec=; unset _as_can_reexec;}
if test "x$CONFIG_SHELL" = x; then
- as_bourne_compatible="as_nop=:
-if test \${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1
+ as_bourne_compatible="if test \${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1
then :
emulate sh
NULLCMD=:
@@ -149,12 +148,13 @@ then :
# is contrary to our usage. Disable this feature.
alias -g '\${1+\"\$@\"}'='\"\$@\"'
setopt NO_GLOB_SUBST
-else \$as_nop
- case \`(set -o) 2>/dev/null\` in #(
+else case e in #(
+ e) case \`(set -o) 2>/dev/null\` in #(
*posix*) :
set -o posix ;; #(
*) :
;;
+esac ;;
esac
fi
"
@@ -172,8 +172,9 @@ as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; }
if ( set x; as_fn_ret_success y && test x = \"\$1\" )
then :
-else \$as_nop
- exitcode=1; echo positional parameters were not saved.
+else case e in #(
+ e) exitcode=1; echo positional parameters were not saved. ;;
+esac
fi
test x\$exitcode = x0 || exit 1
blah=\$(echo \$(echo blah))
@@ -195,14 +196,15 @@ test \$(( 1 + 1 )) = 2 || exit 1
if (eval "$as_required") 2>/dev/null
then :
as_have_required=yes
-else $as_nop
- as_have_required=no
+else case e in #(
+ e) as_have_required=no ;;
+esac
fi
if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null
then :
-else $as_nop
- as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+else case e in #(
+ e) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
as_found=false
for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
do
@@ -235,12 +237,13 @@ IFS=$as_save_IFS
if $as_found
then :
-else $as_nop
- if { test -f "$SHELL" || test -f "$SHELL.exe"; } &&
+else case e in #(
+ e) if { test -f "$SHELL" || test -f "$SHELL.exe"; } &&
as_run=a "$SHELL" -c "$as_bourne_compatible""$as_required" 2>/dev/null
then :
CONFIG_SHELL=$SHELL as_have_required=yes
-fi
+fi ;;
+esac
fi
@@ -262,7 +265,7 @@ case $- in # ((((
esac
exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"}
# Admittedly, this is quite paranoid, since all the known shells bail
-# out after a failed `exec'.
+# out after a failed 'exec'.
printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2
exit 255
fi
@@ -283,7 +286,8 @@ $0: message. Then install a modern shell, or manually run
$0: the script under such a shell if you do have one."
fi
exit 1
-fi
+fi ;;
+esac
fi
fi
SHELL=${CONFIG_SHELL-/bin/sh}
@@ -322,14 +326,6 @@ as_fn_exit ()
as_fn_set_status $1
exit $1
} # as_fn_exit
-# as_fn_nop
-# ---------
-# Do nothing but, unlike ":", preserve the value of $?.
-as_fn_nop ()
-{
- return $?
-}
-as_nop=as_fn_nop
# as_fn_mkdir_p
# -------------
@@ -398,11 +394,12 @@ then :
{
eval $1+=\$2
}'
-else $as_nop
- as_fn_append ()
+else case e in #(
+ e) as_fn_append ()
{
eval $1=\$$1\$2
- }
+ } ;;
+esac
fi # as_fn_append
# as_fn_arith ARG...
@@ -416,21 +413,14 @@ then :
{
as_val=$(( $* ))
}'
-else $as_nop
- as_fn_arith ()
+else case e in #(
+ e) as_fn_arith ()
{
as_val=`expr "$@" || test $? -eq 1`
- }
+ } ;;
+esac
fi # as_fn_arith
-# as_fn_nop
-# ---------
-# Do nothing but, unlike ":", preserve the value of $?.
-as_fn_nop ()
-{
- return $?
-}
-as_nop=as_fn_nop
# as_fn_error STATUS ERROR [LINENO LOG_FD]
# ----------------------------------------
@@ -504,6 +494,8 @@ as_cr_alnum=$as_cr_Letters$as_cr_digits
/[$]LINENO/=
' <$as_myself |
sed '
+ t clear
+ :clear
s/[$]LINENO.*/&-/
t lineno
b
@@ -552,7 +544,6 @@ esac
as_echo='printf %s\n'
as_echo_n='printf %s'
-
rm -f conf$$ conf$$.exe conf$$.file
if test -d conf$$.dir; then
rm -f conf$$.dir/conf$$.file
@@ -564,9 +555,9 @@ if (echo >conf$$.file) 2>/dev/null; then
if ln -s conf$$.file conf$$ 2>/dev/null; then
as_ln_s='ln -s'
# ... but there are two gotchas:
- # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
- # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
- # In both cases, we have to default to `cp -pR'.
+ # 1) On MSYS, both 'ln -s file dir' and 'ln file dir' fail.
+ # 2) DJGPP < 2.04 has no symlinks; 'ln -s' creates a wrapper executable.
+ # In both cases, we have to default to 'cp -pR'.
ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
as_ln_s='cp -pR'
elif ln conf$$.file conf$$ 2>/dev/null; then
@@ -591,10 +582,12 @@ as_test_x='test -x'
as_executable_p=as_fn_executable_p
# Sed expression to map a string onto a valid CPP name.
-as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+as_sed_cpp="y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g"
+as_tr_cpp="eval sed '$as_sed_cpp'" # deprecated
# Sed expression to map a string onto a valid variable name.
-as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+as_sed_sh="y%*+%pp%;s%[^_$as_cr_alnum]%_%g"
+as_tr_sh="eval sed '$as_sed_sh'" # deprecated
SHELL=${CONFIG_SHELL-/bin/sh}
@@ -622,8 +615,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='unbound'
PACKAGE_TARNAME='unbound'
-PACKAGE_VERSION='1.23.0'
-PACKAGE_STRING='unbound 1.23.0'
+PACKAGE_VERSION='1.23.1'
+PACKAGE_STRING='unbound 1.23.1'
PACKAGE_BUGREPORT='unbound-bugs@nlnetlabs.nl or https://github.com/NLnetLabs/unbound/issues'
PACKAGE_URL=''
@@ -660,6 +653,7 @@ ac_includes_default="\
ac_header_c_list=
ac_func_c_list=
+enable_year2038=no
ac_subst_vars='LTLIBOBJS
date
version
@@ -746,6 +740,7 @@ PTHREAD_LIBS
PTHREAD_CXX
PTHREAD_CC
ax_pthread_config
+CPP
ASYNCLOOK_ALLOCCHECK_EXTRA_OBJ
SLDNS_ALLOCCHECK_EXTRA_OBJ
USE_SYSTEMD_FALSE
@@ -756,7 +751,6 @@ SYSTEMD_LIBS
SYSTEMD_CFLAGS
RUNTIME_PATH
LIBOBJS
-CPP
PKG_CONFIG_LIBDIR
PKG_CONFIG_PATH
PKG_CONFIG
@@ -939,6 +933,7 @@ with_libmnl
enable_explicit_port_randomisation
enable_linux_ip_local_port_range
with_libunbound_only
+enable_year2038
'
ac_precious_vars='build_alias
host_alias
@@ -954,11 +949,11 @@ LT_SYS_LIBRARY_PATH
PKG_CONFIG
PKG_CONFIG_PATH
PKG_CONFIG_LIBDIR
-CPP
SYSTEMD_CFLAGS
SYSTEMD_LIBS
SYSTEMD_DAEMON_CFLAGS
SYSTEMD_DAEMON_LIBS
+CPP
PYTHON_VERSION
SOURCE_DATE_EPOCH
PROTOBUFC_CFLAGS
@@ -1071,7 +1066,7 @@ do
ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'`
# Reject names that are not valid shell variable names.
expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
- as_fn_error $? "invalid feature name: \`$ac_useropt'"
+ as_fn_error $? "invalid feature name: '$ac_useropt'"
ac_useropt_orig=$ac_useropt
ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'`
case $ac_user_opts in
@@ -1097,7 +1092,7 @@ do
ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`
# Reject names that are not valid shell variable names.
expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
- as_fn_error $? "invalid feature name: \`$ac_useropt'"
+ as_fn_error $? "invalid feature name: '$ac_useropt'"
ac_useropt_orig=$ac_useropt
ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'`
case $ac_user_opts in
@@ -1310,7 +1305,7 @@ do
ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`
# Reject names that are not valid shell variable names.
expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
- as_fn_error $? "invalid package name: \`$ac_useropt'"
+ as_fn_error $? "invalid package name: '$ac_useropt'"
ac_useropt_orig=$ac_useropt
ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'`
case $ac_user_opts in
@@ -1326,7 +1321,7 @@ do
ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'`
# Reject names that are not valid shell variable names.
expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null &&
- as_fn_error $? "invalid package name: \`$ac_useropt'"
+ as_fn_error $? "invalid package name: '$ac_useropt'"
ac_useropt_orig=$ac_useropt
ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'`
case $ac_user_opts in
@@ -1356,8 +1351,8 @@ do
| --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
x_libraries=$ac_optarg ;;
- -*) as_fn_error $? "unrecognized option: \`$ac_option'
-Try \`$0 --help' for more information"
+ -*) as_fn_error $? "unrecognized option: '$ac_option'
+Try '$0 --help' for more information"
;;
*=*)
@@ -1365,7 +1360,7 @@ Try \`$0 --help' for more information"
# Reject names that are not valid shell variable names.
case $ac_envvar in #(
'' | [0-9]* | *[!_$as_cr_alnum]* )
- as_fn_error $? "invalid variable name: \`$ac_envvar'" ;;
+ as_fn_error $? "invalid variable name: '$ac_envvar'" ;;
esac
eval $ac_envvar=\$ac_optarg
export $ac_envvar ;;
@@ -1415,7 +1410,7 @@ do
as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val"
done
-# There might be people who depend on the old broken behavior: `$host'
+# There might be people who depend on the old broken behavior: '$host'
# used to hold the argument of --host etc.
# FIXME: To remove some day.
build=$build_alias
@@ -1483,7 +1478,7 @@ if test ! -r "$srcdir/$ac_unique_file"; then
test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .."
as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir"
fi
-ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"
+ac_msg="sources are in $srcdir, but 'cd $srcdir' does not work"
ac_abs_confdir=`(
cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg"
pwd)`
@@ -1511,7 +1506,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
-\`configure' configures unbound 1.23.0 to adapt to many kinds of systems.
+'configure' configures unbound 1.23.1 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1525,11 +1520,11 @@ Configuration:
--help=short display options specific to this package
--help=recursive display the short help of all the included packages
-V, --version display version information and exit
- -q, --quiet, --silent do not print \`checking ...' messages
+ -q, --quiet, --silent do not print 'checking ...' messages
--cache-file=FILE cache test results in FILE [disabled]
- -C, --config-cache alias for \`--cache-file=config.cache'
+ -C, --config-cache alias for '--cache-file=config.cache'
-n, --no-create do not create output files
- --srcdir=DIR find the sources in DIR [configure dir or \`..']
+ --srcdir=DIR find the sources in DIR [configure dir or '..']
Installation directories:
--prefix=PREFIX install architecture-independent files in PREFIX
@@ -1537,10 +1532,10 @@ Installation directories:
--exec-prefix=EPREFIX install architecture-dependent files in EPREFIX
[PREFIX]
-By default, \`make install' will install all the files in
-\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify
-an installation prefix other than \`$ac_default_prefix' using \`--prefix',
-for instance \`--prefix=\$HOME'.
+By default, 'make install' will install all the files in
+'$ac_default_prefix/bin', '$ac_default_prefix/lib' etc. You can specify
+an installation prefix other than '$ac_default_prefix' using '--prefix',
+for instance '--prefix=\$HOME'.
For better control, use the options below.
@@ -1577,7 +1572,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of unbound 1.23.0:";;
+ short | recursive ) echo "Configuration of unbound 1.23.1:";;
esac
cat <<\_ACEOF
@@ -1653,6 +1648,7 @@ Optional Features:
randomness. Define this only when the target system
restricts (e.g. some of SELinux enabled
distributions) the use of non-ephemeral ports.
+ --enable-year2038 support timestamps after 2038
Optional Packages:
--with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
@@ -1728,12 +1724,12 @@ Some influential environment variables:
LIBS libraries to pass to the linker, e.g. -l<library>
CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if
you have headers in a nonstandard directory <include dir>
- YACC The `Yet Another Compiler Compiler' implementation to use.
- Defaults to the first program found out of: `bison -y', `byacc',
- `yacc'.
+ YACC The 'Yet Another Compiler Compiler' implementation to use.
+ Defaults to the first program found out of: 'bison -y', 'byacc',
+ 'yacc'.
YFLAGS The list of arguments that will be passed by default to $YACC.
This script will default YFLAGS to the empty string to avoid a
- default value of `-d' given by some make applications.
+ default value of '-d' given by some make applications.
LT_SYS_LIBRARY_PATH
User-defined run-time library search path.
PKG_CONFIG path to pkg-config utility
@@ -1741,7 +1737,6 @@ Some influential environment variables:
directories to add to pkg-config's search path
PKG_CONFIG_LIBDIR
path overriding pkg-config's built-in search path
- CPP C preprocessor
SYSTEMD_CFLAGS
C compiler flags for SYSTEMD, overriding pkg-config
SYSTEMD_LIBS
@@ -1750,6 +1745,7 @@ Some influential environment variables:
C compiler flags for SYSTEMD_DAEMON, overriding pkg-config
SYSTEMD_DAEMON_LIBS
linker flags for SYSTEMD_DAEMON, overriding pkg-config
+ CPP C preprocessor
PYTHON_VERSION
The installed Python version to use, for example '2.3'. This
string will be appended to the Python interpreter canonical
@@ -1763,7 +1759,7 @@ Some influential environment variables:
PROTOBUFC_LIBS
linker flags for PROTOBUFC, overriding pkg-config
-Use these variables to override the choices made by `configure' or to help
+Use these variables to override the choices made by 'configure' or to help
it to find libraries and programs with nonstandard names/locations.
Report bugs to <unbound-bugs@nlnetlabs.nl or https://github.com/NLnetLabs/unbound/issues>.
@@ -1830,10 +1826,10 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
-unbound configure 1.23.0
-generated by GNU Autoconf 2.71
+unbound configure 1.23.1
+generated by GNU Autoconf 2.72
-Copyright (C) 2021 Free Software Foundation, Inc.
+Copyright (C) 2023 Free Software Foundation, Inc.
This configure script is free software; the Free Software Foundation
gives unlimited permission to copy, distribute and modify it.
_ACEOF
@@ -1872,11 +1868,12 @@ printf "%s\n" "$ac_try_echo"; } >&5
} && test -s conftest.$ac_objext
then :
ac_retval=0
-else $as_nop
- printf "%s\n" "$as_me: failed program was:" >&5
+else case e in #(
+ e) printf "%s\n" "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
- ac_retval=1
+ ac_retval=1 ;;
+esac
fi
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
as_fn_set_status $ac_retval
@@ -1895,8 +1892,8 @@ printf %s "checking for $2... " >&6; }
if eval test \${$3+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+else case e in #(
+ e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$4
#include <$2>
@@ -1904,10 +1901,12 @@ _ACEOF
if ac_fn_c_try_compile "$LINENO"
then :
eval "$3=yes"
-else $as_nop
- eval "$3=no"
+else case e in #(
+ e) eval "$3=no" ;;
+esac
fi
-rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;;
+esac
fi
eval ac_res=\$$3
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
@@ -1947,11 +1946,12 @@ printf "%s\n" "$ac_try_echo"; } >&5
}
then :
ac_retval=0
-else $as_nop
- printf "%s\n" "$as_me: failed program was:" >&5
+else case e in #(
+ e) printf "%s\n" "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
- ac_retval=1
+ ac_retval=1 ;;
+esac
fi
# Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information
# created by the PGI compiler (conftest_ipa8_conftest.oo), as it would
@@ -1974,15 +1974,15 @@ printf %s "checking for $2... " >&6; }
if eval test \${$3+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+else case e in #(
+ e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Define $2 to an innocuous variant, in case <limits.h> declares $2.
For example, HP-UX 11i <limits.h> declares gettimeofday. */
#define $2 innocuous_$2
/* System header to define __stub macros and hopefully few prototypes,
- which can conflict with char $2 (); below. */
+ which can conflict with char $2 (void); below. */
#include <limits.h>
#undef $2
@@ -1993,7 +1993,7 @@ else $as_nop
#ifdef __cplusplus
extern "C"
#endif
-char $2 ();
+char $2 (void);
/* The GNU C library defines this for functions which it implements
to always fail with ENOSYS. Some functions are actually named
something starting with __ and the normal name is an alias. */
@@ -2012,11 +2012,13 @@ _ACEOF
if ac_fn_c_try_link "$LINENO"
then :
eval "$3=yes"
-else $as_nop
- eval "$3=no"
+else case e in #(
+ e) eval "$3=no" ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam \
- conftest$ac_exeext conftest.$ac_ext
+ conftest$ac_exeext conftest.$ac_ext ;;
+esac
fi
eval ac_res=\$$3
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
@@ -2037,8 +2039,8 @@ printf %s "checking for $2... " >&6; }
if eval test \${$3+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- eval "$3=no"
+else case e in #(
+ e) eval "$3=no"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$4
@@ -2068,12 +2070,14 @@ _ACEOF
if ac_fn_c_try_compile "$LINENO"
then :
-else $as_nop
- eval "$3=yes"
+else case e in #(
+ e) eval "$3=yes" ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
-rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;;
+esac
fi
eval ac_res=\$$3
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
@@ -2082,44 +2086,6 @@ printf "%s\n" "$ac_res" >&6; }
} # ac_fn_c_check_type
-# ac_fn_c_try_cpp LINENO
-# ----------------------
-# Try to preprocess conftest.$ac_ext, and return whether this succeeded.
-ac_fn_c_try_cpp ()
-{
- as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
- if { { ac_try="$ac_cpp conftest.$ac_ext"
-case "(($ac_try" in
- *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
- *) ac_try_echo=$ac_try;;
-esac
-eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
-printf "%s\n" "$ac_try_echo"; } >&5
- (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err
- ac_status=$?
- if test -s conftest.err; then
- grep -v '^ *+' conftest.err >conftest.er1
- cat conftest.er1 >&5
- mv -f conftest.er1 conftest.err
- fi
- printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
- test $ac_status = 0; } > conftest.i && {
- test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
- test ! -s conftest.err
- }
-then :
- ac_retval=0
-else $as_nop
- printf "%s\n" "$as_me: failed program was:" >&5
-sed 's/^/| /' conftest.$ac_ext >&5
-
- ac_retval=1
-fi
- eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
- as_fn_set_status $ac_retval
-
-} # ac_fn_c_try_cpp
-
# ac_fn_c_try_run LINENO
# ----------------------
# Try to run conftest.$ac_ext, and return whether this succeeded. Assumes that
@@ -2150,12 +2116,13 @@ printf "%s\n" "$ac_try_echo"; } >&5
test $ac_status = 0; }; }
then :
ac_retval=0
-else $as_nop
- printf "%s\n" "$as_me: program exited with status $ac_status" >&5
+else case e in #(
+ e) printf "%s\n" "$as_me: program exited with status $ac_status" >&5
printf "%s\n" "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
- ac_retval=$ac_status
+ ac_retval=$ac_status ;;
+esac
fi
rm -rf conftest.dSYM conftest_ipa8_conftest.oo
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
@@ -2208,18 +2175,19 @@ _ACEOF
if ac_fn_c_try_compile "$LINENO"
then :
ac_hi=$ac_mid; break
-else $as_nop
- as_fn_arith $ac_mid + 1 && ac_lo=$as_val
+else case e in #(
+ e) as_fn_arith $ac_mid + 1 && ac_lo=$as_val
if test $ac_lo -le $ac_mid; then
ac_lo= ac_hi=
break
fi
- as_fn_arith 2 '*' $ac_mid + 1 && ac_mid=$as_val
+ as_fn_arith 2 '*' $ac_mid + 1 && ac_mid=$as_val ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
done
-else $as_nop
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+else case e in #(
+ e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$4
int
@@ -2254,20 +2222,23 @@ _ACEOF
if ac_fn_c_try_compile "$LINENO"
then :
ac_lo=$ac_mid; break
-else $as_nop
- as_fn_arith '(' $ac_mid ')' - 1 && ac_hi=$as_val
+else case e in #(
+ e) as_fn_arith '(' $ac_mid ')' - 1 && ac_hi=$as_val
if test $ac_mid -le $ac_hi; then
ac_lo= ac_hi=
break
fi
- as_fn_arith 2 '*' $ac_mid && ac_mid=$as_val
+ as_fn_arith 2 '*' $ac_mid && ac_mid=$as_val ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
done
-else $as_nop
- ac_lo= ac_hi=
+else case e in #(
+ e) ac_lo= ac_hi= ;;
+esac
fi
-rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
# Binary search between lo and hi bounds.
@@ -2290,8 +2261,9 @@ _ACEOF
if ac_fn_c_try_compile "$LINENO"
then :
ac_hi=$ac_mid
-else $as_nop
- as_fn_arith '(' $ac_mid ')' + 1 && ac_lo=$as_val
+else case e in #(
+ e) as_fn_arith '(' $ac_mid ')' + 1 && ac_lo=$as_val ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
done
@@ -2339,8 +2311,9 @@ _ACEOF
if ac_fn_c_try_run "$LINENO"
then :
echo >>conftest.val; read $3 <conftest.val; ac_retval=0
-else $as_nop
- ac_retval=1
+else case e in #(
+ e) ac_retval=1 ;;
+esac
fi
rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
conftest.$ac_objext conftest.beam conftest.$ac_ext
@@ -2352,6 +2325,45 @@ rm -f conftest.val
} # ac_fn_c_compute_int
+# ac_fn_c_try_cpp LINENO
+# ----------------------
+# Try to preprocess conftest.$ac_ext, and return whether this succeeded.
+ac_fn_c_try_cpp ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ if { { ac_try="$ac_cpp conftest.$ac_ext"
+case "(($ac_try" in
+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;
+ *) ac_try_echo=$ac_try;;
+esac
+eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
+printf "%s\n" "$ac_try_echo"; } >&5
+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err
+ ac_status=$?
+ if test -s conftest.err; then
+ grep -v '^ *+' conftest.err >conftest.er1
+ cat conftest.er1 >&5
+ mv -f conftest.er1 conftest.err
+ fi
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; } > conftest.i && {
+ test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" ||
+ test ! -s conftest.err
+ }
+then :
+ ac_retval=0
+else case e in #(
+ e) printf "%s\n" "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ ac_retval=1 ;;
+esac
+fi
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+ as_fn_set_status $ac_retval
+
+} # ac_fn_c_try_cpp
+
# ac_fn_check_decl LINENO SYMBOL VAR INCLUDES EXTRA-OPTIONS FLAG-VAR
# ------------------------------------------------------------------
# Tests whether SYMBOL is declared in INCLUDES, setting cache variable VAR
@@ -2365,8 +2377,8 @@ printf %s "checking whether $as_decl_name is declared... " >&6; }
if eval test \${$3+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- as_decl_use=`echo $2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'`
+else case e in #(
+ e) as_decl_use=`echo $2|sed -e 's/(/((/' -e 's/)/) 0&/' -e 's/,/) 0& (/g'`
eval ac_save_FLAGS=\$$6
as_fn_append $6 " $5"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
@@ -2390,12 +2402,14 @@ _ACEOF
if ac_fn_c_try_compile "$LINENO"
then :
eval "$3=yes"
-else $as_nop
- eval "$3=no"
+else case e in #(
+ e) eval "$3=no" ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
eval $6=\$ac_save_FLAGS
-
+ ;;
+esac
fi
eval ac_res=\$$3
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
@@ -2416,8 +2430,8 @@ printf %s "checking for $2.$3... " >&6; }
if eval test \${$4+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+else case e in #(
+ e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$5
int
@@ -2433,8 +2447,8 @@ _ACEOF
if ac_fn_c_try_compile "$LINENO"
then :
eval "$4=yes"
-else $as_nop
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+else case e in #(
+ e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$5
int
@@ -2450,12 +2464,15 @@ _ACEOF
if ac_fn_c_try_compile "$LINENO"
then :
eval "$4=yes"
-else $as_nop
- eval "$4=no"
+else case e in #(
+ e) eval "$4=no" ;;
+esac
fi
-rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;;
+esac
fi
-rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;;
+esac
fi
eval ac_res=\$$4
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
@@ -2487,8 +2504,8 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
-It was created by unbound $as_me 1.23.0, which was
-generated by GNU Autoconf 2.71. Invocation command line was
+It was created by unbound $as_me 1.23.1, which was
+generated by GNU Autoconf 2.72. Invocation command line was
$ $0$ac_configure_args_raw
@@ -2734,10 +2751,10 @@ esac
printf "%s\n" "$as_me: loading site script $ac_site_file" >&6;}
sed 's/^/| /' "$ac_site_file" >&5
. "$ac_site_file" \
- || { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+ || { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;}
as_fn_error $? "failed to load site script $ac_site_file
-See \`config.log' for more details" "$LINENO" 5; }
+See 'config.log' for more details" "$LINENO" 5; }
fi
done
@@ -2774,9 +2791,7 @@ struct stat;
/* Most of the following tests are stolen from RCS 5.7 src/conf.sh. */
struct buf { int x; };
struct buf * (*rcsopen) (struct buf *, struct stat *, int);
-static char *e (p, i)
- char **p;
- int i;
+static char *e (char **p, int i)
{
return p[i];
}
@@ -2790,6 +2805,21 @@ static char *f (char * (*g) (char **, int), char **p, ...)
return s;
}
+/* C89 style stringification. */
+#define noexpand_stringify(a) #a
+const char *stringified = noexpand_stringify(arbitrary+token=sequence);
+
+/* C89 style token pasting. Exercises some of the corner cases that
+ e.g. old MSVC gets wrong, but not very hard. */
+#define noexpand_concat(a,b) a##b
+#define expand_concat(a,b) noexpand_concat(a,b)
+extern int vA;
+extern int vbee;
+#define aye A
+#define bee B
+int *pvA = &expand_concat(v,aye);
+int *pvbee = &noexpand_concat(v,bee);
+
/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has
function prototypes and stuff, but not \xHH hex character constants.
These do not provoke an error unfortunately, instead are silently treated
@@ -2817,16 +2847,19 @@ ok |= (argc == 0 || f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]);
# Test code for whether the C compiler supports C99 (global declarations)
ac_c_conftest_c99_globals='
-// Does the compiler advertise C99 conformance?
+/* Does the compiler advertise C99 conformance? */
#if !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L
# error "Compiler does not advertise C99 conformance"
#endif
+// See if C++-style comments work.
+
#include <stdbool.h>
extern int puts (const char *);
extern int printf (const char *, ...);
extern int dprintf (int, const char *, ...);
extern void *malloc (size_t);
+extern void free (void *);
// Check varargs macros. These examples are taken from C99 6.10.3.5.
// dprintf is used instead of fprintf to avoid needing to declare
@@ -2876,7 +2909,6 @@ typedef const char *ccp;
static inline int
test_restrict (ccp restrict text)
{
- // See if C++-style comments work.
// Iterate through items via the restricted pointer.
// Also check for declarations in for loops.
for (unsigned int i = 0; *(text+i) != '\''\0'\''; ++i)
@@ -2942,6 +2974,8 @@ ac_c_conftest_c99_main='
ia->datasize = 10;
for (int i = 0; i < ia->datasize; ++i)
ia->data[i] = i * 1.234;
+ // Work around memory leak warnings.
+ free (ia);
// Check named initializers.
struct named_init ni = {
@@ -2963,7 +2997,7 @@ ac_c_conftest_c99_main='
# Test code for whether the C compiler supports C11 (global declarations)
ac_c_conftest_c11_globals='
-// Does the compiler advertise C11 conformance?
+/* Does the compiler advertise C11 conformance? */
#if !defined __STDC_VERSION__ || __STDC_VERSION__ < 201112L
# error "Compiler does not advertise C11 conformance"
#endif
@@ -3159,8 +3193,9 @@ IFS=$as_save_IFS
if $as_found
then :
-else $as_nop
- as_fn_error $? "cannot find required auxiliary files:$ac_missing_aux_files" "$LINENO" 5
+else case e in #(
+ e) as_fn_error $? "cannot find required auxiliary files:$ac_missing_aux_files" "$LINENO" 5 ;;
+esac
fi
@@ -3188,12 +3223,12 @@ for ac_var in $ac_precious_vars; do
eval ac_new_val=\$ac_env_${ac_var}_value
case $ac_old_set,$ac_new_set in
set,)
- { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
-printf "%s\n" "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: '$ac_var' was set to '$ac_old_val' in the previous run" >&5
+printf "%s\n" "$as_me: error: '$ac_var' was set to '$ac_old_val' in the previous run" >&2;}
ac_cache_corrupted=: ;;
,set)
- { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5
-printf "%s\n" "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: '$ac_var' was not set in the previous run" >&5
+printf "%s\n" "$as_me: error: '$ac_var' was not set in the previous run" >&2;}
ac_cache_corrupted=: ;;
,);;
*)
@@ -3202,18 +3237,18 @@ printf "%s\n" "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}
ac_old_val_w=`echo x $ac_old_val`
ac_new_val_w=`echo x $ac_new_val`
if test "$ac_old_val_w" != "$ac_new_val_w"; then
- { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5
-printf "%s\n" "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: '$ac_var' has changed since the previous run:" >&5
+printf "%s\n" "$as_me: error: '$ac_var' has changed since the previous run:" >&2;}
ac_cache_corrupted=:
else
- { printf "%s\n" "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5
-printf "%s\n" "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;}
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in '$ac_var' since the previous run:" >&5
+printf "%s\n" "$as_me: warning: ignoring whitespace changes in '$ac_var' since the previous run:" >&2;}
eval $ac_var=\$ac_old_val
fi
- { printf "%s\n" "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5
-printf "%s\n" "$as_me: former value: \`$ac_old_val'" >&2;}
- { printf "%s\n" "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5
-printf "%s\n" "$as_me: current value: \`$ac_new_val'" >&2;}
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: former value: '$ac_old_val'" >&5
+printf "%s\n" "$as_me: former value: '$ac_old_val'" >&2;}
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: current value: '$ac_new_val'" >&5
+printf "%s\n" "$as_me: current value: '$ac_new_val'" >&2;}
fi;;
esac
# Pass precious variables to config.status.
@@ -3229,11 +3264,11 @@ printf "%s\n" "$as_me: current value: \`$ac_new_val'" >&2;}
fi
done
if $ac_cache_corrupted; then
- { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;}
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5
printf "%s\n" "$as_me: error: changes in the environment can compromise the build" >&2;}
- as_fn_error $? "run \`${MAKE-make} distclean' and/or \`rm $cache_file'
+ as_fn_error $? "run '${MAKE-make} distclean' and/or 'rm $cache_file'
and start over" "$LINENO" 5
fi
## -------------------- ##
@@ -3251,11 +3286,11 @@ UNBOUND_VERSION_MAJOR=1
UNBOUND_VERSION_MINOR=23
-UNBOUND_VERSION_MICRO=0
+UNBOUND_VERSION_MICRO=1
LIBUNBOUND_CURRENT=9
-LIBUNBOUND_REVISION=31
+LIBUNBOUND_REVISION=32
LIBUNBOUND_AGE=1
# 1.0.0 had 0:12:0
# 1.0.1 had 0:13:0
@@ -3354,6 +3389,7 @@ LIBUNBOUND_AGE=1
# 1.21.1 had 9:29:1
# 1.22.0 had 9:30:1
# 1.23.0 had 9:31:1
+# 1.23.1 had 9:32:1
# Current -- the number of the binary API that we're implementing
# Revision -- which iteration of the implementation of the binary
@@ -3406,8 +3442,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_prog_CC+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test -n "$CC"; then
+else case e in #(
+ e) if test -n "$CC"; then
ac_cv_prog_CC="$CC" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -3429,7 +3465,8 @@ done
done
IFS=$as_save_IFS
-fi
+fi ;;
+esac
fi
CC=$ac_cv_prog_CC
if test -n "$CC"; then
@@ -3451,8 +3488,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_prog_ac_ct_CC+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test -n "$ac_ct_CC"; then
+else case e in #(
+ e) if test -n "$ac_ct_CC"; then
ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -3474,7 +3511,8 @@ done
done
IFS=$as_save_IFS
-fi
+fi ;;
+esac
fi
ac_ct_CC=$ac_cv_prog_ac_ct_CC
if test -n "$ac_ct_CC"; then
@@ -3509,8 +3547,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_prog_CC+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test -n "$CC"; then
+else case e in #(
+ e) if test -n "$CC"; then
ac_cv_prog_CC="$CC" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -3532,7 +3570,8 @@ done
done
IFS=$as_save_IFS
-fi
+fi ;;
+esac
fi
CC=$ac_cv_prog_CC
if test -n "$CC"; then
@@ -3554,8 +3593,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_prog_CC+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test -n "$CC"; then
+else case e in #(
+ e) if test -n "$CC"; then
ac_cv_prog_CC="$CC" # Let the user override the test.
else
ac_prog_rejected=no
@@ -3594,7 +3633,8 @@ if test $ac_prog_rejected = yes; then
ac_cv_prog_CC="$as_dir$ac_word${1+' '}$@"
fi
fi
-fi
+fi ;;
+esac
fi
CC=$ac_cv_prog_CC
if test -n "$CC"; then
@@ -3618,8 +3658,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_prog_CC+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test -n "$CC"; then
+else case e in #(
+ e) if test -n "$CC"; then
ac_cv_prog_CC="$CC" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -3641,7 +3681,8 @@ done
done
IFS=$as_save_IFS
-fi
+fi ;;
+esac
fi
CC=$ac_cv_prog_CC
if test -n "$CC"; then
@@ -3667,8 +3708,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_prog_ac_ct_CC+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test -n "$ac_ct_CC"; then
+else case e in #(
+ e) if test -n "$ac_ct_CC"; then
ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -3690,7 +3731,8 @@ done
done
IFS=$as_save_IFS
-fi
+fi ;;
+esac
fi
ac_ct_CC=$ac_cv_prog_ac_ct_CC
if test -n "$ac_ct_CC"; then
@@ -3728,8 +3770,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_prog_CC+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test -n "$CC"; then
+else case e in #(
+ e) if test -n "$CC"; then
ac_cv_prog_CC="$CC" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -3751,7 +3793,8 @@ done
done
IFS=$as_save_IFS
-fi
+fi ;;
+esac
fi
CC=$ac_cv_prog_CC
if test -n "$CC"; then
@@ -3773,8 +3816,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_prog_ac_ct_CC+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test -n "$ac_ct_CC"; then
+else case e in #(
+ e) if test -n "$ac_ct_CC"; then
ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -3796,7 +3839,8 @@ done
done
IFS=$as_save_IFS
-fi
+fi ;;
+esac
fi
ac_ct_CC=$ac_cv_prog_ac_ct_CC
if test -n "$ac_ct_CC"; then
@@ -3825,10 +3869,10 @@ fi
fi
-test -z "$CC" && { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+test -z "$CC" && { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;}
as_fn_error $? "no acceptable C compiler found in \$PATH
-See \`config.log' for more details" "$LINENO" 5; }
+See 'config.log' for more details" "$LINENO" 5; }
# Provide some information about the compiler.
printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
@@ -3900,8 +3944,8 @@ printf "%s\n" "$ac_try_echo"; } >&5
printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }
then :
- # Autoconf-2.13 could set the ac_cv_exeext variable to `no'.
-# So ignore a value of `no', otherwise this would lead to `EXEEXT = no'
+ # Autoconf-2.13 could set the ac_cv_exeext variable to 'no'.
+# So ignore a value of 'no', otherwise this would lead to 'EXEEXT = no'
# in a Makefile. We should not override ac_cv_exeext if it was cached,
# so that the user can short-circuit this test for compilers unknown to
# Autoconf.
@@ -3921,7 +3965,7 @@ do
ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
fi
# We set ac_cv_exeext here because the later test for it is not
- # safe: cross compilers may not add the suffix if given an `-o'
+ # safe: cross compilers may not add the suffix if given an '-o'
# argument, so we may need to know it at that point already.
# Even if this section looks crufty: it has the advantage of
# actually working.
@@ -3932,8 +3976,9 @@ do
done
test "$ac_cv_exeext" = no && ac_cv_exeext=
-else $as_nop
- ac_file=''
+else case e in #(
+ e) ac_file='' ;;
+esac
fi
if test -z "$ac_file"
then :
@@ -3942,13 +3987,14 @@ printf "%s\n" "no" >&6; }
printf "%s\n" "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
-{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;}
as_fn_error 77 "C compiler cannot create executables
-See \`config.log' for more details" "$LINENO" 5; }
-else $as_nop
- { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-printf "%s\n" "yes" >&6; }
+See 'config.log' for more details" "$LINENO" 5; }
+else case e in #(
+ e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; } ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5
printf %s "checking for C compiler default output file name... " >&6; }
@@ -3972,10 +4018,10 @@ printf "%s\n" "$ac_try_echo"; } >&5
printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }
then :
- # If both `conftest.exe' and `conftest' are `present' (well, observable)
-# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will
-# work properly (i.e., refer to `conftest.exe'), while it won't with
-# `rm'.
+ # If both 'conftest.exe' and 'conftest' are 'present' (well, observable)
+# catch 'conftest.exe'. For instance with Cygwin, 'ls conftest' will
+# work properly (i.e., refer to 'conftest.exe'), while it won't with
+# 'rm'.
for ac_file in conftest.exe conftest conftest.*; do
test -f "$ac_file" || continue
case $ac_file in
@@ -3985,11 +4031,12 @@ for ac_file in conftest.exe conftest conftest.*; do
* ) break;;
esac
done
-else $as_nop
- { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+else case e in #(
+ e) { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;}
as_fn_error $? "cannot compute suffix of executables: cannot compile and link
-See \`config.log' for more details" "$LINENO" 5; }
+See 'config.log' for more details" "$LINENO" 5; } ;;
+esac
fi
rm -f conftest conftest$ac_cv_exeext
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5
@@ -4005,6 +4052,8 @@ int
main (void)
{
FILE *f = fopen ("conftest.out", "w");
+ if (!f)
+ return 1;
return ferror (f) || fclose (f) != 0;
;
@@ -4044,26 +4093,27 @@ printf "%s\n" "$ac_try_echo"; } >&5
if test "$cross_compiling" = maybe; then
cross_compiling=yes
else
- { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;}
as_fn_error 77 "cannot run C compiled programs.
-If you meant to cross compile, use \`--host'.
-See \`config.log' for more details" "$LINENO" 5; }
+If you meant to cross compile, use '--host'.
+See 'config.log' for more details" "$LINENO" 5; }
fi
fi
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5
printf "%s\n" "$cross_compiling" >&6; }
-rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out
+rm -f conftest.$ac_ext conftest$ac_cv_exeext \
+ conftest.o conftest.obj conftest.out
ac_clean_files=$ac_clean_files_save
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5
printf %s "checking for suffix of object files... " >&6; }
if test ${ac_cv_objext+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+else case e in #(
+ e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
@@ -4095,16 +4145,18 @@ then :
break;;
esac
done
-else $as_nop
- printf "%s\n" "$as_me: failed program was:" >&5
+else case e in #(
+ e) printf "%s\n" "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
-{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;}
as_fn_error $? "cannot compute suffix of object files: cannot compile
-See \`config.log' for more details" "$LINENO" 5; }
+See 'config.log' for more details" "$LINENO" 5; } ;;
+esac
fi
-rm -f conftest.$ac_cv_objext conftest.$ac_ext
+rm -f conftest.$ac_cv_objext conftest.$ac_ext ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5
printf "%s\n" "$ac_cv_objext" >&6; }
@@ -4115,8 +4167,8 @@ printf %s "checking whether the compiler supports GNU C... " >&6; }
if test ${ac_cv_c_compiler_gnu+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+else case e in #(
+ e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
@@ -4133,12 +4185,14 @@ _ACEOF
if ac_fn_c_try_compile "$LINENO"
then :
ac_compiler_gnu=yes
-else $as_nop
- ac_compiler_gnu=no
+else case e in #(
+ e) ac_compiler_gnu=no ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
ac_cv_c_compiler_gnu=$ac_compiler_gnu
-
+ ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5
printf "%s\n" "$ac_cv_c_compiler_gnu" >&6; }
@@ -4156,8 +4210,8 @@ printf %s "checking whether $CC accepts -g... " >&6; }
if test ${ac_cv_prog_cc_g+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- ac_save_c_werror_flag=$ac_c_werror_flag
+else case e in #(
+ e) ac_save_c_werror_flag=$ac_c_werror_flag
ac_c_werror_flag=yes
ac_cv_prog_cc_g=no
CFLAGS="-g"
@@ -4175,8 +4229,8 @@ _ACEOF
if ac_fn_c_try_compile "$LINENO"
then :
ac_cv_prog_cc_g=yes
-else $as_nop
- CFLAGS=""
+else case e in #(
+ e) CFLAGS=""
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
@@ -4191,8 +4245,8 @@ _ACEOF
if ac_fn_c_try_compile "$LINENO"
then :
-else $as_nop
- ac_c_werror_flag=$ac_save_c_werror_flag
+else case e in #(
+ e) ac_c_werror_flag=$ac_save_c_werror_flag
CFLAGS="-g"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
@@ -4209,12 +4263,15 @@ if ac_fn_c_try_compile "$LINENO"
then :
ac_cv_prog_cc_g=yes
fi
-rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;;
+esac
fi
-rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
- ac_c_werror_flag=$ac_save_c_werror_flag
+ ac_c_werror_flag=$ac_save_c_werror_flag ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5
printf "%s\n" "$ac_cv_prog_cc_g" >&6; }
@@ -4241,8 +4298,8 @@ printf %s "checking for $CC option to enable C11 features... " >&6; }
if test ${ac_cv_prog_cc_c11+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- ac_cv_prog_cc_c11=no
+else case e in #(
+ e) ac_cv_prog_cc_c11=no
ac_save_CC=$CC
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
@@ -4259,25 +4316,28 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam
test "x$ac_cv_prog_cc_c11" != "xno" && break
done
rm -f conftest.$ac_ext
-CC=$ac_save_CC
+CC=$ac_save_CC ;;
+esac
fi
if test "x$ac_cv_prog_cc_c11" = xno
then :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
printf "%s\n" "unsupported" >&6; }
-else $as_nop
- if test "x$ac_cv_prog_cc_c11" = x
+else case e in #(
+ e) if test "x$ac_cv_prog_cc_c11" = x
then :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
printf "%s\n" "none needed" >&6; }
-else $as_nop
- { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c11" >&5
+else case e in #(
+ e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c11" >&5
printf "%s\n" "$ac_cv_prog_cc_c11" >&6; }
- CC="$CC $ac_cv_prog_cc_c11"
+ CC="$CC $ac_cv_prog_cc_c11" ;;
+esac
fi
ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c11
- ac_prog_cc_stdc=c11
+ ac_prog_cc_stdc=c11 ;;
+esac
fi
fi
if test x$ac_prog_cc_stdc = xno
@@ -4287,8 +4347,8 @@ printf %s "checking for $CC option to enable C99 features... " >&6; }
if test ${ac_cv_prog_cc_c99+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- ac_cv_prog_cc_c99=no
+else case e in #(
+ e) ac_cv_prog_cc_c99=no
ac_save_CC=$CC
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
@@ -4305,25 +4365,28 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam
test "x$ac_cv_prog_cc_c99" != "xno" && break
done
rm -f conftest.$ac_ext
-CC=$ac_save_CC
+CC=$ac_save_CC ;;
+esac
fi
if test "x$ac_cv_prog_cc_c99" = xno
then :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
printf "%s\n" "unsupported" >&6; }
-else $as_nop
- if test "x$ac_cv_prog_cc_c99" = x
+else case e in #(
+ e) if test "x$ac_cv_prog_cc_c99" = x
then :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
printf "%s\n" "none needed" >&6; }
-else $as_nop
- { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5
+else case e in #(
+ e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5
printf "%s\n" "$ac_cv_prog_cc_c99" >&6; }
- CC="$CC $ac_cv_prog_cc_c99"
+ CC="$CC $ac_cv_prog_cc_c99" ;;
+esac
fi
ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c99
- ac_prog_cc_stdc=c99
+ ac_prog_cc_stdc=c99 ;;
+esac
fi
fi
if test x$ac_prog_cc_stdc = xno
@@ -4333,8 +4396,8 @@ printf %s "checking for $CC option to enable C89 features... " >&6; }
if test ${ac_cv_prog_cc_c89+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- ac_cv_prog_cc_c89=no
+else case e in #(
+ e) ac_cv_prog_cc_c89=no
ac_save_CC=$CC
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
@@ -4351,25 +4414,28 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam
test "x$ac_cv_prog_cc_c89" != "xno" && break
done
rm -f conftest.$ac_ext
-CC=$ac_save_CC
+CC=$ac_save_CC ;;
+esac
fi
if test "x$ac_cv_prog_cc_c89" = xno
then :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
printf "%s\n" "unsupported" >&6; }
-else $as_nop
- if test "x$ac_cv_prog_cc_c89" = x
+else case e in #(
+ e) if test "x$ac_cv_prog_cc_c89" = x
then :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
printf "%s\n" "none needed" >&6; }
-else $as_nop
- { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
+else case e in #(
+ e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
printf "%s\n" "$ac_cv_prog_cc_c89" >&6; }
- CC="$CC $ac_cv_prog_cc_c89"
+ CC="$CC $ac_cv_prog_cc_c89" ;;
+esac
fi
ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c89
- ac_prog_cc_stdc=c89
+ ac_prog_cc_stdc=c89 ;;
+esac
fi
fi
@@ -4420,8 +4486,8 @@ printf %s "checking whether it is safe to define __EXTENSIONS__... " >&6; }
if test ${ac_cv_safe_to_define___extensions__+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+else case e in #(
+ e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
# define __EXTENSIONS__ 1
@@ -4437,10 +4503,12 @@ _ACEOF
if ac_fn_c_try_compile "$LINENO"
then :
ac_cv_safe_to_define___extensions__=yes
-else $as_nop
- ac_cv_safe_to_define___extensions__=no
+else case e in #(
+ e) ac_cv_safe_to_define___extensions__=no ;;
+esac
fi
-rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_safe_to_define___extensions__" >&5
printf "%s\n" "$ac_cv_safe_to_define___extensions__" >&6; }
@@ -4450,8 +4518,8 @@ printf %s "checking whether _XOPEN_SOURCE should be defined... " >&6; }
if test ${ac_cv_should_define__xopen_source+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- ac_cv_should_define__xopen_source=no
+else case e in #(
+ e) ac_cv_should_define__xopen_source=no
if test $ac_cv_header_wchar_h = yes
then :
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
@@ -4470,8 +4538,8 @@ _ACEOF
if ac_fn_c_try_compile "$LINENO"
then :
-else $as_nop
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+else case e in #(
+ e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#define _XOPEN_SOURCE 500
@@ -4489,10 +4557,12 @@ if ac_fn_c_try_compile "$LINENO"
then :
ac_cv_should_define__xopen_source=yes
fi
-rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
-fi
+fi ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_should_define__xopen_source" >&5
printf "%s\n" "$ac_cv_should_define__xopen_source" >&6; }
@@ -4517,6 +4587,8 @@ printf "%s\n" "$ac_cv_should_define__xopen_source" >&6; }
printf "%s\n" "#define __STDC_WANT_IEC_60559_DFP_EXT__ 1" >>confdefs.h
+ printf "%s\n" "#define __STDC_WANT_IEC_60559_EXT__ 1" >>confdefs.h
+
printf "%s\n" "#define __STDC_WANT_IEC_60559_FUNCS_EXT__ 1" >>confdefs.h
printf "%s\n" "#define __STDC_WANT_IEC_60559_TYPES_EXT__ 1" >>confdefs.h
@@ -4536,8 +4608,9 @@ then :
printf "%s\n" "#define _POSIX_1_SOURCE 2" >>confdefs.h
-else $as_nop
- MINIX=
+else case e in #(
+ e) MINIX= ;;
+esac
fi
if test $ac_cv_safe_to_define___extensions__ = yes
then :
@@ -4632,13 +4705,14 @@ printf "%s\n" X"$ub_conf_file" |
if test ${with_run_dir+y}
then :
withval=$with_run_dir; UNBOUND_RUN_DIR="$withval"
-else $as_nop
- if test $on_mingw = no; then
+else case e in #(
+ e) if test $on_mingw = no; then
UNBOUND_RUN_DIR=`dirname "$ub_conf_file"`
else
UNBOUND_RUN_DIR=""
fi
-
+ ;;
+esac
fi
@@ -4653,13 +4727,14 @@ printf "%s\n" "#define RUN_DIR \"$hdr_run\"" >>confdefs.h
if test ${with_chroot_dir+y}
then :
withval=$with_chroot_dir; UNBOUND_CHROOT_DIR="$withval"
-else $as_nop
- if test $on_mingw = no; then
+else case e in #(
+ e) if test $on_mingw = no; then
UNBOUND_CHROOT_DIR="$UNBOUND_RUN_DIR"
else
UNBOUND_CHROOT_DIR=""
fi
-
+ ;;
+esac
fi
@@ -4674,8 +4749,9 @@ printf "%s\n" "#define CHROOT_DIR \"$hdr_chroot\"" >>confdefs.h
if test ${with_share_dir+y}
then :
withval=$with_share_dir; UNBOUND_SHARE_DIR="$withval"
-else $as_nop
- UNBOUND_SHARE_DIR="$UNBOUND_RUN_DIR"
+else case e in #(
+ e) UNBOUND_SHARE_DIR="$UNBOUND_RUN_DIR" ;;
+esac
fi
@@ -4688,13 +4764,14 @@ printf "%s\n" "#define SHARE_DIR \"$UNBOUND_SHARE_DIR\"" >>confdefs.h
if test ${with_pidfile+y}
then :
withval=$with_pidfile; UNBOUND_PIDFILE="$withval"
-else $as_nop
- if test $on_mingw = no; then
+else case e in #(
+ e) if test $on_mingw = no; then
UNBOUND_PIDFILE="$UNBOUND_RUN_DIR/unbound.pid"
else
UNBOUND_PIDFILE=""
fi
-
+ ;;
+esac
fi
@@ -4709,13 +4786,14 @@ printf "%s\n" "#define PIDFILE \"$hdr_pid\"" >>confdefs.h
if test ${with_rootkey_file+y}
then :
withval=$with_rootkey_file; UNBOUND_ROOTKEY_FILE="$withval"
-else $as_nop
- if test $on_mingw = no; then
+else case e in #(
+ e) if test $on_mingw = no; then
UNBOUND_ROOTKEY_FILE="$UNBOUND_RUN_DIR/root.key"
else
UNBOUND_ROOTKEY_FILE="C:\\Program Files\\Unbound\\root.key"
fi
-
+ ;;
+esac
fi
@@ -4730,13 +4808,14 @@ printf "%s\n" "#define ROOT_ANCHOR_FILE \"$hdr_rkey\"" >>confdefs.h
if test ${with_rootcert_file+y}
then :
withval=$with_rootcert_file; UNBOUND_ROOTCERT_FILE="$withval"
-else $as_nop
- if test $on_mingw = no; then
+else case e in #(
+ e) if test $on_mingw = no; then
UNBOUND_ROOTCERT_FILE="$UNBOUND_RUN_DIR/icannbundle.pem"
else
UNBOUND_ROOTCERT_FILE="C:\\Program Files\\Unbound\\icannbundle.pem"
fi
-
+ ;;
+esac
fi
@@ -4751,8 +4830,9 @@ printf "%s\n" "#define ROOT_CERT_FILE \"$hdr_rpem\"" >>confdefs.h
if test ${with_username+y}
then :
withval=$with_username; UNBOUND_USERNAME="$withval"
-else $as_nop
- UNBOUND_USERNAME="unbound"
+else case e in #(
+ e) UNBOUND_USERNAME="unbound" ;;
+esac
fi
@@ -4775,8 +4855,8 @@ printf %s "checking for grep that handles long lines and -e... " >&6; }
if test ${ac_cv_path_GREP+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test -z "$GREP"; then
+else case e in #(
+ e) if test -z "$GREP"; then
ac_path_GREP_found=false
# Loop through the user's path and test for each of PROGNAME-LIST
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -4795,9 +4875,10 @@ do
as_fn_executable_p "$ac_path_GREP" || continue
# Check for GNU ac_path_GREP and select it if it is found.
# Check for GNU $ac_path_GREP
-case `"$ac_path_GREP" --version 2>&1` in
+case `"$ac_path_GREP" --version 2>&1` in #(
*GNU*)
ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;;
+#(
*)
ac_count=0
printf %s 0123456789 >"conftest.in"
@@ -4832,7 +4913,8 @@ IFS=$as_save_IFS
else
ac_cv_path_GREP=$GREP
fi
-
+ ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5
printf "%s\n" "$ac_cv_path_GREP" >&6; }
@@ -4846,8 +4928,8 @@ printf %s "checking for an ANSI C-conforming const... " >&6; }
if test ${ac_cv_c_const+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+else case e in #(
+ e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
@@ -4911,10 +4993,12 @@ _ACEOF
if ac_fn_c_try_compile "$LINENO"
then :
ac_cv_c_const=yes
-else $as_nop
- ac_cv_c_const=no
+else case e in #(
+ e) ac_cv_c_const=no ;;
+esac
fi
-rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_const" >&5
printf "%s\n" "$ac_cv_c_const" >&6; }
@@ -4941,8 +5025,8 @@ cache=`echo g | sed 'y%.=/+-%___p_%'`
if eval test \${cv_prog_cc_flag_$cache+y}
then :
printf %s "(cached) " >&6
-else $as_nop
-
+else case e in #(
+ e)
echo 'void f(void){}' >conftest.c
if test -z "`$CC $CPPFLAGS $CFLAGS -g -c conftest.c 2>&1`"; then
eval "cv_prog_cc_flag_$cache=yes"
@@ -4950,7 +5034,8 @@ else
eval "cv_prog_cc_flag_$cache=no"
fi
rm -f conftest conftest.o conftest.c
-
+ ;;
+esac
fi
if eval "test \"`echo '$cv_prog_cc_flag_'$cache`\" = yes"; then
@@ -4973,8 +5058,8 @@ cache=`echo O2 | sed 'y%.=/+-%___p_%'`
if eval test \${cv_prog_cc_flag_$cache+y}
then :
printf %s "(cached) " >&6
-else $as_nop
-
+else case e in #(
+ e)
echo 'void f(void){}' >conftest.c
if test -z "`$CC $CPPFLAGS $CFLAGS -O2 -c conftest.c 2>&1`"; then
eval "cv_prog_cc_flag_$cache=yes"
@@ -4982,7 +5067,8 @@ else
eval "cv_prog_cc_flag_$cache=no"
fi
rm -f conftest conftest.o conftest.c
-
+ ;;
+esac
fi
if eval "test \"`echo '$cv_prog_cc_flag_'$cache`\" = yes"; then
@@ -5012,8 +5098,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_prog_CC+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test -n "$CC"; then
+else case e in #(
+ e) if test -n "$CC"; then
ac_cv_prog_CC="$CC" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -5035,7 +5121,8 @@ done
done
IFS=$as_save_IFS
-fi
+fi ;;
+esac
fi
CC=$ac_cv_prog_CC
if test -n "$CC"; then
@@ -5057,8 +5144,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_prog_ac_ct_CC+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test -n "$ac_ct_CC"; then
+else case e in #(
+ e) if test -n "$ac_ct_CC"; then
ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -5080,7 +5167,8 @@ done
done
IFS=$as_save_IFS
-fi
+fi ;;
+esac
fi
ac_ct_CC=$ac_cv_prog_ac_ct_CC
if test -n "$ac_ct_CC"; then
@@ -5115,8 +5203,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_prog_CC+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test -n "$CC"; then
+else case e in #(
+ e) if test -n "$CC"; then
ac_cv_prog_CC="$CC" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -5138,7 +5226,8 @@ done
done
IFS=$as_save_IFS
-fi
+fi ;;
+esac
fi
CC=$ac_cv_prog_CC
if test -n "$CC"; then
@@ -5160,8 +5249,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_prog_CC+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test -n "$CC"; then
+else case e in #(
+ e) if test -n "$CC"; then
ac_cv_prog_CC="$CC" # Let the user override the test.
else
ac_prog_rejected=no
@@ -5200,7 +5289,8 @@ if test $ac_prog_rejected = yes; then
ac_cv_prog_CC="$as_dir$ac_word${1+' '}$@"
fi
fi
-fi
+fi ;;
+esac
fi
CC=$ac_cv_prog_CC
if test -n "$CC"; then
@@ -5224,8 +5314,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_prog_CC+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test -n "$CC"; then
+else case e in #(
+ e) if test -n "$CC"; then
ac_cv_prog_CC="$CC" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -5247,7 +5337,8 @@ done
done
IFS=$as_save_IFS
-fi
+fi ;;
+esac
fi
CC=$ac_cv_prog_CC
if test -n "$CC"; then
@@ -5273,8 +5364,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_prog_ac_ct_CC+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test -n "$ac_ct_CC"; then
+else case e in #(
+ e) if test -n "$ac_ct_CC"; then
ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -5296,7 +5387,8 @@ done
done
IFS=$as_save_IFS
-fi
+fi ;;
+esac
fi
ac_ct_CC=$ac_cv_prog_ac_ct_CC
if test -n "$ac_ct_CC"; then
@@ -5334,8 +5426,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_prog_CC+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test -n "$CC"; then
+else case e in #(
+ e) if test -n "$CC"; then
ac_cv_prog_CC="$CC" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -5357,7 +5449,8 @@ done
done
IFS=$as_save_IFS
-fi
+fi ;;
+esac
fi
CC=$ac_cv_prog_CC
if test -n "$CC"; then
@@ -5379,8 +5472,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_prog_ac_ct_CC+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test -n "$ac_ct_CC"; then
+else case e in #(
+ e) if test -n "$ac_ct_CC"; then
ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -5402,7 +5495,8 @@ done
done
IFS=$as_save_IFS
-fi
+fi ;;
+esac
fi
ac_ct_CC=$ac_cv_prog_ac_ct_CC
if test -n "$ac_ct_CC"; then
@@ -5431,10 +5525,10 @@ fi
fi
-test -z "$CC" && { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+test -z "$CC" && { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;}
as_fn_error $? "no acceptable C compiler found in \$PATH
-See \`config.log' for more details" "$LINENO" 5; }
+See 'config.log' for more details" "$LINENO" 5; }
# Provide some information about the compiler.
printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
@@ -5466,8 +5560,8 @@ printf %s "checking whether the compiler supports GNU C... " >&6; }
if test ${ac_cv_c_compiler_gnu+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+else case e in #(
+ e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
@@ -5484,12 +5578,14 @@ _ACEOF
if ac_fn_c_try_compile "$LINENO"
then :
ac_compiler_gnu=yes
-else $as_nop
- ac_compiler_gnu=no
+else case e in #(
+ e) ac_compiler_gnu=no ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
ac_cv_c_compiler_gnu=$ac_compiler_gnu
-
+ ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5
printf "%s\n" "$ac_cv_c_compiler_gnu" >&6; }
@@ -5507,8 +5603,8 @@ printf %s "checking whether $CC accepts -g... " >&6; }
if test ${ac_cv_prog_cc_g+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- ac_save_c_werror_flag=$ac_c_werror_flag
+else case e in #(
+ e) ac_save_c_werror_flag=$ac_c_werror_flag
ac_c_werror_flag=yes
ac_cv_prog_cc_g=no
CFLAGS="-g"
@@ -5526,8 +5622,8 @@ _ACEOF
if ac_fn_c_try_compile "$LINENO"
then :
ac_cv_prog_cc_g=yes
-else $as_nop
- CFLAGS=""
+else case e in #(
+ e) CFLAGS=""
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
@@ -5542,8 +5638,8 @@ _ACEOF
if ac_fn_c_try_compile "$LINENO"
then :
-else $as_nop
- ac_c_werror_flag=$ac_save_c_werror_flag
+else case e in #(
+ e) ac_c_werror_flag=$ac_save_c_werror_flag
CFLAGS="-g"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
@@ -5560,12 +5656,15 @@ if ac_fn_c_try_compile "$LINENO"
then :
ac_cv_prog_cc_g=yes
fi
-rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;;
+esac
fi
-rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
- ac_c_werror_flag=$ac_save_c_werror_flag
+ ac_c_werror_flag=$ac_save_c_werror_flag ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5
printf "%s\n" "$ac_cv_prog_cc_g" >&6; }
@@ -5592,8 +5691,8 @@ printf %s "checking for $CC option to enable C11 features... " >&6; }
if test ${ac_cv_prog_cc_c11+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- ac_cv_prog_cc_c11=no
+else case e in #(
+ e) ac_cv_prog_cc_c11=no
ac_save_CC=$CC
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
@@ -5610,25 +5709,28 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam
test "x$ac_cv_prog_cc_c11" != "xno" && break
done
rm -f conftest.$ac_ext
-CC=$ac_save_CC
+CC=$ac_save_CC ;;
+esac
fi
if test "x$ac_cv_prog_cc_c11" = xno
then :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
printf "%s\n" "unsupported" >&6; }
-else $as_nop
- if test "x$ac_cv_prog_cc_c11" = x
+else case e in #(
+ e) if test "x$ac_cv_prog_cc_c11" = x
then :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
printf "%s\n" "none needed" >&6; }
-else $as_nop
- { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c11" >&5
+else case e in #(
+ e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c11" >&5
printf "%s\n" "$ac_cv_prog_cc_c11" >&6; }
- CC="$CC $ac_cv_prog_cc_c11"
+ CC="$CC $ac_cv_prog_cc_c11" ;;
+esac
fi
ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c11
- ac_prog_cc_stdc=c11
+ ac_prog_cc_stdc=c11 ;;
+esac
fi
fi
if test x$ac_prog_cc_stdc = xno
@@ -5638,8 +5740,8 @@ printf %s "checking for $CC option to enable C99 features... " >&6; }
if test ${ac_cv_prog_cc_c99+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- ac_cv_prog_cc_c99=no
+else case e in #(
+ e) ac_cv_prog_cc_c99=no
ac_save_CC=$CC
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
@@ -5656,25 +5758,28 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam
test "x$ac_cv_prog_cc_c99" != "xno" && break
done
rm -f conftest.$ac_ext
-CC=$ac_save_CC
+CC=$ac_save_CC ;;
+esac
fi
if test "x$ac_cv_prog_cc_c99" = xno
then :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
printf "%s\n" "unsupported" >&6; }
-else $as_nop
- if test "x$ac_cv_prog_cc_c99" = x
+else case e in #(
+ e) if test "x$ac_cv_prog_cc_c99" = x
then :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
printf "%s\n" "none needed" >&6; }
-else $as_nop
- { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5
+else case e in #(
+ e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5
printf "%s\n" "$ac_cv_prog_cc_c99" >&6; }
- CC="$CC $ac_cv_prog_cc_c99"
+ CC="$CC $ac_cv_prog_cc_c99" ;;
+esac
fi
ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c99
- ac_prog_cc_stdc=c99
+ ac_prog_cc_stdc=c99 ;;
+esac
fi
fi
if test x$ac_prog_cc_stdc = xno
@@ -5684,8 +5789,8 @@ printf %s "checking for $CC option to enable C89 features... " >&6; }
if test ${ac_cv_prog_cc_c89+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- ac_cv_prog_cc_c89=no
+else case e in #(
+ e) ac_cv_prog_cc_c89=no
ac_save_CC=$CC
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
@@ -5702,25 +5807,28 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam
test "x$ac_cv_prog_cc_c89" != "xno" && break
done
rm -f conftest.$ac_ext
-CC=$ac_save_CC
+CC=$ac_save_CC ;;
+esac
fi
if test "x$ac_cv_prog_cc_c89" = xno
then :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
printf "%s\n" "unsupported" >&6; }
-else $as_nop
- if test "x$ac_cv_prog_cc_c89" = x
+else case e in #(
+ e) if test "x$ac_cv_prog_cc_c89" = x
then :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
printf "%s\n" "none needed" >&6; }
-else $as_nop
- { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
+else case e in #(
+ e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
printf "%s\n" "$ac_cv_prog_cc_c89" >&6; }
- CC="$CC $ac_cv_prog_cc_c89"
+ CC="$CC $ac_cv_prog_cc_c89" ;;
+esac
fi
ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c89
- ac_prog_cc_stdc=c89
+ ac_prog_cc_stdc=c89 ;;
+esac
fi
fi
@@ -5757,8 +5865,8 @@ cache=`echo Werror | sed 'y%.=/+-%___p_%'`
if eval test \${cv_prog_cc_flag_$cache+y}
then :
printf %s "(cached) " >&6
-else $as_nop
-
+else case e in #(
+ e)
echo 'void f(void){}' >conftest.c
if test -z "`$CC $CPPFLAGS $CFLAGS -Werror -c conftest.c 2>&1`"; then
eval "cv_prog_cc_flag_$cache=yes"
@@ -5766,7 +5874,8 @@ else
eval "cv_prog_cc_flag_$cache=no"
fi
rm -f conftest conftest.o conftest.c
-
+ ;;
+esac
fi
if eval "test \"`echo '$cv_prog_cc_flag_'$cache`\" = yes"; then
@@ -5789,8 +5898,8 @@ cache=`echo Wall | sed 'y%.=/+-%___p_%'`
if eval test \${cv_prog_cc_flag_$cache+y}
then :
printf %s "(cached) " >&6
-else $as_nop
-
+else case e in #(
+ e)
echo 'void f(void){}' >conftest.c
if test -z "`$CC $CPPFLAGS $CFLAGS -Wall -c conftest.c 2>&1`"; then
eval "cv_prog_cc_flag_$cache=yes"
@@ -5798,7 +5907,8 @@ else
eval "cv_prog_cc_flag_$cache=no"
fi
rm -f conftest conftest.o conftest.c
-
+ ;;
+esac
fi
if eval "test \"`echo '$cv_prog_cc_flag_'$cache`\" = yes"; then
@@ -5823,8 +5933,8 @@ cache=`echo std=c99 | sed 'y%.=/+-%___p_%'`
if eval test \${cv_prog_cc_flag_$cache+y}
then :
printf %s "(cached) " >&6
-else $as_nop
-
+else case e in #(
+ e)
echo 'void f(void){}' >conftest.c
if test -z "`$CC $CPPFLAGS $CFLAGS -std=c99 -c conftest.c 2>&1`"; then
eval "cv_prog_cc_flag_$cache=yes"
@@ -5832,7 +5942,8 @@ else
eval "cv_prog_cc_flag_$cache=no"
fi
rm -f conftest conftest.o conftest.c
-
+ ;;
+esac
fi
if eval "test \"`echo '$cv_prog_cc_flag_'$cache`\" = yes"; then
@@ -5855,8 +5966,8 @@ cache=`echo xc99 | sed 'y%.=/+-%___p_%'`
if eval test \${cv_prog_cc_flag_$cache+y}
then :
printf %s "(cached) " >&6
-else $as_nop
-
+else case e in #(
+ e)
echo 'void f(void){}' >conftest.c
if test -z "`$CC $CPPFLAGS $CFLAGS -xc99 -c conftest.c 2>&1`"; then
eval "cv_prog_cc_flag_$cache=yes"
@@ -5864,7 +5975,8 @@ else
eval "cv_prog_cc_flag_$cache=no"
fi
rm -f conftest conftest.o conftest.c
-
+ ;;
+esac
fi
if eval "test \"`echo '$cv_prog_cc_flag_'$cache`\" = yes"; then
@@ -5901,12 +6013,12 @@ fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether we need $C99FLAG -D__EXTENSIONS__ -D_BSD_SOURCE -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600 -D_XOPEN_SOURCE_EXTENDED=1 -D_ALL_SOURCE as a flag for $CC" >&5
printf %s "checking whether we need $C99FLAG -D__EXTENSIONS__ -D_BSD_SOURCE -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600 -D_XOPEN_SOURCE_EXTENDED=1 -D_ALL_SOURCE as a flag for $CC... " >&6; }
-cache=`printf "%s\n" "$C99FLAG -D__EXTENSIONS__ -D_BSD_SOURCE -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600 -D_XOPEN_SOURCE_EXTENDED=1 -D_ALL_SOURCE" | $as_tr_sh`
+cache=`printf "%s\n" "$C99FLAG -D__EXTENSIONS__ -D_BSD_SOURCE -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600 -D_XOPEN_SOURCE_EXTENDED=1 -D_ALL_SOURCE" | sed "$as_sed_sh"`
if eval test \${cv_prog_cc_flag_needed_$cache+y}
then :
printf %s "(cached) " >&6
-else $as_nop
-
+else case e in #(
+ e)
echo '
#include "confdefs.h"
#include <stdlib.h>
@@ -5960,7 +6072,8 @@ fi
fi
rm -f conftest conftest.c conftest.o
-
+ ;;
+esac
fi
if eval "test \"`echo '$cv_prog_cc_flag_needed_'$cache`\" = yes"; then
@@ -5993,12 +6106,12 @@ fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether we need $C99FLAG -D__EXTENSIONS__ -D_BSD_SOURCE -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600 -D_ALL_SOURCE as a flag for $CC" >&5
printf %s "checking whether we need $C99FLAG -D__EXTENSIONS__ -D_BSD_SOURCE -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600 -D_ALL_SOURCE as a flag for $CC... " >&6; }
-cache=`printf "%s\n" "$C99FLAG -D__EXTENSIONS__ -D_BSD_SOURCE -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600 -D_ALL_SOURCE" | $as_tr_sh`
+cache=`printf "%s\n" "$C99FLAG -D__EXTENSIONS__ -D_BSD_SOURCE -D_DEFAULT_SOURCE -D_POSIX_C_SOURCE=200112 -D_XOPEN_SOURCE=600 -D_ALL_SOURCE" | sed "$as_sed_sh"`
if eval test \${cv_prog_cc_flag_needed_$cache+y}
then :
printf %s "(cached) " >&6
-else $as_nop
-
+else case e in #(
+ e)
echo '
#include "confdefs.h"
#include <stdlib.h>
@@ -6052,7 +6165,8 @@ fi
fi
rm -f conftest conftest.c conftest.o
-
+ ;;
+esac
fi
if eval "test \"`echo '$cv_prog_cc_flag_needed_'$cache`\" = yes"; then
@@ -6085,12 +6199,12 @@ fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether we need $C99FLAG as a flag for $CC" >&5
printf %s "checking whether we need $C99FLAG as a flag for $CC... " >&6; }
-cache=`printf "%s\n" "$C99FLAG" | $as_tr_sh`
+cache=`printf "%s\n" "$C99FLAG" | sed "$as_sed_sh"`
if eval test \${cv_prog_cc_flag_needed_$cache+y}
then :
printf %s "(cached) " >&6
-else $as_nop
-
+else case e in #(
+ e)
echo '
#include <stdbool.h>
#include <ctype.h>
@@ -6117,7 +6231,8 @@ fi
fi
rm -f conftest conftest.c conftest.o
-
+ ;;
+esac
fi
if eval "test \"`echo '$cv_prog_cc_flag_needed_'$cache`\" = yes"; then
@@ -6154,8 +6269,8 @@ cache=_D_BSD_SOURCE__D_DEFAULT_SOURCE
if eval test \${cv_prog_cc_flag_needed_$cache+y}
then :
printf %s "(cached) " >&6
-else $as_nop
-
+else case e in #(
+ e)
echo '
#include <ctype.h>
@@ -6183,7 +6298,8 @@ fi
fi
rm -f conftest conftest.c conftest.o
-
+ ;;
+esac
fi
if eval "test \"`echo '$cv_prog_cc_flag_needed_'$cache`\" = yes"; then
@@ -6220,8 +6336,8 @@ cache=_D_GNU_SOURCE
if eval test \${cv_prog_cc_flag_needed_$cache+y}
then :
printf %s "(cached) " >&6
-else $as_nop
-
+else case e in #(
+ e)
echo '
#include <netinet/in.h>
@@ -6249,7 +6365,8 @@ fi
fi
rm -f conftest conftest.c conftest.o
-
+ ;;
+esac
fi
if eval "test \"`echo '$cv_prog_cc_flag_needed_'$cache`\" = yes"; then
@@ -6289,8 +6406,8 @@ cache=_D_GNU_SOURCE__D_FRSRESGID
if eval test \${cv_prog_cc_flag_needed_$cache+y}
then :
printf %s "(cached) " >&6
-else $as_nop
-
+else case e in #(
+ e)
echo '
#include <unistd.h>
@@ -6318,7 +6435,8 @@ fi
fi
rm -f conftest conftest.c conftest.o
-
+ ;;
+esac
fi
if eval "test \"`echo '$cv_prog_cc_flag_needed_'$cache`\" = yes"; then
@@ -6355,8 +6473,8 @@ cache=_D_POSIX_C_SOURCE_200112
if eval test \${cv_prog_cc_flag_needed_$cache+y}
then :
printf %s "(cached) " >&6
-else $as_nop
-
+else case e in #(
+ e)
echo '
#include "confdefs.h"
#ifdef HAVE_TIME_H
@@ -6395,7 +6513,8 @@ fi
fi
rm -f conftest conftest.c conftest.o
-
+ ;;
+esac
fi
if eval "test \"`echo '$cv_prog_cc_flag_needed_'$cache`\" = yes"; then
@@ -6432,8 +6551,8 @@ cache=_D__EXTENSIONS__
if eval test \${cv_prog_cc_flag_needed_$cache+y}
then :
printf %s "(cached) " >&6
-else $as_nop
-
+else case e in #(
+ e)
echo '
#include "confdefs.h"
#include <stdlib.h>
@@ -6478,7 +6597,8 @@ fi
fi
rm -f conftest conftest.c conftest.o
-
+ ;;
+esac
fi
if eval "test \"`echo '$cv_prog_cc_flag_needed_'$cache`\" = yes"; then
@@ -6534,8 +6654,8 @@ cache=`echo W | sed 'y%.=/+-%___p_%'`
if eval test \${cv_prog_cc_flag_$cache+y}
then :
printf %s "(cached) " >&6
-else $as_nop
-
+else case e in #(
+ e)
echo 'void f(void){}' >conftest.c
if test -z "`$CC $CPPFLAGS $CFLAGS -W -c conftest.c 2>&1`"; then
eval "cv_prog_cc_flag_$cache=yes"
@@ -6543,7 +6663,8 @@ else
eval "cv_prog_cc_flag_$cache=no"
fi
rm -f conftest conftest.o conftest.c
-
+ ;;
+esac
fi
if eval "test \"`echo '$cv_prog_cc_flag_'$cache`\" = yes"; then
@@ -6566,8 +6687,8 @@ cache=`echo Wall | sed 'y%.=/+-%___p_%'`
if eval test \${cv_prog_cc_flag_$cache+y}
then :
printf %s "(cached) " >&6
-else $as_nop
-
+else case e in #(
+ e)
echo 'void f(void){}' >conftest.c
if test -z "`$CC $CPPFLAGS $CFLAGS -Wall -c conftest.c 2>&1`"; then
eval "cv_prog_cc_flag_$cache=yes"
@@ -6575,7 +6696,8 @@ else
eval "cv_prog_cc_flag_$cache=no"
fi
rm -f conftest conftest.o conftest.c
-
+ ;;
+esac
fi
if eval "test \"`echo '$cv_prog_cc_flag_'$cache`\" = yes"; then
@@ -6598,8 +6720,8 @@ cache=`echo Wextra | sed 'y%.=/+-%___p_%'`
if eval test \${cv_prog_cc_flag_$cache+y}
then :
printf %s "(cached) " >&6
-else $as_nop
-
+else case e in #(
+ e)
echo 'void f(void){}' >conftest.c
if test -z "`$CC $CPPFLAGS $CFLAGS -Wextra -c conftest.c 2>&1`"; then
eval "cv_prog_cc_flag_$cache=yes"
@@ -6607,7 +6729,8 @@ else
eval "cv_prog_cc_flag_$cache=no"
fi
rm -f conftest conftest.o conftest.c
-
+ ;;
+esac
fi
if eval "test \"`echo '$cv_prog_cc_flag_'$cache`\" = yes"; then
@@ -6630,8 +6753,8 @@ cache=`echo Wdeclaration-after-statement | sed 'y%.=/+-%___p_%'`
if eval test \${cv_prog_cc_flag_$cache+y}
then :
printf %s "(cached) " >&6
-else $as_nop
-
+else case e in #(
+ e)
echo 'void f(void){}' >conftest.c
if test -z "`$CC $CPPFLAGS $CFLAGS -Wdeclaration-after-statement -c conftest.c 2>&1`"; then
eval "cv_prog_cc_flag_$cache=yes"
@@ -6639,7 +6762,8 @@ else
eval "cv_prog_cc_flag_$cache=no"
fi
rm -f conftest conftest.o conftest.c
-
+ ;;
+esac
fi
if eval "test \"`echo '$cv_prog_cc_flag_'$cache`\" = yes"; then
@@ -6704,9 +6828,10 @@ printf "%s\n" "yes" >&6; }
fi
rm -f conftest conftest.c conftest.o
-else $as_nop
- CFLAGS="$BAKCFLAGS" ; { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
+else case e in #(
+ e) CFLAGS="$BAKCFLAGS" ; { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; } ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
@@ -6753,9 +6878,10 @@ printf "%s\n" "yes" >&6; }
fi
rm -f conftest conftest.c conftest.o
-else $as_nop
- LDFLAGS="$BAKLDFLAGS" ; CFLAGS="$BAKCFLAGS" ; { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
+else case e in #(
+ e) LDFLAGS="$BAKLDFLAGS" ; CFLAGS="$BAKCFLAGS" ; { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; } ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
@@ -6800,9 +6926,10 @@ printf "%s\n" "yes" >&6; }
fi
rm -f conftest conftest.c conftest.o
-else $as_nop
- LDFLAGS="$BAKLDFLAGS" ; { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
+else case e in #(
+ e) LDFLAGS="$BAKLDFLAGS" ; { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; } ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
@@ -6816,8 +6943,8 @@ printf %s "checking for inline... " >&6; }
if test ${ac_cv_c_inline+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- ac_cv_c_inline=no
+else case e in #(
+ e) ac_cv_c_inline=no
for ac_kw in inline __inline__ __inline; do
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
@@ -6835,7 +6962,8 @@ fi
rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
test "$ac_cv_c_inline" != no && break
done
-
+ ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_inline" >&5
printf "%s\n" "$ac_cv_c_inline" >&6; }
@@ -6861,8 +6989,8 @@ printf %s "checking whether the C compiler (${CC-cc}) accepts the \"format\" att
if test ${ac_cv_c_format_attribute+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- ac_cv_c_format_attribute=no
+else case e in #(
+ e) ac_cv_c_format_attribute=no
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <stdio.h>
@@ -6882,11 +7010,13 @@ _ACEOF
if ac_fn_c_try_compile "$LINENO"
then :
ac_cv_c_format_attribute="yes"
-else $as_nop
- ac_cv_c_format_attribute="no"
+else case e in #(
+ e) ac_cv_c_format_attribute="no" ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
-
+ ;;
+esac
fi
@@ -6904,8 +7034,8 @@ printf %s "checking whether the C compiler (${CC-cc}) accepts the \"unused\" att
if test ${ac_cv_c_unused_attribute+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- ac_cv_c_unused_attribute=no
+else case e in #(
+ e) ac_cv_c_unused_attribute=no
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <stdio.h>
@@ -6924,11 +7054,13 @@ _ACEOF
if ac_fn_c_try_compile "$LINENO"
then :
ac_cv_c_unused_attribute="yes"
-else $as_nop
- ac_cv_c_unused_attribute="no"
+else case e in #(
+ e) ac_cv_c_unused_attribute="no" ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
-
+ ;;
+esac
fi
@@ -6950,8 +7082,8 @@ printf %s "checking whether the C compiler (${CC-cc}) accepts the \"weak\" attri
if test ${ac_cv_c_weak_attribute+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- ac_cv_c_weak_attribute=no
+else case e in #(
+ e) ac_cv_c_weak_attribute=no
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <stdio.h>
@@ -6970,11 +7102,13 @@ _ACEOF
if ac_fn_c_try_compile "$LINENO"
then :
ac_cv_c_weak_attribute="yes"
-else $as_nop
- ac_cv_c_weak_attribute="no"
+else case e in #(
+ e) ac_cv_c_weak_attribute="no" ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
-
+ ;;
+esac
fi
@@ -7001,8 +7135,8 @@ printf %s "checking whether the C compiler (${CC-cc}) accepts the \"noreturn\" a
if test ${ac_cv_c_noreturn_attribute+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- ac_cv_c_noreturn_attribute=no
+else case e in #(
+ e) ac_cv_c_noreturn_attribute=no
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <stdio.h>
@@ -7021,11 +7155,13 @@ _ACEOF
if ac_fn_c_try_compile "$LINENO"
then :
ac_cv_c_noreturn_attribute="yes"
-else $as_nop
- ac_cv_c_noreturn_attribute="no"
+else case e in #(
+ e) ac_cv_c_noreturn_attribute="no" ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
-
+ ;;
+esac
fi
@@ -7054,8 +7190,8 @@ CFLAGS="$CFLAGS -Werror"
if test ${ac_cv_c_fallthrough_attribute+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- ac_cv_c_fallthrough_attribute=no
+else case e in #(
+ e) ac_cv_c_fallthrough_attribute=no
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <stdio.h>
@@ -7089,11 +7225,13 @@ _ACEOF
if ac_fn_c_try_compile "$LINENO"
then :
ac_cv_c_fallthrough_attribute="yes"
-else $as_nop
- ac_cv_c_fallthrough_attribute="no"
+else case e in #(
+ e) ac_cv_c_fallthrough_attribute="no" ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
-
+ ;;
+esac
fi
CFLAGS="$BAKCFLAGS"
@@ -7131,8 +7269,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_prog_LEX+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test -n "$LEX"; then
+else case e in #(
+ e) if test -n "$LEX"; then
ac_cv_prog_LEX="$LEX" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -7154,7 +7292,8 @@ done
done
IFS=$as_save_IFS
-fi
+fi ;;
+esac
fi
LEX=$ac_cv_prog_LEX
if test -n "$LEX"; then
@@ -7212,8 +7351,8 @@ printf %s "checking for lex output file root... " >&6; }
if test ${ac_cv_prog_lex_root+y}
then :
printf %s "(cached) " >&6
-else $as_nop
-
+else case e in #(
+ e)
ac_cv_prog_lex_root=unknown
{ { ac_try="$LEX conftest.l"
case "(($ac_try" in
@@ -7230,7 +7369,8 @@ if test -f lex.yy.c; then
ac_cv_prog_lex_root=lex.yy
elif test -f lexyy.c; then
ac_cv_prog_lex_root=lexyy
-fi
+fi ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_lex_root" >&5
printf "%s\n" "$ac_cv_prog_lex_root" >&6; }
@@ -7245,15 +7385,15 @@ LEX_OUTPUT_ROOT=$ac_cv_prog_lex_root
if test ${LEXLIB+y}
then :
-else $as_nop
-
+else case e in #(
+ e)
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for lex library" >&5
printf %s "checking for lex library... " >&6; }
if test ${ac_cv_lib_lex+y}
then :
printf %s "(cached) " >&6
-else $as_nop
-
+else case e in #(
+ e)
ac_save_LIBS="$LIBS"
ac_found=false
for ac_cv_lib_lex in 'none needed' -lfl -ll 'not found'; do
@@ -7283,7 +7423,8 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
fi
done
LIBS="$ac_save_LIBS"
-
+ ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_lex" >&5
printf "%s\n" "$ac_cv_lib_lex" >&6; }
@@ -7295,10 +7436,12 @@ printf "%s\n" "$as_me: WARNING: required lex library not found; giving up on $LE
elif test "$ac_cv_lib_lex" = 'none needed'
then :
LEXLIB=''
-else $as_nop
- LEXLIB=$ac_cv_lib_lex
+else case e in #(
+ e) LEXLIB=$ac_cv_lib_lex ;;
+esac
fi
-
+ ;;
+esac
fi
@@ -7310,8 +7453,8 @@ printf %s "checking whether yytext is a pointer... " >&6; }
if test ${ac_cv_prog_lex_yytext_pointer+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- # POSIX says lex can declare yytext either as a pointer or an array; the
+else case e in #(
+ e) # POSIX says lex can declare yytext either as a pointer or an array; the
# default is implementation-dependent. Figure out which it is, since
# not all implementations provide the %pointer and %array declarations.
ac_cv_prog_lex_yytext_pointer=no
@@ -7326,7 +7469,8 @@ then :
ac_cv_prog_lex_yytext_pointer=yes
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
-
+ ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_lex_yytext_pointer" >&5
printf "%s\n" "$ac_cv_prog_lex_yytext_pointer" >&6; }
@@ -7386,8 +7530,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_prog_YACC+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test -n "$YACC"; then
+else case e in #(
+ e) if test -n "$YACC"; then
ac_cv_prog_YACC="$YACC" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -7409,7 +7553,8 @@ done
done
IFS=$as_save_IFS
-fi
+fi ;;
+esac
fi
YACC=$ac_cv_prog_YACC
if test -n "$YACC"; then
@@ -7437,8 +7582,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_prog_doxygen+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test -n "$doxygen"; then
+else case e in #(
+ e) if test -n "$doxygen"; then
ac_cv_prog_doxygen="$doxygen" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -7460,7 +7605,8 @@ done
done
IFS=$as_save_IFS
-fi
+fi ;;
+esac
fi
doxygen=$ac_cv_prog_doxygen
if test -n "$doxygen"; then
@@ -7480,8 +7626,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_prog_STRIP+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test -n "$STRIP"; then
+else case e in #(
+ e) if test -n "$STRIP"; then
ac_cv_prog_STRIP="$STRIP" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -7503,7 +7649,8 @@ done
done
IFS=$as_save_IFS
-fi
+fi ;;
+esac
fi
STRIP=$ac_cv_prog_STRIP
if test -n "$STRIP"; then
@@ -7525,8 +7672,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_prog_ac_ct_STRIP+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test -n "$ac_ct_STRIP"; then
+else case e in #(
+ e) if test -n "$ac_ct_STRIP"; then
ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -7548,7 +7695,8 @@ done
done
IFS=$as_save_IFS
-fi
+fi ;;
+esac
fi
ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP
if test -n "$ac_ct_STRIP"; then
@@ -7586,15 +7734,16 @@ printf %s "checking build system type... " >&6; }
if test ${ac_cv_build+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- ac_build_alias=$build_alias
+else case e in #(
+ e) ac_build_alias=$build_alias
test "x$ac_build_alias" = x &&
ac_build_alias=`$SHELL "${ac_aux_dir}config.guess"`
test "x$ac_build_alias" = x &&
as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5
ac_cv_build=`$SHELL "${ac_aux_dir}config.sub" $ac_build_alias` ||
as_fn_error $? "$SHELL ${ac_aux_dir}config.sub $ac_build_alias failed" "$LINENO" 5
-
+ ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5
printf "%s\n" "$ac_cv_build" >&6; }
@@ -7621,14 +7770,15 @@ printf %s "checking host system type... " >&6; }
if test ${ac_cv_host+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test "x$host_alias" = x; then
+else case e in #(
+ e) if test "x$host_alias" = x; then
ac_cv_host=$ac_cv_build
else
ac_cv_host=`$SHELL "${ac_aux_dir}config.sub" $host_alias` ||
as_fn_error $? "$SHELL ${ac_aux_dir}config.sub $host_alias failed" "$LINENO" 5
fi
-
+ ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5
printf "%s\n" "$ac_cv_host" >&6; }
@@ -7678,8 +7828,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_path_AR+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- case $AR in
+else case e in #(
+ e) case $AR in
[\\/]* | ?:[\\/]*)
ac_cv_path_AR="$AR" # Let the user override the test with a path.
;;
@@ -7704,6 +7854,7 @@ done
IFS=$as_save_IFS
;;
+esac ;;
esac
fi
AR=$ac_cv_path_AR
@@ -7726,8 +7877,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_path_ac_pt_AR+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- case $ac_pt_AR in
+else case e in #(
+ e) case $ac_pt_AR in
[\\/]* | ?:[\\/]*)
ac_cv_path_ac_pt_AR="$ac_pt_AR" # Let the user override the test with a path.
;;
@@ -7752,6 +7903,7 @@ done
IFS=$as_save_IFS
;;
+esac ;;
esac
fi
ac_pt_AR=$ac_cv_path_ac_pt_AR
@@ -7883,8 +8035,8 @@ printf %s "checking for a sed that does not truncate output... " >&6; }
if test ${ac_cv_path_SED+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/
+else case e in #(
+ e) ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/
for ac_i in 1 2 3 4 5 6 7; do
ac_script="$ac_script$as_nl$ac_script"
done
@@ -7909,9 +8061,10 @@ do
as_fn_executable_p "$ac_path_SED" || continue
# Check for GNU ac_path_SED and select it if it is found.
# Check for GNU $ac_path_SED
-case `"$ac_path_SED" --version 2>&1` in
+case `"$ac_path_SED" --version 2>&1` in #(
*GNU*)
ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;;
+#(
*)
ac_count=0
printf %s 0123456789 >"conftest.in"
@@ -7946,7 +8099,8 @@ IFS=$as_save_IFS
else
ac_cv_path_SED=$SED
fi
-
+ ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_SED" >&5
printf "%s\n" "$ac_cv_path_SED" >&6; }
@@ -7971,8 +8125,8 @@ printf %s "checking for egrep... " >&6; }
if test ${ac_cv_path_EGREP+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if echo a | $GREP -E '(a|b)' >/dev/null 2>&1
+else case e in #(
+ e) if echo a | $GREP -E '(a|b)' >/dev/null 2>&1
then ac_cv_path_EGREP="$GREP -E"
else
if test -z "$EGREP"; then
@@ -7994,9 +8148,10 @@ do
as_fn_executable_p "$ac_path_EGREP" || continue
# Check for GNU ac_path_EGREP and select it if it is found.
# Check for GNU $ac_path_EGREP
-case `"$ac_path_EGREP" --version 2>&1` in
+case `"$ac_path_EGREP" --version 2>&1` in #(
*GNU*)
ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;;
+#(
*)
ac_count=0
printf %s 0123456789 >"conftest.in"
@@ -8032,20 +8187,23 @@ else
ac_cv_path_EGREP=$EGREP
fi
- fi
+ fi ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5
printf "%s\n" "$ac_cv_path_EGREP" >&6; }
EGREP="$ac_cv_path_EGREP"
+ EGREP_TRADITIONAL=$EGREP
+ ac_cv_path_EGREP_TRADITIONAL=$EGREP
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for fgrep" >&5
printf %s "checking for fgrep... " >&6; }
if test ${ac_cv_path_FGREP+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if echo 'ab*c' | $GREP -F 'ab*c' >/dev/null 2>&1
+else case e in #(
+ e) if echo 'ab*c' | $GREP -F 'ab*c' >/dev/null 2>&1
then ac_cv_path_FGREP="$GREP -F"
else
if test -z "$FGREP"; then
@@ -8067,9 +8225,10 @@ do
as_fn_executable_p "$ac_path_FGREP" || continue
# Check for GNU ac_path_FGREP and select it if it is found.
# Check for GNU $ac_path_FGREP
-case `"$ac_path_FGREP" --version 2>&1` in
+case `"$ac_path_FGREP" --version 2>&1` in #(
*GNU*)
ac_cv_path_FGREP="$ac_path_FGREP" ac_path_FGREP_found=:;;
+#(
*)
ac_count=0
printf %s 0123456789 >"conftest.in"
@@ -8105,7 +8264,8 @@ else
ac_cv_path_FGREP=$FGREP
fi
- fi
+ fi ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_FGREP" >&5
printf "%s\n" "$ac_cv_path_FGREP" >&6; }
@@ -8136,8 +8296,9 @@ test -z "$GREP" && GREP=grep
if test ${with_gnu_ld+y}
then :
withval=$with_gnu_ld; test no = "$withval" || with_gnu_ld=yes
-else $as_nop
- with_gnu_ld=no
+else case e in #(
+ e) with_gnu_ld=no ;;
+esac
fi
ac_prog=ld
@@ -8182,8 +8343,8 @@ fi
if test ${lt_cv_path_LD+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test -z "$LD"; then
+else case e in #(
+ e) if test -z "$LD"; then
lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR
for ac_dir in $PATH; do
IFS=$lt_save_ifs
@@ -8206,7 +8367,8 @@ else $as_nop
IFS=$lt_save_ifs
else
lt_cv_path_LD=$LD # Let the user override the test with a path.
-fi
+fi ;;
+esac
fi
LD=$lt_cv_path_LD
@@ -8223,8 +8385,8 @@ printf %s "checking if the linker ($LD) is GNU ld... " >&6; }
if test ${lt_cv_prog_gnu_ld+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- # I'd rather use --version here, but apparently some GNU lds only accept -v.
+else case e in #(
+ e) # I'd rather use --version here, but apparently some GNU lds only accept -v.
case `$LD -v 2>&1 </dev/null` in
*GNU* | *'with BFD'*)
lt_cv_prog_gnu_ld=yes
@@ -8232,6 +8394,7 @@ case `$LD -v 2>&1 </dev/null` in
*)
lt_cv_prog_gnu_ld=no
;;
+esac ;;
esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_gnu_ld" >&5
@@ -8251,8 +8414,8 @@ printf %s "checking for BSD- or MS-compatible name lister (nm)... " >&6; }
if test ${lt_cv_path_NM+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test -n "$NM"; then
+else case e in #(
+ e) if test -n "$NM"; then
# Let the user override the test.
lt_cv_path_NM=$NM
else
@@ -8299,7 +8462,8 @@ else
IFS=$lt_save_ifs
done
: ${lt_cv_path_NM=no}
-fi
+fi ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_NM" >&5
printf "%s\n" "$lt_cv_path_NM" >&6; }
@@ -8320,8 +8484,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_prog_DUMPBIN+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test -n "$DUMPBIN"; then
+else case e in #(
+ e) if test -n "$DUMPBIN"; then
ac_cv_prog_DUMPBIN="$DUMPBIN" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -8343,7 +8507,8 @@ done
done
IFS=$as_save_IFS
-fi
+fi ;;
+esac
fi
DUMPBIN=$ac_cv_prog_DUMPBIN
if test -n "$DUMPBIN"; then
@@ -8369,8 +8534,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_prog_ac_ct_DUMPBIN+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test -n "$ac_ct_DUMPBIN"; then
+else case e in #(
+ e) if test -n "$ac_ct_DUMPBIN"; then
ac_cv_prog_ac_ct_DUMPBIN="$ac_ct_DUMPBIN" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -8392,7 +8557,8 @@ done
done
IFS=$as_save_IFS
-fi
+fi ;;
+esac
fi
ac_ct_DUMPBIN=$ac_cv_prog_ac_ct_DUMPBIN
if test -n "$ac_ct_DUMPBIN"; then
@@ -8446,8 +8612,8 @@ printf %s "checking the name lister ($NM) interface... " >&6; }
if test ${lt_cv_nm_interface+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- lt_cv_nm_interface="BSD nm"
+else case e in #(
+ e) lt_cv_nm_interface="BSD nm"
echo "int some_variable = 0;" > conftest.$ac_ext
(eval echo "\"\$as_me:$LINENO: $ac_compile\"" >&5)
(eval "$ac_compile" 2>conftest.err)
@@ -8460,7 +8626,8 @@ else $as_nop
if $GREP 'External.*some_variable' conftest.out > /dev/null; then
lt_cv_nm_interface="MS dumpbin"
fi
- rm -f conftest*
+ rm -f conftest* ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_nm_interface" >&5
printf "%s\n" "$lt_cv_nm_interface" >&6; }
@@ -8482,8 +8649,8 @@ printf %s "checking the maximum length of command line arguments... " >&6; }
if test ${lt_cv_sys_max_cmd_len+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- i=0
+else case e in #(
+ e) i=0
teststring=ABCD
case $build_os in
@@ -8605,7 +8772,8 @@ else $as_nop
fi
;;
esac
-
+ ;;
+esac
fi
if test -n "$lt_cv_sys_max_cmd_len"; then
@@ -8662,8 +8830,8 @@ printf %s "checking how to convert $build file names to $host format... " >&6; }
if test ${lt_cv_to_host_file_cmd+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- case $host in
+else case e in #(
+ e) case $host in
*-*-mingw* )
case $build in
*-*-mingw* ) # actually msys
@@ -8694,7 +8862,8 @@ else $as_nop
lt_cv_to_host_file_cmd=func_convert_file_noop
;;
esac
-
+ ;;
+esac
fi
to_host_file_cmd=$lt_cv_to_host_file_cmd
@@ -8710,8 +8879,8 @@ printf %s "checking how to convert $build file names to toolchain format... " >&
if test ${lt_cv_to_tool_file_cmd+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- #assume ordinary cross tools, or native build.
+else case e in #(
+ e) #assume ordinary cross tools, or native build.
lt_cv_to_tool_file_cmd=func_convert_file_noop
case $host in
*-*-mingw* )
@@ -8722,7 +8891,8 @@ case $host in
esac
;;
esac
-
+ ;;
+esac
fi
to_tool_file_cmd=$lt_cv_to_tool_file_cmd
@@ -8738,8 +8908,9 @@ printf %s "checking for $LD option to reload object files... " >&6; }
if test ${lt_cv_ld_reload_flag+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- lt_cv_ld_reload_flag='-r'
+else case e in #(
+ e) lt_cv_ld_reload_flag='-r' ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_reload_flag" >&5
printf "%s\n" "$lt_cv_ld_reload_flag" >&6; }
@@ -8780,8 +8951,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_prog_OBJDUMP+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test -n "$OBJDUMP"; then
+else case e in #(
+ e) if test -n "$OBJDUMP"; then
ac_cv_prog_OBJDUMP="$OBJDUMP" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -8803,7 +8974,8 @@ done
done
IFS=$as_save_IFS
-fi
+fi ;;
+esac
fi
OBJDUMP=$ac_cv_prog_OBJDUMP
if test -n "$OBJDUMP"; then
@@ -8825,8 +8997,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_prog_ac_ct_OBJDUMP+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test -n "$ac_ct_OBJDUMP"; then
+else case e in #(
+ e) if test -n "$ac_ct_OBJDUMP"; then
ac_cv_prog_ac_ct_OBJDUMP="$ac_ct_OBJDUMP" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -8848,7 +9020,8 @@ done
done
IFS=$as_save_IFS
-fi
+fi ;;
+esac
fi
ac_ct_OBJDUMP=$ac_cv_prog_ac_ct_OBJDUMP
if test -n "$ac_ct_OBJDUMP"; then
@@ -8889,8 +9062,8 @@ printf %s "checking how to recognize dependent libraries... " >&6; }
if test ${lt_cv_deplibs_check_method+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- lt_cv_file_magic_cmd='$MAGIC_CMD'
+else case e in #(
+ e) lt_cv_file_magic_cmd='$MAGIC_CMD'
lt_cv_file_magic_test_file=
lt_cv_deplibs_check_method='unknown'
# Need to set the preceding variable on all platforms that support
@@ -9083,7 +9256,8 @@ os2*)
lt_cv_deplibs_check_method=pass_all
;;
esac
-
+ ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_deplibs_check_method" >&5
printf "%s\n" "$lt_cv_deplibs_check_method" >&6; }
@@ -9135,8 +9309,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_prog_DLLTOOL+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test -n "$DLLTOOL"; then
+else case e in #(
+ e) if test -n "$DLLTOOL"; then
ac_cv_prog_DLLTOOL="$DLLTOOL" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -9158,7 +9332,8 @@ done
done
IFS=$as_save_IFS
-fi
+fi ;;
+esac
fi
DLLTOOL=$ac_cv_prog_DLLTOOL
if test -n "$DLLTOOL"; then
@@ -9180,8 +9355,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_prog_ac_ct_DLLTOOL+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test -n "$ac_ct_DLLTOOL"; then
+else case e in #(
+ e) if test -n "$ac_ct_DLLTOOL"; then
ac_cv_prog_ac_ct_DLLTOOL="$ac_ct_DLLTOOL" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -9203,7 +9378,8 @@ done
done
IFS=$as_save_IFS
-fi
+fi ;;
+esac
fi
ac_ct_DLLTOOL=$ac_cv_prog_ac_ct_DLLTOOL
if test -n "$ac_ct_DLLTOOL"; then
@@ -9245,8 +9421,8 @@ printf %s "checking how to associate runtime and link libraries... " >&6; }
if test ${lt_cv_sharedlib_from_linklib_cmd+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- lt_cv_sharedlib_from_linklib_cmd='unknown'
+else case e in #(
+ e) lt_cv_sharedlib_from_linklib_cmd='unknown'
case $host_os in
cygwin* | mingw* | pw32* | cegcc*)
@@ -9266,7 +9442,8 @@ cygwin* | mingw* | pw32* | cegcc*)
lt_cv_sharedlib_from_linklib_cmd=$ECHO
;;
esac
-
+ ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sharedlib_from_linklib_cmd" >&5
printf "%s\n" "$lt_cv_sharedlib_from_linklib_cmd" >&6; }
@@ -9289,8 +9466,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_prog_AR+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test -n "$AR"; then
+else case e in #(
+ e) if test -n "$AR"; then
ac_cv_prog_AR="$AR" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -9312,7 +9489,8 @@ done
done
IFS=$as_save_IFS
-fi
+fi ;;
+esac
fi
AR=$ac_cv_prog_AR
if test -n "$AR"; then
@@ -9338,8 +9516,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_prog_ac_ct_AR+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test -n "$ac_ct_AR"; then
+else case e in #(
+ e) if test -n "$ac_ct_AR"; then
ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -9361,7 +9539,8 @@ done
done
IFS=$as_save_IFS
-fi
+fi ;;
+esac
fi
ac_ct_AR=$ac_cv_prog_ac_ct_AR
if test -n "$ac_ct_AR"; then
@@ -9407,8 +9586,8 @@ printf %s "checking for archiver @FILE support... " >&6; }
if test ${lt_cv_ar_at_file+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- lt_cv_ar_at_file=no
+else case e in #(
+ e) lt_cv_ar_at_file=no
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
@@ -9445,7 +9624,8 @@ then :
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
-
+ ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ar_at_file" >&5
printf "%s\n" "$lt_cv_ar_at_file" >&6; }
@@ -9470,8 +9650,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_prog_STRIP+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test -n "$STRIP"; then
+else case e in #(
+ e) if test -n "$STRIP"; then
ac_cv_prog_STRIP="$STRIP" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -9493,7 +9673,8 @@ done
done
IFS=$as_save_IFS
-fi
+fi ;;
+esac
fi
STRIP=$ac_cv_prog_STRIP
if test -n "$STRIP"; then
@@ -9515,8 +9696,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_prog_ac_ct_STRIP+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test -n "$ac_ct_STRIP"; then
+else case e in #(
+ e) if test -n "$ac_ct_STRIP"; then
ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -9538,7 +9719,8 @@ done
done
IFS=$as_save_IFS
-fi
+fi ;;
+esac
fi
ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP
if test -n "$ac_ct_STRIP"; then
@@ -9579,8 +9761,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_prog_RANLIB+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test -n "$RANLIB"; then
+else case e in #(
+ e) if test -n "$RANLIB"; then
ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -9602,7 +9784,8 @@ done
done
IFS=$as_save_IFS
-fi
+fi ;;
+esac
fi
RANLIB=$ac_cv_prog_RANLIB
if test -n "$RANLIB"; then
@@ -9624,8 +9807,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_prog_ac_ct_RANLIB+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test -n "$ac_ct_RANLIB"; then
+else case e in #(
+ e) if test -n "$ac_ct_RANLIB"; then
ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -9647,7 +9830,8 @@ done
done
IFS=$as_save_IFS
-fi
+fi ;;
+esac
fi
ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB
if test -n "$ac_ct_RANLIB"; then
@@ -9733,8 +9917,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_prog_AWK+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test -n "$AWK"; then
+else case e in #(
+ e) if test -n "$AWK"; then
ac_cv_prog_AWK="$AWK" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -9756,7 +9940,8 @@ done
done
IFS=$as_save_IFS
-fi
+fi ;;
+esac
fi
AWK=$ac_cv_prog_AWK
if test -n "$AWK"; then
@@ -9805,8 +9990,8 @@ printf %s "checking command to parse $NM output from $compiler object... " >&6;
if test ${lt_cv_sys_global_symbol_pipe+y}
then :
printf %s "(cached) " >&6
-else $as_nop
-
+else case e in #(
+ e)
# These are sane defaults that work on at least a few old systems.
# [They come from Ultrix. What could be older than Ultrix?!! ;)]
@@ -10061,7 +10246,8 @@ _LT_EOF
lt_cv_sys_global_symbol_pipe=
fi
done
-
+ ;;
+esac
fi
if test -z "$lt_cv_sys_global_symbol_pipe"; then
@@ -10125,8 +10311,9 @@ printf %s "checking for sysroot... " >&6; }
if test ${with_sysroot+y}
then :
withval=$with_sysroot;
-else $as_nop
- with_sysroot=no
+else case e in #(
+ e) with_sysroot=no ;;
+esac
fi
@@ -10161,8 +10348,8 @@ printf %s "checking for a working dd... " >&6; }
if test ${ac_cv_path_lt_DD+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- printf 0123456789abcdef0123456789abcdef >conftest.i
+else case e in #(
+ e) printf 0123456789abcdef0123456789abcdef >conftest.i
cat conftest.i conftest.i >conftest2.i
: ${lt_DD:=$DD}
if test -z "$lt_DD"; then
@@ -10198,7 +10385,8 @@ else
ac_cv_path_lt_DD=$lt_DD
fi
-rm -f conftest.i conftest2.i conftest.out
+rm -f conftest.i conftest2.i conftest.out ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_lt_DD" >&5
printf "%s\n" "$ac_cv_path_lt_DD" >&6; }
@@ -10209,8 +10397,8 @@ printf %s "checking how to truncate binary pipes... " >&6; }
if test ${lt_cv_truncate_bin+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- printf 0123456789abcdef0123456789abcdef >conftest.i
+else case e in #(
+ e) printf 0123456789abcdef0123456789abcdef >conftest.i
cat conftest.i conftest.i >conftest2.i
lt_cv_truncate_bin=
if "$ac_cv_path_lt_DD" bs=32 count=1 <conftest2.i >conftest.out 2>/dev/null; then
@@ -10218,7 +10406,8 @@ if "$ac_cv_path_lt_DD" bs=32 count=1 <conftest2.i >conftest.out 2>/dev/null; the
&& lt_cv_truncate_bin="$ac_cv_path_lt_DD bs=4096 count=1"
fi
rm -f conftest.i conftest2.i conftest.out
-test -z "$lt_cv_truncate_bin" && lt_cv_truncate_bin="$SED -e 4q"
+test -z "$lt_cv_truncate_bin" && lt_cv_truncate_bin="$SED -e 4q" ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_truncate_bin" >&5
printf "%s\n" "$lt_cv_truncate_bin" >&6; }
@@ -10428,8 +10617,8 @@ printf %s "checking whether the C compiler needs -belf... " >&6; }
if test ${lt_cv_cc_needs_belf+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- ac_ext=c
+else case e in #(
+ e) ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
@@ -10449,8 +10638,9 @@ _ACEOF
if ac_fn_c_try_link "$LINENO"
then :
lt_cv_cc_needs_belf=yes
-else $as_nop
- lt_cv_cc_needs_belf=no
+else case e in #(
+ e) lt_cv_cc_needs_belf=no ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
@@ -10459,7 +10649,8 @@ ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_c_compiler_gnu
-
+ ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_cc_needs_belf" >&5
printf "%s\n" "$lt_cv_cc_needs_belf" >&6; }
@@ -10517,8 +10708,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_prog_MANIFEST_TOOL+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test -n "$MANIFEST_TOOL"; then
+else case e in #(
+ e) if test -n "$MANIFEST_TOOL"; then
ac_cv_prog_MANIFEST_TOOL="$MANIFEST_TOOL" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -10540,7 +10731,8 @@ done
done
IFS=$as_save_IFS
-fi
+fi ;;
+esac
fi
MANIFEST_TOOL=$ac_cv_prog_MANIFEST_TOOL
if test -n "$MANIFEST_TOOL"; then
@@ -10562,8 +10754,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_prog_ac_ct_MANIFEST_TOOL+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test -n "$ac_ct_MANIFEST_TOOL"; then
+else case e in #(
+ e) if test -n "$ac_ct_MANIFEST_TOOL"; then
ac_cv_prog_ac_ct_MANIFEST_TOOL="$ac_ct_MANIFEST_TOOL" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -10585,7 +10777,8 @@ done
done
IFS=$as_save_IFS
-fi
+fi ;;
+esac
fi
ac_ct_MANIFEST_TOOL=$ac_cv_prog_ac_ct_MANIFEST_TOOL
if test -n "$ac_ct_MANIFEST_TOOL"; then
@@ -10617,15 +10810,16 @@ printf %s "checking if $MANIFEST_TOOL is a manifest tool... " >&6; }
if test ${lt_cv_path_mainfest_tool+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- lt_cv_path_mainfest_tool=no
+else case e in #(
+ e) lt_cv_path_mainfest_tool=no
echo "$as_me:$LINENO: $MANIFEST_TOOL '-?'" >&5
$MANIFEST_TOOL '-?' 2>conftest.err > conftest.out
cat conftest.err >&5
if $GREP 'Manifest Tool' conftest.out > /dev/null; then
lt_cv_path_mainfest_tool=yes
fi
- rm -f conftest*
+ rm -f conftest* ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_mainfest_tool" >&5
printf "%s\n" "$lt_cv_path_mainfest_tool" >&6; }
@@ -10648,8 +10842,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_prog_DSYMUTIL+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test -n "$DSYMUTIL"; then
+else case e in #(
+ e) if test -n "$DSYMUTIL"; then
ac_cv_prog_DSYMUTIL="$DSYMUTIL" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -10671,7 +10865,8 @@ done
done
IFS=$as_save_IFS
-fi
+fi ;;
+esac
fi
DSYMUTIL=$ac_cv_prog_DSYMUTIL
if test -n "$DSYMUTIL"; then
@@ -10693,8 +10888,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_prog_ac_ct_DSYMUTIL+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test -n "$ac_ct_DSYMUTIL"; then
+else case e in #(
+ e) if test -n "$ac_ct_DSYMUTIL"; then
ac_cv_prog_ac_ct_DSYMUTIL="$ac_ct_DSYMUTIL" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -10716,7 +10911,8 @@ done
done
IFS=$as_save_IFS
-fi
+fi ;;
+esac
fi
ac_ct_DSYMUTIL=$ac_cv_prog_ac_ct_DSYMUTIL
if test -n "$ac_ct_DSYMUTIL"; then
@@ -10750,8 +10946,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_prog_NMEDIT+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test -n "$NMEDIT"; then
+else case e in #(
+ e) if test -n "$NMEDIT"; then
ac_cv_prog_NMEDIT="$NMEDIT" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -10773,7 +10969,8 @@ done
done
IFS=$as_save_IFS
-fi
+fi ;;
+esac
fi
NMEDIT=$ac_cv_prog_NMEDIT
if test -n "$NMEDIT"; then
@@ -10795,8 +10992,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_prog_ac_ct_NMEDIT+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test -n "$ac_ct_NMEDIT"; then
+else case e in #(
+ e) if test -n "$ac_ct_NMEDIT"; then
ac_cv_prog_ac_ct_NMEDIT="$ac_ct_NMEDIT" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -10818,7 +11015,8 @@ done
done
IFS=$as_save_IFS
-fi
+fi ;;
+esac
fi
ac_ct_NMEDIT=$ac_cv_prog_ac_ct_NMEDIT
if test -n "$ac_ct_NMEDIT"; then
@@ -10852,8 +11050,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_prog_LIPO+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test -n "$LIPO"; then
+else case e in #(
+ e) if test -n "$LIPO"; then
ac_cv_prog_LIPO="$LIPO" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -10875,7 +11073,8 @@ done
done
IFS=$as_save_IFS
-fi
+fi ;;
+esac
fi
LIPO=$ac_cv_prog_LIPO
if test -n "$LIPO"; then
@@ -10897,8 +11096,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_prog_ac_ct_LIPO+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test -n "$ac_ct_LIPO"; then
+else case e in #(
+ e) if test -n "$ac_ct_LIPO"; then
ac_cv_prog_ac_ct_LIPO="$ac_ct_LIPO" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -10920,7 +11119,8 @@ done
done
IFS=$as_save_IFS
-fi
+fi ;;
+esac
fi
ac_ct_LIPO=$ac_cv_prog_ac_ct_LIPO
if test -n "$ac_ct_LIPO"; then
@@ -10954,8 +11154,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_prog_OTOOL+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test -n "$OTOOL"; then
+else case e in #(
+ e) if test -n "$OTOOL"; then
ac_cv_prog_OTOOL="$OTOOL" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -10977,7 +11177,8 @@ done
done
IFS=$as_save_IFS
-fi
+fi ;;
+esac
fi
OTOOL=$ac_cv_prog_OTOOL
if test -n "$OTOOL"; then
@@ -10999,8 +11200,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_prog_ac_ct_OTOOL+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test -n "$ac_ct_OTOOL"; then
+else case e in #(
+ e) if test -n "$ac_ct_OTOOL"; then
ac_cv_prog_ac_ct_OTOOL="$ac_ct_OTOOL" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -11022,7 +11223,8 @@ done
done
IFS=$as_save_IFS
-fi
+fi ;;
+esac
fi
ac_ct_OTOOL=$ac_cv_prog_ac_ct_OTOOL
if test -n "$ac_ct_OTOOL"; then
@@ -11056,8 +11258,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_prog_OTOOL64+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test -n "$OTOOL64"; then
+else case e in #(
+ e) if test -n "$OTOOL64"; then
ac_cv_prog_OTOOL64="$OTOOL64" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -11079,7 +11281,8 @@ done
done
IFS=$as_save_IFS
-fi
+fi ;;
+esac
fi
OTOOL64=$ac_cv_prog_OTOOL64
if test -n "$OTOOL64"; then
@@ -11101,8 +11304,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_prog_ac_ct_OTOOL64+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test -n "$ac_ct_OTOOL64"; then
+else case e in #(
+ e) if test -n "$ac_ct_OTOOL64"; then
ac_cv_prog_ac_ct_OTOOL64="$ac_ct_OTOOL64" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -11124,7 +11327,8 @@ done
done
IFS=$as_save_IFS
-fi
+fi ;;
+esac
fi
ac_ct_OTOOL64=$ac_cv_prog_ac_ct_OTOOL64
if test -n "$ac_ct_OTOOL64"; then
@@ -11181,8 +11385,8 @@ printf %s "checking for -single_module linker flag... " >&6; }
if test ${lt_cv_apple_cc_single_mod+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- lt_cv_apple_cc_single_mod=no
+else case e in #(
+ e) lt_cv_apple_cc_single_mod=no
if test -z "$LT_MULTI_MODULE"; then
# By default we will add the -single_module flag. You can override
# by either setting the environment variable LT_MULTI_MODULE
@@ -11208,7 +11412,8 @@ else $as_nop
fi
rm -rf libconftest.dylib*
rm -f conftest.*
- fi
+ fi ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_apple_cc_single_mod" >&5
printf "%s\n" "$lt_cv_apple_cc_single_mod" >&6; }
@@ -11218,8 +11423,8 @@ printf %s "checking for -exported_symbols_list linker flag... " >&6; }
if test ${lt_cv_ld_exported_symbols_list+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- lt_cv_ld_exported_symbols_list=no
+else case e in #(
+ e) lt_cv_ld_exported_symbols_list=no
save_LDFLAGS=$LDFLAGS
echo "_main" > conftest.sym
LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym"
@@ -11237,13 +11442,15 @@ _ACEOF
if ac_fn_c_try_link "$LINENO"
then :
lt_cv_ld_exported_symbols_list=yes
-else $as_nop
- lt_cv_ld_exported_symbols_list=no
+else case e in #(
+ e) lt_cv_ld_exported_symbols_list=no ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
LDFLAGS=$save_LDFLAGS
-
+ ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_exported_symbols_list" >&5
printf "%s\n" "$lt_cv_ld_exported_symbols_list" >&6; }
@@ -11253,8 +11460,8 @@ printf %s "checking for -force_load linker flag... " >&6; }
if test ${lt_cv_ld_force_load+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- lt_cv_ld_force_load=no
+else case e in #(
+ e) lt_cv_ld_force_load=no
cat > conftest.c << _LT_EOF
int forced_loaded() { return 2;}
_LT_EOF
@@ -11279,7 +11486,8 @@ _LT_EOF
fi
rm -f conftest.err libconftest.a conftest conftest.c
rm -rf conftest.dSYM
-
+ ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_force_load" >&5
printf "%s\n" "$lt_cv_ld_force_load" >&6; }
@@ -11397,8 +11605,9 @@ then :
IFS=$lt_save_ifs
;;
esac
-else $as_nop
- enable_shared=yes
+else case e in #(
+ e) enable_shared=yes ;;
+esac
fi
@@ -11429,8 +11638,9 @@ then :
IFS=$lt_save_ifs
;;
esac
-else $as_nop
- enable_static=yes
+else case e in #(
+ e) enable_static=yes ;;
+esac
fi
@@ -11461,8 +11671,9 @@ then :
IFS=$lt_save_ifs
;;
esac
-else $as_nop
- pic_mode=default
+else case e in #(
+ e) pic_mode=default ;;
+esac
fi
@@ -11492,8 +11703,9 @@ then :
IFS=$lt_save_ifs
;;
esac
-else $as_nop
- enable_fast_install=yes
+else case e in #(
+ e) enable_fast_install=yes ;;
+esac
fi
@@ -11520,15 +11732,17 @@ then :
;;
esac
lt_cv_with_aix_soname=$with_aix_soname
-else $as_nop
- if test ${lt_cv_with_aix_soname+y}
+else case e in #(
+ e) if test ${lt_cv_with_aix_soname+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- lt_cv_with_aix_soname=aix
+else case e in #(
+ e) lt_cv_with_aix_soname=aix ;;
+esac
fi
- with_aix_soname=$lt_cv_with_aix_soname
+ with_aix_soname=$lt_cv_with_aix_soname ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $with_aix_soname" >&5
@@ -11619,8 +11833,8 @@ printf %s "checking for objdir... " >&6; }
if test ${lt_cv_objdir+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- rm -f .libs 2>/dev/null
+else case e in #(
+ e) rm -f .libs 2>/dev/null
mkdir .libs 2>/dev/null
if test -d .libs; then
lt_cv_objdir=.libs
@@ -11628,7 +11842,8 @@ else
# MS-DOS does not allow filenames that begin with a dot.
lt_cv_objdir=_libs
fi
-rmdir .libs 2>/dev/null
+rmdir .libs 2>/dev/null ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_objdir" >&5
printf "%s\n" "$lt_cv_objdir" >&6; }
@@ -11689,8 +11904,8 @@ printf %s "checking for ${ac_tool_prefix}file... " >&6; }
if test ${lt_cv_path_MAGIC_CMD+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- case $MAGIC_CMD in
+else case e in #(
+ e) case $MAGIC_CMD in
[\\/*] | ?:[\\/]*)
lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path.
;;
@@ -11733,6 +11948,7 @@ _LT_EOF
IFS=$lt_save_ifs
MAGIC_CMD=$lt_save_MAGIC_CMD
;;
+esac ;;
esac
fi
@@ -11756,8 +11972,8 @@ printf %s "checking for file... " >&6; }
if test ${lt_cv_path_MAGIC_CMD+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- case $MAGIC_CMD in
+else case e in #(
+ e) case $MAGIC_CMD in
[\\/*] | ?:[\\/]*)
lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path.
;;
@@ -11800,6 +12016,7 @@ _LT_EOF
IFS=$lt_save_ifs
MAGIC_CMD=$lt_save_MAGIC_CMD
;;
+esac ;;
esac
fi
@@ -11895,8 +12112,8 @@ printf %s "checking if $compiler supports -fno-rtti -fno-exceptions... " >&6; }
if test ${lt_cv_prog_compiler_rtti_exceptions+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- lt_cv_prog_compiler_rtti_exceptions=no
+else case e in #(
+ e) lt_cv_prog_compiler_rtti_exceptions=no
ac_outfile=conftest.$ac_objext
echo "$lt_simple_compile_test_code" > conftest.$ac_ext
lt_compiler_flag="-fno-rtti -fno-exceptions" ## exclude from sc_useless_quotes_in_assignment
@@ -11924,7 +12141,8 @@ else $as_nop
fi
fi
$RM conftest*
-
+ ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_rtti_exceptions" >&5
printf "%s\n" "$lt_cv_prog_compiler_rtti_exceptions" >&6; }
@@ -12289,8 +12507,9 @@ printf %s "checking for $compiler option to produce PIC... " >&6; }
if test ${lt_cv_prog_compiler_pic+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- lt_cv_prog_compiler_pic=$lt_prog_compiler_pic
+else case e in #(
+ e) lt_cv_prog_compiler_pic=$lt_prog_compiler_pic ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic" >&5
printf "%s\n" "$lt_cv_prog_compiler_pic" >&6; }
@@ -12305,8 +12524,8 @@ printf %s "checking if $compiler PIC flag $lt_prog_compiler_pic works... " >&6;
if test ${lt_cv_prog_compiler_pic_works+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- lt_cv_prog_compiler_pic_works=no
+else case e in #(
+ e) lt_cv_prog_compiler_pic_works=no
ac_outfile=conftest.$ac_objext
echo "$lt_simple_compile_test_code" > conftest.$ac_ext
lt_compiler_flag="$lt_prog_compiler_pic -DPIC" ## exclude from sc_useless_quotes_in_assignment
@@ -12334,7 +12553,8 @@ else $as_nop
fi
fi
$RM conftest*
-
+ ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works" >&5
printf "%s\n" "$lt_cv_prog_compiler_pic_works" >&6; }
@@ -12370,8 +12590,8 @@ printf %s "checking if $compiler static flag $lt_tmp_static_flag works... " >&6;
if test ${lt_cv_prog_compiler_static_works+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- lt_cv_prog_compiler_static_works=no
+else case e in #(
+ e) lt_cv_prog_compiler_static_works=no
save_LDFLAGS=$LDFLAGS
LDFLAGS="$LDFLAGS $lt_tmp_static_flag"
echo "$lt_simple_link_test_code" > conftest.$ac_ext
@@ -12392,7 +12612,8 @@ else $as_nop
fi
$RM -r conftest*
LDFLAGS=$save_LDFLAGS
-
+ ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works" >&5
printf "%s\n" "$lt_cv_prog_compiler_static_works" >&6; }
@@ -12414,8 +12635,8 @@ printf %s "checking if $compiler supports -c -o file.$ac_objext... " >&6; }
if test ${lt_cv_prog_compiler_c_o+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- lt_cv_prog_compiler_c_o=no
+else case e in #(
+ e) lt_cv_prog_compiler_c_o=no
$RM -r conftest 2>/dev/null
mkdir conftest
cd conftest
@@ -12455,7 +12676,8 @@ else $as_nop
cd ..
$RM -r conftest
$RM conftest*
-
+ ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5
printf "%s\n" "$lt_cv_prog_compiler_c_o" >&6; }
@@ -12470,8 +12692,8 @@ printf %s "checking if $compiler supports -c -o file.$ac_objext... " >&6; }
if test ${lt_cv_prog_compiler_c_o+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- lt_cv_prog_compiler_c_o=no
+else case e in #(
+ e) lt_cv_prog_compiler_c_o=no
$RM -r conftest 2>/dev/null
mkdir conftest
cd conftest
@@ -12511,7 +12733,8 @@ else $as_nop
cd ..
$RM -r conftest
$RM conftest*
-
+ ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5
printf "%s\n" "$lt_cv_prog_compiler_c_o" >&6; }
@@ -13105,8 +13328,8 @@ else
if test ${lt_cv_aix_libpath_+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+else case e in #(
+ e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
@@ -13138,7 +13361,8 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
if test -z "$lt_cv_aix_libpath_"; then
lt_cv_aix_libpath_=/usr/lib:/lib
fi
-
+ ;;
+esac
fi
aix_libpath=$lt_cv_aix_libpath_
@@ -13160,8 +13384,8 @@ else
if test ${lt_cv_aix_libpath_+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+else case e in #(
+ e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
@@ -13193,7 +13417,8 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
if test -z "$lt_cv_aix_libpath_"; then
lt_cv_aix_libpath_=/usr/lib:/lib
fi
-
+ ;;
+esac
fi
aix_libpath=$lt_cv_aix_libpath_
@@ -13444,8 +13669,8 @@ printf %s "checking if $CC understands -b... " >&6; }
if test ${lt_cv_prog_compiler__b+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- lt_cv_prog_compiler__b=no
+else case e in #(
+ e) lt_cv_prog_compiler__b=no
save_LDFLAGS=$LDFLAGS
LDFLAGS="$LDFLAGS -b"
echo "$lt_simple_link_test_code" > conftest.$ac_ext
@@ -13466,7 +13691,8 @@ else $as_nop
fi
$RM -r conftest*
LDFLAGS=$save_LDFLAGS
-
+ ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler__b" >&5
printf "%s\n" "$lt_cv_prog_compiler__b" >&6; }
@@ -13514,8 +13740,8 @@ printf %s "checking whether the $host_os linker accepts -exported_symbol... " >&
if test ${lt_cv_irix_exported_symbol+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- save_LDFLAGS=$LDFLAGS
+else case e in #(
+ e) save_LDFLAGS=$LDFLAGS
LDFLAGS="$LDFLAGS -shared $wl-exported_symbol ${wl}foo $wl-update_registry $wl/dev/null"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
@@ -13524,12 +13750,14 @@ _ACEOF
if ac_fn_c_try_link "$LINENO"
then :
lt_cv_irix_exported_symbol=yes
-else $as_nop
- lt_cv_irix_exported_symbol=no
+else case e in #(
+ e) lt_cv_irix_exported_symbol=no ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
- LDFLAGS=$save_LDFLAGS
+ LDFLAGS=$save_LDFLAGS ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_irix_exported_symbol" >&5
printf "%s\n" "$lt_cv_irix_exported_symbol" >&6; }
@@ -13854,8 +14082,8 @@ printf %s "checking whether -lc should be explicitly linked in... " >&6; }
if test ${lt_cv_archive_cmds_need_lc+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- $RM conftest*
+else case e in #(
+ e) $RM conftest*
echo "$lt_simple_compile_test_code" > conftest.$ac_ext
if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5
@@ -13891,7 +14119,8 @@ else $as_nop
cat conftest.err 1>&5
fi
$RM conftest*
-
+ ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_archive_cmds_need_lc" >&5
printf "%s\n" "$lt_cv_archive_cmds_need_lc" >&6; }
@@ -14618,8 +14847,8 @@ linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)
if test ${lt_cv_shlibpath_overrides_runpath+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- lt_cv_shlibpath_overrides_runpath=no
+else case e in #(
+ e) lt_cv_shlibpath_overrides_runpath=no
save_LDFLAGS=$LDFLAGS
save_libdir=$libdir
eval "libdir=/foo; wl=\"$lt_prog_compiler_wl\"; \
@@ -14646,7 +14875,8 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
LDFLAGS=$save_LDFLAGS
libdir=$save_libdir
-
+ ;;
+esac
fi
shlibpath_overrides_runpath=$lt_cv_shlibpath_overrides_runpath
@@ -15074,16 +15304,22 @@ printf %s "checking for dlopen in -ldl... " >&6; }
if test ${ac_cv_lib_dl_dlopen+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- ac_check_lib_save_LIBS=$LIBS
+else case e in #(
+ e) ac_check_lib_save_LIBS=$LIBS
LIBS="-ldl $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
- builtin and then its argument prototype would still apply. */
-char dlopen ();
+ builtin and then its argument prototype would still apply.
+ The 'extern "C"' is for builds by C++ compilers;
+ although this is not generally supported in C code supporting it here
+ has little cost and some practical benefit (sr 110532). */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dlopen (void);
int
main (void)
{
@@ -15095,24 +15331,27 @@ _ACEOF
if ac_fn_c_try_link "$LINENO"
then :
ac_cv_lib_dl_dlopen=yes
-else $as_nop
- ac_cv_lib_dl_dlopen=no
+else case e in #(
+ e) ac_cv_lib_dl_dlopen=no ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
-LIBS=$ac_check_lib_save_LIBS
+LIBS=$ac_check_lib_save_LIBS ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5
printf "%s\n" "$ac_cv_lib_dl_dlopen" >&6; }
if test "x$ac_cv_lib_dl_dlopen" = xyes
then :
lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl
-else $as_nop
-
+else case e in #(
+ e)
lt_cv_dlopen=dyld
lt_cv_dlopen_libs=
lt_cv_dlopen_self=yes
-
+ ;;
+esac
fi
;;
@@ -15130,22 +15369,28 @@ fi
if test "x$ac_cv_func_shl_load" = xyes
then :
lt_cv_dlopen=shl_load
-else $as_nop
- { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for shl_load in -ldld" >&5
+else case e in #(
+ e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for shl_load in -ldld" >&5
printf %s "checking for shl_load in -ldld... " >&6; }
if test ${ac_cv_lib_dld_shl_load+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- ac_check_lib_save_LIBS=$LIBS
+else case e in #(
+ e) ac_check_lib_save_LIBS=$LIBS
LIBS="-ldld $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
- builtin and then its argument prototype would still apply. */
-char shl_load ();
+ builtin and then its argument prototype would still apply.
+ The 'extern "C"' is for builds by C++ compilers;
+ although this is not generally supported in C code supporting it here
+ has little cost and some practical benefit (sr 110532). */
+#ifdef __cplusplus
+extern "C"
+#endif
+char shl_load (void);
int
main (void)
{
@@ -15157,39 +15402,47 @@ _ACEOF
if ac_fn_c_try_link "$LINENO"
then :
ac_cv_lib_dld_shl_load=yes
-else $as_nop
- ac_cv_lib_dld_shl_load=no
+else case e in #(
+ e) ac_cv_lib_dld_shl_load=no ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
-LIBS=$ac_check_lib_save_LIBS
+LIBS=$ac_check_lib_save_LIBS ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_shl_load" >&5
printf "%s\n" "$ac_cv_lib_dld_shl_load" >&6; }
if test "x$ac_cv_lib_dld_shl_load" = xyes
then :
lt_cv_dlopen=shl_load lt_cv_dlopen_libs=-ldld
-else $as_nop
- ac_fn_c_check_func "$LINENO" "dlopen" "ac_cv_func_dlopen"
+else case e in #(
+ e) ac_fn_c_check_func "$LINENO" "dlopen" "ac_cv_func_dlopen"
if test "x$ac_cv_func_dlopen" = xyes
then :
lt_cv_dlopen=dlopen
-else $as_nop
- { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5
+else case e in #(
+ e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5
printf %s "checking for dlopen in -ldl... " >&6; }
if test ${ac_cv_lib_dl_dlopen+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- ac_check_lib_save_LIBS=$LIBS
+else case e in #(
+ e) ac_check_lib_save_LIBS=$LIBS
LIBS="-ldl $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
- builtin and then its argument prototype would still apply. */
-char dlopen ();
+ builtin and then its argument prototype would still apply.
+ The 'extern "C"' is for builds by C++ compilers;
+ although this is not generally supported in C code supporting it here
+ has little cost and some practical benefit (sr 110532). */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dlopen (void);
int
main (void)
{
@@ -15201,34 +15454,42 @@ _ACEOF
if ac_fn_c_try_link "$LINENO"
then :
ac_cv_lib_dl_dlopen=yes
-else $as_nop
- ac_cv_lib_dl_dlopen=no
+else case e in #(
+ e) ac_cv_lib_dl_dlopen=no ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
-LIBS=$ac_check_lib_save_LIBS
+LIBS=$ac_check_lib_save_LIBS ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5
printf "%s\n" "$ac_cv_lib_dl_dlopen" >&6; }
if test "x$ac_cv_lib_dl_dlopen" = xyes
then :
lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl
-else $as_nop
- { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for dlopen in -lsvld" >&5
+else case e in #(
+ e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for dlopen in -lsvld" >&5
printf %s "checking for dlopen in -lsvld... " >&6; }
if test ${ac_cv_lib_svld_dlopen+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- ac_check_lib_save_LIBS=$LIBS
+else case e in #(
+ e) ac_check_lib_save_LIBS=$LIBS
LIBS="-lsvld $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
- builtin and then its argument prototype would still apply. */
-char dlopen ();
+ builtin and then its argument prototype would still apply.
+ The 'extern "C"' is for builds by C++ compilers;
+ although this is not generally supported in C code supporting it here
+ has little cost and some practical benefit (sr 110532). */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dlopen (void);
int
main (void)
{
@@ -15240,34 +15501,42 @@ _ACEOF
if ac_fn_c_try_link "$LINENO"
then :
ac_cv_lib_svld_dlopen=yes
-else $as_nop
- ac_cv_lib_svld_dlopen=no
+else case e in #(
+ e) ac_cv_lib_svld_dlopen=no ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
-LIBS=$ac_check_lib_save_LIBS
+LIBS=$ac_check_lib_save_LIBS ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_svld_dlopen" >&5
printf "%s\n" "$ac_cv_lib_svld_dlopen" >&6; }
if test "x$ac_cv_lib_svld_dlopen" = xyes
then :
lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-lsvld
-else $as_nop
- { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for dld_link in -ldld" >&5
+else case e in #(
+ e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for dld_link in -ldld" >&5
printf %s "checking for dld_link in -ldld... " >&6; }
if test ${ac_cv_lib_dld_dld_link+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- ac_check_lib_save_LIBS=$LIBS
+else case e in #(
+ e) ac_check_lib_save_LIBS=$LIBS
LIBS="-ldld $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
- builtin and then its argument prototype would still apply. */
-char dld_link ();
+ builtin and then its argument prototype would still apply.
+ The 'extern "C"' is for builds by C++ compilers;
+ although this is not generally supported in C code supporting it here
+ has little cost and some practical benefit (sr 110532). */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dld_link (void);
int
main (void)
{
@@ -15279,12 +15548,14 @@ _ACEOF
if ac_fn_c_try_link "$LINENO"
then :
ac_cv_lib_dld_dld_link=yes
-else $as_nop
- ac_cv_lib_dld_dld_link=no
+else case e in #(
+ e) ac_cv_lib_dld_dld_link=no ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
-LIBS=$ac_check_lib_save_LIBS
+LIBS=$ac_check_lib_save_LIBS ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_dld_link" >&5
printf "%s\n" "$ac_cv_lib_dld_dld_link" >&6; }
@@ -15293,19 +15564,24 @@ then :
lt_cv_dlopen=dld_link lt_cv_dlopen_libs=-ldld
fi
-
+ ;;
+esac
fi
-
+ ;;
+esac
fi
-
+ ;;
+esac
fi
-
+ ;;
+esac
fi
-
+ ;;
+esac
fi
;;
@@ -15333,8 +15609,8 @@ printf %s "checking whether a program can dlopen itself... " >&6; }
if test ${lt_cv_dlopen_self+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test yes = "$cross_compiling"; then :
+else case e in #(
+ e) if test yes = "$cross_compiling"; then :
lt_cv_dlopen_self=cross
else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
@@ -15428,7 +15704,8 @@ _LT_EOF
fi
rm -fr conftest*
-
+ ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self" >&5
printf "%s\n" "$lt_cv_dlopen_self" >&6; }
@@ -15440,8 +15717,8 @@ printf %s "checking whether a statically linked program can dlopen itself... " >
if test ${lt_cv_dlopen_self_static+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test yes = "$cross_compiling"; then :
+else case e in #(
+ e) if test yes = "$cross_compiling"; then :
lt_cv_dlopen_self_static=cross
else
lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
@@ -15535,7 +15812,8 @@ _LT_EOF
fi
rm -fr conftest*
-
+ ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self_static" >&5
printf "%s\n" "$lt_cv_dlopen_self_static" >&6; }
@@ -15710,8 +15988,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_path_PKG_CONFIG+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- case $PKG_CONFIG in
+else case e in #(
+ e) case $PKG_CONFIG in
[\\/]* | ?:[\\/]*)
ac_cv_path_PKG_CONFIG="$PKG_CONFIG" # Let the user override the test with a path.
;;
@@ -15736,6 +16014,7 @@ done
IFS=$as_save_IFS
;;
+esac ;;
esac
fi
PKG_CONFIG=$ac_cv_path_PKG_CONFIG
@@ -15758,8 +16037,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_path_ac_pt_PKG_CONFIG+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- case $ac_pt_PKG_CONFIG in
+else case e in #(
+ e) case $ac_pt_PKG_CONFIG in
[\\/]* | ?:[\\/]*)
ac_cv_path_ac_pt_PKG_CONFIG="$ac_pt_PKG_CONFIG" # Let the user override the test with a path.
;;
@@ -15784,6 +16063,7 @@ done
IFS=$as_save_IFS
;;
+esac ;;
esac
fi
ac_pt_PKG_CONFIG=$ac_cv_path_ac_pt_PKG_CONFIG
@@ -16115,265 +16395,132 @@ ac_fn_c_check_type "$LINENO" "int8_t" "ac_cv_type_int8_t" "$ac_includes_default"
if test "x$ac_cv_type_int8_t" = xyes
then :
-else $as_nop
-
+else case e in #(
+ e)
printf "%s\n" "#define int8_t signed char" >>confdefs.h
-
+ ;;
+esac
fi
ac_fn_c_check_type "$LINENO" "int16_t" "ac_cv_type_int16_t" "$ac_includes_default"
if test "x$ac_cv_type_int16_t" = xyes
then :
-else $as_nop
-
+else case e in #(
+ e)
printf "%s\n" "#define int16_t short" >>confdefs.h
-
+ ;;
+esac
fi
ac_fn_c_check_type "$LINENO" "int32_t" "ac_cv_type_int32_t" "$ac_includes_default"
if test "x$ac_cv_type_int32_t" = xyes
then :
-else $as_nop
-
+else case e in #(
+ e)
printf "%s\n" "#define int32_t int" >>confdefs.h
-
+ ;;
+esac
fi
ac_fn_c_check_type "$LINENO" "int64_t" "ac_cv_type_int64_t" "$ac_includes_default"
if test "x$ac_cv_type_int64_t" = xyes
then :
-else $as_nop
-
+else case e in #(
+ e)
printf "%s\n" "#define int64_t long long" >>confdefs.h
-
+ ;;
+esac
fi
ac_fn_c_check_type "$LINENO" "uint8_t" "ac_cv_type_uint8_t" "$ac_includes_default"
if test "x$ac_cv_type_uint8_t" = xyes
then :
-else $as_nop
-
+else case e in #(
+ e)
printf "%s\n" "#define uint8_t unsigned char" >>confdefs.h
-
+ ;;
+esac
fi
ac_fn_c_check_type "$LINENO" "uint16_t" "ac_cv_type_uint16_t" "$ac_includes_default"
if test "x$ac_cv_type_uint16_t" = xyes
then :
-else $as_nop
-
+else case e in #(
+ e)
printf "%s\n" "#define uint16_t unsigned short" >>confdefs.h
-
+ ;;
+esac
fi
ac_fn_c_check_type "$LINENO" "uint32_t" "ac_cv_type_uint32_t" "$ac_includes_default"
if test "x$ac_cv_type_uint32_t" = xyes
then :
-else $as_nop
-
+else case e in #(
+ e)
printf "%s\n" "#define uint32_t unsigned int" >>confdefs.h
-
+ ;;
+esac
fi
ac_fn_c_check_type "$LINENO" "uint64_t" "ac_cv_type_uint64_t" "$ac_includes_default"
if test "x$ac_cv_type_uint64_t" = xyes
then :
-else $as_nop
-
+else case e in #(
+ e)
printf "%s\n" "#define uint64_t unsigned long long" >>confdefs.h
-
+ ;;
+esac
fi
ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default"
if test "x$ac_cv_type_size_t" = xyes
then :
-else $as_nop
-
+else case e in #(
+ e)
printf "%s\n" "#define size_t unsigned int" >>confdefs.h
-
+ ;;
+esac
fi
ac_fn_c_check_type "$LINENO" "ssize_t" "ac_cv_type_ssize_t" "$ac_includes_default"
if test "x$ac_cv_type_ssize_t" = xyes
then :
-else $as_nop
-
+else case e in #(
+ e)
printf "%s\n" "#define ssize_t int" >>confdefs.h
-
-fi
-
-ac_ext=c
-ac_cpp='$CPP $CPPFLAGS'
-ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
-ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
-ac_compiler_gnu=$ac_cv_c_compiler_gnu
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5
-printf %s "checking how to run the C preprocessor... " >&6; }
-# On Suns, sometimes $CPP names a directory.
-if test -n "$CPP" && test -d "$CPP"; then
- CPP=
-fi
-if test -z "$CPP"; then
- if test ${ac_cv_prog_CPP+y}
-then :
- printf %s "(cached) " >&6
-else $as_nop
- # Double quotes because $CC needs to be expanded
- for CPP in "$CC -E" "$CC -E -traditional-cpp" cpp /lib/cpp
- do
- ac_preproc_ok=false
-for ac_c_preproc_warn_flag in '' yes
-do
- # Use a header file that comes with gcc, so configuring glibc
- # with a fresh cross-compiler works.
- # On the NeXT, cc -E runs the code through the compiler's parser,
- # not just through cpp. "Syntax error" is here to catch this case.
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
-#include <limits.h>
- Syntax error
-_ACEOF
-if ac_fn_c_try_cpp "$LINENO"
-then :
-
-else $as_nop
- # Broken: fails on valid input.
-continue
-fi
-rm -f conftest.err conftest.i conftest.$ac_ext
-
- # OK, works on sane cases. Now check whether nonexistent headers
- # can be detected and how.
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
-#include <ac_nonexistent.h>
-_ACEOF
-if ac_fn_c_try_cpp "$LINENO"
-then :
- # Broken: success on invalid input.
-continue
-else $as_nop
- # Passes both tests.
-ac_preproc_ok=:
-break
-fi
-rm -f conftest.err conftest.i conftest.$ac_ext
-
-done
-# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
-rm -f conftest.i conftest.err conftest.$ac_ext
-if $ac_preproc_ok
-then :
- break
-fi
-
- done
- ac_cv_prog_CPP=$CPP
-
-fi
- CPP=$ac_cv_prog_CPP
-else
- ac_cv_prog_CPP=$CPP
-fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5
-printf "%s\n" "$CPP" >&6; }
-ac_preproc_ok=false
-for ac_c_preproc_warn_flag in '' yes
-do
- # Use a header file that comes with gcc, so configuring glibc
- # with a fresh cross-compiler works.
- # On the NeXT, cc -E runs the code through the compiler's parser,
- # not just through cpp. "Syntax error" is here to catch this case.
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
-#include <limits.h>
- Syntax error
-_ACEOF
-if ac_fn_c_try_cpp "$LINENO"
-then :
-
-else $as_nop
- # Broken: fails on valid input.
-continue
-fi
-rm -f conftest.err conftest.i conftest.$ac_ext
-
- # OK, works on sane cases. Now check whether nonexistent headers
- # can be detected and how.
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
-#include <ac_nonexistent.h>
-_ACEOF
-if ac_fn_c_try_cpp "$LINENO"
-then :
- # Broken: success on invalid input.
-continue
-else $as_nop
- # Passes both tests.
-ac_preproc_ok=:
-break
+ ;;
+esac
fi
-rm -f conftest.err conftest.i conftest.$ac_ext
-done
-# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.
-rm -f conftest.i conftest.err conftest.$ac_ext
-if $ac_preproc_ok
+ac_fn_c_check_type "$LINENO" "uid_t" "ac_cv_type_uid_t" "$ac_includes_default"
+if test "x$ac_cv_type_uid_t" = xyes
then :
-else $as_nop
- { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
-as_fn_error $? "C preprocessor \"$CPP\" fails sanity check
-See \`config.log' for more details" "$LINENO" 5; }
+else case e in #(
+ e)
+printf "%s\n" "#define uid_t int" >>confdefs.h
+ ;;
+esac
fi
-ac_ext=c
-ac_cpp='$CPP $CPPFLAGS'
-ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
-ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
-ac_compiler_gnu=$ac_cv_c_compiler_gnu
-
-
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for uid_t in sys/types.h" >&5
-printf %s "checking for uid_t in sys/types.h... " >&6; }
-if test ${ac_cv_type_uid_t+y}
+ac_fn_c_check_type "$LINENO" "gid_t" "ac_cv_type_gid_t" "$ac_includes_default"
+if test "x$ac_cv_type_gid_t" = xyes
then :
- printf %s "(cached) " >&6
-else $as_nop
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
-#include <sys/types.h>
-
-_ACEOF
-if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
- $EGREP "uid_t" >/dev/null 2>&1
-then :
- ac_cv_type_uid_t=yes
-else $as_nop
- ac_cv_type_uid_t=no
-fi
-rm -rf conftest*
-
-fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_uid_t" >&5
-printf "%s\n" "$ac_cv_type_uid_t" >&6; }
-if test $ac_cv_type_uid_t = no; then
-
-printf "%s\n" "#define uid_t int" >>confdefs.h
-
+else case e in #(
+ e)
printf "%s\n" "#define gid_t int" >>confdefs.h
-
+ ;;
+esac
fi
@@ -16382,8 +16529,8 @@ fi
if test "x$ac_cv_type_pid_t" = xyes
then :
-else $as_nop
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+else case e in #(
+ e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#if defined _WIN64 && !defined __CYGWIN__
@@ -16402,14 +16549,16 @@ _ACEOF
if ac_fn_c_try_compile "$LINENO"
then :
ac_pid_type='int'
-else $as_nop
- ac_pid_type='__int64'
+else case e in #(
+ e) ac_pid_type='__int64' ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
printf "%s\n" "#define pid_t $ac_pid_type" >>confdefs.h
-
+ ;;
+esac
fi
@@ -16417,10 +16566,11 @@ ac_fn_c_check_type "$LINENO" "off_t" "ac_cv_type_off_t" "$ac_includes_default"
if test "x$ac_cv_type_off_t" = xyes
then :
-else $as_nop
-
+else case e in #(
+ e)
printf "%s\n" "#define off_t long int" >>confdefs.h
-
+ ;;
+esac
fi
ac_fn_c_check_type "$LINENO" "u_char" "ac_cv_type_u_char" "
@@ -16433,10 +16583,11 @@ $ac_includes_default
if test "x$ac_cv_type_u_char" = xyes
then :
-else $as_nop
-
+else case e in #(
+ e)
printf "%s\n" "#define u_char unsigned char" >>confdefs.h
-
+ ;;
+esac
fi
ac_fn_c_check_type "$LINENO" "rlim_t" "ac_cv_type_rlim_t" "
@@ -16449,10 +16600,11 @@ $ac_includes_default
if test "x$ac_cv_type_rlim_t" = xyes
then :
-else $as_nop
-
+else case e in #(
+ e)
printf "%s\n" "#define rlim_t unsigned long" >>confdefs.h
-
+ ;;
+esac
fi
@@ -16469,10 +16621,11 @@ $ac_includes_default
if test "x$ac_cv_type_socklen_t" = xyes
then :
-else $as_nop
-
+else case e in #(
+ e)
printf "%s\n" "#define socklen_t int" >>confdefs.h
-
+ ;;
+esac
fi
ac_fn_c_check_type "$LINENO" "in_addr_t" "ac_cv_type_in_addr_t" "
@@ -16488,10 +16641,11 @@ $ac_includes_default
if test "x$ac_cv_type_in_addr_t" = xyes
then :
-else $as_nop
-
+else case e in #(
+ e)
printf "%s\n" "#define in_addr_t uint32_t" >>confdefs.h
-
+ ;;
+esac
fi
ac_fn_c_check_type "$LINENO" "in_port_t" "ac_cv_type_in_port_t" "
@@ -16507,10 +16661,11 @@ $ac_includes_default
if test "x$ac_cv_type_in_port_t" = xyes
then :
-else $as_nop
-
+else case e in #(
+ e)
printf "%s\n" "#define in_port_t uint16_t" >>confdefs.h
-
+ ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if memcmp compares unsigned" >&5
@@ -16529,8 +16684,8 @@ printf "%s\n" "#define MEMCMP_IS_BROKEN 1" >>confdefs.h
esac
-else $as_nop
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+else case e in #(
+ e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <stdio.h>
@@ -16549,8 +16704,8 @@ if ac_fn_c_try_run "$LINENO"
then :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
printf "%s\n" "yes" >&6; }
-else $as_nop
- { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+else case e in #(
+ e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
printf "%s\n" "no" >&6; }
printf "%s\n" "#define MEMCMP_IS_BROKEN 1" >>confdefs.h
@@ -16561,24 +16716,26 @@ printf "%s\n" "#define MEMCMP_IS_BROKEN 1" >>confdefs.h
;;
esac
-
+ ;;
+esac
fi
rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
- conftest.$ac_objext conftest.beam conftest.$ac_ext
+ conftest.$ac_objext conftest.beam conftest.$ac_ext ;;
+esac
fi
# The cast to long int works around a bug in the HP C Compiler
# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
-# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# declarations like 'int a3[[(sizeof (unsigned char)) >= 0]];'.
# This bug is HP SR number 8606223364.
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of time_t" >&5
printf %s "checking size of time_t... " >&6; }
if test ${ac_cv_sizeof_time_t+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (time_t))" "ac_cv_sizeof_time_t" "
+else case e in #(
+ e) if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (time_t))" "ac_cv_sizeof_time_t" "
$ac_includes_default
#ifdef TIME_WITH_SYS_TIME
# include <sys/time.h>
@@ -16594,17 +16751,19 @@ $ac_includes_default
"
then :
-else $as_nop
- if test "$ac_cv_type_time_t" = yes; then
- { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+else case e in #(
+ e) if test "$ac_cv_type_time_t" = yes; then
+ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;}
as_fn_error 77 "cannot compute sizeof (time_t)
-See \`config.log' for more details" "$LINENO" 5; }
+See 'config.log' for more details" "$LINENO" 5; }
else
ac_cv_sizeof_time_t=0
- fi
+ fi ;;
+esac
fi
-
+ ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_time_t" >&5
printf "%s\n" "$ac_cv_sizeof_time_t" >&6; }
@@ -16616,28 +16775,30 @@ printf "%s\n" "#define SIZEOF_TIME_T $ac_cv_sizeof_time_t" >>confdefs.h
# The cast to long int works around a bug in the HP C Compiler
# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
-# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# declarations like 'int a3[[(sizeof (unsigned char)) >= 0]];'.
# This bug is HP SR number 8606223364.
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of size_t" >&5
printf %s "checking size of size_t... " >&6; }
if test ${ac_cv_sizeof_size_t+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (size_t))" "ac_cv_sizeof_size_t" "$ac_includes_default"
+else case e in #(
+ e) if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (size_t))" "ac_cv_sizeof_size_t" "$ac_includes_default"
then :
-else $as_nop
- if test "$ac_cv_type_size_t" = yes; then
- { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+else case e in #(
+ e) if test "$ac_cv_type_size_t" = yes; then
+ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;}
as_fn_error 77 "cannot compute sizeof (size_t)
-See \`config.log' for more details" "$LINENO" 5; }
+See 'config.log' for more details" "$LINENO" 5; }
else
ac_cv_sizeof_size_t=0
- fi
+ fi ;;
+esac
fi
-
+ ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_size_t" >&5
printf "%s\n" "$ac_cv_sizeof_size_t" >&6; }
@@ -16654,8 +16815,9 @@ printf "%s\n" "#define SIZEOF_SIZE_T $ac_cv_sizeof_size_t" >>confdefs.h
if test ${enable_rpath+y}
then :
enableval=$enable_rpath; enable_rpath=$enableval
-else $as_nop
- enable_rpath=yes
+else case e in #(
+ e) enable_rpath=yes ;;
+esac
fi
if test "x$enable_rpath" = xno; then
@@ -16671,15 +16833,21 @@ printf %s "checking for library containing inet_pton... " >&6; }
if test ${ac_cv_search_inet_pton+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- ac_func_search_save_LIBS=$LIBS
+else case e in #(
+ e) ac_func_search_save_LIBS=$LIBS
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
- builtin and then its argument prototype would still apply. */
-char inet_pton ();
+ builtin and then its argument prototype would still apply.
+ The 'extern "C"' is for builds by C++ compilers;
+ although this is not generally supported in C code supporting it here
+ has little cost and some practical benefit (sr 110532). */
+#ifdef __cplusplus
+extern "C"
+#endif
+char inet_pton (void);
int
main (void)
{
@@ -16710,11 +16878,13 @@ done
if test ${ac_cv_search_inet_pton+y}
then :
-else $as_nop
- ac_cv_search_inet_pton=no
+else case e in #(
+ e) ac_cv_search_inet_pton=no ;;
+esac
fi
rm conftest.$ac_ext
-LIBS=$ac_func_search_save_LIBS
+LIBS=$ac_func_search_save_LIBS ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_inet_pton" >&5
printf "%s\n" "$ac_cv_search_inet_pton" >&6; }
@@ -16730,15 +16900,21 @@ printf %s "checking for library containing socket... " >&6; }
if test ${ac_cv_search_socket+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- ac_func_search_save_LIBS=$LIBS
+else case e in #(
+ e) ac_func_search_save_LIBS=$LIBS
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
- builtin and then its argument prototype would still apply. */
-char socket ();
+ builtin and then its argument prototype would still apply.
+ The 'extern "C"' is for builds by C++ compilers;
+ although this is not generally supported in C code supporting it here
+ has little cost and some practical benefit (sr 110532). */
+#ifdef __cplusplus
+extern "C"
+#endif
+char socket (void);
int
main (void)
{
@@ -16769,11 +16945,13 @@ done
if test ${ac_cv_search_socket+y}
then :
-else $as_nop
- ac_cv_search_socket=no
+else case e in #(
+ e) ac_cv_search_socket=no ;;
+esac
fi
rm conftest.$ac_ext
-LIBS=$ac_func_search_save_LIBS
+LIBS=$ac_func_search_save_LIBS ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_socket" >&5
printf "%s\n" "$ac_cv_search_socket" >&6; }
@@ -16793,8 +16971,8 @@ printf %s "checking for working chown... " >&6; }
if test ${ac_cv_func_chown_works+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test "$cross_compiling" = yes
+else case e in #(
+ e) if test "$cross_compiling" = yes
then :
case "$host_os" in # ((
# Guess yes on glibc systems.
@@ -16802,8 +16980,8 @@ then :
# If we don't know, assume the worst.
*) ac_cv_func_chown_works=no ;;
esac
-else $as_nop
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+else case e in #(
+ e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$ac_includes_default
#include <fcntl.h>
@@ -16831,15 +17009,18 @@ _ACEOF
if ac_fn_c_try_run "$LINENO"
then :
ac_cv_func_chown_works=yes
-else $as_nop
- ac_cv_func_chown_works=no
+else case e in #(
+ e) ac_cv_func_chown_works=no ;;
+esac
fi
rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
- conftest.$ac_objext conftest.beam conftest.$ac_ext
+ conftest.$ac_objext conftest.beam conftest.$ac_ext ;;
+esac
fi
rm -f conftest.chown
-
+ ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_chown_works" >&5
printf "%s\n" "$ac_cv_func_chown_works" >&6; }
@@ -16872,19 +17053,19 @@ printf %s "checking for working fork... " >&6; }
if test ${ac_cv_func_fork_works+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test "$cross_compiling" = yes
+else case e in #(
+ e) if test "$cross_compiling" = yes
then :
ac_cv_func_fork_works=cross
-else $as_nop
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+else case e in #(
+ e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$ac_includes_default
int
main (void)
{
- /* By Ruediger Kuhlmann. */
+ /* By R. Kuhlmann. */
return fork () < 0;
;
@@ -16894,13 +17075,16 @@ _ACEOF
if ac_fn_c_try_run "$LINENO"
then :
ac_cv_func_fork_works=yes
-else $as_nop
- ac_cv_func_fork_works=no
+else case e in #(
+ e) ac_cv_func_fork_works=no ;;
+esac
fi
rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
- conftest.$ac_objext conftest.beam conftest.$ac_ext
+ conftest.$ac_objext conftest.beam conftest.$ac_ext ;;
+esac
fi
-
+ ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_fork_works" >&5
printf "%s\n" "$ac_cv_func_fork_works" >&6; }
@@ -16928,12 +17112,12 @@ printf %s "checking for working vfork... " >&6; }
if test ${ac_cv_func_vfork_works+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test "$cross_compiling" = yes
+else case e in #(
+ e) if test "$cross_compiling" = yes
then :
ac_cv_func_vfork_works=cross
-else $as_nop
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+else case e in #(
+ e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Thanks to Paul Eggert for this test. */
$ac_includes_default
@@ -17044,13 +17228,16 @@ _ACEOF
if ac_fn_c_try_run "$LINENO"
then :
ac_cv_func_vfork_works=yes
-else $as_nop
- ac_cv_func_vfork_works=no
+else case e in #(
+ e) ac_cv_func_vfork_works=no ;;
+esac
fi
rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
- conftest.$ac_objext conftest.beam conftest.$ac_ext
+ conftest.$ac_objext conftest.beam conftest.$ac_ext ;;
+esac
fi
-
+ ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_vfork_works" >&5
printf "%s\n" "$ac_cv_func_vfork_works" >&6; }
@@ -17080,74 +17267,99 @@ fi
printf "%s\n" "#define RETSIGTYPE void" >>confdefs.h
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for _LARGEFILE_SOURCE value needed for large files" >&5
-printf %s "checking for _LARGEFILE_SOURCE value needed for large files... " >&6; }
-if test ${ac_cv_sys_largefile_source+y}
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for declarations of fseeko and ftello" >&5
+printf %s "checking for declarations of fseeko and ftello... " >&6; }
+if test ${ac_cv_func_fseeko_ftello+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- while :; do
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+else case e in #(
+ e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
+
+#if defined __hpux && !defined _LARGEFILE_SOURCE
+# include <limits.h>
+# if LONG_MAX >> 31 == 0
+# error "32-bit HP-UX 11/ia64 needs _LARGEFILE_SOURCE for fseeko in C++"
+# endif
+#endif
#include <sys/types.h> /* for off_t */
- #include <stdio.h>
+#include <stdio.h>
+
int
main (void)
{
-int (*fp) (FILE *, off_t, int) = fseeko;
- return fseeko (stdin, 0, 0) && fp (stdin, 0, 0);
+
+ int (*fp1) (FILE *, off_t, int) = fseeko;
+ off_t (*fp2) (FILE *) = ftello;
+ return fseeko (stdin, 0, 0)
+ && fp1 (stdin, 0, 0)
+ && ftello (stdin) >= 0
+ && fp2 (stdin) >= 0;
+
;
return 0;
}
_ACEOF
-if ac_fn_c_try_link "$LINENO"
+if ac_fn_c_try_compile "$LINENO"
then :
- ac_cv_sys_largefile_source=no; break
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.beam \
- conftest$ac_exeext conftest.$ac_ext
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+ ac_cv_func_fseeko_ftello=yes
+else case e in #(
+ e) ac_save_CPPFLAGS="$CPPFLAGS"
+ CPPFLAGS="$CPPFLAGS -D_LARGEFILE_SOURCE=1"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
-#define _LARGEFILE_SOURCE 1
+
+#if defined __hpux && !defined _LARGEFILE_SOURCE
+# include <limits.h>
+# if LONG_MAX >> 31 == 0
+# error "32-bit HP-UX 11/ia64 needs _LARGEFILE_SOURCE for fseeko in C++"
+# endif
+#endif
#include <sys/types.h> /* for off_t */
- #include <stdio.h>
+#include <stdio.h>
+
int
main (void)
{
-int (*fp) (FILE *, off_t, int) = fseeko;
- return fseeko (stdin, 0, 0) && fp (stdin, 0, 0);
+
+ int (*fp1) (FILE *, off_t, int) = fseeko;
+ off_t (*fp2) (FILE *) = ftello;
+ return fseeko (stdin, 0, 0)
+ && fp1 (stdin, 0, 0)
+ && ftello (stdin) >= 0
+ && fp2 (stdin) >= 0;
+
;
return 0;
}
_ACEOF
-if ac_fn_c_try_link "$LINENO"
+if ac_fn_c_try_compile "$LINENO"
then :
- ac_cv_sys_largefile_source=1; break
+ ac_cv_func_fseeko_ftello="need _LARGEFILE_SOURCE"
+else case e in #(
+ e) ac_cv_func_fseeko_ftello=no ;;
+esac
fi
-rm -f core conftest.err conftest.$ac_objext conftest.beam \
- conftest$ac_exeext conftest.$ac_ext
- ac_cv_sys_largefile_source=unknown
- break
-done
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;;
+esac
fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_largefile_source" >&5
-printf "%s\n" "$ac_cv_sys_largefile_source" >&6; }
-case $ac_cv_sys_largefile_source in #(
- no | unknown) ;;
- *)
-printf "%s\n" "#define _LARGEFILE_SOURCE $ac_cv_sys_largefile_source" >>confdefs.h
-;;
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;;
esac
-rm -rf conftest*
-
-# We used to try defining _XOPEN_SOURCE=500 too, to work around a bug
-# in glibc 2.1.3, but that breaks too many other things.
-# If you want fseeko and ftello with glibc, upgrade to a fixed glibc.
-if test $ac_cv_sys_largefile_source != unknown; then
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_fseeko_ftello" >&5
+printf "%s\n" "$ac_cv_func_fseeko_ftello" >&6; }
+if test "$ac_cv_func_fseeko_ftello" != no
+then :
printf "%s\n" "#define HAVE_FSEEKO 1" >>confdefs.h
fi
+if test "$ac_cv_func_fseeko_ftello" = "need _LARGEFILE_SOURCE"
+then :
+
+printf "%s\n" "#define _LARGEFILE_SOURCE 1" >>confdefs.h
+
+fi
# Check whether --enable-largefile was given.
@@ -17155,31 +17367,34 @@ if test ${enable_largefile+y}
then :
enableval=$enable_largefile;
fi
-
-if test "$enable_largefile" != no; then
-
- { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for special C compiler options needed for large files" >&5
-printf %s "checking for special C compiler options needed for large files... " >&6; }
-if test ${ac_cv_sys_largefile_CC+y}
+if test "$enable_largefile,$enable_year2038" != no,no
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable large file support" >&5
+printf %s "checking for $CC option to enable large file support... " >&6; }
+if test ${ac_cv_sys_largefile_opts+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- ac_cv_sys_largefile_CC=no
- if test "$GCC" != yes; then
- ac_save_CC=$CC
- while :; do
- # IRIX 6.2 and later do not support large files by default,
- # so use the C compiler's -n32 option if that helps.
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+else case e in #(
+ e) ac_save_CC="$CC"
+ ac_opt_found=no
+ for ac_opt in "none needed" "-D_FILE_OFFSET_BITS=64" "-D_LARGE_FILES=1" "-n32"; do
+ if test x"$ac_opt" != x"none needed"
+then :
+ CC="$ac_save_CC $ac_opt"
+fi
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <sys/types.h>
- /* Check that off_t can represent 2**63 - 1 correctly.
- We can't simply define LARGE_OFF_T to be 9223372036854775807,
+#ifndef FTYPE
+# define FTYPE off_t
+#endif
+ /* Check that FTYPE can represent 2**63 - 1 correctly.
+ We can't simply define LARGE_FTYPE to be 9223372036854775807,
since some C++ compilers masquerading as C compilers
incorrectly reject 9223372036854775807. */
-#define LARGE_OFF_T (((off_t) 1 << 31 << 31) - 1 + ((off_t) 1 << 31 << 31))
- int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
- && LARGE_OFF_T % 2147483647 == 1)
+#define LARGE_FTYPE (((FTYPE) 1 << 31 << 31) - 1 + ((FTYPE) 1 << 31 << 31))
+ int FTYPE_is_large[(LARGE_FTYPE % 2147483629 == 721
+ && LARGE_FTYPE % 2147483647 == 1)
? 1 : -1];
int
main (void)
@@ -17189,142 +17404,88 @@ main (void)
return 0;
}
_ACEOF
- if ac_fn_c_try_compile "$LINENO"
+if ac_fn_c_try_compile "$LINENO"
then :
- break
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.beam
- CC="$CC -n32"
+ if test x"$ac_opt" = x"none needed"
+then :
+ # GNU/Linux s390x and alpha need _FILE_OFFSET_BITS=64 for wide ino_t.
+ CC="$CC -DFTYPE=ino_t"
if ac_fn_c_try_compile "$LINENO"
then :
- ac_cv_sys_largefile_CC=' -n32'; break
+
+else case e in #(
+ e) CC="$CC -D_FILE_OFFSET_BITS=64"
+ if ac_fn_c_try_compile "$LINENO"
+then :
+ ac_opt='-D_FILE_OFFSET_BITS=64'
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam
- break
- done
- CC=$ac_save_CC
- rm -f conftest.$ac_ext
- fi
fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_largefile_CC" >&5
-printf "%s\n" "$ac_cv_sys_largefile_CC" >&6; }
- if test "$ac_cv_sys_largefile_CC" != no; then
- CC=$CC$ac_cv_sys_largefile_CC
- fi
-
- { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for _FILE_OFFSET_BITS value needed for large files" >&5
-printf %s "checking for _FILE_OFFSET_BITS value needed for large files... " >&6; }
-if test ${ac_cv_sys_file_offset_bits+y}
-then :
- printf %s "(cached) " >&6
-else $as_nop
- while :; do
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
-#include <sys/types.h>
- /* Check that off_t can represent 2**63 - 1 correctly.
- We can't simply define LARGE_OFF_T to be 9223372036854775807,
- since some C++ compilers masquerading as C compilers
- incorrectly reject 9223372036854775807. */
-#define LARGE_OFF_T (((off_t) 1 << 31 << 31) - 1 + ((off_t) 1 << 31 << 31))
- int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
- && LARGE_OFF_T % 2147483647 == 1)
- ? 1 : -1];
-int
-main (void)
-{
-
- ;
- return 0;
-}
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"
-then :
- ac_cv_sys_file_offset_bits=no; break
+ ac_cv_sys_largefile_opts=$ac_opt
+ ac_opt_found=yes
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
-#define _FILE_OFFSET_BITS 64
-#include <sys/types.h>
- /* Check that off_t can represent 2**63 - 1 correctly.
- We can't simply define LARGE_OFF_T to be 9223372036854775807,
- since some C++ compilers masquerading as C compilers
- incorrectly reject 9223372036854775807. */
-#define LARGE_OFF_T (((off_t) 1 << 31 << 31) - 1 + ((off_t) 1 << 31 << 31))
- int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
- && LARGE_OFF_T % 2147483647 == 1)
- ? 1 : -1];
-int
-main (void)
-{
+ test $ac_opt_found = no || break
+ done
+ CC="$ac_save_CC"
- ;
- return 0;
-}
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"
-then :
- ac_cv_sys_file_offset_bits=64; break
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
- ac_cv_sys_file_offset_bits=unknown
- break
-done
+ test $ac_opt_found = yes || ac_cv_sys_largefile_opts="support not detected" ;;
+esac
fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_file_offset_bits" >&5
-printf "%s\n" "$ac_cv_sys_file_offset_bits" >&6; }
-case $ac_cv_sys_file_offset_bits in #(
- no | unknown) ;;
- *)
-printf "%s\n" "#define _FILE_OFFSET_BITS $ac_cv_sys_file_offset_bits" >>confdefs.h
-;;
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_largefile_opts" >&5
+printf "%s\n" "$ac_cv_sys_largefile_opts" >&6; }
+
+ac_have_largefile=yes
+case $ac_cv_sys_largefile_opts in #(
+ "none needed") :
+ ;; #(
+ "supported through gnulib") :
+ ;; #(
+ "support not detected") :
+ ac_have_largefile=no ;; #(
+ "-D_FILE_OFFSET_BITS=64") :
+
+printf "%s\n" "#define _FILE_OFFSET_BITS 64" >>confdefs.h
+ ;; #(
+ "-D_LARGE_FILES=1") :
+
+printf "%s\n" "#define _LARGE_FILES 1" >>confdefs.h
+ ;; #(
+ "-n32") :
+ CC="$CC -n32" ;; #(
+ *) :
+ as_fn_error $? "internal error: bad value for \$ac_cv_sys_largefile_opts" "$LINENO" 5 ;;
esac
-rm -rf conftest*
- if test $ac_cv_sys_file_offset_bits = unknown; then
- { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for _LARGE_FILES value needed for large files" >&5
-printf %s "checking for _LARGE_FILES value needed for large files... " >&6; }
-if test ${ac_cv_sys_large_files+y}
+
+if test "$enable_year2038" != no
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option for timestamps after 2038" >&5
+printf %s "checking for $CC option for timestamps after 2038... " >&6; }
+if test ${ac_cv_sys_year2038_opts+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- while :; do
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
-#include <sys/types.h>
- /* Check that off_t can represent 2**63 - 1 correctly.
- We can't simply define LARGE_OFF_T to be 9223372036854775807,
- since some C++ compilers masquerading as C compilers
- incorrectly reject 9223372036854775807. */
-#define LARGE_OFF_T (((off_t) 1 << 31 << 31) - 1 + ((off_t) 1 << 31 << 31))
- int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
- && LARGE_OFF_T % 2147483647 == 1)
- ? 1 : -1];
-int
-main (void)
-{
-
- ;
- return 0;
-}
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"
+else case e in #(
+ e) ac_save_CPPFLAGS="$CPPFLAGS"
+ ac_opt_found=no
+ for ac_opt in "none needed" "-D_TIME_BITS=64" "-D__MINGW_USE_VC2005_COMPAT" "-U_USE_32_BIT_TIME_T -D__MINGW_USE_VC2005_COMPAT"; do
+ if test x"$ac_opt" != x"none needed"
then :
- ac_cv_sys_large_files=no; break
+ CPPFLAGS="$ac_save_CPPFLAGS $ac_opt"
fi
-rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
-#define _LARGE_FILES 1
-#include <sys/types.h>
- /* Check that off_t can represent 2**63 - 1 correctly.
- We can't simply define LARGE_OFF_T to be 9223372036854775807,
- since some C++ compilers masquerading as C compilers
- incorrectly reject 9223372036854775807. */
-#define LARGE_OFF_T (((off_t) 1 << 31 << 31) - 1 + ((off_t) 1 << 31 << 31))
- int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721
- && LARGE_OFF_T % 2147483647 == 1)
- ? 1 : -1];
+
+ #include <time.h>
+ /* Check that time_t can represent 2**32 - 1 correctly. */
+ #define LARGE_TIME_T \\
+ ((time_t) (((time_t) 1 << 30) - 1 + 3 * ((time_t) 1 << 30)))
+ int verify_time_t_range[(LARGE_TIME_T / 65537 == 65535
+ && LARGE_TIME_T % 65537 == 0)
+ ? 1 : -1];
+
int
main (void)
{
@@ -17335,25 +17496,47 @@ main (void)
_ACEOF
if ac_fn_c_try_compile "$LINENO"
then :
- ac_cv_sys_large_files=1; break
+ ac_cv_sys_year2038_opts="$ac_opt"
+ ac_opt_found=yes
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
- ac_cv_sys_large_files=unknown
- break
-done
+ test $ac_opt_found = no || break
+ done
+ CPPFLAGS="$ac_save_CPPFLAGS"
+ test $ac_opt_found = yes || ac_cv_sys_year2038_opts="support not detected" ;;
+esac
fi
-{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_large_files" >&5
-printf "%s\n" "$ac_cv_sys_large_files" >&6; }
-case $ac_cv_sys_large_files in #(
- no | unknown) ;;
- *)
-printf "%s\n" "#define _LARGE_FILES $ac_cv_sys_large_files" >>confdefs.h
-;;
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_year2038_opts" >&5
+printf "%s\n" "$ac_cv_sys_year2038_opts" >&6; }
+
+ac_have_year2038=yes
+case $ac_cv_sys_year2038_opts in #(
+ "none needed") :
+ ;; #(
+ "support not detected") :
+ ac_have_year2038=no ;; #(
+ "-D_TIME_BITS=64") :
+
+printf "%s\n" "#define _TIME_BITS 64" >>confdefs.h
+ ;; #(
+ "-D__MINGW_USE_VC2005_COMPAT") :
+
+printf "%s\n" "#define __MINGW_USE_VC2005_COMPAT 1" >>confdefs.h
+ ;; #(
+ "-U_USE_32_BIT_TIME_T"*) :
+ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;}
+as_fn_error $? "the 'time_t' type is currently forced to be 32-bit. It
+will stop working after mid-January 2038. Remove
+_USE_32BIT_TIME_T from the compiler flags.
+See 'config.log' for more details" "$LINENO" 5; } ;; #(
+ *) :
+ as_fn_error $? "internal error: bad value for \$ac_cv_sys_year2038_opts" "$LINENO" 5 ;;
esac
-rm -rf conftest*
- fi
+
fi
+fi
@@ -17363,8 +17546,8 @@ cache=_D_LARGEFILE_SOURCE_1
if eval test \${cv_prog_cc_flag_needed_$cache+y}
then :
printf %s "(cached) " >&6
-else $as_nop
-
+else case e in #(
+ e)
echo '
#include <stdio.h>
int test(void) {
@@ -17390,7 +17573,8 @@ fi
fi
rm -f conftest conftest.c conftest.o
-
+ ;;
+esac
fi
if eval "test \"`echo '$cv_prog_cc_flag_needed_'$cache`\" = yes"; then
@@ -17434,8 +17618,8 @@ then :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: crosscompile(yes)" >&5
printf "%s\n" "crosscompile(yes)" >&6; }
-else $as_nop
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+else case e in #(
+ e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
@@ -17570,17 +17754,19 @@ then :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
printf "%s\n" "yes" >&6; }
-else $as_nop
-
+else case e in #(
+ e)
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
printf "%s\n" "no" >&6; }
printf "%s\n" "#define NONBLOCKING_IS_BROKEN 1" >>confdefs.h
-
+ ;;
+esac
fi
rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
- conftest.$ac_objext conftest.beam conftest.$ac_ext
+ conftest.$ac_objext conftest.beam conftest.$ac_ext ;;
+esac
fi
fi
@@ -17618,10 +17804,11 @@ printf "%s\n" "yes" >&6; }
printf "%s\n" "#define MKDIR_HAS_ONE_ARG 1" >>confdefs.h
-else $as_nop
- { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+else case e in #(
+ e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
printf "%s\n" "no" >&6; }
-
+ ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
@@ -17639,8 +17826,8 @@ if test c${cross_compiling} = cno; then
if test "$cross_compiling" = yes
then :
eval "ac_cv_c_strptime_works=maybe"
-else $as_nop
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+else case e in #(
+ e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#define _XOPEN_SOURCE 600
@@ -17655,11 +17842,13 @@ _ACEOF
if ac_fn_c_try_run "$LINENO"
then :
eval "ac_cv_c_strptime_works=yes"
-else $as_nop
- eval "ac_cv_c_strptime_works=no"
+else case e in #(
+ e) eval "ac_cv_c_strptime_works=no" ;;
+esac
fi
rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
- conftest.$ac_objext conftest.beam conftest.$ac_ext
+ conftest.$ac_objext conftest.beam conftest.$ac_ext ;;
+esac
fi
else
@@ -17680,13 +17869,14 @@ printf "%s\n" "#define STRPTIME_WORKS 1" >>confdefs.h
fi
-else $as_nop
- case " $LIBOBJS " in
+else case e in #(
+ e) case " $LIBOBJS " in
*" strptime.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS strptime.$ac_objext"
;;
esac
-
+ ;;
+esac
fi
done
@@ -17713,8 +17903,9 @@ fi
if test ${enable_systemd+y}
then :
enableval=$enable_systemd;
-else $as_nop
- enable_systemd=no
+else case e in #(
+ e) enable_systemd=no ;;
+esac
fi
have_systemd=no
@@ -17795,8 +17986,8 @@ See the pkg-config man page for more details." "$LINENO" 5
elif test $pkg_failed = untried; then
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
printf "%s\n" "no" >&6; }
- { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;}
as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it
is in your PATH or set the PKG_CONFIG environment variable to the full
path to pkg-config.
@@ -17806,7 +17997,7 @@ and SYSTEMD_LIBS to avoid the need to call pkg-config.
See the pkg-config man page for more details.
To get pkg-config, see <http://pkg-config.freedesktop.org/>.
-See \`config.log' for more details" "$LINENO" 5; }
+See 'config.log' for more details" "$LINENO" 5; }
else
SYSTEMD_CFLAGS=$pkg_cv_SYSTEMD_CFLAGS
SYSTEMD_LIBS=$pkg_cv_SYSTEMD_LIBS
@@ -17890,8 +18081,8 @@ See the pkg-config man page for more details." "$LINENO" 5
elif test $pkg_failed = untried; then
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
printf "%s\n" "no" >&6; }
- { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;}
as_fn_error $? "The pkg-config script could not be found or is too old. Make sure it
is in your PATH or set the PKG_CONFIG environment variable to the full
path to pkg-config.
@@ -17901,7 +18092,7 @@ and SYSTEMD_DAEMON_LIBS to avoid the need to call pkg-config.
See the pkg-config man page for more details.
To get pkg-config, see <http://pkg-config.freedesktop.org/>.
-See \`config.log' for more details" "$LINENO" 5; }
+See 'config.log' for more details" "$LINENO" 5; }
else
SYSTEMD_DAEMON_CFLAGS=$pkg_cv_SYSTEMD_DAEMON_CFLAGS
SYSTEMD_DAEMON_LIBS=$pkg_cv_SYSTEMD_DAEMON_LIBS
@@ -17998,8 +18189,8 @@ esac
printf "%s\n" "#define malloc rpl_malloc_unbound" >>confdefs.h
-else $as_nop
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+else case e in #(
+ e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#if defined STDC_HEADERS || defined HAVE_STDLIB_H
#include <stdlib.h>
@@ -18029,15 +18220,17 @@ esac
printf "%s\n" "#define malloc rpl_malloc_unbound" >>confdefs.h
-else $as_nop
- { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+else case e in #(
+ e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
printf "%s\n" "yes" >&6; }
printf "%s\n" "#define HAVE_MALLOC 1" >>confdefs.h
-
+ ;;
+esac
fi
rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
- conftest.$ac_objext conftest.beam conftest.$ac_ext
+ conftest.$ac_objext conftest.beam conftest.$ac_ext ;;
+esac
fi
@@ -18082,10 +18275,11 @@ printf "%s\n" "yes" >&6; }
printf "%s\n" "#define HAVE_WINDOWS_THREADS 1" >>confdefs.h
-else $as_nop
- { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+else case e in #(
+ e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
printf "%s\n" "no" >&6; }
-
+ ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
@@ -18101,12 +18295,286 @@ else
if test ${with_pthreads+y}
then :
withval=$with_pthreads;
-else $as_nop
- withval="yes"
+else case e in #(
+ e) withval="yes" ;;
+esac
fi
ub_have_pthreads=no
if test x_$withval != x_no; then
+ ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5
+printf %s "checking how to run the C preprocessor... " >&6; }
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+ CPP=
+fi
+if test -z "$CPP"; then
+ if test ${ac_cv_prog_CPP+y}
+then :
+ printf %s "(cached) " >&6
+else case e in #(
+ e) # Double quotes because $CC needs to be expanded
+ for CPP in "$CC -E" "$CC -E -traditional-cpp" cpp /lib/cpp
+ do
+ ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+ # Use a header file that comes with gcc, so configuring glibc
+ # with a fresh cross-compiler works.
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp. "Syntax error" is here to catch this case.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <limits.h>
+ Syntax error
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"
+then :
+
+else case e in #(
+ e) # Broken: fails on valid input.
+continue ;;
+esac
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+ # OK, works on sane cases. Now check whether nonexistent headers
+ # can be detected and how.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"
+then :
+ # Broken: success on invalid input.
+continue
+else case e in #(
+ e) # Passes both tests.
+ac_preproc_ok=:
+break ;;
+esac
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of 'break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok
+then :
+ break
+fi
+
+ done
+ ac_cv_prog_CPP=$CPP
+ ;;
+esac
+fi
+ CPP=$ac_cv_prog_CPP
+else
+ ac_cv_prog_CPP=$CPP
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5
+printf "%s\n" "$CPP" >&6; }
+ac_preproc_ok=false
+for ac_c_preproc_warn_flag in '' yes
+do
+ # Use a header file that comes with gcc, so configuring glibc
+ # with a fresh cross-compiler works.
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp. "Syntax error" is here to catch this case.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <limits.h>
+ Syntax error
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"
+then :
+
+else case e in #(
+ e) # Broken: fails on valid input.
+continue ;;
+esac
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+ # OK, works on sane cases. Now check whether nonexistent headers
+ # can be detected and how.
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <ac_nonexistent.h>
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"
+then :
+ # Broken: success on invalid input.
+continue
+else case e in #(
+ e) # Passes both tests.
+ac_preproc_ok=:
+break ;;
+esac
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+
+done
+# Because of 'break', _AC_PREPROC_IFELSE's cleaning code was skipped.
+rm -f conftest.i conftest.err conftest.$ac_ext
+if $ac_preproc_ok
+then :
+
+else case e in #(
+ e) { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;}
+as_fn_error $? "C preprocessor \"$CPP\" fails sanity check
+See 'config.log' for more details" "$LINENO" 5; } ;;
+esac
+fi
+
+ac_ext=c
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for egrep -e" >&5
+printf %s "checking for egrep -e... " >&6; }
+if test ${ac_cv_path_EGREP_TRADITIONAL+y}
+then :
+ printf %s "(cached) " >&6
+else case e in #(
+ e) if test -z "$EGREP_TRADITIONAL"; then
+ ac_path_EGREP_TRADITIONAL_found=false
+ # Loop through the user's path and test for each of PROGNAME-LIST
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+ IFS=$as_save_IFS
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
+ for ac_prog in grep ggrep
+ do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_EGREP_TRADITIONAL="$as_dir$ac_prog$ac_exec_ext"
+ as_fn_executable_p "$ac_path_EGREP_TRADITIONAL" || continue
+# Check for GNU ac_path_EGREP_TRADITIONAL and select it if it is found.
+ # Check for GNU $ac_path_EGREP_TRADITIONAL
+case `"$ac_path_EGREP_TRADITIONAL" --version 2>&1` in #(
+*GNU*)
+ ac_cv_path_EGREP_TRADITIONAL="$ac_path_EGREP_TRADITIONAL" ac_path_EGREP_TRADITIONAL_found=:;;
+#(
+*)
+ ac_count=0
+ printf %s 0123456789 >"conftest.in"
+ while :
+ do
+ cat "conftest.in" "conftest.in" >"conftest.tmp"
+ mv "conftest.tmp" "conftest.in"
+ cp "conftest.in" "conftest.nl"
+ printf "%s\n" 'EGREP_TRADITIONAL' >> "conftest.nl"
+ "$ac_path_EGREP_TRADITIONAL" -E 'EGR(EP|AC)_TRADITIONAL$' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+ as_fn_arith $ac_count + 1 && ac_count=$as_val
+ if test $ac_count -gt ${ac_path_EGREP_TRADITIONAL_max-0}; then
+ # Best one so far, save it but keep looking for a better one
+ ac_cv_path_EGREP_TRADITIONAL="$ac_path_EGREP_TRADITIONAL"
+ ac_path_EGREP_TRADITIONAL_max=$ac_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test $ac_count -gt 10 && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+ $ac_path_EGREP_TRADITIONAL_found && break 3
+ done
+ done
+ done
+IFS=$as_save_IFS
+ if test -z "$ac_cv_path_EGREP_TRADITIONAL"; then
+ :
+ fi
+else
+ ac_cv_path_EGREP_TRADITIONAL=$EGREP_TRADITIONAL
+fi
+
+ if test "$ac_cv_path_EGREP_TRADITIONAL"
+then :
+ ac_cv_path_EGREP_TRADITIONAL="$ac_cv_path_EGREP_TRADITIONAL -E"
+else case e in #(
+ e) if test -z "$EGREP_TRADITIONAL"; then
+ ac_path_EGREP_TRADITIONAL_found=false
+ # Loop through the user's path and test for each of PROGNAME-LIST
+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
+do
+ IFS=$as_save_IFS
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
+ for ac_prog in egrep
+ do
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ ac_path_EGREP_TRADITIONAL="$as_dir$ac_prog$ac_exec_ext"
+ as_fn_executable_p "$ac_path_EGREP_TRADITIONAL" || continue
+# Check for GNU ac_path_EGREP_TRADITIONAL and select it if it is found.
+ # Check for GNU $ac_path_EGREP_TRADITIONAL
+case `"$ac_path_EGREP_TRADITIONAL" --version 2>&1` in #(
+*GNU*)
+ ac_cv_path_EGREP_TRADITIONAL="$ac_path_EGREP_TRADITIONAL" ac_path_EGREP_TRADITIONAL_found=:;;
+#(
+*)
+ ac_count=0
+ printf %s 0123456789 >"conftest.in"
+ while :
+ do
+ cat "conftest.in" "conftest.in" >"conftest.tmp"
+ mv "conftest.tmp" "conftest.in"
+ cp "conftest.in" "conftest.nl"
+ printf "%s\n" 'EGREP_TRADITIONAL' >> "conftest.nl"
+ "$ac_path_EGREP_TRADITIONAL" 'EGR(EP|AC)_TRADITIONAL$' < "conftest.nl" >"conftest.out" 2>/dev/null || break
+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break
+ as_fn_arith $ac_count + 1 && ac_count=$as_val
+ if test $ac_count -gt ${ac_path_EGREP_TRADITIONAL_max-0}; then
+ # Best one so far, save it but keep looking for a better one
+ ac_cv_path_EGREP_TRADITIONAL="$ac_path_EGREP_TRADITIONAL"
+ ac_path_EGREP_TRADITIONAL_max=$ac_count
+ fi
+ # 10*(2^10) chars as input seems more than enough
+ test $ac_count -gt 10 && break
+ done
+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;
+esac
+
+ $ac_path_EGREP_TRADITIONAL_found && break 3
+ done
+ done
+ done
+IFS=$as_save_IFS
+ if test -z "$ac_cv_path_EGREP_TRADITIONAL"; then
+ as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+ fi
+else
+ ac_cv_path_EGREP_TRADITIONAL=$EGREP_TRADITIONAL
+fi
+ ;;
+esac
+fi ;;
+esac
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP_TRADITIONAL" >&5
+printf "%s\n" "$ac_cv_path_EGREP_TRADITIONAL" >&6; }
+ EGREP_TRADITIONAL=$ac_cv_path_EGREP_TRADITIONAL
+
@@ -18147,8 +18615,14 @@ printf %s "checking for pthread_join using $CC $PTHREAD_CFLAGS $PTHREAD_LIBS...
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
- builtin and then its argument prototype would still apply. */
-char pthread_join ();
+ builtin and then its argument prototype would still apply.
+ The 'extern "C"' is for builds by C++ compilers;
+ although this is not generally supported in C code supporting it here
+ has little cost and some practical benefit (sr 110532). */
+#ifdef __cplusplus
+extern "C"
+#endif
+char pthread_join (void);
int
main (void)
{
@@ -18242,7 +18716,7 @@ case $host_os in
_ACEOF
if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
- $EGREP "AX_PTHREAD_ZOS_MISSING" >/dev/null 2>&1
+ $EGREP_TRADITIONAL "AX_PTHREAD_ZOS_MISSING" >/dev/null 2>&1
then :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: IBM z/OS requires -D_OPEN_THREADS or -D_UNIX03_THREADS to enable pthreads support." >&5
printf "%s\n" "$as_me: WARNING: IBM z/OS requires -D_OPEN_THREADS or -D_UNIX03_THREADS to enable pthreads support." >&2;}
@@ -18272,8 +18746,8 @@ printf %s "checking whether $CC is Clang... " >&6; }
if test ${ax_cv_PTHREAD_CLANG+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- ax_cv_PTHREAD_CLANG=no
+else case e in #(
+ e) ax_cv_PTHREAD_CLANG=no
# Note that Autoconf sets GCC=yes for Clang as well as GCC
if test "x$GCC" = "xyes"; then
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
@@ -18285,14 +18759,15 @@ else $as_nop
_ACEOF
if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
- $EGREP "AX_PTHREAD_CC_IS_CLANG" >/dev/null 2>&1
+ $EGREP_TRADITIONAL "AX_PTHREAD_CC_IS_CLANG" >/dev/null 2>&1
then :
ax_cv_PTHREAD_CLANG=yes
fi
rm -rf conftest*
fi
-
+ ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_PTHREAD_CLANG" >&5
printf "%s\n" "$ax_cv_PTHREAD_CLANG" >&6; }
@@ -18342,8 +18817,9 @@ esac
if test "x$ax_pthread_check_macro" = "x--"
then :
ax_pthread_check_cond=0
-else $as_nop
- ax_pthread_check_cond="!defined($ax_pthread_check_macro)"
+else case e in #(
+ e) ax_pthread_check_cond="!defined($ax_pthread_check_macro)" ;;
+esac
fi
@@ -18377,8 +18853,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_prog_ax_pthread_config+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test -n "$ax_pthread_config"; then
+else case e in #(
+ e) if test -n "$ax_pthread_config"; then
ac_cv_prog_ax_pthread_config="$ax_pthread_config" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -18401,7 +18877,8 @@ done
IFS=$as_save_IFS
test -z "$ac_cv_prog_ax_pthread_config" && ac_cv_prog_ax_pthread_config="no"
-fi
+fi ;;
+esac
fi
ax_pthread_config=$ac_cv_prog_ax_pthread_config
if test -n "$ax_pthread_config"; then
@@ -18534,8 +19011,8 @@ printf %s "checking whether Clang needs flag to prevent \"argument unused\" warn
if test ${ax_cv_PTHREAD_CLANG_NO_WARN_FLAG+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- ax_cv_PTHREAD_CLANG_NO_WARN_FLAG=unknown
+else case e in #(
+ e) ax_cv_PTHREAD_CLANG_NO_WARN_FLAG=unknown
# Create an alternate version of $ac_link that compiles and
# links in two steps (.c -> .o, .o -> exe) instead of one
# (.c -> exe), because the warning occurs only in the second
@@ -18581,7 +19058,8 @@ then :
ax_pthread_try=no
fi
ax_cv_PTHREAD_CLANG_NO_WARN_FLAG="$ax_pthread_try"
-
+ ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" >&5
printf "%s\n" "$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" >&6; }
@@ -18608,8 +19086,8 @@ printf %s "checking for joinable pthread attribute... " >&6; }
if test ${ax_cv_PTHREAD_JOINABLE_ATTR+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- ax_cv_PTHREAD_JOINABLE_ATTR=unknown
+else case e in #(
+ e) ax_cv_PTHREAD_JOINABLE_ATTR=unknown
for ax_pthread_attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
@@ -18629,7 +19107,8 @@ fi
rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
done
-
+ ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_PTHREAD_JOINABLE_ATTR" >&5
printf "%s\n" "$ax_cv_PTHREAD_JOINABLE_ATTR" >&6; }
@@ -18649,14 +19128,15 @@ printf %s "checking whether more special flags are required for pthreads... " >&
if test ${ax_cv_PTHREAD_SPECIAL_FLAGS+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- ax_cv_PTHREAD_SPECIAL_FLAGS=no
+else case e in #(
+ e) ax_cv_PTHREAD_SPECIAL_FLAGS=no
case $host_os in
solaris*)
ax_cv_PTHREAD_SPECIAL_FLAGS="-D_POSIX_PTHREAD_SEMANTICS"
;;
esac
-
+ ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_PTHREAD_SPECIAL_FLAGS" >&5
printf "%s\n" "$ax_cv_PTHREAD_SPECIAL_FLAGS" >&6; }
@@ -18672,8 +19152,8 @@ printf %s "checking for PTHREAD_PRIO_INHERIT... " >&6; }
if test ${ax_cv_PTHREAD_PRIO_INHERIT+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+else case e in #(
+ e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <pthread.h>
int
@@ -18688,12 +19168,14 @@ _ACEOF
if ac_fn_c_try_link "$LINENO"
then :
ax_cv_PTHREAD_PRIO_INHERIT=yes
-else $as_nop
- ax_cv_PTHREAD_PRIO_INHERIT=no
+else case e in #(
+ e) ax_cv_PTHREAD_PRIO_INHERIT=no ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
-
+ ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ax_cv_PTHREAD_PRIO_INHERIT" >&5
printf "%s\n" "$ax_cv_PTHREAD_PRIO_INHERIT" >&6; }
@@ -18743,8 +19225,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_prog_PTHREAD_CC+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test -n "$PTHREAD_CC"; then
+else case e in #(
+ e) if test -n "$PTHREAD_CC"; then
ac_cv_prog_PTHREAD_CC="$PTHREAD_CC" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -18766,7 +19248,8 @@ done
done
IFS=$as_save_IFS
-fi
+fi ;;
+esac
fi
PTHREAD_CC=$ac_cv_prog_PTHREAD_CC
if test -n "$PTHREAD_CC"; then
@@ -18793,8 +19276,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_prog_PTHREAD_CXX+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test -n "$PTHREAD_CXX"; then
+else case e in #(
+ e) if test -n "$PTHREAD_CXX"; then
ac_cv_prog_PTHREAD_CXX="$PTHREAD_CXX" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -18816,7 +19299,8 @@ done
done
IFS=$as_save_IFS
-fi
+fi ;;
+esac
fi
PTHREAD_CXX=$ac_cv_prog_PTHREAD_CXX
if test -n "$PTHREAD_CXX"; then
@@ -18886,28 +19370,30 @@ fi
# The cast to long int works around a bug in the HP C Compiler
# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
-# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# declarations like 'int a3[[(sizeof (unsigned char)) >= 0]];'.
# This bug is HP SR number 8606223364.
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of unsigned long" >&5
printf %s "checking size of unsigned long... " >&6; }
if test ${ac_cv_sizeof_unsigned_long+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (unsigned long))" "ac_cv_sizeof_unsigned_long" "$ac_includes_default"
+else case e in #(
+ e) if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (unsigned long))" "ac_cv_sizeof_unsigned_long" "$ac_includes_default"
then :
-else $as_nop
- if test "$ac_cv_type_unsigned_long" = yes; then
- { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+else case e in #(
+ e) if test "$ac_cv_type_unsigned_long" = yes; then
+ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;}
as_fn_error 77 "cannot compute sizeof (unsigned long)
-See \`config.log' for more details" "$LINENO" 5; }
+See 'config.log' for more details" "$LINENO" 5; }
else
ac_cv_sizeof_unsigned_long=0
- fi
+ fi ;;
+esac
fi
-
+ ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_unsigned_long" >&5
printf "%s\n" "$ac_cv_sizeof_unsigned_long" >&6; }
@@ -18919,28 +19405,30 @@ printf "%s\n" "#define SIZEOF_UNSIGNED_LONG $ac_cv_sizeof_unsigned_long" >>confd
# The cast to long int works around a bug in the HP C Compiler
# version HP92453-01 B.11.11.23709.GP, which incorrectly rejects
-# declarations like `int a3[[(sizeof (unsigned char)) >= 0]];'.
+# declarations like 'int a3[[(sizeof (unsigned char)) >= 0]];'.
# This bug is HP SR number 8606223364.
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of pthread_t" >&5
printf %s "checking size of pthread_t... " >&6; }
if test ${ac_cv_sizeof_pthread_t+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (pthread_t))" "ac_cv_sizeof_pthread_t" "$ac_includes_default"
+else case e in #(
+ e) if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (pthread_t))" "ac_cv_sizeof_pthread_t" "$ac_includes_default"
then :
-else $as_nop
- if test "$ac_cv_type_pthread_t" = yes; then
- { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+else case e in #(
+ e) if test "$ac_cv_type_pthread_t" = yes; then
+ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;}
as_fn_error 77 "cannot compute sizeof (pthread_t)
-See \`config.log' for more details" "$LINENO" 5; }
+See 'config.log' for more details" "$LINENO" 5; }
else
ac_cv_sizeof_pthread_t=0
- fi
+ fi ;;
+esac
fi
-
+ ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_pthread_t" >&5
printf "%s\n" "$ac_cv_sizeof_pthread_t" >&6; }
@@ -19008,8 +19496,9 @@ fi
if test ${with_solaris_threads+y}
then :
withval=$with_solaris_threads;
-else $as_nop
- withval="no"
+else case e in #(
+ e) withval="no" ;;
+esac
fi
ub_have_sol_threads=no
@@ -19023,15 +19512,21 @@ printf %s "checking for library containing thr_create... " >&6; }
if test ${ac_cv_search_thr_create+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- ac_func_search_save_LIBS=$LIBS
+else case e in #(
+ e) ac_func_search_save_LIBS=$LIBS
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
- builtin and then its argument prototype would still apply. */
-char thr_create ();
+ builtin and then its argument prototype would still apply.
+ The 'extern "C"' is for builds by C++ compilers;
+ although this is not generally supported in C code supporting it here
+ has little cost and some practical benefit (sr 110532). */
+#ifdef __cplusplus
+extern "C"
+#endif
+char thr_create (void);
int
main (void)
{
@@ -19062,11 +19557,13 @@ done
if test ${ac_cv_search_thr_create+y}
then :
-else $as_nop
- ac_cv_search_thr_create=no
+else case e in #(
+ e) ac_cv_search_thr_create=no ;;
+esac
fi
rm conftest.$ac_ext
-LIBS=$ac_func_search_save_LIBS
+LIBS=$ac_func_search_save_LIBS ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_thr_create" >&5
printf "%s\n" "$ac_cv_search_thr_create" >&6; }
@@ -19087,8 +19584,8 @@ cache=`echo mt | sed 'y%.=/+-%___p_%'`
if eval test \${cv_prog_cc_flag_$cache+y}
then :
printf %s "(cached) " >&6
-else $as_nop
-
+else case e in #(
+ e)
echo 'void f(void){}' >conftest.c
if test -z "`$CC $CPPFLAGS $CFLAGS -mt -c conftest.c 2>&1`"; then
eval "cv_prog_cc_flag_$cache=yes"
@@ -19096,7 +19593,8 @@ else
eval "cv_prog_cc_flag_$cache=no"
fi
rm -f conftest conftest.o conftest.c
-
+ ;;
+esac
fi
if eval "test \"`echo '$cv_prog_cc_flag_'$cache`\" = yes"; then
@@ -19113,10 +19611,11 @@ fi
ub_have_sol_threads=yes
-else $as_nop
-
+else case e in #(
+ e)
as_fn_error $? "no solaris threads found." "$LINENO" 5
-
+ ;;
+esac
fi
fi
@@ -19149,8 +19648,9 @@ printf "%s\n" "#define UB_SYSLOG_FACILITY ${UNBOUND_SYSLOG_FACILITY}" >>confdefs
if test ${with_dynlibmodule+y}
then :
withval=$with_dynlibmodule;
-else $as_nop
- withval="no"
+else case e in #(
+ e) withval="no" ;;
+esac
fi
@@ -19173,15 +19673,21 @@ printf %s "checking for library containing dlopen... " >&6; }
if test ${ac_cv_search_dlopen+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- ac_func_search_save_LIBS=$LIBS
+else case e in #(
+ e) ac_func_search_save_LIBS=$LIBS
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
- builtin and then its argument prototype would still apply. */
-char dlopen ();
+ builtin and then its argument prototype would still apply.
+ The 'extern "C"' is for builds by C++ compilers;
+ although this is not generally supported in C code supporting it here
+ has little cost and some practical benefit (sr 110532). */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dlopen (void);
int
main (void)
{
@@ -19212,11 +19718,13 @@ done
if test ${ac_cv_search_dlopen+y}
then :
-else $as_nop
- ac_cv_search_dlopen=no
+else case e in #(
+ e) ac_cv_search_dlopen=no ;;
+esac
fi
rm conftest.$ac_ext
-LIBS=$ac_func_search_save_LIBS
+LIBS=$ac_func_search_save_LIBS ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_dlopen" >&5
printf "%s\n" "$ac_cv_search_dlopen" >&6; }
@@ -19240,8 +19748,9 @@ fi
if test ${with_pyunbound+y}
then :
withval=$with_pyunbound;
-else $as_nop
- withval="no"
+else case e in #(
+ e) withval="no" ;;
+esac
fi
@@ -19258,8 +19767,9 @@ fi
if test ${with_pythonmodule+y}
then :
withval=$with_pythonmodule;
-else $as_nop
- withval="no"
+else case e in #(
+ e) withval="no" ;;
+esac
fi
@@ -19287,8 +19797,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_path_PYTHON+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- case $PYTHON in
+else case e in #(
+ e) case $PYTHON in
[\\/]* | ?:[\\/]*)
ac_cv_path_PYTHON="$PYTHON" # Let the user override the test with a path.
;;
@@ -19313,6 +19823,7 @@ done
IFS=$as_save_IFS
;;
+esac ;;
esac
fi
PYTHON=$ac_cv_path_PYTHON
@@ -19490,8 +20001,9 @@ _ACEOF
if ac_fn_c_try_link "$LINENO"
then :
pythonexists=yes
-else $as_nop
- pythonexists=no
+else case e in #(
+ e) pythonexists=no ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
@@ -19601,8 +20113,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_path_SWIG+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- case $SWIG in
+else case e in #(
+ e) case $SWIG in
[\\/]* | ?:[\\/]*)
ac_cv_path_SWIG="$SWIG" # Let the user override the test with a path.
;;
@@ -19627,6 +20139,7 @@ done
IFS=$as_save_IFS
;;
+esac ;;
esac
fi
SWIG=$ac_cv_path_SWIG
@@ -19727,8 +20240,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_path_SWIG+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- case $SWIG in
+else case e in #(
+ e) case $SWIG in
[\\/]* | ?:[\\/]*)
ac_cv_path_SWIG="$SWIG" # Let the user override the test with a path.
;;
@@ -19753,6 +20266,7 @@ done
IFS=$as_save_IFS
;;
+esac ;;
esac
fi
SWIG=$ac_cv_path_SWIG
@@ -19914,15 +20428,17 @@ ax_date_fmt="%Y%m%d"
if test x"$SOURCE_DATE_EPOCH" = x
then :
CONFIG_DATE=`date "+$ax_date_fmt"`
-else $as_nop
- ax_build_date=`date -u -d "@$SOURCE_DATE_EPOCH" "+$ax_date_fmt" 2>/dev/null \
+else case e in #(
+ e) ax_build_date=`date -u -d "@$SOURCE_DATE_EPOCH" "+$ax_date_fmt" 2>/dev/null \
|| date -u -r "$SOURCE_DATE_EPOCH" "+$ax_date_fmt" 2>/dev/null`
if test x"$ax_build_date" = x
then :
as_fn_error $? "malformed SOURCE_DATE_EPOCH" "$LINENO" 5
-else $as_nop
- CONFIG_DATE=$ax_build_date
-fi
+else case e in #(
+ e) CONFIG_DATE=$ax_build_date ;;
+esac
+fi ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CONFIG_DATE" >&5
printf "%s\n" "$CONFIG_DATE" >&6; }
@@ -20017,10 +20533,11 @@ if test ${with_ssl+y}
then :
withval=$with_ssl;
-else $as_nop
-
+else case e in #(
+ e)
withval="yes"
-
+ ;;
+esac
fi
if test x_$withval = x_no; then
@@ -20116,8 +20633,8 @@ printf "%s\n" "yes" >&6; }
printf "%s\n" "#define HAVE_EVP_SHA256 1" >>confdefs.h
-else $as_nop
-
+else case e in #(
+ e)
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
printf "%s\n" "no" >&6; }
# check if -lwsock32 or -lgdi32 are needed.
@@ -20150,8 +20667,8 @@ printf "%s\n" "#define HAVE_EVP_SHA256 1" >>confdefs.h
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
printf "%s\n" "yes" >&6; }
-else $as_nop
-
+else case e in #(
+ e)
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
printf "%s\n" "no" >&6; }
LIBS="$BAKLIBS"
@@ -20184,8 +20701,8 @@ printf "%s\n" "#define HAVE_EVP_SHA256 1" >>confdefs.h
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
printf "%s\n" "yes" >&6; }
-else $as_nop
-
+else case e in #(
+ e)
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
printf "%s\n" "no" >&6; }
LIBS="$BAKLIBS"
@@ -20218,8 +20735,8 @@ printf "%s\n" "#define HAVE_EVP_SHA256 1" >>confdefs.h
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
printf "%s\n" "yes" >&6; }
-else $as_nop
-
+else case e in #(
+ e)
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
printf "%s\n" "no" >&6; }
LIBS="$BAKLIBS"
@@ -20252,8 +20769,8 @@ printf "%s\n" "#define HAVE_EVP_SHA256 1" >>confdefs.h
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
printf "%s\n" "yes" >&6; }
-else $as_nop
-
+else case e in #(
+ e)
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
printf "%s\n" "no" >&6; }
LIBS="$BAKLIBS"
@@ -20285,32 +20802,38 @@ printf "%s\n" "#define HAVE_EVP_SHA256 1" >>confdefs.h
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
printf "%s\n" "yes" >&6; }
-else $as_nop
-
+else case e in #(
+ e)
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
printf "%s\n" "no" >&6; }
as_fn_error $? "OpenSSL found in $ssldir, but version 0.9.7 or higher is required" "$LINENO" 5
-
+ ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
-
+ ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
-
+ ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
-
+ ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
-
+ ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
-
+ ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
@@ -20355,8 +20878,14 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
- builtin and then its argument prototype would still apply. */
-char SSL_CTX_new ();
+ builtin and then its argument prototype would still apply.
+ The 'extern "C"' is for builds by C++ compilers;
+ although this is not generally supported in C code supporting it here
+ has little cost and some practical benefit (sr 110532). */
+#ifdef __cplusplus
+extern "C"
+#endif
+char SSL_CTX_new (void);
int
main (void)
{
@@ -20372,8 +20901,8 @@ then :
printf "%s\n" "no" >&6; }
LIBS="$BAKLIBS"
-else $as_nop
-
+else case e in #(
+ e)
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
printf "%s\n" "yes" >&6; }
LIBS="$BAKLIBS"
@@ -20382,15 +20911,21 @@ printf %s "checking for library containing dlopen... " >&6; }
if test ${ac_cv_search_dlopen+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- ac_func_search_save_LIBS=$LIBS
+else case e in #(
+ e) ac_func_search_save_LIBS=$LIBS
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
- builtin and then its argument prototype would still apply. */
-char dlopen ();
+ builtin and then its argument prototype would still apply.
+ The 'extern "C"' is for builds by C++ compilers;
+ although this is not generally supported in C code supporting it here
+ has little cost and some practical benefit (sr 110532). */
+#ifdef __cplusplus
+extern "C"
+#endif
+char dlopen (void);
int
main (void)
{
@@ -20421,11 +20956,13 @@ done
if test ${ac_cv_search_dlopen+y}
then :
-else $as_nop
- ac_cv_search_dlopen=no
+else case e in #(
+ e) ac_cv_search_dlopen=no ;;
+esac
fi
rm conftest.$ac_ext
-LIBS=$ac_func_search_save_LIBS
+LIBS=$ac_func_search_save_LIBS ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_dlopen" >&5
printf "%s\n" "$ac_cv_search_dlopen" >&6; }
@@ -20436,7 +20973,8 @@ then :
fi
-
+ ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
@@ -20471,13 +21009,14 @@ then :
printf "%s\n" "no" >&6; }
LIBS="$BAKLIBS"
-else $as_nop
-
+else case e in #(
+ e)
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
printf "%s\n" "yes" >&6; }
LIBS="$BAKLIBS"
LIBS="$LIBS -lcrypt32"
-
+ ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
@@ -20497,8 +21036,8 @@ printf %s "checking for $CC options needed to detect all undeclared functions...
if test ${ac_cv_c_undeclared_builtin_options+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- ac_save_CFLAGS=$CFLAGS
+else case e in #(
+ e) ac_save_CFLAGS=$CFLAGS
ac_cv_c_undeclared_builtin_options='cannot detect'
for ac_arg in '' -fno-builtin; do
CFLAGS="$ac_save_CFLAGS $ac_arg"
@@ -20517,8 +21056,8 @@ _ACEOF
if ac_fn_c_try_compile "$LINENO"
then :
-else $as_nop
- # This test program should compile successfully.
+else case e in #(
+ e) # This test program should compile successfully.
# No library function is consistently available on
# freestanding implementations, so test against a dummy
# declaration. Include always-available headers on the
@@ -20546,26 +21085,29 @@ then :
if test x"$ac_arg" = x
then :
ac_cv_c_undeclared_builtin_options='none needed'
-else $as_nop
- ac_cv_c_undeclared_builtin_options=$ac_arg
+else case e in #(
+ e) ac_cv_c_undeclared_builtin_options=$ac_arg ;;
+esac
fi
break
fi
-rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
done
CFLAGS=$ac_save_CFLAGS
-
+ ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_undeclared_builtin_options" >&5
printf "%s\n" "$ac_cv_c_undeclared_builtin_options" >&6; }
case $ac_cv_c_undeclared_builtin_options in #(
'cannot detect') :
- { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;}
+ { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in '$ac_pwd':" >&5
+printf "%s\n" "$as_me: error: in '$ac_pwd':" >&2;}
as_fn_error $? "cannot make $CC report undeclared builtins
-See \`config.log' for more details" "$LINENO" 5; } ;; #(
+See 'config.log' for more details" "$LINENO" 5; } ;; #(
'none needed') :
ac_c_undeclared_builtin_options='' ;; #(
*) :
@@ -20576,32 +21118,36 @@ ac_fn_check_decl "$LINENO" "strlcpy" "ac_cv_have_decl_strlcpy" "$ac_includes_def
if test "x$ac_cv_have_decl_strlcpy" = xyes
then :
ac_have_decl=1
-else $as_nop
- ac_have_decl=0
+else case e in #(
+ e) ac_have_decl=0 ;;
+esac
fi
printf "%s\n" "#define HAVE_DECL_STRLCPY $ac_have_decl" >>confdefs.h
ac_fn_check_decl "$LINENO" "strlcat" "ac_cv_have_decl_strlcat" "$ac_includes_default" "$ac_c_undeclared_builtin_options" "CFLAGS"
if test "x$ac_cv_have_decl_strlcat" = xyes
then :
ac_have_decl=1
-else $as_nop
- ac_have_decl=0
+else case e in #(
+ e) ac_have_decl=0 ;;
+esac
fi
printf "%s\n" "#define HAVE_DECL_STRLCAT $ac_have_decl" >>confdefs.h
ac_fn_check_decl "$LINENO" "arc4random" "ac_cv_have_decl_arc4random" "$ac_includes_default" "$ac_c_undeclared_builtin_options" "CFLAGS"
if test "x$ac_cv_have_decl_arc4random" = xyes
then :
ac_have_decl=1
-else $as_nop
- ac_have_decl=0
+else case e in #(
+ e) ac_have_decl=0 ;;
+esac
fi
printf "%s\n" "#define HAVE_DECL_ARC4RANDOM $ac_have_decl" >>confdefs.h
ac_fn_check_decl "$LINENO" "arc4random_uniform" "ac_cv_have_decl_arc4random_uniform" "$ac_includes_default" "$ac_c_undeclared_builtin_options" "CFLAGS"
if test "x$ac_cv_have_decl_arc4random_uniform" = xyes
then :
ac_have_decl=1
-else $as_nop
- ac_have_decl=0
+else case e in #(
+ e) ac_have_decl=0 ;;
+esac
fi
printf "%s\n" "#define HAVE_DECL_ARC4RANDOM_UNIFORM $ac_have_decl" >>confdefs.h
@@ -20920,8 +21466,9 @@ $ac_includes_default
if test "x$ac_cv_have_decl_SSL_COMP_get_compression_methods" = xyes
then :
ac_have_decl=1
-else $as_nop
- ac_have_decl=0
+else case e in #(
+ e) ac_have_decl=0 ;;
+esac
fi
printf "%s\n" "#define HAVE_DECL_SSL_COMP_GET_COMPRESSION_METHODS $ac_have_decl" >>confdefs.h
ac_fn_check_decl "$LINENO" "sk_SSL_COMP_pop_free" "ac_cv_have_decl_sk_SSL_COMP_pop_free" "
@@ -20948,8 +21495,9 @@ $ac_includes_default
if test "x$ac_cv_have_decl_sk_SSL_COMP_pop_free" = xyes
then :
ac_have_decl=1
-else $as_nop
- ac_have_decl=0
+else case e in #(
+ e) ac_have_decl=0 ;;
+esac
fi
printf "%s\n" "#define HAVE_DECL_SK_SSL_COMP_POP_FREE $ac_have_decl" >>confdefs.h
ac_fn_check_decl "$LINENO" "SSL_CTX_set_ecdh_auto" "ac_cv_have_decl_SSL_CTX_set_ecdh_auto" "
@@ -20976,8 +21524,9 @@ $ac_includes_default
if test "x$ac_cv_have_decl_SSL_CTX_set_ecdh_auto" = xyes
then :
ac_have_decl=1
-else $as_nop
- ac_have_decl=0
+else case e in #(
+ e) ac_have_decl=0 ;;
+esac
fi
printf "%s\n" "#define HAVE_DECL_SSL_CTX_SET_ECDH_AUTO $ac_have_decl" >>confdefs.h
@@ -21027,14 +21576,15 @@ then :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: int" >&5
printf "%s\n" "int" >&6; }
-else $as_nop
-
+else case e in #(
+ e)
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: void" >&5
printf "%s\n" "void" >&6; }
printf "%s\n" "#define HMAC_INIT_EX_RETURNS_VOID 1" >>confdefs.h
-
+ ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
@@ -21065,21 +21615,27 @@ fi
if test "x$ac_cv_header_bsd_string_h" = xyes -a "x$ac_cv_header_bsd_stdlib_h" = xyes; then
for func in strlcpy strlcat arc4random arc4random_uniform reallocarray; do
- as_ac_Search=`printf "%s\n" "ac_cv_search_$func" | $as_tr_sh`
+ as_ac_Search=`printf "%s\n" "ac_cv_search_$func" | sed "$as_sed_sh"`
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing $func" >&5
printf %s "checking for library containing $func... " >&6; }
if eval test \${$as_ac_Search+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- ac_func_search_save_LIBS=$LIBS
+else case e in #(
+ e) ac_func_search_save_LIBS=$LIBS
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
- builtin and then its argument prototype would still apply. */
-char $func ();
+ builtin and then its argument prototype would still apply.
+ The 'extern "C"' is for builds by C++ compilers;
+ although this is not generally supported in C code supporting it here
+ has little cost and some practical benefit (sr 110532). */
+#ifdef __cplusplus
+extern "C"
+#endif
+char $func (void);
int
main (void)
{
@@ -21110,11 +21666,13 @@ done
if eval test \${$as_ac_Search+y}
then :
-else $as_nop
- eval "$as_ac_Search=no"
+else case e in #(
+ e) eval "$as_ac_Search=no" ;;
+esac
fi
rm conftest.$ac_ext
-LIBS=$ac_func_search_save_LIBS
+LIBS=$ac_func_search_save_LIBS ;;
+esac
fi
eval ac_res=\$$as_ac_Search
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
@@ -21209,16 +21767,18 @@ case "$enable_gost" in
if test "x$ac_cv_func_EVP_PKEY_set_type_str" = xyes
then :
:
-else $as_nop
- as_fn_error $? "OpenSSL 1.0.0 is needed for GOST support" "$LINENO" 5
+else case e in #(
+ e) as_fn_error $? "OpenSSL 1.0.0 is needed for GOST support" "$LINENO" 5 ;;
+esac
fi
ac_fn_c_check_func "$LINENO" "EC_KEY_new" "ac_cv_func_EC_KEY_new"
if test "x$ac_cv_func_EC_KEY_new" = xyes
then :
-else $as_nop
- as_fn_error $? "OpenSSL does not support ECC, needed for GOST support" "$LINENO" 5
+else case e in #(
+ e) as_fn_error $? "OpenSSL does not support ECC, needed for GOST support" "$LINENO" 5 ;;
+esac
fi
@@ -21232,8 +21792,8 @@ fi
if test "$cross_compiling" = yes
then :
eval "ac_cv_c_gost_works=maybe"
-else $as_nop
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+else case e in #(
+ e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <string.h>
@@ -21321,11 +21881,13 @@ _ACEOF
if ac_fn_c_try_run "$LINENO"
then :
eval "ac_cv_c_gost_works=yes"
-else $as_nop
- eval "ac_cv_c_gost_works=no"
+else case e in #(
+ e) eval "ac_cv_c_gost_works=no" ;;
+esac
fi
rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
- conftest.$ac_objext conftest.beam conftest.$ac_ext
+ conftest.$ac_objext conftest.beam conftest.$ac_ext ;;
+esac
fi
CFLAGS="$BAKCFLAGS"
@@ -21371,26 +21933,29 @@ then :
fi
-else $as_nop
-
+else case e in #(
+ e)
# without EVP_PKEY_fromdata, older openssl, check for support
ac_fn_c_check_func "$LINENO" "ECDSA_sign" "ac_cv_func_ECDSA_sign"
if test "x$ac_cv_func_ECDSA_sign" = xyes
then :
-else $as_nop
- as_fn_error $? "OpenSSL does not support ECDSA: please upgrade or rerun with --disable-ecdsa" "$LINENO" 5
+else case e in #(
+ e) as_fn_error $? "OpenSSL does not support ECDSA: please upgrade or rerun with --disable-ecdsa" "$LINENO" 5 ;;
+esac
fi
ac_fn_c_check_func "$LINENO" "SHA384_Init" "ac_cv_func_SHA384_Init"
if test "x$ac_cv_func_SHA384_Init" = xyes
then :
-else $as_nop
- as_fn_error $? "OpenSSL does not support SHA384: please upgrade or rerun with --disable-ecdsa" "$LINENO" 5
+else case e in #(
+ e) as_fn_error $? "OpenSSL does not support SHA384: please upgrade or rerun with --disable-ecdsa" "$LINENO" 5 ;;
+esac
fi
-
+ ;;
+esac
fi
ac_fn_check_decl "$LINENO" "NID_X9_62_prime256v1" "ac_cv_have_decl_NID_X9_62_prime256v1" "$ac_includes_default
@@ -21400,15 +21965,17 @@ fi
if test "x$ac_cv_have_decl_NID_X9_62_prime256v1" = xyes
then :
ac_have_decl=1
-else $as_nop
- ac_have_decl=0
+else case e in #(
+ e) ac_have_decl=0 ;;
+esac
fi
printf "%s\n" "#define HAVE_DECL_NID_X9_62_PRIME256V1 $ac_have_decl" >>confdefs.h
if test $ac_have_decl = 1
then :
-else $as_nop
- as_fn_error $? "OpenSSL does not support the ECDSA curves: please upgrade or rerun with --disable-ecdsa" "$LINENO" 5
+else case e in #(
+ e) as_fn_error $? "OpenSSL does not support the ECDSA curves: please upgrade or rerun with --disable-ecdsa" "$LINENO" 5 ;;
+esac
fi
ac_fn_check_decl "$LINENO" "NID_secp384r1" "ac_cv_have_decl_NID_secp384r1" "$ac_includes_default
#include <openssl/evp.h>
@@ -21417,15 +21984,17 @@ ac_fn_check_decl "$LINENO" "NID_secp384r1" "ac_cv_have_decl_NID_secp384r1" "$ac_
if test "x$ac_cv_have_decl_NID_secp384r1" = xyes
then :
ac_have_decl=1
-else $as_nop
- ac_have_decl=0
+else case e in #(
+ e) ac_have_decl=0 ;;
+esac
fi
printf "%s\n" "#define HAVE_DECL_NID_SECP384R1 $ac_have_decl" >>confdefs.h
if test $ac_have_decl = 1
then :
-else $as_nop
- as_fn_error $? "OpenSSL does not support the ECDSA curves: please upgrade or rerun with --disable-ecdsa" "$LINENO" 5
+else case e in #(
+ e) as_fn_error $? "OpenSSL does not support the ECDSA curves: please upgrade or rerun with --disable-ecdsa" "$LINENO" 5 ;;
+esac
fi
# see if OPENSSL 1.0.0 or later (has EVP MD and Verify independency)
@@ -21471,7 +22040,7 @@ case "$enable_dsa" in
if test "x$ac_cv_func_DSA_SIG_new" = xyes
then :
- as_ac_Type=`printf "%s\n" "ac_cv_type_DSA_SIG*" | $as_tr_sh`
+ as_ac_Type=`printf "%s\n" "ac_cv_type_DSA_SIG*" | sed "$as_sed_sh"`
ac_fn_c_check_type "$LINENO" "DSA_SIG*" "$as_ac_Type" "
$ac_includes_default
#ifdef HAVE_OPENSSL_ERR_H
@@ -21498,15 +22067,17 @@ then :
printf "%s\n" "#define USE_DSA 1" >>confdefs.h
-else $as_nop
- if test "x$enable_dsa" = "xyes"; then as_fn_error $? "OpenSSL does not support DSA and you used --enable-dsa." "$LINENO" 5
- fi
+else case e in #(
+ e) if test "x$enable_dsa" = "xyes"; then as_fn_error $? "OpenSSL does not support DSA and you used --enable-dsa." "$LINENO" 5
+ fi ;;
+esac
fi
-else $as_nop
- if test "x$enable_dsa" = "xyes"; then as_fn_error $? "OpenSSL does not support DSA and you used --enable-dsa." "$LINENO" 5
- fi
+else case e in #(
+ e) if test "x$enable_dsa" = "xyes"; then as_fn_error $? "OpenSSL does not support DSA and you used --enable-dsa." "$LINENO" 5
+ fi ;;
+esac
fi
else
@@ -21553,8 +22124,9 @@ case "$enable_ed25519" in
if test "x$ac_cv_have_decl_NID_ED25519" = xyes
then :
ac_have_decl=1
-else $as_nop
- ac_have_decl=0
+else case e in #(
+ e) ac_have_decl=0 ;;
+esac
fi
printf "%s\n" "#define HAVE_DECL_NID_ED25519 $ac_have_decl" >>confdefs.h
if test $ac_have_decl = 1
@@ -21562,9 +22134,10 @@ then :
use_ed25519="yes"
-else $as_nop
- if test "x$enable_ed25519" = "xyes"; then as_fn_error $? "OpenSSL does not support ED25519 and you used --enable-ed25519." "$LINENO" 5
- fi
+else case e in #(
+ e) if test "x$enable_ed25519" = "xyes"; then as_fn_error $? "OpenSSL does not support ED25519 and you used --enable-ed25519." "$LINENO" 5
+ fi ;;
+esac
fi
fi
@@ -21608,8 +22181,9 @@ case "$enable_ed448" in
if test "x$ac_cv_have_decl_NID_ED448" = xyes
then :
ac_have_decl=1
-else $as_nop
- ac_have_decl=0
+else case e in #(
+ e) ac_have_decl=0 ;;
+esac
fi
printf "%s\n" "#define HAVE_DECL_NID_ED448 $ac_have_decl" >>confdefs.h
if test $ac_have_decl = 1
@@ -21617,9 +22191,10 @@ then :
use_ed448="yes"
-else $as_nop
- if test "x$enable_ed448" = "xyes"; then as_fn_error $? "OpenSSL does not support ED448 and you used --enable-ed448." "$LINENO" 5
- fi
+else case e in #(
+ e) if test "x$enable_ed448" = "xyes"; then as_fn_error $? "OpenSSL does not support ED448 and you used --enable-ed448." "$LINENO" 5
+ fi ;;
+esac
fi
fi
@@ -21665,8 +22240,9 @@ if test "x$ac_cv_have_decl_MSG_FASTOPEN" = xyes
then :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: Check the platform specific TFO kernel parameters are correctly configured to support client mode TFO" >&5
printf "%s\n" "$as_me: WARNING: Check the platform specific TFO kernel parameters are correctly configured to support client mode TFO" >&2;}
-else $as_nop
- as_fn_error $? "TCP Fast Open is not available for client mode: please rerun without --enable-tfo-client" "$LINENO" 5
+else case e in #(
+ e) as_fn_error $? "TCP Fast Open is not available for client mode: please rerun without --enable-tfo-client" "$LINENO" 5 ;;
+esac
fi
printf "%s\n" "#define USE_MSG_FASTOPEN 1" >>confdefs.h
@@ -21680,8 +22256,9 @@ if test "x$ac_cv_have_decl_CONNECT_RESUME_ON_READ_WRITE" = xyes
then :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: Check the platform specific TFO kernel parameters are correctly configured to support client mode TFO" >&5
printf "%s\n" "$as_me: WARNING: Check the platform specific TFO kernel parameters are correctly configured to support client mode TFO" >&2;}
-else $as_nop
- as_fn_error $? "TCP Fast Open is not available for client mode: please rerun without --enable-tfo-client" "$LINENO" 5
+else case e in #(
+ e) as_fn_error $? "TCP Fast Open is not available for client mode: please rerun without --enable-tfo-client" "$LINENO" 5 ;;
+esac
fi
printf "%s\n" "#define USE_OSX_MSG_FASTOPEN 1" >>confdefs.h
@@ -21709,8 +22286,9 @@ if test "x$ac_cv_have_decl_TCP_FASTOPEN" = xyes
then :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: Check the platform specific TFO kernel parameters are correctly configured to support server mode TFO" >&5
printf "%s\n" "$as_me: WARNING: Check the platform specific TFO kernel parameters are correctly configured to support server mode TFO" >&2;}
-else $as_nop
- as_fn_error $? "TCP Fast Open is not available for server mode: please rerun without --enable-tfo-server" "$LINENO" 5
+else case e in #(
+ e) as_fn_error $? "TCP Fast Open is not available for server mode: please rerun without --enable-tfo-server" "$LINENO" 5 ;;
+esac
fi
printf "%s\n" "#define USE_TCP_FASTOPEN 1" >>confdefs.h
@@ -21726,8 +22304,9 @@ esac
if test ${with_libevent+y}
then :
withval=$with_libevent;
-else $as_nop
- with_libevent="no"
+else case e in #(
+ e) with_libevent="no" ;;
+esac
fi
if test "x_$with_libevent" != x_no; then
@@ -21806,15 +22385,21 @@ printf %s "checking for library containing clock_gettime... " >&6; }
if test ${ac_cv_search_clock_gettime+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- ac_func_search_save_LIBS=$LIBS
+else case e in #(
+ e) ac_func_search_save_LIBS=$LIBS
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
- builtin and then its argument prototype would still apply. */
-char clock_gettime ();
+ builtin and then its argument prototype would still apply.
+ The 'extern "C"' is for builds by C++ compilers;
+ although this is not generally supported in C code supporting it here
+ has little cost and some practical benefit (sr 110532). */
+#ifdef __cplusplus
+extern "C"
+#endif
+char clock_gettime (void);
int
main (void)
{
@@ -21845,11 +22430,13 @@ done
if test ${ac_cv_search_clock_gettime+y}
then :
-else $as_nop
- ac_cv_search_clock_gettime=no
+else case e in #(
+ e) ac_cv_search_clock_gettime=no ;;
+esac
fi
rm conftest.$ac_ext
-LIBS=$ac_func_search_save_LIBS
+LIBS=$ac_func_search_save_LIBS ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_clock_gettime" >&5
printf "%s\n" "$ac_cv_search_clock_gettime" >&6; }
@@ -21882,15 +22469,21 @@ printf %s "checking for library containing event_set... " >&6; }
if test ${ac_cv_search_event_set+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- ac_func_search_save_LIBS=$LIBS
+else case e in #(
+ e) ac_func_search_save_LIBS=$LIBS
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
- builtin and then its argument prototype would still apply. */
-char event_set ();
+ builtin and then its argument prototype would still apply.
+ The 'extern "C"' is for builds by C++ compilers;
+ although this is not generally supported in C code supporting it here
+ has little cost and some practical benefit (sr 110532). */
+#ifdef __cplusplus
+extern "C"
+#endif
+char event_set (void);
int
main (void)
{
@@ -21921,11 +22514,13 @@ done
if test ${ac_cv_search_event_set+y}
then :
-else $as_nop
- ac_cv_search_event_set=no
+else case e in #(
+ e) ac_cv_search_event_set=no ;;
+esac
fi
rm conftest.$ac_ext
-LIBS=$ac_func_search_save_LIBS
+LIBS=$ac_func_search_save_LIBS ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_event_set" >&5
printf "%s\n" "$ac_cv_search_event_set" >&6; }
@@ -21937,22 +22532,28 @@ then :
fi
-else $as_nop
-
+else case e in #(
+ e)
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing event_set" >&5
printf %s "checking for library containing event_set... " >&6; }
if test ${ac_cv_search_event_set+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- ac_func_search_save_LIBS=$LIBS
+else case e in #(
+ e) ac_func_search_save_LIBS=$LIBS
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
- builtin and then its argument prototype would still apply. */
-char event_set ();
+ builtin and then its argument prototype would still apply.
+ The 'extern "C"' is for builds by C++ compilers;
+ although this is not generally supported in C code supporting it here
+ has little cost and some practical benefit (sr 110532). */
+#ifdef __cplusplus
+extern "C"
+#endif
+char event_set (void);
int
main (void)
{
@@ -21983,11 +22584,13 @@ done
if test ${ac_cv_search_event_set+y}
then :
-else $as_nop
- ac_cv_search_event_set=no
+else case e in #(
+ e) ac_cv_search_event_set=no ;;
+esac
fi
rm conftest.$ac_ext
-LIBS=$ac_func_search_save_LIBS
+LIBS=$ac_func_search_save_LIBS ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_event_set" >&5
printf "%s\n" "$ac_cv_search_event_set" >&6; }
@@ -21998,7 +22601,8 @@ then :
fi
-
+ ;;
+esac
fi
ac_fn_c_check_func "$LINENO" "event_base_free" "ac_cv_func_event_base_free"
if test "x$ac_cv_func_event_base_free" = xyes
@@ -22060,8 +22664,9 @@ fi
if test "x$ac_cv_have_decl_evsignal_assign" = xyes
then :
ac_have_decl=1
-else $as_nop
- ac_have_decl=0
+else case e in #(
+ e) ac_have_decl=0 ;;
+esac
fi
printf "%s\n" "#define HAVE_DECL_EVSIGNAL_ASSIGN $ac_have_decl" >>confdefs.h
@@ -22082,8 +22687,9 @@ fi
if test ${with_libexpat+y}
then :
withval=$with_libexpat;
-else $as_nop
- withval="/usr/local /opt/local /usr/lib /usr/pkg /usr/sfw /usr"
+else case e in #(
+ e) withval="/usr/local /opt/local /usr/lib /usr/pkg /usr/sfw /usr" ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for libexpat" >&5
@@ -22119,8 +22725,9 @@ ac_fn_check_decl "$LINENO" "XML_StopParser" "ac_cv_have_decl_XML_StopParser" "$a
if test "x$ac_cv_have_decl_XML_StopParser" = xyes
then :
ac_have_decl=1
-else $as_nop
- ac_have_decl=0
+else case e in #(
+ e) ac_have_decl=0 ;;
+esac
fi
printf "%s\n" "#define HAVE_DECL_XML_STOPPARSER $ac_have_decl" >>confdefs.h
@@ -22131,8 +22738,9 @@ printf "%s\n" "#define HAVE_DECL_XML_STOPPARSER $ac_have_decl" >>confdefs.h
if test ${with_libhiredis+y}
then :
withval=$with_libhiredis;
-else $as_nop
- withval="no"
+else case e in #(
+ e) withval="no" ;;
+esac
fi
found_libhiredis="no"
@@ -22176,8 +22784,9 @@ fi
if test "x$ac_cv_have_decl_redisConnect" = xyes
then :
ac_have_decl=1
-else $as_nop
- ac_have_decl=0
+else case e in #(
+ e) ac_have_decl=0 ;;
+esac
fi
printf "%s\n" "#define HAVE_DECL_REDISCONNECT $ac_have_decl" >>confdefs.h
@@ -22189,8 +22798,9 @@ fi
if test ${with_libnghttp2+y}
then :
withval=$with_libnghttp2;
-else $as_nop
- withval="no"
+else case e in #(
+ e) withval="no" ;;
+esac
fi
found_libnghttp2="no"
@@ -22234,8 +22844,9 @@ fi
if test "x$ac_cv_have_decl_nghttp2_session_server_new" = xyes
then :
ac_have_decl=1
-else $as_nop
- ac_have_decl=0
+else case e in #(
+ e) ac_have_decl=0 ;;
+esac
fi
printf "%s\n" "#define HAVE_DECL_NGHTTP2_SESSION_SERVER_NEW $ac_have_decl" >>confdefs.h
@@ -22247,8 +22858,9 @@ fi
if test ${with_libngtcp2+y}
then :
withval=$with_libngtcp2;
-else $as_nop
- withval="no"
+else case e in #(
+ e) withval="no" ;;
+esac
fi
found_libngtcp2="no"
@@ -22306,8 +22918,9 @@ fi
if test "x$ac_cv_have_decl_ngtcp2_conn_server_new" = xyes
then :
ac_have_decl=1
-else $as_nop
- ac_have_decl=0
+else case e in #(
+ e) ac_have_decl=0 ;;
+esac
fi
printf "%s\n" "#define HAVE_DECL_NGTCP2_CONN_SERVER_NEW $ac_have_decl" >>confdefs.h
@@ -22318,8 +22931,9 @@ printf "%s\n" "#define HAVE_DECL_NGTCP2_CONN_SERVER_NEW $ac_have_decl" >>confdef
if test "x$ac_cv_have_decl_ngtcp2_crypto_encrypt_cb" = xyes
then :
ac_have_decl=1
-else $as_nop
- ac_have_decl=0
+else case e in #(
+ e) ac_have_decl=0 ;;
+esac
fi
printf "%s\n" "#define HAVE_DECL_NGTCP2_CRYPTO_ENCRYPT_CB $ac_have_decl" >>confdefs.h
@@ -22328,16 +22942,22 @@ printf %s "checking for ngtcp2_crypto_encrypt_cb in -lngtcp2_crypto_openssl... "
if test ${ac_cv_lib_ngtcp2_crypto_openssl_ngtcp2_crypto_encrypt_cb+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- ac_check_lib_save_LIBS=$LIBS
+else case e in #(
+ e) ac_check_lib_save_LIBS=$LIBS
LIBS="-lngtcp2_crypto_openssl $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
- builtin and then its argument prototype would still apply. */
-char ngtcp2_crypto_encrypt_cb ();
+ builtin and then its argument prototype would still apply.
+ The 'extern "C"' is for builds by C++ compilers;
+ although this is not generally supported in C code supporting it here
+ has little cost and some practical benefit (sr 110532). */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ngtcp2_crypto_encrypt_cb (void);
int
main (void)
{
@@ -22349,12 +22969,14 @@ _ACEOF
if ac_fn_c_try_link "$LINENO"
then :
ac_cv_lib_ngtcp2_crypto_openssl_ngtcp2_crypto_encrypt_cb=yes
-else $as_nop
- ac_cv_lib_ngtcp2_crypto_openssl_ngtcp2_crypto_encrypt_cb=no
+else case e in #(
+ e) ac_cv_lib_ngtcp2_crypto_openssl_ngtcp2_crypto_encrypt_cb=no ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
-LIBS=$ac_check_lib_save_LIBS
+LIBS=$ac_check_lib_save_LIBS ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ngtcp2_crypto_openssl_ngtcp2_crypto_encrypt_cb" >&5
printf "%s\n" "$ac_cv_lib_ngtcp2_crypto_openssl_ngtcp2_crypto_encrypt_cb" >&6; }
@@ -22368,16 +22990,22 @@ printf %s "checking for ngtcp2_crypto_encrypt_cb in -lngtcp2_crypto_quictls... "
if test ${ac_cv_lib_ngtcp2_crypto_quictls_ngtcp2_crypto_encrypt_cb+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- ac_check_lib_save_LIBS=$LIBS
+else case e in #(
+ e) ac_check_lib_save_LIBS=$LIBS
LIBS="-lngtcp2_crypto_quictls $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
- builtin and then its argument prototype would still apply. */
-char ngtcp2_crypto_encrypt_cb ();
+ builtin and then its argument prototype would still apply.
+ The 'extern "C"' is for builds by C++ compilers;
+ although this is not generally supported in C code supporting it here
+ has little cost and some practical benefit (sr 110532). */
+#ifdef __cplusplus
+extern "C"
+#endif
+char ngtcp2_crypto_encrypt_cb (void);
int
main (void)
{
@@ -22389,12 +23017,14 @@ _ACEOF
if ac_fn_c_try_link "$LINENO"
then :
ac_cv_lib_ngtcp2_crypto_quictls_ngtcp2_crypto_encrypt_cb=yes
-else $as_nop
- ac_cv_lib_ngtcp2_crypto_quictls_ngtcp2_crypto_encrypt_cb=no
+else case e in #(
+ e) ac_cv_lib_ngtcp2_crypto_quictls_ngtcp2_crypto_encrypt_cb=no ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
-LIBS=$ac_check_lib_save_LIBS
+LIBS=$ac_check_lib_save_LIBS ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ngtcp2_crypto_quictls_ngtcp2_crypto_encrypt_cb" >&5
printf "%s\n" "$ac_cv_lib_ngtcp2_crypto_quictls_ngtcp2_crypto_encrypt_cb" >&6; }
@@ -22478,8 +23108,9 @@ if test "x$ac_cv_func_SSL_is_quic" = xyes
then :
printf "%s\n" "#define HAVE_SSL_IS_QUIC 1" >>confdefs.h
-else $as_nop
- as_fn_error $? "No QUIC support detected in OpenSSL. Need OpenSSL version with QUIC support to enable DNS over QUIC with libngtcp2." "$LINENO" 5
+else case e in #(
+ e) as_fn_error $? "No QUIC support detected in OpenSSL. Need OpenSSL version with QUIC support to enable DNS over QUIC with libngtcp2." "$LINENO" 5 ;;
+esac
fi
done
@@ -22578,11 +23209,12 @@ printf "%s\n" "yes" >&6; }
printf "%s\n" "#define HAVE_NGTCP2_CONN_SHUTDOWN_STREAM4 1" >>confdefs.h
-else $as_nop
-
+else case e in #(
+ e)
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
printf "%s\n" "no" >&6; }
-
+ ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
@@ -22612,16 +23244,22 @@ printf %s "checking for compress in -lz... " >&6; }
if test ${ac_cv_lib_z_compress+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- ac_check_lib_save_LIBS=$LIBS
+else case e in #(
+ e) ac_check_lib_save_LIBS=$LIBS
LIBS="-lz $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
- builtin and then its argument prototype would still apply. */
-char compress ();
+ builtin and then its argument prototype would still apply.
+ The 'extern "C"' is for builds by C++ compilers;
+ although this is not generally supported in C code supporting it here
+ has little cost and some practical benefit (sr 110532). */
+#ifdef __cplusplus
+extern "C"
+#endif
+char compress (void);
int
main (void)
{
@@ -22633,12 +23271,14 @@ _ACEOF
if ac_fn_c_try_link "$LINENO"
then :
ac_cv_lib_z_compress=yes
-else $as_nop
- ac_cv_lib_z_compress=no
+else case e in #(
+ e) ac_cv_lib_z_compress=no ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
-LIBS=$ac_check_lib_save_LIBS
+LIBS=$ac_check_lib_save_LIBS ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_z_compress" >&5
printf "%s\n" "$ac_cv_lib_z_compress" >&6; }
@@ -22676,16 +23316,22 @@ printf %s "checking for compress in -lz... " >&6; }
if test ${ac_cv_lib_z_compress+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- ac_check_lib_save_LIBS=$LIBS
+else case e in #(
+ e) ac_check_lib_save_LIBS=$LIBS
LIBS="-lz $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
- builtin and then its argument prototype would still apply. */
-char compress ();
+ builtin and then its argument prototype would still apply.
+ The 'extern "C"' is for builds by C++ compilers;
+ although this is not generally supported in C code supporting it here
+ has little cost and some practical benefit (sr 110532). */
+#ifdef __cplusplus
+extern "C"
+#endif
+char compress (void);
int
main (void)
{
@@ -22697,12 +23343,14 @@ _ACEOF
if ac_fn_c_try_link "$LINENO"
then :
ac_cv_lib_z_compress=yes
-else $as_nop
- ac_cv_lib_z_compress=no
+else case e in #(
+ e) ac_cv_lib_z_compress=no ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
-LIBS=$ac_check_lib_save_LIBS
+LIBS=$ac_check_lib_save_LIBS ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_z_compress" >&5
printf "%s\n" "$ac_cv_lib_z_compress" >&6; }
@@ -22771,8 +23419,8 @@ printf "%s\n" "#define USE_WINSOCK 1" >>confdefs.h
fi
fi
-else $as_nop
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+else case e in #(
+ e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#ifdef HAVE_WS2TCPIP_H
@@ -22799,8 +23447,8 @@ printf "%s\n" "#define USE_WINSOCK 1" >>confdefs.h
USE_WINSOCK="1"
-else $as_nop
- ORIGLIBS="$LIBS"
+else case e in #(
+ e) ORIGLIBS="$LIBS"
LIBS="$LIBS -lws2_32"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
@@ -22829,19 +23477,22 @@ printf "%s\n" "#define USE_WINSOCK 1" >>confdefs.h
USE_WINSOCK="1"
-else $as_nop
-
+else case e in #(
+ e)
ac_cv_func_getaddrinfo="no"
LIBS="$ORIGLIBS"
-
+ ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
-
+ ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
-
+ ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
@@ -22876,8 +23527,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_prog_WINDRES+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test -n "$WINDRES"; then
+else case e in #(
+ e) if test -n "$WINDRES"; then
ac_cv_prog_WINDRES="$WINDRES" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -22899,7 +23550,8 @@ done
done
IFS=$as_save_IFS
-fi
+fi ;;
+esac
fi
WINDRES=$ac_cv_prog_WINDRES
if test -n "$WINDRES"; then
@@ -22921,8 +23573,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_prog_ac_ct_WINDRES+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- if test -n "$ac_ct_WINDRES"; then
+else case e in #(
+ e) if test -n "$ac_ct_WINDRES"; then
ac_cv_prog_ac_ct_WINDRES="$ac_ct_WINDRES" # Let the user override the test.
else
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
@@ -22944,7 +23596,8 @@ done
done
IFS=$as_save_IFS
-fi
+fi ;;
+esac
fi
ac_ct_WINDRES=$ac_cv_prog_ac_ct_WINDRES
if test -n "$ac_ct_WINDRES"; then
@@ -23034,9 +23687,10 @@ printf "%s\n" "yes" >&6; }
printf "%s\n" "#define HAVE_IOCTLSOCKET 1" >>confdefs.h
-else $as_nop
- { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
+else case e in #(
+ e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; } ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
@@ -23059,8 +23713,8 @@ cache=`echo daemon | sed 'y%.=/+-%___p_%'`
if eval test \${cv_cc_deprecated_$cache+y}
then :
printf %s "(cached) " >&6
-else $as_nop
-
+else case e in #(
+ e)
echo '
#include <stdlib.h>
#include <unistd.h>
@@ -23072,7 +23726,8 @@ else
eval "cv_cc_deprecated_$cache=yes"
fi
rm -f conftest conftest.o conftest.c
-
+ ;;
+esac
fi
if eval "test \"`echo '$cv_cc_deprecated_'$cache`\" = yes"; then
@@ -23179,9 +23834,10 @@ printf "%s\n" "yes" >&6; }
printf "%s\n" "#define HAVE_HTOBE64 1" >>confdefs.h
-else $as_nop
- { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
+else case e in #(
+ e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; } ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
@@ -23214,9 +23870,10 @@ printf "%s\n" "yes" >&6; }
printf "%s\n" "#define HAVE_BE64TOH 1" >>confdefs.h
-else $as_nop
- { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
-printf "%s\n" "no" >&6; }
+else case e in #(
+ e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; } ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
@@ -23226,15 +23883,21 @@ printf %s "checking for library containing setusercontext... " >&6; }
if test ${ac_cv_search_setusercontext+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- ac_func_search_save_LIBS=$LIBS
+else case e in #(
+ e) ac_func_search_save_LIBS=$LIBS
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
- builtin and then its argument prototype would still apply. */
-char setusercontext ();
+ builtin and then its argument prototype would still apply.
+ The 'extern "C"' is for builds by C++ compilers;
+ although this is not generally supported in C code supporting it here
+ has little cost and some practical benefit (sr 110532). */
+#ifdef __cplusplus
+extern "C"
+#endif
+char setusercontext (void);
int
main (void)
{
@@ -23265,11 +23928,13 @@ done
if test ${ac_cv_search_setusercontext+y}
then :
-else $as_nop
- ac_cv_search_setusercontext=no
+else case e in #(
+ e) ac_cv_search_setusercontext=no ;;
+esac
fi
rm conftest.$ac_ext
-LIBS=$ac_func_search_save_LIBS
+LIBS=$ac_func_search_save_LIBS ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_setusercontext" >&5
printf "%s\n" "$ac_cv_search_setusercontext" >&6; }
@@ -23493,14 +24158,15 @@ if test "x$ac_cv_func_setresuid" = xyes
then :
printf "%s\n" "#define HAVE_SETRESUID 1" >>confdefs.h
-else $as_nop
- ac_fn_c_check_func "$LINENO" "setreuid" "ac_cv_func_setreuid"
+else case e in #(
+ e) ac_fn_c_check_func "$LINENO" "setreuid" "ac_cv_func_setreuid"
if test "x$ac_cv_func_setreuid" = xyes
then :
printf "%s\n" "#define HAVE_SETREUID 1" >>confdefs.h
fi
-
+ ;;
+esac
fi
done
@@ -23512,14 +24178,15 @@ if test "x$ac_cv_func_setresgid" = xyes
then :
printf "%s\n" "#define HAVE_SETRESGID 1" >>confdefs.h
-else $as_nop
- ac_fn_c_check_func "$LINENO" "setregid" "ac_cv_func_setregid"
+else case e in #(
+ e) ac_fn_c_check_func "$LINENO" "setregid" "ac_cv_func_setregid"
if test "x$ac_cv_func_setregid" = xyes
then :
printf "%s\n" "#define HAVE_SETREGID 1" >>confdefs.h
fi
-
+ ;;
+esac
fi
done
@@ -23563,11 +24230,12 @@ printf "%s\n" "yes" >&6; }
printf "%s\n" "#define HAVE_LINK_ATOMIC_STORE 1" >>confdefs.h
-else $as_nop
-
+else case e in #(
+ e)
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
printf "%s\n" "no" >&6; }
-
+ ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
@@ -23598,8 +24266,9 @@ $ac_includes_default
if test "x$ac_cv_have_decl_inet_pton" = xyes
then :
ac_have_decl=1
-else $as_nop
- ac_have_decl=0
+else case e in #(
+ e) ac_have_decl=0 ;;
+esac
fi
printf "%s\n" "#define HAVE_DECL_INET_PTON $ac_have_decl" >>confdefs.h
ac_fn_check_decl "$LINENO" "inet_ntop" "ac_cv_have_decl_inet_ntop" "
@@ -23628,8 +24297,9 @@ $ac_includes_default
if test "x$ac_cv_have_decl_inet_ntop" = xyes
then :
ac_have_decl=1
-else $as_nop
- ac_have_decl=0
+else case e in #(
+ e) ac_have_decl=0 ;;
+esac
fi
printf "%s\n" "#define HAVE_DECL_INET_NTOP $ac_have_decl" >>confdefs.h
@@ -23638,13 +24308,14 @@ if test "x$ac_cv_func_inet_aton" = xyes
then :
printf "%s\n" "#define HAVE_INET_ATON 1" >>confdefs.h
-else $as_nop
- case " $LIBOBJS " in
+else case e in #(
+ e) case " $LIBOBJS " in
*" inet_aton.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS inet_aton.$ac_objext"
;;
esac
-
+ ;;
+esac
fi
ac_fn_c_check_func "$LINENO" "inet_pton" "ac_cv_func_inet_pton"
@@ -23652,13 +24323,14 @@ if test "x$ac_cv_func_inet_pton" = xyes
then :
printf "%s\n" "#define HAVE_INET_PTON 1" >>confdefs.h
-else $as_nop
- case " $LIBOBJS " in
+else case e in #(
+ e) case " $LIBOBJS " in
*" inet_pton.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS inet_pton.$ac_objext"
;;
esac
-
+ ;;
+esac
fi
ac_fn_c_check_func "$LINENO" "inet_ntop" "ac_cv_func_inet_ntop"
@@ -23666,13 +24338,14 @@ if test "x$ac_cv_func_inet_ntop" = xyes
then :
printf "%s\n" "#define HAVE_INET_NTOP 1" >>confdefs.h
-else $as_nop
- case " $LIBOBJS " in
+else case e in #(
+ e) case " $LIBOBJS " in
*" inet_ntop.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS inet_ntop.$ac_objext"
;;
esac
-
+ ;;
+esac
fi
ac_fn_c_check_func "$LINENO" "snprintf" "ac_cv_func_snprintf"
@@ -23680,13 +24353,14 @@ if test "x$ac_cv_func_snprintf" = xyes
then :
printf "%s\n" "#define HAVE_SNPRINTF 1" >>confdefs.h
-else $as_nop
- case " $LIBOBJS " in
+else case e in #(
+ e) case " $LIBOBJS " in
*" snprintf.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS snprintf.$ac_objext"
;;
esac
-
+ ;;
+esac
fi
# test if snprintf return the proper length
@@ -23698,8 +24372,8 @@ printf %s "checking for correct snprintf return value... " >&6; }
then :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: maybe" >&5
printf "%s\n" "maybe" >&6; }
-else $as_nop
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+else case e in #(
+ e) cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$ac_includes_default
@@ -23710,8 +24384,8 @@ if ac_fn_c_try_run "$LINENO"
then :
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
printf "%s\n" "yes" >&6; }
-else $as_nop
-
+else case e in #(
+ e)
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
printf "%s\n" "no" >&6; }
@@ -23723,10 +24397,12 @@ printf "%s\n" "#define SNPRINTF_RET_BROKEN /**/" >>confdefs.h
;;
esac
-
+ ;;
+esac
fi
rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
- conftest.$ac_objext conftest.beam conftest.$ac_ext
+ conftest.$ac_objext conftest.beam conftest.$ac_ext ;;
+esac
fi
fi
@@ -23736,13 +24412,14 @@ if test "x$ac_cv_func_strlcat" = xyes
then :
printf "%s\n" "#define HAVE_STRLCAT 1" >>confdefs.h
-else $as_nop
- case " $LIBOBJS " in
+else case e in #(
+ e) case " $LIBOBJS " in
*" strlcat.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS strlcat.$ac_objext"
;;
esac
-
+ ;;
+esac
fi
ac_fn_c_check_func "$LINENO" "strlcpy" "ac_cv_func_strlcpy"
@@ -23750,13 +24427,14 @@ if test "x$ac_cv_func_strlcpy" = xyes
then :
printf "%s\n" "#define HAVE_STRLCPY 1" >>confdefs.h
-else $as_nop
- case " $LIBOBJS " in
+else case e in #(
+ e) case " $LIBOBJS " in
*" strlcpy.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS strlcpy.$ac_objext"
;;
esac
-
+ ;;
+esac
fi
ac_fn_c_check_func "$LINENO" "memmove" "ac_cv_func_memmove"
@@ -23764,13 +24442,14 @@ if test "x$ac_cv_func_memmove" = xyes
then :
printf "%s\n" "#define HAVE_MEMMOVE 1" >>confdefs.h
-else $as_nop
- case " $LIBOBJS " in
+else case e in #(
+ e) case " $LIBOBJS " in
*" memmove.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS memmove.$ac_objext"
;;
esac
-
+ ;;
+esac
fi
ac_fn_c_check_func "$LINENO" "gmtime_r" "ac_cv_func_gmtime_r"
@@ -23778,13 +24457,14 @@ if test "x$ac_cv_func_gmtime_r" = xyes
then :
printf "%s\n" "#define HAVE_GMTIME_R 1" >>confdefs.h
-else $as_nop
- case " $LIBOBJS " in
+else case e in #(
+ e) case " $LIBOBJS " in
*" gmtime_r.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS gmtime_r.$ac_objext"
;;
esac
-
+ ;;
+esac
fi
ac_fn_c_check_func "$LINENO" "isblank" "ac_cv_func_isblank"
@@ -23792,13 +24472,14 @@ if test "x$ac_cv_func_isblank" = xyes
then :
printf "%s\n" "#define HAVE_ISBLANK 1" >>confdefs.h
-else $as_nop
- case " $LIBOBJS " in
+else case e in #(
+ e) case " $LIBOBJS " in
*" isblank.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS isblank.$ac_objext"
;;
esac
-
+ ;;
+esac
fi
ac_fn_c_check_func "$LINENO" "explicit_bzero" "ac_cv_func_explicit_bzero"
@@ -23806,13 +24487,14 @@ if test "x$ac_cv_func_explicit_bzero" = xyes
then :
printf "%s\n" "#define HAVE_EXPLICIT_BZERO 1" >>confdefs.h
-else $as_nop
- case " $LIBOBJS " in
+else case e in #(
+ e) case " $LIBOBJS " in
*" explicit_bzero.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS explicit_bzero.$ac_objext"
;;
esac
-
+ ;;
+esac
fi
LIBOBJ_WITHOUT_CTIMEARC4="$LIBOBJS"
@@ -23842,8 +24524,8 @@ printf "%s\n" "yes" >&6; }
printf "%s\n" "#define HAVE_REALLOCARRAY 1" >>confdefs.h
-else $as_nop
-
+else case e in #(
+ e)
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
printf "%s\n" "no" >&6; }
case " $LIBOBJS " in
@@ -23852,7 +24534,8 @@ printf "%s\n" "no" >&6; }
;;
esac
-
+ ;;
+esac
fi
rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
@@ -23860,8 +24543,9 @@ ac_fn_check_decl "$LINENO" "reallocarray" "ac_cv_have_decl_reallocarray" "$ac_in
if test "x$ac_cv_have_decl_reallocarray" = xyes
then :
ac_have_decl=1
-else $as_nop
- ac_have_decl=0
+else case e in #(
+ e) ac_have_decl=0 ;;
+esac
fi
printf "%s\n" "#define HAVE_DECL_REALLOCARRAY $ac_have_decl" >>confdefs.h
@@ -23871,13 +24555,14 @@ if test "x$ac_cv_func_arc4random" = xyes
then :
printf "%s\n" "#define HAVE_ARC4RANDOM 1" >>confdefs.h
-else $as_nop
- case " $LIBOBJS " in
+else case e in #(
+ e) case " $LIBOBJS " in
*" arc4random.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS arc4random.$ac_objext"
;;
esac
-
+ ;;
+esac
fi
ac_fn_c_check_func "$LINENO" "arc4random_uniform" "ac_cv_func_arc4random_uniform"
@@ -23885,13 +24570,14 @@ if test "x$ac_cv_func_arc4random_uniform" = xyes
then :
printf "%s\n" "#define HAVE_ARC4RANDOM_UNIFORM 1" >>confdefs.h
-else $as_nop
- case " $LIBOBJS " in
+else case e in #(
+ e) case " $LIBOBJS " in
*" arc4random_uniform.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS arc4random_uniform.$ac_objext"
;;
esac
-
+ ;;
+esac
fi
if test "$ac_cv_func_arc4random" = "no"; then
@@ -23909,8 +24595,8 @@ if test "x$ac_cv_func_getentropy" = xyes
then :
printf "%s\n" "#define HAVE_GETENTROPY 1" >>confdefs.h
-else $as_nop
-
+else case e in #(
+ e)
if test "$USE_WINSOCK" = 1; then
case " $LIBOBJS " in
*" getentropy_win.$ac_objext "* ) ;;
@@ -23943,8 +24629,8 @@ if test "x$ac_cv_header_sys_sha2_h" = xyes
then :
printf "%s\n" "#define HAVE_SYS_SHA2_H 1" >>confdefs.h
-else $as_nop
-
+else case e in #(
+ e)
for ac_func in SHA512_Update
do :
@@ -23953,19 +24639,21 @@ if test "x$ac_cv_func_SHA512_Update" = xyes
then :
printf "%s\n" "#define HAVE_SHA512_UPDATE 1" >>confdefs.h
-else $as_nop
-
+else case e in #(
+ e)
case " $LIBOBJS " in
*" sha512.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS sha512.$ac_objext"
;;
esac
-
+ ;;
+esac
fi
done
-
+ ;;
+esac
fi
done
@@ -23978,15 +24666,21 @@ printf %s "checking for library containing clock_gettime... " >&6; }
if test ${ac_cv_search_clock_gettime+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- ac_func_search_save_LIBS=$LIBS
+else case e in #(
+ e) ac_func_search_save_LIBS=$LIBS
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
- builtin and then its argument prototype would still apply. */
-char clock_gettime ();
+ builtin and then its argument prototype would still apply.
+ The 'extern "C"' is for builds by C++ compilers;
+ although this is not generally supported in C code supporting it here
+ has little cost and some practical benefit (sr 110532). */
+#ifdef __cplusplus
+extern "C"
+#endif
+char clock_gettime (void);
int
main (void)
{
@@ -24017,11 +24711,13 @@ done
if test ${ac_cv_search_clock_gettime+y}
then :
-else $as_nop
- ac_cv_search_clock_gettime=no
+else case e in #(
+ e) ac_cv_search_clock_gettime=no ;;
+esac
fi
rm conftest.$ac_ext
-LIBS=$ac_func_search_save_LIBS
+LIBS=$ac_func_search_save_LIBS ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_clock_gettime" >&5
printf "%s\n" "$ac_cv_search_clock_gettime" >&6; }
@@ -24056,8 +24752,8 @@ if test "x$ac_cv_func_SHA512_Update" = xyes
then :
printf "%s\n" "#define HAVE_SHA512_UPDATE 1" >>confdefs.h
-else $as_nop
-
+else case e in #(
+ e)
printf "%s\n" "#define COMPAT_SHA512 1" >>confdefs.h
@@ -24067,7 +24763,8 @@ printf "%s\n" "#define COMPAT_SHA512 1" >>confdefs.h
;;
esac
-
+ ;;
+esac
fi
done
@@ -24091,15 +24788,21 @@ printf %s "checking for library containing clock_gettime... " >&6; }
if test ${ac_cv_search_clock_gettime+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- ac_func_search_save_LIBS=$LIBS
+else case e in #(
+ e) ac_func_search_save_LIBS=$LIBS
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
- builtin and then its argument prototype would still apply. */
-char clock_gettime ();
+ builtin and then its argument prototype would still apply.
+ The 'extern "C"' is for builds by C++ compilers;
+ although this is not generally supported in C code supporting it here
+ has little cost and some practical benefit (sr 110532). */
+#ifdef __cplusplus
+extern "C"
+#endif
+char clock_gettime (void);
int
main (void)
{
@@ -24130,11 +24833,13 @@ done
if test ${ac_cv_search_clock_gettime+y}
then :
-else $as_nop
- ac_cv_search_clock_gettime=no
+else case e in #(
+ e) ac_cv_search_clock_gettime=no ;;
+esac
fi
rm conftest.$ac_ext
-LIBS=$ac_func_search_save_LIBS
+LIBS=$ac_func_search_save_LIBS ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_clock_gettime" >&5
printf "%s\n" "$ac_cv_search_clock_gettime" >&6; }
@@ -24148,7 +24853,8 @@ fi
;;
esac
fi
-
+ ;;
+esac
fi
done
@@ -24161,13 +24867,14 @@ if test "x$ac_cv_func_ctime_r" = xyes
then :
printf "%s\n" "#define HAVE_CTIME_R 1" >>confdefs.h
-else $as_nop
- case " $LIBOBJS " in
+else case e in #(
+ e) case " $LIBOBJS " in
*" ctime_r.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS ctime_r.$ac_objext"
;;
esac
-
+ ;;
+esac
fi
ac_fn_c_check_func "$LINENO" "strsep" "ac_cv_func_strsep"
@@ -24175,13 +24882,14 @@ if test "x$ac_cv_func_strsep" = xyes
then :
printf "%s\n" "#define HAVE_STRSEP 1" >>confdefs.h
-else $as_nop
- case " $LIBOBJS " in
+else case e in #(
+ e) case " $LIBOBJS " in
*" strsep.$ac_objext "* ) ;;
*) LIBOBJS="$LIBOBJS strsep.$ac_objext"
;;
esac
-
+ ;;
+esac
fi
@@ -24232,8 +24940,9 @@ fi
if test ${enable_dnstap+y}
then :
enableval=$enable_dnstap; opt_dnstap=$enableval
-else $as_nop
- opt_dnstap=no
+else case e in #(
+ e) opt_dnstap=no ;;
+esac
fi
@@ -24242,8 +24951,9 @@ fi
if test ${with_dnstap_socket_path+y}
then :
withval=$with_dnstap_socket_path; opt_dnstap_socket_path=$withval
-else $as_nop
- opt_dnstap_socket_path="$UNBOUND_RUN_DIR/dnstap.sock"
+else case e in #(
+ e) opt_dnstap_socket_path="$UNBOUND_RUN_DIR/dnstap.sock" ;;
+esac
fi
@@ -24255,8 +24965,8 @@ printf %s "checking for $ac_word... " >&6; }
if test ${ac_cv_path_PROTOC_C+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- case $PROTOC_C in
+else case e in #(
+ e) case $PROTOC_C in
[\\/]* | ?:[\\/]*)
ac_cv_path_PROTOC_C="$PROTOC_C" # Let the user override the test with a path.
;;
@@ -24281,6 +24991,7 @@ done
IFS=$as_save_IFS
;;
+esac ;;
esac
fi
PROTOC_C=$ac_cv_path_PROTOC_C
@@ -24309,8 +25020,8 @@ then :
fi
LDFLAGS="$LDFLAGS -L$withval/lib"
-else $as_nop
-
+else case e in #(
+ e)
if test -n "$PKG_CONFIG"; then
pkg_failed=no
@@ -24426,7 +25137,8 @@ fi
fi
fi
-
+ ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing protobuf_c_message_pack" >&5
@@ -24434,15 +25146,21 @@ printf %s "checking for library containing protobuf_c_message_pack... " >&6; }
if test ${ac_cv_search_protobuf_c_message_pack+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- ac_func_search_save_LIBS=$LIBS
+else case e in #(
+ e) ac_func_search_save_LIBS=$LIBS
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
- builtin and then its argument prototype would still apply. */
-char protobuf_c_message_pack ();
+ builtin and then its argument prototype would still apply.
+ The 'extern "C"' is for builds by C++ compilers;
+ although this is not generally supported in C code supporting it here
+ has little cost and some practical benefit (sr 110532). */
+#ifdef __cplusplus
+extern "C"
+#endif
+char protobuf_c_message_pack (void);
int
main (void)
{
@@ -24473,11 +25191,13 @@ done
if test ${ac_cv_search_protobuf_c_message_pack+y}
then :
-else $as_nop
- ac_cv_search_protobuf_c_message_pack=no
+else case e in #(
+ e) ac_cv_search_protobuf_c_message_pack=no ;;
+esac
fi
rm conftest.$ac_ext
-LIBS=$ac_func_search_save_LIBS
+LIBS=$ac_func_search_save_LIBS ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_protobuf_c_message_pack" >&5
printf "%s\n" "$ac_cv_search_protobuf_c_message_pack" >&6; }
@@ -24486,8 +25206,9 @@ if test "$ac_res" != no
then :
test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
-else $as_nop
- as_fn_error $? "The protobuf-c library was not found. Please install the development libraries for protobuf-c!" "$LINENO" 5
+else case e in #(
+ e) as_fn_error $? "The protobuf-c library was not found. Please install the development libraries for protobuf-c!" "$LINENO" 5 ;;
+esac
fi
@@ -24527,8 +25248,9 @@ printf "%s\n" "#define DNSTAP_SOCKET_PATH \"$hdr_dnstap_socket_path\"" >>confdef
if test ${enable_dnscrypt+y}
then :
enableval=$enable_dnscrypt; opt_dnscrypt=$enableval
-else $as_nop
- opt_dnscrypt=no
+else case e in #(
+ e) opt_dnscrypt=no ;;
+esac
fi
@@ -24548,15 +25270,21 @@ printf %s "checking for library containing sodium_init... " >&6; }
if test ${ac_cv_search_sodium_init+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- ac_func_search_save_LIBS=$LIBS
+else case e in #(
+ e) ac_func_search_save_LIBS=$LIBS
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
- builtin and then its argument prototype would still apply. */
-char sodium_init ();
+ builtin and then its argument prototype would still apply.
+ The 'extern "C"' is for builds by C++ compilers;
+ although this is not generally supported in C code supporting it here
+ has little cost and some practical benefit (sr 110532). */
+#ifdef __cplusplus
+extern "C"
+#endif
+char sodium_init (void);
int
main (void)
{
@@ -24587,11 +25315,13 @@ done
if test ${ac_cv_search_sodium_init+y}
then :
-else $as_nop
- ac_cv_search_sodium_init=no
+else case e in #(
+ e) ac_cv_search_sodium_init=no ;;
+esac
fi
rm conftest.$ac_ext
-LIBS=$ac_func_search_save_LIBS
+LIBS=$ac_func_search_save_LIBS ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_sodium_init" >&5
printf "%s\n" "$ac_cv_search_sodium_init" >&6; }
@@ -24600,8 +25330,9 @@ if test "$ac_res" != no
then :
test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
-else $as_nop
- as_fn_error $? "The sodium library was not found. Please install sodium!" "$LINENO" 5
+else case e in #(
+ e) as_fn_error $? "The sodium library was not found. Please install sodium!" "$LINENO" 5 ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing crypto_box_curve25519xchacha20poly1305_beforenm" >&5
@@ -24609,15 +25340,21 @@ printf %s "checking for library containing crypto_box_curve25519xchacha20poly130
if test ${ac_cv_search_crypto_box_curve25519xchacha20poly1305_beforenm+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- ac_func_search_save_LIBS=$LIBS
+else case e in #(
+ e) ac_func_search_save_LIBS=$LIBS
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
- builtin and then its argument prototype would still apply. */
-char crypto_box_curve25519xchacha20poly1305_beforenm ();
+ builtin and then its argument prototype would still apply.
+ The 'extern "C"' is for builds by C++ compilers;
+ although this is not generally supported in C code supporting it here
+ has little cost and some practical benefit (sr 110532). */
+#ifdef __cplusplus
+extern "C"
+#endif
+char crypto_box_curve25519xchacha20poly1305_beforenm (void);
int
main (void)
{
@@ -24648,11 +25385,13 @@ done
if test ${ac_cv_search_crypto_box_curve25519xchacha20poly1305_beforenm+y}
then :
-else $as_nop
- ac_cv_search_crypto_box_curve25519xchacha20poly1305_beforenm=no
+else case e in #(
+ e) ac_cv_search_crypto_box_curve25519xchacha20poly1305_beforenm=no ;;
+esac
fi
rm conftest.$ac_ext
-LIBS=$ac_func_search_save_LIBS
+LIBS=$ac_func_search_save_LIBS ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_crypto_box_curve25519xchacha20poly1305_beforenm" >&5
printf "%s\n" "$ac_cv_search_crypto_box_curve25519xchacha20poly1305_beforenm" >&6; }
@@ -24667,11 +25406,12 @@ then :
printf "%s\n" "#define USE_DNSCRYPT_XCHACHA20 1" >>confdefs.h
-else $as_nop
-
+else case e in #(
+ e)
ENABLE_DNSCRYPT_XCHACHA20=0
-
+ ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing sodium_set_misuse_handler" >&5
@@ -24679,15 +25419,21 @@ printf %s "checking for library containing sodium_set_misuse_handler... " >&6; }
if test ${ac_cv_search_sodium_set_misuse_handler+y}
then :
printf %s "(cached) " >&6
-else $as_nop
- ac_func_search_save_LIBS=$LIBS
+else case e in #(
+ e) ac_func_search_save_LIBS=$LIBS
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
/* Override any GCC internal prototype to avoid an error.
Use char because int might match the return type of a GCC
- builtin and then its argument prototype would still apply. */
-char sodium_set_misuse_handler ();
+ builtin and then its argument prototype would still apply.
+ The 'extern "C"' is for builds by C++ compilers;
+ although this is not generally supported in C code supporting it here
+ has little cost and some practical benefit (sr 110532). */
+#ifdef __cplusplus
+extern "C"
+#endif
+char sodium_set_misuse_handler (void);
int
main (void)
{
@@ -24718,11 +25464,13 @@ done
if test ${ac_cv_search_sodium_set_misuse_handler+y}
then :
-else $as_nop
- ac_cv_search_sodium_set_misuse_handler=no
+else case e in #(
+ e) ac_cv_search_sodium_set_misuse_handler=no ;;
+esac
fi
rm conftest.$ac_ext
-LIBS=$ac_func_search_save_LIBS
+LIBS=$ac_func_search_save_LIBS ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_sodium_set_misuse_handler" >&5
printf "%s\n" "$ac_cv_search_sodium_set_misuse_handler" >&6; }
@@ -24835,16 +25583,17 @@ if test "x$ac_cv_header_net_pfvar_h" = xyes
then :
printf "%s\n" "#define HAVE_NET_PFVAR_H 1" >>confdefs.h
-else $as_nop
-
+else case e in #(
+ e)
# mnl
# Check whether --with-libmnl was given.
if test ${with_libmnl+y}
then :
withval=$with_libmnl;
-else $as_nop
- withval="yes"
+else case e in #(
+ e) withval="yes" ;;
+esac
fi
found_libmnl="no"
@@ -24875,7 +25624,8 @@ printf "%s\n" "found in $dir" >&6; }
if test x_$found_libmnl != x_yes; then
as_fn_error $? "Could not find libmnl, libmnl.h" "$LINENO" 5
fi
-
+ ;;
+esac
fi
done
@@ -25073,7 +25823,7 @@ printf "%s\n" "#define MAXSYSLOGMSGLEN 10240" >>confdefs.h
-version=1.23.0
+version=1.23.1
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for build time" >&5
printf %s "checking for build time... " >&6; }
@@ -25081,15 +25831,17 @@ ax_date_fmt="%b %e, %Y"
if test x"$SOURCE_DATE_EPOCH" = x
then :
date=`date "+$ax_date_fmt"`
-else $as_nop
- ax_build_date=`date -u -d "@$SOURCE_DATE_EPOCH" "+$ax_date_fmt" 2>/dev/null \
+else case e in #(
+ e) ax_build_date=`date -u -d "@$SOURCE_DATE_EPOCH" "+$ax_date_fmt" 2>/dev/null \
|| date -u -r "$SOURCE_DATE_EPOCH" "+$ax_date_fmt" 2>/dev/null`
if test x"$ax_build_date" = x
then :
as_fn_error $? "malformed SOURCE_DATE_EPOCH" "$LINENO" 5
-else $as_nop
- date=$ax_build_date
-fi
+else case e in #(
+ e) date=$ax_build_date ;;
+esac
+fi ;;
+esac
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $date" >&5
printf "%s\n" "$date" >&6; }
@@ -25110,8 +25862,8 @@ cat >confcache <<\_ACEOF
# config.status only pays attention to the cache file if you give it
# the --recheck option to rerun configure.
#
-# `ac_cv_env_foo' variables (set or unset) will be overridden when
-# loading this file, other *unset* `ac_cv_foo' will be assigned the
+# 'ac_cv_env_foo' variables (set or unset) will be overridden when
+# loading this file, other *unset* 'ac_cv_foo' will be assigned the
# following values.
_ACEOF
@@ -25141,14 +25893,14 @@ printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;}
(set) 2>&1 |
case $as_nl`(ac_space=' '; set) 2>&1` in #(
*${as_nl}ac_space=\ *)
- # `set' does not quote correctly, so add quotes: double-quote
+ # 'set' does not quote correctly, so add quotes: double-quote
# substitution turns \\\\ into \\, and sed turns \\ into \.
sed -n \
"s/'/'\\\\''/g;
s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"
;; #(
*)
- # `set' quotes correctly as required by POSIX, so do not add quotes.
+ # 'set' quotes correctly as required by POSIX, so do not add quotes.
sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"
;;
esac |
@@ -25209,6 +25961,12 @@ LIBOBJS=$ac_libobjs
LTLIBOBJS=$ac_ltlibobjs
+# Check whether --enable-year2038 was given.
+if test ${enable_year2038+y}
+then :
+ enableval=$enable_year2038;
+fi
+
if test -z "${USE_SYSTEMD_TRUE}" && test -z "${USE_SYSTEMD_FALSE}"; then
as_fn_error $? "conditional \"USE_SYSTEMD\" was never defined.
Usually this means the macro was only invoked conditionally." "$LINENO" 5
@@ -25242,7 +26000,6 @@ cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1
# Be more Bourne compatible
DUALCASE=1; export DUALCASE # for MKS sh
-as_nop=:
if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1
then :
emulate sh
@@ -25251,12 +26008,13 @@ then :
# is contrary to our usage. Disable this feature.
alias -g '${1+"$@"}'='"$@"'
setopt NO_GLOB_SUBST
-else $as_nop
- case `(set -o) 2>/dev/null` in #(
+else case e in #(
+ e) case `(set -o) 2>/dev/null` in #(
*posix*) :
set -o posix ;; #(
*) :
;;
+esac ;;
esac
fi
@@ -25328,7 +26086,7 @@ IFS=$as_save_IFS
;;
esac
-# We did not find ourselves, most probably we were run as `sh COMMAND'
+# We did not find ourselves, most probably we were run as 'sh COMMAND'
# in which case we are not to be found in the path.
if test "x$as_myself" = x; then
as_myself=$0
@@ -25357,7 +26115,6 @@ as_fn_error ()
} # as_fn_error
-
# as_fn_set_status STATUS
# -----------------------
# Set $? to STATUS, without forking.
@@ -25397,11 +26154,12 @@ then :
{
eval $1+=\$2
}'
-else $as_nop
- as_fn_append ()
+else case e in #(
+ e) as_fn_append ()
{
eval $1=\$$1\$2
- }
+ } ;;
+esac
fi # as_fn_append
# as_fn_arith ARG...
@@ -25415,11 +26173,12 @@ then :
{
as_val=$(( $* ))
}'
-else $as_nop
- as_fn_arith ()
+else case e in #(
+ e) as_fn_arith ()
{
as_val=`expr "$@" || test $? -eq 1`
- }
+ } ;;
+esac
fi # as_fn_arith
@@ -25502,9 +26261,9 @@ if (echo >conf$$.file) 2>/dev/null; then
if ln -s conf$$.file conf$$ 2>/dev/null; then
as_ln_s='ln -s'
# ... but there are two gotchas:
- # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.
- # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.
- # In both cases, we have to default to `cp -pR'.
+ # 1) On MSYS, both 'ln -s file dir' and 'ln file dir' fail.
+ # 2) DJGPP < 2.04 has no symlinks; 'ln -s' creates a wrapper executable.
+ # In both cases, we have to default to 'cp -pR'.
ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||
as_ln_s='cp -pR'
elif ln conf$$.file conf$$ 2>/dev/null; then
@@ -25585,10 +26344,12 @@ as_test_x='test -x'
as_executable_p=as_fn_executable_p
# Sed expression to map a string onto a valid CPP name.
-as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"
+as_sed_cpp="y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g"
+as_tr_cpp="eval sed '$as_sed_cpp'" # deprecated
# Sed expression to map a string onto a valid variable name.
-as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"
+as_sed_sh="y%*+%pp%;s%[^_$as_cr_alnum]%_%g"
+as_tr_sh="eval sed '$as_sed_sh'" # deprecated
exec 6>&1
@@ -25603,8 +26364,8 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
-This file was extended by unbound $as_me 1.23.0, which was
-generated by GNU Autoconf 2.71. Invocation command line was
+This file was extended by unbound $as_me 1.23.1, which was
+generated by GNU Autoconf 2.72. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
CONFIG_HEADERS = $CONFIG_HEADERS
@@ -25636,7 +26397,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
ac_cs_usage="\
-\`$as_me' instantiates files and other configuration actions
+'$as_me' instantiates files and other configuration actions
from templates according to the current configuration. Unless the files
and actions are specified as TAGs, all are instantiated by default.
@@ -25671,11 +26432,11 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config='$ac_cs_config_escaped'
ac_cs_version="\\
-unbound config.status 1.23.0
-configured by $0, generated by GNU Autoconf 2.71,
+unbound config.status 1.23.1
+configured by $0, generated by GNU Autoconf 2.72,
with options \\"\$ac_cs_config\\"
-Copyright (C) 2021 Free Software Foundation, Inc.
+Copyright (C) 2023 Free Software Foundation, Inc.
This config.status script is free software; the Free Software Foundation
gives unlimited permission to copy, distribute and modify it."
@@ -25735,8 +26496,8 @@ do
ac_need_defaults=false;;
--he | --h)
# Conflict between --help and --header
- as_fn_error $? "ambiguous option: \`$1'
-Try \`$0 --help' for more information.";;
+ as_fn_error $? "ambiguous option: '$1'
+Try '$0 --help' for more information.";;
--help | --hel | -h )
printf "%s\n" "$ac_cs_usage"; exit ;;
-q | -quiet | --quiet | --quie | --qui | --qu | --q \
@@ -25744,8 +26505,8 @@ Try \`$0 --help' for more information.";;
ac_cs_silent=: ;;
# This is an error.
- -*) as_fn_error $? "unrecognized option: \`$1'
-Try \`$0 --help' for more information." ;;
+ -*) as_fn_error $? "unrecognized option: '$1'
+Try '$0 --help' for more information." ;;
*) as_fn_append ac_config_targets " $1"
ac_need_defaults=false ;;
@@ -26100,7 +26861,7 @@ do
"contrib/unbound_portable.service") CONFIG_FILES="$CONFIG_FILES contrib/unbound_portable.service" ;;
"config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;;
- *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
+ *) as_fn_error $? "invalid argument: '$ac_config_target'" "$LINENO" 5;;
esac
done
@@ -26120,7 +26881,7 @@ fi
# creating and moving files from /tmp can sometimes cause problems.
# Hook for its removal unless debugging.
# Note that there is a small window in which the directory will not be cleaned:
-# after its creation but before its name has been assigned to `$tmp'.
+# after its creation but before its name has been assigned to '$tmp'.
$debug ||
{
tmp= ac_tmp=
@@ -26144,7 +26905,7 @@ ac_tmp=$tmp
# Set up the scripts for CONFIG_FILES section.
# No need to generate them if there are no CONFIG_FILES.
-# This happens for instance with `./config.status config.h'.
+# This happens for instance with './config.status config.h'.
if test -n "$CONFIG_FILES"; then
@@ -26302,13 +27063,13 @@ fi # test -n "$CONFIG_FILES"
# Set up the scripts for CONFIG_HEADERS section.
# No need to generate them if there are no CONFIG_HEADERS.
-# This happens for instance with `./config.status Makefile'.
+# This happens for instance with './config.status Makefile'.
if test -n "$CONFIG_HEADERS"; then
cat >"$ac_tmp/defines.awk" <<\_ACAWK ||
BEGIN {
_ACEOF
-# Transform confdefs.h into an awk script `defines.awk', embedded as
+# Transform confdefs.h into an awk script 'defines.awk', embedded as
# here-document in config.status, that substitutes the proper values into
# config.h.in to produce config.h.
@@ -26418,7 +27179,7 @@ do
esac
case $ac_mode$ac_tag in
:[FHL]*:*);;
- :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;;
+ :L* | :C*:*) as_fn_error $? "invalid tag '$ac_tag'" "$LINENO" 5;;
:[FH]-) ac_tag=-:-;;
:[FH]*) ac_tag=$ac_tag:$ac_tag.in;;
esac
@@ -26440,19 +27201,19 @@ do
-) ac_f="$ac_tmp/stdin";;
*) # Look for the file first in the build tree, then in the source tree
# (if the path is not absolute). The absolute path cannot be DOS-style,
- # because $ac_f cannot contain `:'.
+ # because $ac_f cannot contain ':'.
test -f "$ac_f" ||
case $ac_f in
[\\/$]*) false;;
*) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;
esac ||
- as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;;
+ as_fn_error 1 "cannot find input file: '$ac_f'" "$LINENO" 5;;
esac
case $ac_f in *\'*) ac_f=`printf "%s\n" "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac
as_fn_append ac_file_inputs " '$ac_f'"
done
- # Let's still pretend it is `configure' which instantiates (i.e., don't
+ # Let's still pretend it is 'configure' which instantiates (i.e., don't
# use $as_me), people would be surprised to read:
# /* config.h. Generated by config.status. */
configure_input='Generated from '`
@@ -26576,7 +27337,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
esac
_ACEOF
-# Neutralize VPATH when `$srcdir' = `.'.
+# Neutralize VPATH when '$srcdir' = '.'.
# Shell code in configure.ac might set extrasub.
# FIXME: do we really want to maintain this feature?
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
@@ -26605,9 +27366,9 @@ test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&
{ ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } &&
{ ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \
"$ac_tmp/out"`; test -z "$ac_out"; } &&
- { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable 'datarootdir'
which seems to be undefined. Please make sure it is defined" >&5
-printf "%s\n" "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'
+printf "%s\n" "$as_me: WARNING: $ac_file contains a reference to the variable 'datarootdir'
which seems to be undefined. Please make sure it is defined" >&2;}
rm -f "$ac_tmp/stdin"
diff --git a/contrib/unbound/configure.ac b/contrib/unbound/configure.ac
index 76239c099572..051e7b392e33 100644
--- a/contrib/unbound/configure.ac
+++ b/contrib/unbound/configure.ac
@@ -12,14 +12,14 @@ sinclude(dnscrypt/dnscrypt.m4)
# must be numbers. ac_defun because of later processing
m4_define([VERSION_MAJOR],[1])
m4_define([VERSION_MINOR],[23])
-m4_define([VERSION_MICRO],[0])
+m4_define([VERSION_MICRO],[1])
AC_INIT([unbound],m4_defn([VERSION_MAJOR]).m4_defn([VERSION_MINOR]).m4_defn([VERSION_MICRO]),[unbound-bugs@nlnetlabs.nl or https://github.com/NLnetLabs/unbound/issues],[unbound])
AC_SUBST(UNBOUND_VERSION_MAJOR, [VERSION_MAJOR])
AC_SUBST(UNBOUND_VERSION_MINOR, [VERSION_MINOR])
AC_SUBST(UNBOUND_VERSION_MICRO, [VERSION_MICRO])
LIBUNBOUND_CURRENT=9
-LIBUNBOUND_REVISION=31
+LIBUNBOUND_REVISION=32
LIBUNBOUND_AGE=1
# 1.0.0 had 0:12:0
# 1.0.1 had 0:13:0
@@ -118,6 +118,7 @@ LIBUNBOUND_AGE=1
# 1.21.1 had 9:29:1
# 1.22.0 had 9:30:1
# 1.23.0 had 9:31:1
+# 1.23.1 had 9:32:1
# Current -- the number of the binary API that we're implementing
# Revision -- which iteration of the implementation of the binary
diff --git a/contrib/unbound/doc/README b/contrib/unbound/doc/README
index 50d9022dbdca..1cd60bb0b12d 100644
--- a/contrib/unbound/doc/README
+++ b/contrib/unbound/doc/README
@@ -1,4 +1,4 @@
-README for Unbound 1.23.0
+README for Unbound 1.23.1
Copyright 2007 NLnet Labs
http://unbound.net
diff --git a/contrib/unbound/doc/example.conf b/contrib/unbound/doc/example.conf
index ab9445fc69f1..3a5188f00477 100644
--- a/contrib/unbound/doc/example.conf
+++ b/contrib/unbound/doc/example.conf
@@ -1,7 +1,7 @@
#
# Example configuration file.
#
-# See unbound.conf(5) man page, version 1.15.0.
+# See unbound.conf(5) man page, version 1.23.1.
#
# this is a comment.
@@ -17,7 +17,7 @@ server:
# whitespace is not necessary, but looks cleaner.
# verbosity number, 0 is least verbose. 1 is default.
- verbosity: 1
+ # verbosity: 1
# print statistics to the log (for every thread) every N seconds.
# Set to "" or 0 to disable. Default is disabled.
@@ -35,9 +35,14 @@ server:
# statistics-cumulative: no
# enable extended statistics (query types, answer codes, status)
- # printed from unbound-control. default off, because of speed.
+ # printed from unbound-control. Default off, because of speed.
# extended-statistics: no
+ # Inhibits selected extended statistics (qtype, qclass, qopcode, rcode,
+ # rpz-actions) from printing if their value is 0.
+ # Default on.
+ # statistics-inhibit-zero: yes
+
# number of threads to create. 1 disables threading.
# num-threads: 1
@@ -50,11 +55,16 @@ server:
# interface: 192.0.2.154
# interface: 192.0.2.154@5003
# interface: 2001:DB8::5
+ # interface: eth0@5003
# enable this feature to copy the source address of queries to reply.
# Socket options are not supported on all platforms. experimental.
# interface-automatic: no
+ # instead of the default port, open additional ports separated by
+ # spaces when interface-automatic is enabled, by listing them here.
+ # interface-automatic-ports: ""
+
# port to answer queries from
# port: 53
@@ -133,8 +143,8 @@ server:
# edns-buffer-size: 1232
# Maximum UDP response size (not applied to TCP response).
- # Suggested values are 512 to 4096. Default is 4096. 65536 disables it.
- # max-udp-size: 4096
+ # Suggested values are 512 to 4096. Default is 1232. 65536 disables it.
+ # max-udp-size: 1232
# max memory to use for stream(tcp and tls) waiting result buffers.
# stream-wait-size: 4m
@@ -164,13 +174,53 @@ server:
# perform connect for UDP sockets to mitigate ICMP side channel.
# udp-connect: yes
- # The number of retries when a non-positive response is received.
+ # The number of retries, per upstream nameserver in a delegation, when
+ # a throwaway response (also timeouts) is received.
# outbound-msg-retry: 5
+ # Hard limit on the number of outgoing queries Unbound will make while
+ # resolving a name, making sure large NS sets do not loop.
+ # It resets on query restarts (e.g., CNAME) and referrals.
+ # max-sent-count: 32
+
+ # Hard limit on the number of times Unbound is allowed to restart a
+ # query upon encountering a CNAME record.
+ # max-query-restarts: 11
+
+ # Limit on number of NS records in NS RRset for incoming packets.
+ # iter-scrub-ns: 20
+
+ # Limit on number of CNAME, DNAME records for incoming packets.
+ # iter-scrub-cname: 11
+
+ # Limit on upstream queries for an incoming query and its recursion.
+ # max-global-quota: 200
+
# msec for waiting for an unknown server to reply. Increase if you
# are behind a slow satellite link, to eg. 1128.
# unknown-server-time-limit: 376
+ # msec before recursion replies are dropped. The work item continues.
+ # discard-timeout: 1900
+
+ # Max number of replies waiting for recursion per IP address.
+ # wait-limit: 1000
+
+ # Max replies waiting for recursion for IP address with cookie.
+ # wait-limit-cookie: 10000
+
+ # Apart from the default, the wait limit can be set for a netblock.
+ # wait-limit-netblock: 192.0.2.0/24 50000
+
+ # Apart from the default, the wait limit with cookie can be adjusted.
+ # wait-limit-cookie-netblock: 192.0.2.0/24 50000
+
+ # Defaults for loopback, it has no wait limit.
+ # wait-limit-netblock: 127.0.0.0/8 -1
+ # wait-limit-netblock: ::1/128 -1
+ # wait-limit-cookie-netblock: 127.0.0.0/8 -1
+ # wait-limit-cookie-netblock: ::1/128 -1
+
# the amount of memory to use for the RRset cache.
# plain value in bytes or you can append k, m or G. default is "4Mb".
# rrset-cache-size: 4m
@@ -191,6 +241,11 @@ server:
# the time to live (TTL) value cap for negative responses in the cache
# cache-max-negative-ttl: 3600
+ # the time to live (TTL) value lower bound, in seconds. Default 0.
+ # For negative responses in the cache. If disabled, default,
+ # cache-min-ttl applies if configured.
+ # cache-min-negative-ttl: 0
+
# the time to live (TTL) value for cached roundtrip times, lameness and
# EDNS version information for hosts. In seconds.
# infra-host-ttl: 900
@@ -198,6 +253,9 @@ server:
# minimum wait time for responses, increase if uplink is long. In msec.
# infra-cache-min-rtt: 50
+ # maximum wait time for responses. In msec.
+ # infra-cache-max-rtt: 120000
+
# enable to make server probe down hosts more frequently.
# infra-keep-probing: no
@@ -209,7 +267,8 @@ server:
# the maximum number of hosts that are cached (roundtrip, EDNS, lame).
# infra-cache-numhosts: 10000
- # define a number of tags here, use with local-zone, access-control.
+ # define a number of tags here, use with local-zone, access-control,
+ # interface-*.
# repeat the define-tag statement to add additional tags.
# define-tag: "tag1 tag2 tag3"
@@ -219,6 +278,18 @@ server:
# Enable IPv6, "yes" or "no".
# do-ip6: yes
+ # If running unbound on an IPv6-only host, domains that only have
+ # IPv4 servers would become unresolveable. If NAT64 is available in
+ # the network, unbound can use NAT64 to reach these servers with
+ # the following option. This is NOT needed for enabling DNS64 on a
+ # system that has IPv4 connectivity.
+ # Consider also enabling prefer-ip6 to prefer native IPv6 connections
+ # to nameservers.
+ # do-nat64: no
+
+ # NAT64 prefix. Defaults to using dns64-prefix value.
+ # nat64-prefix: 64:ff9b::0/96
+
# Enable UDP, "yes" or "no".
# do-udp: yes
@@ -247,9 +318,14 @@ server:
# Enable EDNS TCP keepalive option.
# edns-tcp-keepalive: no
- # Timeout for EDNS TCP keepalive, in msec.
+ # Timeout for EDNS TCP keepalive, in msec. Overrides tcp-idle-timeout
+ # if edns-tcp-keepalive is set.
# edns-tcp-keepalive-timeout: 120000
+ # UDP queries that have waited in the socket buffer for a long time
+ # can be dropped. Default is 0, disabled. In seconds, such as 3.
+ # sock-queue-timeout: 0
+
# Use systemd socket activation for UDP, TCP, and control sockets.
# use-systemd: no
@@ -263,11 +339,10 @@ server:
# Choose deny (drop message), refuse (polite error reply),
# allow (recursive ok), allow_setrd (recursive ok, rd bit is forced on),
# allow_snoop (recursive and nonrecursive ok)
+ # allow_cookie (allow UDP with valid cookie or stateful transport)
# deny_non_local (drop queries unless can be answered from local-data)
# refuse_non_local (like deny_non_local but polite error reply).
- # access-control: 0.0.0.0/0 refuse
# access-control: 127.0.0.0/8 allow
- # access-control: ::0/0 refuse
# access-control: ::1 allow
# access-control: ::ffff:127.0.0.1 allow
@@ -276,7 +351,7 @@ server:
# are tagged with one of these tags.
# access-control-tag: 192.0.2.0/24 "tag2 tag3"
- # set action for particular tag for given access control element
+ # set action for particular tag for given access control element.
# if you have multiple tag values, the tag used to lookup the action
# is the first tag match between access-control-tag and local-zone-tag
# where "first" comes from the order of the define-tag values.
@@ -288,6 +363,58 @@ server:
# Set view for access control element
# access-control-view: 192.0.2.0/24 viewname
+ # Similar to 'access-control:' but for interfaces.
+ # Control which listening interfaces are allowed to accept (recursive)
+ # queries for this server.
+ # The specified interfaces should be the same as the ones specified in
+ # 'interface:' followed by the action.
+ # The actions are the same as 'access-control:' above.
+ # By default all the interfaces configured are refused.
+ # Note: any 'access-control*:' setting overrides all 'interface-*:'
+ # settings for targeted clients.
+ # interface-action: 192.0.2.153 allow
+ # interface-action: 192.0.2.154 allow
+ # interface-action: 192.0.2.154@5003 allow
+ # interface-action: 2001:DB8::5 allow
+ # interface-action: eth0@5003 allow
+
+ # Similar to 'access-control-tag:' but for interfaces.
+ # Tag interfaces with a list of tags (in "" with spaces between).
+ # Interfaces using these tags use localzones that are tagged with one
+ # of these tags.
+ # The specified interfaces should be the same as the ones specified in
+ # 'interface:' followed by the list of tags.
+ # Note: any 'access-control*:' setting overrides all 'interface-*:'
+ # settings for targeted clients.
+ # interface-tag: eth0@5003 "tag2 tag3"
+
+ # Similar to 'access-control-tag-action:' but for interfaces.
+ # Set action for particular tag for a given interface element.
+ # If you have multiple tag values, the tag used to lookup the action
+ # is the first tag match between interface-tag and local-zone-tag
+ # where "first" comes from the order of the define-tag values.
+ # The specified interfaces should be the same as the ones specified in
+ # 'interface:' followed by the tag and action.
+ # Note: any 'access-control*:' setting overrides all 'interface-*:'
+ # settings for targeted clients.
+ # interface-tag-action: eth0@5003 tag3 refuse
+
+ # Similar to 'access-control-tag-data:' but for interfaces.
+ # Set redirect data for a particular tag for an interface element.
+ # The specified interfaces should be the same as the ones specified in
+ # 'interface:' followed by the tag and the redirect data.
+ # Note: any 'access-control*:' setting overrides all 'interface-*:'
+ # settings for targeted clients.
+ # interface-tag-data: eth0@5003 tag2 "A 127.0.0.1"
+
+ # Similar to 'access-control-view:' but for interfaces.
+ # Set view for an interface element.
+ # The specified interfaces should be the same as the ones specified in
+ # 'interface:' followed by the view name.
+ # Note: any 'access-control*:' setting overrides all 'interface-*:'
+ # settings for targeted clients.
+ # interface-view: eth0@5003 viewname
+
# if given, a chroot(2) is done to the given directory.
# i.e. you can chroot to the working directory, for example,
# for extra security, but make sure all files are in that directory.
@@ -311,19 +438,19 @@ server:
# How to do this is specific to your OS.
#
# If you give "" no chroot is performed. The path must not end in a /.
- # chroot: "@UNBOUND_CHROOT_DIR@"
+ # chroot: "/var/unbound"
# if given, user privileges are dropped (after binding port),
# and the given username is assumed. Default is user "unbound".
# If you give "" no privileges are dropped.
- # username: "@UNBOUND_USERNAME@"
+ # username: "unbound"
# the working directory. The relative files in this config are
# relative to this directory. If you give "" the working directory
# is not changed.
# If you give a server: directory: dir before include: file statements
# then those includes can be relative to the working directory.
- # directory: "@UNBOUND_RUN_DIR@"
+ # directory: "/var/unbound"
# the log file, "" means log to stderr.
# Use of this option sets use-syslog to "no".
@@ -340,6 +467,10 @@ server:
# print UTC timestamp in ascii to logfile, default is epoch in seconds.
# log-time-ascii: no
+ # log timestamp in ISO8601 format if also log-time-ascii is enabled.
+ # (y-m-dTh:m:s.msec[+-]tzhours:tzminutes)
+ # log-time-iso: no
+
# print one line with time, IP, name, type, class for every query.
# log-queries: no
@@ -351,6 +482,9 @@ server:
# filtering log-queries and log-replies from the log.
# log-tag-queryreply: no
+ # log with destination address, port and type for log-replies.
+ # log-destaddr: no
+
# log the local-zone actions, like local-zone type inform is enabled
# also for the other local zone types.
# log-local-actions: no
@@ -359,7 +493,7 @@ server:
# log-servfail: no
# the pid file. Can be an absolute path outside of chroot/work dir.
- # pidfile: "@UNBOUND_PIDFILE@"
+ # pidfile: "/var/unbound/unbound.pid"
# file to read root hints from.
# get one from https://www.internic.net/domain/named.cache
@@ -409,6 +543,9 @@ server:
# Harden against out of zone rrsets, to avoid spoofing attempts.
# harden-glue: yes
+ # Harden against unverified (outside-zone, including sibling zone) glue rrsets
+ # harden-unverified-glue: no
+
# Harden against receiving dnssec-stripped data. If you turn it
# off, failing to validate dnskey data for a trustanchor will
# trigger insecure mode for that zone (like without a trustanchor).
@@ -425,10 +562,15 @@ server:
# harden-referral-path: no
# Harden against algorithm downgrade when multiple algorithms are
- # advertised in the DS record. If no, allows the weakest algorithm
- # to validate the zone.
+ # advertised in the DS record. If no, allows any algorithm
+ # to validate the zone which is the standard behavior for validators.
+ # Check the manpage for detailed information.
# harden-algo-downgrade: no
+ # Harden against unknown records in the authority section and the
+ # additional section.
+ # harden-unknown-additional: no
+
# Sent minimum amount of information to upstream servers to enhance
# privacy. Only sent minimum required labels of the QNAME and set QTYPE
# to A when possible.
@@ -521,7 +663,7 @@ server:
# And then enable the auto-trust-anchor-file config item.
# Please note usage of unbound-anchor root anchor is at your own risk
# and under the terms of our LICENSE (see that file in the source).
- # auto-trust-anchor-file: "@UNBOUND_ROOTKEY_FILE@"
+ # auto-trust-anchor-file: "/var/unbound/root.key"
# trust anchor signaling sends a RFC8145 key tag query after priming.
# trust-anchor-signaling: yes
@@ -589,13 +731,19 @@ server:
# that set CD but cannot validate themselves.
# ignore-cd-flag: no
+ # Disable the DO flag in outgoing requests. It is helpful for upstream
+ # devices that cannot handle DNSSEC information. But do not enable it
+ # otherwise, because it would stop DNSSEC validation.
+ # disable-edns-do: no
+
# Serve expired responses from cache, with serve-expired-reply-ttl in
- # the response, and then attempt to fetch the data afresh.
+ # the response. By default it first tries to refresh an expired answer.
+ # Can be configured with serve-expired-client-timeout.
# serve-expired: no
#
# Limit serving of expired responses to configured seconds after
# expiration. 0 disables the limit.
- # serve-expired-ttl: 0
+ # serve-expired-ttl: 86400
#
# Set the TTL of expired records to the serve-expired-ttl value after a
# failed attempt to retrieve the record from upstream. This makes sure
@@ -608,10 +756,9 @@ server:
#
# Time in milliseconds before replying to the client with expired data.
# This essentially enables the serve-stale behavior as specified in
- # RFC 8767 that first tries to resolve before
- # immediately responding with expired data. 0 disables this behavior.
- # A recommended value is 1800.
- # serve-expired-client-timeout: 0
+ # RFC 8767 that first tries to resolve before immediately responding
+ # with expired data. 0 disables this behavior.
+ # serve-expired-client-timeout: 1800
# Return the original TTL as received from the upstream name server rather
# than the decrementing TTL as stored in the cache. Enabling this feature
@@ -670,6 +817,8 @@ server:
# local-zone: "127.in-addr.arpa." nodefault
# local-zone: "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa." nodefault
# local-zone: "home.arpa." nodefault
+ # local-zone: "resolver.arpa." nodefault
+ # local-zone: "service.arpa." nodefault
# local-zone: "onion." nodefault
# local-zone: "test." nodefault
# local-zone: "invalid." nodefault
@@ -736,6 +885,8 @@ server:
# o always_transparent, always_refuse, always_nxdomain, always_nodata,
# always_deny resolve in that way but ignore local data for
# that name
+ # o block_a resolves all records normally but returns
+ # NODATA for A queries and ignores local data for that name
# o always_null returns 0.0.0.0 or ::0 for any name in the zone.
# o noview breaks out of that view towards global local-zones.
#
@@ -778,6 +929,7 @@ server:
# tls-service-pem: "path/to/publiccertfile.pem"
# tls-port: 853
# https-port: 443
+ # quic-port: 853
# cipher setting for TLSv1.2
# tls-ciphers: "DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256"
@@ -811,6 +963,8 @@ server:
# Add system certs to the cert bundle, from the Windows Cert Store
# tls-win-cert: no
+ # and on other systems, the default openssl certificates
+ # tls-system-cert: no
# Pad queries over TLS upstreams
# pad-queries: yes
@@ -840,6 +994,13 @@ server:
# Disable TLS for DNS-over-HTTP downstream service.
# http-notls-downstream: no
+ # Maximum number of bytes used for QUIC buffers.
+ # quic-size: 8m
+
+ # The interfaces that use these listed port numbers will support and
+ # expect PROXYv2. For UDP and TCP/TLS interfaces.
+ # proxy-protocol-port: portno for each of the port numbers.
+
# DNS64 prefix. Must be specified when DNS64 is use.
# Enable dns64 in module-config. Used to synthesize IPv6 from IPv4.
# dns64-prefix: 64:ff9b::0/96
@@ -876,6 +1037,13 @@ server:
# if 0(default) it is disabled, otherwise states qps allowed per ip address
# ip-ratelimit: 0
+ # global query ratelimit for all ip addresses with a valid DNS Cookie.
+ # feature is experimental.
+ # if 0(default) it is disabled, otherwise states qps allowed per ip address
+ # useful in combination with 'allow_cookie'.
+ # If used, suggested to be higher than ip-ratelimit, tenfold.
+ # ip-ratelimit-cookie: 0
+
# ip ratelimits are tracked in a cache, size in bytes of cache (or k,m).
# ip-ratelimit-size: 4m
# ip ratelimit cache slabs, reduces lock contention if equal to cpucount.
@@ -897,6 +1065,32 @@ server:
# the number of servers that will be used in the fast server selection.
# fast-server-num: 3
+ # reply to requests containing DNS Cookies as specified in RFC 7873 and RFC 9018.
+ # answer-cookie: no
+
+ # secret for DNS Cookie generation.
+ # useful for anycast deployments.
+ # example value "000102030405060708090a0b0c0d0e0f".
+ # cookie-secret: <128 bit random hex string>
+
+ # File with cookie secrets, the 'cookie-secret:' option is ignored
+ # and the file can be managed to have staging and active secrets
+ # with remote control commands. Disabled with "". Default is "".
+ # cookie-secret-file: "/usr/local/etc/unbound_cookiesecrets.txt"
+
+ # Enable to attach Extended DNS Error codes (RFC8914) to responses.
+ # ede: no
+
+ # Enable to attach an Extended DNS Error (RFC8914) Code 3 - Stale
+ # Answer as EDNS0 option to expired responses.
+ # Note that the ede option above needs to be enabled for this to work.
+ # ede-serve-expired: no
+
+ # Enable DNS Error Reporting (RFC9567).
+ # qname-minimisation is advised to be turned on as well to increase
+ # privacy on the outgoing reports.
+ # dns-error-reporting: no
+
# Specific options for ipsecmod. Unbound needs to be configured with
# --enable-ipsecmod for these to take effect.
#
@@ -941,7 +1135,7 @@ server:
# o and give a python-script to run.
python:
# Script file to load
- # python-script: "@UNBOUND_SHARE_DIR@/ubmodule-tst.py"
+ # python-script: "/var/unbound/ubmodule-tst.py"
# Dynamic library config section. To enable:
# o use --with-dynlibmodule to configure before compiling.
@@ -952,7 +1146,7 @@ python:
# the module-config then you need one dynlib-file per instance.
dynlib:
# Script file to load
- # dynlib-file: "@UNBOUND_SHARE_DIR@/dynlib.so"
+ # dynlib-file: "/var/unbound/dynlib.so"
# Remote control config section.
remote-control:
@@ -975,16 +1169,16 @@ remote-control:
# control-use-cert: "yes"
# Unbound server key file.
- # server-key-file: "@UNBOUND_RUN_DIR@/unbound_server.key"
+ # server-key-file: "/var/unbound/unbound_server.key"
# Unbound server certificate file.
- # server-cert-file: "@UNBOUND_RUN_DIR@/unbound_server.pem"
+ # server-cert-file: "/var/unbound/unbound_server.pem"
# unbound-control key file.
- # control-key-file: "@UNBOUND_RUN_DIR@/unbound_control.key"
+ # control-key-file: "/var/unbound/unbound_control.key"
# unbound-control certificate file.
- # control-cert-file: "@UNBOUND_RUN_DIR@/unbound_control.pem"
+ # control-cert-file: "/var/unbound/unbound_control.pem"
# Stub zones.
# Create entries like below, to make all queries for 'example.com' and
@@ -1031,11 +1225,11 @@ remote-control:
# has a copy of the root for local usage. The second serves example.org
# authoritatively. zonefile: reads from file (and writes to it if you also
# download it), primary: fetches with AXFR and IXFR, or url to zonefile.
-# With allow-notify: you can give additional (apart from primaries) sources of
-# notifies.
+# With allow-notify: you can give additional (apart from primaries and urls)
+# sources of notifies.
# auth-zone:
# name: "."
-# primary: 199.9.14.201 # b.root-servers.net
+# primary: 170.247.170.2 # b.root-servers.net
# primary: 192.33.4.12 # c.root-servers.net
# primary: 199.7.91.13 # d.root-servers.net
# primary: 192.5.5.241 # f.root-servers.net
@@ -1043,7 +1237,7 @@ remote-control:
# primary: 193.0.14.129 # k.root-servers.net
# primary: 192.0.47.132 # xfr.cjr.dns.icann.org
# primary: 192.0.32.132 # xfr.lax.dns.icann.org
-# primary: 2001:500:200::b # b.root-servers.net
+# primary: 2801:1b8:10::b # b.root-servers.net
# primary: 2001:500:2::c # c.root-servers.net
# primary: 2001:500:2d::d # d.root-servers.net
# primary: 2001:500:2f::f # f.root-servers.net
@@ -1111,6 +1305,11 @@ remote-control:
# backend: "testframe"
# # secret seed string to calculate hashed keys
# secret-seed: "default"
+# # if the backend should be read from, but not written to.
+# cachedb-no-store: no
+# # if the cachedb should be checked before a serve-expired response is
+# # given, when serve-expired is enabled.
+# cachedb-check-when-serve-expired: yes
#
# # For "redis" backend:
# # (to enable, use --with-libhiredis to configure before compiling)
@@ -1118,10 +1317,36 @@ remote-control:
# redis-server-host: 127.0.0.1
# # redis server's TCP port
# redis-server-port: 6379
+# # if the server uses a unix socket, set its path, or "" when not used.
+# redis-server-path: "/var/lib/redis/redis-server.sock"
+# # if the server uses an AUTH password, specify here, or "" when not used.
+# redis-server-password: ""
# # timeout (in ms) for communication with the redis server
# redis-timeout: 100
+# # timeout (in ms) for commands, if 0, uses redis-timeout.
+# redis-command-timeout: 0
+# # timeout (in ms) for connection set up, if 0, uses redis-timeout.
+# redis-connect-timeout: 0
# # set timeout on redis records based on DNS response TTL
# redis-expire-records: no
+# # redis logical database to use, 0 is the default database.
+# redis-logical-db: 0
+# # redis replica server's IP address or host name
+# redis-replica-server-host: 127.0.0.1
+# # redis replica server's TCP port
+# redis-replica-server-port: 6379
+# # if the replica server uses a unix socket, set its path, or "" when not used.
+# redis-replica-server-path: "/var/lib/redis/redis-server.sock"
+# # if the replica server uses an AUTH password, specify here, or "" when not used.
+# redis-replica-server-password: ""
+# # timeout (in ms) for communication with the redis replica server
+# redis-replica-timeout: 100
+# # timeout (in ms) for redis replica commands, if 0, uses redis-replica-timeout.
+# redis-replica-command-timeout: 0
+# # timeout (in ms) for redis replica connection set up, if 0, uses redis-replica-timeout.
+# redis-replica-connect-timeout: 0
+# # redis logical database to use for the replica server, 0 is the default database.
+# redis-replica-logical-db: 0
# IPSet
# Add specify domain into set via ipset.
@@ -1143,7 +1368,7 @@ remote-control:
# dnstap-enable: no
# # if set to yes frame streams will be used in bidirectional mode
# dnstap-bidirectional: yes
-# dnstap-socket-path: "@DNSTAP_SOCKET_PATH@"
+# dnstap-socket-path: ""
# # if "" use the unix socket in dnstap-socket-path, otherwise,
# # set it to "IPaddress[@port]" of the destination.
# dnstap-ip: ""
@@ -1163,6 +1388,8 @@ remote-control:
# dnstap-identity: ""
# # if "" it uses the package version.
# dnstap-version: ""
+# # log only 1/N messages, if 0 it is disabled. default 0.
+# dnstap-sample-rate: 0
# dnstap-log-resolver-query-messages: no
# dnstap-log-resolver-response-messages: no
# dnstap-log-client-query-messages: no
@@ -1171,7 +1398,8 @@ remote-control:
# dnstap-log-forwarder-response-messages: no
# Response Policy Zones
-# RPZ policies. Applied in order of configuration. QNAME, Response IP
+# RPZ policies. Applied in order of configuration. Any match from an earlier
+# RPZ zone will terminate the RPZ lookup. QNAME, Response IP
# Address, nsdname, nsip and clientip triggers are supported. Supported
# actions are: NXDOMAIN, NODATA, PASSTHRU, DROP, Local Data, tcp-only
# and drop. Policies can be loaded from a file, or using zone
diff --git a/contrib/unbound/doc/example.conf.in b/contrib/unbound/doc/example.conf.in
index ba817288bb62..e0149a2df6aa 100644
--- a/contrib/unbound/doc/example.conf.in
+++ b/contrib/unbound/doc/example.conf.in
@@ -1,7 +1,7 @@
#
# Example configuration file.
#
-# See unbound.conf(5) man page, version 1.23.0.
+# See unbound.conf(5) man page, version 1.23.1.
#
# this is a comment.
diff --git a/contrib/unbound/doc/libunbound.3 b/contrib/unbound/doc/libunbound.3
index 7df4f59d7831..8ef33b0998a2 100644
--- a/contrib/unbound/doc/libunbound.3
+++ b/contrib/unbound/doc/libunbound.3
@@ -1,4 +1,4 @@
-.TH "libunbound" "3" "Feb 10, 2022" "NLnet Labs" "unbound 1.15.0"
+.TH "libunbound" "3" "Jul 16, 2025" "NLnet Labs" "unbound 1.23.1"
.\"
.\" libunbound.3 -- unbound library functions manual
.\"
@@ -44,7 +44,7 @@
.B ub_ctx_zone_remove,
.B ub_ctx_data_add,
.B ub_ctx_data_remove
-\- Unbound DNS validating resolver 1.15.0 functions.
+\- Unbound DNS validating resolver 1.23.1 functions.
.SH "SYNOPSIS"
.B #include <unbound.h>
.LP
diff --git a/contrib/unbound/doc/libunbound.3.in b/contrib/unbound/doc/libunbound.3.in
index 4edc9b3c30af..8ef33b0998a2 100644
--- a/contrib/unbound/doc/libunbound.3.in
+++ b/contrib/unbound/doc/libunbound.3.in
@@ -1,4 +1,4 @@
-.TH "libunbound" "3" "Apr 24, 2025" "NLnet Labs" "unbound 1.23.0"
+.TH "libunbound" "3" "Jul 16, 2025" "NLnet Labs" "unbound 1.23.1"
.\"
.\" libunbound.3 -- unbound library functions manual
.\"
@@ -44,7 +44,7 @@
.B ub_ctx_zone_remove,
.B ub_ctx_data_add,
.B ub_ctx_data_remove
-\- Unbound DNS validating resolver 1.23.0 functions.
+\- Unbound DNS validating resolver 1.23.1 functions.
.SH "SYNOPSIS"
.B #include <unbound.h>
.LP
diff --git a/contrib/unbound/doc/unbound-anchor.8 b/contrib/unbound/doc/unbound-anchor.8
index 268640d8155c..23c1dd4b060f 100644
--- a/contrib/unbound/doc/unbound-anchor.8
+++ b/contrib/unbound/doc/unbound-anchor.8
@@ -1,4 +1,4 @@
-.TH "unbound-anchor" "8" "Feb 10, 2022" "NLnet Labs" "unbound 1.15.0"
+.TH "unbound-anchor" "8" "Jul 16, 2025" "NLnet Labs" "unbound 1.23.1"
.\"
.\" unbound-anchor.8 -- unbound anchor maintenance utility manual
.\"
@@ -26,13 +26,13 @@ Suggested usage:
.nf
# in the init scripts.
# provide or update the root anchor (if necessary)
- unbound-anchor \-a "@UNBOUND_ROOTKEY_FILE@"
+ unbound-anchor \-a "/var/unbound/root.key"
# Please note usage of this root anchor is at your own risk
# and under the terms of our LICENSE (see source).
#
# start validating resolver
# the unbound.conf contains:
- # auto-trust-anchor-file: "@UNBOUND_ROOTKEY_FILE@"
+ # auto-trust-anchor-file: "/var/unbound/root.key"
unbound \-c unbound.conf
.fi
.P
@@ -53,12 +53,12 @@ The available options are:
.TP
.B \-a \fIfile
The root anchor key file, that is read in and written out.
-Default is @UNBOUND_ROOTKEY_FILE@.
+Default is /var/unbound/root.key.
If the file does not exist, or is empty, a builtin root key is written to it.
.TP
.B \-c \fIfile
The root update certificate file, that is read in.
-Default is @UNBOUND_ROOTCERT_FILE@.
+Default is /var/unbound/icannbundle.pem.
If the file does not exist, or is empty, a builtin certificate is used.
.TP
.B \-l
@@ -169,11 +169,11 @@ The build\-in configuration can be overridden by providing a root\-cert
file and a rootkey file.
.SH "FILES"
.TP
-.I @UNBOUND_ROOTKEY_FILE@
+.I /var/unbound/root.key
The root anchor file, updated with 5011 tracking, and read and written to.
The file is created if it does not exist.
.TP
-.I @UNBOUND_ROOTCERT_FILE@
+.I /var/unbound/icannbundle.pem
The trusted self\-signed certificate that is used to verify the downloaded
DNSSEC root trust anchor. You can update it by fetching it from
https://data.iana.org/root\-anchors/icannbundle.pem (and validate it).
diff --git a/contrib/unbound/doc/unbound-anchor.8.in b/contrib/unbound/doc/unbound-anchor.8.in
index c3a8d64cabbe..f93c5d0cd045 100644
--- a/contrib/unbound/doc/unbound-anchor.8.in
+++ b/contrib/unbound/doc/unbound-anchor.8.in
@@ -1,4 +1,4 @@
-.TH "unbound-anchor" "8" "Apr 24, 2025" "NLnet Labs" "unbound 1.23.0"
+.TH "unbound-anchor" "8" "Jul 16, 2025" "NLnet Labs" "unbound 1.23.1"
.\"
.\" unbound-anchor.8 -- unbound anchor maintenance utility manual
.\"
diff --git a/contrib/unbound/doc/unbound-checkconf.8 b/contrib/unbound/doc/unbound-checkconf.8
index ac8782dcde40..9afc0df10de3 100644
--- a/contrib/unbound/doc/unbound-checkconf.8
+++ b/contrib/unbound/doc/unbound-checkconf.8
@@ -1,4 +1,4 @@
-.TH "unbound-checkconf" "8" "Feb 10, 2022" "NLnet Labs" "unbound 1.15.0"
+.TH "unbound-checkconf" "8" "Jul 16, 2025" "NLnet Labs" "unbound 1.23.1"
.\"
.\" unbound-checkconf.8 -- unbound configuration checker manual
.\"
@@ -14,6 +14,7 @@ unbound\-checkconf
.B unbound\-checkconf
.RB [ \-h ]
.RB [ \-f ]
+.RB [ \-q ]
.RB [ \-o
.IR option ]
.RI [ cfgfile ]
@@ -37,6 +38,9 @@ Print full pathname, with chroot applied to it. Use with the \-o option.
If given, after checking the config file the value of this option is
printed to stdout. For "" (disabled) options an empty line is printed.
.TP
+.B \-q
+Make the operation quiet, suppress output on success.
+.TP
.I cfgfile
The config file to read with settings for Unbound. It is checked.
If omitted, the config file at the default location is checked.
@@ -45,7 +49,7 @@ The unbound\-checkconf program exits with status code 1 on error,
0 for a correct config file.
.SH "FILES"
.TP
-.I @ub_conf_file@
+.I /var/unbound/unbound.conf
Unbound configuration file.
.SH "SEE ALSO"
\fIunbound.conf\fR(5),
diff --git a/contrib/unbound/doc/unbound-checkconf.8.in b/contrib/unbound/doc/unbound-checkconf.8.in
index 73cb0ebcbb5f..8902784bf0c9 100644
--- a/contrib/unbound/doc/unbound-checkconf.8.in
+++ b/contrib/unbound/doc/unbound-checkconf.8.in
@@ -1,4 +1,4 @@
-.TH "unbound-checkconf" "8" "Apr 24, 2025" "NLnet Labs" "unbound 1.23.0"
+.TH "unbound-checkconf" "8" "Jul 16, 2025" "NLnet Labs" "unbound 1.23.1"
.\"
.\" unbound-checkconf.8 -- unbound configuration checker manual
.\"
diff --git a/contrib/unbound/doc/unbound-control.8 b/contrib/unbound/doc/unbound-control.8
index 9d0c10e942bd..abc333c42ff9 100644
--- a/contrib/unbound/doc/unbound-control.8
+++ b/contrib/unbound/doc/unbound-control.8
@@ -1,4 +1,4 @@
-.TH "unbound-control" "8" "Feb 10, 2022" "NLnet Labs" "unbound 1.15.0"
+.TH "unbound-control" "8" "Jul 16, 2025" "NLnet Labs" "unbound 1.23.1"
.\"
.\" unbound-control.8 -- unbound remote control manual
.\"
@@ -32,7 +32,7 @@ Show the version and commandline option help.
.TP
.B \-c \fIcfgfile
The config file to read with settings. If not given the default
-config file @ub_conf_file@ is used.
+config file /var/unbound/unbound.conf is used.
.TP
.B \-s \fIserver[@port]
IPv4 or IPv6 address of the server to contact. If not given, the
@@ -54,6 +54,161 @@ Stop the server. The server daemon exits.
.B reload
Reload the server. This flushes the cache and reads the config file fresh.
.TP
+.B reload_keep_cache
+Reload the server but try to keep the RRset and message cache if
+(re)configuration allows for it.
+That means the caches sizes and the number of threads must not change between
+reloads.
+.TP
+.B fast_reload \fR[\fI+dpv\fR]
+Reload the server, but keep downtime to a minimum, so that user queries
+keep seeing service. This needs the code compiled with threads. The config
+is loaded in a thread, and prepared, then it briefly pauses the existing
+server and updates config options. The intent is that the pause does not
+impact the service of user queries. The cache is kept. Also user queries
+worked on are kept and continue, but with the new config options.
+.IP
+This command is experimental at this time.
+.IP
+The amount of temporal memory needed during a fast_reload is twice the
+amount needed for configuration.
+This is because Unbound temporarily needs to store both current configuration
+values and new ones while trying to fast_reload.
+Zones loaded from disk (authority zones and RPZ zones) are included in such
+memory needs.
+.IP
+Options that can be changed are for
+forwards,
+stubs,
+views,
+authority zones,
+RPZ zones and
+local zones.
+.IP
+Also
+access-control and similar options,
+interface-action and similar options and
+tcp-connection-limit.
+It can reload some
+define-tag
+changes, more on that below.
+Further options include
+insecure-lan-zones,
+domain-insecure,
+trust-anchor-file,
+trust-anchor,
+trusted-keys-file,
+auto-trust-anchor-file,
+edns-client-string,
+ipset,
+log-identity,
+infra-cache-numhosts,
+msg-cache-size,
+rrset-cache-size,
+key-cache-size,
+ratelimit-size,
+neg-cache-size,
+num-queries-per-thread,
+jostle-timeout,
+use-caps-for-id,
+unwanted-reply-threshold,
+tls-use-sni,
+outgoing-tcp-mss,
+ip-dscp,
+max-reuse-tcp-queries,
+tcp-reuse-timeout,
+tcp-auth-query-timeout,
+delay-close.
+.IP
+It does not work with
+interface and
+outgoing-interface changes,
+also not with
+remote control,
+outgoing-port-permit,
+outgoing-port-avoid,
+msg-buffer-size,
+any **\*-slabs** options and
+statistics-interval changes.
+.IP
+For dnstap these options can be changed:
+dnstap-log-resolver-query-messages,
+dnstap-log-resolver-response-messages,
+dnstap-log-client-query-messages,
+dnstap-log-client-response-messages,
+dnstap-log-forwarder-query-messages and
+dnstap-log-forwarder-response-messages.
+.IP
+It does not work with these options:
+dnstap-enable,
+dnstap-bidirectional,
+dnstap-socket-path,
+dnstap-ip,
+dnstap-tls,
+dnstap-tls-server-name,
+dnstap-tls-cert-bundle,
+dnstap-tls-client-key-file and
+dnstap-tls-client-cert-file.
+.IP
+The options
+dnstap-send-identity,
+dnstap-send-version,
+dnstap-identity, and
+dnstap-version can be loaded
+when ``+p`` is not used.
+.IP
+The '+v' option makes the output verbose which includes the time it took to do
+the reload.
+With '+vv' it is more verbose which includes the amount of memory that was
+allocated temporarily to perform the reload; this amount of memory can be big
+if the config has large contents.
+In the timing output the 'reload' time is the time during which the server was
+paused.
+.IP
+The '+p' option makes the reload not pause threads, they keep running.
+Locks are acquired, but items are updated in sequence, so it is possible
+for threads to see an inconsistent state with some options from the old
+and some options from the new config, such as cache TTL parameters from the
+old config and forwards from the new config. The stubs and forwards are
+updated at the same time, so that they are viewed consistently, either old
+or new values together. The option makes the reload time take eg. 3
+microseconds instead of 0.3 milliseconds during which the worker threads are
+interrupted. So, the interruption is much shorter, at the expense of some
+inconsistency. After the reload itself, every worker thread is briefly
+contacted to make them release resources, this makes the delete timing
+a little longer, and takes up time from the remote control servicing
+worker thread.
+.IP
+With the nopause option, the reload does not work to reload some options,
+that fast reload works on without the nopause option: val-bogus-ttl,
+val-override-date, val-sig-skew-min, val-sig-skew-max, val-max-restart,
+val-nsec3-keysize-iterations, target-fetch-policy, outbound-msg-retry,
+max-sent-count, max-query-restarts, do-not-query-address,
+do-not-query-localhost, private-address, private-domain, caps-exempt,
+nat64-prefix, do-nat64, infra-host-ttl, infra-keep-probing, ratelimit,
+ip-ratelimit, ip-ratelimit-cookie, wait-limit-netblock,
+wait-limit-cookie-netblock, ratelimit-below-domain, ratelimit-for-domain.
+.IP
+The '+d' option makes the reload drop queries that the worker threads are
+working on. This is like flush_requestlist. Without it the queries are kept
+so that users keep getting answers for those queries that are currently
+processed. The drop makes it so that queries during the life time of the
+query processing see only old, or only new config options.
+.IP
+When there are changes to the config tags, from the \fBdefine\-tag\fR option,
+then the '+d' option is implicitly turned on with a warning printout, and
+queries are dropped.
+This is to stop references to the old tag information, by the old
+queries. If the number of tags is increased in the newly loaded config, by
+adding tags at the end, then the implicit '+d' option is not needed.
+.IP
+For response ip, that is actions associated with IP addresses, and perhaps
+intersected with access control tag and action information, those settings
+are stored with a query when it comes in based on its source IP address.
+The old information is kept with the query until the queries are done.
+This is gone when those queries are resolved and finished, or it is possible
+to flush the requestlist with '+d'.
+.TP
.B verbosity \fInumber
Change verbosity value for logging. Same values as \fBverbosity\fR keyword in
\fIunbound.conf\fR(5). This new setting lasts until the server is issued
@@ -115,31 +270,38 @@ Remove local data RRs read from stdin of unbound\-control. Input is one name per
line. For bulk removals.
.TP
.B dump_cache
-The contents of the cache is printed in a text format to stdout. You can
-redirect it to a file to store the cache in a file.
+The content of the cache is printed in a text format to stdout.
+You can redirect it to a file to store the cache in a file.
+Not supported in remote Unbounds in multi-process operation.
.TP
.B load_cache
-The contents of the cache is loaded from stdin. Uses the same format as
-dump_cache uses. Loading the cache with old, or wrong data can result
-in old or wrong data returned to clients. Loading data into the cache
-in this way is supported in order to aid with debugging.
+The content of the cache is loaded from stdin.
+Uses the same format as dump_cache uses.
+Loading the cache with old, or wrong data can result in old or wrong data
+returned to clients.
+Loading data into the cache in this way is supported in order to aid with
+debugging.
+Not supported in remote Unbounds in multi-process operation.
.TP
.B lookup \fIname
Print to stdout the name servers that would be used to look up the
name specified.
.TP
-.B flush \fIname
+.B flush \fR[\fI+c\fR] \fIname
Remove the name from the cache. Removes the types
-A, AAAA, NS, SOA, CNAME, DNAME, MX, PTR, SRV and NAPTR.
+A, AAAA, NS, SOA, CNAME, DNAME, MX, PTR, SRV, NAPTR, SVCB and HTTPS.
Because that is fast to do. Other record types can be removed using
.B flush_type
or
.B flush_zone\fR.
+.IP
+The '+c' option removes the items also from the cachedb cache. If
+cachedb is in use.
.TP
-.B flush_type \fIname\fR \fItype
+.B flush_type \fR[\fI+c\fR] \fIname\fR \fItype
Remove the name, type information from the cache.
.TP
-.B flush_zone \fIname
+.B flush_zone \fR[\fI+c\fR] \fIname
Remove all information at or below the name from the cache.
The rrsets and key entries are removed so that new lookups will be performed.
This needs to walk and inspect the entire cache, and is a slow operation.
@@ -147,10 +309,10 @@ The entries are set to expired in the implementation of this command (so,
with serve\-expired enabled, it'll serve that information but schedule a
prefetch for new information).
.TP
-.B flush_bogus
+.B flush_bogus \fR[\fI+c\fR]
Remove all bogus data from the cache.
.TP
-.B flush_negative
+.B flush_negative \fR[\fI+c\fR]
Remove all negative data from the cache. This is nxdomain answers,
nodata answers and servfail answers. Also removes bad key entries
(which could be due to failed lookups) from the dnssec key cache, and
@@ -233,22 +395,24 @@ still be bogus, use \fBflush_zone\fR to remove it), does not affect the config f
.B insecure_remove \fIzone
Removes domain\-insecure for the given zone.
.TP
-.B forward_add \fR[\fI+i\fR] \fIzone addr ...
+.B forward_add \fR[\fI+it\fR] \fIzone addr ...
Add a new forward zone to running Unbound. With +i option also adds a
\fIdomain\-insecure\fR for the zone (so it can resolve insecurely if you have
a DNSSEC root trust anchor configured for other names).
The addr can be IP4, IP6 or nameserver names, like \fIforward-zone\fR config
in unbound.conf.
+The +t option sets it to use tls upstream, like \fIforward\-tls\-upstream\fR: yes.
.TP
.B forward_remove \fR[\fI+i\fR] \fIzone
Remove a forward zone from running Unbound. The +i also removes a
\fIdomain\-insecure\fR for the zone.
.TP
-.B stub_add \fR[\fI+ip\fR] \fIzone addr ...
+.B stub_add \fR[\fI+ipt\fR] \fIzone addr ...
Add a new stub zone to running Unbound. With +i option also adds a
\fIdomain\-insecure\fR for the zone. With +p the stub zone is set to prime,
without it it is set to notprime. The addr can be IP4, IP6 or nameserver
names, like the \fIstub-zone\fR config in unbound.conf.
+The +t option sets it to use tls upstream, like \fIstub\-tls\-upstream\fR: yes.
.TP
.B stub_remove \fR[\fI+i\fR] \fIzone
Remove a stub zone from running Unbound. The +i also removes a
@@ -289,20 +453,22 @@ just the ratelimited ips, with their estimated qps. The ratelimited
ips are dropped before checking the cache.
.TP
.B list_auth_zones
-List the auth zones that are configured. Printed one per line with a
-status, indicating if the zone is expired and current serial number.
+List the auth zones that are configured. Printed one per line with a status,
+indicating if the zone is expired and current serial number. Configured RPZ
+zones are included.
.TP
.B auth_zone_reload \fIzone\fR
-Reload the auth zone from zonefile. The zonefile is read in overwriting
-the current contents of the zone in memory. This changes the auth zone
-contents itself, not the cache contents. Such cache contents exists if
-you set Unbound to validate with for-upstream yes and that can be cleared
-with \fBflush_zone\fR \fIzone\fR.
+Reload the auth zone (or RPZ zone) from zonefile. The zonefile is read in
+overwriting the current contents of the zone in memory. This changes the auth
+zone contents itself, not the cache contents. Such cache contents exists if
+you set Unbound to validate with for-upstream yes and that can be cleared with
+\fBflush_zone\fR \fIzone\fR.
.TP
.B auth_zone_transfer \fIzone\fR
-Transfer the auth zone from master. The auth zone probe sequence is started,
-where the masters are probed to see if they have an updated zone (with the SOA
-serial check). And then the zone is transferred for a newer zone version.
+Transfer the auth zone (or RPZ zone) from master. The auth zone probe sequence
+is started, where the masters are probed to see if they have an updated zone
+(with the SOA serial check). And then the zone is transferred for a newer zone
+version.
.TP
.B rpz_enable \fIzone\fR
Enable the RPZ zone if it had previously been disabled.
@@ -333,6 +499,41 @@ Remove a list of \fIlocal_data\fR for given view from stdin. Like local_datas_re
.TP
.B view_local_datas \fIview\fR
Add a list of \fIlocal_data\fR for given view from stdin. Like local_datas.
+.TP
+.B add_cookie_secret <secret>
+Add or replace a cookie secret persistently. <secret> needs to be an 128 bit
+hex string.
+.IP
+Cookie secrets can be either \fIactive\fR or \fIstaging\fR. \fIActive\fR cookie
+secrets are used to create DNS Cookies, but verification of a DNS Cookie
+succeeds with any of the \fIactive\fR or \fIstaging\fR cookie secrets. The
+state of the current cookie secrets can be printed with the
+\fBprint_cookie_secrets\fR command.
+.IP
+When there are no cookie secrets configured yet, the <secret> is added as
+\fIactive\fR. If there is already an \fIactive\fR cookie secret, the <secret>
+is added as \fIstaging\fR or replacing an existing \fIstaging\fR secret.
+.IP
+To "roll" a cookie secret used in an anycast set. The new secret has to be
+added as staging secret to \fBall\fR nodes in the anycast set. When \fBall\fR
+nodes can verify DNS Cookies with the new secret, the new secret can be
+activated with the \fBactivate_cookie_secret\fR command. After \fBall\fR nodes
+have the new secret \fIactive\fR for at least one hour, the previous secret can
+be dropped with the \fBdrop_cookie_secret\fR command.
+.IP
+Persistence is accomplished by writing to a file which if configured with the
+\fBcookie\-secret\-file\fR option in the server section of the config file.
+This is disabled by default, "".
+.TP
+.B drop_cookie_secret
+Drop the \fIstaging\fR cookie secret.
+.TP
+.B activate_cookie_secret
+Make the current \fIstaging\fR cookie secret \fIactive\fR, and the current
+\fIactive\fR cookie secret \fIstaging\fR.
+.TP
+.B print_cookie_secrets
+Show the current configured cookie secrets with their status.
.SH "EXIT CODE"
The unbound\-control program exits with status code 1 on error, 0 on success.
.SH "SET UP"
@@ -361,6 +562,21 @@ number of queries received by thread
.I threadX.num.queries_ip_ratelimited
number of queries rate limited by thread
.TP
+.I threadX.num.queries_cookie_valid
+number of queries with a valid DNS Cookie by thread
+.TP
+.I threadX.num.queries_cookie_client
+number of queries with a client part only DNS Cookie by thread
+.TP
+.I threadX.num.queries_cookie_invalid
+number of queries with an invalid DNS Cookie by thread
+.TP
+.I threadX.num.queries_discard_timeout
+number of queries removed due to discard-timeout by thread
+.TP
+.I threadX.num.queries_wait_limit
+number of queries removed due to wait-limit by thread
+.TP
.I threadX.num.cachehits
number of queries that were successfully answered using a cache lookup
.TP
@@ -380,6 +596,9 @@ request for certificates.
.I threadX.num.dnscrypt.malformed
number of request that were neither cleartext, not valid dnscrypt messages.
.TP
+.I threadX.num.dns_error_reports
+number of DNS Error Reports generated by thread
+.TP
.I threadX.num.prefetch
number of cache prefetches performed. This number is included in
cachehits, as the original query had the unprefetched answer from cache,
@@ -390,6 +609,14 @@ as a cache response was sent.
.I threadX.num.expired
number of replies that served an expired cache entry.
.TP
+.I threadX.num.queries_timed_out
+number of queries that are dropped because they waited in the UDP socket buffer
+for too long.
+.TP
+.I threadX.query.queue_time_us.max
+The maximum wait time for packets in the socket buffer, in microseconds. This
+is only reported when sock-queue-timeout is enabled.
+.TP
.I threadX.num.recursivereplies
The number of replies sent to queries that needed recursive processing. Could be smaller than threadX.num.cachemiss if due to timeouts no replies were sent for some queries.
.TP
@@ -430,6 +657,24 @@ buffers are full.
.I total.num.queries
summed over threads.
.TP
+.I total.num.queries_ip_ratelimited
+summed over threads.
+.TP
+.I total.num.queries_cookie_valid
+summed over threads.
+.TP
+.I total.num.queries_cookie_client
+summed over threads.
+.TP
+.I total.num.queries_cookie_invalid
+summed over threads.
+.TP
+.I total.num.queries_discard_timeout
+summed over threads.
+.TP
+.I total.num.queries_wait_limit
+summed over threads.
+.TP
.I total.num.cachehits
summed over threads.
.TP
@@ -448,12 +693,21 @@ summed over threads.
.I total.num.dnscrypt.malformed
summed over threads.
.TP
+.I total.num.dns_error_reports
+summed over threads.
+.TP
.I total.num.prefetch
summed over threads.
.TP
.I total.num.expired
summed over threads.
.TP
+.I total.num.queries_timed_out
+summed over threads.
+.TP
+.I total.query.queue_time_us.max
+the maximum of the thread values.
+.TP
.I total.num.recursivereplies
summed over threads.
.TP
@@ -519,6 +773,10 @@ queries waiting for request stream completion.
Memory in bytes used by the HTTP/2 response buffers. Containing DNS responses
waiting to be written back to the clients.
.TP
+.I mem.quic
+Memory in bytes used by QUIC. Containing connection information, stream
+information, queries read and responses written back to the clients.
+.TP
.I histogram.<sec>.<usec>.to.<sec>.<usec>
Shows a histogram, summed over all threads. Every element counts the
recursive queries whose reply time fit between the lower and upper bound.
@@ -550,6 +808,10 @@ Number of queries that were made using TCP towards the Unbound server.
Number of queries that the Unbound server made using TCP outgoing towards
other servers.
.TP
+.I num.query.udpout
+Number of queries that the Unbound server made using UDP outgoing towards
+other servers.
+.TP
.I num.query.tls
Number of queries that were made using TLS towards the Unbound server.
These are also counted in num.query.tcp, because TLS uses TCP.
@@ -563,6 +825,10 @@ Number of queries that were made using HTTPS towards the Unbound server.
These are also counted in num.query.tcp and num.query.tls, because HTTPS
uses TLS and TCP.
.TP
+.I num.query.quic
+Number of queries that were made using QUIC towards the Unbound server.
+These are also counted in num.query.tls, because TLS is used for these queries.
+.TP
.I num.query.ipv6
Number of queries that were made using IPv6 towards the Unbound server.
.TP
@@ -585,7 +851,7 @@ ratelimiting.
.TP
.I num.query.dnscrypt.shared_secret.cachemiss
The number of dnscrypt queries that did not find a shared secret in the cache.
-The can be use to compute the shared secret hitrate.
+This can be used to compute the shared secret hitrate.
.TP
.I num.query.dnscrypt.replay
The number of dnscrypt queries that found a nonce hit in the nonce cache and
@@ -641,6 +907,18 @@ timing and protocol support information.
The number of items in the key cache. These are DNSSEC keys, one item
per delegation point, and their validation status.
.TP
+.I msg.cache.max_collisions
+The maximum number of hash table collisions in the msg cache. This is the
+number of hashes that are identical when a new element is inserted in the
+hash table. If the value is very large, like hundreds, something is wrong
+with the performance of the hash table, hash values are incorrect or malicious.
+.TP
+.I rrset.cache.max_collisions
+The maximum number of hash table collisions in the rrset cache. This is the
+number of hashes that are identical when a new element is inserted in the
+hash table. If the value is very large, like hundreds, something is wrong
+with the performance of the hash table, hash values are incorrect or malicious.
+.TP
.I dnscrypt_shared_secret.cache.count
The number of items in the shared secret cache. These are precomputed shared
secrets for a given client public key/server secret key pair. Shared secrets
@@ -680,7 +958,12 @@ Number of queries that got an answer that contained EDNS client subnet data.
.I num.query.subnet_cache
Number of queries answered from the edns client subnet cache. These are
counted as cachemiss by the main counters, but hit the client subnet
-specific cache, after getting processed by the edns client subnet module.
+specific cache after getting processed by the edns client subnet module.
+.TP
+.I num.query.cachedb
+Number of queries answered from the external cache of cachedb.
+These are counted as cachemiss by the main counters, but hit the cachedb
+external cache after getting processed by the cachedb module.
.TP
.I num.rpz.action.<rpz_action>
Number of queries answered using configured RPZ policy, per RPZ action type.
@@ -688,10 +971,10 @@ Possible actions are: nxdomain, nodata, passthru, drop, tcp\-only, local\-data,
disabled, and cname\-override.
.SH "FILES"
.TP
-.I @ub_conf_file@
+.I /var/unbound/unbound.conf
Unbound configuration file.
.TP
-.I @UNBOUND_RUN_DIR@
+.I /var/unbound
directory with private keys (unbound_server.key and unbound_control.key) and
self\-signed certificates (unbound_server.pem and unbound_control.pem).
.SH "SEE ALSO"
diff --git a/contrib/unbound/doc/unbound-control.8.in b/contrib/unbound/doc/unbound-control.8.in
index 449c279d6499..f7a497782701 100644
--- a/contrib/unbound/doc/unbound-control.8.in
+++ b/contrib/unbound/doc/unbound-control.8.in
@@ -1,4 +1,4 @@
-.TH "unbound-control" "8" "Apr 24, 2025" "NLnet Labs" "unbound 1.23.0"
+.TH "unbound-control" "8" "Jul 16, 2025" "NLnet Labs" "unbound 1.23.1"
.\"
.\" unbound-control.8 -- unbound remote control manual
.\"
diff --git a/contrib/unbound/doc/unbound-host.1 b/contrib/unbound/doc/unbound-host.1
index 94c8ca3dd569..7fba066968ec 100644
--- a/contrib/unbound/doc/unbound-host.1
+++ b/contrib/unbound/doc/unbound-host.1
@@ -1,4 +1,4 @@
-.TH "unbound\-host" "1" "Feb 10, 2022" "NLnet Labs" "unbound 1.15.0"
+.TH "unbound\-host" "1" "Jul 16, 2025" "NLnet Labs" "unbound 1.23.1"
.\"
.\" unbound-host.1 -- unbound DNS lookup utility
.\"
@@ -73,7 +73,7 @@ For example \-y "example.com DS 31560 5 1 1CFED84787E6E19CCF9372C1187325972FE546
.TP
.B \-D
Enables DNSSEC validation. Reads the root anchor from the default configured
-root anchor at the default location, \fI@UNBOUND_ROOTKEY_FILE@\fR.
+root anchor at the default location, \fI/var/unbound/root.key\fR.
.TP
.B \-f \fIkeyfile
Reads keys from a file. Every line has a DS or DNSKEY record, in the format
diff --git a/contrib/unbound/doc/unbound-host.1.in b/contrib/unbound/doc/unbound-host.1.in
index f56a8d4d18ed..a99bab0f7be6 100644
--- a/contrib/unbound/doc/unbound-host.1.in
+++ b/contrib/unbound/doc/unbound-host.1.in
@@ -1,4 +1,4 @@
-.TH "unbound\-host" "1" "Apr 24, 2025" "NLnet Labs" "unbound 1.23.0"
+.TH "unbound\-host" "1" "Jul 16, 2025" "NLnet Labs" "unbound 1.23.1"
.\"
.\" unbound-host.1 -- unbound DNS lookup utility
.\"
diff --git a/contrib/unbound/doc/unbound.8 b/contrib/unbound/doc/unbound.8
index 723f23238b8e..331fc47f358e 100644
--- a/contrib/unbound/doc/unbound.8
+++ b/contrib/unbound/doc/unbound.8
@@ -1,4 +1,4 @@
-.TH "unbound" "8" "Feb 10, 2022" "NLnet Labs" "unbound 1.15.0"
+.TH "unbound" "8" "Jul 16, 2025" "NLnet Labs" "unbound 1.23.1"
.\"
.\" unbound.8 -- unbound manual
.\"
@@ -9,7 +9,7 @@
.\"
.SH "NAME"
.B unbound
-\- Unbound DNS validating resolver 1.15.0.
+\- Unbound DNS validating resolver 1.23.1.
.SH "SYNOPSIS"
.B unbound
.RB [ \-h ]
@@ -58,7 +58,7 @@ Show the version number and commandline option help, and exit.
.TP
.B \-c\fI cfgfile
Set the config file with settings for Unbound to read instead of reading the
-file at the default location, @ub_conf_file@. The syntax is
+file at the default location, /var/unbound/unbound.conf. The syntax is
described in \fIunbound.conf\fR(5).
.TP
.B \-d
@@ -75,7 +75,7 @@ concurrently.
.TP
.B \-v
Increase verbosity. If given multiple times, more information is logged.
-This is in addition to the verbosity (if any) from the config file.
+This is added to the verbosity (if any) from the config file.
.TP
.B \-V
Show the version number and build options, and exit.
diff --git a/contrib/unbound/doc/unbound.8.in b/contrib/unbound/doc/unbound.8.in
index 33c87cde4edf..1ec4c304b70c 100644
--- a/contrib/unbound/doc/unbound.8.in
+++ b/contrib/unbound/doc/unbound.8.in
@@ -1,4 +1,4 @@
-.TH "unbound" "8" "Apr 24, 2025" "NLnet Labs" "unbound 1.23.0"
+.TH "unbound" "8" "Jul 16, 2025" "NLnet Labs" "unbound 1.23.1"
.\"
.\" unbound.8 -- unbound manual
.\"
@@ -9,7 +9,7 @@
.\"
.SH "NAME"
.B unbound
-\- Unbound DNS validating resolver 1.23.0.
+\- Unbound DNS validating resolver 1.23.1.
.SH "SYNOPSIS"
.B unbound
.RB [ \-h ]
diff --git a/contrib/unbound/doc/unbound.conf.5 b/contrib/unbound/doc/unbound.conf.5
index e1cc5c020756..af03aefb1855 100644
--- a/contrib/unbound/doc/unbound.conf.5
+++ b/contrib/unbound/doc/unbound.conf.5
@@ -1,4 +1,4 @@
-.TH "unbound.conf" "5" "Feb 10, 2022" "NLnet Labs" "unbound 1.15.0"
+.TH "unbound.conf" "5" "Jul 16, 2025" "NLnet Labs" "unbound 1.23.1"
.\"
.\" unbound.conf.5 -- unbound.conf manual
.\"
@@ -112,13 +112,21 @@ If enabled, extended statistics are printed from \fIunbound\-control\fR(8).
Default is off, because keeping track of more statistics takes time. The
counters are listed in \fIunbound\-control\fR(8).
.TP
+.B statistics\-inhibit\-zero: \fI<yes or no>
+If enabled, selected extended statistics with a value of 0 are inhibited from
+printing with \fIunbound\-control\fR(8).
+These are query types, query classes, query opcodes, answer rcodes
+(except NOERROR, FORMERR, SERVFAIL, NXDOMAIN, NOTIMPL, REFUSED) and
+RPZ actions.
+Default is on.
+.TP
.B num\-threads: \fI<number>
The number of threads to create to serve clients. Use 1 for no threading.
.TP
.B port: \fI<port number>
The port number, default 53, on which the server responds to queries.
.TP
-.B interface: \fI<ip address[@port]>
+.B interface: \fI<ip address or interface name [@port]>
Interface to use to connect to the network. This interface is listened to
for queries from clients, and answers to clients are given from it.
Can be given multiple times to work on several interfaces. If none are
@@ -129,7 +137,7 @@ A port number can be specified with @port (without spaces between
interface and port number), if not specified the default port (from
\fBport\fR) is used.
.TP
-.B ip\-address: \fI<ip address[@port]>
+.B ip\-address: \fI<ip address or interface name [@port]>
Same as interface: (for ease of compatibility with nsd.conf).
.TP
.B interface\-automatic: \fI<yes or no>
@@ -140,6 +148,15 @@ ip\-transparent you can select which (future) interfaces Unbound provides
service on. This feature is experimental, and needs support in your OS for
particular socket options. Default value is no.
.TP
+.B interface\-automatic\-ports: \fI<string>
+List the port numbers that interface-automatic listens on. If empty, the
+default port is listened on. The port numbers are separated by spaces in the
+string. Default is "".
+.IP
+This can be used to have interface automatic to deal with the interface,
+and listen on the normal port number, by including it in the list, and
+also https or dns over tls port numbers by putting them in the list as well.
+.TP
.B outgoing\-interface: \fI<ip address or ip6 netblock>
Interface to use to connect to the network. This interface is used to send
queries to authoritative servers and receive their replies. Can be given
@@ -216,7 +233,8 @@ number).
.B max\-udp\-size: \fI<number>
Maximum UDP response size (not applied to TCP response). 65536 disables the
udp response size maximum, and uses the choice from the client, always.
-Suggested values are 512 to 4096. Default is 4096.
+Suggested values are 512 to 4096. Default is 1232. The default value is the
+same as the default for edns\-buffer\-size.
.TP
.B stream\-wait\-size: \fI<number>
Number of bytes size maximum to use for waiting stream buffers. Default is
@@ -284,6 +302,40 @@ Increase this if you are behind a slow satellite link, to eg. 1128.
That would then avoid re\-querying every initial query because it times out.
Default is 376 msec.
.TP
+.B discard\-timeout: \fI<msec>
+The wait time in msec where recursion requests are dropped. This is
+to stop a large number of replies from accumulating. They receive
+no reply, the work item continues to recurse. It is nice to be a bit
+larger than serve\-expired\-client\-timeout if that is enabled.
+A value of 1900 msec is suggested. The value 0 disables it.
+Default 1900 msec.
+.TP
+.B wait\-limit: \fI<number>
+The number of replies that can wait for recursion, for an IP address.
+This makes a ratelimit per IP address of waiting replies for recursion.
+It stops very large amounts of queries waiting to be returned to one
+destination. The value 0 disables wait limits. Default is 1000.
+.TP
+.B wait\-limit\-cookie: \fI<number>
+The number of replies that can wait for recursion, for an IP address
+that sent the query with a valid DNS cookie. Since the cookie validates
+the client address, the limit can be higher. Default is 10000.
+.TP
+.B wait\-limit\-netblock: \fI<netblock> <number>
+The wait limit for the netblock. If not given the wait\-limit value is
+used. The most specific netblock is used to determine the limit. Useful for
+overriding the default for a specific, group or individual, server.
+The value -1 disables wait limits for the netblock.
+By default the loopback has a wait limit netblock of -1, it is not limited,
+because it is separated from the rest of network for spoofed packets.
+The loopback addresses 127.0.0.0/8 and ::1/128 are default at -1.
+.TP
+.B wait\-limit\-cookie\-netblock: \fI<netblock> <number>
+The wait limit for the netblock, when the query has a DNS cookie.
+If not given, the wait\-limit\-cookie value is used.
+The value -1 disables wait limits for the netblock.
+The loopback addresses 127.0.0.0/8 and ::1/128 are default at -1.
+.TP
.B so\-rcvbuf: \fI<number>
If not 0, then set the SO_RCVBUF socket option to get more buffer
space on UDP port 53 incoming queries. So that short spikes on busy
@@ -340,7 +392,7 @@ ip\-transparent option is also available.
The value of the Differentiated Services Codepoint (DSCP) in the
differentiated services field (DS) of the outgoing IP packet headers.
The field replaces the outdated IPv4 Type-Of-Service field and the
-IPV6 traffic class field.
+IPv6 traffic class field.
.TP
.B rrset\-cache\-size: \fI<number>
Number of bytes size of the RRset cache. Default is 4 megabytes.
@@ -370,6 +422,15 @@ Time to live maximum for negative responses, these have a SOA in the
authority section that is limited in time. Default is 3600.
This applies to nxdomain and nodata answers.
.TP
+.B cache\-min\-negative\-ttl: \fI<seconds>
+Time to live minimum for negative responses, these have a SOA in the
+authority section that is limited in time.
+Default is 0 (disabled).
+If this is disabled and \fBcache-min-ttl\fR is configured, it will take effect
+instead.
+In that case you can set this to 1 to honor the upstream TTL.
+This applies to nxdomain and nodata answers.
+.TP
.B infra\-host\-ttl: \fI<seconds>
Time to live for entries in the host cache. The host cache contains
roundtrip timing, lameness and EDNS support information. Default is 900.
@@ -386,6 +447,10 @@ Lower limit for dynamic retransmit timeout calculation in infrastructure
cache. Default is 50 milliseconds. Increase this value if using forwarders
needing more time to do recursive name resolution.
.TP
+.B infra\-cache\-max\-rtt: \fI<msec>
+Upper limit for dynamic retransmit timeout calculation in infrastructure
+cache. Default is 2 minutes.
+.TP
.B infra\-keep\-probing: \fI<yes or no>
If enabled the server keeps probing hosts that are down, in the one probe
at a time regime. Default is no. Hosts that are down, eg. they did
@@ -403,7 +468,7 @@ Enable or disable whether ip4 queries are answered or issued. Default is yes.
Enable or disable whether ip6 queries are answered or issued. Default is yes.
If disabled, queries are not answered on IPv6, and queries are not sent on
IPv6 to the internet nameservers. With this option you can disable the
-ipv6 transport for sending DNS traffic, it does not impact the contents of
+IPv6 transport for sending DNS traffic, it does not impact the contents of
the DNS traffic, which may have ip4 and ip6 addresses in it.
.TP
.B prefer\-ip4: \fI<yes or no>
@@ -450,6 +515,8 @@ configured value if the number of free buffers falls below 35% of the
total number configured, and finally to 0 if the number of free buffers
falls below 20% of the total number configured. A minimum timeout of
200 milliseconds is observed regardless of the option value used.
+It will be overridden by \fBedns\-tcp\-keepalive\-timeout\fR if
+\fBedns\-tcp\-keepalive\fR is enabled.
.TP
.B tcp-reuse-timeout: \fI<msec>\fR
The period Unbound will keep TCP persistent connections open to
@@ -468,20 +535,19 @@ This option defaults to 3000 milliseconds.
Enable or disable EDNS TCP Keepalive. Default is no.
.TP
.B edns-tcp-keepalive-timeout: \fI<msec>\fR
-The period Unbound will wait for a query on a TCP connection when
-EDNS TCP Keepalive is active. If this timeout expires Unbound closes
-the connection. If the client supports the EDNS TCP Keepalive option,
+Overrides \fBtcp\-idle\-timeout\fR when \fBedns\-tcp\-keepalive\fR is enabled.
+If the client supports the EDNS TCP Keepalive option,
Unbound sends the timeout value to the client to encourage it to
close the connection before the server times out.
This option defaults to 120000 milliseconds.
-When the number of free incoming TCP buffers falls below 50% of
-the total number configured, the advertised timeout is progressively
-reduced to 1% of the configured value, then to 0.2% of the configured
-value if the number of free buffers falls below 35% of the total number
-configured, and finally to 0 if the number of free buffers falls below
-20% of the total number configured.
-A minimum actual timeout of 200 milliseconds is observed regardless of the
-advertised timeout.
+.TP
+.B sock\-queue\-timeout: \fI<sec>\fR
+UDP queries that have waited in the socket buffer for a long time can be
+dropped. Default is 0, disabled. The time is set in seconds, 3 could be a
+good value to ignore old queries that likely the client does not need a reply
+for any more. This could happen if the host has not been able to service
+the queries for a while, i.e. Unbound is not running, and then is enabled
+again. It uses timestamp socket options.
.TP
.B tcp\-upstream: \fI<yes or no>
Enable or disable whether the upstream queries use TCP only for transport.
@@ -499,10 +565,14 @@ Enabled or disable whether the upstream queries use TLS only for transport.
Default is no. Useful in tunneling scenarios. The TLS contains plain DNS in
TCP wireformat. The other server must support this (see
\fBtls\-service\-key\fR).
-If you enable this, also configure a tls\-cert\-bundle or use tls\-win\-cert to
-load CA certs, otherwise the connections cannot be authenticated.
-This option enables TLS for all of them, but if you do not set this you can
-configure TLS specifically for some forward zones with forward\-tls\-upstream. And also with stub\-tls\-upstream.
+If you enable this, also configure a tls\-cert\-bundle or use tls\-win\-cert or
+tls\-system\-cert to load CA certs, otherwise the connections cannot be
+authenticated. This option enables TLS for all of them, but if you do not set
+this you can configure TLS specifically for some forward zones with
+forward\-tls\-upstream. And also with stub\-tls\-upstream.
+If the tls\-upstream option is enabled, it is for all the forwards and stubs,
+where the forward\-tls\-upstream and stub\-tls\-upstream options are ignored,
+as if they had been set to yes.
.TP
.B ssl\-upstream: \fI<yes or no>
Alternate syntax for \fBtls\-upstream\fR. If both are present in the config
@@ -551,7 +621,12 @@ Alternate syntax for \fBtls\-cert\-bundle\fR.
Add the system certificates to the cert bundle certificates for authentication.
If no cert bundle, it uses only these certificates. Default is no.
On windows this option uses the certificates from the cert store. Use
-the tls\-cert\-bundle option on other systems.
+the tls\-cert\-bundle option on other systems. On other systems, this option
+enables the system certificates.
+.TP
+.B tls\-system\-cert: \fI<yes or no>
+This the same setting as the tls\-win\-cert setting, under a different name.
+Because it is not windows specific.
.TP
.B tls\-additional\-port: \fI<portnr>
List portnumbers as tls\-additional\-port, and when interfaces are defined,
@@ -637,6 +712,29 @@ Ignored if the option is not available. Default is yes.
Disable use of TLS for the downstream DNS-over-HTTP connections. Useful for
local back end servers. Default is no.
.TP
+.B proxy\-protocol\-port: \fI<portnr>
+List port numbers as proxy\-protocol\-port, and when interfaces are defined,
+eg. with the @port suffix, as this port number, they support and expect PROXYv2.
+In this case the proxy address will only be used for the network communication
+and initial ACL (check if the proxy itself is denied/refused by configuration).
+The proxied address (if any) will then be used as the true client address and
+will be used where applicable for logging, ACL, DNSTAP, RPZ and IP ratelimiting.
+PROXYv2 is supported for UDP and TCP/TLS listening interfaces.
+There is no support for PROXYv2 on a DoH, DoQ or DNSCrypt listening interface.
+Can list multiple, each on a new statement.
+.TP
+.B quic\-port: \fI<number>
+The port number on which to provide DNS-over-QUIC service, default 853, only
+interfaces configured with that port number as @number get the QUIC service.
+The interface uses QUIC for the UDP traffic on that port number.
+.TP
+.B quic\-size: \fI<size in bytes>
+Maximum number of bytes for all QUIC buffers and data combined. Default is 8
+megabytes. A plain number is in bytes, append 'k', 'm' or 'g' for kilobytes,
+megabytes or gigabytes (1024*1024 bytes in a megabyte). New connections receive
+connection refused when the limit is exceeded. New streams are reset when the
+limit is exceeded.
+.TP
.B use\-systemd: \fI<yes or no>
Enable or disable systemd socket activation.
Default is no.
@@ -652,19 +750,25 @@ When at the limit, further connections are accepted but closed immediately.
This option is experimental at this time.
.TP
.B access\-control: \fI<IP netblock> <action>
+Specify treatment of incoming queries from their originating IP address.
+Queries can be allowed to have access to this server that gives DNS
+answers, or refused, with other actions possible. The IP address range
+can be specified as a netblock, it is possible to give the statement
+several times in order to specify the treatment of different netblocks.
+.IP
The netblock is given as an IP4 or IP6 address with /size appended for a
classless network block. The action can be \fIdeny\fR, \fIrefuse\fR,
-\fIallow\fR, \fIallow_setrd\fR, \fIallow_snoop\fR, \fIdeny_non_local\fR or
-\fIrefuse_non_local\fR.
-The most specific netblock match is used, if none match \fIdeny\fR is used.
+\fIallow\fR, \fIallow_setrd\fR, \fIallow_snoop\fR, \fIallow_cookie\fR,
+\fIdeny_non_local\fR or \fIrefuse_non_local\fR.
+The most specific netblock match is used, if none match \fIrefuse\fR is used.
The order of the access\-control statements therefore does not matter.
.IP
-The action \fIdeny\fR stops queries from hosts from that netblock.
+The \fIdeny\fR action stops queries from hosts from that netblock.
.IP
-The action \fIrefuse\fR stops queries too, but sends a DNS rcode REFUSED
+The \fIrefuse\fR action stops queries too, but sends a DNS rcode REFUSED
error message back.
.IP
-The action \fIallow\fR gives access to clients from that netblock.
+The \fIallow\fR action gives access to clients from that netblock.
It gives only access for recursion clients (which is
what almost all clients need). Nonrecursive queries are refused.
.IP
@@ -684,14 +788,27 @@ may be useful if another DNS server must forward requests for specific
zones to a resolver DNS server, but only supports stub domains and
sends queries to the resolver DNS server with the RD bit cleared.
.IP
-The action \fIallow_snoop\fR gives nonrecursive access too. This give
+The \fIallow_snoop\fR action gives nonrecursive access too. This give
both recursive and non recursive access. The name \fIallow_snoop\fR refers
to cache snooping, a technique to use nonrecursive queries to examine
the cache contents (for malicious acts). However, nonrecursive queries can
also be a valuable debugging tool (when you want to examine the cache
contents). In that case use \fIallow_snoop\fR for your administration host.
.IP
-By default only localhost is \fIallow\fRed, the rest is \fIrefuse\fRd.
+The \fIallow_cookie\fR action allows access only to UDP queries that contain a
+valid DNS Cookie as specified in RFC 7873 and RFC 9018, when the
+\fBanswer\-cookie\fR option is enabled.
+UDP queries containing only a DNS Client Cookie and no Server Cookie, or an
+invalid DNS Cookie, will receive a BADCOOKIE response including a newly
+generated DNS Cookie, allowing clients to retry with that DNS Cookie.
+The \fIallow_cookie\fR action will also accept requests over stateful
+transports, regardless of the presence of an DNS Cookie and regardless of the
+\fBanswer\-cookie\fR setting.
+UDP queries without a DNS Cookie receive REFUSED responses with the TC flag set,
+that may trigger fall back to TCP for those clients.
+.IP
+By default only localhost (the 127.0.0.0/8 IP netblock, not the loopback
+interface) is implicitly \fIallow\fRed, the rest is \fIrefuse\fRd.
The default is \fIrefuse\fRd, because that is protocol\-friendly. The DNS
protocol is not designed to handle dropped packets due to policy, and
dropping may result in (possibly excessive) retried queries.
@@ -722,6 +839,50 @@ Set redirect data for particular tag for given access control element.
.B access\-control\-view: \fI<IP netblock> <view name>
Set view for given access control element.
.TP
+.B interface\-action: \fI<ip address or interface name [@port]> <action>
+Similar to \fBaccess\-control:\fR but for interfaces.
+.IP
+The action is the same as the ones defined under \fBaccess\-control:\fR.
+Interfaces are \fIrefuse\fRd by default.
+By default only localhost (the 127.0.0.0/8 IP netblock, not the loopback
+interface) is implicitly \fIallow\fRed through the default
+\fBaccess\-control:\fR behavior.
+This also means that any attempt to use the \fBinterface-*:\fR options for the
+loopback interface will not work as they will be overridden by the implicit
+default "\fBaccess\-control:\fR 127.0.0.0/8 allow" option.
+.IP
+Note that the interface needs to be already specified with \fBinterface:\fR
+and that any \fBaccess-control*:\fR setting overrides all \fBinterface-*:\fR
+settings for targeted clients.
+.TP
+.B interface\-tag: \fI<ip address or interface name [@port]> <"list of tags">
+Similar to \fBaccess\-control-tag:\fR but for interfaces.
+.IP
+Note that the interface needs to be already specified with \fBinterface:\fR
+and that any \fBaccess-control*:\fR setting overrides all \fBinterface-*:\fR
+settings for targeted clients.
+.TP
+.B interface\-tag\-action: \fI<ip address or interface name [@port]> <tag> <action>
+Similar to \fBaccess\-control-tag-action:\fR but for interfaces.
+.IP
+Note that the interface needs to be already specified with \fBinterface:\fR
+and that any \fBaccess-control*:\fR setting overrides all \fBinterface-*:\fR
+settings for targeted clients.
+.TP
+.B interface\-tag\-data: \fI<ip address or interface name [@port]> <tag> <"resource record string">
+Similar to \fBaccess\-control-tag-data:\fR but for interfaces.
+.IP
+Note that the interface needs to be already specified with \fBinterface:\fR
+and that any \fBaccess-control*:\fR setting overrides all \fBinterface-*:\fR
+settings for targeted clients.
+.TP
+.B interface\-view: \fI<ip address or interface name [@port]> <view name>
+Similar to \fBaccess\-control-view:\fR but for interfaces.
+.IP
+Note that the interface needs to be already specified with \fBinterface:\fR
+and that any \fBaccess-control*:\fR setting overrides all \fBinterface-*:\fR
+settings for targeted clients.
+.TP
.B chroot: \fI<directory>
If chroot is enabled, you should pass the configfile (from the
commandline) as a full path from the original root. After the
@@ -745,13 +906,12 @@ outside of the chroot directory.
Additionally, Unbound may need to access /dev/urandom (for entropy)
from inside the chroot.
.IP
-If given a chroot is done to the given directory. By default chroot is
-enabled and the default is "@UNBOUND_CHROOT_DIR@". If you give "" no
-chroot is performed.
+If given a chroot is done to the given directory. The chroot is by default
+set to "/var/unbound". If you give "" no chroot is performed.
.TP
.B username: \fI<name>
If given, after binding the port the user privileges are dropped. Default is
-"@UNBOUND_USERNAME@". If you give username: "" no user change is performed.
+"unbound". If you give username: "" no user change is performed.
.IP
If this user is not capable of binding the
port, reloads (by signal HUP) will still retain the opened ports.
@@ -759,7 +919,7 @@ If you change the port number in the config file, and that new port number
requires privileges, then a reload will fail; a restart is needed.
.TP
.B directory: \fI<directory>
-Sets the working directory for the program. Default is "@UNBOUND_RUN_DIR@".
+Sets the working directory for the program. Default is "/var/unbound".
On Windows the string "%EXECUTABLE%" tries to change to the directory
that unbound.exe resides in.
If you give a server: directory: dir before include: file statements
@@ -794,6 +954,10 @@ Sets logfile lines to use a timestamp in UTC ascii. Default is no, which
prints the seconds since 1970 in brackets. No effect if using syslog, in
that case syslog formats the timestamp printed into the log files.
.TP
+.B log\-time\-iso:\fR <yes or no>
+Log time in ISO8601 format, if \fBlog\-time\-ascii:\fR yes is also set.
+Default is no.
+.TP
.B log\-queries: \fI<yes or no>
Prints one line per query to the log, with the log timestamp and IP address,
name, type and class. Default is no. Note that it takes time to print these
@@ -812,6 +976,11 @@ Prints the word 'query' and 'reply' with log\-queries and log\-replies.
This makes filtering logs easier. The default is off (for backwards
compatibility).
.TP
+.B log\-destaddr: \fI<yes or no>
+Prints the destination address, port and type in the log\-replies output.
+This disambiguates what type of traffic, eg. udp or tcp, and to what local
+port the traffic was sent to.
+.TP
.B log\-local\-actions: \fI<yes or no>
Print log lines to inform about local zone actions. These lines are like the
local\-zone type inform prints out, but they are also printed for the other
@@ -823,14 +992,14 @@ This is separate from the verbosity debug logs, much smaller, and printed
at the error level, not the info level of debug info from verbosity.
.TP
.B pidfile: \fI<filename>
-The process id is written to the file. Default is "@UNBOUND_PIDFILE@".
+The process id is written to the file. Default is "/var/unbound/unbound.pid".
So,
.nf
-kill \-HUP `cat @UNBOUND_PIDFILE@`
+kill \-HUP `cat /var/unbound/unbound.pid`
.fi
triggers a reload,
.nf
-kill \-TERM `cat @UNBOUND_PIDFILE@`
+kill \-TERM `cat /var/unbound/unbound.pid`
.fi
gracefully terminates.
.TP
@@ -890,17 +1059,22 @@ closer to that of BIND 9, while setting "\-1 \-1 \-1 \-1 \-1" gives behaviour
rumoured to be closer to that of BIND 8.
.TP
.B harden\-short\-bufsize: \fI<yes or no>
-Very small EDNS buffer sizes from queries are ignored. Default is on, as
+Very small EDNS buffer sizes from queries are ignored. Default is yes, as
described in the standard.
.TP
.B harden\-large\-queries: \fI<yes or no>
-Very large queries are ignored. Default is off, since it is legal protocol
+Very large queries are ignored. Default is no, since it is legal protocol
wise to send these, and could be necessary for operation if TSIG or EDNS
payload is very large.
.TP
.B harden\-glue: \fI<yes or no>
Will trust glue only if it is within the servers authority. Default is yes.
.TP
+.B harden\-unverified\-glue: \fI<yes or no>
+Will trust only in-zone glue. Will try to resolve all out of zone
+(\fI<unverfied>) glue. Will fallback to the original glue if unable to resolve.
+Default is no.
+.TP
.B harden\-dnssec\-stripped: \fI<yes or no>
Require DNSSEC data for trust\-anchored zones, if such data is absent,
the zone becomes bogus. If turned off, and no DNSSEC data is received
@@ -936,10 +1110,29 @@ to increase the max depth that is checked to.
.TP
.B harden\-algo\-downgrade: \fI<yes or no>
Harden against algorithm downgrade when multiple algorithms are
-advertised in the DS record. If no, allows the weakest algorithm to
-validate the zone. Default is no. Zone signers must produce zones
-that allow this feature to work, but sometimes they do not, and turning
-this option off avoids that validation failure.
+advertised in the DS record.
+This works by first choosing only the strongest DS digest type as per RFC 4509
+(Unbound treats the highest algorithm as the strongest) and then
+expecting signatures from all the advertised signing algorithms from the chosen
+DS(es) to be present.
+If no, allows any one supported algorithm to validate the zone, even if other advertised algorithms are broken.
+Default is no.
+RFC 6840 mandates that zone signers must produce zones signed with all
+advertised algorithms, but sometimes they do not.
+RFC 6840 also clarifies that this requirement is not for validators and
+validators should accept any single valid path.
+It should thus be explicitly noted that this option violates RFC 6840 for
+DNSSEC validation and should only be used to perform a signature
+completeness test to support troubleshooting.
+Using this option may break DNSSEC resolution with non-RFC6840-conforming
+signers and/or in multi-signer configurations that don't send all the
+advertised signatures.
+.TP
+.B harden\-unknown\-additional: \fI<yes or no>
+Harden against unknown records in the authority section and additional
+section. Default is no. If no, such records are copied from the upstream
+and presented to the client together with the answer. If yes, it could
+hamper future protocol developments that want to add records.
.TP
.B use\-caps\-for\-id: \fI<yes or no>
Use 0x20\-encoded random bits in the query to foil spoof attempts.
@@ -954,7 +1147,7 @@ queries. For domains that do not support 0x20 and also fail with fallback
because they keep sending different answers, like some load balancers.
Can be given multiple times, for different domains.
.TP
-.B caps\-whitelist: \fI<yes or no>
+.B caps\-whitelist: \fI<domain>
Alternate syntax for \fBcaps\-exempt\fR.
.TP
.B qname\-minimisation: \fI<yes or no>
@@ -1018,10 +1211,11 @@ IP6 ::1 and IP4 127.0.0.1/8. If no, then localhost can be used to send
queries to. Default is yes.
.TP
.B prefetch: \fI<yes or no>
-If yes, message cache elements are prefetched before they expire to
-keep the cache up to date. Default is no. Turning it on gives about
-10 percent more traffic and load on the machine, but popular items do
-not expire from the cache.
+If yes, cache hits on message cache elements that are on their last 10 percent
+of their TTL value trigger a prefetch to keep the cache up to date.
+Default is no.
+Turning it on gives about 10 percent more traffic and load on the machine, but
+popular items do not expire from the cache.
.TP
.B prefetch\-key: \fI<yes or no>
If yes, fetch the DNSKEYs earlier in the validation process, when a DS
@@ -1041,12 +1235,13 @@ from the query ID, for speed and thread safety). Default is yes.
.B minimal-responses: \fI<yes or no>
If yes, Unbound does not insert authority/additional sections into response
messages when those sections are not required. This reduces response
-size significantly, and may avoid TCP fallback for some responses.
-This may cause a slight speedup. The default is yes, even though the DNS
+size significantly, and may avoid TCP fallback for some responses which may
+cause a slight speedup. The default is yes, even though the DNS
protocol RFCs mandate these sections, and the additional content could
-be of use and save roundtrips for clients. Because they are not used,
-and the saved roundtrips are easier saved with prefetch, whilst this is
-faster.
+save roundtrips for clients that use the additional content.
+However these sections are hardly used by clients.
+Enabling prefetch can benefit clients that need the additional content
+by trying to keep that content fresh in the cache.
.TP
.B disable-dnssec-lame-check: \fI<yes or no>
If true, disables the DNSSEC lameness check in the iterator. This check
@@ -1069,9 +1264,6 @@ Adding \fIrespip\fR to the front will cause RPZ processing to be done on
all queries.
The default is "\fIvalidator iterator\fR".
.IP
-When the server is built with
-EDNS client subnet support the default is "\fIsubnetcache validator
-iterator\fR".
Most modules that need to be listed here have to be listed at the beginning
of the line. The subnetcachedb module has to be listed just before
the iterator.
@@ -1195,17 +1387,33 @@ servers that set the CD flag but cannot validate DNSSEC themselves are
the clients, and then Unbound provides them with DNSSEC protection.
The default value is "no".
.TP
+.B disable\-edns\-do: \fI<yes or no>
+Disable the EDNS DO flag in upstream requests.
+It breaks DNSSEC validation for Unbound's clients.
+This results in the upstream name servers to not include DNSSEC records in
+their replies and could be helpful for devices that cannot handle DNSSEC
+information.
+When the option is enabled, clients that set the DO flag receive no EDNS
+record in the response to indicate the lack of support to them.
+If this option is enabled but Unbound is already configured for DNSSEC
+validation (i.e., the validator module is enabled; default) this option is
+implicitly turned off with a warning as to not break DNSSEC validation in
+Unbound.
+Default is no.
+.TP
.B serve\-expired: \fI<yes or no>
If enabled, Unbound attempts to serve old responses from cache with a
-TTL of \fBserve\-expired\-reply\-ttl\fR in the response without waiting for the
-actual resolution to finish. The actual resolution answer ends up in the cache
-later on. Default is "no".
+TTL of \fBserve\-expired\-reply\-ttl\fR in the response.
+By default the expired answer will be used after a resolution attempt errored
+out or is taking more than serve\-expired\-client\-timeout to resolve.
+Default is "no".
.TP
.B serve\-expired\-ttl: \fI<seconds>
-Limit serving of expired responses to configured seconds after expiration. 0
-disables the limit. This option only applies when \fBserve\-expired\fR is
-enabled. A suggested value per RFC 8767 is between
-86400 (1 day) and 259200 (3 days). The default is 0.
+Limit serving of expired responses to configured seconds after expiration.
+0 disables the limit.
+This option only applies when \fBserve\-expired\fR is enabled.
+A suggested value per RFC 8767 is between 86400 (1 day) and 259200 (3 days).
+The default is 86400.
.TP
.B serve\-expired\-ttl\-reset: \fI<yes or no>
Set the TTL of expired records to the \fBserve\-expired\-ttl\fR value after a
@@ -1219,12 +1427,14 @@ TTL value to use when replying with expired data. If
use 30 as the value (RFC 8767). The default is 30.
.TP
.B serve\-expired\-client\-timeout: \fI<msec>
-Time in milliseconds before replying to the client with expired data. This
-essentially enables the serve-stale behavior as specified in
+Time in milliseconds before replying to the client with expired data.
+This essentially enables the serve-stale behavior as specified in
RFC 8767 that first tries to resolve before immediately
-responding with expired data. A recommended value per
-RFC 8767 is 1800. Setting this to 0 will disable this
-behavior. Default is 0.
+responding with expired data.
+Setting this to 0 will disable this behavior and instead serve the expired
+record immediately from the cache before attempting to refresh it via
+resolution.
+Default is 1800.
.TP
.B serve\-original\-ttl: \fI<yes or no>
If enabled, Unbound will always return the original TTL as received from
@@ -1313,14 +1523,24 @@ address space are not validated. This is usually required whenever
Configure a local zone. The type determines the answer to give if
there is no match from local\-data. The types are deny, refuse, static,
transparent, redirect, nodefault, typetransparent, inform, inform_deny,
-inform_redirect, always_transparent, always_refuse, always_nxdomain, always_null, noview,
-and are explained below. After that the default settings are listed. Use
-local\-data: to enter data into the local zone. Answers for local zones
-are authoritative DNS answers. By default the zones are class IN.
+inform_redirect, always_transparent, block_a, always_refuse, always_nxdomain,
+always_null, noview, and are explained below. After that the default settings
+are listed. Use local\-data: to enter data into the local zone. Answers for
+local zones are authoritative DNS answers. By default the zones are class IN.
.IP
If you need more complicated authoritative data, with referrals, wildcards,
CNAME/DNAME support, or DNSSEC authoritative service, setup a stub\-zone for
-it as detailed in the stub zone section below.
+it as detailed in the stub zone section below. A stub\-zone can be used to
+have unbound send queries to another server, an authoritative server, to
+fetch the information. With a forward\-zone, unbound sends queries to a server
+that is a recursive server to fetch the information. With an auth\-zone a
+zone can be loaded from file and used, it can be used like a local\-zone
+for users downstream, or the auth\-zone information can be used to fetch
+information from when resolving like it is an upstream server. The
+forward\-zone and auth\-zone options are described in their sections below.
+If you want to perform filtering of the information that the users can fetch,
+the local\-zone and local\-data statements allow for this, but also the
+rpz functionality can be used, described in the RPZ section.
.TP 10
\h'5'\fIdeny\fR
Do not send an answer, drop the query.
@@ -1381,6 +1601,12 @@ Ie. answer queries with fixed data and also log the machines that ask.
\h'5'\fIalways_transparent\fR
Like transparent, but ignores local data and resolves normally.
.TP 10
+\h'5'\fIblock_a\fR
+Like transparent, but ignores local data and resolves normally all query
+types excluding A. For A queries it unconditionally returns NODATA.
+Useful in cases when there is a need to explicitly force all apps to use
+IPv6 protocol and avoid any queries to IPv4.
+.TP 10
\h'5'\fIalways_refuse\fR
Like refuse, but ignores local data and refuses the query.
.TP 10
@@ -1413,6 +1639,7 @@ given zone. Use \fInodefault\fR if you use exactly that zone, if you want to
use a subzone, use \fItransparent\fR.
.P
The default zones are localhost, reverse 127.0.0.1 and ::1, the home.arpa,
+the resolver.arpa, the service.arpa,
the onion, test, invalid and the AS112 zones. The AS112 zones are reverse
DNS zones for private use and reserved IP addresses for which the servers
on the internet cannot provide correct answers. They are configured by
@@ -1468,6 +1695,24 @@ local\-data: "home.arpa. 10800 IN
SOA localhost. nobody.invalid. 1 3600 1200 604800 10800"
.fi
.TP 10
+\h'5'\fIresolver.arpa (RFC 9462)\fR
+Default content:
+.nf
+local\-zone: "resolver.arpa." static
+local\-data: "resolver.arpa. 10800 IN NS localhost."
+local\-data: "resolver.arpa. 10800 IN
+ SOA localhost. nobody.invalid. 1 3600 1200 604800 10800"
+.fi
+.TP 10
+\h'5'\fIservice.arpa (draft-ietf-dnssd-srp-25)\fR
+Default content:
+.nf
+local\-zone: "service.arpa." static
+local\-data: "service.arpa. 10800 IN NS localhost."
+local\-data: "service.arpa. 10800 IN
+ SOA localhost. nobody.invalid. 1 3600 1200 604800 10800"
+.fi
+.TP 10
\h'5'\fIonion (RFC 7686)\fR
Default content:
.nf
@@ -1591,7 +1836,7 @@ This specifies the action data for \fIresponse-ip\fR with action being
to redirect as specified by "\fIresource record string\fR". "Resource
record string" is similar to that of \fIaccess-control-tag-action\fR,
but it must be of either AAAA, A or CNAME types.
-If the IP-netblock is an IPv6/IPV4 prefix, the record
+If the IP-netblock is an IPv6/IPv4 prefix, the record
must be AAAA/A respectively, unless it is a CNAME (which can be used
for both versions of IP netblocks). If it is CNAME there must not be
more than one \fIresponse-ip-data\fR for the same IP-netblock.
@@ -1697,11 +1942,30 @@ A value of 0 will disable ratelimiting for domain names that end in this name.
.TP 5
.B ip\-ratelimit: \fI<number or 0>
Enable global ratelimiting of queries accepted per IP address.
-If 0, the default, it is disabled. This option is experimental at this time.
+This option is experimental at this time.
The ratelimit is in queries per second that are allowed. More queries are
completely dropped and will not receive a reply, SERVFAIL or otherwise.
IP ratelimiting happens before looking in the cache. This may be useful for
mitigating amplification attacks.
+Clients with a valid DNS Cookie will bypass the ratelimit.
+If a ratelimit for such clients is still needed, \fBip\-ratelimit\-cookie\fR
+can be used instead.
+Default is 0 (disabled).
+.TP 5
+.B ip\-ratelimit\-cookie: \fI<number or 0>
+Enable global ratelimiting of queries accepted per IP address with a valid DNS
+Cookie.
+This option is experimental at this time.
+The ratelimit is in queries per second that are allowed.
+More queries are completely dropped and will not receive a reply, SERVFAIL or
+otherwise.
+IP ratelimiting happens before looking in the cache.
+This option could be useful in combination with \fIallow_cookie\fR in an
+attempt to mitigate other amplification attacks than UDP reflections (e.g.,
+attacks targeting Unbound itself) which are already handled with DNS Cookies.
+If used, the value is suggested to be higher than \fBip\-ratelimit\fR e.g.,
+tenfold.
+Default is 0 (disabled).
.TP 5
.B ip\-ratelimit\-size: \fI<memory size>
Give the size of the data structure in which the current ongoing rates are
@@ -1733,9 +1997,44 @@ set ip\-ratelimit to a suspicious rate to aggressively limit unusually high
traffic. Default is off.
.TP 5
.B outbound\-msg\-retry: \fI<number>
-The number of retries Unbound will do in case of a non positive response is
-received. If a forward nameserver is used, this is the number of retries per
-forward nameserver in case of throwaway response.
+The number of retries, per upstream nameserver in a delegation, that Unbound
+will attempt in case a throwaway response is received.
+No response (timeout) contributes to the retry counter.
+If a forward/stub zone is used, this is the number of retries per nameserver in
+the zone.
+Default is 5.
+.TP 5
+.B max\-sent\-count: \fI<number>
+Hard limit on the number of outgoing queries Unbound will make while resolving
+a name, making sure large NS sets do not loop.
+Results in SERVFAIL when reached.
+It resets on query restarts (e.g., CNAME) and referrals.
+Default is 32.
+.TP 5
+.B max\-query\-restarts: \fI<number>
+Hard limit on the number of times Unbound is allowed to restart a query upon
+encountering a CNAME record.
+Results in SERVFAIL when reached.
+Changing this value needs caution as it can allow long CNAME chains to be
+accepted, where Unbound needs to verify (resolve) each link individually.
+Default is 11.
+.TP 5
+.B iter\-scrub\-ns: \fI<number>
+Limit on the number of NS records allowed in an rrset of type NS, from the
+iterator scrubber. This protects the internals of the resolver from overly
+large NS sets. Default is 20.
+.TP 5
+.B iter\-scrub\-cname: \fI<number>
+Limit on the number of CNAME, DNAME records in an answer, from the iterator
+scrubber. This protects the internals of the resolver from overly long
+indirection chains. Clips off the remainder of the reply packet at that point.
+Default is 11.
+.TP 5
+.B max\-global\-quota: \fI<number>
+Limit on the number of upstream queries sent out for an incoming query and
+its subqueries from recursion. It is not reset during the resolution. When
+it is exceeded the query is failed and the lookup process stops.
+Default is 200.
.TP 5
.B fast\-server\-permil: \fI<number>
Specify how many times out of 1000 to pick from the set of fastest servers.
@@ -1752,6 +2051,32 @@ Set the number of servers that should be used for fast server selection. Only
use the fastest specified number of servers with the fast\-server\-permil
option, that turns this on or off. The default is to use the fastest 3 servers.
.TP 5
+.B answer\-cookie: \fI<yes or no>
+If enabled, Unbound will answer to requests containing DNS Cookies as
+specified in RFC 7873 and RFC 9018.
+Default is no.
+.TP 5
+.B cookie\-secret: \fI<128 bit hex string>
+Server's secret for DNS Cookie generation.
+Useful to explicitly set for servers in an anycast deployment that need to
+share the secret in order to verify each other's Server Cookies.
+An example hex string would be "000102030405060708090a0b0c0d0e0f".
+Default is a 128 bits random secret generated at startup time.
+This option is ignored if a \fBcookie\-secret\-file\fR is
+present. In that case the secrets from that file are used in DNS Cookie
+calculations.
+.TP 5
+.B cookie\-secret\-file: \fI<filename>
+File from which the secrets are read used in DNS Cookie calculations. When this
+file exists, the secrets in this file are used and the secret specified by the
+\fBcookie-secret\fR option is ignored.
+Enable it by setting a filename, like "/usr/local/etc/unbound_cookiesecrets.txt".
+The content of this file must be manipulated with the \fBadd_cookie_secret\fR,
+\fBdrop_cookie_secret\fR and \fBactivate_cookie_secret\fR commands to the
+\fIunbound\-control\fR(8) tool. Please see that manpage on how to perform a
+safe cookie secret rollover.
+Default is "" (disabled).
+.TP 5
.B edns\-client\-string: \fI<IP netblock> <string>
Include an EDNS0 option containing configured ascii string in queries with
destination address matching the configured IP netblock. This configuration
@@ -1761,6 +2086,34 @@ option can be used multiple times. The most specific match will be used.
EDNS0 option code for the \fIedns\-client\-string\fR option, from 0 to 65535.
A value from the `Reserved for Local/Experimental` range (65001-65534) should
be used. Default is 65001.
+.TP 5
+.B ede: \fI<yes or no>
+If enabled, Unbound will respond with Extended DNS Error codes (RFC8914).
+These EDEs provide additional information with a response mainly for, but not
+limited to, DNS and DNSSEC errors.
+
+When the \fBval-log-level\fR option is also set to \fB2\fR, responses with
+Extended DNS Errors concerning DNSSEC failures will also contain a descriptive
+text message about the reason for the failure.
+Default is "no".
+.TP 5
+.B ede\-serve\-expired: \fI<yes or no>
+If enabled, Unbound will attach an Extended DNS Error (RFC8914) Code 3 - Stale
+Answer as EDNS0 option to the expired response.
+The \fBede\fR option needs to be enabled as well for this to work.
+Default is "no".
+.TP 5
+.B dns\-error\-reporting: \fI<yes or no>
+If enabled, Unbound will send DNS Error Reports (RFC9567).
+The name servers need to express support by attaching the Report-Channel EDNS0
+option on their replies specifying the reporting agent for the zone.
+Any errors encountered during resolution that would result in Unbound
+generating an Extended DNS Error (RFC8914) will be reported to the zone's
+reporting agent.
+The \fBede\fR option does not need to be enabled for this to work.
+It is advised that the \fBqname\-minimisation\fR option is also enabled to
+increase privacy on the outgoing reports.
+Default is "no".
.SS "Remote Control Options"
In the
.B remote\-control:
@@ -1776,15 +2129,17 @@ section for options. To setup the correct self\-signed certificates use the
The option is used to enable remote control, default is "no".
If turned off, the server does not listen for control commands.
.TP 5
-.B control\-interface: \fI<ip address or path>
+.B control\-interface: \fI<ip address or interface name or path>
Give IPv4 or IPv6 addresses or local socket path to listen on for
control commands.
+If an interface name is used instead of an ip address, the list of ip addresses
+on that interface are used.
By default localhost (127.0.0.1 and ::1) is listened to.
Use 0.0.0.0 and ::0 to listen to all interfaces.
If you change this and permissions have been dropped, you must restart
the server for the change to take effect.
.IP
-If you set it to an absolute path, a local socket is used. The local socket
+If you set it to an absolute path, a unix domain socket is used. This socket
does not use the certificates and keys, so those files need not be present.
To restrict access, Unbound sets permissions on the file to the user and
group that is configured, the access bits are set to allow the group members
@@ -1968,13 +2323,32 @@ useful when you want immediate changes to be visible.
Authority zones are configured with \fBauth\-zone:\fR, and each one must
have a \fBname:\fR. There can be multiple ones, by listing multiple auth\-zone clauses, each with a different name, pertaining to that part of the namespace.
The authority zone with the name closest to the name looked up is used.
-Authority zones are processed after \fBlocal\-zones\fR and before
-cache (\fBfor\-downstream:\fR \fIyes\fR), and when used in this manner
-make Unbound respond like an authority server. Authority zones are also
-processed after cache, just before going to the network to fetch
-information for recursion (\fBfor\-upstream:\fR \fIyes\fR), and when used
-in this manner provide a local copy of an authority server that speeds up
-lookups of that data.
+Authority zones can be processed on two distinct, non-exclusive, configurable
+stages.
+.LP
+With \fBfor\-downstream:\fR \fIyes\fR (default), authority zones are processed
+after \fBlocal\-zones\fR and before cache.
+When used in this manner, Unbound responds like an authority server with no
+further processing other than returning an answer from the zone contents.
+A notable example, in this case, is CNAME records which are returned verbatim
+to downstream clients without further resolution.
+.LP
+With \fBfor\-upstream:\fR \fIyes\fR (default), authority zones are processed
+after the cache lookup, just before going to the network to fetch
+information for recursion.
+When used in this manner they provide a local copy of an authority server
+that speeds up lookups for that data during resolving.
+.LP
+If both options are enabled (default), client queries for an authority zone are
+answered authoritatively from Unbound, while internal queries that require data
+from the authority zone consult the local zone data instead of going to the
+network.
+.LP
+An interesting configuration is \fBfor\-downstream:\fR \fIno\fR,
+\fBfor\-upstream:\fR \fIyes\fR that allows for hyperlocal behavior where both
+client and internal queries consult the local zone data while resolving.
+In this case, the aforementioned CNAME example will result in a thoroughly
+resolved answer.
.LP
Authority zones can be read from zonefile. And can be kept updated via
AXFR and IXFR. After update the zonefile is rewritten. The update mechanism
@@ -2027,8 +2401,8 @@ With allow\-notify you can specify additional sources of notifies.
When notified, the server attempts to first probe and then zone transfer.
If the notify is from a primary, it first attempts that primary. Otherwise
other primaries are attempted. If there are no primaries, but only urls, the
-file is downloaded when notified. The primaries from primary: statements are
-allowed notify by default.
+file is downloaded when notified. The primaries from primary: and url:
+statements are allowed notify by default.
.TP
.B fallback\-enabled: \fI<yes or no>
Default no. If enabled, Unbound falls back to querying the internet as
@@ -2151,8 +2525,8 @@ The dynamic library file to load. Repeat this option for every dynlib module
instance added to the \fBmodule\-config:\fR option.
.SS "DNS64 Module Options"
.LP
-The dns64 module must be configured in the \fBmodule\-config:\fR "dns64
-validator iterator" directive and be compiled into the daemon to be
+The dns64 module must be configured in the \fBmodule\-config:\fR directive
+e.g., "dns64 validator iterator" and be compiled into the daemon to be
enabled. These settings go in the \fBserver:\fR section.
.TP
.B dns64\-prefix: \fI<IPv6 prefix>\fR
@@ -2168,6 +2542,21 @@ List domain for which the AAAA records are ignored and the A record is
used by dns64 processing instead. Can be entered multiple times, list a
new domain for which it applies, one per line. Applies also to names
underneath the name given.
+.SS "NAT64 Operation"
+.LP
+NAT64 operation allows using a NAT64 prefix for outbound requests to IPv4-only
+servers. It is controlled by two options in the \fBserver:\fR section:
+.TP
+.B do\-nat64: \fI<yes or no>\fR
+Use NAT64 to reach IPv4-only servers.
+Consider also enabling \fBprefer\-ip6\fR to prefer native IPv6 connections to
+nameservers.
+Default no.
+.TP
+.B nat64\-prefix: \fI<IPv6 prefix>\fR
+Use a specific NAT64 prefix to reach IPv4-only servers. Defaults to using
+the prefix configured in \fBdns64\-prefix\fR, which in turn defaults to
+64:ff9b::/96. The prefix length must be one of /32, /40, /48, /56, /64 or /96.
.SS "DNSCrypt Options"
.LP
The
@@ -2237,8 +2626,8 @@ in the dnscrypt nonce cache. Close to the number of cpus is
a fairly good setting.
.SS "EDNS Client Subnet Module Options"
.LP
-The ECS module must be configured in the \fBmodule\-config:\fR "subnetcache
-validator iterator" directive and be compiled into the daemon to be
+The ECS module must be configured in the \fBmodule\-config:\fR directive e.g.,
+"subnetcache validator iterator" and be compiled into the daemon to be
enabled. These settings go in the \fBserver:\fR section.
.LP
If the destination address is allowed in the configuration Unbound will add the
@@ -2258,6 +2647,18 @@ The maximum size of the ECS cache is controlled by 'msg-cache-size' in the
configuration file. On top of that, for each query only 100 different subnets
are allowed to be stored for each address family. Exceeding that number, older
entries will be purged from cache.
+.LP
+Note that due to the nature of how EDNS Client Subnet works, by segregating the
+client IP space in order to try and have tailored responses for prefixes of
+unknown sizes, resolution and cache response performance are impacted as a
+result.
+Usage of the subnetcache module should only be enabled in installations that
+require such functionality where the resolver and the clients belong to
+different networks.
+An example of that is an open resolver installation.
+.LP
+This module does not interact with the \fBserve\-expired*\fR and
+\fBprefetch:\fR options.
.TP
.B send\-client\-subnet: \fI<IP address>\fR
Send client source address to this authority. Append /num to indicate a
@@ -2306,8 +2707,8 @@ Specifies the maximum number of subnets ECS answers kept in the ECS radix tree.
This number applies for each qname/qclass/qtype tuple. Defaults to 100.
.SS "Opportunistic IPsec Support Module Options"
.LP
-The IPsec module must be configured in the \fBmodule\-config:\fR "ipsecmod
-validator iterator" directive and be compiled into Unbound by using
+The IPsec module must be configured in the \fBmodule\-config:\fR directive
+e.g., "ipsecmod validator iterator" and be compiled into Unbound by using
\fB\-\-enable\-ipsecmod\fR to be enabled.
These settings go in the \fBserver:\fR section.
.LP
@@ -2372,12 +2773,12 @@ Allow the ipsecmod functionality for the domain so that the module logic will be
executed. Can be given multiple times, for different domains. If the option is
not specified, all domains are treated as being allowed (default).
.TP
-.B ipsecmod\-whitelist: \fI<yes or no>
+.B ipsecmod\-whitelist: \fI<domain>
Alternate syntax for \fBipsecmod\-allow\fR.
.SS "Cache DB Module Options"
.LP
-The Cache DB module must be configured in the \fBmodule\-config:\fR
-"validator cachedb iterator" directive and be compiled into the daemon
+The Cache DB module must be configured in the \fBmodule\-config:\fR directive
+e.g., "validator cachedb iterator" and be compiled into the daemon
with \fB\-\-enable\-cachedb\fR.
If this module is enabled and configured, the specified backend database
works as a second level cache:
@@ -2389,11 +2790,7 @@ If Unbound cannot even find an answer in the backend, it resolves the
query as usual, and stores the answer in the backend.
.P
This module interacts with the \fBserve\-expired\-*\fR options and will reply
-with expired data if Unbound is configured for that. Currently the use
-of \fBserve\-expired\-client\-timeout:\fR and
-\fBserve\-expired\-reply\-ttl:\fR is not consistent for data originating from
-the external cache as these will result in a reply with 0 TTL without trying to
-update the data first, ignoring the configured values.
+with expired data if Unbound is configured for that.
.P
If Unbound was built with
\fB\-\-with\-libhiredis\fR
@@ -2444,6 +2841,21 @@ operationally.
If the backend database is shared by multiple Unbound instances,
all instances must use the same secret seed.
This option defaults to "default".
+.TP
+.B cachedb-no-store: \fI<yes or no>\fR
+If the backend should be read from, but not written to. This makes this
+instance not store dns messages in the backend. But if data is available it
+is retrieved. The default is no.
+.TP
+.B cachedb-check-when-serve-expired: \fI<yes or no>\fR
+If enabled, the cachedb is checked before an expired response is returned.
+When \fBserve\-expired\fR is enabled, without \fBserve\-expired\-client\-timeout\fR, it then
+does not immediately respond with an expired response from cache, but instead
+first checks the cachedb for valid contents, and if so returns it. If the
+cachedb also has no valid contents, the serve expired response is sent.
+If also \fBserve\-expired\-client\-timeout\fR is enabled, the expired response
+is delayed until the timeout expires. Unless the lookup succeeds within the
+timeout. The default is yes.
.P
The following
.B cachedb
@@ -2460,6 +2872,16 @@ This option defaults to "127.0.0.1".
The TCP port number of the Redis server.
This option defaults to 6379.
.TP
+.B redis-server-path: \fI<unix socket path>\fR
+The unix socket path to connect to the Redis server. Off by default, and it
+can be set to "" to turn this off. Unix sockets may have better throughput
+than the IP address option.
+.TP
+.B redis-server-password: \fI"<password>"\fR
+The Redis AUTH password to use for the Redis server.
+Only relevant if Redis is configured for client password authorisation.
+Off by default, and it can be set to "" to turn this off.
+.TP
.B redis-timeout: \fI<msec>\fR
The period until when Unbound waits for a response from the Redis sever.
If this timeout expires Unbound closes the connection, treats it as
@@ -2467,6 +2889,16 @@ if the Redis server does not have the requested data, and will try to
re-establish a new connection later.
This option defaults to 100 milliseconds.
.TP
+.B redis-command-timeout: \fI<msec>\fR
+The timeout to use for Redis commands, in milliseconds.
+If 0, it uses the \fBredis\-timeout\fR value.
+The default is 0.
+.TP
+.B redis-connect-timeout: \fI<msec>\fR
+The timeout to use for Redis connection set up, in milliseconds.
+If 0, it uses the \fBredis\-timeout\fR value.
+The default is 0.
+.TP
.B redis-expire-records: \fI<yes or no>
If Redis record expiration is enabled. If yes, Unbound sets timeout for Redis
records so that Redis can evict keys that have expired automatically. If
@@ -2474,6 +2906,63 @@ Unbound is configured with \fBserve-expired\fR and \fBserve-expired-ttl\fR is 0,
this option is internally reverted to "no". Redis SETEX support is required
for this option (Redis >= 2.0.0).
This option defaults to no.
+.TP
+.B redis-logical-db: \fI<logical database index>
+The logical database in Redis to use.
+These are databases in the same Redis instance sharing the same configuration
+and persisted in the same RDB/AOF file.
+If unsure about using this option, Redis documentation
+(https://redis.io/commands/select/) suggests not to use a single Redis instance
+for multiple unrelated applications.
+The default database in Redis is 0 while other logical databases need to be
+explicitly SELECT'ed upon connecting.
+This option defaults to 0.
+.TP
+.B redis-replica-server-host: \fI<server address or name>\fR
+The IP (either v6 or v4) address or domain name of the Redis replica server.
+In general an IP address should be specified as otherwise Unbound will have to
+resolve the name of the server every time it establishes a connection
+to the server.
+This server is treated as a read-only replica server
+(https://redis.io/docs/management/replication/#read-only-replica).
+If specified, all Redis read commands will go to this replica server, while
+the write commands will go to the \fBredis-server-host\fR.
+This option defaults to "" (disabled).
+.TP
+.B redis-replica-server-port: \fI<port number>\fR
+The TCP port number of the Redis replica server.
+This option defaults to 6379.
+.TP
+.B redis-replica-server-path: \fI<unix socket path>\fR
+The unix socket path to connect to the Redis server. Off by default, and it
+can be set to "" to turn this off. Unix sockets may have better throughput
+than the IP address option.
+.TP
+.B redis-replica-server-password: \fI"<password>"\fR
+The Redis AUTH password to use for the Redis replica server.
+Only relevant if Redis is configured for client password authorisation.
+Off by default, and it can be set to "" to turn this off.
+.TP
+.B redis-replica-timeout: \fI<msec>\fR
+The period until when Unbound waits for a response from the Redis replica sever.
+If this timeout expires Unbound closes the connection, treats it as
+if the Redis replica server does not have the requested data, and will try to
+re-establish a new connection later.
+This option defaults to 100 milliseconds.
+.TP
+.B redis-replica-command-timeout: \fI<msec>\fR
+The timeout to use for Redis replica commands, in milliseconds.
+If 0, it uses the \fBredis\-replica\-timeout\fR value.
+The default is 0.
+.TP
+.B redis-replica-connect-timeout: \fI<msec>\fR
+The timeout to use for Redis replica connection set up, in milliseconds.
+If 0, it uses the \fBredis\-replica\-timeout\fR value.
+The default is 0.
+.TP
+.B redis-replica-logical-db: \fI<logical database index>
+Same as \fBredis-logical-db\fR but for the Redis replica server.
+This option defaults to 0.
.SS DNSTAP Logging Options
DNSTAP support, when compiled in by using \fB\-\-enable\-dnstap\fR, is enabled
in the \fBdnstap:\fR section.
@@ -2493,7 +2982,7 @@ yes.
.TP
.B dnstap-socket-path: \fI<file name>
Sets the unix socket file name for connecting to the server that is
-listening on that socket. Default is "@DNSTAP_SOCKET_PATH@".
+listening on that socket. Default is "".
.TP
.B dnstap-ip: \fI<IPaddress[@port]>
If "", the unix socket is used, if set with an IP address (IPv4 or IPv6)
@@ -2534,6 +3023,13 @@ Default is "".
The version to send with messages, if "" the package version is used.
Default is "".
.TP
+.B dnstap-sample-rate: \fI<number>
+The sample rate for log of messages, it logs only 1/N messages. With 0 it
+is disabled. Default is 0. This is useful in a high volume environment,
+where log functionality would otherwise not be reliable. For example 10
+would spend only 1/10th time on logging, and 100 would only spend a
+hundredth of the time on logging.
+.TP
.B dnstap-log-resolver-query-messages: \fI<yes or no>
Enable to log resolver query messages. Default is no.
These are messages from Unbound to upstream servers.
@@ -2558,9 +3054,11 @@ Enable to log forwarder response messages. Default is no.
.SS Response Policy Zone Options
.LP
Response Policy Zones are configured with \fBrpz:\fR, and each one must have a
-\fBname:\fR. There can be multiple ones, by listing multiple rpz clauses, each
-with a different name. RPZ clauses are applied in order of configuration. The
-\fBrespip\fR module needs to be added to the \fBmodule-config\fR, e.g.:
+\fBname:\fR. There can be multiple ones, by listing multiple RPZ clauses, each
+with a different name. RPZ clauses are applied in order of configuration and
+any match from an earlier RPZ zone will terminate the RPZ lookup. Note that a
+PASSTHRU action is still considered a match.
+The \fBrespip\fR module needs to be added to the \fBmodule-config\fR, e.g.:
\fBmodule-config: "respip validator iterator"\fR.
.P
QNAME, Response IP Address, nsdname, nsip and clientip triggers are supported.
@@ -2568,12 +3066,13 @@ Supported actions are: NXDOMAIN, NODATA, PASSTHRU, DROP, Local Data, tcp\-only
and drop. RPZ QNAME triggers are applied after \fBlocal\-zones\fR and
before \fBauth\-zones\fR.
.P
-The rpz zone is formatted with a SOA start record as usual. The items in
-the zone are entries, that specify what to act on (the trigger) and what to
-do (the action). The trigger to act on is recorded in the name, the action
-to do is recorded as the resource record. The names all end in the zone
-name, so you could type the trigger names without a trailing dot in the
-zonefile.
+The RPZ zone is a regular DNS zone formatted with a SOA start record as usual.
+The items in the zone are entries, that specify what to act on (the trigger)
+and what to do (the action).
+The trigger to act on is recorded in the name, the action to do is recorded as
+the resource record.
+The names all end in the zone name, so you could type the trigger names without
+a trailing dot in the zonefile.
.P
An example RPZ record, that answers example.com with NXDOMAIN
.nf
@@ -2642,8 +3141,8 @@ With allow\-notify you can specify additional sources of notifies.
When notified, the server attempts to first probe and then zone transfer.
If the notify is from a primary, it first attempts that primary. Otherwise
other primaries are attempted. If there are no primaries, but only urls, the
-file is downloaded when notified. The primaries from primary: statements are
-allowed notify by default.
+file is downloaded when notified. The primaries from primary: and url:
+statements are allowed notify by default.
.TP
.B zonefile: \fI<filename>
The filename where the zone is stored. If not given then no zonefile is used.
@@ -2673,7 +3172,7 @@ externally blocked. Default is no.
If enabled the zone is authoritatively answered for and queries for the RPZ
zone information are answered to downstream clients. This is useful for
monitoring scripts, that can then access the SOA information to check if
-the rpz information is up to date. Default is no.
+the RPZ information is up to date. Default is no.
.TP
.B tags: \fI<list of tags>
Limit the policies from this RPZ clause to clients with a matching tag. Tags
@@ -2714,18 +3213,18 @@ server:
.fi
.SH "FILES"
.TP
-.I @UNBOUND_RUN_DIR@
+.I /var/unbound
default Unbound working directory.
.TP
-.I @UNBOUND_CHROOT_DIR@
+.I /var/unbound
default
\fIchroot\fR(2)
location.
.TP
-.I @ub_conf_file@
+.I /var/unbound/unbound.conf
Unbound configuration file.
.TP
-.I @UNBOUND_PIDFILE@
+.I /var/unbound/unbound.pid
default Unbound pidfile with process ID of the running daemon.
.TP
.I unbound.log
diff --git a/contrib/unbound/doc/unbound.conf.5.in b/contrib/unbound/doc/unbound.conf.5.in
index 4484267dbc79..46af53802479 100644
--- a/contrib/unbound/doc/unbound.conf.5.in
+++ b/contrib/unbound/doc/unbound.conf.5.in
@@ -1,4 +1,4 @@
-.TH "unbound.conf" "5" "Apr 24, 2025" "NLnet Labs" "unbound 1.23.0"
+.TH "unbound.conf" "5" "Jul 16, 2025" "NLnet Labs" "unbound 1.23.1"
.\"
.\" unbound.conf.5 -- unbound.conf manual
.\"
diff --git a/contrib/unbound/edns-subnet/subnetmod.c b/contrib/unbound/edns-subnet/subnetmod.c
index ead720f34006..c5e215b8b684 100644
--- a/contrib/unbound/edns-subnet/subnetmod.c
+++ b/contrib/unbound/edns-subnet/subnetmod.c
@@ -51,6 +51,7 @@
#include "services/cache/dns.h"
#include "util/module.h"
#include "util/regional.h"
+#include "util/fptr_wlist.h"
#include "util/storage/slabhash.h"
#include "util/config_file.h"
#include "util/data/msgreply.h"
@@ -155,7 +156,8 @@ int ecs_whitelist_check(struct query_info* qinfo,
/* Cache by default, might be disabled after parsing EDNS option
* received from nameserver. */
- if(!iter_stub_fwd_no_cache(qstate, &qstate->qinfo, NULL, NULL, NULL, 0)) {
+ if(!iter_stub_fwd_no_cache(qstate, &qstate->qinfo, NULL, NULL, NULL, 0)
+ && sq->ecs_client_in.subnet_validdata) {
qstate->no_cache_store = 0;
}
@@ -522,6 +524,69 @@ common_prefix(uint8_t *a, uint8_t *b, uint8_t net)
return !memcmp(a, b, n) && ((net % 8) == 0 || a[n] == b[n]);
}
+/**
+ * Create sub request that looks up the query.
+ * @param qstate: query state
+ * @param sq: subnet qstate
+ * @return false on failure.
+ */
+static int
+generate_sub_request(struct module_qstate *qstate, struct subnet_qstate* sq)
+{
+ struct module_qstate* subq = NULL;
+ uint16_t qflags = 0; /* OPCODE QUERY, no flags */
+ int prime = 0;
+ int valrec = 0;
+ struct query_info qinf;
+ qinf.qname = qstate->qinfo.qname;
+ qinf.qname_len = qstate->qinfo.qname_len;
+ qinf.qtype = qstate->qinfo.qtype;
+ qinf.qclass = qstate->qinfo.qclass;
+ qinf.local_alias = NULL;
+
+ qflags |= BIT_RD;
+ if((qstate->query_flags & BIT_CD)!=0) {
+ qflags |= BIT_CD;
+ valrec = 1;
+ }
+
+ fptr_ok(fptr_whitelist_modenv_attach_sub(qstate->env->attach_sub));
+ if(!(*qstate->env->attach_sub)(qstate, &qinf, qflags, prime, valrec,
+ &subq)) {
+ return 0;
+ }
+ if(subq) {
+ /* It is possible to access the subquery module state. */
+ if(sq->ecs_client_in.subnet_source_mask == 0 &&
+ edns_opt_list_find(qstate->edns_opts_front_in,
+ qstate->env->cfg->client_subnet_opcode)) {
+ subq->no_cache_store = 1;
+ }
+ }
+ return 1;
+}
+
+/**
+ * Perform the query without subnet
+ * @param qstate: query state
+ * @param sq: subnet qstate
+ * @return module state
+ */
+static enum module_ext_state
+generate_lookup_without_subnet(struct module_qstate *qstate,
+ struct subnet_qstate* sq)
+{
+ verbose(VERB_ALGO, "subnetcache: make subquery to look up without subnet");
+ if(!generate_sub_request(qstate, sq)) {
+ verbose(VERB_ALGO, "Could not generate sub query");
+ qstate->return_rcode = LDNS_RCODE_FORMERR;
+ qstate->return_msg = NULL;
+ return module_finished;
+ }
+ sq->wait_subquery = 1;
+ return module_wait_subquery;
+}
+
static enum module_ext_state
eval_response(struct module_qstate *qstate, int id, struct subnet_qstate *sq)
{
@@ -557,14 +622,7 @@ eval_response(struct module_qstate *qstate, int id, struct subnet_qstate *sq)
* is still useful to put it in the edns subnet cache for
* when a client explicitly asks for subnet specific answer. */
verbose(VERB_QUERY, "subnetcache: Authority indicates no support");
- if(!sq->started_no_cache_store) {
- lock_rw_wrlock(&sne->biglock);
- update_cache(qstate, id);
- lock_rw_unlock(&sne->biglock);
- }
- if (sq->subnet_downstream)
- cp_edns_bad_response(c_out, c_in);
- return module_finished;
+ return generate_lookup_without_subnet(qstate, sq);
}
/* Purposefully there was no sent subnet, and there is consequently
@@ -589,14 +647,14 @@ eval_response(struct module_qstate *qstate, int id, struct subnet_qstate *sq)
!common_prefix(s_out->subnet_addr, s_in->subnet_addr,
s_out->subnet_source_mask))
{
- /* we can not accept, restart query without option */
+ /* we can not accept, perform query without option */
verbose(VERB_QUERY, "subnetcache: forged data");
s_out->subnet_validdata = 0;
(void)edns_opt_list_remove(&qstate->edns_opts_back_out,
qstate->env->cfg->client_subnet_opcode);
sq->subnet_sent = 0;
sq->subnet_sent_no_subnet = 0;
- return module_restart_next;
+ return generate_lookup_without_subnet(qstate, sq);
}
lock_rw_wrlock(&sne->biglock);
@@ -795,6 +853,9 @@ ecs_edns_back_parsed(struct module_qstate* qstate, int id,
} else if(sq->subnet_sent_no_subnet) {
/* The answer can be stored as scope 0, not in global cache. */
qstate->no_cache_store = 1;
+ } else if(sq->subnet_sent) {
+ /* Need another query to be able to store in global cache. */
+ qstate->no_cache_store = 1;
}
return 1;
@@ -812,6 +873,32 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event,
strmodulevent(event));
log_query_info(VERB_QUERY, "subnetcache operate: query", &qstate->qinfo);
+ if(sq && sq->wait_subquery_done) {
+ /* The subquery lookup returned. */
+ if(sq->ecs_client_in.subnet_source_mask == 0 &&
+ edns_opt_list_find(qstate->edns_opts_front_in,
+ qstate->env->cfg->client_subnet_opcode)) {
+ if(!sq->started_no_cache_store &&
+ qstate->return_msg) {
+ lock_rw_wrlock(&sne->biglock);
+ update_cache(qstate, id);
+ lock_rw_unlock(&sne->biglock);
+ }
+ if (sq->subnet_downstream)
+ cp_edns_bad_response(&sq->ecs_client_out,
+ &sq->ecs_client_in);
+ /* It is a scope zero lookup, append edns subnet
+ * option to the querier. */
+ subnet_ecs_opt_list_append(&sq->ecs_client_out,
+ &qstate->edns_opts_front_out, qstate,
+ qstate->region);
+ }
+ sq->wait_subquery_done = 0;
+ qstate->ext_state[id] = module_finished;
+ qstate->no_cache_store = sq->started_no_cache_store;
+ qstate->no_cache_lookup = sq->started_no_cache_lookup;
+ return;
+ }
if((event == module_event_new || event == module_event_pass) &&
sq == NULL) {
struct edns_option* ecs_opt;
@@ -822,6 +909,8 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event,
}
sq = (struct subnet_qstate*)qstate->minfo[id];
+ if(sq->wait_subquery)
+ return; /* Wait for that subquery to return */
if((ecs_opt = edns_opt_list_find(
qstate->edns_opts_front_in,
@@ -851,6 +940,14 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event,
/* No clients are interested in result or we could not
* parse it, we don't do client subnet */
sq->ecs_server_out.subnet_validdata = 0;
+ if(edns_opt_list_find(qstate->edns_opts_front_in,
+ qstate->env->cfg->client_subnet_opcode)) {
+ /* aggregated this deaggregated state */
+ qstate->ext_state[id] =
+ generate_lookup_without_subnet(
+ qstate, sq);
+ return;
+ }
verbose(VERB_ALGO, "subnetcache: pass to next module");
qstate->ext_state[id] = module_wait_module;
return;
@@ -891,6 +988,14 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event,
}
lock_rw_unlock(&sne->biglock);
}
+ if(sq->ecs_client_in.subnet_source_mask == 0 &&
+ edns_opt_list_find(qstate->edns_opts_front_in,
+ qstate->env->cfg->client_subnet_opcode)) {
+ /* client asked for resolution without edns subnet */
+ qstate->ext_state[id] = generate_lookup_without_subnet(
+ qstate, sq);
+ return;
+ }
sq->ecs_server_out.subnet_addr_fam =
sq->ecs_client_in.subnet_addr_fam;
@@ -927,6 +1032,8 @@ subnetmod_operate(struct module_qstate *qstate, enum module_ev event,
qstate->ext_state[id] = module_wait_module;
return;
}
+ if(sq && sq->wait_subquery)
+ return; /* Wait for that subquery to return */
/* Query handed back by next module, we have a 'final' answer */
if(sq && event == module_event_moddone) {
qstate->ext_state[id] = eval_response(qstate, id, sq);
@@ -975,10 +1082,27 @@ subnetmod_clear(struct module_qstate *ATTR_UNUSED(qstate),
}
void
-subnetmod_inform_super(struct module_qstate *ATTR_UNUSED(qstate),
- int ATTR_UNUSED(id), struct module_qstate *ATTR_UNUSED(super))
+subnetmod_inform_super(struct module_qstate *qstate, int id,
+ struct module_qstate *super)
{
- /* Not used */
+ struct subnet_qstate* super_sq =
+ (struct subnet_qstate*)super->minfo[id];
+ log_query_info(VERB_ALGO, "subnetcache inform_super: query",
+ &super->qinfo);
+ super_sq->wait_subquery = 0;
+ super_sq->wait_subquery_done = 1;
+ if(qstate->return_rcode != LDNS_RCODE_NOERROR ||
+ !qstate->return_msg) {
+ super->return_msg = NULL;
+ super->return_rcode = LDNS_RCODE_SERVFAIL;
+ return;
+ }
+ super->return_rcode = LDNS_RCODE_NOERROR;
+ super->return_msg = dns_copy_msg(qstate->return_msg, super->region);
+ if(!super->return_msg) {
+ log_err("subnetcache: copy response, out of memory");
+ super->return_rcode = LDNS_RCODE_SERVFAIL;
+ }
}
size_t
diff --git a/contrib/unbound/edns-subnet/subnetmod.h b/contrib/unbound/edns-subnet/subnetmod.h
index 1ff8a23ecdba..3893820fabaf 100644
--- a/contrib/unbound/edns-subnet/subnetmod.h
+++ b/contrib/unbound/edns-subnet/subnetmod.h
@@ -102,6 +102,10 @@ struct subnet_qstate {
int started_no_cache_store;
/** has the subnet module been started with no_cache_lookup? */
int started_no_cache_lookup;
+ /** Wait for subquery that has been started for nonsubnet lookup. */
+ int wait_subquery;
+ /** The subquery waited for is done. */
+ int wait_subquery_done;
};
void subnet_data_delete(void* d, void* ATTR_UNUSED(arg));
diff --git a/contrib/unbound/ltmain.sh b/contrib/unbound/ltmain.sh
index 7f3523d335c5..3e6a3db3a5a1 100644
--- a/contrib/unbound/ltmain.sh
+++ b/contrib/unbound/ltmain.sh
@@ -1,12 +1,12 @@
-#! /bin/sh
+#! /usr/bin/env sh
## DO NOT EDIT - This file generated from ./build-aux/ltmain.in
-## by inline-source v2014-01-03.01
+## by inline-source v2019-02-19.15
-# libtool (GNU libtool) 2.4.6
+# libtool (GNU libtool) 2.5.4
# Provide generalized library-building support services.
# Written by Gordon Matzigkeit <gord@gnu.ai.mit.edu>, 1996
-# Copyright (C) 1996-2015 Free Software Foundation, Inc.
+# Copyright (C) 1996-2019, 2021-2024 Free Software Foundation, Inc.
# This is free software; see the source for copying conditions. There is NO
# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
@@ -31,8 +31,8 @@
PROGRAM=libtool
PACKAGE=libtool
-VERSION=2.4.6
-package_revision=2.4.6
+VERSION=2.5.4
+package_revision=2.5.4
## ------ ##
@@ -64,34 +64,25 @@ package_revision=2.4.6
# libraries, which are installed to $pkgauxdir.
# Set a version string for this script.
-scriptversion=2015-01-20.17; # UTC
+scriptversion=2019-02-19.15; # UTC
# General shell script boiler plate, and helper functions.
# Written by Gary V. Vaughan, 2004
-# Copyright (C) 2004-2015 Free Software Foundation, Inc.
-# This is free software; see the source for copying conditions. There is NO
-# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 3 of the License, or
-# (at your option) any later version.
-
-# As a special exception to the GNU General Public License, if you distribute
-# this file as part of a program or library that is built using GNU Libtool,
-# you may include this file under the same distribution terms that you use
-# for the rest of that program.
-
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNES FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+# This is free software. There is NO warranty; not even for
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# Copyright (C) 2004-2019, 2021, 2023-2024 Bootstrap Authors
+#
+# This file is dual licensed under the terms of the MIT license
+# <https://opensource.org/licenses/MIT>, and GPL version 2 or later
+# <https://www.gnu.org/licenses/gpl-2.0.html>. You must apply one of
+# these licenses when using or redistributing this software or any of
+# the files within it. See the URLs above, or the file `LICENSE`
+# included in the Bootstrap distribution for the full license texts.
-# Please report bugs or propose patches to gary@gnu.org.
+# Please report bugs or propose patches to:
+# <https://github.com/gnulib-modules/bootstrap/issues>
## ------ ##
@@ -139,9 +130,12 @@ do
_G_safe_locale=\"$_G_var=C; \$_G_safe_locale\"
fi"
done
-
-# CDPATH.
-(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
+# These NLS vars are set unconditionally (bootstrap issue #24). Unset those
+# in case the environment reset is needed later and the $save_* variant is not
+# defined (see the code above).
+LC_ALL=C
+LANGUAGE=C
+export LANGUAGE LC_ALL
# Make sure IFS has a sensible default
sp=' '
@@ -149,7 +143,7 @@ nl='
'
IFS="$sp $nl"
-# There are apparently some retarded systems that use ';' as a PATH separator!
+# There are apparently some systems that use ';' as a PATH separator!
if test "${PATH_SEPARATOR+set}" != set; then
PATH_SEPARATOR=:
(PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
@@ -159,6 +153,26 @@ if test "${PATH_SEPARATOR+set}" != set; then
fi
+# func_unset VAR
+# --------------
+# Portably unset VAR.
+# In some shells, an 'unset VAR' statement leaves a non-zero return
+# status if VAR is already unset, which might be problematic if the
+# statement is used at the end of a function (thus poisoning its return
+# value) or when 'set -e' is active (causing even a spurious abort of
+# the script in this case).
+func_unset ()
+{
+ { eval $1=; (eval unset $1) >/dev/null 2>&1 && eval unset $1 || : ; }
+}
+
+
+# Make sure CDPATH doesn't cause `cd` commands to output the target dir.
+func_unset CDPATH
+
+# Make sure ${,E,F}GREP behave sanely.
+func_unset GREP_OPTIONS
+
## ------------------------- ##
## Locate command utilities. ##
@@ -259,7 +273,7 @@ test -z "$SED" && {
rm -f conftest.in conftest.tmp conftest.nl conftest.out
}
- func_path_progs "sed gsed" func_check_prog_sed $PATH:/usr/xpg4/bin
+ func_path_progs "sed gsed" func_check_prog_sed "$PATH:/usr/xpg4/bin"
rm -f conftest.sed
SED=$func_path_progs_result
}
@@ -295,7 +309,7 @@ test -z "$GREP" && {
rm -f conftest.in conftest.tmp conftest.nl conftest.out
}
- func_path_progs "grep ggrep" func_check_prog_grep $PATH:/usr/xpg4/bin
+ func_path_progs "grep ggrep" func_check_prog_grep "$PATH:/usr/xpg4/bin"
GREP=$func_path_progs_result
}
@@ -360,6 +374,35 @@ sed_double_backslash="\
s/\\([^$_G_bs]\\)$_G_bs2$_G_dollar/\\1$_G_bs2$_G_bs$_G_dollar/g
s/\n//g"
+# require_check_ifs_backslash
+# ---------------------------
+# Check if we can use backslash as IFS='\' separator, and set
+# $check_ifs_backshlash_broken to ':' or 'false'.
+require_check_ifs_backslash=func_require_check_ifs_backslash
+func_require_check_ifs_backslash ()
+{
+ _G_save_IFS=$IFS
+ IFS='\'
+ _G_check_ifs_backshlash='a\\b'
+ for _G_i in $_G_check_ifs_backshlash
+ do
+ case $_G_i in
+ a)
+ check_ifs_backshlash_broken=false
+ ;;
+ '')
+ break
+ ;;
+ *)
+ check_ifs_backshlash_broken=:
+ break
+ ;;
+ esac
+ done
+ IFS=$_G_save_IFS
+ require_check_ifs_backslash=:
+}
+
## ----------------- ##
## Global variables. ##
@@ -546,7 +589,7 @@ func_require_term_colors ()
# _G_HAVE_PLUSEQ_OP
# Can be empty, in which case the shell is probed, "yes" if += is
- # useable or anything else if it does not work.
+ # usable or anything else if it does not work.
test -z "$_G_HAVE_PLUSEQ_OP" \
&& (eval 'x=a; x+=" b"; test "a b" = "$x"') 2>/dev/null \
&& _G_HAVE_PLUSEQ_OP=yes
@@ -580,16 +623,16 @@ if test yes = "$_G_HAVE_PLUSEQ_OP"; then
{
$debug_cmd
- func_quote_for_eval "$2"
- eval "$1+=\\ \$func_quote_for_eval_result"
+ func_quote_arg pretty "$2"
+ eval "$1+=\\ \$func_quote_arg_result"
}'
else
func_append_quoted ()
{
$debug_cmd
- func_quote_for_eval "$2"
- eval "$1=\$$1\\ \$func_quote_for_eval_result"
+ func_quote_arg pretty "$2"
+ eval "$1=\$$1\\ \$func_quote_arg_result"
}
fi
@@ -696,7 +739,7 @@ eval 'func_dirname ()
# to NONDIR_REPLACEMENT.
# value returned in "$func_dirname_result"
# basename: Compute filename of FILE.
-# value retuned in "$func_basename_result"
+# value returned in "$func_basename_result"
# For efficiency, we do not delegate to the functions above but instead
# duplicate the functionality here.
eval 'func_dirname_and_basename ()
@@ -854,7 +897,7 @@ func_mkdir_p ()
# While some portion of DIR does not yet exist...
while test ! -d "$_G_directory_path"; do
# ...make a list in topmost first order. Use a colon delimited
- # list incase some portion of path contains whitespace.
+ # list in case some portion of path contains whitespace.
_G_dir_list=$_G_directory_path:$_G_dir_list
# If the last portion added has no slash in it, the list is done
@@ -1091,85 +1134,203 @@ func_relative_path ()
}
-# func_quote_for_eval ARG...
-# --------------------------
-# Aesthetically quote ARGs to be evaled later.
-# This function returns two values:
-# i) func_quote_for_eval_result
-# double-quoted, suitable for a subsequent eval
-# ii) func_quote_for_eval_unquoted_result
-# has all characters that are still active within double
-# quotes backslashified.
-func_quote_for_eval ()
+# func_quote_portable EVAL ARG
+# ----------------------------
+# Internal function to portably implement func_quote_arg. Note that we still
+# keep attention to performance here so we as much as possible try to avoid
+# calling sed binary (so far O(N) complexity as long as func_append is O(1)).
+func_quote_portable ()
{
$debug_cmd
- func_quote_for_eval_unquoted_result=
- func_quote_for_eval_result=
- while test 0 -lt $#; do
- case $1 in
- *[\\\`\"\$]*)
- _G_unquoted_arg=`printf '%s\n' "$1" |$SED "$sed_quote_subst"` ;;
- *)
- _G_unquoted_arg=$1 ;;
- esac
- if test -n "$func_quote_for_eval_unquoted_result"; then
- func_append func_quote_for_eval_unquoted_result " $_G_unquoted_arg"
- else
- func_append func_quote_for_eval_unquoted_result "$_G_unquoted_arg"
+ $require_check_ifs_backslash
+
+ func_quote_portable_result=$2
+
+ # one-time-loop (easy break)
+ while true
+ do
+ if $1; then
+ func_quote_portable_result=`$ECHO "$2" | $SED \
+ -e "$sed_double_quote_subst" -e "$sed_double_backslash"`
+ break
fi
- case $_G_unquoted_arg in
- # Double-quote args containing shell metacharacters to delay
- # word splitting, command substitution and variable expansion
- # for a subsequent eval.
- # Many Bourne shells cannot handle close brackets correctly
- # in scan sets, so we specify it separately.
- *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"")
- _G_quoted_arg=\"$_G_unquoted_arg\"
+ # Quote for eval.
+ case $func_quote_portable_result in
+ *[\\\`\"\$]*)
+ # Fallback to sed for $func_check_bs_ifs_broken=:, or when the string
+ # contains the shell wildcard characters.
+ case $check_ifs_backshlash_broken$func_quote_portable_result in
+ :*|*[\[\*\?]*)
+ func_quote_portable_result=`$ECHO "$func_quote_portable_result" \
+ | $SED "$sed_quote_subst"`
+ break
+ ;;
+ esac
+
+ func_quote_portable_old_IFS=$IFS
+ for _G_char in '\' '`' '"' '$'
+ do
+ # STATE($1) PREV($2) SEPARATOR($3)
+ set start "" ""
+ func_quote_portable_result=dummy"$_G_char$func_quote_portable_result$_G_char"dummy
+ IFS=$_G_char
+ for _G_part in $func_quote_portable_result
+ do
+ case $1 in
+ quote)
+ func_append func_quote_portable_result "$3$2"
+ set quote "$_G_part" "\\$_G_char"
+ ;;
+ start)
+ set first "" ""
+ func_quote_portable_result=
+ ;;
+ first)
+ set quote "$_G_part" ""
+ ;;
+ esac
+ done
+ done
+ IFS=$func_quote_portable_old_IFS
;;
- *)
- _G_quoted_arg=$_G_unquoted_arg
- ;;
+ *) ;;
esac
-
- if test -n "$func_quote_for_eval_result"; then
- func_append func_quote_for_eval_result " $_G_quoted_arg"
- else
- func_append func_quote_for_eval_result "$_G_quoted_arg"
- fi
- shift
+ break
done
+
+ func_quote_portable_unquoted_result=$func_quote_portable_result
+ case $func_quote_portable_result in
+ # double-quote args containing shell metacharacters to delay
+ # word splitting, command substitution and variable expansion
+ # for a subsequent eval.
+ # many bourne shells cannot handle close brackets correctly
+ # in scan sets, so we specify it separately.
+ *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"")
+ func_quote_portable_result=\"$func_quote_portable_result\"
+ ;;
+ esac
}
-# func_quote_for_expand ARG
-# -------------------------
-# Aesthetically quote ARG to be evaled later; same as above,
-# but do not quote variable references.
-func_quote_for_expand ()
-{
- $debug_cmd
+# func_quotefast_eval ARG
+# -----------------------
+# Quote one ARG (internal). This is equivalent to 'func_quote_arg eval ARG',
+# but optimized for speed. Result is stored in $func_quotefast_eval.
+if test xyes = `(x=; printf -v x %q yes; echo x"$x") 2>/dev/null`; then
+ printf -v _GL_test_printf_tilde %q '~'
+ if test '\~' = "$_GL_test_printf_tilde"; then
+ func_quotefast_eval ()
+ {
+ printf -v func_quotefast_eval_result %q "$1"
+ }
+ else
+ # Broken older Bash implementations. Make those faster too if possible.
+ func_quotefast_eval ()
+ {
+ case $1 in
+ '~'*)
+ func_quote_portable false "$1"
+ func_quotefast_eval_result=$func_quote_portable_result
+ ;;
+ *)
+ printf -v func_quotefast_eval_result %q "$1"
+ ;;
+ esac
+ }
+ fi
+else
+ func_quotefast_eval ()
+ {
+ func_quote_portable false "$1"
+ func_quotefast_eval_result=$func_quote_portable_result
+ }
+fi
- case $1 in
- *[\\\`\"]*)
- _G_arg=`$ECHO "$1" | $SED \
- -e "$sed_double_quote_subst" -e "$sed_double_backslash"` ;;
- *)
- _G_arg=$1 ;;
+
+# func_quote_arg MODEs ARG
+# ------------------------
+# Quote one ARG to be evaled later. MODEs argument may contain zero or more
+# specifiers listed below separated by ',' character. This function returns two
+# values:
+# i) func_quote_arg_result
+# double-quoted (when needed), suitable for a subsequent eval
+# ii) func_quote_arg_unquoted_result
+# has all characters that are still active within double
+# quotes backslashified. Available only if 'unquoted' is specified.
+#
+# Available modes:
+# ----------------
+# 'eval' (default)
+# - escape shell special characters
+# 'expand'
+# - the same as 'eval'; but do not quote variable references
+# 'pretty'
+# - request aesthetic output, i.e. '"a b"' instead of 'a\ b'. This might
+# be used later in func_quote to get output like: 'echo "a b"' instead
+# of 'echo a\ b'. This is slower than default on some shells.
+# 'unquoted'
+# - produce also $func_quote_arg_unquoted_result which does not contain
+# wrapping double-quotes.
+#
+# Examples for 'func_quote_arg pretty,unquoted string':
+#
+# string | *_result | *_unquoted_result
+# ------------+-----------------------+-------------------
+# " | \" | \"
+# a b | "a b" | a b
+# "a b" | "\"a b\"" | \"a b\"
+# * | "*" | *
+# z="${x-$y}" | "z=\"\${x-\$y}\"" | z=\"\${x-\$y}\"
+#
+# Examples for 'func_quote_arg pretty,unquoted,expand string':
+#
+# string | *_result | *_unquoted_result
+# --------------+---------------------+--------------------
+# z="${x-$y}" | "z=\"${x-$y}\"" | z=\"${x-$y}\"
+func_quote_arg ()
+{
+ _G_quote_expand=false
+ case ,$1, in
+ *,expand,*)
+ _G_quote_expand=:
+ ;;
esac
- case $_G_arg in
- # Double-quote args containing shell metacharacters to delay
- # word splitting and command substitution for a subsequent eval.
- # Many Bourne shells cannot handle close brackets correctly
- # in scan sets, so we specify it separately.
- *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"")
- _G_arg=\"$_G_arg\"
+ case ,$1, in
+ *,pretty,*|*,expand,*|*,unquoted,*)
+ func_quote_portable $_G_quote_expand "$2"
+ func_quote_arg_result=$func_quote_portable_result
+ func_quote_arg_unquoted_result=$func_quote_portable_unquoted_result
+ ;;
+ *)
+ # Faster quote-for-eval for some shells.
+ func_quotefast_eval "$2"
+ func_quote_arg_result=$func_quotefast_eval_result
;;
esac
+}
- func_quote_for_expand_result=$_G_arg
+
+# func_quote MODEs ARGs...
+# ------------------------
+# Quote all ARGs to be evaled later and join them into single command. See
+# func_quote_arg's description for more info.
+func_quote ()
+{
+ $debug_cmd
+ _G_func_quote_mode=$1 ; shift
+ func_quote_result=
+ while test 0 -lt $#; do
+ func_quote_arg "$_G_func_quote_mode" "$1"
+ if test -n "$func_quote_result"; then
+ func_append func_quote_result " $func_quote_arg_result"
+ else
+ func_append func_quote_result "$func_quote_arg_result"
+ fi
+ shift
+ done
}
@@ -1215,8 +1376,8 @@ func_show_eval ()
_G_cmd=$1
_G_fail_exp=${2-':'}
- func_quote_for_expand "$_G_cmd"
- eval "func_notquiet $func_quote_for_expand_result"
+ func_quote_arg pretty,expand "$_G_cmd"
+ eval "func_notquiet $func_quote_arg_result"
$opt_dry_run || {
eval "$_G_cmd"
@@ -1241,8 +1402,8 @@ func_show_eval_locale ()
_G_fail_exp=${2-':'}
$opt_quiet || {
- func_quote_for_expand "$_G_cmd"
- eval "func_echo $func_quote_for_expand_result"
+ func_quote_arg expand,pretty "$_G_cmd"
+ eval "func_echo $func_quote_arg_result"
}
$opt_dry_run || {
@@ -1369,30 +1530,26 @@ func_lt_ver ()
# End:
#! /bin/sh
-# Set a version string for this script.
-scriptversion=2014-01-07.03; # UTC
-
# A portable, pluggable option parser for Bourne shell.
# Written by Gary V. Vaughan, 2010
-# Copyright (C) 2010-2015 Free Software Foundation, Inc.
-# This is free software; see the source for copying conditions. There is NO
-# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
+# This is free software. There is NO warranty; not even for
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# Copyright (C) 2010-2019, 2021, 2023-2024 Bootstrap Authors
+#
+# This file is dual licensed under the terms of the MIT license
+# <https://opensource.org/licenses/MIT>, and GPL version 2 or later
+# <https://www.gnu.org/licenses/gpl-2.0.html>. You must apply one of
+# these licenses when using or redistributing this software or any of
+# the files within it. See the URLs above, or the file `LICENSE`
+# included in the Bootstrap distribution for the full license texts.
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
+# Please report bugs or propose patches to:
+# <https://github.com/gnulib-modules/bootstrap/issues>
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-# Please report bugs or propose patches to gary@gnu.org.
+# Set a version string for this script.
+scriptversion=2019-02-19.15; # UTC
## ------ ##
@@ -1415,7 +1572,7 @@ scriptversion=2014-01-07.03; # UTC
#
# In order for the '--version' option to work, you will need to have a
# suitably formatted comment like the one at the top of this file
-# starting with '# Written by ' and ending with '# warranty; '.
+# starting with '# Written by ' and ending with '# Copyright'.
#
# For '-h' and '--help' to work, you will also need a one line
# description of your script's purpose in a comment directly above the
@@ -1427,7 +1584,7 @@ scriptversion=2014-01-07.03; # UTC
# to display verbose messages only when your user has specified
# '--verbose'.
#
-# After sourcing this file, you can plug processing for additional
+# After sourcing this file, you can plug in processing for additional
# options by amending the variables from the 'Configuration' section
# below, and following the instructions in the 'Option parsing'
# section further down.
@@ -1476,8 +1633,8 @@ fatal_help="Try '\$progname --help' for more information."
## ------------------------- ##
# This section contains functions for adding, removing, and running hooks
-# to the main code. A hook is just a named list of of function, that can
-# be run in order later on.
+# in the main code. A hook is just a list of function names that can be
+# run in order later on.
# func_hookable FUNC_NAME
# -----------------------
@@ -1510,7 +1667,8 @@ func_add_hook ()
# func_remove_hook FUNC_NAME HOOK_FUNC
# ------------------------------------
-# Remove HOOK_FUNC from the list of functions called by FUNC_NAME.
+# Remove HOOK_FUNC from the list of hook functions to be called by
+# FUNC_NAME.
func_remove_hook ()
{
$debug_cmd
@@ -1519,10 +1677,28 @@ func_remove_hook ()
}
+# func_propagate_result FUNC_NAME_A FUNC_NAME_B
+# ---------------------------------------------
+# If the *_result variable of FUNC_NAME_A _is set_, assign its value to
+# *_result variable of FUNC_NAME_B.
+func_propagate_result ()
+{
+ $debug_cmd
+
+ func_propagate_result_result=:
+ if eval "test \"\${${1}_result+set}\" = set"
+ then
+ eval "${2}_result=\$${1}_result"
+ else
+ func_propagate_result_result=false
+ fi
+}
+
+
# func_run_hooks FUNC_NAME [ARG]...
# ---------------------------------
# Run all hook functions registered to FUNC_NAME.
-# It is assumed that the list of hook functions contains nothing more
+# It's assumed that the list of hook functions contains nothing more
# than a whitespace-delimited list of legal shell function names, and
# no effort is wasted trying to catch shell meta-characters or preserve
# whitespace.
@@ -1532,22 +1708,19 @@ func_run_hooks ()
case " $hookable_fns " in
*" $1 "*) ;;
- *) func_fatal_error "'$1' does not support hook funcions.n" ;;
+ *) func_fatal_error "'$1' does not support hook functions." ;;
esac
eval _G_hook_fns=\$$1_hooks; shift
for _G_hook in $_G_hook_fns; do
- eval $_G_hook '"$@"'
-
- # store returned options list back into positional
- # parameters for next 'cmd' execution.
- eval _G_hook_result=\$${_G_hook}_result
- eval set dummy "$_G_hook_result"; shift
+ func_unset "${_G_hook}_result"
+ eval $_G_hook '${1+"$@"}'
+ func_propagate_result $_G_hook func_run_hooks
+ if $func_propagate_result_result; then
+ eval set dummy "$func_run_hooks_result"; shift
+ fi
done
-
- func_quote_for_eval ${1+"$@"}
- func_run_hooks_result=$func_quote_for_eval_result
}
@@ -1557,10 +1730,18 @@ func_run_hooks ()
## --------------- ##
# In order to add your own option parsing hooks, you must accept the
-# full positional parameter list in your hook function, remove any
-# options that you action, and then pass back the remaining unprocessed
-# options in '<hooked_function_name>_result', escaped suitably for
-# 'eval'. Like this:
+# full positional parameter list from your hook function. You may remove
+# or edit any options that you action, and then pass back the remaining
+# unprocessed options in '<hooked_function_name>_result', escaped
+# suitably for 'eval'.
+#
+# The '<hooked_function_name>_result' variable is automatically unset
+# before your hook gets called; for best performance, only set the
+# *_result variable when necessary (i.e. don't call the 'func_quote'
+# function unnecessarily because it can be an expensive operation on some
+# machines).
+#
+# Like this:
#
# my_options_prep ()
# {
@@ -1570,9 +1751,8 @@ func_run_hooks ()
# usage_message=$usage_message'
# -s, --silent don'\''t print informational messages
# '
-#
-# func_quote_for_eval ${1+"$@"}
-# my_options_prep_result=$func_quote_for_eval_result
+# # No change in '$@' (ignored completely by this hook). Leave
+# # my_options_prep_result variable intact.
# }
# func_add_hook func_options_prep my_options_prep
#
@@ -1581,25 +1761,36 @@ func_run_hooks ()
# {
# $debug_cmd
#
-# # Note that for efficiency, we parse as many options as we can
+# args_changed=false
+#
+# # Note that, for efficiency, we parse as many options as we can
# # recognise in a loop before passing the remainder back to the
# # caller on the first unrecognised argument we encounter.
# while test $# -gt 0; do
# opt=$1; shift
# case $opt in
-# --silent|-s) opt_silent=: ;;
+# --silent|-s) opt_silent=:
+# args_changed=:
+# ;;
# # Separate non-argument short options:
# -s*) func_split_short_opt "$_G_opt"
# set dummy "$func_split_short_opt_name" \
# "-$func_split_short_opt_arg" ${1+"$@"}
# shift
+# args_changed=:
# ;;
-# *) set dummy "$_G_opt" "$*"; shift; break ;;
+# *) # Make sure the first unrecognised option "$_G_opt"
+# # is added back to "$@" in case we need it later,
+# # if $args_changed was set to 'true'.
+# set dummy "$_G_opt" ${1+"$@"}; shift; break ;;
# esac
# done
#
-# func_quote_for_eval ${1+"$@"}
-# my_silent_option_result=$func_quote_for_eval_result
+# # Only call 'func_quote' here if we processed at least one argument.
+# if $args_changed; then
+# func_quote eval ${1+"$@"}
+# my_silent_option_result=$func_quote_result
+# fi
# }
# func_add_hook func_parse_options my_silent_option
#
@@ -1610,17 +1801,26 @@ func_run_hooks ()
#
# $opt_silent && $opt_verbose && func_fatal_help "\
# '--silent' and '--verbose' options are mutually exclusive."
-#
-# func_quote_for_eval ${1+"$@"}
-# my_option_validation_result=$func_quote_for_eval_result
# }
# func_add_hook func_validate_options my_option_validation
#
-# You'll alse need to manually amend $usage_message to reflect the extra
+# You'll also need to manually amend $usage_message to reflect the extra
# options you parse. It's preferable to append if you can, so that
# multiple option parsing hooks can be added safely.
+# func_options_finish [ARG]...
+# ----------------------------
+# Finishing the option parse loop (call 'func_options' hooks ATM).
+func_options_finish ()
+{
+ $debug_cmd
+
+ func_run_hooks func_options ${1+"$@"}
+ func_propagate_result func_run_hooks func_options_finish
+}
+
+
# func_options [ARG]...
# ---------------------
# All the functions called inside func_options are hookable. See the
@@ -1630,17 +1830,27 @@ func_options ()
{
$debug_cmd
- func_options_prep ${1+"$@"}
- eval func_parse_options \
- ${func_options_prep_result+"$func_options_prep_result"}
- eval func_validate_options \
- ${func_parse_options_result+"$func_parse_options_result"}
+ _G_options_quoted=false
- eval func_run_hooks func_options \
- ${func_validate_options_result+"$func_validate_options_result"}
+ for my_func in options_prep parse_options validate_options options_finish
+ do
+ func_unset func_${my_func}_result
+ func_unset func_run_hooks_result
+ eval func_$my_func '${1+"$@"}'
+ func_propagate_result func_$my_func func_options
+ if $func_propagate_result_result; then
+ eval set dummy "$func_options_result"; shift
+ _G_options_quoted=:
+ fi
+ done
- # save modified positional parameters for caller
- func_options_result=$func_run_hooks_result
+ $_G_options_quoted || {
+ # As we (func_options) are top-level options-parser function and
+ # nobody quoted "$@" for us yet, we need to do it explicitly for
+ # caller.
+ func_quote eval ${1+"$@"}
+ func_options_result=$func_quote_result
+ }
}
@@ -1649,9 +1859,8 @@ func_options ()
# All initialisations required before starting the option parse loop.
# Note that when calling hook functions, we pass through the list of
# positional parameters. If a hook function modifies that list, and
-# needs to propogate that back to rest of this script, then the complete
-# modified list must be put in 'func_run_hooks_result' before
-# returning.
+# needs to propagate that back to rest of this script, then the complete
+# modified list must be put in 'func_run_hooks_result' before returning.
func_hookable func_options_prep
func_options_prep ()
{
@@ -1662,9 +1871,7 @@ func_options_prep ()
opt_warning_types=
func_run_hooks func_options_prep ${1+"$@"}
-
- # save modified positional parameters for caller
- func_options_prep_result=$func_run_hooks_result
+ func_propagate_result func_run_hooks func_options_prep
}
@@ -1676,25 +1883,32 @@ func_parse_options ()
{
$debug_cmd
- func_parse_options_result=
-
+ _G_parse_options_requote=false
# this just eases exit handling
while test $# -gt 0; do
# Defer to hook functions for initial option parsing, so they
# get priority in the event of reusing an option name.
func_run_hooks func_parse_options ${1+"$@"}
-
- # Adjust func_parse_options positional parameters to match
- eval set dummy "$func_run_hooks_result"; shift
+ func_propagate_result func_run_hooks func_parse_options
+ if $func_propagate_result_result; then
+ eval set dummy "$func_parse_options_result"; shift
+ # Even though we may have changed "$@", we passed the "$@" array
+ # down into the hook and it quoted it for us (because we are in
+ # this if-branch). No need to quote it again.
+ _G_parse_options_requote=false
+ fi
# Break out of the loop if we already parsed every option.
test $# -gt 0 || break
+ # We expect that one of the options parsed in this function matches
+ # and thus we remove _G_opt from "$@" and need to re-quote.
+ _G_match_parse_options=:
_G_opt=$1
shift
case $_G_opt in
--debug|-x) debug_cmd='set -x'
- func_echo "enabling shell trace mode"
+ func_echo "enabling shell trace mode" >&2
$debug_cmd
;;
@@ -1704,7 +1918,10 @@ func_parse_options ()
;;
--warnings|--warning|-W)
- test $# = 0 && func_missing_arg $_G_opt && break
+ if test $# = 0 && func_missing_arg $_G_opt; then
+ _G_parse_options_requote=:
+ break
+ fi
case " $warning_categories $1" in
*" $1 "*)
# trailing space prevents matching last $1 above
@@ -1757,15 +1974,24 @@ func_parse_options ()
shift
;;
- --) break ;;
+ --) _G_parse_options_requote=: ; break ;;
-*) func_fatal_help "unrecognised option: '$_G_opt'" ;;
- *) set dummy "$_G_opt" ${1+"$@"}; shift; break ;;
+ *) set dummy "$_G_opt" ${1+"$@"}; shift
+ _G_match_parse_options=false
+ break
+ ;;
esac
+
+ if $_G_match_parse_options; then
+ _G_parse_options_requote=:
+ fi
done
- # save modified positional parameters for caller
- func_quote_for_eval ${1+"$@"}
- func_parse_options_result=$func_quote_for_eval_result
+ if $_G_parse_options_requote; then
+ # save modified positional parameters for caller
+ func_quote eval ${1+"$@"}
+ func_parse_options_result=$func_quote_result
+ fi
}
@@ -1782,12 +2008,10 @@ func_validate_options ()
test -n "$opt_warning_types" || opt_warning_types=" $warning_categories"
func_run_hooks func_validate_options ${1+"$@"}
+ func_propagate_result func_run_hooks func_validate_options
# Bail if the options were screwed!
$exit_cmd $EXIT_FAILURE
-
- # save modified positional parameters for caller
- func_validate_options_result=$func_run_hooks_result
}
@@ -1843,8 +2067,8 @@ func_missing_arg ()
# func_split_equals STRING
# ------------------------
-# Set func_split_equals_lhs and func_split_equals_rhs shell variables after
-# splitting STRING at the '=' sign.
+# Set func_split_equals_lhs and func_split_equals_rhs shell variables
+# after splitting STRING at the '=' sign.
test -z "$_G_HAVE_XSI_OPS" \
&& (eval 'x=a/b/c;
test 5aa/bb/cc = "${#x}${x%%/*}${x%/*}${x#*/}${x##*/}"') 2>/dev/null \
@@ -1859,8 +2083,9 @@ then
func_split_equals_lhs=${1%%=*}
func_split_equals_rhs=${1#*=}
- test "x$func_split_equals_lhs" = "x$1" \
- && func_split_equals_rhs=
+ if test "x$func_split_equals_lhs" = "x$1"; then
+ func_split_equals_rhs=
+ fi
}'
else
# ...otherwise fall back to using expr, which is often a shell builtin.
@@ -1870,7 +2095,7 @@ else
func_split_equals_lhs=`expr "x$1" : 'x\([^=]*\)'`
func_split_equals_rhs=
- test "x$func_split_equals_lhs" = "x$1" \
+ test "x$func_split_equals_lhs=" = "x$1" \
|| func_split_equals_rhs=`expr "x$1" : 'x[^=]*=\(.*\)$'`
}
fi #func_split_equals
@@ -1896,7 +2121,7 @@ else
{
$debug_cmd
- func_split_short_opt_name=`expr "x$1" : 'x-\(.\)'`
+ func_split_short_opt_name=`expr "x$1" : 'x\(-.\)'`
func_split_short_opt_arg=`expr "x$1" : 'x-.\(.*\)$'`
}
fi #func_split_short_opt
@@ -1938,31 +2163,44 @@ func_usage_message ()
# func_version
# ------------
# Echo version message to standard output and exit.
+# The version message is extracted from the calling file's header
+# comments, with leading '# ' stripped:
+# 1. First display the progname and version
+# 2. Followed by the header comment line matching /^# Written by /
+# 3. Then a blank line followed by the first following line matching
+# /^# Copyright /
+# 4. Immediately followed by any lines between the previous matches,
+# except lines preceding the intervening completely blank line.
+# For example, see the header comments of this file.
func_version ()
{
$debug_cmd
printf '%s\n' "$progname $scriptversion"
$SED -n '
- /(C)/!b go
- :more
- /\./!{
- N
- s|\n# | |
- b more
- }
- :go
- /^# Written by /,/# warranty; / {
- s|^# ||
- s|^# *$||
- s|\((C)\)[ 0-9,-]*[ ,-]\([1-9][0-9]* \)|\1 \2|
- p
+ /^# Written by /!b
+ s|^# ||; p; n
+
+ :fwd2blnk
+ /./ {
+ n
+ b fwd2blnk
}
- /^# Written by / {
- s|^# ||
- p
+ p; n
+
+ :holdwrnt
+ s|^# ||
+ s|^# *$||
+ /^Copyright /!{
+ /./H
+ n
+ b holdwrnt
}
- /^warranty; /q' < "$progpath"
+
+ s|\((C)\)[ 0-9,-]*[ ,-]\([1-9][0-9]* \)|\1 \2|
+ G
+ s|\(\n\)\n*|\1|g
+ p; q' < "$progpath"
exit $?
}
@@ -1972,12 +2210,35 @@ func_version ()
# mode: shell-script
# sh-indentation: 2
# eval: (add-hook 'before-save-hook 'time-stamp)
-# time-stamp-pattern: "10/scriptversion=%:y-%02m-%02d.%02H; # UTC"
+# time-stamp-pattern: "30/scriptversion=%:y-%02m-%02d.%02H; # UTC"
# time-stamp-time-zone: "UTC"
# End:
# Set a version string.
-scriptversion='(GNU libtool) 2.4.6'
+scriptversion='(GNU libtool) 2.5.4'
+
+# func_version
+# ------------
+# Echo version message to standard output and exit.
+func_version ()
+{
+ $debug_cmd
+
+ year=`date +%Y`
+
+ cat <<EOF
+$progname $scriptversion
+Copyright (C) $year Free Software Foundation, Inc.
+License GPLv2+: GNU GPL version 2 or later <https://gnu.org/licenses/gpl.html>
+This is free software: you are free to change and redistribute it.
+There is NO WARRANTY, to the extent permitted by law.
+
+Originally written by Gordon Matzigkeit, 1996
+(See AUTHORS for complete contributor listing)
+EOF
+
+ exit $?
+}
# func_echo ARG...
@@ -2000,18 +2261,6 @@ func_echo ()
}
-# func_warning ARG...
-# -------------------
-# Libtool warnings are not categorized, so override funclib.sh
-# func_warning with this simpler definition.
-func_warning ()
-{
- $debug_cmd
-
- $warning_func ${1+"$@"}
-}
-
-
## ---------------- ##
## Options parsing. ##
## ---------------- ##
@@ -2023,19 +2272,23 @@ usage='$progpath [OPTION]... [MODE-ARG]...'
# Short help message in response to '-h'.
usage_message="Options:
- --config show all configuration variables
- --debug enable verbose shell tracing
- -n, --dry-run display commands without modifying any files
- --features display basic configuration information and exit
- --mode=MODE use operation mode MODE
- --no-warnings equivalent to '-Wnone'
- --preserve-dup-deps don't remove duplicate dependency libraries
- --quiet, --silent don't print informational messages
- --tag=TAG use configuration variables from tag TAG
- -v, --verbose print more informational messages than default
- --version print version information
- -W, --warnings=CATEGORY report the warnings falling in CATEGORY [all]
- -h, --help, --help-all print short, long, or detailed help message
+ --config show all configuration variables
+ --debug enable verbose shell tracing
+ -n, --dry-run display commands without modifying any files
+ --features display basic configuration information
+ --finish use operation '--mode=finish'
+ --mode=MODE use operation mode MODE
+ --no-finish don't update shared library cache
+ --no-quiet, --no-silent print default informational messages
+ --no-warnings equivalent to '-Wnone'
+ --preserve-dup-deps don't remove duplicate dependency libraries
+ --quiet, --silent don't print informational messages
+ --reorder-cache=DIRS reorder shared library cache for preferred DIRS
+ --tag=TAG use configuration variables from tag TAG
+ -v, --verbose print more informational messages than default
+ --version print version information
+ -W, --warnings=CATEGORY report the warnings falling in CATEGORY [all]
+ -h, --help, --help-all print short, long, or detailed help message
"
# Additional text appended to 'usage_message' in response to '--help'.
@@ -2068,13 +2321,13 @@ include the following information:
compiler: $LTCC
compiler flags: $LTCFLAGS
linker: $LD (gnu? $with_gnu_ld)
- version: $progname (GNU libtool) 2.4.6
+ version: $progname $scriptversion
automake: `($AUTOMAKE --version) 2>/dev/null |$SED 1q`
autoconf: `($AUTOCONF --version) 2>/dev/null |$SED 1q`
Report bugs to <bug-libtool@gnu.org>.
-GNU libtool home page: <http://www.gnu.org/software/libtool/>.
-General help using GNU software: <http://www.gnu.org/gethelp/>."
+GNU libtool home page: <https://www.gnu.org/software/libtool/>.
+General help using GNU software: <https://www.gnu.org/gethelp/>."
exit 0
}
@@ -2264,12 +2517,17 @@ libtool_options_prep ()
opt_dry_run=false
opt_help=false
opt_mode=
+ opt_reorder_cache=false
opt_preserve_dup_deps=false
opt_quiet=false
+ opt_finishing=true
+ opt_warning=
nonopt=
preserve_args=
+ _G_rc_lt_options_prep=:
+
# Shorthand for --mode=foo, only valid as the first argument
case $1 in
clean|clea|cle|cl)
@@ -2293,11 +2551,16 @@ libtool_options_prep ()
uninstall|uninstal|uninsta|uninst|unins|unin|uni|un|u)
shift; set dummy --mode uninstall ${1+"$@"}; shift
;;
+ *)
+ _G_rc_lt_options_prep=false
+ ;;
esac
- # Pass back the list of options.
- func_quote_for_eval ${1+"$@"}
- libtool_options_prep_result=$func_quote_for_eval_result
+ if $_G_rc_lt_options_prep; then
+ # Pass back the list of options.
+ func_quote eval ${1+"$@"}
+ libtool_options_prep_result=$func_quote_result
+ fi
}
func_add_hook func_options_prep libtool_options_prep
@@ -2309,9 +2572,12 @@ libtool_parse_options ()
{
$debug_cmd
+ _G_rc_lt_parse_options=false
+
# Perform our own loop to consume as many options as possible in
# each iteration.
while test $# -gt 0; do
+ _G_match_lt_parse_options=:
_G_opt=$1
shift
case $_G_opt in
@@ -2345,14 +2611,18 @@ libtool_parse_options ()
clean|compile|execute|finish|install|link|relink|uninstall) ;;
# Catch anything else as an error
- *) func_error "invalid argument for $_G_opt"
+ *) func_error "invalid argument '$1' for $_G_opt"
exit_cmd=exit
- break
;;
esac
shift
;;
+ --no-finish)
+ opt_finishing=false
+ func_append preserve_args " $_G_opt"
+ ;;
+
--no-silent|--no-quiet)
opt_quiet=false
func_append preserve_args " $_G_opt"
@@ -2368,6 +2638,24 @@ libtool_parse_options ()
func_append preserve_args " $_G_opt"
;;
+ --reorder-cache)
+ opt_reorder_cache=true
+ shared_lib_dirs=$1
+ if test -n "$shared_lib_dirs"; then
+ case $1 in
+ # Must begin with /:
+ /*) ;;
+
+ # Catch anything else as an error (relative paths)
+ *) func_error "invalid argument '$1' for $_G_opt"
+ func_error "absolute paths are required for $_G_opt"
+ exit_cmd=exit
+ ;;
+ esac
+ fi
+ shift
+ ;;
+
--silent|--quiet)
opt_quiet=:
opt_verbose=false
@@ -2386,19 +2674,36 @@ libtool_parse_options ()
func_append preserve_args " $_G_opt"
;;
- # An option not handled by this hook function:
- *) set dummy "$_G_opt" ${1+"$@"}; shift; break ;;
+ # An option not handled by this hook function:
+ *) set dummy "$_G_opt" ${1+"$@"} ; shift
+ _G_match_lt_parse_options=false
+ break
+ ;;
esac
+ $_G_match_lt_parse_options && _G_rc_lt_parse_options=:
done
-
- # save modified positional parameters for caller
- func_quote_for_eval ${1+"$@"}
- libtool_parse_options_result=$func_quote_for_eval_result
+ if $_G_rc_lt_parse_options; then
+ # save modified positional parameters for caller
+ func_quote eval ${1+"$@"}
+ libtool_parse_options_result=$func_quote_result
+ fi
}
func_add_hook func_parse_options libtool_parse_options
+# func_warning ARG...
+# -------------------
+# Libtool warnings are not categorized, so override funclib.sh
+# func_warning with this simpler definition.
+func_warning ()
+{
+ if $opt_warning; then
+ $debug_cmd
+ $warning_func ${1+"$@"}
+ fi
+}
+
# libtool_validate_options [ARG]...
# ---------------------------------
@@ -2415,10 +2720,10 @@ libtool_validate_options ()
# preserve --debug
test : = "$debug_cmd" || func_append preserve_args " --debug"
- case $host in
+ case $host_os in
# Solaris2 added to fix http://debbugs.gnu.org/cgi/bugreport.cgi?bug=16452
# see also: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=59788
- *cygwin* | *mingw* | *pw32* | *cegcc* | *solaris2* | *os2*)
+ cygwin* | mingw* | windows* | pw32* | cegcc* | solaris2* | os2*)
# don't eliminate duplications in $postdeps and $predeps
opt_duplicate_compiler_generated_deps=:
;;
@@ -2451,8 +2756,8 @@ libtool_validate_options ()
}
# Pass back the unparsed argument list
- func_quote_for_eval ${1+"$@"}
- libtool_validate_options_result=$func_quote_for_eval_result
+ func_quote eval ${1+"$@"}
+ libtool_validate_options_result=$func_quote_result
}
func_add_hook func_validate_options libtool_validate_options
@@ -2750,7 +3055,7 @@ EOF
# func_convert_core_file_wine_to_w32 ARG
# Helper function used by file name conversion functions when $build is *nix,
-# and $host is mingw, cygwin, or some other w32 environment. Relies on a
+# and $host is mingw, windows, cygwin, or some other w32 environment. Relies on a
# correctly configured wine environment available, with the winepath program
# in $build's $PATH.
#
@@ -2782,9 +3087,10 @@ func_convert_core_file_wine_to_w32 ()
# func_convert_core_path_wine_to_w32 ARG
# Helper function used by path conversion functions when $build is *nix, and
-# $host is mingw, cygwin, or some other w32 environment. Relies on a correctly
-# configured wine environment available, with the winepath program in $build's
-# $PATH. Assumes ARG has no leading or trailing path separator characters.
+# $host is mingw, windows, cygwin, or some other w32 environment. Relies on a
+# correctly configured wine environment available, with the winepath program
+# in $build's $PATH. Assumes ARG has no leading or trailing path separator
+# characters.
#
# ARG is path to be converted from $build format to win32.
# Result is available in $func_convert_core_path_wine_to_w32_result.
@@ -2927,6 +3233,15 @@ func_convert_path_front_back_pathsep ()
# end func_convert_path_front_back_pathsep
+# func_convert_delimited_path PATH ORIG_DELIMITER NEW_DELIMITER
+# Replaces a delimiter for a given path.
+func_convert_delimited_path ()
+{
+ converted_path=`$ECHO "$1" | $SED "s#$2#$3#g"`
+}
+# end func_convert_delimited_path
+
+
##################################################
# $build to $host FILE NAME CONVERSION FUNCTIONS #
##################################################
@@ -3261,6 +3576,65 @@ func_dll_def_p ()
}
+# func_reorder_shared_lib_cache DIRS
+# Reorder the shared library cache by unconfiguring previous shared library cache
+# and configuring preferred search directories before previous search directories.
+# Previous shared library cache: /usr/lib /usr/local/lib
+# Preferred search directories: /tmp/testing
+# Reordered shared library cache: /tmp/testing /usr/lib /usr/local/lib
+func_reorder_shared_lib_cache ()
+{
+ $debug_cmd
+
+ case $host_os in
+ openbsd*)
+ get_search_directories=`PATH="$PATH:/sbin" ldconfig -r | $GREP "search directories" | $SED "s#.*search directories:\ ##g"`
+ func_convert_delimited_path "$get_search_directories" ':' '\ '
+ save_search_directories=$converted_path
+ func_convert_delimited_path "$1" ':' '\ '
+
+ # Ensure directories exist
+ for dir in $converted_path; do
+ # Ensure each directory is an absolute path
+ case $dir in
+ /*) ;;
+ *) func_error "Directory '$dir' is not an absolute path"
+ exit $EXIT_FAILURE ;;
+ esac
+ # Ensure no trailing slashes
+ func_stripname '' '/' "$dir"
+ dir=$func_stripname_result
+ if test -d "$dir"; then
+ if test -n "$preferred_search_directories"; then
+ preferred_search_directories="$preferred_search_directories $dir"
+ else
+ preferred_search_directories=$dir
+ fi
+ else
+ func_error "Directory '$dir' does not exist"
+ exit $EXIT_FAILURE
+ fi
+ done
+
+ PATH="$PATH:/sbin" ldconfig -U $save_search_directories
+ PATH="$PATH:/sbin" ldconfig -m $preferred_search_directories $save_search_directories
+ get_search_directories=`PATH="$PATH:/sbin" ldconfig -r | $GREP "search directories" | $SED "s#.*search directories:\ ##g"`
+ func_convert_delimited_path "$get_search_directories" ':' '\ '
+ reordered_search_directories=$converted_path
+
+ $ECHO "Original: $save_search_directories"
+ $ECHO "Reordered: $reordered_search_directories"
+ exit $EXIT_SUCCESS
+ ;;
+ *)
+ func_error "--reorder-cache is not supported for host_os=$host_os."
+ exit $EXIT_FAILURE
+ ;;
+ esac
+}
+# end func_reorder_shared_lib_cache
+
+
# func_mode_compile arg...
func_mode_compile ()
{
@@ -3418,8 +3792,8 @@ func_mode_compile ()
esac
done
- func_quote_for_eval "$libobj"
- test "X$libobj" != "X$func_quote_for_eval_result" \
+ func_quote_arg pretty "$libobj"
+ test "X$libobj" != "X$func_quote_arg_result" \
&& $ECHO "X$libobj" | $GREP '[]~#^*{};<>?"'"'"' &()|`$[]' \
&& func_warning "libobj name '$libobj' may not contain shell special characters."
func_dirname_and_basename "$obj" "/" ""
@@ -3439,7 +3813,7 @@ func_mode_compile ()
# On Cygwin there's no "real" PIC flag so we must build both object types
case $host_os in
- cygwin* | mingw* | pw32* | os2* | cegcc*)
+ cygwin* | mingw* | windows* | pw32* | os2* | cegcc*)
pic_mode=default
;;
esac
@@ -3492,8 +3866,8 @@ compiler."
func_to_tool_file "$srcfile" func_convert_file_msys_to_w32
srcfile=$func_to_tool_file_result
- func_quote_for_eval "$srcfile"
- qsrcfile=$func_quote_for_eval_result
+ func_quote_arg pretty "$srcfile"
+ qsrcfile=$func_quote_arg_result
# Only build a PIC object if we are building libtool libraries.
if test yes = "$build_libtool_libs"; then
@@ -3648,7 +4022,8 @@ This mode accepts the following additional options:
-prefer-non-pic try to build non-PIC objects only
-shared do not build a '.o' file suitable for static linking
-static only build a '.o' file suitable for static linking
- -Wc,FLAG pass FLAG directly to the compiler
+ -Wc,FLAG
+ -Xcompiler FLAG pass FLAG directly to the compiler
COMPILE-COMMAND is a command to be used in creating a 'standard' object file
from the given SOURCEFILE.
@@ -3754,6 +4129,8 @@ The following components of LINK-COMMAND are treated specially:
-weak LIBNAME declare that the target provides the LIBNAME interface
-Wc,FLAG
-Xcompiler FLAG pass linker-specific FLAG directly to the compiler
+ -Wa,FLAG
+ -Xassembler FLAG pass linker-specific FLAG directly to the assembler
-Wl,FLAG
-Xlinker FLAG pass linker-specific FLAG directly to the linker
-XCClinker FLAG pass link-specific FLAG to the compiler driver (CC)
@@ -3830,6 +4207,12 @@ if $opt_help; then
fi
+# If option '--reorder-cache', reorder the shared library cache and exit.
+if $opt_reorder_cache; then
+ func_reorder_shared_lib_cache $shared_lib_dirs
+fi
+
+
# func_mode_execute arg...
func_mode_execute ()
{
@@ -4014,7 +4397,7 @@ func_mode_finish ()
fi
fi
- if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then
+ if test -n "$finish_cmds$finish_eval" && test -n "$libdirs" && $opt_finishing; then
for libdir in $libdirs; do
if test -n "$finish_cmds"; then
# Do each command in the finish commands.
@@ -4039,6 +4422,12 @@ func_mode_finish ()
for libdir in $libdirs; do
$ECHO " $libdir"
done
+ if test "false" = "$opt_finishing"; then
+ echo
+ echo "NOTE: finish_cmds were not executed during testing, so you must"
+ echo "manually run ldconfig to add a given test directory, LIBDIR, to"
+ echo "the search path for generated executables."
+ fi
echo
echo "If you ever happen to want to link against installed libraries"
echo "in a given directory, LIBDIR, you must either use libtool, and"
@@ -4096,8 +4485,8 @@ func_mode_install ()
case $nonopt in *shtool*) :;; *) false;; esac
then
# Aesthetically quote it.
- func_quote_for_eval "$nonopt"
- install_prog="$func_quote_for_eval_result "
+ func_quote_arg pretty "$nonopt"
+ install_prog="$func_quote_arg_result "
arg=$1
shift
else
@@ -4107,8 +4496,8 @@ func_mode_install ()
# The real first argument should be the name of the installation program.
# Aesthetically quote it.
- func_quote_for_eval "$arg"
- func_append install_prog "$func_quote_for_eval_result"
+ func_quote_arg pretty "$arg"
+ func_append install_prog "$func_quote_arg_result"
install_shared_prog=$install_prog
case " $install_prog " in
*[\\\ /]cp\ *) install_cp=: ;;
@@ -4165,12 +4554,12 @@ func_mode_install ()
esac
# Aesthetically quote the argument.
- func_quote_for_eval "$arg"
- func_append install_prog " $func_quote_for_eval_result"
+ func_quote_arg pretty "$arg"
+ func_append install_prog " $func_quote_arg_result"
if test -n "$arg2"; then
- func_quote_for_eval "$arg2"
+ func_quote_arg pretty "$arg2"
fi
- func_append install_shared_prog " $func_quote_for_eval_result"
+ func_append install_shared_prog " $func_quote_arg_result"
done
test -z "$install_prog" && \
@@ -4181,8 +4570,8 @@ func_mode_install ()
if test -n "$install_override_mode" && $no_mode; then
if $install_cp; then :; else
- func_quote_for_eval "$install_override_mode"
- func_append install_shared_prog " -m $func_quote_for_eval_result"
+ func_quote_arg pretty "$install_override_mode"
+ func_append install_shared_prog " -m $func_quote_arg_result"
fi
fi
@@ -4275,8 +4664,15 @@ func_mode_install ()
func_append dir "$objdir"
if test -n "$relink_command"; then
+ # Strip any trailing slash from the destination.
+ func_stripname '' '/' "$libdir"
+ destlibdir=$func_stripname_result
+
+ func_stripname '' '/' "$destdir"
+ s_destdir=$func_stripname_result
+
# Determine the prefix the user has applied to our future dir.
- inst_prefix_dir=`$ECHO "$destdir" | $SED -e "s%$libdir\$%%"`
+ inst_prefix_dir=`$ECHO "X$s_destdir" | $Xsed -e "s%$destlibdir\$%%"`
# Don't allow the user to place us outside of our expected
# location b/c this prevents finding dependent libraries that
@@ -4313,7 +4709,7 @@ func_mode_install ()
'exit $?'
tstripme=$stripme
case $host_os in
- cygwin* | mingw* | pw32* | cegcc*)
+ cygwin* | mingw* | windows* | pw32* | cegcc*)
case $realname in
*.dll.a)
tstripme=
@@ -4426,7 +4822,7 @@ func_mode_install ()
# Do a test to see if this is really a libtool program.
case $host in
- *cygwin* | *mingw*)
+ *cygwin* | *mingw* | *windows*)
if func_ltwrapper_executable_p "$file"; then
func_ltwrapper_scriptname "$file"
wrapper=$func_ltwrapper_scriptname_result
@@ -4478,8 +4874,8 @@ func_mode_install ()
relink_command=`$ECHO "$relink_command" | $SED 's%@OUTPUT@%'"$outputname"'%g'`
$opt_quiet || {
- func_quote_for_expand "$relink_command"
- eval "func_echo $func_quote_for_expand_result"
+ func_quote_arg expand,pretty "$relink_command"
+ eval "func_echo $func_quote_arg_result"
}
if eval "$relink_command"; then :
else
@@ -4654,7 +5050,7 @@ extern \"C\" {
$RM $export_symbols
eval "$SED -n -e '/^: @PROGRAM@ $/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"'
case $host in
- *cygwin* | *mingw* | *cegcc* )
+ *cygwin* | *mingw* | *windows* | *cegcc* )
eval "echo EXPORTS "'> "$output_objdir/$outputname.def"'
eval 'cat "$export_symbols" >> "$output_objdir/$outputname.def"'
;;
@@ -4666,7 +5062,7 @@ extern \"C\" {
eval '$GREP -f "$output_objdir/$outputname.exp" < "$nlist" > "$nlist"T'
eval '$MV "$nlist"T "$nlist"'
case $host in
- *cygwin* | *mingw* | *cegcc* )
+ *cygwin* | *mingw* | *windows* | *cegcc* )
eval "echo EXPORTS "'> "$output_objdir/$outputname.def"'
eval 'cat "$nlist" >> "$output_objdir/$outputname.def"'
;;
@@ -4680,7 +5076,7 @@ extern \"C\" {
func_basename "$dlprefile"
name=$func_basename_result
case $host in
- *cygwin* | *mingw* | *cegcc* )
+ *cygwin* | *mingw* | *windows* | *cegcc* )
# if an import library, we need to obtain dlname
if func_win32_import_lib_p "$dlprefile"; then
func_tr_sh "$dlprefile"
@@ -4706,8 +5102,16 @@ extern \"C\" {
eval '$ECHO ": $name " >> "$nlist"'
fi
func_to_tool_file "$dlprefile" func_convert_file_msys_to_w32
- eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe |
- $SED -e '/I __imp/d' -e 's/I __nm_/D /;s/_nm__//' >> '$nlist'"
+ case $host in
+ i[3456]86-*-mingw32*)
+ eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe |
+ $SED -e '/I __imp/d' -e 's/I __nm_/D /;s/_nm__//' >> '$nlist'"
+ ;;
+ *)
+ eval "$NM \"$func_to_tool_file_result\" 2>/dev/null | $global_symbol_pipe |
+ $SED -e '/I __imp/d' -e 's/I __nm_/D /;s/__nm_//' >> '$nlist'"
+ ;;
+ esac
}
else # not an import lib
$opt_dry_run || {
@@ -4855,7 +5259,7 @@ static const void *lt_preloaded_setup() {
# Transform the symbol file into the correct name.
symfileobj=$output_objdir/${my_outputname}S.$objext
case $host in
- *cygwin* | *mingw* | *cegcc* )
+ *cygwin* | *mingw* | *windows* | *cegcc* )
if test -f "$output_objdir/$my_outputname.def"; then
compile_command=`$ECHO "$compile_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"`
finalize_command=`$ECHO "$finalize_command" | $SED "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"`
@@ -4931,7 +5335,7 @@ func_win32_libid ()
*ar\ archive*) # could be an import, or static
# Keep the egrep pattern in sync with the one in _LT_CHECK_MAGIC_METHOD.
if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null |
- $EGREP 'file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' >/dev/null; then
+ $EGREP 'file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64|pe-aarch64)' >/dev/null; then
case $nm_interface in
"MS dumpbin")
if func_cygming_ms_implib_p "$1" ||
@@ -5198,7 +5602,7 @@ func_extract_archives ()
#
# Emit a libtool wrapper script on stdout.
# Don't directly open a file because we may want to
-# incorporate the script contents within a cygwin/mingw
+# incorporate the script contents within a cygwin/mingw/windows
# wrapper executable. Must ONLY be called from within
# func_mode_link because it depends on a number of variables
# set therein.
@@ -5206,7 +5610,7 @@ func_extract_archives ()
# ARG is the value that the WRAPPER_SCRIPT_BELONGS_IN_OBJDIR
# variable will take. If 'yes', then the emitted script
# will assume that the directory where it is stored is
-# the $objdir directory. This is a cygwin/mingw-specific
+# the $objdir directory. This is a cygwin/mingw/windows-specific
# behavior.
func_emit_wrapper ()
{
@@ -5258,7 +5662,8 @@ else
if test \"\$libtool_execute_magic\" != \"$magic\"; then
file=\"\$0\""
- qECHO=`$ECHO "$ECHO" | $SED "$sed_quote_subst"`
+ func_quote_arg pretty "$ECHO"
+ qECHO=$func_quote_arg_result
$ECHO "\
# A function that is used when there is no print builtin or printf.
@@ -5268,7 +5673,7 @@ func_fallback_echo ()
\$1
_LTECHO_EOF'
}
- ECHO=\"$qECHO\"
+ ECHO=$qECHO
fi
# Very basic option parsing. These options are (a) specific to
@@ -5330,7 +5735,7 @@ func_exec_program_core ()
"
case $host in
# Backslashes separate directories on plain windows
- *-*-mingw | *-*-os2* | *-cegcc*)
+ *-*-mingw* | *-*-windows* | *-*-os2* | *-cegcc*)
$ECHO "\
if test -n \"\$lt_option_debug\"; then
\$ECHO \"$outputname:$output:\$LINENO: newargv[0]: \$progdir\\\\\$program\" 1>&2
@@ -5398,7 +5803,7 @@ func_exec_program ()
file=\`ls -ld \"\$thisdir/\$file\" | $SED -n 's/.*-> //p'\`
done
- # Usually 'no', except on cygwin/mingw when embedded into
+ # Usually 'no', except on cygwin/mingw/windows when embedded into
# the cwrapper.
WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=$func_emit_wrapper_arg1
if test \"\$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR\" = \"yes\"; then
@@ -5530,7 +5935,7 @@ EOF
#endif
#include <stdio.h>
#include <stdlib.h>
-#ifdef _MSC_VER
+#if defined _WIN32 && !defined __GNUC__
# include <direct.h>
# include <process.h>
# include <io.h>
@@ -5555,7 +5960,7 @@ EOF
/* declarations of non-ANSI functions */
#if defined __MINGW32__
# ifdef __STRICT_ANSI__
-int _putenv (const char *);
+_CRTIMP int __cdecl _putenv (const char *);
# endif
#elif defined __CYGWIN__
# ifdef __STRICT_ANSI__
@@ -5753,7 +6158,7 @@ main (int argc, char *argv[])
{
EOF
case $host in
- *mingw* | *cygwin* )
+ *mingw* | *windows* | *cygwin* )
# make stdout use "unix" line endings
echo " setmode(1,_O_BINARY);"
;;
@@ -5772,7 +6177,7 @@ EOF
{
/* however, if there is an option in the LTWRAPPER_OPTION_PREFIX
namespace, but it is not one of the ones we know about and
- have already dealt with, above (inluding dump-script), then
+ have already dealt with, above (including dump-script), then
report an error. Otherwise, targets might begin to believe
they are allowed to use options in the LTWRAPPER_OPTION_PREFIX
namespace. The first time any user complains about this, we'll
@@ -5856,7 +6261,7 @@ EOF
EOF
case $host_os in
- mingw*)
+ mingw* | windows*)
cat <<"EOF"
{
char* p;
@@ -5898,7 +6303,7 @@ EOF
EOF
case $host_os in
- mingw*)
+ mingw* | windows*)
cat <<"EOF"
/* execv doesn't actually work on mingw as expected on unix */
newargz = prepare_spawn (newargz);
@@ -6317,7 +6722,7 @@ lt_update_lib_path (const char *name, const char *value)
EOF
case $host_os in
- mingw*)
+ mingw* | windows*)
cat <<"EOF"
/* Prepares an argument vector before calling spawn().
@@ -6492,7 +6897,7 @@ func_mode_link ()
$debug_cmd
case $host in
- *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*)
+ *-*-cygwin* | *-*-mingw* | *-*-windows* | *-*-pw32* | *-*-os2* | *-cegcc*)
# It is impossible to link a dll without this setting, and
# we shouldn't force the makefile maintainer to figure out
# what system we are compiling for in order to pass an extra
@@ -6516,6 +6921,7 @@ func_mode_link ()
finalize_command=$nonopt
compile_rpath=
+ compile_rpath_tail=
finalize_rpath=
compile_shlibpath=
finalize_shlibpath=
@@ -6556,10 +6962,12 @@ func_mode_link ()
xrpath=
perm_rpath=
temp_rpath=
+ temp_rpath_tail=
thread_safe=no
vinfo=
vinfo_number=no
weak_libs=
+ rpath_arg=
single_module=$wl-single_module
func_infer_tag $base_compile
@@ -6611,9 +7019,9 @@ func_mode_link ()
while test "$#" -gt 0; do
arg=$1
shift
- func_quote_for_eval "$arg"
- qarg=$func_quote_for_eval_unquoted_result
- func_append libtool_args " $func_quote_for_eval_result"
+ func_quote_arg pretty,unquoted "$arg"
+ qarg=$func_quote_arg_unquoted_result
+ func_append libtool_args " $func_quote_arg_result"
# If the previous option needs an argument, assign it.
if test -n "$prev"; then
@@ -6822,7 +7230,7 @@ func_mode_link ()
case $arg in
[\\/]* | [A-Za-z]:[\\/]*) ;;
*)
- func_fatal_error "only absolute run-paths are allowed"
+ func_fatal_error "argument to -rpath is not absolute: $arg"
;;
esac
if test rpath = "$prev"; then
@@ -6849,6 +7257,13 @@ func_mode_link ()
prev=
continue
;;
+ xassembler)
+ func_append compiler_flags " -Xassembler $qarg"
+ prev=
+ func_append compile_command " -Xassembler $qarg"
+ func_append finalize_command " -Xassembler $qarg"
+ continue
+ ;;
xcclinker)
func_append linker_flags " $qarg"
func_append compiler_flags " $qarg"
@@ -6991,7 +7406,7 @@ func_mode_link ()
;;
esac
case $host in
- *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*)
+ *-*-cygwin* | *-*-mingw* | *-*-windows* | *-*-pw32* | *-*-os2* | *-cegcc*)
testbindir=`$ECHO "$dir" | $SED 's*/lib$*/bin*'`
case :$dllsearchpath: in
*":$dir:"*) ;;
@@ -7011,7 +7426,7 @@ func_mode_link ()
-l*)
if test X-lc = "X$arg" || test X-lm = "X$arg"; then
case $host in
- *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-beos* | *-cegcc* | *-*-haiku*)
+ *-*-cygwin* | *-*-mingw* | *-*-windows* | *-*-pw32* | *-*-beos* | *-cegcc* | *-*-haiku*)
# These systems don't actually have a C or math library (as such)
continue
;;
@@ -7019,7 +7434,7 @@ func_mode_link ()
# These systems don't actually have a C library (as such)
test X-lc = "X$arg" && continue
;;
- *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-bitrig*)
+ *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-midnightbsd*)
# Do not include libc due to us having libc/libc_r.
test X-lc = "X$arg" && continue
;;
@@ -7039,7 +7454,7 @@ func_mode_link ()
esac
elif test X-lc_r = "X$arg"; then
case $host in
- *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-bitrig*)
+ *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-midnightbsd*)
# Do not include libc_r directly, use -pthread flag.
continue
;;
@@ -7062,16 +7477,29 @@ func_mode_link ()
# Tru64 UNIX uses -model [arg] to determine the layout of C++
# classes, name mangling, and exception handling.
# Darwin uses the -arch flag to determine output architecture.
- -model|-arch|-isysroot|--sysroot)
+ # -q <option> for IBM XL C/C++ compiler.
+ -model|-arch|-isysroot|--sysroot|-q)
func_append compiler_flags " $arg"
func_append compile_command " $arg"
func_append finalize_command " $arg"
prev=xcompiler
continue
;;
-
- -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \
- |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*)
+ # Solaris ld rejects as of 11.4. Refer to Oracle bug 22985199.
+ -pthread)
+ case $host in
+ *solaris2*) ;;
+ *)
+ case "$new_inherited_linker_flags " in
+ *" $arg "*) ;;
+ * ) func_append new_inherited_linker_flags " $arg" ;;
+ esac
+ ;;
+ esac
+ continue
+ ;;
+ -mt|-mthreads|-kthread|-Kthread|-pthreads|--thread-safe \
+ |-threads|-fopenmp|-fopenmp=*|-openmp|-mp|-xopenmp|-omp|-qsmp=*)
func_append compiler_flags " $arg"
func_append compile_command " $arg"
func_append finalize_command " $arg"
@@ -7094,7 +7522,7 @@ func_mode_link ()
-no-install)
case $host in
- *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-darwin* | *-cegcc*)
+ *-*-cygwin* | *-*-mingw* | *-*-windows* | *-*-pw32* | *-*-os2* | *-*-darwin* | *-cegcc*)
# The PATH hackery in wrapper scripts is required on Windows
# and Darwin in order for the loader to find any dlls it needs.
func_warning "'-no-install' is ignored for $host"
@@ -7154,7 +7582,7 @@ func_mode_link ()
dir=$lt_sysroot$func_stripname_result
;;
*)
- func_fatal_error "only absolute run-paths are allowed"
+ func_fatal_error "argument ($arg) to '-R' is not an absolute path: $dir"
;;
esac
case "$xrpath " in
@@ -7211,9 +7639,9 @@ func_mode_link ()
save_ifs=$IFS; IFS=,
for flag in $args; do
IFS=$save_ifs
- func_quote_for_eval "$flag"
- func_append arg " $func_quote_for_eval_result"
- func_append compiler_flags " $func_quote_for_eval_result"
+ func_quote_arg pretty "$flag"
+ func_append arg " $func_quote_arg_result"
+ func_append compiler_flags " $func_quote_arg_result"
done
IFS=$save_ifs
func_stripname ' ' '' "$arg"
@@ -7227,16 +7655,21 @@ func_mode_link ()
save_ifs=$IFS; IFS=,
for flag in $args; do
IFS=$save_ifs
- func_quote_for_eval "$flag"
- func_append arg " $wl$func_quote_for_eval_result"
- func_append compiler_flags " $wl$func_quote_for_eval_result"
- func_append linker_flags " $func_quote_for_eval_result"
+ func_quote_arg pretty "$flag"
+ func_append arg " $wl$func_quote_arg_result"
+ func_append compiler_flags " $wl$func_quote_arg_result"
+ func_append linker_flags " $func_quote_arg_result"
done
IFS=$save_ifs
func_stripname ' ' '' "$arg"
arg=$func_stripname_result
;;
+ -Xassembler)
+ prev=xassembler
+ continue
+ ;;
+
-Xcompiler)
prev=xcompiler
continue
@@ -7254,8 +7687,8 @@ func_mode_link ()
# -msg_* for osf cc
-msg_*)
- func_quote_for_eval "$arg"
- arg=$func_quote_for_eval_result
+ func_quote_arg pretty "$arg"
+ arg=$func_quote_arg_result
;;
# Flags to be passed through unchanged, with rationale:
@@ -7274,12 +7707,31 @@ func_mode_link ()
# -O*, -g*, -flto*, -fwhopr*, -fuse-linker-plugin GCC link-time optimization
# -specs=* GCC specs files
# -stdlib=* select c++ std lib with clang
+ # -fdiagnostics-color* simply affects output
+ # -frecord-gcc-switches used to verify flags were respected
+ # -fsanitize=* Clang/GCC memory and address sanitizer
+ # -fno-sanitize* Clang/GCC memory and address sanitizer
+ # -shared-libsan Link with shared sanitizer runtimes (Clang)
+ # -static-libsan Link with static sanitizer runtimes (Clang)
+ # -no-canonical-prefixes Do not expand any symbolic links
+ # -fuse-ld=* Linker select flags for GCC
+ # -static-* direct GCC to link specific libraries statically
+ # -fcilkplus Cilk Plus language extension features for C/C++
+ # -rtlib=* select c runtime lib with clang
+ # --unwindlib=* select unwinder library with clang
+ # -f{file|debug|macro|profile}-prefix-map=* needed for lto linking
+ # -Wa,* Pass flags directly to the assembler
+ # -Werror, -Werror=* Report (specified) warnings as errors
-64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \
-t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*|-tp=*|--sysroot=*| \
- -O*|-g*|-flto*|-fwhopr*|-fuse-linker-plugin|-fstack-protector*|-stdlib=*| \
- -specs=*)
- func_quote_for_eval "$arg"
- arg=$func_quote_for_eval_result
+ -O*|-g*|-flto*|-fwhopr*|-fuse-linker-plugin|-fstack-protector*|-no-canonical-prefixes| \
+ -stdlib=*|-rtlib=*|--unwindlib=*| \
+ -specs=*|-fsanitize=*|-fno-sanitize*|-shared-libsan|-static-libsan| \
+ -ffile-prefix-map=*|-fdebug-prefix-map=*|-fmacro-prefix-map=*|-fprofile-prefix-map=*| \
+ -fdiagnostics-color*|-frecord-gcc-switches| \
+ -fuse-ld=*|-static-*|-fcilkplus|-Wa,*|-Werror|-Werror=*)
+ func_quote_arg pretty "$arg"
+ arg=$func_quote_arg_result
func_append compile_command " $arg"
func_append finalize_command " $arg"
func_append compiler_flags " $arg"
@@ -7300,15 +7752,15 @@ func_mode_link ()
continue
else
# Otherwise treat like 'Some other compiler flag' below
- func_quote_for_eval "$arg"
- arg=$func_quote_for_eval_result
+ func_quote_arg pretty "$arg"
+ arg=$func_quote_arg_result
fi
;;
# Some other compiler flag.
-* | +*)
- func_quote_for_eval "$arg"
- arg=$func_quote_for_eval_result
+ func_quote_arg pretty "$arg"
+ arg=$func_quote_arg_result
;;
*.$objext)
@@ -7428,15 +7880,27 @@ func_mode_link ()
*)
# Unknown arguments in both finalize_command and compile_command need
# to be aesthetically quoted because they are evaled later.
- func_quote_for_eval "$arg"
- arg=$func_quote_for_eval_result
+ func_quote_arg pretty "$arg"
+ arg=$func_quote_arg_result
;;
esac # arg
# Now actually substitute the argument into the commands.
if test -n "$arg"; then
- func_append compile_command " $arg"
- func_append finalize_command " $arg"
+ if test -n "$rpath_arg"; then
+ func_append finalize_rpath " ${arg##*,}"
+ unset rpath_arg
+ else
+ case $arg in
+ -Wl,-rpath,*)
+ func_append finalize_rpath " ${arg##*,}";;
+ -Wl,-rpath)
+ rpath_arg=1;;
+ *)
+ func_append compile_command " $arg"
+ func_append finalize_command " $arg"
+ esac
+ fi
fi
done # argument parsing loop
@@ -7607,7 +8071,7 @@ func_mode_link ()
found=false
case $deplib in
-mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \
- |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*)
+ |-threads|-fopenmp|-fopenmp=*|-openmp|-mp|-xopenmp|-omp|-qsmp=*)
if test prog,link = "$linkmode,$pass"; then
compile_deplibs="$deplib $compile_deplibs"
finalize_deplibs="$deplib $finalize_deplibs"
@@ -7784,18 +8248,15 @@ func_mode_link ()
;;
esac
if $valid_a_lib; then
- echo
- $ECHO "*** Warning: Linking the shared library $output against the"
- $ECHO "*** static library $deplib is not portable!"
+ func_warning "Linking the shared library $output against the static library $deplib is not portable!"
deplibs="$deplib $deplibs"
else
- echo
- $ECHO "*** Warning: Trying to link with static lib archive $deplib."
- echo "*** I have the capability to make that library automatically link in when"
- echo "*** you link to this library. But I can only do this if you have a"
- echo "*** shared version of the library, which you do not appear to have"
- echo "*** because the file extensions .$libext of this argument makes me believe"
- echo "*** that it is just a static archive that I should not use here."
+ func_warning "Trying to link with static lib archive $deplib."
+ func_warning "I have the capability to make that library automatically link in when"
+ func_warning "you link to this library. But I can only do this if you have a"
+ func_warning "shared version of the library, which you do not appear to have"
+ func_warning "because the file extensions .$libext of this argument makes me believe"
+ func_warning "that it is just a static archive that I should not use here."
fi
;;
esac
@@ -7990,7 +8451,7 @@ func_mode_link ()
fi
case $host in
# special handling for platforms with PE-DLLs.
- *cygwin* | *mingw* | *cegcc* )
+ *cygwin* | *mingw* | *windows* | *cegcc* )
# Linker will automatically link against shared library if both
# static and shared are present. Therefore, ensure we extract
# symbols from the import library if a shared library is present
@@ -8090,7 +8551,10 @@ func_mode_link ()
# Make sure the rpath contains only unique directories.
case $temp_rpath: in
*"$absdir:"*) ;;
- *) func_append temp_rpath "$absdir:" ;;
+ *) case $absdir in
+ "$progdir/"*) func_append temp_rpath "$absdir:" ;;
+ *) func_append temp_rpath_tail "$absdir:" ;;
+ esac
esac
fi
@@ -8100,9 +8564,12 @@ func_mode_link ()
case " $sys_lib_dlsearch_path " in
*" $absdir "*) ;;
*)
- case "$compile_rpath " in
+ case "$compile_rpath$compile_rpath_tail " in
*" $absdir "*) ;;
- *) func_append compile_rpath " $absdir" ;;
+ *) case $absdir in
+ "$progdir/"*) func_append compile_rpath " $absdir" ;;
+ *) func_append compile_rpath_tail " $absdir" ;;
+ esac
esac
;;
esac
@@ -8133,8 +8600,8 @@ func_mode_link ()
fi
if test -n "$library_names" &&
{ test no = "$use_static_libs" || test -z "$old_library"; }; then
- case $host in
- *cygwin* | *mingw* | *cegcc* | *os2*)
+ case $host_os in
+ cygwin* | mingw* | windows* | cegcc* | os2*)
# No point in relinking DLLs because paths are not encoded
func_append notinst_deplibs " $lib"
need_relink=no
@@ -8160,11 +8627,11 @@ func_mode_link ()
if test -z "$dlopenmodule" && test yes = "$shouldnotlink" && test link = "$pass"; then
echo
if test prog = "$linkmode"; then
- $ECHO "*** Warning: Linking the executable $output against the loadable module"
+ func_warning "Linking the executable $output against the loadable module"
else
- $ECHO "*** Warning: Linking the shared library $output against the loadable module"
+ func_warning "Linking the shared library $output against the loadable module"
fi
- $ECHO "*** $linklib is not portable!"
+ func_warning "$linklib is not portable!"
fi
if test lib = "$linkmode" &&
test yes = "$hardcode_into_libs"; then
@@ -8174,9 +8641,12 @@ func_mode_link ()
case " $sys_lib_dlsearch_path " in
*" $absdir "*) ;;
*)
- case "$compile_rpath " in
+ case "$compile_rpath$compile_rpath_tail " in
*" $absdir "*) ;;
- *) func_append compile_rpath " $absdir" ;;
+ *) case $absdir in
+ "$progdir/"*) func_append compile_rpath " $absdir" ;;
+ *) func_append compile_rpath_tail " $absdir" ;;
+ esac
esac
;;
esac
@@ -8203,8 +8673,8 @@ func_mode_link ()
soname=$dlname
elif test -n "$soname_spec"; then
# bleh windows
- case $host in
- *cygwin* | mingw* | *cegcc* | *os2*)
+ case $host_os in
+ cygwin* | mingw* | windows* | cegcc* | os2*)
func_arith $current - $age
major=$func_arith_result
versuffix=-$major
@@ -8251,6 +8721,7 @@ func_mode_link ()
case $host in
*-*-sco3.2v5.0.[024]*) add_dir=-L$dir ;;
*-*-sysv4*uw2*) add_dir=-L$dir ;;
+ *-*-emscripten*) add_dir=-L$dir ;;
*-*-sysv5OpenUNIX* | *-*-sysv5UnixWare7.[01].[10]* | \
*-*-unixware7*) add_dir=-L$dir ;;
*-*-darwin* )
@@ -8259,11 +8730,10 @@ func_mode_link ()
if /usr/bin/file -L $add 2> /dev/null |
$GREP ": [^:]* bundle" >/dev/null; then
if test "X$dlopenmodule" != "X$lib"; then
- $ECHO "*** Warning: lib $linklib is a module, not a shared library"
+ func_warning "lib $linklib is a module, not a shared library"
if test -z "$old_library"; then
- echo
- echo "*** And there doesn't seem to be a static archive available"
- echo "*** The link will probably fail, sorry"
+ func_warning "And there doesn't seem to be a static archive available"
+ func_warning "The link will probably fail, sorry"
else
add=$dir/$old_library
fi
@@ -8346,7 +8816,7 @@ func_mode_link ()
test no = "$hardcode_direct_absolute"; then
add=$libdir/$linklib
elif test yes = "$hardcode_minus_L"; then
- add_dir=-L$libdir
+ add_dir=-L$lt_sysroot$libdir
add=-l$name
elif test yes = "$hardcode_shlibpath_var"; then
case :$finalize_shlibpath: in
@@ -8363,7 +8833,7 @@ func_mode_link ()
fi
else
# We cannot seem to hardcode it, guess we'll fake it.
- add_dir=-L$libdir
+ add_dir=-L$lt_sysroot$libdir
# Try looking first in the location we're being installed to.
if test -n "$inst_prefix_dir"; then
case $libdir in
@@ -8403,21 +8873,19 @@ func_mode_link ()
# Just print a warning and add the library to dependency_libs so
# that the program can be linked against the static library.
- echo
- $ECHO "*** Warning: This system cannot link to static lib archive $lib."
- echo "*** I have the capability to make that library automatically link in when"
- echo "*** you link to this library. But I can only do this if you have a"
- echo "*** shared version of the library, which you do not appear to have."
+ func_warning "This system cannot link to static lib archive $lib."
+ func_warning "I have the capability to make that library automatically link in when"
+ func_warning "you link to this library. But I can only do this if you have a"
+ func_warning "shared version of the library, which you do not appear to have."
if test yes = "$module"; then
- echo "*** But as you try to build a module library, libtool will still create "
- echo "*** a static module, that should work as long as the dlopening application"
- echo "*** is linked with the -dlopen flag to resolve symbols at runtime."
+ func_warning "But as you try to build a module library, libtool will still create "
+ func_warning "a static module, that should work as long as the dlopening application"
+ func_warning "is linked with the -dlopen flag to resolve symbols at runtime."
if test -z "$global_symbol_pipe"; then
- echo
- echo "*** However, this would only work if libtool was able to extract symbol"
- echo "*** lists from a program, using 'nm' or equivalent, but libtool could"
- echo "*** not find such a program. So, this module is probably useless."
- echo "*** 'nm' from GNU binutils and a full rebuild may help."
+ func_warning "However, this would only work if libtool was able to extract symbol"
+ func_warning "lists from a program, using 'nm' or equivalent, but libtool could"
+ func_warning "not find such a program. So, this module is probably useless."
+ func_warning "'nm' from GNU binutils and a full rebuild may help."
fi
if test no = "$build_old_libs"; then
build_libtool_libs=module
@@ -8540,6 +9008,10 @@ func_mode_link ()
fi # link_all_deplibs != no
fi # linkmode = lib
done # for deplib in $libs
+
+ func_append temp_rpath "$temp_rpath_tail"
+ func_append compile_rpath "$compile_rpath_tail"
+
if test link = "$pass"; then
if test prog = "$linkmode"; then
compile_deplibs="$new_inherited_linker_flags $compile_deplibs"
@@ -8577,42 +9049,46 @@ func_mode_link ()
# Add libraries to $var in reverse order
eval tmp_libs=\"\$$var\"
new_libs=
+ # FIXME: Pedantically, this is the right thing to do, so
+ # that some nasty dependency loop isn't accidentally
+ # broken: new_libs="$deplib $new_libs"
for deplib in $tmp_libs; do
- # FIXME: Pedantically, this is the right thing to do, so
- # that some nasty dependency loop isn't accidentally
- # broken:
- #new_libs="$deplib $new_libs"
- # Pragmatically, this seems to cause very few problems in
- # practice:
- case $deplib in
- -L*) new_libs="$deplib $new_libs" ;;
- -R*) ;;
- *)
- # And here is the reason: when a library appears more
- # than once as an explicit dependence of a library, or
- # is implicitly linked in more than once by the
- # compiler, it is considered special, and multiple
- # occurrences thereof are not removed. Compare this
- # with having the same library being listed as a
- # dependency of multiple other libraries: in this case,
- # we know (pedantically, we assume) the library does not
- # need to be listed more than once, so we keep only the
- # last copy. This is not always right, but it is rare
- # enough that we require users that really mean to play
- # such unportable linking tricks to link the library
- # using -Wl,-lname, so that libtool does not consider it
- # for duplicate removal.
- case " $specialdeplibs " in
- *" $deplib "*) new_libs="$deplib $new_libs" ;;
+ if $opt_preserve_dup_deps; then
+ new_libs="$deplib $new_libs"
+ else
+ # Pragmatically, this seems to cause very few problems in
+ # practice:
+ case $deplib in
+ -L*) new_libs="$deplib $new_libs" ;;
+ -R*) ;;
*)
- case " $new_libs " in
- *" $deplib "*) ;;
- *) new_libs="$deplib $new_libs" ;;
- esac
- ;;
+ # And here is the reason: when a library appears more
+ # than once as an explicit dependence of a library, or
+ # is implicitly linked in more than once by the
+ # compiler, it is considered special, and multiple
+ # occurrences thereof are not removed. Compare this
+ # with having the same library being listed as a
+ # dependency of multiple other libraries: in this case,
+ # we know (pedantically, we assume) the library does not
+ # need to be listed more than once, so we keep only the
+ # last copy. This is not always right, but it is rare
+ # enough that we require users that really mean to play
+ # such unportable linking tricks to link the library
+ # using -Wl,-lname, so that libtool does not consider it
+ # for duplicate removal. And if not possible for portability
+ # reasons, then --preserve-dup-deps should be used.
+ case " $specialdeplibs " in
+ *" $deplib "*) new_libs="$deplib $new_libs" ;;
+ *)
+ case " $new_libs " in
+ *" $deplib "*) ;;
+ *) new_libs="$deplib $new_libs" ;;
+ esac
+ ;;
+ esac
+ ;;
esac
- ;;
- esac
+ fi
done
tmp_libs=
for deplib in $new_libs; do
@@ -8634,7 +9110,7 @@ func_mode_link ()
test CXX = "$tagname" && {
case $host_os in
linux*)
- case `$CC -V 2>&1 | sed 5q` in
+ case `$CC -V 2>&1 | $SED 5q` in
*Sun\ C*) # Sun C++ 5.9
func_suncc_cstd_abi
@@ -8744,9 +9220,7 @@ func_mode_link ()
if test pass_all != "$deplibs_check_method"; then
func_fatal_error "cannot build libtool library '$output' from non-libtool objects on this host:$objs"
else
- echo
- $ECHO "*** Warning: Linking the shared library $output against the non-libtool"
- $ECHO "*** objects $objs is not portable!"
+ func_warning "Linking the shared library $output against the non-libtool objects $objs is not portable!"
func_append libobjs " $objs"
fi
fi
@@ -8807,13 +9281,13 @@ func_mode_link ()
#
case $version_type in
# correct linux to gnu/linux during the next big refactor
- darwin|freebsd-elf|linux|osf|windows|none)
+ darwin|freebsd-elf|linux|midnightbsd-elf|osf|qnx|windows|none)
func_arith $number_major + $number_minor
current=$func_arith_result
age=$number_minor
revision=$number_revision
;;
- freebsd-aout|qnx|sunos)
+ freebsd-aout|sco|sunos)
current=$number_major
revision=$number_minor
age=0
@@ -8825,6 +9299,9 @@ func_mode_link ()
revision=$number_minor
lt_irix_increment=no
;;
+ *)
+ func_fatal_configuration "$modename: unknown library version type '$version_type'"
+ ;;
esac
;;
no)
@@ -8898,7 +9375,7 @@ func_mode_link ()
versuffix=.$current.$revision
;;
- freebsd-elf)
+ freebsd-elf | midnightbsd-elf)
func_arith $current - $age
major=.$func_arith_result
versuffix=$major.$age.$revision
@@ -8960,8 +9437,9 @@ func_mode_link ()
;;
qnx)
- major=.$current
- versuffix=.$current
+ func_arith $current - $age
+ major=.$func_arith_result
+ versuffix=$major.$age.$revision
;;
sco)
@@ -9114,7 +9592,7 @@ func_mode_link ()
if test yes = "$build_libtool_libs"; then
if test -n "$rpath"; then
case $host in
- *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos* | *-cegcc* | *-*-haiku*)
+ *-*-cygwin* | *-*-mingw* | *-*-windows* | *-*-pw32* | *-*-os2* | *-*-beos* | *-cegcc* | *-*-haiku*)
# these systems don't actually have a c library (as such)!
;;
*-*-rhapsody* | *-*-darwin1.[012])
@@ -9124,7 +9602,7 @@ func_mode_link ()
*-*-netbsd*)
# Don't link with libc until the a.out ld.so is fixed.
;;
- *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*)
+ *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-midnightbsd*)
# Do not include libc due to us having libc/libc_r.
;;
*-*-sco3.2v5* | *-*-sco5v6*)
@@ -9165,108 +9643,6 @@ func_mode_link ()
# implementing what was already the behavior.
newdeplibs=$deplibs
;;
- test_compile)
- # This code stresses the "libraries are programs" paradigm to its
- # limits. Maybe even breaks it. We compile a program, linking it
- # against the deplibs as a proxy for the library. Then we can check
- # whether they linked in statically or dynamically with ldd.
- $opt_dry_run || $RM conftest.c
- cat > conftest.c <<EOF
- int main() { return 0; }
-EOF
- $opt_dry_run || $RM conftest
- if $LTCC $LTCFLAGS -o conftest conftest.c $deplibs; then
- ldd_output=`ldd conftest`
- for i in $deplibs; do
- case $i in
- -l*)
- func_stripname -l '' "$i"
- name=$func_stripname_result
- if test yes = "$allow_libtool_libs_with_static_runtimes"; then
- case " $predeps $postdeps " in
- *" $i "*)
- func_append newdeplibs " $i"
- i=
- ;;
- esac
- fi
- if test -n "$i"; then
- libname=`eval "\\$ECHO \"$libname_spec\""`
- deplib_matches=`eval "\\$ECHO \"$library_names_spec\""`
- set dummy $deplib_matches; shift
- deplib_match=$1
- if test `expr "$ldd_output" : ".*$deplib_match"` -ne 0; then
- func_append newdeplibs " $i"
- else
- droppeddeps=yes
- echo
- $ECHO "*** Warning: dynamic linker does not accept needed library $i."
- echo "*** I have the capability to make that library automatically link in when"
- echo "*** you link to this library. But I can only do this if you have a"
- echo "*** shared version of the library, which I believe you do not have"
- echo "*** because a test_compile did reveal that the linker did not use it for"
- echo "*** its dynamic dependency list that programs get resolved with at runtime."
- fi
- fi
- ;;
- *)
- func_append newdeplibs " $i"
- ;;
- esac
- done
- else
- # Error occurred in the first compile. Let's try to salvage
- # the situation: Compile a separate program for each library.
- for i in $deplibs; do
- case $i in
- -l*)
- func_stripname -l '' "$i"
- name=$func_stripname_result
- $opt_dry_run || $RM conftest
- if $LTCC $LTCFLAGS -o conftest conftest.c $i; then
- ldd_output=`ldd conftest`
- if test yes = "$allow_libtool_libs_with_static_runtimes"; then
- case " $predeps $postdeps " in
- *" $i "*)
- func_append newdeplibs " $i"
- i=
- ;;
- esac
- fi
- if test -n "$i"; then
- libname=`eval "\\$ECHO \"$libname_spec\""`
- deplib_matches=`eval "\\$ECHO \"$library_names_spec\""`
- set dummy $deplib_matches; shift
- deplib_match=$1
- if test `expr "$ldd_output" : ".*$deplib_match"` -ne 0; then
- func_append newdeplibs " $i"
- else
- droppeddeps=yes
- echo
- $ECHO "*** Warning: dynamic linker does not accept needed library $i."
- echo "*** I have the capability to make that library automatically link in when"
- echo "*** you link to this library. But I can only do this if you have a"
- echo "*** shared version of the library, which you do not appear to have"
- echo "*** because a test_compile did reveal that the linker did not use this one"
- echo "*** as a dynamic dependency that programs can get resolved with at runtime."
- fi
- fi
- else
- droppeddeps=yes
- echo
- $ECHO "*** Warning! Library $i is needed by this library but I was not able to"
- echo "*** make it link in! You will probably need to install it or some"
- echo "*** library that it depends on before this library will be fully"
- echo "*** functional. Installing it before continuing would be even better."
- fi
- ;;
- *)
- func_append newdeplibs " $i"
- ;;
- esac
- done
- fi
- ;;
file_magic*)
set dummy $deplibs_check_method; shift
file_magic_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"`
@@ -9330,17 +9706,16 @@ EOF
fi
if test -n "$a_deplib"; then
droppeddeps=yes
- echo
- $ECHO "*** Warning: linker path does not have real file for library $a_deplib."
- echo "*** I have the capability to make that library automatically link in when"
- echo "*** you link to this library. But I can only do this if you have a"
- echo "*** shared version of the library, which you do not appear to have"
- echo "*** because I did check the linker path looking for a file starting"
+ func_warning "Linker path does not have real file for library $a_deplib."
+ func_warning "I have the capability to make that library automatically link in when"
+ func_warning "you link to this library. But I can only do this if you have a"
+ func_warning "shared version of the library, which you do not appear to have"
+ func_warning "because I did check the linker path looking for a file starting"
if test -z "$potlib"; then
- $ECHO "*** with $libname but no candidates were found. (...for file magic test)"
+ func_warning "with $libname but no candidates were found. (...for file magic test)"
else
- $ECHO "*** with $libname and none of the candidates passed a file format test"
- $ECHO "*** using a file magic. Last file checked: $potlib"
+ func_warning "with $libname and none of the candidates passed a file format test"
+ func_warning "using a file magic. Last file checked: $potlib"
fi
fi
;;
@@ -9384,17 +9759,16 @@ EOF
fi
if test -n "$a_deplib"; then
droppeddeps=yes
- echo
- $ECHO "*** Warning: linker path does not have real file for library $a_deplib."
- echo "*** I have the capability to make that library automatically link in when"
- echo "*** you link to this library. But I can only do this if you have a"
- echo "*** shared version of the library, which you do not appear to have"
- echo "*** because I did check the linker path looking for a file starting"
+ func_warning "Linker path does not have real file for library $a_deplib."
+ func_warning "I have the capability to make that library automatically link in when"
+ func_warning "you link to this library. But I can only do this if you have a"
+ func_warning "shared version of the library, which you do not appear to have"
+ func_warning "because I did check the linker path looking for a file starting"
if test -z "$potlib"; then
- $ECHO "*** with $libname but no candidates were found. (...for regex pattern test)"
+ func_warning "with $libname but no candidates were found. (...for regex pattern test)"
else
- $ECHO "*** with $libname and none of the candidates passed a file format test"
- $ECHO "*** using a regex pattern. Last file checked: $potlib"
+ func_warning "with $libname and none of the candidates passed a file format test"
+ func_warning "using a regex pattern. Last file checked: $potlib"
fi
fi
;;
@@ -9418,11 +9792,11 @@ EOF
*[!\ \ ]*)
echo
if test none = "$deplibs_check_method"; then
- echo "*** Warning: inter-library dependencies are not supported in this platform."
+ func_warning "Inter-library dependencies are not supported in this platform."
else
- echo "*** Warning: inter-library dependencies are not known to be supported."
+ func_warning "Inter-library dependencies are not known to be supported."
fi
- echo "*** All declared inter-library dependencies are being dropped."
+ func_warning "All declared inter-library dependencies are being dropped."
droppeddeps=yes
;;
esac
@@ -9443,17 +9817,15 @@ EOF
if test yes = "$droppeddeps"; then
if test yes = "$module"; then
- echo
- echo "*** Warning: libtool could not satisfy all declared inter-library"
- $ECHO "*** dependencies of module $libname. Therefore, libtool will create"
- echo "*** a static module, that should work as long as the dlopening"
- echo "*** application is linked with the -dlopen flag."
+ func_warning "libtool could not satisfy all declared inter-library"
+ func_warning "dependencies of module $libname. Therefore, libtool will create"
+ func_warning "a static module, that should work as long as the dlopening"
+ func_warning "application is linked with the -dlopen flag."
if test -z "$global_symbol_pipe"; then
- echo
- echo "*** However, this would only work if libtool was able to extract symbol"
- echo "*** lists from a program, using 'nm' or equivalent, but libtool could"
- echo "*** not find such a program. So, this module is probably useless."
- echo "*** 'nm' from GNU binutils and a full rebuild may help."
+ func_warning "However, this would only work if libtool was able to extract symbol"
+ func_warning "lists from a program, using 'nm' or equivalent, but libtool could"
+ func_warning "not find such a program. So, this module is probably useless."
+ func_warning "'nm' from GNU binutils and a full rebuild may help."
fi
if test no = "$build_old_libs"; then
oldlibs=$output_objdir/$libname.$libext
@@ -9628,7 +10000,7 @@ EOF
orig_export_symbols=
case $host_os in
- cygwin* | mingw* | cegcc*)
+ cygwin* | mingw* | windows* | cegcc*)
if test -n "$export_symbols" && test -z "$export_symbols_regex"; then
# exporting using user supplied symfile
func_dll_def_p "$export_symbols" || {
@@ -9826,20 +10198,7 @@ EOF
last_robj=
k=1
- if test -n "$save_libobjs" && test : != "$skipped_export" && test yes = "$with_gnu_ld"; then
- output=$output_objdir/$output_la.lnkscript
- func_verbose "creating GNU ld script: $output"
- echo 'INPUT (' > $output
- for obj in $save_libobjs
- do
- func_to_tool_file "$obj"
- $ECHO "$func_to_tool_file_result" >> $output
- done
- echo ')' >> $output
- func_append delfiles " $output"
- func_to_tool_file "$output"
- output=$func_to_tool_file_result
- elif test -n "$save_libobjs" && test : != "$skipped_export" && test -n "$file_list_spec"; then
+ if test -n "$save_libobjs" && test : != "$skipped_export" && test -n "$file_list_spec"; then
output=$output_objdir/$output_la.lnk
func_verbose "creating linker input file list: $output"
: > $output
@@ -9858,6 +10217,19 @@ EOF
func_append delfiles " $output"
func_to_tool_file "$output"
output=$firstobj\"$file_list_spec$func_to_tool_file_result\"
+ elif test -n "$save_libobjs" && test : != "$skipped_export" && test yes = "$with_gnu_ld"; then
+ output=$output_objdir/$output_la.lnkscript
+ func_verbose "creating GNU ld script: $output"
+ echo 'INPUT (' > $output
+ for obj in $save_libobjs
+ do
+ func_to_tool_file "$obj"
+ $ECHO "$func_to_tool_file_result" >> $output
+ done
+ echo ')' >> $output
+ func_append delfiles " $output"
+ func_to_tool_file "$output"
+ output=$func_to_tool_file_result
else
if test -n "$save_libobjs"; then
func_verbose "creating reloadable object files..."
@@ -9935,8 +10307,8 @@ EOF
for cmd in $concat_cmds; do
IFS=$save_ifs
$opt_quiet || {
- func_quote_for_expand "$cmd"
- eval "func_echo $func_quote_for_expand_result"
+ func_quote_arg expand,pretty "$cmd"
+ eval "func_echo $func_quote_arg_result"
}
$opt_dry_run || eval "$cmd" || {
lt_exit=$?
@@ -10029,8 +10401,8 @@ EOF
eval cmd=\"$cmd\"
IFS=$save_ifs
$opt_quiet || {
- func_quote_for_expand "$cmd"
- eval "func_echo $func_quote_for_expand_result"
+ func_quote_arg expand,pretty "$cmd"
+ eval "func_echo $func_quote_arg_result"
}
$opt_dry_run || eval "$cmd" || {
lt_exit=$?
@@ -10298,7 +10670,7 @@ EOF
esac
fi
case $host in
- *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*)
+ *-*-cygwin* | *-*-mingw* | *-*-windows* | *-*-pw32* | *-*-os2* | *-cegcc*)
testbindir=`$ECHO "$libdir" | $SED -e 's*/lib$*/bin*'`
case :$dllsearchpath: in
*":$libdir:"*) ;;
@@ -10376,7 +10748,7 @@ EOF
# Disable wrappers for cegcc and mingw32ce hosts, we are cross compiling anyway.
wrappers_required=false
;;
- *cygwin* | *mingw* )
+ *cygwin* | *mingw* | *windows* )
test yes = "$build_libtool_libs" || wrappers_required=false
;;
*)
@@ -10504,12 +10876,13 @@ EOF
elif eval var_value=\$$var; test -z "$var_value"; then
relink_command="$var=; export $var; $relink_command"
else
- func_quote_for_eval "$var_value"
- relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command"
+ func_quote_arg pretty "$var_value"
+ relink_command="$var=$func_quote_arg_result; export $var; $relink_command"
fi
done
- relink_command="(cd `pwd`; $relink_command)"
- relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"`
+ func_quote eval cd "`pwd`"
+ func_quote_arg pretty,unquoted "($func_quote_result; $relink_command)"
+ relink_command=$func_quote_arg_unquoted_result
fi
# Only actually do things if not in dry run mode.
@@ -10529,7 +10902,7 @@ EOF
*) exeext= ;;
esac
case $host in
- *cygwin* | *mingw* )
+ *cygwin* | *mingw* | windows* )
func_dirname_and_basename "$output" "" "."
output_name=$func_basename_result
output_path=$func_dirname_result
@@ -10749,13 +11122,15 @@ EOF
elif eval var_value=\$$var; test -z "$var_value"; then
relink_command="$var=; export $var; $relink_command"
else
- func_quote_for_eval "$var_value"
- relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command"
+ func_quote_arg pretty,unquoted "$var_value"
+ relink_command="$var=$func_quote_arg_unquoted_result; export $var; $relink_command"
fi
done
# Quote the link command for shipping.
- relink_command="(cd `pwd`; $SHELL \"$progpath\" $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)"
- relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"`
+ func_quote eval cd "`pwd`"
+ relink_command="($func_quote_result; $SHELL \"$progpath\" $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)"
+ func_quote_arg pretty,unquoted "$relink_command"
+ relink_command=$func_quote_arg_unquoted_result
if test yes = "$hardcode_automatic"; then
relink_command=
fi
@@ -10861,7 +11236,7 @@ EOF
# tests/bindir.at for full details.
tdlname=$dlname
case $host,$output,$installed,$module,$dlname in
- *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll | *cegcc*,*lai,yes,no,*.dll)
+ *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll | *windows*,*lai,yes,no,*.dll | *cegcc*,*lai,yes,no,*.dll)
# If a -bindir argument was supplied, place the dll there.
if test -n "$bindir"; then
func_relative_path "$install_libdir" "$bindir"
diff --git a/contrib/unbound/smallapp/unbound-control-setup.sh b/contrib/unbound/smallapp/unbound-control-setup.sh
index 3709a8c3118b..872133ccc536 100755..100644
--- a/contrib/unbound/smallapp/unbound-control-setup.sh
+++ b/contrib/unbound/smallapp/unbound-control-setup.sh
@@ -5,22 +5,22 @@
# Copyright (c) 2008, NLnet Labs. All rights reserved.
#
# This software is open source.
-#
+#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
-#
+#
# Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
-#
+#
# Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
-#
+#
# Neither the name of the NLNET LABS nor the names of its contributors may
# be used to endorse or promote products derived from this software without
# specific prior written permission.
-#
+#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@@ -57,87 +57,162 @@ SVR_BASE=unbound_server
# base name for unbound-control keys
CTL_BASE=unbound_control
+# flag to recreate generated certificates
+RECREATE=0
+
# we want -rw-r----- access (say you run this as root: grp=yes (server), all=no).
umask 0027
# end of options
-# functions:
-error ( ) {
- echo "$0 fatal error: $1"
- exit 1
+set -eu
+
+cleanup() {
+ echo "removing artifacts"
+
+ rm -rf \
+ server.cnf \
+ client.cnf \
+ "${SVR_BASE}_trust.pem" \
+ "${CTL_BASE}_trust.pem" \
+ "${SVR_BASE}_trust.srl"
}
-# check arguments:
-while test $# -ne 0; do
- case $1 in
- -d)
- if test $# -eq 1; then error "need argument for -d"; fi
- DESTDIR="$2"
- shift
- ;;
- *)
- echo "unbound-control-setup.sh - setup SSL keys for unbound-control"
- echo " -d dir use directory to store keys and certificates."
- echo " default: $DESTDIR"
- echo "please run this command using the same user id that the "
- echo "unbound daemon uses, it needs read privileges."
- exit 1
- ;;
- esac
- shift
+fatal() {
+ printf "fatal error: $*\n" >/dev/stderr
+ exit 1
+}
+
+usage() {
+ cat <<EOF
+usage: $0 OPTIONS
+OPTIONS
+-d <dir> used directory to store keys and certificates (default: $DESTDIR)
+-h show help notice
+-r recreate certificates
+EOF
+}
+
+OPTIND=1
+while getopts 'd:hr' arg; do
+ case "$arg" in
+ d) DESTDIR="$OPTARG" ;;
+ h) usage; exit 1 ;;
+ r) RECREATE=1 ;;
+ ?) fatal "'$arg' unknown option" ;;
+ esac
done
+shift $((OPTIND - 1))
+
+if ! openssl version </dev/null >/dev/null 2>&1; then
+ echo "$0 requires openssl to be installed for keys/certificates generation." >&2
+ exit 1
+fi
-# go!:
echo "setup in directory $DESTDIR"
-cd "$DESTDIR" || error "could not cd to $DESTDIR"
-
-# create certificate keys; do not recreate if they already exist.
-if test -f $SVR_BASE.key; then
- echo "$SVR_BASE.key exists"
-else
- echo "generating $SVR_BASE.key"
- openssl genrsa -out $SVR_BASE.key $BITS || error "could not genrsa"
+cd "$DESTDIR"
+
+trap cleanup INT
+
+# ===
+# Generate server certificate
+# ===
+
+# generate private key; do no recreate it if they already exist.
+if [ ! -f "$SVR_BASE.key" ]; then
+ openssl genrsa -out "$SVR_BASE.key" "$BITS"
+fi
+
+cat >server.cnf <<EOF
+[req]
+default_bits=$BITS
+default_md=$HASH
+prompt=no
+distinguished_name=req_distinguished_name
+x509_extensions=v3_ca
+[req_distinguished_name]
+commonName=$SERVERNAME
+[v3_ca]
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid:always,issuer:always
+basicConstraints=critical,CA:TRUE,pathlen:0
+subjectAltName=DNS:$SERVERNAME
+EOF
+
+[ -f server.cnf ] || fatal "cannot create openssl configuration"
+
+if [ ! -f "$SVR_BASE.pem" -o $RECREATE -eq 1 ]; then
+ openssl req \
+ -new -x509 \
+ -key "$SVR_BASE.key" \
+ -config server.cnf \
+ -days "$DAYS" \
+ -out "$SVR_BASE.pem"
+
+ [ ! -f "SVR_BASE.pem" ] || fatal "cannot create server certificate"
fi
-if test -f $CTL_BASE.key; then
- echo "$CTL_BASE.key exists"
-else
- echo "generating $CTL_BASE.key"
- openssl genrsa -out $CTL_BASE.key $BITS || error "could not genrsa"
+
+# ===
+# Generate client certificate
+# ===
+
+# generate private key; do no recreate it if they already exist.
+if [ ! -f "$CTL_BASE.key" ]; then
+ openssl genrsa -out "$CTL_BASE.key" "$BITS"
fi
-# create self-signed cert for server
-echo "[req]" > request.cfg
-echo "default_bits=$BITS" >> request.cfg
-echo "default_md=$HASH" >> request.cfg
-echo "prompt=no" >> request.cfg
-echo "distinguished_name=req_distinguished_name" >> request.cfg
-echo "" >> request.cfg
-echo "[req_distinguished_name]" >> request.cfg
-echo "commonName=$SERVERNAME" >> request.cfg
+cat >client.cnf <<EOF
+[req]
+default_bits=$BITS
+default_md=$HASH
+prompt=no
+distinguished_name=req_distinguished_name
+req_extensions=v3_req
+[req_distinguished_name]
+commonName=$CLIENTNAME
+[v3_req]
+basicConstraints=critical,CA:FALSE
+subjectAltName=DNS:$CLIENTNAME
+EOF
+
+[ -f client.cnf ] || fatal "cannot create openssl configuration"
+
+if [ ! -f "$CTL_BASE.pem" -o $RECREATE -eq 1 ]; then
+ openssl x509 \
+ -addtrust serverAuth \
+ -in "$SVR_BASE.pem" \
+ -out "${SVR_BASE}_trust.pem"
+
+ openssl req \
+ -new \
+ -config client.cnf \
+ -key "$CTL_BASE.key" \
+ | openssl x509 \
+ -req \
+ -days "$DAYS" \
+ -CA "${SVR_BASE}_trust.pem" \
+ -CAkey "$SVR_BASE.key" \
+ -CAcreateserial \
+ -$HASH \
+ -extfile client.cnf \
+ -extensions v3_req \
+ -out "$CTL_BASE.pem"
+
+ [ ! -f "CTL_BASE.pem" ] || fatal "cannot create signed client certificate"
+fi
-test -f request.cfg || error "could not create request.cfg"
+# remove unused permissions
+chmod o-rw \
+ "$SVR_BASE.pem" \
+ "$SVR_BASE.key"
+chmod g+r,o-rw \
+ "$CTL_BASE.pem" \
+ "$CTL_BASE.key"
+
+cleanup
+
+echo "Setup success. Certificates created. Enable in unbound.conf file to use"
-echo "create $SVR_BASE.pem (self signed certificate)"
-openssl req -key $SVR_BASE.key -config request.cfg -new -x509 -days $DAYS -out $SVR_BASE.pem || error "could not create $SVR_BASE.pem"
-# create trusted usage pem
-openssl x509 -in $SVR_BASE.pem -addtrust serverAuth -out $SVR_BASE"_trust.pem"
-
-# create client request and sign it, piped
-echo "[req]" > request.cfg
-echo "default_bits=$BITS" >> request.cfg
-echo "default_md=$HASH" >> request.cfg
-echo "prompt=no" >> request.cfg
-echo "distinguished_name=req_distinguished_name" >> request.cfg
-echo "" >> request.cfg
-echo "[req_distinguished_name]" >> request.cfg
-echo "commonName=$CLIENTNAME" >> request.cfg
-
-test -f request.cfg || error "could not create request.cfg"
-
-echo "create $CTL_BASE.pem (signed client certificate)"
-openssl req -key $CTL_BASE.key -config request.cfg -new | openssl x509 -req -days $DAYS -CA $SVR_BASE"_trust.pem" -CAkey $SVR_BASE.key -CAcreateserial -$HASH -out $CTL_BASE.pem
-test -f $CTL_BASE.pem || error "could not create $CTL_BASE.pem"
# create trusted usage pem
# openssl x509 -in $CTL_BASE.pem -addtrust clientAuth -out $CTL_BASE"_trust.pem"
@@ -148,13 +223,3 @@ test -f $CTL_BASE.pem || error "could not create $CTL_BASE.pem"
# echo "empty password is used, simply click OK on the password dialog box."
# openssl pkcs12 -export -in $CTL_BASE"_trust.pem" -inkey $CTL_BASE.key -name "unbound remote control client cert" -out $CTL_BASE"_browser.pfx" -password "pass:" || error "could not create browser certificate"
-# set desired permissions
-chmod 0640 $SVR_BASE.pem $SVR_BASE.key $CTL_BASE.pem $CTL_BASE.key
-
-# remove crap
-rm -f request.cfg
-rm -f $CTL_BASE"_trust.pem" $SVR_BASE"_trust.pem" $SVR_BASE"_trust.srl"
-
-echo "Setup success. Certificates created. Enable in unbound.conf file to use"
-
-exit 0