summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOran Agra <oran@redislabs.com>2023-05-15 13:08:15 +0300
committerGitHub <noreply@github.com>2023-05-15 13:08:15 +0300
commita51eb05b1895babb17c37c36b963e2bcbd5496d5 (patch)
tree7be24b09e0a5621a03e9f9ffe9ef27fcb44d8345
parente26a769d9627ebecb8607375580970a740348956 (diff)
parent986dbf716e0cb904c80bb444635cea3242859cc1 (diff)
downloadredis-7.2.tar.gz
Release Redis 7.2 RC27.2-rc27.2
-rw-r--r--.github/workflows/ci.yml2
-rw-r--r--.github/workflows/daily.yml176
-rw-r--r--00-RELEASENOTES62
-rw-r--r--deps/Makefile2
-rw-r--r--deps/jemalloc/.appveyor.yml1
-rw-r--r--deps/jemalloc/.cirrus.yml33
-rw-r--r--deps/jemalloc/.clang-format122
-rw-r--r--deps/jemalloc/.gitignore8
-rw-r--r--deps/jemalloc/.travis.yml448
-rw-r--r--deps/jemalloc/ChangeLog100
-rw-r--r--deps/jemalloc/INSTALL.md27
-rw-r--r--deps/jemalloc/Makefile.in219
-rw-r--r--deps/jemalloc/TUNING.md8
-rw-r--r--deps/jemalloc/VERSION2
-rw-r--r--deps/jemalloc/bin/jeprof.in112
-rwxr-xr-xdeps/jemalloc/build-aux/config.guess1033
-rwxr-xr-xdeps/jemalloc/build-aux/config.sub2626
-rwxr-xr-xdeps/jemalloc/configure8920
-rw-r--r--deps/jemalloc/configure.ac591
-rw-r--r--deps/jemalloc/doc/jemalloc.xml.in302
-rw-r--r--deps/jemalloc/doc_internal/PROFILING_INTERNALS.md127
-rw-r--r--deps/jemalloc/doc_internal/jemalloc.svg1
-rw-r--r--deps/jemalloc/include/jemalloc/internal/activity_callback.h23
-rw-r--r--deps/jemalloc/include/jemalloc/internal/arena_externs.h77
-rw-r--r--deps/jemalloc/include/jemalloc/internal/arena_inlines_a.h35
-rw-r--r--deps/jemalloc/include/jemalloc/internal/arena_inlines_b.h489
-rw-r--r--deps/jemalloc/include/jemalloc/internal/arena_stats.h227
-rw-r--r--deps/jemalloc/include/jemalloc/internal/arena_structs.h101
-rw-r--r--deps/jemalloc/include/jemalloc/internal/arena_structs_a.h11
-rw-r--r--deps/jemalloc/include/jemalloc/internal/arena_structs_b.h232
-rw-r--r--deps/jemalloc/include/jemalloc/internal/arena_types.h23
-rw-r--r--deps/jemalloc/include/jemalloc/internal/atomic.h33
-rw-r--r--deps/jemalloc/include/jemalloc/internal/background_thread_externs.h7
-rw-r--r--deps/jemalloc/include/jemalloc/internal/background_thread_inlines.h14
-rw-r--r--deps/jemalloc/include/jemalloc/internal/background_thread_structs.h12
-rw-r--r--deps/jemalloc/include/jemalloc/internal/base.h110
-rw-r--r--deps/jemalloc/include/jemalloc/internal/base_externs.h22
-rw-r--r--deps/jemalloc/include/jemalloc/internal/base_inlines.h13
-rw-r--r--deps/jemalloc/include/jemalloc/internal/base_structs.h59
-rw-r--r--deps/jemalloc/include/jemalloc/internal/base_types.h33
-rw-r--r--deps/jemalloc/include/jemalloc/internal/bin.h85
-rw-r--r--deps/jemalloc/include/jemalloc/internal/bin_info.h50
-rw-r--r--deps/jemalloc/include/jemalloc/internal/bin_stats.h5
-rw-r--r--deps/jemalloc/include/jemalloc/internal/bin_types.h2
-rw-r--r--deps/jemalloc/include/jemalloc/internal/bit_util.h457
-rw-r--r--deps/jemalloc/include/jemalloc/internal/bitmap.h21
-rw-r--r--deps/jemalloc/include/jemalloc/internal/buf_writer.h32
-rw-r--r--deps/jemalloc/include/jemalloc/internal/cache_bin.h625
-rw-r--r--deps/jemalloc/include/jemalloc/internal/counter.h34
-rw-r--r--deps/jemalloc/include/jemalloc/internal/ctl.h31
-rw-r--r--deps/jemalloc/include/jemalloc/internal/decay.h186
-rw-r--r--deps/jemalloc/include/jemalloc/internal/ecache.h55
-rw-r--r--deps/jemalloc/include/jemalloc/internal/edata.h698
-rw-r--r--deps/jemalloc/include/jemalloc/internal/edata_cache.h49
-rw-r--r--deps/jemalloc/include/jemalloc/internal/ehooks.h412
-rw-r--r--deps/jemalloc/include/jemalloc/internal/emap.h357
-rw-r--r--deps/jemalloc/include/jemalloc/internal/emitter.h74
-rw-r--r--deps/jemalloc/include/jemalloc/internal/eset.h77
-rw-r--r--deps/jemalloc/include/jemalloc/internal/exp_grow.h50
-rw-r--r--deps/jemalloc/include/jemalloc/internal/extent.h137
-rw-r--r--deps/jemalloc/include/jemalloc/internal/extent_externs.h83
-rw-r--r--deps/jemalloc/include/jemalloc/internal/extent_inlines.h501
-rw-r--r--deps/jemalloc/include/jemalloc/internal/extent_structs.h256
-rw-r--r--deps/jemalloc/include/jemalloc/internal/extent_types.h23
-rw-r--r--deps/jemalloc/include/jemalloc/internal/fb.h373
-rw-r--r--deps/jemalloc/include/jemalloc/internal/fxp.h126
-rw-r--r--deps/jemalloc/include/jemalloc/internal/hash.h65
-rw-r--r--deps/jemalloc/include/jemalloc/internal/hpa.h182
-rw-r--r--deps/jemalloc/include/jemalloc/internal/hpa_hooks.h17
-rw-r--r--deps/jemalloc/include/jemalloc/internal/hpa_opts.h74
-rw-r--r--deps/jemalloc/include/jemalloc/internal/hpdata.h413
-rw-r--r--deps/jemalloc/include/jemalloc/internal/inspect.h40
-rw-r--r--deps/jemalloc/include/jemalloc/internal/jemalloc_internal_decls.h16
-rw-r--r--deps/jemalloc/include/jemalloc/internal/jemalloc_internal_defs.h.in61
-rw-r--r--deps/jemalloc/include/jemalloc/internal/jemalloc_internal_externs.h28
-rw-r--r--deps/jemalloc/include/jemalloc/internal/jemalloc_internal_includes.h16
-rw-r--r--deps/jemalloc/include/jemalloc/internal/jemalloc_internal_inlines_a.h82
-rw-r--r--deps/jemalloc/include/jemalloc/internal/jemalloc_internal_inlines_b.h52
-rw-r--r--deps/jemalloc/include/jemalloc/internal/jemalloc_internal_inlines_c.h152
-rw-r--r--deps/jemalloc/include/jemalloc/internal/jemalloc_internal_macros.h13
-rw-r--r--deps/jemalloc/include/jemalloc/internal/jemalloc_internal_types.h28
-rw-r--r--deps/jemalloc/include/jemalloc/internal/jemalloc_preamble.h.in52
-rw-r--r--deps/jemalloc/include/jemalloc/internal/large_externs.h26
-rw-r--r--deps/jemalloc/include/jemalloc/internal/lockedint.h204
-rw-r--r--deps/jemalloc/include/jemalloc/internal/malloc_io.h11
-rw-r--r--deps/jemalloc/include/jemalloc/internal/mpsc_queue.h134
-rw-r--r--deps/jemalloc/include/jemalloc/internal/mutex.h63
-rw-r--r--deps/jemalloc/include/jemalloc/internal/mutex_pool.h94
-rw-r--r--deps/jemalloc/include/jemalloc/internal/mutex_prof.h13
-rw-r--r--deps/jemalloc/include/jemalloc/internal/nstime.h43
-rw-r--r--deps/jemalloc/include/jemalloc/internal/pa.h243
-rw-r--r--deps/jemalloc/include/jemalloc/internal/pac.h179
-rw-r--r--deps/jemalloc/include/jemalloc/internal/pages.h31
-rw-r--r--deps/jemalloc/include/jemalloc/internal/pai.h95
-rw-r--r--deps/jemalloc/include/jemalloc/internal/peak.h37
-rw-r--r--deps/jemalloc/include/jemalloc/internal/peak_event.h24
-rw-r--r--deps/jemalloc/include/jemalloc/internal/ph.h817
-rw-r--r--deps/jemalloc/include/jemalloc/internal/prng.h93
-rw-r--r--deps/jemalloc/include/jemalloc/internal/prof_data.h37
-rw-r--r--deps/jemalloc/include/jemalloc/internal/prof_externs.h116
-rw-r--r--deps/jemalloc/include/jemalloc/internal/prof_hook.h21
-rw-r--r--deps/jemalloc/include/jemalloc/internal/prof_inlines.h261
-rw-r--r--deps/jemalloc/include/jemalloc/internal/prof_inlines_a.h85
-rw-r--r--deps/jemalloc/include/jemalloc/internal/prof_inlines_b.h250
-rw-r--r--deps/jemalloc/include/jemalloc/internal/prof_log.h22
-rw-r--r--deps/jemalloc/include/jemalloc/internal/prof_recent.h23
-rw-r--r--deps/jemalloc/include/jemalloc/internal/prof_stats.h17
-rw-r--r--deps/jemalloc/include/jemalloc/internal/prof_structs.h47
-rw-r--r--deps/jemalloc/include/jemalloc/internal/prof_sys.h30
-rw-r--r--deps/jemalloc/include/jemalloc/internal/prof_types.h37
-rw-r--r--deps/jemalloc/include/jemalloc/internal/psset.h131
-rw-r--r--deps/jemalloc/include/jemalloc/internal/ql.h129
-rw-r--r--deps/jemalloc/include/jemalloc/internal/qr.h130
-rw-r--r--deps/jemalloc/include/jemalloc/internal/quantum.h12
-rw-r--r--deps/jemalloc/include/jemalloc/internal/rb.h920
-rw-r--r--deps/jemalloc/include/jemalloc/internal/rtree.h520
-rw-r--r--deps/jemalloc/include/jemalloc/internal/rtree_tsd.h24
-rw-r--r--deps/jemalloc/include/jemalloc/internal/safety_check.h7
-rw-r--r--deps/jemalloc/include/jemalloc/internal/san.h191
-rw-r--r--deps/jemalloc/include/jemalloc/internal/san_bump.h52
-rw-r--r--deps/jemalloc/include/jemalloc/internal/sc.h78
-rw-r--r--deps/jemalloc/include/jemalloc/internal/sec.h120
-rw-r--r--deps/jemalloc/include/jemalloc/internal/sec_opts.h59
-rw-r--r--deps/jemalloc/include/jemalloc/internal/slab_data.h12
-rw-r--r--deps/jemalloc/include/jemalloc/internal/stats.h29
-rw-r--r--deps/jemalloc/include/jemalloc/internal/sz.h97
-rw-r--r--deps/jemalloc/include/jemalloc/internal/tcache_externs.h60
-rw-r--r--deps/jemalloc/include/jemalloc/internal/tcache_inlines.h156
-rw-r--r--deps/jemalloc/include/jemalloc/internal/tcache_structs.h62
-rw-r--r--deps/jemalloc/include/jemalloc/internal/tcache_types.h40
-rw-r--r--deps/jemalloc/include/jemalloc/internal/test_hooks.h23
-rw-r--r--deps/jemalloc/include/jemalloc/internal/thread_event.h301
-rw-r--r--deps/jemalloc/include/jemalloc/internal/ticker.h92
-rw-r--r--deps/jemalloc/include/jemalloc/internal/tsd.h243
-rw-r--r--deps/jemalloc/include/jemalloc/internal/tsd_generic.h23
-rw-r--r--deps/jemalloc/include/jemalloc/internal/tsd_malloc_thread_cleanup.h2
-rw-r--r--deps/jemalloc/include/jemalloc/internal/tsd_types.h2
-rw-r--r--deps/jemalloc/include/jemalloc/internal/tsd_win.h2
-rw-r--r--deps/jemalloc/include/jemalloc/internal/typed_list.h55
-rw-r--r--deps/jemalloc/include/jemalloc/internal/util.h56
-rw-r--r--deps/jemalloc/include/jemalloc/internal/witness.h183
-rw-r--r--deps/jemalloc/include/jemalloc/jemalloc_defs.h.in6
-rw-r--r--deps/jemalloc/include/jemalloc/jemalloc_macros.h.in20
-rw-r--r--deps/jemalloc/include/jemalloc/jemalloc_protos.h.in23
-rw-r--r--deps/jemalloc/m4/ax_cxx_compile_stdcxx.m4449
-rw-r--r--deps/jemalloc/msvc/projects/vc2015/jemalloc/jemalloc.vcxproj40
-rw-r--r--deps/jemalloc/msvc/projects/vc2015/jemalloc/jemalloc.vcxproj.filters108
-rw-r--r--deps/jemalloc/msvc/projects/vc2017/jemalloc/jemalloc.vcxproj39
-rw-r--r--deps/jemalloc/msvc/projects/vc2017/jemalloc/jemalloc.vcxproj.filters107
-rw-r--r--deps/jemalloc/msvc/test_threads/test_threads.cpp1
-rwxr-xr-xdeps/jemalloc/scripts/check-formatting.sh28
-rw-r--r--deps/jemalloc/scripts/freebsd/before_install.sh3
-rw-r--r--deps/jemalloc/scripts/freebsd/before_script.sh10
-rw-r--r--deps/jemalloc/scripts/freebsd/script.sh3
-rwxr-xr-xdeps/jemalloc/scripts/gen_run_tests.py36
-rwxr-xr-xdeps/jemalloc/scripts/gen_travis.py380
-rw-r--r--deps/jemalloc/scripts/linux/before_install.sh13
-rw-r--r--deps/jemalloc/scripts/windows/before_install.sh83
-rw-r--r--deps/jemalloc/scripts/windows/before_script.sh20
-rw-r--r--deps/jemalloc/scripts/windows/script.sh10
-rw-r--r--deps/jemalloc/src/arena.c2063
-rw-r--r--deps/jemalloc/src/background_thread.c335
-rw-r--r--deps/jemalloc/src/base.c193
-rw-r--r--deps/jemalloc/src/bin.c30
-rw-r--r--deps/jemalloc/src/bin_info.c30
-rw-r--r--deps/jemalloc/src/bitmap.c1
-rw-r--r--deps/jemalloc/src/buf_writer.c144
-rw-r--r--deps/jemalloc/src/cache_bin.c99
-rw-r--r--deps/jemalloc/src/ckh.c7
-rw-r--r--deps/jemalloc/src/counter.c30
-rw-r--r--deps/jemalloc/src/ctl.c1687
-rw-r--r--deps/jemalloc/src/decay.c295
-rw-r--r--deps/jemalloc/src/ecache.c35
-rw-r--r--deps/jemalloc/src/edata.c6
-rw-r--r--deps/jemalloc/src/edata_cache.c154
-rw-r--r--deps/jemalloc/src/ehooks.c275
-rw-r--r--deps/jemalloc/src/emap.c386
-rw-r--r--deps/jemalloc/src/eset.c282
-rw-r--r--deps/jemalloc/src/exp_grow.c8
-rw-r--r--deps/jemalloc/src/extent.c2481
-rw-r--r--deps/jemalloc/src/extent_dss.c42
-rw-r--r--deps/jemalloc/src/extent_mmap.c1
-rw-r--r--deps/jemalloc/src/fxp.c124
-rw-r--r--deps/jemalloc/src/hash.c3
-rw-r--r--deps/jemalloc/src/hook.c6
-rw-r--r--deps/jemalloc/src/hpa.c1044
-rw-r--r--deps/jemalloc/src/hpa_hooks.c63
-rw-r--r--deps/jemalloc/src/hpdata.c325
-rw-r--r--deps/jemalloc/src/inspect.c77
-rw-r--r--deps/jemalloc/src/jemalloc.c2178
-rw-r--r--deps/jemalloc/src/jemalloc_cpp.cpp131
-rw-r--r--deps/jemalloc/src/large.c299
-rw-r--r--deps/jemalloc/src/malloc_io.c46
-rw-r--r--deps/jemalloc/src/mutex.c19
-rw-r--r--deps/jemalloc/src/mutex_pool.c18
-rw-r--r--deps/jemalloc/src/nstime.c127
-rw-r--r--deps/jemalloc/src/pa.c277
-rw-r--r--deps/jemalloc/src/pa_extra.c191
-rw-r--r--deps/jemalloc/src/pac.c587
-rw-r--r--deps/jemalloc/src/pages.c209
-rw-r--r--deps/jemalloc/src/pai.c31
-rw-r--r--deps/jemalloc/src/peak_event.c82
-rw-r--r--deps/jemalloc/src/prng.c3
-rw-r--r--deps/jemalloc/src/prof.c2923
-rw-r--r--deps/jemalloc/src/prof_data.c1447
-rw-r--r--deps/jemalloc/src/prof_log.c717
-rw-r--r--deps/jemalloc/src/prof_recent.c600
-rw-r--r--deps/jemalloc/src/prof_stats.c57
-rw-r--r--deps/jemalloc/src/prof_sys.c669
-rw-r--r--deps/jemalloc/src/psset.c385
-rw-r--r--deps/jemalloc/src/rtree.c75
-rw-r--r--deps/jemalloc/src/safety_check.c16
-rw-r--r--deps/jemalloc/src/san.c208
-rw-r--r--deps/jemalloc/src/san_bump.c104
-rw-r--r--deps/jemalloc/src/sc.c17
-rw-r--r--deps/jemalloc/src/sec.c422
-rw-r--r--deps/jemalloc/src/stats.c794
-rw-r--r--deps/jemalloc/src/sz.c52
-rw-r--r--deps/jemalloc/src/tcache.c1137
-rw-r--r--deps/jemalloc/src/thread_event.c343
-rw-r--r--deps/jemalloc/src/ticker.c31
-rwxr-xr-xdeps/jemalloc/src/ticker.py15
-rw-r--r--deps/jemalloc/src/tsd.c75
-rw-r--r--deps/jemalloc/src/witness.c44
-rw-r--r--deps/jemalloc/test/analyze/prof_bias.c60
-rw-r--r--deps/jemalloc/test/analyze/rand.c276
-rw-r--r--deps/jemalloc/test/analyze/sizes.c53
-rw-r--r--deps/jemalloc/test/include/test/arena_util.h155
-rw-r--r--deps/jemalloc/test/include/test/bench.h60
-rw-r--r--deps/jemalloc/test/include/test/bgthd.h17
-rw-r--r--deps/jemalloc/test/include/test/btalloc.h2
-rw-r--r--deps/jemalloc/test/include/test/extent_hooks.h40
-rw-r--r--deps/jemalloc/test/include/test/jemalloc_test.h.in15
-rw-r--r--deps/jemalloc/test/include/test/mq.h4
-rw-r--r--deps/jemalloc/test/include/test/nbits.h111
-rw-r--r--deps/jemalloc/test/include/test/san.h14
-rw-r--r--deps/jemalloc/test/include/test/sleep.h1
-rw-r--r--deps/jemalloc/test/include/test/test.h385
-rw-r--r--deps/jemalloc/test/integration/MALLOCX_ARENA.c8
-rw-r--r--deps/jemalloc/test/integration/aligned_alloc.c16
-rw-r--r--deps/jemalloc/test/integration/allocated.c22
-rw-r--r--deps/jemalloc/test/integration/cpp/basic.cpp5
-rw-r--r--deps/jemalloc/test/integration/cpp/infallible_new_false.cpp23
-rw-r--r--deps/jemalloc/test/integration/cpp/infallible_new_false.sh8
-rw-r--r--deps/jemalloc/test/integration/cpp/infallible_new_true.cpp67
-rw-r--r--deps/jemalloc/test/integration/cpp/infallible_new_true.sh8
-rw-r--r--deps/jemalloc/test/integration/extent.c155
-rw-r--r--deps/jemalloc/test/integration/malloc.c2
-rw-r--r--deps/jemalloc/test/integration/mallocx.c56
-rw-r--r--deps/jemalloc/test/integration/overflow.c20
-rw-r--r--deps/jemalloc/test/integration/posix_memalign.c14
-rw-r--r--deps/jemalloc/test/integration/rallocx.c118
-rw-r--r--deps/jemalloc/test/integration/slab_sizes.c22
-rw-r--r--deps/jemalloc/test/integration/smallocx.c66
-rw-r--r--deps/jemalloc/test/integration/thread_arena.c10
-rw-r--r--deps/jemalloc/test/integration/thread_tcache_enabled.c38
-rw-r--r--deps/jemalloc/test/integration/xallocx.c110
-rw-r--r--deps/jemalloc/test/src/sleep.c (renamed from deps/jemalloc/test/src/mq.c)4
-rw-r--r--deps/jemalloc/test/src/test.c4
-rw-r--r--deps/jemalloc/test/src/timer.c3
-rw-r--r--deps/jemalloc/test/stress/batch_alloc.c198
-rw-r--r--deps/jemalloc/test/stress/fill_flush.c76
-rw-r--r--deps/jemalloc/test/stress/large_microbench.c33
-rw-r--r--deps/jemalloc/test/stress/mallctl.c74
-rw-r--r--deps/jemalloc/test/stress/microbench.c43
-rw-r--r--deps/jemalloc/test/unit/SFMT.c32
-rw-r--r--deps/jemalloc/test/unit/a0.c2
-rw-r--r--deps/jemalloc/test/unit/arena_decay.c436
-rw-r--r--deps/jemalloc/test/unit/arena_decay.sh3
-rw-r--r--deps/jemalloc/test/unit/arena_reset.c90
-rw-r--r--deps/jemalloc/test/unit/atomic.c46
-rw-r--r--deps/jemalloc/test/unit/background_thread.c37
-rw-r--r--deps/jemalloc/test/unit/background_thread_enable.c55
-rw-r--r--deps/jemalloc/test/unit/base.c109
-rw-r--r--deps/jemalloc/test/unit/batch_alloc.c189
-rw-r--r--deps/jemalloc/test/unit/batch_alloc.sh3
-rw-r--r--deps/jemalloc/test/unit/batch_alloc_prof.c1
-rw-r--r--deps/jemalloc/test/unit/batch_alloc_prof.sh3
-rw-r--r--deps/jemalloc/test/unit/binshard.c38
-rw-r--r--deps/jemalloc/test/unit/bit_util.c256
-rw-r--r--deps/jemalloc/test/unit/bitmap.c194
-rw-r--r--deps/jemalloc/test/unit/buf_writer.c196
-rw-r--r--deps/jemalloc/test/unit/cache_bin.c384
-rw-r--r--deps/jemalloc/test/unit/ckh.c74
-rw-r--r--deps/jemalloc/test/unit/counter.c80
-rw-r--r--deps/jemalloc/test/unit/decay.c784
-rw-r--r--deps/jemalloc/test/unit/decay.sh3
-rw-r--r--deps/jemalloc/test/unit/div.c2
-rw-r--r--deps/jemalloc/test/unit/double_free.c77
-rw-r--r--deps/jemalloc/test/unit/double_free.h1
-rw-r--r--deps/jemalloc/test/unit/edata_cache.c226
-rw-r--r--deps/jemalloc/test/unit/emitter.c180
-rw-r--r--deps/jemalloc/test/unit/extent_quantize.c50
-rw-r--r--deps/jemalloc/test/unit/fb.c954
-rw-r--r--deps/jemalloc/test/unit/fork.c8
-rw-r--r--deps/jemalloc/test/unit/fxp.c394
-rw-r--r--deps/jemalloc/test/unit/hash.c2
-rw-r--r--deps/jemalloc/test/unit/hook.c348
-rw-r--r--deps/jemalloc/test/unit/hpa.c459
-rw-r--r--deps/jemalloc/test/unit/hpa_background_thread.c188
-rw-r--r--deps/jemalloc/test/unit/hpa_background_thread.sh4
-rw-r--r--deps/jemalloc/test/unit/hpdata.c244
-rw-r--r--deps/jemalloc/test/unit/huge.c58
-rw-r--r--deps/jemalloc/test/unit/inspect.c (renamed from deps/jemalloc/test/unit/extent_util.c)87
-rw-r--r--deps/jemalloc/test/unit/inspect.sh5
-rw-r--r--deps/jemalloc/test/unit/junk.c282
-rw-r--r--deps/jemalloc/test/unit/log.c31
-rw-r--r--deps/jemalloc/test/unit/mallctl.c684
-rw-r--r--deps/jemalloc/test/unit/malloc_conf_2.c29
-rw-r--r--deps/jemalloc/test/unit/malloc_conf_2.sh1
-rw-r--r--deps/jemalloc/test/unit/malloc_io.c28
-rw-r--r--deps/jemalloc/test/unit/math.c12
-rw-r--r--deps/jemalloc/test/unit/mpsc_queue.c304
-rw-r--r--deps/jemalloc/test/unit/mq.c18
-rw-r--r--deps/jemalloc/test/unit/mtx.c6
-rw-r--r--deps/jemalloc/test/unit/nstime.c119
-rw-r--r--deps/jemalloc/test/unit/oversize_threshold.c133
-rw-r--r--deps/jemalloc/test/unit/pa.c126
-rw-r--r--deps/jemalloc/test/unit/pack.c20
-rw-r--r--deps/jemalloc/test/unit/pages.c6
-rw-r--r--deps/jemalloc/test/unit/peak.c47
-rw-r--r--deps/jemalloc/test/unit/ph.c110
-rw-r--r--deps/jemalloc/test/unit/prng.c226
-rw-r--r--deps/jemalloc/test/unit/prof_accum.c13
-rw-r--r--deps/jemalloc/test/unit/prof_active.c16
-rw-r--r--deps/jemalloc/test/unit/prof_active.sh2
-rw-r--r--deps/jemalloc/test/unit/prof_gdump.c29
-rw-r--r--deps/jemalloc/test/unit/prof_hook.c169
-rw-r--r--deps/jemalloc/test/unit/prof_hook.sh6
-rw-r--r--deps/jemalloc/test/unit/prof_idump.c25
-rw-r--r--deps/jemalloc/test/unit/prof_log.c65
-rw-r--r--deps/jemalloc/test/unit/prof_log.sh2
-rw-r--r--deps/jemalloc/test/unit/prof_mdump.c216
-rw-r--r--deps/jemalloc/test/unit/prof_mdump.sh6
-rw-r--r--deps/jemalloc/test/unit/prof_recent.c678
-rw-r--r--deps/jemalloc/test/unit/prof_recent.sh5
-rw-r--r--deps/jemalloc/test/unit/prof_reset.c112
-rw-r--r--deps/jemalloc/test/unit/prof_reset.sh2
-rw-r--r--deps/jemalloc/test/unit/prof_stats.c151
-rw-r--r--deps/jemalloc/test/unit/prof_stats.sh5
-rw-r--r--deps/jemalloc/test/unit/prof_sys_thread_name.c77
-rw-r--r--deps/jemalloc/test/unit/prof_sys_thread_name.sh5
-rw-r--r--deps/jemalloc/test/unit/prof_tctx.c38
-rw-r--r--deps/jemalloc/test/unit/prof_tctx.sh2
-rw-r--r--deps/jemalloc/test/unit/prof_thread_name.c22
-rw-r--r--deps/jemalloc/test/unit/psset.c748
-rw-r--r--deps/jemalloc/test/unit/ql.c139
-rw-r--r--deps/jemalloc/test/unit/qr.c44
-rw-r--r--deps/jemalloc/test/unit/rb.c794
-rw-r--r--deps/jemalloc/test/unit/retained.c60
-rw-r--r--deps/jemalloc/test/unit/rtree.c321
-rw-r--r--deps/jemalloc/test/unit/safety_check.c33
-rw-r--r--deps/jemalloc/test/unit/safety_check.sh2
-rw-r--r--deps/jemalloc/test/unit/san.c207
-rw-r--r--deps/jemalloc/test/unit/san.sh3
-rw-r--r--deps/jemalloc/test/unit/san_bump.c111
-rw-r--r--deps/jemalloc/test/unit/sc.c6
-rw-r--r--deps/jemalloc/test/unit/sec.c634
-rw-r--r--deps/jemalloc/test/unit/seq.c12
-rw-r--r--deps/jemalloc/test/unit/size_check.c79
-rw-r--r--deps/jemalloc/test/unit/size_check.sh5
-rw-r--r--deps/jemalloc/test/unit/size_classes.c88
-rw-r--r--deps/jemalloc/test/unit/slab.c22
-rw-r--r--deps/jemalloc/test/unit/smoothstep.c12
-rw-r--r--deps/jemalloc/test/unit/stats.c229
-rw-r--r--deps/jemalloc/test/unit/stats_print.c26
-rw-r--r--deps/jemalloc/test/unit/sz.c66
-rw-r--r--deps/jemalloc/test/unit/tcache_max.c175
-rw-r--r--deps/jemalloc/test/unit/tcache_max.sh3
-rw-r--r--deps/jemalloc/test/unit/test_hooks.c10
-rw-r--r--deps/jemalloc/test/unit/thread_event.c34
-rw-r--r--deps/jemalloc/test/unit/thread_event.sh5
-rw-r--r--deps/jemalloc/test/unit/ticker.c65
-rw-r--r--deps/jemalloc/test/unit/tsd.c55
-rw-r--r--deps/jemalloc/test/unit/uaf.c262
-rw-r--r--deps/jemalloc/test/unit/witness.c32
-rw-r--r--deps/jemalloc/test/unit/zero.c10
-rw-r--r--deps/jemalloc/test/unit/zero_realloc_abort.c26
-rw-r--r--deps/jemalloc/test/unit/zero_realloc_abort.sh3
-rw-r--r--deps/jemalloc/test/unit/zero_realloc_alloc.c48
-rw-r--r--deps/jemalloc/test/unit/zero_realloc_alloc.sh3
-rw-r--r--deps/jemalloc/test/unit/zero_realloc_free.c33
-rw-r--r--deps/jemalloc/test/unit/zero_realloc_free.sh3
-rw-r--r--deps/jemalloc/test/unit/zero_reallocs.c40
-rw-r--r--deps/jemalloc/test/unit/zero_reallocs.sh3
-rwxr-xr-xruntest-moduleapi1
-rw-r--r--sentinel.conf10
-rw-r--r--src/Makefile25
-rw-r--r--src/acl.c9
-rw-r--r--src/ae.c49
-rw-r--r--src/anet.c6
-rw-r--r--src/anet.h1
-rw-r--r--src/aof.c39
-rw-r--r--src/blocked.c3
-rw-r--r--src/cli_commands.c13
-rw-r--r--src/cli_commands.h46
-rw-r--r--src/cli_common.c2
-rw-r--r--src/cli_common.h2
-rw-r--r--src/cluster.c10
-rw-r--r--src/cluster.h2
-rw-r--r--src/commands.c7509
-rw-r--r--src/commands.def10843
-rw-r--r--src/commands.h40
-rw-r--r--src/commands/acl-cat.json8
-rw-r--r--src/commands/acl-deluser.json2
-rw-r--r--src/commands/acl-dryrun.json2
-rw-r--r--src/commands/acl-genpass.json2
-rw-r--r--src/commands/acl-getuser.json2
-rw-r--r--src/commands/acl-help.json2
-rw-r--r--src/commands/acl-list.json2
-rw-r--r--src/commands/acl-load.json2
-rw-r--r--src/commands/acl-log.json2
-rw-r--r--src/commands/acl-save.json2
-rw-r--r--src/commands/acl-setuser.json2
-rw-r--r--src/commands/acl-users.json2
-rw-r--r--src/commands/acl-whoami.json2
-rw-r--r--src/commands/acl.json2
-rw-r--r--src/commands/append.json4
-rw-r--r--src/commands/asking.json2
-rw-r--r--src/commands/auth.json2
-rw-r--r--src/commands/bgrewriteaof.json2
-rw-r--r--src/commands/bgsave.json2
-rw-r--r--src/commands/bitcount.json2
-rw-r--r--src/commands/bitfield.json2
-rw-r--r--src/commands/bitfield_ro.json2
-rw-r--r--src/commands/bitop.json2
-rw-r--r--src/commands/bitpos.json2
-rw-r--r--src/commands/blmove.json2
-rw-r--r--src/commands/blmpop.json2
-rw-r--r--src/commands/blpop.json2
-rw-r--r--src/commands/brpop.json2
-rw-r--r--src/commands/brpoplpush.json2
-rw-r--r--src/commands/bzmpop.json2
-rw-r--r--src/commands/bzpopmax.json2
-rw-r--r--src/commands/bzpopmin.json2
-rw-r--r--src/commands/client-caching.json2
-rw-r--r--src/commands/client-getname.json2
-rw-r--r--src/commands/client-getredir.json2
-rw-r--r--src/commands/client-help.json2
-rw-r--r--src/commands/client-id.json2
-rw-r--r--src/commands/client-info.json2
-rw-r--r--src/commands/client-kill.json2
-rw-r--r--src/commands/client-list.json2
-rw-r--r--src/commands/client-no-evict.json2
-rw-r--r--src/commands/client-no-touch.json2
-rw-r--r--src/commands/client-pause.json2
-rw-r--r--src/commands/client-reply.json2
-rw-r--r--src/commands/client-setinfo.json2
-rw-r--r--src/commands/client-setname.json2
-rw-r--r--src/commands/client-tracking.json2
-rw-r--r--src/commands/client-trackinginfo.json2
-rw-r--r--src/commands/client-unblock.json2
-rw-r--r--src/commands/client-unpause.json2
-rw-r--r--src/commands/client.json2
-rw-r--r--src/commands/cluster-addslots.json2
-rw-r--r--src/commands/cluster-addslotsrange.json2
-rw-r--r--src/commands/cluster-bumpepoch.json2
-rw-r--r--src/commands/cluster-count-failure-reports.json2
-rw-r--r--src/commands/cluster-countkeysinslot.json2
-rw-r--r--src/commands/cluster-delslots.json2
-rw-r--r--src/commands/cluster-delslotsrange.json2
-rw-r--r--src/commands/cluster-flushslots.json2
-rw-r--r--src/commands/cluster-forget.json2
-rw-r--r--src/commands/cluster-getkeysinslot.json2
-rw-r--r--src/commands/cluster-help.json2
-rw-r--r--src/commands/cluster-info.json2
-rw-r--r--src/commands/cluster-keyslot.json2
-rw-r--r--src/commands/cluster-links.json2
-rw-r--r--src/commands/cluster-meet.json2
-rw-r--r--src/commands/cluster-myid.json2
-rw-r--r--src/commands/cluster-myshardid.json2
-rw-r--r--src/commands/cluster-nodes.json2
-rw-r--r--src/commands/cluster-replicas.json2
-rw-r--r--src/commands/cluster-replicate.json2
-rw-r--r--src/commands/cluster-reset.json2
-rw-r--r--src/commands/cluster-saveconfig.json2
-rw-r--r--src/commands/cluster-set-config-epoch.json2
-rw-r--r--src/commands/cluster-setslot.json2
-rw-r--r--src/commands/cluster-shards.json2
-rw-r--r--src/commands/cluster-slaves.json2
-rw-r--r--src/commands/cluster-slots.json2
-rw-r--r--src/commands/cluster.json2
-rw-r--r--src/commands/command-count.json2
-rw-r--r--src/commands/command-docs.json2
-rw-r--r--src/commands/command-getkeys.json2
-rw-r--r--src/commands/command-getkeysandflags.json2
-rw-r--r--src/commands/command-help.json2
-rw-r--r--src/commands/command-info.json2
-rw-r--r--src/commands/command-list.json2
-rw-r--r--src/commands/command.json2
-rw-r--r--src/commands/config-get.json2
-rw-r--r--src/commands/config-help.json2
-rw-r--r--src/commands/config-resetstat.json2
-rw-r--r--src/commands/config-rewrite.json2
-rw-r--r--src/commands/config-set.json2
-rw-r--r--src/commands/config.json2
-rw-r--r--src/commands/copy.json2
-rw-r--r--src/commands/dbsize.json2
-rw-r--r--src/commands/debug.json2
-rw-r--r--src/commands/decr.json2
-rw-r--r--src/commands/decrby.json2
-rw-r--r--src/commands/del.json2
-rw-r--r--src/commands/discard.json2
-rw-r--r--src/commands/dump.json2
-rw-r--r--src/commands/echo.json2
-rw-r--r--src/commands/eval.json2
-rw-r--r--src/commands/eval_ro.json2
-rw-r--r--src/commands/evalsha.json2
-rw-r--r--src/commands/evalsha_ro.json2
-rw-r--r--src/commands/exec.json2
-rw-r--r--src/commands/exists.json2
-rw-r--r--src/commands/expire.json2
-rw-r--r--src/commands/expireat.json2
-rw-r--r--src/commands/expiretime.json2
-rw-r--r--src/commands/failover.json2
-rw-r--r--src/commands/fcall.json2
-rw-r--r--src/commands/fcall_ro.json2
-rw-r--r--src/commands/flushall.json2
-rw-r--r--src/commands/flushdb.json2
-rw-r--r--src/commands/function-delete.json2
-rw-r--r--src/commands/function-dump.json2
-rw-r--r--src/commands/function-flush.json2
-rw-r--r--src/commands/function-help.json2
-rw-r--r--src/commands/function-kill.json2
-rw-r--r--src/commands/function-list.json2
-rw-r--r--src/commands/function-load.json2
-rw-r--r--src/commands/function-restore.json2
-rw-r--r--src/commands/function-stats.json2
-rw-r--r--src/commands/function.json2
-rw-r--r--src/commands/geoadd.json2
-rw-r--r--src/commands/geodist.json2
-rw-r--r--src/commands/geohash.json2
-rw-r--r--src/commands/geopos.json2
-rw-r--r--src/commands/georadius.json35
-rw-r--r--src/commands/georadius_ro.json2
-rw-r--r--src/commands/georadiusbymember.json35
-rw-r--r--src/commands/georadiusbymember_ro.json2
-rw-r--r--src/commands/geosearch.json2
-rw-r--r--src/commands/geosearchstore.json2
-rw-r--r--src/commands/get.json2
-rw-r--r--src/commands/getbit.json2
-rw-r--r--src/commands/getdel.json2
-rw-r--r--src/commands/getex.json2
-rw-r--r--src/commands/getrange.json2
-rw-r--r--src/commands/getset.json2
-rw-r--r--src/commands/hdel.json2
-rw-r--r--src/commands/hello.json2
-rw-r--r--src/commands/hexists.json2
-rw-r--r--src/commands/hget.json2
-rw-r--r--src/commands/hgetall.json2
-rw-r--r--src/commands/hincrby.json2
-rw-r--r--src/commands/hincrbyfloat.json2
-rw-r--r--src/commands/hkeys.json2
-rw-r--r--src/commands/hlen.json2
-rw-r--r--src/commands/hmget.json2
-rw-r--r--src/commands/hmset.json2
-rw-r--r--src/commands/hrandfield.json2
-rw-r--r--src/commands/hscan.json2
-rw-r--r--src/commands/hset.json3
-rw-r--r--src/commands/hsetnx.json2
-rw-r--r--src/commands/hstrlen.json2
-rw-r--r--src/commands/hvals.json2
-rw-r--r--src/commands/incr.json2
-rw-r--r--src/commands/incrby.json2
-rw-r--r--src/commands/incrbyfloat.json2
-rw-r--r--src/commands/info.json3
-rw-r--r--src/commands/keys.json2
-rw-r--r--src/commands/lastsave.json2
-rw-r--r--src/commands/latency-doctor.json2
-rw-r--r--src/commands/latency-graph.json2
-rw-r--r--src/commands/latency-help.json2
-rw-r--r--src/commands/latency-histogram.json2
-rw-r--r--src/commands/latency-history.json2
-rw-r--r--src/commands/latency-latest.json2
-rw-r--r--src/commands/latency-reset.json2
-rw-r--r--src/commands/latency.json2
-rw-r--r--src/commands/lcs.json2
-rw-r--r--src/commands/lindex.json2
-rw-r--r--src/commands/linsert.json2
-rw-r--r--src/commands/llen.json2
-rw-r--r--src/commands/lmove.json2
-rw-r--r--src/commands/lmpop.json2
-rw-r--r--src/commands/lolwut.json2
-rw-r--r--src/commands/lpop.json2
-rw-r--r--src/commands/lpos.json2
-rw-r--r--src/commands/lpush.json2
-rw-r--r--src/commands/lpushx.json2
-rw-r--r--src/commands/lrange.json2
-rw-r--r--src/commands/lrem.json2
-rw-r--r--src/commands/lset.json2
-rw-r--r--src/commands/ltrim.json2
-rw-r--r--src/commands/memory-doctor.json2
-rw-r--r--src/commands/memory-help.json2
-rw-r--r--src/commands/memory-malloc-stats.json2
-rw-r--r--src/commands/memory-purge.json2
-rw-r--r--src/commands/memory-stats.json2
-rw-r--r--src/commands/memory-usage.json2
-rw-r--r--src/commands/memory.json2
-rw-r--r--src/commands/mget.json2
-rw-r--r--src/commands/migrate.json2
-rw-r--r--src/commands/module-help.json2
-rw-r--r--src/commands/module-list.json2
-rw-r--r--src/commands/module-load.json2
-rw-r--r--src/commands/module-loadex.json2
-rw-r--r--src/commands/module-unload.json2
-rw-r--r--src/commands/module.json2
-rw-r--r--src/commands/monitor.json2
-rw-r--r--src/commands/move.json2
-rw-r--r--src/commands/mset.json2
-rw-r--r--src/commands/msetnx.json2
-rw-r--r--src/commands/multi.json2
-rw-r--r--src/commands/object-encoding.json2
-rw-r--r--src/commands/object-freq.json2
-rw-r--r--src/commands/object-help.json2
-rw-r--r--src/commands/object-idletime.json2
-rw-r--r--src/commands/object-refcount.json2
-rw-r--r--src/commands/object.json2
-rw-r--r--src/commands/persist.json2
-rw-r--r--src/commands/pexpire.json2
-rw-r--r--src/commands/pexpireat.json2
-rw-r--r--src/commands/pexpiretime.json2
-rw-r--r--src/commands/pfadd.json2
-rw-r--r--src/commands/pfcount.json2
-rw-r--r--src/commands/pfdebug.json2
-rw-r--r--src/commands/pfmerge.json2
-rw-r--r--src/commands/pfselftest.json2
-rw-r--r--src/commands/ping.json2
-rw-r--r--src/commands/psetex.json2
-rw-r--r--src/commands/psubscribe.json2
-rw-r--r--src/commands/psync.json2
-rw-r--r--src/commands/pttl.json2
-rw-r--r--src/commands/publish.json2
-rw-r--r--src/commands/pubsub-channels.json2
-rw-r--r--src/commands/pubsub-help.json2
-rw-r--r--src/commands/pubsub-numpat.json2
-rw-r--r--src/commands/pubsub-numsub.json2
-rw-r--r--src/commands/pubsub-shardchannels.json2
-rw-r--r--src/commands/pubsub-shardnumsub.json2
-rw-r--r--src/commands/pubsub.json2
-rw-r--r--src/commands/punsubscribe.json2
-rw-r--r--src/commands/quit.json2
-rw-r--r--src/commands/randomkey.json2
-rw-r--r--src/commands/readonly.json2
-rw-r--r--src/commands/readwrite.json2
-rw-r--r--src/commands/rename.json2
-rw-r--r--src/commands/renamenx.json2
-rw-r--r--src/commands/replconf.json2
-rw-r--r--src/commands/replicaof.json2
-rw-r--r--src/commands/reset.json2
-rw-r--r--src/commands/restore-asking.json2
-rw-r--r--src/commands/restore.json2
-rw-r--r--src/commands/role.json2
-rw-r--r--src/commands/rpop.json2
-rw-r--r--src/commands/rpoplpush.json2
-rw-r--r--src/commands/rpush.json2
-rw-r--r--src/commands/rpushx.json2
-rw-r--r--src/commands/sadd.json2
-rw-r--r--src/commands/save.json2
-rw-r--r--src/commands/scan.json2
-rw-r--r--src/commands/scard.json2
-rw-r--r--src/commands/script-debug.json2
-rw-r--r--src/commands/script-exists.json2
-rw-r--r--src/commands/script-flush.json2
-rw-r--r--src/commands/script-help.json2
-rw-r--r--src/commands/script-kill.json2
-rw-r--r--src/commands/script-load.json2
-rw-r--r--src/commands/script.json2
-rw-r--r--src/commands/sdiff.json2
-rw-r--r--src/commands/sdiffstore.json2
-rw-r--r--src/commands/select.json2
-rw-r--r--src/commands/sentinel-ckquorum.json2
-rw-r--r--src/commands/sentinel-config.json2
-rw-r--r--src/commands/sentinel-debug.json17
-rw-r--r--src/commands/sentinel-failover.json2
-rw-r--r--src/commands/sentinel-flushconfig.json2
-rw-r--r--src/commands/sentinel-get-master-addr-by-name.json2
-rw-r--r--src/commands/sentinel-help.json2
-rw-r--r--src/commands/sentinel-info-cache.json43
-rw-r--r--src/commands/sentinel-is-master-down-by-addr.json2
-rw-r--r--src/commands/sentinel-master.json2
-rw-r--r--src/commands/sentinel-masters.json14
-rw-r--r--src/commands/sentinel-monitor.json2
-rw-r--r--src/commands/sentinel-myid.json8
-rw-r--r--src/commands/sentinel-pending-scripts.json41
-rw-r--r--src/commands/sentinel-remove.json2
-rw-r--r--src/commands/sentinel-replicas.json2
-rw-r--r--src/commands/sentinel-reset.json6
-rw-r--r--src/commands/sentinel-sentinels.json12
-rw-r--r--src/commands/sentinel-set.json2
-rw-r--r--src/commands/sentinel-simulate-failure.json17
-rw-r--r--src/commands/sentinel-slaves.json14
-rw-r--r--src/commands/sentinel.json2
-rw-r--r--src/commands/set.json2
-rw-r--r--src/commands/setbit.json2
-rw-r--r--src/commands/setex.json2
-rw-r--r--src/commands/setnx.json2
-rw-r--r--src/commands/setrange.json2
-rw-r--r--src/commands/shutdown.json2
-rw-r--r--src/commands/sinter.json2
-rw-r--r--src/commands/sintercard.json2
-rw-r--r--src/commands/sinterstore.json2
-rw-r--r--src/commands/sismember.json2
-rw-r--r--src/commands/slaveof.json2
-rw-r--r--src/commands/slowlog-get.json2
-rw-r--r--src/commands/slowlog-len.json2
-rw-r--r--src/commands/slowlog-reset.json2
-rw-r--r--src/commands/slowlog.json2
-rw-r--r--src/commands/smembers.json2
-rw-r--r--src/commands/smismember.json2
-rw-r--r--src/commands/smove.json2
-rw-r--r--src/commands/sort.json2
-rw-r--r--src/commands/sort_ro.json2
-rw-r--r--src/commands/spop.json2
-rw-r--r--src/commands/srem.json2
-rw-r--r--src/commands/sscan.json2
-rw-r--r--src/commands/ssubscribe.json2
-rw-r--r--src/commands/strlen.json2
-rw-r--r--src/commands/subscribe.json2
-rw-r--r--src/commands/substr.json2
-rw-r--r--src/commands/sunion.json2
-rw-r--r--src/commands/sunionstore.json2
-rw-r--r--src/commands/sunsubscribe.json2
-rw-r--r--src/commands/swapdb.json2
-rw-r--r--src/commands/sync.json2
-rw-r--r--src/commands/time.json2
-rw-r--r--src/commands/touch.json2
-rw-r--r--src/commands/ttl.json2
-rw-r--r--src/commands/type.json2
-rw-r--r--src/commands/unlink.json2
-rw-r--r--src/commands/unsubscribe.json2
-rw-r--r--src/commands/unwatch.json2
-rw-r--r--src/commands/wait.json2
-rw-r--r--src/commands/waitaof.json2
-rw-r--r--src/commands/watch.json2
-rw-r--r--src/commands/xack.json2
-rw-r--r--src/commands/xadd.json2
-rw-r--r--src/commands/xautoclaim.json2
-rw-r--r--src/commands/xclaim.json2
-rw-r--r--src/commands/xdel.json2
-rw-r--r--src/commands/xgroup-create.json2
-rw-r--r--src/commands/xgroup-createconsumer.json2
-rw-r--r--src/commands/xgroup-delconsumer.json2
-rw-r--r--src/commands/xgroup-destroy.json2
-rw-r--r--src/commands/xgroup-help.json2
-rw-r--r--src/commands/xgroup-setid.json2
-rw-r--r--src/commands/xgroup.json2
-rw-r--r--src/commands/xinfo-consumers.json2
-rw-r--r--src/commands/xinfo-groups.json2
-rw-r--r--src/commands/xinfo-help.json2
-rw-r--r--src/commands/xinfo-stream.json2
-rw-r--r--src/commands/xinfo.json2
-rw-r--r--src/commands/xlen.json2
-rw-r--r--src/commands/xpending.json2
-rw-r--r--src/commands/xrange.json2
-rw-r--r--src/commands/xread.json3
-rw-r--r--src/commands/xreadgroup.json2
-rw-r--r--src/commands/xrevrange.json2
-rw-r--r--src/commands/xsetid.json2
-rw-r--r--src/commands/xtrim.json2
-rw-r--r--src/commands/zadd.json2
-rw-r--r--src/commands/zcard.json2
-rw-r--r--src/commands/zcount.json2
-rw-r--r--src/commands/zdiff.json2
-rw-r--r--src/commands/zdiffstore.json2
-rw-r--r--src/commands/zincrby.json2
-rw-r--r--src/commands/zinter.json2
-rw-r--r--src/commands/zintercard.json2
-rw-r--r--src/commands/zinterstore.json2
-rw-r--r--src/commands/zlexcount.json2
-rw-r--r--src/commands/zmpop.json2
-rw-r--r--src/commands/zmscore.json2
-rw-r--r--src/commands/zpopmax.json2
-rw-r--r--src/commands/zpopmin.json2
-rw-r--r--src/commands/zrandmember.json4
-rw-r--r--src/commands/zrange.json2
-rw-r--r--src/commands/zrangebylex.json2
-rw-r--r--src/commands/zrangebyscore.json2
-rw-r--r--src/commands/zrangestore.json2
-rw-r--r--src/commands/zrank.json2
-rw-r--r--src/commands/zrem.json2
-rw-r--r--src/commands/zremrangebylex.json2
-rw-r--r--src/commands/zremrangebyrank.json2
-rw-r--r--src/commands/zremrangebyscore.json2
-rw-r--r--src/commands/zrevrange.json2
-rw-r--r--src/commands/zrevrangebylex.json2
-rw-r--r--src/commands/zrevrangebyscore.json2
-rw-r--r--src/commands/zrevrank.json2
-rw-r--r--src/commands/zscan.json2
-rw-r--r--src/commands/zscore.json2
-rw-r--r--src/commands/zunion.json2
-rw-r--r--src/commands/zunionstore.json2
-rw-r--r--src/config.c11
-rw-r--r--src/config.h10
-rw-r--r--src/connection.c10
-rw-r--r--src/connection.h16
-rw-r--r--src/db.c13
-rw-r--r--src/debug.c11
-rw-r--r--src/defrag.c4
-rw-r--r--src/dict.c6
-rw-r--r--src/eval.c8
-rw-r--r--src/evict.c2
-rw-r--r--src/fmacros.h10
-rw-r--r--src/function_lua.c2
-rw-r--r--src/functions.c16
-rw-r--r--src/functions.h18
-rw-r--r--src/geo.c2
-rw-r--r--src/help.h1884
-rw-r--r--src/latency.c12
-rw-r--r--src/latency.h16
-rw-r--r--src/lazyfree.c2
-rw-r--r--src/listpack.c4
-rw-r--r--src/listpack_malloc.h7
-rw-r--r--src/logreqres.c9
-rw-r--r--src/module.c262
-rw-r--r--src/monotonic.c22
-rw-r--r--src/monotonic.h6
-rw-r--r--src/networking.c44
-rw-r--r--src/pubsub.c4
-rw-r--r--src/rand.c2
-rw-r--r--src/rand.h2
-rw-r--r--src/rdb.c76
-rw-r--r--src/rdb.h1
-rw-r--r--src/redis-benchmark.c71
-rw-r--r--src/redis-check-aof.c2
-rw-r--r--src/redis-check-rdb.c2
-rw-r--r--src/redis-cli.c1098
-rw-r--r--src/redismodule.h31
-rw-r--r--src/replication.c13
-rw-r--r--src/script.c16
-rw-r--r--src/script.h14
-rw-r--r--src/sentinel.c26
-rw-r--r--src/server.c310
-rw-r--r--src/server.h148
-rw-r--r--src/sha1.h5
-rw-r--r--src/slowlog.c15
-rw-r--r--src/socket.c4
-rw-r--r--src/t_hash.c10
-rw-r--r--src/t_list.c2
-rw-r--r--src/t_set.c34
-rw-r--r--src/t_zset.c2
-rw-r--r--src/tls.c8
-rw-r--r--src/unix.c2
-rw-r--r--src/version.h4
-rw-r--r--src/ziplist.c4
-rw-r--r--src/zmalloc.c82
-rw-r--r--src/zmalloc.h46
-rw-r--r--tests/assets/default.conf8
-rw-r--r--tests/assets/test_cli_hint_suite.txt111
-rw-r--r--tests/cluster/run.tcl1
-rw-r--r--tests/integration/aof-multi-part.tcl4
-rw-r--r--tests/integration/block-repl.tcl2
-rw-r--r--tests/integration/failover.tcl30
-rw-r--r--tests/integration/psync2-master-restart.tcl44
-rw-r--r--tests/integration/psync2.tcl2
-rw-r--r--tests/integration/rdb.tcl5
-rw-r--r--tests/integration/redis-cli.tcl21
-rw-r--r--tests/integration/replication-2.tcl4
-rw-r--r--tests/integration/replication-4.tcl12
-rw-r--r--tests/integration/replication-buffer.tcl4
-rw-r--r--tests/integration/replication-psync.tcl4
-rw-r--r--tests/integration/replication.tcl44
-rw-r--r--tests/integration/shutdown.tcl36
-rw-r--r--tests/modules/Makefile3
-rw-r--r--tests/modules/blockonkeys.c2
-rw-r--r--tests/modules/cmdintrospection.c2
-rw-r--r--tests/modules/rdbloadsave.c162
-rw-r--r--tests/modules/reply.c10
-rw-r--r--tests/modules/zset.c73
-rw-r--r--tests/sentinel/run.tcl1
-rw-r--r--tests/sentinel/tests/00-base.tcl51
-rw-r--r--tests/sentinel/tests/05-manual.tcl25
-rw-r--r--tests/sentinel/tests/07-down-conditions.tcl4
-rw-r--r--tests/support/cluster_util.tcl2
-rw-r--r--tests/support/server.tcl3
-rw-r--r--tests/support/test.tcl9
-rw-r--r--tests/support/util.tcl39
-rw-r--r--tests/test_helper.tcl9
-rw-r--r--tests/unit/acl.tcl14
-rw-r--r--tests/unit/aofrw.tcl2
-rw-r--r--tests/unit/client-eviction.tcl4
-rw-r--r--tests/unit/cluster/cli.tcl6
-rw-r--r--tests/unit/cluster/links.tcl4
-rw-r--r--tests/unit/expire.tcl14
-rw-r--r--tests/unit/info.tcl63
-rw-r--r--tests/unit/introspection-2.tcl4
-rw-r--r--tests/unit/introspection.tcl15
-rw-r--r--tests/unit/maxmemory.tcl4
-rw-r--r--tests/unit/moduleapi/cluster.tcl4
-rw-r--r--tests/unit/moduleapi/misc.tcl2
-rw-r--r--tests/unit/moduleapi/rdbloadsave.tcl200
-rw-r--r--tests/unit/moduleapi/reply.tcl11
-rw-r--r--tests/unit/moduleapi/stream.tcl17
-rw-r--r--tests/unit/moduleapi/testrdb.tcl8
-rw-r--r--tests/unit/moduleapi/zset.tcl20
-rw-r--r--tests/unit/multi.tcl6
-rw-r--r--tests/unit/querybuf.tcl32
-rw-r--r--tests/unit/shutdown.tcl30
-rw-r--r--tests/unit/slowlog.tcl9
-rw-r--r--tests/unit/type/hash.tcl11
-rw-r--r--tests/unit/type/incr.tcl11
-rw-r--r--tests/unit/type/list.tcl10
-rw-r--r--tests/unit/type/string.tcl16
-rw-r--r--tests/unit/type/zset.tcl40
-rw-r--r--tests/unit/wait.tcl66
-rwxr-xr-xutils/generate-command-code.py126
-rwxr-xr-xutils/generate-command-help.rb151
-rwxr-xr-xutils/req-res-log-validator.py47
907 files changed, 66760 insertions, 31282 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index d2edf2f4b..ecbaf50bd 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -35,7 +35,7 @@ jobs:
build-debian-old:
runs-on: ubuntu-latest
- container: debian:oldoldstable
+ container: debian:buster
steps:
- uses: actions/checkout@v3
- name: make
diff --git a/.github/workflows/daily.yml b/.github/workflows/daily.yml
index d792ae3ab..42a13f100 100644
--- a/.github/workflows/daily.yml
+++ b/.github/workflows/daily.yml
@@ -11,7 +11,7 @@ on:
inputs:
skipjobs:
description: 'jobs to skip (delete the ones you wanna keep, do not leave empty)'
- default: 'valgrind,sanitizer,tls,freebsd,macos,alpine,32bit,iothreads,ubuntu,centos,malloc,specific,reply-schema'
+ default: 'valgrind,sanitizer,tls,freebsd,macos,alpine,32bit,iothreads,ubuntu,centos,malloc,specific,fortify,reply-schema'
skiptests:
description: 'tests to skip (delete the ones you wanna keep, do not leave empty)'
default: 'redis,modules,sentinel,cluster,unittest'
@@ -43,7 +43,10 @@ jobs:
run: |
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
- echo "skipping: ${{github.event.inputs.skipjobs}} and ${{github.event.inputs.skiptests}}"
+ echo "skipjobs: ${{github.event.inputs.skipjobs}}"
+ echo "skiptests: ${{github.event.inputs.skiptests}}"
+ echo "test_args: ${{github.event.inputs.test_args}}"
+ echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
- uses: actions/checkout@v3
with:
repository: ${{ env.GITHUB_REPOSITORY }}
@@ -68,6 +71,50 @@ jobs:
if: true && !contains(github.event.inputs.skiptests, 'unittest')
run: ./src/redis-server test all --accurate
+ test-ubuntu-jemalloc-fortify:
+ runs-on: ubuntu-latest
+ if: |
+ (github.event_name == 'workflow_dispatch' || (github.event_name != 'workflow_dispatch' && github.repository == 'redis/redis')) &&
+ !contains(github.event.inputs.skipjobs, 'fortify')
+ container: ubuntu:lunar
+ timeout-minutes: 14400
+ steps:
+ - name: prep
+ if: github.event_name == 'workflow_dispatch'
+ run: |
+ echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
+ echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
+ echo "skipjobs: ${{github.event.inputs.skipjobs}}"
+ echo "skiptests: ${{github.event.inputs.skiptests}}"
+ echo "test_args: ${{github.event.inputs.test_args}}"
+ echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
+ - uses: actions/checkout@v3
+ with:
+ repository: ${{ env.GITHUB_REPOSITORY }}
+ ref: ${{ env.GITHUB_HEAD_REF }}
+ - name: make
+ run: |
+ apt-get update && apt-get install -y make gcc-13
+ update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-13 100
+ make CC=gcc REDIS_CFLAGS='-Werror -DREDIS_TEST -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3'
+ - name: testprep
+ run: apt-get install -y tcl8.6 tclx procps
+ - name: test
+ if: true && !contains(github.event.inputs.skiptests, 'redis')
+ run: ./runtest --accurate --verbose --dump-logs ${{github.event.inputs.test_args}}
+ - name: module api test
+ if: true && !contains(github.event.inputs.skiptests, 'modules')
+ run: ./runtest-moduleapi --verbose --dump-logs ${{github.event.inputs.test_args}}
+ - name: sentinel tests
+ if: true && !contains(github.event.inputs.skiptests, 'sentinel')
+ run: ./runtest-sentinel ${{github.event.inputs.cluster_test_args}}
+ - name: cluster tests
+ if: true && !contains(github.event.inputs.skiptests, 'cluster')
+ run: ./runtest-cluster ${{github.event.inputs.cluster_test_args}}
+ - name: unittest
+ if: true && !contains(github.event.inputs.skiptests, 'unittest')
+ run: ./src/redis-server test all --accurate
+
test-ubuntu-libc-malloc:
runs-on: ubuntu-latest
if: |
@@ -80,6 +127,10 @@ jobs:
run: |
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
+ echo "skipjobs: ${{github.event.inputs.skipjobs}}"
+ echo "skiptests: ${{github.event.inputs.skiptests}}"
+ echo "test_args: ${{github.event.inputs.test_args}}"
+ echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
- uses: actions/checkout@v3
with:
repository: ${{ env.GITHUB_REPOSITORY }}
@@ -113,6 +164,10 @@ jobs:
run: |
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
+ echo "skipjobs: ${{github.event.inputs.skipjobs}}"
+ echo "skiptests: ${{github.event.inputs.skiptests}}"
+ echo "test_args: ${{github.event.inputs.test_args}}"
+ echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
- uses: actions/checkout@v3
with:
repository: ${{ env.GITHUB_REPOSITORY }}
@@ -146,6 +201,10 @@ jobs:
run: |
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
+ echo "skipjobs: ${{github.event.inputs.skipjobs}}"
+ echo "skiptests: ${{github.event.inputs.skiptests}}"
+ echo "test_args: ${{github.event.inputs.test_args}}"
+ echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
- uses: actions/checkout@v3
with:
repository: ${{ env.GITHUB_REPOSITORY }}
@@ -186,6 +245,10 @@ jobs:
run: |
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
+ echo "skipjobs: ${{github.event.inputs.skipjobs}}"
+ echo "skiptests: ${{github.event.inputs.skiptests}}"
+ echo "test_args: ${{github.event.inputs.test_args}}"
+ echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
- uses: actions/checkout@v3
with:
repository: ${{ env.GITHUB_REPOSITORY }}
@@ -226,6 +289,10 @@ jobs:
run: |
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
+ echo "skipjobs: ${{github.event.inputs.skipjobs}}"
+ echo "skiptests: ${{github.event.inputs.skiptests}}"
+ echo "test_args: ${{github.event.inputs.test_args}}"
+ echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
- uses: actions/checkout@v3
with:
repository: ${{ env.GITHUB_REPOSITORY }}
@@ -266,6 +333,10 @@ jobs:
run: |
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
+ echo "skipjobs: ${{github.event.inputs.skipjobs}}"
+ echo "skiptests: ${{github.event.inputs.skiptests}}"
+ echo "test_args: ${{github.event.inputs.test_args}}"
+ echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
- uses: actions/checkout@v3
with:
repository: ${{ env.GITHUB_REPOSITORY }}
@@ -294,6 +365,10 @@ jobs:
run: |
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
+ echo "skipjobs: ${{github.event.inputs.skipjobs}}"
+ echo "skiptests: ${{github.event.inputs.skiptests}}"
+ echo "test_args: ${{github.event.inputs.test_args}}"
+ echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
- uses: actions/checkout@v3
with:
repository: ${{ env.GITHUB_REPOSITORY }}
@@ -323,10 +398,10 @@ jobs:
./src/redis-cli -p 8080 save > /dev/null
VMOUT=$(vmtouch -v /tmp/master/dump.rdb)
echo $VMOUT
- grep -q "0%" <<< $VMOUT
+ grep -q " 0%" <<< $VMOUT
CACHE=$(grep -w file /sys/fs/cgroup/memory.stat | awk '{print $2}')
echo "$CACHE"
- if [ "$(( $CACHE-$CACHE0 ))" -gt "500000" ]; then exit 1; fi
+ if [ "$(( $CACHE-$CACHE0 ))" -gt "8000000" ]; then exit 1; fi
echo "test replication doesn't increase cache"
./src/redis-cli -p 8081 REPLICAOF 127.0.0.1 8080 > /dev/null
@@ -334,13 +409,13 @@ jobs:
sleep 1 # wait for the completion of cache reclaim bio
VMOUT=$(vmtouch -v /tmp/master/dump.rdb)
echo $VMOUT
- grep -q "0%" <<< $VMOUT
+ grep -q " 0%" <<< $VMOUT
VMOUT=$(vmtouch -v /tmp/slave/dump.rdb)
echo $VMOUT
- grep -q "0%" <<< $VMOUT
+ grep -q " 0%" <<< $VMOUT
CACHE=$(grep -w file /sys/fs/cgroup/memory.stat | awk '{print $2}')
echo "$CACHE"
- if [ "$(( $CACHE-$CACHE0 ))" -gt "500000" ]; then exit 1; fi
+ if [ "$(( $CACHE-$CACHE0 ))" -gt "8000000" ]; then exit 1; fi
echo "test reboot doesn't increase cache"
PID=$(cat /tmp/master/redis.pid)
@@ -351,10 +426,10 @@ jobs:
sleep 1 # wait for the completion of cache reclaim bio
VMOUT=$(vmtouch -v /tmp/master/dump.rdb)
echo $VMOUT
- grep -q "0%" <<< $VMOUT
+ grep -q " 0%" <<< $VMOUT
CACHE=$(grep -w file /sys/fs/cgroup/memory.stat | awk '{print $2}')
echo "$CACHE"
- if [ "$(( $CACHE-$CACHE0 ))" -gt "500000" ]; then exit 1; fi
+ if [ "$(( $CACHE-$CACHE0 ))" -gt "8000000" ]; then exit 1; fi
test-valgrind-test:
runs-on: ubuntu-latest
@@ -368,6 +443,10 @@ jobs:
run: |
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
+ echo "skipjobs: ${{github.event.inputs.skipjobs}}"
+ echo "skiptests: ${{github.event.inputs.skiptests}}"
+ echo "test_args: ${{github.event.inputs.test_args}}"
+ echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
- uses: actions/checkout@v3
with:
repository: ${{ env.GITHUB_REPOSITORY }}
@@ -394,6 +473,10 @@ jobs:
run: |
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
+ echo "skipjobs: ${{github.event.inputs.skipjobs}}"
+ echo "skiptests: ${{github.event.inputs.skiptests}}"
+ echo "test_args: ${{github.event.inputs.test_args}}"
+ echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
- uses: actions/checkout@v3
with:
repository: ${{ env.GITHUB_REPOSITORY }}
@@ -425,6 +508,10 @@ jobs:
run: |
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
+ echo "skipjobs: ${{github.event.inputs.skipjobs}}"
+ echo "skiptests: ${{github.event.inputs.skiptests}}"
+ echo "test_args: ${{github.event.inputs.test_args}}"
+ echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
- uses: actions/checkout@v3
with:
repository: ${{ env.GITHUB_REPOSITORY }}
@@ -451,6 +538,10 @@ jobs:
run: |
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
+ echo "skipjobs: ${{github.event.inputs.skipjobs}}"
+ echo "skiptests: ${{github.event.inputs.skiptests}}"
+ echo "test_args: ${{github.event.inputs.test_args}}"
+ echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
- uses: actions/checkout@v3
with:
repository: ${{ env.GITHUB_REPOSITORY }}
@@ -487,6 +578,10 @@ jobs:
run: |
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
+ echo "skipjobs: ${{github.event.inputs.skipjobs}}"
+ echo "skiptests: ${{github.event.inputs.skiptests}}"
+ echo "test_args: ${{github.event.inputs.test_args}}"
+ echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
- uses: actions/checkout@v3
with:
repository: ${{ env.GITHUB_REPOSITORY }}
@@ -530,6 +625,10 @@ jobs:
run: |
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
+ echo "skipjobs: ${{github.event.inputs.skipjobs}}"
+ echo "skiptests: ${{github.event.inputs.skiptests}}"
+ echo "test_args: ${{github.event.inputs.test_args}}"
+ echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
- uses: actions/checkout@v3
with:
repository: ${{ env.GITHUB_REPOSITORY }}
@@ -569,6 +668,10 @@ jobs:
run: |
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
+ echo "skipjobs: ${{github.event.inputs.skipjobs}}"
+ echo "skiptests: ${{github.event.inputs.skiptests}}"
+ echo "test_args: ${{github.event.inputs.test_args}}"
+ echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
- uses: actions/checkout@v3
with:
repository: ${{ env.GITHUB_REPOSITORY }}
@@ -605,6 +708,10 @@ jobs:
run: |
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
+ echo "skipjobs: ${{github.event.inputs.skipjobs}}"
+ echo "skiptests: ${{github.event.inputs.skiptests}}"
+ echo "test_args: ${{github.event.inputs.test_args}}"
+ echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
- uses: actions/checkout@v3
with:
repository: ${{ env.GITHUB_REPOSITORY }}
@@ -648,6 +755,10 @@ jobs:
run: |
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
+ echo "skipjobs: ${{github.event.inputs.skipjobs}}"
+ echo "skiptests: ${{github.event.inputs.skiptests}}"
+ echo "test_args: ${{github.event.inputs.test_args}}"
+ echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
- uses: actions/checkout@v3
with:
repository: ${{ env.GITHUB_REPOSITORY }}
@@ -690,6 +801,10 @@ jobs:
run: |
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
+ echo "skipjobs: ${{github.event.inputs.skipjobs}}"
+ echo "skiptests: ${{github.event.inputs.skiptests}}"
+ echo "test_args: ${{github.event.inputs.test_args}}"
+ echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
- uses: actions/checkout@v3
with:
repository: ${{ env.GITHUB_REPOSITORY }}
@@ -698,10 +813,10 @@ jobs:
run: make REDIS_CFLAGS='-Werror'
- name: test
if: true && !contains(github.event.inputs.skiptests, 'redis')
- run: ./runtest --accurate --verbose --no-latency --dump-logs ${{github.event.inputs.test_args}}
+ run: ./runtest --accurate --verbose --verbose --clients 1 --no-latency --dump-logs ${{github.event.inputs.test_args}}
- name: module api test
if: true && !contains(github.event.inputs.skiptests, 'modules')
- run: ./runtest-moduleapi --verbose --no-latency --dump-logs ${{github.event.inputs.test_args}}
+ run: ./runtest-moduleapi --verbose --verbose --clients 1 --no-latency --dump-logs ${{github.event.inputs.test_args}}
test-macos-latest-sentinel:
runs-on: macos-latest
@@ -715,6 +830,10 @@ jobs:
run: |
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
+ echo "skipjobs: ${{github.event.inputs.skipjobs}}"
+ echo "skiptests: ${{github.event.inputs.skiptests}}"
+ echo "test_args: ${{github.event.inputs.test_args}}"
+ echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
- uses: actions/checkout@v3
with:
repository: ${{ env.GITHUB_REPOSITORY }}
@@ -737,6 +856,10 @@ jobs:
run: |
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
+ echo "skipjobs: ${{github.event.inputs.skipjobs}}"
+ echo "skiptests: ${{github.event.inputs.skiptests}}"
+ echo "test_args: ${{github.event.inputs.test_args}}"
+ echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
- uses: actions/checkout@v3
with:
repository: ${{ env.GITHUB_REPOSITORY }}
@@ -759,6 +882,10 @@ jobs:
run: |
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
+ echo "skipjobs: ${{github.event.inputs.skipjobs}}"
+ echo "skiptests: ${{github.event.inputs.skiptests}}"
+ echo "test_args: ${{github.event.inputs.test_args}}"
+ echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
- uses: actions/checkout@v3
with:
repository: ${{ env.GITHUB_REPOSITORY }}
@@ -787,6 +914,10 @@ jobs:
run: |
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
+ echo "skipjobs: ${{github.event.inputs.skipjobs}}"
+ echo "skiptests: ${{github.event.inputs.skiptests}}"
+ echo "test_args: ${{github.event.inputs.test_args}}"
+ echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
- uses: actions/checkout@v3
with:
repository: ${{ env.GITHUB_REPOSITORY }}
@@ -814,6 +945,10 @@ jobs:
run: |
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
+ echo "skipjobs: ${{github.event.inputs.skipjobs}}"
+ echo "skiptests: ${{github.event.inputs.skiptests}}"
+ echo "test_args: ${{github.event.inputs.test_args}}"
+ echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
- uses: actions/checkout@v3
with:
repository: ${{ env.GITHUB_REPOSITORY }}
@@ -841,6 +976,10 @@ jobs:
run: |
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
+ echo "skipjobs: ${{github.event.inputs.skipjobs}}"
+ echo "skiptests: ${{github.event.inputs.skiptests}}"
+ echo "test_args: ${{github.event.inputs.test_args}}"
+ echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
- uses: actions/checkout@v3
with:
repository: ${{ env.GITHUB_REPOSITORY }}
@@ -876,6 +1015,10 @@ jobs:
run: |
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
+ echo "skipjobs: ${{github.event.inputs.skipjobs}}"
+ echo "skiptests: ${{github.event.inputs.skiptests}}"
+ echo "test_args: ${{github.event.inputs.test_args}}"
+ echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
- uses: actions/checkout@v3
with:
repository: ${{ env.GITHUB_REPOSITORY }}
@@ -911,7 +1054,10 @@ jobs:
run: |
echo "GITHUB_REPOSITORY=${{github.event.inputs.use_repo}}" >> $GITHUB_ENV
echo "GITHUB_HEAD_REF=${{github.event.inputs.use_git_ref}}" >> $GITHUB_ENV
- echo "skipping: ${{github.event.inputs.skipjobs}} and ${{github.event.inputs.skiptests}}"
+ echo "skipjobs: ${{github.event.inputs.skipjobs}}"
+ echo "skiptests: ${{github.event.inputs.skiptests}}"
+ echo "test_args: ${{github.event.inputs.test_args}}"
+ echo "cluster_test_args: ${{github.event.inputs.cluster_test_args}}"
- uses: actions/checkout@v3
with:
repository: ${{ env.GITHUB_REPOSITORY }}
@@ -922,10 +1068,10 @@ jobs:
run: sudo apt-get install tcl8.6 tclx
- name: test
if: true && !contains(github.event.inputs.skiptests, 'redis')
- run: ./runtest --log-req-res --dont-clean --force-resp3 --tags -slow --verbose --dump-logs ${{github.event.inputs.test_args}}
+ run: ./runtest --log-req-res --no-latency --dont-clean --force-resp3 --tags -slow --verbose --dump-logs ${{github.event.inputs.test_args}}
- name: module api test
if: true && !contains(github.event.inputs.skiptests, 'modules')
- run: ./runtest-moduleapi --log-req-res --dont-clean --force-resp3 --dont-pre-clean --verbose --dump-logs ${{github.event.inputs.test_args}}
+ run: ./runtest-moduleapi --log-req-res --no-latency --dont-clean --force-resp3 --dont-pre-clean --verbose --dump-logs ${{github.event.inputs.test_args}}
- name: sentinel tests
if: true && !contains(github.event.inputs.skiptests, 'sentinel')
run: ./runtest-sentinel --log-req-res --dont-clean --force-resp3 ${{github.event.inputs.cluster_test_args}}
@@ -937,5 +1083,5 @@ jobs:
with:
path: "./utils/req-res-validator/requirements.txt"
- name: validator
- run: ./utils/req-res-log-validator.py --verbose --fail-missing-reply-schemas --fail-commands-not-all-hit
+ run: ./utils/req-res-log-validator.py --verbose --fail-missing-reply-schemas ${{ (!contains(github.event.inputs.skiptests, 'redis') && !contains(github.event.inputs.skiptests, 'module') && !contains(github.event.inputs.sentinel, 'redis') && !contains(github.event.inputs.skiptests, 'cluster')) && github.event.inputs.test_args == '' && github.event.inputs.cluster_test_args == '' && '--fail-commands-not-all-hit' || '' }}
diff --git a/00-RELEASENOTES b/00-RELEASENOTES
index 0e7656ecd..851a1e543 100644
--- a/00-RELEASENOTES
+++ b/00-RELEASENOTES
@@ -12,6 +12,60 @@ SECURITY: There are security fixes in the release.
--------------------------------------------------------------------------------
================================================================================
+Redis 7.2 RC2 Released Mon May 15 12:00:00 IST 2023
+================================================================================
+
+Upgrade urgency LOW: This is the second Release Candidate for Redis 7.2.
+
+INFO fields and introspection changes
+=====================================
+
+* Add a few low level event loop metrics to help diagnose latency (#11963)
+
+Performance and resource utilization improvements
+=================================================
+
+* Minor performance improvement to SADD and HSET (#12019)
+
+Platform / toolchain support related changes
+=================================================
+
+* Upgrade to Jemalloc 5.3.0, resolves a rare fork child hang (#12115)
+* Fix a compiler fortification induced crash when used with link time optimizations (#11982)
+* Fix local clients detection, 127.*.*.* instead of 127.0.0.1 (#11664)
+* Report AOF failure status to systemd in shutdown (#12065)
+
+Changes in CLI tools
+====================
+
+* redis-cli: Reimplement and improve help hints based on actual command arg docs (#10515)
+* redis-cli: Add option --count for tuning SCAN based features (#12042)
+* redis-benchmark: Add --seed option to seed the random number generator (#11945)
+
+Module API changes
+==================
+
+* Add RM_RdbLoad and RM_RdbSave APIs (#11852)
+* Add RM_ReplyWithErrorFormat that can support format string (#11923)
+* Fix: Delete empty key when RM_ZsetAdd, RM_ZsetIncrby, RM_StreamAdd fail (#12129)
+
+Bug Fixes
+=========
+
+* LPOS with RANK set to LONG_MIN returning wrong result (#12167)
+* Avoid unnecessary full sync after master restart in a rare case (#12088)
+* Iterate clients fairly when processing background chores (#12025)
+* Avoid incorrect shrinking of query buffer when reading large data from clients (#12000)
+* Sentinel: Fix config rewrite error when old known-slave is used (#11775)
+* ACL: Disconnect pub-sub subscribers when revoking allchannels permission (#11992)
+* Add a missing fsync of AOF file in rare cases (#11973)
+
+Fixes for issues in previous releases of Redis 7.2
+--------------------------------------------------
+
+* Fix tracking of command duration metrics for MULTI, EVAL, WAIT and modules (#11970)
+
+================================================================================
Redis 7.2 RC1 Released Wed Mar 22 12:00:00 IST 2023
================================================================================
@@ -200,28 +254,28 @@ A special thank you for the amount of work put into this release by:
- Zhu Binbin
- Guy Benoish
- Oran Agra
-- Ran Shidlansik
- sundb
+- Ran Shidlansik
- Zhenwei Pi
- Karthik Subbarao
- Huang Zhw
- Ping Xie
- Madelyn Olson
- Brennan Cathcart
-- Zhu Tian
+- Chen Tianjie
- Slava Koyfman
+- Ozan Tezcan
+- Zhu Tian
- Roshan Khatri
- Shaya Potter
- Moti Cohen
- Arad Zilberstein
- Wen Hui
-- Chen Tianjie
- Basel Naamna
- Mingyi Kang
- Uri Yagelnik
- Filipe Oliveira
- Valentino Geron
-- Ozan Tezcan
- Adi Pinsky
- Li Changjun
diff --git a/deps/Makefile b/deps/Makefile
index c1796941b..34656201d 100644
--- a/deps/Makefile
+++ b/deps/Makefile
@@ -106,7 +106,7 @@ endif
jemalloc: .make-prerequisites
@printf '%b %b\n' $(MAKECOLOR)MAKE$(ENDCOLOR) $(BINCOLOR)$@$(ENDCOLOR)
- cd jemalloc && ./configure --disable-cxx --with-version=5.2.1-0-g0 --with-lg-quantum=3 --with-jemalloc-prefix=je_ CFLAGS="$(JEMALLOC_CFLAGS)" LDFLAGS="$(JEMALLOC_LDFLAGS)" $(JEMALLOC_CONFIGURE_OPTS)
+ cd jemalloc && ./configure --disable-cxx --with-version=5.3.0-0-g0 --with-lg-quantum=3 --with-jemalloc-prefix=je_ CFLAGS="$(JEMALLOC_CFLAGS)" LDFLAGS="$(JEMALLOC_LDFLAGS)" $(JEMALLOC_CONFIGURE_OPTS)
cd jemalloc && $(MAKE) lib/libjemalloc.a
.PHONY: jemalloc
diff --git a/deps/jemalloc/.appveyor.yml b/deps/jemalloc/.appveyor.yml
index 90b03688d..d31f9aedc 100644
--- a/deps/jemalloc/.appveyor.yml
+++ b/deps/jemalloc/.appveyor.yml
@@ -31,7 +31,6 @@ install:
- set PATH=c:\msys64\%MSYSTEM%\bin;c:\msys64\usr\bin;%PATH%
- if defined MSVC call "c:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" %MSVC%
- if defined MSVC pacman --noconfirm -Rsc mingw-w64-%CPU%-gcc gcc
- - pacman --noconfirm -Suy mingw-w64-%CPU%-make
build_script:
- bash -c "autoconf"
diff --git a/deps/jemalloc/.cirrus.yml b/deps/jemalloc/.cirrus.yml
index 019d2c38c..75695398d 100644
--- a/deps/jemalloc/.cirrus.yml
+++ b/deps/jemalloc/.cirrus.yml
@@ -3,18 +3,43 @@ env:
ARCH: amd64
task:
+ matrix:
+ env:
+ DEBUG_CONFIG: --enable-debug
+ env:
+ DEBUG_CONFIG: --disable-debug
+ matrix:
+ - env:
+ PROF_CONFIG: --enable-prof
+ - env:
+ PROF_CONFIG: --disable-prof
+ matrix:
+ - name: 64-bit
+ env:
+ CC:
+ CXX:
+ - name: 32-bit
+ env:
+ CC: cc -m32
+ CXX: c++ -m32
+ matrix:
+ - env:
+ UNCOMMON_CONFIG:
+ - env:
+ UNCOMMON_CONFIG: --with-lg-page=16 --with-malloc-conf=tcache:false
freebsd_instance:
matrix:
- image: freebsd-12-0-release-amd64
- image: freebsd-11-2-release-amd64
+ image: freebsd-12-3-release-amd64
install_script:
- sed -i.bak -e 's,pkg+http://pkg.FreeBSD.org/\${ABI}/quarterly,pkg+http://pkg.FreeBSD.org/\${ABI}/latest,' /etc/pkg/FreeBSD.conf
- pkg upgrade -y
- pkg install -y autoconf gmake
script:
- autoconf
- #- ./configure ${COMPILER_FLAGS:+ CC="$CC $COMPILER_FLAGS" CXX="$CXX $COMPILER_FLAGS" } $CONFIGURE_FLAGS
- - ./configure
+ # 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_ ${DEBUG_CONFIG} ${PROF_CONFIG} ${UNCOMMON_CONFIG}
- export JFLAG=`sysctl -n kern.smp.cpus`
- gmake -j${JFLAG}
- gmake -j${JFLAG} tests
diff --git a/deps/jemalloc/.clang-format b/deps/jemalloc/.clang-format
new file mode 100644
index 000000000..719c03c59
--- /dev/null
+++ b/deps/jemalloc/.clang-format
@@ -0,0 +1,122 @@
+# jemalloc targets clang-format version 8. We include every option it supports
+# here, but comment out the ones that aren't relevant for us.
+---
+# AccessModifierOffset: -2
+AlignAfterOpenBracket: DontAlign
+AlignConsecutiveAssignments: false
+AlignConsecutiveDeclarations: false
+AlignEscapedNewlines: Right
+AlignOperands: false
+AlignTrailingComments: false
+AllowAllParametersOfDeclarationOnNextLine: true
+AllowShortBlocksOnASingleLine: false
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: Empty
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
+AlwaysBreakAfterReturnType: AllDefinitions
+AlwaysBreakBeforeMultilineStrings: true
+# AlwaysBreakTemplateDeclarations: Yes
+BinPackArguments: true
+BinPackParameters: true
+BraceWrapping:
+ AfterClass: false
+ AfterControlStatement: false
+ AfterEnum: false
+ AfterFunction: false
+ AfterNamespace: false
+ AfterObjCDeclaration: false
+ AfterStruct: false
+ AfterUnion: false
+ BeforeCatch: false
+ BeforeElse: false
+ IndentBraces: false
+# BreakAfterJavaFieldAnnotations: true
+BreakBeforeBinaryOperators: NonAssignment
+BreakBeforeBraces: Attach
+BreakBeforeTernaryOperators: true
+# BreakConstructorInitializers: BeforeColon
+# BreakInheritanceList: BeforeColon
+BreakStringLiterals: false
+ColumnLimit: 80
+# CommentPragmas: ''
+# CompactNamespaces: true
+# ConstructorInitializerAllOnOneLineOrOnePerLine: true
+# ConstructorInitializerIndentWidth: 4
+ContinuationIndentWidth: 2
+Cpp11BracedListStyle: true
+DerivePointerAlignment: false
+DisableFormat: false
+ExperimentalAutoDetectBinPacking: false
+FixNamespaceComments: true
+ForEachMacros: [ ql_foreach, qr_foreach, ]
+# IncludeBlocks: Preserve
+# IncludeCategories:
+# - Regex: '^<.*\.h(pp)?>'
+# Priority: 1
+# IncludeIsMainRegex: ''
+IndentCaseLabels: false
+IndentPPDirectives: AfterHash
+IndentWidth: 4
+IndentWrappedFunctionNames: false
+# JavaImportGroups: []
+# JavaScriptQuotes: Leave
+# JavaScriptWrapImports: True
+KeepEmptyLinesAtTheStartOfBlocks: false
+Language: Cpp
+MacroBlockBegin: ''
+MacroBlockEnd: ''
+MaxEmptyLinesToKeep: 1
+# NamespaceIndentation: None
+# ObjCBinPackProtocolList: Auto
+# ObjCBlockIndentWidth: 2
+# ObjCSpaceAfterProperty: false
+# ObjCSpaceBeforeProtocolList: false
+
+PenaltyBreakAssignment: 2
+PenaltyBreakBeforeFirstCallParameter: 1
+PenaltyBreakComment: 300
+PenaltyBreakFirstLessLess: 120
+PenaltyBreakString: 1000
+# PenaltyBreakTemplateDeclaration: 10
+PenaltyExcessCharacter: 1000000
+PenaltyReturnTypeOnItsOwnLine: 60
+PointerAlignment: Right
+# RawStringFormats:
+# - Language: TextProto
+# Delimiters:
+# - 'pb'
+# - 'proto'
+# EnclosingFunctions:
+# - 'PARSE_TEXT_PROTO'
+# BasedOnStyle: google
+# - Language: Cpp
+# Delimiters:
+# - 'cc'
+# - 'cpp'
+# BasedOnStyle: llvm
+# CanonicalDelimiter: 'cc'
+ReflowComments: true
+SortIncludes: false
+SpaceAfterCStyleCast: false
+# SpaceAfterTemplateKeyword: true
+SpaceBeforeAssignmentOperators: true
+# SpaceBeforeCpp11BracedList: false
+# SpaceBeforeCtorInitializerColon: true
+# SpaceBeforeInheritanceColon: true
+SpaceBeforeParens: ControlStatements
+# SpaceBeforeRangeBasedForLoopColon: true
+SpaceInEmptyParentheses: false
+SpacesBeforeTrailingComments: 2
+SpacesInAngles: false
+SpacesInCStyleCastParentheses: false
+# SpacesInContainerLiterals: false
+SpacesInParentheses: false
+SpacesInSquareBrackets: false
+# Standard: Cpp11
+# This is nominally supported in clang-format version 8, but not in the build
+# used by some of the core jemalloc developers.
+# StatementMacros: []
+TabWidth: 8
+UseTab: Never
+...
diff --git a/deps/jemalloc/.gitignore b/deps/jemalloc/.gitignore
index 5ca0ad1da..1c0b33855 100644
--- a/deps/jemalloc/.gitignore
+++ b/deps/jemalloc/.gitignore
@@ -13,6 +13,8 @@
/doc/jemalloc.html
/doc/jemalloc.3
+/doc_internal/PROFILING_INTERNALS.pdf
+
/jemalloc.pc
/lib/
@@ -50,6 +52,7 @@ test/include/test/jemalloc_test.h
test/include/test/jemalloc_test_defs.h
/test/integration/[A-Za-z]*
+!/test/integration/cpp/
!/test/integration/[A-Za-z]*.*
/test/integration/*.[od]
/test/integration/*.out
@@ -71,6 +74,11 @@ test/include/test/jemalloc_test_defs.h
/test/unit/*.[od]
/test/unit/*.out
+/test/analyze/[A-Za-z]*
+!/test/analyze/[A-Za-z]*.*
+/test/analyze/*.[od]
+/test/analyze/*.out
+
/VERSION
*.pdb
diff --git a/deps/jemalloc/.travis.yml b/deps/jemalloc/.travis.yml
index 2da5da8eb..bf44fad4e 100644
--- a/deps/jemalloc/.travis.yml
+++ b/deps/jemalloc/.travis.yml
@@ -1,195 +1,413 @@
-language: generic
-dist: precise
+# This config file is generated by ./scripts/gen_travis.py.
+# Do not edit by hand.
-matrix:
+# 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:
+ - os: windows
+ arch: amd64
+ env: CC=gcc CXX=g++ EXTRA_CFLAGS="-fcommon"
+ - os: windows
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-debug" EXTRA_CFLAGS="-fcommon"
+ - os: windows
+ arch: amd64
+ env: CC=cl.exe CXX=cl.exe
+ - os: windows
+ arch: amd64
+ env: CC=gcc CXX=g++ CROSS_COMPILE_32BIT=yes EXTRA_CFLAGS="-fcommon"
+ - os: windows
+ arch: amd64
+ env: CC=cl.exe CXX=cl.exe CONFIGURE_FLAGS="--enable-debug"
+ - os: windows
+ arch: amd64
+ env: CC=gcc CXX=g++ CROSS_COMPILE_32BIT=yes CONFIGURE_FLAGS="--enable-debug" EXTRA_CFLAGS="-fcommon"
+ - os: windows
+ arch: amd64
+ env: CC=cl.exe CXX=cl.exe CROSS_COMPILE_32BIT=yes
+ - os: windows
+ arch: amd64
+ env: CC=cl.exe CXX=cl.exe CROSS_COMPILE_32BIT=yes CONFIGURE_FLAGS="--enable-debug"
+ - os: freebsd
+ arch: amd64
+ env: CC=gcc CXX=g++
+ - os: freebsd
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-debug"
+ - os: freebsd
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-prof --enable-prof-libunwind"
+ - os: freebsd
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--with-lg-page=16 --with-malloc-conf=tcache:false"
+ - os: freebsd
+ arch: amd64
+ env: CC=gcc CXX=g++ CROSS_COMPILE_32BIT=yes
+ - os: freebsd
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-debug --enable-prof --enable-prof-libunwind"
+ - os: freebsd
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-debug --with-lg-page=16 --with-malloc-conf=tcache:false"
+ - os: freebsd
+ arch: amd64
+ env: CC=gcc CXX=g++ CROSS_COMPILE_32BIT=yes CONFIGURE_FLAGS="--enable-debug"
+ - os: freebsd
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-prof --enable-prof-libunwind --with-lg-page=16 --with-malloc-conf=tcache:false"
+ - os: freebsd
+ arch: amd64
+ env: CC=gcc CXX=g++ CROSS_COMPILE_32BIT=yes CONFIGURE_FLAGS="--enable-prof --enable-prof-libunwind"
+ - os: freebsd
+ arch: amd64
+ env: CC=gcc CXX=g++ CROSS_COMPILE_32BIT=yes CONFIGURE_FLAGS="--with-lg-page=16 --with-malloc-conf=tcache:false"
+ - os: freebsd
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-debug --enable-prof --enable-prof-libunwind --with-lg-page=16 --with-malloc-conf=tcache:false"
+ - os: freebsd
+ arch: amd64
+ env: CC=gcc CXX=g++ CROSS_COMPILE_32BIT=yes CONFIGURE_FLAGS="--enable-debug --enable-prof --enable-prof-libunwind"
+ - os: freebsd
+ arch: amd64
+ env: CC=gcc CXX=g++ CROSS_COMPILE_32BIT=yes CONFIGURE_FLAGS="--enable-debug --with-lg-page=16 --with-malloc-conf=tcache:false"
+ - os: freebsd
+ arch: amd64
+ env: CC=gcc CXX=g++ CROSS_COMPILE_32BIT=yes CONFIGURE_FLAGS="--enable-prof --enable-prof-libunwind --with-lg-page=16 --with-malloc-conf=tcache:false"
+ - os: freebsd
+ arch: amd64
+ env: CC=gcc CXX=g++ CROSS_COMPILE_32BIT=yes CONFIGURE_FLAGS="--enable-debug --enable-prof --enable-prof-libunwind --with-lg-page=16 --with-malloc-conf=tcache:false"
+ - os: linux
+ arch: amd64
+ env: CC=gcc CXX=g++ EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ - os: linux
+ arch: amd64
+ env: CC=clang CXX=clang++ EXTRA_CFLAGS="-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes"
+ - os: linux
+ arch: amd64
+ env: CC=gcc CXX=g++ CROSS_COMPILE_32BIT=yes COMPILER_FLAGS="-m32" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ - os: linux
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-debug" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ - os: linux
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-prof" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ - os: linux
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--disable-stats" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ - os: linux
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--disable-libdl" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ - os: linux
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-opt-safety-checks" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ - os: linux
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--with-lg-page=16" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ - os: linux
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--with-malloc-conf=tcache:false" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ - os: linux
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--with-malloc-conf=dss:primary" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ - os: linux
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--with-malloc-conf=percpu_arena:percpu" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ - os: linux
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--with-malloc-conf=background_thread:true" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ - os: linux
+ arch: amd64
+ env: CC=clang CXX=clang++ CROSS_COMPILE_32BIT=yes COMPILER_FLAGS="-m32" EXTRA_CFLAGS="-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes"
+ - os: linux
+ arch: amd64
+ env: CC=clang CXX=clang++ CONFIGURE_FLAGS="--enable-debug" EXTRA_CFLAGS="-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes"
+ - os: linux
+ arch: amd64
+ env: CC=clang CXX=clang++ CONFIGURE_FLAGS="--enable-prof" EXTRA_CFLAGS="-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- - os: osx
- env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: amd64
+ env: CC=clang CXX=clang++ CONFIGURE_FLAGS="--disable-stats" EXTRA_CFLAGS="-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes"
- os: linux
- env: CC=clang CXX=clang++ COMPILER_FLAGS="" CONFIGURE_FLAGS="" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: amd64
+ env: CC=clang CXX=clang++ CONFIGURE_FLAGS="--disable-libdl" EXTRA_CFLAGS="-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="-m32" CONFIGURE_FLAGS="" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- addons: &gcc_multilib
- apt:
- packages:
- - gcc-multilib
+ arch: amd64
+ env: CC=clang CXX=clang++ CONFIGURE_FLAGS="--enable-opt-safety-checks" EXTRA_CFLAGS="-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--enable-debug" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: amd64
+ env: CC=clang CXX=clang++ CONFIGURE_FLAGS="--with-lg-page=16" EXTRA_CFLAGS="-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--enable-prof" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: amd64
+ env: CC=clang CXX=clang++ CONFIGURE_FLAGS="--with-malloc-conf=tcache:false" EXTRA_CFLAGS="-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--disable-stats" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: amd64
+ env: CC=clang CXX=clang++ CONFIGURE_FLAGS="--with-malloc-conf=dss:primary" EXTRA_CFLAGS="-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--disable-libdl" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: amd64
+ env: CC=clang CXX=clang++ CONFIGURE_FLAGS="--with-malloc-conf=percpu_arena:percpu" EXTRA_CFLAGS="-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--enable-opt-safety-checks" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: amd64
+ env: CC=clang CXX=clang++ CONFIGURE_FLAGS="--with-malloc-conf=background_thread:true" EXTRA_CFLAGS="-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--with-malloc-conf=tcache:false" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: amd64
+ env: CC=gcc CXX=g++ CROSS_COMPILE_32BIT=yes COMPILER_FLAGS="-m32" CONFIGURE_FLAGS="--enable-debug" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--with-malloc-conf=dss:primary" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: amd64
+ env: CC=gcc CXX=g++ CROSS_COMPILE_32BIT=yes COMPILER_FLAGS="-m32" CONFIGURE_FLAGS="--enable-prof" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--with-malloc-conf=percpu_arena:percpu" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: amd64
+ env: CC=gcc CXX=g++ CROSS_COMPILE_32BIT=yes COMPILER_FLAGS="-m32" CONFIGURE_FLAGS="--disable-stats" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--with-malloc-conf=background_thread:true" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- - os: osx
- env: CC=clang CXX=clang++ COMPILER_FLAGS="" CONFIGURE_FLAGS="" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- - os: osx
- env: CC=gcc CXX=g++ COMPILER_FLAGS="-m32" CONFIGURE_FLAGS="" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- - os: osx
- env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--enable-debug" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- - os: osx
- env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--disable-stats" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- - os: osx
- env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--disable-libdl" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- - os: osx
- env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--enable-opt-safety-checks" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- - os: osx
- env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--with-malloc-conf=tcache:false" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: amd64
+ env: CC=gcc CXX=g++ CROSS_COMPILE_32BIT=yes COMPILER_FLAGS="-m32" CONFIGURE_FLAGS="--disable-libdl" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ - os: linux
+ arch: amd64
+ env: CC=gcc CXX=g++ CROSS_COMPILE_32BIT=yes COMPILER_FLAGS="-m32" CONFIGURE_FLAGS="--enable-opt-safety-checks" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ - os: linux
+ arch: amd64
+ env: CC=gcc CXX=g++ CROSS_COMPILE_32BIT=yes COMPILER_FLAGS="-m32" CONFIGURE_FLAGS="--with-lg-page=16" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ - os: linux
+ arch: amd64
+ env: CC=gcc CXX=g++ CROSS_COMPILE_32BIT=yes COMPILER_FLAGS="-m32" CONFIGURE_FLAGS="--with-malloc-conf=tcache:false" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ - os: linux
+ arch: amd64
+ env: CC=gcc CXX=g++ CROSS_COMPILE_32BIT=yes COMPILER_FLAGS="-m32" CONFIGURE_FLAGS="--with-malloc-conf=dss:primary" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=clang CXX=clang++ COMPILER_FLAGS="-m32" CONFIGURE_FLAGS="" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- addons: *gcc_multilib
+ arch: amd64
+ env: CC=gcc CXX=g++ CROSS_COMPILE_32BIT=yes COMPILER_FLAGS="-m32" CONFIGURE_FLAGS="--with-malloc-conf=percpu_arena:percpu" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=clang CXX=clang++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--enable-debug" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: amd64
+ env: CC=gcc CXX=g++ CROSS_COMPILE_32BIT=yes COMPILER_FLAGS="-m32" CONFIGURE_FLAGS="--with-malloc-conf=background_thread:true" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=clang CXX=clang++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--enable-prof" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-debug --enable-prof" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=clang CXX=clang++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--disable-stats" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-debug --disable-stats" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=clang CXX=clang++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--disable-libdl" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-debug --disable-libdl" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=clang CXX=clang++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--enable-opt-safety-checks" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-debug --enable-opt-safety-checks" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=clang CXX=clang++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--with-malloc-conf=tcache:false" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-debug --with-lg-page=16" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=clang CXX=clang++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--with-malloc-conf=dss:primary" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-debug --with-malloc-conf=tcache:false" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=clang CXX=clang++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--with-malloc-conf=percpu_arena:percpu" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-debug --with-malloc-conf=dss:primary" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=clang CXX=clang++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--with-malloc-conf=background_thread:true" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-debug --with-malloc-conf=percpu_arena:percpu" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="-m32" CONFIGURE_FLAGS="--enable-debug" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- addons: *gcc_multilib
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-debug --with-malloc-conf=background_thread:true" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="-m32" CONFIGURE_FLAGS="--enable-prof" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- addons: *gcc_multilib
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-prof --disable-stats" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="-m32" CONFIGURE_FLAGS="--disable-stats" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- addons: *gcc_multilib
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-prof --disable-libdl" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="-m32" CONFIGURE_FLAGS="--disable-libdl" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- addons: *gcc_multilib
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-prof --enable-opt-safety-checks" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="-m32" CONFIGURE_FLAGS="--enable-opt-safety-checks" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- addons: *gcc_multilib
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-prof --with-lg-page=16" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="-m32" CONFIGURE_FLAGS="--with-malloc-conf=tcache:false" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- addons: *gcc_multilib
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-prof --with-malloc-conf=tcache:false" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="-m32" CONFIGURE_FLAGS="--with-malloc-conf=dss:primary" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- addons: *gcc_multilib
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-prof --with-malloc-conf=dss:primary" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="-m32" CONFIGURE_FLAGS="--with-malloc-conf=percpu_arena:percpu" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- addons: *gcc_multilib
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-prof --with-malloc-conf=percpu_arena:percpu" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="-m32" CONFIGURE_FLAGS="--with-malloc-conf=background_thread:true" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- addons: *gcc_multilib
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-prof --with-malloc-conf=background_thread:true" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--enable-debug --enable-prof" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--disable-stats --disable-libdl" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--enable-debug --disable-stats" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--disable-stats --enable-opt-safety-checks" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--enable-debug --disable-libdl" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--disable-stats --with-lg-page=16" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--enable-debug --enable-opt-safety-checks" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--disable-stats --with-malloc-conf=tcache:false" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--enable-debug --with-malloc-conf=tcache:false" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--disable-stats --with-malloc-conf=dss:primary" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--enable-debug --with-malloc-conf=dss:primary" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--disable-stats --with-malloc-conf=percpu_arena:percpu" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--enable-debug --with-malloc-conf=percpu_arena:percpu" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--disable-stats --with-malloc-conf=background_thread:true" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--enable-debug --with-malloc-conf=background_thread:true" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--disable-libdl --enable-opt-safety-checks" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--enable-prof --disable-stats" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--disable-libdl --with-lg-page=16" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--enable-prof --disable-libdl" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--disable-libdl --with-malloc-conf=tcache:false" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--enable-prof --enable-opt-safety-checks" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--disable-libdl --with-malloc-conf=dss:primary" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--enable-prof --with-malloc-conf=tcache:false" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--disable-libdl --with-malloc-conf=percpu_arena:percpu" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--enable-prof --with-malloc-conf=dss:primary" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--disable-libdl --with-malloc-conf=background_thread:true" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--enable-prof --with-malloc-conf=percpu_arena:percpu" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-opt-safety-checks --with-lg-page=16" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--enable-prof --with-malloc-conf=background_thread:true" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-opt-safety-checks --with-malloc-conf=tcache:false" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--disable-stats --disable-libdl" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-opt-safety-checks --with-malloc-conf=dss:primary" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--disable-stats --enable-opt-safety-checks" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-opt-safety-checks --with-malloc-conf=percpu_arena:percpu" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--disable-stats --with-malloc-conf=tcache:false" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-opt-safety-checks --with-malloc-conf=background_thread:true" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--disable-stats --with-malloc-conf=dss:primary" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--with-lg-page=16 --with-malloc-conf=tcache:false" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--disable-stats --with-malloc-conf=percpu_arena:percpu" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--with-lg-page=16 --with-malloc-conf=dss:primary" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--disable-stats --with-malloc-conf=background_thread:true" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--with-lg-page=16 --with-malloc-conf=percpu_arena:percpu" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--disable-libdl --enable-opt-safety-checks" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--with-lg-page=16 --with-malloc-conf=background_thread:true" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--disable-libdl --with-malloc-conf=tcache:false" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--with-malloc-conf=tcache:false,dss:primary" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--disable-libdl --with-malloc-conf=dss:primary" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--with-malloc-conf=tcache:false,percpu_arena:percpu" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--disable-libdl --with-malloc-conf=percpu_arena:percpu" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--with-malloc-conf=tcache:false,background_thread:true" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--disable-libdl --with-malloc-conf=background_thread:true" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--with-malloc-conf=dss:primary,percpu_arena:percpu" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--enable-opt-safety-checks --with-malloc-conf=tcache:false" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--with-malloc-conf=dss:primary,background_thread:true" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--enable-opt-safety-checks --with-malloc-conf=dss:primary" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--with-malloc-conf=percpu_arena:percpu,background_thread:true" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--enable-opt-safety-checks --with-malloc-conf=percpu_arena:percpu" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: ppc64le
+ env: CC=gcc CXX=g++ EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--enable-opt-safety-checks --with-malloc-conf=background_thread:true" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: ppc64le
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-debug" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--with-malloc-conf=tcache:false,dss:primary" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: ppc64le
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-prof" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--with-malloc-conf=tcache:false,percpu_arena:percpu" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: ppc64le
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--disable-stats" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--with-malloc-conf=tcache:false,background_thread:true" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: ppc64le
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--disable-libdl" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--with-malloc-conf=dss:primary,percpu_arena:percpu" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: ppc64le
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-opt-safety-checks" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--with-malloc-conf=dss:primary,background_thread:true" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: ppc64le
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--with-lg-page=16" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--with-malloc-conf=percpu_arena:percpu,background_thread:true" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ arch: ppc64le
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--with-malloc-conf=tcache:false" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ - os: linux
+ arch: ppc64le
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--with-malloc-conf=dss:primary" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ - os: linux
+ arch: ppc64le
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--with-malloc-conf=percpu_arena:percpu" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ - os: linux
+ arch: ppc64le
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--with-malloc-conf=background_thread:true" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ - os: osx
+ arch: amd64
+ env: CC=gcc CXX=g++ EXTRA_CFLAGS="-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes -Wno-deprecated-declarations"
+ - os: osx
+ arch: amd64
+ env: CC=gcc CXX=g++ CROSS_COMPILE_32BIT=yes EXTRA_CFLAGS="-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes -Wno-deprecated-declarations"
+ - os: osx
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-debug" EXTRA_CFLAGS="-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes -Wno-deprecated-declarations"
+ - os: osx
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--disable-stats" EXTRA_CFLAGS="-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes -Wno-deprecated-declarations"
+ - os: osx
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--disable-libdl" EXTRA_CFLAGS="-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes -Wno-deprecated-declarations"
+ - os: osx
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-opt-safety-checks" EXTRA_CFLAGS="-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes -Wno-deprecated-declarations"
+ - os: osx
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--with-lg-page=16" EXTRA_CFLAGS="-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes -Wno-deprecated-declarations"
+ - os: osx
+ arch: amd64
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--with-malloc-conf=tcache:false" EXTRA_CFLAGS="-Werror -Wno-array-bounds -Wno-unknown-warning-option -Wno-ignored-attributes -Wno-deprecated-declarations"
# Development build
- os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--enable-debug --disable-cache-oblivious --enable-stats --enable-log --enable-prof" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ 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++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--enable-debug --enable-experimental-smallocx --enable-stats --enable-prof" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-debug --enable-experimental-smallocx --enable-stats --enable-prof" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
- # Valgrind
- - os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="" EXTRA_CFLAGS="-Werror -Wno-array-bounds" JEMALLOC_TEST_PREFIX="valgrind"
- addons:
- apt:
- packages:
- - valgrind
+before_install:
+ - |-
+ if test -f "./scripts/$TRAVIS_OS_NAME/before_install.sh"; then
+ source ./scripts/$TRAVIS_OS_NAME/before_install.sh
+ fi
before_script:
- - autoconf
- - scripts/gen_travis.py > travis_script && diff .travis.yml travis_script
- - ./configure ${COMPILER_FLAGS:+ CC="$CC $COMPILER_FLAGS" CXX="$CXX $COMPILER_FLAGS" } $CONFIGURE_FLAGS
- - make -j3
- - make -j3 tests
+ - |-
+ 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:
- - make check
+ - |-
+ if test -f "./scripts/$TRAVIS_OS_NAME/script.sh"; then
+ source ./scripts/$TRAVIS_OS_NAME/script.sh
+ else
+ make check
+ fi
diff --git a/deps/jemalloc/ChangeLog b/deps/jemalloc/ChangeLog
index e55813b7b..32fde5624 100644
--- a/deps/jemalloc/ChangeLog
+++ b/deps/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/deps/jemalloc/INSTALL.md b/deps/jemalloc/INSTALL.md
index b8f729b0d..90da718d2 100644
--- a/deps/jemalloc/INSTALL.md
+++ b/deps/jemalloc/INSTALL.md
@@ -9,14 +9,17 @@ If building from unpackaged developer sources, the simplest command sequence
that might work is:
./autogen.sh
- make dist
make
make install
-Note that documentation is not built by the default target because doing so
-would create a dependency on xsltproc in packaged releases, hence the
-requirement to either run 'make dist' or avoid installing docs via the various
-install_* targets documented below.
+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
@@ -188,13 +191,13 @@ any of the following arguments (not a definitive list) to 'configure':
* `--disable-cache-oblivious`
- Disable cache-oblivious large allocation alignment 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 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`
diff --git a/deps/jemalloc/Makefile.in b/deps/jemalloc/Makefile.in
index 7128b007e..1193cd859 100644
--- a/deps/jemalloc/Makefile.in
+++ b/deps/jemalloc/Makefile.in
@@ -99,31 +99,60 @@ C_SRCS := $(srcroot)src/jemalloc.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/hash.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/mutex_pool.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/prng.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/stats.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
@@ -148,88 +177,124 @@ else
LJEMALLOC := $(objroot)lib/$(LIBJEMALLOC).$(IMPORTLIB)
endif
PC := $(objroot)jemalloc.pc
-MAN3 := $(objroot)doc/jemalloc$(install_suffix).3
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/mq.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
+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/binshard.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/extent_util.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/sc.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/nstime.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.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/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 \
@@ -251,16 +316,26 @@ TESTS_INTEGRATION += \
endif
ifeq (@enable_cxx@, 1)
CPP_SRCS := $(srcroot)src/jemalloc_cpp.cpp
-TESTS_INTEGRATION_CPP := $(srcroot)test/integration/cpp/basic.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_STRESS := $(srcroot)test/stress/microbench.c \
- $(srcroot)test/stress/hookbench.c
+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_STRESS)
+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)
@@ -276,14 +351,19 @@ 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_STRESS_OBJS)
+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_STRESS_OBJS)
+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
@@ -298,7 +378,7 @@ all: build_lib
dist: build_doc
-$(objroot)doc/%.html : $(objroot)doc/%.xml $(srcroot)doc/stylesheet.xsl $(objroot)doc/html.xsl
+$(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
@@ -308,9 +388,16 @@ endif
@echo "Missing xsltproc. "$@" not (re)built."
endif
-$(objroot)doc/%.3 : $(objroot)doc/%.xml $(srcroot)doc/stylesheet.xsl $(objroot)doc/manpages.xsl
+$(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." > $@
@@ -357,12 +444,15 @@ $(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
@@ -382,7 +472,7 @@ $(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_STRESS_OBJS) $(TESTS_UNIT_OBJS) $(TESTS_STRESS_OBJS): $(objroot)include/jemalloc/internal/private_namespace_jet.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)
@@ -406,7 +496,7 @@ $(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
+ @if ! `cmp -s $< $@` ; then echo "cp $< $@"; cp $< $@ ; fi
$(CPP_OBJS) $(CPP_PIC_OBJS) $(TESTS_CPP_OBJS): %.$(O):
@mkdir -p $(@D)
@@ -445,6 +535,10 @@ $(objroot)test/integration/cpp/%$(EXE): $(objroot)test/integration/cpp/%.$(O) $(
@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)
@@ -461,20 +555,18 @@ endif
install_bin:
$(INSTALL) -d $(BINDIR)
@for b in $(BINS); do \
- echo "$(INSTALL) -m 755 $$b $(BINDIR)"; \
- $(INSTALL) -m 755 $$b $(BINDIR); \
+ $(INSTALL) -v -m 755 $$b $(BINDIR); \
done
install_include:
$(INSTALL) -d $(INCLUDEDIR)/jemalloc
@for h in $(C_HDRS); do \
- echo "$(INSTALL) -m 644 $$h $(INCLUDEDIR)/jemalloc"; \
- $(INSTALL) -m 644 $$h $(INCLUDEDIR)/jemalloc; \
+ $(INSTALL) -v -m 644 $$h $(INCLUDEDIR)/jemalloc; \
done
install_lib_shared: $(DSOS)
$(INSTALL) -d $(LIBDIR)
- $(INSTALL) -m 755 $(objroot)lib/$(LIBJEMALLOC).$(SOREV) $(LIBDIR)
+ $(INSTALL) -v -m 755 $(objroot)lib/$(LIBJEMALLOC).$(SOREV) $(LIBDIR)
ifneq ($(SOREV),$(SO))
ln -sf $(LIBJEMALLOC).$(SOREV) $(LIBDIR)/$(LIBJEMALLOC).$(SO)
endif
@@ -482,15 +574,13 @@ endif
install_lib_static: $(STATIC_LIBS)
$(INSTALL) -d $(LIBDIR)
@for l in $(STATIC_LIBS); do \
- echo "$(INSTALL) -m 755 $$l $(LIBDIR)"; \
- $(INSTALL) -m 755 $$l $(LIBDIR); \
+ $(INSTALL) -v -m 755 $$l $(LIBDIR); \
done
install_lib_pc: $(PC)
$(INSTALL) -d $(LIBDIR)/pkgconfig
@for l in $(PC); do \
- echo "$(INSTALL) -m 644 $$l $(LIBDIR)/pkgconfig"; \
- $(INSTALL) -m 644 $$l $(LIBDIR)/pkgconfig; \
+ $(INSTALL) -v -m 644 $$l $(LIBDIR)/pkgconfig; \
done
ifeq ($(enable_shared), 1)
@@ -501,21 +591,19 @@ install_lib: install_lib_static
endif
install_lib: install_lib_pc
-install_doc_html:
+install_doc_html: build_doc_html
$(INSTALL) -d $(DATADIR)/doc/jemalloc$(install_suffix)
@for d in $(DOCS_HTML); do \
- echo "$(INSTALL) -m 644 $$d $(DATADIR)/doc/jemalloc$(install_suffix)"; \
- $(INSTALL) -m 644 $$d $(DATADIR)/doc/jemalloc$(install_suffix); \
+ $(INSTALL) -v -m 644 $$d $(DATADIR)/doc/jemalloc$(install_suffix); \
done
-install_doc_man:
+install_doc_man: build_doc_man
$(INSTALL) -d $(MANDIR)/man3
@for d in $(DOCS_MAN3); do \
- echo "$(INSTALL) -m 644 $$d $(MANDIR)/man3"; \
- $(INSTALL) -m 644 $$d $(MANDIR)/man3; \
+ $(INSTALL) -v -m 644 $$d $(MANDIR)/man3; \
done
-install_doc: build_doc install_doc_html install_doc_man
+install_doc: install_doc_html install_doc_man
install: install_bin install_include install_lib
@@ -523,15 +611,60 @@ 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_stress
+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
@@ -548,6 +681,12 @@ check_integration_decay: tests_integration check_integration_dir
$(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
diff --git a/deps/jemalloc/TUNING.md b/deps/jemalloc/TUNING.md
index 34fca05b4..e96399d7c 100644
--- a/deps/jemalloc/TUNING.md
+++ b/deps/jemalloc/TUNING.md
@@ -1,5 +1,5 @@
This document summarizes the common approaches for performance fine tuning with
-jemalloc (as of 5.1.0). The default configuration of jemalloc tends to work
+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
@@ -76,14 +76,14 @@ Examples:
* High resource consumption application, prioritizing memory usage:
- `background_thread:true` combined with shorter decay time (decreased
- `dirty_decay_ms` and / or `muzzy_decay_ms`,
+ `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,lg_tcache_max:13` combined with shorter decay time (decreased
+ `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`).
diff --git a/deps/jemalloc/VERSION b/deps/jemalloc/VERSION
index 05500db52..3028a808e 100644
--- a/deps/jemalloc/VERSION
+++ b/deps/jemalloc/VERSION
@@ -1 +1 @@
-5.2.1-0-g0
+5.3.0-0-g0
diff --git a/deps/jemalloc/bin/jeprof.in b/deps/jemalloc/bin/jeprof.in
index 3ed408c9c..dbf6252b9 100644
--- a/deps/jemalloc/bin/jeprof.in
+++ b/deps/jemalloc/bin/jeprof.in
@@ -205,6 +205,8 @@ Output type:
--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]
@@ -238,6 +240,7 @@ Miscellaneous:
--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
@@ -332,6 +335,7 @@ sub Init() {
$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;
@@ -362,6 +366,7 @@ sub Init() {
$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;
@@ -405,6 +410,7 @@ sub Init() {
"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,
@@ -429,6 +435,7 @@ sub Init() {
"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)");
@@ -490,6 +497,7 @@ sub Init() {
$main::opt_svg +
$main::opt_gif +
$main::opt_raw +
+ $main::opt_collapsed +
$main::opt_interactive +
0;
if ($modes > 1) {
@@ -572,6 +580,11 @@ sub Init() {
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 {
@@ -621,6 +634,8 @@ sub FilterAndPrint {
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 {
@@ -2810,6 +2825,40 @@ sub IsSecondPcAlwaysTheSame {
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;
@@ -2884,6 +2933,17 @@ sub FilterFrames {
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;
@@ -4440,16 +4500,54 @@ sub FindLibrary {
# For libc libraries, the copy in /usr/lib/debug contains debugging symbols
sub DebuggingLibrary {
my $file = shift;
- if ($file =~ m|^/|) {
- if (-f "/usr/lib/debug$file") {
- return "/usr/lib/debug$file";
- } elsif (-f "/usr/lib/debug$file.debug") {
- return "/usr/lib/debug$file.debug";
+
+ 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;
@@ -4987,7 +5085,7 @@ sub MapToSymbols {
} 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 paramters,
+ # 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
@@ -5339,7 +5437,7 @@ sub GetProcedureBoundaries {
# "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 occurence)
+ # 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
diff --git a/deps/jemalloc/build-aux/config.guess b/deps/jemalloc/build-aux/config.guess
index 2e9ad7fe8..f7727026b 100755
--- a/deps/jemalloc/build-aux/config.guess
+++ b/deps/jemalloc/build-aux/config.guess
@@ -1,8 +1,8 @@
#! /bin/sh
# Attempt to guess a canonical system name.
-# Copyright 1992-2016 Free Software Foundation, Inc.
+# Copyright 1992-2021 Free Software Foundation, Inc.
-timestamp='2016-10-02'
+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
@@ -15,7 +15,7 @@ timestamp='2016-10-02'
# 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/>.
+# 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
@@ -27,19 +27,19 @@ timestamp='2016-10-02'
# Originally written by Per Bothner; maintained since 2000 by Ben Elliston.
#
# You can get the latest version of this script from:
-# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess
+# 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,.*/,,'`
+me=$(echo "$0" | sed -e 's,.*/,,')
usage="\
Usage: $0 [OPTION]
Output the configuration name of the system \`$me' is run on.
-Operation modes:
+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
@@ -50,7 +50,7 @@ version="\
GNU config.guess ($timestamp)
Originally written by Per Bothner.
-Copyright 1992-2016 Free Software Foundation, Inc.
+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."
@@ -84,8 +84,6 @@ if test $# != 0; then
exit 1
fi
-trap 'exit 1' 1 2 15
-
# 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
@@ -96,66 +94,89 @@ trap 'exit 1' 1 2 15
# Portable tmp directory creation inspired by the Autoconf team.
-set_cc_for_build='
-trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ;
-trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ;
-: ${TMPDIR=/tmp} ;
- { 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) ; } ||
- { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } ||
- { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ;
-dummy=$tmp/dummy ;
-tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ;
-case $CC_FOR_BUILD,$HOST_CC,$CC in
- ,,) echo "int x;" > $dummy.c ;
- for c in cc gcc c89 c99 ; do
- if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then
- CC_FOR_BUILD="$c"; 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 ; set_cc_for_build= ;'
+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) >/dev/null 2>&1 ; then
+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
+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
+case "$UNAME_SYSTEM" in
Linux|GNU|GNU/*)
- # If the system lacks a compiler, then just pick glibc.
- # We could probably try harder.
- LIBC=gnu
+ LIBC=unknown
- eval $set_cc_for_build
- cat <<-EOF > $dummy.c
+ set_cc_for_build
+ cat <<-EOF > "$dummy.c"
#include <features.h>
#if defined(__UCLIBC__)
LIBC=uclibc
#elif defined(__dietlibc__)
LIBC=dietlibc
- #else
+ #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'`
+ 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
+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*,
@@ -168,31 +189,32 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
# 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
+ 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
+ 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 ;;
+ *) 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
+ case "$UNAME_MACHINE_ARCH" in
earm*)
os=netbsdelf
;;
arm*|i386|m68k|ns32k|sh3*|sparc|vax)
- eval $set_cc_for_build
+ set_cc_for_build
if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
| grep -q __ELF__
then
@@ -208,10 +230,10 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
;;
esac
# Determine ABI tags.
- case "${UNAME_MACHINE_ARCH}" in
+ case "$UNAME_MACHINE_ARCH" in
earm*)
expr='s/^earmv[0-9]/-eabi/;s/eb$//'
- abi=`echo ${UNAME_MACHINE_ARCH} | sed -e "$expr"`
+ abi=$(echo "$UNAME_MACHINE_ARCH" | sed -e "$expr")
;;
esac
# The OS release
@@ -219,60 +241,75 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
# 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
+ case "$UNAME_VERSION" in
Debian*)
release='-gnu'
;;
*)
- release=`echo ${UNAME_RELEASE} | sed -e 's/[-_].*//' | cut -d. -f1,2`
+ 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}"
+ echo "$machine-${os}${release}${abi-}"
exit ;;
*:Bitrig:*:*)
- UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'`
- echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE}
+ 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}
+ 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}
+ 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}
+ echo "$UNAME_MACHINE"-unknown-ekkobsd"$UNAME_RELEASE"
exit ;;
*:SolidBSD:*:*)
- echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE}
+ 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}
+ echo powerpc-unknown-mirbsd"$UNAME_RELEASE"
exit ;;
*:MirBSD:*:*)
- echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE}
+ echo "$UNAME_MACHINE"-unknown-mirbsd"$UNAME_RELEASE"
exit ;;
*:Sortix:*:*)
- echo ${UNAME_MACHINE}-unknown-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}'`
+ UNAME_RELEASE=$(/usr/sbin/sizer -v | awk '{print $3}')
;;
*5.*)
- UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
+ 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`
+ 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 ;;
@@ -310,28 +347,19 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
# 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`
+ 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 ;;
- Alpha\ *:Windows_NT*:*)
- # How do we know it's Interix rather than the generic POSIX subsystem?
- # Should we change UNAME_MACHINE based on the output of uname instead
- # of the specific Alpha model?
- echo alpha-pc-interix
- exit ;;
- 21064:Windows_NT:50:3)
- echo alpha-dec-winnt3.5
- exit ;;
Amiga*:UNIX_System_V:4.0:*)
echo m68k-unknown-sysv4
exit ;;
*:[Aa]miga[Oo][Ss]:*:*)
- echo ${UNAME_MACHINE}-unknown-amigaos
+ echo "$UNAME_MACHINE"-unknown-amigaos
exit ;;
*:[Mm]orph[Oo][Ss]:*:*)
- echo ${UNAME_MACHINE}-unknown-morphos
+ echo "$UNAME_MACHINE"-unknown-morphos
exit ;;
*:OS/390:*:*)
echo i370-ibm-openedition
@@ -343,7 +371,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
echo powerpc-ibm-os400
exit ;;
arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
- echo arm-acorn-riscix${UNAME_RELEASE}
+ echo arm-acorn-riscix"$UNAME_RELEASE"
exit ;;
arm*:riscos:*:*|arm*:RISCOS:*:*)
echo arm-unknown-riscos
@@ -353,7 +381,7 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
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
+ if test "$( (/bin/universe) 2>/dev/null)" = att ; then
echo pyramid-pyramid-sysv3
else
echo pyramid-pyramid-bsd
@@ -366,28 +394,28 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
echo sparc-icl-nx6
exit ;;
DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*)
- case `/usr/bin/uname -p` in
+ 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/[^.]*//'`
+ 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/[^.]*//'`
+ 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/[^.]*//'`
+ echo sparc-sun-solaris2"$(echo "$UNAME_RELEASE" | sed -e 's/[^.]*//')"
exit ;;
i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*)
- echo i386-pc-auroraux${UNAME_RELEASE}
+ echo i386-pc-auroraux"$UNAME_RELEASE"
exit ;;
i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)
- eval $set_cc_for_build
+ 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 [ "$CC_FOR_BUILD" != no_compiler_found ]; then
+ 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
@@ -395,40 +423,40 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
SUN_ARCH=x86_64
fi
fi
- echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ 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/[^.]*//'`
+ echo sparc-sun-solaris3"$(echo "$UNAME_RELEASE"|sed -e 's/[^.]*//')"
exit ;;
sun4*:SunOS:*:*)
- case "`/usr/bin/arch -k`" in
+ case "$(/usr/bin/arch -k)" in
Series*|S4*)
- UNAME_RELEASE=`uname -v`
+ 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/-/_/'`
+ echo sparc-sun-sunos"$(echo "$UNAME_RELEASE"|sed -e 's/-/_/')"
exit ;;
sun3*:SunOS:*:*)
- echo m68k-sun-sunos${UNAME_RELEASE}
+ 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
+ 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}
+ echo m68k-sun-sunos"$UNAME_RELEASE"
;;
sun4)
- echo sparc-sun-sunos${UNAME_RELEASE}
+ echo sparc-sun-sunos"$UNAME_RELEASE"
;;
esac
exit ;;
aushp:SunOS:*:*)
- echo sparc-auspex-sunos${UNAME_RELEASE}
+ 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
@@ -439,44 +467,44 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
# 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}
+ echo m68k-atari-mint"$UNAME_RELEASE"
exit ;;
atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
- echo m68k-atari-mint${UNAME_RELEASE}
+ echo m68k-atari-mint"$UNAME_RELEASE"
exit ;;
*falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
- echo m68k-atari-mint${UNAME_RELEASE}
+ echo m68k-atari-mint"$UNAME_RELEASE"
exit ;;
milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
- echo m68k-milan-mint${UNAME_RELEASE}
+ echo m68k-milan-mint"$UNAME_RELEASE"
exit ;;
hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
- echo m68k-hades-mint${UNAME_RELEASE}
+ echo m68k-hades-mint"$UNAME_RELEASE"
exit ;;
*:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
- echo m68k-unknown-mint${UNAME_RELEASE}
+ echo m68k-unknown-mint"$UNAME_RELEASE"
exit ;;
m68k:machten:*:*)
- echo m68k-apple-machten${UNAME_RELEASE}
+ echo m68k-apple-machten"$UNAME_RELEASE"
exit ;;
powerpc:machten:*:*)
- echo powerpc-apple-machten${UNAME_RELEASE}
+ echo powerpc-apple-machten"$UNAME_RELEASE"
exit ;;
RISC*:Mach:*:*)
echo mips-dec-mach_bsd4.3
exit ;;
RISC*:ULTRIX:*:*)
- echo mips-dec-ultrix${UNAME_RELEASE}
+ echo mips-dec-ultrix"$UNAME_RELEASE"
exit ;;
VAX*:ULTRIX*:*:*)
- echo vax-dec-ultrix${UNAME_RELEASE}
+ echo vax-dec-ultrix"$UNAME_RELEASE"
exit ;;
2020:CLIX:*:* | 2430:CLIX:*:*)
- echo clipper-intergraph-clix${UNAME_RELEASE}
+ echo clipper-intergraph-clix"$UNAME_RELEASE"
exit ;;
mips:*:*:UMIPS | mips:*:*:RISCos)
- eval $set_cc_for_build
- sed 's/^ //' << EOF >$dummy.c
+ set_cc_for_build
+ sed 's/^ //' << EOF > "$dummy.c"
#ifdef __cplusplus
#include <stdio.h> /* for printf() prototype */
int main (int argc, char *argv[]) {
@@ -485,23 +513,23 @@ case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in
#endif
#if defined (host_mips) && defined (MIPSEB)
#if defined (SYSTYPE_SYSV)
- printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0);
+ 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);
+ 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);
+ 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` &&
+ $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}
+ echo mips-mips-riscos"$UNAME_RELEASE"
exit ;;
Motorola:PowerMAX_OS:*:*)
echo powerpc-motorola-powermax
@@ -526,18 +554,18 @@ EOF
exit ;;
AViiON:dgux:*:*)
# DG/UX returns AViiON for all architectures
- UNAME_PROCESSOR=`/usr/bin/uname -p`
- if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ]
+ UNAME_PROCESSOR=$(/usr/bin/uname -p)
+ if test "$UNAME_PROCESSOR" = mc88100 || test "$UNAME_PROCESSOR" = mc88110
then
- if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \
- [ ${TARGET_BINARY_INTERFACE}x = x ]
+ if test "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx || \
+ test "$TARGET_BINARY_INTERFACE"x = x
then
- echo m88k-dg-dgux${UNAME_RELEASE}
+ echo m88k-dg-dgux"$UNAME_RELEASE"
else
- echo m88k-dg-dguxbcs${UNAME_RELEASE}
+ echo m88k-dg-dguxbcs"$UNAME_RELEASE"
fi
else
- echo i586-dg-dgux${UNAME_RELEASE}
+ echo i586-dg-dgux"$UNAME_RELEASE"
fi
exit ;;
M88*:DolphinOS:*:*) # DolphinOS (SVR3)
@@ -554,26 +582,26 @@ EOF
echo m68k-tektronix-bsd
exit ;;
*:IRIX*:*:*)
- echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'`
+ 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 '
+ exit ;; # Note that: echo "'$(uname -s)'" gives 'AIX '
i*86:AIX:*:*)
echo i386-ibm-aix
exit ;;
ia64:AIX:*:*)
- if [ -x /usr/bin/oslevel ] ; then
- IBM_REV=`/usr/bin/oslevel`
+ if test -x /usr/bin/oslevel ; then
+ IBM_REV=$(/usr/bin/oslevel)
else
- IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE}
+ IBM_REV="$UNAME_VERSION.$UNAME_RELEASE"
fi
- echo ${UNAME_MACHINE}-ibm-aix${IBM_REV}
+ echo "$UNAME_MACHINE"-ibm-aix"$IBM_REV"
exit ;;
*:AIX:2:3)
if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
- eval $set_cc_for_build
- sed 's/^ //' << EOF >$dummy.c
+ set_cc_for_build
+ sed 's/^ //' << EOF > "$dummy.c"
#include <sys/systemcfg.h>
main()
@@ -584,7 +612,7 @@ EOF
exit(0);
}
EOF
- if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy`
+ if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=$("$dummy")
then
echo "$SYSTEM_NAME"
else
@@ -597,28 +625,28 @@ EOF
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_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 [ -x /usr/bin/lslpp ] ; then
- IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc |
- awk -F: '{ print $3 }' | sed s/[0-9]*$/0/`
+ 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}
+ IBM_REV="$UNAME_VERSION.$UNAME_RELEASE"
fi
- echo ${IBM_ARCH}-ibm-aix${IBM_REV}
+ echo "$IBM_ARCH"-ibm-aix"$IBM_REV"
exit ;;
*:AIX:*:*)
echo rs6000-ibm-aix
exit ;;
- ibmrt:4.4BSD:*|romp-ibm:BSD:*)
+ 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
+ echo romp-ibm-bsd"$UNAME_RELEASE" # 4.3 with uname added to
exit ;; # report: romp-ibm BSD 4.3
*:BOSX:*:*)
echo rs6000-bull-bosx
@@ -633,28 +661,28 @@ EOF
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 ;;
+ 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 [ -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
+ 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
+ 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 [ "${HP_ARCH}" = "" ]; then
- eval $set_cc_for_build
- sed 's/^ //' << EOF >$dummy.c
+ if test "$HP_ARCH" = ""; then
+ set_cc_for_build
+ sed 's/^ //' << EOF > "$dummy.c"
#define _HPUX_SOURCE
#include <stdlib.h>
@@ -687,13 +715,13 @@ EOF
exit (0);
}
EOF
- (CCOPTS="" $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy`
+ (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=$("$dummy")
test -z "$HP_ARCH" && HP_ARCH=hppa
fi ;;
esac
- if [ ${HP_ARCH} = hppa2.0w ]
+ if test "$HP_ARCH" = hppa2.0w
then
- eval $set_cc_for_build
+ 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
@@ -712,15 +740,15 @@ EOF
HP_ARCH=hppa64
fi
fi
- echo ${HP_ARCH}-hp-hpux${HPUX_REV}
+ 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}
+ HPUX_REV=$(echo "$UNAME_RELEASE"|sed -e 's/[^.]*.[0B]*//')
+ echo ia64-hp-hpux"$HPUX_REV"
exit ;;
3050*:HI-UX:*:*)
- eval $set_cc_for_build
- sed 's/^ //' << EOF >$dummy.c
+ set_cc_for_build
+ sed 's/^ //' << EOF > "$dummy.c"
#include <unistd.h>
int
main ()
@@ -745,11 +773,11 @@ EOF
exit (0);
}
EOF
- $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` &&
+ $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:*:* )
+ 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:*)
echo hppa1.1-hp-bsd
exit ;;
9000/8??:4.3bsd:*:*)
@@ -758,17 +786,17 @@ EOF
*9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
echo hppa1.0-hp-mpeix
exit ;;
- hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* )
+ hp7??:OSF1:*:* | hp8?[79]:OSF1:*:*)
echo hppa1.1-hp-osf
exit ;;
hp8??:OSF1:*:*)
echo hppa1.0-hp-osf
exit ;;
i*86:OSF1:*:*)
- if [ -x /usr/sbin/sysversion ] ; then
- echo ${UNAME_MACHINE}-unknown-osf1mk
+ if test -x /usr/sbin/sysversion ; then
+ echo "$UNAME_MACHINE"-unknown-osf1mk
else
- echo ${UNAME_MACHINE}-unknown-osf1
+ echo "$UNAME_MACHINE"-unknown-osf1
fi
exit ;;
parisc*:Lites*:*:*)
@@ -793,130 +821,123 @@ EOF
echo c4-convex-bsd
exit ;;
CRAY*Y-MP:*:*:*)
- echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ echo ymp-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
exit ;;
CRAY*[A-Z]90:*:*:*)
- echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \
+ 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/'
+ echo t90-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
exit ;;
CRAY*T3E:*:*:*)
- echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ echo alphaev5-cray-unicosmk"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
exit ;;
CRAY*SV1:*:*:*)
- echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ echo sv1-cray-unicos"$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'
exit ;;
*:UNICOS/mp:*:*)
- echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/'
+ 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/ /_/'`
+ 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/ /_/'`
+ 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}
+ echo "$UNAME_MACHINE"-pc-bsdi"$UNAME_RELEASE"
exit ;;
sparc*:BSD/OS:*:*)
- echo sparc-unknown-bsdi${UNAME_RELEASE}
+ echo sparc-unknown-bsdi"$UNAME_RELEASE"
exit ;;
*:BSD/OS:*:*)
- echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE}
+ 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
+ UNAME_PROCESSOR=$(/usr/bin/uname -p)
+ case "$UNAME_PROCESSOR" in
amd64)
- echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
- *)
- echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;;
+ 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
+ echo "$UNAME_MACHINE"-pc-cygwin
exit ;;
*:MINGW64*:*)
- echo ${UNAME_MACHINE}-pc-mingw64
+ echo "$UNAME_MACHINE"-pc-mingw64
exit ;;
*:MINGW*:*)
- echo ${UNAME_MACHINE}-pc-mingw32
+ echo "$UNAME_MACHINE"-pc-mingw32
exit ;;
*:MSYS*:*)
- echo ${UNAME_MACHINE}-pc-msys
- exit ;;
- i*:windows32*:*)
- # uname -m includes "-pc" on this system.
- echo ${UNAME_MACHINE}-mingw32
+ echo "$UNAME_MACHINE"-pc-msys
exit ;;
i*:PW*:*)
- echo ${UNAME_MACHINE}-pc-pw32
+ echo "$UNAME_MACHINE"-pc-pw32
exit ;;
*:Interix*:*)
- case ${UNAME_MACHINE} in
+ case "$UNAME_MACHINE" in
x86)
- echo i586-pc-interix${UNAME_RELEASE}
+ echo i586-pc-interix"$UNAME_RELEASE"
exit ;;
authenticamd | genuineintel | EM64T)
- echo x86_64-unknown-interix${UNAME_RELEASE}
+ echo x86_64-unknown-interix"$UNAME_RELEASE"
exit ;;
IA64)
- echo ia64-unknown-interix${UNAME_RELEASE}
+ echo ia64-unknown-interix"$UNAME_RELEASE"
exit ;;
esac ;;
- [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*)
- echo i${UNAME_MACHINE}-pc-mks
- exit ;;
- 8664:Windows_NT:*)
- echo x86_64-pc-mks
- exit ;;
- i*:Windows_NT*:* | Pentium*:Windows_NT*:*)
- # How do we know it's Interix rather than the generic POSIX subsystem?
- # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we
- # UNAME_MACHINE based on the output of uname instead of i386?
- echo i586-pc-interix
- exit ;;
i*:UWIN*:*)
- echo ${UNAME_MACHINE}-pc-uwin
+ echo "$UNAME_MACHINE"-pc-uwin
exit ;;
amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*)
- echo x86_64-unknown-cygwin
- exit ;;
- p*:CYGWIN*:*)
- echo powerpcle-unknown-cygwin
+ echo x86_64-pc-cygwin
exit ;;
prep*:SunOS:5.*:*)
- echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'`
+ 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,/.*$,,'`
+ 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}
+ echo "$UNAME_MACHINE-unknown-$(echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]")$(echo "$UNAME_RELEASE"|sed -e 's/[-(].*//')-$LIBC"
exit ;;
- i*86:Minix:*:*)
- echo ${UNAME_MACHINE}-pc-minix
+ *:Minix:*:*)
+ echo "$UNAME_MACHINE"-unknown-minix
exit ;;
aarch64:Linux:*:*)
- echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
exit ;;
aarch64_be:Linux:*:*)
UNAME_MACHINE=aarch64_be
- echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
exit ;;
alpha:Linux:*:*)
- case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in
+ 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 ;;
@@ -927,140 +948,181 @@ EOF
esac
objdump --private-headers /bin/sh | grep -q ld.so.1
if test "$?" = 0 ; then LIBC=gnulibc1 ; fi
- echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
exit ;;
arc:Linux:*:* | arceb:Linux:*:*)
- echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
exit ;;
arm*:Linux:*:*)
- eval $set_cc_for_build
+ 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}
+ 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
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabi
else
- echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabihf
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"eabihf
fi
fi
exit ;;
avr32*:Linux:*:*)
- echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
exit ;;
cris:Linux:*:*)
- echo ${UNAME_MACHINE}-axis-linux-${LIBC}
+ echo "$UNAME_MACHINE"-axis-linux-"$LIBC"
exit ;;
crisv32:Linux:*:*)
- echo ${UNAME_MACHINE}-axis-linux-${LIBC}
+ echo "$UNAME_MACHINE"-axis-linux-"$LIBC"
exit ;;
e2k:Linux:*:*)
- echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
exit ;;
frv:Linux:*:*)
- echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
exit ;;
hexagon:Linux:*:*)
- echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
exit ;;
i*86:Linux:*:*)
- echo ${UNAME_MACHINE}-pc-linux-${LIBC}
+ echo "$UNAME_MACHINE"-pc-linux-"$LIBC"
exit ;;
ia64:Linux:*:*)
- echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
exit ;;
k1om:Linux:*:*)
- echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ 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}
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
exit ;;
m68*:Linux:*:*)
- echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
exit ;;
mips:Linux:*:* | mips64:Linux:*:*)
- eval $set_cc_for_build
- sed 's/^ //' << EOF >$dummy.c
+ set_cc_for_build
+ IS_GLIBC=0
+ test x"${LIBC}" = xgnu && IS_GLIBC=1
+ sed 's/^ //' << EOF > "$dummy.c"
#undef CPU
- #undef ${UNAME_MACHINE}
- #undef ${UNAME_MACHINE}el
+ #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)
- CPU=${UNAME_MACHINE}el
+ MIPS_ENDIAN=el
#else
#if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
- CPU=${UNAME_MACHINE}
+ MIPS_ENDIAN=
#else
- CPU=
+ MIPS_ENDIAN=
#endif
#endif
EOF
- eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'`
- test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; }
+ 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}
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
exit ;;
openrisc*:Linux:*:*)
- echo or1k-unknown-linux-${LIBC}
+ echo or1k-unknown-linux-"$LIBC"
exit ;;
or32:Linux:*:* | or1k*:Linux:*:*)
- echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
exit ;;
padre:Linux:*:*)
- echo sparc-unknown-linux-${LIBC}
+ echo sparc-unknown-linux-"$LIBC"
exit ;;
parisc64:Linux:*:* | hppa64:Linux:*:*)
- echo hppa64-unknown-linux-${LIBC}
+ 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} ;;
+ 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}
+ echo powerpc64-unknown-linux-"$LIBC"
exit ;;
ppc:Linux:*:*)
- echo powerpc-unknown-linux-${LIBC}
+ echo powerpc-unknown-linux-"$LIBC"
exit ;;
ppc64le:Linux:*:*)
- echo powerpc64le-unknown-linux-${LIBC}
+ echo powerpc64le-unknown-linux-"$LIBC"
exit ;;
ppcle:Linux:*:*)
- echo powerpcle-unknown-linux-${LIBC}
+ echo powerpcle-unknown-linux-"$LIBC"
exit ;;
- riscv32:Linux:*:* | riscv64:Linux:*:*)
- echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ 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}
+ echo "$UNAME_MACHINE"-ibm-linux-"$LIBC"
exit ;;
sh64*:Linux:*:*)
- echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
exit ;;
sh*:Linux:*:*)
- echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
exit ;;
sparc:Linux:*:* | sparc64:Linux:*:*)
- echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
exit ;;
tile*:Linux:*:*)
- echo ${UNAME_MACHINE}-unknown-linux-${LIBC}
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
exit ;;
vax:Linux:*:*)
- echo ${UNAME_MACHINE}-dec-linux-${LIBC}
+ echo "$UNAME_MACHINE"-dec-linux-"$LIBC"
exit ;;
x86_64:Linux:*:*)
- echo ${UNAME_MACHINE}-pc-linux-${LIBC}
+ 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}
+ echo "$UNAME_MACHINE"-unknown-linux-"$LIBC"
exit ;;
i*86:DYNIX/ptx:4*:*)
# ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
@@ -1074,51 +1136,51 @@ EOF
# 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}
+ 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
+ echo "$UNAME_MACHINE"-pc-os2-emx
exit ;;
i*86:XTS-300:*:STOP)
- echo ${UNAME_MACHINE}-unknown-stop
+ echo "$UNAME_MACHINE"-unknown-stop
exit ;;
i*86:atheos:*:*)
- echo ${UNAME_MACHINE}-unknown-atheos
+ echo "$UNAME_MACHINE"-unknown-atheos
exit ;;
i*86:syllable:*:*)
- echo ${UNAME_MACHINE}-pc-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}
+ echo i386-unknown-lynxos"$UNAME_RELEASE"
exit ;;
i*86:*DOS:*:*)
- echo ${UNAME_MACHINE}-pc-msdosdjgpp
+ echo "$UNAME_MACHINE"-pc-msdosdjgpp
exit ;;
- i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*)
- UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'`
+ 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}
+ echo "$UNAME_MACHINE"-univel-sysv"$UNAME_REL"
else
- echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL}
+ 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
+ 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}
+ 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
+ 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/.*= //')`
+ 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
@@ -1126,9 +1188,9 @@ EOF
&& UNAME_MACHINE=i686
(/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
&& UNAME_MACHINE=i686
- echo ${UNAME_MACHINE}-pc-sco$UNAME_REL
+ echo "$UNAME_MACHINE"-pc-sco"$UNAME_REL"
else
- echo ${UNAME_MACHINE}-pc-sysv32
+ echo "$UNAME_MACHINE"-pc-sysv32
fi
exit ;;
pc:*:*:*)
@@ -1148,9 +1210,9 @@ EOF
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
+ 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
+ echo i860-unknown-sysv"$UNAME_RELEASE" # Unknown i860-SVR4
fi
exit ;;
mini*:CTIX:SYS*5:*)
@@ -1168,41 +1230,41 @@ EOF
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`
+ && 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; }
+ && { 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; } ;;
+ && { 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`
+ && 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; }
+ && { 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; }
+ && { 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; } ;;
+ && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;;
m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
- echo m68k-unknown-lynxos${UNAME_RELEASE}
+ 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}
+ echo sparc-unknown-lynxos"$UNAME_RELEASE"
exit ;;
rs6000:LynxOS:2.*:*)
- echo rs6000-unknown-lynxos${UNAME_RELEASE}
+ echo rs6000-unknown-lynxos"$UNAME_RELEASE"
exit ;;
PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*)
- echo powerpc-unknown-lynxos${UNAME_RELEASE}
+ echo powerpc-unknown-lynxos"$UNAME_RELEASE"
exit ;;
SM[BE]S:UNIX_SV:*:*)
- echo mips-dde-sysv${UNAME_RELEASE}
+ echo mips-dde-sysv"$UNAME_RELEASE"
exit ;;
RM*:ReliantUNIX-*:*:*)
echo mips-sni-sysv4
@@ -1212,8 +1274,8 @@ EOF
exit ;;
*:SINIX-*:*:*)
if uname -p 2>/dev/null >/dev/null ; then
- UNAME_MACHINE=`(uname -p) 2>/dev/null`
- echo ${UNAME_MACHINE}-sni-sysv4
+ UNAME_MACHINE=$( (uname -p) 2>/dev/null)
+ echo "$UNAME_MACHINE"-sni-sysv4
else
echo ns32k-sni-sysv
fi
@@ -1233,23 +1295,23 @@ EOF
exit ;;
i*86:VOS:*:*)
# From Paul.Green@stratus.com.
- echo ${UNAME_MACHINE}-stratus-vos
+ 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}
+ 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 [ -d /usr/nec ]; then
- echo mips-nec-sysv${UNAME_RELEASE}
+ if test -d /usr/nec; then
+ echo mips-nec-sysv"$UNAME_RELEASE"
else
- echo mips-unknown-sysv${UNAME_RELEASE}
+ echo mips-unknown-sysv"$UNAME_RELEASE"
fi
exit ;;
BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only.
@@ -1268,80 +1330,97 @@ EOF
echo x86_64-unknown-haiku
exit ;;
SX-4:SUPER-UX:*:*)
- echo sx4-nec-superux${UNAME_RELEASE}
+ echo sx4-nec-superux"$UNAME_RELEASE"
exit ;;
SX-5:SUPER-UX:*:*)
- echo sx5-nec-superux${UNAME_RELEASE}
+ echo sx5-nec-superux"$UNAME_RELEASE"
exit ;;
SX-6:SUPER-UX:*:*)
- echo sx6-nec-superux${UNAME_RELEASE}
+ echo sx6-nec-superux"$UNAME_RELEASE"
exit ;;
SX-7:SUPER-UX:*:*)
- echo sx7-nec-superux${UNAME_RELEASE}
+ echo sx7-nec-superux"$UNAME_RELEASE"
exit ;;
SX-8:SUPER-UX:*:*)
- echo sx8-nec-superux${UNAME_RELEASE}
+ echo sx8-nec-superux"$UNAME_RELEASE"
exit ;;
SX-8R:SUPER-UX:*:*)
- echo sx8r-nec-superux${UNAME_RELEASE}
+ echo sx8r-nec-superux"$UNAME_RELEASE"
exit ;;
SX-ACE:SUPER-UX:*:*)
- echo sxace-nec-superux${UNAME_RELEASE}
+ echo sxace-nec-superux"$UNAME_RELEASE"
exit ;;
Power*:Rhapsody:*:*)
- echo powerpc-apple-rhapsody${UNAME_RELEASE}
+ echo powerpc-apple-rhapsody"$UNAME_RELEASE"
exit ;;
*:Rhapsody:*:*)
- echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE}
+ echo "$UNAME_MACHINE"-apple-rhapsody"$UNAME_RELEASE"
+ exit ;;
+ arm64:Darwin:*:*)
+ echo aarch64-apple-darwin"$UNAME_RELEASE"
exit ;;
*:Darwin:*:*)
- UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown
- eval $set_cc_for_build
- if test "$UNAME_PROCESSOR" = unknown ; then
- UNAME_PROCESSOR=powerpc
+ 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 `echo "$UNAME_RELEASE" | sed -e 's/\..*//'` -le 10 ; then
- if [ "$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
+ 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
- # Avoid executing cc on OS X 10.9, as it ships with a stub
- # that puts up a graphical alert prompting to install
- # developer tools. Any system running Mac OS X 10.7 or
- # later (Darwin 11 and later) is required to have a 64-bit
- # processor. This is not true of the ARM version of Darwin
- # that Apple uses in portable devices.
- UNAME_PROCESSOR=x86_64
+ # uname -m returns i386 or x86_64
+ UNAME_PROCESSOR=$UNAME_MACHINE
fi
- echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE}
+ echo "$UNAME_PROCESSOR"-apple-darwin"$UNAME_RELEASE"
exit ;;
*:procnto*:*:* | *:QNX:[0123456789]*:*)
- UNAME_PROCESSOR=`uname -p`
+ 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}
+ 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}
+ NEO-*:NONSTOP_KERNEL:*:*)
+ echo neo-tandem-nsk"$UNAME_RELEASE"
exit ;;
NSE-*:NONSTOP_KERNEL:*:*)
- echo nse-tandem-nsk${UNAME_RELEASE}
+ 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 ;;
- NSR-?:NONSTOP_KERNEL:*:*)
- echo nsr-tandem-nsk${UNAME_RELEASE}
+ NSX-*:NONSTOP_KERNEL:*:*)
+ echo nsx-tandem-nsk"$UNAME_RELEASE"
exit ;;
*:NonStop-UX:*:*)
echo mips-compaq-nonstopux
@@ -1350,18 +1429,19 @@ EOF
echo bs2000-siemens-sysv
exit ;;
DS/*:UNIX_System_V:*:*)
- echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE}
+ 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
+ echo "$UNAME_MACHINE"-unknown-plan9
exit ;;
*:TOPS-10:*:*)
echo pdp10-unknown-tops10
@@ -1382,14 +1462,14 @@ EOF
echo pdp10-unknown-its
exit ;;
SEI:*:*:SEIUX)
- echo mips-sei-seiux${UNAME_RELEASE}
+ echo mips-sei-seiux"$UNAME_RELEASE"
exit ;;
*:DragonFly:*:*)
- echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`
+ echo "$UNAME_MACHINE"-unknown-dragonfly"$(echo "$UNAME_RELEASE"|sed -e 's/[-(].*//')"
exit ;;
*:*VMS:*:*)
- UNAME_MACHINE=`(uname -p) 2>/dev/null`
- case "${UNAME_MACHINE}" in
+ 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 ;;
@@ -1398,32 +1478,190 @@ EOF
echo i386-pc-xenix
exit ;;
i*86:skyos:*:*)
- echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE} | sed -e 's/ .*$//'`
+ echo "$UNAME_MACHINE"-pc-skyos"$(echo "$UNAME_RELEASE" | sed -e 's/ .*$//')"
exit ;;
i*86:rdos:*:*)
- echo ${UNAME_MACHINE}-pc-rdos
+ echo "$UNAME_MACHINE"-pc-rdos
exit ;;
i*86:AROS:*:*)
- echo ${UNAME_MACHINE}-pc-aros
+ echo "$UNAME_MACHINE"-pc-aros
exit ;;
x86_64:VMkernel:*:*)
- echo ${UNAME_MACHINE}-unknown-esx
+ 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
-$0: unable to guess system type
This script (version $timestamp), has failed to recognize the
-operating system you are using. If your script is old, overwrite
-config.guess and config.sub with the latest versions from:
+operating system you are using. If your script is old, overwrite *all*
+copies of config.guess and config.sub with the latest versions from:
- http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess
+ https://git.savannah.gnu.org/cgit/config.git/plain/config.guess
and
- http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub
+ 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
@@ -1431,31 +1669,32 @@ 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`
+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`
+/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`
+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}
+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 'write-file-hooks 'time-stamp)
+# eval: (add-hook 'before-save-hook 'time-stamp)
# time-stamp-start: "timestamp='"
# time-stamp-format: "%:y-%02m-%02d"
# time-stamp-end: "'"
diff --git a/deps/jemalloc/build-aux/config.sub b/deps/jemalloc/build-aux/config.sub
index dd2ca93c6..b0f849234 100755
--- a/deps/jemalloc/build-aux/config.sub
+++ b/deps/jemalloc/build-aux/config.sub
@@ -1,8 +1,8 @@
#! /bin/sh
# Configuration validation subroutine script.
-# Copyright 1992-2016 Free Software Foundation, Inc.
+# Copyright 1992-2021 Free Software Foundation, Inc.
-timestamp='2016-11-04'
+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
@@ -15,7 +15,7 @@ timestamp='2016-11-04'
# 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/>.
+# 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
@@ -33,7 +33,7 @@ timestamp='2016-11-04'
# Otherwise, we print the canonical config type on stdout and succeed.
# You can get the latest version of this script from:
-# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub
+# 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
@@ -50,14 +50,14 @@ timestamp='2016-11-04'
# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
# It is wrong to echo any other type of specification.
-me=`echo "$0" | sed -e 's,.*/,,'`
+me=$(echo "$0" | sed -e 's,.*/,,')
usage="\
Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS
Canonicalize a configuration name.
-Operation modes:
+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
@@ -67,7 +67,7 @@ Report bugs and patches to <config-patches@gnu.org>."
version="\
GNU config.sub ($timestamp)
-Copyright 1992-2016 Free Software Foundation, Inc.
+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."
@@ -89,12 +89,12 @@ while test $# -gt 0 ; do
- ) # Use stdin as input.
break ;;
-* )
- echo "$me: invalid option $1$help"
+ echo "$me: invalid option $1$help" >&2
exit 1 ;;
*local*)
# First pass through any local machine types.
- echo $1
+ echo "$1"
exit ;;
* )
@@ -110,1244 +110,1169 @@ case $# in
exit 1;;
esac
-# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any).
-# Here we must recognize all the valid KERNEL-OS combinations.
-maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
-case $maybe_os in
- nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \
- linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \
- knetbsd*-gnu* | netbsd*-gnu* | netbsd*-eabi* | \
- kopensolaris*-gnu* | cloudabi*-eabi* | \
- storm-chaos* | os2-emx* | rtmk-nova*)
- os=-$maybe_os
- basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
- ;;
- android-linux)
- os=-linux-android
- basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown
- ;;
- *)
- basic_machine=`echo $1 | sed 's/-[^-]*$//'`
- if [ $basic_machine != $1 ]
- then os=`echo $1 | sed 's/.*-/-/'`
- else os=; fi
- ;;
-esac
+# Split fields of configuration type
+# shellcheck disable=SC2162
+IFS="-" read field1 field2 field3 field4 <<EOF
+$1
+EOF
-### Let's recognize common machines as not being operating systems so
-### that things like config.sub decstation-3100 work. We also
-### recognize some manufacturers as not being operating systems, so we
-### can provide default operating systems below.
-case $os in
- -sun*os*)
- # Prevent following clause from handling this invalid input.
- ;;
- -dec* | -mips* | -sequent* | -encore* | -pc532* | -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*)
- os=
- basic_machine=$1
- ;;
- -bluegene*)
- os=-cnk
- ;;
- -sim | -cisco | -oki | -wec | -winbond)
- os=
- basic_machine=$1
- ;;
- -scout)
- ;;
- -wrs)
- os=-vxworks
- basic_machine=$1
- ;;
- -chorusos*)
- os=-chorusos
- basic_machine=$1
- ;;
- -chorusrdb)
- os=-chorusrdb
- basic_machine=$1
- ;;
- -hiux*)
- os=-hiuxwe2
- ;;
- -sco6)
- os=-sco5v6
- basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
- ;;
- -sco5)
- os=-sco3.2v5
- basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
- ;;
- -sco4)
- os=-sco3.2v4
- basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
- ;;
- -sco3.2.[4-9]*)
- os=`echo $os | sed -e 's/sco3.2./sco3.2v/'`
- basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
- ;;
- -sco3.2v[4-9]*)
- # Don't forget version if it is 3.2v4 or newer.
- basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
- ;;
- -sco5v6*)
- # Don't forget version if it is 3.2v4 or newer.
- basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
- ;;
- -sco*)
- os=-sco3.2v2
- basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
- ;;
- -udk*)
- basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
- ;;
- -isc)
- os=-isc2.2
- basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
- ;;
- -clix*)
- basic_machine=clipper-intergraph
- ;;
- -isc*)
- basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'`
- ;;
- -lynx*178)
- os=-lynxos178
- ;;
- -lynx*5)
- os=-lynxos5
- ;;
- -lynx*)
- os=-lynxos
+# Separate into logical components for further validation
+case $1 in
+ *-*-*-*-*)
+ echo Invalid configuration \`"$1"\': more than four components >&2
+ exit 1
;;
- -ptx*)
- basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'`
+ *-*-*-*)
+ basic_machine=$field1-$field2
+ basic_os=$field3-$field4
;;
- -windowsnt*)
- os=`echo $os | sed -e 's/windowsnt/winnt/'`
+ *-*-*)
+ # 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
;;
- -psos*)
- os=-psos
+ *-*)
+ # 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
;;
- -mint | -mint[0-9]*)
- basic_machine=m68k-atari
- os=-mint
+ *)
+ # 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 aliases for certain CPU-COMPANY combinations.
+# Decode 1-component or ad-hoc basic machines
case $basic_machine in
- # Recognize the basic CPU types without company name.
- # Some are omitted here because they have special meanings below.
- 1750a | 580 \
- | a29k \
- | aarch64 | aarch64_be \
- | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \
- | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \
- | am33_2.0 \
- | arc | arceb \
- | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \
- | avr | avr32 \
- | ba \
- | be32 | be64 \
- | bfin \
- | c4x | c8051 | clipper \
- | d10v | d30v | dlx | dsp16xx \
- | e2k | epiphany \
- | fido | fr30 | frv | ft32 \
- | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \
- | hexagon \
- | i370 | i860 | i960 | ia64 \
- | ip2k | iq2000 \
- | k1om \
- | le32 | le64 \
- | lm32 \
- | m32c | m32r | m32rle | m68000 | m68k | m88k \
- | maxq | mb | microblaze | microblazeel | mcore | mep | metag \
- | mips | mipsbe | mipseb | mipsel | mipsle \
- | mips16 \
- | mips64 | 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 \
- | mn10200 | mn10300 \
- | moxie \
- | mt \
- | msp430 \
- | nds32 | nds32le | nds32be \
- | nios | nios2 | nios2eb | nios2el \
- | ns16k | ns32k \
- | open8 | or1k | or1knd | or32 \
- | pdp10 | pdp11 | pj | pjl \
- | powerpc | powerpc64 | powerpc64le | powerpcle \
- | pru \
- | pyramid \
- | riscv32 | riscv64 \
- | rl78 | rx \
- | score \
- | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[234]eb | sheb | shbe | shle | sh[1234]le | sh3ele \
- | sh64 | sh64le \
- | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \
- | sparcv8 | sparcv9 | sparcv9b | sparcv9v \
- | spu \
- | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \
- | ubicom32 \
- | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \
- | visium \
- | we32k \
- | x86 | xc16x | xstormy16 | xtensa \
- | z8k | z80)
- basic_machine=$basic_machine-unknown
- ;;
- c54x)
- basic_machine=tic54x-unknown
- ;;
- c55x)
- basic_machine=tic55x-unknown
- ;;
- c6x)
- basic_machine=tic6x-unknown
- ;;
- leon|leon[3-9])
- basic_machine=sparc-$basic_machine
- ;;
- m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip)
- basic_machine=$basic_machine-unknown
- os=-none
+ # 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
;;
- m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k)
+ op50n)
+ cpu=hppa1.1
+ vendor=oki
;;
- ms1)
- basic_machine=mt-unknown
+ op60c)
+ cpu=hppa1.1
+ vendor=oki
;;
-
- strongarm | thumb | xscale)
- basic_machine=arm-unknown
+ ibm*)
+ cpu=i370
+ vendor=ibm
;;
- xgate)
- basic_machine=$basic_machine-unknown
- os=-none
+ orion105)
+ cpu=clipper
+ vendor=highlevel
;;
- xscaleeb)
- basic_machine=armeb-unknown
+ mac | mpw | mac-mpw)
+ cpu=m68k
+ vendor=apple
;;
-
- xscaleel)
- basic_machine=armel-unknown
+ pmac | pmac-mpw)
+ cpu=powerpc
+ vendor=apple
;;
- # 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)
- basic_machine=$basic_machine-pc
- ;;
- # Object if more than one company name word.
- *-*-*)
- echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
- exit 1
- ;;
- # Recognize the basic CPU types with company name.
- 580-* \
- | a29k-* \
- | aarch64-* | aarch64_be-* \
- | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \
- | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \
- | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \
- | arm-* | armbe-* | armle-* | armeb-* | armv*-* \
- | avr-* | avr32-* \
- | ba-* \
- | be32-* | be64-* \
- | bfin-* | bs2000-* \
- | c[123]* | c30-* | [cjt]90-* | c4x-* \
- | c8051-* | clipper-* | craynv-* | cydra-* \
- | d10v-* | d30v-* | dlx-* \
- | e2k-* | elxsi-* \
- | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \
- | h8300-* | h8500-* \
- | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \
- | hexagon-* \
- | i*86-* | i860-* | i960-* | ia64-* \
- | ip2k-* | iq2000-* \
- | k1om-* \
- | le32-* | le64-* \
- | lm32-* \
- | m32c-* | m32r-* | m32rle-* \
- | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \
- | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \
- | microblaze-* | microblazeel-* \
- | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \
- | mips16-* \
- | mips64-* | 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-* \
- | mt-* \
- | msp430-* \
- | nds32-* | nds32le-* | nds32be-* \
- | nios-* | nios2-* | nios2eb-* | nios2el-* \
- | none-* | np1-* | ns16k-* | ns32k-* \
- | open8-* \
- | or1k*-* \
- | orion-* \
- | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \
- | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \
- | pru-* \
- | pyramid-* \
- | riscv32-* | riscv64-* \
- | rl78-* | romp-* | rs6000-* | rx-* \
- | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \
- | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \
- | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \
- | sparclite-* \
- | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx*-* \
- | tahoe-* \
- | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \
- | tile*-* \
- | tron-* \
- | ubicom32-* \
- | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \
- | vax-* \
- | visium-* \
- | we32k-* \
- | x86-* | x86_64-* | xc16x-* | xps100-* \
- | xstormy16-* | xtensa*-* \
- | ymp-* \
- | z8k-* | z80-*)
- ;;
- # Recognize the basic CPU types without company name, with glob match.
- xtensa*)
- basic_machine=$basic_machine-unknown
- ;;
# Recognize the various machine names and aliases which stand
# for a CPU type and a company and sometimes even an OS.
- 386bsd)
- basic_machine=i386-unknown
- os=-bsd
- ;;
3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
- basic_machine=m68000-att
+ cpu=m68000
+ vendor=att
;;
3b*)
- basic_machine=we32k-att
- ;;
- a29khif)
- basic_machine=a29k-amd
- os=-udi
- ;;
- abacus)
- basic_machine=abacus-unknown
- ;;
- adobe68k)
- basic_machine=m68010-adobe
- os=-scout
- ;;
- alliant | fx80)
- basic_machine=fx80-alliant
- ;;
- altos | altos3068)
- basic_machine=m68k-altos
- ;;
- am29k)
- basic_machine=a29k-none
- os=-bsd
- ;;
- amd64)
- basic_machine=x86_64-pc
- ;;
- amd64-*)
- basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'`
- ;;
- amdahl)
- basic_machine=580-amdahl
- os=-sysv
- ;;
- amiga | amiga-*)
- basic_machine=m68k-unknown
- ;;
- amigaos | amigados)
- basic_machine=m68k-unknown
- os=-amigaos
- ;;
- amigaunix | amix)
- basic_machine=m68k-unknown
- os=-sysv4
- ;;
- apollo68)
- basic_machine=m68k-apollo
- os=-sysv
- ;;
- apollo68bsd)
- basic_machine=m68k-apollo
- os=-bsd
- ;;
- aros)
- basic_machine=i386-pc
- os=-aros
- ;;
- asmjs)
- basic_machine=asmjs-unknown
- ;;
- aux)
- basic_machine=m68k-apple
- os=-aux
- ;;
- balance)
- basic_machine=ns32k-sequent
- os=-dynix
- ;;
- blackfin)
- basic_machine=bfin-unknown
- os=-linux
- ;;
- blackfin-*)
- basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'`
- os=-linux
+ cpu=we32k
+ vendor=att
;;
bluegene*)
- basic_machine=powerpc-ibm
- os=-cnk
- ;;
- c54x-*)
- basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'`
- ;;
- c55x-*)
- basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'`
- ;;
- c6x-*)
- basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'`
- ;;
- c90)
- basic_machine=c90-cray
- os=-unicos
- ;;
- cegcc)
- basic_machine=arm-unknown
- os=-cegcc
- ;;
- convex-c1)
- basic_machine=c1-convex
- os=-bsd
- ;;
- convex-c2)
- basic_machine=c2-convex
- os=-bsd
- ;;
- convex-c32)
- basic_machine=c32-convex
- os=-bsd
- ;;
- convex-c34)
- basic_machine=c34-convex
- os=-bsd
- ;;
- convex-c38)
- basic_machine=c38-convex
- os=-bsd
- ;;
- cray | j90)
- basic_machine=j90-cray
- os=-unicos
- ;;
- craynv)
- basic_machine=craynv-cray
- os=-unicosmp
- ;;
- cr16 | cr16-*)
- basic_machine=cr16-unknown
- os=-elf
- ;;
- crds | unos)
- basic_machine=m68k-crds
- ;;
- crisv32 | crisv32-* | etraxfs*)
- basic_machine=crisv32-axis
- ;;
- cris | cris-* | etrax*)
- basic_machine=cris-axis
- ;;
- crx)
- basic_machine=crx-unknown
- os=-elf
- ;;
- da30 | da30-*)
- basic_machine=m68k-da30
- ;;
- decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn)
- basic_machine=mips-dec
+ cpu=powerpc
+ vendor=ibm
+ basic_os=cnk
;;
decsystem10* | dec10*)
- basic_machine=pdp10-dec
- os=-tops10
+ cpu=pdp10
+ vendor=dec
+ basic_os=tops10
;;
decsystem20* | dec20*)
- basic_machine=pdp10-dec
- os=-tops20
+ cpu=pdp10
+ vendor=dec
+ basic_os=tops20
;;
delta | 3300 | motorola-3300 | motorola-delta \
| 3300-motorola | delta-motorola)
- basic_machine=m68k-motorola
- ;;
- delta88)
- basic_machine=m88k-motorola
- os=-sysv3
- ;;
- dicos)
- basic_machine=i686-pc
- os=-dicos
- ;;
- djgpp)
- basic_machine=i586-pc
- os=-msdosdjgpp
+ cpu=m68k
+ vendor=motorola
;;
- dpx20 | dpx20-*)
- basic_machine=rs6000-bull
- os=-bosx
- ;;
- dpx2* | dpx2*-bull)
- basic_machine=m68k-bull
- os=-sysv3
- ;;
- e500v[12])
- basic_machine=powerpc-unknown
- os=$os"spe"
- ;;
- e500v[12]-*)
- basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
- os=$os"spe"
- ;;
- ebmon29k)
- basic_machine=a29k-amd
- os=-ebmon
- ;;
- elxsi)
- basic_machine=elxsi-elxsi
- os=-bsd
+ dpx2*)
+ cpu=m68k
+ vendor=bull
+ basic_os=sysv3
;;
encore | umax | mmax)
- basic_machine=ns32k-encore
+ cpu=ns32k
+ vendor=encore
;;
- es1800 | OSE68k | ose68k | ose | OSE)
- basic_machine=m68k-ericsson
- os=-ose
+ elxsi)
+ cpu=elxsi
+ vendor=elxsi
+ basic_os=${basic_os:-bsd}
;;
fx2800)
- basic_machine=i860-alliant
+ cpu=i860
+ vendor=alliant
;;
genix)
- basic_machine=ns32k-ns
- ;;
- gmicro)
- basic_machine=tron-gmicro
- os=-sysv
- ;;
- go32)
- basic_machine=i386-pc
- os=-go32
+ cpu=ns32k
+ vendor=ns
;;
h3050r* | hiux*)
- basic_machine=hppa1.1-hitachi
- os=-hiuxwe2
- ;;
- h8300hms)
- basic_machine=h8300-hitachi
- os=-hms
- ;;
- h8300xray)
- basic_machine=h8300-hitachi
- os=-xray
- ;;
- h8500hms)
- basic_machine=h8500-hitachi
- os=-hms
- ;;
- harris)
- basic_machine=m88k-harris
- os=-sysv3
- ;;
- hp300-*)
- basic_machine=m68k-hp
- ;;
- hp300bsd)
- basic_machine=m68k-hp
- os=-bsd
- ;;
- hp300hpux)
- basic_machine=m68k-hp
- os=-hpux
+ cpu=hppa1.1
+ vendor=hitachi
+ basic_os=hiuxwe2
;;
hp3k9[0-9][0-9] | hp9[0-9][0-9])
- basic_machine=hppa1.0-hp
+ cpu=hppa1.0
+ vendor=hp
;;
hp9k2[0-9][0-9] | hp9k31[0-9])
- basic_machine=m68000-hp
+ cpu=m68000
+ vendor=hp
;;
hp9k3[2-9][0-9])
- basic_machine=m68k-hp
+ cpu=m68k
+ vendor=hp
;;
hp9k6[0-9][0-9] | hp6[0-9][0-9])
- basic_machine=hppa1.0-hp
+ cpu=hppa1.0
+ vendor=hp
;;
hp9k7[0-79][0-9] | hp7[0-79][0-9])
- basic_machine=hppa1.1-hp
+ cpu=hppa1.1
+ vendor=hp
;;
hp9k78[0-9] | hp78[0-9])
# FIXME: really hppa2.0-hp
- basic_machine=hppa1.1-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
- basic_machine=hppa1.1-hp
+ cpu=hppa1.1
+ vendor=hp
;;
hp9k8[0-9][13679] | hp8[0-9][13679])
- basic_machine=hppa1.1-hp
+ cpu=hppa1.1
+ vendor=hp
;;
hp9k8[0-9][0-9] | hp8[0-9][0-9])
- basic_machine=hppa1.0-hp
- ;;
- hppa-next)
- os=-nextstep3
- ;;
- hppaosf)
- basic_machine=hppa1.1-hp
- os=-osf
- ;;
- hppro)
- basic_machine=hppa1.1-hp
- os=-proelf
- ;;
- i370-ibm* | ibm*)
- basic_machine=i370-ibm
+ cpu=hppa1.0
+ vendor=hp
;;
i*86v32)
- basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
- os=-sysv32
+ cpu=$(echo "$1" | sed -e 's/86.*/86/')
+ vendor=pc
+ basic_os=sysv32
;;
i*86v4*)
- basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
- os=-sysv4
+ cpu=$(echo "$1" | sed -e 's/86.*/86/')
+ vendor=pc
+ basic_os=sysv4
;;
i*86v)
- basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
- os=-sysv
+ cpu=$(echo "$1" | sed -e 's/86.*/86/')
+ vendor=pc
+ basic_os=sysv
;;
i*86sol2)
- basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'`
- os=-solaris2
- ;;
- i386mach)
- basic_machine=i386-mach
- os=-mach
+ cpu=$(echo "$1" | sed -e 's/86.*/86/')
+ vendor=pc
+ basic_os=solaris2
;;
- i386-vsta | vsta)
- basic_machine=i386-unknown
- os=-vsta
+ j90 | j90-cray)
+ cpu=j90
+ vendor=cray
+ basic_os=${basic_os:-unicos}
;;
iris | iris4d)
- basic_machine=mips-sgi
- case $os in
- -irix*)
+ cpu=mips
+ vendor=sgi
+ case $basic_os in
+ irix*)
;;
*)
- os=-irix4
+ basic_os=irix4
;;
esac
;;
- isi68 | isi)
- basic_machine=m68k-isi
- os=-sysv
- ;;
- leon-*|leon[3-9]-*)
- basic_machine=sparc-`echo $basic_machine | sed 's/-.*//'`
- ;;
- m68knommu)
- basic_machine=m68k-unknown
- os=-linux
- ;;
- m68knommu-*)
- basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'`
- os=-linux
- ;;
- m88k-omron*)
- basic_machine=m88k-omron
- ;;
- magnum | m3230)
- basic_machine=mips-mips
- os=-sysv
- ;;
- merlin)
- basic_machine=ns32k-utek
- os=-sysv
- ;;
- microblaze*)
- basic_machine=microblaze-xilinx
- ;;
- mingw64)
- basic_machine=x86_64-pc
- os=-mingw64
- ;;
- mingw32)
- basic_machine=i686-pc
- os=-mingw32
- ;;
- mingw32ce)
- basic_machine=arm-unknown
- os=-mingw32ce
- ;;
miniframe)
- basic_machine=m68000-convergent
+ cpu=m68000
+ vendor=convergent
;;
- *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*)
- basic_machine=m68k-atari
- os=-mint
- ;;
- mips3*-*)
- basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`
- ;;
- mips3*)
- basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown
- ;;
- monitor)
- basic_machine=m68k-rom68k
- os=-coff
- ;;
- morphos)
- basic_machine=powerpc-unknown
- os=-morphos
- ;;
- moxiebox)
- basic_machine=moxie-unknown
- os=-moxiebox
- ;;
- msdos)
- basic_machine=i386-pc
- os=-msdos
- ;;
- ms1-*)
- basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'`
- ;;
- msys)
- basic_machine=i686-pc
- os=-msys
- ;;
- mvs)
- basic_machine=i370-ibm
- os=-mvs
- ;;
- nacl)
- basic_machine=le32-unknown
- os=-nacl
- ;;
- ncr3000)
- basic_machine=i486-ncr
- os=-sysv4
- ;;
- netbsd386)
- basic_machine=i386-unknown
- os=-netbsd
- ;;
- netwinder)
- basic_machine=armv4l-rebel
- os=-linux
- ;;
- news | news700 | news800 | news900)
- basic_machine=m68k-sony
- os=-newsos
- ;;
- news1000)
- basic_machine=m68030-sony
- os=-newsos
+ *mint | mint[0-9]* | *MiNT | *MiNT[0-9]*)
+ cpu=m68k
+ vendor=atari
+ basic_os=mint
;;
news-3600 | risc-news)
- basic_machine=mips-sony
- os=-newsos
- ;;
- necv70)
- basic_machine=v70-nec
- os=-sysv
- ;;
- next | m*-next )
- basic_machine=m68k-next
- case $os in
- -nextstep* )
+ cpu=mips
+ vendor=sony
+ basic_os=newsos
+ ;;
+ next | m*-next)
+ cpu=m68k
+ vendor=next
+ case $basic_os in
+ openstep*)
+ ;;
+ nextstep*)
;;
- -ns2*)
- os=-nextstep2
+ ns2*)
+ basic_os=nextstep2
;;
*)
- os=-nextstep3
+ basic_os=nextstep3
;;
esac
;;
- nh3000)
- basic_machine=m68k-harris
- os=-cxux
- ;;
- nh[45]000)
- basic_machine=m88k-harris
- os=-cxux
- ;;
- nindy960)
- basic_machine=i960-intel
- os=-nindy
- ;;
- mon960)
- basic_machine=i960-intel
- os=-mon960
- ;;
- nonstopux)
- basic_machine=mips-compaq
- os=-nonstopux
- ;;
np1)
- basic_machine=np1-gould
- ;;
- neo-tandem)
- basic_machine=neo-tandem
- ;;
- nse-tandem)
- basic_machine=nse-tandem
- ;;
- nsr-tandem)
- basic_machine=nsr-tandem
+ cpu=np1
+ vendor=gould
;;
op50n-* | op60c-*)
- basic_machine=hppa1.1-oki
- os=-proelf
- ;;
- openrisc | openrisc-*)
- basic_machine=or32-unknown
- ;;
- os400)
- basic_machine=powerpc-ibm
- os=-os400
- ;;
- OSE68000 | ose68000)
- basic_machine=m68000-ericsson
- os=-ose
- ;;
- os68k)
- basic_machine=m68k-none
- os=-os68k
+ cpu=hppa1.1
+ vendor=oki
+ basic_os=proelf
;;
pa-hitachi)
- basic_machine=hppa1.1-hitachi
- os=-hiuxwe2
- ;;
- paragon)
- basic_machine=i860-intel
- os=-osf
- ;;
- parisc)
- basic_machine=hppa-unknown
- os=-linux
- ;;
- parisc-*)
- basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'`
- os=-linux
+ cpu=hppa1.1
+ vendor=hitachi
+ basic_os=hiuxwe2
;;
pbd)
- basic_machine=sparc-tti
+ cpu=sparc
+ vendor=tti
;;
pbb)
- basic_machine=m68k-tti
- ;;
- pc532 | pc532-*)
- basic_machine=ns32k-pc532
- ;;
- pc98)
- basic_machine=i386-pc
- ;;
- pc98-*)
- basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'`
- ;;
- pentium | p5 | k5 | k6 | nexgen | viac3)
- basic_machine=i586-pc
- ;;
- pentiumpro | p6 | 6x86 | athlon | athlon_*)
- basic_machine=i686-pc
- ;;
- pentiumii | pentium2 | pentiumiii | pentium3)
- basic_machine=i686-pc
- ;;
- pentium4)
- basic_machine=i786-pc
- ;;
- pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
- basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'`
+ cpu=m68k
+ vendor=tti
;;
- pentiumpro-* | p6-* | 6x86-* | athlon-*)
- basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
- ;;
- pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
- basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'`
- ;;
- pentium4-*)
- basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'`
+ pc532)
+ cpu=ns32k
+ vendor=pc532
;;
pn)
- basic_machine=pn-gould
- ;;
- power) basic_machine=power-ibm
- ;;
- ppc | ppcbe) basic_machine=powerpc-unknown
- ;;
- ppc-* | ppcbe-*)
- basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'`
- ;;
- ppcle | powerpclittle)
- basic_machine=powerpcle-unknown
- ;;
- ppcle-* | powerpclittle-*)
- basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'`
- ;;
- ppc64) basic_machine=powerpc64-unknown
- ;;
- ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'`
- ;;
- ppc64le | powerpc64little)
- basic_machine=powerpc64le-unknown
+ cpu=pn
+ vendor=gould
;;
- ppc64le-* | powerpc64little-*)
- basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'`
+ power)
+ cpu=power
+ vendor=ibm
;;
ps2)
- basic_machine=i386-ibm
- ;;
- pw32)
- basic_machine=i586-unknown
- os=-pw32
- ;;
- rdos | rdos64)
- basic_machine=x86_64-pc
- os=-rdos
- ;;
- rdos32)
- basic_machine=i386-pc
- os=-rdos
- ;;
- rom68k)
- basic_machine=m68k-rom68k
- os=-coff
+ cpu=i386
+ vendor=ibm
;;
rm[46]00)
- basic_machine=mips-siemens
+ cpu=mips
+ vendor=siemens
;;
rtpc | rtpc-*)
- basic_machine=romp-ibm
- ;;
- s390 | s390-*)
- basic_machine=s390-ibm
- ;;
- s390x | s390x-*)
- basic_machine=s390x-ibm
+ cpu=romp
+ vendor=ibm
;;
- sa29200)
- basic_machine=a29k-amd
- os=-udi
- ;;
- sb1)
- basic_machine=mipsisa64sb1-unknown
+ sde)
+ cpu=mipsisa32
+ vendor=sde
+ basic_os=${basic_os:-elf}
;;
- sb1el)
- basic_machine=mipsisa64sb1el-unknown
+ simso-wrs)
+ cpu=sparclite
+ vendor=wrs
+ basic_os=vxworks
;;
- sde)
- basic_machine=mipsisa32-sde
- os=-elf
+ tower | tower-32)
+ cpu=m68k
+ vendor=ncr
;;
- sei)
- basic_machine=mips-sei
- os=-seiux
+ vpp*|vx|vx-*)
+ cpu=f301
+ vendor=fujitsu
;;
- sequent)
- basic_machine=i386-sequent
+ w65)
+ cpu=w65
+ vendor=wdc
;;
- sh)
- basic_machine=sh-hitachi
- os=-hms
+ w89k-*)
+ cpu=hppa1.1
+ vendor=winbond
+ basic_os=proelf
;;
- sh5el)
- basic_machine=sh5le-unknown
+ none)
+ cpu=none
+ vendor=none
;;
- sh64)
- basic_machine=sh64-unknown
+ leon|leon[3-9])
+ cpu=sparc
+ vendor=$basic_machine
;;
- sparclite-wrs | simso-wrs)
- basic_machine=sparclite-wrs
- os=-vxworks
+ leon-*|leon[3-9]-*)
+ cpu=sparc
+ vendor=$(echo "$basic_machine" | sed 's/-.*//')
;;
- sps7)
- basic_machine=m68k-bull
- os=-sysv2
+
+ *-*)
+ # shellcheck disable=SC2162
+ IFS="-" read cpu vendor <<EOF
+$basic_machine
+EOF
;;
- spur)
- basic_machine=spur-unknown
+ # 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
;;
- st2000)
- basic_machine=m68k-tandem
+ # 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
;;
- stratus)
- basic_machine=i860-stratus
- os=-sysv4
+ x64 | amd64)
+ cpu=x86_64
+ vendor=pc
;;
- strongarm-* | thumb-*)
- basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'`
+ # Recognize the basic CPU types without company name.
+ *)
+ cpu=$basic_machine
+ vendor=unknown
;;
- sun2)
- basic_machine=m68000-sun
+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}
;;
- sun2os3)
- basic_machine=m68000-sun
- os=-sunos3
+ c90-unknown | c90-cray)
+ vendor=cray
+ basic_os=${Basic_os:-unicos}
;;
- sun2os4)
- basic_machine=m68000-sun
- os=-sunos4
+ fx80-unknown)
+ vendor=alliant
;;
- sun3os3)
- basic_machine=m68k-sun
- os=-sunos3
+ romp-unknown)
+ vendor=ibm
;;
- sun3os4)
- basic_machine=m68k-sun
- os=-sunos4
+ mmix-unknown)
+ vendor=knuth
;;
- sun4os3)
- basic_machine=sparc-sun
- os=-sunos3
+ microblaze-unknown | microblazeel-unknown)
+ vendor=xilinx
;;
- sun4os4)
- basic_machine=sparc-sun
- os=-sunos4
+ rs6000-unknown)
+ vendor=ibm
;;
- sun4sol2)
- basic_machine=sparc-sun
- os=-solaris2
+ vax-unknown)
+ vendor=dec
;;
- sun3 | sun3-*)
- basic_machine=m68k-sun
+ pdp11-unknown)
+ vendor=dec
;;
- sun4)
- basic_machine=sparc-sun
+ we32k-unknown)
+ vendor=att
;;
- sun386 | sun386i | roadrunner)
- basic_machine=i386-sun
+ cydra-unknown)
+ vendor=cydrome
;;
- sv1)
- basic_machine=sv1-cray
- os=-unicos
+ i370-ibm*)
+ vendor=ibm
;;
- symmetry)
- basic_machine=i386-sequent
- os=-dynix
+ orion-unknown)
+ vendor=highlevel
;;
- t3e)
- basic_machine=alphaev5-cray
- os=-unicos
+ xps-unknown | xps100-unknown)
+ cpu=xps100
+ vendor=honeywell
;;
- t90)
- basic_machine=t90-cray
- os=-unicos
+
+ # Here we normalize CPU types with a missing or matching vendor
+ dpx20-unknown | dpx20-bull)
+ cpu=rs6000
+ vendor=bull
+ basic_os=${basic_os:-bosx}
;;
- tile*)
- basic_machine=$basic_machine-unknown
- os=-linux-gnu
+
+ # Here we normalize CPU types irrespective of the vendor
+ amd64-*)
+ cpu=x86_64
;;
- tx39)
- basic_machine=mipstx39-unknown
+ blackfin-*)
+ cpu=bfin
+ basic_os=linux
;;
- tx39el)
- basic_machine=mipstx39el-unknown
+ c54x-*)
+ cpu=tic54x
;;
- toad1)
- basic_machine=pdp10-xkl
- os=-tops20
+ c55x-*)
+ cpu=tic55x
;;
- tower | tower-32)
- basic_machine=m68k-ncr
+ c6x-*)
+ cpu=tic6x
;;
- tpf)
- basic_machine=s390x-ibm
- os=-tpf
+ e500v[12]-*)
+ cpu=powerpc
+ basic_os=${basic_os}"spe"
;;
- udi29k)
- basic_machine=a29k-amd
- os=-udi
+ mips3*-*)
+ cpu=mips64
;;
- ultra3)
- basic_machine=a29k-nyu
- os=-sym1
+ ms1-*)
+ cpu=mt
;;
- v810 | necv810)
- basic_machine=v810-nec
- os=-none
+ m68knommu-*)
+ cpu=m68k
+ basic_os=linux
;;
- vaxv)
- basic_machine=vax-dec
- os=-sysv
+ m9s12z-* | m68hcs12z-* | hcs12z-* | s12z-*)
+ cpu=s12z
;;
- vms)
- basic_machine=vax-dec
- os=-vms
+ openrisc-*)
+ cpu=or32
;;
- vpp*|vx|vx-*)
- basic_machine=f301-fujitsu
+ parisc-*)
+ cpu=hppa
+ basic_os=linux
;;
- vxworks960)
- basic_machine=i960-wrs
- os=-vxworks
+ pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
+ cpu=i586
;;
- vxworks68)
- basic_machine=m68k-wrs
- os=-vxworks
+ pentiumpro-* | p6-* | 6x86-* | athlon-* | athalon_*-*)
+ cpu=i686
;;
- vxworks29k)
- basic_machine=a29k-wrs
- os=-vxworks
+ pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
+ cpu=i686
;;
- w65*)
- basic_machine=w65-wdc
- os=-none
+ pentium4-*)
+ cpu=i786
;;
- w89k-*)
- basic_machine=hppa1.1-winbond
- os=-proelf
+ pc98-*)
+ cpu=i386
;;
- xbox)
- basic_machine=i686-pc
- os=-mingw32
+ ppc-* | ppcbe-*)
+ cpu=powerpc
;;
- xps | xps100)
- basic_machine=xps100-honeywell
+ ppcle-* | powerpclittle-*)
+ cpu=powerpcle
;;
- xscale-* | xscalee[bl]-*)
- basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'`
+ ppc64-*)
+ cpu=powerpc64
;;
- ymp)
- basic_machine=ymp-cray
- os=-unicos
+ ppc64le-* | powerpc64little-*)
+ cpu=powerpc64le
;;
- z8k-*-coff)
- basic_machine=z8k-unknown
- os=-sim
+ sb1-*)
+ cpu=mipsisa64sb1
;;
- z80-*-coff)
- basic_machine=z80-unknown
- os=-sim
+ sb1el-*)
+ cpu=mipsisa64sb1el
;;
- none)
- basic_machine=none-none
- os=-none
+ sh5e[lb]-*)
+ cpu=$(echo "$cpu" | sed 's/^\(sh.\)e\(.\)$/\1\2e/')
;;
-
-# 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)
- basic_machine=hppa1.1-winbond
+ spur-*)
+ cpu=spur
;;
- op50n)
- basic_machine=hppa1.1-oki
+ strongarm-* | thumb-*)
+ cpu=arm
;;
- op60c)
- basic_machine=hppa1.1-oki
+ tx39-*)
+ cpu=mipstx39
;;
- romp)
- basic_machine=romp-ibm
+ tx39el-*)
+ cpu=mipstx39el
;;
- mmix)
- basic_machine=mmix-knuth
+ x64-*)
+ cpu=x86_64
;;
- rs6000)
- basic_machine=rs6000-ibm
+ xscale-* | xscalee[bl]-*)
+ cpu=$(echo "$cpu" | sed 's/^xscale/arm/')
;;
- vax)
- basic_machine=vax-dec
+ arm64-*)
+ cpu=aarch64
;;
- pdp10)
- # there are many clones, so DEC is not a safe bet
- basic_machine=pdp10-unknown
+
+ # Recognize the canonical CPU Types that limit and/or modify the
+ # company names they are paired with.
+ cr16-*)
+ basic_os=${basic_os:-elf}
;;
- pdp11)
- basic_machine=pdp11-dec
+ crisv32-* | etraxfs*-*)
+ cpu=crisv32
+ vendor=axis
;;
- we32k)
- basic_machine=we32k-att
+ cris-* | etrax*-*)
+ cpu=cris
+ vendor=axis
;;
- sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele)
- basic_machine=sh-unknown
+ crx-*)
+ basic_os=${basic_os:-elf}
;;
- sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v)
- basic_machine=sparc-sun
+ neo-tandem)
+ cpu=neo
+ vendor=tandem
;;
- cydra)
- basic_machine=cydra-cydrome
+ nse-tandem)
+ cpu=nse
+ vendor=tandem
;;
- orion)
- basic_machine=orion-highlevel
+ nsr-tandem)
+ cpu=nsr
+ vendor=tandem
;;
- orion105)
- basic_machine=clipper-highlevel
+ nsv-tandem)
+ cpu=nsv
+ vendor=tandem
;;
- mac | mpw | mac-mpw)
- basic_machine=m68k-apple
+ nsx-tandem)
+ cpu=nsx
+ vendor=tandem
;;
- pmac | pmac-mpw)
- basic_machine=powerpc-apple
+ mipsallegrexel-sony)
+ cpu=mipsallegrexel
+ vendor=sony
;;
- *-unknown)
- # Make sure to match an already-canonicalized machine name.
+ tile*-*)
+ basic_os=${basic_os:-linux-gnu}
;;
+
*)
- echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2
- exit 1
+ # 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 $basic_machine in
- *-digital*)
- basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'`
+case $vendor in
+ digital*)
+ vendor=dec
;;
- *-commodore*)
- basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'`
+ commodore*)
+ vendor=cbm
;;
*)
;;
@@ -1355,203 +1280,213 @@ esac
# Decode manufacturer-specific aliases for certain operating systems.
-if [ x"$os" != x"" ]
+if test x$basic_os != x
then
-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
+
+# 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|')
;;
- -solaris1 | -solaris1.*)
- os=`echo $os | sed -e 's|solaris1|sunos4|'`
+ *)
+ 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
;;
- -solaris)
- os=-solaris2
+ bluegene*)
+ os=cnk
;;
- -svr4*)
- os=-sysv4
+ solaris1 | solaris1.*)
+ os=$(echo $os | sed -e 's|solaris1|sunos4|')
;;
- -unixware*)
- os=-sysv4.2uw
+ solaris)
+ os=solaris2
;;
- -gnu/linux*)
- os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'`
+ unixware*)
+ os=sysv4.2uw
;;
- # First accept the basic system types.
- # The portable systems comes first.
- # Each alternative MUST END IN A *, to match a version number.
- # -sysv* is not here because it comes later, after sysvr4.
- -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \
- | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\
- | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \
- | -sym* | -kopensolaris* | -plan9* \
- | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \
- | -aos* | -aros* | -cloudabi* | -sortix* \
- | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \
- | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \
- | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \
- | -bitrig* | -openbsd* | -solidbsd* | -libertybsd* \
- | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \
- | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \
- | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \
- | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
- | -chorusos* | -chorusrdb* | -cegcc* \
- | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
- | -midipix* | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \
- | -linux-newlib* | -linux-musl* | -linux-uclibc* \
- | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \
- | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
- | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \
- | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \
- | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \
- | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \
- | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \
- | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* \
- | -onefs* | -tirtos* | -phoenix* | -fuchsia*)
- # Remember, each alternative MUST END IN *, to match a version number.
- ;;
- -qnx*)
- case $basic_machine in
- x86-* | i*86-*)
- ;;
- *)
- os=-nto$os
- ;;
- esac
+ # es1800 is here to avoid being matched by es* (a different OS)
+ es1800*)
+ os=ose
;;
- -nto-qnx*)
+ # Some version numbers need modification
+ chorusos*)
+ os=chorusos
;;
- -nto*)
- os=`echo $os | sed -e 's|nto|nto-qnx|'`
+ isc)
+ os=isc2.2
;;
- -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \
- | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \
- | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*)
+ sco6)
+ os=sco5v6
;;
- -mac*)
- os=`echo $os | sed -e 's|mac|macos|'`
+ sco5)
+ os=sco3.2v5
;;
- -linux-dietlibc)
- os=-linux-dietlibc
+ sco4)
+ os=sco3.2v4
;;
- -linux*)
- os=`echo $os | sed -e 's|linux|linux-gnu|'`
+ sco3.2.[4-9]*)
+ os=$(echo $os | sed -e 's/sco3.2./sco3.2v/')
;;
- -sunos5*)
- os=`echo $os | sed -e 's|sunos5|solaris2|'`
+ sco*v* | scout)
+ # Don't match below
;;
- -sunos6*)
- os=`echo $os | sed -e 's|sunos6|solaris3|'`
+ sco*)
+ os=sco3.2v2
;;
- -opened*)
- os=-openedition
+ psos*)
+ os=psos
;;
- -os400*)
- os=-os400
+ qnx*)
+ os=qnx
;;
- -wince*)
- os=-wince
+ hiux*)
+ os=hiuxwe2
;;
- -osfrose*)
- os=-osfrose
+ lynx*178)
+ os=lynxos178
;;
- -osf*)
- os=-osf
+ lynx*5)
+ os=lynxos5
;;
- -utek*)
- os=-bsd
+ lynxos*)
+ # don't get caught up in next wildcard
;;
- -dynix*)
- os=-bsd
+ lynx*)
+ os=lynxos
;;
- -acis*)
- os=-aos
+ mac[0-9]*)
+ os=$(echo "$os" | sed -e 's|mac|macos|')
;;
- -atheos*)
- os=-atheos
+ opened*)
+ os=openedition
;;
- -syllable*)
- os=-syllable
+ os400*)
+ os=os400
;;
- -386bsd)
- os=-bsd
+ sunos5*)
+ os=$(echo "$os" | sed -e 's|sunos5|solaris2|')
;;
- -ctix* | -uts*)
- os=-sysv
+ sunos6*)
+ os=$(echo "$os" | sed -e 's|sunos6|solaris3|')
;;
- -nova*)
- os=-rtmk-nova
+ wince*)
+ os=wince
;;
- -ns2 )
- os=-nextstep2
+ utek*)
+ os=bsd
;;
- -nsk*)
- os=-nsk
+ dynix*)
+ os=bsd
;;
- # Preserve the version number of sinix5.
- -sinix5.*)
- os=`echo $os | sed -e 's|sinix|sysv|'`
+ acis*)
+ os=aos
;;
- -sinix*)
- os=-sysv4
+ atheos*)
+ os=atheos
;;
- -tpf*)
- os=-tpf
+ syllable*)
+ os=syllable
;;
- -triton*)
- os=-sysv3
+ 386bsd)
+ os=bsd
;;
- -oss*)
- os=-sysv3
+ ctix* | uts*)
+ os=sysv
;;
- -svr4)
- os=-sysv4
+ nova*)
+ os=rtmk-nova
;;
- -svr3)
- os=-sysv3
+ ns2)
+ os=nextstep2
;;
- -sysvr4)
- os=-sysv4
+ # Preserve the version number of sinix5.
+ sinix5.*)
+ os=$(echo $os | sed -e 's|sinix|sysv|')
;;
- # This must come after -sysvr4.
- -sysv*)
+ sinix*)
+ os=sysv4
;;
- -ose*)
- os=-ose
+ tpf*)
+ os=tpf
;;
- -es1800*)
- os=-ose
+ triton*)
+ os=sysv3
;;
- -xenix)
- os=-xenix
+ oss*)
+ os=sysv3
;;
- -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
- os=-mint
+ svr4*)
+ os=sysv4
;;
- -aros*)
- os=-aros
+ svr3)
+ os=sysv3
;;
- -zvmoe)
- os=-zvmoe
+ sysvr4)
+ os=sysv4
;;
- -dicos*)
- os=-dicos
+ ose*)
+ os=ose
;;
- -nacl*)
+ *mint | mint[0-9]* | *MiNT | MiNT[0-9]*)
+ os=mint
;;
- -ios)
+ dicos*)
+ os=dicos
;;
- -none)
+ 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
;;
*)
- # Get rid of the `-' at the beginning of $os.
- os=`echo $os | sed 's/[^-]*-//'`
- echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2
- exit 1
+ # No normalization, but not necessarily accepted, that comes below.
;;
esac
+
else
# Here we handle the default operating systems that come with various machines.
@@ -1564,261 +1499,356 @@ else
# will signal an error saying that MANUFACTURER isn't an operating
# system, and we'll never get to this point.
-case $basic_machine in
+kernel=
+case $cpu-$vendor in
score-*)
- os=-elf
+ os=elf
;;
spu-*)
- os=-elf
+ os=elf
;;
*-acorn)
- os=-riscix1.2
+ os=riscix1.2
;;
arm*-rebel)
- os=-linux
+ kernel=linux
+ os=gnu
;;
arm*-semi)
- os=-aout
+ os=aout
;;
c4x-* | tic4x-*)
- os=-coff
+ os=coff
;;
c8051-*)
- os=-elf
+ os=elf
+ ;;
+ clipper-intergraph)
+ os=clix
;;
hexagon-*)
- os=-elf
+ os=elf
;;
tic54x-*)
- os=-coff
+ os=coff
;;
tic55x-*)
- os=-coff
+ os=coff
;;
tic6x-*)
- os=-coff
+ os=coff
;;
# This must come before the *-dec entry.
pdp10-*)
- os=-tops20
+ os=tops20
;;
pdp11-*)
- os=-none
+ os=none
;;
*-dec | vax-*)
- os=-ultrix4.2
+ os=ultrix4.2
;;
m68*-apollo)
- os=-domain
+ os=domain
;;
i386-sun)
- os=-sunos4.0.2
+ os=sunos4.0.2
;;
m68000-sun)
- os=-sunos3
+ os=sunos3
;;
m68*-cisco)
- os=-aout
+ os=aout
;;
mep-*)
- os=-elf
+ os=elf
;;
mips*-cisco)
- os=-elf
+ os=elf
;;
mips*-*)
- os=-elf
+ os=elf
;;
or32-*)
- os=-coff
+ os=coff
;;
*-tti) # must be before sparc entry or we get the wrong os.
- os=-sysv3
+ os=sysv3
;;
sparc-* | *-sun)
- os=-sunos4.1.1
+ os=sunos4.1.1
;;
- *-be)
- os=-beos
+ pru-*)
+ os=elf
;;
- *-haiku)
- os=-haiku
+ *-be)
+ os=beos
;;
*-ibm)
- os=-aix
+ os=aix
;;
*-knuth)
- os=-mmixware
+ os=mmixware
;;
*-wec)
- os=-proelf
+ os=proelf
;;
*-winbond)
- os=-proelf
+ os=proelf
;;
*-oki)
- os=-proelf
+ os=proelf
;;
*-hp)
- os=-hpux
+ os=hpux
;;
*-hitachi)
- os=-hiux
+ os=hiux
;;
i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
- os=-sysv
+ os=sysv
;;
*-cbm)
- os=-amigaos
+ os=amigaos
;;
*-dg)
- os=-dgux
+ os=dgux
;;
*-dolphin)
- os=-sysv3
+ os=sysv3
;;
m68k-ccur)
- os=-rtu
+ os=rtu
;;
m88k-omron*)
- os=-luna
+ os=luna
;;
- *-next )
- os=-nextstep
+ *-next)
+ os=nextstep
;;
*-sequent)
- os=-ptx
+ os=ptx
;;
*-crds)
- os=-unos
+ os=unos
;;
*-ns)
- os=-genix
+ os=genix
;;
i370-*)
- os=-mvs
- ;;
- *-next)
- os=-nextstep3
+ os=mvs
;;
*-gould)
- os=-sysv
+ os=sysv
;;
*-highlevel)
- os=-bsd
+ os=bsd
;;
*-encore)
- os=-bsd
+ os=bsd
;;
*-sgi)
- os=-irix
+ os=irix
;;
*-siemens)
- os=-sysv4
+ os=sysv4
;;
*-masscomp)
- os=-rtu
+ os=rtu
;;
f30[01]-fujitsu | f700-fujitsu)
- os=-uxpv
+ os=uxpv
;;
*-rom68k)
- os=-coff
+ os=coff
;;
*-*bug)
- os=-coff
+ os=coff
;;
*-apple)
- os=-macos
+ os=macos
;;
*-atari*)
- os=-mint
+ os=mint
+ ;;
+ *-wrs)
+ os=vxworks
;;
*)
- os=-none
+ 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.
-vendor=unknown
-case $basic_machine in
- *-unknown)
- case $os in
- -riscix*)
+case $vendor in
+ unknown)
+ case $cpu-$os in
+ *-riscix*)
vendor=acorn
;;
- -sunos*)
+ *-sunos*)
vendor=sun
;;
- -cnk*|-aix*)
+ *-cnk* | *-aix*)
vendor=ibm
;;
- -beos*)
+ *-beos*)
vendor=be
;;
- -hpux*)
+ *-hpux*)
vendor=hp
;;
- -mpeix*)
+ *-mpeix*)
vendor=hp
;;
- -hiux*)
+ *-hiux*)
vendor=hitachi
;;
- -unos*)
+ *-unos*)
vendor=crds
;;
- -dgux*)
+ *-dgux*)
vendor=dg
;;
- -luna*)
+ *-luna*)
vendor=omron
;;
- -genix*)
+ *-genix*)
vendor=ns
;;
- -mvs* | -opened*)
+ *-clix*)
+ vendor=intergraph
+ ;;
+ *-mvs* | *-opened*)
+ vendor=ibm
+ ;;
+ *-os400*)
vendor=ibm
;;
- -os400*)
+ s390-* | s390x-*)
vendor=ibm
;;
- -ptx*)
+ *-ptx*)
vendor=sequent
;;
- -tpf*)
+ *-tpf*)
vendor=ibm
;;
- -vxsim* | -vxworks* | -windiss*)
+ *-vxsim* | *-vxworks* | *-windiss*)
vendor=wrs
;;
- -aux*)
+ *-aux*)
vendor=apple
;;
- -hms*)
+ *-hms*)
vendor=hitachi
;;
- -mpw* | -macos*)
+ *-mpw* | *-macos*)
vendor=apple
;;
- -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*)
+ *-*mint | *-mint[0-9]* | *-*MiNT | *-MiNT[0-9]*)
vendor=atari
;;
- -vos*)
+ *-vos*)
vendor=stratus
;;
esac
- basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"`
;;
esac
-echo $basic_machine$os
+echo "$cpu-$vendor-${kernel:+$kernel-}$os"
exit
# Local variables:
-# eval: (add-hook 'write-file-hooks 'time-stamp)
+# eval: (add-hook 'before-save-hook 'time-stamp)
# time-stamp-start: "timestamp='"
# time-stamp-format: "%:y-%02m-%02d"
# time-stamp-end: "'"
diff --git a/deps/jemalloc/configure b/deps/jemalloc/configure
index dace1f69e..6d6264750 100755
--- a/deps/jemalloc/configure
+++ b/deps/jemalloc/configure
@@ -1,9 +1,10 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69.
+# Generated by GNU Autoconf 2.71.
#
#
-# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc.
+# Copyright (C) 1992-1996, 1998-2017, 2020-2021 Free Software Foundation,
+# Inc.
#
#
# This configure script is free software; the Free Software Foundation
@@ -14,14 +15,16 @@
# Be more Bourne compatible
DUALCASE=1; export DUALCASE # for MKS sh
-if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+as_nop=:
+if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1
+then :
emulate sh
NULLCMD=:
# Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
# is contrary to our usage. Disable this feature.
alias -g '${1+"$@"}'='"$@"'
setopt NO_GLOB_SUBST
-else
+else $as_nop
case `(set -o) 2>/dev/null` in #(
*posix*) :
set -o posix ;; #(
@@ -31,46 +34,46 @@ esac
fi
+
+# Reset variables that may have inherited troublesome values from
+# the environment.
+
+# IFS needs to be set, to space, tab, and newline, in precisely that order.
+# (If _AS_PATH_WALK were called with IFS unset, it would have the
+# side effect of setting IFS to empty, thus disabling word splitting.)
+# Quoting is to prevent editors from complaining about space-tab.
as_nl='
'
export as_nl
-# Printing a long string crashes Solaris 7 /usr/bin/printf.
-as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
-as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
-as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
-# Prefer a ksh shell builtin over an external printf program on Solaris,
-# but without wasting forks for bash or zsh.
-if test -z "$BASH_VERSION$ZSH_VERSION" \
- && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
- as_echo='print -r --'
- as_echo_n='print -rn --'
-elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
- as_echo='printf %s\n'
- as_echo_n='printf %s'
-else
- if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
- as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
- as_echo_n='/usr/ucb/echo -n'
- else
- as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
- as_echo_n_body='eval
- arg=$1;
- case $arg in #(
- *"$as_nl"*)
- expr "X$arg" : "X\\(.*\\)$as_nl";
- arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
- esac;
- expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
- '
- export as_echo_n_body
- as_echo_n='sh -c $as_echo_n_body as_echo'
- fi
- export as_echo_body
- as_echo='sh -c $as_echo_body as_echo'
-fi
+IFS=" "" $as_nl"
+
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# Ensure predictable behavior from utilities with locale-dependent output.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# We cannot yet rely on "unset" to work, but we need these variables
+# to be unset--not just set to an empty or harmless value--now, to
+# avoid bugs in old shells (e.g. pre-3.0 UWIN ksh). This construct
+# also avoids known problems related to "unset" and subshell syntax
+# in other old shells (e.g. bash 2.01 and pdksh 5.2.14).
+for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH
+do eval test \${$as_var+y} \
+ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+
+# Ensure that fds 0, 1, and 2 are open.
+if (exec 3>&0) 2>/dev/null; then :; else exec 0</dev/null; fi
+if (exec 3>&1) 2>/dev/null; then :; else exec 1>/dev/null; fi
+if (exec 3>&2) ; then :; else exec 2>/dev/null; fi
# The user is always right.
-if test "${PATH_SEPARATOR+set}" != set; then
+if ${PATH_SEPARATOR+false} :; then
PATH_SEPARATOR=:
(PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
(PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
@@ -79,13 +82,6 @@ if test "${PATH_SEPARATOR+set}" != set; then
fi
-# IFS
-# We need space, tab and new line, in precisely that order. Quoting is
-# there to prevent editors from complaining about space-tab.
-# (If _AS_PATH_WALK were called with IFS unset, it would disable word
-# splitting by setting IFS to empty value.)
-IFS=" "" $as_nl"
-
# Find who we are. Look in the path if we contain no directory separator.
as_myself=
case $0 in #((
@@ -94,8 +90,12 @@ case $0 in #((
for as_dir in $PATH
do
IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
- test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
+ test -r "$as_dir$0" && as_myself=$as_dir$0 && break
done
IFS=$as_save_IFS
@@ -107,30 +107,10 @@ if test "x$as_myself" = x; then
as_myself=$0
fi
if test ! -f "$as_myself"; then
- $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+ printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
exit 1
fi
-# Unset variables that we do not need and which cause bugs (e.g. in
-# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1"
-# suppresses any "Segmentation fault" message there. '((' could
-# trigger a bug in pdksh 5.2.14.
-for as_var in BASH_ENV ENV MAIL MAILPATH
-do eval test x\${$as_var+set} = xset \
- && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
-done
-PS1='$ '
-PS2='> '
-PS4='+ '
-
-# NLS nuisances.
-LC_ALL=C
-export LC_ALL
-LANGUAGE=C
-export LANGUAGE
-
-# CDPATH.
-(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
# Use a proper internal environment variable to ensure we don't fall
# into an infinite loop, continuously re-executing ourselves.
@@ -152,20 +132,22 @@ 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'.
-$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
-as_fn_exit 255
+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="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then :
+ as_bourne_compatible="as_nop=:
+if test \${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1
+then :
emulate sh
NULLCMD=:
# Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which
# is contrary to our usage. Disable this feature.
alias -g '\${1+\"\$@\"}'='\"\$@\"'
setopt NO_GLOB_SUBST
-else
+else \$as_nop
case \`(set -o) 2>/dev/null\` in #(
*posix*) :
set -o posix ;; #(
@@ -185,42 +167,53 @@ as_fn_success || { exitcode=1; echo as_fn_success failed.; }
as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; }
as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; }
as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; }
-if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then :
+if ( set x; as_fn_ret_success y && test x = \"\$1\" )
+then :
-else
+else \$as_nop
exitcode=1; echo positional parameters were not saved.
fi
test x\$exitcode = x0 || exit 1
+blah=\$(echo \$(echo blah))
+test x\"\$blah\" = xblah || exit 1
test -x / || exit 1"
as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO
as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO
eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" &&
test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1
test \$(( 1 + 1 )) = 2 || exit 1"
- if (eval "$as_required") 2>/dev/null; then :
+ if (eval "$as_required") 2>/dev/null
+then :
as_have_required=yes
-else
+else $as_nop
as_have_required=no
fi
- if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then :
+ if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null
+then :
-else
+else $as_nop
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
as_found=false
for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH
do
IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
as_found=:
case $as_dir in #(
/*)
for as_base in sh bash ksh sh5; do
# Try only shells that exist, to save several forks.
- as_shell=$as_dir/$as_base
+ as_shell=$as_dir$as_base
if { test -f "$as_shell" || test -f "$as_shell.exe"; } &&
- { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then :
+ as_run=a "$as_shell" -c "$as_bourne_compatible""$as_required" 2>/dev/null
+then :
CONFIG_SHELL=$as_shell as_have_required=yes
- if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then :
+ if as_run=a "$as_shell" -c "$as_bourne_compatible""$as_suggested" 2>/dev/null
+then :
break 2
fi
fi
@@ -228,14 +221,21 @@ fi
esac
as_found=false
done
-$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } &&
- { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then :
- CONFIG_SHELL=$SHELL as_have_required=yes
-fi; }
IFS=$as_save_IFS
+if $as_found
+then :
+else $as_nop
+ 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
- if test "x$CONFIG_SHELL" != x; then :
+
+ if test "x$CONFIG_SHELL" != x
+then :
export CONFIG_SHELL
# We cannot yet assume a decent shell, so we have to provide a
# neutralization value for shells without unset; and this also
@@ -253,18 +253,19 @@ 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'.
-$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2
+printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2
exit 255
fi
- if test x$as_have_required = xno; then :
- $as_echo "$0: This script requires a shell more modern than all"
- $as_echo "$0: the shells that I found on your system."
- if test x${ZSH_VERSION+set} = xset ; then
- $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should"
- $as_echo "$0: be upgraded to zsh 4.3.4 or later."
+ if test x$as_have_required = xno
+then :
+ printf "%s\n" "$0: This script requires a shell more modern than all"
+ printf "%s\n" "$0: the shells that I found on your system."
+ if test ${ZSH_VERSION+y} ; then
+ printf "%s\n" "$0: In particular, zsh $ZSH_VERSION has bugs and should"
+ printf "%s\n" "$0: be upgraded to zsh 4.3.4 or later."
else
- $as_echo "$0: Please tell bug-autoconf@gnu.org about your system,
+ printf "%s\n" "$0: Please tell bug-autoconf@gnu.org about your system,
$0: including any error possibly output before this
$0: message. Then install a modern shell, or manually run
$0: the script under such a shell if you do have one."
@@ -291,6 +292,7 @@ as_fn_unset ()
}
as_unset=as_fn_unset
+
# as_fn_set_status STATUS
# -----------------------
# Set $? to STATUS, without forking.
@@ -308,6 +310,14 @@ 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
# -------------
@@ -322,7 +332,7 @@ as_fn_mkdir_p ()
as_dirs=
while :; do
case $as_dir in #(
- *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+ *\'*) as_qdir=`printf "%s\n" "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
*) as_qdir=$as_dir;;
esac
as_dirs="'$as_qdir' $as_dirs"
@@ -331,7 +341,7 @@ $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
X"$as_dir" : 'X\(//\)[^/]' \| \
X"$as_dir" : 'X\(//\)$' \| \
X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
-$as_echo X"$as_dir" |
+printf "%s\n" X"$as_dir" |
sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
s//\1/
q
@@ -370,12 +380,13 @@ as_fn_executable_p ()
# advantage of any shell optimizations that allow amortized linear growth over
# repeated appends, instead of the typical quadratic growth present in naive
# implementations.
-if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null
+then :
eval 'as_fn_append ()
{
eval $1+=\$2
}'
-else
+else $as_nop
as_fn_append ()
{
eval $1=\$$1\$2
@@ -387,18 +398,27 @@ fi # as_fn_append
# Perform arithmetic evaluation on the ARGs, and store the result in the
# global $as_val. Take advantage of shells that can avoid forks. The arguments
# must be portable across $(()) and expr.
-if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null
+then :
eval 'as_fn_arith ()
{
as_val=$(( $* ))
}'
-else
+else $as_nop
as_fn_arith ()
{
as_val=`expr "$@" || test $? -eq 1`
}
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]
# ----------------------------------------
@@ -410,9 +430,9 @@ as_fn_error ()
as_status=$1; test $as_status -eq 0 && as_status=1
if test "$4"; then
as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
- $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
fi
- $as_echo "$as_me: error: $2" >&2
+ printf "%s\n" "$as_me: error: $2" >&2
as_fn_exit $as_status
} # as_fn_error
@@ -439,7 +459,7 @@ as_me=`$as_basename -- "$0" ||
$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
X"$0" : 'X\(//\)$' \| \
X"$0" : 'X\(/\)' \| . 2>/dev/null ||
-$as_echo X/"$0" |
+printf "%s\n" X/"$0" |
sed '/^.*\/\([^/][^/]*\)\/*$/{
s//\1/
q
@@ -483,7 +503,7 @@ as_cr_alnum=$as_cr_Letters$as_cr_digits
s/-\n.*//
' >$as_me.lineno &&
chmod +x "$as_me.lineno" ||
- { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; }
+ { printf "%s\n" "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; }
# If we had to re-execute with $CONFIG_SHELL, we're ensured to have
# already done that, so ensure we don't try to do so again and fall
@@ -497,6 +517,10 @@ as_cr_alnum=$as_cr_Letters$as_cr_digits
exit
}
+
+# Determine whether it's possible to make 'echo' print without a newline.
+# These variables are no longer used directly by Autoconf, but are AC_SUBSTed
+# for compatibility with existing Makefiles.
ECHO_C= ECHO_N= ECHO_T=
case `echo -n x` in #(((((
-n*)
@@ -510,6 +534,13 @@ case `echo -n x` in #(((((
ECHO_N='-n';;
esac
+# For backward compatibility with old third-party macros, we provide
+# the shell variables $as_echo and $as_echo_n. New code should use
+# AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively.
+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
@@ -575,50 +606,46 @@ MFLAGS=
MAKEFLAGS=
# Identity of this package.
-PACKAGE_NAME=
-PACKAGE_TARNAME=
-PACKAGE_VERSION=
-PACKAGE_STRING=
-PACKAGE_BUGREPORT=
-PACKAGE_URL=
+PACKAGE_NAME=''
+PACKAGE_TARNAME=''
+PACKAGE_VERSION=''
+PACKAGE_STRING=''
+PACKAGE_BUGREPORT=''
+PACKAGE_URL=''
ac_unique_file="Makefile.in"
# Factoring default headers for most tests.
ac_includes_default="\
-#include <stdio.h>
-#ifdef HAVE_SYS_TYPES_H
-# include <sys/types.h>
-#endif
-#ifdef HAVE_SYS_STAT_H
-# include <sys/stat.h>
+#include <stddef.h>
+#ifdef HAVE_STDIO_H
+# include <stdio.h>
#endif
-#ifdef STDC_HEADERS
+#ifdef HAVE_STDLIB_H
# include <stdlib.h>
-# include <stddef.h>
-#else
-# ifdef HAVE_STDLIB_H
-# include <stdlib.h>
-# endif
#endif
#ifdef HAVE_STRING_H
-# if !defined STDC_HEADERS && defined HAVE_MEMORY_H
-# include <memory.h>
-# endif
# include <string.h>
#endif
-#ifdef HAVE_STRINGS_H
-# include <strings.h>
-#endif
#ifdef HAVE_INTTYPES_H
# include <inttypes.h>
#endif
#ifdef HAVE_STDINT_H
# include <stdint.h>
#endif
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+#ifdef HAVE_SYS_TYPES_H
+# include <sys/types.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+# include <sys/stat.h>
+#endif
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif"
+ac_header_c_list=
ac_subst_vars='LTLIBOBJS
LIBOBJS
cfgoutputs_out
@@ -630,6 +657,8 @@ enable_zone_allocator
enable_tls
enable_lazy_lock
libdl
+enable_uaf_detection
+enable_opt_size_checks
enable_opt_safety_checks
enable_readlinkat
enable_log
@@ -697,13 +726,12 @@ build_os
build_vendor
build_cpu
build
-EGREP
-GREP
EXTRA_CXXFLAGS
SPECIFIED_CXXFLAGS
CONFIGURE_CXXFLAGS
enable_cxx
HAVE_CXX14
+HAVE_CXX17
ac_ct_CXX
CXXFLAGS
CXX
@@ -804,7 +832,10 @@ enable_cache_oblivious
enable_log
enable_readlinkat
enable_opt_safety_checks
+enable_opt_size_checks
+enable_uaf_detection
with_lg_quantum
+with_lg_slab_maxregs
with_lg_page
with_lg_hugepage
enable_libdl
@@ -893,8 +924,6 @@ do
*) ac_optarg=yes ;;
esac
- # Accept the important Cygnus configure options, so we can diagnose typos.
-
case $ac_dashdash$ac_option in
--)
ac_dashdash=yes ;;
@@ -935,9 +964,9 @@ 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=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'`
case $ac_user_opts in
*"
"enable_$ac_useropt"
@@ -961,9 +990,9 @@ 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=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'`
case $ac_user_opts in
*"
"enable_$ac_useropt"
@@ -1174,9 +1203,9 @@ 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=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'`
case $ac_user_opts in
*"
"with_$ac_useropt"
@@ -1190,9 +1219,9 @@ 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=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'`
+ ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'`
case $ac_user_opts in
*"
"with_$ac_useropt"
@@ -1236,9 +1265,9 @@ Try \`$0 --help' for more information"
*)
# FIXME: should be removed in autoconf 3.0.
- $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2
+ printf "%s\n" "$as_me: WARNING: you should use --build, --host, --target" >&2
expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&
- $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2
+ printf "%s\n" "$as_me: WARNING: invalid host type: $ac_option" >&2
: "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}"
;;
@@ -1254,7 +1283,7 @@ if test -n "$ac_unrecognized_opts"; then
case $enable_option_checking in
no) ;;
fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;;
- *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;;
+ *) printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;;
esac
fi
@@ -1318,7 +1347,7 @@ $as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
X"$as_myself" : 'X\(//\)[^/]' \| \
X"$as_myself" : 'X\(//\)$' \| \
X"$as_myself" : 'X\(/\)' \| . 2>/dev/null ||
-$as_echo X"$as_myself" |
+printf "%s\n" X"$as_myself" |
sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
s//\1/
q
@@ -1449,7 +1478,7 @@ Optional Features:
--enable-FEATURE[=ARG] include FEATURE [ARG=yes]
--disable-cxx Disable C++ integration
--enable-autogen Automatically regenerate configure output
- --enable-documentation Build documentation
+ --enable-doc Build documentation
--enable-shared Build shared libaries
--enable-static Build static libaries
--enable-debug Build debugging code
@@ -1471,6 +1500,11 @@ Optional Features:
--enable-opt-safety-checks
Perform certain low-overhead checks, even in opt
mode
+ --enable-opt-size-checks
+ Perform sized-deallocation argument checks, even in
+ opt mode
+ --enable-uaf-detection Allow sampled junk-filling on deallocation to detect
+ use-after-free
--disable-libdl Do not use libdl
--disable-syscall Disable use of syscall(2)
--enable-lazy-lock Enable lazy locking (only lock when multi-threaded)
@@ -1503,6 +1537,9 @@ Optional Packages:
dynamically linking
--with-lg-quantum=<lg-quantum>
Base 2 log of minimum allocation alignment
+ --with-lg-slab-maxregs=<lg-slab-maxregs>
+ Base 2 log of maximum number of regions in a slab
+ (used with malloc_conf slab_sizes)
--with-lg-page=<lg-page>
Base 2 log of system page size
--with-lg-hugepage=<lg-hugepage>
@@ -1539,9 +1576,9 @@ if test "$ac_init_help" = "recursive"; then
case "$ac_dir" in
.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
*)
- ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+ ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'`
# A ".." for each directory in $ac_dir_suffix.
- ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+ ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
case $ac_top_builddir_sub in
"") ac_top_builddir_sub=. ac_top_build_prefix= ;;
*) ac_top_build_prefix=$ac_top_builddir_sub/ ;;
@@ -1569,7 +1606,8 @@ esac
ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
cd "$ac_dir" || { ac_status=$?; continue; }
- # Check for guested configure.
+ # Check for configure.gnu first; this name is used for a wrapper for
+ # Metaconfig's "Configure" on case-insensitive file systems.
if test -f "$ac_srcdir/configure.gnu"; then
echo &&
$SHELL "$ac_srcdir/configure.gnu" --help=recursive
@@ -1577,7 +1615,7 @@ ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix
echo &&
$SHELL "$ac_srcdir/configure" --help=recursive
else
- $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2
+ printf "%s\n" "$as_me: WARNING: no configuration information is in $ac_dir" >&2
fi || ac_status=$?
cd "$ac_pwd" || { ac_status=$?; break; }
done
@@ -1587,9 +1625,9 @@ test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
configure
-generated by GNU Autoconf 2.69
+generated by GNU Autoconf 2.71
-Copyright (C) 2012 Free Software Foundation, Inc.
+Copyright (C) 2021 Free Software Foundation, Inc.
This configure script is free software; the Free Software Foundation
gives unlimited permission to copy, distribute and modify it.
_ACEOF
@@ -1606,14 +1644,14 @@ fi
ac_fn_c_try_compile ()
{
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
- rm -f conftest.$ac_objext
+ rm -f conftest.$ac_objext conftest.beam
if { { ac_try="$ac_compile"
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\""
-$as_echo "$ac_try_echo"; } >&5
+printf "%s\n" "$ac_try_echo"; } >&5
(eval "$ac_compile") 2>conftest.err
ac_status=$?
if test -s conftest.err; then
@@ -1621,14 +1659,15 @@ $as_echo "$ac_try_echo"; } >&5
cat conftest.er1 >&5
mv -f conftest.er1 conftest.err
fi
- $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; } && {
test -z "$ac_c_werror_flag" ||
test ! -s conftest.err
- } && test -s conftest.$ac_objext; then :
+ } && test -s conftest.$ac_objext
+then :
ac_retval=0
-else
- $as_echo "$as_me: failed program was:" >&5
+else $as_nop
+ printf "%s\n" "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
ac_retval=1
@@ -1650,7 +1689,7 @@ case "(($ac_try" in
*) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
-$as_echo "$ac_try_echo"; } >&5
+printf "%s\n" "$ac_try_echo"; } >&5
(eval "$ac_cpp conftest.$ac_ext") 2>conftest.err
ac_status=$?
if test -s conftest.err; then
@@ -1658,14 +1697,15 @@ $as_echo "$ac_try_echo"; } >&5
cat conftest.er1 >&5
mv -f conftest.er1 conftest.err
fi
- $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ 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 :
+ }
+then :
ac_retval=0
-else
- $as_echo "$as_me: failed program was:" >&5
+else $as_nop
+ printf "%s\n" "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
ac_retval=1
@@ -1681,14 +1721,14 @@ fi
ac_fn_cxx_try_compile ()
{
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
- rm -f conftest.$ac_objext
+ rm -f conftest.$ac_objext conftest.beam
if { { ac_try="$ac_compile"
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\""
-$as_echo "$ac_try_echo"; } >&5
+printf "%s\n" "$ac_try_echo"; } >&5
(eval "$ac_compile") 2>conftest.err
ac_status=$?
if test -s conftest.err; then
@@ -1696,14 +1736,15 @@ $as_echo "$ac_try_echo"; } >&5
cat conftest.er1 >&5
mv -f conftest.er1 conftest.err
fi
- $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; } && {
test -z "$ac_cxx_werror_flag" ||
test ! -s conftest.err
- } && test -s conftest.$ac_objext; then :
+ } && test -s conftest.$ac_objext
+then :
ac_retval=0
-else
- $as_echo "$as_me: failed program was:" >&5
+else $as_nop
+ printf "%s\n" "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
ac_retval=1
@@ -1719,14 +1760,14 @@ fi
ac_fn_c_try_link ()
{
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
- rm -f conftest.$ac_objext conftest$ac_exeext
+ rm -f conftest.$ac_objext conftest.beam conftest$ac_exeext
if { { ac_try="$ac_link"
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\""
-$as_echo "$ac_try_echo"; } >&5
+printf "%s\n" "$ac_try_echo"; } >&5
(eval "$ac_link") 2>conftest.err
ac_status=$?
if test -s conftest.err; then
@@ -1734,17 +1775,18 @@ $as_echo "$ac_try_echo"; } >&5
cat conftest.er1 >&5
mv -f conftest.er1 conftest.err
fi
- $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; } && {
test -z "$ac_c_werror_flag" ||
test ! -s conftest.err
} && test -s conftest$ac_exeext && {
test "$cross_compiling" = yes ||
test -x conftest$ac_exeext
- }; then :
+ }
+then :
ac_retval=0
-else
- $as_echo "$as_me: failed program was:" >&5
+else $as_nop
+ printf "%s\n" "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
ac_retval=1
@@ -1761,8 +1803,8 @@ fi
# ac_fn_c_try_run LINENO
# ----------------------
-# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes
-# that executables *can* be run.
+# Try to run conftest.$ac_ext, and return whether this succeeded. Assumes that
+# executables *can* be run.
ac_fn_c_try_run ()
{
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
@@ -1772,25 +1814,26 @@ case "(($ac_try" in
*) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
-$as_echo "$ac_try_echo"; } >&5
+printf "%s\n" "$ac_try_echo"; } >&5
(eval "$ac_link") 2>&5
ac_status=$?
- $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; } && { ac_try='./conftest$ac_exeext'
{ { 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\""
-$as_echo "$ac_try_echo"; } >&5
+printf "%s\n" "$ac_try_echo"; } >&5
(eval "$ac_try") 2>&5
ac_status=$?
- $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
- test $ac_status = 0; }; }; then :
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }; }
+then :
ac_retval=0
-else
- $as_echo "$as_me: program exited with status $ac_status" >&5
- $as_echo "$as_me: failed program was:" >&5
+else $as_nop
+ 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
@@ -1808,26 +1851,28 @@ fi
ac_fn_c_check_header_compile ()
{
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
-$as_echo_n "checking for $2... " >&6; }
-if eval \${$3+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+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
/* end confdefs.h. */
$4
#include <$2>
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
eval "$3=yes"
-else
+else $as_nop
eval "$3=no"
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
eval ac_res=\$$3
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-$as_echo "$ac_res" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf "%s\n" "$ac_res" >&6; }
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
} # ac_fn_c_check_header_compile
@@ -1846,7 +1891,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$4
int
-main ()
+main (void)
{
static int test_array [1 - 2 * !(($2) >= 0)];
test_array [0] = 0;
@@ -1856,14 +1901,15 @@ return test_array [0];
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
ac_lo=0 ac_mid=0
while :; do
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$4
int
-main ()
+main (void)
{
static int test_array [1 - 2 * !(($2) <= $ac_mid)];
test_array [0] = 0;
@@ -1873,9 +1919,10 @@ return test_array [0];
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
ac_hi=$ac_mid; break
-else
+else $as_nop
as_fn_arith $ac_mid + 1 && ac_lo=$as_val
if test $ac_lo -le $ac_mid; then
ac_lo= ac_hi=
@@ -1883,14 +1930,14 @@ else
fi
as_fn_arith 2 '*' $ac_mid + 1 && ac_mid=$as_val
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
done
-else
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$4
int
-main ()
+main (void)
{
static int test_array [1 - 2 * !(($2) < 0)];
test_array [0] = 0;
@@ -1900,14 +1947,15 @@ return test_array [0];
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
ac_hi=-1 ac_mid=-1
while :; do
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$4
int
-main ()
+main (void)
{
static int test_array [1 - 2 * !(($2) >= $ac_mid)];
test_array [0] = 0;
@@ -1917,9 +1965,10 @@ return test_array [0];
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
ac_lo=$ac_mid; break
-else
+else $as_nop
as_fn_arith '(' $ac_mid ')' - 1 && ac_hi=$as_val
if test $ac_mid -le $ac_hi; then
ac_lo= ac_hi=
@@ -1927,14 +1976,14 @@ else
fi
as_fn_arith 2 '*' $ac_mid && ac_mid=$as_val
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
done
-else
+else $as_nop
ac_lo= ac_hi=
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
# Binary search between lo and hi bounds.
while test "x$ac_lo" != "x$ac_hi"; do
as_fn_arith '(' $ac_hi - $ac_lo ')' / 2 + $ac_lo && ac_mid=$as_val
@@ -1942,7 +1991,7 @@ while test "x$ac_lo" != "x$ac_hi"; do
/* end confdefs.h. */
$4
int
-main ()
+main (void)
{
static int test_array [1 - 2 * !(($2) <= $ac_mid)];
test_array [0] = 0;
@@ -1952,12 +2001,13 @@ return test_array [0];
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
ac_hi=$ac_mid
-else
+else $as_nop
as_fn_arith '(' $ac_mid ')' + 1 && ac_lo=$as_val
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
done
case $ac_lo in #((
?*) eval "$3=\$ac_lo"; ac_retval=0 ;;
@@ -1967,12 +2017,12 @@ esac
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$4
-static long int longval () { return $2; }
-static unsigned long int ulongval () { return $2; }
+static long int longval (void) { return $2; }
+static unsigned long int ulongval (void) { return $2; }
#include <stdio.h>
#include <stdlib.h>
int
-main ()
+main (void)
{
FILE *f = fopen ("conftest.val", "w");
@@ -2000,9 +2050,10 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_run "$LINENO"; then :
+if ac_fn_c_try_run "$LINENO"
+then :
echo >>conftest.val; read $3 <conftest.val; ac_retval=0
-else
+else $as_nop
ac_retval=1
fi
rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
@@ -2015,104 +2066,18 @@ rm -f conftest.val
} # ac_fn_c_compute_int
-# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES
-# -------------------------------------------------------
-# Tests whether HEADER exists, giving a warning if it cannot be compiled using
-# the include files in INCLUDES and setting the cache variable VAR
-# accordingly.
-ac_fn_c_check_header_mongrel ()
-{
- as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
- if eval \${$3+:} false; then :
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
-$as_echo_n "checking for $2... " >&6; }
-if eval \${$3+:} false; then :
- $as_echo_n "(cached) " >&6
-fi
-eval ac_res=\$$3
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-$as_echo "$ac_res" >&6; }
-else
- # Is the header compilable?
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5
-$as_echo_n "checking $2 usability... " >&6; }
-cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
-$4
-#include <$2>
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
- ac_header_compiler=yes
-else
- ac_header_compiler=no
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5
-$as_echo "$ac_header_compiler" >&6; }
-
-# Is the header present?
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5
-$as_echo_n "checking $2 presence... " >&6; }
-cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
-#include <$2>
-_ACEOF
-if ac_fn_c_try_cpp "$LINENO"; then :
- ac_header_preproc=yes
-else
- ac_header_preproc=no
-fi
-rm -f conftest.err conftest.i conftest.$ac_ext
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5
-$as_echo "$ac_header_preproc" >&6; }
-
-# So? What about this header?
-case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #((
- yes:no: )
- { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5
-$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;}
- { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
-$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
- ;;
- no:yes:* )
- { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5
-$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;}
- { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5
-$as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;}
- { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5
-$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;}
- { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5
-$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;}
- { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5
-$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;}
- ;;
-esac
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
-$as_echo_n "checking for $2... " >&6; }
-if eval \${$3+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- eval "$3=\$ac_header_compiler"
-fi
-eval ac_res=\$$3
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-$as_echo "$ac_res" >&6; }
-fi
- eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
-
-} # ac_fn_c_check_header_mongrel
-
# ac_fn_c_check_func LINENO FUNC VAR
# ----------------------------------
# Tests whether FUNC exists, setting the cache variable VAR accordingly
ac_fn_c_check_func ()
{
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
-$as_echo_n "checking for $2... " >&6; }
-if eval \${$3+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+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
/* end confdefs.h. */
/* Define $2 to an innocuous variant, in case <limits.h> declares $2.
@@ -2120,16 +2085,9 @@ else
#define $2 innocuous_$2
/* System header to define __stub macros and hopefully few prototypes,
- which can conflict with char $2 (); below.
- Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
- <limits.h> exists even on freestanding compilers. */
-
-#ifdef __STDC__
-# include <limits.h>
-#else
-# include <assert.h>
-#endif
+ which can conflict with char $2 (); below. */
+#include <limits.h>
#undef $2
/* Override any GCC internal prototype to avoid an error.
@@ -2147,24 +2105,25 @@ choke me
#endif
int
-main ()
+main (void)
{
return $2 ();
;
return 0;
}
_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
+if ac_fn_c_try_link "$LINENO"
+then :
eval "$3=yes"
-else
+else $as_nop
eval "$3=no"
fi
-rm -f core conftest.err conftest.$ac_objext \
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
fi
eval ac_res=\$$3
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-$as_echo "$ac_res" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf "%s\n" "$ac_res" >&6; }
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
} # ac_fn_c_check_func
@@ -2176,17 +2135,18 @@ $as_echo "$ac_res" >&6; }
ac_fn_c_check_type ()
{
as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
-$as_echo_n "checking for $2... " >&6; }
-if eval \${$3+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5
+printf %s "checking for $2... " >&6; }
+if eval test \${$3+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
eval "$3=no"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$4
int
-main ()
+main (void)
{
if (sizeof ($2))
return 0;
@@ -2194,12 +2154,13 @@ if (sizeof ($2))
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$4
int
-main ()
+main (void)
{
if (sizeof (($2)))
return 0;
@@ -2207,29 +2168,50 @@ if (sizeof (($2)))
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
-else
+else $as_nop
eval "$3=yes"
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
eval ac_res=\$$3
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-$as_echo "$ac_res" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf "%s\n" "$ac_res" >&6; }
eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
} # ac_fn_c_check_type
+ac_configure_args_raw=
+for ac_arg
+do
+ case $ac_arg in
+ *\'*)
+ ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ esac
+ as_fn_append ac_configure_args_raw " '$ac_arg'"
+done
+
+case $ac_configure_args_raw in
+ *$as_nl*)
+ ac_safe_unquote= ;;
+ *)
+ ac_unsafe_z='|&;<>()$`\\"*?[ '' ' # This string ends in space, tab.
+ ac_unsafe_a="$ac_unsafe_z#~"
+ ac_safe_unquote="s/ '\\([^$ac_unsafe_a][^$ac_unsafe_z]*\\)'/ \\1/g"
+ ac_configure_args_raw=` printf "%s\n" "$ac_configure_args_raw" | sed "$ac_safe_unquote"`;;
+esac
+
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 $as_me, which was
-generated by GNU Autoconf 2.69. Invocation command line was
+generated by GNU Autoconf 2.71. Invocation command line was
- $ $0 $@
+ $ $0$ac_configure_args_raw
_ACEOF
exec 5>>config.log
@@ -2262,8 +2244,12 @@ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
- $as_echo "PATH: $as_dir"
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
+ printf "%s\n" "PATH: $as_dir"
done
IFS=$as_save_IFS
@@ -2298,7 +2284,7 @@ do
| -silent | --silent | --silen | --sile | --sil)
continue ;;
*\'*)
- ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;
esac
case $ac_pass in
1) as_fn_append ac_configure_args0 " '$ac_arg'" ;;
@@ -2333,11 +2319,13 @@ done
# WARNING: Use '\'' to represent an apostrophe within the trap.
# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.
trap 'exit_status=$?
+ # Sanitize IFS.
+ IFS=" "" $as_nl"
# Save into config.log some information that might help in debugging.
{
echo
- $as_echo "## ---------------- ##
+ printf "%s\n" "## ---------------- ##
## Cache variables. ##
## ---------------- ##"
echo
@@ -2348,8 +2336,8 @@ trap 'exit_status=$?
case $ac_val in #(
*${as_nl}*)
case $ac_var in #(
- *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
-$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+ *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
esac
case $ac_var in #(
_ | IFS | as_nl) ;; #(
@@ -2373,7 +2361,7 @@ $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
)
echo
- $as_echo "## ----------------- ##
+ printf "%s\n" "## ----------------- ##
## Output variables. ##
## ----------------- ##"
echo
@@ -2381,14 +2369,14 @@ $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
do
eval ac_val=\$$ac_var
case $ac_val in
- *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+ *\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
esac
- $as_echo "$ac_var='\''$ac_val'\''"
+ printf "%s\n" "$ac_var='\''$ac_val'\''"
done | sort
echo
if test -n "$ac_subst_files"; then
- $as_echo "## ------------------- ##
+ printf "%s\n" "## ------------------- ##
## File substitutions. ##
## ------------------- ##"
echo
@@ -2396,15 +2384,15 @@ $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
do
eval ac_val=\$$ac_var
case $ac_val in
- *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
+ *\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;
esac
- $as_echo "$ac_var='\''$ac_val'\''"
+ printf "%s\n" "$ac_var='\''$ac_val'\''"
done | sort
echo
fi
if test -s confdefs.h; then
- $as_echo "## ----------- ##
+ printf "%s\n" "## ----------- ##
## confdefs.h. ##
## ----------- ##"
echo
@@ -2412,8 +2400,8 @@ $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
echo
fi
test "$ac_signal" != 0 &&
- $as_echo "$as_me: caught signal $ac_signal"
- $as_echo "$as_me: exit $exit_status"
+ printf "%s\n" "$as_me: caught signal $ac_signal"
+ printf "%s\n" "$as_me: exit $exit_status"
} >&5
rm -f core *.core core.conftest.* &&
rm -f -r conftest* confdefs* conf$$* $ac_clean_files &&
@@ -2427,63 +2415,48 @@ ac_signal=0
# confdefs.h avoids OS command line length limits that DEFS can exceed.
rm -f -r conftest* confdefs.h
-$as_echo "/* confdefs.h */" > confdefs.h
+printf "%s\n" "/* confdefs.h */" > confdefs.h
# Predefined preprocessor variables.
-cat >>confdefs.h <<_ACEOF
-#define PACKAGE_NAME "$PACKAGE_NAME"
-_ACEOF
+printf "%s\n" "#define PACKAGE_NAME \"$PACKAGE_NAME\"" >>confdefs.h
-cat >>confdefs.h <<_ACEOF
-#define PACKAGE_TARNAME "$PACKAGE_TARNAME"
-_ACEOF
+printf "%s\n" "#define PACKAGE_TARNAME \"$PACKAGE_TARNAME\"" >>confdefs.h
-cat >>confdefs.h <<_ACEOF
-#define PACKAGE_VERSION "$PACKAGE_VERSION"
-_ACEOF
+printf "%s\n" "#define PACKAGE_VERSION \"$PACKAGE_VERSION\"" >>confdefs.h
-cat >>confdefs.h <<_ACEOF
-#define PACKAGE_STRING "$PACKAGE_STRING"
-_ACEOF
+printf "%s\n" "#define PACKAGE_STRING \"$PACKAGE_STRING\"" >>confdefs.h
-cat >>confdefs.h <<_ACEOF
-#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"
-_ACEOF
+printf "%s\n" "#define PACKAGE_BUGREPORT \"$PACKAGE_BUGREPORT\"" >>confdefs.h
-cat >>confdefs.h <<_ACEOF
-#define PACKAGE_URL "$PACKAGE_URL"
-_ACEOF
+printf "%s\n" "#define PACKAGE_URL \"$PACKAGE_URL\"" >>confdefs.h
# Let the site file select an alternate cache file if it wants to.
# Prefer an explicitly selected file to automatically selected ones.
-ac_site_file1=NONE
-ac_site_file2=NONE
if test -n "$CONFIG_SITE"; then
- # We do not want a PATH search for config.site.
- case $CONFIG_SITE in #((
- -*) ac_site_file1=./$CONFIG_SITE;;
- */*) ac_site_file1=$CONFIG_SITE;;
- *) ac_site_file1=./$CONFIG_SITE;;
- esac
+ ac_site_files="$CONFIG_SITE"
elif test "x$prefix" != xNONE; then
- ac_site_file1=$prefix/share/config.site
- ac_site_file2=$prefix/etc/config.site
+ ac_site_files="$prefix/share/config.site $prefix/etc/config.site"
else
- ac_site_file1=$ac_default_prefix/share/config.site
- ac_site_file2=$ac_default_prefix/etc/config.site
+ ac_site_files="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
fi
-for ac_site_file in "$ac_site_file1" "$ac_site_file2"
+
+for ac_site_file in $ac_site_files
do
- test "x$ac_site_file" = xNONE && continue
- if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5
-$as_echo "$as_me: loading site script $ac_site_file" >&6;}
+ case $ac_site_file in #(
+ */*) :
+ ;; #(
+ *) :
+ ac_site_file=./$ac_site_file ;;
+esac
+ if test -f "$ac_site_file" && test -r "$ac_site_file"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5
+printf "%s\n" "$as_me: loading site script $ac_site_file" >&6;}
sed 's/^/| /' "$ac_site_file" >&5
. "$ac_site_file" \
- || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-$as_echo "$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; }
fi
@@ -2493,19 +2466,650 @@ if test -r "$cache_file"; then
# Some versions of bash will fail to source /dev/null (special files
# actually), so we avoid doing that. DJGPP emulates it as a regular file.
if test /dev/null != "$cache_file" && test -f "$cache_file"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5
-$as_echo "$as_me: loading cache $cache_file" >&6;}
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5
+printf "%s\n" "$as_me: loading cache $cache_file" >&6;}
case $cache_file in
[\\/]* | ?:[\\/]* ) . "$cache_file";;
*) . "./$cache_file";;
esac
fi
else
- { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5
-$as_echo "$as_me: creating cache $cache_file" >&6;}
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5
+printf "%s\n" "$as_me: creating cache $cache_file" >&6;}
>$cache_file
fi
+# Test code for whether the C compiler supports C89 (global declarations)
+ac_c_conftest_c89_globals='
+/* Does the compiler advertise C89 conformance?
+ Do not test the value of __STDC__, because some compilers set it to 0
+ while being otherwise adequately conformant. */
+#if !defined __STDC__
+# error "Compiler does not advertise C89 conformance"
+#endif
+
+#include <stddef.h>
+#include <stdarg.h>
+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;
+{
+ return p[i];
+}
+static char *f (char * (*g) (char **, int), char **p, ...)
+{
+ char *s;
+ va_list v;
+ va_start (v,p);
+ s = g (p, va_arg (v,int));
+ va_end (v);
+ return s;
+}
+
+/* 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
+ as an "x". The following induces an error, until -std is added to get
+ proper ANSI mode. Curiously \x00 != x always comes out true, for an
+ array size at least. It is necessary to write \x00 == 0 to get something
+ that is true only with -std. */
+int osf4_cc_array ['\''\x00'\'' == 0 ? 1 : -1];
+
+/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
+ inside strings and character constants. */
+#define FOO(x) '\''x'\''
+int xlc6_cc_array[FOO(a) == '\''x'\'' ? 1 : -1];
+
+int test (int i, double x);
+struct s1 {int (*f) (int a);};
+struct s2 {int (*f) (double a);};
+int pairnames (int, char **, int *(*)(struct buf *, struct stat *, int),
+ int, int);'
+
+# Test code for whether the C compiler supports C89 (body of main).
+ac_c_conftest_c89_main='
+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?
+#if !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L
+# error "Compiler does not advertise C99 conformance"
+#endif
+
+#include <stdbool.h>
+extern int puts (const char *);
+extern int printf (const char *, ...);
+extern int dprintf (int, const char *, ...);
+extern void *malloc (size_t);
+
+// Check varargs macros. These examples are taken from C99 6.10.3.5.
+// dprintf is used instead of fprintf to avoid needing to declare
+// FILE and stderr.
+#define debug(...) dprintf (2, __VA_ARGS__)
+#define showlist(...) puts (#__VA_ARGS__)
+#define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__))
+static void
+test_varargs_macros (void)
+{
+ int x = 1234;
+ int y = 5678;
+ debug ("Flag");
+ debug ("X = %d\n", x);
+ showlist (The first, second, and third items.);
+ report (x>y, "x is %d but y is %d", x, y);
+}
+
+// Check long long types.
+#define BIG64 18446744073709551615ull
+#define BIG32 4294967295ul
+#define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0)
+#if !BIG_OK
+ #error "your preprocessor is broken"
+#endif
+#if BIG_OK
+#else
+ #error "your preprocessor is broken"
+#endif
+static long long int bignum = -9223372036854775807LL;
+static unsigned long long int ubignum = BIG64;
+
+struct incomplete_array
+{
+ int datasize;
+ double data[];
+};
+
+struct named_init {
+ int number;
+ const wchar_t *name;
+ double average;
+};
+
+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)
+ continue;
+ return 0;
+}
+
+// Check varargs and va_copy.
+static bool
+test_varargs (const char *format, ...)
+{
+ va_list args;
+ va_start (args, format);
+ va_list args_copy;
+ va_copy (args_copy, args);
+
+ const char *str = "";
+ int number = 0;
+ float fnumber = 0;
+
+ while (*format)
+ {
+ switch (*format++)
+ {
+ case '\''s'\'': // string
+ str = va_arg (args_copy, const char *);
+ break;
+ case '\''d'\'': // int
+ number = va_arg (args_copy, int);
+ break;
+ case '\''f'\'': // float
+ fnumber = va_arg (args_copy, double);
+ break;
+ default:
+ break;
+ }
+ }
+ va_end (args_copy);
+ va_end (args);
+
+ return *str && number && fnumber;
+}
+'
+
+# Test code for whether the C compiler supports C99 (body of main).
+ac_c_conftest_c99_main='
+ // Check bool.
+ _Bool success = false;
+ success |= (argc != 0);
+
+ // Check restrict.
+ if (test_restrict ("String literal") == 0)
+ success = true;
+ char *restrict newvar = "Another string";
+
+ // Check varargs.
+ success &= test_varargs ("s, d'\'' f .", "string", 65, 34.234);
+ test_varargs_macros ();
+
+ // Check flexible array members.
+ struct incomplete_array *ia =
+ malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10));
+ ia->datasize = 10;
+ for (int i = 0; i < ia->datasize; ++i)
+ ia->data[i] = i * 1.234;
+
+ // Check named initializers.
+ struct named_init ni = {
+ .number = 34,
+ .name = L"Test wide string",
+ .average = 543.34343,
+ };
+
+ ni.number = 58;
+
+ int dynamic_array[ni.number];
+ dynamic_array[0] = argv[0][0];
+ dynamic_array[ni.number - 1] = 543;
+
+ // work around unused variable warnings
+ ok |= (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == '\''x'\''
+ || dynamic_array[ni.number - 1] != 543);
+'
+
+# Test code for whether the C compiler supports C11 (global declarations)
+ac_c_conftest_c11_globals='
+// Does the compiler advertise C11 conformance?
+#if !defined __STDC_VERSION__ || __STDC_VERSION__ < 201112L
+# error "Compiler does not advertise C11 conformance"
+#endif
+
+// Check _Alignas.
+char _Alignas (double) aligned_as_double;
+char _Alignas (0) no_special_alignment;
+extern char aligned_as_int;
+char _Alignas (0) _Alignas (int) aligned_as_int;
+
+// Check _Alignof.
+enum
+{
+ int_alignment = _Alignof (int),
+ int_array_alignment = _Alignof (int[100]),
+ char_alignment = _Alignof (char)
+};
+_Static_assert (0 < -_Alignof (int), "_Alignof is signed");
+
+// Check _Noreturn.
+int _Noreturn does_not_return (void) { for (;;) continue; }
+
+// Check _Static_assert.
+struct test_static_assert
+{
+ int x;
+ _Static_assert (sizeof (int) <= sizeof (long int),
+ "_Static_assert does not work in struct");
+ long int y;
+};
+
+// Check UTF-8 literals.
+#define u8 syntax error!
+char const utf8_literal[] = u8"happens to be ASCII" "another string";
+
+// Check duplicate typedefs.
+typedef long *long_ptr;
+typedef long int *long_ptr;
+typedef long_ptr long_ptr;
+
+// Anonymous structures and unions -- taken from C11 6.7.2.1 Example 1.
+struct anonymous
+{
+ union {
+ struct { int i; int j; };
+ struct { int k; long int l; } w;
+ };
+ int m;
+} v1;
+'
+
+# Test code for whether the C compiler supports C11 (body of main).
+ac_c_conftest_c11_main='
+ _Static_assert ((offsetof (struct anonymous, i)
+ == offsetof (struct anonymous, w.k)),
+ "Anonymous union alignment botch");
+ v1.i = 2;
+ v1.w.k = 5;
+ ok |= v1.i != 5;
+'
+
+# Test code for whether the C compiler supports C11 (complete).
+ac_c_conftest_c11_program="${ac_c_conftest_c89_globals}
+${ac_c_conftest_c99_globals}
+${ac_c_conftest_c11_globals}
+
+int
+main (int argc, char **argv)
+{
+ int ok = 0;
+ ${ac_c_conftest_c89_main}
+ ${ac_c_conftest_c99_main}
+ ${ac_c_conftest_c11_main}
+ return ok;
+}
+"
+
+# Test code for whether the C compiler supports C99 (complete).
+ac_c_conftest_c99_program="${ac_c_conftest_c89_globals}
+${ac_c_conftest_c99_globals}
+
+int
+main (int argc, char **argv)
+{
+ int ok = 0;
+ ${ac_c_conftest_c89_main}
+ ${ac_c_conftest_c99_main}
+ return ok;
+}
+"
+
+# Test code for whether the C compiler supports C89 (complete).
+ac_c_conftest_c89_program="${ac_c_conftest_c89_globals}
+
+int
+main (int argc, char **argv)
+{
+ int ok = 0;
+ ${ac_c_conftest_c89_main}
+ return ok;
+}
+"
+
+# Test code for whether the C++ compiler supports C++98 (global declarations)
+ac_cxx_conftest_cxx98_globals='
+// Does the compiler advertise C++98 conformance?
+#if !defined __cplusplus || __cplusplus < 199711L
+# error "Compiler does not advertise C++98 conformance"
+#endif
+
+// These inclusions are to reject old compilers that
+// lack the unsuffixed header files.
+#include <cstdlib>
+#include <exception>
+
+// <cassert> and <cstring> are *not* freestanding headers in C++98.
+extern void assert (int);
+namespace std {
+ extern int strcmp (const char *, const char *);
+}
+
+// Namespaces, exceptions, and templates were all added after "C++ 2.0".
+using std::exception;
+using std::strcmp;
+
+namespace {
+
+void test_exception_syntax()
+{
+ try {
+ throw "test";
+ } catch (const char *s) {
+ // Extra parentheses suppress a warning when building autoconf itself,
+ // due to lint rules shared with more typical C programs.
+ assert (!(strcmp) (s, "test"));
+ }
+}
+
+template <typename T> struct test_template
+{
+ T const val;
+ explicit test_template(T t) : val(t) {}
+ template <typename U> T add(U u) { return static_cast<T>(u) + val; }
+};
+
+} // anonymous namespace
+'
+
+# Test code for whether the C++ compiler supports C++98 (body of main)
+ac_cxx_conftest_cxx98_main='
+ assert (argc);
+ assert (! argv[0]);
+{
+ test_exception_syntax ();
+ test_template<double> tt (2.0);
+ assert (tt.add (4) == 6.0);
+ assert (true && !false);
+}
+'
+
+# Test code for whether the C++ compiler supports C++11 (global declarations)
+ac_cxx_conftest_cxx11_globals='
+// Does the compiler advertise C++ 2011 conformance?
+#if !defined __cplusplus || __cplusplus < 201103L
+# error "Compiler does not advertise C++11 conformance"
+#endif
+
+namespace cxx11test
+{
+ constexpr int get_val() { return 20; }
+
+ struct testinit
+ {
+ int i;
+ double d;
+ };
+
+ class delegate
+ {
+ public:
+ delegate(int n) : n(n) {}
+ delegate(): delegate(2354) {}
+
+ virtual int getval() { return this->n; };
+ protected:
+ int n;
+ };
+
+ class overridden : public delegate
+ {
+ public:
+ overridden(int n): delegate(n) {}
+ virtual int getval() override final { return this->n * 2; }
+ };
+
+ class nocopy
+ {
+ public:
+ nocopy(int i): i(i) {}
+ nocopy() = default;
+ nocopy(const nocopy&) = delete;
+ nocopy & operator=(const nocopy&) = delete;
+ private:
+ int i;
+ };
+
+ // for testing lambda expressions
+ template <typename Ret, typename Fn> Ret eval(Fn f, Ret v)
+ {
+ return f(v);
+ }
+
+ // for testing variadic templates and trailing return types
+ template <typename V> auto sum(V first) -> V
+ {
+ return first;
+ }
+ template <typename V, typename... Args> auto sum(V first, Args... rest) -> V
+ {
+ return first + sum(rest...);
+ }
+}
+'
+
+# Test code for whether the C++ compiler supports C++11 (body of main)
+ac_cxx_conftest_cxx11_main='
+{
+ // Test auto and decltype
+ auto a1 = 6538;
+ auto a2 = 48573953.4;
+ auto a3 = "String literal";
+
+ int total = 0;
+ for (auto i = a3; *i; ++i) { total += *i; }
+
+ decltype(a2) a4 = 34895.034;
+}
+{
+ // Test constexpr
+ short sa[cxx11test::get_val()] = { 0 };
+}
+{
+ // Test initializer lists
+ cxx11test::testinit il = { 4323, 435234.23544 };
+}
+{
+ // Test range-based for
+ int array[] = {9, 7, 13, 15, 4, 18, 12, 10, 5, 3,
+ 14, 19, 17, 8, 6, 20, 16, 2, 11, 1};
+ for (auto &x : array) { x += 23; }
+}
+{
+ // Test lambda expressions
+ using cxx11test::eval;
+ assert (eval ([](int x) { return x*2; }, 21) == 42);
+ double d = 2.0;
+ assert (eval ([&](double x) { return d += x; }, 3.0) == 5.0);
+ assert (d == 5.0);
+ assert (eval ([=](double x) mutable { return d += x; }, 4.0) == 9.0);
+ assert (d == 5.0);
+}
+{
+ // Test use of variadic templates
+ using cxx11test::sum;
+ auto a = sum(1);
+ auto b = sum(1, 2);
+ auto c = sum(1.0, 2.0, 3.0);
+}
+{
+ // Test constructor delegation
+ cxx11test::delegate d1;
+ cxx11test::delegate d2();
+ cxx11test::delegate d3(45);
+}
+{
+ // Test override and final
+ cxx11test::overridden o1(55464);
+}
+{
+ // Test nullptr
+ char *c = nullptr;
+}
+{
+ // Test template brackets
+ test_template<::test_template<int>> v(test_template<int>(12));
+}
+{
+ // Unicode literals
+ char const *utf8 = u8"UTF-8 string \u2500";
+ char16_t const *utf16 = u"UTF-8 string \u2500";
+ char32_t const *utf32 = U"UTF-32 string \u2500";
+}
+'
+
+# Test code for whether the C compiler supports C++11 (complete).
+ac_cxx_conftest_cxx11_program="${ac_cxx_conftest_cxx98_globals}
+${ac_cxx_conftest_cxx11_globals}
+
+int
+main (int argc, char **argv)
+{
+ int ok = 0;
+ ${ac_cxx_conftest_cxx98_main}
+ ${ac_cxx_conftest_cxx11_main}
+ return ok;
+}
+"
+
+# Test code for whether the C compiler supports C++98 (complete).
+ac_cxx_conftest_cxx98_program="${ac_cxx_conftest_cxx98_globals}
+int
+main (int argc, char **argv)
+{
+ int ok = 0;
+ ${ac_cxx_conftest_cxx98_main}
+ return ok;
+}
+"
+
+as_fn_append ac_header_c_list " stdio.h stdio_h HAVE_STDIO_H"
+as_fn_append ac_header_c_list " stdlib.h stdlib_h HAVE_STDLIB_H"
+as_fn_append ac_header_c_list " string.h string_h HAVE_STRING_H"
+as_fn_append ac_header_c_list " inttypes.h inttypes_h HAVE_INTTYPES_H"
+as_fn_append ac_header_c_list " stdint.h stdint_h HAVE_STDINT_H"
+as_fn_append ac_header_c_list " strings.h strings_h HAVE_STRINGS_H"
+as_fn_append ac_header_c_list " sys/stat.h sys_stat_h HAVE_SYS_STAT_H"
+as_fn_append ac_header_c_list " sys/types.h sys_types_h HAVE_SYS_TYPES_H"
+as_fn_append ac_header_c_list " unistd.h unistd_h HAVE_UNISTD_H"
+
+# Auxiliary files required by this configure script.
+ac_aux_files="install-sh config.guess config.sub"
+
+# Locations in which to look for auxiliary files.
+ac_aux_dir_candidates="${srcdir}/build-aux"
+
+# Search for a directory containing all of the required auxiliary files,
+# $ac_aux_files, from the $PATH-style list $ac_aux_dir_candidates.
+# If we don't find one directory that contains all the files we need,
+# we report the set of missing files from the *first* directory in
+# $ac_aux_dir_candidates and give up.
+ac_missing_aux_files=""
+ac_first_candidate=:
+printf "%s\n" "$as_me:${as_lineno-$LINENO}: looking for aux files: $ac_aux_files" >&5
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+as_found=false
+for as_dir in $ac_aux_dir_candidates
+do
+ IFS=$as_save_IFS
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
+ as_found=:
+
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: trying $as_dir" >&5
+ ac_aux_dir_found=yes
+ ac_install_sh=
+ for ac_aux in $ac_aux_files
+ do
+ # As a special case, if "install-sh" is required, that requirement
+ # can be satisfied by any of "install-sh", "install.sh", or "shtool",
+ # and $ac_install_sh is set appropriately for whichever one is found.
+ if test x"$ac_aux" = x"install-sh"
+ then
+ if test -f "${as_dir}install-sh"; then
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}install-sh found" >&5
+ ac_install_sh="${as_dir}install-sh -c"
+ elif test -f "${as_dir}install.sh"; then
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}install.sh found" >&5
+ ac_install_sh="${as_dir}install.sh -c"
+ elif test -f "${as_dir}shtool"; then
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}shtool found" >&5
+ ac_install_sh="${as_dir}shtool install -c"
+ else
+ ac_aux_dir_found=no
+ if $ac_first_candidate; then
+ ac_missing_aux_files="${ac_missing_aux_files} install-sh"
+ else
+ break
+ fi
+ fi
+ else
+ if test -f "${as_dir}${ac_aux}"; then
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: ${as_dir}${ac_aux} found" >&5
+ else
+ ac_aux_dir_found=no
+ if $ac_first_candidate; then
+ ac_missing_aux_files="${ac_missing_aux_files} ${ac_aux}"
+ else
+ break
+ fi
+ fi
+ fi
+ done
+ if test "$ac_aux_dir_found" = yes; then
+ ac_aux_dir="$as_dir"
+ break
+ fi
+ ac_first_candidate=false
+
+ as_found=false
+done
+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
+fi
+
+
+# These three variables are undocumented and unsupported,
+# and are intended to be withdrawn in a future Autoconf release.
+# They can cause serious problems if a builder's source tree is in a directory
+# whose full name contains unusual characters.
+if test -f "${ac_aux_dir}config.guess"; then
+ ac_config_guess="$SHELL ${ac_aux_dir}config.guess"
+fi
+if test -f "${ac_aux_dir}config.sub"; then
+ ac_config_sub="$SHELL ${ac_aux_dir}config.sub"
+fi
+if test -f "$ac_aux_dir/configure"; then
+ ac_configure="$SHELL ${ac_aux_dir}configure"
+fi
+
# Check that the precious variables saved in the cache have kept the same
# value.
ac_cache_corrupted=false
@@ -2516,12 +3120,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,)
- { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5
-$as_echo "$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)
- { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5
-$as_echo "$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=: ;;
,);;
*)
@@ -2530,24 +3134,24 @@ $as_echo "$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
- { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5
-$as_echo "$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
- { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5
-$as_echo "$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
- { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5
-$as_echo "$as_me: former value: \`$ac_old_val'" >&2;}
- { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5
-$as_echo "$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.
if test "$ac_new_set" = set; then
case $ac_new_val in
- *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
+ *\'*) ac_arg=$ac_var=`printf "%s\n" "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;
*) ac_arg=$ac_var=$ac_new_val ;;
esac
case " $ac_configure_args " in
@@ -2557,11 +3161,12 @@ $as_echo "$as_me: current value: \`$ac_new_val'" >&2;}
fi
done
if $ac_cache_corrupted; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
- { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5
-$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;}
- as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 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: 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'
+ and start over" "$LINENO" 5
fi
## -------------------- ##
## Main body of script. ##
@@ -2575,34 +3180,6 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
-ac_aux_dir=
-for ac_dir in build-aux "$srcdir"/build-aux; do
- if test -f "$ac_dir/install-sh"; then
- ac_aux_dir=$ac_dir
- ac_install_sh="$ac_aux_dir/install-sh -c"
- break
- elif test -f "$ac_dir/install.sh"; then
- ac_aux_dir=$ac_dir
- ac_install_sh="$ac_aux_dir/install.sh -c"
- break
- elif test -f "$ac_dir/shtool"; then
- ac_aux_dir=$ac_dir
- ac_install_sh="$ac_aux_dir/shtool install -c"
- break
- fi
-done
-if test -z "$ac_aux_dir"; then
- as_fn_error $? "cannot find install-sh, install.sh, or shtool in build-aux \"$srcdir\"/build-aux" "$LINENO" 5
-fi
-
-# These three variables are undocumented and unsupported,
-# and are intended to be withdrawn in a future Autoconf release.
-# They can cause serious problems if a builder's source tree is in a directory
-# whose full name contains unusual characters.
-ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var.
-ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var.
-ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var.
-
@@ -2645,12 +3222,14 @@ objroot=""
abs_objroot="`pwd`/"
-if test "x$prefix" = "xNONE" ; then
- prefix="/usr/local"
-fi
-if test "x$exec_prefix" = "xNONE" ; then
- exec_prefix=$prefix
-fi
+case "$prefix" in
+ *\ * ) as_fn_error $? "Prefix should not contain spaces" "$LINENO" 5 ;;
+ "NONE" ) prefix="/usr/local" ;;
+esac
+case "$exec_prefix" in
+ *\ * ) as_fn_error $? "Exec prefix should not contain spaces" "$LINENO" 5 ;;
+ "NONE" ) exec_prefix=$prefix ;;
+esac
PREFIX=$prefix
BINDIR=`eval echo $bindir`
@@ -2671,11 +3250,12 @@ MANDIR=`eval echo $MANDIR`
# Extract the first word of "xsltproc", so it can be a program name with args.
set dummy xsltproc; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_path_XSLTPROC+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_path_XSLTPROC+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
case $XSLTPROC in
[\\/]* | ?:[\\/]*)
ac_cv_path_XSLTPROC="$XSLTPROC" # Let the user override the test with a path.
@@ -2685,11 +3265,15 @@ else
for as_dir in $PATH
do
IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
for ac_exec_ext in '' $ac_executable_extensions; do
- if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
- ac_cv_path_XSLTPROC="$as_dir/$ac_word$ac_exec_ext"
- $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+ ac_cv_path_XSLTPROC="$as_dir$ac_word$ac_exec_ext"
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
break 2
fi
done
@@ -2702,11 +3286,11 @@ esac
fi
XSLTPROC=$ac_cv_path_XSLTPROC
if test -n "$XSLTPROC"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $XSLTPROC" >&5
-$as_echo "$XSLTPROC" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $XSLTPROC" >&5
+printf "%s\n" "$XSLTPROC" >&6; }
else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
fi
@@ -2719,7 +3303,8 @@ else
fi
# Check whether --with-xslroot was given.
-if test "${with_xslroot+set}" = set; then :
+if test ${with_xslroot+y}
+then :
withval=$with_xslroot;
if test "x$with_xslroot" = "xno" ; then
XSLROOT="${DEFAULT_XSLROOT}"
@@ -2727,7 +3312,7 @@ else
XSLROOT="${with_xslroot}"
fi
-else
+else $as_nop
XSLROOT="${DEFAULT_XSLROOT}"
fi
@@ -2738,6 +3323,15 @@ fi
CFLAGS=$CFLAGS
+
+
+
+
+
+
+
+
+
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
@@ -2746,11 +3340,12 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
if test -n "$ac_tool_prefix"; then
# Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
set dummy ${ac_tool_prefix}gcc; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_prog_CC+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+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
ac_cv_prog_CC="$CC" # Let the user override the test.
else
@@ -2758,11 +3353,15 @@ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
for ac_exec_ext in '' $ac_executable_extensions; do
- if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
ac_cv_prog_CC="${ac_tool_prefix}gcc"
- $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
break 2
fi
done
@@ -2773,11 +3372,11 @@ fi
fi
CC=$ac_cv_prog_CC
if test -n "$CC"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
-$as_echo "$CC" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+printf "%s\n" "$CC" >&6; }
else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
fi
@@ -2786,11 +3385,12 @@ if test -z "$ac_cv_prog_CC"; then
ac_ct_CC=$CC
# Extract the first word of "gcc", so it can be a program name with args.
set dummy gcc; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_prog_ac_ct_CC+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+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
ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
else
@@ -2798,11 +3398,15 @@ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
for ac_exec_ext in '' $ac_executable_extensions; do
- if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_CC="gcc"
- $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
break 2
fi
done
@@ -2813,11 +3417,11 @@ fi
fi
ac_ct_CC=$ac_cv_prog_ac_ct_CC
if test -n "$ac_ct_CC"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
-$as_echo "$ac_ct_CC" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+printf "%s\n" "$ac_ct_CC" >&6; }
else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
fi
if test "x$ac_ct_CC" = x; then
@@ -2825,8 +3429,8 @@ fi
else
case $cross_compiling:$ac_tool_warned in
yes:)
-{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
-$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
ac_tool_warned=yes ;;
esac
CC=$ac_ct_CC
@@ -2839,11 +3443,12 @@ if test -z "$CC"; then
if test -n "$ac_tool_prefix"; then
# Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args.
set dummy ${ac_tool_prefix}cc; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_prog_CC+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+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
ac_cv_prog_CC="$CC" # Let the user override the test.
else
@@ -2851,11 +3456,15 @@ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
for ac_exec_ext in '' $ac_executable_extensions; do
- if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
ac_cv_prog_CC="${ac_tool_prefix}cc"
- $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
break 2
fi
done
@@ -2866,11 +3475,11 @@ fi
fi
CC=$ac_cv_prog_CC
if test -n "$CC"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
-$as_echo "$CC" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+printf "%s\n" "$CC" >&6; }
else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
fi
@@ -2879,11 +3488,12 @@ fi
if test -z "$CC"; then
# Extract the first word of "cc", so it can be a program name with args.
set dummy cc; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_prog_CC+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+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
ac_cv_prog_CC="$CC" # Let the user override the test.
else
@@ -2892,15 +3502,19 @@ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
for ac_exec_ext in '' $ac_executable_extensions; do
- if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
- if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+ if test "$as_dir$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then
ac_prog_rejected=yes
continue
fi
ac_cv_prog_CC="cc"
- $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
break 2
fi
done
@@ -2916,18 +3530,18 @@ if test $ac_prog_rejected = yes; then
# However, it has the same basename, so the bogon will be chosen
# first if we set CC to just the basename; use the full file name.
shift
- ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@"
+ ac_cv_prog_CC="$as_dir$ac_word${1+' '}$@"
fi
fi
fi
fi
CC=$ac_cv_prog_CC
if test -n "$CC"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
-$as_echo "$CC" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+printf "%s\n" "$CC" >&6; }
else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
fi
@@ -2938,11 +3552,12 @@ if test -z "$CC"; then
do
# Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
set dummy $ac_tool_prefix$ac_prog; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_prog_CC+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+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
ac_cv_prog_CC="$CC" # Let the user override the test.
else
@@ -2950,11 +3565,15 @@ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
for ac_exec_ext in '' $ac_executable_extensions; do
- if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
ac_cv_prog_CC="$ac_tool_prefix$ac_prog"
- $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
break 2
fi
done
@@ -2965,11 +3584,11 @@ fi
fi
CC=$ac_cv_prog_CC
if test -n "$CC"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
-$as_echo "$CC" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+printf "%s\n" "$CC" >&6; }
else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
fi
@@ -2982,11 +3601,12 @@ if test -z "$CC"; then
do
# Extract the first word of "$ac_prog", so it can be a program name with args.
set dummy $ac_prog; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_prog_ac_ct_CC+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+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
ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
else
@@ -2994,11 +3614,15 @@ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
for ac_exec_ext in '' $ac_executable_extensions; do
- if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_CC="$ac_prog"
- $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
break 2
fi
done
@@ -3009,11 +3633,11 @@ fi
fi
ac_ct_CC=$ac_cv_prog_ac_ct_CC
if test -n "$ac_ct_CC"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
-$as_echo "$ac_ct_CC" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+printf "%s\n" "$ac_ct_CC" >&6; }
else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
fi
@@ -3025,8 +3649,8 @@ done
else
case $cross_compiling:$ac_tool_warned in
yes:)
-{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
-$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
ac_tool_warned=yes ;;
esac
CC=$ac_ct_CC
@@ -3034,25 +3658,129 @@ esac
fi
fi
+if test -z "$CC"; then
+ if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "${ac_tool_prefix}clang", so it can be a program name with args.
+set dummy ${ac_tool_prefix}clang; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+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
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+ ac_cv_prog_CC="${ac_tool_prefix}clang"
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+CC=$ac_cv_prog_CC
+if test -n "$CC"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5
+printf "%s\n" "$CC" >&6; }
+else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+
+fi
+if test -z "$ac_cv_prog_CC"; then
+ ac_ct_CC=$CC
+ # Extract the first word of "clang", so it can be a program name with args.
+set dummy clang; ac_word=$2
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+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
+ ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+ IFS=$as_save_IFS
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
+ for ac_exec_ext in '' $ac_executable_extensions; do
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+ ac_cv_prog_ac_ct_CC="clang"
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
+ break 2
+ fi
+done
+ done
+IFS=$as_save_IFS
+
+fi
+fi
+ac_ct_CC=$ac_cv_prog_ac_ct_CC
+if test -n "$ac_ct_CC"; then
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5
+printf "%s\n" "$ac_ct_CC" >&6; }
+else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+fi
+
+ if test "x$ac_ct_CC" = x; then
+ CC=""
+ else
+ case $cross_compiling:$ac_tool_warned in
+yes:)
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+ac_tool_warned=yes ;;
+esac
+ CC=$ac_ct_CC
+ fi
+else
+ CC="$ac_cv_prog_CC"
+fi
+
+fi
-test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-$as_echo "$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; }
# Provide some information about the compiler.
-$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
+printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5
set X $ac_compile
ac_compiler=$2
-for ac_option in --version -v -V -qversion; do
+for ac_option in --version -v -V -qversion -version; do
{ { ac_try="$ac_compiler $ac_option >&5"
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\""
-$as_echo "$ac_try_echo"; } >&5
+printf "%s\n" "$ac_try_echo"; } >&5
(eval "$ac_compiler $ac_option >&5") 2>conftest.err
ac_status=$?
if test -s conftest.err; then
@@ -3062,7 +3790,7 @@ $as_echo "$ac_try_echo"; } >&5
cat conftest.er1 >&5
fi
rm -f conftest.er1 conftest.err
- $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }
done
@@ -3070,7 +3798,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
-main ()
+main (void)
{
;
@@ -3082,9 +3810,9 @@ ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out"
# Try to create an executable without -o first, disregard a.out.
# It will help us diagnose broken compilers, and finding out an intuition
# of exeext.
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5
-$as_echo_n "checking whether the C compiler works... " >&6; }
-ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5
+printf %s "checking whether the C compiler works... " >&6; }
+ac_link_default=`printf "%s\n" "$ac_link" | sed 's/ -o *conftest[^ ]*//'`
# The possible output files:
ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*"
@@ -3105,11 +3833,12 @@ case "(($ac_try" in
*) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
-$as_echo "$ac_try_echo"; } >&5
+printf "%s\n" "$ac_try_echo"; } >&5
(eval "$ac_link_default") 2>&5
ac_status=$?
- $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
- test $ac_status = 0; }; then :
+ 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'
# in a Makefile. We should not override ac_cv_exeext if it was cached,
@@ -3126,7 +3855,7 @@ do
# certainly right.
break;;
*.* )
- if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no;
+ if test ${ac_cv_exeext+y} && test "$ac_cv_exeext" != no;
then :; else
ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`
fi
@@ -3142,44 +3871,46 @@ do
done
test "$ac_cv_exeext" = no && ac_cv_exeext=
-else
+else $as_nop
ac_file=''
fi
-if test -z "$ac_file"; then :
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
-$as_echo "$as_me: failed program was:" >&5
+if test -z "$ac_file"
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+printf "%s\n" "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
-{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-$as_echo "$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_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5
-$as_echo_n "checking for C compiler default output file name... " >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5
-$as_echo "$ac_file" >&6; }
+else $as_nop
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+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; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5
+printf "%s\n" "$ac_file" >&6; }
ac_exeext=$ac_cv_exeext
rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out
ac_clean_files=$ac_clean_files_save
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5
-$as_echo_n "checking for suffix of executables... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5
+printf %s "checking for suffix of executables... " >&6; }
if { { ac_try="$ac_link"
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\""
-$as_echo "$ac_try_echo"; } >&5
+printf "%s\n" "$ac_try_echo"; } >&5
(eval "$ac_link") 2>&5
ac_status=$?
- $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
- test $ac_status = 0; }; then :
+ 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
@@ -3193,15 +3924,15 @@ for ac_file in conftest.exe conftest conftest.*; do
* ) break;;
esac
done
-else
- { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+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 $? "cannot compute suffix of executables: cannot compile and link
See \`config.log' for more details" "$LINENO" 5; }
fi
rm -f conftest conftest$ac_cv_exeext
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5
-$as_echo "$ac_cv_exeext" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5
+printf "%s\n" "$ac_cv_exeext" >&6; }
rm -f conftest.$ac_ext
EXEEXT=$ac_cv_exeext
@@ -3210,7 +3941,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <stdio.h>
int
-main ()
+main (void)
{
FILE *f = fopen ("conftest.out", "w");
return ferror (f) || fclose (f) != 0;
@@ -3222,8 +3953,8 @@ _ACEOF
ac_clean_files="$ac_clean_files conftest.out"
# Check that the compiler produces executables we can run. If not, either
# the compiler is broken, or we cross compile.
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5
-$as_echo_n "checking whether we are cross compiling... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5
+printf %s "checking whether we are cross compiling... " >&6; }
if test "$cross_compiling" != yes; then
{ { ac_try="$ac_link"
case "(($ac_try" in
@@ -3231,10 +3962,10 @@ case "(($ac_try" in
*) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
-$as_echo "$ac_try_echo"; } >&5
+printf "%s\n" "$ac_try_echo"; } >&5
(eval "$ac_link") 2>&5
ac_status=$?
- $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }
if { ac_try='./conftest$ac_cv_exeext'
{ { case "(($ac_try" in
@@ -3242,39 +3973,40 @@ $as_echo "$ac_try_echo"; } >&5
*) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
-$as_echo "$ac_try_echo"; } >&5
+printf "%s\n" "$ac_try_echo"; } >&5
(eval "$ac_try") 2>&5
ac_status=$?
- $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }; }; then
cross_compiling=no
else
if test "$cross_compiling" = maybe; then
cross_compiling=yes
else
- { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
-as_fn_error $? "cannot run C compiled programs.
+ { { 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; }
fi
fi
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5
-$as_echo "$cross_compiling" >&6; }
+{ 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
ac_clean_files=$ac_clean_files_save
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5
-$as_echo_n "checking for suffix of object files... " >&6; }
-if ${ac_cv_objext+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ 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
/* end confdefs.h. */
int
-main ()
+main (void)
{
;
@@ -3288,11 +4020,12 @@ case "(($ac_try" in
*) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
-$as_echo "$ac_try_echo"; } >&5
+printf "%s\n" "$ac_try_echo"; } >&5
(eval "$ac_compile") 2>&5
ac_status=$?
- $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
- test $ac_status = 0; }; then :
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ test $ac_status = 0; }
+then :
for ac_file in conftest.o conftest.obj conftest.*; do
test -f "$ac_file" || continue;
case $ac_file in
@@ -3301,31 +4034,32 @@ $as_echo "$ac_try_echo"; } >&5
break;;
esac
done
-else
- $as_echo "$as_me: failed program was:" >&5
+else $as_nop
+ printf "%s\n" "$as_me: failed program was:" >&5
sed 's/^/| /' conftest.$ac_ext >&5
-{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-$as_echo "$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; }
fi
rm -f conftest.$ac_cv_objext conftest.$ac_ext
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5
-$as_echo "$ac_cv_objext" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5
+printf "%s\n" "$ac_cv_objext" >&6; }
OBJEXT=$ac_cv_objext
ac_objext=$OBJEXT
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5
-$as_echo_n "checking whether we are using the GNU C compiler... " >&6; }
-if ${ac_cv_c_compiler_gnu+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports GNU C" >&5
+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
/* end confdefs.h. */
int
-main ()
+main (void)
{
#ifndef __GNUC__
choke me
@@ -3335,29 +4069,33 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
ac_compiler_gnu=yes
-else
+else $as_nop
ac_compiler_gnu=no
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
ac_cv_c_compiler_gnu=$ac_compiler_gnu
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5
-$as_echo "$ac_cv_c_compiler_gnu" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5
+printf "%s\n" "$ac_cv_c_compiler_gnu" >&6; }
+ac_compiler_gnu=$ac_cv_c_compiler_gnu
+
if test $ac_compiler_gnu = yes; then
GCC=yes
else
GCC=
fi
-ac_test_CFLAGS=${CFLAGS+set}
+ac_test_CFLAGS=${CFLAGS+y}
ac_save_CFLAGS=$CFLAGS
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5
-$as_echo_n "checking whether $CC accepts -g... " >&6; }
-if ${ac_cv_prog_cc_g+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5
+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
ac_c_werror_flag=yes
ac_cv_prog_cc_g=no
@@ -3366,57 +4104,60 @@ else
/* end confdefs.h. */
int
-main ()
+main (void)
{
;
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
ac_cv_prog_cc_g=yes
-else
+else $as_nop
CFLAGS=""
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
-main ()
+main (void)
{
;
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
-else
+else $as_nop
ac_c_werror_flag=$ac_save_c_werror_flag
CFLAGS="-g"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
-main ()
+main (void)
{
;
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
ac_cv_prog_cc_g=yes
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
ac_c_werror_flag=$ac_save_c_werror_flag
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5
-$as_echo "$ac_cv_prog_cc_g" >&6; }
-if test "$ac_test_CFLAGS" = set; then
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5
+printf "%s\n" "$ac_cv_prog_cc_g" >&6; }
+if test $ac_test_CFLAGS; then
CFLAGS=$ac_save_CFLAGS
elif test $ac_cv_prog_cc_g = yes; then
if test "$GCC" = yes; then
@@ -3431,94 +4172,144 @@ else
CFLAGS=
fi
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5
-$as_echo_n "checking for $CC option to accept ISO C89... " >&6; }
-if ${ac_cv_prog_cc_c89+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- ac_cv_prog_cc_c89=no
+ac_prog_cc_stdc=no
+if test x$ac_prog_cc_stdc = xno
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C11 features" >&5
+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
ac_save_CC=$CC
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
-#include <stdarg.h>
-#include <stdio.h>
-struct stat;
-/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */
-struct buf { int x; };
-FILE * (*rcsopen) (struct buf *, struct stat *, int);
-static char *e (p, i)
- char **p;
- int i;
-{
- return p[i];
-}
-static char *f (char * (*g) (char **, int), char **p, ...)
-{
- char *s;
- va_list v;
- va_start (v,p);
- s = g (p, va_arg (v,int));
- va_end (v);
- return s;
-}
-
-/* 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 don't provoke an error unfortunately, instead are silently treated
- as 'x'. The following induces an error, until -std is added to get
- proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an
- array size at least. It's necessary to write '\x00'==0 to get something
- that's true only with -std. */
-int osf4_cc_array ['\x00' == 0 ? 1 : -1];
+$ac_c_conftest_c11_program
+_ACEOF
+for ac_arg in '' -std=gnu11
+do
+ CC="$ac_save_CC $ac_arg"
+ if ac_fn_c_try_compile "$LINENO"
+then :
+ ac_cv_prog_cc_c11=$ac_arg
+fi
+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
+fi
-/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters
- inside strings and character constants. */
-#define FOO(x) 'x'
-int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1];
+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
+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
+printf "%s\n" "$ac_cv_prog_cc_c11" >&6; }
+ CC="$CC $ac_cv_prog_cc_c11"
+fi
+ ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c11
+ ac_prog_cc_stdc=c11
+fi
+fi
+if test x$ac_prog_cc_stdc = xno
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C99 features" >&5
+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
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$ac_c_conftest_c99_program
+_ACEOF
+for ac_arg in '' -std=gnu99 -std=c99 -c99 -qlanglvl=extc1x -qlanglvl=extc99 -AC99 -D_STDC_C99=
+do
+ CC="$ac_save_CC $ac_arg"
+ if ac_fn_c_try_compile "$LINENO"
+then :
+ ac_cv_prog_cc_c99=$ac_arg
+fi
+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
+fi
-int test (int i, double x);
-struct s1 {int (*f) (int a);};
-struct s2 {int (*f) (double a);};
-int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int);
-int argc;
-char **argv;
-int
-main ()
-{
-return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1];
- ;
- return 0;
-}
+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
+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
+printf "%s\n" "$ac_cv_prog_cc_c99" >&6; }
+ CC="$CC $ac_cv_prog_cc_c99"
+fi
+ ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c99
+ ac_prog_cc_stdc=c99
+fi
+fi
+if test x$ac_prog_cc_stdc = xno
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C89 features" >&5
+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
+ac_save_CC=$CC
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$ac_c_conftest_c89_program
_ACEOF
-for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \
- -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
+for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__"
do
CC="$ac_save_CC $ac_arg"
- if ac_fn_c_try_compile "$LINENO"; then :
+ if ac_fn_c_try_compile "$LINENO"
+then :
ac_cv_prog_cc_c89=$ac_arg
fi
-rm -f core conftest.err conftest.$ac_objext
+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
-
fi
-# AC_CACHE_VAL
-case "x$ac_cv_prog_cc_c89" in
- x)
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5
-$as_echo "none needed" >&6; } ;;
- xno)
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5
-$as_echo "unsupported" >&6; } ;;
- *)
- CC="$CC $ac_cv_prog_cc_c89"
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5
-$as_echo "$ac_cv_prog_cc_c89" >&6; } ;;
-esac
-if test "x$ac_cv_prog_cc_c89" != xno; then :
+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
+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
+printf "%s\n" "$ac_cv_prog_cc_c89" >&6; }
+ CC="$CC $ac_cv_prog_cc_c89"
+fi
+ ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c89
+ ac_prog_cc_stdc=c89
+fi
fi
ac_ext=c
@@ -3530,16 +4321,17 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
if test "x$GCC" != "xyes" ; then
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler is MSVC" >&5
-$as_echo_n "checking whether compiler is MSVC... " >&6; }
-if ${je_cv_msvc+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler is MSVC" >&5
+printf %s "checking whether compiler is MSVC... " >&6; }
+if test ${je_cv_msvc+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
-main ()
+main (void)
{
#ifndef _MSC_VER
@@ -3550,15 +4342,16 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
je_cv_msvc=yes
-else
+else $as_nop
je_cv_msvc=no
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $je_cv_msvc" >&5
-$as_echo "$je_cv_msvc" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $je_cv_msvc" >&5
+printf "%s\n" "$je_cv_msvc" >&6; }
fi
je_cv_cray_prgenv_wrapper=""
@@ -3572,16 +4365,17 @@ if test "x${PE_ENV}" != "x" ; then
esac
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler is cray" >&5
-$as_echo_n "checking whether compiler is cray... " >&6; }
-if ${je_cv_cray+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler is cray" >&5
+printf %s "checking whether compiler is cray... " >&6; }
+if test ${je_cv_cray+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
-main ()
+main (void)
{
#ifndef _CRAYC
@@ -3592,27 +4386,29 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
je_cv_cray=yes
-else
+else $as_nop
je_cv_cray=no
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $je_cv_cray" >&5
-$as_echo "$je_cv_cray" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $je_cv_cray" >&5
+printf "%s\n" "$je_cv_cray" >&6; }
if test "x${je_cv_cray}" = "xyes" ; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether cray compiler version is 8.4" >&5
-$as_echo_n "checking whether cray compiler version is 8.4... " >&6; }
-if ${je_cv_cray_84+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether cray compiler version is 8.4" >&5
+printf %s "checking whether cray compiler version is 8.4... " >&6; }
+if test ${je_cv_cray_84+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
-main ()
+main (void)
{
#if !(_RELEASE_MAJOR == 8 && _RELEASE_MINOR == 4)
@@ -3623,21 +4419,22 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
je_cv_cray_84=yes
-else
+else $as_nop
je_cv_cray_84=no
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $je_cv_cray_84" >&5
-$as_echo "$je_cv_cray_84" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $je_cv_cray_84" >&5
+printf "%s\n" "$je_cv_cray_84" >&6; }
fi
if test "x$GCC" = "xyes" ; then
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -std=gnu11" >&5
-$as_echo_n "checking whether compiler supports -std=gnu11... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -std=gnu11" >&5
+printf %s "checking whether compiler supports -std=gnu11... " >&6; }
T_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
T_APPEND_V=-std=gnu11
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
@@ -3658,7 +4455,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
int
-main ()
+main (void)
{
return 0;
@@ -3667,18 +4464,19 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
je_cv_cflags_added=-std=gnu11
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
je_cv_cflags_added=
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
CONFIGURE_CFLAGS="${T_CONFIGURE_CFLAGS}"
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
else
@@ -3687,14 +4485,13 @@ fi
if test "x$je_cv_cflags_added" = "x-std=gnu11" ; then
- cat >>confdefs.h <<_ACEOF
-#define JEMALLOC_HAS_RESTRICT 1
-_ACEOF
+
+printf "%s\n" "#define JEMALLOC_HAS_RESTRICT " >>confdefs.h
else
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -std=gnu99" >&5
-$as_echo_n "checking whether compiler supports -std=gnu99... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -std=gnu99" >&5
+printf %s "checking whether compiler supports -std=gnu99... " >&6; }
T_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
T_APPEND_V=-std=gnu99
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
@@ -3715,7 +4512,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
int
-main ()
+main (void)
{
return 0;
@@ -3724,18 +4521,19 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
je_cv_cflags_added=-std=gnu99
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
je_cv_cflags_added=
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
CONFIGURE_CFLAGS="${T_CONFIGURE_CFLAGS}"
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
else
@@ -3744,15 +4542,66 @@ fi
if test "x$je_cv_cflags_added" = "x-std=gnu99" ; then
- cat >>confdefs.h <<_ACEOF
-#define JEMALLOC_HAS_RESTRICT 1
-_ACEOF
+
+printf "%s\n" "#define JEMALLOC_HAS_RESTRICT " >>confdefs.h
fi
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -Wall" >&5
-$as_echo_n "checking whether compiler supports -Wall... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -Werror=unknown-warning-option" >&5
+printf %s "checking whether compiler supports -Werror=unknown-warning-option... " >&6; }
+T_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
+T_APPEND_V=-Werror=unknown-warning-option
+ if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
+ CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}${T_APPEND_V}"
+else
+ CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS} ${T_APPEND_V}"
+fi
+
+
+if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
+ CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
+else
+ CFLAGS="${CONFIGURE_CFLAGS} ${SPECIFIED_CFLAGS}"
+fi
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+
+int
+main (void)
+{
+
+ return 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ je_cv_cflags_added=-Werror=unknown-warning-option
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
+ je_cv_cflags_added=
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+ CONFIGURE_CFLAGS="${T_CONFIGURE_CFLAGS}"
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
+ CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
+else
+ CFLAGS="${CONFIGURE_CFLAGS} ${SPECIFIED_CFLAGS}"
+fi
+
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -Wall" >&5
+printf %s "checking whether compiler supports -Wall... " >&6; }
T_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
T_APPEND_V=-Wall
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
@@ -3773,7 +4622,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
int
-main ()
+main (void)
{
return 0;
@@ -3782,18 +4631,19 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
je_cv_cflags_added=-Wall
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
je_cv_cflags_added=
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
CONFIGURE_CFLAGS="${T_CONFIGURE_CFLAGS}"
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
else
@@ -3802,8 +4652,8 @@ fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -Wextra" >&5
-$as_echo_n "checking whether compiler supports -Wextra... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -Wextra" >&5
+printf %s "checking whether compiler supports -Wextra... " >&6; }
T_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
T_APPEND_V=-Wextra
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
@@ -3824,7 +4674,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
int
-main ()
+main (void)
{
return 0;
@@ -3833,18 +4683,19 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
je_cv_cflags_added=-Wextra
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
je_cv_cflags_added=
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
CONFIGURE_CFLAGS="${T_CONFIGURE_CFLAGS}"
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
else
@@ -3853,8 +4704,8 @@ fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -Wshorten-64-to-32" >&5
-$as_echo_n "checking whether compiler supports -Wshorten-64-to-32... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -Wshorten-64-to-32" >&5
+printf %s "checking whether compiler supports -Wshorten-64-to-32... " >&6; }
T_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
T_APPEND_V=-Wshorten-64-to-32
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
@@ -3875,7 +4726,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
int
-main ()
+main (void)
{
return 0;
@@ -3884,18 +4735,19 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
je_cv_cflags_added=-Wshorten-64-to-32
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
je_cv_cflags_added=
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
CONFIGURE_CFLAGS="${T_CONFIGURE_CFLAGS}"
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
else
@@ -3904,8 +4756,8 @@ fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -Wsign-compare" >&5
-$as_echo_n "checking whether compiler supports -Wsign-compare... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -Wsign-compare" >&5
+printf %s "checking whether compiler supports -Wsign-compare... " >&6; }
T_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
T_APPEND_V=-Wsign-compare
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
@@ -3926,7 +4778,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
int
-main ()
+main (void)
{
return 0;
@@ -3935,18 +4787,19 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
je_cv_cflags_added=-Wsign-compare
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
je_cv_cflags_added=
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
CONFIGURE_CFLAGS="${T_CONFIGURE_CFLAGS}"
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
else
@@ -3955,8 +4808,8 @@ fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -Wundef" >&5
-$as_echo_n "checking whether compiler supports -Wundef... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -Wundef" >&5
+printf %s "checking whether compiler supports -Wundef... " >&6; }
T_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
T_APPEND_V=-Wundef
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
@@ -3977,7 +4830,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
int
-main ()
+main (void)
{
return 0;
@@ -3986,18 +4839,19 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
je_cv_cflags_added=-Wundef
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
je_cv_cflags_added=
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
CONFIGURE_CFLAGS="${T_CONFIGURE_CFLAGS}"
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
else
@@ -4006,8 +4860,8 @@ fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -Wno-format-zero-length" >&5
-$as_echo_n "checking whether compiler supports -Wno-format-zero-length... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -Wno-format-zero-length" >&5
+printf %s "checking whether compiler supports -Wno-format-zero-length... " >&6; }
T_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
T_APPEND_V=-Wno-format-zero-length
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
@@ -4028,7 +4882,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
int
-main ()
+main (void)
{
return 0;
@@ -4037,18 +4891,123 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
je_cv_cflags_added=-Wno-format-zero-length
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
+ je_cv_cflags_added=
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+ CONFIGURE_CFLAGS="${T_CONFIGURE_CFLAGS}"
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
+ CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
else
+ CFLAGS="${CONFIGURE_CFLAGS} ${SPECIFIED_CFLAGS}"
+fi
+
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -Wpointer-arith" >&5
+printf %s "checking whether compiler supports -Wpointer-arith... " >&6; }
+T_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
+T_APPEND_V=-Wpointer-arith
+ if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
+ CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}${T_APPEND_V}"
+else
+ CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS} ${T_APPEND_V}"
+fi
+
+
+if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
+ CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
+else
+ CFLAGS="${CONFIGURE_CFLAGS} ${SPECIFIED_CFLAGS}"
+fi
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+
+int
+main (void)
+{
+
+ return 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ je_cv_cflags_added=-Wpointer-arith
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
+ je_cv_cflags_added=
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+ CONFIGURE_CFLAGS="${T_CONFIGURE_CFLAGS}"
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
+ CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
+else
+ CFLAGS="${CONFIGURE_CFLAGS} ${SPECIFIED_CFLAGS}"
+fi
+
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -Wno-missing-braces" >&5
+printf %s "checking whether compiler supports -Wno-missing-braces... " >&6; }
+T_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
+T_APPEND_V=-Wno-missing-braces
+ if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
+ CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}${T_APPEND_V}"
+else
+ CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS} ${T_APPEND_V}"
+fi
+
+
+if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
+ CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
+else
+ CFLAGS="${CONFIGURE_CFLAGS} ${SPECIFIED_CFLAGS}"
+fi
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+
+int
+main (void)
+{
+
+ return 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ je_cv_cflags_added=-Wno-missing-braces
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
je_cv_cflags_added=
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
CONFIGURE_CFLAGS="${T_CONFIGURE_CFLAGS}"
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
else
@@ -4057,8 +5016,112 @@ fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -pipe" >&5
-$as_echo_n "checking whether compiler supports -pipe... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -Wno-missing-field-initializers" >&5
+printf %s "checking whether compiler supports -Wno-missing-field-initializers... " >&6; }
+T_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
+T_APPEND_V=-Wno-missing-field-initializers
+ if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
+ CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}${T_APPEND_V}"
+else
+ CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS} ${T_APPEND_V}"
+fi
+
+
+if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
+ CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
+else
+ CFLAGS="${CONFIGURE_CFLAGS} ${SPECIFIED_CFLAGS}"
+fi
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+
+int
+main (void)
+{
+
+ return 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ je_cv_cflags_added=-Wno-missing-field-initializers
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
+ je_cv_cflags_added=
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+ CONFIGURE_CFLAGS="${T_CONFIGURE_CFLAGS}"
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
+ CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
+else
+ CFLAGS="${CONFIGURE_CFLAGS} ${SPECIFIED_CFLAGS}"
+fi
+
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -Wno-missing-attributes" >&5
+printf %s "checking whether compiler supports -Wno-missing-attributes... " >&6; }
+T_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
+T_APPEND_V=-Wno-missing-attributes
+ if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
+ CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}${T_APPEND_V}"
+else
+ CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS} ${T_APPEND_V}"
+fi
+
+
+if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
+ CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
+else
+ CFLAGS="${CONFIGURE_CFLAGS} ${SPECIFIED_CFLAGS}"
+fi
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+
+int
+main (void)
+{
+
+ return 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ je_cv_cflags_added=-Wno-missing-attributes
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
+ je_cv_cflags_added=
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+ CONFIGURE_CFLAGS="${T_CONFIGURE_CFLAGS}"
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
+ CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
+else
+ CFLAGS="${CONFIGURE_CFLAGS} ${SPECIFIED_CFLAGS}"
+fi
+
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -pipe" >&5
+printf %s "checking whether compiler supports -pipe... " >&6; }
T_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
T_APPEND_V=-pipe
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
@@ -4079,7 +5142,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
int
-main ()
+main (void)
{
return 0;
@@ -4088,18 +5151,19 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
je_cv_cflags_added=-pipe
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
je_cv_cflags_added=
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
CONFIGURE_CFLAGS="${T_CONFIGURE_CFLAGS}"
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
else
@@ -4108,8 +5172,8 @@ fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -g3" >&5
-$as_echo_n "checking whether compiler supports -g3... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -g3" >&5
+printf %s "checking whether compiler supports -g3... " >&6; }
T_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
T_APPEND_V=-g3
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
@@ -4130,7 +5194,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
int
-main ()
+main (void)
{
return 0;
@@ -4139,18 +5203,19 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
je_cv_cflags_added=-g3
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
je_cv_cflags_added=
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
CONFIGURE_CFLAGS="${T_CONFIGURE_CFLAGS}"
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
else
@@ -4161,8 +5226,8 @@ fi
elif test "x$je_cv_msvc" = "xyes" ; then
CC="$CC -nologo"
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -Zi" >&5
-$as_echo_n "checking whether compiler supports -Zi... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -Zi" >&5
+printf %s "checking whether compiler supports -Zi... " >&6; }
T_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
T_APPEND_V=-Zi
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
@@ -4183,7 +5248,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
int
-main ()
+main (void)
{
return 0;
@@ -4192,18 +5257,19 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
je_cv_cflags_added=-Zi
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
je_cv_cflags_added=
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
CONFIGURE_CFLAGS="${T_CONFIGURE_CFLAGS}"
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
else
@@ -4212,8 +5278,8 @@ fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -MT" >&5
-$as_echo_n "checking whether compiler supports -MT... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -MT" >&5
+printf %s "checking whether compiler supports -MT... " >&6; }
T_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
T_APPEND_V=-MT
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
@@ -4234,7 +5300,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
int
-main ()
+main (void)
{
return 0;
@@ -4243,18 +5309,19 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
je_cv_cflags_added=-MT
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
je_cv_cflags_added=
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
CONFIGURE_CFLAGS="${T_CONFIGURE_CFLAGS}"
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
else
@@ -4263,8 +5330,8 @@ fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -W3" >&5
-$as_echo_n "checking whether compiler supports -W3... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -W3" >&5
+printf %s "checking whether compiler supports -W3... " >&6; }
T_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
T_APPEND_V=-W3
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
@@ -4285,7 +5352,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
int
-main ()
+main (void)
{
return 0;
@@ -4294,18 +5361,19 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
je_cv_cflags_added=-W3
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
je_cv_cflags_added=
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
CONFIGURE_CFLAGS="${T_CONFIGURE_CFLAGS}"
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
else
@@ -4314,8 +5382,8 @@ fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -FS" >&5
-$as_echo_n "checking whether compiler supports -FS... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -FS" >&5
+printf %s "checking whether compiler supports -FS... " >&6; }
T_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
T_APPEND_V=-FS
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
@@ -4336,7 +5404,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
int
-main ()
+main (void)
{
return 0;
@@ -4345,18 +5413,19 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
je_cv_cflags_added=-FS
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
je_cv_cflags_added=
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
CONFIGURE_CFLAGS="${T_CONFIGURE_CFLAGS}"
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
else
@@ -4376,8 +5445,8 @@ fi
if test "x$je_cv_cray" = "xyes" ; then
if test "x$je_cv_cray_84" = "xyes" ; then
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -hipa2" >&5
-$as_echo_n "checking whether compiler supports -hipa2... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -hipa2" >&5
+printf %s "checking whether compiler supports -hipa2... " >&6; }
T_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
T_APPEND_V=-hipa2
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
@@ -4398,7 +5467,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
int
-main ()
+main (void)
{
return 0;
@@ -4407,18 +5476,19 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
je_cv_cflags_added=-hipa2
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
je_cv_cflags_added=
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
CONFIGURE_CFLAGS="${T_CONFIGURE_CFLAGS}"
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
else
@@ -4427,8 +5497,8 @@ fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -hnognu" >&5
-$as_echo_n "checking whether compiler supports -hnognu... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -hnognu" >&5
+printf %s "checking whether compiler supports -hnognu... " >&6; }
T_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
T_APPEND_V=-hnognu
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
@@ -4449,7 +5519,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
int
-main ()
+main (void)
{
return 0;
@@ -4458,18 +5528,19 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
je_cv_cflags_added=-hnognu
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
je_cv_cflags_added=
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
CONFIGURE_CFLAGS="${T_CONFIGURE_CFLAGS}"
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
else
@@ -4479,8 +5550,8 @@ fi
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -hnomessage=128" >&5
-$as_echo_n "checking whether compiler supports -hnomessage=128... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -hnomessage=128" >&5
+printf %s "checking whether compiler supports -hnomessage=128... " >&6; }
T_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
T_APPEND_V=-hnomessage=128
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
@@ -4501,7 +5572,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
int
-main ()
+main (void)
{
return 0;
@@ -4510,18 +5581,19 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
je_cv_cflags_added=-hnomessage=128
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
je_cv_cflags_added=
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
CONFIGURE_CFLAGS="${T_CONFIGURE_CFLAGS}"
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
else
@@ -4530,8 +5602,8 @@ fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -hnomessage=1357" >&5
-$as_echo_n "checking whether compiler supports -hnomessage=1357... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -hnomessage=1357" >&5
+printf %s "checking whether compiler supports -hnomessage=1357... " >&6; }
T_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
T_APPEND_V=-hnomessage=1357
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
@@ -4552,7 +5624,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
int
-main ()
+main (void)
{
return 0;
@@ -4561,18 +5633,19 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
je_cv_cflags_added=-hnomessage=1357
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
je_cv_cflags_added=
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
CONFIGURE_CFLAGS="${T_CONFIGURE_CFLAGS}"
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
else
@@ -4589,40 +5662,36 @@ 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
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5
-$as_echo_n "checking how to run the C preprocessor... " >&6; }
+{ 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 ${ac_cv_prog_CPP+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- # Double quotes because CPP needs to be expanded
- for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp"
+ 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.
- # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
- # <limits.h> exists even on freestanding compilers.
# 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. */
-#ifdef __STDC__
-# include <limits.h>
-#else
-# include <assert.h>
-#endif
+#include <limits.h>
Syntax error
_ACEOF
-if ac_fn_c_try_cpp "$LINENO"; then :
+if ac_fn_c_try_cpp "$LINENO"
+then :
-else
+else $as_nop
# Broken: fails on valid input.
continue
fi
@@ -4634,10 +5703,11 @@ rm -f conftest.err conftest.i conftest.$ac_ext
/* end confdefs.h. */
#include <ac_nonexistent.h>
_ACEOF
-if ac_fn_c_try_cpp "$LINENO"; then :
+if ac_fn_c_try_cpp "$LINENO"
+then :
# Broken: success on invalid input.
continue
-else
+else $as_nop
# Passes both tests.
ac_preproc_ok=:
break
@@ -4647,7 +5717,8 @@ 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 :
+if $ac_preproc_ok
+then :
break
fi
@@ -4659,29 +5730,24 @@ fi
else
ac_cv_prog_CPP=$CPP
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5
-$as_echo "$CPP" >&6; }
+{ 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.
- # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
- # <limits.h> exists even on freestanding compilers.
# 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. */
-#ifdef __STDC__
-# include <limits.h>
-#else
-# include <assert.h>
-#endif
+#include <limits.h>
Syntax error
_ACEOF
-if ac_fn_c_try_cpp "$LINENO"; then :
+if ac_fn_c_try_cpp "$LINENO"
+then :
-else
+else $as_nop
# Broken: fails on valid input.
continue
fi
@@ -4693,10 +5759,11 @@ rm -f conftest.err conftest.i conftest.$ac_ext
/* end confdefs.h. */
#include <ac_nonexistent.h>
_ACEOF
-if ac_fn_c_try_cpp "$LINENO"; then :
+if ac_fn_c_try_cpp "$LINENO"
+then :
# Broken: success on invalid input.
continue
-else
+else $as_nop
# Passes both tests.
ac_preproc_ok=:
break
@@ -4706,11 +5773,12 @@ 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 :
+if $ac_preproc_ok
+then :
-else
- { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-$as_echo "$as_me: error: in \`$ac_pwd':" >&2;}
+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; }
fi
@@ -4723,21 +5791,22 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
# Check whether --enable-cxx was given.
-if test "${enable_cxx+set}" = set; then :
+if test ${enable_cxx+y}
+then :
enableval=$enable_cxx; if test "x$enable_cxx" = "xno" ; then
enable_cxx="0"
else
enable_cxx="1"
fi
-else
+else $as_nop
enable_cxx="1"
fi
if test "x$enable_cxx" = "x1" ; then
# ===========================================================================
-# http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html
+# https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html
# ===========================================================================
#
# SYNOPSIS
@@ -4771,13 +5840,15 @@ if test "x$enable_cxx" = "x1" ; then
# 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 4
+#serial 11
@@ -4798,7 +5869,18 @@ if test "x$enable_cxx" = "x1" ; then
- ac_ext=cpp
+
+
+
+
+
+
+
+
+
+
+
+ac_ext=cpp
ac_cpp='$CXXCPP $CPPFLAGS'
ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
@@ -4808,15 +5890,16 @@ if test -z "$CXX"; then
CXX=$CCC
else
if test -n "$ac_tool_prefix"; then
- for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC
+ for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC clang++
do
# Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.
set dummy $ac_tool_prefix$ac_prog; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_prog_CXX+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_CXX+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
if test -n "$CXX"; then
ac_cv_prog_CXX="$CXX" # Let the user override the test.
else
@@ -4824,11 +5907,15 @@ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
for ac_exec_ext in '' $ac_executable_extensions; do
- if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
ac_cv_prog_CXX="$ac_tool_prefix$ac_prog"
- $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
break 2
fi
done
@@ -4839,11 +5926,11 @@ fi
fi
CXX=$ac_cv_prog_CXX
if test -n "$CXX"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5
-$as_echo "$CXX" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5
+printf "%s\n" "$CXX" >&6; }
else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
fi
@@ -4852,15 +5939,16 @@ fi
fi
if test -z "$CXX"; then
ac_ct_CXX=$CXX
- for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC
+ for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC clang++
do
# Extract the first word of "$ac_prog", so it can be a program name with args.
set dummy $ac_prog; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_prog_ac_ct_CXX+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_ac_ct_CXX+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
if test -n "$ac_ct_CXX"; then
ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test.
else
@@ -4868,11 +5956,15 @@ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
for ac_exec_ext in '' $ac_executable_extensions; do
- if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_CXX="$ac_prog"
- $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
break 2
fi
done
@@ -4883,11 +5975,11 @@ fi
fi
ac_ct_CXX=$ac_cv_prog_ac_ct_CXX
if test -n "$ac_ct_CXX"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5
-$as_echo "$ac_ct_CXX" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5
+printf "%s\n" "$ac_ct_CXX" >&6; }
else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
fi
@@ -4899,8 +5991,8 @@ done
else
case $cross_compiling:$ac_tool_warned in
yes:)
-{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
-$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
ac_tool_warned=yes ;;
esac
CXX=$ac_ct_CXX
@@ -4910,7 +6002,7 @@ fi
fi
fi
# Provide some information about the compiler.
-$as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5
+printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5
set X $ac_compile
ac_compiler=$2
for ac_option in --version -v -V -qversion; do
@@ -4920,7 +6012,7 @@ case "(($ac_try" in
*) ac_try_echo=$ac_try;;
esac
eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\""
-$as_echo "$ac_try_echo"; } >&5
+printf "%s\n" "$ac_try_echo"; } >&5
(eval "$ac_compiler $ac_option >&5") 2>conftest.err
ac_status=$?
if test -s conftest.err; then
@@ -4930,20 +6022,21 @@ $as_echo "$ac_try_echo"; } >&5
cat conftest.er1 >&5
fi
rm -f conftest.er1 conftest.err
- $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
test $ac_status = 0; }
done
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C++ compiler" >&5
-$as_echo_n "checking whether we are using the GNU C++ compiler... " >&6; }
-if ${ac_cv_cxx_compiler_gnu+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports GNU C++" >&5
+printf %s "checking whether the compiler supports GNU C++... " >&6; }
+if test ${ac_cv_cxx_compiler_gnu+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
-main ()
+main (void)
{
#ifndef __GNUC__
choke me
@@ -4953,29 +6046,33 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_cxx_try_compile "$LINENO"; then :
+if ac_fn_cxx_try_compile "$LINENO"
+then :
ac_compiler_gnu=yes
-else
+else $as_nop
ac_compiler_gnu=no
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
ac_cv_cxx_compiler_gnu=$ac_compiler_gnu
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5
-$as_echo "$ac_cv_cxx_compiler_gnu" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5
+printf "%s\n" "$ac_cv_cxx_compiler_gnu" >&6; }
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+
if test $ac_compiler_gnu = yes; then
GXX=yes
else
GXX=
fi
-ac_test_CXXFLAGS=${CXXFLAGS+set}
+ac_test_CXXFLAGS=${CXXFLAGS+y}
ac_save_CXXFLAGS=$CXXFLAGS
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5
-$as_echo_n "checking whether $CXX accepts -g... " >&6; }
-if ${ac_cv_prog_cxx_g+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5
+printf %s "checking whether $CXX accepts -g... " >&6; }
+if test ${ac_cv_prog_cxx_g+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
ac_save_cxx_werror_flag=$ac_cxx_werror_flag
ac_cxx_werror_flag=yes
ac_cv_prog_cxx_g=no
@@ -4984,57 +6081,60 @@ else
/* end confdefs.h. */
int
-main ()
+main (void)
{
;
return 0;
}
_ACEOF
-if ac_fn_cxx_try_compile "$LINENO"; then :
+if ac_fn_cxx_try_compile "$LINENO"
+then :
ac_cv_prog_cxx_g=yes
-else
+else $as_nop
CXXFLAGS=""
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
-main ()
+main (void)
{
;
return 0;
}
_ACEOF
-if ac_fn_cxx_try_compile "$LINENO"; then :
+if ac_fn_cxx_try_compile "$LINENO"
+then :
-else
+else $as_nop
ac_cxx_werror_flag=$ac_save_cxx_werror_flag
CXXFLAGS="-g"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
-main ()
+main (void)
{
;
return 0;
}
_ACEOF
-if ac_fn_cxx_try_compile "$LINENO"; then :
+if ac_fn_cxx_try_compile "$LINENO"
+then :
ac_cv_prog_cxx_g=yes
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
ac_cxx_werror_flag=$ac_save_cxx_werror_flag
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5
-$as_echo "$ac_cv_prog_cxx_g" >&6; }
-if test "$ac_test_CXXFLAGS" = set; then
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5
+printf "%s\n" "$ac_cv_prog_cxx_g" >&6; }
+if test $ac_test_CXXFLAGS; then
CXXFLAGS=$ac_save_CXXFLAGS
elif test $ac_cv_prog_cxx_g = yes; then
if test "$GXX" = yes; then
@@ -5049,6 +6149,100 @@ else
CXXFLAGS=
fi
fi
+ac_prog_cxx_stdcxx=no
+if test x$ac_prog_cxx_stdcxx = xno
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CXX option to enable C++11 features" >&5
+printf %s "checking for $CXX option to enable C++11 features... " >&6; }
+if test ${ac_cv_prog_cxx_11+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ ac_cv_prog_cxx_11=no
+ac_save_CXX=$CXX
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$ac_cxx_conftest_cxx11_program
+_ACEOF
+for ac_arg in '' -std=gnu++11 -std=gnu++0x -std=c++11 -std=c++0x -qlanglvl=extended0x -AA
+do
+ CXX="$ac_save_CXX $ac_arg"
+ if ac_fn_cxx_try_compile "$LINENO"
+then :
+ ac_cv_prog_cxx_cxx11=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam
+ test "x$ac_cv_prog_cxx_cxx11" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CXX=$ac_save_CXX
+fi
+
+if test "x$ac_cv_prog_cxx_cxx11" = 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_cxx_cxx11" = 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_cxx_cxx11" >&5
+printf "%s\n" "$ac_cv_prog_cxx_cxx11" >&6; }
+ CXX="$CXX $ac_cv_prog_cxx_cxx11"
+fi
+ ac_cv_prog_cxx_stdcxx=$ac_cv_prog_cxx_cxx11
+ ac_prog_cxx_stdcxx=cxx11
+fi
+fi
+if test x$ac_prog_cxx_stdcxx = xno
+then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CXX option to enable C++98 features" >&5
+printf %s "checking for $CXX option to enable C++98 features... " >&6; }
+if test ${ac_cv_prog_cxx_98+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ ac_cv_prog_cxx_98=no
+ac_save_CXX=$CXX
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$ac_cxx_conftest_cxx98_program
+_ACEOF
+for ac_arg in '' -std=gnu++98 -std=c++98 -qlanglvl=extended -AA
+do
+ CXX="$ac_save_CXX $ac_arg"
+ if ac_fn_cxx_try_compile "$LINENO"
+then :
+ ac_cv_prog_cxx_cxx98=$ac_arg
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam
+ test "x$ac_cv_prog_cxx_cxx98" != "xno" && break
+done
+rm -f conftest.$ac_ext
+CXX=$ac_save_CXX
+fi
+
+if test "x$ac_cv_prog_cxx_cxx98" = 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_cxx_cxx98" = 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_cxx_cxx98" >&5
+printf "%s\n" "$ac_cv_prog_cxx_cxx98" >&6; }
+ CXX="$CXX $ac_cv_prog_cxx_cxx98"
+fi
+ ac_cv_prog_cxx_stdcxx=$ac_cv_prog_cxx_cxx98
+ ac_prog_cxx_stdcxx=cxx98
+fi
+fi
+
ac_ext=cpp
ac_cpp='$CXXCPP $CPPFLAGS'
ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
@@ -5056,19 +6250,29 @@ ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ex
ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
- ax_cxx_compile_cxx14_required=false
+ ax_cxx_compile_alternatives="17 1z" ax_cxx_compile_cxx17_required=false
ac_ext=cpp
ac_cpp='$CXXCPP $CPPFLAGS'
ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
ac_success=no
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX supports C++14 features by default" >&5
-$as_echo_n "checking whether $CXX supports C++14 features by default... " >&6; }
-if ${ax_cv_cxx_compile_cxx14+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+
+
+
+ if test x$ac_success = xno; then
+ for alternative in ${ax_cxx_compile_alternatives}; do
+ for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do
+ cachevar=`printf "%s\n" "ax_cv_cxx_compile_cxx17_$switch" | $as_tr_sh`
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CXX supports C++17 features with $switch" >&5
+printf %s "checking whether $CXX supports C++17 features with $switch... " >&6; }
+if eval test \${$cachevar+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ ac_save_CXX="$CXX"
+ CXX="$CXX $switch"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
@@ -5104,11 +6308,13 @@ namespace cxx11
struct Base
{
+ virtual ~Base() {}
virtual void f() {}
};
struct Derived : public Base
{
+ virtual ~Derived() override {}
virtual void f() override {}
};
@@ -5433,7 +6639,7 @@ namespace cxx14
}
- namespace test_digit_seperators
+ namespace test_digit_separators
{
constexpr auto ten_million = 100'000'000;
@@ -5476,33 +6682,458 @@ namespace cxx14
+
+// 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
+
+
+
_ACEOF
-if ac_fn_cxx_try_compile "$LINENO"; then :
- ax_cv_cxx_compile_cxx14=yes
-else
- ax_cv_cxx_compile_cxx14=no
+if ac_fn_cxx_try_compile "$LINENO"
+then :
+ eval $cachevar=yes
+else $as_nop
+ eval $cachevar=no
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+ CXX="$ac_save_CXX"
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_cxx_compile_cxx14" >&5
-$as_echo "$ax_cv_cxx_compile_cxx14" >&6; }
- if test x$ax_cv_cxx_compile_cxx14 = xyes; then
- ac_success=yes
+eval ac_res=\$$cachevar
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf "%s\n" "$ac_res" >&6; }
+ 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_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
+
+ if test x$ax_cxx_compile_cxx17_required = xtrue; then
+ if test x$ac_success = xno; then
+ as_fn_error $? "*** A compiler with support for C++17 language features is required." "$LINENO" 5
+ fi
fi
+ if test x$ac_success = xno; then
+ HAVE_CXX17=0
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: No compiler with C++17 support was found" >&5
+printf "%s\n" "$as_me: No compiler with C++17 support was found" >&6;}
+ else
+ HAVE_CXX17=1
+
+printf "%s\n" "#define HAVE_CXX17 1" >>confdefs.h
+
+ fi
+
+
+ if test "x${HAVE_CXX17}" != "x1"; then
+ ax_cxx_compile_alternatives="14 1y" ax_cxx_compile_cxx14_required=false
+ ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+ ac_success=no
if test x$ac_success = xno; then
- for switch in -std=c++14 -std=c++0x +std=c++14 "-h std=c++14"; do
- cachevar=`$as_echo "ax_cv_cxx_compile_cxx14_$switch" | $as_tr_sh`
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX supports C++14 features with $switch" >&5
-$as_echo_n "checking whether $CXX supports C++14 features with $switch... " >&6; }
-if eval \${$cachevar+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+ for alternative in ${ax_cxx_compile_alternatives}; do
+ for switch in -std=c++${alternative} +std=c++${alternative} "-h std=c++${alternative}"; do
+ cachevar=`printf "%s\n" "ax_cv_cxx_compile_cxx14_$switch" | $as_tr_sh`
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CXX supports C++14 features with $switch" >&5
+printf %s "checking whether $CXX supports C++14 features with $switch... " >&6; }
+if eval test \${$cachevar+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
ac_save_CXX="$CXX"
- CXX="$CXX $switch"
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+ CXX="$CXX $switch"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
@@ -5538,11 +7169,13 @@ namespace cxx11
struct Base
{
+ virtual ~Base() {}
virtual void f() {}
};
struct Derived : public Base
{
+ virtual ~Derived() override {}
virtual void f() override {}
};
@@ -5867,7 +7500,7 @@ namespace cxx14
}
- namespace test_digit_seperators
+ namespace test_digit_separators
{
constexpr auto ten_million = 100'000'000;
@@ -5911,23 +7544,28 @@ namespace cxx14
_ACEOF
-if ac_fn_cxx_try_compile "$LINENO"; then :
+if ac_fn_cxx_try_compile "$LINENO"
+then :
eval $cachevar=yes
-else
+else $as_nop
eval $cachevar=no
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
- CXX="$ac_save_CXX"
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+ CXX="$ac_save_CXX"
fi
eval ac_res=\$$cachevar
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
-$as_echo "$ac_res" >&6; }
- if eval test x\$$cachevar = xyes; then
- CXX="$CXX $switch"
- if test -n "$CXXCPP" ; then
- CXXCPP="$CXXCPP $switch"
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+printf "%s\n" "$ac_res" >&6; }
+ if eval test x\$$cachevar = xyes; then
+ CXX="$CXX $switch"
+ if test -n "$CXXCPP" ; then
+ CXXCPP="$CXXCPP $switch"
+ fi
+ ac_success=yes
+ break
fi
- ac_success=yes
+ done
+ if test x$ac_success = xyes; then
break
fi
done
@@ -5945,20 +7583,21 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
fi
if test x$ac_success = xno; then
HAVE_CXX14=0
- { $as_echo "$as_me:${as_lineno-$LINENO}: No compiler with C++14 support was found" >&5
-$as_echo "$as_me: No compiler with C++14 support was found" >&6;}
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: No compiler with C++14 support was found" >&5
+printf "%s\n" "$as_me: No compiler with C++14 support was found" >&6;}
else
HAVE_CXX14=1
-$as_echo "#define HAVE_CXX14 1" >>confdefs.h
+printf "%s\n" "#define HAVE_CXX14 1" >>confdefs.h
fi
- if test "x${HAVE_CXX14}" = "x1" ; then
+ fi
+ if test "x${HAVE_CXX14}" = "x1" -o "x${HAVE_CXX17}" = "x1"; then
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -Wall" >&5
-$as_echo_n "checking whether compiler supports -Wall... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -Wall" >&5
+printf %s "checking whether compiler supports -Wall... " >&6; }
T_CONFIGURE_CXXFLAGS="${CONFIGURE_CXXFLAGS}"
T_APPEND_V=-Wall
if test "x${CONFIGURE_CXXFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
@@ -5985,7 +7624,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
int
-main ()
+main (void)
{
return 0;
@@ -5994,18 +7633,19 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_cxx_try_compile "$LINENO"; then :
+if ac_fn_cxx_try_compile "$LINENO"
+then :
je_cv_cxxflags_added=-Wall
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
je_cv_cxxflags_added=
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
CONFIGURE_CXXFLAGS="${T_CONFIGURE_CXXFLAGS}"
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
@@ -6020,8 +7660,8 @@ fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -Wextra" >&5
-$as_echo_n "checking whether compiler supports -Wextra... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -Wextra" >&5
+printf %s "checking whether compiler supports -Wextra... " >&6; }
T_CONFIGURE_CXXFLAGS="${CONFIGURE_CXXFLAGS}"
T_APPEND_V=-Wextra
if test "x${CONFIGURE_CXXFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
@@ -6048,7 +7688,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
int
-main ()
+main (void)
{
return 0;
@@ -6057,18 +7697,19 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_cxx_try_compile "$LINENO"; then :
+if ac_fn_cxx_try_compile "$LINENO"
+then :
je_cv_cxxflags_added=-Wextra
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
je_cv_cxxflags_added=
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
CONFIGURE_CXXFLAGS="${T_CONFIGURE_CXXFLAGS}"
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
@@ -6083,8 +7724,8 @@ fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -g3" >&5
-$as_echo_n "checking whether compiler supports -g3... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -g3" >&5
+printf %s "checking whether compiler supports -g3... " >&6; }
T_CONFIGURE_CXXFLAGS="${CONFIGURE_CXXFLAGS}"
T_APPEND_V=-g3
if test "x${CONFIGURE_CXXFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
@@ -6111,7 +7752,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
int
-main ()
+main (void)
{
return 0;
@@ -6120,18 +7761,19 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_cxx_try_compile "$LINENO"; then :
+if ac_fn_cxx_try_compile "$LINENO"
+then :
je_cv_cxxflags_added=-g3
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
je_cv_cxxflags_added=
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
CONFIGURE_CXXFLAGS="${T_CONFIGURE_CXXFLAGS}"
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
@@ -6156,18 +7798,19 @@ fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether libstdc++ linkage is compilable" >&5
-$as_echo_n "checking whether libstdc++ linkage is compilable... " >&6; }
-if ${je_cv_libstdcxx+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether libstdc++ linkage is compilable" >&5
+printf %s "checking whether libstdc++ linkage is compilable... " >&6; }
+if test ${je_cv_libstdcxx+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <stdlib.h>
int
-main ()
+main (void)
{
int *arr = (int *)malloc(sizeof(int) * 42);
@@ -6178,16 +7821,17 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
+if ac_fn_c_try_link "$LINENO"
+then :
je_cv_libstdcxx=yes
-else
+else $as_nop
je_cv_libstdcxx=no
fi
-rm -f core conftest.err conftest.$ac_objext \
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $je_cv_libstdcxx" >&5
-$as_echo "$je_cv_libstdcxx" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $je_cv_libstdcxx" >&5
+printf "%s\n" "$je_cv_libstdcxx" >&6; }
if test "x${je_cv_libstdcxx}" = "xno" ; then
LIBS="${SAVED_LIBS}"
@@ -6196,276 +7840,51 @@ $as_echo "$je_cv_libstdcxx" >&6; }
enable_cxx="0"
fi
fi
+if test "x$enable_cxx" = "x1"; then
+printf "%s\n" "#define JEMALLOC_ENABLE_CXX " >>confdefs.h
+fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5
-$as_echo_n "checking for grep that handles long lines and -e... " >&6; }
-if ${ac_cv_path_GREP+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- 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
-for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin
-do
- IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
- for ac_prog in grep ggrep; do
- for ac_exec_ext in '' $ac_executable_extensions; do
- ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext"
- 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
-*GNU*)
- ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;;
-*)
- ac_count=0
- $as_echo_n 0123456789 >"conftest.in"
- while :
- do
- cat "conftest.in" "conftest.in" >"conftest.tmp"
- mv "conftest.tmp" "conftest.in"
- cp "conftest.in" "conftest.nl"
- $as_echo 'GREP' >> "conftest.nl"
- "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "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_GREP_max-0}; then
- # Best one so far, save it but keep looking for a better one
- ac_cv_path_GREP="$ac_path_GREP"
- ac_path_GREP_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_GREP_found && break 3
- done
- done
- done
-IFS=$as_save_IFS
- if test -z "$ac_cv_path_GREP"; then
- as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
- fi
-else
- ac_cv_path_GREP=$GREP
-fi
-
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5
-$as_echo "$ac_cv_path_GREP" >&6; }
- GREP="$ac_cv_path_GREP"
-
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5
-$as_echo_n "checking for egrep... " >&6; }
-if ${ac_cv_path_EGREP+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- if echo a | $GREP -E '(a|b)' >/dev/null 2>&1
- then ac_cv_path_EGREP="$GREP -E"
- else
- if test -z "$EGREP"; then
- ac_path_EGREP_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
+ac_header= ac_cache=
+for ac_item in $ac_header_c_list
do
- IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
- for ac_prog in egrep; do
- for ac_exec_ext in '' $ac_executable_extensions; do
- ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext"
- 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
-*GNU*)
- ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;;
-*)
- ac_count=0
- $as_echo_n 0123456789 >"conftest.in"
- while :
- do
- cat "conftest.in" "conftest.in" >"conftest.tmp"
- mv "conftest.tmp" "conftest.in"
- cp "conftest.in" "conftest.nl"
- $as_echo 'EGREP' >> "conftest.nl"
- "$ac_path_EGREP" 'EGREP$' < "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_max-0}; then
- # Best one so far, save it but keep looking for a better one
- ac_cv_path_EGREP="$ac_path_EGREP"
- ac_path_EGREP_max=$ac_count
+ if test $ac_cache; then
+ ac_fn_c_check_header_compile "$LINENO" $ac_header ac_cv_header_$ac_cache "$ac_includes_default"
+ if eval test \"x\$ac_cv_header_$ac_cache\" = xyes; then
+ printf "%s\n" "#define $ac_item 1" >> confdefs.h
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_found && break 3
- done
- done
- done
-IFS=$as_save_IFS
- if test -z "$ac_cv_path_EGREP"; then
- as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5
+ ac_header= ac_cache=
+ elif test $ac_header; then
+ ac_cache=$ac_item
+ else
+ ac_header=$ac_item
fi
-else
- ac_cv_path_EGREP=$EGREP
-fi
-
- fi
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5
-$as_echo "$ac_cv_path_EGREP" >&6; }
- EGREP="$ac_cv_path_EGREP"
-
-
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5
-$as_echo_n "checking for ANSI C header files... " >&6; }
-if ${ac_cv_header_stdc+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
-#include <stdlib.h>
-#include <stdarg.h>
-#include <string.h>
-#include <float.h>
-
-int
-main ()
-{
-
- ;
- return 0;
-}
-_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
- ac_cv_header_stdc=yes
-else
- ac_cv_header_stdc=no
-fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-
-if test $ac_cv_header_stdc = yes; then
- # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
-#include <string.h>
-
-_ACEOF
-if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
- $EGREP "memchr" >/dev/null 2>&1; then :
-
-else
- ac_cv_header_stdc=no
-fi
-rm -f conftest*
-
-fi
-
-if test $ac_cv_header_stdc = yes; then
- # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
-#include <stdlib.h>
+done
-_ACEOF
-if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
- $EGREP "free" >/dev/null 2>&1; then :
-else
- ac_cv_header_stdc=no
-fi
-rm -f conftest*
-fi
-if test $ac_cv_header_stdc = yes; then
- # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
- if test "$cross_compiling" = yes; then :
- :
-else
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
-#include <ctype.h>
-#include <stdlib.h>
-#if ((' ' & 0x0FF) == 0x020)
-# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
-# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
-#else
-# define ISLOWER(c) \
- (('a' <= (c) && (c) <= 'i') \
- || ('j' <= (c) && (c) <= 'r') \
- || ('s' <= (c) && (c) <= 'z'))
-# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))
-#endif
-
-#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))
-int
-main ()
-{
- int i;
- for (i = 0; i < 256; i++)
- if (XOR (islower (i), ISLOWER (i))
- || toupper (i) != TOUPPER (i))
- return 2;
- return 0;
-}
-_ACEOF
-if ac_fn_c_try_run "$LINENO"; then :
-else
- ac_cv_header_stdc=no
-fi
-rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
- conftest.$ac_objext conftest.beam conftest.$ac_ext
-fi
-fi
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5
-$as_echo "$ac_cv_header_stdc" >&6; }
-if test $ac_cv_header_stdc = yes; then
-$as_echo "#define STDC_HEADERS 1" >>confdefs.h
-fi
+if test $ac_cv_header_stdlib_h = yes && test $ac_cv_header_string_h = yes
+then :
-# On IRIX 5.3, sys/types and inttypes.h are conflicting.
-for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \
- inttypes.h stdint.h unistd.h
-do :
- as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
-ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default
-"
-if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
- cat >>confdefs.h <<_ACEOF
-#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
-_ACEOF
+printf "%s\n" "#define STDC_HEADERS 1" >>confdefs.h
fi
-
-done
-
-
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether byte ordering is bigendian" >&5
-$as_echo_n "checking whether byte ordering is bigendian... " >&6; }
-if ${ac_cv_c_bigendian+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether byte ordering is bigendian" >&5
+printf %s "checking whether byte ordering is bigendian... " >&6; }
+if test ${ac_cv_c_bigendian+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
ac_cv_c_bigendian=unknown
# See if we're dealing with a universal compiler.
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
@@ -6476,7 +7895,8 @@ else
typedef int dummy;
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
# Check for potential -arch flags. It is not universal unless
# there are at least two -arch flags with different values.
@@ -6500,7 +7920,7 @@ if ac_fn_c_try_compile "$LINENO"; then :
fi
done
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
if test $ac_cv_c_bigendian = unknown; then
# See if sys/param.h defines the BYTE_ORDER macro.
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
@@ -6509,7 +7929,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
#include <sys/param.h>
int
-main ()
+main (void)
{
#if ! (defined BYTE_ORDER && defined BIG_ENDIAN \
&& defined LITTLE_ENDIAN && BYTE_ORDER && BIG_ENDIAN \
@@ -6521,7 +7941,8 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
# It does; now see whether it defined to BIG_ENDIAN or not.
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
@@ -6529,7 +7950,7 @@ if ac_fn_c_try_compile "$LINENO"; then :
#include <sys/param.h>
int
-main ()
+main (void)
{
#if BYTE_ORDER != BIG_ENDIAN
not big endian
@@ -6539,14 +7960,15 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
ac_cv_c_bigendian=yes
-else
+else $as_nop
ac_cv_c_bigendian=no
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
if test $ac_cv_c_bigendian = unknown; then
# See if <limits.h> defines _LITTLE_ENDIAN or _BIG_ENDIAN (e.g., Solaris).
@@ -6555,7 +7977,7 @@ rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
#include <limits.h>
int
-main ()
+main (void)
{
#if ! (defined _LITTLE_ENDIAN || defined _BIG_ENDIAN)
bogus endian macros
@@ -6565,14 +7987,15 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
# It does; now see whether it defined to _BIG_ENDIAN or not.
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <limits.h>
int
-main ()
+main (void)
{
#ifndef _BIG_ENDIAN
not big endian
@@ -6582,31 +8005,33 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
ac_cv_c_bigendian=yes
-else
+else $as_nop
ac_cv_c_bigendian=no
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
if test $ac_cv_c_bigendian = unknown; then
# Compile a test program.
- if test "$cross_compiling" = yes; then :
+ if test "$cross_compiling" = yes
+then :
# Try to guess by grepping values from an object file.
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
-short int ascii_mm[] =
+unsigned short int ascii_mm[] =
{ 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 };
- short int ascii_ii[] =
+ unsigned short int ascii_ii[] =
{ 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 };
int use_ascii (int i) {
return ascii_mm[i] + ascii_ii[i];
}
- short int ebcdic_ii[] =
+ unsigned short int ebcdic_ii[] =
{ 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 };
- short int ebcdic_mm[] =
+ unsigned short int ebcdic_mm[] =
{ 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 };
int use_ebcdic (int i) {
return ebcdic_mm[i] + ebcdic_ii[i];
@@ -6614,14 +8039,15 @@ short int ascii_mm[] =
extern int foo;
int
-main ()
+main (void)
{
return use_ascii (foo) == use_ebcdic (foo);
;
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
if grep BIGenDianSyS conftest.$ac_objext >/dev/null; then
ac_cv_c_bigendian=yes
fi
@@ -6634,13 +8060,13 @@ if ac_fn_c_try_compile "$LINENO"; then :
fi
fi
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-else
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
$ac_includes_default
int
-main ()
+main (void)
{
/* Are we little or big endian? From Harbison&Steele. */
@@ -6656,9 +8082,10 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_run "$LINENO"; then :
+if ac_fn_c_try_run "$LINENO"
+then :
ac_cv_c_bigendian=no
-else
+else $as_nop
ac_cv_c_bigendian=yes
fi
rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
@@ -6667,8 +8094,8 @@ fi
fi
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_bigendian" >&5
-$as_echo "$ac_cv_c_bigendian" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_bigendian" >&5
+printf "%s\n" "$ac_cv_c_bigendian" >&6; }
case $ac_cv_c_bigendian in #(
yes)
ac_cv_big_endian=1;; #(
@@ -6676,7 +8103,7 @@ $as_echo "$ac_cv_c_bigendian" >&6; }
ac_cv_big_endian=0 ;; #(
universal)
-$as_echo "#define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h
+printf "%s\n" "#define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h
;; #(
*)
@@ -6685,9 +8112,8 @@ $as_echo "#define AC_APPLE_UNIVERSAL_BUILD 1" >>confdefs.h
esac
if test "x${ac_cv_big_endian}" = "x1" ; then
- cat >>confdefs.h <<_ACEOF
-#define JEMALLOC_BIG_ENDIAN
-_ACEOF
+
+printf "%s\n" "#define JEMALLOC_BIG_ENDIAN " >>confdefs.h
fi
@@ -6704,24 +8130,26 @@ fi
if test "x${je_cv_msvc}" = "xyes" ; then
LG_SIZEOF_PTR=LG_SIZEOF_PTR_WIN
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: Using a predefined value for sizeof(void *): 4 for 32-bit, 8 for 64-bit" >&5
-$as_echo "Using a predefined value for sizeof(void *): 4 for 32-bit, 8 for 64-bit" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Using a predefined value for sizeof(void *): 4 for 32-bit, 8 for 64-bit" >&5
+printf "%s\n" "Using a predefined value for sizeof(void *): 4 for 32-bit, 8 for 64-bit" >&6; }
else
# 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]];'.
# This bug is HP SR number 8606223364.
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of void *" >&5
-$as_echo_n "checking size of void *... " >&6; }
-if ${ac_cv_sizeof_void_p+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (void *))" "ac_cv_sizeof_void_p" "$ac_includes_default"; then :
-
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of void *" >&5
+printf %s "checking size of void *... " >&6; }
+if test ${ac_cv_sizeof_void_p+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (void *))" "ac_cv_sizeof_void_p" "$ac_includes_default"
+then :
+
+else $as_nop
if test "$ac_cv_type_void_p" = yes; then
- { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-$as_echo "$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 compute sizeof (void *)
See \`config.log' for more details" "$LINENO" 5; }
else
@@ -6730,14 +8158,12 @@ See \`config.log' for more details" "$LINENO" 5; }
fi
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_void_p" >&5
-$as_echo "$ac_cv_sizeof_void_p" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_void_p" >&5
+printf "%s\n" "$ac_cv_sizeof_void_p" >&6; }
-cat >>confdefs.h <<_ACEOF
-#define SIZEOF_VOID_P $ac_cv_sizeof_void_p
-_ACEOF
+printf "%s\n" "#define SIZEOF_VOID_P $ac_cv_sizeof_void_p" >>confdefs.h
if test "x${ac_cv_sizeof_void_p}" = "x8" ; then
@@ -6748,26 +8174,27 @@ _ACEOF
as_fn_error $? "Unsupported pointer size: ${ac_cv_sizeof_void_p}" "$LINENO" 5
fi
fi
-cat >>confdefs.h <<_ACEOF
-#define LG_SIZEOF_PTR $LG_SIZEOF_PTR
-_ACEOF
+
+printf "%s\n" "#define LG_SIZEOF_PTR $LG_SIZEOF_PTR" >>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]];'.
# This bug is HP SR number 8606223364.
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of int" >&5
-$as_echo_n "checking size of int... " >&6; }
-if ${ac_cv_sizeof_int+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (int))" "ac_cv_sizeof_int" "$ac_includes_default"; then :
-
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of int" >&5
+printf %s "checking size of int... " >&6; }
+if test ${ac_cv_sizeof_int+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (int))" "ac_cv_sizeof_int" "$ac_includes_default"
+then :
+
+else $as_nop
if test "$ac_cv_type_int" = yes; then
- { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-$as_echo "$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 compute sizeof (int)
See \`config.log' for more details" "$LINENO" 5; }
else
@@ -6776,14 +8203,12 @@ See \`config.log' for more details" "$LINENO" 5; }
fi
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_int" >&5
-$as_echo "$ac_cv_sizeof_int" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_int" >&5
+printf "%s\n" "$ac_cv_sizeof_int" >&6; }
-cat >>confdefs.h <<_ACEOF
-#define SIZEOF_INT $ac_cv_sizeof_int
-_ACEOF
+printf "%s\n" "#define SIZEOF_INT $ac_cv_sizeof_int" >>confdefs.h
if test "x${ac_cv_sizeof_int}" = "x8" ; then
@@ -6793,26 +8218,27 @@ elif test "x${ac_cv_sizeof_int}" = "x4" ; then
else
as_fn_error $? "Unsupported int size: ${ac_cv_sizeof_int}" "$LINENO" 5
fi
-cat >>confdefs.h <<_ACEOF
-#define LG_SIZEOF_INT $LG_SIZEOF_INT
-_ACEOF
+
+printf "%s\n" "#define LG_SIZEOF_INT $LG_SIZEOF_INT" >>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]];'.
# This bug is HP SR number 8606223364.
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of long" >&5
-$as_echo_n "checking size of long... " >&6; }
-if ${ac_cv_sizeof_long+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (long))" "ac_cv_sizeof_long" "$ac_includes_default"; then :
-
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of long" >&5
+printf %s "checking size of long... " >&6; }
+if test ${ac_cv_sizeof_long+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (long))" "ac_cv_sizeof_long" "$ac_includes_default"
+then :
+
+else $as_nop
if test "$ac_cv_type_long" = yes; then
- { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-$as_echo "$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 compute sizeof (long)
See \`config.log' for more details" "$LINENO" 5; }
else
@@ -6821,14 +8247,12 @@ See \`config.log' for more details" "$LINENO" 5; }
fi
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_long" >&5
-$as_echo "$ac_cv_sizeof_long" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_long" >&5
+printf "%s\n" "$ac_cv_sizeof_long" >&6; }
-cat >>confdefs.h <<_ACEOF
-#define SIZEOF_LONG $ac_cv_sizeof_long
-_ACEOF
+printf "%s\n" "#define SIZEOF_LONG $ac_cv_sizeof_long" >>confdefs.h
if test "x${ac_cv_sizeof_long}" = "x8" ; then
@@ -6838,26 +8262,27 @@ elif test "x${ac_cv_sizeof_long}" = "x4" ; then
else
as_fn_error $? "Unsupported long size: ${ac_cv_sizeof_long}" "$LINENO" 5
fi
-cat >>confdefs.h <<_ACEOF
-#define LG_SIZEOF_LONG $LG_SIZEOF_LONG
-_ACEOF
+
+printf "%s\n" "#define LG_SIZEOF_LONG $LG_SIZEOF_LONG" >>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]];'.
# This bug is HP SR number 8606223364.
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of long long" >&5
-$as_echo_n "checking size of long long... " >&6; }
-if ${ac_cv_sizeof_long_long+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (long long))" "ac_cv_sizeof_long_long" "$ac_includes_default"; then :
-
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of long long" >&5
+printf %s "checking size of long long... " >&6; }
+if test ${ac_cv_sizeof_long_long+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (long long))" "ac_cv_sizeof_long_long" "$ac_includes_default"
+then :
+
+else $as_nop
if test "$ac_cv_type_long_long" = yes; then
- { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-$as_echo "$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 compute sizeof (long long)
See \`config.log' for more details" "$LINENO" 5; }
else
@@ -6866,14 +8291,12 @@ See \`config.log' for more details" "$LINENO" 5; }
fi
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_long_long" >&5
-$as_echo "$ac_cv_sizeof_long_long" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_long_long" >&5
+printf "%s\n" "$ac_cv_sizeof_long_long" >&6; }
-cat >>confdefs.h <<_ACEOF
-#define SIZEOF_LONG_LONG $ac_cv_sizeof_long_long
-_ACEOF
+printf "%s\n" "#define SIZEOF_LONG_LONG $ac_cv_sizeof_long_long" >>confdefs.h
if test "x${ac_cv_sizeof_long_long}" = "x8" ; then
@@ -6883,26 +8306,27 @@ elif test "x${ac_cv_sizeof_long_long}" = "x4" ; then
else
as_fn_error $? "Unsupported long long size: ${ac_cv_sizeof_long_long}" "$LINENO" 5
fi
-cat >>confdefs.h <<_ACEOF
-#define LG_SIZEOF_LONG_LONG $LG_SIZEOF_LONG_LONG
-_ACEOF
+
+printf "%s\n" "#define LG_SIZEOF_LONG_LONG $LG_SIZEOF_LONG_LONG" >>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]];'.
# This bug is HP SR number 8606223364.
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking size of intmax_t" >&5
-$as_echo_n "checking size of intmax_t... " >&6; }
-if ${ac_cv_sizeof_intmax_t+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (intmax_t))" "ac_cv_sizeof_intmax_t" "$ac_includes_default"; then :
-
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking size of intmax_t" >&5
+printf %s "checking size of intmax_t... " >&6; }
+if test ${ac_cv_sizeof_intmax_t+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ if ac_fn_c_compute_int "$LINENO" "(long int) (sizeof (intmax_t))" "ac_cv_sizeof_intmax_t" "$ac_includes_default"
+then :
+
+else $as_nop
if test "$ac_cv_type_intmax_t" = yes; then
- { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5
-$as_echo "$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 compute sizeof (intmax_t)
See \`config.log' for more details" "$LINENO" 5; }
else
@@ -6911,14 +8335,12 @@ See \`config.log' for more details" "$LINENO" 5; }
fi
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_intmax_t" >&5
-$as_echo "$ac_cv_sizeof_intmax_t" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sizeof_intmax_t" >&5
+printf "%s\n" "$ac_cv_sizeof_intmax_t" >&6; }
-cat >>confdefs.h <<_ACEOF
-#define SIZEOF_INTMAX_T $ac_cv_sizeof_intmax_t
-_ACEOF
+printf "%s\n" "#define SIZEOF_INTMAX_T $ac_cv_sizeof_intmax_t" >>confdefs.h
if test "x${ac_cv_sizeof_intmax_t}" = "x16" ; then
@@ -6930,31 +8352,34 @@ elif test "x${ac_cv_sizeof_intmax_t}" = "x4" ; then
else
as_fn_error $? "Unsupported intmax_t size: ${ac_cv_sizeof_intmax_t}" "$LINENO" 5
fi
-cat >>confdefs.h <<_ACEOF
-#define LG_SIZEOF_INTMAX_T $LG_SIZEOF_INTMAX_T
-_ACEOF
+printf "%s\n" "#define LG_SIZEOF_INTMAX_T $LG_SIZEOF_INTMAX_T" >>confdefs.h
-# Make sure we can run config.sub.
-$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 ||
- as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5
-$as_echo_n "checking build system type... " >&6; }
-if ${ac_cv_build+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+
+
+
+ # Make sure we can run config.sub.
+$SHELL "${ac_aux_dir}config.sub" sun4 >/dev/null 2>&1 ||
+ as_fn_error $? "cannot run $SHELL ${ac_aux_dir}config.sub" "$LINENO" 5
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking build system type" >&5
+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
test "x$ac_build_alias" = x &&
- ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"`
+ 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
+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
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5
-$as_echo "$ac_cv_build" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5
+printf "%s\n" "$ac_cv_build" >&6; }
case $ac_cv_build in
*-*-*) ;;
*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;;
@@ -6973,21 +8398,22 @@ IFS=$ac_save_IFS
case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5
-$as_echo_n "checking host system type... " >&6; }
-if ${ac_cv_host+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking host system type" >&5
+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
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
+ 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
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5
-$as_echo "$ac_cv_host" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5
+printf "%s\n" "$ac_cv_host" >&6; }
case $ac_cv_host in
*-*-*) ;;
*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;;
@@ -7011,36 +8437,39 @@ case "${host_cpu}" in
i686|x86_64)
HAVE_CPU_SPINWAIT=1
if test "x${je_cv_msvc}" = "xyes" ; then
- if ${je_cv_pause_msvc+:} false; then :
- $as_echo_n "(cached) " >&6
-else
-
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether pause instruction MSVC is compilable" >&5
-$as_echo_n "checking whether pause instruction MSVC is compilable... " >&6; }
-if ${je_cv_pause_msvc+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+ if test ${je_cv_pause_msvc+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether pause instruction MSVC is compilable" >&5
+printf %s "checking whether pause instruction MSVC is compilable... " >&6; }
+if test ${je_cv_pause_msvc+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
-main ()
+main (void)
{
_mm_pause(); return 0;
;
return 0;
}
_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
+if ac_fn_c_try_link "$LINENO"
+then :
je_cv_pause_msvc=yes
-else
+else $as_nop
je_cv_pause_msvc=no
fi
-rm -f core conftest.err conftest.$ac_objext \
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $je_cv_pause_msvc" >&5
-$as_echo "$je_cv_pause_msvc" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $je_cv_pause_msvc" >&5
+printf "%s\n" "$je_cv_pause_msvc" >&6; }
fi
@@ -7048,36 +8477,39 @@ fi
CPU_SPINWAIT='_mm_pause()'
fi
else
- if ${je_cv_pause+:} false; then :
- $as_echo_n "(cached) " >&6
-else
-
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether pause instruction is compilable" >&5
-$as_echo_n "checking whether pause instruction is compilable... " >&6; }
-if ${je_cv_pause+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+ if test ${je_cv_pause+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether pause instruction is compilable" >&5
+printf %s "checking whether pause instruction is compilable... " >&6; }
+if test ${je_cv_pause+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
-main ()
+main (void)
{
__asm__ volatile("pause"); return 0;
;
return 0;
}
_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
+if ac_fn_c_try_link "$LINENO"
+then :
je_cv_pause=yes
-else
+else $as_nop
je_cv_pause=no
fi
-rm -f core conftest.err conftest.$ac_objext \
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $je_cv_pause" >&5
-$as_echo "$je_cv_pause" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $je_cv_pause" >&5
+printf "%s\n" "$je_cv_pause" >&6; }
fi
@@ -7086,24 +8518,65 @@ fi
fi
fi
;;
+ aarch64|arm*)
+ HAVE_CPU_SPINWAIT=1
+ if test ${je_cv_isb+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether isb instruction is compilable" >&5
+printf %s "checking whether isb instruction is compilable... " >&6; }
+if test ${je_cv_isb+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main (void)
+{
+__asm__ volatile("isb"); return 0;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+ je_cv_isb=yes
+else $as_nop
+ je_cv_isb=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $je_cv_isb" >&5
+printf "%s\n" "$je_cv_isb" >&6; }
+
+fi
+
+ if test "x${je_cv_isb}" = "xyes" ; then
+ CPU_SPINWAIT='__asm__ volatile("isb")'
+ fi
+ ;;
*)
HAVE_CPU_SPINWAIT=0
;;
esac
-cat >>confdefs.h <<_ACEOF
-#define HAVE_CPU_SPINWAIT $HAVE_CPU_SPINWAIT
-_ACEOF
-cat >>confdefs.h <<_ACEOF
-#define CPU_SPINWAIT $CPU_SPINWAIT
-_ACEOF
+printf "%s\n" "#define HAVE_CPU_SPINWAIT $HAVE_CPU_SPINWAIT" >>confdefs.h
+
+
+printf "%s\n" "#define CPU_SPINWAIT $CPU_SPINWAIT" >>confdefs.h
# Check whether --with-lg_vaddr was given.
-if test "${with_lg_vaddr+set}" = set; then :
+if test ${with_lg_vaddr+y}
+then :
withval=$with_lg_vaddr; LG_VADDR="$with_lg_vaddr"
-else
+else $as_nop
LG_VADDR="detect"
fi
@@ -7111,8 +8584,8 @@ fi
case "${host_cpu}" in
aarch64)
if test "x$LG_VADDR" = "xdetect"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking number of significant virtual address bits" >&5
-$as_echo_n "checking number of significant virtual address bits... " >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking number of significant virtual address bits" >&5
+printf %s "checking number of significant virtual address bits... " >&6; }
if test "x${LG_SIZEOF_PTR}" = "x2" ; then
#aarch64 ILP32
LG_VADDR=32
@@ -7120,20 +8593,22 @@ $as_echo_n "checking number of significant virtual address bits... " >&6; }
#aarch64 LP64
LG_VADDR=48
fi
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LG_VADDR" >&5
-$as_echo "$LG_VADDR" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $LG_VADDR" >&5
+printf "%s\n" "$LG_VADDR" >&6; }
fi
;;
x86_64)
if test "x$LG_VADDR" = "xdetect"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking number of significant virtual address bits" >&5
-$as_echo_n "checking number of significant virtual address bits... " >&6; }
-if ${je_cv_lg_vaddr+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- if test "$cross_compiling" = yes; then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking number of significant virtual address bits" >&5
+printf %s "checking number of significant virtual address bits... " >&6; }
+if test ${je_cv_lg_vaddr+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ if test "$cross_compiling" = yes
+then :
je_cv_lg_vaddr=57
-else
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
@@ -7147,7 +8622,7 @@ typedef unsigned __int32 uint32_t;
#endif
int
-main ()
+main (void)
{
uint32_t r[4];
@@ -7177,9 +8652,10 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_run "$LINENO"; then :
+if ac_fn_c_try_run "$LINENO"
+then :
je_cv_lg_vaddr=`cat conftest.out`
-else
+else $as_nop
je_cv_lg_vaddr=error
fi
rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
@@ -7187,15 +8663,14 @@ rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
fi
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $je_cv_lg_vaddr" >&5
-$as_echo "$je_cv_lg_vaddr" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $je_cv_lg_vaddr" >&5
+printf "%s\n" "$je_cv_lg_vaddr" >&6; }
if test "x${je_cv_lg_vaddr}" != "x" ; then
LG_VADDR="${je_cv_lg_vaddr}"
fi
if test "x${LG_VADDR}" != "xerror" ; then
- cat >>confdefs.h <<_ACEOF
-#define LG_VADDR $LG_VADDR
-_ACEOF
+
+printf "%s\n" "#define LG_VADDR $LG_VADDR" >>confdefs.h
else
as_fn_error $? "cannot determine number of significant virtual address bits" "$LINENO" 5
@@ -7204,8 +8679,8 @@ _ACEOF
;;
*)
if test "x$LG_VADDR" = "xdetect"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking number of significant virtual address bits" >&5
-$as_echo_n "checking number of significant virtual address bits... " >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking number of significant virtual address bits" >&5
+printf %s "checking number of significant virtual address bits... " >&6; }
if test "x${LG_SIZEOF_PTR}" = "x3" ; then
LG_VADDR=64
elif test "x${LG_SIZEOF_PTR}" = "x2" ; then
@@ -7215,14 +8690,13 @@ $as_echo_n "checking number of significant virtual address bits... " >&6; }
else
as_fn_error $? "Unsupported lg(pointer size): ${LG_SIZEOF_PTR}" "$LINENO" 5
fi
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LG_VADDR" >&5
-$as_echo "$LG_VADDR" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $LG_VADDR" >&5
+printf "%s\n" "$LG_VADDR" >&6; }
fi
;;
esac
-cat >>confdefs.h <<_ACEOF
-#define LG_VADDR $LG_VADDR
-_ACEOF
+
+printf "%s\n" "#define LG_VADDR $LG_VADDR" >>confdefs.h
LD_PRELOAD_VAR="LD_PRELOAD"
@@ -7241,7 +8715,7 @@ CTARGET='-o $@'
LDTARGET='-o $@'
TEST_LD_MODE=
EXTRA_LDFLAGS=
-ARFLAGS='crus'
+ARFLAGS='crs'
AROUT=' $@'
CC_MM=1
@@ -7259,11 +8733,12 @@ fi
if test -n "$ac_tool_prefix"; then
# Extract the first word of "${ac_tool_prefix}ar", so it can be a program name with args.
set dummy ${ac_tool_prefix}ar; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_prog_AR+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+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
ac_cv_prog_AR="$AR" # Let the user override the test.
else
@@ -7271,11 +8746,15 @@ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
for ac_exec_ext in '' $ac_executable_extensions; do
- if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
ac_cv_prog_AR="${ac_tool_prefix}ar"
- $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
break 2
fi
done
@@ -7286,11 +8765,11 @@ fi
fi
AR=$ac_cv_prog_AR
if test -n "$AR"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5
-$as_echo "$AR" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $AR" >&5
+printf "%s\n" "$AR" >&6; }
else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
fi
@@ -7299,11 +8778,12 @@ if test -z "$ac_cv_prog_AR"; then
ac_ct_AR=$AR
# Extract the first word of "ar", so it can be a program name with args.
set dummy ar; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_prog_ac_ct_AR+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+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
ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test.
else
@@ -7311,11 +8791,15 @@ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
for ac_exec_ext in '' $ac_executable_extensions; do
- if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_AR="ar"
- $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
break 2
fi
done
@@ -7326,11 +8810,11 @@ fi
fi
ac_ct_AR=$ac_cv_prog_ac_ct_AR
if test -n "$ac_ct_AR"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5
-$as_echo "$ac_ct_AR" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5
+printf "%s\n" "$ac_ct_AR" >&6; }
else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
fi
if test "x$ac_ct_AR" = x; then
@@ -7338,8 +8822,8 @@ fi
else
case $cross_compiling:$ac_tool_warned in
yes:)
-{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
-$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
ac_tool_warned=yes ;;
esac
AR=$ac_ct_AR
@@ -7355,11 +8839,12 @@ fi
if test -n "$ac_tool_prefix"; then
# Extract the first word of "${ac_tool_prefix}nm", so it can be a program name with args.
set dummy ${ac_tool_prefix}nm; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_prog_NM+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_NM+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
if test -n "$NM"; then
ac_cv_prog_NM="$NM" # Let the user override the test.
else
@@ -7367,11 +8852,15 @@ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
for ac_exec_ext in '' $ac_executable_extensions; do
- if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
ac_cv_prog_NM="${ac_tool_prefix}nm"
- $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
break 2
fi
done
@@ -7382,11 +8871,11 @@ fi
fi
NM=$ac_cv_prog_NM
if test -n "$NM"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $NM" >&5
-$as_echo "$NM" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $NM" >&5
+printf "%s\n" "$NM" >&6; }
else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
fi
@@ -7395,11 +8884,12 @@ if test -z "$ac_cv_prog_NM"; then
ac_ct_NM=$NM
# Extract the first word of "nm", so it can be a program name with args.
set dummy nm; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_prog_ac_ct_NM+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_prog_ac_ct_NM+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
if test -n "$ac_ct_NM"; then
ac_cv_prog_ac_ct_NM="$ac_ct_NM" # Let the user override the test.
else
@@ -7407,11 +8897,15 @@ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
for ac_exec_ext in '' $ac_executable_extensions; do
- if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_NM="nm"
- $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
break 2
fi
done
@@ -7422,11 +8916,11 @@ fi
fi
ac_ct_NM=$ac_cv_prog_ac_ct_NM
if test -n "$ac_ct_NM"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_NM" >&5
-$as_echo "$ac_ct_NM" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_NM" >&5
+printf "%s\n" "$ac_ct_NM" >&6; }
else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
fi
if test "x$ac_ct_NM" = x; then
@@ -7434,8 +8928,8 @@ fi
else
case $cross_compiling:$ac_tool_warned in
yes:)
-{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
-$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
ac_tool_warned=yes ;;
esac
NM=$ac_ct_NM
@@ -7449,11 +8943,12 @@ for ac_prog in gawk mawk nawk awk
do
# Extract the first word of "$ac_prog", so it can be a program name with args.
set dummy $ac_prog; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_prog_AWK+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+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
ac_cv_prog_AWK="$AWK" # Let the user override the test.
else
@@ -7461,11 +8956,15 @@ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
for ac_exec_ext in '' $ac_executable_extensions; do
- if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
ac_cv_prog_AWK="$ac_prog"
- $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
break 2
fi
done
@@ -7476,11 +8975,11 @@ fi
fi
AWK=$ac_cv_prog_AWK
if test -n "$AWK"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5
-$as_echo "$AWK" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5
+printf "%s\n" "$AWK" >&6; }
else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
fi
@@ -7491,7 +8990,8 @@ done
# Check whether --with-version was given.
-if test "${with_version+set}" = set; then :
+if test ${with_version+y}
+then :
withval=$with_version;
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
@@ -7503,7 +9003,7 @@ if test "${with_version+set}" = set; then :
fi
fi
-else
+else $as_nop
if test "x`test ! \"${srcroot}\" && cd \"${srcroot}\"; git rev-parse --is-inside-work-tree 2>/dev/null`" = "xtrue" ; then
for pattern in '[0-9].[0-9].[0-9]' '[0-9].[0-9].[0-9][0-9]' \
@@ -7525,9 +9025,9 @@ fi
if test ! -e "${objroot}VERSION" ; then
if test ! -e "${srcroot}VERSION" ; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: Missing VERSION file, and unable to generate it; creating bogus VERSION" >&5
-$as_echo "Missing VERSION file, and unable to generate it; creating bogus VERSION" >&6; }
- echo "0.0.0-0-g0000000000000000000000000000000000000000" > "${objroot}VERSION"
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Missing VERSION file, and unable to generate it; creating bogus VERSION" >&5
+printf "%s\n" "Missing VERSION file, and unable to generate it; creating bogus VERSION" >&6; }
+ echo "0.0.0-0-g000000missing_version_try_git_fetch_tags" > "${objroot}VERSION"
else
cp ${srcroot}VERSION ${objroot}VERSION
fi
@@ -7546,6 +9046,7 @@ jemalloc_version_gid=`echo ${jemalloc_version} | tr ".g-" " " | awk '{print $5}'
default_retain="0"
+zero_realloc_default_free="0"
maps_coalesce="1"
DUMP_SYMS="${NM} -a"
SYM_PREFIX=""
@@ -7563,8 +9064,17 @@ case "${host}" in
SYM_PREFIX="_"
;;
*-*-freebsd*)
+ T_APPEND_V=-D_BSD_SOURCE
+ if test "x${CPPFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
+ CPPFLAGS="${CPPFLAGS}${T_APPEND_V}"
+else
+ CPPFLAGS="${CPPFLAGS} ${T_APPEND_V}"
+fi
+
+
abi="elf"
- $as_echo "#define JEMALLOC_SYSCTL_VM_OVERCOMMIT " >>confdefs.h
+
+printf "%s\n" "#define JEMALLOC_SYSCTL_VM_OVERCOMMIT " >>confdefs.h
force_lazy_lock="1"
;;
@@ -7578,7 +9088,7 @@ case "${host}" in
*-*-bitrig*)
abi="elf"
;;
- *-*-linux-android)
+ *-*-linux-android*)
T_APPEND_V=-D_GNU_SOURCE
if test "x${CPPFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
CPPFLAGS="${CPPFLAGS}${T_APPEND_V}"
@@ -7588,20 +9098,27 @@ fi
abi="elf"
- $as_echo "#define JEMALLOC_PURGE_MADVISE_DONTNEED_ZEROS " >>confdefs.h
+ glibc="0"
+
+printf "%s\n" "#define JEMALLOC_PURGE_MADVISE_DONTNEED_ZEROS " >>confdefs.h
+
+
+printf "%s\n" "#define JEMALLOC_HAS_ALLOCA_H " >>confdefs.h
+
+
+printf "%s\n" "#define JEMALLOC_PROC_SYS_VM_OVERCOMMIT_MEMORY " >>confdefs.h
- $as_echo "#define JEMALLOC_HAS_ALLOCA_H 1" >>confdefs.h
- $as_echo "#define JEMALLOC_PROC_SYS_VM_OVERCOMMIT_MEMORY " >>confdefs.h
+printf "%s\n" "#define JEMALLOC_THREADED_INIT " >>confdefs.h
- $as_echo "#define JEMALLOC_THREADED_INIT " >>confdefs.h
- $as_echo "#define JEMALLOC_C11_ATOMICS 1" >>confdefs.h
+printf "%s\n" "#define JEMALLOC_C11_ATOMICS " >>confdefs.h
force_tls="0"
if test "${LG_SIZEOF_PTR}" = "3"; then
default_retain="1"
fi
+ zero_realloc_default_free="1"
;;
*-*-linux*)
T_APPEND_V=-D_GNU_SOURCE
@@ -7613,19 +9130,26 @@ fi
abi="elf"
- $as_echo "#define JEMALLOC_PURGE_MADVISE_DONTNEED_ZEROS " >>confdefs.h
+ glibc="1"
- $as_echo "#define JEMALLOC_HAS_ALLOCA_H 1" >>confdefs.h
+printf "%s\n" "#define JEMALLOC_PURGE_MADVISE_DONTNEED_ZEROS " >>confdefs.h
- $as_echo "#define JEMALLOC_PROC_SYS_VM_OVERCOMMIT_MEMORY " >>confdefs.h
- $as_echo "#define JEMALLOC_THREADED_INIT " >>confdefs.h
+printf "%s\n" "#define JEMALLOC_HAS_ALLOCA_H " >>confdefs.h
- $as_echo "#define JEMALLOC_USE_CXX_THROW " >>confdefs.h
+
+printf "%s\n" "#define JEMALLOC_PROC_SYS_VM_OVERCOMMIT_MEMORY " >>confdefs.h
+
+
+printf "%s\n" "#define JEMALLOC_THREADED_INIT " >>confdefs.h
+
+
+printf "%s\n" "#define JEMALLOC_USE_CXX_THROW " >>confdefs.h
if test "${LG_SIZEOF_PTR}" = "3"; then
default_retain="1"
fi
+ zero_realloc_default_free="1"
;;
*-*-kfreebsd*)
T_APPEND_V=-D_GNU_SOURCE
@@ -7637,18 +9161,22 @@ fi
abi="elf"
- $as_echo "#define JEMALLOC_HAS_ALLOCA_H 1" >>confdefs.h
- $as_echo "#define JEMALLOC_SYSCTL_VM_OVERCOMMIT " >>confdefs.h
+printf "%s\n" "#define JEMALLOC_HAS_ALLOCA_H " >>confdefs.h
+
+
+printf "%s\n" "#define JEMALLOC_SYSCTL_VM_OVERCOMMIT " >>confdefs.h
+
+
+printf "%s\n" "#define JEMALLOC_THREADED_INIT " >>confdefs.h
- $as_echo "#define JEMALLOC_THREADED_INIT " >>confdefs.h
- $as_echo "#define JEMALLOC_USE_CXX_THROW " >>confdefs.h
+printf "%s\n" "#define JEMALLOC_USE_CXX_THROW " >>confdefs.h
;;
*-*-netbsd*)
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking ABI" >&5
-$as_echo_n "checking ABI... " >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking ABI" >&5
+printf %s "checking ABI... " >&6; }
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#ifdef __ELF__
@@ -7658,21 +9186,22 @@ $as_echo_n "checking ABI... " >&6; }
#endif
int
-main ()
+main (void)
{
;
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
abi="elf"
-else
+else $as_nop
abi="aout"
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $abi" >&5
-$as_echo "$abi" >&6; }
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $abi" >&5
+printf "%s\n" "$abi" >&6; }
;;
*-*-solaris2*)
abi="elf"
@@ -7737,25 +9266,32 @@ fi
if test "${LG_SIZEOF_PTR}" = "3"; then
default_retain="1"
fi
+ zero_realloc_default_free="1"
+ ;;
+ *-*-nto-qnx)
+ abi="elf"
+ force_tls="0"
+
+printf "%s\n" "#define JEMALLOC_HAS_ALLOCA_H " >>confdefs.h
+
;;
*)
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: Unsupported operating system: ${host}" >&5
-$as_echo "Unsupported operating system: ${host}" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Unsupported operating system: ${host}" >&5
+printf "%s\n" "Unsupported operating system: ${host}" >&6; }
abi="elf"
;;
esac
JEMALLOC_USABLE_SIZE_CONST=const
-for ac_header in malloc.h
+ for ac_header in malloc.h
do :
- ac_fn_c_check_header_mongrel "$LINENO" "malloc.h" "ac_cv_header_malloc_h" "$ac_includes_default"
-if test "x$ac_cv_header_malloc_h" = xyes; then :
- cat >>confdefs.h <<_ACEOF
-#define HAVE_MALLOC_H 1
-_ACEOF
+ ac_fn_c_check_header_compile "$LINENO" "malloc.h" "ac_cv_header_malloc_h" "$ac_includes_default"
+if test "x$ac_cv_header_malloc_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_MALLOC_H 1" >>confdefs.h
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether malloc_usable_size definition can use const argument" >&5
-$as_echo_n "checking whether malloc_usable_size definition can use const argument... " >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether malloc_usable_size definition can use const argument" >&5
+printf %s "checking whether malloc_usable_size definition can use const argument... " >&6; }
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <malloc.h>
@@ -7763,34 +9299,33 @@ $as_echo_n "checking whether malloc_usable_size definition can use const argumen
size_t malloc_usable_size(const void *ptr);
int
-main ()
+main (void)
{
;
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
-else
+else $as_nop
JEMALLOC_USABLE_SIZE_CONST=
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
done
-cat >>confdefs.h <<_ACEOF
-#define JEMALLOC_USABLE_SIZE_CONST $JEMALLOC_USABLE_SIZE_CONST
-_ACEOF
+printf "%s\n" "#define JEMALLOC_USABLE_SIZE_CONST $JEMALLOC_USABLE_SIZE_CONST" >>confdefs.h
@@ -7815,11 +9350,12 @@ _ACEOF
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing log" >&5
-$as_echo_n "checking for library containing log... " >&6; }
-if ${ac_cv_search_log+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing log" >&5
+printf %s "checking for library containing log... " >&6; }
+if test ${ac_cv_search_log+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
ac_func_search_save_LIBS=$LIBS
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
@@ -7827,49 +9363,51 @@ 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. */
-#ifdef __cplusplus
-extern "C"
-#endif
char log ();
int
-main ()
+main (void)
{
return log ();
;
return 0;
}
_ACEOF
-for ac_lib in '' m; do
+for ac_lib in '' m
+do
if test -z "$ac_lib"; then
ac_res="none required"
else
ac_res=-l$ac_lib
LIBS="-l$ac_lib $ac_func_search_save_LIBS"
fi
- if ac_fn_c_try_link "$LINENO"; then :
+ if ac_fn_c_try_link "$LINENO"
+then :
ac_cv_search_log=$ac_res
fi
-rm -f core conftest.err conftest.$ac_objext \
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext
- if ${ac_cv_search_log+:} false; then :
+ if test ${ac_cv_search_log+y}
+then :
break
fi
done
-if ${ac_cv_search_log+:} false; then :
+if test ${ac_cv_search_log+y}
+then :
-else
+else $as_nop
ac_cv_search_log=no
fi
rm conftest.$ac_ext
LIBS=$ac_func_search_save_LIBS
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_log" >&5
-$as_echo "$ac_cv_search_log" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_log" >&5
+printf "%s\n" "$ac_cv_search_log" >&6; }
ac_res=$ac_cv_search_log
-if test "$ac_res" != no; then :
+if test "$ac_res" != no
+then :
test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
-else
+else $as_nop
as_fn_error $? "Missing math functions" "$LINENO" 5
fi
@@ -7881,40 +9419,43 @@ fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether __attribute__ syntax is compilable" >&5
-$as_echo_n "checking whether __attribute__ syntax is compilable... " >&6; }
-if ${je_cv_attribute+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether __attribute__ syntax is compilable" >&5
+printf %s "checking whether __attribute__ syntax is compilable... " >&6; }
+if test ${je_cv_attribute+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
static __attribute__((unused)) void foo(void){}
int
-main ()
+main (void)
{
;
return 0;
}
_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
+if ac_fn_c_try_link "$LINENO"
+then :
je_cv_attribute=yes
-else
+else $as_nop
je_cv_attribute=no
fi
-rm -f core conftest.err conftest.$ac_objext \
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $je_cv_attribute" >&5
-$as_echo "$je_cv_attribute" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $je_cv_attribute" >&5
+printf "%s\n" "$je_cv_attribute" >&6; }
if test "x${je_cv_attribute}" = "xyes" ; then
- $as_echo "#define JEMALLOC_HAVE_ATTR " >>confdefs.h
+
+printf "%s\n" "#define JEMALLOC_HAVE_ATTR " >>confdefs.h
if test "x${GCC}" = "xyes" -a "x${abi}" = "xelf"; then
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -fvisibility=hidden" >&5
-$as_echo_n "checking whether compiler supports -fvisibility=hidden... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -fvisibility=hidden" >&5
+printf %s "checking whether compiler supports -fvisibility=hidden... " >&6; }
T_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
T_APPEND_V=-fvisibility=hidden
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
@@ -7935,7 +9476,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
int
-main ()
+main (void)
{
return 0;
@@ -7944,18 +9485,19 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
je_cv_cflags_added=-fvisibility=hidden
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
je_cv_cflags_added=
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
CONFIGURE_CFLAGS="${T_CONFIGURE_CFLAGS}"
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
else
@@ -7964,8 +9506,8 @@ fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -fvisibility=hidden" >&5
-$as_echo_n "checking whether compiler supports -fvisibility=hidden... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -fvisibility=hidden" >&5
+printf %s "checking whether compiler supports -fvisibility=hidden... " >&6; }
T_CONFIGURE_CXXFLAGS="${CONFIGURE_CXXFLAGS}"
T_APPEND_V=-fvisibility=hidden
if test "x${CONFIGURE_CXXFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
@@ -7992,7 +9534,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
int
-main ()
+main (void)
{
return 0;
@@ -8001,18 +9543,19 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_cxx_try_compile "$LINENO"; then :
+if ac_fn_cxx_try_compile "$LINENO"
+then :
je_cv_cxxflags_added=-fvisibility=hidden
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
je_cv_cxxflags_added=
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
CONFIGURE_CXXFLAGS="${T_CONFIGURE_CXXFLAGS}"
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
@@ -8031,8 +9574,8 @@ fi
SAVED_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -Werror" >&5
-$as_echo_n "checking whether compiler supports -Werror... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -Werror" >&5
+printf %s "checking whether compiler supports -Werror... " >&6; }
T_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
T_APPEND_V=-Werror
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
@@ -8053,7 +9596,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
int
-main ()
+main (void)
{
return 0;
@@ -8062,18 +9605,19 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
je_cv_cflags_added=-Werror
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
je_cv_cflags_added=
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
CONFIGURE_CFLAGS="${T_CONFIGURE_CFLAGS}"
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
else
@@ -8082,8 +9626,8 @@ fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -herror_on_warning" >&5
-$as_echo_n "checking whether compiler supports -herror_on_warning... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -herror_on_warning" >&5
+printf %s "checking whether compiler supports -herror_on_warning... " >&6; }
T_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
T_APPEND_V=-herror_on_warning
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
@@ -8104,7 +9648,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
int
-main ()
+main (void)
{
return 0;
@@ -8113,18 +9657,19 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
je_cv_cflags_added=-herror_on_warning
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
je_cv_cflags_added=
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
CONFIGURE_CFLAGS="${T_CONFIGURE_CFLAGS}"
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
else
@@ -8133,16 +9678,17 @@ fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether tls_model attribute is compilable" >&5
-$as_echo_n "checking whether tls_model attribute is compilable... " >&6; }
-if ${je_cv_tls_model+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether tls_model attribute is compilable" >&5
+printf %s "checking whether tls_model attribute is compilable... " >&6; }
+if test ${je_cv_tls_model+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
-main ()
+main (void)
{
static __thread int
__attribute__((tls_model("initial-exec"), unused)) foo;
@@ -8151,16 +9697,17 @@ static __thread int
return 0;
}
_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
+if ac_fn_c_try_link "$LINENO"
+then :
je_cv_tls_model=yes
-else
+else $as_nop
je_cv_tls_model=no
fi
-rm -f core conftest.err conftest.$ac_objext \
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $je_cv_tls_model" >&5
-$as_echo "$je_cv_tls_model" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $je_cv_tls_model" >&5
+printf "%s\n" "$je_cv_tls_model" >&6; }
CONFIGURE_CFLAGS="${SAVED_CONFIGURE_CFLAGS}"
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
@@ -8174,8 +9721,8 @@ fi
SAVED_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -Werror" >&5
-$as_echo_n "checking whether compiler supports -Werror... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -Werror" >&5
+printf %s "checking whether compiler supports -Werror... " >&6; }
T_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
T_APPEND_V=-Werror
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
@@ -8196,7 +9743,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
int
-main ()
+main (void)
{
return 0;
@@ -8205,18 +9752,19 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
je_cv_cflags_added=-Werror
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
je_cv_cflags_added=
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
CONFIGURE_CFLAGS="${T_CONFIGURE_CFLAGS}"
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
else
@@ -8225,8 +9773,8 @@ fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -herror_on_warning" >&5
-$as_echo_n "checking whether compiler supports -herror_on_warning... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -herror_on_warning" >&5
+printf %s "checking whether compiler supports -herror_on_warning... " >&6; }
T_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
T_APPEND_V=-herror_on_warning
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
@@ -8247,7 +9795,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
int
-main ()
+main (void)
{
return 0;
@@ -8256,18 +9804,19 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
je_cv_cflags_added=-herror_on_warning
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
je_cv_cflags_added=
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
CONFIGURE_CFLAGS="${T_CONFIGURE_CFLAGS}"
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
else
@@ -8276,32 +9825,34 @@ fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether alloc_size attribute is compilable" >&5
-$as_echo_n "checking whether alloc_size attribute is compilable... " >&6; }
-if ${je_cv_alloc_size+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether alloc_size attribute is compilable" >&5
+printf %s "checking whether alloc_size attribute is compilable... " >&6; }
+if test ${je_cv_alloc_size+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <stdlib.h>
int
-main ()
+main (void)
{
void *foo(size_t size) __attribute__((alloc_size(1)));
;
return 0;
}
_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
+if ac_fn_c_try_link "$LINENO"
+then :
je_cv_alloc_size=yes
-else
+else $as_nop
je_cv_alloc_size=no
fi
-rm -f core conftest.err conftest.$ac_objext \
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $je_cv_alloc_size" >&5
-$as_echo "$je_cv_alloc_size" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $je_cv_alloc_size" >&5
+printf "%s\n" "$je_cv_alloc_size" >&6; }
CONFIGURE_CFLAGS="${SAVED_CONFIGURE_CFLAGS}"
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
@@ -8312,14 +9863,15 @@ fi
if test "x${je_cv_alloc_size}" = "xyes" ; then
- $as_echo "#define JEMALLOC_HAVE_ATTR_ALLOC_SIZE " >>confdefs.h
+
+printf "%s\n" "#define JEMALLOC_HAVE_ATTR_ALLOC_SIZE " >>confdefs.h
fi
SAVED_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -Werror" >&5
-$as_echo_n "checking whether compiler supports -Werror... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -Werror" >&5
+printf %s "checking whether compiler supports -Werror... " >&6; }
T_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
T_APPEND_V=-Werror
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
@@ -8340,7 +9892,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
int
-main ()
+main (void)
{
return 0;
@@ -8349,18 +9901,19 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
je_cv_cflags_added=-Werror
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
je_cv_cflags_added=
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
CONFIGURE_CFLAGS="${T_CONFIGURE_CFLAGS}"
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
else
@@ -8369,8 +9922,8 @@ fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -herror_on_warning" >&5
-$as_echo_n "checking whether compiler supports -herror_on_warning... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -herror_on_warning" >&5
+printf %s "checking whether compiler supports -herror_on_warning... " >&6; }
T_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
T_APPEND_V=-herror_on_warning
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
@@ -8391,7 +9944,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
int
-main ()
+main (void)
{
return 0;
@@ -8400,18 +9953,19 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
je_cv_cflags_added=-herror_on_warning
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
je_cv_cflags_added=
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
CONFIGURE_CFLAGS="${T_CONFIGURE_CFLAGS}"
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
else
@@ -8420,32 +9974,34 @@ fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether format(gnu_printf, ...) attribute is compilable" >&5
-$as_echo_n "checking whether format(gnu_printf, ...) attribute is compilable... " >&6; }
-if ${je_cv_format_gnu_printf+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether format(gnu_printf, ...) attribute is compilable" >&5
+printf %s "checking whether format(gnu_printf, ...) attribute is compilable... " >&6; }
+if test ${je_cv_format_gnu_printf+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <stdlib.h>
int
-main ()
+main (void)
{
void *foo(const char *format, ...) __attribute__((format(gnu_printf, 1, 2)));
;
return 0;
}
_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
+if ac_fn_c_try_link "$LINENO"
+then :
je_cv_format_gnu_printf=yes
-else
+else $as_nop
je_cv_format_gnu_printf=no
fi
-rm -f core conftest.err conftest.$ac_objext \
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $je_cv_format_gnu_printf" >&5
-$as_echo "$je_cv_format_gnu_printf" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $je_cv_format_gnu_printf" >&5
+printf "%s\n" "$je_cv_format_gnu_printf" >&6; }
CONFIGURE_CFLAGS="${SAVED_CONFIGURE_CFLAGS}"
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
@@ -8456,14 +10012,15 @@ fi
if test "x${je_cv_format_gnu_printf}" = "xyes" ; then
- $as_echo "#define JEMALLOC_HAVE_ATTR_FORMAT_GNU_PRINTF " >>confdefs.h
+
+printf "%s\n" "#define JEMALLOC_HAVE_ATTR_FORMAT_GNU_PRINTF " >>confdefs.h
fi
SAVED_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -Werror" >&5
-$as_echo_n "checking whether compiler supports -Werror... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -Werror" >&5
+printf %s "checking whether compiler supports -Werror... " >&6; }
T_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
T_APPEND_V=-Werror
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
@@ -8484,7 +10041,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
int
-main ()
+main (void)
{
return 0;
@@ -8493,18 +10050,19 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
je_cv_cflags_added=-Werror
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
je_cv_cflags_added=
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
CONFIGURE_CFLAGS="${T_CONFIGURE_CFLAGS}"
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
else
@@ -8513,8 +10071,8 @@ fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -herror_on_warning" >&5
-$as_echo_n "checking whether compiler supports -herror_on_warning... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -herror_on_warning" >&5
+printf %s "checking whether compiler supports -herror_on_warning... " >&6; }
T_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
T_APPEND_V=-herror_on_warning
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
@@ -8535,7 +10093,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
int
-main ()
+main (void)
{
return 0;
@@ -8544,18 +10102,19 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
je_cv_cflags_added=-herror_on_warning
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
je_cv_cflags_added=
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
CONFIGURE_CFLAGS="${T_CONFIGURE_CFLAGS}"
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
else
@@ -8564,32 +10123,34 @@ fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether format(printf, ...) attribute is compilable" >&5
-$as_echo_n "checking whether format(printf, ...) attribute is compilable... " >&6; }
-if ${je_cv_format_printf+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether format(printf, ...) attribute is compilable" >&5
+printf %s "checking whether format(printf, ...) attribute is compilable... " >&6; }
+if test ${je_cv_format_printf+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <stdlib.h>
int
-main ()
+main (void)
{
void *foo(const char *format, ...) __attribute__((format(printf, 1, 2)));
;
return 0;
}
_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
+if ac_fn_c_try_link "$LINENO"
+then :
je_cv_format_printf=yes
-else
+else $as_nop
je_cv_format_printf=no
fi
-rm -f core conftest.err conftest.$ac_objext \
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $je_cv_format_printf" >&5
-$as_echo "$je_cv_format_printf" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $je_cv_format_printf" >&5
+printf "%s\n" "$je_cv_format_printf" >&6; }
CONFIGURE_CFLAGS="${SAVED_CONFIGURE_CFLAGS}"
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
@@ -8600,15 +10161,16 @@ fi
if test "x${je_cv_format_printf}" = "xyes" ; then
- $as_echo "#define JEMALLOC_HAVE_ATTR_FORMAT_PRINTF " >>confdefs.h
+
+printf "%s\n" "#define JEMALLOC_HAVE_ATTR_FORMAT_PRINTF " >>confdefs.h
fi
SAVED_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -Werror" >&5
-$as_echo_n "checking whether compiler supports -Werror... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -Werror" >&5
+printf %s "checking whether compiler supports -Werror... " >&6; }
T_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
T_APPEND_V=-Werror
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
@@ -8629,7 +10191,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
int
-main ()
+main (void)
{
return 0;
@@ -8638,18 +10200,19 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
je_cv_cflags_added=-Werror
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
je_cv_cflags_added=
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
CONFIGURE_CFLAGS="${T_CONFIGURE_CFLAGS}"
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
else
@@ -8658,8 +10221,8 @@ fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -herror_on_warning" >&5
-$as_echo_n "checking whether compiler supports -herror_on_warning... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -herror_on_warning" >&5
+printf %s "checking whether compiler supports -herror_on_warning... " >&6; }
T_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
T_APPEND_V=-herror_on_warning
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
@@ -8680,7 +10243,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
int
-main ()
+main (void)
{
return 0;
@@ -8689,18 +10252,19 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
je_cv_cflags_added=-herror_on_warning
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
je_cv_cflags_added=
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
CONFIGURE_CFLAGS="${T_CONFIGURE_CFLAGS}"
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
else
@@ -8709,32 +10273,34 @@ fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether format(printf, ...) attribute is compilable" >&5
-$as_echo_n "checking whether format(printf, ...) attribute is compilable... " >&6; }
-if ${je_cv_format_arg+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether format(printf, ...) attribute is compilable" >&5
+printf %s "checking whether format(printf, ...) attribute is compilable... " >&6; }
+if test ${je_cv_format_arg+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <stdlib.h>
int
-main ()
+main (void)
{
const char * __attribute__((__format_arg__(1))) foo(const char *format);
;
return 0;
}
_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
+if ac_fn_c_try_link "$LINENO"
+then :
je_cv_format_arg=yes
-else
+else $as_nop
je_cv_format_arg=no
fi
-rm -f core conftest.err conftest.$ac_objext \
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $je_cv_format_arg" >&5
-$as_echo "$je_cv_format_arg" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $je_cv_format_arg" >&5
+printf "%s\n" "$je_cv_format_arg" >&6; }
CONFIGURE_CFLAGS="${SAVED_CONFIGURE_CFLAGS}"
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
@@ -8745,19 +10311,430 @@ fi
if test "x${je_cv_format_arg}" = "xyes" ; then
- $as_echo "#define JEMALLOC_HAVE_ATTR_FORMAT_ARG " >>confdefs.h
+
+printf "%s\n" "#define JEMALLOC_HAVE_ATTR_FORMAT_ARG " >>confdefs.h
+
+fi
+
+SAVED_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -Wimplicit-fallthrough" >&5
+printf %s "checking whether compiler supports -Wimplicit-fallthrough... " >&6; }
+T_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
+T_APPEND_V=-Wimplicit-fallthrough
+ if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
+ CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}${T_APPEND_V}"
+else
+ CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS} ${T_APPEND_V}"
+fi
+
+
+if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
+ CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
+else
+ CFLAGS="${CONFIGURE_CFLAGS} ${SPECIFIED_CFLAGS}"
+fi
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+
+int
+main (void)
+{
+
+ return 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ je_cv_cflags_added=-Wimplicit-fallthrough
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
+ je_cv_cflags_added=
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+ CONFIGURE_CFLAGS="${T_CONFIGURE_CFLAGS}"
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
+ CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
+else
+ CFLAGS="${CONFIGURE_CFLAGS} ${SPECIFIED_CFLAGS}"
+fi
+
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether fallthrough attribute is compilable" >&5
+printf %s "checking whether fallthrough attribute is compilable... " >&6; }
+if test ${je_cv_fallthrough+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#if !__has_attribute(fallthrough)
+ #error "foo"
+ #endif
+int
+main (void)
+{
+int x = 0;
+ switch (x) {
+ case 0: __attribute__((__fallthrough__));
+ case 1: return 1;
+ }
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+ je_cv_fallthrough=yes
+else $as_nop
+ je_cv_fallthrough=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $je_cv_fallthrough" >&5
+printf "%s\n" "$je_cv_fallthrough" >&6; }
+
+CONFIGURE_CFLAGS="${SAVED_CONFIGURE_CFLAGS}"
+if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
+ CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
+else
+ CFLAGS="${CONFIGURE_CFLAGS} ${SPECIFIED_CFLAGS}"
+fi
+
+
+if test "x${je_cv_fallthrough}" = "xyes" ; then
+
+printf "%s\n" "#define JEMALLOC_HAVE_ATTR_FALLTHROUGH " >>confdefs.h
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -Wimplicit-fallthrough" >&5
+printf %s "checking whether compiler supports -Wimplicit-fallthrough... " >&6; }
+T_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
+T_APPEND_V=-Wimplicit-fallthrough
+ if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
+ CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}${T_APPEND_V}"
+else
+ CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS} ${T_APPEND_V}"
+fi
+
+
+if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
+ CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
+else
+ CFLAGS="${CONFIGURE_CFLAGS} ${SPECIFIED_CFLAGS}"
+fi
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+
+int
+main (void)
+{
+
+ return 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ je_cv_cflags_added=-Wimplicit-fallthrough
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
+ je_cv_cflags_added=
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+ CONFIGURE_CFLAGS="${T_CONFIGURE_CFLAGS}"
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
+ CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
+else
+ CFLAGS="${CONFIGURE_CFLAGS} ${SPECIFIED_CFLAGS}"
+fi
+
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -Wimplicit-fallthrough" >&5
+printf %s "checking whether compiler supports -Wimplicit-fallthrough... " >&6; }
+T_CONFIGURE_CXXFLAGS="${CONFIGURE_CXXFLAGS}"
+T_APPEND_V=-Wimplicit-fallthrough
+ if test "x${CONFIGURE_CXXFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
+ CONFIGURE_CXXFLAGS="${CONFIGURE_CXXFLAGS}${T_APPEND_V}"
+else
+ CONFIGURE_CXXFLAGS="${CONFIGURE_CXXFLAGS} ${T_APPEND_V}"
+fi
+
+
+if test "x${CONFIGURE_CXXFLAGS}" = "x" -o "x${SPECIFIED_CXXFLAGS}" = "x" ; then
+ CXXFLAGS="${CONFIGURE_CXXFLAGS}${SPECIFIED_CXXFLAGS}"
+else
+ CXXFLAGS="${CONFIGURE_CXXFLAGS} ${SPECIFIED_CXXFLAGS}"
+fi
+
+ac_ext=cpp
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'
+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'
+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+
+int
+main (void)
+{
+
+ return 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_cxx_try_compile "$LINENO"
+then :
+ je_cv_cxxflags_added=-Wimplicit-fallthrough
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
+ je_cv_cxxflags_added=
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+ CONFIGURE_CXXFLAGS="${T_CONFIGURE_CXXFLAGS}"
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+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
+
+if test "x${CONFIGURE_CXXFLAGS}" = "x" -o "x${SPECIFIED_CXXFLAGS}" = "x" ; then
+ CXXFLAGS="${CONFIGURE_CXXFLAGS}${SPECIFIED_CXXFLAGS}"
+else
+ CXXFLAGS="${CONFIGURE_CXXFLAGS} ${SPECIFIED_CXXFLAGS}"
+fi
+
+
+fi
+
+SAVED_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -Werror" >&5
+printf %s "checking whether compiler supports -Werror... " >&6; }
+T_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
+T_APPEND_V=-Werror
+ if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
+ CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}${T_APPEND_V}"
+else
+ CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS} ${T_APPEND_V}"
+fi
+
+
+if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
+ CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
+else
+ CFLAGS="${CONFIGURE_CFLAGS} ${SPECIFIED_CFLAGS}"
+fi
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+
+int
+main (void)
+{
+
+ return 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ je_cv_cflags_added=-Werror
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
+ je_cv_cflags_added=
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+ CONFIGURE_CFLAGS="${T_CONFIGURE_CFLAGS}"
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
+ CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
+else
+ CFLAGS="${CONFIGURE_CFLAGS} ${SPECIFIED_CFLAGS}"
+fi
+
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -herror_on_warning" >&5
+printf %s "checking whether compiler supports -herror_on_warning... " >&6; }
+T_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
+T_APPEND_V=-herror_on_warning
+ if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
+ CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}${T_APPEND_V}"
+else
+ CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS} ${T_APPEND_V}"
+fi
+
+
+if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
+ CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
+else
+ CFLAGS="${CONFIGURE_CFLAGS} ${SPECIFIED_CFLAGS}"
+fi
+
+cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+
+int
+main (void)
+{
+
+ return 0;
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"
+then :
+ je_cv_cflags_added=-herror_on_warning
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
+ je_cv_cflags_added=
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
+ CONFIGURE_CFLAGS="${T_CONFIGURE_CFLAGS}"
+
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
+if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
+ CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
+else
+ CFLAGS="${CONFIGURE_CFLAGS} ${SPECIFIED_CFLAGS}"
+fi
+
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether cold attribute is compilable" >&5
+printf %s "checking whether cold attribute is compilable... " >&6; }
+if test ${je_cv_cold+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main (void)
+{
+__attribute__((__cold__)) void foo();
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+ je_cv_cold=yes
+else $as_nop
+ je_cv_cold=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $je_cv_cold" >&5
+printf "%s\n" "$je_cv_cold" >&6; }
+
+CONFIGURE_CFLAGS="${SAVED_CONFIGURE_CFLAGS}"
+if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
+ CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
+else
+ CFLAGS="${CONFIGURE_CFLAGS} ${SPECIFIED_CFLAGS}"
+fi
+
+
+if test "x${je_cv_cold}" = "xyes" ; then
+
+printf "%s\n" "#define JEMALLOC_HAVE_ATTR_COLD " >>confdefs.h
+
+fi
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether vm_make_tag is compilable" >&5
+printf %s "checking whether vm_make_tag is compilable... " >&6; }
+if test ${je_cv_vm_make_tag+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <sys/mman.h>
+ #include <mach/vm_statistics.h>
+int
+main (void)
+{
+void *p;
+ p = mmap(0, 16, PROT_READ, MAP_ANON|MAP_PRIVATE, VM_MAKE_TAG(1), 0);
+ munmap(p, 16);
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+ je_cv_vm_make_tag=yes
+else $as_nop
+ je_cv_vm_make_tag=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $je_cv_vm_make_tag" >&5
+printf "%s\n" "$je_cv_vm_make_tag" >&6; }
+
+if test "x${je_cv_vm_make_tag}" = "xyes" ; then
+
+printf "%s\n" "#define JEMALLOC_HAVE_VM_MAKE_TAG " >>confdefs.h
fi
# Check whether --with-rpath was given.
-if test "${with_rpath+set}" = set; then :
+if test ${with_rpath+y}
+then :
withval=$with_rpath; if test "x$with_rpath" = "xno" ; then
RPATH_EXTRA=
else
RPATH_EXTRA="`echo $with_rpath | tr \":\" \" \"`"
fi
-else
+else $as_nop
RPATH_EXTRA=
fi
@@ -8765,21 +10742,23 @@ fi
# Check whether --enable-autogen was given.
-if test "${enable_autogen+set}" = set; then :
+if test ${enable_autogen+y}
+then :
enableval=$enable_autogen; if test "x$enable_autogen" = "xno" ; then
enable_autogen="0"
else
enable_autogen="1"
fi
-else
+else $as_nop
enable_autogen="0"
fi
-# Find a good install program. We prefer a C program (faster),
+
+ # Find a good install program. We prefer a C program (faster),
# so one script is as good as another. But avoid the broken or
# incompatible versions:
# SysV /etc/install, /usr/sbin/install
@@ -8793,20 +10772,25 @@ fi
# OS/2's system install, which has a completely different semantic
# ./install, which can be erroneously created by make from ./install.sh.
# Reject install programs that cannot install multiple files.
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5
-$as_echo_n "checking for a BSD-compatible install... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5
+printf %s "checking for a BSD-compatible install... " >&6; }
if test -z "$INSTALL"; then
-if ${ac_cv_path_install+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+if test ${ac_cv_path_install+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
- # Account for people who put trailing slashes in PATH elements.
-case $as_dir/ in #((
- ./ | .// | /[cC]/* | \
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
+ # Account for fact that we put trailing slashes in our PATH walk.
+case $as_dir in #((
+ ./ | /[cC]/* | \
/etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \
?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \
/usr/ucb/* ) ;;
@@ -8816,13 +10800,13 @@ case $as_dir/ in #((
# by default.
for ac_prog in ginstall scoinst install; do
for ac_exec_ext in '' $ac_executable_extensions; do
- if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then
+ if as_fn_executable_p "$as_dir$ac_prog$ac_exec_ext"; then
if test $ac_prog = install &&
- grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+ grep dspmsg "$as_dir$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
# AIX install. It has an incompatible calling convention.
:
elif test $ac_prog = install &&
- grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
+ grep pwplus "$as_dir$ac_prog$ac_exec_ext" >/dev/null 2>&1; then
# program-specific install script used by HP pwplus--don't use.
:
else
@@ -8830,12 +10814,12 @@ case $as_dir/ in #((
echo one > conftest.one
echo two > conftest.two
mkdir conftest.dir
- if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" &&
+ if "$as_dir$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir/" &&
test -s conftest.one && test -s conftest.two &&
test -s conftest.dir/conftest.one &&
test -s conftest.dir/conftest.two
then
- ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c"
+ ac_cv_path_install="$as_dir$ac_prog$ac_exec_ext -c"
break 3
fi
fi
@@ -8851,7 +10835,7 @@ IFS=$as_save_IFS
rm -rf conftest.one conftest.two conftest.dir
fi
- if test "${ac_cv_path_install+set}" = set; then
+ if test ${ac_cv_path_install+y}; then
INSTALL=$ac_cv_path_install
else
# As a last resort, use the slow shell script. Don't cache a
@@ -8861,8 +10845,8 @@ fi
INSTALL=$ac_install_sh
fi
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5
-$as_echo "$INSTALL" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5
+printf "%s\n" "$INSTALL" >&6; }
# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
# It thinks the first close brace ends the variable substitution.
@@ -8875,11 +10859,12 @@ test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
if test -n "$ac_tool_prefix"; then
# Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args.
set dummy ${ac_tool_prefix}ranlib; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_prog_RANLIB+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+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
ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
else
@@ -8887,11 +10872,15 @@ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
for ac_exec_ext in '' $ac_executable_extensions; do
- if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
- $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
break 2
fi
done
@@ -8902,11 +10891,11 @@ fi
fi
RANLIB=$ac_cv_prog_RANLIB
if test -n "$RANLIB"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5
-$as_echo "$RANLIB" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5
+printf "%s\n" "$RANLIB" >&6; }
else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
fi
@@ -8915,11 +10904,12 @@ if test -z "$ac_cv_prog_RANLIB"; then
ac_ct_RANLIB=$RANLIB
# Extract the first word of "ranlib", so it can be a program name with args.
set dummy ranlib; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_prog_ac_ct_RANLIB+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+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
ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test.
else
@@ -8927,11 +10917,15 @@ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
for as_dir in $PATH
do
IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
for ac_exec_ext in '' $ac_executable_extensions; do
- if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
ac_cv_prog_ac_ct_RANLIB="ranlib"
- $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
break 2
fi
done
@@ -8942,11 +10936,11 @@ fi
fi
ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB
if test -n "$ac_ct_RANLIB"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5
-$as_echo "$ac_ct_RANLIB" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5
+printf "%s\n" "$ac_ct_RANLIB" >&6; }
else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
fi
if test "x$ac_ct_RANLIB" = x; then
@@ -8954,8 +10948,8 @@ fi
else
case $cross_compiling:$ac_tool_warned in
yes:)
-{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
-$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5
+printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;}
ac_tool_warned=yes ;;
esac
RANLIB=$ac_ct_RANLIB
@@ -8966,11 +10960,12 @@ fi
# Extract the first word of "ld", so it can be a program name with args.
set dummy ld; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_path_LD+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_path_LD+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
case $LD in
[\\/]* | ?:[\\/]*)
ac_cv_path_LD="$LD" # Let the user override the test with a path.
@@ -8980,11 +10975,15 @@ else
for as_dir in $PATH
do
IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
for ac_exec_ext in '' $ac_executable_extensions; do
- if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
- ac_cv_path_LD="$as_dir/$ac_word$ac_exec_ext"
- $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+ ac_cv_path_LD="$as_dir$ac_word$ac_exec_ext"
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
break 2
fi
done
@@ -8997,21 +10996,22 @@ esac
fi
LD=$ac_cv_path_LD
if test -n "$LD"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LD" >&5
-$as_echo "$LD" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $LD" >&5
+printf "%s\n" "$LD" >&6; }
else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
fi
# Extract the first word of "autoconf", so it can be a program name with args.
set dummy autoconf; ac_word=$2
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
-$as_echo_n "checking for $ac_word... " >&6; }
-if ${ac_cv_path_AUTOCONF+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5
+printf %s "checking for $ac_word... " >&6; }
+if test ${ac_cv_path_AUTOCONF+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
case $AUTOCONF in
[\\/]* | ?:[\\/]*)
ac_cv_path_AUTOCONF="$AUTOCONF" # Let the user override the test with a path.
@@ -9021,11 +11021,15 @@ else
for as_dir in $PATH
do
IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
for ac_exec_ext in '' $ac_executable_extensions; do
- if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
- ac_cv_path_AUTOCONF="$as_dir/$ac_word$ac_exec_ext"
- $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5
+ if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then
+ ac_cv_path_AUTOCONF="$as_dir$ac_word$ac_exec_ext"
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5
break 2
fi
done
@@ -9038,24 +11042,25 @@ esac
fi
AUTOCONF=$ac_cv_path_AUTOCONF
if test -n "$AUTOCONF"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AUTOCONF" >&5
-$as_echo "$AUTOCONF" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $AUTOCONF" >&5
+printf "%s\n" "$AUTOCONF" >&6; }
else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
fi
# Check whether --enable-doc was given.
-if test "${enable_doc+set}" = set; then :
+if test ${enable_doc+y}
+then :
enableval=$enable_doc; if test "x$enable_doc" = "xno" ; then
enable_doc="0"
else
enable_doc="1"
fi
-else
+else $as_nop
enable_doc="1"
fi
@@ -9063,14 +11068,15 @@ fi
# Check whether --enable-shared was given.
-if test "${enable_shared+set}" = set; then :
+if test ${enable_shared+y}
+then :
enableval=$enable_shared; if test "x$enable_shared" = "xno" ; then
enable_shared="0"
else
enable_shared="1"
fi
-else
+else $as_nop
enable_shared="1"
fi
@@ -9078,14 +11084,15 @@ fi
# Check whether --enable-static was given.
-if test "${enable_static+set}" = set; then :
+if test ${enable_static+y}
+then :
enableval=$enable_static; if test "x$enable_static" = "xno" ; then
enable_static="0"
else
enable_static="1"
fi
-else
+else $as_nop
enable_static="1"
fi
@@ -9098,18 +11105,20 @@ fi
# Check whether --with-mangling was given.
-if test "${with_mangling+set}" = set; then :
+if test ${with_mangling+y}
+then :
withval=$with_mangling; mangling_map="$with_mangling"
-else
+else $as_nop
mangling_map=""
fi
# Check whether --with-jemalloc_prefix was given.
-if test "${with_jemalloc_prefix+set}" = set; then :
+if test ${with_jemalloc_prefix+y}
+then :
withval=$with_jemalloc_prefix; JEMALLOC_PREFIX="$with_jemalloc_prefix"
-else
+else $as_nop
if test "x$abi" != "xmacho" -a "x$abi" != "xpecoff"; then
JEMALLOC_PREFIX=""
else
@@ -9119,17 +11128,16 @@ fi
fi
if test "x$JEMALLOC_PREFIX" = "x" ; then
- $as_echo "#define JEMALLOC_IS_MALLOC 1" >>confdefs.h
+
+printf "%s\n" "#define JEMALLOC_IS_MALLOC " >>confdefs.h
else
JEMALLOC_CPREFIX=`echo ${JEMALLOC_PREFIX} | tr "a-z" "A-Z"`
- cat >>confdefs.h <<_ACEOF
-#define JEMALLOC_PREFIX "$JEMALLOC_PREFIX"
-_ACEOF
- cat >>confdefs.h <<_ACEOF
-#define JEMALLOC_CPREFIX "$JEMALLOC_CPREFIX"
-_ACEOF
+printf "%s\n" "#define JEMALLOC_PREFIX \"$JEMALLOC_PREFIX\"" >>confdefs.h
+
+
+printf "%s\n" "#define JEMALLOC_CPREFIX \"$JEMALLOC_CPREFIX\"" >>confdefs.h
fi
@@ -9137,78 +11145,107 @@ fi
# Check whether --with-export was given.
-if test "${with_export+set}" = set; then :
+if test ${with_export+y}
+then :
withval=$with_export; if test "x$with_export" = "xno"; then
- $as_echo "#define JEMALLOC_EXPORT /**/" >>confdefs.h
+
+printf "%s\n" "#define JEMALLOC_EXPORT /**/" >>confdefs.h
fi
fi
-public_syms="aligned_alloc calloc dallocx free mallctl mallctlbymib mallctlnametomib malloc malloc_conf malloc_message malloc_stats_print malloc_usable_size mallocx smallocx_${jemalloc_version_gid} nallocx posix_memalign rallocx realloc sallocx sdallocx xallocx"
+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"
ac_fn_c_check_func "$LINENO" "memalign" "ac_cv_func_memalign"
-if test "x$ac_cv_func_memalign" = xyes; then :
- $as_echo "#define JEMALLOC_OVERRIDE_MEMALIGN " >>confdefs.h
+if test "x$ac_cv_func_memalign" = xyes
+then :
+
+printf "%s\n" "#define JEMALLOC_OVERRIDE_MEMALIGN " >>confdefs.h
public_syms="${public_syms} memalign"
fi
ac_fn_c_check_func "$LINENO" "valloc" "ac_cv_func_valloc"
-if test "x$ac_cv_func_valloc" = xyes; then :
- $as_echo "#define JEMALLOC_OVERRIDE_VALLOC " >>confdefs.h
+if test "x$ac_cv_func_valloc" = xyes
+then :
+
+printf "%s\n" "#define JEMALLOC_OVERRIDE_VALLOC " >>confdefs.h
public_syms="${public_syms} valloc"
fi
+ac_fn_c_check_func "$LINENO" "malloc_size" "ac_cv_func_malloc_size"
+if test "x$ac_cv_func_malloc_size" = xyes
+then :
+
+printf "%s\n" "#define JEMALLOC_HAVE_MALLOC_SIZE " >>confdefs.h
+
+ public_syms="${public_syms} malloc_size"
+fi
+
wrap_syms=
if test "x${JEMALLOC_PREFIX}" = "x" ; then
ac_fn_c_check_func "$LINENO" "__libc_calloc" "ac_cv_func___libc_calloc"
-if test "x$ac_cv_func___libc_calloc" = xyes; then :
- $as_echo "#define JEMALLOC_OVERRIDE___LIBC_CALLOC " >>confdefs.h
+if test "x$ac_cv_func___libc_calloc" = xyes
+then :
+
+printf "%s\n" "#define JEMALLOC_OVERRIDE___LIBC_CALLOC " >>confdefs.h
wrap_syms="${wrap_syms} __libc_calloc"
fi
ac_fn_c_check_func "$LINENO" "__libc_free" "ac_cv_func___libc_free"
-if test "x$ac_cv_func___libc_free" = xyes; then :
- $as_echo "#define JEMALLOC_OVERRIDE___LIBC_FREE " >>confdefs.h
+if test "x$ac_cv_func___libc_free" = xyes
+then :
+
+printf "%s\n" "#define JEMALLOC_OVERRIDE___LIBC_FREE " >>confdefs.h
wrap_syms="${wrap_syms} __libc_free"
fi
ac_fn_c_check_func "$LINENO" "__libc_malloc" "ac_cv_func___libc_malloc"
-if test "x$ac_cv_func___libc_malloc" = xyes; then :
- $as_echo "#define JEMALLOC_OVERRIDE___LIBC_MALLOC " >>confdefs.h
+if test "x$ac_cv_func___libc_malloc" = xyes
+then :
+
+printf "%s\n" "#define JEMALLOC_OVERRIDE___LIBC_MALLOC " >>confdefs.h
wrap_syms="${wrap_syms} __libc_malloc"
fi
ac_fn_c_check_func "$LINENO" "__libc_memalign" "ac_cv_func___libc_memalign"
-if test "x$ac_cv_func___libc_memalign" = xyes; then :
- $as_echo "#define JEMALLOC_OVERRIDE___LIBC_MEMALIGN " >>confdefs.h
+if test "x$ac_cv_func___libc_memalign" = xyes
+then :
+
+printf "%s\n" "#define JEMALLOC_OVERRIDE___LIBC_MEMALIGN " >>confdefs.h
wrap_syms="${wrap_syms} __libc_memalign"
fi
ac_fn_c_check_func "$LINENO" "__libc_realloc" "ac_cv_func___libc_realloc"
-if test "x$ac_cv_func___libc_realloc" = xyes; then :
- $as_echo "#define JEMALLOC_OVERRIDE___LIBC_REALLOC " >>confdefs.h
+if test "x$ac_cv_func___libc_realloc" = xyes
+then :
+
+printf "%s\n" "#define JEMALLOC_OVERRIDE___LIBC_REALLOC " >>confdefs.h
wrap_syms="${wrap_syms} __libc_realloc"
fi
ac_fn_c_check_func "$LINENO" "__libc_valloc" "ac_cv_func___libc_valloc"
-if test "x$ac_cv_func___libc_valloc" = xyes; then :
- $as_echo "#define JEMALLOC_OVERRIDE___LIBC_VALLOC " >>confdefs.h
+if test "x$ac_cv_func___libc_valloc" = xyes
+then :
+
+printf "%s\n" "#define JEMALLOC_OVERRIDE___LIBC_VALLOC " >>confdefs.h
wrap_syms="${wrap_syms} __libc_valloc"
fi
ac_fn_c_check_func "$LINENO" "__posix_memalign" "ac_cv_func___posix_memalign"
-if test "x$ac_cv_func___posix_memalign" = xyes; then :
- $as_echo "#define JEMALLOC_OVERRIDE___POSIX_MEMALIGN " >>confdefs.h
+if test "x$ac_cv_func___posix_memalign" = xyes
+then :
+
+printf "%s\n" "#define JEMALLOC_OVERRIDE___POSIX_MEMALIGN " >>confdefs.h
wrap_syms="${wrap_syms} __posix_memalign"
fi
@@ -9225,25 +11262,29 @@ esac
# Check whether --with-private_namespace was given.
-if test "${with_private_namespace+set}" = set; then :
+if test ${with_private_namespace+y}
+then :
withval=$with_private_namespace; JEMALLOC_PRIVATE_NAMESPACE="${with_private_namespace}je_"
-else
+else $as_nop
JEMALLOC_PRIVATE_NAMESPACE="je_"
fi
-cat >>confdefs.h <<_ACEOF
-#define JEMALLOC_PRIVATE_NAMESPACE $JEMALLOC_PRIVATE_NAMESPACE
-_ACEOF
+
+printf "%s\n" "#define JEMALLOC_PRIVATE_NAMESPACE $JEMALLOC_PRIVATE_NAMESPACE" >>confdefs.h
private_namespace="$JEMALLOC_PRIVATE_NAMESPACE"
# Check whether --with-install_suffix was given.
-if test "${with_install_suffix+set}" = set; then :
- withval=$with_install_suffix; INSTALL_SUFFIX="$with_install_suffix"
-else
+if test ${with_install_suffix+y}
+then :
+ withval=$with_install_suffix; case "$with_install_suffix" in
+ *\ * ) as_fn_error $? "Install suffix should not contain spaces" "$LINENO" 5 ;;
+ * ) INSTALL_SUFFIX="$with_install_suffix" ;;
+esac
+else $as_nop
INSTALL_SUFFIX=
fi
@@ -9253,17 +11294,17 @@ install_suffix="$INSTALL_SUFFIX"
# Check whether --with-malloc_conf was given.
-if test "${with_malloc_conf+set}" = set; then :
+if test ${with_malloc_conf+y}
+then :
withval=$with_malloc_conf; JEMALLOC_CONFIG_MALLOC_CONF="$with_malloc_conf"
-else
+else $as_nop
JEMALLOC_CONFIG_MALLOC_CONF=""
fi
config_malloc_conf="$JEMALLOC_CONFIG_MALLOC_CONF"
-cat >>confdefs.h <<_ACEOF
-#define JEMALLOC_CONFIG_MALLOC_CONF "$config_malloc_conf"
-_ACEOF
+
+printf "%s\n" "#define JEMALLOC_CONFIG_MALLOC_CONF \"$config_malloc_conf\"" >>confdefs.h
je_="je_"
@@ -9336,24 +11377,22 @@ cfghdrs_tup="${cfghdrs_tup} test/include/test/jemalloc_test_defs.h:test/include/
# Check whether --enable-debug was given.
-if test "${enable_debug+set}" = set; then :
+if test ${enable_debug+y}
+then :
enableval=$enable_debug; if test "x$enable_debug" = "xno" ; then
enable_debug="0"
else
enable_debug="1"
fi
-else
+else $as_nop
enable_debug="0"
fi
if test "x$enable_debug" = "x1" ; then
- $as_echo "#define JEMALLOC_DEBUG " >>confdefs.h
-fi
-if test "x$enable_debug" = "x1" ; then
- $as_echo "#define JEMALLOC_DEBUG " >>confdefs.h
+printf "%s\n" "#define JEMALLOC_DEBUG " >>confdefs.h
fi
@@ -9361,8 +11400,8 @@ fi
if test "x$enable_debug" = "x0" ; then
if test "x$GCC" = "xyes" ; then
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -O3" >&5
-$as_echo_n "checking whether compiler supports -O3... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -O3" >&5
+printf %s "checking whether compiler supports -O3... " >&6; }
T_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
T_APPEND_V=-O3
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
@@ -9383,7 +11422,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
int
-main ()
+main (void)
{
return 0;
@@ -9392,18 +11431,19 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
je_cv_cflags_added=-O3
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
je_cv_cflags_added=
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
CONFIGURE_CFLAGS="${T_CONFIGURE_CFLAGS}"
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
else
@@ -9412,8 +11452,8 @@ fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -O3" >&5
-$as_echo_n "checking whether compiler supports -O3... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -O3" >&5
+printf %s "checking whether compiler supports -O3... " >&6; }
T_CONFIGURE_CXXFLAGS="${CONFIGURE_CXXFLAGS}"
T_APPEND_V=-O3
if test "x${CONFIGURE_CXXFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
@@ -9440,7 +11480,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
int
-main ()
+main (void)
{
return 0;
@@ -9449,18 +11489,19 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_cxx_try_compile "$LINENO"; then :
+if ac_fn_cxx_try_compile "$LINENO"
+then :
je_cv_cxxflags_added=-O3
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
je_cv_cxxflags_added=
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
CONFIGURE_CXXFLAGS="${T_CONFIGURE_CXXFLAGS}"
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
@@ -9475,8 +11516,8 @@ fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -funroll-loops" >&5
-$as_echo_n "checking whether compiler supports -funroll-loops... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -funroll-loops" >&5
+printf %s "checking whether compiler supports -funroll-loops... " >&6; }
T_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
T_APPEND_V=-funroll-loops
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
@@ -9497,7 +11538,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
int
-main ()
+main (void)
{
return 0;
@@ -9506,18 +11547,19 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
je_cv_cflags_added=-funroll-loops
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
je_cv_cflags_added=
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
CONFIGURE_CFLAGS="${T_CONFIGURE_CFLAGS}"
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
else
@@ -9527,8 +11569,8 @@ fi
elif test "x$je_cv_msvc" = "xyes" ; then
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -O2" >&5
-$as_echo_n "checking whether compiler supports -O2... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -O2" >&5
+printf %s "checking whether compiler supports -O2... " >&6; }
T_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
T_APPEND_V=-O2
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
@@ -9549,7 +11591,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
int
-main ()
+main (void)
{
return 0;
@@ -9558,18 +11600,19 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
je_cv_cflags_added=-O2
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
je_cv_cflags_added=
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
CONFIGURE_CFLAGS="${T_CONFIGURE_CFLAGS}"
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
else
@@ -9578,8 +11621,8 @@ fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -O2" >&5
-$as_echo_n "checking whether compiler supports -O2... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -O2" >&5
+printf %s "checking whether compiler supports -O2... " >&6; }
T_CONFIGURE_CXXFLAGS="${CONFIGURE_CXXFLAGS}"
T_APPEND_V=-O2
if test "x${CONFIGURE_CXXFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
@@ -9606,7 +11649,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
int
-main ()
+main (void)
{
return 0;
@@ -9615,18 +11658,19 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_cxx_try_compile "$LINENO"; then :
+if ac_fn_cxx_try_compile "$LINENO"
+then :
je_cv_cxxflags_added=-O2
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
je_cv_cxxflags_added=
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
CONFIGURE_CXXFLAGS="${T_CONFIGURE_CXXFLAGS}"
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
@@ -9642,8 +11686,8 @@ fi
else
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -O" >&5
-$as_echo_n "checking whether compiler supports -O... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -O" >&5
+printf %s "checking whether compiler supports -O... " >&6; }
T_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
T_APPEND_V=-O
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
@@ -9664,7 +11708,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
int
-main ()
+main (void)
{
return 0;
@@ -9673,18 +11717,19 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
je_cv_cflags_added=-O
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
je_cv_cflags_added=
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
CONFIGURE_CFLAGS="${T_CONFIGURE_CFLAGS}"
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
else
@@ -9693,8 +11738,8 @@ fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -O" >&5
-$as_echo_n "checking whether compiler supports -O... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -O" >&5
+printf %s "checking whether compiler supports -O... " >&6; }
T_CONFIGURE_CXXFLAGS="${CONFIGURE_CXXFLAGS}"
T_APPEND_V=-O
if test "x${CONFIGURE_CXXFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
@@ -9721,7 +11766,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
int
-main ()
+main (void)
{
return 0;
@@ -9730,18 +11775,19 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_cxx_try_compile "$LINENO"; then :
+if ac_fn_cxx_try_compile "$LINENO"
+then :
je_cv_cxxflags_added=-O
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
je_cv_cxxflags_added=
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
CONFIGURE_CXXFLAGS="${T_CONFIGURE_CXXFLAGS}"
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
@@ -9759,52 +11805,57 @@ fi
fi
# Check whether --enable-stats was given.
-if test "${enable_stats+set}" = set; then :
+if test ${enable_stats+y}
+then :
enableval=$enable_stats; if test "x$enable_stats" = "xno" ; then
enable_stats="0"
else
enable_stats="1"
fi
-else
+else $as_nop
enable_stats="1"
fi
if test "x$enable_stats" = "x1" ; then
- $as_echo "#define JEMALLOC_STATS " >>confdefs.h
+
+printf "%s\n" "#define JEMALLOC_STATS " >>confdefs.h
fi
# Check whether --enable-experimental_smallocx was given.
-if test "${enable_experimental_smallocx+set}" = set; then :
+if test ${enable_experimental_smallocx+y}
+then :
enableval=$enable_experimental_smallocx; if test "x$enable_experimental_smallocx" = "xno" ; then
enable_experimental_smallocx="0"
else
enable_experimental_smallocx="1"
fi
-else
+else $as_nop
enable_experimental_smallocx="0"
fi
if test "x$enable_experimental_smallocx" = "x1" ; then
- $as_echo "#define JEMALLOC_EXPERIMENTAL_SMALLOCX_API 1" >>confdefs.h
+
+printf "%s\n" "#define JEMALLOC_EXPERIMENTAL_SMALLOCX_API " >>confdefs.h
fi
# Check whether --enable-prof was given.
-if test "${enable_prof+set}" = set; then :
+if test ${enable_prof+y}
+then :
enableval=$enable_prof; if test "x$enable_prof" = "xno" ; then
enable_prof="0"
else
enable_prof="1"
fi
-else
+else $as_nop
enable_prof="0"
fi
@@ -9816,21 +11867,26 @@ else
fi
# Check whether --enable-prof-libunwind was given.
-if test "${enable_prof_libunwind+set}" = set; then :
+if test ${enable_prof_libunwind+y}
+then :
enableval=$enable_prof_libunwind; if test "x$enable_prof_libunwind" = "xno" ; then
enable_prof_libunwind="0"
else
enable_prof_libunwind="1"
+ if test "x$enable_prof" = "x0" ; then
+ as_fn_error $? "--enable-prof-libunwind should only be used with --enable-prof" "$LINENO" 5
+ fi
fi
-else
+else $as_nop
enable_prof_libunwind="0"
fi
# Check whether --with-static_libunwind was given.
-if test "${with_static_libunwind+set}" = set; then :
+if test ${with_static_libunwind+y}
+then :
withval=$with_static_libunwind; if test "x$with_static_libunwind" = "xno" ; then
LUNWIND="-lunwind"
else
@@ -9839,32 +11895,31 @@ else
fi
LUNWIND="$with_static_libunwind"
fi
-else
+else $as_nop
LUNWIND="-lunwind"
fi
if test "x$backtrace_method" = "x" -a "x$enable_prof_libunwind" = "x1" ; then
- for ac_header in libunwind.h
+ for ac_header in libunwind.h
do :
- ac_fn_c_check_header_mongrel "$LINENO" "libunwind.h" "ac_cv_header_libunwind_h" "$ac_includes_default"
-if test "x$ac_cv_header_libunwind_h" = xyes; then :
- cat >>confdefs.h <<_ACEOF
-#define HAVE_LIBUNWIND_H 1
-_ACEOF
+ ac_fn_c_check_header_compile "$LINENO" "libunwind.h" "ac_cv_header_libunwind_h" "$ac_includes_default"
+if test "x$ac_cv_header_libunwind_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_LIBUNWIND_H 1" >>confdefs.h
-else
+else $as_nop
enable_prof_libunwind="0"
fi
done
-
if test "x$LUNWIND" = "x-lunwind" ; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for unw_backtrace in -lunwind" >&5
-$as_echo_n "checking for unw_backtrace in -lunwind... " >&6; }
-if ${ac_cv_lib_unwind_unw_backtrace+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for unw_backtrace in -lunwind" >&5
+printf %s "checking for unw_backtrace in -lunwind... " >&6; }
+if test ${ac_cv_lib_unwind_unw_backtrace+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
ac_check_lib_save_LIBS=$LIBS
LIBS="-lunwind $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
@@ -9873,30 +11928,29 @@ 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. */
-#ifdef __cplusplus
-extern "C"
-#endif
char unw_backtrace ();
int
-main ()
+main (void)
{
return unw_backtrace ();
;
return 0;
}
_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
+if ac_fn_c_try_link "$LINENO"
+then :
ac_cv_lib_unwind_unw_backtrace=yes
-else
+else $as_nop
ac_cv_lib_unwind_unw_backtrace=no
fi
-rm -f core conftest.err conftest.$ac_objext \
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_unwind_unw_backtrace" >&5
-$as_echo "$ac_cv_lib_unwind_unw_backtrace" >&6; }
-if test "x$ac_cv_lib_unwind_unw_backtrace" = xyes; then :
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_unwind_unw_backtrace" >&5
+printf "%s\n" "$ac_cv_lib_unwind_unw_backtrace" >&6; }
+if test "x$ac_cv_lib_unwind_unw_backtrace" = xyes
+then :
T_APPEND_V=$LUNWIND
if test "x${LIBS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
LIBS="${LIBS}${T_APPEND_V}"
@@ -9905,7 +11959,7 @@ else
fi
-else
+else $as_nop
enable_prof_libunwind="0"
fi
@@ -9921,46 +11975,47 @@ fi
fi
if test "x${enable_prof_libunwind}" = "x1" ; then
backtrace_method="libunwind"
- $as_echo "#define JEMALLOC_PROF_LIBUNWIND " >>confdefs.h
+
+printf "%s\n" "#define JEMALLOC_PROF_LIBUNWIND " >>confdefs.h
fi
fi
# Check whether --enable-prof-libgcc was given.
-if test "${enable_prof_libgcc+set}" = set; then :
+if test ${enable_prof_libgcc+y}
+then :
enableval=$enable_prof_libgcc; if test "x$enable_prof_libgcc" = "xno" ; then
enable_prof_libgcc="0"
else
enable_prof_libgcc="1"
fi
-else
+else $as_nop
enable_prof_libgcc="1"
fi
if test "x$backtrace_method" = "x" -a "x$enable_prof_libgcc" = "x1" \
-a "x$GCC" = "xyes" ; then
- for ac_header in unwind.h
+ for ac_header in unwind.h
do :
- ac_fn_c_check_header_mongrel "$LINENO" "unwind.h" "ac_cv_header_unwind_h" "$ac_includes_default"
-if test "x$ac_cv_header_unwind_h" = xyes; then :
- cat >>confdefs.h <<_ACEOF
-#define HAVE_UNWIND_H 1
-_ACEOF
+ ac_fn_c_check_header_compile "$LINENO" "unwind.h" "ac_cv_header_unwind_h" "$ac_includes_default"
+if test "x$ac_cv_header_unwind_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_UNWIND_H 1" >>confdefs.h
-else
+else $as_nop
enable_prof_libgcc="0"
fi
done
-
if test "x${enable_prof_libgcc}" = "x1" ; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _Unwind_Backtrace in -lgcc" >&5
-$as_echo_n "checking for _Unwind_Backtrace in -lgcc... " >&6; }
-if ${ac_cv_lib_gcc__Unwind_Backtrace+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for _Unwind_Backtrace in -lgcc" >&5
+printf %s "checking for _Unwind_Backtrace in -lgcc... " >&6; }
+if test ${ac_cv_lib_gcc__Unwind_Backtrace+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
ac_check_lib_save_LIBS=$LIBS
LIBS="-lgcc $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
@@ -9969,30 +12024,29 @@ 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. */
-#ifdef __cplusplus
-extern "C"
-#endif
char _Unwind_Backtrace ();
int
-main ()
+main (void)
{
return _Unwind_Backtrace ();
;
return 0;
}
_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
+if ac_fn_c_try_link "$LINENO"
+then :
ac_cv_lib_gcc__Unwind_Backtrace=yes
-else
+else $as_nop
ac_cv_lib_gcc__Unwind_Backtrace=no
fi
-rm -f core conftest.err conftest.$ac_objext \
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_gcc__Unwind_Backtrace" >&5
-$as_echo "$ac_cv_lib_gcc__Unwind_Backtrace" >&6; }
-if test "x$ac_cv_lib_gcc__Unwind_Backtrace" = xyes; then :
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_gcc__Unwind_Backtrace" >&5
+printf "%s\n" "$ac_cv_lib_gcc__Unwind_Backtrace" >&6; }
+if test "x$ac_cv_lib_gcc__Unwind_Backtrace" = xyes
+then :
T_APPEND_V=-lgcc
if test "x${LIBS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
LIBS="${LIBS}${T_APPEND_V}"
@@ -10001,14 +12055,15 @@ else
fi
-else
+else $as_nop
enable_prof_libgcc="0"
fi
fi
if test "x${enable_prof_libgcc}" = "x1" ; then
backtrace_method="libgcc"
- $as_echo "#define JEMALLOC_PROF_LIBGCC " >>confdefs.h
+
+printf "%s\n" "#define JEMALLOC_PROF_LIBGCC " >>confdefs.h
fi
else
@@ -10016,14 +12071,15 @@ else
fi
# Check whether --enable-prof-gcc was given.
-if test "${enable_prof_gcc+set}" = set; then :
+if test ${enable_prof_gcc+y}
+then :
enableval=$enable_prof_gcc; if test "x$enable_prof_gcc" = "xno" ; then
enable_prof_gcc="0"
else
enable_prof_gcc="1"
fi
-else
+else $as_nop
enable_prof_gcc="1"
fi
@@ -10031,8 +12087,8 @@ fi
if test "x$backtrace_method" = "x" -a "x$enable_prof_gcc" = "x1" \
-a "x$GCC" = "xyes" ; then
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -fno-omit-frame-pointer" >&5
-$as_echo_n "checking whether compiler supports -fno-omit-frame-pointer... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -fno-omit-frame-pointer" >&5
+printf %s "checking whether compiler supports -fno-omit-frame-pointer... " >&6; }
T_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
T_APPEND_V=-fno-omit-frame-pointer
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
@@ -10053,7 +12109,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
int
-main ()
+main (void)
{
return 0;
@@ -10062,18 +12118,19 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
je_cv_cflags_added=-fno-omit-frame-pointer
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
je_cv_cflags_added=
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
CONFIGURE_CFLAGS="${T_CONFIGURE_CFLAGS}"
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
else
@@ -10082,7 +12139,8 @@ fi
backtrace_method="gcc intrinsics"
- $as_echo "#define JEMALLOC_PROF_GCC " >>confdefs.h
+
+printf "%s\n" "#define JEMALLOC_PROF_GCC " >>confdefs.h
else
enable_prof_gcc="0"
@@ -10092,10 +12150,10 @@ if test "x$backtrace_method" = "x" ; then
backtrace_method="none (disabling profiling)"
enable_prof="0"
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking configured backtracing method" >&5
-$as_echo_n "checking configured backtracing method... " >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $backtrace_method" >&5
-$as_echo "$backtrace_method" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking configured backtracing method" >&5
+printf %s "checking configured backtracing method... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $backtrace_method" >&5
+printf "%s\n" "$backtrace_method" >&6; }
if test "x$enable_prof" = "x1" ; then
T_APPEND_V=$LM
if test "x${LIBS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
@@ -10106,33 +12164,43 @@ fi
- $as_echo "#define JEMALLOC_PROF " >>confdefs.h
+
+printf "%s\n" "#define JEMALLOC_PROF " >>confdefs.h
fi
if test "x${maps_coalesce}" = "x1" ; then
- $as_echo "#define JEMALLOC_MAPS_COALESCE " >>confdefs.h
+
+printf "%s\n" "#define JEMALLOC_MAPS_COALESCE " >>confdefs.h
fi
if test "x$default_retain" = "x1" ; then
- $as_echo "#define JEMALLOC_RETAIN " >>confdefs.h
+
+printf "%s\n" "#define JEMALLOC_RETAIN " >>confdefs.h
+
+fi
+
+if test "x$zero_realloc_default_free" = "x1" ; then
+
+printf "%s\n" "#define JEMALLOC_ZERO_REALLOC_DEFAULT_FREE " >>confdefs.h
fi
have_dss="1"
ac_fn_c_check_func "$LINENO" "sbrk" "ac_cv_func_sbrk"
-if test "x$ac_cv_func_sbrk" = xyes; then :
+if test "x$ac_cv_func_sbrk" = xyes
+then :
have_sbrk="1"
-else
+else $as_nop
have_sbrk="0"
fi
if test "x$have_sbrk" = "x1" ; then
if test "x$sbrk_deprecated" = "x1" ; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: Disabling dss allocation because sbrk is deprecated" >&5
-$as_echo "Disabling dss allocation because sbrk is deprecated" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Disabling dss allocation because sbrk is deprecated" >&5
+printf "%s\n" "Disabling dss allocation because sbrk is deprecated" >&6; }
have_dss="0"
fi
else
@@ -10140,48 +12208,53 @@ else
fi
if test "x$have_dss" = "x1" ; then
- $as_echo "#define JEMALLOC_DSS " >>confdefs.h
+
+printf "%s\n" "#define JEMALLOC_DSS " >>confdefs.h
fi
# Check whether --enable-fill was given.
-if test "${enable_fill+set}" = set; then :
+if test ${enable_fill+y}
+then :
enableval=$enable_fill; if test "x$enable_fill" = "xno" ; then
enable_fill="0"
else
enable_fill="1"
fi
-else
+else $as_nop
enable_fill="1"
fi
if test "x$enable_fill" = "x1" ; then
- $as_echo "#define JEMALLOC_FILL " >>confdefs.h
+
+printf "%s\n" "#define JEMALLOC_FILL " >>confdefs.h
fi
# Check whether --enable-utrace was given.
-if test "${enable_utrace+set}" = set; then :
+if test ${enable_utrace+y}
+then :
enableval=$enable_utrace; if test "x$enable_utrace" = "xno" ; then
enable_utrace="0"
else
enable_utrace="1"
fi
-else
+else $as_nop
enable_utrace="0"
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether utrace(2) is compilable" >&5
-$as_echo_n "checking whether utrace(2) is compilable... " >&6; }
-if ${je_cv_utrace+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether utrace(2) is compilable" >&5
+printf %s "checking whether utrace(2) is compilable... " >&6; }
+if test ${je_cv_utrace+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
@@ -10192,7 +12265,7 @@ else
#include <sys/ktrace.h>
int
-main ()
+main (void)
{
utrace((void *)0, 0);
@@ -10201,127 +12274,227 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
+if ac_fn_c_try_link "$LINENO"
+then :
je_cv_utrace=yes
-else
+else $as_nop
je_cv_utrace=no
fi
-rm -f core conftest.err conftest.$ac_objext \
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $je_cv_utrace" >&5
-$as_echo "$je_cv_utrace" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $je_cv_utrace" >&5
+printf "%s\n" "$je_cv_utrace" >&6; }
if test "x${je_cv_utrace}" = "xno" ; then
- enable_utrace="0"
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether utrace(2) with label is compilable" >&5
+printf %s "checking whether utrace(2) with label is compilable... " >&6; }
+if test ${je_cv_utrace_label+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+ #include <sys/types.h>
+ #include <sys/param.h>
+ #include <sys/time.h>
+ #include <sys/uio.h>
+ #include <sys/ktrace.h>
+
+int
+main (void)
+{
+
+ utrace((void *)0, (void *)0, 0);
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+ je_cv_utrace_label=yes
+else $as_nop
+ je_cv_utrace_label=no
fi
-if test "x$enable_utrace" = "x1" ; then
- $as_echo "#define JEMALLOC_UTRACE " >>confdefs.h
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $je_cv_utrace_label" >&5
+printf "%s\n" "$je_cv_utrace_label" >&6; }
+
+ if test "x${je_cv_utrace_label}" = "xno"; then
+ enable_utrace="0"
+ fi
+ if test "x$enable_utrace" = "x1" ; then
+
+printf "%s\n" "#define JEMALLOC_UTRACE_LABEL " >>confdefs.h
+
+ fi
+else
+ if test "x$enable_utrace" = "x1" ; then
+
+printf "%s\n" "#define JEMALLOC_UTRACE " >>confdefs.h
+ fi
fi
# Check whether --enable-xmalloc was given.
-if test "${enable_xmalloc+set}" = set; then :
+if test ${enable_xmalloc+y}
+then :
enableval=$enable_xmalloc; if test "x$enable_xmalloc" = "xno" ; then
enable_xmalloc="0"
else
enable_xmalloc="1"
fi
-else
+else $as_nop
enable_xmalloc="0"
fi
if test "x$enable_xmalloc" = "x1" ; then
- $as_echo "#define JEMALLOC_XMALLOC " >>confdefs.h
+
+printf "%s\n" "#define JEMALLOC_XMALLOC " >>confdefs.h
fi
# Check whether --enable-cache-oblivious was given.
-if test "${enable_cache_oblivious+set}" = set; then :
+if test ${enable_cache_oblivious+y}
+then :
enableval=$enable_cache_oblivious; if test "x$enable_cache_oblivious" = "xno" ; then
enable_cache_oblivious="0"
else
enable_cache_oblivious="1"
fi
-else
+else $as_nop
enable_cache_oblivious="1"
fi
if test "x$enable_cache_oblivious" = "x1" ; then
- $as_echo "#define JEMALLOC_CACHE_OBLIVIOUS " >>confdefs.h
+
+printf "%s\n" "#define JEMALLOC_CACHE_OBLIVIOUS " >>confdefs.h
fi
# Check whether --enable-log was given.
-if test "${enable_log+set}" = set; then :
+if test ${enable_log+y}
+then :
enableval=$enable_log; if test "x$enable_log" = "xno" ; then
enable_log="0"
else
enable_log="1"
fi
-else
+else $as_nop
enable_log="0"
fi
if test "x$enable_log" = "x1" ; then
- $as_echo "#define JEMALLOC_LOG " >>confdefs.h
+
+printf "%s\n" "#define JEMALLOC_LOG " >>confdefs.h
fi
# Check whether --enable-readlinkat was given.
-if test "${enable_readlinkat+set}" = set; then :
+if test ${enable_readlinkat+y}
+then :
enableval=$enable_readlinkat; if test "x$enable_readlinkat" = "xno" ; then
enable_readlinkat="0"
else
enable_readlinkat="1"
fi
-else
+else $as_nop
enable_readlinkat="0"
fi
if test "x$enable_readlinkat" = "x1" ; then
- $as_echo "#define JEMALLOC_READLINKAT " >>confdefs.h
+
+printf "%s\n" "#define JEMALLOC_READLINKAT " >>confdefs.h
fi
# Check whether --enable-opt-safety-checks was given.
-if test "${enable_opt_safety_checks+set}" = set; then :
+if test ${enable_opt_safety_checks+y}
+then :
enableval=$enable_opt_safety_checks; if test "x$enable_opt_safety_checks" = "xno" ; then
enable_opt_safety_checks="0"
else
enable_opt_safety_checks="1"
fi
-else
+else $as_nop
enable_opt_safety_checks="0"
fi
if test "x$enable_opt_safety_checks" = "x1" ; then
- $as_echo "#define JEMALLOC_OPT_SAFETY_CHECKS " >>confdefs.h
+
+printf "%s\n" "#define JEMALLOC_OPT_SAFETY_CHECKS " >>confdefs.h
+
+fi
+
+
+# Check whether --enable-opt-size-checks was given.
+if test ${enable_opt_size_checks+y}
+then :
+ enableval=$enable_opt_size_checks; if test "x$enable_opt_size_checks" = "xno" ; then
+ enable_opt_size_checks="0"
+else
+ enable_opt_size_checks="1"
+fi
+
+else $as_nop
+ enable_opt_size_checks="0"
fi
+if test "x$enable_opt_size_checks" = "x1" ; then
+
+printf "%s\n" "#define JEMALLOC_OPT_SIZE_CHECKS " >>confdefs.h
+fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a program using __builtin_unreachable is compilable" >&5
-$as_echo_n "checking whether a program using __builtin_unreachable is compilable... " >&6; }
-if ${je_cv_gcc_builtin_unreachable+:} false; then :
- $as_echo_n "(cached) " >&6
+
+# Check whether --enable-uaf-detection was given.
+if test ${enable_uaf_detection+y}
+then :
+ enableval=$enable_uaf_detection; if test "x$enable_uaf_detection" = "xno" ; then
+ enable_uaf_detection="0"
else
+ enable_uaf_detection="1"
+fi
+
+else $as_nop
+ enable_uaf_detection="0"
+
+fi
+
+if test "x$enable_uaf_detection" = "x1" ; then
+ printf "%s\n" "#define JEMALLOC_UAF_DETECTION " >>confdefs.h
+
+fi
+
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether a program using __builtin_unreachable is compilable" >&5
+printf %s "checking whether a program using __builtin_unreachable is compilable... " >&6; }
+if test ${je_cv_gcc_builtin_unreachable+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
@@ -10330,7 +12503,7 @@ void foo (void) {
}
int
-main ()
+main (void)
{
{
@@ -10341,31 +12514,35 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
+if ac_fn_c_try_link "$LINENO"
+then :
je_cv_gcc_builtin_unreachable=yes
-else
+else $as_nop
je_cv_gcc_builtin_unreachable=no
fi
-rm -f core conftest.err conftest.$ac_objext \
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $je_cv_gcc_builtin_unreachable" >&5
-$as_echo "$je_cv_gcc_builtin_unreachable" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $je_cv_gcc_builtin_unreachable" >&5
+printf "%s\n" "$je_cv_gcc_builtin_unreachable" >&6; }
if test "x${je_cv_gcc_builtin_unreachable}" = "xyes" ; then
- $as_echo "#define JEMALLOC_INTERNAL_UNREACHABLE __builtin_unreachable" >>confdefs.h
+
+printf "%s\n" "#define JEMALLOC_INTERNAL_UNREACHABLE __builtin_unreachable" >>confdefs.h
else
- $as_echo "#define JEMALLOC_INTERNAL_UNREACHABLE abort" >>confdefs.h
+
+printf "%s\n" "#define JEMALLOC_INTERNAL_UNREACHABLE abort" >>confdefs.h
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a program using __builtin_ffsl is compilable" >&5
-$as_echo_n "checking whether a program using __builtin_ffsl is compilable... " >&6; }
-if ${je_cv_gcc_builtin_ffsl+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether a program using __builtin_ffsl is compilable" >&5
+printf %s "checking whether a program using __builtin_ffsl is compilable... " >&6; }
+if test ${je_cv_gcc_builtin_ffsl+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
@@ -10374,7 +12551,7 @@ else
#include <string.h>
int
-main ()
+main (void)
{
{
@@ -10386,31 +12563,36 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
+if ac_fn_c_try_link "$LINENO"
+then :
je_cv_gcc_builtin_ffsl=yes
-else
+else $as_nop
je_cv_gcc_builtin_ffsl=no
fi
-rm -f core conftest.err conftest.$ac_objext \
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $je_cv_gcc_builtin_ffsl" >&5
-$as_echo "$je_cv_gcc_builtin_ffsl" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $je_cv_gcc_builtin_ffsl" >&5
+printf "%s\n" "$je_cv_gcc_builtin_ffsl" >&6; }
if test "x${je_cv_gcc_builtin_ffsl}" = "xyes" ; then
- $as_echo "#define JEMALLOC_INTERNAL_FFSLL __builtin_ffsll" >>confdefs.h
- $as_echo "#define JEMALLOC_INTERNAL_FFSL __builtin_ffsl" >>confdefs.h
+printf "%s\n" "#define JEMALLOC_INTERNAL_FFSLL __builtin_ffsll" >>confdefs.h
- $as_echo "#define JEMALLOC_INTERNAL_FFS __builtin_ffs" >>confdefs.h
-else
+printf "%s\n" "#define JEMALLOC_INTERNAL_FFSL __builtin_ffsl" >>confdefs.h
+
+
+printf "%s\n" "#define JEMALLOC_INTERNAL_FFS __builtin_ffs" >>confdefs.h
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a program using ffsl is compilable" >&5
-$as_echo_n "checking whether a program using ffsl is compilable... " >&6; }
-if ${je_cv_function_ffsl+:} false; then :
- $as_echo_n "(cached) " >&6
else
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether a program using ffsl is compilable" >&5
+printf %s "checking whether a program using ffsl is compilable... " >&6; }
+if test ${je_cv_function_ffsl+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
@@ -10419,7 +12601,7 @@ else
#include <string.h>
int
-main ()
+main (void)
{
{
@@ -10431,23 +12613,27 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
+if ac_fn_c_try_link "$LINENO"
+then :
je_cv_function_ffsl=yes
-else
+else $as_nop
je_cv_function_ffsl=no
fi
-rm -f core conftest.err conftest.$ac_objext \
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $je_cv_function_ffsl" >&5
-$as_echo "$je_cv_function_ffsl" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $je_cv_function_ffsl" >&5
+printf "%s\n" "$je_cv_function_ffsl" >&6; }
if test "x${je_cv_function_ffsl}" = "xyes" ; then
- $as_echo "#define JEMALLOC_INTERNAL_FFSLL ffsll" >>confdefs.h
- $as_echo "#define JEMALLOC_INTERNAL_FFSL ffsl" >>confdefs.h
+printf "%s\n" "#define JEMALLOC_INTERNAL_FFSLL ffsll" >>confdefs.h
+
+
+printf "%s\n" "#define JEMALLOC_INTERNAL_FFSL ffsl" >>confdefs.h
- $as_echo "#define JEMALLOC_INTERNAL_FFS ffs" >>confdefs.h
+
+printf "%s\n" "#define JEMALLOC_INTERNAL_FFS ffs" >>confdefs.h
else
as_fn_error $? "Cannot build without ffsl(3) or __builtin_ffsl()" "$LINENO" 5
@@ -10455,11 +12641,12 @@ $as_echo "$je_cv_function_ffsl" >&6; }
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a program using __builtin_popcountl is compilable" >&5
-$as_echo_n "checking whether a program using __builtin_popcountl is compilable... " >&6; }
-if ${je_cv_gcc_builtin_popcountl+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether a program using __builtin_popcountl is compilable" >&5
+printf %s "checking whether a program using __builtin_popcountl is compilable... " >&6; }
+if test ${je_cv_gcc_builtin_popcountl+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
@@ -10468,7 +12655,7 @@ else
#include <string.h>
int
-main ()
+main (void)
{
{
@@ -10480,56 +12667,85 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
+if ac_fn_c_try_link "$LINENO"
+then :
je_cv_gcc_builtin_popcountl=yes
-else
+else $as_nop
je_cv_gcc_builtin_popcountl=no
fi
-rm -f core conftest.err conftest.$ac_objext \
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $je_cv_gcc_builtin_popcountl" >&5
-$as_echo "$je_cv_gcc_builtin_popcountl" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $je_cv_gcc_builtin_popcountl" >&5
+printf "%s\n" "$je_cv_gcc_builtin_popcountl" >&6; }
if test "x${je_cv_gcc_builtin_popcountl}" = "xyes" ; then
- $as_echo "#define JEMALLOC_INTERNAL_POPCOUNT __builtin_popcount" >>confdefs.h
- $as_echo "#define JEMALLOC_INTERNAL_POPCOUNTL __builtin_popcountl" >>confdefs.h
+printf "%s\n" "#define JEMALLOC_INTERNAL_POPCOUNT __builtin_popcount" >>confdefs.h
+
+
+printf "%s\n" "#define JEMALLOC_INTERNAL_POPCOUNTL __builtin_popcountl" >>confdefs.h
+
+
+printf "%s\n" "#define JEMALLOC_INTERNAL_POPCOUNTLL __builtin_popcountll" >>confdefs.h
fi
# Check whether --with-lg_quantum was given.
-if test "${with_lg_quantum+set}" = set; then :
- withval=$with_lg_quantum; LG_QUANTA="$with_lg_quantum"
-else
- LG_QUANTA="3 4"
+if test ${with_lg_quantum+y}
+then :
+ withval=$with_lg_quantum;
fi
if test "x$with_lg_quantum" != "x" ; then
- cat >>confdefs.h <<_ACEOF
-#define LG_QUANTUM $with_lg_quantum
-_ACEOF
+
+printf "%s\n" "#define LG_QUANTUM $with_lg_quantum" >>confdefs.h
+
+fi
+
+
+# Check whether --with-lg_slab_maxregs was given.
+if test ${with_lg_slab_maxregs+y}
+then :
+ withval=$with_lg_slab_maxregs; CONFIG_LG_SLAB_MAXREGS="with_lg_slab_maxregs"
+else $as_nop
+ CONFIG_LG_SLAB_MAXREGS=""
+fi
+
+if test "x$with_lg_slab_maxregs" != "x" ; then
+
+printf "%s\n" "#define CONFIG_LG_SLAB_MAXREGS $with_lg_slab_maxregs" >>confdefs.h
fi
# Check whether --with-lg_page was given.
-if test "${with_lg_page+set}" = set; then :
+if test ${with_lg_page+y}
+then :
withval=$with_lg_page; LG_PAGE="$with_lg_page"
-else
+else $as_nop
LG_PAGE="detect"
fi
+case "${host}" in
+ aarch64-apple-darwin*)
+ if test "x${host}" != "x${build}" -a "x$LG_PAGE" = "xdetect"; then
+ LG_PAGE=14
+ fi
+ ;;
+esac
if test "x$LG_PAGE" = "xdetect"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking LG_PAGE" >&5
-$as_echo_n "checking LG_PAGE... " >&6; }
-if ${je_cv_lg_page+:} false; then :
- $as_echo_n "(cached) " >&6
-else
- if test "$cross_compiling" = yes; then :
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking LG_PAGE" >&5
+printf %s "checking LG_PAGE... " >&6; }
+if test ${je_cv_lg_page+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ if test "$cross_compiling" = yes
+then :
je_cv_lg_page=12
-else
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
@@ -10542,7 +12758,7 @@ else
#include <stdio.h>
int
-main ()
+main (void)
{
int result;
@@ -10573,9 +12789,10 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_run "$LINENO"; then :
+if ac_fn_c_try_run "$LINENO"
+then :
je_cv_lg_page=`cat conftest.out`
-else
+else $as_nop
je_cv_lg_page=undefined
fi
rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
@@ -10583,16 +12800,15 @@ rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
fi
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $je_cv_lg_page" >&5
-$as_echo "$je_cv_lg_page" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $je_cv_lg_page" >&5
+printf "%s\n" "$je_cv_lg_page" >&6; }
fi
if test "x${je_cv_lg_page}" != "x" ; then
LG_PAGE="${je_cv_lg_page}"
fi
if test "x${LG_PAGE}" != "xundefined" ; then
- cat >>confdefs.h <<_ACEOF
-#define LG_PAGE $LG_PAGE
-_ACEOF
+
+printf "%s\n" "#define LG_PAGE $LG_PAGE" >>confdefs.h
else
as_fn_error $? "cannot determine value for LG_PAGE" "$LINENO" 5
@@ -10600,9 +12816,10 @@ fi
# Check whether --with-lg_hugepage was given.
-if test "${with_lg_hugepage+set}" = set; then :
+if test ${with_lg_hugepage+y}
+then :
withval=$with_lg_hugepage; je_cv_lg_hugepage="${with_lg_hugepage}"
-else
+else $as_nop
je_cv_lg_hugepage=""
fi
@@ -10628,20 +12845,20 @@ if test "x${LG_PAGE}" != "xundefined" -a \
"${je_cv_lg_hugepage}" -lt "${LG_PAGE}" ; then
as_fn_error $? "Huge page size (2^${je_cv_lg_hugepage}) must be at least page size (2^${LG_PAGE})" "$LINENO" 5
fi
-cat >>confdefs.h <<_ACEOF
-#define LG_HUGEPAGE ${je_cv_lg_hugepage}
-_ACEOF
+
+printf "%s\n" "#define LG_HUGEPAGE ${je_cv_lg_hugepage}" >>confdefs.h
# Check whether --enable-libdl was given.
-if test "${enable_libdl+set}" = set; then :
+if test ${enable_libdl+y}
+then :
enableval=$enable_libdl; if test "x$enable_libdl" = "xno" ; then
enable_libdl="0"
else
enable_libdl="1"
fi
-else
+else $as_nop
enable_libdl="1"
fi
@@ -10650,27 +12867,27 @@ fi
if test "x$abi" != "xpecoff" ; then
- $as_echo "#define JEMALLOC_HAVE_PTHREAD " >>confdefs.h
- for ac_header in pthread.h
+printf "%s\n" "#define JEMALLOC_HAVE_PTHREAD " >>confdefs.h
+
+ for ac_header in pthread.h
do :
- ac_fn_c_check_header_mongrel "$LINENO" "pthread.h" "ac_cv_header_pthread_h" "$ac_includes_default"
-if test "x$ac_cv_header_pthread_h" = xyes; then :
- cat >>confdefs.h <<_ACEOF
-#define HAVE_PTHREAD_H 1
-_ACEOF
+ ac_fn_c_check_header_compile "$LINENO" "pthread.h" "ac_cv_header_pthread_h" "$ac_includes_default"
+if test "x$ac_cv_header_pthread_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_PTHREAD_H 1" >>confdefs.h
-else
+else $as_nop
as_fn_error $? "pthread.h is missing" "$LINENO" 5
fi
done
-
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_create in -lpthread" >&5
-$as_echo_n "checking for pthread_create in -lpthread... " >&6; }
-if ${ac_cv_lib_pthread_pthread_create+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for pthread_create in -lpthread" >&5
+printf %s "checking for pthread_create in -lpthread... " >&6; }
+if test ${ac_cv_lib_pthread_pthread_create+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
ac_check_lib_save_LIBS=$LIBS
LIBS="-lpthread $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
@@ -10679,30 +12896,29 @@ 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. */
-#ifdef __cplusplus
-extern "C"
-#endif
char pthread_create ();
int
-main ()
+main (void)
{
return pthread_create ();
;
return 0;
}
_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
+if ac_fn_c_try_link "$LINENO"
+then :
ac_cv_lib_pthread_pthread_create=yes
-else
+else $as_nop
ac_cv_lib_pthread_pthread_create=no
fi
-rm -f core conftest.err conftest.$ac_objext \
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pthread_pthread_create" >&5
-$as_echo "$ac_cv_lib_pthread_pthread_create" >&6; }
-if test "x$ac_cv_lib_pthread_pthread_create" = xyes; then :
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pthread_pthread_create" >&5
+printf "%s\n" "$ac_cv_lib_pthread_pthread_create" >&6; }
+if test "x$ac_cv_lib_pthread_pthread_create" = xyes
+then :
T_APPEND_V=-pthread
if test "x${LIBS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
LIBS="${LIBS}${T_APPEND_V}"
@@ -10711,12 +12927,13 @@ else
fi
-else
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing pthread_create" >&5
-$as_echo_n "checking for library containing pthread_create... " >&6; }
-if ${ac_cv_search_pthread_create+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+else $as_nop
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing pthread_create" >&5
+printf %s "checking for library containing pthread_create... " >&6; }
+if test ${ac_cv_search_pthread_create+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
ac_func_search_save_LIBS=$LIBS
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
@@ -10724,49 +12941,51 @@ 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. */
-#ifdef __cplusplus
-extern "C"
-#endif
char pthread_create ();
int
-main ()
+main (void)
{
return pthread_create ();
;
return 0;
}
_ACEOF
-for ac_lib in '' ; do
+for ac_lib in ''
+do
if test -z "$ac_lib"; then
ac_res="none required"
else
ac_res=-l$ac_lib
LIBS="-l$ac_lib $ac_func_search_save_LIBS"
fi
- if ac_fn_c_try_link "$LINENO"; then :
+ if ac_fn_c_try_link "$LINENO"
+then :
ac_cv_search_pthread_create=$ac_res
fi
-rm -f core conftest.err conftest.$ac_objext \
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext
- if ${ac_cv_search_pthread_create+:} false; then :
+ if test ${ac_cv_search_pthread_create+y}
+then :
break
fi
done
-if ${ac_cv_search_pthread_create+:} false; then :
+if test ${ac_cv_search_pthread_create+y}
+then :
-else
+else $as_nop
ac_cv_search_pthread_create=no
fi
rm conftest.$ac_ext
LIBS=$ac_func_search_save_LIBS
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_pthread_create" >&5
-$as_echo "$ac_cv_search_pthread_create" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_pthread_create" >&5
+printf "%s\n" "$ac_cv_search_pthread_create" >&6; }
ac_res=$ac_cv_search_pthread_create
-if test "$ac_res" != no; then :
+if test "$ac_res" != no
+then :
test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
-else
+else $as_nop
as_fn_error $? "libpthread is missing" "$LINENO" 5
fi
@@ -10777,22 +12996,23 @@ fi
if test "x$enable_libdl" = "x1" ; then
have_dlsym="1"
- for ac_header in dlfcn.h
+ for ac_header in dlfcn.h
do :
- ac_fn_c_check_header_mongrel "$LINENO" "dlfcn.h" "ac_cv_header_dlfcn_h" "$ac_includes_default"
-if test "x$ac_cv_header_dlfcn_h" = xyes; then :
- cat >>confdefs.h <<_ACEOF
-#define HAVE_DLFCN_H 1
-_ACEOF
+ ac_fn_c_check_header_compile "$LINENO" "dlfcn.h" "ac_cv_header_dlfcn_h" "$ac_includes_default"
+if test "x$ac_cv_header_dlfcn_h" = xyes
+then :
+ printf "%s\n" "#define HAVE_DLFCN_H 1" >>confdefs.h
ac_fn_c_check_func "$LINENO" "dlsym" "ac_cv_func_dlsym"
-if test "x$ac_cv_func_dlsym" = xyes; then :
-
-else
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlsym in -ldl" >&5
-$as_echo_n "checking for dlsym in -ldl... " >&6; }
-if ${ac_cv_lib_dl_dlsym+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+if test "x$ac_cv_func_dlsym" = xyes
+then :
+
+else $as_nop
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for dlsym in -ldl" >&5
+printf %s "checking for dlsym in -ldl... " >&6; }
+if test ${ac_cv_lib_dl_dlsym+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
ac_check_lib_save_LIBS=$LIBS
LIBS="-ldl $LIBS"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
@@ -10801,45 +13021,44 @@ 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. */
-#ifdef __cplusplus
-extern "C"
-#endif
char dlsym ();
int
-main ()
+main (void)
{
return dlsym ();
;
return 0;
}
_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
+if ac_fn_c_try_link "$LINENO"
+then :
ac_cv_lib_dl_dlsym=yes
-else
+else $as_nop
ac_cv_lib_dl_dlsym=no
fi
-rm -f core conftest.err conftest.$ac_objext \
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
LIBS=$ac_check_lib_save_LIBS
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlsym" >&5
-$as_echo "$ac_cv_lib_dl_dlsym" >&6; }
-if test "x$ac_cv_lib_dl_dlsym" = xyes; then :
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlsym" >&5
+printf "%s\n" "$ac_cv_lib_dl_dlsym" >&6; }
+if test "x$ac_cv_lib_dl_dlsym" = xyes
+then :
LIBS="$LIBS -ldl"
-else
+else $as_nop
have_dlsym="0"
fi
fi
-else
+else $as_nop
have_dlsym="0"
fi
done
-
if test "x$have_dlsym" = "x1" ; then
- $as_echo "#define JEMALLOC_HAVE_DLSYM " >>confdefs.h
+
+printf "%s\n" "#define JEMALLOC_HAVE_DLSYM " >>confdefs.h
fi
else
@@ -10847,18 +13066,19 @@ done
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether pthread_atfork(3) is compilable" >&5
-$as_echo_n "checking whether pthread_atfork(3) is compilable... " >&6; }
-if ${je_cv_pthread_atfork+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether pthread_atfork(3) is compilable" >&5
+printf %s "checking whether pthread_atfork(3) is compilable... " >&6; }
+if test ${je_cv_pthread_atfork+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <pthread.h>
int
-main ()
+main (void)
{
pthread_atfork((void *)0, (void *)0, (void *)0);
@@ -10867,34 +13087,37 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
+if ac_fn_c_try_link "$LINENO"
+then :
je_cv_pthread_atfork=yes
-else
+else $as_nop
je_cv_pthread_atfork=no
fi
-rm -f core conftest.err conftest.$ac_objext \
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $je_cv_pthread_atfork" >&5
-$as_echo "$je_cv_pthread_atfork" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $je_cv_pthread_atfork" >&5
+printf "%s\n" "$je_cv_pthread_atfork" >&6; }
if test "x${je_cv_pthread_atfork}" = "xyes" ; then
- $as_echo "#define JEMALLOC_HAVE_PTHREAD_ATFORK " >>confdefs.h
+
+printf "%s\n" "#define JEMALLOC_HAVE_PTHREAD_ATFORK " >>confdefs.h
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether pthread_setname_np(3) is compilable" >&5
-$as_echo_n "checking whether pthread_setname_np(3) is compilable... " >&6; }
-if ${je_cv_pthread_setname_np+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether pthread_setname_np(3) is compilable" >&5
+printf %s "checking whether pthread_setname_np(3) is compilable... " >&6; }
+if test ${je_cv_pthread_setname_np+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <pthread.h>
int
-main ()
+main (void)
{
pthread_setname_np(pthread_self(), "setname_test");
@@ -10903,19 +13126,110 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
+if ac_fn_c_try_link "$LINENO"
+then :
je_cv_pthread_setname_np=yes
-else
+else $as_nop
je_cv_pthread_setname_np=no
fi
-rm -f core conftest.err conftest.$ac_objext \
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $je_cv_pthread_setname_np" >&5
-$as_echo "$je_cv_pthread_setname_np" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $je_cv_pthread_setname_np" >&5
+printf "%s\n" "$je_cv_pthread_setname_np" >&6; }
if test "x${je_cv_pthread_setname_np}" = "xyes" ; then
- $as_echo "#define JEMALLOC_HAVE_PTHREAD_SETNAME_NP " >>confdefs.h
+
+printf "%s\n" "#define JEMALLOC_HAVE_PTHREAD_SETNAME_NP " >>confdefs.h
+
+ fi
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether pthread_getname_np(3) is compilable" >&5
+printf %s "checking whether pthread_getname_np(3) is compilable... " >&6; }
+if test ${je_cv_pthread_getname_np+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#include <pthread.h>
+#include <stdlib.h>
+
+int
+main (void)
+{
+
+ {
+ char *name = malloc(16);
+ pthread_getname_np(pthread_self(), name, 16);
+ free(name);
+ }
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+ je_cv_pthread_getname_np=yes
+else $as_nop
+ je_cv_pthread_getname_np=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $je_cv_pthread_getname_np" >&5
+printf "%s\n" "$je_cv_pthread_getname_np" >&6; }
+
+ if test "x${je_cv_pthread_getname_np}" = "xyes" ; then
+
+printf "%s\n" "#define JEMALLOC_HAVE_PTHREAD_GETNAME_NP " >>confdefs.h
+
+ fi
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether pthread_get_name_np(3) is compilable" >&5
+printf %s "checking whether pthread_get_name_np(3) is compilable... " >&6; }
+if test ${je_cv_pthread_get_name_np+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#include <pthread.h>
+#include <pthread_np.h>
+#include <stdlib.h>
+
+int
+main (void)
+{
+
+ {
+ char *name = malloc(16);
+ pthread_get_name_np(pthread_self(), name, 16);
+ free(name);
+ }
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+ je_cv_pthread_get_name_np=yes
+else $as_nop
+ je_cv_pthread_get_name_np=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $je_cv_pthread_get_name_np" >&5
+printf "%s\n" "$je_cv_pthread_get_name_np" >&6; }
+
+ if test "x${je_cv_pthread_get_name_np}" = "xyes" ; then
+
+printf "%s\n" "#define JEMALLOC_HAVE_PTHREAD_GET_NAME_NP " >>confdefs.h
fi
fi
@@ -10929,11 +13243,12 @@ fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing clock_gettime" >&5
-$as_echo_n "checking for library containing clock_gettime... " >&6; }
-if ${ac_cv_search_clock_gettime+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing clock_gettime" >&5
+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
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
@@ -10941,46 +13256,48 @@ 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. */
-#ifdef __cplusplus
-extern "C"
-#endif
char clock_gettime ();
int
-main ()
+main (void)
{
return clock_gettime ();
;
return 0;
}
_ACEOF
-for ac_lib in '' rt; do
+for ac_lib in '' rt
+do
if test -z "$ac_lib"; then
ac_res="none required"
else
ac_res=-l$ac_lib
LIBS="-l$ac_lib $ac_func_search_save_LIBS"
fi
- if ac_fn_c_try_link "$LINENO"; then :
+ if ac_fn_c_try_link "$LINENO"
+then :
ac_cv_search_clock_gettime=$ac_res
fi
-rm -f core conftest.err conftest.$ac_objext \
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext
- if ${ac_cv_search_clock_gettime+:} false; then :
+ if test ${ac_cv_search_clock_gettime+y}
+then :
break
fi
done
-if ${ac_cv_search_clock_gettime+:} false; then :
+if test ${ac_cv_search_clock_gettime+y}
+then :
-else
+else $as_nop
ac_cv_search_clock_gettime=no
fi
rm conftest.$ac_ext
LIBS=$ac_func_search_save_LIBS
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_clock_gettime" >&5
-$as_echo "$ac_cv_search_clock_gettime" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_clock_gettime" >&5
+printf "%s\n" "$ac_cv_search_clock_gettime" >&6; }
ac_res=$ac_cv_search_clock_gettime
-if test "$ac_res" != no; then :
+if test "$ac_res" != no
+then :
test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
fi
@@ -10993,8 +13310,8 @@ if test "x$je_cv_cray_prgenv_wrapper" = "xyes" ; then
unset ac_cv_search_clock_gettime
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -dynamic" >&5
-$as_echo_n "checking whether compiler supports -dynamic... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -dynamic" >&5
+printf %s "checking whether compiler supports -dynamic... " >&6; }
T_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
T_APPEND_V=-dynamic
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
@@ -11015,7 +13332,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
int
-main ()
+main (void)
{
return 0;
@@ -11024,18 +13341,19 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
je_cv_cflags_added=-dynamic
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
je_cv_cflags_added=
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
CONFIGURE_CFLAGS="${T_CONFIGURE_CFLAGS}"
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
else
@@ -11043,11 +13361,12 @@ else
fi
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing clock_gettime" >&5
-$as_echo_n "checking for library containing clock_gettime... " >&6; }
-if ${ac_cv_search_clock_gettime+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for library containing clock_gettime" >&5
+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
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
@@ -11055,46 +13374,48 @@ 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. */
-#ifdef __cplusplus
-extern "C"
-#endif
char clock_gettime ();
int
-main ()
+main (void)
{
return clock_gettime ();
;
return 0;
}
_ACEOF
-for ac_lib in '' rt; do
+for ac_lib in '' rt
+do
if test -z "$ac_lib"; then
ac_res="none required"
else
ac_res=-l$ac_lib
LIBS="-l$ac_lib $ac_func_search_save_LIBS"
fi
- if ac_fn_c_try_link "$LINENO"; then :
+ if ac_fn_c_try_link "$LINENO"
+then :
ac_cv_search_clock_gettime=$ac_res
fi
-rm -f core conftest.err conftest.$ac_objext \
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext
- if ${ac_cv_search_clock_gettime+:} false; then :
+ if test ${ac_cv_search_clock_gettime+y}
+then :
break
fi
done
-if ${ac_cv_search_clock_gettime+:} false; then :
+if test ${ac_cv_search_clock_gettime+y}
+then :
-else
+else $as_nop
ac_cv_search_clock_gettime=no
fi
rm conftest.$ac_ext
LIBS=$ac_func_search_save_LIBS
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_clock_gettime" >&5
-$as_echo "$ac_cv_search_clock_gettime" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_clock_gettime" >&5
+printf "%s\n" "$ac_cv_search_clock_gettime" >&6; }
ac_res=$ac_cv_search_clock_gettime
-if test "$ac_res" != no; then :
+if test "$ac_res" != no
+then :
test "$ac_res" = "none required" || LIBS="$ac_res $LIBS"
fi
@@ -11112,18 +13433,19 @@ fi
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether clock_gettime(CLOCK_MONOTONIC_COARSE, ...) is compilable" >&5
-$as_echo_n "checking whether clock_gettime(CLOCK_MONOTONIC_COARSE, ...) is compilable... " >&6; }
-if ${je_cv_clock_monotonic_coarse+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether clock_gettime(CLOCK_MONOTONIC_COARSE, ...) is compilable" >&5
+printf %s "checking whether clock_gettime(CLOCK_MONOTONIC_COARSE, ...) is compilable... " >&6; }
+if test ${je_cv_clock_monotonic_coarse+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <time.h>
int
-main ()
+main (void)
{
struct timespec ts;
@@ -11134,28 +13456,31 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
+if ac_fn_c_try_link "$LINENO"
+then :
je_cv_clock_monotonic_coarse=yes
-else
+else $as_nop
je_cv_clock_monotonic_coarse=no
fi
-rm -f core conftest.err conftest.$ac_objext \
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $je_cv_clock_monotonic_coarse" >&5
-$as_echo "$je_cv_clock_monotonic_coarse" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $je_cv_clock_monotonic_coarse" >&5
+printf "%s\n" "$je_cv_clock_monotonic_coarse" >&6; }
if test "x${je_cv_clock_monotonic_coarse}" = "xyes" ; then
- $as_echo "#define JEMALLOC_HAVE_CLOCK_MONOTONIC_COARSE 1" >>confdefs.h
+
+printf "%s\n" "#define JEMALLOC_HAVE_CLOCK_MONOTONIC_COARSE " >>confdefs.h
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether clock_gettime(CLOCK_MONOTONIC, ...) is compilable" >&5
-$as_echo_n "checking whether clock_gettime(CLOCK_MONOTONIC, ...) is compilable... " >&6; }
-if ${je_cv_clock_monotonic+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether clock_gettime(CLOCK_MONOTONIC, ...) is compilable" >&5
+printf %s "checking whether clock_gettime(CLOCK_MONOTONIC, ...) is compilable... " >&6; }
+if test ${je_cv_clock_monotonic+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
@@ -11163,7 +13488,7 @@ else
#include <time.h>
int
-main ()
+main (void)
{
struct timespec ts;
@@ -11177,35 +13502,38 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
+if ac_fn_c_try_link "$LINENO"
+then :
je_cv_clock_monotonic=yes
-else
+else $as_nop
je_cv_clock_monotonic=no
fi
-rm -f core conftest.err conftest.$ac_objext \
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $je_cv_clock_monotonic" >&5
-$as_echo "$je_cv_clock_monotonic" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $je_cv_clock_monotonic" >&5
+printf "%s\n" "$je_cv_clock_monotonic" >&6; }
if test "x${je_cv_clock_monotonic}" = "xyes" ; then
- $as_echo "#define JEMALLOC_HAVE_CLOCK_MONOTONIC 1" >>confdefs.h
+
+printf "%s\n" "#define JEMALLOC_HAVE_CLOCK_MONOTONIC " >>confdefs.h
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether mach_absolute_time() is compilable" >&5
-$as_echo_n "checking whether mach_absolute_time() is compilable... " >&6; }
-if ${je_cv_mach_absolute_time+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether mach_absolute_time() is compilable" >&5
+printf %s "checking whether mach_absolute_time() is compilable... " >&6; }
+if test ${je_cv_mach_absolute_time+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <mach/mach_time.h>
int
-main ()
+main (void)
{
mach_absolute_time();
@@ -11214,31 +13542,76 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
+if ac_fn_c_try_link "$LINENO"
+then :
je_cv_mach_absolute_time=yes
-else
+else $as_nop
je_cv_mach_absolute_time=no
fi
-rm -f core conftest.err conftest.$ac_objext \
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $je_cv_mach_absolute_time" >&5
-$as_echo "$je_cv_mach_absolute_time" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $je_cv_mach_absolute_time" >&5
+printf "%s\n" "$je_cv_mach_absolute_time" >&6; }
if test "x${je_cv_mach_absolute_time}" = "xyes" ; then
- $as_echo "#define JEMALLOC_HAVE_MACH_ABSOLUTE_TIME 1" >>confdefs.h
+
+printf "%s\n" "#define JEMALLOC_HAVE_MACH_ABSOLUTE_TIME " >>confdefs.h
+
+fi
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether clock_gettime(CLOCK_REALTIME, ...) is compilable" >&5
+printf %s "checking whether clock_gettime(CLOCK_REALTIME, ...) is compilable... " >&6; }
+if test ${je_cv_clock_realtime+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#include <time.h>
+
+int
+main (void)
+{
+
+ struct timespec ts;
+
+ clock_gettime(CLOCK_REALTIME, &ts);
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+ je_cv_clock_realtime=yes
+else $as_nop
+ je_cv_clock_realtime=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $je_cv_clock_realtime" >&5
+printf "%s\n" "$je_cv_clock_realtime" >&6; }
+
+if test "x${je_cv_clock_realtime}" = "xyes" ; then
+
+printf "%s\n" "#define JEMALLOC_HAVE_CLOCK_REALTIME " >>confdefs.h
fi
# Check whether --enable-syscall was given.
-if test "${enable_syscall+set}" = set; then :
+if test ${enable_syscall+y}
+then :
enableval=$enable_syscall; if test "x$enable_syscall" = "xno" ; then
enable_syscall="0"
else
enable_syscall="1"
fi
-else
+else $as_nop
enable_syscall="1"
fi
@@ -11247,8 +13620,8 @@ if test "x$enable_syscall" = "x1" ; then
SAVED_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -Werror" >&5
-$as_echo_n "checking whether compiler supports -Werror... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -Werror" >&5
+printf %s "checking whether compiler supports -Werror... " >&6; }
T_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
T_APPEND_V=-Werror
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
@@ -11269,7 +13642,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
int
-main ()
+main (void)
{
return 0;
@@ -11278,18 +13651,19 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
je_cv_cflags_added=-Werror
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
je_cv_cflags_added=
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
CONFIGURE_CFLAGS="${T_CONFIGURE_CFLAGS}"
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
else
@@ -11298,11 +13672,12 @@ fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether syscall(2) is compilable" >&5
-$as_echo_n "checking whether syscall(2) is compilable... " >&6; }
-if ${je_cv_syscall+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether syscall(2) is compilable" >&5
+printf %s "checking whether syscall(2) is compilable... " >&6; }
+if test ${je_cv_syscall+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
@@ -11310,7 +13685,7 @@ else
#include <unistd.h>
int
-main ()
+main (void)
{
syscall(SYS_write, 2, "hello", 5);
@@ -11319,16 +13694,17 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
+if ac_fn_c_try_link "$LINENO"
+then :
je_cv_syscall=yes
-else
+else $as_nop
je_cv_syscall=no
fi
-rm -f core conftest.err conftest.$ac_objext \
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $je_cv_syscall" >&5
-$as_echo "$je_cv_syscall" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $je_cv_syscall" >&5
+printf "%s\n" "$je_cv_syscall" >&6; }
CONFIGURE_CFLAGS="${SAVED_CONFIGURE_CFLAGS}"
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
@@ -11339,122 +13715,151 @@ fi
if test "x$je_cv_syscall" = "xyes" ; then
- $as_echo "#define JEMALLOC_USE_SYSCALL " >>confdefs.h
+
+printf "%s\n" "#define JEMALLOC_USE_SYSCALL " >>confdefs.h
fi
fi
ac_fn_c_check_func "$LINENO" "secure_getenv" "ac_cv_func_secure_getenv"
-if test "x$ac_cv_func_secure_getenv" = xyes; then :
+if test "x$ac_cv_func_secure_getenv" = xyes
+then :
have_secure_getenv="1"
-else
+else $as_nop
have_secure_getenv="0"
fi
if test "x$have_secure_getenv" = "x1" ; then
- $as_echo "#define JEMALLOC_HAVE_SECURE_GETENV " >>confdefs.h
+
+printf "%s\n" "#define JEMALLOC_HAVE_SECURE_GETENV " >>confdefs.h
fi
ac_fn_c_check_func "$LINENO" "sched_getcpu" "ac_cv_func_sched_getcpu"
-if test "x$ac_cv_func_sched_getcpu" = xyes; then :
+if test "x$ac_cv_func_sched_getcpu" = xyes
+then :
have_sched_getcpu="1"
-else
+else $as_nop
have_sched_getcpu="0"
fi
if test "x$have_sched_getcpu" = "x1" ; then
- $as_echo "#define JEMALLOC_HAVE_SCHED_GETCPU " >>confdefs.h
+
+printf "%s\n" "#define JEMALLOC_HAVE_SCHED_GETCPU " >>confdefs.h
fi
ac_fn_c_check_func "$LINENO" "sched_setaffinity" "ac_cv_func_sched_setaffinity"
-if test "x$ac_cv_func_sched_setaffinity" = xyes; then :
+if test "x$ac_cv_func_sched_setaffinity" = xyes
+then :
have_sched_setaffinity="1"
-else
+else $as_nop
have_sched_setaffinity="0"
fi
if test "x$have_sched_setaffinity" = "x1" ; then
- $as_echo "#define JEMALLOC_HAVE_SCHED_SETAFFINITY " >>confdefs.h
+
+printf "%s\n" "#define JEMALLOC_HAVE_SCHED_SETAFFINITY " >>confdefs.h
fi
ac_fn_c_check_func "$LINENO" "issetugid" "ac_cv_func_issetugid"
-if test "x$ac_cv_func_issetugid" = xyes; then :
+if test "x$ac_cv_func_issetugid" = xyes
+then :
have_issetugid="1"
-else
+else $as_nop
have_issetugid="0"
fi
if test "x$have_issetugid" = "x1" ; then
- $as_echo "#define JEMALLOC_HAVE_ISSETUGID " >>confdefs.h
+
+printf "%s\n" "#define JEMALLOC_HAVE_ISSETUGID " >>confdefs.h
fi
ac_fn_c_check_func "$LINENO" "_malloc_thread_cleanup" "ac_cv_func__malloc_thread_cleanup"
-if test "x$ac_cv_func__malloc_thread_cleanup" = xyes; then :
+if test "x$ac_cv_func__malloc_thread_cleanup" = xyes
+then :
have__malloc_thread_cleanup="1"
-else
+else $as_nop
have__malloc_thread_cleanup="0"
fi
if test "x$have__malloc_thread_cleanup" = "x1" ; then
- $as_echo "#define JEMALLOC_MALLOC_THREAD_CLEANUP " >>confdefs.h
- wrap_syms="${wrap_syms} _malloc_thread_cleanup"
+printf "%s\n" "#define JEMALLOC_MALLOC_THREAD_CLEANUP " >>confdefs.h
+
+ wrap_syms="${wrap_syms} _malloc_thread_cleanup _malloc_tsd_cleanup_register"
force_tls="1"
fi
ac_fn_c_check_func "$LINENO" "_pthread_mutex_init_calloc_cb" "ac_cv_func__pthread_mutex_init_calloc_cb"
-if test "x$ac_cv_func__pthread_mutex_init_calloc_cb" = xyes; then :
+if test "x$ac_cv_func__pthread_mutex_init_calloc_cb" = xyes
+then :
have__pthread_mutex_init_calloc_cb="1"
-else
+else $as_nop
have__pthread_mutex_init_calloc_cb="0"
fi
if test "x$have__pthread_mutex_init_calloc_cb" = "x1" ; then
- $as_echo "#define JEMALLOC_MUTEX_INIT_CB 1" >>confdefs.h
+
+printf "%s\n" "#define JEMALLOC_MUTEX_INIT_CB " >>confdefs.h
wrap_syms="${wrap_syms} _malloc_prefork _malloc_postfork"
fi
+ac_fn_c_check_func "$LINENO" "memcntl" "ac_cv_func_memcntl"
+if test "x$ac_cv_func_memcntl" = xyes
+then :
+ have_memcntl="1"
+else $as_nop
+ have_memcntl="0"
+fi
+
+if test "x$have_memcntl" = "x1" ; then
+
+printf "%s\n" "#define JEMALLOC_HAVE_MEMCNTL " >>confdefs.h
+
+fi
+
# Check whether --enable-lazy_lock was given.
-if test "${enable_lazy_lock+set}" = set; then :
+if test ${enable_lazy_lock+y}
+then :
enableval=$enable_lazy_lock; if test "x$enable_lazy_lock" = "xno" ; then
enable_lazy_lock="0"
else
enable_lazy_lock="1"
fi
-else
+else $as_nop
enable_lazy_lock=""
fi
if test "x${enable_lazy_lock}" = "x" ; then
if test "x${force_lazy_lock}" = "x1" ; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: Forcing lazy-lock to avoid allocator/threading bootstrap issues" >&5
-$as_echo "Forcing lazy-lock to avoid allocator/threading bootstrap issues" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Forcing lazy-lock to avoid allocator/threading bootstrap issues" >&5
+printf "%s\n" "Forcing lazy-lock to avoid allocator/threading bootstrap issues" >&6; }
enable_lazy_lock="1"
else
enable_lazy_lock="0"
fi
fi
if test "x${enable_lazy_lock}" = "x1" -a "x${abi}" = "xpecoff" ; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: Forcing no lazy-lock because thread creation monitoring is unimplemented" >&5
-$as_echo "Forcing no lazy-lock because thread creation monitoring is unimplemented" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: Forcing no lazy-lock because thread creation monitoring is unimplemented" >&5
+printf "%s\n" "Forcing no lazy-lock because thread creation monitoring is unimplemented" >&6; }
enable_lazy_lock="0"
fi
if test "x$enable_lazy_lock" = "x1" ; then
if test "x$have_dlsym" = "x1" ; then
- $as_echo "#define JEMALLOC_LAZY_LOCK " >>confdefs.h
+
+printf "%s\n" "#define JEMALLOC_LAZY_LOCK " >>confdefs.h
else
as_fn_error $? "Missing dlsym support: lazy-lock cannot be enabled." "$LINENO" 5
@@ -11470,15 +13875,15 @@ else
enable_tls="1"
fi
if test "x${enable_tls}" = "x1" ; then
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for TLS" >&5
-$as_echo_n "checking for TLS... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for TLS" >&5
+printf %s "checking for TLS... " >&6; }
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
__thread int x;
int
-main ()
+main (void)
{
x = 42;
@@ -11489,33 +13894,34 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-else
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+if ac_fn_c_try_compile "$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
+printf "%s\n" "no" >&6; }
enable_tls="0"
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
else
enable_tls="0"
fi
if test "x${enable_tls}" = "x1" ; then
- cat >>confdefs.h <<_ACEOF
-#define JEMALLOC_TLS
-_ACEOF
+
+printf "%s\n" "#define JEMALLOC_TLS " >>confdefs.h
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C11 atomics is compilable" >&5
-$as_echo_n "checking whether C11 atomics is compilable... " >&6; }
-if ${je_cv_c11_atomics+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether C11 atomics is compilable" >&5
+printf %s "checking whether C11 atomics is compilable... " >&6; }
+if test ${je_cv_c11_atomics+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
@@ -11527,7 +13933,7 @@ else
#endif
int
-main ()
+main (void)
{
uint64_t *p = (uint64_t *)0;
@@ -11540,35 +13946,38 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
+if ac_fn_c_try_link "$LINENO"
+then :
je_cv_c11_atomics=yes
-else
+else $as_nop
je_cv_c11_atomics=no
fi
-rm -f core conftest.err conftest.$ac_objext \
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $je_cv_c11_atomics" >&5
-$as_echo "$je_cv_c11_atomics" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $je_cv_c11_atomics" >&5
+printf "%s\n" "$je_cv_c11_atomics" >&6; }
if test "x${je_cv_c11_atomics}" = "xyes" ; then
- $as_echo "#define JEMALLOC_C11_ATOMICS 1" >>confdefs.h
+
+printf "%s\n" "#define JEMALLOC_C11_ATOMICS " >>confdefs.h
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether GCC __atomic atomics is compilable" >&5
-$as_echo_n "checking whether GCC __atomic atomics is compilable... " >&6; }
-if ${je_cv_gcc_atomic_atomics+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether GCC __atomic atomics is compilable" >&5
+printf %s "checking whether GCC __atomic atomics is compilable... " >&6; }
+if test ${je_cv_gcc_atomic_atomics+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
-main ()
+main (void)
{
int x = 0;
@@ -11581,33 +13990,36 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
+if ac_fn_c_try_link "$LINENO"
+then :
je_cv_gcc_atomic_atomics=yes
-else
+else $as_nop
je_cv_gcc_atomic_atomics=no
fi
-rm -f core conftest.err conftest.$ac_objext \
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $je_cv_gcc_atomic_atomics" >&5
-$as_echo "$je_cv_gcc_atomic_atomics" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $je_cv_gcc_atomic_atomics" >&5
+printf "%s\n" "$je_cv_gcc_atomic_atomics" >&6; }
if test "x${je_cv_gcc_atomic_atomics}" = "xyes" ; then
- $as_echo "#define JEMALLOC_GCC_ATOMIC_ATOMICS 1" >>confdefs.h
+printf "%s\n" "#define JEMALLOC_GCC_ATOMIC_ATOMICS " >>confdefs.h
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether GCC 8-bit __atomic atomics is compilable" >&5
-$as_echo_n "checking whether GCC 8-bit __atomic atomics is compilable... " >&6; }
-if ${je_cv_gcc_u8_atomic_atomics+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether GCC 8-bit __atomic atomics is compilable" >&5
+printf %s "checking whether GCC 8-bit __atomic atomics is compilable... " >&6; }
+if test ${je_cv_gcc_u8_atomic_atomics+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
-main ()
+main (void)
{
unsigned char x = 0;
@@ -11620,36 +14032,39 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
+if ac_fn_c_try_link "$LINENO"
+then :
je_cv_gcc_u8_atomic_atomics=yes
-else
+else $as_nop
je_cv_gcc_u8_atomic_atomics=no
fi
-rm -f core conftest.err conftest.$ac_objext \
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $je_cv_gcc_u8_atomic_atomics" >&5
-$as_echo "$je_cv_gcc_u8_atomic_atomics" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $je_cv_gcc_u8_atomic_atomics" >&5
+printf "%s\n" "$je_cv_gcc_u8_atomic_atomics" >&6; }
if test "x${je_cv_gcc_u8_atomic_atomics}" = "xyes" ; then
- $as_echo "#define JEMALLOC_GCC_U8_ATOMIC_ATOMICS 1" >>confdefs.h
+
+printf "%s\n" "#define JEMALLOC_GCC_U8_ATOMIC_ATOMICS " >>confdefs.h
fi
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether GCC __sync atomics is compilable" >&5
-$as_echo_n "checking whether GCC __sync atomics is compilable... " >&6; }
-if ${je_cv_gcc_sync_atomics+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether GCC __sync atomics is compilable" >&5
+printf %s "checking whether GCC __sync atomics is compilable... " >&6; }
+if test ${je_cv_gcc_sync_atomics+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
-main ()
+main (void)
{
int x = 0;
@@ -11661,33 +14076,36 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
+if ac_fn_c_try_link "$LINENO"
+then :
je_cv_gcc_sync_atomics=yes
-else
+else $as_nop
je_cv_gcc_sync_atomics=no
fi
-rm -f core conftest.err conftest.$ac_objext \
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $je_cv_gcc_sync_atomics" >&5
-$as_echo "$je_cv_gcc_sync_atomics" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $je_cv_gcc_sync_atomics" >&5
+printf "%s\n" "$je_cv_gcc_sync_atomics" >&6; }
if test "x${je_cv_gcc_sync_atomics}" = "xyes" ; then
- $as_echo "#define JEMALLOC_GCC_SYNC_ATOMICS 1" >>confdefs.h
+printf "%s\n" "#define JEMALLOC_GCC_SYNC_ATOMICS " >>confdefs.h
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether GCC 8-bit __sync atomics is compilable" >&5
-$as_echo_n "checking whether GCC 8-bit __sync atomics is compilable... " >&6; }
-if ${je_cv_gcc_u8_sync_atomics+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether GCC 8-bit __sync atomics is compilable" >&5
+printf %s "checking whether GCC 8-bit __sync atomics is compilable... " >&6; }
+if test ${je_cv_gcc_u8_sync_atomics+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
-main ()
+main (void)
{
unsigned char x = 0;
@@ -11699,30 +14117,33 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
+if ac_fn_c_try_link "$LINENO"
+then :
je_cv_gcc_u8_sync_atomics=yes
-else
+else $as_nop
je_cv_gcc_u8_sync_atomics=no
fi
-rm -f core conftest.err conftest.$ac_objext \
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $je_cv_gcc_u8_sync_atomics" >&5
-$as_echo "$je_cv_gcc_u8_sync_atomics" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $je_cv_gcc_u8_sync_atomics" >&5
+printf "%s\n" "$je_cv_gcc_u8_sync_atomics" >&6; }
if test "x${je_cv_gcc_u8_sync_atomics}" = "xyes" ; then
- $as_echo "#define JEMALLOC_GCC_U8_SYNC_ATOMICS 1" >>confdefs.h
+
+printf "%s\n" "#define JEMALLOC_GCC_U8_SYNC_ATOMICS " >>confdefs.h
fi
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether Darwin OSAtomic*() is compilable" >&5
-$as_echo_n "checking whether Darwin OSAtomic*() is compilable... " >&6; }
-if ${je_cv_osatomic+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether Darwin OSAtomic*() is compilable" >&5
+printf %s "checking whether Darwin OSAtomic*() is compilable... " >&6; }
+if test ${je_cv_osatomic+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
@@ -11730,7 +14151,7 @@ else
#include <inttypes.h>
int
-main ()
+main (void)
{
{
@@ -11748,36 +14169,39 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
+if ac_fn_c_try_link "$LINENO"
+then :
je_cv_osatomic=yes
-else
+else $as_nop
je_cv_osatomic=no
fi
-rm -f core conftest.err conftest.$ac_objext \
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $je_cv_osatomic" >&5
-$as_echo "$je_cv_osatomic" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $je_cv_osatomic" >&5
+printf "%s\n" "$je_cv_osatomic" >&6; }
if test "x${je_cv_osatomic}" = "xyes" ; then
- $as_echo "#define JEMALLOC_OSATOMIC " >>confdefs.h
+
+printf "%s\n" "#define JEMALLOC_OSATOMIC " >>confdefs.h
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether madvise(2) is compilable" >&5
-$as_echo_n "checking whether madvise(2) is compilable... " >&6; }
-if ${je_cv_madvise+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether madvise(2) is compilable" >&5
+printf %s "checking whether madvise(2) is compilable... " >&6; }
+if test ${je_cv_madvise+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <sys/mman.h>
int
-main ()
+main (void)
{
madvise((void *)0, 0, 0);
@@ -11786,34 +14210,37 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
+if ac_fn_c_try_link "$LINENO"
+then :
je_cv_madvise=yes
-else
+else $as_nop
je_cv_madvise=no
fi
-rm -f core conftest.err conftest.$ac_objext \
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $je_cv_madvise" >&5
-$as_echo "$je_cv_madvise" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $je_cv_madvise" >&5
+printf "%s\n" "$je_cv_madvise" >&6; }
if test "x${je_cv_madvise}" = "xyes" ; then
- $as_echo "#define JEMALLOC_HAVE_MADVISE " >>confdefs.h
+printf "%s\n" "#define JEMALLOC_HAVE_MADVISE " >>confdefs.h
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether madvise(..., MADV_FREE) is compilable" >&5
-$as_echo_n "checking whether madvise(..., MADV_FREE) is compilable... " >&6; }
-if ${je_cv_madv_free+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether madvise(..., MADV_FREE) is compilable" >&5
+printf %s "checking whether madvise(..., MADV_FREE) is compilable... " >&6; }
+if test ${je_cv_madv_free+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <sys/mman.h>
int
-main ()
+main (void)
{
madvise((void *)0, 0, MADV_FREE);
@@ -11822,26 +14249,30 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
+if ac_fn_c_try_link "$LINENO"
+then :
je_cv_madv_free=yes
-else
+else $as_nop
je_cv_madv_free=no
fi
-rm -f core conftest.err conftest.$ac_objext \
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $je_cv_madv_free" >&5
-$as_echo "$je_cv_madv_free" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $je_cv_madv_free" >&5
+printf "%s\n" "$je_cv_madv_free" >&6; }
if test "x${je_cv_madv_free}" = "xyes" ; then
- $as_echo "#define JEMALLOC_PURGE_MADVISE_FREE " >>confdefs.h
+
+printf "%s\n" "#define JEMALLOC_PURGE_MADVISE_FREE " >>confdefs.h
elif test "x${je_cv_madvise}" = "xyes" ; then
case "${host_cpu}" in i686|x86_64)
case "${host}" in *-*-linux*)
- $as_echo "#define JEMALLOC_PURGE_MADVISE_FREE " >>confdefs.h
- $as_echo "#define JEMALLOC_DEFINE_MADVISE_FREE " >>confdefs.h
+printf "%s\n" "#define JEMALLOC_PURGE_MADVISE_FREE " >>confdefs.h
+
+
+printf "%s\n" "#define JEMALLOC_DEFINE_MADVISE_FREE " >>confdefs.h
;;
esac
@@ -11850,18 +14281,19 @@ $as_echo "$je_cv_madv_free" >&6; }
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether madvise(..., MADV_DONTNEED) is compilable" >&5
-$as_echo_n "checking whether madvise(..., MADV_DONTNEED) is compilable... " >&6; }
-if ${je_cv_madv_dontneed+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether madvise(..., MADV_DONTNEED) is compilable" >&5
+printf %s "checking whether madvise(..., MADV_DONTNEED) is compilable... " >&6; }
+if test ${je_cv_madv_dontneed+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <sys/mman.h>
int
-main ()
+main (void)
{
madvise((void *)0, 0, MADV_DONTNEED);
@@ -11870,35 +14302,38 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
+if ac_fn_c_try_link "$LINENO"
+then :
je_cv_madv_dontneed=yes
-else
+else $as_nop
je_cv_madv_dontneed=no
fi
-rm -f core conftest.err conftest.$ac_objext \
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $je_cv_madv_dontneed" >&5
-$as_echo "$je_cv_madv_dontneed" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $je_cv_madv_dontneed" >&5
+printf "%s\n" "$je_cv_madv_dontneed" >&6; }
if test "x${je_cv_madv_dontneed}" = "xyes" ; then
- $as_echo "#define JEMALLOC_PURGE_MADVISE_DONTNEED " >>confdefs.h
+
+printf "%s\n" "#define JEMALLOC_PURGE_MADVISE_DONTNEED " >>confdefs.h
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether madvise(..., MADV_DO[NT]DUMP) is compilable" >&5
-$as_echo_n "checking whether madvise(..., MADV_DO[NT]DUMP) is compilable... " >&6; }
-if ${je_cv_madv_dontdump+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether madvise(..., MADV_DO[NT]DUMP) is compilable" >&5
+printf %s "checking whether madvise(..., MADV_DO[NT]DUMP) is compilable... " >&6; }
+if test ${je_cv_madv_dontdump+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <sys/mman.h>
int
-main ()
+main (void)
{
madvise((void *)0, 0, MADV_DONTDUMP);
@@ -11908,35 +14343,38 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
+if ac_fn_c_try_link "$LINENO"
+then :
je_cv_madv_dontdump=yes
-else
+else $as_nop
je_cv_madv_dontdump=no
fi
-rm -f core conftest.err conftest.$ac_objext \
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $je_cv_madv_dontdump" >&5
-$as_echo "$je_cv_madv_dontdump" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $je_cv_madv_dontdump" >&5
+printf "%s\n" "$je_cv_madv_dontdump" >&6; }
if test "x${je_cv_madv_dontdump}" = "xyes" ; then
- $as_echo "#define JEMALLOC_MADVISE_DONTDUMP " >>confdefs.h
+
+printf "%s\n" "#define JEMALLOC_MADVISE_DONTDUMP " >>confdefs.h
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether madvise(..., MADV_[NO]HUGEPAGE) is compilable" >&5
-$as_echo_n "checking whether madvise(..., MADV_[NO]HUGEPAGE) is compilable... " >&6; }
-if ${je_cv_thp+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether madvise(..., MADV_[NO]HUGEPAGE) is compilable" >&5
+printf %s "checking whether madvise(..., MADV_[NO]HUGEPAGE) is compilable... " >&6; }
+if test ${je_cv_thp+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <sys/mman.h>
int
-main ()
+main (void)
{
madvise((void *)0, 0, MADV_HUGEPAGE);
@@ -11946,40 +14384,204 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
+if ac_fn_c_try_link "$LINENO"
+then :
je_cv_thp=yes
-else
+else $as_nop
je_cv_thp=no
fi
-rm -f core conftest.err conftest.$ac_objext \
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $je_cv_thp" >&5
+printf "%s\n" "$je_cv_thp" >&6; }
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether madvise(..., MADV_[NO]CORE) is compilable" >&5
+printf %s "checking whether madvise(..., MADV_[NO]CORE) is compilable... " >&6; }
+if test ${je_cv_madv_nocore+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#include <sys/mman.h>
+
+int
+main (void)
+{
+
+ madvise((void *)0, 0, MADV_NOCORE);
+ madvise((void *)0, 0, MADV_CORE);
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+ je_cv_madv_nocore=yes
+else $as_nop
+ je_cv_madv_nocore=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $je_cv_thp" >&5
-$as_echo "$je_cv_thp" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $je_cv_madv_nocore" >&5
+printf "%s\n" "$je_cv_madv_nocore" >&6; }
+
+ if test "x${je_cv_madv_nocore}" = "xyes" ; then
+printf "%s\n" "#define JEMALLOC_MADVISE_NOCORE " >>confdefs.h
+
+ fi
case "${host_cpu}" in
arm*)
;;
*)
if test "x${je_cv_thp}" = "xyes" ; then
- $as_echo "#define JEMALLOC_HAVE_MADVISE_HUGE " >>confdefs.h
+
+printf "%s\n" "#define JEMALLOC_HAVE_MADVISE_HUGE " >>confdefs.h
fi
;;
esac
+else
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether posix_madvise is compilable" >&5
+printf %s "checking whether posix_madvise is compilable... " >&6; }
+if test ${je_cv_posix_madvise+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+ #include <sys/mman.h>
+
+int
+main (void)
+{
+
+ posix_madvise((void *)0, 0, 0);
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+ je_cv_posix_madvise=yes
+else $as_nop
+ je_cv_posix_madvise=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+ conftest$ac_exeext conftest.$ac_ext
fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $je_cv_posix_madvise" >&5
+printf "%s\n" "$je_cv_posix_madvise" >&6; }
+ if test "x${je_cv_posix_madvise}" = "xyes" ; then
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for __builtin_clz" >&5
-$as_echo_n "checking for __builtin_clz... " >&6; }
-if ${je_cv_builtin_clz+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+printf "%s\n" "#define JEMALLOC_HAVE_POSIX_MADVISE " >>confdefs.h
+
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether posix_madvise(..., POSIX_MADV_DONTNEED) is compilable" >&5
+printf %s "checking whether posix_madvise(..., POSIX_MADV_DONTNEED) is compilable... " >&6; }
+if test ${je_cv_posix_madv_dontneed+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
+ #include <sys/mman.h>
+
int
-main ()
+main (void)
+{
+
+ posix_madvise((void *)0, 0, POSIX_MADV_DONTNEED);
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+ je_cv_posix_madv_dontneed=yes
+else $as_nop
+ je_cv_posix_madv_dontneed=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $je_cv_posix_madv_dontneed" >&5
+printf "%s\n" "$je_cv_posix_madv_dontneed" >&6; }
+
+ if test "x${je_cv_posix_madv_dontneed}" = "xyes" ; then
+
+printf "%s\n" "#define JEMALLOC_PURGE_POSIX_MADVISE_DONTNEED " >>confdefs.h
+
+ fi
+ fi
+fi
+
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether mprotect(2) is compilable" >&5
+printf %s "checking whether mprotect(2) is compilable... " >&6; }
+if test ${je_cv_mprotect+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+#include <sys/mman.h>
+
+int
+main (void)
+{
+
+ mprotect((void *)0, 0, PROT_NONE);
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"
+then :
+ je_cv_mprotect=yes
+else $as_nop
+ je_cv_mprotect=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
+ conftest$ac_exeext conftest.$ac_ext
+fi
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $je_cv_mprotect" >&5
+printf "%s\n" "$je_cv_mprotect" >&6; }
+
+if test "x${je_cv_mprotect}" = "xyes" ; then
+
+printf "%s\n" "#define JEMALLOC_HAVE_MPROTECT " >>confdefs.h
+
+fi
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for __builtin_clz" >&5
+printf %s "checking for __builtin_clz... " >&6; }
+if test ${je_cv_builtin_clz+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+int
+main (void)
{
{
@@ -11990,34 +14592,41 @@ main ()
unsigned long x = 0;
int y = __builtin_clzl(x);
}
+ {
+ unsigned long long x = 0;
+ int y = __builtin_clzll(x);
+ }
;
return 0;
}
_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
+if ac_fn_c_try_link "$LINENO"
+then :
je_cv_builtin_clz=yes
-else
+else $as_nop
je_cv_builtin_clz=no
fi
-rm -f core conftest.err conftest.$ac_objext \
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $je_cv_builtin_clz" >&5
-$as_echo "$je_cv_builtin_clz" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $je_cv_builtin_clz" >&5
+printf "%s\n" "$je_cv_builtin_clz" >&6; }
if test "x${je_cv_builtin_clz}" = "xyes" ; then
- $as_echo "#define JEMALLOC_HAVE_BUILTIN_CLZ " >>confdefs.h
+
+printf "%s\n" "#define JEMALLOC_HAVE_BUILTIN_CLZ " >>confdefs.h
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether Darwin os_unfair_lock_*() is compilable" >&5
-$as_echo_n "checking whether Darwin os_unfair_lock_*() is compilable... " >&6; }
-if ${je_cv_os_unfair_lock+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether Darwin os_unfair_lock_*() is compilable" >&5
+printf %s "checking whether Darwin os_unfair_lock_*() is compilable... " >&6; }
+if test ${je_cv_os_unfair_lock+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
@@ -12025,7 +14634,7 @@ else
#include <AvailabilityMacros.h>
int
-main ()
+main (void)
{
#if MAC_OS_X_VERSION_MIN_REQUIRED < 101200
@@ -12040,32 +14649,35 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
+if ac_fn_c_try_link "$LINENO"
+then :
je_cv_os_unfair_lock=yes
-else
+else $as_nop
je_cv_os_unfair_lock=no
fi
-rm -f core conftest.err conftest.$ac_objext \
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $je_cv_os_unfair_lock" >&5
-$as_echo "$je_cv_os_unfair_lock" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $je_cv_os_unfair_lock" >&5
+printf "%s\n" "$je_cv_os_unfair_lock" >&6; }
if test "x${je_cv_os_unfair_lock}" = "xyes" ; then
- $as_echo "#define JEMALLOC_OS_UNFAIR_LOCK " >>confdefs.h
+
+printf "%s\n" "#define JEMALLOC_OS_UNFAIR_LOCK " >>confdefs.h
fi
# Check whether --enable-zone-allocator was given.
-if test "${enable_zone_allocator+set}" = set; then :
+if test ${enable_zone_allocator+y}
+then :
enableval=$enable_zone_allocator; if test "x$enable_zone_allocator" = "xno" ; then
enable_zone_allocator="0"
else
enable_zone_allocator="1"
fi
-else
+else $as_nop
if test "x${abi}" = "xmacho"; then
enable_zone_allocator="1"
fi
@@ -12079,19 +14691,21 @@ if test "x${enable_zone_allocator}" = "x1" ; then
if test "x${abi}" != "xmacho"; then
as_fn_error $? "--enable-zone-allocator is only supported on Darwin" "$LINENO" 5
fi
- $as_echo "#define JEMALLOC_ZONE " >>confdefs.h
+
+printf "%s\n" "#define JEMALLOC_ZONE " >>confdefs.h
fi
# Check whether --enable-initial-exec-tls was given.
-if test "${enable_initial_exec_tls+set}" = set; then :
+if test ${enable_initial_exec_tls+y}
+then :
enableval=$enable_initial_exec_tls; if test "x$enable_initial_exec_tls" = "xno" ; then
enable_initial_exec_tls="0"
else
enable_initial_exec_tls="1"
fi
-else
+else $as_nop
enable_initial_exec_tls="1"
fi
@@ -12100,123 +14714,136 @@ fi
if test "x${je_cv_tls_model}" = "xyes" -a \
"x${enable_initial_exec_tls}" = "x1" ; then
- $as_echo "#define JEMALLOC_TLS_MODEL __attribute__((tls_model(\"initial-exec\")))" >>confdefs.h
+
+printf "%s\n" "#define JEMALLOC_TLS_MODEL __attribute__((tls_model(\"initial-exec\")))" >>confdefs.h
else
- $as_echo "#define JEMALLOC_TLS_MODEL " >>confdefs.h
+
+printf "%s\n" "#define JEMALLOC_TLS_MODEL " >>confdefs.h
fi
-if test "x${have_pthread}" = "x1" -a "x${je_cv_os_unfair_lock}" != "xyes" ; then
- $as_echo "#define JEMALLOC_BACKGROUND_THREAD 1" >>confdefs.h
+if test "x${have_pthread}" = "x1" -a "x${je_cv_os_unfair_lock}" != "xyes" -a \
+ "x${abi}" != "xmacho" ; then
+
+printf "%s\n" "#define JEMALLOC_BACKGROUND_THREAD " >>confdefs.h
fi
+if test "x$glibc" = "x1" ; then
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether glibc malloc hook is compilable" >&5
-$as_echo_n "checking whether glibc malloc hook is compilable... " >&6; }
-if ${je_cv_glibc_malloc_hook+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether glibc malloc hook is compilable" >&5
+printf %s "checking whether glibc malloc hook is compilable... " >&6; }
+if test ${je_cv_glibc_malloc_hook+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
-#include <stddef.h>
+ #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);
+ extern void (* __free_hook)(void *ptr);
+ extern void *(* __malloc_hook)(size_t size);
+ extern void *(* __realloc_hook)(void *ptr, size_t size);
int
-main ()
+main (void)
{
- 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);
+ 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);
;
return 0;
}
_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
+if ac_fn_c_try_link "$LINENO"
+then :
je_cv_glibc_malloc_hook=yes
-else
+else $as_nop
je_cv_glibc_malloc_hook=no
fi
-rm -f core conftest.err conftest.$ac_objext \
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $je_cv_glibc_malloc_hook" >&5
-$as_echo "$je_cv_glibc_malloc_hook" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $je_cv_glibc_malloc_hook" >&5
+printf "%s\n" "$je_cv_glibc_malloc_hook" >&6; }
-if test "x${je_cv_glibc_malloc_hook}" = "xyes" ; then
- if test "x${JEMALLOC_PREFIX}" = "x" ; then
- $as_echo "#define JEMALLOC_GLIBC_MALLOC_HOOK " >>confdefs.h
+ if test "x${je_cv_glibc_malloc_hook}" = "xyes" ; then
+ if test "x${JEMALLOC_PREFIX}" = "x" ; then
- wrap_syms="${wrap_syms} __free_hook __malloc_hook __realloc_hook"
+printf "%s\n" "#define JEMALLOC_GLIBC_MALLOC_HOOK " >>confdefs.h
+
+ wrap_syms="${wrap_syms} __free_hook __malloc_hook __realloc_hook"
+ fi
fi
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether glibc memalign hook is compilable" >&5
-$as_echo_n "checking whether glibc memalign hook is compilable... " >&6; }
-if ${je_cv_glibc_memalign_hook+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether glibc memalign hook is compilable" >&5
+printf %s "checking whether glibc memalign hook is compilable... " >&6; }
+if test ${je_cv_glibc_memalign_hook+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
-#include <stddef.h>
+ #include <stddef.h>
-extern void *(* __memalign_hook)(size_t alignment, size_t size);
+ extern void *(* __memalign_hook)(size_t alignment, size_t size);
int
-main ()
+main (void)
{
- void *ptr = 0L;
- if (__memalign_hook) ptr = __memalign_hook(16, 7);
+ void *ptr = 0L;
+ if (__memalign_hook) ptr = __memalign_hook(16, 7);
;
return 0;
}
_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
+if ac_fn_c_try_link "$LINENO"
+then :
je_cv_glibc_memalign_hook=yes
-else
+else $as_nop
je_cv_glibc_memalign_hook=no
fi
-rm -f core conftest.err conftest.$ac_objext \
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $je_cv_glibc_memalign_hook" >&5
-$as_echo "$je_cv_glibc_memalign_hook" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $je_cv_glibc_memalign_hook" >&5
+printf "%s\n" "$je_cv_glibc_memalign_hook" >&6; }
-if test "x${je_cv_glibc_memalign_hook}" = "xyes" ; then
- if test "x${JEMALLOC_PREFIX}" = "x" ; then
- $as_echo "#define JEMALLOC_GLIBC_MEMALIGN_HOOK " >>confdefs.h
+ if test "x${je_cv_glibc_memalign_hook}" = "xyes" ; then
+ if test "x${JEMALLOC_PREFIX}" = "x" ; then
- wrap_syms="${wrap_syms} __memalign_hook"
+printf "%s\n" "#define JEMALLOC_GLIBC_MEMALIGN_HOOK " >>confdefs.h
+
+ wrap_syms="${wrap_syms} __memalign_hook"
+ fi
fi
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether pthreads adaptive mutexes is compilable" >&5
-$as_echo_n "checking whether pthreads adaptive mutexes is compilable... " >&6; }
-if ${je_cv_pthread_mutex_adaptive_np+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether pthreads adaptive mutexes is compilable" >&5
+printf %s "checking whether pthreads adaptive mutexes is compilable... " >&6; }
+if test ${je_cv_pthread_mutex_adaptive_np+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <pthread.h>
int
-main ()
+main (void)
{
pthread_mutexattr_t attr;
@@ -12228,27 +14855,29 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
+if ac_fn_c_try_link "$LINENO"
+then :
je_cv_pthread_mutex_adaptive_np=yes
-else
+else $as_nop
je_cv_pthread_mutex_adaptive_np=no
fi
-rm -f core conftest.err conftest.$ac_objext \
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $je_cv_pthread_mutex_adaptive_np" >&5
-$as_echo "$je_cv_pthread_mutex_adaptive_np" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $je_cv_pthread_mutex_adaptive_np" >&5
+printf "%s\n" "$je_cv_pthread_mutex_adaptive_np" >&6; }
if test "x${je_cv_pthread_mutex_adaptive_np}" = "xyes" ; then
- $as_echo "#define JEMALLOC_HAVE_PTHREAD_MUTEX_ADAPTIVE_NP " >>confdefs.h
+
+printf "%s\n" "#define JEMALLOC_HAVE_PTHREAD_MUTEX_ADAPTIVE_NP " >>confdefs.h
fi
SAVED_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -D_GNU_SOURCE" >&5
-$as_echo_n "checking whether compiler supports -D_GNU_SOURCE... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -D_GNU_SOURCE" >&5
+printf %s "checking whether compiler supports -D_GNU_SOURCE... " >&6; }
T_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
T_APPEND_V=-D_GNU_SOURCE
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
@@ -12269,7 +14898,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
int
-main ()
+main (void)
{
return 0;
@@ -12278,18 +14907,19 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
je_cv_cflags_added=-D_GNU_SOURCE
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
je_cv_cflags_added=
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
CONFIGURE_CFLAGS="${T_CONFIGURE_CFLAGS}"
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
else
@@ -12298,8 +14928,8 @@ fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -Werror" >&5
-$as_echo_n "checking whether compiler supports -Werror... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -Werror" >&5
+printf %s "checking whether compiler supports -Werror... " >&6; }
T_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
T_APPEND_V=-Werror
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
@@ -12320,7 +14950,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
int
-main ()
+main (void)
{
return 0;
@@ -12329,18 +14959,19 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
je_cv_cflags_added=-Werror
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
je_cv_cflags_added=
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
CONFIGURE_CFLAGS="${T_CONFIGURE_CFLAGS}"
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
else
@@ -12349,8 +14980,8 @@ fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -herror_on_warning" >&5
-$as_echo_n "checking whether compiler supports -herror_on_warning... " >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether compiler supports -herror_on_warning" >&5
+printf %s "checking whether compiler supports -herror_on_warning... " >&6; }
T_CONFIGURE_CFLAGS="${CONFIGURE_CFLAGS}"
T_APPEND_V=-herror_on_warning
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${T_APPEND_V}" = "x" ; then
@@ -12371,7 +15002,7 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
int
-main ()
+main (void)
{
return 0;
@@ -12380,18 +15011,19 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
je_cv_cflags_added=-herror_on_warning
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5
-$as_echo "yes" >&6; }
-else
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
+printf "%s\n" "yes" >&6; }
+else $as_nop
je_cv_cflags_added=
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5
-$as_echo "no" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
+printf "%s\n" "no" >&6; }
CONFIGURE_CFLAGS="${T_CONFIGURE_CFLAGS}"
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
CFLAGS="${CONFIGURE_CFLAGS}${SPECIFIED_CFLAGS}"
else
@@ -12400,11 +15032,12 @@ fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether strerror_r returns char with gnu source is compilable" >&5
-$as_echo_n "checking whether strerror_r returns char with gnu source is compilable... " >&6; }
-if ${je_cv_strerror_r_returns_char_with_gnu_source+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether strerror_r returns char with gnu source is compilable" >&5
+printf %s "checking whether strerror_r returns char with gnu source is compilable... " >&6; }
+if test ${je_cv_strerror_r_returns_char_with_gnu_source+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
@@ -12414,7 +15047,7 @@ else
#include <string.h>
int
-main ()
+main (void)
{
char *buffer = (char *) malloc(100);
@@ -12425,16 +15058,17 @@ main ()
return 0;
}
_ACEOF
-if ac_fn_c_try_link "$LINENO"; then :
+if ac_fn_c_try_link "$LINENO"
+then :
je_cv_strerror_r_returns_char_with_gnu_source=yes
-else
+else $as_nop
je_cv_strerror_r_returns_char_with_gnu_source=no
fi
-rm -f core conftest.err conftest.$ac_objext \
+rm -f core conftest.err conftest.$ac_objext conftest.beam \
conftest$ac_exeext conftest.$ac_ext
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $je_cv_strerror_r_returns_char_with_gnu_source" >&5
-$as_echo "$je_cv_strerror_r_returns_char_with_gnu_source" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $je_cv_strerror_r_returns_char_with_gnu_source" >&5
+printf "%s\n" "$je_cv_strerror_r_returns_char_with_gnu_source" >&6; }
CONFIGURE_CFLAGS="${SAVED_CONFIGURE_CFLAGS}"
if test "x${CONFIGURE_CFLAGS}" = "x" -o "x${SPECIFIED_CFLAGS}" = "x" ; then
@@ -12445,100 +15079,150 @@ fi
if test "x${je_cv_strerror_r_returns_char_with_gnu_source}" = "xyes" ; then
- $as_echo "#define JEMALLOC_STRERROR_R_RETURNS_CHAR_WITH_GNU_SOURCE " >>confdefs.h
+
+printf "%s\n" "#define JEMALLOC_STRERROR_R_RETURNS_CHAR_WITH_GNU_SOURCE " >>confdefs.h
fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for stdbool.h that conforms to C99" >&5
-$as_echo_n "checking for stdbool.h that conforms to C99... " >&6; }
-if ${ac_cv_header_stdbool_h+:} false; then :
- $as_echo_n "(cached) " >&6
-else
+ac_fn_c_check_type "$LINENO" "_Bool" "ac_cv_type__Bool" "$ac_includes_default"
+if test "x$ac_cv_type__Bool" = xyes
+then :
+
+printf "%s\n" "#define HAVE__BOOL 1" >>confdefs.h
+
+
+fi
+
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for stdbool.h that conforms to C99" >&5
+printf %s "checking for stdbool.h that conforms to C99... " >&6; }
+if test ${ac_cv_header_stdbool_h+y}
+then :
+ printf %s "(cached) " >&6
+else $as_nop
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
+#include <stdbool.h>
- #include <stdbool.h>
- #ifndef bool
- "error: bool is not defined"
- #endif
- #ifndef false
- "error: false is not defined"
- #endif
- #if false
- "error: false is not 0"
+ #ifndef __bool_true_false_are_defined
+ #error "__bool_true_false_are_defined is not defined"
#endif
- #ifndef true
- "error: true is not defined"
+ char a[__bool_true_false_are_defined == 1 ? 1 : -1];
+
+ /* Regardless of whether this is C++ or "_Bool" is a
+ valid type name, "true" and "false" should be usable
+ in #if expressions and integer constant expressions,
+ and "bool" should be a valid type name. */
+
+ #if !true
+ #error "'true' is not true"
#endif
#if true != 1
- "error: true is not 1"
+ #error "'true' is not equal to 1"
#endif
- #ifndef __bool_true_false_are_defined
- "error: __bool_true_false_are_defined is not defined"
+ char b[true == 1 ? 1 : -1];
+ char c[true];
+
+ #if false
+ #error "'false' is not false"
#endif
+ #if false != 0
+ #error "'false' is not equal to 0"
+ #endif
+ char d[false == 0 ? 1 : -1];
+
+ enum { e = false, f = true, g = false * true, h = true * 256 };
+
+ char i[(bool) 0.5 == true ? 1 : -1];
+ char j[(bool) 0.0 == false ? 1 : -1];
+ char k[sizeof (bool) > 0 ? 1 : -1];
+
+ struct sb { bool s: 1; bool t; } s;
+ char l[sizeof s.t > 0 ? 1 : -1];
- struct s { _Bool s: 1; _Bool t; } s;
-
- char a[true == 1 ? 1 : -1];
- char b[false == 0 ? 1 : -1];
- char c[__bool_true_false_are_defined == 1 ? 1 : -1];
- char d[(bool) 0.5 == true ? 1 : -1];
- /* See body of main program for 'e'. */
- char f[(_Bool) 0.0 == false ? 1 : -1];
- char g[true];
- char h[sizeof (_Bool)];
- char i[sizeof s.t];
- enum { j = false, k = true, l = false * true, m = true * 256 };
/* The following fails for
HP aC++/ANSI C B3910B A.05.55 [Dec 04 2003]. */
- _Bool n[m];
- char o[sizeof n == m * sizeof n[0] ? 1 : -1];
- char p[-1 - (_Bool) 0 < 0 && -1 - (bool) 0 < 0 ? 1 : -1];
+ bool m[h];
+ char n[sizeof m == h * sizeof m[0] ? 1 : -1];
+ char o[-1 - (bool) 0 < 0 ? 1 : -1];
/* Catch a bug in an HP-UX C compiler. See
- http://gcc.gnu.org/ml/gcc-patches/2003-12/msg02303.html
- http://lists.gnu.org/archive/html/bug-coreutils/2005-11/msg00161.html
+ https://gcc.gnu.org/ml/gcc-patches/2003-12/msg02303.html
+ https://lists.gnu.org/archive/html/bug-coreutils/2005-11/msg00161.html
*/
- _Bool q = true;
- _Bool *pq = &q;
+ bool p = true;
+ bool *pp = &p;
+
+ /* C 1999 specifies that bool, true, and false are to be
+ macros, but C++ 2011 and later overrule this. */
+ #if __cplusplus < 201103
+ #ifndef bool
+ #error "bool is not defined"
+ #endif
+ #ifndef false
+ #error "false is not defined"
+ #endif
+ #ifndef true
+ #error "true is not defined"
+ #endif
+ #endif
+
+ /* If _Bool is available, repeat with it all the tests
+ above that used bool. */
+ #ifdef HAVE__BOOL
+ struct sB { _Bool s: 1; _Bool t; } t;
+
+ char q[(_Bool) 0.5 == true ? 1 : -1];
+ char r[(_Bool) 0.0 == false ? 1 : -1];
+ char u[sizeof (_Bool) > 0 ? 1 : -1];
+ char v[sizeof t.t > 0 ? 1 : -1];
+
+ _Bool w[h];
+ char x[sizeof m == h * sizeof m[0] ? 1 : -1];
+ char y[-1 - (_Bool) 0 < 0 ? 1 : -1];
+ _Bool z = true;
+ _Bool *pz = &p;
+ #endif
int
-main ()
+main (void)
{
- bool e = &s;
- *pq |= q;
- *pq |= ! q;
- /* Refer to every declared value, to avoid compiler optimizations. */
- return (!a + !b + !c + !d + !e + !f + !g + !h + !i + !!j + !k + !!l
- + !m + !n + !o + !p + !q + !pq);
+ bool ps = &s;
+ *pp |= p;
+ *pp |= ! p;
+
+ #ifdef HAVE__BOOL
+ _Bool pt = &t;
+ *pz |= z;
+ *pz |= ! z;
+ #endif
+
+ /* Refer to every declared value, so they cannot be
+ discarded as unused. */
+ return (!a + !b + !c + !d + !e + !f + !g + !h + !i + !j + !k
+ + !l + !m + !n + !o + !p + !pp + !ps
+ #ifdef HAVE__BOOL
+ + !q + !r + !u + !v + !w + !x + !y + !z + !pt
+ #endif
+ );
;
return 0;
}
_ACEOF
-if ac_fn_c_try_compile "$LINENO"; then :
+if ac_fn_c_try_compile "$LINENO"
+then :
ac_cv_header_stdbool_h=yes
-else
+else $as_nop
ac_cv_header_stdbool_h=no
fi
-rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-fi
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdbool_h" >&5
-$as_echo "$ac_cv_header_stdbool_h" >&6; }
- ac_fn_c_check_type "$LINENO" "_Bool" "ac_cv_type__Bool" "$ac_includes_default"
-if test "x$ac_cv_type__Bool" = xyes; then :
-
-cat >>confdefs.h <<_ACEOF
-#define HAVE__BOOL 1
-_ACEOF
-
-
+rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
fi
-
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdbool_h" >&5
+printf "%s\n" "$ac_cv_header_stdbool_h" >&6; }
if test $ac_cv_header_stdbool_h = yes; then
-$as_echo "#define HAVE_STDBOOL_H 1" >>confdefs.h
+printf "%s\n" "#define HAVE_STDBOOL_H 1" >>confdefs.h
fi
@@ -12602,8 +15286,8 @@ _ACEOF
case $ac_val in #(
*${as_nl}*)
case $ac_var in #(
- *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
-$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
+ *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5
+printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
esac
case $ac_var in #(
_ | IFS | as_nl) ;; #(
@@ -12633,15 +15317,15 @@ $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;;
/^ac_cv_env_/b end
t clear
:clear
- s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/
+ s/^\([^=]*\)=\(.*[{}].*\)$/test ${\1+y} || &/
t end
s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/
:end' >>confcache
if diff "$cache_file" confcache >/dev/null 2>&1; then :; else
if test -w "$cache_file"; then
if test "x$cache_file" != "x/dev/null"; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5
-$as_echo "$as_me: updating cache $cache_file" >&6;}
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5
+printf "%s\n" "$as_me: updating cache $cache_file" >&6;}
if test ! -f "$cache_file" || test -h "$cache_file"; then
cat confcache >"$cache_file"
else
@@ -12655,8 +15339,8 @@ $as_echo "$as_me: updating cache $cache_file" >&6;}
fi
fi
else
- { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5
-$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;}
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5
+printf "%s\n" "$as_me: not updating unwritable cache $cache_file" >&6;}
fi
fi
rm -f confcache
@@ -12673,7 +15357,7 @@ U=
for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue
# 1. Remove the extension, and $U if already installed.
ac_script='s/\$U\././;s/\.o$//;s/\.obj$//'
- ac_i=`$as_echo "$ac_i" | sed "$ac_script"`
+ ac_i=`printf "%s\n" "$ac_i" | sed "$ac_script"`
# 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR
# will be set to the directory where LIBOBJS objects are built.
as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext"
@@ -12690,8 +15374,8 @@ LTLIBOBJS=$ac_ltlibobjs
ac_write_fail=0
ac_clean_files_save=$ac_clean_files
ac_clean_files="$ac_clean_files $CONFIG_STATUS"
-{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5
-$as_echo "$as_me: creating $CONFIG_STATUS" >&6;}
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5
+printf "%s\n" "$as_me: creating $CONFIG_STATUS" >&6;}
as_write_fail=0
cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1
#! $SHELL
@@ -12714,14 +15398,16 @@ cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1
# Be more Bourne compatible
DUALCASE=1; export DUALCASE # for MKS sh
-if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then :
+as_nop=:
+if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1
+then :
emulate sh
NULLCMD=:
# Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which
# is contrary to our usage. Disable this feature.
alias -g '${1+"$@"}'='"$@"'
setopt NO_GLOB_SUBST
-else
+else $as_nop
case `(set -o) 2>/dev/null` in #(
*posix*) :
set -o posix ;; #(
@@ -12731,46 +15417,46 @@ esac
fi
+
+# Reset variables that may have inherited troublesome values from
+# the environment.
+
+# IFS needs to be set, to space, tab, and newline, in precisely that order.
+# (If _AS_PATH_WALK were called with IFS unset, it would have the
+# side effect of setting IFS to empty, thus disabling word splitting.)
+# Quoting is to prevent editors from complaining about space-tab.
as_nl='
'
export as_nl
-# Printing a long string crashes Solaris 7 /usr/bin/printf.
-as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'
-as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo
-as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo
-# Prefer a ksh shell builtin over an external printf program on Solaris,
-# but without wasting forks for bash or zsh.
-if test -z "$BASH_VERSION$ZSH_VERSION" \
- && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then
- as_echo='print -r --'
- as_echo_n='print -rn --'
-elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then
- as_echo='printf %s\n'
- as_echo_n='printf %s'
-else
- if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then
- as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"'
- as_echo_n='/usr/ucb/echo -n'
- else
- as_echo_body='eval expr "X$1" : "X\\(.*\\)"'
- as_echo_n_body='eval
- arg=$1;
- case $arg in #(
- *"$as_nl"*)
- expr "X$arg" : "X\\(.*\\)$as_nl";
- arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;;
- esac;
- expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl"
- '
- export as_echo_n_body
- as_echo_n='sh -c $as_echo_n_body as_echo'
- fi
- export as_echo_body
- as_echo='sh -c $as_echo_body as_echo'
-fi
+IFS=" "" $as_nl"
+
+PS1='$ '
+PS2='> '
+PS4='+ '
+
+# Ensure predictable behavior from utilities with locale-dependent output.
+LC_ALL=C
+export LC_ALL
+LANGUAGE=C
+export LANGUAGE
+
+# We cannot yet rely on "unset" to work, but we need these variables
+# to be unset--not just set to an empty or harmless value--now, to
+# avoid bugs in old shells (e.g. pre-3.0 UWIN ksh). This construct
+# also avoids known problems related to "unset" and subshell syntax
+# in other old shells (e.g. bash 2.01 and pdksh 5.2.14).
+for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH
+do eval test \${$as_var+y} \
+ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
+done
+
+# Ensure that fds 0, 1, and 2 are open.
+if (exec 3>&0) 2>/dev/null; then :; else exec 0</dev/null; fi
+if (exec 3>&1) 2>/dev/null; then :; else exec 1>/dev/null; fi
+if (exec 3>&2) ; then :; else exec 2>/dev/null; fi
# The user is always right.
-if test "${PATH_SEPARATOR+set}" != set; then
+if ${PATH_SEPARATOR+false} :; then
PATH_SEPARATOR=:
(PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && {
(PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 ||
@@ -12779,13 +15465,6 @@ if test "${PATH_SEPARATOR+set}" != set; then
fi
-# IFS
-# We need space, tab and new line, in precisely that order. Quoting is
-# there to prevent editors from complaining about space-tab.
-# (If _AS_PATH_WALK were called with IFS unset, it would disable word
-# splitting by setting IFS to empty value.)
-IFS=" "" $as_nl"
-
# Find who we are. Look in the path if we contain no directory separator.
as_myself=
case $0 in #((
@@ -12794,8 +15473,12 @@ case $0 in #((
for as_dir in $PATH
do
IFS=$as_save_IFS
- test -z "$as_dir" && as_dir=.
- test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break
+ case $as_dir in #(((
+ '') as_dir=./ ;;
+ */) ;;
+ *) as_dir=$as_dir/ ;;
+ esac
+ test -r "$as_dir$0" && as_myself=$as_dir$0 && break
done
IFS=$as_save_IFS
@@ -12807,30 +15490,10 @@ if test "x$as_myself" = x; then
as_myself=$0
fi
if test ! -f "$as_myself"; then
- $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
+ printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2
exit 1
fi
-# Unset variables that we do not need and which cause bugs (e.g. in
-# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1"
-# suppresses any "Segmentation fault" message there. '((' could
-# trigger a bug in pdksh 5.2.14.
-for as_var in BASH_ENV ENV MAIL MAILPATH
-do eval test x\${$as_var+set} = xset \
- && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || :
-done
-PS1='$ '
-PS2='> '
-PS4='+ '
-
-# NLS nuisances.
-LC_ALL=C
-export LC_ALL
-LANGUAGE=C
-export LANGUAGE
-
-# CDPATH.
-(unset CDPATH) >/dev/null 2>&1 && unset CDPATH
# as_fn_error STATUS ERROR [LINENO LOG_FD]
@@ -12843,13 +15506,14 @@ as_fn_error ()
as_status=$1; test $as_status -eq 0 && as_status=1
if test "$4"; then
as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
- $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
+ printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4
fi
- $as_echo "$as_me: error: $2" >&2
+ printf "%s\n" "$as_me: error: $2" >&2
as_fn_exit $as_status
} # as_fn_error
+
# as_fn_set_status STATUS
# -----------------------
# Set $? to STATUS, without forking.
@@ -12876,18 +15540,20 @@ as_fn_unset ()
{ eval $1=; unset $1;}
}
as_unset=as_fn_unset
+
# as_fn_append VAR VALUE
# ----------------------
# Append the text in VALUE to the end of the definition contained in VAR. Take
# advantage of any shell optimizations that allow amortized linear growth over
# repeated appends, instead of the typical quadratic growth present in naive
# implementations.
-if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then :
+if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null
+then :
eval 'as_fn_append ()
{
eval $1+=\$2
}'
-else
+else $as_nop
as_fn_append ()
{
eval $1=\$$1\$2
@@ -12899,12 +15565,13 @@ fi # as_fn_append
# Perform arithmetic evaluation on the ARGs, and store the result in the
# global $as_val. Take advantage of shells that can avoid forks. The arguments
# must be portable across $(()) and expr.
-if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then :
+if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null
+then :
eval 'as_fn_arith ()
{
as_val=$(( $* ))
}'
-else
+else $as_nop
as_fn_arith ()
{
as_val=`expr "$@" || test $? -eq 1`
@@ -12935,7 +15602,7 @@ as_me=`$as_basename -- "$0" ||
$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \
X"$0" : 'X\(//\)$' \| \
X"$0" : 'X\(/\)' \| . 2>/dev/null ||
-$as_echo X/"$0" |
+printf "%s\n" X/"$0" |
sed '/^.*\/\([^/][^/]*\)\/*$/{
s//\1/
q
@@ -12957,6 +15624,10 @@ as_cr_Letters=$as_cr_letters$as_cr_LETTERS
as_cr_digits='0123456789'
as_cr_alnum=$as_cr_Letters$as_cr_digits
+
+# Determine whether it's possible to make 'echo' print without a newline.
+# These variables are no longer used directly by Autoconf, but are AC_SUBSTed
+# for compatibility with existing Makefiles.
ECHO_C= ECHO_N= ECHO_T=
case `echo -n x` in #(((((
-n*)
@@ -12970,6 +15641,12 @@ case `echo -n x` in #(((((
ECHO_N='-n';;
esac
+# For backward compatibility with old third-party macros, we provide
+# the shell variables $as_echo and $as_echo_n. New code should use
+# AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively.
+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
@@ -13011,7 +15688,7 @@ as_fn_mkdir_p ()
as_dirs=
while :; do
case $as_dir in #(
- *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
+ *\'*) as_qdir=`printf "%s\n" "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'(
*) as_qdir=$as_dir;;
esac
as_dirs="'$as_qdir' $as_dirs"
@@ -13020,7 +15697,7 @@ $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
X"$as_dir" : 'X\(//\)[^/]' \| \
X"$as_dir" : 'X\(//\)$' \| \
X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||
-$as_echo X"$as_dir" |
+printf "%s\n" X"$as_dir" |
sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
s//\1/
q
@@ -13083,7 +15760,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# values after options handling.
ac_log="
This file was extended by $as_me, which was
-generated by GNU Autoconf 2.69. Invocation command line was
+generated by GNU Autoconf 2.71. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
CONFIG_HEADERS = $CONFIG_HEADERS
@@ -13145,14 +15822,16 @@ $config_commands
Report bugs to the package provider."
_ACEOF
+ac_cs_config=`printf "%s\n" "$ac_configure_args" | sed "$ac_safe_unquote"`
+ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\''/g"`
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
-ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
+ac_cs_config='$ac_cs_config_escaped'
ac_cs_version="\\
config.status
-configured by $0, generated by GNU Autoconf 2.69,
+configured by $0, generated by GNU Autoconf 2.71,
with options \\"\$ac_cs_config\\"
-Copyright (C) 2012 Free Software Foundation, Inc.
+Copyright (C) 2021 Free Software Foundation, Inc.
This config.status script is free software; the Free Software Foundation
gives unlimited permission to copy, distribute and modify it."
@@ -13191,15 +15870,15 @@ do
-recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
ac_cs_recheck=: ;;
--version | --versio | --versi | --vers | --ver | --ve | --v | -V )
- $as_echo "$ac_cs_version"; exit ;;
+ printf "%s\n" "$ac_cs_version"; exit ;;
--config | --confi | --conf | --con | --co | --c )
- $as_echo "$ac_cs_config"; exit ;;
+ printf "%s\n" "$ac_cs_config"; exit ;;
--debug | --debu | --deb | --de | --d | -d )
debug=: ;;
--file | --fil | --fi | --f )
$ac_shift
case $ac_optarg in
- *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ *\'*) ac_optarg=`printf "%s\n" "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
'') as_fn_error $? "missing file argument" ;;
esac
as_fn_append CONFIG_FILES " '$ac_optarg'"
@@ -13207,7 +15886,7 @@ do
--header | --heade | --head | --hea )
$ac_shift
case $ac_optarg in
- *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
+ *\'*) ac_optarg=`printf "%s\n" "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;;
esac
as_fn_append CONFIG_HEADERS " '$ac_optarg'"
ac_need_defaults=false;;
@@ -13216,7 +15895,7 @@ do
as_fn_error $? "ambiguous option: \`$1'
Try \`$0 --help' for more information.";;
--help | --hel | -h )
- $as_echo "$ac_cs_usage"; exit ;;
+ printf "%s\n" "$ac_cs_usage"; exit ;;
-q | -quiet | --quiet | --quie | --qui | --qu | --q \
| -silent | --silent | --silen | --sile | --sil | --si | --s)
ac_cs_silent=: ;;
@@ -13244,7 +15923,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
if \$ac_cs_recheck; then
set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion
shift
- \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6
+ \printf "%s\n" "running CONFIG_SHELL=$SHELL \$*" >&6
CONFIG_SHELL='$SHELL'
export CONFIG_SHELL
exec "\$@"
@@ -13258,7 +15937,7 @@ exec 5>>config.log
sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX
## Running $as_me. ##
_ASBOX
- $as_echo "$ac_log"
+ printf "%s\n" "$ac_log"
} >&5
_ACEOF
@@ -13353,9 +16032,9 @@ done
# We use the long form for the default assignment because of an extremely
# bizarre bug on SunOS 4.1.3.
if $ac_need_defaults; then
- test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files
- test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers
- test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands
+ test ${CONFIG_FILES+y} || CONFIG_FILES=$config_files
+ test ${CONFIG_HEADERS+y} || CONFIG_HEADERS=$config_headers
+ test ${CONFIG_COMMANDS+y} || CONFIG_COMMANDS=$config_commands
fi
# Have a temporary directory for convenience. Make it in the build tree
@@ -13691,7 +16370,7 @@ do
esac ||
as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;;
esac
- case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; 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
@@ -13699,17 +16378,17 @@ do
# use $as_me), people would be surprised to read:
# /* config.h. Generated by config.status. */
configure_input='Generated from '`
- $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g'
+ printf "%s\n" "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g'
`' by configure.'
if test x"$ac_file" != x-; then
configure_input="$ac_file. $configure_input"
- { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5
-$as_echo "$as_me: creating $ac_file" >&6;}
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5
+printf "%s\n" "$as_me: creating $ac_file" >&6;}
fi
# Neutralize special characters interpreted by sed in replacement strings.
case $configure_input in #(
*\&* | *\|* | *\\* )
- ac_sed_conf_input=`$as_echo "$configure_input" |
+ ac_sed_conf_input=`printf "%s\n" "$configure_input" |
sed 's/[\\\\&|]/\\\\&/g'`;; #(
*) ac_sed_conf_input=$configure_input;;
esac
@@ -13726,7 +16405,7 @@ $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \
X"$ac_file" : 'X\(//\)[^/]' \| \
X"$ac_file" : 'X\(//\)$' \| \
X"$ac_file" : 'X\(/\)' \| . 2>/dev/null ||
-$as_echo X"$ac_file" |
+printf "%s\n" X"$ac_file" |
sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{
s//\1/
q
@@ -13750,9 +16429,9 @@ $as_echo X"$ac_file" |
case "$ac_dir" in
.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;
*)
- ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'`
+ ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'`
# A ".." for each directory in $ac_dir_suffix.
- ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
+ ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'`
case $ac_top_builddir_sub in
"") ac_top_builddir_sub=. ac_top_build_prefix= ;;
*) ac_top_build_prefix=$ac_top_builddir_sub/ ;;
@@ -13809,8 +16488,8 @@ ac_sed_dataroot='
case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in
*datarootdir*) ac_datarootdir_seen=yes;;
*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)
- { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5
-$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;}
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5
+printf "%s\n" "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;}
_ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_datarootdir_hack='
@@ -13853,9 +16532,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"; } &&
- { $as_echo "$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
-$as_echo "$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"
@@ -13871,27 +16550,27 @@ which seems to be undefined. Please make sure it is defined" >&2;}
#
if test x"$ac_file" != x-; then
{
- $as_echo "/* $configure_input */" \
+ printf "%s\n" "/* $configure_input */" >&1 \
&& eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs"
} >"$ac_tmp/config.h" \
|| as_fn_error $? "could not create $ac_file" "$LINENO" 5
if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5
-$as_echo "$as_me: $ac_file is unchanged" >&6;}
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5
+printf "%s\n" "$as_me: $ac_file is unchanged" >&6;}
else
rm -f "$ac_file"
mv "$ac_tmp/config.h" "$ac_file" \
|| as_fn_error $? "could not create $ac_file" "$LINENO" 5
fi
else
- $as_echo "/* $configure_input */" \
+ printf "%s\n" "/* $configure_input */" >&1 \
&& eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \
|| as_fn_error $? "could not create -" "$LINENO" 5
fi
;;
- :C) { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5
-$as_echo "$as_me: executing $ac_file commands" >&6;}
+ :C) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5
+printf "%s\n" "$as_me: executing $ac_file commands" >&6;}
;;
esac
@@ -13987,126 +16666,127 @@ if test "$no_create" != yes; then
$ac_cs_success || as_fn_exit 1
fi
if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
- { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
-$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
-fi
-
-
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ===============================================================================" >&5
-$as_echo "===============================================================================" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: jemalloc version : ${jemalloc_version}" >&5
-$as_echo "jemalloc version : ${jemalloc_version}" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: library revision : ${rev}" >&5
-$as_echo "library revision : ${rev}" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: " >&5
-$as_echo "" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: CONFIG : ${CONFIG}" >&5
-$as_echo "CONFIG : ${CONFIG}" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: CC : ${CC}" >&5
-$as_echo "CC : ${CC}" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: CONFIGURE_CFLAGS : ${CONFIGURE_CFLAGS}" >&5
-$as_echo "CONFIGURE_CFLAGS : ${CONFIGURE_CFLAGS}" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: SPECIFIED_CFLAGS : ${SPECIFIED_CFLAGS}" >&5
-$as_echo "SPECIFIED_CFLAGS : ${SPECIFIED_CFLAGS}" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: EXTRA_CFLAGS : ${EXTRA_CFLAGS}" >&5
-$as_echo "EXTRA_CFLAGS : ${EXTRA_CFLAGS}" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: CPPFLAGS : ${CPPFLAGS}" >&5
-$as_echo "CPPFLAGS : ${CPPFLAGS}" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: CXX : ${CXX}" >&5
-$as_echo "CXX : ${CXX}" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: CONFIGURE_CXXFLAGS : ${CONFIGURE_CXXFLAGS}" >&5
-$as_echo "CONFIGURE_CXXFLAGS : ${CONFIGURE_CXXFLAGS}" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: SPECIFIED_CXXFLAGS : ${SPECIFIED_CXXFLAGS}" >&5
-$as_echo "SPECIFIED_CXXFLAGS : ${SPECIFIED_CXXFLAGS}" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: EXTRA_CXXFLAGS : ${EXTRA_CXXFLAGS}" >&5
-$as_echo "EXTRA_CXXFLAGS : ${EXTRA_CXXFLAGS}" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: LDFLAGS : ${LDFLAGS}" >&5
-$as_echo "LDFLAGS : ${LDFLAGS}" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: EXTRA_LDFLAGS : ${EXTRA_LDFLAGS}" >&5
-$as_echo "EXTRA_LDFLAGS : ${EXTRA_LDFLAGS}" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: DSO_LDFLAGS : ${DSO_LDFLAGS}" >&5
-$as_echo "DSO_LDFLAGS : ${DSO_LDFLAGS}" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: LIBS : ${LIBS}" >&5
-$as_echo "LIBS : ${LIBS}" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: RPATH_EXTRA : ${RPATH_EXTRA}" >&5
-$as_echo "RPATH_EXTRA : ${RPATH_EXTRA}" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: " >&5
-$as_echo "" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: XSLTPROC : ${XSLTPROC}" >&5
-$as_echo "XSLTPROC : ${XSLTPROC}" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: XSLROOT : ${XSLROOT}" >&5
-$as_echo "XSLROOT : ${XSLROOT}" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: " >&5
-$as_echo "" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: PREFIX : ${PREFIX}" >&5
-$as_echo "PREFIX : ${PREFIX}" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: BINDIR : ${BINDIR}" >&5
-$as_echo "BINDIR : ${BINDIR}" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: DATADIR : ${DATADIR}" >&5
-$as_echo "DATADIR : ${DATADIR}" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: INCLUDEDIR : ${INCLUDEDIR}" >&5
-$as_echo "INCLUDEDIR : ${INCLUDEDIR}" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: LIBDIR : ${LIBDIR}" >&5
-$as_echo "LIBDIR : ${LIBDIR}" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: MANDIR : ${MANDIR}" >&5
-$as_echo "MANDIR : ${MANDIR}" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: " >&5
-$as_echo "" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: srcroot : ${srcroot}" >&5
-$as_echo "srcroot : ${srcroot}" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: abs_srcroot : ${abs_srcroot}" >&5
-$as_echo "abs_srcroot : ${abs_srcroot}" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: objroot : ${objroot}" >&5
-$as_echo "objroot : ${objroot}" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: abs_objroot : ${abs_objroot}" >&5
-$as_echo "abs_objroot : ${abs_objroot}" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: " >&5
-$as_echo "" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: JEMALLOC_PREFIX : ${JEMALLOC_PREFIX}" >&5
-$as_echo "JEMALLOC_PREFIX : ${JEMALLOC_PREFIX}" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: JEMALLOC_PRIVATE_NAMESPACE" >&5
-$as_echo "JEMALLOC_PRIVATE_NAMESPACE" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: : ${JEMALLOC_PRIVATE_NAMESPACE}" >&5
-$as_echo " : ${JEMALLOC_PRIVATE_NAMESPACE}" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: install_suffix : ${install_suffix}" >&5
-$as_echo "install_suffix : ${install_suffix}" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: malloc_conf : ${config_malloc_conf}" >&5
-$as_echo "malloc_conf : ${config_malloc_conf}" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: documentation : ${enable_doc}" >&5
-$as_echo "documentation : ${enable_doc}" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: shared libs : ${enable_shared}" >&5
-$as_echo "shared libs : ${enable_shared}" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: static libs : ${enable_static}" >&5
-$as_echo "static libs : ${enable_static}" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: autogen : ${enable_autogen}" >&5
-$as_echo "autogen : ${enable_autogen}" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: debug : ${enable_debug}" >&5
-$as_echo "debug : ${enable_debug}" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: stats : ${enable_stats}" >&5
-$as_echo "stats : ${enable_stats}" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: experimetal_smallocx : ${enable_experimental_smallocx}" >&5
-$as_echo "experimetal_smallocx : ${enable_experimental_smallocx}" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: prof : ${enable_prof}" >&5
-$as_echo "prof : ${enable_prof}" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: prof-libunwind : ${enable_prof_libunwind}" >&5
-$as_echo "prof-libunwind : ${enable_prof_libunwind}" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: prof-libgcc : ${enable_prof_libgcc}" >&5
-$as_echo "prof-libgcc : ${enable_prof_libgcc}" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: prof-gcc : ${enable_prof_gcc}" >&5
-$as_echo "prof-gcc : ${enable_prof_gcc}" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: fill : ${enable_fill}" >&5
-$as_echo "fill : ${enable_fill}" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: utrace : ${enable_utrace}" >&5
-$as_echo "utrace : ${enable_utrace}" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: xmalloc : ${enable_xmalloc}" >&5
-$as_echo "xmalloc : ${enable_xmalloc}" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: log : ${enable_log}" >&5
-$as_echo "log : ${enable_log}" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: lazy_lock : ${enable_lazy_lock}" >&5
-$as_echo "lazy_lock : ${enable_lazy_lock}" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: cache-oblivious : ${enable_cache_oblivious}" >&5
-$as_echo "cache-oblivious : ${enable_cache_oblivious}" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: cxx : ${enable_cxx}" >&5
-$as_echo "cxx : ${enable_cxx}" >&6; }
-{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ===============================================================================" >&5
-$as_echo "===============================================================================" >&6; }
+ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5
+printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;}
+fi
+
+
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: ===============================================================================" >&5
+printf "%s\n" "===============================================================================" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: jemalloc version : ${jemalloc_version}" >&5
+printf "%s\n" "jemalloc version : ${jemalloc_version}" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: library revision : ${rev}" >&5
+printf "%s\n" "library revision : ${rev}" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: " >&5
+printf "%s\n" "" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: CONFIG : ${CONFIG}" >&5
+printf "%s\n" "CONFIG : ${CONFIG}" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: CC : ${CC}" >&5
+printf "%s\n" "CC : ${CC}" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: CONFIGURE_CFLAGS : ${CONFIGURE_CFLAGS}" >&5
+printf "%s\n" "CONFIGURE_CFLAGS : ${CONFIGURE_CFLAGS}" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: SPECIFIED_CFLAGS : ${SPECIFIED_CFLAGS}" >&5
+printf "%s\n" "SPECIFIED_CFLAGS : ${SPECIFIED_CFLAGS}" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: EXTRA_CFLAGS : ${EXTRA_CFLAGS}" >&5
+printf "%s\n" "EXTRA_CFLAGS : ${EXTRA_CFLAGS}" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: CPPFLAGS : ${CPPFLAGS}" >&5
+printf "%s\n" "CPPFLAGS : ${CPPFLAGS}" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: CXX : ${CXX}" >&5
+printf "%s\n" "CXX : ${CXX}" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: CONFIGURE_CXXFLAGS : ${CONFIGURE_CXXFLAGS}" >&5
+printf "%s\n" "CONFIGURE_CXXFLAGS : ${CONFIGURE_CXXFLAGS}" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: SPECIFIED_CXXFLAGS : ${SPECIFIED_CXXFLAGS}" >&5
+printf "%s\n" "SPECIFIED_CXXFLAGS : ${SPECIFIED_CXXFLAGS}" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: EXTRA_CXXFLAGS : ${EXTRA_CXXFLAGS}" >&5
+printf "%s\n" "EXTRA_CXXFLAGS : ${EXTRA_CXXFLAGS}" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: LDFLAGS : ${LDFLAGS}" >&5
+printf "%s\n" "LDFLAGS : ${LDFLAGS}" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: EXTRA_LDFLAGS : ${EXTRA_LDFLAGS}" >&5
+printf "%s\n" "EXTRA_LDFLAGS : ${EXTRA_LDFLAGS}" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: DSO_LDFLAGS : ${DSO_LDFLAGS}" >&5
+printf "%s\n" "DSO_LDFLAGS : ${DSO_LDFLAGS}" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: LIBS : ${LIBS}" >&5
+printf "%s\n" "LIBS : ${LIBS}" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: RPATH_EXTRA : ${RPATH_EXTRA}" >&5
+printf "%s\n" "RPATH_EXTRA : ${RPATH_EXTRA}" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: " >&5
+printf "%s\n" "" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: XSLTPROC : ${XSLTPROC}" >&5
+printf "%s\n" "XSLTPROC : ${XSLTPROC}" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: XSLROOT : ${XSLROOT}" >&5
+printf "%s\n" "XSLROOT : ${XSLROOT}" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: " >&5
+printf "%s\n" "" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: PREFIX : ${PREFIX}" >&5
+printf "%s\n" "PREFIX : ${PREFIX}" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: BINDIR : ${BINDIR}" >&5
+printf "%s\n" "BINDIR : ${BINDIR}" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: DATADIR : ${DATADIR}" >&5
+printf "%s\n" "DATADIR : ${DATADIR}" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: INCLUDEDIR : ${INCLUDEDIR}" >&5
+printf "%s\n" "INCLUDEDIR : ${INCLUDEDIR}" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: LIBDIR : ${LIBDIR}" >&5
+printf "%s\n" "LIBDIR : ${LIBDIR}" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: MANDIR : ${MANDIR}" >&5
+printf "%s\n" "MANDIR : ${MANDIR}" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: " >&5
+printf "%s\n" "" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: srcroot : ${srcroot}" >&5
+printf "%s\n" "srcroot : ${srcroot}" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: abs_srcroot : ${abs_srcroot}" >&5
+printf "%s\n" "abs_srcroot : ${abs_srcroot}" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: objroot : ${objroot}" >&5
+printf "%s\n" "objroot : ${objroot}" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: abs_objroot : ${abs_objroot}" >&5
+printf "%s\n" "abs_objroot : ${abs_objroot}" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: " >&5
+printf "%s\n" "" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: JEMALLOC_PREFIX : ${JEMALLOC_PREFIX}" >&5
+printf "%s\n" "JEMALLOC_PREFIX : ${JEMALLOC_PREFIX}" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: JEMALLOC_PRIVATE_NAMESPACE" >&5
+printf "%s\n" "JEMALLOC_PRIVATE_NAMESPACE" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: : ${JEMALLOC_PRIVATE_NAMESPACE}" >&5
+printf "%s\n" " : ${JEMALLOC_PRIVATE_NAMESPACE}" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: install_suffix : ${install_suffix}" >&5
+printf "%s\n" "install_suffix : ${install_suffix}" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: malloc_conf : ${config_malloc_conf}" >&5
+printf "%s\n" "malloc_conf : ${config_malloc_conf}" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: documentation : ${enable_doc}" >&5
+printf "%s\n" "documentation : ${enable_doc}" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: shared libs : ${enable_shared}" >&5
+printf "%s\n" "shared libs : ${enable_shared}" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: static libs : ${enable_static}" >&5
+printf "%s\n" "static libs : ${enable_static}" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: autogen : ${enable_autogen}" >&5
+printf "%s\n" "autogen : ${enable_autogen}" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: debug : ${enable_debug}" >&5
+printf "%s\n" "debug : ${enable_debug}" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: stats : ${enable_stats}" >&5
+printf "%s\n" "stats : ${enable_stats}" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: experimental_smallocx : ${enable_experimental_smallocx}" >&5
+printf "%s\n" "experimental_smallocx : ${enable_experimental_smallocx}" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: prof : ${enable_prof}" >&5
+printf "%s\n" "prof : ${enable_prof}" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: prof-libunwind : ${enable_prof_libunwind}" >&5
+printf "%s\n" "prof-libunwind : ${enable_prof_libunwind}" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: prof-libgcc : ${enable_prof_libgcc}" >&5
+printf "%s\n" "prof-libgcc : ${enable_prof_libgcc}" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: prof-gcc : ${enable_prof_gcc}" >&5
+printf "%s\n" "prof-gcc : ${enable_prof_gcc}" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: fill : ${enable_fill}" >&5
+printf "%s\n" "fill : ${enable_fill}" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: utrace : ${enable_utrace}" >&5
+printf "%s\n" "utrace : ${enable_utrace}" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: xmalloc : ${enable_xmalloc}" >&5
+printf "%s\n" "xmalloc : ${enable_xmalloc}" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: log : ${enable_log}" >&5
+printf "%s\n" "log : ${enable_log}" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: lazy_lock : ${enable_lazy_lock}" >&5
+printf "%s\n" "lazy_lock : ${enable_lazy_lock}" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: cache-oblivious : ${enable_cache_oblivious}" >&5
+printf "%s\n" "cache-oblivious : ${enable_cache_oblivious}" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: cxx : ${enable_cxx}" >&5
+printf "%s\n" "cxx : ${enable_cxx}" >&6; }
+{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: ===============================================================================" >&5
+printf "%s\n" "===============================================================================" >&6; }
+
diff --git a/deps/jemalloc/configure.ac b/deps/jemalloc/configure.ac
index f867172f7..5190bfe9d 100644
--- a/deps/jemalloc/configure.ac
+++ b/deps/jemalloc/configure.ac
@@ -131,12 +131,14 @@ abs_objroot="`pwd`/"
AC_SUBST([abs_objroot])
dnl Munge install path variables.
-if test "x$prefix" = "xNONE" ; then
- prefix="/usr/local"
-fi
-if test "x$exec_prefix" = "xNONE" ; then
- exec_prefix=$prefix
-fi
+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`
@@ -237,19 +239,29 @@ 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])
+ 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])
+ 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
@@ -290,8 +302,11 @@ 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([14], [noext], [optional])
- if test "x${HAVE_CXX14}" = "x1" ; then
+ 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])
@@ -312,6 +327,9 @@ if test "x$enable_cxx" = "x1" ; then
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])
@@ -319,7 +337,7 @@ 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], [ ])
+ AC_DEFINE_UNQUOTED([JEMALLOC_BIG_ENDIAN], [ ], [ ])
fi
if test "x${je_cv_msvc}" = "xyes" -a "x${ac_cv_header_inttypes_h}" = "xno"; then
@@ -339,7 +357,7 @@ else
AC_MSG_ERROR([Unsupported pointer size: ${ac_cv_sizeof_void_p}])
fi
fi
-AC_DEFINE_UNQUOTED([LG_SIZEOF_PTR], [$LG_SIZEOF_PTR])
+AC_DEFINE_UNQUOTED([LG_SIZEOF_PTR], [$LG_SIZEOF_PTR], [ ])
AC_CHECK_SIZEOF([int])
if test "x${ac_cv_sizeof_int}" = "x8" ; then
@@ -349,7 +367,7 @@ elif test "x${ac_cv_sizeof_int}" = "x4" ; then
else
AC_MSG_ERROR([Unsupported int size: ${ac_cv_sizeof_int}])
fi
-AC_DEFINE_UNQUOTED([LG_SIZEOF_INT], [$LG_SIZEOF_INT])
+AC_DEFINE_UNQUOTED([LG_SIZEOF_INT], [$LG_SIZEOF_INT], [ ])
AC_CHECK_SIZEOF([long])
if test "x${ac_cv_sizeof_long}" = "x8" ; then
@@ -359,7 +377,7 @@ elif test "x${ac_cv_sizeof_long}" = "x4" ; then
else
AC_MSG_ERROR([Unsupported long size: ${ac_cv_sizeof_long}])
fi
-AC_DEFINE_UNQUOTED([LG_SIZEOF_LONG], [$LG_SIZEOF_LONG])
+AC_DEFINE_UNQUOTED([LG_SIZEOF_LONG], [$LG_SIZEOF_LONG], [ ])
AC_CHECK_SIZEOF([long long])
if test "x${ac_cv_sizeof_long_long}" = "x8" ; then
@@ -369,7 +387,7 @@ elif test "x${ac_cv_sizeof_long_long}" = "x4" ; then
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_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
@@ -381,7 +399,7 @@ elif test "x${ac_cv_sizeof_intmax_t}" = "x4" ; then
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_DEFINE_UNQUOTED([LG_SIZEOF_INTMAX_T], [$LG_SIZEOF_INTMAX_T], [ ])
AC_CANONICAL_HOST
dnl CPU-specific settings.
@@ -407,12 +425,23 @@ case "${host_cpu}" in
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_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])],
@@ -477,7 +506,7 @@ typedef unsigned __int32 uint32_t;
LG_VADDR="${je_cv_lg_vaddr}"
fi
if test "x${LG_VADDR}" != "xerror" ; then
- AC_DEFINE_UNQUOTED([LG_VADDR], [$LG_VADDR])
+ AC_DEFINE_UNQUOTED([LG_VADDR], [$LG_VADDR], [ ])
else
AC_MSG_ERROR([cannot determine number of significant virtual address bits])
fi
@@ -499,7 +528,7 @@ typedef unsigned __int32 uint32_t;
fi
;;
esac
-AC_DEFINE_UNQUOTED([LG_VADDR], [$LG_VADDR])
+AC_DEFINE_UNQUOTED([LG_VADDR], [$LG_VADDR], [ ])
LD_PRELOAD_VAR="LD_PRELOAD"
so="so"
@@ -583,7 +612,7 @@ 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-g0000000000000000000000000000000000000000" > "${objroot}VERSION"
+ echo "0.0.0-0-g000000missing_version_try_git_fetch_tags" > "${objroot}VERSION"
else
cp ${srcroot}VERSION ${objroot}VERSION
fi
@@ -609,6 +638,7 @@ 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=""
@@ -626,8 +656,9 @@ case "${host}" in
SYM_PREFIX="_"
;;
*-*-freebsd*)
+ JE_APPEND_VS(CPPFLAGS, -D_BSD_SOURCE)
abi="elf"
- AC_DEFINE([JEMALLOC_SYSCTL_VM_OVERCOMMIT], [ ])
+ AC_DEFINE([JEMALLOC_SYSCTL_VM_OVERCOMMIT], [ ], [ ])
force_lazy_lock="1"
;;
*-*-dragonfly*)
@@ -640,41 +671,45 @@ case "${host}" in
*-*-bitrig*)
abi="elf"
;;
- *-*-linux-android)
+ *-*-linux-android*)
dnl syscall(2) and secure_getenv(3) are exposed by _GNU_SOURCE.
JE_APPEND_VS(CPPFLAGS, -D_GNU_SOURCE)
abi="elf"
- 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])
+ 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"
- 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], [ ])
+ 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], [ ])
+ 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])
@@ -741,6 +776,12 @@ case "${host}" in
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}])
@@ -763,7 +804,7 @@ AC_CHECK_HEADERS([malloc.h], [
AC_MSG_RESULT([no])
])
])
-AC_DEFINE_UNQUOTED([JEMALLOC_USABLE_SIZE_CONST], [$JEMALLOC_USABLE_SIZE_CONST])
+AC_DEFINE_UNQUOTED([JEMALLOC_USABLE_SIZE_CONST], [$JEMALLOC_USABLE_SIZE_CONST], [ ])
AC_SUBST([abi])
AC_SUBST([RPATH])
AC_SUBST([LD_PRELOAD_VAR])
@@ -801,7 +842,7 @@ JE_COMPILABLE([__attribute__ syntax],
[],
[je_cv_attribute])
if test "x${je_cv_attribute}" = "xyes" ; then
- AC_DEFINE([JEMALLOC_HAVE_ATTR], [ ])
+ 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])
@@ -829,7 +870,7 @@ JE_COMPILABLE([alloc_size attribute], [#include <stdlib.h>],
[je_cv_alloc_size])
JE_CFLAGS_RESTORE()
if test "x${je_cv_alloc_size}" = "xyes" ; then
- AC_DEFINE([JEMALLOC_HAVE_ATTR_ALLOC_SIZE], [ ])
+ AC_DEFINE([JEMALLOC_HAVE_ATTR_ALLOC_SIZE], [ ], [ ])
fi
dnl Check for format(gnu_printf, ...) attribute support.
JE_CFLAGS_SAVE()
@@ -840,7 +881,7 @@ JE_COMPILABLE([format(gnu_printf, ...) attribute], [#include <stdlib.h>],
[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], [ ])
+ AC_DEFINE([JEMALLOC_HAVE_ATTR_FORMAT_GNU_PRINTF], [ ], [ ])
fi
dnl Check for format(printf, ...) attribute support.
JE_CFLAGS_SAVE()
@@ -851,7 +892,7 @@ JE_COMPILABLE([format(printf, ...) attribute], [#include <stdlib.h>],
[je_cv_format_printf])
JE_CFLAGS_RESTORE()
if test "x${je_cv_format_printf}" = "xyes" ; then
- AC_DEFINE([JEMALLOC_HAVE_ATTR_FORMAT_PRINTF], [ ])
+ AC_DEFINE([JEMALLOC_HAVE_ATTR_FORMAT_PRINTF], [ ], [ ])
fi
dnl Check for format_arg(...) attribute support.
@@ -863,7 +904,51 @@ JE_COMPILABLE([format(printf, ...) attribute], [#include <stdlib.h>],
[je_cv_format_arg])
JE_CFLAGS_RESTORE()
if test "x${je_cv_format_arg}" = "xyes" ; then
- AC_DEFINE([JEMALLOC_HAVE_ATTR_FORMAT_ARG], [ ])
+ 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.
@@ -898,7 +983,7 @@ AC_PATH_PROG([AUTOCONF], [autoconf], [false], [$PATH])
dnl Enable documentation
AC_ARG_ENABLE([doc],
- [AS_HELP_STRING([--enable-documentation], [Build documentation])],
+ [AS_HELP_STRING([--enable-doc], [Build documentation])],
if test "x$enable_doc" = "xno" ; then
enable_doc="0"
else
@@ -955,11 +1040,11 @@ else
fi]
)
if test "x$JEMALLOC_PREFIX" = "x" ; then
- AC_DEFINE([JEMALLOC_IS_MALLOC])
+ 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"])
+ AC_DEFINE_UNQUOTED([JEMALLOC_PREFIX], ["$JEMALLOC_PREFIX"], [ ])
+ AC_DEFINE_UNQUOTED([JEMALLOC_CPREFIX], ["$JEMALLOC_CPREFIX"], [ ])
fi
AC_SUBST([JEMALLOC_PREFIX])
AC_SUBST([JEMALLOC_CPREFIX])
@@ -967,42 +1052,45 @@ 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],[])
+ AC_DEFINE([JEMALLOC_EXPORT],[], [ ])
fi]
)
-public_syms="aligned_alloc calloc dallocx free mallctl mallctlbymib mallctlnametomib malloc malloc_conf malloc_message malloc_stats_print malloc_usable_size mallocx smallocx_${jemalloc_version_gid} nallocx posix_memalign rallocx realloc sallocx sdallocx xallocx"
+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], [ ])
+ [AC_DEFINE([JEMALLOC_OVERRIDE_MEMALIGN], [ ], [ ])
public_syms="${public_syms} memalign"])
AC_CHECK_FUNC([valloc],
- [AC_DEFINE([JEMALLOC_OVERRIDE_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], [ ])
+ [AC_DEFINE([JEMALLOC_OVERRIDE___LIBC_CALLOC], [ ], [ ])
wrap_syms="${wrap_syms} __libc_calloc"])
AC_CHECK_FUNC([__libc_free],
- [AC_DEFINE([JEMALLOC_OVERRIDE___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], [ ])
+ [AC_DEFINE([JEMALLOC_OVERRIDE___LIBC_MALLOC], [ ], [ ])
wrap_syms="${wrap_syms} __libc_malloc"])
AC_CHECK_FUNC([__libc_memalign],
- [AC_DEFINE([JEMALLOC_OVERRIDE___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], [ ])
+ [AC_DEFINE([JEMALLOC_OVERRIDE___LIBC_REALLOC], [ ], [ ])
wrap_syms="${wrap_syms} __libc_realloc"])
AC_CHECK_FUNC([__libc_valloc],
- [AC_DEFINE([JEMALLOC_OVERRIDE___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], [ ])
+ [AC_DEFINE([JEMALLOC_OVERRIDE___POSIX_MEMALIGN], [ ], [ ])
wrap_syms="${wrap_syms} __posix_memalign"])
fi
@@ -1020,14 +1108,17 @@ AC_ARG_WITH([private_namespace],
[JEMALLOC_PRIVATE_NAMESPACE="${with_private_namespace}je_"],
[JEMALLOC_PRIVATE_NAMESPACE="je_"]
)
-AC_DEFINE_UNQUOTED([JEMALLOC_PRIVATE_NAMESPACE], [$JEMALLOC_PRIVATE_NAMESPACE])
+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])],
- [INSTALL_SUFFIX="$with_install_suffix"],
+ [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"
@@ -1040,7 +1131,7 @@ AC_ARG_WITH([malloc_conf],
[JEMALLOC_CONFIG_MALLOC_CONF=""]
)
config_malloc_conf="$JEMALLOC_CONFIG_MALLOC_CONF"
-AC_DEFINE_UNQUOTED([JEMALLOC_CONFIG_MALLOC_CONF], ["$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.
@@ -1129,10 +1220,7 @@ fi
[enable_debug="0"]
)
if test "x$enable_debug" = "x1" ; then
- AC_DEFINE([JEMALLOC_DEBUG], [ ])
-fi
-if test "x$enable_debug" = "x1" ; then
- AC_DEFINE([JEMALLOC_DEBUG], [ ])
+ AC_DEFINE([JEMALLOC_DEBUG], [ ], [ ])
fi
AC_SUBST([enable_debug])
@@ -1164,7 +1252,7 @@ fi
[enable_stats="1"]
)
if test "x$enable_stats" = "x1" ; then
- AC_DEFINE([JEMALLOC_STATS], [ ])
+ AC_DEFINE([JEMALLOC_STATS], [ ], [ ])
fi
AC_SUBST([enable_stats])
@@ -1180,7 +1268,7 @@ fi
[enable_experimental_smallocx="0"]
)
if test "x$enable_experimental_smallocx" = "x1" ; then
- AC_DEFINE([JEMALLOC_EXPERIMENTAL_SMALLOCX_API])
+ AC_DEFINE([JEMALLOC_EXPERIMENTAL_SMALLOCX_API], [ ], [ ])
fi
AC_SUBST([enable_experimental_smallocx])
@@ -1207,6 +1295,9 @@ AC_ARG_ENABLE([prof-libunwind],
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"]
@@ -1234,7 +1325,7 @@ if test "x$backtrace_method" = "x" -a "x$enable_prof_libunwind" = "x1" ; then
fi
if test "x${enable_prof_libunwind}" = "x1" ; then
backtrace_method="libunwind"
- AC_DEFINE([JEMALLOC_PROF_LIBUNWIND], [ ])
+ AC_DEFINE([JEMALLOC_PROF_LIBUNWIND], [ ], [ ])
fi
fi
@@ -1257,7 +1348,7 @@ if test "x$backtrace_method" = "x" -a "x$enable_prof_libgcc" = "x1" \
fi
if test "x${enable_prof_libgcc}" = "x1" ; then
backtrace_method="libgcc"
- AC_DEFINE([JEMALLOC_PROF_LIBGCC], [ ])
+ AC_DEFINE([JEMALLOC_PROF_LIBGCC], [ ], [ ])
fi
else
enable_prof_libgcc="0"
@@ -1278,7 +1369,7 @@ 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], [ ])
+ AC_DEFINE([JEMALLOC_PROF_GCC], [ ], [ ])
else
enable_prof_gcc="0"
fi
@@ -1293,19 +1384,24 @@ if test "x$enable_prof" = "x1" ; then
dnl Heap profiling uses the log(3) function.
JE_APPEND_VS(LIBS, $LM)
- AC_DEFINE([JEMALLOC_PROF], [ ])
+ 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], [ ])
+ 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], [ ])
+ 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.
@@ -1322,7 +1418,7 @@ else
fi
if test "x$have_dss" = "x1" ; then
- AC_DEFINE([JEMALLOC_DSS], [ ])
+ AC_DEFINE([JEMALLOC_DSS], [ ], [ ])
fi
dnl Support the junk/zero filling option by default.
@@ -1337,7 +1433,7 @@ fi
[enable_fill="1"]
)
if test "x$enable_fill" = "x1" ; then
- AC_DEFINE([JEMALLOC_FILL], [ ])
+ AC_DEFINE([JEMALLOC_FILL], [ ], [ ])
fi
AC_SUBST([enable_fill])
@@ -1362,10 +1458,25 @@ JE_COMPILABLE([utrace(2)], [
utrace((void *)0, 0);
], [je_cv_utrace])
if test "x${je_cv_utrace}" = "xno" ; then
- enable_utrace="0"
-fi
-if test "x$enable_utrace" = "x1" ; then
- AC_DEFINE([JEMALLOC_UTRACE], [ ])
+ 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])
@@ -1381,7 +1492,7 @@ fi
[enable_xmalloc="0"]
)
if test "x$enable_xmalloc" = "x1" ; then
- AC_DEFINE([JEMALLOC_XMALLOC], [ ])
+ AC_DEFINE([JEMALLOC_XMALLOC], [ ], [ ])
fi
AC_SUBST([enable_xmalloc])
@@ -1398,7 +1509,7 @@ fi
[enable_cache_oblivious="1"]
)
if test "x$enable_cache_oblivious" = "x1" ; then
- AC_DEFINE([JEMALLOC_CACHE_OBLIVIOUS], [ ])
+ AC_DEFINE([JEMALLOC_CACHE_OBLIVIOUS], [ ], [ ])
fi
AC_SUBST([enable_cache_oblivious])
@@ -1414,7 +1525,7 @@ fi
[enable_log="0"]
)
if test "x$enable_log" = "x1" ; then
- AC_DEFINE([JEMALLOC_LOG], [ ])
+ AC_DEFINE([JEMALLOC_LOG], [ ], [ ])
fi
AC_SUBST([enable_log])
@@ -1430,7 +1541,7 @@ fi
[enable_readlinkat="0"]
)
if test "x$enable_readlinkat" = "x1" ; then
- AC_DEFINE([JEMALLOC_READLINKAT], [ ])
+ AC_DEFINE([JEMALLOC_READLINKAT], [ ], [ ])
fi
AC_SUBST([enable_readlinkat])
@@ -1447,10 +1558,44 @@ fi
[enable_opt_safety_checks="0"]
)
if test "x$enable_opt_safety_checks" = "x1" ; then
- AC_DEFINE([JEMALLOC_OPT_SAFETY_CHECKS], [ ])
+ 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();
@@ -1461,9 +1606,9 @@ void foo (void) {
}
], [je_cv_gcc_builtin_unreachable])
if test "x${je_cv_gcc_builtin_unreachable}" = "xyes" ; then
- AC_DEFINE([JEMALLOC_INTERNAL_UNREACHABLE], [__builtin_unreachable])
+ AC_DEFINE([JEMALLOC_INTERNAL_UNREACHABLE], [__builtin_unreachable], [ ])
else
- AC_DEFINE([JEMALLOC_INTERNAL_UNREACHABLE], [abort])
+ AC_DEFINE([JEMALLOC_INTERNAL_UNREACHABLE], [abort], [ ])
fi
dnl ============================================================================
@@ -1483,9 +1628,9 @@ JE_COMPILABLE([a program using __builtin_ffsl], [
}
], [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])
+ 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>
@@ -1498,9 +1643,9 @@ else
}
], [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])
+ 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
@@ -1517,22 +1662,39 @@ JE_COMPILABLE([a program using __builtin_popcountl], [
}
], [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_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])],
- [LG_QUANTA="$with_lg_quantum"],
- [LG_QUANTA="3 4"])
+ [Base 2 log of minimum allocation alignment])])
if test "x$with_lg_quantum" != "x" ; then
- AC_DEFINE_UNQUOTED([LG_QUANTUM], [$with_lg_quantum])
+ 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],
@@ -1579,7 +1741,7 @@ 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])
+ AC_DEFINE_UNQUOTED([LG_PAGE], [$LG_PAGE], [ ])
else
AC_MSG_ERROR([cannot determine value for LG_PAGE])
fi
@@ -1616,7 +1778,7 @@ 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}])
+AC_DEFINE_UNQUOTED([LG_HUGEPAGE], [${je_cv_lg_hugepage}], [ ])
dnl ============================================================================
dnl Enable libdl by default.
@@ -1637,7 +1799,7 @@ dnl ============================================================================
dnl Configure pthreads.
if test "x$abi" != "xpecoff" ; then
- AC_DEFINE([JEMALLOC_HAVE_PTHREAD], [ ])
+ 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.
@@ -1655,7 +1817,7 @@ dnl Check if we have dlsym support.
[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], [ ])
+ AC_DEFINE([JEMALLOC_HAVE_DLSYM], [ ], [ ])
fi
else
have_dlsym="0"
@@ -1667,7 +1829,7 @@ dnl Check if we have dlsym support.
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], [ ])
+ 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)], [
@@ -1676,7 +1838,38 @@ dnl Check if we have dlsym support.
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], [ ])
+ 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
@@ -1708,7 +1901,7 @@ JE_COMPILABLE([clock_gettime(CLOCK_MONOTONIC_COARSE, ...)], [
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])
+ AC_DEFINE([JEMALLOC_HAVE_CLOCK_MONOTONIC_COARSE], [ ], [ ])
fi
dnl check for CLOCK_MONOTONIC.
@@ -1724,7 +1917,7 @@ JE_COMPILABLE([clock_gettime(CLOCK_MONOTONIC, ...)], [
#endif
], [je_cv_clock_monotonic])
if test "x${je_cv_clock_monotonic}" = "xyes" ; then
- AC_DEFINE([JEMALLOC_HAVE_CLOCK_MONOTONIC])
+ AC_DEFINE([JEMALLOC_HAVE_CLOCK_MONOTONIC], [ ], [ ])
fi
dnl Check for mach_absolute_time().
@@ -1734,7 +1927,19 @@ JE_COMPILABLE([mach_absolute_time()], [
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])
+ 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.
@@ -1762,7 +1967,7 @@ if test "x$enable_syscall" = "x1" ; then
[je_cv_syscall])
JE_CFLAGS_RESTORE()
if test "x$je_cv_syscall" = "xyes" ; then
- AC_DEFINE([JEMALLOC_USE_SYSCALL], [ ])
+ AC_DEFINE([JEMALLOC_USE_SYSCALL], [ ], [ ])
fi
fi
@@ -1772,7 +1977,7 @@ AC_CHECK_FUNC([secure_getenv],
[have_secure_getenv="0"]
)
if test "x$have_secure_getenv" = "x1" ; then
- AC_DEFINE([JEMALLOC_HAVE_SECURE_GETENV], [ ])
+ AC_DEFINE([JEMALLOC_HAVE_SECURE_GETENV], [ ], [ ])
fi
dnl Check if the GNU-specific sched_getcpu function exists.
@@ -1781,7 +1986,7 @@ AC_CHECK_FUNC([sched_getcpu],
[have_sched_getcpu="0"]
)
if test "x$have_sched_getcpu" = "x1" ; then
- AC_DEFINE([JEMALLOC_HAVE_SCHED_GETCPU], [ ])
+ AC_DEFINE([JEMALLOC_HAVE_SCHED_GETCPU], [ ], [ ])
fi
dnl Check if the GNU-specific sched_setaffinity function exists.
@@ -1790,7 +1995,7 @@ AC_CHECK_FUNC([sched_setaffinity],
[have_sched_setaffinity="0"]
)
if test "x$have_sched_setaffinity" = "x1" ; then
- AC_DEFINE([JEMALLOC_HAVE_SCHED_SETAFFINITY], [ ])
+ AC_DEFINE([JEMALLOC_HAVE_SCHED_SETAFFINITY], [ ], [ ])
fi
dnl Check if the Solaris/BSD issetugid function exists.
@@ -1799,7 +2004,7 @@ AC_CHECK_FUNC([issetugid],
[have_issetugid="0"]
)
if test "x$have_issetugid" = "x1" ; then
- AC_DEFINE([JEMALLOC_HAVE_ISSETUGID], [ ])
+ AC_DEFINE([JEMALLOC_HAVE_ISSETUGID], [ ], [ ])
fi
dnl Check whether the BSD-specific _malloc_thread_cleanup() exists. If so, use
@@ -1811,8 +2016,8 @@ AC_CHECK_FUNC([_malloc_thread_cleanup],
[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"
+ AC_DEFINE([JEMALLOC_MALLOC_THREAD_CLEANUP], [ ], [ ])
+ wrap_syms="${wrap_syms} _malloc_thread_cleanup _malloc_tsd_cleanup_register"
force_tls="1"
fi
@@ -1824,10 +2029,18 @@ AC_CHECK_FUNC([_pthread_mutex_init_calloc_cb],
[have__pthread_mutex_init_calloc_cb="0"]
)
if test "x$have__pthread_mutex_init_calloc_cb" = "x1" ; then
- AC_DEFINE([JEMALLOC_MUTEX_INIT_CB])
+ 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],
@@ -1854,7 +2067,7 @@ if test "x${enable_lazy_lock}" = "x1" -a "x${abi}" = "xpecoff" ; then
fi
if test "x$enable_lazy_lock" = "x1" ; then
if test "x$have_dlsym" = "x1" ; then
- AC_DEFINE([JEMALLOC_LAZY_LOCK], [ ])
+ AC_DEFINE([JEMALLOC_LAZY_LOCK], [ ], [ ])
else
AC_MSG_ERROR([Missing dlsym support: lazy-lock cannot be enabled.])
fi
@@ -1887,7 +2100,7 @@ else
fi
AC_SUBST([enable_tls])
if test "x${enable_tls}" = "x1" ; then
- AC_DEFINE_UNQUOTED([JEMALLOC_TLS], [ ])
+ AC_DEFINE_UNQUOTED([JEMALLOC_TLS], [ ], [ ])
fi
dnl ============================================================================
@@ -1908,7 +2121,7 @@ JE_COMPILABLE([C11 atomics], [
return r == 0;
], [je_cv_c11_atomics])
if test "x${je_cv_c11_atomics}" = "xyes" ; then
- AC_DEFINE([JEMALLOC_C11_ATOMICS])
+ AC_DEFINE([JEMALLOC_C11_ATOMICS], [ ], [ ])
fi
dnl ============================================================================
@@ -1923,7 +2136,7 @@ JE_COMPILABLE([GCC __atomic atomics], [
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])
+ AC_DEFINE([JEMALLOC_GCC_ATOMIC_ATOMICS], [ ], [ ])
dnl check for 8-bit atomic support
JE_COMPILABLE([GCC 8-bit __atomic atomics], [
@@ -1935,7 +2148,7 @@ if test "x${je_cv_gcc_atomic_atomics}" = "xyes" ; then
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])
+ AC_DEFINE([JEMALLOC_GCC_U8_ATOMIC_ATOMICS], [ ], [ ])
fi
fi
@@ -1950,7 +2163,7 @@ JE_COMPILABLE([GCC __sync atomics], [
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])
+ AC_DEFINE([JEMALLOC_GCC_SYNC_ATOMICS], [ ], [ ])
dnl check for 8-bit atomic support
JE_COMPILABLE([GCC 8-bit __sync atomics], [
@@ -1961,7 +2174,7 @@ if test "x${je_cv_gcc_sync_atomics}" = "xyes" ; then
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])
+ AC_DEFINE([JEMALLOC_GCC_U8_SYNC_ATOMICS], [ ], [ ])
fi
fi
@@ -1986,7 +2199,7 @@ JE_COMPILABLE([Darwin OSAtomic*()], [
}
], [je_cv_osatomic])
if test "x${je_cv_osatomic}" = "xyes" ; then
- AC_DEFINE([JEMALLOC_OSATOMIC], [ ])
+ AC_DEFINE([JEMALLOC_OSATOMIC], [ ], [ ])
fi
dnl ============================================================================
@@ -1998,7 +2211,7 @@ JE_COMPILABLE([madvise(2)], [
madvise((void *)0, 0, 0);
], [je_cv_madvise])
if test "x${je_cv_madvise}" = "xyes" ; then
- AC_DEFINE([JEMALLOC_HAVE_MADVISE], [ ])
+ AC_DEFINE([JEMALLOC_HAVE_MADVISE], [ ], [ ])
dnl Check for madvise(..., MADV_FREE).
JE_COMPILABLE([madvise(..., MADV_FREE)], [
@@ -2007,12 +2220,12 @@ if test "x${je_cv_madvise}" = "xyes" ; then
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], [ ])
+ 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], [ ])
+ AC_DEFINE([JEMALLOC_PURGE_MADVISE_FREE], [ ], [ ])
+ AC_DEFINE([JEMALLOC_DEFINE_MADVISE_FREE], [ ], [ ])
;;
esac
;;
@@ -2026,7 +2239,7 @@ if test "x${je_cv_madvise}" = "xyes" ; then
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], [ ])
+ AC_DEFINE([JEMALLOC_PURGE_MADVISE_DONTNEED], [ ], [ ])
fi
dnl Check for madvise(..., MADV_DO[NT]DUMP).
@@ -2037,7 +2250,7 @@ if test "x${je_cv_madvise}" = "xyes" ; then
madvise((void *)0, 0, MADV_DODUMP);
], [je_cv_madv_dontdump])
if test "x${je_cv_madv_dontdump}" = "xyes" ; then
- AC_DEFINE([JEMALLOC_MADVISE_DONTDUMP], [ ])
+ AC_DEFINE([JEMALLOC_MADVISE_DONTDUMP], [ ], [ ])
fi
dnl Check for madvise(..., MADV_[NO]HUGEPAGE).
@@ -2047,19 +2260,61 @@ if test "x${je_cv_madvise}" = "xyes" ; then
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], [ ])
+ 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 __builtin_clz() and __builtin_clzl().
+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],
@@ -2073,12 +2328,16 @@ AC_CACHE_CHECK([for __builtin_clz],
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], [ ])
+ AC_DEFINE([JEMALLOC_HAVE_BUILTIN_CLZ], [ ], [ ])
fi
dnl ============================================================================
@@ -2097,7 +2356,7 @@ JE_COMPILABLE([Darwin os_unfair_lock_*()], [
#endif
], [je_cv_os_unfair_lock])
if test "x${je_cv_os_unfair_lock}" = "xyes" ; then
- AC_DEFINE([JEMALLOC_OS_UNFAIR_LOCK], [ ])
+ AC_DEFINE([JEMALLOC_OS_UNFAIR_LOCK], [ ], [ ])
fi
dnl ============================================================================
@@ -2123,7 +2382,7 @@ 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], [ ])
+ AC_DEFINE([JEMALLOC_ZONE], [ ], [ ])
fi
dnl ============================================================================
@@ -2144,52 +2403,56 @@ 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")))])
+ [__attribute__((tls_model("initial-exec")))],
+ [ ])
else
- AC_DEFINE([JEMALLOC_TLS_MODEL], [ ])
+ 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" ; then
- AC_DEFINE([JEMALLOC_BACKGROUND_THREAD])
+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
-JE_COMPILABLE([glibc malloc hook], [
-#include <stddef.h>
+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);
+ 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);
+ 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"
+ 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
-fi
-JE_COMPILABLE([glibc memalign hook], [
-#include <stddef.h>
+ JE_COMPILABLE([glibc memalign hook], [
+ #include <stddef.h>
-extern void *(* __memalign_hook)(size_t alignment, size_t size);
+ extern void *(* __memalign_hook)(size_t alignment, size_t size);
], [
- void *ptr = 0L;
- if (__memalign_hook) ptr = __memalign_hook(16, 7);
+ 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"
+ 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
@@ -2202,7 +2465,7 @@ JE_COMPILABLE([pthreads adaptive mutexes], [
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], [ ])
+ AC_DEFINE([JEMALLOC_HAVE_PTHREAD_MUTEX_ADAPTIVE_NP], [ ], [ ])
fi
JE_CFLAGS_SAVE()
@@ -2221,7 +2484,7 @@ JE_COMPILABLE([strerror_r returns char with gnu source], [
], [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], [ ])
+ AC_DEFINE([JEMALLOC_STRERROR_R_RETURNS_CHAR_WITH_GNU_SOURCE], [ ], [ ])
fi
dnl ============================================================================
@@ -2391,7 +2654,7 @@ 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([experimetal_smallocx : ${enable_experimental_smallocx}])
+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}])
diff --git a/deps/jemalloc/doc/jemalloc.xml.in b/deps/jemalloc/doc/jemalloc.xml.in
index 7fecda7cb..e28e8f386 100644
--- a/deps/jemalloc/doc/jemalloc.xml.in
+++ b/deps/jemalloc/doc/jemalloc.xml.in
@@ -630,7 +630,7 @@ for (i = 0; i < nbins; i++) {
</row>
<row>
<entry>8 KiB</entry>
- <entry>[40 KiB, 48 KiB, 54 KiB, 64 KiB]</entry>
+ <entry>[40 KiB, 48 KiB, 56 KiB, 64 KiB]</entry>
</row>
<row>
<entry>16 KiB</entry>
@@ -936,6 +936,22 @@ mallctl("arena." STRINGIFY(MALLCTL_ARENAS_ALL) ".decay",
</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>
@@ -950,6 +966,17 @@ mallctl("arena." STRINGIFY(MALLCTL_ARENAS_ALL) ".decay",
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>
@@ -1185,6 +1212,41 @@ mallctl("arena." STRINGIFY(MALLCTL_ARENAS_ALL) ".decay",
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>
@@ -1266,21 +1328,23 @@ malloc_conf = "xmalloc:true";]]></programlisting>
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.lg_tcache_max"><mallctl>opt.lg_tcache_max</mallctl></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.lg_tcache_max">
+ <varlistentry id="opt.tcache_max">
<term>
- <mallctl>opt.lg_tcache_max</mallctl>
+ <mallctl>opt.tcache_max</mallctl>
(<type>size_t</type>)
<literal>r-</literal>
</term>
- <listitem><para>Maximum size class (log base 2) to cache in the
- thread-specific cache (tcache). At a minimum, all small size classes
- are cached, and at a maximum all large size classes are cached. The
- default maximum is 32 KiB (2^15).</para></listitem>
+ <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">
@@ -1344,7 +1408,9 @@ malloc_conf = "xmalloc:true";]]></programlisting>
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>.</para></listitem>
+ <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">
@@ -1423,8 +1489,9 @@ malloc_conf = "xmalloc:true";]]></programlisting>
<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>
- option. By default, interval-triggered profile dumping is disabled
+ 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>
@@ -1456,8 +1523,9 @@ malloc_conf = "xmalloc:true";]]></programlisting>
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>
- option. Note that <function>atexit()</function> may allocate
+ 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
@@ -1478,8 +1546,57 @@ malloc_conf = "xmalloc:true";]]></programlisting>
<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. This option is disabled
- by default.</para></listitem>
+ 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">
@@ -1520,7 +1637,8 @@ malloc_conf = "xmalloc:true";]]></programlisting>
<link
linkend="thread.allocated"><mallctl>thread.allocated</mallctl></link>
mallctl. This is useful for avoiding the overhead of repeated
- <function>mallctl*()</function> calls.</para></listitem>
+ <function>mallctl*()</function> calls. Note that the underlying counter
+ should not be modified by the application.</para></listitem>
</varlistentry>
<varlistentry id="thread.deallocated">
@@ -1547,7 +1665,44 @@ malloc_conf = "xmalloc:true";]]></programlisting>
<link
linkend="thread.deallocated"><mallctl>thread.deallocated</mallctl></link>
mallctl. This is useful for avoiding the overhead of repeated
- <function>mallctl*()</function> calls.</para></listitem>
+ <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">
@@ -1618,6 +1773,28 @@ malloc_conf = "xmalloc:true";]]></programlisting>
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>
@@ -1631,7 +1808,16 @@ malloc_conf = "xmalloc:true";]]></programlisting>
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">
@@ -2171,7 +2357,14 @@ struct extent_hooks_s {
</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></listitem>
+ 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">
@@ -2223,9 +2416,24 @@ struct extent_hooks_s {
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>
- option.</para></listitem>
+ for the default setting. This can be useful to differentiate profile
+ dumps such as from forked processes.
+ </para></listitem>
</varlistentry>
<varlistentry id="prof.gdump">
@@ -2240,8 +2448,9 @@ struct extent_hooks_s {
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>
- option.</para></listitem>
+ 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">
@@ -2398,6 +2607,21 @@ struct extent_hooks_s {
</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>
@@ -2509,6 +2733,30 @@ struct extent_hooks_s {
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>
@@ -3250,7 +3498,7 @@ heap_v2/524288
[...]
@ 0x5f86da8 0x5f5a1dc [...] 0x29e4d4e 0xa200316 0xabb2988 [...]
t*: 13: 6688 [0: 0]
- t3: 12: 6496 [0: ]
+ t3: 12: 6496 [0: 0]
t99: 1: 192 [0: 0]
[...]
@@ -3261,9 +3509,9 @@ 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_3_aggregate>: <curobjs>: <curbytes> [<cumobjs>: <cumbytes>]
[...]
- <thread_99_aggregate>: <curobjs>: <curbytes>[<cumobjs>: <cumbytes>]
+ <thread_99_aggregate>: <curobjs>: <curbytes> [<cumobjs>: <cumbytes>]
[...]
@ <top_frame> <frame> [...] <frame> <frame> <frame> [...]
<backtrace_aggregate>: <curobjs>: <curbytes> [<cumobjs>: <cumbytes>]
@@ -3420,8 +3668,10 @@ MAPPED_LIBRARIES:
<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; in this case as much data as possible
- are read despite the error.</para></listitem>
+ 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>
diff --git a/deps/jemalloc/doc_internal/PROFILING_INTERNALS.md b/deps/jemalloc/doc_internal/PROFILING_INTERNALS.md
new file mode 100644
index 000000000..0a9f31c0c
--- /dev/null
+++ b/deps/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/deps/jemalloc/doc_internal/jemalloc.svg b/deps/jemalloc/doc_internal/jemalloc.svg
new file mode 100644
index 000000000..5e77327e6
--- /dev/null
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/activity_callback.h b/deps/jemalloc/include/jemalloc/internal/activity_callback.h
new file mode 100644
index 000000000..6c2e84e31
--- /dev/null
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/arena_externs.h b/deps/jemalloc/include/jemalloc/internal/arena_externs.h
index a4523ae0c..e6fceaafe 100644
--- a/deps/jemalloc/include/jemalloc/internal/arena_externs.h
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/arena_inlines_a.h b/deps/jemalloc/include/jemalloc/internal/arena_inlines_a.h
index 9abf7f6ac..8568358c7 100644
--- a/deps/jemalloc/include/jemalloc/internal/arena_inlines_a.h
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/arena_inlines_b.h b/deps/jemalloc/include/jemalloc/internal/arena_inlines_b.h
index dd926575f..fa81537c4 100644
--- a/deps/jemalloc/include/jemalloc/internal/arena_inlines_b.h
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/arena_stats.h b/deps/jemalloc/include/jemalloc/internal/arena_stats.h
index 23949ed92..15f1d345f 100644
--- a/deps/jemalloc/include/jemalloc/internal/arena_stats.h
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/arena_structs.h b/deps/jemalloc/include/jemalloc/internal/arena_structs.h
new file mode 100644
index 000000000..e2a5a4087
--- /dev/null
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/arena_structs_a.h b/deps/jemalloc/include/jemalloc/internal/arena_structs_a.h
deleted file mode 100644
index 46aa77c88..000000000
--- a/deps/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/deps/jemalloc/include/jemalloc/internal/arena_structs_b.h b/deps/jemalloc/include/jemalloc/internal/arena_structs_b.h
deleted file mode 100644
index eeab57fd6..000000000
--- a/deps/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/deps/jemalloc/include/jemalloc/internal/arena_types.h b/deps/jemalloc/include/jemalloc/internal/arena_types.h
index 624937e4f..d0e129176 100644
--- a/deps/jemalloc/include/jemalloc/internal/arena_types.h
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/atomic.h b/deps/jemalloc/include/jemalloc/internal/atomic.h
index a76f54cee..c0f73122a 100644
--- a/deps/jemalloc/include/jemalloc/internal/atomic.h
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/background_thread_externs.h b/deps/jemalloc/include/jemalloc/internal/background_thread_externs.h
index 0f997e18b..6ae3c8d89 100644
--- a/deps/jemalloc/include/jemalloc/internal/background_thread_externs.h
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/background_thread_inlines.h b/deps/jemalloc/include/jemalloc/internal/background_thread_inlines.h
index f85e86fa3..92c5febe7 100644
--- a/deps/jemalloc/include/jemalloc/internal/background_thread_inlines.h
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/background_thread_structs.h b/deps/jemalloc/include/jemalloc/internal/background_thread_structs.h
index c02aa434c..83a919846 100644
--- a/deps/jemalloc/include/jemalloc/internal/background_thread_structs.h
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/base.h b/deps/jemalloc/include/jemalloc/internal/base.h
new file mode 100644
index 000000000..9b2c9fb10
--- /dev/null
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/base_externs.h b/deps/jemalloc/include/jemalloc/internal/base_externs.h
deleted file mode 100644
index 7b705c9b4..000000000
--- a/deps/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/deps/jemalloc/include/jemalloc/internal/base_inlines.h b/deps/jemalloc/include/jemalloc/internal/base_inlines.h
deleted file mode 100644
index aec0e2e1e..000000000
--- a/deps/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/deps/jemalloc/include/jemalloc/internal/base_structs.h b/deps/jemalloc/include/jemalloc/internal/base_structs.h
deleted file mode 100644
index 07f214eb2..000000000
--- a/deps/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/deps/jemalloc/include/jemalloc/internal/base_types.h b/deps/jemalloc/include/jemalloc/internal/base_types.h
deleted file mode 100644
index b6db77df7..000000000
--- a/deps/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/deps/jemalloc/include/jemalloc/internal/bin.h b/deps/jemalloc/include/jemalloc/internal/bin.h
index 8547e8930..63f97395e 100644
--- a/deps/jemalloc/include/jemalloc/internal/bin.h
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/bin_info.h b/deps/jemalloc/include/jemalloc/internal/bin_info.h
new file mode 100644
index 000000000..7fe65c866
--- /dev/null
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/bin_stats.h b/deps/jemalloc/include/jemalloc/internal/bin_stats.h
index d04519c82..0b99297c0 100644
--- a/deps/jemalloc/include/jemalloc/internal/bin_stats.h
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/bin_types.h b/deps/jemalloc/include/jemalloc/internal/bin_types.h
index 3533606b9..945e8326c 100644
--- a/deps/jemalloc/include/jemalloc/internal/bin_types.h
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/bit_util.h b/deps/jemalloc/include/jemalloc/internal/bit_util.h
index c045eb868..bac59140f 100644
--- a/deps/jemalloc/include/jemalloc/internal/bit_util.h
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/bitmap.h b/deps/jemalloc/include/jemalloc/internal/bitmap.h
index c3f9cb490..dc19454d4 100644
--- a/deps/jemalloc/include/jemalloc/internal/bitmap.h
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/buf_writer.h b/deps/jemalloc/include/jemalloc/internal/buf_writer.h
new file mode 100644
index 000000000..37aa6de5b
--- /dev/null
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/cache_bin.h b/deps/jemalloc/include/jemalloc/internal/cache_bin.h
index d14556a3d..caf5be338 100644
--- a/deps/jemalloc/include/jemalloc/internal/cache_bin.h
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/counter.h b/deps/jemalloc/include/jemalloc/internal/counter.h
new file mode 100644
index 000000000..79abf0648
--- /dev/null
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/ctl.h b/deps/jemalloc/include/jemalloc/internal/ctl.h
index 1d1aacc6f..63d27f8aa 100644
--- a/deps/jemalloc/include/jemalloc/internal/ctl.h
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/decay.h b/deps/jemalloc/include/jemalloc/internal/decay.h
new file mode 100644
index 000000000..cf6a9d22c
--- /dev/null
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/ecache.h b/deps/jemalloc/include/jemalloc/internal/ecache.h
new file mode 100644
index 000000000..71cae3e34
--- /dev/null
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/edata.h b/deps/jemalloc/include/jemalloc/internal/edata.h
new file mode 100644
index 000000000..af039ea73
--- /dev/null
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/edata_cache.h b/deps/jemalloc/include/jemalloc/internal/edata_cache.h
new file mode 100644
index 000000000..8b6c0ef79
--- /dev/null
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/ehooks.h b/deps/jemalloc/include/jemalloc/internal/ehooks.h
new file mode 100644
index 000000000..8d9513e25
--- /dev/null
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/emap.h b/deps/jemalloc/include/jemalloc/internal/emap.h
new file mode 100644
index 000000000..847af3278
--- /dev/null
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/emitter.h b/deps/jemalloc/include/jemalloc/internal/emitter.h
index 542bc79c3..9482f68bc 100644
--- a/deps/jemalloc/include/jemalloc/internal/emitter.h
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/eset.h b/deps/jemalloc/include/jemalloc/internal/eset.h
new file mode 100644
index 000000000..4f689b47d
--- /dev/null
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/exp_grow.h b/deps/jemalloc/include/jemalloc/internal/exp_grow.h
new file mode 100644
index 000000000..8566b8a4c
--- /dev/null
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/extent.h b/deps/jemalloc/include/jemalloc/internal/extent.h
new file mode 100644
index 000000000..1d51d4109
--- /dev/null
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/extent_externs.h b/deps/jemalloc/include/jemalloc/internal/extent_externs.h
deleted file mode 100644
index 8aba57633..000000000
--- a/deps/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/deps/jemalloc/include/jemalloc/internal/extent_inlines.h b/deps/jemalloc/include/jemalloc/internal/extent_inlines.h
deleted file mode 100644
index 77fa4c4a2..000000000
--- a/deps/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/deps/jemalloc/include/jemalloc/internal/extent_structs.h b/deps/jemalloc/include/jemalloc/internal/extent_structs.h
deleted file mode 100644
index 767cd8930..000000000
--- a/deps/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/deps/jemalloc/include/jemalloc/internal/extent_types.h b/deps/jemalloc/include/jemalloc/internal/extent_types.h
deleted file mode 100644
index 96925cf95..000000000
--- a/deps/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/deps/jemalloc/include/jemalloc/internal/fb.h b/deps/jemalloc/include/jemalloc/internal/fb.h
new file mode 100644
index 000000000..90c4091ff
--- /dev/null
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/fxp.h b/deps/jemalloc/include/jemalloc/internal/fxp.h
new file mode 100644
index 000000000..415a98289
--- /dev/null
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/hash.h b/deps/jemalloc/include/jemalloc/internal/hash.h
index 0270034e8..7f945679e 100644
--- a/deps/jemalloc/include/jemalloc/internal/hash.h
+++ b/deps/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,29 +177,29 @@ 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;
- JEMALLOC_FALLTHROUGH
+ 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/deps/jemalloc/include/jemalloc/internal/hpa.h b/deps/jemalloc/include/jemalloc/internal/hpa.h
new file mode 100644
index 000000000..f3562853e
--- /dev/null
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/hpa_hooks.h b/deps/jemalloc/include/jemalloc/internal/hpa_hooks.h
new file mode 100644
index 000000000..4ea221cb0
--- /dev/null
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/hpa_opts.h b/deps/jemalloc/include/jemalloc/internal/hpa_opts.h
new file mode 100644
index 000000000..ee84fea13
--- /dev/null
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/hpdata.h b/deps/jemalloc/include/jemalloc/internal/hpdata.h
new file mode 100644
index 000000000..1fb534db0
--- /dev/null
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/inspect.h b/deps/jemalloc/include/jemalloc/internal/inspect.h
new file mode 100644
index 000000000..65fef51df
--- /dev/null
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/jemalloc_internal_decls.h b/deps/jemalloc/include/jemalloc/internal/jemalloc_internal_decls.h
index 7d6053e21..983027c86 100644
--- a/deps/jemalloc/include/jemalloc/internal/jemalloc_internal_decls.h
+++ b/deps/jemalloc/include/jemalloc/internal/jemalloc_internal_decls.h
@@ -5,6 +5,7 @@
#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
@@ -31,8 +32,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
@@ -91,4 +96,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/deps/jemalloc/include/jemalloc/internal/jemalloc_internal_defs.h.in b/deps/jemalloc/include/jemalloc/internal/jemalloc_internal_defs.h.in
index c442a2191..3588072f1 100644
--- a/deps/jemalloc/include/jemalloc/internal/jemalloc_internal_defs.h.in
+++ b/deps/jemalloc/include/jemalloc/internal/jemalloc_internal_defs.h.in
@@ -85,6 +85,12 @@
/* 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.
*/
@@ -101,6 +107,11 @@
#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
@@ -162,6 +173,9 @@
/* 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
@@ -177,6 +191,9 @@
/* 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
@@ -291,11 +308,40 @@
#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
@@ -363,4 +409,19 @@
/* 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/deps/jemalloc/include/jemalloc/internal/jemalloc_internal_externs.h b/deps/jemalloc/include/jemalloc/internal/jemalloc_internal_externs.h
index d291170be..fc834c673 100644
--- a/deps/jemalloc/include/jemalloc/internal/jemalloc_internal_externs.h
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/jemalloc_internal_includes.h b/deps/jemalloc/include/jemalloc/internal/jemalloc_internal_includes.h
index 437eaa407..751c112ff 100644
--- a/deps/jemalloc/include/jemalloc/internal/jemalloc_internal_includes.h
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/jemalloc_internal_inlines_a.h b/deps/jemalloc/include/jemalloc/internal/jemalloc_internal_inlines_a.h
index ddde9b4e6..9e27cc301 100644
--- a/deps/jemalloc/include/jemalloc/internal/jemalloc_internal_inlines_a.h
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/jemalloc_internal_inlines_b.h b/deps/jemalloc/include/jemalloc/internal/jemalloc_internal_inlines_b.h
index 70d6e5788..152f8a039 100644
--- a/deps/jemalloc/include/jemalloc/internal/jemalloc_internal_inlines_b.h
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/jemalloc_internal_inlines_c.h b/deps/jemalloc/include/jemalloc/internal/jemalloc_internal_inlines_c.h
index 0775b354f..2cd7e7ce9 100644
--- a/deps/jemalloc/include/jemalloc/internal/jemalloc_internal_inlines_c.h
+++ b/deps/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,25 +221,140 @@ 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);
+}
+
JEMALLOC_ALWAYS_INLINE int
iget_defrag_hint(tsdn_t *tsdn, void* ptr) {
int defrag = 0;
- rtree_ctx_t rtree_ctx_fallback;
- rtree_ctx_t *rtree_ctx = tsdn_rtree_ctx(tsdn, &rtree_ctx_fallback);
- szind_t szind;
- bool is_slab;
- rtree_szind_slab_read(tsdn, &extents_rtree, rtree_ctx, (uintptr_t)ptr, true, &szind, &is_slab);
- if (likely(is_slab)) {
+ emap_alloc_ctx_t alloc_ctx;
+ emap_alloc_ctx_lookup(tsdn, &arena_emap_global, ptr, &alloc_ctx);
+ if (likely(alloc_ctx.slab)) {
/* Small allocation. */
- extent_t *slab = iealloc(tsdn, ptr);
- arena_t *arena = extent_arena_get(slab);
- szind_t binind = extent_szind_get(slab);
- unsigned binshard = extent_binshard_get(slab);
- bin_t *bin = &arena->bins[binind].bin_shards[binshard];
+ edata_t *slab = emap_edata_lookup(tsdn, &arena_emap_global, ptr);
+ arena_t *arena = arena_get_from_edata(slab);
+ szind_t binind = edata_szind_get(slab);
+ unsigned binshard = edata_binshard_get(slab);
+ bin_t *bin = arena_get_bin(arena, binind, binshard);
malloc_mutex_lock(tsdn, &bin->lock);
+ arena_dalloc_bin_locked_info_t info;
+ arena_dalloc_bin_locked_begin(&info, binind);
/* Don't bother moving allocations from the slab currently used for new allocations */
if (slab != bin->slabcur) {
- int free_in_slab = extent_nfree_get(slab);
+ int free_in_slab = edata_nfree_get(slab);
if (free_in_slab) {
const bin_info_t *bin_info = &bin_infos[binind];
/* Find number of non-full slabs and the number of regs in them */
@@ -245,14 +362,14 @@ iget_defrag_hint(tsdn_t *tsdn, void* ptr) {
size_t curregs = 0;
/* Run on all bin shards (usually just one) */
for (uint32_t i=0; i< bin_info->n_shards; i++) {
- bin_t *bb = &arena->bins[binind].bin_shards[i];
+ bin_t *bb = arena_get_bin(arena, binind, i);
curslabs += bb->stats.nonfull_slabs;
/* Deduct the regs in full slabs (they're not part of the game) */
unsigned long full_slabs = bb->stats.curslabs - bb->stats.nonfull_slabs;
curregs += bb->stats.curregs - full_slabs * bin_info->nregs;
if (bb->slabcur) {
/* Remove slabcur from the overall utilization (not a candidate to nove from) */
- curregs -= bin_info->nregs - extent_nfree_get(bb->slabcur);
+ curregs -= bin_info->nregs - edata_nfree_get(bb->slabcur);
curslabs -= 1;
}
}
@@ -265,6 +382,7 @@ iget_defrag_hint(tsdn_t *tsdn, void* ptr) {
defrag = (bin_info->nregs - free_in_slab) * curslabs <= curregs + curregs / 8;
}
}
+ arena_dalloc_bin_locked_finish(tsdn, arena, bin, &info);
malloc_mutex_unlock(tsdn, &bin->lock);
}
return defrag;
diff --git a/deps/jemalloc/include/jemalloc/internal/jemalloc_internal_macros.h b/deps/jemalloc/include/jemalloc/internal/jemalloc_internal_macros.h
index d8ea06f6d..e97b5f907 100644
--- a/deps/jemalloc/include/jemalloc/internal/jemalloc_internal_macros.h
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/jemalloc_internal_types.h b/deps/jemalloc/include/jemalloc/internal/jemalloc_internal_types.h
index e296c5a7e..62c2b59c7 100644
--- a/deps/jemalloc/include/jemalloc/internal/jemalloc_internal_types.h
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/jemalloc_preamble.h.in b/deps/jemalloc/include/jemalloc/internal/jemalloc_preamble.h.in
index 3418cbfa2..5ce77d96f 100644
--- a/deps/jemalloc/include/jemalloc/internal/jemalloc_preamble.h.in
+++ b/deps/jemalloc/include/jemalloc/internal/jemalloc_preamble.h.in
@@ -4,8 +4,14 @@
#include "jemalloc_internal_defs.h"
#include "jemalloc/internal/jemalloc_internal_decls.h"
-#ifdef JEMALLOC_UTRACE
+#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
#define JEMALLOC_NO_DEMANGLE
@@ -180,6 +186,35 @@ static const bool config_opt_safety_checks =
#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
@@ -209,5 +244,20 @@ static const bool have_background_thread =
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/deps/jemalloc/include/jemalloc/internal/large_externs.h b/deps/jemalloc/include/jemalloc/internal/large_externs.h
index a05019e8a..8e09122df 100644
--- a/deps/jemalloc/include/jemalloc/internal/large_externs.h
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/lockedint.h b/deps/jemalloc/include/jemalloc/internal/lockedint.h
new file mode 100644
index 000000000..d020ebec1
--- /dev/null
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/malloc_io.h b/deps/jemalloc/include/jemalloc/internal/malloc_io.h
index 1d1a414e0..a375bdae0 100644
--- a/deps/jemalloc/include/jemalloc/internal/malloc_io.h
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/mpsc_queue.h b/deps/jemalloc/include/jemalloc/internal/mpsc_queue.h
new file mode 100644
index 000000000..316ea9b16
--- /dev/null
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/mutex.h b/deps/jemalloc/include/jemalloc/internal/mutex.h
index 7c24f0725..63a0b1b36 100644
--- a/deps/jemalloc/include/jemalloc/internal/mutex.h
+++ b/deps/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)
@@ -245,22 +241,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
@@ -285,4 +284,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/deps/jemalloc/include/jemalloc/internal/mutex_pool.h b/deps/jemalloc/include/jemalloc/internal/mutex_pool.h
deleted file mode 100644
index 726cece90..000000000
--- a/deps/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/deps/jemalloc/include/jemalloc/internal/mutex_prof.h b/deps/jemalloc/include/jemalloc/internal/mutex_prof.h
index 2cb8fb0cb..4a526a5ae 100644
--- a/deps/jemalloc/include/jemalloc/internal/mutex_prof.h
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/nstime.h b/deps/jemalloc/include/jemalloc/internal/nstime.h
index 17c177c7f..486e5ccac 100644
--- a/deps/jemalloc/include/jemalloc/internal/nstime.h
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/pa.h b/deps/jemalloc/include/jemalloc/internal/pa.h
new file mode 100644
index 000000000..4748a05b6
--- /dev/null
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/pac.h b/deps/jemalloc/include/jemalloc/internal/pac.h
new file mode 100644
index 000000000..01c4e6afa
--- /dev/null
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/pages.h b/deps/jemalloc/include/jemalloc/internal/pages.h
index 7dae633af..ad1f606a8 100644
--- a/deps/jemalloc/include/jemalloc/internal/pages.h
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/pai.h b/deps/jemalloc/include/jemalloc/internal/pai.h
new file mode 100644
index 000000000..d978cd7d2
--- /dev/null
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/peak.h b/deps/jemalloc/include/jemalloc/internal/peak.h
new file mode 100644
index 000000000..59da3e41b
--- /dev/null
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/peak_event.h b/deps/jemalloc/include/jemalloc/internal/peak_event.h
new file mode 100644
index 000000000..b808ce043
--- /dev/null
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/ph.h b/deps/jemalloc/include/jemalloc/internal/ph.h
index 84d6778a9..5f091c5fb 100644
--- a/deps/jemalloc/include/jemalloc/internal/ph.h
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/prng.h b/deps/jemalloc/include/jemalloc/internal/prng.h
index 15cc2d18f..14542aa12 100644
--- a/deps/jemalloc/include/jemalloc/internal/prng.h
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/prof_data.h b/deps/jemalloc/include/jemalloc/internal/prof_data.h
new file mode 100644
index 000000000..4c8e22c76
--- /dev/null
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/prof_externs.h b/deps/jemalloc/include/jemalloc/internal/prof_externs.h
index 094f3e170..bdff1349a 100644
--- a/deps/jemalloc/include/jemalloc/internal/prof_externs.h
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/prof_hook.h b/deps/jemalloc/include/jemalloc/internal/prof_hook.h
new file mode 100644
index 000000000..150d19d3d
--- /dev/null
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/prof_inlines.h b/deps/jemalloc/include/jemalloc/internal/prof_inlines.h
new file mode 100644
index 000000000..a8e7e7fb6
--- /dev/null
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/prof_inlines_a.h b/deps/jemalloc/include/jemalloc/internal/prof_inlines_a.h
deleted file mode 100644
index 471d9853c..000000000
--- a/deps/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/deps/jemalloc/include/jemalloc/internal/prof_inlines_b.h b/deps/jemalloc/include/jemalloc/internal/prof_inlines_b.h
deleted file mode 100644
index 8ba8a1e1f..000000000
--- a/deps/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/deps/jemalloc/include/jemalloc/internal/prof_log.h b/deps/jemalloc/include/jemalloc/internal/prof_log.h
new file mode 100644
index 000000000..ccb557dde
--- /dev/null
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/prof_recent.h b/deps/jemalloc/include/jemalloc/internal/prof_recent.h
new file mode 100644
index 000000000..df4102362
--- /dev/null
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/prof_stats.h b/deps/jemalloc/include/jemalloc/internal/prof_stats.h
new file mode 100644
index 000000000..7954e82de
--- /dev/null
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/prof_structs.h b/deps/jemalloc/include/jemalloc/internal/prof_structs.h
index 34ed4822b..dd22115f6 100644
--- a/deps/jemalloc/include/jemalloc/internal/prof_structs.h
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/prof_sys.h b/deps/jemalloc/include/jemalloc/internal/prof_sys.h
new file mode 100644
index 000000000..3d25a4295
--- /dev/null
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/prof_types.h b/deps/jemalloc/include/jemalloc/internal/prof_types.h
index 1eff995ec..ba6286548 100644
--- a/deps/jemalloc/include/jemalloc/internal/prof_types.h
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/psset.h b/deps/jemalloc/include/jemalloc/internal/psset.h
new file mode 100644
index 000000000..e1d64970e
--- /dev/null
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/ql.h b/deps/jemalloc/include/jemalloc/internal/ql.h
index 802904077..c7f52f862 100644
--- a/deps/jemalloc/include/jemalloc/internal/ql.h
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/qr.h b/deps/jemalloc/include/jemalloc/internal/qr.h
index 1e1056b38..ece4f5568 100644
--- a/deps/jemalloc/include/jemalloc/internal/qr.h
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/quantum.h b/deps/jemalloc/include/jemalloc/internal/quantum.h
index 821086e99..c22d753aa 100644
--- a/deps/jemalloc/include/jemalloc/internal/quantum.h
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/rb.h b/deps/jemalloc/include/jemalloc/internal/rb.h
index 47fa5ca99..a9a51cb68 100644
--- a/deps/jemalloc/include/jemalloc/internal/rb.h
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/rtree.h b/deps/jemalloc/include/jemalloc/internal/rtree.h
index 16ccbebee..a00adb298 100644
--- a/deps/jemalloc/include/jemalloc/internal/rtree.h
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/rtree_tsd.h b/deps/jemalloc/include/jemalloc/internal/rtree_tsd.h
index 562e29297..e45525c5e 100644
--- a/deps/jemalloc/include/jemalloc/internal/rtree_tsd.h
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/safety_check.h b/deps/jemalloc/include/jemalloc/internal/safety_check.h
index 53339ac12..f1a74f174 100644
--- a/deps/jemalloc/include/jemalloc/internal/safety_check.h
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/san.h b/deps/jemalloc/include/jemalloc/internal/san.h
new file mode 100644
index 000000000..8813d6bbe
--- /dev/null
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/san_bump.h b/deps/jemalloc/include/jemalloc/internal/san_bump.h
new file mode 100644
index 000000000..8ec4a710d
--- /dev/null
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/sc.h b/deps/jemalloc/include/jemalloc/internal/sc.h
index 9a099d8b6..9bab347be 100644
--- a/deps/jemalloc/include/jemalloc/internal/sc.h
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/sec.h b/deps/jemalloc/include/jemalloc/internal/sec.h
new file mode 100644
index 000000000..fa863382d
--- /dev/null
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/sec_opts.h b/deps/jemalloc/include/jemalloc/internal/sec_opts.h
new file mode 100644
index 000000000..a3ad72fbe
--- /dev/null
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/slab_data.h b/deps/jemalloc/include/jemalloc/internal/slab_data.h
new file mode 100644
index 000000000..e821863d8
--- /dev/null
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/stats.h b/deps/jemalloc/include/jemalloc/internal/stats.h
index 3b9e0eac1..727f7dcbd 100644
--- a/deps/jemalloc/include/jemalloc/internal/stats.h
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/sz.h b/deps/jemalloc/include/jemalloc/internal/sz.h
index 68e558abf..3c0fc1da3 100644
--- a/deps/jemalloc/include/jemalloc/internal/sz.h
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/tcache_externs.h b/deps/jemalloc/include/jemalloc/internal/tcache_externs.h
index d63eafde8..a2ab7101b 100644
--- a/deps/jemalloc/include/jemalloc/internal/tcache_externs.h
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/tcache_inlines.h b/deps/jemalloc/include/jemalloc/internal/tcache_inlines.h
index 5eca20e89..2634f145d 100644
--- a/deps/jemalloc/include/jemalloc/internal/tcache_inlines.h
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/tcache_structs.h b/deps/jemalloc/include/jemalloc/internal/tcache_structs.h
index 172ef9040..176d73de9 100644
--- a/deps/jemalloc/include/jemalloc/internal/tcache_structs.h
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/tcache_types.h b/deps/jemalloc/include/jemalloc/internal/tcache_types.h
index dce69382e..583677ea2 100644
--- a/deps/jemalloc/include/jemalloc/internal/tcache_types.h
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/test_hooks.h b/deps/jemalloc/include/jemalloc/internal/test_hooks.h
index a6351e59a..3d530b5c5 100644
--- a/deps/jemalloc/include/jemalloc/internal/test_hooks.h
+++ b/deps/jemalloc/include/jemalloc/internal/test_hooks.h
@@ -4,16 +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_HOOK(open, test_hooks_libc_hook)
-#define read JEMALLOC_HOOK(read, test_hooks_libc_hook)
-#define write JEMALLOC_HOOK(write, test_hooks_libc_hook)
-#define readlink JEMALLOC_HOOK(readlink, test_hooks_libc_hook)
-#define close JEMALLOC_HOOK(close, test_hooks_libc_hook)
-#define creat JEMALLOC_HOOK(creat, test_hooks_libc_hook)
-#define secure_getenv JEMALLOC_HOOK(secure_getenv, test_hooks_libc_hook)
+# 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/deps/jemalloc/include/jemalloc/internal/thread_event.h b/deps/jemalloc/include/jemalloc/internal/thread_event.h
new file mode 100644
index 000000000..2f4e1b39c
--- /dev/null
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/ticker.h b/deps/jemalloc/include/jemalloc/internal/ticker.h
index 52d0db4c8..6b51ddec4 100644
--- a/deps/jemalloc/include/jemalloc/internal/ticker.h
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/tsd.h b/deps/jemalloc/include/jemalloc/internal/tsd.h
index 9ba260045..66d688227 100644
--- a/deps/jemalloc/include/jemalloc/internal/tsd.h
+++ b/deps/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
};
@@ -262,7 +320,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. */
@@ -281,7 +341,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
/*
@@ -297,7 +359,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. */
@@ -306,7 +370,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. */
@@ -317,7 +383,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
@@ -382,7 +450,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 *
@@ -412,4 +483,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/deps/jemalloc/include/jemalloc/internal/tsd_generic.h b/deps/jemalloc/include/jemalloc/internal/tsd_generic.h
index cf73c0c71..a718472f3 100644
--- a/deps/jemalloc/include/jemalloc/internal/tsd_generic.h
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/tsd_malloc_thread_cleanup.h b/deps/jemalloc/include/jemalloc/internal/tsd_malloc_thread_cleanup.h
index 65852d5c1..d8f3ef13c 100644
--- a/deps/jemalloc/include/jemalloc/internal/tsd_malloc_thread_cleanup.h
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/tsd_types.h b/deps/jemalloc/include/jemalloc/internal/tsd_types.h
index 6200af61f..a6ae37da5 100644
--- a/deps/jemalloc/include/jemalloc/internal/tsd_types.h
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/tsd_win.h b/deps/jemalloc/include/jemalloc/internal/tsd_win.h
index cf30d18e3..a91dac88e 100644
--- a/deps/jemalloc/include/jemalloc/internal/tsd_win.h
+++ b/deps/jemalloc/include/jemalloc/internal/tsd_win.h
@@ -72,7 +72,7 @@ tsd_boot0(void) {
if (tsd_tsd == TLS_OUT_OF_INDEXES) {
return true;
}
- malloc_tsd_cleanup_register(&tsd_cleanup_wrapper);
+ _malloc_tsd_cleanup_register(&tsd_cleanup_wrapper);
tsd_wrapper_set(&tsd_boot_wrapper);
tsd_booted = true;
return false;
diff --git a/deps/jemalloc/include/jemalloc/internal/typed_list.h b/deps/jemalloc/include/jemalloc/internal/typed_list.h
new file mode 100644
index 000000000..6535055a1
--- /dev/null
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/util.h b/deps/jemalloc/include/jemalloc/internal/util.h
index 304cb545a..dcb1c0a5d 100644
--- a/deps/jemalloc/include/jemalloc/internal/util.h
+++ b/deps/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/deps/jemalloc/include/jemalloc/internal/witness.h b/deps/jemalloc/include/jemalloc/internal/witness.h
index fff9e98cb..e81b9a006 100644
--- a/deps/jemalloc/include/jemalloc/internal/witness.h
+++ b/deps/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/deps/jemalloc/include/jemalloc/jemalloc_defs.h.in b/deps/jemalloc/include/jemalloc/jemalloc_defs.h.in
index 11c39181b..cbe2fca6b 100644
--- a/deps/jemalloc/include/jemalloc/jemalloc_defs.h.in
+++ b/deps/jemalloc/include/jemalloc/jemalloc_defs.h.in
@@ -13,6 +13,12 @@
/* 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.
diff --git a/deps/jemalloc/include/jemalloc/jemalloc_macros.h.in b/deps/jemalloc/include/jemalloc/jemalloc_macros.h.in
index 3421321a4..d04af34d9 100644
--- a/deps/jemalloc/include/jemalloc/jemalloc_macros.h.in
+++ b/deps/jemalloc/include/jemalloc/jemalloc_macros.h.in
@@ -71,6 +71,7 @@
# 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)
@@ -84,6 +85,7 @@
# 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))
@@ -109,11 +111,21 @@
# 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)
@@ -121,11 +133,19 @@
# 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
/* This version of Jemalloc, modified for Redis, has the je_get_defrag_hint()
diff --git a/deps/jemalloc/include/jemalloc/jemalloc_protos.h.in b/deps/jemalloc/include/jemalloc/jemalloc_protos.h.in
index a78414b19..356221cc8 100644
--- a/deps/jemalloc/include/jemalloc/jemalloc_protos.h.in
+++ b/deps/jemalloc/include/jemalloc/jemalloc_protos.h.in
@@ -8,21 +8,22 @@ extern JEMALLOC_EXPORT void (*@je_@malloc_message)(void *cbopaque,
const char *s);
JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
- void JEMALLOC_NOTHROW *@je_@malloc(size_t size)
+ 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_NOTHROW *@je_@calloc(size_t num, size_t size)
+ 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_NOTHROW @je_@posix_memalign(void **memptr,
- size_t alignment, size_t size) JEMALLOC_CXX_THROW JEMALLOC_ATTR(nonnull(1));
+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_NOTHROW *@je_@aligned_alloc(size_t alignment,
+ 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_NOTHROW *@je_@realloc(void *ptr, size_t size)
+ void JEMALLOC_SYS_NOTHROW *@je_@realloc(void *ptr, size_t size)
JEMALLOC_CXX_THROW JEMALLOC_ALLOC_SIZE(2);
-JEMALLOC_EXPORT void JEMALLOC_NOTHROW @je_@free(void *ptr)
+JEMALLOC_EXPORT void JEMALLOC_SYS_NOTHROW @je_@free(void *ptr)
JEMALLOC_CXX_THROW;
JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
@@ -52,15 +53,19 @@ JEMALLOC_EXPORT void JEMALLOC_NOTHROW @je_@malloc_stats_print(
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_NOTHROW *@je_@memalign(size_t alignment, size_t size)
+ 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_NOTHROW *@je_@valloc(size_t size) JEMALLOC_CXX_THROW
+ void JEMALLOC_SYS_NOTHROW *@je_@valloc(size_t size) JEMALLOC_CXX_THROW
JEMALLOC_ATTR(malloc);
#endif
diff --git a/deps/jemalloc/m4/ax_cxx_compile_stdcxx.m4 b/deps/jemalloc/m4/ax_cxx_compile_stdcxx.m4
index 2c18e49c5..43087b2e6 100644
--- a/deps/jemalloc/m4/ax_cxx_compile_stdcxx.m4
+++ b/deps/jemalloc/m4/ax_cxx_compile_stdcxx.m4
@@ -1,5 +1,5 @@
# ===========================================================================
-# http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html
+# https://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html
# ===========================================================================
#
# SYNOPSIS
@@ -33,21 +33,23 @@
# 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 4
+#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], [],
- [$1], [14], [],
- [$1], [17], [m4_fatal([support for C++17 not yet implemented in AX_CXX_COMPILE_STDCXX])],
+ 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], [],
@@ -59,18 +61,11 @@ AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl
[m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])])
AC_LANG_PUSH([C++])dnl
ac_success=no
- AC_CACHE_CHECK(whether $CXX supports C++$1 features by default,
- ax_cv_cxx_compile_cxx$1,
- [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])],
- [ax_cv_cxx_compile_cxx$1=yes],
- [ax_cv_cxx_compile_cxx$1=no])])
- if test x$ax_cv_cxx_compile_cxx$1 = xyes; then
- ac_success=yes
- fi
m4_if([$2], [noext], [], [dnl
if test x$ac_success = xno; then
- for switch in -std=gnu++$1 -std=gnu++0x; do
+ 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,
@@ -96,22 +91,27 @@ AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl
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 switch in -std=c++$1 -std=c++0x +std=c++$1 "-h std=c++$1"; 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"
+ 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
- ac_success=yes
+ done
+ if test x$ac_success = xyes; then
break
fi
done
@@ -148,6 +148,11 @@ m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14],
_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
@@ -185,11 +190,13 @@ namespace cxx11
struct Base
{
+ virtual ~Base() {}
virtual void f() {}
};
struct Derived : public Base
{
+ virtual ~Derived() override {}
virtual void f() override {}
};
@@ -518,7 +525,7 @@ namespace cxx14
}
- namespace test_digit_seperators
+ namespace test_digit_separators
{
constexpr auto ten_million = 100'000'000;
@@ -560,3 +567,385 @@ 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/deps/jemalloc/msvc/projects/vc2015/jemalloc/jemalloc.vcxproj b/deps/jemalloc/msvc/projects/vc2015/jemalloc/jemalloc.vcxproj
index 228e8be0c..ec028a1aa 100644
--- a/deps/jemalloc/msvc/projects/vc2015/jemalloc/jemalloc.vcxproj
+++ b/deps/jemalloc/msvc/projects/vc2015/jemalloc/jemalloc.vcxproj
@@ -39,34 +39,64 @@
<ClCompile Include="..\..\..\..\src\background_thread.c" />
<ClCompile Include="..\..\..\..\src\base.c" />
<ClCompile Include="..\..\..\..\src\bin.c" />
+ <ClCompile Include="..\..\..\..\src\bin_info.c" />
<ClCompile Include="..\..\..\..\src\bitmap.c" />
+ <ClCompile Include="..\..\..\..\src\buf_writer.c" />
+ <ClCompile Include="..\..\..\..\src\cache_bin.c" />
<ClCompile Include="..\..\..\..\src\ckh.c" />
+ <ClCompile Include="..\..\..\..\src\counter.c" />
<ClCompile Include="..\..\..\..\src\ctl.c" />
+ <ClCompile Include="..\..\..\..\src\decay.c" />
<ClCompile Include="..\..\..\..\src\div.c" />
+ <ClCompile Include="..\..\..\..\src\ecache.c" />
+ <ClCompile Include="..\..\..\..\src\edata.c" />
+ <ClCompile Include="..\..\..\..\src\edata_cache.c" />
+ <ClCompile Include="..\..\..\..\src\ehooks.c" />
+ <ClCompile Include="..\..\..\..\src\emap.c" />
+ <ClCompile Include="..\..\..\..\src\eset.c" />
+ <ClCompile Include="..\..\..\..\src\exp_grow.c" />
<ClCompile Include="..\..\..\..\src\extent.c" />
<ClCompile Include="..\..\..\..\src\extent_dss.c" />
<ClCompile Include="..\..\..\..\src\extent_mmap.c" />
- <ClCompile Include="..\..\..\..\src\hash.c" />
+ <ClCompile Include="..\..\..\..\src\fxp.c" />
<ClCompile Include="..\..\..\..\src\hook.c" />
+ <ClCompile Include="..\..\..\..\src\hpa.c" />
+ <ClCompile Include="..\..\..\..\src\hpa_hooks.c" />
+ <ClCompile Include="..\..\..\..\src\hpdata.c" />
+ <ClCompile Include="..\..\..\..\src\inspect.c" />
<ClCompile Include="..\..\..\..\src\jemalloc.c" />
<ClCompile Include="..\..\..\..\src\large.c" />
<ClCompile Include="..\..\..\..\src\log.c" />
<ClCompile Include="..\..\..\..\src\malloc_io.c" />
<ClCompile Include="..\..\..\..\src\mutex.c" />
- <ClCompile Include="..\..\..\..\src\mutex_pool.c" />
<ClCompile Include="..\..\..\..\src\nstime.c" />
+ <ClCompile Include="..\..\..\..\src\pa.c" />
+ <ClCompile Include="..\..\..\..\src\pa_extra.c" />
+ <ClCompile Include="..\..\..\..\src\pai.c" />
+ <ClCompile Include="..\..\..\..\src\pac.c" />
<ClCompile Include="..\..\..\..\src\pages.c" />
- <ClCompile Include="..\..\..\..\src\prng.c" />
+ <ClCompile Include="..\..\..\..\src\peak_event.c" />
<ClCompile Include="..\..\..\..\src\prof.c" />
+ <ClCompile Include="..\..\..\..\src\prof_data.c" />
+ <ClCompile Include="..\..\..\..\src\prof_log.c" />
+ <ClCompile Include="..\..\..\..\src\prof_recent.c" />
+ <ClCompile Include="..\..\..\..\src\prof_stats.c" />
+ <ClCompile Include="..\..\..\..\src\prof_sys.c" />
+ <ClCompile Include="..\..\..\..\src\psset.c" />
<ClCompile Include="..\..\..\..\src\rtree.c" />
+ <ClCompile Include="..\..\..\..\src\safety_check.c" />
+ <ClCompile Include="..\..\..\..\src\san.c" />
+ <ClCompile Include="..\..\..\..\src\san_bump.c" />
<ClCompile Include="..\..\..\..\src\sc.c" />
+ <ClCompile Include="..\..\..\..\src\sec.c" />
<ClCompile Include="..\..\..\..\src\stats.c" />
<ClCompile Include="..\..\..\..\src\sz.c" />
<ClCompile Include="..\..\..\..\src\tcache.c" />
+ <ClCompile Include="..\..\..\..\src\test_hooks.c" />
+ <ClCompile Include="..\..\..\..\src\thread_event.c" />
<ClCompile Include="..\..\..\..\src\ticker.c" />
<ClCompile Include="..\..\..\..\src\tsd.c" />
<ClCompile Include="..\..\..\..\src\witness.c" />
- <ClCompile Include="..\..\..\..\src\safety_check.c" />
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{8D6BB292-9E1C-413D-9F98-4864BDC1514A}</ProjectGuid>
@@ -347,4 +377,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
-</Project>
+</Project> \ No newline at end of file
diff --git a/deps/jemalloc/msvc/projects/vc2015/jemalloc/jemalloc.vcxproj.filters b/deps/jemalloc/msvc/projects/vc2015/jemalloc/jemalloc.vcxproj.filters
index d839515b0..1b43e9f2f 100644
--- a/deps/jemalloc/msvc/projects/vc2015/jemalloc/jemalloc.vcxproj.filters
+++ b/deps/jemalloc/msvc/projects/vc2015/jemalloc/jemalloc.vcxproj.filters
@@ -16,15 +16,39 @@
<ClCompile Include="..\..\..\..\src\base.c">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\..\..\..\src\bin.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\..\..\..\src\bitmap.c">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\..\..\..\src\buf_writer.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\..\src\cache_bin.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\..\..\..\src\ckh.c">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\..\..\..\src\counter.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\..\..\..\src\ctl.c">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\..\..\..\src\decay.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\..\src\div.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\..\src\emap.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\..\src\exp_grow.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\..\..\..\src\extent.c">
<Filter>Source Files</Filter>
</ClCompile>
@@ -34,45 +58,93 @@
<ClCompile Include="..\..\..\..\src\extent_mmap.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="..\..\..\..\src\hash.c">
+ <ClCompile Include="..\..\..\..\src\fxp.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\src\hook.c">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\..\..\..\src\hpa.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\..\src\hpa_hooks.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\..\src\hpdata.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\..\src\inspect.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\..\..\..\src\jemalloc.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\src\large.c">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\..\..\..\src\log.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\..\..\..\src\malloc_io.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\src\mutex.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="..\..\..\..\src\mutex_pool.c">
+ <ClCompile Include="..\..\..\..\src\nstime.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="..\..\..\..\src\nstime.c">
+ <ClCompile Include="..\..\..\..\src\pa.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\..\src\pa_extra.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\..\src\pai.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\..\src\pac.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\src\pages.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="..\..\..\..\src\prng.c">
+ <ClCompile Include="..\..\..\..\src\peak_event.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\src\prof.c">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\..\..\..\src\prof_data.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\..\src\prof_log.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\..\src\prof_recent.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\..\src\prof_stats.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\..\src\prof_sys.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\..\src\psset.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\..\..\..\src\rtree.c">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\..\..\..\src\safety_check.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\..\..\..\src\sc.c">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\..\..\..\src\sec.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\..\..\..\src\stats.c">
<Filter>Source Files</Filter>
</ClCompile>
@@ -82,6 +154,12 @@
<ClCompile Include="..\..\..\..\src\tcache.c">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\..\..\..\src\test_hooks.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\..\src\thread_event.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\..\..\..\src\ticker.c">
<Filter>Source Files</Filter>
</ClCompile>
@@ -91,17 +169,29 @@
<ClCompile Include="..\..\..\..\src\witness.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="..\..\..\..\src\log.c">
+ <ClCompile Include="..\..\..\..\src\bin_info.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="..\..\..\..\src\bin.c">
+ <ClCompile Include="..\..\..\..\src\ecache.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="..\..\..\..\src\div.c">
+ <ClCompile Include="..\..\..\..\src\edata.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="..\..\..\..\src\safety_check.c">
+ <ClCompile Include="..\..\..\..\src\edata_cache.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\..\src\ehooks.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\..\src\eset.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\..\src\san.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\..\src\san_bump.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
-</Project>
+</Project> \ No newline at end of file
diff --git a/deps/jemalloc/msvc/projects/vc2017/jemalloc/jemalloc.vcxproj b/deps/jemalloc/msvc/projects/vc2017/jemalloc/jemalloc.vcxproj
index edcceedec..a8004dbda 100644
--- a/deps/jemalloc/msvc/projects/vc2017/jemalloc/jemalloc.vcxproj
+++ b/deps/jemalloc/msvc/projects/vc2017/jemalloc/jemalloc.vcxproj
@@ -39,35 +39,64 @@
<ClCompile Include="..\..\..\..\src\background_thread.c" />
<ClCompile Include="..\..\..\..\src\base.c" />
<ClCompile Include="..\..\..\..\src\bin.c" />
+ <ClCompile Include="..\..\..\..\src\bin_info.c" />
<ClCompile Include="..\..\..\..\src\bitmap.c" />
+ <ClCompile Include="..\..\..\..\src\buf_writer.c" />
+ <ClCompile Include="..\..\..\..\src\cache_bin.c" />
<ClCompile Include="..\..\..\..\src\ckh.c" />
+ <ClCompile Include="..\..\..\..\src\counter.c" />
<ClCompile Include="..\..\..\..\src\ctl.c" />
+ <ClCompile Include="..\..\..\..\src\decay.c" />
<ClCompile Include="..\..\..\..\src\div.c" />
+ <ClCompile Include="..\..\..\..\src\ecache.c" />
+ <ClCompile Include="..\..\..\..\src\edata.c" />
+ <ClCompile Include="..\..\..\..\src\edata_cache.c" />
+ <ClCompile Include="..\..\..\..\src\ehooks.c" />
+ <ClCompile Include="..\..\..\..\src\emap.c" />
+ <ClCompile Include="..\..\..\..\src\eset.c" />
+ <ClCompile Include="..\..\..\..\src\exp_grow.c" />
<ClCompile Include="..\..\..\..\src\extent.c" />
<ClCompile Include="..\..\..\..\src\extent_dss.c" />
<ClCompile Include="..\..\..\..\src\extent_mmap.c" />
- <ClCompile Include="..\..\..\..\src\hash.c" />
+ <ClCompile Include="..\..\..\..\src\fxp.c" />
<ClCompile Include="..\..\..\..\src\hook.c" />
+ <ClCompile Include="..\..\..\..\src\hpa.c" />
+ <ClCompile Include="..\..\..\..\src\hpa_hooks.c" />
+ <ClCompile Include="..\..\..\..\src\hpdata.c" />
+ <ClCompile Include="..\..\..\..\src\inspect.c" />
<ClCompile Include="..\..\..\..\src\jemalloc.c" />
<ClCompile Include="..\..\..\..\src\large.c" />
<ClCompile Include="..\..\..\..\src\log.c" />
<ClCompile Include="..\..\..\..\src\malloc_io.c" />
<ClCompile Include="..\..\..\..\src\mutex.c" />
- <ClCompile Include="..\..\..\..\src\mutex_pool.c" />
<ClCompile Include="..\..\..\..\src\nstime.c" />
+ <ClCompile Include="..\..\..\..\src\pa.c" />
+ <ClCompile Include="..\..\..\..\src\pa_extra.c" />
+ <ClCompile Include="..\..\..\..\src\pai.c" />
+ <ClCompile Include="..\..\..\..\src\pac.c" />
<ClCompile Include="..\..\..\..\src\pages.c" />
- <ClCompile Include="..\..\..\..\src\prng.c" />
+ <ClCompile Include="..\..\..\..\src\peak_event.c" />
<ClCompile Include="..\..\..\..\src\prof.c" />
+ <ClCompile Include="..\..\..\..\src\prof_data.c" />
+ <ClCompile Include="..\..\..\..\src\prof_log.c" />
+ <ClCompile Include="..\..\..\..\src\prof_recent.c" />
+ <ClCompile Include="..\..\..\..\src\prof_stats.c" />
+ <ClCompile Include="..\..\..\..\src\prof_sys.c" />
+ <ClCompile Include="..\..\..\..\src\psset.c" />
<ClCompile Include="..\..\..\..\src\rtree.c" />
+ <ClCompile Include="..\..\..\..\src\safety_check.c" />
+ <ClCompile Include="..\..\..\..\src\san.c" />
+ <ClCompile Include="..\..\..\..\src\san_bump.c" />
<ClCompile Include="..\..\..\..\src\sc.c" />
+ <ClCompile Include="..\..\..\..\src\sec.c" />
<ClCompile Include="..\..\..\..\src\stats.c" />
<ClCompile Include="..\..\..\..\src\sz.c" />
<ClCompile Include="..\..\..\..\src\tcache.c" />
<ClCompile Include="..\..\..\..\src\test_hooks.c" />
+ <ClCompile Include="..\..\..\..\src\thread_event.c" />
<ClCompile Include="..\..\..\..\src\ticker.c" />
<ClCompile Include="..\..\..\..\src\tsd.c" />
<ClCompile Include="..\..\..\..\src\witness.c" />
- <ClCompile Include="..\..\..\..\src\safety_check.c" />
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{8D6BB292-9E1C-413D-9F98-4864BDC1514A}</ProjectGuid>
@@ -347,4 +376,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
-</Project>
+</Project> \ No newline at end of file
diff --git a/deps/jemalloc/msvc/projects/vc2017/jemalloc/jemalloc.vcxproj.filters b/deps/jemalloc/msvc/projects/vc2017/jemalloc/jemalloc.vcxproj.filters
index 6df726012..1b43e9f2f 100644
--- a/deps/jemalloc/msvc/projects/vc2017/jemalloc/jemalloc.vcxproj.filters
+++ b/deps/jemalloc/msvc/projects/vc2017/jemalloc/jemalloc.vcxproj.filters
@@ -16,15 +16,39 @@
<ClCompile Include="..\..\..\..\src\base.c">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\..\..\..\src\bin.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\..\..\..\src\bitmap.c">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\..\..\..\src\buf_writer.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\..\src\cache_bin.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\..\..\..\src\ckh.c">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\..\..\..\src\counter.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\..\..\..\src\ctl.c">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\..\..\..\src\decay.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\..\src\div.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\..\src\emap.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\..\src\exp_grow.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\..\..\..\src\extent.c">
<Filter>Source Files</Filter>
</ClCompile>
@@ -34,45 +58,93 @@
<ClCompile Include="..\..\..\..\src\extent_mmap.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="..\..\..\..\src\hash.c">
+ <ClCompile Include="..\..\..\..\src\fxp.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\src\hook.c">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\..\..\..\src\hpa.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\..\src\hpa_hooks.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\..\src\hpdata.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\..\src\inspect.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\..\..\..\src\jemalloc.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\src\large.c">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\..\..\..\src\log.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\..\..\..\src\malloc_io.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\src\mutex.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="..\..\..\..\src\mutex_pool.c">
+ <ClCompile Include="..\..\..\..\src\nstime.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="..\..\..\..\src\nstime.c">
+ <ClCompile Include="..\..\..\..\src\pa.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\..\src\pa_extra.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\..\src\pai.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\..\src\pac.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\src\pages.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="..\..\..\..\src\prng.c">
+ <ClCompile Include="..\..\..\..\src\peak_event.c">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\..\..\..\src\prof.c">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\..\..\..\src\prof_data.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\..\src\prof_log.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\..\src\prof_recent.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\..\src\prof_stats.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\..\src\prof_sys.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\..\src\psset.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\..\..\..\src\rtree.c">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\..\..\..\src\safety_check.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\..\..\..\src\sc.c">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\..\..\..\src\sec.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\..\..\..\src\stats.c">
<Filter>Source Files</Filter>
</ClCompile>
@@ -82,6 +154,12 @@
<ClCompile Include="..\..\..\..\src\tcache.c">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\..\..\..\src\test_hooks.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\..\src\thread_event.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\..\..\..\src\ticker.c">
<Filter>Source Files</Filter>
</ClCompile>
@@ -91,20 +169,29 @@
<ClCompile Include="..\..\..\..\src\witness.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="..\..\..\..\src\log.c">
+ <ClCompile Include="..\..\..\..\src\bin_info.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="..\..\..\..\src\bin.c">
+ <ClCompile Include="..\..\..\..\src\ecache.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="..\..\..\..\src\div.c">
+ <ClCompile Include="..\..\..\..\src\edata.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="..\..\..\..\src\test_hooks.c">
+ <ClCompile Include="..\..\..\..\src\edata_cache.c">
<Filter>Source Files</Filter>
</ClCompile>
- <ClCompile Include="..\..\..\..\src\safety_check.c">
+ <ClCompile Include="..\..\..\..\src\ehooks.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\..\src\eset.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\..\src\san.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\..\src\san_bump.c">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
-</Project>
+</Project> \ No newline at end of file
diff --git a/deps/jemalloc/msvc/test_threads/test_threads.cpp b/deps/jemalloc/msvc/test_threads/test_threads.cpp
index 92e316243..6eed028d8 100644
--- a/deps/jemalloc/msvc/test_threads/test_threads.cpp
+++ b/deps/jemalloc/msvc/test_threads/test_threads.cpp
@@ -9,6 +9,7 @@
#include <thread>
#include <vector>
#include <stdio.h>
+#define JEMALLOC_NO_DEMANGLE
#include <jemalloc/jemalloc.h>
using std::vector;
diff --git a/deps/jemalloc/scripts/check-formatting.sh b/deps/jemalloc/scripts/check-formatting.sh
new file mode 100755
index 000000000..68cafd8e5
--- /dev/null
+++ b/deps/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/deps/jemalloc/scripts/freebsd/before_install.sh b/deps/jemalloc/scripts/freebsd/before_install.sh
new file mode 100644
index 000000000..f2bee321f
--- /dev/null
+++ b/deps/jemalloc/scripts/freebsd/before_install.sh
@@ -0,0 +1,3 @@
+#!/bin/tcsh
+
+su -m root -c 'pkg install -y git'
diff --git a/deps/jemalloc/scripts/freebsd/before_script.sh b/deps/jemalloc/scripts/freebsd/before_script.sh
new file mode 100644
index 000000000..29406f6fb
--- /dev/null
+++ b/deps/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/deps/jemalloc/scripts/freebsd/script.sh b/deps/jemalloc/scripts/freebsd/script.sh
new file mode 100644
index 000000000..d9c53a201
--- /dev/null
+++ b/deps/jemalloc/scripts/freebsd/script.sh
@@ -0,0 +1,3 @@
+#!/bin/tcsh
+
+gmake check
diff --git a/deps/jemalloc/scripts/gen_run_tests.py b/deps/jemalloc/scripts/gen_run_tests.py
index a414f812a..7c3075f9f 100755
--- a/deps/jemalloc/scripts/gen_run_tests.py
+++ b/deps/jemalloc/scripts/gen_run_tests.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
import sys
from itertools import combinations
@@ -14,14 +14,14 @@ nparallel = cpu_count() * 2
uname = uname()[0]
-if "BSD" in uname:
+if call("command -v gmake", shell=True) == 0:
make_cmd = 'gmake'
else:
make_cmd = 'make'
def powerset(items):
result = []
- for i in xrange(len(items) + 1):
+ for i in range(len(items) + 1):
result += combinations(items, i)
return result
@@ -41,6 +41,7 @@ possible_config_opts = [
'--enable-prof',
'--disable-stats',
'--enable-opt-safety-checks',
+ '--with-lg-page=16',
]
if bits_64:
possible_config_opts.append('--with-lg-vaddr=56')
@@ -52,19 +53,20 @@ possible_malloc_conf_opts = [
'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'
+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 is 'clang' \
+ if cc == 'clang' \
and '-m32' in possible_compiler_opts \
and '--enable-prof' in config_opts:
continue
@@ -79,9 +81,9 @@ for cc, cxx in possible_compilers:
)
# 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
+ 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 \
@@ -92,7 +94,7 @@ for cc, cxx in possible_compilers:
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
+ print("""cat <<EOF > run_test_%(ind)d.sh
#!/bin/sh
set -e
@@ -120,7 +122,9 @@ 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}
+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}
+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/deps/jemalloc/scripts/gen_travis.py b/deps/jemalloc/scripts/gen_travis.py
index f1478c62c..4366a066e 100755
--- a/deps/jemalloc/scripts/gen_travis.py
+++ b/deps/jemalloc/scripts/gen_travis.py
@@ -1,149 +1,327 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
-from itertools import combinations
+from itertools import combinations, chain
+from enum import Enum, auto
-travis_template = """\
-language: generic
-dist: precise
-matrix:
+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:
-%s
+{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:
- - autoconf
- - scripts/gen_travis.py > travis_script && diff .travis.yml travis_script
- - ./configure ${COMPILER_FLAGS:+ \
- CC="$CC $COMPILER_FLAGS" \
- CXX="$CXX $COMPILER_FLAGS" } \
- $CONFIGURE_FLAGS
- - make -j3
- - make -j3 tests
+ - |-
+ 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:
- - make check
+ - |-
+ 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.
-# Things at once, for C(7, 0) + C(7, 1) + C(7, 2) = 29
MAX_UNUSUAL_OPTIONS = 2
-os_default = 'linux'
-os_unusual = 'osx'
-compilers_default = 'CC=gcc CXX=g++'
-compilers_unusual = 'CC=clang CXX=clang++'
+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')
+
-compiler_flag_unusuals = ['-m32']
+compilers_unusual = [CLANG,]
-configure_flag_unusuals = [
+
+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 = [
+
+malloc_conf_unusuals = [Option.as_malloc_conf(opt) for opt in (
'tcache:false',
'dss:primary',
'percpu_arena:percpu',
'background_thread:true',
-]
+)]
-all_unusuals = (
- [os_unusual] + [compilers_unusual] + compiler_flag_unusuals
- + configure_flag_unusuals + malloc_conf_unusuals
-)
-unusual_combinations_to_test = []
-for i in xrange(MAX_UNUSUAL_OPTIONS + 1):
- unusual_combinations_to_test += combinations(all_unusuals, i)
+all_unusuals = (compilers_unusual + feature_unusuals
+ + configure_flag_unusuals + malloc_conf_unusuals)
-gcc_multilib_set = False
-# Formats a job from a combination of flags
-def format_job(combination):
- global gcc_multilib_set
- os = os_unusual if os_unusual in combination else os_default
- compilers = compilers_unusual if compilers_unusual in combination else compilers_default
+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
- compiler_flags = [x for x in combination if x in compiler_flag_unusuals]
- configure_flags = [x for x in combination if x in configure_flag_unusuals]
- malloc_conf = [x for x in combination if x in malloc_conf_unusuals]
- # Filter out unsupported configurations on OS X.
- if os == 'osx' and ('dss:primary' in malloc_conf or \
- 'percpu_arena:percpu' in malloc_conf or 'background_thread:true' \
- in malloc_conf):
- return ""
+# 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))
+ configure_flags.append('--with-malloc-conf=' + ','.join(malloc_conf))
- # Filter out an unsupported configuration - heap profiling on OS X.
- if os == 'osx' and '--enable-prof' in configure_flags:
- return ""
+ if not compilers:
+ compiler = GCC.value
+ else:
+ compiler = compilers[0]
- # We get some spurious errors when -Warray-bounds is enabled.
- env_string = ('{} COMPILER_FLAGS="{}" CONFIGURE_FLAGS="{}" '
- 'EXTRA_CFLAGS="-Werror -Wno-array-bounds"').format(
- compilers, " ".join(compiler_flags), " ".join(configure_flags))
-
- job = ""
- job += ' - os: %s\n' % os
- job += ' env: %s\n' % env_string
- if '-m32' in combination and os == 'linux':
- job += ' addons:'
- if gcc_multilib_set:
- job += ' *gcc_multilib\n'
- else:
- job += ' &gcc_multilib\n'
- job += ' apt:\n'
- job += ' packages:\n'
- job += ' - gcc-multilib\n'
- gcc_multilib_set = True
+ 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
-include_rows = ""
-for combination in unusual_combinations_to_test:
- include_rows += format_job(combination)
-# Development build
-include_rows += '''\
+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++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--enable-debug --disable-cache-oblivious --enable-stats --enable-log --enable-prof" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
-'''
-
-# Enable-expermental-smallocx
-include_rows += '''\
+ 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++ COMPILER_FLAGS="" CONFIGURE_FLAGS="--enable-debug --enable-experimental-smallocx --enable-stats --enable-prof" EXTRA_CFLAGS="-Werror -Wno-array-bounds"
-'''
+ env: CC=gcc CXX=g++ CONFIGURE_FLAGS="--enable-debug \
+--enable-experimental-smallocx --enable-stats --enable-prof" \
+EXTRA_CFLAGS="-Werror -Wno-array-bounds"
+"""
-# Valgrind build bots
-include_rows += '''
- # Valgrind
- - os: linux
- env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="" EXTRA_CFLAGS="-Werror -Wno-array-bounds" JEMALLOC_TEST_PREFIX="valgrind"
- addons:
- apt:
- packages:
- - valgrind
-'''
-
-# To enable valgrind on macosx add:
-#
-# - os: osx
-# env: CC=gcc CXX=g++ COMPILER_FLAGS="" CONFIGURE_FLAGS="" EXTRA_CFLAGS="-Werror -Wno-array-bounds" JEMALLOC_TEST_PREFIX="valgrind"
-# install: brew install valgrind
-#
-# It currently fails due to: https://github.com/jemalloc/jemalloc/issues/1274
-
-print travis_template % include_rows
+
+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/deps/jemalloc/scripts/linux/before_install.sh b/deps/jemalloc/scripts/linux/before_install.sh
new file mode 100644
index 000000000..674174639
--- /dev/null
+++ b/deps/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/deps/jemalloc/scripts/windows/before_install.sh b/deps/jemalloc/scripts/windows/before_install.sh
new file mode 100644
index 000000000..2740c4588
--- /dev/null
+++ b/deps/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/deps/jemalloc/scripts/windows/before_script.sh b/deps/jemalloc/scripts/windows/before_script.sh
new file mode 100644
index 000000000..9d30ababd
--- /dev/null
+++ b/deps/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/deps/jemalloc/scripts/windows/script.sh b/deps/jemalloc/scripts/windows/script.sh
new file mode 100644
index 000000000..3a27f70aa
--- /dev/null
+++ b/deps/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/deps/jemalloc/src/arena.c b/deps/jemalloc/src/arena.c
index ba50e4103..857b27c52 100644
--- a/deps/jemalloc/src/arena.c
+++ b/deps/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/deps/jemalloc/src/background_thread.c b/deps/jemalloc/src/background_thread.c
index 57b9b256b..3bb8d26cd 100644
--- a/deps/jemalloc/src/background_thread.c
+++ b/deps/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/deps/jemalloc/src/base.c b/deps/jemalloc/src/base.c
index f3c61661a..7f4d67564 100644
--- a/deps/jemalloc/src/base.c
+++ b/deps/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/deps/jemalloc/src/bin.c b/deps/jemalloc/src/bin.c
index bca6b12c3..fa2045870 100644
--- a/deps/jemalloc/src/bin.c
+++ b/deps/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/deps/jemalloc/src/bin_info.c b/deps/jemalloc/src/bin_info.c
new file mode 100644
index 000000000..8629ef881
--- /dev/null
+++ b/deps/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/deps/jemalloc/src/bitmap.c b/deps/jemalloc/src/bitmap.c
index 468b3178e..0ccedc5db 100644
--- a/deps/jemalloc/src/bitmap.c
+++ b/deps/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/deps/jemalloc/src/buf_writer.c b/deps/jemalloc/src/buf_writer.c
new file mode 100644
index 000000000..7c6f79403
--- /dev/null
+++ b/deps/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/deps/jemalloc/src/cache_bin.c b/deps/jemalloc/src/cache_bin.c
new file mode 100644
index 000000000..9ae072a0e
--- /dev/null
+++ b/deps/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/deps/jemalloc/src/ckh.c b/deps/jemalloc/src/ckh.c
index 1bf6df5a1..8db4319c5 100644
--- a/deps/jemalloc/src/ckh.c
+++ b/deps/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/deps/jemalloc/src/counter.c b/deps/jemalloc/src/counter.c
new file mode 100644
index 000000000..8f1ae3af4
--- /dev/null
+++ b/deps/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/deps/jemalloc/src/ctl.c b/deps/jemalloc/src/ctl.c
index 48afaa61f..135271baf 100644
--- a/deps/jemalloc/src/ctl.c
+++ b/deps/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/deps/jemalloc/src/decay.c b/deps/jemalloc/src/decay.c
new file mode 100644
index 000000000..d801b2bc0
--- /dev/null
+++ b/deps/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/deps/jemalloc/src/ecache.c b/deps/jemalloc/src/ecache.c
new file mode 100644
index 000000000..a242227d3
--- /dev/null
+++ b/deps/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/deps/jemalloc/src/edata.c b/deps/jemalloc/src/edata.c
new file mode 100644
index 000000000..82b6f5654
--- /dev/null
+++ b/deps/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/deps/jemalloc/src/edata_cache.c b/deps/jemalloc/src/edata_cache.c
new file mode 100644
index 000000000..6bc1848cb
--- /dev/null
+++ b/deps/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/deps/jemalloc/src/ehooks.c b/deps/jemalloc/src/ehooks.c
new file mode 100644
index 000000000..383e9de6a
--- /dev/null
+++ b/deps/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/deps/jemalloc/src/emap.c b/deps/jemalloc/src/emap.c
new file mode 100644
index 000000000..9cc95a724
--- /dev/null
+++ b/deps/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/deps/jemalloc/src/eset.c b/deps/jemalloc/src/eset.c
new file mode 100644
index 000000000..6f8f335e1
--- /dev/null
+++ b/deps/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/deps/jemalloc/src/exp_grow.c b/deps/jemalloc/src/exp_grow.c
new file mode 100644
index 000000000..386471f49
--- /dev/null
+++ b/deps/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/deps/jemalloc/src/extent.c b/deps/jemalloc/src/extent.c
index 9237f903d..cf3d1f311 100644
--- a/deps/jemalloc/src/extent.c
+++ b/deps/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,467 +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 = 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 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(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);
+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);
+}
+
+/* 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);
}
/*
@@ -2085,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);
+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_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;
-}
-
-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));
- extent_hooks_assure_initialized(arena, r_extent_hooks);
+ 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);
- 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;
@@ -2272,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;
- }
+ emap_prepare_t prepare;
+ emap_merge_prepare(tsdn, pac->emap, &prepare, a, b);
- 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));
+ 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_rtree_write_acquired(tsdn, a_elm_a, b_elm_b, a, SC_NSIZES,
- false);
-
- 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/deps/jemalloc/src/extent_dss.c b/deps/jemalloc/src/extent_dss.c
index 858178911..9a35bacfb 100644
--- a/deps/jemalloc/src/extent_dss.c
+++ b/deps/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/deps/jemalloc/src/extent_mmap.c b/deps/jemalloc/src/extent_mmap.c
index 17fd1c8f9..5f0ee2d24 100644
--- a/deps/jemalloc/src/extent_mmap.c
+++ b/deps/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/deps/jemalloc/src/fxp.c b/deps/jemalloc/src/fxp.c
new file mode 100644
index 000000000..96585f0a6
--- /dev/null
+++ b/deps/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/deps/jemalloc/src/hash.c b/deps/jemalloc/src/hash.c
deleted file mode 100644
index 7b2bdc2bd..000000000
--- a/deps/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/deps/jemalloc/src/hook.c b/deps/jemalloc/src/hook.c
index 9ac703cf9..493edbbe5 100644
--- a/deps/jemalloc/src/hook.c
+++ b/deps/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/deps/jemalloc/src/hpa.c b/deps/jemalloc/src/hpa.c
new file mode 100644
index 000000000..7e2aeba0c
--- /dev/null
+++ b/deps/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/deps/jemalloc/src/hpa_hooks.c b/deps/jemalloc/src/hpa_hooks.c
new file mode 100644
index 000000000..ade581e8d
--- /dev/null
+++ b/deps/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/deps/jemalloc/src/hpdata.c b/deps/jemalloc/src/hpdata.c
new file mode 100644
index 000000000..e7d7294c7
--- /dev/null
+++ b/deps/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/deps/jemalloc/src/inspect.c b/deps/jemalloc/src/inspect.c
new file mode 100644
index 000000000..911b5d524
--- /dev/null
+++ b/deps/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/deps/jemalloc/src/jemalloc.c b/deps/jemalloc/src/jemalloc.c
index 97da1ee72..83026093b 100644
--- a/deps/jemalloc/src/jemalloc.c
+++ b/deps/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"
/******************************************************************************/
@@ -29,6 +35,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
@@ -66,16 +95,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.
@@ -94,13 +180,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;
@@ -180,7 +260,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)
@@ -205,11 +285,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)) {
@@ -257,7 +332,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).
*/
@@ -315,7 +390,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());
@@ -337,7 +412,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;
}
@@ -361,11 +436,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);
@@ -394,14 +469,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
@@ -418,82 +498,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) {
@@ -576,8 +580,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);
@@ -629,20 +632,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) {
@@ -661,11 +650,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);
@@ -730,18 +721,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
@@ -750,9 +751,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++) {
@@ -763,16 +802,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. */
@@ -854,10 +893,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;
}
}
@@ -876,6 +917,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;
@@ -932,7 +974,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]) {
@@ -1010,6 +1052,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;
@@ -1024,9 +1069,11 @@ malloc_conf_init_helper(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS],
static const char *opts_explain[MALLOC_CONF_NSOURCES] = {
"string specified via --with-malloc-conf",
"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"
+ ("\"name\" of the file referenced by the symbolic link named "
+ "/etc/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;
@@ -1094,39 +1141,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, \
@@ -1134,27 +1192,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 <= \
@@ -1174,13 +1220,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;
}
@@ -1193,18 +1240,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;
}
@@ -1216,9 +1263,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;
@@ -1241,6 +1306,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) :
@@ -1251,7 +1319,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) {
@@ -1287,9 +1364,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
@@ -1309,16 +1438,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;
}
@@ -1336,7 +1465,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;
@@ -1378,7 +1583,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")) {
@@ -1392,15 +1634,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;
}
@@ -1411,6 +1653,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
@@ -1421,7 +1712,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
@@ -1436,15 +1729,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
@@ -1488,8 +1799,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];
@@ -1503,8 +1814,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. */
@@ -1515,12 +1827,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;
}
@@ -1530,8 +1850,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,
@@ -1550,11 +1882,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;
@@ -1576,6 +1926,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) && \
@@ -1606,7 +1979,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;
}
@@ -1765,10 +2144,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)
}
@@ -1907,38 +2287,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)) {
@@ -1962,6 +2411,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) :
@@ -1978,6 +2428,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;
}
@@ -2031,16 +2482,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;
@@ -2057,31 +2506,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);
@@ -2107,26 +2537,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;
@@ -2135,17 +2564,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)) {
@@ -2157,12 +2581,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) {
@@ -2273,7 +2702,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);
@@ -2306,86 +2739,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
@@ -2502,56 +2860,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) {
@@ -2565,30 +2873,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
@@ -2604,166 +2932,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);
+ isdalloct(tsd_tsdn(tsd), ptr, usize, tcache, &alloc_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)) {
- if (ptr != NULL) {
- /* realloc(ptr, 0) is equivalent to free(ptr). */
- UTRACE(ptr, 0, 0);
- tcache_t *tcache;
- tsd_t *tsd = tsd_fetch();
- if (tsd_reentrancy_level_get(tsd) == 0) {
- tcache = tcache_get(tsd);
- } else {
- tcache = NULL;
- }
-
- uintptr_t args[3] = {(uintptr_t)ptr, size};
- hook_invoke_dalloc(hook_dalloc_realloc, ptr, args);
-
- ifree(tsd, ptr, tcache, true);
-
- LOG("core.realloc.exit", "result: %p", NULL);
- return NULL;
+ if (config_fill && slow_path && opt_junk_free) {
+ junk_free_callback(ptr, usize);
}
- 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);
- } 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();
- }
- 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
@@ -2782,79 +3007,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;
+ }
- 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)) {
+ 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);
+
+ 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;
}
@@ -2965,6 +3260,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;
@@ -2973,7 +3270,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
@@ -3016,6 +3313,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
@@ -3060,25 +3377,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);
@@ -3113,25 +3415,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);
@@ -3154,6 +3441,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,
@@ -3166,66 +3455,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);
@@ -3233,44 +3504,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;
@@ -3281,20 +3539,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)) {
@@ -3304,10 +3564,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) {
@@ -3324,51 +3677,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);
@@ -3376,13 +3724,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;
}
@@ -3391,7 +3754,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);
@@ -3403,10 +3766,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));
@@ -3434,13 +3804,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))) {
@@ -3490,31 +3872,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)) {
@@ -3533,13 +3897,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;
}
@@ -3549,33 +3909,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)) {
@@ -3587,7 +3928,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
@@ -3595,7 +3935,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);
}
@@ -3704,6 +4044,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) {
@@ -3713,23 +4054,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 {
@@ -3740,12 +4088,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.
*/
@@ -3812,7 +4359,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) {
@@ -3841,12 +4388,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);
}
@@ -3874,6 +4426,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;
@@ -3903,6 +4456,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/deps/jemalloc/src/jemalloc_cpp.cpp b/deps/jemalloc/src/jemalloc_cpp.cpp
index da0441a7c..451655f1b 100644
--- a/deps/jemalloc/src/jemalloc_cpp.cpp
+++ b/deps/jemalloc/src/jemalloc_cpp.cpp
@@ -39,9 +39,29 @@ 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) {
@@ -72,14 +92,21 @@ handleOOM(std::size_t size, bool nothrow) {
}
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) {
- void *ptr = je_malloc(size);
- if (likely(ptr != nullptr))
- return ptr;
-
- return handleOOM(size, IsNoExcept);
+ return imalloc_fastpath(size, &fallback_impl<IsNoExcept>);
}
void *
@@ -102,6 +129,42 @@ 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);
@@ -123,19 +186,69 @@ void operator delete[](void *ptr, const std::nothrow_t &) noexcept {
#if __cpp_sized_deallocation >= 201309
+JEMALLOC_ALWAYS_INLINE
void
-operator delete(void *ptr, std::size_t size) noexcept {
+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 {
+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_noflags(ptr, size);
+ je_sdallocx(ptr, size, MALLOCX_ALIGN(alignment));
}
-#endif // __cpp_sized_deallocation
+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/deps/jemalloc/src/large.c b/deps/jemalloc/src/large.c
index 8e7a781d3..5fc4bf584 100644
--- a/deps/jemalloc/src/large.c
+++ b/deps/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/deps/jemalloc/src/malloc_io.c b/deps/jemalloc/src/malloc_io.c
index d7cb0f528..b76885cbb 100644
--- a/deps/jemalloc/src/malloc_io.c
+++ b/deps/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));
}
@@ -135,10 +133,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;
}
@@ -289,7 +287,7 @@ d2s(intmax_t x, char sign, char *s, size_t *slen_p) {
if (!neg) {
break;
}
- /* Fall through. */
+ JEMALLOC_FALLTHROUGH;
case ' ':
case '+':
s--;
@@ -323,6 +321,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;
@@ -348,7 +347,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. */ \
@@ -420,6 +423,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. */
@@ -456,7 +461,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);
@@ -464,6 +474,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;
@@ -521,6 +532,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);
@@ -620,8 +643,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) {
@@ -644,8 +667,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/deps/jemalloc/src/mutex.c b/deps/jemalloc/src/mutex.c
index 3f920f5b1..0b3547a87 100644
--- a/deps/jemalloc/src/mutex.c
+++ b/deps/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. */
@@ -46,13 +51,13 @@ JEMALLOC_EXPORT int _pthread_mutex_init_calloc_cb(pthread_mutex_t *mutex,
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)
@@ -60,7 +65,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. */
@@ -68,7 +73,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);
@@ -104,8 +109,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/deps/jemalloc/src/mutex_pool.c b/deps/jemalloc/src/mutex_pool.c
deleted file mode 100644
index f24d10e44..000000000
--- a/deps/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/deps/jemalloc/src/nstime.c b/deps/jemalloc/src/nstime.c
index 71db35396..a1a53777f 100644
--- a/deps/jemalloc/src/nstime.c
+++ b/deps/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/deps/jemalloc/src/pa.c b/deps/jemalloc/src/pa.c
new file mode 100644
index 000000000..eb7e4620e
--- /dev/null
+++ b/deps/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/deps/jemalloc/src/pa_extra.c b/deps/jemalloc/src/pa_extra.c
new file mode 100644
index 000000000..0f488be69
--- /dev/null
+++ b/deps/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/deps/jemalloc/src/pac.c b/deps/jemalloc/src/pac.c
new file mode 100644
index 000000000..53e3d8237
--- /dev/null
+++ b/deps/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/deps/jemalloc/src/pages.c b/deps/jemalloc/src/pages.c
index 13de27a00..8c83a7de7 100644
--- a/deps/jemalloc/src/pages.c
+++ b/deps/jemalloc/src/pages.c
@@ -1,4 +1,3 @@
-#define JEMALLOC_PAGES_C_
#include "jemalloc/internal/jemalloc_preamble.h"
#include "jemalloc/internal/pages.h"
@@ -14,6 +13,14 @@
#include <vm/vm_param.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. */
@@ -40,6 +47,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
@@ -74,9 +132,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);
@@ -197,8 +267,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);
@@ -246,14 +316,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)));
@@ -261,7 +327,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;
}
@@ -278,6 +344,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);
@@ -288,6 +363,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);
@@ -318,6 +453,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
@@ -334,7 +472,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);
@@ -349,8 +492,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
@@ -394,8 +542,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
@@ -405,8 +555,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
@@ -547,14 +699,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";
@@ -563,6 +715,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
@@ -578,7 +733,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) {
@@ -591,6 +746,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;
}
@@ -606,6 +765,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
@@ -619,6 +792,8 @@ pages_boot(void) {
mmap_flags |= MAP_NORESERVE;
}
# endif
+#elif defined(__NetBSD__)
+ os_overcommits = true;
#else
os_overcommits = false;
#endif
diff --git a/deps/jemalloc/src/pai.c b/deps/jemalloc/src/pai.c
new file mode 100644
index 000000000..45c877292
--- /dev/null
+++ b/deps/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/deps/jemalloc/src/peak_event.c b/deps/jemalloc/src/peak_event.c
new file mode 100644
index 000000000..4093fbcc6
--- /dev/null
+++ b/deps/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/deps/jemalloc/src/prng.c b/deps/jemalloc/src/prng.c
deleted file mode 100644
index 83c04bf9b..000000000
--- a/deps/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/deps/jemalloc/src/prof.c b/deps/jemalloc/src/prof.c
index 13334cb4c..7a6d5d569 100644
--- a/deps/jemalloc/src/prof.c
+++ b/deps/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/deps/jemalloc/src/prof_data.c b/deps/jemalloc/src/prof_data.c
new file mode 100644
index 000000000..bfa55be1c
--- /dev/null
+++ b/deps/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/deps/jemalloc/src/prof_log.c b/deps/jemalloc/src/prof_log.c
new file mode 100644
index 000000000..0632c3b37
--- /dev/null
+++ b/deps/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/deps/jemalloc/src/prof_recent.c b/deps/jemalloc/src/prof_recent.c
new file mode 100644
index 000000000..834a9446c
--- /dev/null
+++ b/deps/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/deps/jemalloc/src/prof_stats.c b/deps/jemalloc/src/prof_stats.c
new file mode 100644
index 000000000..5d1a506bb
--- /dev/null
+++ b/deps/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/deps/jemalloc/src/prof_sys.c b/deps/jemalloc/src/prof_sys.c
new file mode 100644
index 000000000..b5f1f5b22
--- /dev/null
+++ b/deps/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/deps/jemalloc/src/psset.c b/deps/jemalloc/src/psset.c
new file mode 100644
index 000000000..9a8f054f1
--- /dev/null
+++ b/deps/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/deps/jemalloc/src/rtree.c b/deps/jemalloc/src/rtree.c
index 4ae41fe2f..6496b5afd 100644
--- a/deps/jemalloc/src/rtree.c
+++ b/deps/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/deps/jemalloc/src/safety_check.c b/deps/jemalloc/src/safety_check.c
index 804155dcf..209fdda92 100644
--- a/deps/jemalloc/src/safety_check.c
+++ b/deps/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/deps/jemalloc/src/san.c b/deps/jemalloc/src/san.c
new file mode 100644
index 000000000..6e5129113
--- /dev/null
+++ b/deps/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/deps/jemalloc/src/san_bump.c b/deps/jemalloc/src/san_bump.c
new file mode 100644
index 000000000..888974555
--- /dev/null
+++ b/deps/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/deps/jemalloc/src/sc.c b/deps/jemalloc/src/sc.c
index 89ddb6ba6..e4a94d89f 100644
--- a/deps/jemalloc/src/sc.c
+++ b/deps/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/deps/jemalloc/src/sec.c b/deps/jemalloc/src/sec.c
new file mode 100644
index 000000000..df6755904
--- /dev/null
+++ b/deps/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/deps/jemalloc/src/stats.c b/deps/jemalloc/src/stats.c
index 118e05d29..efc70fd3c 100644
--- a/deps/jemalloc/src/stats.c
+++ b/deps/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/deps/jemalloc/src/sz.c b/deps/jemalloc/src/sz.c
index 8633fb050..d3115dda7 100644
--- a/deps/jemalloc/src/sz.c
+++ b/deps/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/deps/jemalloc/src/tcache.c b/deps/jemalloc/src/tcache.c
index 50099a9f2..fa16732e4 100644
--- a/deps/jemalloc/src/tcache.c
+++ b/deps/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/deps/jemalloc/src/thread_event.c b/deps/jemalloc/src/thread_event.c
new file mode 100644
index 000000000..37eb5827d
--- /dev/null
+++ b/deps/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/deps/jemalloc/src/ticker.c b/deps/jemalloc/src/ticker.c
index d7b8cd26c..790b5c200 100644
--- a/deps/jemalloc/src/ticker.c
+++ b/deps/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/deps/jemalloc/src/ticker.py b/deps/jemalloc/src/ticker.py
new file mode 100755
index 000000000..3807740c3
--- /dev/null
+++ b/deps/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/deps/jemalloc/src/tsd.c b/deps/jemalloc/src/tsd.c
index a31f6b969..e8e4f3a33 100644
--- a/deps/jemalloc/src/tsd.c
+++ b/deps/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/deps/jemalloc/src/witness.c b/deps/jemalloc/src/witness.c
index f42b72ad1..4474af04c 100644
--- a/deps/jemalloc/src/witness.c
+++ b/deps/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/deps/jemalloc/test/analyze/prof_bias.c b/deps/jemalloc/test/analyze/prof_bias.c
new file mode 100644
index 000000000..a96ca942a
--- /dev/null
+++ b/deps/jemalloc/test/analyze/prof_bias.c
@@ -0,0 +1,60 @@
+#include "test/jemalloc_test.h"
+
+/*
+ * This is a helper utility, only meant to be run manually (and, for example,
+ * doesn't check for failures, try to skip execution in non-prof modes, etc.).
+ * It runs, allocates objects of two different sizes from the same stack trace,
+ * and exits.
+ *
+ * The idea is that some human operator will run it like:
+ * MALLOC_CONF="prof:true,prof_final:true" test/analyze/prof_bias
+ * and manually inspect the results.
+ *
+ * The results should be:
+ * jeprof --text test/analyze/prof_bias --inuse_space jeprof.<pid>.0.f.heap:
+ * around 1024 MB
+ * jeprof --text test/analyze/prof_bias --inuse_objects jeprof.<pid>.0.f.heap:
+ * around 33554448 = 16 + 32 * 1024 * 1024
+ *
+ * And, if prof_accum is on:
+ * jeprof --text test/analyze/prof_bias --alloc_space jeprof.<pid>.0.f.heap:
+ * around 2048 MB
+ * jeprof --text test/analyze/prof_bias --alloc_objects jeprof.<pid>.0.f.heap:
+ * around 67108896 = 2 * (16 + 32 * 1024 * 1024)
+ */
+
+static void
+mock_backtrace(void **vec, unsigned *len, unsigned max_len) {
+ *len = 4;
+ vec[0] = (void *)0x111;
+ vec[1] = (void *)0x222;
+ vec[2] = (void *)0x333;
+ vec[3] = (void *)0x444;
+}
+
+static void
+do_allocs(size_t sz, size_t cnt, bool do_frees) {
+ for (size_t i = 0; i < cnt; i++) {
+ void *ptr = mallocx(sz, 0);
+ assert_ptr_not_null(ptr, "Unexpected mallocx failure");
+ if (do_frees) {
+ dallocx(ptr, 0);
+ }
+ }
+}
+
+int
+main(void) {
+ size_t lg_prof_sample_local = 19;
+ int err = mallctl("prof.reset", NULL, NULL,
+ (void *)&lg_prof_sample_local, sizeof(lg_prof_sample_local));
+ assert(err == 0);
+
+ prof_backtrace_hook_set(mock_backtrace);
+ do_allocs(16, 32 * 1024 * 1024, /* do_frees */ true);
+ do_allocs(32 * 1024* 1024, 16, /* do_frees */ true);
+ do_allocs(16, 32 * 1024 * 1024, /* do_frees */ false);
+ do_allocs(32 * 1024* 1024, 16, /* do_frees */ false);
+
+ return 0;
+}
diff --git a/deps/jemalloc/test/analyze/rand.c b/deps/jemalloc/test/analyze/rand.c
new file mode 100644
index 000000000..bb20b06ec
--- /dev/null
+++ b/deps/jemalloc/test/analyze/rand.c
@@ -0,0 +1,276 @@
+#include "test/jemalloc_test.h"
+
+/******************************************************************************/
+
+/*
+ * General purpose tool for examining random number distributions.
+ *
+ * Input -
+ * (a) a random number generator, and
+ * (b) the buckets:
+ * (1) number of buckets,
+ * (2) width of each bucket, in log scale,
+ * (3) expected mean and stddev of the count of random numbers in each
+ * bucket, and
+ * (c) number of iterations to invoke the generator.
+ *
+ * The program generates the specified amount of random numbers, and assess how
+ * well they conform to the expectations: for each bucket, output -
+ * (a) the (given) expected mean and stddev,
+ * (b) the actual count and any interesting level of deviation:
+ * (1) ~68% buckets should show no interesting deviation, meaning a
+ * deviation less than stddev from the expectation;
+ * (2) ~27% buckets should show '+' / '-', meaning a deviation in the range
+ * of [stddev, 2 * stddev) from the expectation;
+ * (3) ~4% buckets should show '++' / '--', meaning a deviation in the
+ * range of [2 * stddev, 3 * stddev) from the expectation; and
+ * (4) less than 0.3% buckets should show more than two '+'s / '-'s.
+ *
+ * Technical remarks:
+ * (a) The generator is expected to output uint64_t numbers, so you might need
+ * to define a wrapper.
+ * (b) The buckets must be of equal width and the lowest bucket starts at
+ * [0, 2^lg_bucket_width - 1).
+ * (c) Any generated number >= n_bucket * 2^lg_bucket_width will be counted
+ * towards the last bucket; the expected mean and stddev provided should
+ * also reflect that.
+ * (d) The number of iterations is advised to be determined so that the bucket
+ * with the minimal expected proportion gets a sufficient count.
+ */
+
+static void
+fill(size_t a[], const size_t n, const size_t k) {
+ for (size_t i = 0; i < n; ++i) {
+ a[i] = k;
+ }
+}
+
+static void
+collect_buckets(uint64_t (*gen)(void *), void *opaque, size_t buckets[],
+ const size_t n_bucket, const size_t lg_bucket_width, const size_t n_iter) {
+ for (size_t i = 0; i < n_iter; ++i) {
+ uint64_t num = gen(opaque);
+ uint64_t bucket_id = num >> lg_bucket_width;
+ if (bucket_id >= n_bucket) {
+ bucket_id = n_bucket - 1;
+ }
+ ++buckets[bucket_id];
+ }
+}
+
+static void
+print_buckets(const size_t buckets[], const size_t means[],
+ const size_t stddevs[], const size_t n_bucket) {
+ for (size_t i = 0; i < n_bucket; ++i) {
+ malloc_printf("%zu:\tmean = %zu,\tstddev = %zu,\tbucket = %zu",
+ i, means[i], stddevs[i], buckets[i]);
+
+ /* Make sure there's no overflow. */
+ assert(buckets[i] + stddevs[i] >= stddevs[i]);
+ assert(means[i] + stddevs[i] >= stddevs[i]);
+
+ if (buckets[i] + stddevs[i] <= means[i]) {
+ malloc_write(" ");
+ for (size_t t = means[i] - buckets[i]; t >= stddevs[i];
+ t -= stddevs[i]) {
+ malloc_write("-");
+ }
+ } else if (buckets[i] >= means[i] + stddevs[i]) {
+ malloc_write(" ");
+ for (size_t t = buckets[i] - means[i]; t >= stddevs[i];
+ t -= stddevs[i]) {
+ malloc_write("+");
+ }
+ }
+ malloc_write("\n");
+ }
+}
+
+static void
+bucket_analysis(uint64_t (*gen)(void *), void *opaque, size_t buckets[],
+ const size_t means[], const size_t stddevs[], const size_t n_bucket,
+ const size_t lg_bucket_width, const size_t n_iter) {
+ for (size_t i = 1; i <= 3; ++i) {
+ malloc_printf("round %zu\n", i);
+ fill(buckets, n_bucket, 0);
+ collect_buckets(gen, opaque, buckets, n_bucket,
+ lg_bucket_width, n_iter);
+ print_buckets(buckets, means, stddevs, n_bucket);
+ }
+}
+
+/* (Recommended) minimal bucket mean. */
+#define MIN_BUCKET_MEAN 10000
+
+/******************************************************************************/
+
+/* Uniform random number generator. */
+
+typedef struct uniform_gen_arg_s uniform_gen_arg_t;
+struct uniform_gen_arg_s {
+ uint64_t state;
+ const unsigned lg_range;
+};
+
+static uint64_t
+uniform_gen(void *opaque) {
+ uniform_gen_arg_t *arg = (uniform_gen_arg_t *)opaque;
+ return prng_lg_range_u64(&arg->state, arg->lg_range);
+}
+
+TEST_BEGIN(test_uniform) {
+#define LG_N_BUCKET 5
+#define N_BUCKET (1 << LG_N_BUCKET)
+
+#define QUOTIENT_CEIL(n, d) (((n) - 1) / (d) + 1)
+
+ const unsigned lg_range_test = 25;
+
+ /*
+ * Mathematical tricks to guarantee that both mean and stddev are
+ * integers, and that the minimal bucket mean is at least
+ * MIN_BUCKET_MEAN.
+ */
+ const size_t q = 1 << QUOTIENT_CEIL(LG_CEIL(QUOTIENT_CEIL(
+ MIN_BUCKET_MEAN, N_BUCKET * (N_BUCKET - 1))), 2);
+ const size_t stddev = (N_BUCKET - 1) * q;
+ const size_t mean = N_BUCKET * stddev * q;
+ const size_t n_iter = N_BUCKET * mean;
+
+ size_t means[N_BUCKET];
+ fill(means, N_BUCKET, mean);
+ size_t stddevs[N_BUCKET];
+ fill(stddevs, N_BUCKET, stddev);
+
+ uniform_gen_arg_t arg = {(uint64_t)(uintptr_t)&lg_range_test,
+ lg_range_test};
+ size_t buckets[N_BUCKET];
+ assert_zu_ge(lg_range_test, LG_N_BUCKET, "");
+ const size_t lg_bucket_width = lg_range_test - LG_N_BUCKET;
+
+ bucket_analysis(uniform_gen, &arg, buckets, means, stddevs,
+ N_BUCKET, lg_bucket_width, n_iter);
+
+#undef LG_N_BUCKET
+#undef N_BUCKET
+#undef QUOTIENT_CEIL
+}
+TEST_END
+
+/******************************************************************************/
+
+/* Geometric random number generator; compiled only when prof is on. */
+
+#ifdef JEMALLOC_PROF
+
+/*
+ * Fills geometric proportions and returns the minimal proportion. See
+ * comments in test_prof_sample for explanations for n_divide.
+ */
+static double
+fill_geometric_proportions(double proportions[], const size_t n_bucket,
+ const size_t n_divide) {
+ assert(n_bucket > 0);
+ assert(n_divide > 0);
+ double x = 1.;
+ for (size_t i = 0; i < n_bucket; ++i) {
+ if (i == n_bucket - 1) {
+ proportions[i] = x;
+ } else {
+ double y = x * exp(-1. / n_divide);
+ proportions[i] = x - y;
+ x = y;
+ }
+ }
+ /*
+ * The minimal proportion is the smaller one of the last two
+ * proportions for geometric distribution.
+ */
+ double min_proportion = proportions[n_bucket - 1];
+ if (n_bucket >= 2 && proportions[n_bucket - 2] < min_proportion) {
+ min_proportion = proportions[n_bucket - 2];
+ }
+ return min_proportion;
+}
+
+static size_t
+round_to_nearest(const double x) {
+ return (size_t)(x + .5);
+}
+
+static void
+fill_references(size_t means[], size_t stddevs[], const double proportions[],
+ const size_t n_bucket, const size_t n_iter) {
+ for (size_t i = 0; i < n_bucket; ++i) {
+ double x = n_iter * proportions[i];
+ means[i] = round_to_nearest(x);
+ stddevs[i] = round_to_nearest(sqrt(x * (1. - proportions[i])));
+ }
+}
+
+static uint64_t
+prof_sample_gen(void *opaque) {
+ return prof_sample_new_event_wait((tsd_t *)opaque) - 1;
+}
+
+#endif /* JEMALLOC_PROF */
+
+TEST_BEGIN(test_prof_sample) {
+ test_skip_if(!config_prof);
+#ifdef JEMALLOC_PROF
+
+/* Number of divisions within [0, mean). */
+#define LG_N_DIVIDE 3
+#define N_DIVIDE (1 << LG_N_DIVIDE)
+
+/* Coverage of buckets in terms of multiples of mean. */
+#define LG_N_MULTIPLY 2
+#define N_GEO_BUCKET (N_DIVIDE << LG_N_MULTIPLY)
+
+ test_skip_if(!opt_prof);
+
+ size_t lg_prof_sample_test = 25;
+
+ size_t lg_prof_sample_orig = lg_prof_sample;
+ assert_d_eq(mallctl("prof.reset", NULL, NULL, &lg_prof_sample_test,
+ sizeof(size_t)), 0, "");
+ malloc_printf("lg_prof_sample = %zu\n", lg_prof_sample_test);
+
+ double proportions[N_GEO_BUCKET + 1];
+ const double min_proportion = fill_geometric_proportions(proportions,
+ N_GEO_BUCKET + 1, N_DIVIDE);
+ const size_t n_iter = round_to_nearest(MIN_BUCKET_MEAN /
+ min_proportion);
+ size_t means[N_GEO_BUCKET + 1];
+ size_t stddevs[N_GEO_BUCKET + 1];
+ fill_references(means, stddevs, proportions, N_GEO_BUCKET + 1, n_iter);
+
+ tsd_t *tsd = tsd_fetch();
+ assert_ptr_not_null(tsd, "");
+ size_t buckets[N_GEO_BUCKET + 1];
+ assert_zu_ge(lg_prof_sample, LG_N_DIVIDE, "");
+ const size_t lg_bucket_width = lg_prof_sample - LG_N_DIVIDE;
+
+ bucket_analysis(prof_sample_gen, tsd, buckets, means, stddevs,
+ N_GEO_BUCKET + 1, lg_bucket_width, n_iter);
+
+ assert_d_eq(mallctl("prof.reset", NULL, NULL, &lg_prof_sample_orig,
+ sizeof(size_t)), 0, "");
+
+#undef LG_N_DIVIDE
+#undef N_DIVIDE
+#undef LG_N_MULTIPLY
+#undef N_GEO_BUCKET
+
+#endif /* JEMALLOC_PROF */
+}
+TEST_END
+
+/******************************************************************************/
+
+int
+main(void) {
+ return test_no_reentrancy(
+ test_uniform,
+ test_prof_sample);
+}
diff --git a/deps/jemalloc/test/analyze/sizes.c b/deps/jemalloc/test/analyze/sizes.c
new file mode 100644
index 000000000..44c9de5ed
--- /dev/null
+++ b/deps/jemalloc/test/analyze/sizes.c
@@ -0,0 +1,53 @@
+#include "test/jemalloc_test.h"
+
+#include <stdio.h>
+
+/*
+ * Print the sizes of various important core data structures. OK, I guess this
+ * isn't really a "stress" test, but it does give useful information about
+ * low-level performance characteristics, as the other things in this directory
+ * do.
+ */
+
+static void
+do_print(const char *name, size_t sz_bytes) {
+ const char *sizes[] = {"bytes", "KB", "MB", "GB", "TB", "PB", "EB",
+ "ZB"};
+ size_t sizes_max = sizeof(sizes)/sizeof(sizes[0]);
+
+ size_t ind = 0;
+ double sz = sz_bytes;
+ while (sz >= 1024 && ind < sizes_max - 1) {
+ sz /= 1024;
+ ind++;
+ }
+ if (ind == 0) {
+ printf("%-20s: %zu bytes\n", name, sz_bytes);
+ } else {
+ printf("%-20s: %f %s\n", name, sz, sizes[ind]);
+ }
+}
+
+int
+main() {
+#define P(type) \
+ do_print(#type, sizeof(type))
+ P(arena_t);
+ P(arena_stats_t);
+ P(base_t);
+ P(decay_t);
+ P(edata_t);
+ P(ecache_t);
+ P(eset_t);
+ P(malloc_mutex_t);
+ P(prof_tctx_t);
+ P(prof_gctx_t);
+ P(prof_tdata_t);
+ P(rtree_t);
+ P(rtree_leaf_elm_t);
+ P(slab_data_t);
+ P(tcache_t);
+ P(tcache_slow_t);
+ P(tsd_t);
+#undef P
+}
diff --git a/deps/jemalloc/test/include/test/arena_util.h b/deps/jemalloc/test/include/test/arena_util.h
new file mode 100644
index 000000000..9a41dacbd
--- /dev/null
+++ b/deps/jemalloc/test/include/test/arena_util.h
@@ -0,0 +1,155 @@
+static inline unsigned
+do_arena_create(ssize_t dirty_decay_ms, ssize_t muzzy_decay_ms) {
+ unsigned arena_ind;
+ size_t sz = sizeof(unsigned);
+ expect_d_eq(mallctl("arenas.create", (void *)&arena_ind, &sz, NULL, 0),
+ 0, "Unexpected mallctl() failure");
+ size_t mib[3];
+ size_t miblen = sizeof(mib)/sizeof(size_t);
+
+ expect_d_eq(mallctlnametomib("arena.0.dirty_decay_ms", mib, &miblen),
+ 0, "Unexpected mallctlnametomib() failure");
+ mib[1] = (size_t)arena_ind;
+ expect_d_eq(mallctlbymib(mib, miblen, NULL, NULL,
+ (void *)&dirty_decay_ms, sizeof(dirty_decay_ms)), 0,
+ "Unexpected mallctlbymib() failure");
+
+ expect_d_eq(mallctlnametomib("arena.0.muzzy_decay_ms", mib, &miblen),
+ 0, "Unexpected mallctlnametomib() failure");
+ mib[1] = (size_t)arena_ind;
+ expect_d_eq(mallctlbymib(mib, miblen, NULL, NULL,
+ (void *)&muzzy_decay_ms, sizeof(muzzy_decay_ms)), 0,
+ "Unexpected mallctlbymib() failure");
+
+ return arena_ind;
+}
+
+static inline void
+do_arena_destroy(unsigned arena_ind) {
+ /*
+ * For convenience, flush tcache in case there are cached items.
+ * However not assert success since the tcache may be disabled.
+ */
+ mallctl("thread.tcache.flush", NULL, NULL, NULL, 0);
+
+ size_t mib[3];
+ size_t miblen = sizeof(mib)/sizeof(size_t);
+ expect_d_eq(mallctlnametomib("arena.0.destroy", mib, &miblen), 0,
+ "Unexpected mallctlnametomib() failure");
+ mib[1] = (size_t)arena_ind;
+ expect_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0,
+ "Unexpected mallctlbymib() failure");
+}
+
+static inline void
+do_epoch(void) {
+ uint64_t epoch = 1;
+ expect_d_eq(mallctl("epoch", NULL, NULL, (void *)&epoch, sizeof(epoch)),
+ 0, "Unexpected mallctl() failure");
+}
+
+static inline void
+do_purge(unsigned arena_ind) {
+ size_t mib[3];
+ size_t miblen = sizeof(mib)/sizeof(size_t);
+ expect_d_eq(mallctlnametomib("arena.0.purge", mib, &miblen), 0,
+ "Unexpected mallctlnametomib() failure");
+ mib[1] = (size_t)arena_ind;
+ expect_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0,
+ "Unexpected mallctlbymib() failure");
+}
+
+static inline void
+do_decay(unsigned arena_ind) {
+ size_t mib[3];
+ size_t miblen = sizeof(mib)/sizeof(size_t);
+ expect_d_eq(mallctlnametomib("arena.0.decay", mib, &miblen), 0,
+ "Unexpected mallctlnametomib() failure");
+ mib[1] = (size_t)arena_ind;
+ expect_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0,
+ "Unexpected mallctlbymib() failure");
+}
+
+static inline uint64_t
+get_arena_npurge_impl(const char *mibname, unsigned arena_ind) {
+ size_t mib[4];
+ size_t miblen = sizeof(mib)/sizeof(size_t);
+ expect_d_eq(mallctlnametomib(mibname, mib, &miblen), 0,
+ "Unexpected mallctlnametomib() failure");
+ mib[2] = (size_t)arena_ind;
+ uint64_t npurge = 0;
+ size_t sz = sizeof(npurge);
+ expect_d_eq(mallctlbymib(mib, miblen, (void *)&npurge, &sz, NULL, 0),
+ config_stats ? 0 : ENOENT, "Unexpected mallctlbymib() failure");
+ return npurge;
+}
+
+static inline uint64_t
+get_arena_dirty_npurge(unsigned arena_ind) {
+ do_epoch();
+ return get_arena_npurge_impl("stats.arenas.0.dirty_npurge", arena_ind);
+}
+
+static inline uint64_t
+get_arena_dirty_purged(unsigned arena_ind) {
+ do_epoch();
+ return get_arena_npurge_impl("stats.arenas.0.dirty_purged", arena_ind);
+}
+
+static inline uint64_t
+get_arena_muzzy_npurge(unsigned arena_ind) {
+ do_epoch();
+ return get_arena_npurge_impl("stats.arenas.0.muzzy_npurge", arena_ind);
+}
+
+static inline uint64_t
+get_arena_npurge(unsigned arena_ind) {
+ do_epoch();
+ return get_arena_npurge_impl("stats.arenas.0.dirty_npurge", arena_ind) +
+ get_arena_npurge_impl("stats.arenas.0.muzzy_npurge", arena_ind);
+}
+
+static inline size_t
+get_arena_pdirty(unsigned arena_ind) {
+ do_epoch();
+ size_t mib[4];
+ size_t miblen = sizeof(mib)/sizeof(size_t);
+ expect_d_eq(mallctlnametomib("stats.arenas.0.pdirty", mib, &miblen), 0,
+ "Unexpected mallctlnametomib() failure");
+ mib[2] = (size_t)arena_ind;
+ size_t pdirty;
+ size_t sz = sizeof(pdirty);
+ expect_d_eq(mallctlbymib(mib, miblen, (void *)&pdirty, &sz, NULL, 0), 0,
+ "Unexpected mallctlbymib() failure");
+ return pdirty;
+}
+
+static inline size_t
+get_arena_pmuzzy(unsigned arena_ind) {
+ do_epoch();
+ size_t mib[4];
+ size_t miblen = sizeof(mib)/sizeof(size_t);
+ expect_d_eq(mallctlnametomib("stats.arenas.0.pmuzzy", mib, &miblen), 0,
+ "Unexpected mallctlnametomib() failure");
+ mib[2] = (size_t)arena_ind;
+ size_t pmuzzy;
+ size_t sz = sizeof(pmuzzy);
+ expect_d_eq(mallctlbymib(mib, miblen, (void *)&pmuzzy, &sz, NULL, 0), 0,
+ "Unexpected mallctlbymib() failure");
+ return pmuzzy;
+}
+
+static inline void *
+do_mallocx(size_t size, int flags) {
+ void *p = mallocx(size, flags);
+ expect_ptr_not_null(p, "Unexpected mallocx() failure");
+ return p;
+}
+
+static inline void
+generate_dirty(unsigned arena_ind, size_t size) {
+ int flags = MALLOCX_ARENA(arena_ind) | MALLOCX_TCACHE_NONE;
+ void *p = do_mallocx(size, flags);
+ dallocx(p, flags);
+}
+
diff --git a/deps/jemalloc/test/include/test/bench.h b/deps/jemalloc/test/include/test/bench.h
new file mode 100644
index 000000000..0397c9487
--- /dev/null
+++ b/deps/jemalloc/test/include/test/bench.h
@@ -0,0 +1,60 @@
+static inline void
+time_func(timedelta_t *timer, uint64_t nwarmup, uint64_t niter,
+ void (*func)(void)) {
+ uint64_t i;
+
+ for (i = 0; i < nwarmup; i++) {
+ func();
+ }
+ timer_start(timer);
+ for (i = 0; i < niter; i++) {
+ func();
+ }
+ timer_stop(timer);
+}
+
+#define FMT_NSECS_BUF_SIZE 100
+/* Print nanoseconds / iter into the buffer "buf". */
+static inline void
+fmt_nsecs(uint64_t usec, uint64_t iters, char *buf) {
+ uint64_t nsec = usec * 1000;
+ /* We'll display 3 digits after the decimal point. */
+ uint64_t nsec1000 = nsec * 1000;
+ uint64_t nsecs_per_iter1000 = nsec1000 / iters;
+ uint64_t intpart = nsecs_per_iter1000 / 1000;
+ uint64_t fracpart = nsecs_per_iter1000 % 1000;
+ malloc_snprintf(buf, FMT_NSECS_BUF_SIZE, "%"FMTu64".%03"FMTu64, intpart,
+ fracpart);
+}
+
+static inline void
+compare_funcs(uint64_t nwarmup, uint64_t niter, const char *name_a,
+ void (*func_a), const char *name_b, void (*func_b)) {
+ timedelta_t timer_a, timer_b;
+ char ratio_buf[6];
+ void *p;
+
+ p = mallocx(1, 0);
+ if (p == NULL) {
+ test_fail("Unexpected mallocx() failure");
+ return;
+ }
+
+ time_func(&timer_a, nwarmup, niter, func_a);
+ time_func(&timer_b, nwarmup, niter, func_b);
+
+ uint64_t usec_a = timer_usec(&timer_a);
+ char buf_a[FMT_NSECS_BUF_SIZE];
+ fmt_nsecs(usec_a, niter, buf_a);
+
+ uint64_t usec_b = timer_usec(&timer_b);
+ char buf_b[FMT_NSECS_BUF_SIZE];
+ fmt_nsecs(usec_b, niter, buf_b);
+
+ timer_ratio(&timer_a, &timer_b, ratio_buf, sizeof(ratio_buf));
+ malloc_printf("%"FMTu64" iterations, %s=%"FMTu64"us (%s ns/iter), "
+ "%s=%"FMTu64"us (%s ns/iter), ratio=1:%s\n",
+ niter, name_a, usec_a, buf_a, name_b, usec_b, buf_b, ratio_buf);
+
+ dallocx(p, 0);
+}
diff --git a/deps/jemalloc/test/include/test/bgthd.h b/deps/jemalloc/test/include/test/bgthd.h
new file mode 100644
index 000000000..4fa2395e5
--- /dev/null
+++ b/deps/jemalloc/test/include/test/bgthd.h
@@ -0,0 +1,17 @@
+/*
+ * Shared utility for checking if background_thread is enabled, which affects
+ * the purging behavior and assumptions in some tests.
+ */
+
+static inline bool
+is_background_thread_enabled(void) {
+ bool enabled;
+ size_t sz = sizeof(bool);
+ int ret = mallctl("background_thread", (void *)&enabled, &sz, NULL,0);
+ if (ret == ENOENT) {
+ return false;
+ }
+ assert_d_eq(ret, 0, "Unexpected mallctl error");
+
+ return enabled;
+}
diff --git a/deps/jemalloc/test/include/test/btalloc.h b/deps/jemalloc/test/include/test/btalloc.h
index 5877ea77e..8f3459936 100644
--- a/deps/jemalloc/test/include/test/btalloc.h
+++ b/deps/jemalloc/test/include/test/btalloc.h
@@ -25,6 +25,6 @@ btalloc_##n(size_t size, unsigned bits) { \
} \
} \
/* Intentionally sabotage tail call optimization. */ \
- assert_ptr_not_null(p, "Unexpected mallocx() failure"); \
+ expect_ptr_not_null(p, "Unexpected mallocx() failure"); \
return p; \
}
diff --git a/deps/jemalloc/test/include/test/extent_hooks.h b/deps/jemalloc/test/include/test/extent_hooks.h
index 1f0620154..aad0a46c4 100644
--- a/deps/jemalloc/test/include/test/extent_hooks.h
+++ b/deps/jemalloc/test/include/test/extent_hooks.h
@@ -86,9 +86,9 @@ extent_alloc_hook(extent_hooks_t *extent_hooks, void *new_addr, size_t size,
"*zero=%s, *commit=%s, arena_ind=%u)\n", __func__, extent_hooks,
new_addr, size, alignment, *zero ? "true" : "false", *commit ?
"true" : "false", arena_ind);
- assert_ptr_eq(extent_hooks, &hooks,
+ expect_ptr_eq(extent_hooks, &hooks,
"extent_hooks should be same as pointer used to set hooks");
- assert_ptr_eq(extent_hooks->alloc, extent_alloc_hook,
+ expect_ptr_eq(extent_hooks->alloc, extent_alloc_hook,
"Wrong hook function");
called_alloc = true;
if (!try_alloc) {
@@ -108,9 +108,9 @@ extent_dalloc_hook(extent_hooks_t *extent_hooks, void *addr, size_t size,
TRACE_HOOK("%s(extent_hooks=%p, addr=%p, size=%zu, committed=%s, "
"arena_ind=%u)\n", __func__, extent_hooks, addr, size, committed ?
"true" : "false", arena_ind);
- assert_ptr_eq(extent_hooks, &hooks,
+ expect_ptr_eq(extent_hooks, &hooks,
"extent_hooks should be same as pointer used to set hooks");
- assert_ptr_eq(extent_hooks->dalloc, extent_dalloc_hook,
+ expect_ptr_eq(extent_hooks->dalloc, extent_dalloc_hook,
"Wrong hook function");
called_dalloc = true;
if (!try_dalloc) {
@@ -127,9 +127,9 @@ extent_destroy_hook(extent_hooks_t *extent_hooks, void *addr, size_t size,
TRACE_HOOK("%s(extent_hooks=%p, addr=%p, size=%zu, committed=%s, "
"arena_ind=%u)\n", __func__, extent_hooks, addr, size, committed ?
"true" : "false", arena_ind);
- assert_ptr_eq(extent_hooks, &hooks,
+ expect_ptr_eq(extent_hooks, &hooks,
"extent_hooks should be same as pointer used to set hooks");
- assert_ptr_eq(extent_hooks->destroy, extent_destroy_hook,
+ expect_ptr_eq(extent_hooks->destroy, extent_destroy_hook,
"Wrong hook function");
called_destroy = true;
if (!try_destroy) {
@@ -147,9 +147,9 @@ extent_commit_hook(extent_hooks_t *extent_hooks, void *addr, size_t size,
TRACE_HOOK("%s(extent_hooks=%p, addr=%p, size=%zu, offset=%zu, "
"length=%zu, arena_ind=%u)\n", __func__, extent_hooks, addr, size,
offset, length, arena_ind);
- assert_ptr_eq(extent_hooks, &hooks,
+ expect_ptr_eq(extent_hooks, &hooks,
"extent_hooks should be same as pointer used to set hooks");
- assert_ptr_eq(extent_hooks->commit, extent_commit_hook,
+ expect_ptr_eq(extent_hooks->commit, extent_commit_hook,
"Wrong hook function");
called_commit = true;
if (!try_commit) {
@@ -169,9 +169,9 @@ extent_decommit_hook(extent_hooks_t *extent_hooks, void *addr, size_t size,
TRACE_HOOK("%s(extent_hooks=%p, addr=%p, size=%zu, offset=%zu, "
"length=%zu, arena_ind=%u)\n", __func__, extent_hooks, addr, size,
offset, length, arena_ind);
- assert_ptr_eq(extent_hooks, &hooks,
+ expect_ptr_eq(extent_hooks, &hooks,
"extent_hooks should be same as pointer used to set hooks");
- assert_ptr_eq(extent_hooks->decommit, extent_decommit_hook,
+ expect_ptr_eq(extent_hooks->decommit, extent_decommit_hook,
"Wrong hook function");
called_decommit = true;
if (!try_decommit) {
@@ -191,9 +191,9 @@ extent_purge_lazy_hook(extent_hooks_t *extent_hooks, void *addr, size_t size,
TRACE_HOOK("%s(extent_hooks=%p, addr=%p, size=%zu, offset=%zu, "
"length=%zu arena_ind=%u)\n", __func__, extent_hooks, addr, size,
offset, length, arena_ind);
- assert_ptr_eq(extent_hooks, &hooks,
+ expect_ptr_eq(extent_hooks, &hooks,
"extent_hooks should be same as pointer used to set hooks");
- assert_ptr_eq(extent_hooks->purge_lazy, extent_purge_lazy_hook,
+ expect_ptr_eq(extent_hooks->purge_lazy, extent_purge_lazy_hook,
"Wrong hook function");
called_purge_lazy = true;
if (!try_purge_lazy) {
@@ -214,9 +214,9 @@ extent_purge_forced_hook(extent_hooks_t *extent_hooks, void *addr, size_t size,
TRACE_HOOK("%s(extent_hooks=%p, addr=%p, size=%zu, offset=%zu, "
"length=%zu arena_ind=%u)\n", __func__, extent_hooks, addr, size,
offset, length, arena_ind);
- assert_ptr_eq(extent_hooks, &hooks,
+ expect_ptr_eq(extent_hooks, &hooks,
"extent_hooks should be same as pointer used to set hooks");
- assert_ptr_eq(extent_hooks->purge_forced, extent_purge_forced_hook,
+ expect_ptr_eq(extent_hooks->purge_forced, extent_purge_forced_hook,
"Wrong hook function");
called_purge_forced = true;
if (!try_purge_forced) {
@@ -238,9 +238,9 @@ extent_split_hook(extent_hooks_t *extent_hooks, void *addr, size_t size,
"size_b=%zu, committed=%s, arena_ind=%u)\n", __func__, extent_hooks,
addr, size, size_a, size_b, committed ? "true" : "false",
arena_ind);
- assert_ptr_eq(extent_hooks, &hooks,
+ expect_ptr_eq(extent_hooks, &hooks,
"extent_hooks should be same as pointer used to set hooks");
- assert_ptr_eq(extent_hooks->split, extent_split_hook,
+ expect_ptr_eq(extent_hooks->split, extent_split_hook,
"Wrong hook function");
called_split = true;
if (!try_split) {
@@ -262,11 +262,11 @@ extent_merge_hook(extent_hooks_t *extent_hooks, void *addr_a, size_t size_a,
"size_b=%zu, committed=%s, arena_ind=%u)\n", __func__, extent_hooks,
addr_a, size_a, addr_b, size_b, committed ? "true" : "false",
arena_ind);
- assert_ptr_eq(extent_hooks, &hooks,
+ expect_ptr_eq(extent_hooks, &hooks,
"extent_hooks should be same as pointer used to set hooks");
- assert_ptr_eq(extent_hooks->merge, extent_merge_hook,
+ expect_ptr_eq(extent_hooks->merge, extent_merge_hook,
"Wrong hook function");
- assert_ptr_eq((void *)((uintptr_t)addr_a + size_a), addr_b,
+ expect_ptr_eq((void *)((uintptr_t)addr_a + size_a), addr_b,
"Extents not mergeable");
called_merge = true;
if (!try_merge) {
@@ -284,6 +284,6 @@ extent_hooks_prep(void) {
size_t sz;
sz = sizeof(default_hooks);
- assert_d_eq(mallctl("arena.0.extent_hooks", (void *)&default_hooks, &sz,
+ expect_d_eq(mallctl("arena.0.extent_hooks", (void *)&default_hooks, &sz,
NULL, 0), 0, "Unexpected mallctl() error");
}
diff --git a/deps/jemalloc/test/include/test/jemalloc_test.h.in b/deps/jemalloc/test/include/test/jemalloc_test.h.in
index c46af5d9b..3f8c0da7f 100644
--- a/deps/jemalloc/test/include/test/jemalloc_test.h.in
+++ b/deps/jemalloc/test/include/test/jemalloc_test.h.in
@@ -38,9 +38,9 @@ extern "C" {
/******************************************************************************/
/*
- * For unit tests, expose all public and private interfaces.
+ * For unit tests and analytics tests, expose all public and private interfaces.
*/
-#ifdef JEMALLOC_UNIT_TEST
+#if defined(JEMALLOC_UNIT_TEST) || defined (JEMALLOC_ANALYZE_TEST)
# define JEMALLOC_JET
# define JEMALLOC_MANGLE
# include "jemalloc/internal/jemalloc_preamble.h"
@@ -124,12 +124,19 @@ static const bool config_debug =
#include "test/math.h"
#include "test/mtx.h"
#include "test/mq.h"
+#include "test/sleep.h"
#include "test/test.h"
#include "test/timer.h"
#include "test/thd.h"
+#include "test/bgthd.h"
#define MEXP 19937
#include "test/SFMT.h"
+#ifndef JEMALLOC_HAVE_MALLOC_SIZE
+#define TEST_MALLOC_SIZE malloc_usable_size
+#else
+#define TEST_MALLOC_SIZE malloc_size
+#endif
/******************************************************************************/
/*
* Define always-enabled assertion macros, so that test assertions execute even
@@ -138,7 +145,7 @@ static const bool config_debug =
#undef assert
#undef not_reached
#undef not_implemented
-#undef assert_not_implemented
+#undef expect_not_implemented
#define assert(e) do { \
if (!(e)) { \
@@ -162,7 +169,7 @@ static const bool config_debug =
abort(); \
} while (0)
-#define assert_not_implemented(e) do { \
+#define expect_not_implemented(e) do { \
if (!(e)) { \
not_implemented(); \
} \
diff --git a/deps/jemalloc/test/include/test/mq.h b/deps/jemalloc/test/include/test/mq.h
index af2c078da..5dc6486c7 100644
--- a/deps/jemalloc/test/include/test/mq.h
+++ b/deps/jemalloc/test/include/test/mq.h
@@ -1,4 +1,4 @@
-void mq_nanosleep(unsigned ns);
+#include "test/sleep.h"
/*
* Simple templated message queue implementation that relies on only mutexes for
@@ -82,7 +82,7 @@ a_prefix##get(a_mq_type *mq) { \
\
ns = 1; \
while (true) { \
- mq_nanosleep(ns); \
+ sleep_ns(ns); \
msg = a_prefix##tryget(mq); \
if (msg != NULL) { \
return msg; \
diff --git a/deps/jemalloc/test/include/test/nbits.h b/deps/jemalloc/test/include/test/nbits.h
new file mode 100644
index 000000000..c06cf1b4a
--- /dev/null
+++ b/deps/jemalloc/test/include/test/nbits.h
@@ -0,0 +1,111 @@
+#ifndef TEST_NBITS_H
+#define TEST_NBITS_H
+
+/* Interesting bitmap counts to test. */
+
+#define NBITS_TAB \
+ NB( 1) \
+ NB( 2) \
+ NB( 3) \
+ NB( 4) \
+ NB( 5) \
+ NB( 6) \
+ NB( 7) \
+ NB( 8) \
+ NB( 9) \
+ NB(10) \
+ NB(11) \
+ NB(12) \
+ NB(13) \
+ NB(14) \
+ NB(15) \
+ NB(16) \
+ NB(17) \
+ NB(18) \
+ NB(19) \
+ NB(20) \
+ NB(21) \
+ NB(22) \
+ NB(23) \
+ NB(24) \
+ NB(25) \
+ NB(26) \
+ NB(27) \
+ NB(28) \
+ NB(29) \
+ NB(30) \
+ NB(31) \
+ NB(32) \
+ \
+ NB(33) \
+ NB(34) \
+ NB(35) \
+ NB(36) \
+ NB(37) \
+ NB(38) \
+ NB(39) \
+ NB(40) \
+ NB(41) \
+ NB(42) \
+ NB(43) \
+ NB(44) \
+ NB(45) \
+ NB(46) \
+ NB(47) \
+ NB(48) \
+ NB(49) \
+ NB(50) \
+ NB(51) \
+ NB(52) \
+ NB(53) \
+ NB(54) \
+ NB(55) \
+ NB(56) \
+ NB(57) \
+ NB(58) \
+ NB(59) \
+ NB(60) \
+ NB(61) \
+ NB(62) \
+ NB(63) \
+ NB(64) \
+ NB(65) \
+ NB(66) \
+ NB(67) \
+ \
+ NB(126) \
+ NB(127) \
+ NB(128) \
+ NB(129) \
+ NB(130) \
+ \
+ NB(254) \
+ NB(255) \
+ NB(256) \
+ NB(257) \
+ NB(258) \
+ \
+ NB(510) \
+ NB(511) \
+ NB(512) \
+ NB(513) \
+ NB(514) \
+ \
+ NB(1022) \
+ NB(1023) \
+ NB(1024) \
+ NB(1025) \
+ NB(1026) \
+ \
+ NB(2048) \
+ \
+ NB(4094) \
+ NB(4095) \
+ NB(4096) \
+ NB(4097) \
+ NB(4098) \
+ \
+ NB(8192) \
+ NB(16384)
+
+#endif /* TEST_NBITS_H */
diff --git a/deps/jemalloc/test/include/test/san.h b/deps/jemalloc/test/include/test/san.h
new file mode 100644
index 000000000..da07865ce
--- /dev/null
+++ b/deps/jemalloc/test/include/test/san.h
@@ -0,0 +1,14 @@
+#if defined(JEMALLOC_UAF_DETECTION) || defined(JEMALLOC_DEBUG)
+# define TEST_SAN_UAF_ALIGN_ENABLE "lg_san_uaf_align:12"
+# define TEST_SAN_UAF_ALIGN_DISABLE "lg_san_uaf_align:-1"
+#else
+# define TEST_SAN_UAF_ALIGN_ENABLE ""
+# define TEST_SAN_UAF_ALIGN_DISABLE ""
+#endif
+
+static inline bool
+extent_is_guarded(tsdn_t *tsdn, void *ptr) {
+ edata_t *edata = emap_edata_lookup(tsdn, &arena_emap_global, ptr);
+ return edata_guarded_get(edata);
+}
+
diff --git a/deps/jemalloc/test/include/test/sleep.h b/deps/jemalloc/test/include/test/sleep.h
new file mode 100644
index 000000000..c232f6334
--- /dev/null
+++ b/deps/jemalloc/test/include/test/sleep.h
@@ -0,0 +1 @@
+void sleep_ns(unsigned ns);
diff --git a/deps/jemalloc/test/include/test/test.h b/deps/jemalloc/test/include/test/test.h
index fd0e5265d..d4b65912d 100644
--- a/deps/jemalloc/test/include/test/test.h
+++ b/deps/jemalloc/test/include/test/test.h
@@ -1,8 +1,8 @@
#define ASSERT_BUFSIZE 256
-#define assert_cmp(t, a, b, cmp, neg_cmp, pri, ...) do { \
- t a_ = (a); \
- t b_ = (b); \
+#define verify_cmp(may_abort, t, a, b, cmp, neg_cmp, pri, ...) do { \
+ const t a_ = (a); \
+ const t b_ = (b); \
if (!(a_ cmp b_)) { \
char prefix[ASSERT_BUFSIZE]; \
char message[ASSERT_BUFSIZE]; \
@@ -13,10 +13,316 @@
__func__, __FILE__, __LINE__, \
#a, #b, a_, b_); \
malloc_snprintf(message, sizeof(message), __VA_ARGS__); \
+ if (may_abort) { \
+ abort(); \
+ } else { \
+ p_test_fail(prefix, message); \
+ } \
+ } \
+} while (0)
+
+#define expect_cmp(t, a, b, cmp, neg_cmp, pri, ...) verify_cmp(false, \
+ t, a, b, cmp, neg_cmp, pri, __VA_ARGS__)
+
+#define expect_ptr_eq(a, b, ...) expect_cmp(void *, a, b, ==, \
+ !=, "p", __VA_ARGS__)
+#define expect_ptr_ne(a, b, ...) expect_cmp(void *, a, b, !=, \
+ ==, "p", __VA_ARGS__)
+#define expect_ptr_null(a, ...) expect_cmp(void *, a, NULL, ==, \
+ !=, "p", __VA_ARGS__)
+#define expect_ptr_not_null(a, ...) expect_cmp(void *, a, NULL, !=, \
+ ==, "p", __VA_ARGS__)
+
+#define expect_c_eq(a, b, ...) expect_cmp(char, a, b, ==, !=, "c", __VA_ARGS__)
+#define expect_c_ne(a, b, ...) expect_cmp(char, a, b, !=, ==, "c", __VA_ARGS__)
+#define expect_c_lt(a, b, ...) expect_cmp(char, a, b, <, >=, "c", __VA_ARGS__)
+#define expect_c_le(a, b, ...) expect_cmp(char, a, b, <=, >, "c", __VA_ARGS__)
+#define expect_c_ge(a, b, ...) expect_cmp(char, a, b, >=, <, "c", __VA_ARGS__)
+#define expect_c_gt(a, b, ...) expect_cmp(char, a, b, >, <=, "c", __VA_ARGS__)
+
+#define expect_x_eq(a, b, ...) expect_cmp(int, a, b, ==, !=, "#x", __VA_ARGS__)
+#define expect_x_ne(a, b, ...) expect_cmp(int, a, b, !=, ==, "#x", __VA_ARGS__)
+#define expect_x_lt(a, b, ...) expect_cmp(int, a, b, <, >=, "#x", __VA_ARGS__)
+#define expect_x_le(a, b, ...) expect_cmp(int, a, b, <=, >, "#x", __VA_ARGS__)
+#define expect_x_ge(a, b, ...) expect_cmp(int, a, b, >=, <, "#x", __VA_ARGS__)
+#define expect_x_gt(a, b, ...) expect_cmp(int, a, b, >, <=, "#x", __VA_ARGS__)
+
+#define expect_d_eq(a, b, ...) expect_cmp(int, a, b, ==, !=, "d", __VA_ARGS__)
+#define expect_d_ne(a, b, ...) expect_cmp(int, a, b, !=, ==, "d", __VA_ARGS__)
+#define expect_d_lt(a, b, ...) expect_cmp(int, a, b, <, >=, "d", __VA_ARGS__)
+#define expect_d_le(a, b, ...) expect_cmp(int, a, b, <=, >, "d", __VA_ARGS__)
+#define expect_d_ge(a, b, ...) expect_cmp(int, a, b, >=, <, "d", __VA_ARGS__)
+#define expect_d_gt(a, b, ...) expect_cmp(int, a, b, >, <=, "d", __VA_ARGS__)
+
+#define expect_u_eq(a, b, ...) expect_cmp(int, a, b, ==, !=, "u", __VA_ARGS__)
+#define expect_u_ne(a, b, ...) expect_cmp(int, a, b, !=, ==, "u", __VA_ARGS__)
+#define expect_u_lt(a, b, ...) expect_cmp(int, a, b, <, >=, "u", __VA_ARGS__)
+#define expect_u_le(a, b, ...) expect_cmp(int, a, b, <=, >, "u", __VA_ARGS__)
+#define expect_u_ge(a, b, ...) expect_cmp(int, a, b, >=, <, "u", __VA_ARGS__)
+#define expect_u_gt(a, b, ...) expect_cmp(int, a, b, >, <=, "u", __VA_ARGS__)
+
+#define expect_ld_eq(a, b, ...) expect_cmp(long, a, b, ==, \
+ !=, "ld", __VA_ARGS__)
+#define expect_ld_ne(a, b, ...) expect_cmp(long, a, b, !=, \
+ ==, "ld", __VA_ARGS__)
+#define expect_ld_lt(a, b, ...) expect_cmp(long, a, b, <, \
+ >=, "ld", __VA_ARGS__)
+#define expect_ld_le(a, b, ...) expect_cmp(long, a, b, <=, \
+ >, "ld", __VA_ARGS__)
+#define expect_ld_ge(a, b, ...) expect_cmp(long, a, b, >=, \
+ <, "ld", __VA_ARGS__)
+#define expect_ld_gt(a, b, ...) expect_cmp(long, a, b, >, \
+ <=, "ld", __VA_ARGS__)
+
+#define expect_lu_eq(a, b, ...) expect_cmp(unsigned long, \
+ a, b, ==, !=, "lu", __VA_ARGS__)
+#define expect_lu_ne(a, b, ...) expect_cmp(unsigned long, \
+ a, b, !=, ==, "lu", __VA_ARGS__)
+#define expect_lu_lt(a, b, ...) expect_cmp(unsigned long, \
+ a, b, <, >=, "lu", __VA_ARGS__)
+#define expect_lu_le(a, b, ...) expect_cmp(unsigned long, \
+ a, b, <=, >, "lu", __VA_ARGS__)
+#define expect_lu_ge(a, b, ...) expect_cmp(unsigned long, \
+ a, b, >=, <, "lu", __VA_ARGS__)
+#define expect_lu_gt(a, b, ...) expect_cmp(unsigned long, \
+ a, b, >, <=, "lu", __VA_ARGS__)
+
+#define expect_qd_eq(a, b, ...) expect_cmp(long long, a, b, ==, \
+ !=, "qd", __VA_ARGS__)
+#define expect_qd_ne(a, b, ...) expect_cmp(long long, a, b, !=, \
+ ==, "qd", __VA_ARGS__)
+#define expect_qd_lt(a, b, ...) expect_cmp(long long, a, b, <, \
+ >=, "qd", __VA_ARGS__)
+#define expect_qd_le(a, b, ...) expect_cmp(long long, a, b, <=, \
+ >, "qd", __VA_ARGS__)
+#define expect_qd_ge(a, b, ...) expect_cmp(long long, a, b, >=, \
+ <, "qd", __VA_ARGS__)
+#define expect_qd_gt(a, b, ...) expect_cmp(long long, a, b, >, \
+ <=, "qd", __VA_ARGS__)
+
+#define expect_qu_eq(a, b, ...) expect_cmp(unsigned long long, \
+ a, b, ==, !=, "qu", __VA_ARGS__)
+#define expect_qu_ne(a, b, ...) expect_cmp(unsigned long long, \
+ a, b, !=, ==, "qu", __VA_ARGS__)
+#define expect_qu_lt(a, b, ...) expect_cmp(unsigned long long, \
+ a, b, <, >=, "qu", __VA_ARGS__)
+#define expect_qu_le(a, b, ...) expect_cmp(unsigned long long, \
+ a, b, <=, >, "qu", __VA_ARGS__)
+#define expect_qu_ge(a, b, ...) expect_cmp(unsigned long long, \
+ a, b, >=, <, "qu", __VA_ARGS__)
+#define expect_qu_gt(a, b, ...) expect_cmp(unsigned long long, \
+ a, b, >, <=, "qu", __VA_ARGS__)
+
+#define expect_jd_eq(a, b, ...) expect_cmp(intmax_t, a, b, ==, \
+ !=, "jd", __VA_ARGS__)
+#define expect_jd_ne(a, b, ...) expect_cmp(intmax_t, a, b, !=, \
+ ==, "jd", __VA_ARGS__)
+#define expect_jd_lt(a, b, ...) expect_cmp(intmax_t, a, b, <, \
+ >=, "jd", __VA_ARGS__)
+#define expect_jd_le(a, b, ...) expect_cmp(intmax_t, a, b, <=, \
+ >, "jd", __VA_ARGS__)
+#define expect_jd_ge(a, b, ...) expect_cmp(intmax_t, a, b, >=, \
+ <, "jd", __VA_ARGS__)
+#define expect_jd_gt(a, b, ...) expect_cmp(intmax_t, a, b, >, \
+ <=, "jd", __VA_ARGS__)
+
+#define expect_ju_eq(a, b, ...) expect_cmp(uintmax_t, a, b, ==, \
+ !=, "ju", __VA_ARGS__)
+#define expect_ju_ne(a, b, ...) expect_cmp(uintmax_t, a, b, !=, \
+ ==, "ju", __VA_ARGS__)
+#define expect_ju_lt(a, b, ...) expect_cmp(uintmax_t, a, b, <, \
+ >=, "ju", __VA_ARGS__)
+#define expect_ju_le(a, b, ...) expect_cmp(uintmax_t, a, b, <=, \
+ >, "ju", __VA_ARGS__)
+#define expect_ju_ge(a, b, ...) expect_cmp(uintmax_t, a, b, >=, \
+ <, "ju", __VA_ARGS__)
+#define expect_ju_gt(a, b, ...) expect_cmp(uintmax_t, a, b, >, \
+ <=, "ju", __VA_ARGS__)
+
+#define expect_zd_eq(a, b, ...) expect_cmp(ssize_t, a, b, ==, \
+ !=, "zd", __VA_ARGS__)
+#define expect_zd_ne(a, b, ...) expect_cmp(ssize_t, a, b, !=, \
+ ==, "zd", __VA_ARGS__)
+#define expect_zd_lt(a, b, ...) expect_cmp(ssize_t, a, b, <, \
+ >=, "zd", __VA_ARGS__)
+#define expect_zd_le(a, b, ...) expect_cmp(ssize_t, a, b, <=, \
+ >, "zd", __VA_ARGS__)
+#define expect_zd_ge(a, b, ...) expect_cmp(ssize_t, a, b, >=, \
+ <, "zd", __VA_ARGS__)
+#define expect_zd_gt(a, b, ...) expect_cmp(ssize_t, a, b, >, \
+ <=, "zd", __VA_ARGS__)
+
+#define expect_zu_eq(a, b, ...) expect_cmp(size_t, a, b, ==, \
+ !=, "zu", __VA_ARGS__)
+#define expect_zu_ne(a, b, ...) expect_cmp(size_t, a, b, !=, \
+ ==, "zu", __VA_ARGS__)
+#define expect_zu_lt(a, b, ...) expect_cmp(size_t, a, b, <, \
+ >=, "zu", __VA_ARGS__)
+#define expect_zu_le(a, b, ...) expect_cmp(size_t, a, b, <=, \
+ >, "zu", __VA_ARGS__)
+#define expect_zu_ge(a, b, ...) expect_cmp(size_t, a, b, >=, \
+ <, "zu", __VA_ARGS__)
+#define expect_zu_gt(a, b, ...) expect_cmp(size_t, a, b, >, \
+ <=, "zu", __VA_ARGS__)
+
+#define expect_d32_eq(a, b, ...) expect_cmp(int32_t, a, b, ==, \
+ !=, FMTd32, __VA_ARGS__)
+#define expect_d32_ne(a, b, ...) expect_cmp(int32_t, a, b, !=, \
+ ==, FMTd32, __VA_ARGS__)
+#define expect_d32_lt(a, b, ...) expect_cmp(int32_t, a, b, <, \
+ >=, FMTd32, __VA_ARGS__)
+#define expect_d32_le(a, b, ...) expect_cmp(int32_t, a, b, <=, \
+ >, FMTd32, __VA_ARGS__)
+#define expect_d32_ge(a, b, ...) expect_cmp(int32_t, a, b, >=, \
+ <, FMTd32, __VA_ARGS__)
+#define expect_d32_gt(a, b, ...) expect_cmp(int32_t, a, b, >, \
+ <=, FMTd32, __VA_ARGS__)
+
+#define expect_u32_eq(a, b, ...) expect_cmp(uint32_t, a, b, ==, \
+ !=, FMTu32, __VA_ARGS__)
+#define expect_u32_ne(a, b, ...) expect_cmp(uint32_t, a, b, !=, \
+ ==, FMTu32, __VA_ARGS__)
+#define expect_u32_lt(a, b, ...) expect_cmp(uint32_t, a, b, <, \
+ >=, FMTu32, __VA_ARGS__)
+#define expect_u32_le(a, b, ...) expect_cmp(uint32_t, a, b, <=, \
+ >, FMTu32, __VA_ARGS__)
+#define expect_u32_ge(a, b, ...) expect_cmp(uint32_t, a, b, >=, \
+ <, FMTu32, __VA_ARGS__)
+#define expect_u32_gt(a, b, ...) expect_cmp(uint32_t, a, b, >, \
+ <=, FMTu32, __VA_ARGS__)
+
+#define expect_d64_eq(a, b, ...) expect_cmp(int64_t, a, b, ==, \
+ !=, FMTd64, __VA_ARGS__)
+#define expect_d64_ne(a, b, ...) expect_cmp(int64_t, a, b, !=, \
+ ==, FMTd64, __VA_ARGS__)
+#define expect_d64_lt(a, b, ...) expect_cmp(int64_t, a, b, <, \
+ >=, FMTd64, __VA_ARGS__)
+#define expect_d64_le(a, b, ...) expect_cmp(int64_t, a, b, <=, \
+ >, FMTd64, __VA_ARGS__)
+#define expect_d64_ge(a, b, ...) expect_cmp(int64_t, a, b, >=, \
+ <, FMTd64, __VA_ARGS__)
+#define expect_d64_gt(a, b, ...) expect_cmp(int64_t, a, b, >, \
+ <=, FMTd64, __VA_ARGS__)
+
+#define expect_u64_eq(a, b, ...) expect_cmp(uint64_t, a, b, ==, \
+ !=, FMTu64, __VA_ARGS__)
+#define expect_u64_ne(a, b, ...) expect_cmp(uint64_t, a, b, !=, \
+ ==, FMTu64, __VA_ARGS__)
+#define expect_u64_lt(a, b, ...) expect_cmp(uint64_t, a, b, <, \
+ >=, FMTu64, __VA_ARGS__)
+#define expect_u64_le(a, b, ...) expect_cmp(uint64_t, a, b, <=, \
+ >, FMTu64, __VA_ARGS__)
+#define expect_u64_ge(a, b, ...) expect_cmp(uint64_t, a, b, >=, \
+ <, FMTu64, __VA_ARGS__)
+#define expect_u64_gt(a, b, ...) expect_cmp(uint64_t, a, b, >, \
+ <=, FMTu64, __VA_ARGS__)
+
+#define verify_b_eq(may_abort, a, b, ...) do { \
+ bool a_ = (a); \
+ bool b_ = (b); \
+ if (!(a_ == b_)) { \
+ char prefix[ASSERT_BUFSIZE]; \
+ char message[ASSERT_BUFSIZE]; \
+ malloc_snprintf(prefix, sizeof(prefix), \
+ "%s:%s:%d: Failed assertion: " \
+ "(%s) == (%s) --> %s != %s: ", \
+ __func__, __FILE__, __LINE__, \
+ #a, #b, a_ ? "true" : "false", \
+ b_ ? "true" : "false"); \
+ malloc_snprintf(message, sizeof(message), __VA_ARGS__); \
+ if (may_abort) { \
+ abort(); \
+ } else { \
+ p_test_fail(prefix, message); \
+ } \
+ } \
+} while (0)
+
+#define verify_b_ne(may_abort, a, b, ...) do { \
+ bool a_ = (a); \
+ bool b_ = (b); \
+ if (!(a_ != b_)) { \
+ char prefix[ASSERT_BUFSIZE]; \
+ char message[ASSERT_BUFSIZE]; \
+ malloc_snprintf(prefix, sizeof(prefix), \
+ "%s:%s:%d: Failed assertion: " \
+ "(%s) != (%s) --> %s == %s: ", \
+ __func__, __FILE__, __LINE__, \
+ #a, #b, a_ ? "true" : "false", \
+ b_ ? "true" : "false"); \
+ malloc_snprintf(message, sizeof(message), __VA_ARGS__); \
+ if (may_abort) { \
+ abort(); \
+ } else { \
+ p_test_fail(prefix, message); \
+ } \
+ } \
+} while (0)
+
+#define expect_b_eq(a, b, ...) verify_b_eq(false, a, b, __VA_ARGS__)
+#define expect_b_ne(a, b, ...) verify_b_ne(false, a, b, __VA_ARGS__)
+
+#define expect_true(a, ...) expect_b_eq(a, true, __VA_ARGS__)
+#define expect_false(a, ...) expect_b_eq(a, false, __VA_ARGS__)
+
+#define verify_str_eq(may_abort, a, b, ...) do { \
+ if (strcmp((a), (b))) { \
+ char prefix[ASSERT_BUFSIZE]; \
+ char message[ASSERT_BUFSIZE]; \
+ malloc_snprintf(prefix, sizeof(prefix), \
+ "%s:%s:%d: Failed assertion: " \
+ "(%s) same as (%s) --> " \
+ "\"%s\" differs from \"%s\": ", \
+ __func__, __FILE__, __LINE__, #a, #b, a, b); \
+ malloc_snprintf(message, sizeof(message), __VA_ARGS__); \
+ if (may_abort) { \
+ abort(); \
+ } else { \
+ p_test_fail(prefix, message); \
+ } \
+ } \
+} while (0)
+
+#define verify_str_ne(may_abort, a, b, ...) do { \
+ if (!strcmp((a), (b))) { \
+ char prefix[ASSERT_BUFSIZE]; \
+ char message[ASSERT_BUFSIZE]; \
+ malloc_snprintf(prefix, sizeof(prefix), \
+ "%s:%s:%d: Failed assertion: " \
+ "(%s) differs from (%s) --> " \
+ "\"%s\" same as \"%s\": ", \
+ __func__, __FILE__, __LINE__, #a, #b, a, b); \
+ malloc_snprintf(message, sizeof(message), __VA_ARGS__); \
+ if (may_abort) { \
+ abort(); \
+ } else { \
+ p_test_fail(prefix, message); \
+ } \
+ } \
+} while (0)
+
+#define expect_str_eq(a, b, ...) verify_str_eq(false, a, b, __VA_ARGS__)
+#define expect_str_ne(a, b, ...) verify_str_ne(false, a, b, __VA_ARGS__)
+
+#define verify_not_reached(may_abort, ...) do { \
+ char prefix[ASSERT_BUFSIZE]; \
+ char message[ASSERT_BUFSIZE]; \
+ malloc_snprintf(prefix, sizeof(prefix), \
+ "%s:%s:%d: Unreachable code reached: ", \
+ __func__, __FILE__, __LINE__); \
+ malloc_snprintf(message, sizeof(message), __VA_ARGS__); \
+ if (may_abort) { \
+ abort(); \
+ } else { \
p_test_fail(prefix, message); \
} \
} while (0)
+#define expect_not_reached(...) verify_not_reached(false, __VA_ARGS__)
+
+#define assert_cmp(t, a, b, cmp, neg_cmp, pri, ...) verify_cmp(true, \
+ t, a, b, cmp, neg_cmp, pri, __VA_ARGS__)
+
#define assert_ptr_eq(a, b, ...) assert_cmp(void *, a, b, ==, \
!=, "p", __VA_ARGS__)
#define assert_ptr_ne(a, b, ...) assert_cmp(void *, a, b, !=, \
@@ -210,77 +516,16 @@
#define assert_u64_gt(a, b, ...) assert_cmp(uint64_t, a, b, >, \
<=, FMTu64, __VA_ARGS__)
-#define assert_b_eq(a, b, ...) do { \
- bool a_ = (a); \
- bool b_ = (b); \
- if (!(a_ == b_)) { \
- char prefix[ASSERT_BUFSIZE]; \
- char message[ASSERT_BUFSIZE]; \
- malloc_snprintf(prefix, sizeof(prefix), \
- "%s:%s:%d: Failed assertion: " \
- "(%s) == (%s) --> %s != %s: ", \
- __func__, __FILE__, __LINE__, \
- #a, #b, a_ ? "true" : "false", \
- b_ ? "true" : "false"); \
- malloc_snprintf(message, sizeof(message), __VA_ARGS__); \
- p_test_fail(prefix, message); \
- } \
-} while (0)
-#define assert_b_ne(a, b, ...) do { \
- bool a_ = (a); \
- bool b_ = (b); \
- if (!(a_ != b_)) { \
- char prefix[ASSERT_BUFSIZE]; \
- char message[ASSERT_BUFSIZE]; \
- malloc_snprintf(prefix, sizeof(prefix), \
- "%s:%s:%d: Failed assertion: " \
- "(%s) != (%s) --> %s == %s: ", \
- __func__, __FILE__, __LINE__, \
- #a, #b, a_ ? "true" : "false", \
- b_ ? "true" : "false"); \
- malloc_snprintf(message, sizeof(message), __VA_ARGS__); \
- p_test_fail(prefix, message); \
- } \
-} while (0)
+#define assert_b_eq(a, b, ...) verify_b_eq(true, a, b, __VA_ARGS__)
+#define assert_b_ne(a, b, ...) verify_b_ne(true, a, b, __VA_ARGS__)
+
#define assert_true(a, ...) assert_b_eq(a, true, __VA_ARGS__)
#define assert_false(a, ...) assert_b_eq(a, false, __VA_ARGS__)
-#define assert_str_eq(a, b, ...) do { \
- if (strcmp((a), (b))) { \
- char prefix[ASSERT_BUFSIZE]; \
- char message[ASSERT_BUFSIZE]; \
- malloc_snprintf(prefix, sizeof(prefix), \
- "%s:%s:%d: Failed assertion: " \
- "(%s) same as (%s) --> " \
- "\"%s\" differs from \"%s\": ", \
- __func__, __FILE__, __LINE__, #a, #b, a, b); \
- malloc_snprintf(message, sizeof(message), __VA_ARGS__); \
- p_test_fail(prefix, message); \
- } \
-} while (0)
-#define assert_str_ne(a, b, ...) do { \
- if (!strcmp((a), (b))) { \
- char prefix[ASSERT_BUFSIZE]; \
- char message[ASSERT_BUFSIZE]; \
- malloc_snprintf(prefix, sizeof(prefix), \
- "%s:%s:%d: Failed assertion: " \
- "(%s) differs from (%s) --> " \
- "\"%s\" same as \"%s\": ", \
- __func__, __FILE__, __LINE__, #a, #b, a, b); \
- malloc_snprintf(message, sizeof(message), __VA_ARGS__); \
- p_test_fail(prefix, message); \
- } \
-} while (0)
+#define assert_str_eq(a, b, ...) verify_str_eq(true, a, b, __VA_ARGS__)
+#define assert_str_ne(a, b, ...) verify_str_ne(true, a, b, __VA_ARGS__)
-#define assert_not_reached(...) do { \
- char prefix[ASSERT_BUFSIZE]; \
- char message[ASSERT_BUFSIZE]; \
- malloc_snprintf(prefix, sizeof(prefix), \
- "%s:%s:%d: Unreachable code reached: ", \
- __func__, __FILE__, __LINE__); \
- malloc_snprintf(message, sizeof(message), __VA_ARGS__); \
- p_test_fail(prefix, message); \
-} while (0)
+#define assert_not_reached(...) verify_not_reached(true, __VA_ARGS__)
/*
* If this enum changes, corresponding changes in test/test.sh.in are also
diff --git a/deps/jemalloc/test/integration/MALLOCX_ARENA.c b/deps/jemalloc/test/integration/MALLOCX_ARENA.c
index 222164d69..7e61df082 100644
--- a/deps/jemalloc/test/integration/MALLOCX_ARENA.c
+++ b/deps/jemalloc/test/integration/MALLOCX_ARENA.c
@@ -18,7 +18,7 @@ thd_start(void *arg) {
size_t sz;
sz = sizeof(arena_ind);
- assert_d_eq(mallctl("arenas.create", (void *)&arena_ind, &sz, NULL, 0),
+ expect_d_eq(mallctl("arenas.create", (void *)&arena_ind, &sz, NULL, 0),
0, "Error in arenas.create");
if (thread_ind % 4 != 3) {
@@ -29,16 +29,16 @@ thd_start(void *arg) {
(sizeof(dss_precs)/sizeof(char*));
const char *dss = dss_precs[prec_ind];
int expected_err = (have_dss || prec_ind == 0) ? 0 : EFAULT;
- assert_d_eq(mallctlnametomib("arena.0.dss", mib, &miblen), 0,
+ expect_d_eq(mallctlnametomib("arena.0.dss", mib, &miblen), 0,
"Error in mallctlnametomib()");
mib[1] = arena_ind;
- assert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, (void *)&dss,
+ expect_d_eq(mallctlbymib(mib, miblen, NULL, NULL, (void *)&dss,
sizeof(const char *)), expected_err,
"Error in mallctlbymib()");
}
p = mallocx(1, MALLOCX_ARENA(arena_ind));
- assert_ptr_not_null(p, "Unexpected mallocx() error");
+ expect_ptr_not_null(p, "Unexpected mallocx() error");
dallocx(p, 0);
return NULL;
diff --git a/deps/jemalloc/test/integration/aligned_alloc.c b/deps/jemalloc/test/integration/aligned_alloc.c
index 4375b172a..b37d5ba0b 100644
--- a/deps/jemalloc/test/integration/aligned_alloc.c
+++ b/deps/jemalloc/test/integration/aligned_alloc.c
@@ -9,7 +9,7 @@
*/
static void
purge(void) {
- assert_d_eq(mallctl("arena.0.purge", NULL, NULL, NULL, 0), 0,
+ expect_d_eq(mallctl("arena.0.purge", NULL, NULL, NULL, 0), 0,
"Unexpected mallctl error");
}
@@ -20,14 +20,14 @@ TEST_BEGIN(test_alignment_errors) {
alignment = 0;
set_errno(0);
p = aligned_alloc(alignment, 1);
- assert_false(p != NULL || get_errno() != EINVAL,
+ expect_false(p != NULL || get_errno() != EINVAL,
"Expected error for invalid alignment %zu", alignment);
for (alignment = sizeof(size_t); alignment < MAXALIGN;
alignment <<= 1) {
set_errno(0);
p = aligned_alloc(alignment + 1, 1);
- assert_false(p != NULL || get_errno() != EINVAL,
+ expect_false(p != NULL || get_errno() != EINVAL,
"Expected error for invalid alignment %zu",
alignment + 1);
}
@@ -58,7 +58,7 @@ TEST_BEGIN(test_oom_errors) {
#endif
set_errno(0);
p = aligned_alloc(alignment, size);
- assert_false(p != NULL || get_errno() != ENOMEM,
+ expect_false(p != NULL || get_errno() != ENOMEM,
"Expected error for aligned_alloc(%zu, %zu)",
alignment, size);
@@ -71,7 +71,7 @@ TEST_BEGIN(test_oom_errors) {
#endif
set_errno(0);
p = aligned_alloc(alignment, size);
- assert_false(p != NULL || get_errno() != ENOMEM,
+ expect_false(p != NULL || get_errno() != ENOMEM,
"Expected error for aligned_alloc(%zu, %zu)",
alignment, size);
@@ -83,7 +83,7 @@ TEST_BEGIN(test_oom_errors) {
#endif
set_errno(0);
p = aligned_alloc(alignment, size);
- assert_false(p != NULL || get_errno() != ENOMEM,
+ expect_false(p != NULL || get_errno() != ENOMEM,
"Expected error for aligned_alloc(&p, %zu, %zu)",
alignment, size);
}
@@ -120,7 +120,7 @@ TEST_BEGIN(test_alignment_and_size) {
"size=%zu (%#zx): %s",
alignment, size, size, buf);
}
- total += malloc_usable_size(ps[i]);
+ total += TEST_MALLOC_SIZE(ps[i]);
if (total >= (MAXALIGN << 1)) {
break;
}
@@ -141,7 +141,7 @@ TEST_END
TEST_BEGIN(test_zero_alloc) {
void *res = aligned_alloc(8, 0);
assert(res);
- size_t usable = malloc_usable_size(res);
+ size_t usable = TEST_MALLOC_SIZE(res);
assert(usable > 0);
free(res);
}
diff --git a/deps/jemalloc/test/integration/allocated.c b/deps/jemalloc/test/integration/allocated.c
index 1425fd0aa..0c64272ce 100644
--- a/deps/jemalloc/test/integration/allocated.c
+++ b/deps/jemalloc/test/integration/allocated.c
@@ -32,7 +32,7 @@ thd_start(void *arg) {
test_fail("%s(): Error in mallctl(): %s", __func__,
strerror(err));
}
- assert_u64_eq(*ap0, a0,
+ expect_u64_eq(*ap0, a0,
"\"thread.allocatedp\" should provide a pointer to internal "
"storage");
@@ -53,25 +53,25 @@ thd_start(void *arg) {
test_fail("%s(): Error in mallctl(): %s", __func__,
strerror(err));
}
- assert_u64_eq(*dp0, d0,
+ expect_u64_eq(*dp0, d0,
"\"thread.deallocatedp\" should provide a pointer to internal "
"storage");
p = malloc(1);
- assert_ptr_not_null(p, "Unexpected malloc() error");
+ expect_ptr_not_null(p, "Unexpected malloc() error");
sz = sizeof(a1);
mallctl("thread.allocated", (void *)&a1, &sz, NULL, 0);
sz = sizeof(ap1);
mallctl("thread.allocatedp", (void *)&ap1, &sz, NULL, 0);
- assert_u64_eq(*ap1, a1,
+ expect_u64_eq(*ap1, a1,
"Dereferenced \"thread.allocatedp\" value should equal "
"\"thread.allocated\" value");
- assert_ptr_eq(ap0, ap1,
+ expect_ptr_eq(ap0, ap1,
"Pointer returned by \"thread.allocatedp\" should not change");
- usize = malloc_usable_size(p);
- assert_u64_le(a0 + usize, a1,
+ usize = TEST_MALLOC_SIZE(p);
+ expect_u64_le(a0 + usize, a1,
"Allocated memory counter should increase by at least the amount "
"explicitly allocated");
@@ -81,19 +81,19 @@ thd_start(void *arg) {
mallctl("thread.deallocated", (void *)&d1, &sz, NULL, 0);
sz = sizeof(dp1);
mallctl("thread.deallocatedp", (void *)&dp1, &sz, NULL, 0);
- assert_u64_eq(*dp1, d1,
+ expect_u64_eq(*dp1, d1,
"Dereferenced \"thread.deallocatedp\" value should equal "
"\"thread.deallocated\" value");
- assert_ptr_eq(dp0, dp1,
+ expect_ptr_eq(dp0, dp1,
"Pointer returned by \"thread.deallocatedp\" should not change");
- assert_u64_le(d0 + usize, d1,
+ expect_u64_le(d0 + usize, d1,
"Deallocated memory counter should increase by at least the amount "
"explicitly deallocated");
return NULL;
label_ENOENT:
- assert_false(config_stats,
+ expect_false(config_stats,
"ENOENT should only be returned if stats are disabled");
test_skip("\"thread.allocated\" mallctl not available");
return NULL;
diff --git a/deps/jemalloc/test/integration/cpp/basic.cpp b/deps/jemalloc/test/integration/cpp/basic.cpp
index 65890ecd5..c1cf6cd87 100644
--- a/deps/jemalloc/test/integration/cpp/basic.cpp
+++ b/deps/jemalloc/test/integration/cpp/basic.cpp
@@ -1,16 +1,15 @@
-#include <memory>
#include "test/jemalloc_test.h"
TEST_BEGIN(test_basic) {
auto foo = new long(4);
- assert_ptr_not_null(foo, "Unexpected new[] failure");
+ expect_ptr_not_null(foo, "Unexpected new[] failure");
delete foo;
// Test nullptr handling.
foo = nullptr;
delete foo;
auto bar = new long;
- assert_ptr_not_null(bar, "Unexpected new failure");
+ expect_ptr_not_null(bar, "Unexpected new failure");
delete bar;
// Test nullptr handling.
bar = nullptr;
diff --git a/deps/jemalloc/test/integration/cpp/infallible_new_false.cpp b/deps/jemalloc/test/integration/cpp/infallible_new_false.cpp
new file mode 100644
index 000000000..42196d6ad
--- /dev/null
+++ b/deps/jemalloc/test/integration/cpp/infallible_new_false.cpp
@@ -0,0 +1,23 @@
+#include <memory>
+
+#include "test/jemalloc_test.h"
+
+TEST_BEGIN(test_failing_alloc) {
+ bool saw_exception = false;
+ try {
+ /* Too big of an allocation to succeed. */
+ void *volatile ptr = ::operator new((size_t)-1);
+ (void)ptr;
+ } catch (...) {
+ saw_exception = true;
+ }
+ expect_true(saw_exception, "Didn't get a failure");
+}
+TEST_END
+
+int
+main(void) {
+ return test(
+ test_failing_alloc);
+}
+
diff --git a/deps/jemalloc/test/integration/cpp/infallible_new_false.sh b/deps/jemalloc/test/integration/cpp/infallible_new_false.sh
new file mode 100644
index 000000000..7d41812ce
--- /dev/null
+++ b/deps/jemalloc/test/integration/cpp/infallible_new_false.sh
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+XMALLOC_STR=""
+if [ "x${enable_xmalloc}" = "x1" ] ; then
+ XMALLOC_STR="xmalloc:false,"
+fi
+
+export MALLOC_CONF="${XMALLOC_STR}experimental_infallible_new:false"
diff --git a/deps/jemalloc/test/integration/cpp/infallible_new_true.cpp b/deps/jemalloc/test/integration/cpp/infallible_new_true.cpp
new file mode 100644
index 000000000..d67541281
--- /dev/null
+++ b/deps/jemalloc/test/integration/cpp/infallible_new_true.cpp
@@ -0,0 +1,67 @@
+#include <stdio.h>
+
+#include "test/jemalloc_test.h"
+
+/*
+ * We can't test C++ in unit tests. In order to intercept abort, use a secret
+ * safety check abort hook in integration tests.
+ */
+typedef void (*abort_hook_t)(const char *message);
+bool fake_abort_called;
+void fake_abort(const char *message) {
+ if (strcmp(message, "<jemalloc>: Allocation failed and "
+ "opt.experimental_infallible_new is true. Aborting.\n") != 0) {
+ abort();
+ }
+ fake_abort_called = true;
+}
+
+static bool
+own_operator_new(void) {
+ uint64_t before, after;
+ size_t sz = sizeof(before);
+
+ /* thread.allocated is always available, even w/o config_stats. */
+ expect_d_eq(mallctl("thread.allocated", (void *)&before, &sz, NULL, 0),
+ 0, "Unexpected mallctl failure reading stats");
+ void *volatile ptr = ::operator new((size_t)8);
+ expect_ptr_not_null(ptr, "Unexpected allocation failure");
+ expect_d_eq(mallctl("thread.allocated", (void *)&after, &sz, NULL, 0),
+ 0, "Unexpected mallctl failure reading stats");
+
+ return (after != before);
+}
+
+TEST_BEGIN(test_failing_alloc) {
+ abort_hook_t abort_hook = &fake_abort;
+ expect_d_eq(mallctl("experimental.hooks.safety_check_abort", NULL, NULL,
+ (void *)&abort_hook, sizeof(abort_hook)), 0,
+ "Unexpected mallctl failure setting abort hook");
+
+ /*
+ * Not owning operator new is only expected to happen on MinGW which
+ * does not support operator new / delete replacement.
+ */
+#ifdef _WIN32
+ test_skip_if(!own_operator_new());
+#else
+ expect_true(own_operator_new(), "No operator new overload");
+#endif
+ void *volatile ptr = (void *)1;
+ try {
+ /* Too big of an allocation to succeed. */
+ ptr = ::operator new((size_t)-1);
+ } catch (...) {
+ abort();
+ }
+ expect_ptr_null(ptr, "Allocation should have failed");
+ expect_b_eq(fake_abort_called, true, "Abort hook not invoked");
+}
+TEST_END
+
+int
+main(void) {
+ return test(
+ test_failing_alloc);
+}
+
diff --git a/deps/jemalloc/test/integration/cpp/infallible_new_true.sh b/deps/jemalloc/test/integration/cpp/infallible_new_true.sh
new file mode 100644
index 000000000..4a0ff542d
--- /dev/null
+++ b/deps/jemalloc/test/integration/cpp/infallible_new_true.sh
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+XMALLOC_STR=""
+if [ "x${enable_xmalloc}" = "x1" ] ; then
+ XMALLOC_STR="xmalloc:false,"
+fi
+
+export MALLOC_CONF="${XMALLOC_STR}experimental_infallible_new:true"
diff --git a/deps/jemalloc/test/integration/extent.c b/deps/jemalloc/test/integration/extent.c
index b5db08766..7a028f181 100644
--- a/deps/jemalloc/test/integration/extent.c
+++ b/deps/jemalloc/test/integration/extent.c
@@ -2,17 +2,7 @@
#include "test/extent_hooks.h"
-static bool
-check_background_thread_enabled(void) {
- bool enabled;
- size_t sz = sizeof(bool);
- int ret = mallctl("background_thread", (void *)&enabled, &sz, NULL,0);
- if (ret == ENOENT) {
- return false;
- }
- assert_d_eq(ret, 0, "Unexpected mallctl error");
- return enabled;
-}
+#include "jemalloc/internal/arena_types.h"
static void
test_extent_body(unsigned arena_ind) {
@@ -27,16 +17,16 @@ test_extent_body(unsigned arena_ind) {
/* Get large size classes. */
sz = sizeof(size_t);
- assert_d_eq(mallctl("arenas.lextent.0.size", (void *)&large0, &sz, NULL,
+ expect_d_eq(mallctl("arenas.lextent.0.size", (void *)&large0, &sz, NULL,
0), 0, "Unexpected arenas.lextent.0.size failure");
- assert_d_eq(mallctl("arenas.lextent.1.size", (void *)&large1, &sz, NULL,
+ expect_d_eq(mallctl("arenas.lextent.1.size", (void *)&large1, &sz, NULL,
0), 0, "Unexpected arenas.lextent.1.size failure");
- assert_d_eq(mallctl("arenas.lextent.2.size", (void *)&large2, &sz, NULL,
+ expect_d_eq(mallctl("arenas.lextent.2.size", (void *)&large2, &sz, NULL,
0), 0, "Unexpected arenas.lextent.2.size failure");
/* Test dalloc/decommit/purge cascade. */
purge_miblen = sizeof(purge_mib)/sizeof(size_t);
- assert_d_eq(mallctlnametomib("arena.0.purge", purge_mib, &purge_miblen),
+ expect_d_eq(mallctlnametomib("arena.0.purge", purge_mib, &purge_miblen),
0, "Unexpected mallctlnametomib() failure");
purge_mib[1] = (size_t)arena_ind;
called_alloc = false;
@@ -44,23 +34,23 @@ test_extent_body(unsigned arena_ind) {
try_dalloc = false;
try_decommit = false;
p = mallocx(large0 * 2, flags);
- assert_ptr_not_null(p, "Unexpected mallocx() error");
- assert_true(called_alloc, "Expected alloc call");
+ expect_ptr_not_null(p, "Unexpected mallocx() error");
+ expect_true(called_alloc, "Expected alloc call");
called_dalloc = false;
called_decommit = false;
did_purge_lazy = false;
did_purge_forced = false;
called_split = false;
xallocx_success_a = (xallocx(p, large0, 0, flags) == large0);
- assert_d_eq(mallctlbymib(purge_mib, purge_miblen, NULL, NULL, NULL, 0),
+ expect_d_eq(mallctlbymib(purge_mib, purge_miblen, NULL, NULL, NULL, 0),
0, "Unexpected arena.%u.purge error", arena_ind);
if (xallocx_success_a) {
- assert_true(called_dalloc, "Expected dalloc call");
- assert_true(called_decommit, "Expected decommit call");
- assert_true(did_purge_lazy || did_purge_forced,
+ expect_true(called_dalloc, "Expected dalloc call");
+ expect_true(called_decommit, "Expected decommit call");
+ expect_true(did_purge_lazy || did_purge_forced,
"Expected purge");
+ expect_true(called_split, "Expected split call");
}
- assert_true(called_split, "Expected split call");
dallocx(p, flags);
try_dalloc = true;
@@ -68,25 +58,25 @@ test_extent_body(unsigned arena_ind) {
try_dalloc = false;
try_decommit = true;
p = mallocx(large0 * 2, flags);
- assert_ptr_not_null(p, "Unexpected mallocx() error");
+ expect_ptr_not_null(p, "Unexpected mallocx() error");
did_decommit = false;
did_commit = false;
called_split = false;
did_split = false;
did_merge = false;
xallocx_success_b = (xallocx(p, large0, 0, flags) == large0);
- assert_d_eq(mallctlbymib(purge_mib, purge_miblen, NULL, NULL, NULL, 0),
+ expect_d_eq(mallctlbymib(purge_mib, purge_miblen, NULL, NULL, NULL, 0),
0, "Unexpected arena.%u.purge error", arena_ind);
if (xallocx_success_b) {
- assert_true(did_split, "Expected split");
+ expect_true(did_split, "Expected split");
}
xallocx_success_c = (xallocx(p, large0 * 2, 0, flags) == large0 * 2);
if (did_split) {
- assert_b_eq(did_decommit, did_commit,
+ expect_b_eq(did_decommit, did_commit,
"Expected decommit/commit match");
}
if (xallocx_success_b && xallocx_success_c) {
- assert_true(did_merge, "Expected merge");
+ expect_true(did_merge, "Expected merge");
}
dallocx(p, flags);
try_dalloc = true;
@@ -94,7 +84,7 @@ test_extent_body(unsigned arena_ind) {
/* Make sure non-large allocation succeeds. */
p = mallocx(42, flags);
- assert_ptr_not_null(p, "Unexpected mallocx() error");
+ expect_ptr_not_null(p, "Unexpected mallocx() error");
dallocx(p, flags);
}
@@ -110,7 +100,7 @@ test_manual_hook_auto_arena(void) {
sz = sizeof(unsigned);
/* Get number of auto arenas. */
- assert_d_eq(mallctl("opt.narenas", (void *)&narenas, &sz, NULL, 0),
+ expect_d_eq(mallctl("opt.narenas", (void *)&narenas, &sz, NULL, 0),
0, "Unexpected mallctl() failure");
if (narenas == 1) {
return;
@@ -118,18 +108,18 @@ test_manual_hook_auto_arena(void) {
/* Install custom extent hooks on arena 1 (might not be initialized). */
hooks_miblen = sizeof(hooks_mib)/sizeof(size_t);
- assert_d_eq(mallctlnametomib("arena.0.extent_hooks", hooks_mib,
+ expect_d_eq(mallctlnametomib("arena.0.extent_hooks", hooks_mib,
&hooks_miblen), 0, "Unexpected mallctlnametomib() failure");
hooks_mib[1] = 1;
old_size = sizeof(extent_hooks_t *);
new_hooks = &hooks;
new_size = sizeof(extent_hooks_t *);
- assert_d_eq(mallctlbymib(hooks_mib, hooks_miblen, (void *)&old_hooks,
+ expect_d_eq(mallctlbymib(hooks_mib, hooks_miblen, (void *)&old_hooks,
&old_size, (void *)&new_hooks, new_size), 0,
"Unexpected extent_hooks error");
static bool auto_arena_created = false;
if (old_hooks != &hooks) {
- assert_b_eq(auto_arena_created, false,
+ expect_b_eq(auto_arena_created, false,
"Expected auto arena 1 created only once.");
auto_arena_created = true;
}
@@ -146,62 +136,62 @@ test_manual_hook_body(void) {
extent_hooks_prep();
sz = sizeof(unsigned);
- assert_d_eq(mallctl("arenas.create", (void *)&arena_ind, &sz, NULL, 0),
+ expect_d_eq(mallctl("arenas.create", (void *)&arena_ind, &sz, NULL, 0),
0, "Unexpected mallctl() failure");
/* Install custom extent hooks. */
hooks_miblen = sizeof(hooks_mib)/sizeof(size_t);
- assert_d_eq(mallctlnametomib("arena.0.extent_hooks", hooks_mib,
+ expect_d_eq(mallctlnametomib("arena.0.extent_hooks", hooks_mib,
&hooks_miblen), 0, "Unexpected mallctlnametomib() failure");
hooks_mib[1] = (size_t)arena_ind;
old_size = sizeof(extent_hooks_t *);
new_hooks = &hooks;
new_size = sizeof(extent_hooks_t *);
- assert_d_eq(mallctlbymib(hooks_mib, hooks_miblen, (void *)&old_hooks,
+ expect_d_eq(mallctlbymib(hooks_mib, hooks_miblen, (void *)&old_hooks,
&old_size, (void *)&new_hooks, new_size), 0,
"Unexpected extent_hooks error");
- assert_ptr_ne(old_hooks->alloc, extent_alloc_hook,
+ expect_ptr_ne(old_hooks->alloc, extent_alloc_hook,
"Unexpected extent_hooks error");
- assert_ptr_ne(old_hooks->dalloc, extent_dalloc_hook,
+ expect_ptr_ne(old_hooks->dalloc, extent_dalloc_hook,
"Unexpected extent_hooks error");
- assert_ptr_ne(old_hooks->commit, extent_commit_hook,
+ expect_ptr_ne(old_hooks->commit, extent_commit_hook,
"Unexpected extent_hooks error");
- assert_ptr_ne(old_hooks->decommit, extent_decommit_hook,
+ expect_ptr_ne(old_hooks->decommit, extent_decommit_hook,
"Unexpected extent_hooks error");
- assert_ptr_ne(old_hooks->purge_lazy, extent_purge_lazy_hook,
+ expect_ptr_ne(old_hooks->purge_lazy, extent_purge_lazy_hook,
"Unexpected extent_hooks error");
- assert_ptr_ne(old_hooks->purge_forced, extent_purge_forced_hook,
+ expect_ptr_ne(old_hooks->purge_forced, extent_purge_forced_hook,
"Unexpected extent_hooks error");
- assert_ptr_ne(old_hooks->split, extent_split_hook,
+ expect_ptr_ne(old_hooks->split, extent_split_hook,
"Unexpected extent_hooks error");
- assert_ptr_ne(old_hooks->merge, extent_merge_hook,
+ expect_ptr_ne(old_hooks->merge, extent_merge_hook,
"Unexpected extent_hooks error");
- if (!check_background_thread_enabled()) {
+ if (!is_background_thread_enabled()) {
test_extent_body(arena_ind);
}
/* Restore extent hooks. */
- assert_d_eq(mallctlbymib(hooks_mib, hooks_miblen, NULL, NULL,
+ expect_d_eq(mallctlbymib(hooks_mib, hooks_miblen, NULL, NULL,
(void *)&old_hooks, new_size), 0, "Unexpected extent_hooks error");
- assert_d_eq(mallctlbymib(hooks_mib, hooks_miblen, (void *)&old_hooks,
+ expect_d_eq(mallctlbymib(hooks_mib, hooks_miblen, (void *)&old_hooks,
&old_size, NULL, 0), 0, "Unexpected extent_hooks error");
- assert_ptr_eq(old_hooks, default_hooks, "Unexpected extent_hooks error");
- assert_ptr_eq(old_hooks->alloc, default_hooks->alloc,
+ expect_ptr_eq(old_hooks, default_hooks, "Unexpected extent_hooks error");
+ expect_ptr_eq(old_hooks->alloc, default_hooks->alloc,
"Unexpected extent_hooks error");
- assert_ptr_eq(old_hooks->dalloc, default_hooks->dalloc,
+ expect_ptr_eq(old_hooks->dalloc, default_hooks->dalloc,
"Unexpected extent_hooks error");
- assert_ptr_eq(old_hooks->commit, default_hooks->commit,
+ expect_ptr_eq(old_hooks->commit, default_hooks->commit,
"Unexpected extent_hooks error");
- assert_ptr_eq(old_hooks->decommit, default_hooks->decommit,
+ expect_ptr_eq(old_hooks->decommit, default_hooks->decommit,
"Unexpected extent_hooks error");
- assert_ptr_eq(old_hooks->purge_lazy, default_hooks->purge_lazy,
+ expect_ptr_eq(old_hooks->purge_lazy, default_hooks->purge_lazy,
"Unexpected extent_hooks error");
- assert_ptr_eq(old_hooks->purge_forced, default_hooks->purge_forced,
+ expect_ptr_eq(old_hooks->purge_forced, default_hooks->purge_forced,
"Unexpected extent_hooks error");
- assert_ptr_eq(old_hooks->split, default_hooks->split,
+ expect_ptr_eq(old_hooks->split, default_hooks->split,
"Unexpected extent_hooks error");
- assert_ptr_eq(old_hooks->merge, default_hooks->merge,
+ expect_ptr_eq(old_hooks->merge, default_hooks->merge,
"Unexpected extent_hooks error");
}
@@ -232,17 +222,66 @@ TEST_BEGIN(test_extent_auto_hook) {
sz = sizeof(unsigned);
new_hooks = &hooks;
new_size = sizeof(extent_hooks_t *);
- assert_d_eq(mallctl("arenas.create", (void *)&arena_ind, &sz,
+ expect_d_eq(mallctl("arenas.create", (void *)&arena_ind, &sz,
(void *)&new_hooks, new_size), 0, "Unexpected mallctl() failure");
- test_skip_if(check_background_thread_enabled());
+ test_skip_if(is_background_thread_enabled());
test_extent_body(arena_ind);
}
TEST_END
+static void
+test_arenas_create_ext_base(arena_config_t config,
+ bool expect_hook_data, bool expect_hook_metadata)
+{
+ unsigned arena, arena1;
+ void *ptr;
+ size_t sz = sizeof(unsigned);
+
+ extent_hooks_prep();
+
+ called_alloc = false;
+ expect_d_eq(mallctl("experimental.arenas_create_ext",
+ (void *)&arena, &sz, &config, sizeof(arena_config_t)), 0,
+ "Unexpected mallctl() failure");
+ expect_b_eq(called_alloc, expect_hook_metadata,
+ "expected hook metadata alloc mismatch");
+
+ called_alloc = false;
+ ptr = mallocx(42, MALLOCX_ARENA(arena) | MALLOCX_TCACHE_NONE);
+ expect_b_eq(called_alloc, expect_hook_data,
+ "expected hook data alloc mismatch");
+
+ expect_ptr_not_null(ptr, "Unexpected mallocx() failure");
+ expect_d_eq(mallctl("arenas.lookup", &arena1, &sz, &ptr, sizeof(ptr)),
+ 0, "Unexpected mallctl() failure");
+ expect_u_eq(arena, arena1, "Unexpected arena index");
+ dallocx(ptr, 0);
+}
+
+TEST_BEGIN(test_arenas_create_ext_with_ehooks_no_metadata) {
+ arena_config_t config;
+ config.extent_hooks = &hooks;
+ config.metadata_use_hooks = false;
+
+ test_arenas_create_ext_base(config, true, false);
+}
+TEST_END
+
+TEST_BEGIN(test_arenas_create_ext_with_ehooks_with_metadata) {
+ arena_config_t config;
+ config.extent_hooks = &hooks;
+ config.metadata_use_hooks = true;
+
+ test_arenas_create_ext_base(config, true, true);
+}
+TEST_END
+
int
main(void) {
return test(
test_extent_manual_hook,
- test_extent_auto_hook);
+ test_extent_auto_hook,
+ test_arenas_create_ext_with_ehooks_no_metadata,
+ test_arenas_create_ext_with_ehooks_with_metadata);
}
diff --git a/deps/jemalloc/test/integration/malloc.c b/deps/jemalloc/test/integration/malloc.c
index 8b33bc8f3..ef4491636 100644
--- a/deps/jemalloc/test/integration/malloc.c
+++ b/deps/jemalloc/test/integration/malloc.c
@@ -3,7 +3,7 @@
TEST_BEGIN(test_zero_alloc) {
void *res = malloc(0);
assert(res);
- size_t usable = malloc_usable_size(res);
+ size_t usable = TEST_MALLOC_SIZE(res);
assert(usable > 0);
free(res);
}
diff --git a/deps/jemalloc/test/integration/mallocx.c b/deps/jemalloc/test/integration/mallocx.c
index 645d4db48..fdf1e3f43 100644
--- a/deps/jemalloc/test/integration/mallocx.c
+++ b/deps/jemalloc/test/integration/mallocx.c
@@ -6,7 +6,7 @@ get_nsizes_impl(const char *cmd) {
size_t z;
z = sizeof(unsigned);
- assert_d_eq(mallctl(cmd, (void *)&ret, &z, NULL, 0), 0,
+ expect_d_eq(mallctl(cmd, (void *)&ret, &z, NULL, 0), 0,
"Unexpected mallctl(\"%s\", ...) failure", cmd);
return ret;
@@ -25,11 +25,11 @@ get_size_impl(const char *cmd, size_t ind) {
size_t miblen = 4;
z = sizeof(size_t);
- assert_d_eq(mallctlnametomib(cmd, mib, &miblen),
+ expect_d_eq(mallctlnametomib(cmd, mib, &miblen),
0, "Unexpected mallctlnametomib(\"%s\", ...) failure", cmd);
mib[2] = ind;
z = sizeof(size_t);
- assert_d_eq(mallctlbymib(mib, miblen, (void *)&ret, &z, NULL, 0),
+ expect_d_eq(mallctlbymib(mib, miblen, (void *)&ret, &z, NULL, 0),
0, "Unexpected mallctlbymib([\"%s\", %zu], ...) failure", cmd, ind);
return ret;
@@ -47,7 +47,7 @@ get_large_size(size_t ind) {
*/
static void
purge(void) {
- assert_d_eq(mallctl("arena.0.purge", NULL, NULL, NULL, 0), 0,
+ expect_d_eq(mallctl("arena.0.purge", NULL, NULL, NULL, 0), 0,
"Unexpected mallctl error");
}
@@ -66,16 +66,16 @@ TEST_BEGIN(test_overflow) {
largemax = get_large_size(get_nlarge()-1);
- assert_ptr_null(mallocx(largemax+1, 0),
+ expect_ptr_null(mallocx(largemax+1, 0),
"Expected OOM for mallocx(size=%#zx, 0)", largemax+1);
- assert_ptr_null(mallocx(ZU(PTRDIFF_MAX)+1, 0),
+ expect_ptr_null(mallocx(ZU(PTRDIFF_MAX)+1, 0),
"Expected OOM for mallocx(size=%#zx, 0)", ZU(PTRDIFF_MAX)+1);
- assert_ptr_null(mallocx(SIZE_T_MAX, 0),
+ expect_ptr_null(mallocx(SIZE_T_MAX, 0),
"Expected OOM for mallocx(size=%#zx, 0)", SIZE_T_MAX);
- assert_ptr_null(mallocx(1, MALLOCX_ALIGN(ZU(PTRDIFF_MAX)+1)),
+ expect_ptr_null(mallocx(1, MALLOCX_ALIGN(ZU(PTRDIFF_MAX)+1)),
"Expected OOM for mallocx(size=1, MALLOCX_ALIGN(%#zx))",
ZU(PTRDIFF_MAX)+1);
}
@@ -85,11 +85,11 @@ static void *
remote_alloc(void *arg) {
unsigned arena;
size_t sz = sizeof(unsigned);
- assert_d_eq(mallctl("arenas.create", (void *)&arena, &sz, NULL, 0), 0,
+ expect_d_eq(mallctl("arenas.create", (void *)&arena, &sz, NULL, 0), 0,
"Unexpected mallctl() failure");
size_t large_sz;
sz = sizeof(size_t);
- assert_d_eq(mallctl("arenas.lextent.0.size", (void *)&large_sz, &sz,
+ expect_d_eq(mallctl("arenas.lextent.0.size", (void *)&large_sz, &sz,
NULL, 0), 0, "Unexpected mallctl failure");
void *ptr = mallocx(large_sz, MALLOCX_ARENA(arena)
@@ -105,7 +105,7 @@ TEST_BEGIN(test_remote_free) {
void *ret;
thd_create(&thd, remote_alloc, (void *)&ret);
thd_join(thd, NULL);
- assert_ptr_not_null(ret, "Unexpected mallocx failure");
+ expect_ptr_not_null(ret, "Unexpected mallocx failure");
/* Avoid TCACHE_NONE to explicitly test tcache_flush(). */
dallocx(ret, 0);
@@ -131,7 +131,7 @@ TEST_BEGIN(test_oom) {
oom = true;
}
}
- assert_true(oom,
+ expect_true(oom,
"Expected OOM during series of calls to mallocx(size=%zu, 0)",
largemax);
for (i = 0; i < sizeof(ptrs) / sizeof(void *); i++) {
@@ -142,14 +142,14 @@ TEST_BEGIN(test_oom) {
purge();
#if LG_SIZEOF_PTR == 3
- assert_ptr_null(mallocx(0x8000000000000000ULL,
+ expect_ptr_null(mallocx(0x8000000000000000ULL,
MALLOCX_ALIGN(0x8000000000000000ULL)),
"Expected OOM for mallocx()");
- assert_ptr_null(mallocx(0x8000000000000000ULL,
+ expect_ptr_null(mallocx(0x8000000000000000ULL,
MALLOCX_ALIGN(0x80000000)),
"Expected OOM for mallocx()");
#else
- assert_ptr_null(mallocx(0x80000000UL, MALLOCX_ALIGN(0x80000000UL)),
+ expect_ptr_null(mallocx(0x80000000UL, MALLOCX_ALIGN(0x80000000UL)),
"Expected OOM for mallocx()");
#endif
}
@@ -166,28 +166,28 @@ TEST_BEGIN(test_basic) {
size_t nsz, rsz;
void *p;
nsz = nallocx(sz, 0);
- assert_zu_ne(nsz, 0, "Unexpected nallocx() error");
+ expect_zu_ne(nsz, 0, "Unexpected nallocx() error");
p = mallocx(sz, 0);
- assert_ptr_not_null(p,
+ expect_ptr_not_null(p,
"Unexpected mallocx(size=%zx, flags=0) error", sz);
rsz = sallocx(p, 0);
- assert_zu_ge(rsz, sz, "Real size smaller than expected");
- assert_zu_eq(nsz, rsz, "nallocx()/sallocx() size mismatch");
+ expect_zu_ge(rsz, sz, "Real size smaller than expected");
+ expect_zu_eq(nsz, rsz, "nallocx()/sallocx() size mismatch");
dallocx(p, 0);
p = mallocx(sz, 0);
- assert_ptr_not_null(p,
+ expect_ptr_not_null(p,
"Unexpected mallocx(size=%zx, flags=0) error", sz);
dallocx(p, 0);
nsz = nallocx(sz, MALLOCX_ZERO);
- assert_zu_ne(nsz, 0, "Unexpected nallocx() error");
+ expect_zu_ne(nsz, 0, "Unexpected nallocx() error");
p = mallocx(sz, MALLOCX_ZERO);
- assert_ptr_not_null(p,
+ expect_ptr_not_null(p,
"Unexpected mallocx(size=%zx, flags=MALLOCX_ZERO) error",
nsz);
rsz = sallocx(p, 0);
- assert_zu_eq(nsz, rsz, "nallocx()/sallocx() rsize mismatch");
+ expect_zu_eq(nsz, rsz, "nallocx()/sallocx() rsize mismatch");
dallocx(p, 0);
purge();
}
@@ -224,22 +224,22 @@ TEST_BEGIN(test_alignment_and_size) {
for (i = 0; i < NITER; i++) {
nsz = nallocx(sz, MALLOCX_ALIGN(alignment) |
MALLOCX_ZERO | MALLOCX_ARENA(0));
- assert_zu_ne(nsz, 0,
+ expect_zu_ne(nsz, 0,
"nallocx() error for alignment=%zu, "
"size=%zu (%#zx)", alignment, sz, sz);
ps[i] = mallocx(sz, MALLOCX_ALIGN(alignment) |
MALLOCX_ZERO | MALLOCX_ARENA(0));
- assert_ptr_not_null(ps[i],
+ expect_ptr_not_null(ps[i],
"mallocx() error for alignment=%zu, "
"size=%zu (%#zx)", alignment, sz, sz);
rsz = sallocx(ps[i], 0);
- assert_zu_ge(rsz, sz,
+ expect_zu_ge(rsz, sz,
"Real size smaller than expected for "
"alignment=%zu, size=%zu", alignment, sz);
- assert_zu_eq(nsz, rsz,
+ expect_zu_eq(nsz, rsz,
"nallocx()/sallocx() size mismatch for "
"alignment=%zu, size=%zu", alignment, sz);
- assert_ptr_null(
+ expect_ptr_null(
(void *)((uintptr_t)ps[i] & (alignment-1)),
"%p inadequately aligned for"
" alignment=%zu, size=%zu", ps[i],
diff --git a/deps/jemalloc/test/integration/overflow.c b/deps/jemalloc/test/integration/overflow.c
index 748ebb677..ce63327ca 100644
--- a/deps/jemalloc/test/integration/overflow.c
+++ b/deps/jemalloc/test/integration/overflow.c
@@ -17,33 +17,33 @@ TEST_BEGIN(test_overflow) {
void *p;
sz = sizeof(unsigned);
- assert_d_eq(mallctl("arenas.nlextents", (void *)&nlextents, &sz, NULL,
+ expect_d_eq(mallctl("arenas.nlextents", (void *)&nlextents, &sz, NULL,
0), 0, "Unexpected mallctl() error");
miblen = sizeof(mib) / sizeof(size_t);
- assert_d_eq(mallctlnametomib("arenas.lextent.0.size", mib, &miblen), 0,
+ expect_d_eq(mallctlnametomib("arenas.lextent.0.size", mib, &miblen), 0,
"Unexpected mallctlnametomib() error");
mib[2] = nlextents - 1;
sz = sizeof(size_t);
- assert_d_eq(mallctlbymib(mib, miblen, (void *)&max_size_class, &sz,
+ expect_d_eq(mallctlbymib(mib, miblen, (void *)&max_size_class, &sz,
NULL, 0), 0, "Unexpected mallctlbymib() error");
- assert_ptr_null(malloc(max_size_class + 1),
+ expect_ptr_null(malloc(max_size_class + 1),
"Expected OOM due to over-sized allocation request");
- assert_ptr_null(malloc(SIZE_T_MAX),
+ expect_ptr_null(malloc(SIZE_T_MAX),
"Expected OOM due to over-sized allocation request");
- assert_ptr_null(calloc(1, max_size_class + 1),
+ expect_ptr_null(calloc(1, max_size_class + 1),
"Expected OOM due to over-sized allocation request");
- assert_ptr_null(calloc(1, SIZE_T_MAX),
+ expect_ptr_null(calloc(1, SIZE_T_MAX),
"Expected OOM due to over-sized allocation request");
p = malloc(1);
- assert_ptr_not_null(p, "Unexpected malloc() OOM");
- assert_ptr_null(realloc(p, max_size_class + 1),
+ expect_ptr_not_null(p, "Unexpected malloc() OOM");
+ expect_ptr_null(realloc(p, max_size_class + 1),
"Expected OOM due to over-sized allocation request");
- assert_ptr_null(realloc(p, SIZE_T_MAX),
+ expect_ptr_null(realloc(p, SIZE_T_MAX),
"Expected OOM due to over-sized allocation request");
free(p);
}
diff --git a/deps/jemalloc/test/integration/posix_memalign.c b/deps/jemalloc/test/integration/posix_memalign.c
index d992260a2..2da0549bf 100644
--- a/deps/jemalloc/test/integration/posix_memalign.c
+++ b/deps/jemalloc/test/integration/posix_memalign.c
@@ -9,7 +9,7 @@
*/
static void
purge(void) {
- assert_d_eq(mallctl("arena.0.purge", NULL, NULL, NULL, 0), 0,
+ expect_d_eq(mallctl("arena.0.purge", NULL, NULL, NULL, 0), 0,
"Unexpected mallctl error");
}
@@ -18,14 +18,14 @@ TEST_BEGIN(test_alignment_errors) {
void *p;
for (alignment = 0; alignment < sizeof(void *); alignment++) {
- assert_d_eq(posix_memalign(&p, alignment, 1), EINVAL,
+ expect_d_eq(posix_memalign(&p, alignment, 1), EINVAL,
"Expected error for invalid alignment %zu",
alignment);
}
for (alignment = sizeof(size_t); alignment < MAXALIGN;
alignment <<= 1) {
- assert_d_ne(posix_memalign(&p, alignment + 1, 1), 0,
+ expect_d_ne(posix_memalign(&p, alignment + 1, 1), 0,
"Expected error for invalid alignment %zu",
alignment + 1);
}
@@ -43,7 +43,7 @@ TEST_BEGIN(test_oom_errors) {
alignment = 0x80000000LU;
size = 0x80000000LU;
#endif
- assert_d_ne(posix_memalign(&p, alignment, size), 0,
+ expect_d_ne(posix_memalign(&p, alignment, size), 0,
"Expected error for posix_memalign(&p, %zu, %zu)",
alignment, size);
@@ -54,7 +54,7 @@ TEST_BEGIN(test_oom_errors) {
alignment = 0x40000000LU;
size = 0xc0000001LU;
#endif
- assert_d_ne(posix_memalign(&p, alignment, size), 0,
+ expect_d_ne(posix_memalign(&p, alignment, size), 0,
"Expected error for posix_memalign(&p, %zu, %zu)",
alignment, size);
@@ -64,7 +64,7 @@ TEST_BEGIN(test_oom_errors) {
#else
size = 0xfffffff0LU;
#endif
- assert_d_ne(posix_memalign(&p, alignment, size), 0,
+ expect_d_ne(posix_memalign(&p, alignment, size), 0,
"Expected error for posix_memalign(&p, %zu, %zu)",
alignment, size);
}
@@ -101,7 +101,7 @@ TEST_BEGIN(test_alignment_and_size) {
"size=%zu (%#zx): %s",
alignment, size, size, buf);
}
- total += malloc_usable_size(ps[i]);
+ total += TEST_MALLOC_SIZE(ps[i]);
if (total >= (MAXALIGN << 1)) {
break;
}
diff --git a/deps/jemalloc/test/integration/rallocx.c b/deps/jemalloc/test/integration/rallocx.c
index 08ed08d3f..68b8f3816 100644
--- a/deps/jemalloc/test/integration/rallocx.c
+++ b/deps/jemalloc/test/integration/rallocx.c
@@ -6,7 +6,7 @@ get_nsizes_impl(const char *cmd) {
size_t z;
z = sizeof(unsigned);
- assert_d_eq(mallctl(cmd, (void *)&ret, &z, NULL, 0), 0,
+ expect_d_eq(mallctl(cmd, (void *)&ret, &z, NULL, 0), 0,
"Unexpected mallctl(\"%s\", ...) failure", cmd);
return ret;
@@ -25,11 +25,11 @@ get_size_impl(const char *cmd, size_t ind) {
size_t miblen = 4;
z = sizeof(size_t);
- assert_d_eq(mallctlnametomib(cmd, mib, &miblen),
+ expect_d_eq(mallctlnametomib(cmd, mib, &miblen),
0, "Unexpected mallctlnametomib(\"%s\", ...) failure", cmd);
mib[2] = ind;
z = sizeof(size_t);
- assert_d_eq(mallctlbymib(mib, miblen, (void *)&ret, &z, NULL, 0),
+ expect_d_eq(mallctlbymib(mib, miblen, (void *)&ret, &z, NULL, 0),
0, "Unexpected mallctlbymib([\"%s\", %zu], ...) failure", cmd, ind);
return ret;
@@ -41,7 +41,11 @@ get_large_size(size_t ind) {
}
TEST_BEGIN(test_grow_and_shrink) {
- void *p, *q;
+ /*
+ * Use volatile to workaround buffer overflow false positives
+ * (-D_FORTIFY_SOURCE=3).
+ */
+ void *volatile p, *volatile q;
size_t tsz;
#define NCYCLES 3
unsigned i, j;
@@ -50,28 +54,28 @@ TEST_BEGIN(test_grow_and_shrink) {
#define MAXSZ ZU(12 * 1024 * 1024)
p = mallocx(1, 0);
- assert_ptr_not_null(p, "Unexpected mallocx() error");
+ expect_ptr_not_null(p, "Unexpected mallocx() error");
szs[0] = sallocx(p, 0);
for (i = 0; i < NCYCLES; i++) {
for (j = 1; j < NSZS && szs[j-1] < MAXSZ; j++) {
q = rallocx(p, szs[j-1]+1, 0);
- assert_ptr_not_null(q,
+ expect_ptr_not_null(q,
"Unexpected rallocx() error for size=%zu-->%zu",
szs[j-1], szs[j-1]+1);
szs[j] = sallocx(q, 0);
- assert_zu_ne(szs[j], szs[j-1]+1,
+ expect_zu_ne(szs[j], szs[j-1]+1,
"Expected size to be at least: %zu", szs[j-1]+1);
p = q;
}
for (j--; j > 0; j--) {
q = rallocx(p, szs[j-1], 0);
- assert_ptr_not_null(q,
+ expect_ptr_not_null(q,
"Unexpected rallocx() error for size=%zu-->%zu",
szs[j], szs[j-1]);
tsz = sallocx(q, 0);
- assert_zu_eq(tsz, szs[j-1],
+ expect_zu_eq(tsz, szs[j-1],
"Expected size=%zu, got size=%zu", szs[j-1], tsz);
p = q;
}
@@ -85,9 +89,13 @@ TEST_BEGIN(test_grow_and_shrink) {
TEST_END
static bool
-validate_fill(const void *p, uint8_t c, size_t offset, size_t len) {
+validate_fill(void *p, uint8_t c, size_t offset, size_t len) {
bool ret = false;
- const uint8_t *buf = (const uint8_t *)p;
+ /*
+ * Use volatile to workaround buffer overflow false positives
+ * (-D_FORTIFY_SOURCE=3).
+ */
+ uint8_t *volatile buf = (uint8_t *)p;
size_t i;
for (i = 0; i < len; i++) {
@@ -104,7 +112,11 @@ validate_fill(const void *p, uint8_t c, size_t offset, size_t len) {
}
TEST_BEGIN(test_zero) {
- void *p, *q;
+ /*
+ * Use volatile to workaround buffer overflow false positives
+ * (-D_FORTIFY_SOURCE=3).
+ */
+ void *volatile p, *volatile q;
size_t psz, qsz, i, j;
size_t start_sizes[] = {1, 3*1024, 63*1024, 4095*1024};
#define FILL_BYTE 0xaaU
@@ -113,23 +125,23 @@ TEST_BEGIN(test_zero) {
for (i = 0; i < sizeof(start_sizes)/sizeof(size_t); i++) {
size_t start_size = start_sizes[i];
p = mallocx(start_size, MALLOCX_ZERO);
- assert_ptr_not_null(p, "Unexpected mallocx() error");
+ expect_ptr_not_null(p, "Unexpected mallocx() error");
psz = sallocx(p, 0);
- assert_false(validate_fill(p, 0, 0, psz),
+ expect_false(validate_fill(p, 0, 0, psz),
"Expected zeroed memory");
memset(p, FILL_BYTE, psz);
- assert_false(validate_fill(p, FILL_BYTE, 0, psz),
+ expect_false(validate_fill(p, FILL_BYTE, 0, psz),
"Expected filled memory");
for (j = 1; j < RANGE; j++) {
q = rallocx(p, start_size+j, MALLOCX_ZERO);
- assert_ptr_not_null(q, "Unexpected rallocx() error");
+ expect_ptr_not_null(q, "Unexpected rallocx() error");
qsz = sallocx(q, 0);
if (q != p || qsz != psz) {
- assert_false(validate_fill(q, FILL_BYTE, 0,
+ expect_false(validate_fill(q, FILL_BYTE, 0,
psz), "Expected filled memory");
- assert_false(validate_fill(q, 0, psz, qsz-psz),
+ expect_false(validate_fill(q, 0, psz, qsz-psz),
"Expected zeroed memory");
}
if (psz != qsz) {
@@ -139,7 +151,7 @@ TEST_BEGIN(test_zero) {
}
p = q;
}
- assert_false(validate_fill(p, FILL_BYTE, 0, psz),
+ expect_false(validate_fill(p, FILL_BYTE, 0, psz),
"Expected filled memory");
dallocx(p, 0);
}
@@ -154,13 +166,13 @@ TEST_BEGIN(test_align) {
align = ZU(1);
p = mallocx(1, MALLOCX_ALIGN(align));
- assert_ptr_not_null(p, "Unexpected mallocx() error");
+ expect_ptr_not_null(p, "Unexpected mallocx() error");
for (align <<= 1; align <= MAX_ALIGN; align <<= 1) {
q = rallocx(p, 1, MALLOCX_ALIGN(align));
- assert_ptr_not_null(q,
+ expect_ptr_not_null(q,
"Unexpected rallocx() error for align=%zu", align);
- assert_ptr_null(
+ expect_ptr_null(
(void *)((uintptr_t)q & (align-1)),
"%p inadequately aligned for align=%zu",
q, align);
@@ -171,8 +183,45 @@ TEST_BEGIN(test_align) {
}
TEST_END
+TEST_BEGIN(test_align_enum) {
+/* Span both small sizes and large sizes. */
+#define LG_MIN 12
+#define LG_MAX 15
+ for (size_t lg_align = LG_MIN; lg_align <= LG_MAX; ++lg_align) {
+ for (size_t lg_size = LG_MIN; lg_size <= LG_MAX; ++lg_size) {
+ size_t size = 1 << lg_size;
+ for (size_t lg_align_next = LG_MIN;
+ lg_align_next <= LG_MAX; ++lg_align_next) {
+ int flags = MALLOCX_LG_ALIGN(lg_align);
+ void *p = mallocx(1, flags);
+ assert_ptr_not_null(p,
+ "Unexpected mallocx() error");
+ assert_zu_eq(nallocx(1, flags),
+ TEST_MALLOC_SIZE(p),
+ "Wrong mallocx() usable size");
+ int flags_next =
+ MALLOCX_LG_ALIGN(lg_align_next);
+ p = rallocx(p, size, flags_next);
+ assert_ptr_not_null(p,
+ "Unexpected rallocx() error");
+ expect_zu_eq(nallocx(size, flags_next),
+ TEST_MALLOC_SIZE(p),
+ "Wrong rallocx() usable size");
+ free(p);
+ }
+ }
+ }
+#undef LG_MAX
+#undef LG_MIN
+}
+TEST_END
+
TEST_BEGIN(test_lg_align_and_zero) {
- void *p, *q;
+ /*
+ * Use volatile to workaround buffer overflow false positives
+ * (-D_FORTIFY_SOURCE=3).
+ */
+ void *volatile p, *volatile q;
unsigned lg_align;
size_t sz;
#define MAX_LG_ALIGN 25
@@ -180,23 +229,23 @@ TEST_BEGIN(test_lg_align_and_zero) {
lg_align = 0;
p = mallocx(1, MALLOCX_LG_ALIGN(lg_align)|MALLOCX_ZERO);
- assert_ptr_not_null(p, "Unexpected mallocx() error");
+ expect_ptr_not_null(p, "Unexpected mallocx() error");
for (lg_align++; lg_align <= MAX_LG_ALIGN; lg_align++) {
q = rallocx(p, 1, MALLOCX_LG_ALIGN(lg_align)|MALLOCX_ZERO);
- assert_ptr_not_null(q,
+ expect_ptr_not_null(q,
"Unexpected rallocx() error for lg_align=%u", lg_align);
- assert_ptr_null(
+ expect_ptr_null(
(void *)((uintptr_t)q & ((ZU(1) << lg_align)-1)),
"%p inadequately aligned for lg_align=%u", q, lg_align);
sz = sallocx(q, 0);
if ((sz << 1) <= MAX_VALIDATE) {
- assert_false(validate_fill(q, 0, 0, sz),
+ expect_false(validate_fill(q, 0, 0, sz),
"Expected zeroed memory");
} else {
- assert_false(validate_fill(q, 0, 0, MAX_VALIDATE),
+ expect_false(validate_fill(q, 0, 0, MAX_VALIDATE),
"Expected zeroed memory");
- assert_false(validate_fill(
+ expect_false(validate_fill(
(void *)((uintptr_t)q+sz-MAX_VALIDATE),
0, 0, MAX_VALIDATE), "Expected zeroed memory");
}
@@ -225,18 +274,18 @@ TEST_BEGIN(test_overflow) {
largemax = get_large_size(get_nlarge()-1);
p = mallocx(1, 0);
- assert_ptr_not_null(p, "Unexpected mallocx() failure");
+ expect_ptr_not_null(p, "Unexpected mallocx() failure");
- assert_ptr_null(rallocx(p, largemax+1, 0),
+ expect_ptr_null(rallocx(p, largemax+1, 0),
"Expected OOM for rallocx(p, size=%#zx, 0)", largemax+1);
- assert_ptr_null(rallocx(p, ZU(PTRDIFF_MAX)+1, 0),
+ expect_ptr_null(rallocx(p, ZU(PTRDIFF_MAX)+1, 0),
"Expected OOM for rallocx(p, size=%#zx, 0)", ZU(PTRDIFF_MAX)+1);
- assert_ptr_null(rallocx(p, SIZE_T_MAX, 0),
+ expect_ptr_null(rallocx(p, SIZE_T_MAX, 0),
"Expected OOM for rallocx(p, size=%#zx, 0)", SIZE_T_MAX);
- assert_ptr_null(rallocx(p, 1, MALLOCX_ALIGN(ZU(PTRDIFF_MAX)+1)),
+ expect_ptr_null(rallocx(p, 1, MALLOCX_ALIGN(ZU(PTRDIFF_MAX)+1)),
"Expected OOM for rallocx(p, size=1, MALLOCX_ALIGN(%#zx))",
ZU(PTRDIFF_MAX)+1);
@@ -253,6 +302,7 @@ main(void) {
test_grow_and_shrink,
test_zero,
test_align,
+ test_align_enum,
test_lg_align_and_zero,
test_overflow);
}
diff --git a/deps/jemalloc/test/integration/slab_sizes.c b/deps/jemalloc/test/integration/slab_sizes.c
index af250c3f4..f6a66f216 100644
--- a/deps/jemalloc/test/integration/slab_sizes.c
+++ b/deps/jemalloc/test/integration/slab_sizes.c
@@ -10,19 +10,19 @@ TEST_BEGIN(test_slab_sizes) {
size_t len;
len = sizeof(nbins);
- assert_d_eq(mallctl("arenas.nbins", &nbins, &len, NULL, 0), 0,
+ expect_d_eq(mallctl("arenas.nbins", &nbins, &len, NULL, 0), 0,
"nbins mallctl failure");
len = sizeof(page);
- assert_d_eq(mallctl("arenas.page", &page, &len, NULL, 0), 0,
+ expect_d_eq(mallctl("arenas.page", &page, &len, NULL, 0), 0,
"page mallctl failure");
len = 4;
- assert_d_eq(mallctlnametomib("arenas.bin.0.size", sizemib, &len), 0,
+ expect_d_eq(mallctlnametomib("arenas.bin.0.size", sizemib, &len), 0,
"bin size mallctlnametomib failure");
len = 4;
- assert_d_eq(mallctlnametomib("arenas.bin.0.slab_size", slabmib, &len),
+ expect_d_eq(mallctlnametomib("arenas.bin.0.slab_size", slabmib, &len),
0, "slab size mallctlnametomib failure");
size_t biggest_slab_seen = 0;
@@ -33,11 +33,11 @@ TEST_BEGIN(test_slab_sizes) {
len = sizeof(size_t);
sizemib[2] = i;
slabmib[2] = i;
- assert_d_eq(mallctlbymib(sizemib, 4, (void *)&bin_size, &len,
+ expect_d_eq(mallctlbymib(sizemib, 4, (void *)&bin_size, &len,
NULL, 0), 0, "bin size mallctlbymib failure");
len = sizeof(size_t);
- assert_d_eq(mallctlbymib(slabmib, 4, (void *)&slab_size, &len,
+ expect_d_eq(mallctlbymib(slabmib, 4, (void *)&slab_size, &len,
NULL, 0), 0, "slab size mallctlbymib failure");
if (bin_size < 100) {
@@ -48,19 +48,19 @@ TEST_BEGIN(test_slab_sizes) {
* should at least make sure that the number of pages
* goes up.
*/
- assert_zu_ge(slab_size, biggest_slab_seen,
+ expect_zu_ge(slab_size, biggest_slab_seen,
"Slab sizes should go up");
biggest_slab_seen = slab_size;
} else if (
(100 <= bin_size && bin_size < 128)
|| (128 < bin_size && bin_size <= 200)) {
- assert_zu_eq(slab_size, page,
+ expect_zu_eq(slab_size, page,
"Forced-small slabs should be small");
} else if (bin_size == 128) {
- assert_zu_eq(slab_size, 2 * page,
+ expect_zu_eq(slab_size, 2 * page,
"Forced-2-page slab should be 2 pages");
} else if (200 < bin_size && bin_size <= 4096) {
- assert_zu_ge(slab_size, biggest_slab_seen,
+ expect_zu_ge(slab_size, biggest_slab_seen,
"Slab sizes should go up");
biggest_slab_seen = slab_size;
}
@@ -69,7 +69,7 @@ TEST_BEGIN(test_slab_sizes) {
* For any reasonable configuration, 17 pages should be a valid slab
* size for 4096-byte items.
*/
- assert_zu_eq(biggest_slab_seen, 17 * page, "Didn't hit page target");
+ expect_zu_eq(biggest_slab_seen, 17 * page, "Didn't hit page target");
}
TEST_END
diff --git a/deps/jemalloc/test/integration/smallocx.c b/deps/jemalloc/test/integration/smallocx.c
index 2486752be..389319b7f 100644
--- a/deps/jemalloc/test/integration/smallocx.c
+++ b/deps/jemalloc/test/integration/smallocx.c
@@ -26,7 +26,7 @@ get_nsizes_impl(const char *cmd) {
size_t z;
z = sizeof(unsigned);
- assert_d_eq(mallctl(cmd, (void *)&ret, &z, NULL, 0), 0,
+ expect_d_eq(mallctl(cmd, (void *)&ret, &z, NULL, 0), 0,
"Unexpected mallctl(\"%s\", ...) failure", cmd);
return ret;
@@ -45,11 +45,11 @@ get_size_impl(const char *cmd, size_t ind) {
size_t miblen = 4;
z = sizeof(size_t);
- assert_d_eq(mallctlnametomib(cmd, mib, &miblen),
+ expect_d_eq(mallctlnametomib(cmd, mib, &miblen),
0, "Unexpected mallctlnametomib(\"%s\", ...) failure", cmd);
mib[2] = ind;
z = sizeof(size_t);
- assert_d_eq(mallctlbymib(mib, miblen, (void *)&ret, &z, NULL, 0),
+ expect_d_eq(mallctlbymib(mib, miblen, (void *)&ret, &z, NULL, 0),
0, "Unexpected mallctlbymib([\"%s\", %zu], ...) failure", cmd, ind);
return ret;
@@ -67,7 +67,7 @@ get_large_size(size_t ind) {
*/
static void
purge(void) {
- assert_d_eq(mallctl("arena.0.purge", NULL, NULL, NULL, 0), 0,
+ expect_d_eq(mallctl("arena.0.purge", NULL, NULL, NULL, 0), 0,
"Unexpected mallctl error");
}
@@ -86,16 +86,16 @@ TEST_BEGIN(test_overflow) {
largemax = get_large_size(get_nlarge()-1);
- assert_ptr_null(smallocx(largemax+1, 0).ptr,
+ expect_ptr_null(smallocx(largemax+1, 0).ptr,
"Expected OOM for smallocx(size=%#zx, 0)", largemax+1);
- assert_ptr_null(smallocx(ZU(PTRDIFF_MAX)+1, 0).ptr,
+ expect_ptr_null(smallocx(ZU(PTRDIFF_MAX)+1, 0).ptr,
"Expected OOM for smallocx(size=%#zx, 0)", ZU(PTRDIFF_MAX)+1);
- assert_ptr_null(smallocx(SIZE_T_MAX, 0).ptr,
+ expect_ptr_null(smallocx(SIZE_T_MAX, 0).ptr,
"Expected OOM for smallocx(size=%#zx, 0)", SIZE_T_MAX);
- assert_ptr_null(smallocx(1, MALLOCX_ALIGN(ZU(PTRDIFF_MAX)+1)).ptr,
+ expect_ptr_null(smallocx(1, MALLOCX_ALIGN(ZU(PTRDIFF_MAX)+1)).ptr,
"Expected OOM for smallocx(size=1, MALLOCX_ALIGN(%#zx))",
ZU(PTRDIFF_MAX)+1);
}
@@ -105,17 +105,17 @@ static void *
remote_alloc(void *arg) {
unsigned arena;
size_t sz = sizeof(unsigned);
- assert_d_eq(mallctl("arenas.create", (void *)&arena, &sz, NULL, 0), 0,
+ expect_d_eq(mallctl("arenas.create", (void *)&arena, &sz, NULL, 0), 0,
"Unexpected mallctl() failure");
size_t large_sz;
sz = sizeof(size_t);
- assert_d_eq(mallctl("arenas.lextent.0.size", (void *)&large_sz, &sz,
+ expect_d_eq(mallctl("arenas.lextent.0.size", (void *)&large_sz, &sz,
NULL, 0), 0, "Unexpected mallctl failure");
smallocx_return_t r
= smallocx(large_sz, MALLOCX_ARENA(arena) | MALLOCX_TCACHE_NONE);
void *ptr = r.ptr;
- assert_zu_eq(r.size,
+ expect_zu_eq(r.size,
nallocx(large_sz, MALLOCX_ARENA(arena) | MALLOCX_TCACHE_NONE),
"Expected smalloc(size,flags).size == nallocx(size,flags)");
void **ret = (void **)arg;
@@ -129,7 +129,7 @@ TEST_BEGIN(test_remote_free) {
void *ret;
thd_create(&thd, remote_alloc, (void *)&ret);
thd_join(thd, NULL);
- assert_ptr_not_null(ret, "Unexpected smallocx failure");
+ expect_ptr_not_null(ret, "Unexpected smallocx failure");
/* Avoid TCACHE_NONE to explicitly test tcache_flush(). */
dallocx(ret, 0);
@@ -155,7 +155,7 @@ TEST_BEGIN(test_oom) {
oom = true;
}
}
- assert_true(oom,
+ expect_true(oom,
"Expected OOM during series of calls to smallocx(size=%zu, 0)",
largemax);
for (i = 0; i < sizeof(ptrs) / sizeof(void *); i++) {
@@ -166,14 +166,14 @@ TEST_BEGIN(test_oom) {
purge();
#if LG_SIZEOF_PTR == 3
- assert_ptr_null(smallocx(0x8000000000000000ULL,
+ expect_ptr_null(smallocx(0x8000000000000000ULL,
MALLOCX_ALIGN(0x8000000000000000ULL)).ptr,
"Expected OOM for smallocx()");
- assert_ptr_null(smallocx(0x8000000000000000ULL,
+ expect_ptr_null(smallocx(0x8000000000000000ULL,
MALLOCX_ALIGN(0x80000000)).ptr,
"Expected OOM for smallocx()");
#else
- assert_ptr_null(smallocx(0x80000000UL, MALLOCX_ALIGN(0x80000000UL)).ptr,
+ expect_ptr_null(smallocx(0x80000000UL, MALLOCX_ALIGN(0x80000000UL)).ptr,
"Expected OOM for smallocx()");
#endif
}
@@ -191,36 +191,36 @@ TEST_BEGIN(test_basic) {
size_t nsz, rsz, smz;
void *p;
nsz = nallocx(sz, 0);
- assert_zu_ne(nsz, 0, "Unexpected nallocx() error");
+ expect_zu_ne(nsz, 0, "Unexpected nallocx() error");
ret = smallocx(sz, 0);
p = ret.ptr;
smz = ret.size;
- assert_ptr_not_null(p,
+ expect_ptr_not_null(p,
"Unexpected smallocx(size=%zx, flags=0) error", sz);
rsz = sallocx(p, 0);
- assert_zu_ge(rsz, sz, "Real size smaller than expected");
- assert_zu_eq(nsz, rsz, "nallocx()/sallocx() size mismatch");
- assert_zu_eq(nsz, smz, "nallocx()/smallocx() size mismatch");
+ expect_zu_ge(rsz, sz, "Real size smaller than expected");
+ expect_zu_eq(nsz, rsz, "nallocx()/sallocx() size mismatch");
+ expect_zu_eq(nsz, smz, "nallocx()/smallocx() size mismatch");
dallocx(p, 0);
ret = smallocx(sz, 0);
p = ret.ptr;
smz = ret.size;
- assert_ptr_not_null(p,
+ expect_ptr_not_null(p,
"Unexpected smallocx(size=%zx, flags=0) error", sz);
dallocx(p, 0);
nsz = nallocx(sz, MALLOCX_ZERO);
- assert_zu_ne(nsz, 0, "Unexpected nallocx() error");
- assert_zu_ne(smz, 0, "Unexpected smallocx() error");
+ expect_zu_ne(nsz, 0, "Unexpected nallocx() error");
+ expect_zu_ne(smz, 0, "Unexpected smallocx() error");
ret = smallocx(sz, MALLOCX_ZERO);
p = ret.ptr;
- assert_ptr_not_null(p,
+ expect_ptr_not_null(p,
"Unexpected smallocx(size=%zx, flags=MALLOCX_ZERO) error",
nsz);
rsz = sallocx(p, 0);
- assert_zu_eq(nsz, rsz, "nallocx()/sallocx() rsize mismatch");
- assert_zu_eq(nsz, smz, "nallocx()/smallocx() size mismatch");
+ expect_zu_eq(nsz, rsz, "nallocx()/sallocx() rsize mismatch");
+ expect_zu_eq(nsz, smz, "nallocx()/smallocx() size mismatch");
dallocx(p, 0);
purge();
}
@@ -257,27 +257,27 @@ TEST_BEGIN(test_alignment_and_size) {
for (i = 0; i < NITER; i++) {
nsz = nallocx(sz, MALLOCX_ALIGN(alignment) |
MALLOCX_ZERO);
- assert_zu_ne(nsz, 0,
+ expect_zu_ne(nsz, 0,
"nallocx() error for alignment=%zu, "
"size=%zu (%#zx)", alignment, sz, sz);
smallocx_return_t ret
= smallocx(sz, MALLOCX_ALIGN(alignment) | MALLOCX_ZERO);
ps[i] = ret.ptr;
- assert_ptr_not_null(ps[i],
+ expect_ptr_not_null(ps[i],
"smallocx() error for alignment=%zu, "
"size=%zu (%#zx)", alignment, sz, sz);
rsz = sallocx(ps[i], 0);
smz = ret.size;
- assert_zu_ge(rsz, sz,
+ expect_zu_ge(rsz, sz,
"Real size smaller than expected for "
"alignment=%zu, size=%zu", alignment, sz);
- assert_zu_eq(nsz, rsz,
+ expect_zu_eq(nsz, rsz,
"nallocx()/sallocx() size mismatch for "
"alignment=%zu, size=%zu", alignment, sz);
- assert_zu_eq(nsz, smz,
+ expect_zu_eq(nsz, smz,
"nallocx()/smallocx() size mismatch for "
"alignment=%zu, size=%zu", alignment, sz);
- assert_ptr_null(
+ expect_ptr_null(
(void *)((uintptr_t)ps[i] & (alignment-1)),
"%p inadequately aligned for"
" alignment=%zu, size=%zu", ps[i],
diff --git a/deps/jemalloc/test/integration/thread_arena.c b/deps/jemalloc/test/integration/thread_arena.c
index 1e5ec05d8..4a6abf645 100644
--- a/deps/jemalloc/test/integration/thread_arena.c
+++ b/deps/jemalloc/test/integration/thread_arena.c
@@ -11,7 +11,7 @@ thd_start(void *arg) {
int err;
p = malloc(1);
- assert_ptr_not_null(p, "Error in malloc()");
+ expect_ptr_not_null(p, "Error in malloc()");
free(p);
size = sizeof(arena_ind);
@@ -31,7 +31,7 @@ thd_start(void *arg) {
buferror(err, buf, sizeof(buf));
test_fail("Error in mallctl(): %s", buf);
}
- assert_u_eq(arena_ind, main_arena_ind,
+ expect_u_eq(arena_ind, main_arena_ind,
"Arena index should be same as for main thread");
return NULL;
@@ -52,11 +52,11 @@ TEST_BEGIN(test_thread_arena) {
unsigned i;
p = malloc(1);
- assert_ptr_not_null(p, "Error in malloc()");
+ expect_ptr_not_null(p, "Error in malloc()");
unsigned arena_ind, old_arena_ind;
size_t sz = sizeof(unsigned);
- assert_d_eq(mallctl("arenas.create", (void *)&arena_ind, &sz, NULL, 0),
+ expect_d_eq(mallctl("arenas.create", (void *)&arena_ind, &sz, NULL, 0),
0, "Arena creation failure");
size_t size = sizeof(arena_ind);
@@ -73,7 +73,7 @@ TEST_BEGIN(test_thread_arena) {
for (i = 0; i < NTHREADS; i++) {
intptr_t join_ret;
thd_join(thds[i], (void *)&join_ret);
- assert_zd_eq(join_ret, 0, "Unexpected thread join error");
+ expect_zd_eq(join_ret, 0, "Unexpected thread join error");
}
free(p);
}
diff --git a/deps/jemalloc/test/integration/thread_tcache_enabled.c b/deps/jemalloc/test/integration/thread_tcache_enabled.c
index 95c9acc13..d44dbe904 100644
--- a/deps/jemalloc/test/integration/thread_tcache_enabled.c
+++ b/deps/jemalloc/test/integration/thread_tcache_enabled.c
@@ -4,59 +4,59 @@ void *
thd_start(void *arg) {
bool e0, e1;
size_t sz = sizeof(bool);
- assert_d_eq(mallctl("thread.tcache.enabled", (void *)&e0, &sz, NULL,
+ expect_d_eq(mallctl("thread.tcache.enabled", (void *)&e0, &sz, NULL,
0), 0, "Unexpected mallctl failure");
if (e0) {
e1 = false;
- assert_d_eq(mallctl("thread.tcache.enabled", (void *)&e0, &sz,
+ expect_d_eq(mallctl("thread.tcache.enabled", (void *)&e0, &sz,
(void *)&e1, sz), 0, "Unexpected mallctl() error");
- assert_true(e0, "tcache should be enabled");
+ expect_true(e0, "tcache should be enabled");
}
e1 = true;
- assert_d_eq(mallctl("thread.tcache.enabled", (void *)&e0, &sz,
+ expect_d_eq(mallctl("thread.tcache.enabled", (void *)&e0, &sz,
(void *)&e1, sz), 0, "Unexpected mallctl() error");
- assert_false(e0, "tcache should be disabled");
+ expect_false(e0, "tcache should be disabled");
e1 = true;
- assert_d_eq(mallctl("thread.tcache.enabled", (void *)&e0, &sz,
+ expect_d_eq(mallctl("thread.tcache.enabled", (void *)&e0, &sz,
(void *)&e1, sz), 0, "Unexpected mallctl() error");
- assert_true(e0, "tcache should be enabled");
+ expect_true(e0, "tcache should be enabled");
e1 = false;
- assert_d_eq(mallctl("thread.tcache.enabled", (void *)&e0, &sz,
+ expect_d_eq(mallctl("thread.tcache.enabled", (void *)&e0, &sz,
(void *)&e1, sz), 0, "Unexpected mallctl() error");
- assert_true(e0, "tcache should be enabled");
+ expect_true(e0, "tcache should be enabled");
e1 = false;
- assert_d_eq(mallctl("thread.tcache.enabled", (void *)&e0, &sz,
+ expect_d_eq(mallctl("thread.tcache.enabled", (void *)&e0, &sz,
(void *)&e1, sz), 0, "Unexpected mallctl() error");
- assert_false(e0, "tcache should be disabled");
+ expect_false(e0, "tcache should be disabled");
free(malloc(1));
e1 = true;
- assert_d_eq(mallctl("thread.tcache.enabled", (void *)&e0, &sz,
+ expect_d_eq(mallctl("thread.tcache.enabled", (void *)&e0, &sz,
(void *)&e1, sz), 0, "Unexpected mallctl() error");
- assert_false(e0, "tcache should be disabled");
+ expect_false(e0, "tcache should be disabled");
free(malloc(1));
e1 = true;
- assert_d_eq(mallctl("thread.tcache.enabled", (void *)&e0, &sz,
+ expect_d_eq(mallctl("thread.tcache.enabled", (void *)&e0, &sz,
(void *)&e1, sz), 0, "Unexpected mallctl() error");
- assert_true(e0, "tcache should be enabled");
+ expect_true(e0, "tcache should be enabled");
free(malloc(1));
e1 = false;
- assert_d_eq(mallctl("thread.tcache.enabled", (void *)&e0, &sz,
+ expect_d_eq(mallctl("thread.tcache.enabled", (void *)&e0, &sz,
(void *)&e1, sz), 0, "Unexpected mallctl() error");
- assert_true(e0, "tcache should be enabled");
+ expect_true(e0, "tcache should be enabled");
free(malloc(1));
e1 = false;
- assert_d_eq(mallctl("thread.tcache.enabled", (void *)&e0, &sz,
+ expect_d_eq(mallctl("thread.tcache.enabled", (void *)&e0, &sz,
(void *)&e1, sz), 0, "Unexpected mallctl() error");
- assert_false(e0, "tcache should be disabled");
+ expect_false(e0, "tcache should be disabled");
free(malloc(1));
return NULL;
diff --git a/deps/jemalloc/test/integration/xallocx.c b/deps/jemalloc/test/integration/xallocx.c
index cd0ca048d..137085486 100644
--- a/deps/jemalloc/test/integration/xallocx.c
+++ b/deps/jemalloc/test/integration/xallocx.c
@@ -11,7 +11,7 @@ arena_ind(void) {
if (ind == 0) {
size_t sz = sizeof(ind);
- assert_d_eq(mallctl("arenas.create", (void *)&ind, &sz, NULL,
+ expect_d_eq(mallctl("arenas.create", (void *)&ind, &sz, NULL,
0), 0, "Unexpected mallctl failure creating arena");
}
@@ -23,11 +23,11 @@ TEST_BEGIN(test_same_size) {
size_t sz, tsz;
p = mallocx(42, 0);
- assert_ptr_not_null(p, "Unexpected mallocx() error");
+ expect_ptr_not_null(p, "Unexpected mallocx() error");
sz = sallocx(p, 0);
tsz = xallocx(p, sz, 0, 0);
- assert_zu_eq(tsz, sz, "Unexpected size change: %zu --> %zu", sz, tsz);
+ expect_zu_eq(tsz, sz, "Unexpected size change: %zu --> %zu", sz, tsz);
dallocx(p, 0);
}
@@ -38,11 +38,11 @@ TEST_BEGIN(test_extra_no_move) {
size_t sz, tsz;
p = mallocx(42, 0);
- assert_ptr_not_null(p, "Unexpected mallocx() error");
+ expect_ptr_not_null(p, "Unexpected mallocx() error");
sz = sallocx(p, 0);
tsz = xallocx(p, sz, sz-42, 0);
- assert_zu_eq(tsz, sz, "Unexpected size change: %zu --> %zu", sz, tsz);
+ expect_zu_eq(tsz, sz, "Unexpected size change: %zu --> %zu", sz, tsz);
dallocx(p, 0);
}
@@ -53,11 +53,11 @@ TEST_BEGIN(test_no_move_fail) {
size_t sz, tsz;
p = mallocx(42, 0);
- assert_ptr_not_null(p, "Unexpected mallocx() error");
+ expect_ptr_not_null(p, "Unexpected mallocx() error");
sz = sallocx(p, 0);
tsz = xallocx(p, sz + 5, 0, 0);
- assert_zu_eq(tsz, sz, "Unexpected size change: %zu --> %zu", sz, tsz);
+ expect_zu_eq(tsz, sz, "Unexpected size change: %zu --> %zu", sz, tsz);
dallocx(p, 0);
}
@@ -69,7 +69,7 @@ get_nsizes_impl(const char *cmd) {
size_t z;
z = sizeof(unsigned);
- assert_d_eq(mallctl(cmd, (void *)&ret, &z, NULL, 0), 0,
+ expect_d_eq(mallctl(cmd, (void *)&ret, &z, NULL, 0), 0,
"Unexpected mallctl(\"%s\", ...) failure", cmd);
return ret;
@@ -93,11 +93,11 @@ get_size_impl(const char *cmd, size_t ind) {
size_t miblen = 4;
z = sizeof(size_t);
- assert_d_eq(mallctlnametomib(cmd, mib, &miblen),
+ expect_d_eq(mallctlnametomib(cmd, mib, &miblen),
0, "Unexpected mallctlnametomib(\"%s\", ...) failure", cmd);
mib[2] = ind;
z = sizeof(size_t);
- assert_d_eq(mallctlbymib(mib, miblen, (void *)&ret, &z, NULL, 0),
+ expect_d_eq(mallctlbymib(mib, miblen, (void *)&ret, &z, NULL, 0),
0, "Unexpected mallctlbymib([\"%s\", %zu], ...) failure", cmd, ind);
return ret;
@@ -122,20 +122,20 @@ TEST_BEGIN(test_size) {
largemax = get_large_size(get_nlarge()-1);
p = mallocx(small0, 0);
- assert_ptr_not_null(p, "Unexpected mallocx() error");
+ expect_ptr_not_null(p, "Unexpected mallocx() error");
/* Test smallest supported size. */
- assert_zu_eq(xallocx(p, 1, 0, 0), small0,
+ expect_zu_eq(xallocx(p, 1, 0, 0), small0,
"Unexpected xallocx() behavior");
/* Test largest supported size. */
- assert_zu_le(xallocx(p, largemax, 0, 0), largemax,
+ expect_zu_le(xallocx(p, largemax, 0, 0), largemax,
"Unexpected xallocx() behavior");
/* Test size overflow. */
- assert_zu_le(xallocx(p, largemax+1, 0, 0), largemax,
+ expect_zu_le(xallocx(p, largemax+1, 0, 0), largemax,
"Unexpected xallocx() behavior");
- assert_zu_le(xallocx(p, SIZE_T_MAX, 0, 0), largemax,
+ expect_zu_le(xallocx(p, SIZE_T_MAX, 0, 0), largemax,
"Unexpected xallocx() behavior");
dallocx(p, 0);
@@ -151,22 +151,22 @@ TEST_BEGIN(test_size_extra_overflow) {
largemax = get_large_size(get_nlarge()-1);
p = mallocx(small0, 0);
- assert_ptr_not_null(p, "Unexpected mallocx() error");
+ expect_ptr_not_null(p, "Unexpected mallocx() error");
/* Test overflows that can be resolved by clamping extra. */
- assert_zu_le(xallocx(p, largemax-1, 2, 0), largemax,
+ expect_zu_le(xallocx(p, largemax-1, 2, 0), largemax,
"Unexpected xallocx() behavior");
- assert_zu_le(xallocx(p, largemax, 1, 0), largemax,
+ expect_zu_le(xallocx(p, largemax, 1, 0), largemax,
"Unexpected xallocx() behavior");
/* Test overflow such that largemax-size underflows. */
- assert_zu_le(xallocx(p, largemax+1, 2, 0), largemax,
+ expect_zu_le(xallocx(p, largemax+1, 2, 0), largemax,
"Unexpected xallocx() behavior");
- assert_zu_le(xallocx(p, largemax+2, 3, 0), largemax,
+ expect_zu_le(xallocx(p, largemax+2, 3, 0), largemax,
"Unexpected xallocx() behavior");
- assert_zu_le(xallocx(p, SIZE_T_MAX-2, 2, 0), largemax,
+ expect_zu_le(xallocx(p, SIZE_T_MAX-2, 2, 0), largemax,
"Unexpected xallocx() behavior");
- assert_zu_le(xallocx(p, SIZE_T_MAX-1, 1, 0), largemax,
+ expect_zu_le(xallocx(p, SIZE_T_MAX-1, 1, 0), largemax,
"Unexpected xallocx() behavior");
dallocx(p, 0);
@@ -183,21 +183,21 @@ TEST_BEGIN(test_extra_small) {
largemax = get_large_size(get_nlarge()-1);
p = mallocx(small0, 0);
- assert_ptr_not_null(p, "Unexpected mallocx() error");
+ expect_ptr_not_null(p, "Unexpected mallocx() error");
- assert_zu_eq(xallocx(p, small1, 0, 0), small0,
+ expect_zu_eq(xallocx(p, small1, 0, 0), small0,
"Unexpected xallocx() behavior");
- assert_zu_eq(xallocx(p, small1, 0, 0), small0,
+ expect_zu_eq(xallocx(p, small1, 0, 0), small0,
"Unexpected xallocx() behavior");
- assert_zu_eq(xallocx(p, small0, small1 - small0, 0), small0,
+ expect_zu_eq(xallocx(p, small0, small1 - small0, 0), small0,
"Unexpected xallocx() behavior");
/* Test size+extra overflow. */
- assert_zu_eq(xallocx(p, small0, largemax - small0 + 1, 0), small0,
+ expect_zu_eq(xallocx(p, small0, largemax - small0 + 1, 0), small0,
"Unexpected xallocx() behavior");
- assert_zu_eq(xallocx(p, small0, SIZE_T_MAX - small0, 0), small0,
+ expect_zu_eq(xallocx(p, small0, SIZE_T_MAX - small0, 0), small0,
"Unexpected xallocx() behavior");
dallocx(p, 0);
@@ -217,56 +217,56 @@ TEST_BEGIN(test_extra_large) {
largemax = get_large_size(get_nlarge()-1);
p = mallocx(large3, flags);
- assert_ptr_not_null(p, "Unexpected mallocx() error");
+ expect_ptr_not_null(p, "Unexpected mallocx() error");
- assert_zu_eq(xallocx(p, large3, 0, flags), large3,
+ expect_zu_eq(xallocx(p, large3, 0, flags), large3,
"Unexpected xallocx() behavior");
/* Test size decrease with zero extra. */
- assert_zu_ge(xallocx(p, large1, 0, flags), large1,
+ expect_zu_ge(xallocx(p, large1, 0, flags), large1,
"Unexpected xallocx() behavior");
- assert_zu_ge(xallocx(p, smallmax, 0, flags), large1,
+ expect_zu_ge(xallocx(p, smallmax, 0, flags), large1,
"Unexpected xallocx() behavior");
if (xallocx(p, large3, 0, flags) != large3) {
p = rallocx(p, large3, flags);
- assert_ptr_not_null(p, "Unexpected rallocx() failure");
+ expect_ptr_not_null(p, "Unexpected rallocx() failure");
}
/* Test size decrease with non-zero extra. */
- assert_zu_eq(xallocx(p, large1, large3 - large1, flags), large3,
+ expect_zu_eq(xallocx(p, large1, large3 - large1, flags), large3,
"Unexpected xallocx() behavior");
- assert_zu_eq(xallocx(p, large2, large3 - large2, flags), large3,
+ expect_zu_eq(xallocx(p, large2, large3 - large2, flags), large3,
"Unexpected xallocx() behavior");
- assert_zu_ge(xallocx(p, large1, large2 - large1, flags), large2,
+ expect_zu_ge(xallocx(p, large1, large2 - large1, flags), large2,
"Unexpected xallocx() behavior");
- assert_zu_ge(xallocx(p, smallmax, large1 - smallmax, flags), large1,
+ expect_zu_ge(xallocx(p, smallmax, large1 - smallmax, flags), large1,
"Unexpected xallocx() behavior");
- assert_zu_ge(xallocx(p, large1, 0, flags), large1,
+ expect_zu_ge(xallocx(p, large1, 0, flags), large1,
"Unexpected xallocx() behavior");
/* Test size increase with zero extra. */
- assert_zu_le(xallocx(p, large3, 0, flags), large3,
+ expect_zu_le(xallocx(p, large3, 0, flags), large3,
"Unexpected xallocx() behavior");
- assert_zu_le(xallocx(p, largemax+1, 0, flags), large3,
+ expect_zu_le(xallocx(p, largemax+1, 0, flags), large3,
"Unexpected xallocx() behavior");
- assert_zu_ge(xallocx(p, large1, 0, flags), large1,
+ expect_zu_ge(xallocx(p, large1, 0, flags), large1,
"Unexpected xallocx() behavior");
/* Test size increase with non-zero extra. */
- assert_zu_le(xallocx(p, large1, SIZE_T_MAX - large1, flags), largemax,
+ expect_zu_le(xallocx(p, large1, SIZE_T_MAX - large1, flags), largemax,
"Unexpected xallocx() behavior");
- assert_zu_ge(xallocx(p, large1, 0, flags), large1,
+ expect_zu_ge(xallocx(p, large1, 0, flags), large1,
"Unexpected xallocx() behavior");
/* Test size increase with non-zero extra. */
- assert_zu_le(xallocx(p, large1, large3 - large1, flags), large3,
+ expect_zu_le(xallocx(p, large1, large3 - large1, flags), large3,
"Unexpected xallocx() behavior");
if (xallocx(p, large3, 0, flags) != large3) {
p = rallocx(p, large3, flags);
- assert_ptr_not_null(p, "Unexpected rallocx() failure");
+ expect_ptr_not_null(p, "Unexpected rallocx() failure");
}
/* Test size+extra overflow. */
- assert_zu_le(xallocx(p, large3, largemax - large3 + 1, flags), largemax,
+ expect_zu_le(xallocx(p, large3, largemax - large3 + 1, flags), largemax,
"Unexpected xallocx() behavior");
dallocx(p, flags);
@@ -320,8 +320,8 @@ test_zero(size_t szmin, size_t szmax) {
sz = szmax;
p = mallocx(sz, flags);
- assert_ptr_not_null(p, "Unexpected mallocx() error");
- assert_false(validate_fill(p, 0x00, 0, sz), "Memory not filled: sz=%zu",
+ expect_ptr_not_null(p, "Unexpected mallocx() error");
+ expect_false(validate_fill(p, 0x00, 0, sz), "Memory not filled: sz=%zu",
sz);
/*
@@ -329,30 +329,30 @@ test_zero(size_t szmin, size_t szmax) {
* errors.
*/
memset(p, FILL_BYTE, sz);
- assert_false(validate_fill(p, FILL_BYTE, 0, sz),
+ expect_false(validate_fill(p, FILL_BYTE, 0, sz),
"Memory not filled: sz=%zu", sz);
/* Shrink in place so that we can expect growing in place to succeed. */
sz = szmin;
if (xallocx(p, sz, 0, flags) != sz) {
p = rallocx(p, sz, flags);
- assert_ptr_not_null(p, "Unexpected rallocx() failure");
+ expect_ptr_not_null(p, "Unexpected rallocx() failure");
}
- assert_false(validate_fill(p, FILL_BYTE, 0, sz),
+ expect_false(validate_fill(p, FILL_BYTE, 0, sz),
"Memory not filled: sz=%zu", sz);
for (sz = szmin; sz < szmax; sz = nsz) {
nsz = nallocx(sz+1, flags);
if (xallocx(p, sz+1, 0, flags) != nsz) {
p = rallocx(p, sz+1, flags);
- assert_ptr_not_null(p, "Unexpected rallocx() failure");
+ expect_ptr_not_null(p, "Unexpected rallocx() failure");
}
- assert_false(validate_fill(p, FILL_BYTE, 0, sz),
+ expect_false(validate_fill(p, FILL_BYTE, 0, sz),
"Memory not filled: sz=%zu", sz);
- assert_false(validate_fill(p, 0x00, sz, nsz-sz),
+ expect_false(validate_fill(p, 0x00, sz, nsz-sz),
"Memory not filled: sz=%zu, nsz-sz=%zu", sz, nsz-sz);
memset((void *)((uintptr_t)p + sz), FILL_BYTE, nsz-sz);
- assert_false(validate_fill(p, FILL_BYTE, 0, nsz),
+ expect_false(validate_fill(p, FILL_BYTE, 0, nsz),
"Memory not filled: nsz=%zu", nsz);
}
diff --git a/deps/jemalloc/test/src/mq.c b/deps/jemalloc/test/src/sleep.c
index 9b5f672d6..2234b4bcd 100644
--- a/deps/jemalloc/test/src/mq.c
+++ b/deps/jemalloc/test/src/sleep.c
@@ -5,11 +5,11 @@
* time is guaranteed.
*/
void
-mq_nanosleep(unsigned ns) {
+sleep_ns(unsigned ns) {
assert(ns <= 1000*1000*1000);
#ifdef _WIN32
- Sleep(ns / 1000);
+ Sleep(ns / 1000 / 1000);
#else
{
struct timespec timeout;
diff --git a/deps/jemalloc/test/src/test.c b/deps/jemalloc/test/src/test.c
index f97ce4d18..4cd803e5f 100644
--- a/deps/jemalloc/test/src/test.c
+++ b/deps/jemalloc/test/src/test.c
@@ -87,8 +87,8 @@ test_fail(const char *format, ...) {
}
static const char *
-test_status_string(test_status_t test_status) {
- switch (test_status) {
+test_status_string(test_status_t current_status) {
+ switch (current_status) {
case test_status_pass: return "pass";
case test_status_skip: return "skip";
case test_status_fail: return "fail";
diff --git a/deps/jemalloc/test/src/timer.c b/deps/jemalloc/test/src/timer.c
index c451c6391..6e8b8edbc 100644
--- a/deps/jemalloc/test/src/timer.c
+++ b/deps/jemalloc/test/src/timer.c
@@ -2,8 +2,7 @@
void
timer_start(timedelta_t *timer) {
- nstime_init(&timer->t0, 0);
- nstime_update(&timer->t0);
+ nstime_init_update(&timer->t0);
}
void
diff --git a/deps/jemalloc/test/stress/batch_alloc.c b/deps/jemalloc/test/stress/batch_alloc.c
new file mode 100644
index 000000000..427e1cba8
--- /dev/null
+++ b/deps/jemalloc/test/stress/batch_alloc.c
@@ -0,0 +1,198 @@
+#include "test/jemalloc_test.h"
+#include "test/bench.h"
+
+#define MIBLEN 8
+static size_t mib[MIBLEN];
+static size_t miblen = MIBLEN;
+
+#define TINY_BATCH 10
+#define TINY_BATCH_ITER (10 * 1000 * 1000)
+#define HUGE_BATCH (1000 * 1000)
+#define HUGE_BATCH_ITER 100
+#define LEN (100 * 1000 * 1000)
+static void *batch_ptrs[LEN];
+static size_t batch_ptrs_next = 0;
+static void *item_ptrs[LEN];
+static size_t item_ptrs_next = 0;
+
+#define SIZE 7
+
+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 void
+batch_alloc_wrapper(size_t batch) {
+ batch_alloc_packet_t batch_alloc_packet =
+ {batch_ptrs + batch_ptrs_next, batch, SIZE, 0};
+ size_t filled;
+ size_t len = sizeof(size_t);
+ assert_d_eq(mallctlbymib(mib, miblen, &filled, &len,
+ &batch_alloc_packet, sizeof(batch_alloc_packet)), 0, "");
+ assert_zu_eq(filled, batch, "");
+}
+
+static void
+item_alloc_wrapper(size_t batch) {
+ for (size_t i = item_ptrs_next, end = i + batch; i < end; ++i) {
+ item_ptrs[i] = malloc(SIZE);
+ }
+}
+
+static void
+release_and_clear(void **ptrs, size_t len) {
+ for (size_t i = 0; i < len; ++i) {
+ void *p = ptrs[i];
+ assert_ptr_not_null(p, "allocation failed");
+ sdallocx(p, SIZE, 0);
+ ptrs[i] = NULL;
+ }
+}
+
+static void
+batch_alloc_without_free(size_t batch) {
+ batch_alloc_wrapper(batch);
+ batch_ptrs_next += batch;
+}
+
+static void
+item_alloc_without_free(size_t batch) {
+ item_alloc_wrapper(batch);
+ item_ptrs_next += batch;
+}
+
+static void
+batch_alloc_with_free(size_t batch) {
+ batch_alloc_wrapper(batch);
+ release_and_clear(batch_ptrs + batch_ptrs_next, batch);
+ batch_ptrs_next += batch;
+}
+
+static void
+item_alloc_with_free(size_t batch) {
+ item_alloc_wrapper(batch);
+ release_and_clear(item_ptrs + item_ptrs_next, batch);
+ item_ptrs_next += batch;
+}
+
+static void
+compare_without_free(size_t batch, size_t iter,
+ void (*batch_alloc_without_free_func)(void),
+ void (*item_alloc_without_free_func)(void)) {
+ assert(batch_ptrs_next == 0);
+ assert(item_ptrs_next == 0);
+ assert(batch * iter <= LEN);
+ for (size_t i = 0; i < iter; ++i) {
+ batch_alloc_without_free_func();
+ item_alloc_without_free_func();
+ }
+ release_and_clear(batch_ptrs, batch_ptrs_next);
+ batch_ptrs_next = 0;
+ release_and_clear(item_ptrs, item_ptrs_next);
+ item_ptrs_next = 0;
+ compare_funcs(0, iter,
+ "batch allocation", batch_alloc_without_free_func,
+ "item allocation", item_alloc_without_free_func);
+ release_and_clear(batch_ptrs, batch_ptrs_next);
+ batch_ptrs_next = 0;
+ release_and_clear(item_ptrs, item_ptrs_next);
+ item_ptrs_next = 0;
+}
+
+static void
+compare_with_free(size_t batch, size_t iter,
+ void (*batch_alloc_with_free_func)(void),
+ void (*item_alloc_with_free_func)(void)) {
+ assert(batch_ptrs_next == 0);
+ assert(item_ptrs_next == 0);
+ assert(batch * iter <= LEN);
+ for (size_t i = 0; i < iter; ++i) {
+ batch_alloc_with_free_func();
+ item_alloc_with_free_func();
+ }
+ batch_ptrs_next = 0;
+ item_ptrs_next = 0;
+ compare_funcs(0, iter,
+ "batch allocation", batch_alloc_with_free_func,
+ "item allocation", item_alloc_with_free_func);
+ batch_ptrs_next = 0;
+ item_ptrs_next = 0;
+}
+
+static void
+batch_alloc_without_free_tiny() {
+ batch_alloc_without_free(TINY_BATCH);
+}
+
+static void
+item_alloc_without_free_tiny() {
+ item_alloc_without_free(TINY_BATCH);
+}
+
+TEST_BEGIN(test_tiny_batch_without_free) {
+ compare_without_free(TINY_BATCH, TINY_BATCH_ITER,
+ batch_alloc_without_free_tiny, item_alloc_without_free_tiny);
+}
+TEST_END
+
+static void
+batch_alloc_with_free_tiny() {
+ batch_alloc_with_free(TINY_BATCH);
+}
+
+static void
+item_alloc_with_free_tiny() {
+ item_alloc_with_free(TINY_BATCH);
+}
+
+TEST_BEGIN(test_tiny_batch_with_free) {
+ compare_with_free(TINY_BATCH, TINY_BATCH_ITER,
+ batch_alloc_with_free_tiny, item_alloc_with_free_tiny);
+}
+TEST_END
+
+static void
+batch_alloc_without_free_huge() {
+ batch_alloc_without_free(HUGE_BATCH);
+}
+
+static void
+item_alloc_without_free_huge() {
+ item_alloc_without_free(HUGE_BATCH);
+}
+
+TEST_BEGIN(test_huge_batch_without_free) {
+ compare_without_free(HUGE_BATCH, HUGE_BATCH_ITER,
+ batch_alloc_without_free_huge, item_alloc_without_free_huge);
+}
+TEST_END
+
+static void
+batch_alloc_with_free_huge() {
+ batch_alloc_with_free(HUGE_BATCH);
+}
+
+static void
+item_alloc_with_free_huge() {
+ item_alloc_with_free(HUGE_BATCH);
+}
+
+TEST_BEGIN(test_huge_batch_with_free) {
+ compare_with_free(HUGE_BATCH, HUGE_BATCH_ITER,
+ batch_alloc_with_free_huge, item_alloc_with_free_huge);
+}
+TEST_END
+
+int main(void) {
+ assert_d_eq(mallctlnametomib("experimental.batch_alloc", mib, &miblen),
+ 0, "");
+ return test_no_reentrancy(
+ test_tiny_batch_without_free,
+ test_tiny_batch_with_free,
+ test_huge_batch_without_free,
+ test_huge_batch_with_free);
+}
diff --git a/deps/jemalloc/test/stress/fill_flush.c b/deps/jemalloc/test/stress/fill_flush.c
new file mode 100644
index 000000000..a2db044dd
--- /dev/null
+++ b/deps/jemalloc/test/stress/fill_flush.c
@@ -0,0 +1,76 @@
+#include "test/jemalloc_test.h"
+#include "test/bench.h"
+
+#define SMALL_ALLOC_SIZE 128
+#define LARGE_ALLOC_SIZE SC_LARGE_MINCLASS
+#define NALLOCS 1000
+
+/*
+ * We make this volatile so the 1-at-a-time variants can't leave the allocation
+ * in a register, just to try to get the cache behavior closer.
+ */
+void *volatile allocs[NALLOCS];
+
+static void
+array_alloc_dalloc_small(void) {
+ for (int i = 0; i < NALLOCS; i++) {
+ void *p = mallocx(SMALL_ALLOC_SIZE, 0);
+ assert_ptr_not_null(p, "mallocx shouldn't fail");
+ allocs[i] = p;
+ }
+ for (int i = 0; i < NALLOCS; i++) {
+ sdallocx(allocs[i], SMALL_ALLOC_SIZE, 0);
+ }
+}
+
+static void
+item_alloc_dalloc_small(void) {
+ for (int i = 0; i < NALLOCS; i++) {
+ void *p = mallocx(SMALL_ALLOC_SIZE, 0);
+ assert_ptr_not_null(p, "mallocx shouldn't fail");
+ allocs[i] = p;
+ sdallocx(allocs[i], SMALL_ALLOC_SIZE, 0);
+ }
+}
+
+TEST_BEGIN(test_array_vs_item_small) {
+ compare_funcs(1 * 1000, 10 * 1000,
+ "array of small allocations", array_alloc_dalloc_small,
+ "small item allocation", item_alloc_dalloc_small);
+}
+TEST_END
+
+static void
+array_alloc_dalloc_large(void) {
+ for (int i = 0; i < NALLOCS; i++) {
+ void *p = mallocx(LARGE_ALLOC_SIZE, 0);
+ assert_ptr_not_null(p, "mallocx shouldn't fail");
+ allocs[i] = p;
+ }
+ for (int i = 0; i < NALLOCS; i++) {
+ sdallocx(allocs[i], LARGE_ALLOC_SIZE, 0);
+ }
+}
+
+static void
+item_alloc_dalloc_large(void) {
+ for (int i = 0; i < NALLOCS; i++) {
+ void *p = mallocx(LARGE_ALLOC_SIZE, 0);
+ assert_ptr_not_null(p, "mallocx shouldn't fail");
+ allocs[i] = p;
+ sdallocx(allocs[i], LARGE_ALLOC_SIZE, 0);
+ }
+}
+
+TEST_BEGIN(test_array_vs_item_large) {
+ compare_funcs(100, 1000,
+ "array of large allocations", array_alloc_dalloc_large,
+ "large item allocation", item_alloc_dalloc_large);
+}
+TEST_END
+
+int main(void) {
+ return test_no_reentrancy(
+ test_array_vs_item_small,
+ test_array_vs_item_large);
+}
diff --git a/deps/jemalloc/test/stress/large_microbench.c b/deps/jemalloc/test/stress/large_microbench.c
new file mode 100644
index 000000000..c66b33a1c
--- /dev/null
+++ b/deps/jemalloc/test/stress/large_microbench.c
@@ -0,0 +1,33 @@
+#include "test/jemalloc_test.h"
+#include "test/bench.h"
+
+static void
+large_mallocx_free(void) {
+ /*
+ * We go a bit larger than the large minclass on its own to better
+ * expose costs from things like zeroing.
+ */
+ void *p = mallocx(SC_LARGE_MINCLASS, MALLOCX_TCACHE_NONE);
+ assert_ptr_not_null(p, "mallocx shouldn't fail");
+ free(p);
+}
+
+static void
+small_mallocx_free(void) {
+ void *p = mallocx(16, 0);
+ assert_ptr_not_null(p, "mallocx shouldn't fail");
+ free(p);
+}
+
+TEST_BEGIN(test_large_vs_small) {
+ compare_funcs(100*1000, 1*1000*1000, "large mallocx",
+ large_mallocx_free, "small mallocx", small_mallocx_free);
+}
+TEST_END
+
+int
+main(void) {
+ return test_no_reentrancy(
+ test_large_vs_small);
+}
+
diff --git a/deps/jemalloc/test/stress/mallctl.c b/deps/jemalloc/test/stress/mallctl.c
new file mode 100644
index 000000000..d29b31184
--- /dev/null
+++ b/deps/jemalloc/test/stress/mallctl.c
@@ -0,0 +1,74 @@
+#include "test/jemalloc_test.h"
+#include "test/bench.h"
+
+static void
+mallctl_short(void) {
+ const char *version;
+ size_t sz = sizeof(version);
+ int err = mallctl("version", &version, &sz, NULL, 0);
+ assert_d_eq(err, 0, "mallctl failure");
+}
+
+size_t mib_short[1];
+
+static void
+mallctlbymib_short(void) {
+ size_t miblen = sizeof(mib_short)/sizeof(mib_short[0]);
+ const char *version;
+ size_t sz = sizeof(version);
+ int err = mallctlbymib(mib_short, miblen, &version, &sz, NULL, 0);
+ assert_d_eq(err, 0, "mallctlbymib failure");
+}
+
+TEST_BEGIN(test_mallctl_vs_mallctlbymib_short) {
+ size_t miblen = sizeof(mib_short)/sizeof(mib_short[0]);
+
+ int err = mallctlnametomib("version", mib_short, &miblen);
+ assert_d_eq(err, 0, "mallctlnametomib failure");
+ compare_funcs(10*1000*1000, 10*1000*1000, "mallctl_short",
+ mallctl_short, "mallctlbymib_short", mallctlbymib_short);
+}
+TEST_END
+
+static void
+mallctl_long(void) {
+ uint64_t nmalloc;
+ size_t sz = sizeof(nmalloc);
+ int err = mallctl("stats.arenas.0.bins.0.nmalloc", &nmalloc, &sz, NULL,
+ 0);
+ assert_d_eq(err, 0, "mallctl failure");
+}
+
+size_t mib_long[6];
+
+static void
+mallctlbymib_long(void) {
+ size_t miblen = sizeof(mib_long)/sizeof(mib_long[0]);
+ uint64_t nmalloc;
+ size_t sz = sizeof(nmalloc);
+ int err = mallctlbymib(mib_long, miblen, &nmalloc, &sz, NULL, 0);
+ assert_d_eq(err, 0, "mallctlbymib failure");
+}
+
+TEST_BEGIN(test_mallctl_vs_mallctlbymib_long) {
+ /*
+ * We want to use the longest mallctl we have; that needs stats support
+ * to be allowed.
+ */
+ test_skip_if(!config_stats);
+
+ size_t miblen = sizeof(mib_long)/sizeof(mib_long[0]);
+ int err = mallctlnametomib("stats.arenas.0.bins.0.nmalloc", mib_long,
+ &miblen);
+ assert_d_eq(err, 0, "mallctlnametomib failure");
+ compare_funcs(10*1000*1000, 10*1000*1000, "mallctl_long",
+ mallctl_long, "mallctlbymib_long", mallctlbymib_long);
+}
+TEST_END
+
+int
+main(void) {
+ return test_no_reentrancy(
+ test_mallctl_vs_mallctlbymib_short,
+ test_mallctl_vs_mallctlbymib_long);
+}
diff --git a/deps/jemalloc/test/stress/microbench.c b/deps/jemalloc/test/stress/microbench.c
index 988b7938f..062e32fde 100644
--- a/deps/jemalloc/test/stress/microbench.c
+++ b/deps/jemalloc/test/stress/microbench.c
@@ -1,44 +1,5 @@
#include "test/jemalloc_test.h"
-
-static inline void
-time_func(timedelta_t *timer, uint64_t nwarmup, uint64_t niter,
- void (*func)(void)) {
- uint64_t i;
-
- for (i = 0; i < nwarmup; i++) {
- func();
- }
- timer_start(timer);
- for (i = 0; i < niter; i++) {
- func();
- }
- timer_stop(timer);
-}
-
-void
-compare_funcs(uint64_t nwarmup, uint64_t niter, const char *name_a,
- void (*func_a), const char *name_b, void (*func_b)) {
- timedelta_t timer_a, timer_b;
- char ratio_buf[6];
- void *p;
-
- p = mallocx(1, 0);
- if (p == NULL) {
- test_fail("Unexpected mallocx() failure");
- return;
- }
-
- time_func(&timer_a, nwarmup, niter, func_a);
- time_func(&timer_b, nwarmup, niter, func_b);
-
- timer_ratio(&timer_a, &timer_b, ratio_buf, sizeof(ratio_buf));
- malloc_printf("%"FMTu64" iterations, %s=%"FMTu64"us, "
- "%s=%"FMTu64"us, ratio=1:%s\n",
- niter, name_a, timer_usec(&timer_a), name_b, timer_usec(&timer_b),
- ratio_buf);
-
- dallocx(p, 0);
-}
+#include "test/bench.h"
static void
malloc_free(void) {
@@ -108,7 +69,7 @@ malloc_mus_free(void) {
test_fail("Unexpected malloc() failure");
return;
}
- malloc_usable_size(p);
+ TEST_MALLOC_SIZE(p);
free(p);
}
diff --git a/deps/jemalloc/test/unit/SFMT.c b/deps/jemalloc/test/unit/SFMT.c
index 1fc8cf1bc..b9f85dd92 100644
--- a/deps/jemalloc/test/unit/SFMT.c
+++ b/deps/jemalloc/test/unit/SFMT.c
@@ -1456,7 +1456,7 @@ TEST_BEGIN(test_gen_rand_32) {
uint32_t r32;
sfmt_t *ctx;
- assert_d_le(get_min_array_size32(), BLOCK_SIZE,
+ expect_d_le(get_min_array_size32(), BLOCK_SIZE,
"Array size too small");
ctx = init_gen_rand(1234);
fill_array32(ctx, array32, BLOCK_SIZE);
@@ -1466,16 +1466,16 @@ TEST_BEGIN(test_gen_rand_32) {
ctx = init_gen_rand(1234);
for (i = 0; i < BLOCK_SIZE; i++) {
if (i < COUNT_1) {
- assert_u32_eq(array32[i], init_gen_rand_32_expected[i],
+ expect_u32_eq(array32[i], init_gen_rand_32_expected[i],
"Output mismatch for i=%d", i);
}
r32 = gen_rand32(ctx);
- assert_u32_eq(r32, array32[i],
+ expect_u32_eq(r32, array32[i],
"Mismatch at array32[%d]=%x, gen=%x", i, array32[i], r32);
}
for (i = 0; i < COUNT_2; i++) {
r32 = gen_rand32(ctx);
- assert_u32_eq(r32, array32_2[i],
+ expect_u32_eq(r32, array32_2[i],
"Mismatch at array32_2[%d]=%x, gen=%x", i, array32_2[i],
r32);
}
@@ -1491,7 +1491,7 @@ TEST_BEGIN(test_by_array_32) {
uint32_t r32;
sfmt_t *ctx;
- assert_d_le(get_min_array_size32(), BLOCK_SIZE,
+ expect_d_le(get_min_array_size32(), BLOCK_SIZE,
"Array size too small");
ctx = init_by_array(ini, 4);
fill_array32(ctx, array32, BLOCK_SIZE);
@@ -1501,16 +1501,16 @@ TEST_BEGIN(test_by_array_32) {
ctx = init_by_array(ini, 4);
for (i = 0; i < BLOCK_SIZE; i++) {
if (i < COUNT_1) {
- assert_u32_eq(array32[i], init_by_array_32_expected[i],
+ expect_u32_eq(array32[i], init_by_array_32_expected[i],
"Output mismatch for i=%d", i);
}
r32 = gen_rand32(ctx);
- assert_u32_eq(r32, array32[i],
+ expect_u32_eq(r32, array32[i],
"Mismatch at array32[%d]=%x, gen=%x", i, array32[i], r32);
}
for (i = 0; i < COUNT_2; i++) {
r32 = gen_rand32(ctx);
- assert_u32_eq(r32, array32_2[i],
+ expect_u32_eq(r32, array32_2[i],
"Mismatch at array32_2[%d]=%x, gen=%x", i, array32_2[i],
r32);
}
@@ -1525,7 +1525,7 @@ TEST_BEGIN(test_gen_rand_64) {
uint64_t r;
sfmt_t *ctx;
- assert_d_le(get_min_array_size64(), BLOCK_SIZE64,
+ expect_d_le(get_min_array_size64(), BLOCK_SIZE64,
"Array size too small");
ctx = init_gen_rand(4321);
fill_array64(ctx, array64, BLOCK_SIZE64);
@@ -1535,17 +1535,17 @@ TEST_BEGIN(test_gen_rand_64) {
ctx = init_gen_rand(4321);
for (i = 0; i < BLOCK_SIZE64; i++) {
if (i < COUNT_1) {
- assert_u64_eq(array64[i], init_gen_rand_64_expected[i],
+ expect_u64_eq(array64[i], init_gen_rand_64_expected[i],
"Output mismatch for i=%d", i);
}
r = gen_rand64(ctx);
- assert_u64_eq(r, array64[i],
+ expect_u64_eq(r, array64[i],
"Mismatch at array64[%d]=%"FMTx64", gen=%"FMTx64, i,
array64[i], r);
}
for (i = 0; i < COUNT_2; i++) {
r = gen_rand64(ctx);
- assert_u64_eq(r, array64_2[i],
+ expect_u64_eq(r, array64_2[i],
"Mismatch at array64_2[%d]=%"FMTx64" gen=%"FMTx64"", i,
array64_2[i], r);
}
@@ -1561,7 +1561,7 @@ TEST_BEGIN(test_by_array_64) {
uint32_t ini[] = {5, 4, 3, 2, 1};
sfmt_t *ctx;
- assert_d_le(get_min_array_size64(), BLOCK_SIZE64,
+ expect_d_le(get_min_array_size64(), BLOCK_SIZE64,
"Array size too small");
ctx = init_by_array(ini, 5);
fill_array64(ctx, array64, BLOCK_SIZE64);
@@ -1571,17 +1571,17 @@ TEST_BEGIN(test_by_array_64) {
ctx = init_by_array(ini, 5);
for (i = 0; i < BLOCK_SIZE64; i++) {
if (i < COUNT_1) {
- assert_u64_eq(array64[i], init_by_array_64_expected[i],
+ expect_u64_eq(array64[i], init_by_array_64_expected[i],
"Output mismatch for i=%d", i);
}
r = gen_rand64(ctx);
- assert_u64_eq(r, array64[i],
+ expect_u64_eq(r, array64[i],
"Mismatch at array64[%d]=%"FMTx64" gen=%"FMTx64, i,
array64[i], r);
}
for (i = 0; i < COUNT_2; i++) {
r = gen_rand64(ctx);
- assert_u64_eq(r, array64_2[i],
+ expect_u64_eq(r, array64_2[i],
"Mismatch at array64_2[%d]=%"FMTx64" gen=%"FMTx64, i,
array64_2[i], r);
}
diff --git a/deps/jemalloc/test/unit/a0.c b/deps/jemalloc/test/unit/a0.c
index a27ab3f42..c1be79a66 100644
--- a/deps/jemalloc/test/unit/a0.c
+++ b/deps/jemalloc/test/unit/a0.c
@@ -4,7 +4,7 @@ TEST_BEGIN(test_a0) {
void *p;
p = a0malloc(1);
- assert_ptr_not_null(p, "Unexpected a0malloc() error");
+ expect_ptr_not_null(p, "Unexpected a0malloc() error");
a0dalloc(p);
}
TEST_END
diff --git a/deps/jemalloc/test/unit/arena_decay.c b/deps/jemalloc/test/unit/arena_decay.c
new file mode 100644
index 000000000..e991f4dd1
--- /dev/null
+++ b/deps/jemalloc/test/unit/arena_decay.c
@@ -0,0 +1,436 @@
+#include "test/jemalloc_test.h"
+#include "test/arena_util.h"
+
+#include "jemalloc/internal/ticker.h"
+
+static nstime_monotonic_t *nstime_monotonic_orig;
+static nstime_update_t *nstime_update_orig;
+
+static unsigned nupdates_mock;
+static nstime_t time_mock;
+static bool monotonic_mock;
+
+static bool
+nstime_monotonic_mock(void) {
+ return monotonic_mock;
+}
+
+static void
+nstime_update_mock(nstime_t *time) {
+ nupdates_mock++;
+ if (monotonic_mock) {
+ nstime_copy(time, &time_mock);
+ }
+}
+
+TEST_BEGIN(test_decay_ticks) {
+ test_skip_if(is_background_thread_enabled());
+ test_skip_if(opt_hpa);
+
+ ticker_geom_t *decay_ticker;
+ unsigned tick0, tick1, arena_ind;
+ size_t sz, large0;
+ void *p;
+
+ sz = sizeof(size_t);
+ expect_d_eq(mallctl("arenas.lextent.0.size", (void *)&large0, &sz, NULL,
+ 0), 0, "Unexpected mallctl failure");
+
+ /* Set up a manually managed arena for test. */
+ arena_ind = do_arena_create(0, 0);
+
+ /* Migrate to the new arena, and get the ticker. */
+ unsigned old_arena_ind;
+ size_t sz_arena_ind = sizeof(old_arena_ind);
+ expect_d_eq(mallctl("thread.arena", (void *)&old_arena_ind,
+ &sz_arena_ind, (void *)&arena_ind, sizeof(arena_ind)), 0,
+ "Unexpected mallctl() failure");
+ decay_ticker = tsd_arena_decay_tickerp_get(tsd_fetch());
+ expect_ptr_not_null(decay_ticker,
+ "Unexpected failure getting decay ticker");
+
+ /*
+ * Test the standard APIs using a large size class, since we can't
+ * control tcache interactions for small size classes (except by
+ * completely disabling tcache for the entire test program).
+ */
+
+ /* malloc(). */
+ tick0 = ticker_geom_read(decay_ticker);
+ p = malloc(large0);
+ expect_ptr_not_null(p, "Unexpected malloc() failure");
+ tick1 = ticker_geom_read(decay_ticker);
+ expect_u32_ne(tick1, tick0, "Expected ticker to tick during malloc()");
+ /* free(). */
+ tick0 = ticker_geom_read(decay_ticker);
+ free(p);
+ tick1 = ticker_geom_read(decay_ticker);
+ expect_u32_ne(tick1, tick0, "Expected ticker to tick during free()");
+
+ /* calloc(). */
+ tick0 = ticker_geom_read(decay_ticker);
+ p = calloc(1, large0);
+ expect_ptr_not_null(p, "Unexpected calloc() failure");
+ tick1 = ticker_geom_read(decay_ticker);
+ expect_u32_ne(tick1, tick0, "Expected ticker to tick during calloc()");
+ free(p);
+
+ /* posix_memalign(). */
+ tick0 = ticker_geom_read(decay_ticker);
+ expect_d_eq(posix_memalign(&p, sizeof(size_t), large0), 0,
+ "Unexpected posix_memalign() failure");
+ tick1 = ticker_geom_read(decay_ticker);
+ expect_u32_ne(tick1, tick0,
+ "Expected ticker to tick during posix_memalign()");
+ free(p);
+
+ /* aligned_alloc(). */
+ tick0 = ticker_geom_read(decay_ticker);
+ p = aligned_alloc(sizeof(size_t), large0);
+ expect_ptr_not_null(p, "Unexpected aligned_alloc() failure");
+ tick1 = ticker_geom_read(decay_ticker);
+ expect_u32_ne(tick1, tick0,
+ "Expected ticker to tick during aligned_alloc()");
+ free(p);
+
+ /* realloc(). */
+ /* Allocate. */
+ tick0 = ticker_geom_read(decay_ticker);
+ p = realloc(NULL, large0);
+ expect_ptr_not_null(p, "Unexpected realloc() failure");
+ tick1 = ticker_geom_read(decay_ticker);
+ expect_u32_ne(tick1, tick0, "Expected ticker to tick during realloc()");
+ /* Reallocate. */
+ tick0 = ticker_geom_read(decay_ticker);
+ p = realloc(p, large0);
+ expect_ptr_not_null(p, "Unexpected realloc() failure");
+ tick1 = ticker_geom_read(decay_ticker);
+ expect_u32_ne(tick1, tick0, "Expected ticker to tick during realloc()");
+ /* Deallocate. */
+ tick0 = ticker_geom_read(decay_ticker);
+ realloc(p, 0);
+ tick1 = ticker_geom_read(decay_ticker);
+ expect_u32_ne(tick1, tick0, "Expected ticker to tick during realloc()");
+
+ /*
+ * Test the *allocx() APIs using large and small size classes, with
+ * tcache explicitly disabled.
+ */
+ {
+ unsigned i;
+ size_t allocx_sizes[2];
+ allocx_sizes[0] = large0;
+ allocx_sizes[1] = 1;
+
+ for (i = 0; i < sizeof(allocx_sizes) / sizeof(size_t); i++) {
+ sz = allocx_sizes[i];
+
+ /* mallocx(). */
+ tick0 = ticker_geom_read(decay_ticker);
+ p = mallocx(sz, MALLOCX_TCACHE_NONE);
+ expect_ptr_not_null(p, "Unexpected mallocx() failure");
+ tick1 = ticker_geom_read(decay_ticker);
+ expect_u32_ne(tick1, tick0,
+ "Expected ticker to tick during mallocx() (sz=%zu)",
+ sz);
+ /* rallocx(). */
+ tick0 = ticker_geom_read(decay_ticker);
+ p = rallocx(p, sz, MALLOCX_TCACHE_NONE);
+ expect_ptr_not_null(p, "Unexpected rallocx() failure");
+ tick1 = ticker_geom_read(decay_ticker);
+ expect_u32_ne(tick1, tick0,
+ "Expected ticker to tick during rallocx() (sz=%zu)",
+ sz);
+ /* xallocx(). */
+ tick0 = ticker_geom_read(decay_ticker);
+ xallocx(p, sz, 0, MALLOCX_TCACHE_NONE);
+ tick1 = ticker_geom_read(decay_ticker);
+ expect_u32_ne(tick1, tick0,
+ "Expected ticker to tick during xallocx() (sz=%zu)",
+ sz);
+ /* dallocx(). */
+ tick0 = ticker_geom_read(decay_ticker);
+ dallocx(p, MALLOCX_TCACHE_NONE);
+ tick1 = ticker_geom_read(decay_ticker);
+ expect_u32_ne(tick1, tick0,
+ "Expected ticker to tick during dallocx() (sz=%zu)",
+ sz);
+ /* sdallocx(). */
+ p = mallocx(sz, MALLOCX_TCACHE_NONE);
+ expect_ptr_not_null(p, "Unexpected mallocx() failure");
+ tick0 = ticker_geom_read(decay_ticker);
+ sdallocx(p, sz, MALLOCX_TCACHE_NONE);
+ tick1 = ticker_geom_read(decay_ticker);
+ expect_u32_ne(tick1, tick0,
+ "Expected ticker to tick during sdallocx() "
+ "(sz=%zu)", sz);
+ }
+ }
+
+ /*
+ * Test tcache fill/flush interactions for large and small size classes,
+ * using an explicit tcache.
+ */
+ unsigned tcache_ind, i;
+ size_t tcache_sizes[2];
+ tcache_sizes[0] = large0;
+ tcache_sizes[1] = 1;
+
+ size_t tcache_max, sz_tcache_max;
+ sz_tcache_max = sizeof(tcache_max);
+ expect_d_eq(mallctl("arenas.tcache_max", (void *)&tcache_max,
+ &sz_tcache_max, NULL, 0), 0, "Unexpected mallctl() failure");
+
+ sz = sizeof(unsigned);
+ expect_d_eq(mallctl("tcache.create", (void *)&tcache_ind, &sz,
+ NULL, 0), 0, "Unexpected mallctl failure");
+
+ for (i = 0; i < sizeof(tcache_sizes) / sizeof(size_t); i++) {
+ sz = tcache_sizes[i];
+
+ /* tcache fill. */
+ tick0 = ticker_geom_read(decay_ticker);
+ p = mallocx(sz, MALLOCX_TCACHE(tcache_ind));
+ expect_ptr_not_null(p, "Unexpected mallocx() failure");
+ tick1 = ticker_geom_read(decay_ticker);
+ expect_u32_ne(tick1, tick0,
+ "Expected ticker to tick during tcache fill "
+ "(sz=%zu)", sz);
+ /* tcache flush. */
+ dallocx(p, MALLOCX_TCACHE(tcache_ind));
+ tick0 = ticker_geom_read(decay_ticker);
+ expect_d_eq(mallctl("tcache.flush", NULL, NULL,
+ (void *)&tcache_ind, sizeof(unsigned)), 0,
+ "Unexpected mallctl failure");
+ tick1 = ticker_geom_read(decay_ticker);
+
+ /* Will only tick if it's in tcache. */
+ expect_u32_ne(tick1, tick0,
+ "Expected ticker to tick during tcache flush (sz=%zu)", sz);
+ }
+}
+TEST_END
+
+static void
+decay_ticker_helper(unsigned arena_ind, int flags, bool dirty, ssize_t dt,
+ uint64_t dirty_npurge0, uint64_t muzzy_npurge0, bool terminate_asap) {
+#define NINTERVALS 101
+ nstime_t time, update_interval, decay_ms, deadline;
+
+ nstime_init_update(&time);
+
+ nstime_init2(&decay_ms, dt, 0);
+ nstime_copy(&deadline, &time);
+ nstime_add(&deadline, &decay_ms);
+
+ nstime_init2(&update_interval, dt, 0);
+ nstime_idivide(&update_interval, NINTERVALS);
+
+ /*
+ * Keep q's slab from being deallocated during the looping below. If a
+ * cached slab were to repeatedly come and go during looping, it could
+ * prevent the decay backlog ever becoming empty.
+ */
+ void *p = do_mallocx(1, flags);
+ uint64_t dirty_npurge1, muzzy_npurge1;
+ do {
+ for (unsigned i = 0; i < ARENA_DECAY_NTICKS_PER_UPDATE / 2;
+ i++) {
+ void *q = do_mallocx(1, flags);
+ dallocx(q, flags);
+ }
+ dirty_npurge1 = get_arena_dirty_npurge(arena_ind);
+ muzzy_npurge1 = get_arena_muzzy_npurge(arena_ind);
+
+ nstime_add(&time_mock, &update_interval);
+ nstime_update(&time);
+ } while (nstime_compare(&time, &deadline) <= 0 && ((dirty_npurge1 ==
+ dirty_npurge0 && muzzy_npurge1 == muzzy_npurge0) ||
+ !terminate_asap));
+ dallocx(p, flags);
+
+ if (config_stats) {
+ expect_u64_gt(dirty_npurge1 + muzzy_npurge1, dirty_npurge0 +
+ muzzy_npurge0, "Expected purging to occur");
+ }
+#undef NINTERVALS
+}
+
+TEST_BEGIN(test_decay_ticker) {
+ test_skip_if(is_background_thread_enabled());
+ test_skip_if(opt_hpa);
+#define NPS 2048
+ ssize_t ddt = opt_dirty_decay_ms;
+ ssize_t mdt = opt_muzzy_decay_ms;
+ unsigned arena_ind = do_arena_create(ddt, mdt);
+ int flags = (MALLOCX_ARENA(arena_ind) | MALLOCX_TCACHE_NONE);
+ void *ps[NPS];
+
+ /*
+ * Allocate a bunch of large objects, pause the clock, deallocate every
+ * other object (to fragment virtual memory), restore the clock, then
+ * [md]allocx() in a tight loop while advancing time rapidly to verify
+ * the ticker triggers purging.
+ */
+ size_t large;
+ size_t sz = sizeof(size_t);
+ expect_d_eq(mallctl("arenas.lextent.0.size", (void *)&large, &sz, NULL,
+ 0), 0, "Unexpected mallctl failure");
+
+ do_purge(arena_ind);
+ uint64_t dirty_npurge0 = get_arena_dirty_npurge(arena_ind);
+ uint64_t muzzy_npurge0 = get_arena_muzzy_npurge(arena_ind);
+
+ for (unsigned i = 0; i < NPS; i++) {
+ ps[i] = do_mallocx(large, flags);
+ }
+
+ nupdates_mock = 0;
+ nstime_init_update(&time_mock);
+ monotonic_mock = true;
+
+ nstime_monotonic_orig = nstime_monotonic;
+ nstime_update_orig = nstime_update;
+ nstime_monotonic = nstime_monotonic_mock;
+ nstime_update = nstime_update_mock;
+
+ for (unsigned i = 0; i < NPS; i += 2) {
+ dallocx(ps[i], flags);
+ unsigned nupdates0 = nupdates_mock;
+ do_decay(arena_ind);
+ expect_u_gt(nupdates_mock, nupdates0,
+ "Expected nstime_update() to be called");
+ }
+
+ decay_ticker_helper(arena_ind, flags, true, ddt, dirty_npurge0,
+ muzzy_npurge0, true);
+ decay_ticker_helper(arena_ind, flags, false, ddt+mdt, dirty_npurge0,
+ muzzy_npurge0, false);
+
+ do_arena_destroy(arena_ind);
+
+ nstime_monotonic = nstime_monotonic_orig;
+ nstime_update = nstime_update_orig;
+#undef NPS
+}
+TEST_END
+
+TEST_BEGIN(test_decay_nonmonotonic) {
+ test_skip_if(is_background_thread_enabled());
+ test_skip_if(opt_hpa);
+#define NPS (SMOOTHSTEP_NSTEPS + 1)
+ int flags = (MALLOCX_ARENA(0) | MALLOCX_TCACHE_NONE);
+ void *ps[NPS];
+ uint64_t npurge0 = 0;
+ uint64_t npurge1 = 0;
+ size_t sz, large0;
+ unsigned i, nupdates0;
+
+ sz = sizeof(size_t);
+ expect_d_eq(mallctl("arenas.lextent.0.size", (void *)&large0, &sz, NULL,
+ 0), 0, "Unexpected mallctl failure");
+
+ expect_d_eq(mallctl("arena.0.purge", NULL, NULL, NULL, 0), 0,
+ "Unexpected mallctl failure");
+ do_epoch();
+ sz = sizeof(uint64_t);
+ npurge0 = get_arena_npurge(0);
+
+ nupdates_mock = 0;
+ nstime_init_update(&time_mock);
+ monotonic_mock = false;
+
+ nstime_monotonic_orig = nstime_monotonic;
+ nstime_update_orig = nstime_update;
+ nstime_monotonic = nstime_monotonic_mock;
+ nstime_update = nstime_update_mock;
+
+ for (i = 0; i < NPS; i++) {
+ ps[i] = mallocx(large0, flags);
+ expect_ptr_not_null(ps[i], "Unexpected mallocx() failure");
+ }
+
+ for (i = 0; i < NPS; i++) {
+ dallocx(ps[i], flags);
+ nupdates0 = nupdates_mock;
+ expect_d_eq(mallctl("arena.0.decay", NULL, NULL, NULL, 0), 0,
+ "Unexpected arena.0.decay failure");
+ expect_u_gt(nupdates_mock, nupdates0,
+ "Expected nstime_update() to be called");
+ }
+
+ do_epoch();
+ sz = sizeof(uint64_t);
+ npurge1 = get_arena_npurge(0);
+
+ if (config_stats) {
+ expect_u64_eq(npurge0, npurge1, "Unexpected purging occurred");
+ }
+
+ nstime_monotonic = nstime_monotonic_orig;
+ nstime_update = nstime_update_orig;
+#undef NPS
+}
+TEST_END
+
+TEST_BEGIN(test_decay_now) {
+ test_skip_if(is_background_thread_enabled());
+ test_skip_if(opt_hpa);
+
+ unsigned arena_ind = do_arena_create(0, 0);
+ expect_zu_eq(get_arena_pdirty(arena_ind), 0, "Unexpected dirty pages");
+ expect_zu_eq(get_arena_pmuzzy(arena_ind), 0, "Unexpected muzzy pages");
+ size_t sizes[] = {16, PAGE<<2, HUGEPAGE<<2};
+ /* Verify that dirty/muzzy pages never linger after deallocation. */
+ for (unsigned i = 0; i < sizeof(sizes)/sizeof(size_t); i++) {
+ size_t size = sizes[i];
+ generate_dirty(arena_ind, size);
+ expect_zu_eq(get_arena_pdirty(arena_ind), 0,
+ "Unexpected dirty pages");
+ expect_zu_eq(get_arena_pmuzzy(arena_ind), 0,
+ "Unexpected muzzy pages");
+ }
+ do_arena_destroy(arena_ind);
+}
+TEST_END
+
+TEST_BEGIN(test_decay_never) {
+ test_skip_if(is_background_thread_enabled() || !config_stats);
+ test_skip_if(opt_hpa);
+
+ unsigned arena_ind = do_arena_create(-1, -1);
+ int flags = MALLOCX_ARENA(arena_ind) | MALLOCX_TCACHE_NONE;
+ expect_zu_eq(get_arena_pdirty(arena_ind), 0, "Unexpected dirty pages");
+ expect_zu_eq(get_arena_pmuzzy(arena_ind), 0, "Unexpected muzzy pages");
+ size_t sizes[] = {16, PAGE<<2, HUGEPAGE<<2};
+ void *ptrs[sizeof(sizes)/sizeof(size_t)];
+ for (unsigned i = 0; i < sizeof(sizes)/sizeof(size_t); i++) {
+ ptrs[i] = do_mallocx(sizes[i], flags);
+ }
+ /* Verify that each deallocation generates additional dirty pages. */
+ size_t pdirty_prev = get_arena_pdirty(arena_ind);
+ size_t pmuzzy_prev = get_arena_pmuzzy(arena_ind);
+ expect_zu_eq(pdirty_prev, 0, "Unexpected dirty pages");
+ expect_zu_eq(pmuzzy_prev, 0, "Unexpected muzzy pages");
+ for (unsigned i = 0; i < sizeof(sizes)/sizeof(size_t); i++) {
+ dallocx(ptrs[i], flags);
+ size_t pdirty = get_arena_pdirty(arena_ind);
+ size_t pmuzzy = get_arena_pmuzzy(arena_ind);
+ expect_zu_gt(pdirty + (size_t)get_arena_dirty_purged(arena_ind),
+ pdirty_prev, "Expected dirty pages to increase.");
+ expect_zu_eq(pmuzzy, 0, "Unexpected muzzy pages");
+ pdirty_prev = pdirty;
+ }
+ do_arena_destroy(arena_ind);
+}
+TEST_END
+
+int
+main(void) {
+ return test(
+ test_decay_ticks,
+ test_decay_ticker,
+ test_decay_nonmonotonic,
+ test_decay_now,
+ test_decay_never);
+}
diff --git a/deps/jemalloc/test/unit/arena_decay.sh b/deps/jemalloc/test/unit/arena_decay.sh
new file mode 100644
index 000000000..52f1b2079
--- /dev/null
+++ b/deps/jemalloc/test/unit/arena_decay.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+export MALLOC_CONF="dirty_decay_ms:1000,muzzy_decay_ms:1000,tcache_max:1024"
diff --git a/deps/jemalloc/test/unit/arena_reset.c b/deps/jemalloc/test/unit/arena_reset.c
index b182f31a6..8ef0786cc 100644
--- a/deps/jemalloc/test/unit/arena_reset.c
+++ b/deps/jemalloc/test/unit/arena_reset.c
@@ -13,7 +13,7 @@ get_nsizes_impl(const char *cmd) {
size_t z;
z = sizeof(unsigned);
- assert_d_eq(mallctl(cmd, (void *)&ret, &z, NULL, 0), 0,
+ expect_d_eq(mallctl(cmd, (void *)&ret, &z, NULL, 0), 0,
"Unexpected mallctl(\"%s\", ...) failure", cmd);
return ret;
@@ -37,11 +37,11 @@ get_size_impl(const char *cmd, size_t ind) {
size_t miblen = 4;
z = sizeof(size_t);
- assert_d_eq(mallctlnametomib(cmd, mib, &miblen),
+ expect_d_eq(mallctlnametomib(cmd, mib, &miblen),
0, "Unexpected mallctlnametomib(\"%s\", ...) failure", cmd);
mib[2] = ind;
z = sizeof(size_t);
- assert_d_eq(mallctlbymib(mib, miblen, (void *)&ret, &z, NULL, 0),
+ expect_d_eq(mallctlbymib(mib, miblen, (void *)&ret, &z, NULL, 0),
0, "Unexpected mallctlbymib([\"%s\", %zu], ...) failure", cmd, ind);
return ret;
@@ -60,35 +60,32 @@ get_large_size(size_t ind) {
/* Like ivsalloc(), but safe to call on discarded allocations. */
static size_t
vsalloc(tsdn_t *tsdn, const void *ptr) {
- 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;
}
- if (extent_state_get(extent) != extent_state_active) {
+ if (edata_state_get(full_alloc_ctx.edata) != extent_state_active) {
return 0;
}
- if (szind == SC_NSIZES) {
+ if (full_alloc_ctx.szind == SC_NSIZES) {
return 0;
}
- return sz_index2size(szind);
+ return sz_index2size(full_alloc_ctx.szind);
}
static unsigned
do_arena_create(extent_hooks_t *h) {
unsigned arena_ind;
size_t sz = sizeof(unsigned);
- assert_d_eq(mallctl("arenas.create", (void *)&arena_ind, &sz,
+ expect_d_eq(mallctl("arenas.create", (void *)&arena_ind, &sz,
(void *)(h != NULL ? &h : NULL), (h != NULL ? sizeof(h) : 0)), 0,
"Unexpected mallctl() failure");
return arena_ind;
@@ -108,19 +105,19 @@ do_arena_reset_pre(unsigned arena_ind, void ***ptrs, unsigned *nptrs) {
nlarge = get_nlarge() > NLARGE ? NLARGE : get_nlarge();
*nptrs = nsmall + nlarge;
*ptrs = (void **)malloc(*nptrs * sizeof(void *));
- assert_ptr_not_null(*ptrs, "Unexpected malloc() failure");
+ expect_ptr_not_null(*ptrs, "Unexpected malloc() failure");
/* Allocate objects with a wide range of sizes. */
for (i = 0; i < nsmall; i++) {
sz = get_small_size(i);
(*ptrs)[i] = mallocx(sz, flags);
- assert_ptr_not_null((*ptrs)[i],
+ expect_ptr_not_null((*ptrs)[i],
"Unexpected mallocx(%zu, %#x) failure", sz, flags);
}
for (i = 0; i < nlarge; i++) {
sz = get_large_size(i);
(*ptrs)[nsmall + i] = mallocx(sz, flags);
- assert_ptr_not_null((*ptrs)[i],
+ expect_ptr_not_null((*ptrs)[i],
"Unexpected mallocx(%zu, %#x) failure", sz, flags);
}
@@ -128,7 +125,7 @@ do_arena_reset_pre(unsigned arena_ind, void ***ptrs, unsigned *nptrs) {
/* Verify allocations. */
for (i = 0; i < *nptrs; i++) {
- assert_zu_gt(ivsalloc(tsdn, (*ptrs)[i]), 0,
+ expect_zu_gt(ivsalloc(tsdn, (*ptrs)[i]), 0,
"Allocation should have queryable size");
}
}
@@ -146,7 +143,7 @@ do_arena_reset_post(void **ptrs, unsigned nptrs, unsigned arena_ind) {
}
/* Verify allocations no longer exist. */
for (i = 0; i < nptrs; i++) {
- assert_zu_eq(vsalloc(tsdn, ptrs[i]), 0,
+ expect_zu_eq(vsalloc(tsdn, ptrs[i]), 0,
"Allocation should no longer exist");
}
if (have_background_thread) {
@@ -163,10 +160,10 @@ do_arena_reset_destroy(const char *name, unsigned arena_ind) {
size_t miblen;
miblen = sizeof(mib)/sizeof(size_t);
- assert_d_eq(mallctlnametomib(name, mib, &miblen), 0,
+ expect_d_eq(mallctlnametomib(name, mib, &miblen), 0,
"Unexpected mallctlnametomib() failure");
mib[1] = (size_t)arena_ind;
- assert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0,
+ expect_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0,
"Unexpected mallctlbymib() failure");
}
@@ -200,23 +197,23 @@ arena_i_initialized(unsigned arena_ind, bool refresh) {
if (refresh) {
uint64_t epoch = 1;
- assert_d_eq(mallctl("epoch", NULL, NULL, (void *)&epoch,
+ expect_d_eq(mallctl("epoch", NULL, NULL, (void *)&epoch,
sizeof(epoch)), 0, "Unexpected mallctl() failure");
}
miblen = sizeof(mib)/sizeof(size_t);
- assert_d_eq(mallctlnametomib("arena.0.initialized", mib, &miblen), 0,
+ expect_d_eq(mallctlnametomib("arena.0.initialized", mib, &miblen), 0,
"Unexpected mallctlnametomib() failure");
mib[1] = (size_t)arena_ind;
sz = sizeof(initialized);
- assert_d_eq(mallctlbymib(mib, miblen, (void *)&initialized, &sz, NULL,
+ expect_d_eq(mallctlbymib(mib, miblen, (void *)&initialized, &sz, NULL,
0), 0, "Unexpected mallctlbymib() failure");
return initialized;
}
TEST_BEGIN(test_arena_destroy_initial) {
- assert_false(arena_i_initialized(MALLCTL_ARENAS_DESTROYED, false),
+ expect_false(arena_i_initialized(MALLCTL_ARENAS_DESTROYED, false),
"Destroyed arena stats should not be initialized");
}
TEST_END
@@ -229,9 +226,9 @@ TEST_BEGIN(test_arena_destroy_hooks_default) {
arena_ind = do_arena_create(NULL);
do_arena_reset_pre(arena_ind, &ptrs, &nptrs);
- assert_false(arena_i_initialized(arena_ind, false),
+ expect_false(arena_i_initialized(arena_ind, false),
"Arena stats should not be initialized");
- assert_true(arena_i_initialized(arena_ind, true),
+ expect_true(arena_i_initialized(arena_ind, true),
"Arena stats should be initialized");
/*
@@ -242,9 +239,9 @@ TEST_BEGIN(test_arena_destroy_hooks_default) {
do_arena_destroy(arena_ind);
- assert_false(arena_i_initialized(arena_ind, true),
+ expect_false(arena_i_initialized(arena_ind, true),
"Arena stats should not be initialized");
- assert_true(arena_i_initialized(MALLCTL_ARENAS_DESTROYED, false),
+ expect_true(arena_i_initialized(MALLCTL_ARENAS_DESTROYED, false),
"Destroyed arena stats should be initialized");
do_arena_reset_post(ptrs, nptrs, arena_ind);
@@ -252,12 +249,27 @@ TEST_BEGIN(test_arena_destroy_hooks_default) {
arena_ind_prev = arena_ind;
arena_ind = do_arena_create(NULL);
do_arena_reset_pre(arena_ind, &ptrs, &nptrs);
- assert_u_eq(arena_ind, arena_ind_prev,
+ expect_u_eq(arena_ind, arena_ind_prev,
"Arena index should have been recycled");
do_arena_destroy(arena_ind);
do_arena_reset_post(ptrs, nptrs, arena_ind);
do_arena_destroy(arena_ind_another);
+
+ /* Try arena.create with custom hooks. */
+ size_t sz = sizeof(extent_hooks_t *);
+ extent_hooks_t *a0_default_hooks;
+ expect_d_eq(mallctl("arena.0.extent_hooks", (void *)&a0_default_hooks,
+ &sz, NULL, 0), 0, "Unexpected mallctlnametomib() failure");
+
+ /* Default impl; but wrapped as "customized". */
+ extent_hooks_t new_hooks = *a0_default_hooks;
+ extent_hooks_t *hook = &new_hooks;
+ sz = sizeof(unsigned);
+ expect_d_eq(mallctl("arenas.create", (void *)&arena_ind, &sz,
+ (void *)&hook, sizeof(void *)), 0,
+ "Unexpected mallctl() failure");
+ do_arena_destroy(arena_ind);
}
TEST_END
@@ -271,9 +283,9 @@ extent_dalloc_unmap(extent_hooks_t *extent_hooks, void *addr, size_t size,
TRACE_HOOK("%s(extent_hooks=%p, addr=%p, size=%zu, committed=%s, "
"arena_ind=%u)\n", __func__, extent_hooks, addr, size, committed ?
"true" : "false", arena_ind);
- assert_ptr_eq(extent_hooks, &hooks,
+ expect_ptr_eq(extent_hooks, &hooks,
"extent_hooks should be same as pointer used to set hooks");
- assert_ptr_eq(extent_hooks->dalloc, extent_dalloc_unmap,
+ expect_ptr_eq(extent_hooks->dalloc, extent_dalloc_unmap,
"Wrong hook function");
called_dalloc = true;
if (!try_dalloc) {
@@ -317,20 +329,20 @@ TEST_BEGIN(test_arena_destroy_hooks_unmap) {
arena_ind = do_arena_create(&hooks);
do_arena_reset_pre(arena_ind, &ptrs, &nptrs);
- assert_true(did_alloc, "Expected alloc");
+ expect_true(did_alloc, "Expected alloc");
- assert_false(arena_i_initialized(arena_ind, false),
+ expect_false(arena_i_initialized(arena_ind, false),
"Arena stats should not be initialized");
- assert_true(arena_i_initialized(arena_ind, true),
+ expect_true(arena_i_initialized(arena_ind, true),
"Arena stats should be initialized");
did_dalloc = false;
do_arena_destroy(arena_ind);
- assert_true(did_dalloc, "Expected dalloc");
+ expect_true(did_dalloc, "Expected dalloc");
- assert_false(arena_i_initialized(arena_ind, true),
+ expect_false(arena_i_initialized(arena_ind, true),
"Arena stats should not be initialized");
- assert_true(arena_i_initialized(MALLCTL_ARENAS_DESTROYED, false),
+ expect_true(arena_i_initialized(MALLCTL_ARENAS_DESTROYED, false),
"Destroyed arena stats should be initialized");
do_arena_reset_post(ptrs, nptrs, arena_ind);
diff --git a/deps/jemalloc/test/unit/atomic.c b/deps/jemalloc/test/unit/atomic.c
index 572d8d23f..c2ec8c7e1 100644
--- a/deps/jemalloc/test/unit/atomic.c
+++ b/deps/jemalloc/test/unit/atomic.c
@@ -6,7 +6,7 @@
* some places and "ptr" in others. In the long run it would be nice to unify
* these, but in the short run we'll use this shim.
*/
-#define assert_p_eq assert_ptr_eq
+#define expect_p_eq expect_ptr_eq
/*
* t: the non-atomic type, like "uint32_t".
@@ -24,20 +24,20 @@
\
/* ATOMIC_INIT and load. */ \
val = atomic_load_##ta(&atom, ATOMIC_RELAXED); \
- assert_##ta##_eq(val1, val, "Load or init failed"); \
+ expect_##ta##_eq(val1, val, "Load or init failed"); \
\
/* Store. */ \
atomic_store_##ta(&atom, val1, ATOMIC_RELAXED); \
atomic_store_##ta(&atom, val2, ATOMIC_RELAXED); \
val = atomic_load_##ta(&atom, ATOMIC_RELAXED); \
- assert_##ta##_eq(val2, val, "Store failed"); \
+ expect_##ta##_eq(val2, val, "Store failed"); \
\
/* Exchange. */ \
atomic_store_##ta(&atom, val1, ATOMIC_RELAXED); \
val = atomic_exchange_##ta(&atom, val2, ATOMIC_RELAXED); \
- assert_##ta##_eq(val1, val, "Exchange returned invalid value"); \
+ expect_##ta##_eq(val1, val, "Exchange returned invalid value"); \
val = atomic_load_##ta(&atom, ATOMIC_RELAXED); \
- assert_##ta##_eq(val2, val, "Exchange store invalid value"); \
+ expect_##ta##_eq(val2, val, "Exchange store invalid value"); \
\
/* \
* Weak CAS. Spurious failures are allowed, so we loop a few \
@@ -45,21 +45,21 @@
*/ \
atomic_store_##ta(&atom, val1, ATOMIC_RELAXED); \
success = false; \
- for (int i = 0; i < 10 && !success; i++) { \
+ for (int retry = 0; retry < 10 && !success; retry++) { \
expected = val2; \
success = atomic_compare_exchange_weak_##ta(&atom, \
&expected, val3, ATOMIC_RELAXED, ATOMIC_RELAXED); \
- assert_##ta##_eq(val1, expected, \
+ expect_##ta##_eq(val1, expected, \
"CAS should update expected"); \
} \
- assert_b_eq(val1 == val2, success, \
+ expect_b_eq(val1 == val2, success, \
"Weak CAS did the wrong state update"); \
val = atomic_load_##ta(&atom, ATOMIC_RELAXED); \
if (success) { \
- assert_##ta##_eq(val3, val, \
+ expect_##ta##_eq(val3, val, \
"Successful CAS should update atomic"); \
} else { \
- assert_##ta##_eq(val1, val, \
+ expect_##ta##_eq(val1, val, \
"Unsuccessful CAS should not update atomic"); \
} \
\
@@ -68,14 +68,14 @@
expected = val2; \
success = atomic_compare_exchange_strong_##ta(&atom, &expected, \
val3, ATOMIC_RELAXED, ATOMIC_RELAXED); \
- assert_b_eq(val1 == val2, success, \
+ expect_b_eq(val1 == val2, success, \
"Strong CAS did the wrong state update"); \
val = atomic_load_##ta(&atom, ATOMIC_RELAXED); \
if (success) { \
- assert_##ta##_eq(val3, val, \
+ expect_##ta##_eq(val3, val, \
"Successful CAS should update atomic"); \
} else { \
- assert_##ta##_eq(val1, val, \
+ expect_##ta##_eq(val1, val, \
"Unsuccessful CAS should not update atomic"); \
} \
\
@@ -89,46 +89,46 @@
/* Fetch-add. */ \
atomic_store_##ta(&atom, val1, ATOMIC_RELAXED); \
val = atomic_fetch_add_##ta(&atom, val2, ATOMIC_RELAXED); \
- assert_##ta##_eq(val1, val, \
+ expect_##ta##_eq(val1, val, \
"Fetch-add should return previous value"); \
val = atomic_load_##ta(&atom, ATOMIC_RELAXED); \
- assert_##ta##_eq(val1 + val2, val, \
+ expect_##ta##_eq(val1 + val2, val, \
"Fetch-add should update atomic"); \
\
/* Fetch-sub. */ \
atomic_store_##ta(&atom, val1, ATOMIC_RELAXED); \
val = atomic_fetch_sub_##ta(&atom, val2, ATOMIC_RELAXED); \
- assert_##ta##_eq(val1, val, \
+ expect_##ta##_eq(val1, val, \
"Fetch-sub should return previous value"); \
val = atomic_load_##ta(&atom, ATOMIC_RELAXED); \
- assert_##ta##_eq(val1 - val2, val, \
+ expect_##ta##_eq(val1 - val2, val, \
"Fetch-sub should update atomic"); \
\
/* Fetch-and. */ \
atomic_store_##ta(&atom, val1, ATOMIC_RELAXED); \
val = atomic_fetch_and_##ta(&atom, val2, ATOMIC_RELAXED); \
- assert_##ta##_eq(val1, val, \
+ expect_##ta##_eq(val1, val, \
"Fetch-and should return previous value"); \
val = atomic_load_##ta(&atom, ATOMIC_RELAXED); \
- assert_##ta##_eq(val1 & val2, val, \
+ expect_##ta##_eq(val1 & val2, val, \
"Fetch-and should update atomic"); \
\
/* Fetch-or. */ \
atomic_store_##ta(&atom, val1, ATOMIC_RELAXED); \
val = atomic_fetch_or_##ta(&atom, val2, ATOMIC_RELAXED); \
- assert_##ta##_eq(val1, val, \
+ expect_##ta##_eq(val1, val, \
"Fetch-or should return previous value"); \
val = atomic_load_##ta(&atom, ATOMIC_RELAXED); \
- assert_##ta##_eq(val1 | val2, val, \
+ expect_##ta##_eq(val1 | val2, val, \
"Fetch-or should update atomic"); \
\
/* Fetch-xor. */ \
atomic_store_##ta(&atom, val1, ATOMIC_RELAXED); \
val = atomic_fetch_xor_##ta(&atom, val2, ATOMIC_RELAXED); \
- assert_##ta##_eq(val1, val, \
+ expect_##ta##_eq(val1, val, \
"Fetch-xor should return previous value"); \
val = atomic_load_##ta(&atom, ATOMIC_RELAXED); \
- assert_##ta##_eq(val1 ^ val2, val, \
+ expect_##ta##_eq(val1 ^ val2, val, \
"Fetch-xor should update atomic"); \
} while (0)
diff --git a/deps/jemalloc/test/unit/background_thread.c b/deps/jemalloc/test/unit/background_thread.c
index f7bd37c42..c60010a81 100644
--- a/deps/jemalloc/test/unit/background_thread.c
+++ b/deps/jemalloc/test/unit/background_thread.c
@@ -8,15 +8,15 @@ test_switch_background_thread_ctl(bool new_val) {
size_t sz = sizeof(bool);
e1 = new_val;
- assert_d_eq(mallctl("background_thread", (void *)&e0, &sz,
+ expect_d_eq(mallctl("background_thread", (void *)&e0, &sz,
&e1, sz), 0, "Unexpected mallctl() failure");
- assert_b_eq(e0, !e1,
+ expect_b_eq(e0, !e1,
"background_thread should be %d before.\n", !e1);
if (e1) {
- assert_zu_gt(n_background_threads, 0,
+ expect_zu_gt(n_background_threads, 0,
"Number of background threads should be non zero.\n");
} else {
- assert_zu_eq(n_background_threads, 0,
+ expect_zu_eq(n_background_threads, 0,
"Number of background threads should be zero.\n");
}
}
@@ -27,15 +27,15 @@ test_repeat_background_thread_ctl(bool before) {
size_t sz = sizeof(bool);
e1 = before;
- assert_d_eq(mallctl("background_thread", (void *)&e0, &sz,
+ expect_d_eq(mallctl("background_thread", (void *)&e0, &sz,
&e1, sz), 0, "Unexpected mallctl() failure");
- assert_b_eq(e0, before,
+ expect_b_eq(e0, before,
"background_thread should be %d.\n", before);
if (e1) {
- assert_zu_gt(n_background_threads, 0,
+ expect_zu_gt(n_background_threads, 0,
"Number of background threads should be non zero.\n");
} else {
- assert_zu_eq(n_background_threads, 0,
+ expect_zu_eq(n_background_threads, 0,
"Number of background threads should be zero.\n");
}
}
@@ -46,16 +46,16 @@ TEST_BEGIN(test_background_thread_ctl) {
bool e0, e1;
size_t sz = sizeof(bool);
- assert_d_eq(mallctl("opt.background_thread", (void *)&e0, &sz,
+ expect_d_eq(mallctl("opt.background_thread", (void *)&e0, &sz,
NULL, 0), 0, "Unexpected mallctl() failure");
- assert_d_eq(mallctl("background_thread", (void *)&e1, &sz,
+ expect_d_eq(mallctl("background_thread", (void *)&e1, &sz,
NULL, 0), 0, "Unexpected mallctl() failure");
- assert_b_eq(e0, e1,
+ expect_b_eq(e0, e1,
"Default and opt.background_thread does not match.\n");
if (e0) {
test_switch_background_thread_ctl(false);
}
- assert_zu_eq(n_background_threads, 0,
+ expect_zu_eq(n_background_threads, 0,
"Number of background threads should be 0.\n");
for (unsigned i = 0; i < 4; i++) {
@@ -80,12 +80,11 @@ TEST_BEGIN(test_background_thread_running) {
test_repeat_background_thread_ctl(false);
test_switch_background_thread_ctl(true);
- assert_b_eq(info->state, background_thread_started,
+ expect_b_eq(info->state, background_thread_started,
"Background_thread did not start.\n");
- nstime_t start, now;
- nstime_init(&start, 0);
- nstime_update(&start);
+ nstime_t start;
+ nstime_init_update(&start);
bool ran = false;
while (true) {
@@ -98,10 +97,10 @@ TEST_BEGIN(test_background_thread_running) {
break;
}
- nstime_init(&now, 0);
- nstime_update(&now);
+ nstime_t now;
+ nstime_init_update(&now);
nstime_subtract(&now, &start);
- assert_u64_lt(nstime_sec(&now), 1000,
+ expect_u64_lt(nstime_sec(&now), 1000,
"Background threads did not run for 1000 seconds.");
sleep(1);
}
diff --git a/deps/jemalloc/test/unit/background_thread_enable.c b/deps/jemalloc/test/unit/background_thread_enable.c
index d894e9371..44034ac67 100644
--- a/deps/jemalloc/test/unit/background_thread_enable.c
+++ b/deps/jemalloc/test/unit/background_thread_enable.c
@@ -2,12 +2,8 @@
const char *malloc_conf = "background_thread:false,narenas:1,max_background_threads:20";
-TEST_BEGIN(test_deferred) {
- test_skip_if(!have_background_thread);
-
- unsigned id;
- size_t sz_u = sizeof(unsigned);
-
+static unsigned
+max_test_narenas(void) {
/*
* 10 here is somewhat arbitrary, except insofar as we want to ensure
* that the number of background threads is smaller than the number of
@@ -15,17 +11,32 @@ TEST_BEGIN(test_deferred) {
* cpu to handle background purging, so this is a conservative
* approximation.
*/
- for (unsigned i = 0; i < 10 * ncpus; i++) {
- assert_d_eq(mallctl("arenas.create", &id, &sz_u, NULL, 0), 0,
+ unsigned ret = 10 * ncpus;
+ /* Limit the max to avoid VM exhaustion on 32-bit . */
+ if (ret > 512) {
+ ret = 512;
+ }
+
+ return ret;
+}
+
+TEST_BEGIN(test_deferred) {
+ test_skip_if(!have_background_thread);
+
+ unsigned id;
+ size_t sz_u = sizeof(unsigned);
+
+ for (unsigned i = 0; i < max_test_narenas(); i++) {
+ expect_d_eq(mallctl("arenas.create", &id, &sz_u, NULL, 0), 0,
"Failed to create arena");
}
bool enable = true;
size_t sz_b = sizeof(bool);
- assert_d_eq(mallctl("background_thread", NULL, NULL, &enable, sz_b), 0,
+ expect_d_eq(mallctl("background_thread", NULL, NULL, &enable, sz_b), 0,
"Failed to enable background threads");
enable = false;
- assert_d_eq(mallctl("background_thread", NULL, NULL, &enable, sz_b), 0,
+ expect_d_eq(mallctl("background_thread", NULL, NULL, &enable, sz_b), 0,
"Failed to disable background threads");
}
TEST_END
@@ -36,43 +47,43 @@ TEST_BEGIN(test_max_background_threads) {
size_t max_n_thds;
size_t opt_max_n_thds;
size_t sz_m = sizeof(max_n_thds);
- assert_d_eq(mallctl("opt.max_background_threads",
+ expect_d_eq(mallctl("opt.max_background_threads",
&opt_max_n_thds, &sz_m, NULL, 0), 0,
"Failed to get opt.max_background_threads");
- assert_d_eq(mallctl("max_background_threads", &max_n_thds, &sz_m, NULL,
+ expect_d_eq(mallctl("max_background_threads", &max_n_thds, &sz_m, NULL,
0), 0, "Failed to get max background threads");
- assert_zu_eq(opt_max_n_thds, max_n_thds,
+ expect_zu_eq(opt_max_n_thds, max_n_thds,
"max_background_threads and "
"opt.max_background_threads should match");
- assert_d_eq(mallctl("max_background_threads", NULL, NULL, &max_n_thds,
+ expect_d_eq(mallctl("max_background_threads", NULL, NULL, &max_n_thds,
sz_m), 0, "Failed to set max background threads");
unsigned id;
size_t sz_u = sizeof(unsigned);
- for (unsigned i = 0; i < 10 * ncpus; i++) {
- assert_d_eq(mallctl("arenas.create", &id, &sz_u, NULL, 0), 0,
+ for (unsigned i = 0; i < max_test_narenas(); i++) {
+ expect_d_eq(mallctl("arenas.create", &id, &sz_u, NULL, 0), 0,
"Failed to create arena");
}
bool enable = true;
size_t sz_b = sizeof(bool);
- assert_d_eq(mallctl("background_thread", NULL, NULL, &enable, sz_b), 0,
+ expect_d_eq(mallctl("background_thread", NULL, NULL, &enable, sz_b), 0,
"Failed to enable background threads");
- assert_zu_eq(n_background_threads, max_n_thds,
+ expect_zu_eq(n_background_threads, max_n_thds,
"Number of background threads should not change.\n");
size_t new_max_thds = max_n_thds - 1;
if (new_max_thds > 0) {
- assert_d_eq(mallctl("max_background_threads", NULL, NULL,
+ expect_d_eq(mallctl("max_background_threads", NULL, NULL,
&new_max_thds, sz_m), 0,
"Failed to set max background threads");
- assert_zu_eq(n_background_threads, new_max_thds,
+ expect_zu_eq(n_background_threads, new_max_thds,
"Number of background threads should decrease by 1.\n");
}
new_max_thds = 1;
- assert_d_eq(mallctl("max_background_threads", NULL, NULL, &new_max_thds,
+ expect_d_eq(mallctl("max_background_threads", NULL, NULL, &new_max_thds,
sz_m), 0, "Failed to set max background threads");
- assert_zu_eq(n_background_threads, new_max_thds,
+ expect_zu_eq(n_background_threads, new_max_thds,
"Number of background threads should be 1.\n");
}
TEST_END
diff --git a/deps/jemalloc/test/unit/base.c b/deps/jemalloc/test/unit/base.c
index 6b792cf21..15e04a8ce 100644
--- a/deps/jemalloc/test/unit/base.c
+++ b/deps/jemalloc/test/unit/base.c
@@ -31,26 +31,28 @@ TEST_BEGIN(test_base_hooks_default) {
size_t allocated0, allocated1, resident, mapped, n_thp;
tsdn_t *tsdn = tsd_tsdn(tsd_fetch());
- base = base_new(tsdn, 0, (extent_hooks_t *)&extent_hooks_default);
+ base = base_new(tsdn, 0,
+ (extent_hooks_t *)&ehooks_default_extent_hooks,
+ /* metadata_use_hooks */ true);
if (config_stats) {
base_stats_get(tsdn, base, &allocated0, &resident, &mapped,
&n_thp);
- assert_zu_ge(allocated0, sizeof(base_t),
+ expect_zu_ge(allocated0, sizeof(base_t),
"Base header should count as allocated");
if (opt_metadata_thp == metadata_thp_always) {
- assert_zu_gt(n_thp, 0,
+ expect_zu_gt(n_thp, 0,
"Base should have 1 THP at least.");
}
}
- assert_ptr_not_null(base_alloc(tsdn, base, 42, 1),
+ expect_ptr_not_null(base_alloc(tsdn, base, 42, 1),
"Unexpected base_alloc() failure");
if (config_stats) {
base_stats_get(tsdn, base, &allocated1, &resident, &mapped,
&n_thp);
- assert_zu_ge(allocated1 - allocated0, 42,
+ expect_zu_ge(allocated1 - allocated0, 42,
"At least 42 bytes were allocated by base_alloc()");
}
@@ -73,27 +75,27 @@ TEST_BEGIN(test_base_hooks_null) {
memcpy(&hooks, &hooks_null, sizeof(extent_hooks_t));
tsdn_t *tsdn = tsd_tsdn(tsd_fetch());
- base = base_new(tsdn, 0, &hooks);
- assert_ptr_not_null(base, "Unexpected base_new() failure");
+ base = base_new(tsdn, 0, &hooks, /* metadata_use_hooks */ true);
+ expect_ptr_not_null(base, "Unexpected base_new() failure");
if (config_stats) {
base_stats_get(tsdn, base, &allocated0, &resident, &mapped,
&n_thp);
- assert_zu_ge(allocated0, sizeof(base_t),
+ expect_zu_ge(allocated0, sizeof(base_t),
"Base header should count as allocated");
if (opt_metadata_thp == metadata_thp_always) {
- assert_zu_gt(n_thp, 0,
+ expect_zu_gt(n_thp, 0,
"Base should have 1 THP at least.");
}
}
- assert_ptr_not_null(base_alloc(tsdn, base, 42, 1),
+ expect_ptr_not_null(base_alloc(tsdn, base, 42, 1),
"Unexpected base_alloc() failure");
if (config_stats) {
base_stats_get(tsdn, base, &allocated1, &resident, &mapped,
&n_thp);
- assert_zu_ge(allocated1 - allocated0, 42,
+ expect_zu_ge(allocated1 - allocated0, 42,
"At least 42 bytes were allocated by base_alloc()");
}
@@ -119,9 +121,9 @@ TEST_BEGIN(test_base_hooks_not_null) {
tsdn_t *tsdn = tsd_tsdn(tsd_fetch());
did_alloc = false;
- base = base_new(tsdn, 0, &hooks);
- assert_ptr_not_null(base, "Unexpected base_new() failure");
- assert_true(did_alloc, "Expected alloc");
+ base = base_new(tsdn, 0, &hooks, /* metadata_use_hooks */ true);
+ expect_ptr_not_null(base, "Unexpected base_new() failure");
+ expect_true(did_alloc, "Expected alloc");
/*
* Check for tight packing at specified alignment under simple
@@ -142,21 +144,21 @@ TEST_BEGIN(test_base_hooks_not_null) {
size_t align_ceil = ALIGNMENT_CEILING(alignment,
QUANTUM);
p = base_alloc(tsdn, base, 1, alignment);
- assert_ptr_not_null(p,
+ expect_ptr_not_null(p,
"Unexpected base_alloc() failure");
- assert_ptr_eq(p,
+ expect_ptr_eq(p,
(void *)(ALIGNMENT_CEILING((uintptr_t)p,
alignment)), "Expected quantum alignment");
q = base_alloc(tsdn, base, alignment, alignment);
- assert_ptr_not_null(q,
+ expect_ptr_not_null(q,
"Unexpected base_alloc() failure");
- assert_ptr_eq((void *)((uintptr_t)p + align_ceil), q,
+ expect_ptr_eq((void *)((uintptr_t)p + align_ceil), q,
"Minimal allocation should take up %zu bytes",
align_ceil);
r = base_alloc(tsdn, base, 1, alignment);
- assert_ptr_not_null(r,
+ expect_ptr_not_null(r,
"Unexpected base_alloc() failure");
- assert_ptr_eq((void *)((uintptr_t)q + align_ceil), r,
+ expect_ptr_eq((void *)((uintptr_t)q + align_ceil), r,
"Minimal allocation should take up %zu bytes",
align_ceil);
}
@@ -167,23 +169,23 @@ TEST_BEGIN(test_base_hooks_not_null) {
* that the first block's remaining space is considered for subsequent
* allocation.
*/
- assert_zu_ge(extent_bsize_get(&base->blocks->extent), QUANTUM,
+ expect_zu_ge(edata_bsize_get(&base->blocks->edata), QUANTUM,
"Remainder insufficient for test");
/* Use up all but one quantum of block. */
- while (extent_bsize_get(&base->blocks->extent) > QUANTUM) {
+ while (edata_bsize_get(&base->blocks->edata) > QUANTUM) {
p = base_alloc(tsdn, base, QUANTUM, QUANTUM);
- assert_ptr_not_null(p, "Unexpected base_alloc() failure");
+ expect_ptr_not_null(p, "Unexpected base_alloc() failure");
}
- r_exp = extent_addr_get(&base->blocks->extent);
- assert_zu_eq(base->extent_sn_next, 1, "One extant block expected");
+ r_exp = edata_addr_get(&base->blocks->edata);
+ expect_zu_eq(base->extent_sn_next, 1, "One extant block expected");
q = base_alloc(tsdn, base, QUANTUM + 1, QUANTUM);
- assert_ptr_not_null(q, "Unexpected base_alloc() failure");
- assert_ptr_ne(q, r_exp, "Expected allocation from new block");
- assert_zu_eq(base->extent_sn_next, 2, "Two extant blocks expected");
+ expect_ptr_not_null(q, "Unexpected base_alloc() failure");
+ expect_ptr_ne(q, r_exp, "Expected allocation from new block");
+ expect_zu_eq(base->extent_sn_next, 2, "Two extant blocks expected");
r = base_alloc(tsdn, base, QUANTUM, QUANTUM);
- assert_ptr_not_null(r, "Unexpected base_alloc() failure");
- assert_ptr_eq(r, r_exp, "Expected allocation from first block");
- assert_zu_eq(base->extent_sn_next, 2, "Two extant blocks expected");
+ expect_ptr_not_null(r, "Unexpected base_alloc() failure");
+ expect_ptr_eq(r, r_exp, "Expected allocation from first block");
+ expect_zu_eq(base->extent_sn_next, 2, "Two extant blocks expected");
/*
* Check for proper alignment support when normal blocks are too small.
@@ -198,9 +200,9 @@ TEST_BEGIN(test_base_hooks_not_null) {
for (i = 0; i < sizeof(alignments) / sizeof(size_t); i++) {
size_t alignment = alignments[i];
p = base_alloc(tsdn, base, QUANTUM, alignment);
- assert_ptr_not_null(p,
+ expect_ptr_not_null(p,
"Unexpected base_alloc() failure");
- assert_ptr_eq(p,
+ expect_ptr_eq(p,
(void *)(ALIGNMENT_CEILING((uintptr_t)p,
alignment)), "Expected %zu-byte alignment",
alignment);
@@ -210,11 +212,11 @@ TEST_BEGIN(test_base_hooks_not_null) {
called_dalloc = called_destroy = called_decommit = called_purge_lazy =
called_purge_forced = false;
base_delete(tsdn, base);
- assert_true(called_dalloc, "Expected dalloc call");
- assert_true(!called_destroy, "Unexpected destroy call");
- assert_true(called_decommit, "Expected decommit call");
- assert_true(called_purge_lazy, "Expected purge_lazy call");
- assert_true(called_purge_forced, "Expected purge_forced call");
+ expect_true(called_dalloc, "Expected dalloc call");
+ expect_true(!called_destroy, "Unexpected destroy call");
+ expect_true(called_decommit, "Expected decommit call");
+ expect_true(called_purge_lazy, "Expected purge_lazy call");
+ expect_true(called_purge_forced, "Expected purge_forced call");
try_dalloc = true;
try_destroy = true;
@@ -225,10 +227,39 @@ TEST_BEGIN(test_base_hooks_not_null) {
}
TEST_END
+TEST_BEGIN(test_base_ehooks_get_for_metadata_default_hook) {
+ extent_hooks_prep();
+ memcpy(&hooks, &hooks_not_null, sizeof(extent_hooks_t));
+ base_t *base;
+ tsdn_t *tsdn = tsd_tsdn(tsd_fetch());
+ base = base_new(tsdn, 0, &hooks, /* metadata_use_hooks */ false);
+ ehooks_t *ehooks = base_ehooks_get_for_metadata(base);
+ expect_true(ehooks_are_default(ehooks),
+ "Expected default extent hook functions pointer");
+ base_delete(tsdn, base);
+}
+TEST_END
+
+
+TEST_BEGIN(test_base_ehooks_get_for_metadata_custom_hook) {
+ extent_hooks_prep();
+ memcpy(&hooks, &hooks_not_null, sizeof(extent_hooks_t));
+ base_t *base;
+ tsdn_t *tsdn = tsd_tsdn(tsd_fetch());
+ base = base_new(tsdn, 0, &hooks, /* metadata_use_hooks */ true);
+ ehooks_t *ehooks = base_ehooks_get_for_metadata(base);
+ expect_ptr_eq(&hooks, ehooks_get_extent_hooks_ptr(ehooks),
+ "Expected user-specified extend hook functions pointer");
+ base_delete(tsdn, base);
+}
+TEST_END
+
int
main(void) {
return test(
test_base_hooks_default,
test_base_hooks_null,
- test_base_hooks_not_null);
+ test_base_hooks_not_null,
+ test_base_ehooks_get_for_metadata_default_hook,
+ test_base_ehooks_get_for_metadata_custom_hook);
}
diff --git a/deps/jemalloc/test/unit/batch_alloc.c b/deps/jemalloc/test/unit/batch_alloc.c
new file mode 100644
index 000000000..901c52b1a
--- /dev/null
+++ b/deps/jemalloc/test/unit/batch_alloc.c
@@ -0,0 +1,189 @@
+#include "test/jemalloc_test.h"
+
+#define BATCH_MAX ((1U << 16) + 1024)
+static void *global_ptrs[BATCH_MAX];
+
+#define PAGE_ALIGNED(ptr) (((uintptr_t)ptr & PAGE_MASK) == 0)
+
+static void
+verify_batch_basic(tsd_t *tsd, void **ptrs, size_t batch, size_t usize,
+ bool zero) {
+ for (size_t i = 0; i < batch; ++i) {
+ void *p = ptrs[i];
+ expect_zu_eq(isalloc(tsd_tsdn(tsd), p), usize, "");
+ if (zero) {
+ for (size_t k = 0; k < usize; ++k) {
+ expect_true(*((unsigned char *)p + k) == 0, "");
+ }
+ }
+ }
+}
+
+static void
+verify_batch_locality(tsd_t *tsd, void **ptrs, size_t batch, size_t usize,
+ arena_t *arena, unsigned nregs) {
+ if (config_prof && opt_prof) {
+ /*
+ * Checking batch locality when prof is on is feasible but
+ * complicated, while checking the non-prof case suffices for
+ * unit-test purpose.
+ */
+ return;
+ }
+ for (size_t i = 0, j = 0; i < batch; ++i, ++j) {
+ if (j == nregs) {
+ j = 0;
+ }
+ if (j == 0 && batch - i < nregs) {
+ break;
+ }
+ void *p = ptrs[i];
+ expect_ptr_eq(iaalloc(tsd_tsdn(tsd), p), arena, "");
+ if (j == 0) {
+ expect_true(PAGE_ALIGNED(p), "");
+ continue;
+ }
+ assert(i > 0);
+ void *q = ptrs[i - 1];
+ expect_true((uintptr_t)p > (uintptr_t)q
+ && (size_t)((uintptr_t)p - (uintptr_t)q) == usize, "");
+ }
+}
+
+static void
+release_batch(void **ptrs, size_t batch, size_t size) {
+ for (size_t i = 0; i < batch; ++i) {
+ sdallocx(ptrs[i], size, 0);
+ }
+}
+
+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 size_t
+batch_alloc_wrapper(void **ptrs, size_t num, size_t size, int flags) {
+ batch_alloc_packet_t batch_alloc_packet = {ptrs, num, size, flags};
+ size_t filled;
+ size_t len = sizeof(size_t);
+ assert_d_eq(mallctl("experimental.batch_alloc", &filled, &len,
+ &batch_alloc_packet, sizeof(batch_alloc_packet)), 0, "");
+ return filled;
+}
+
+static void
+test_wrapper(size_t size, size_t alignment, bool zero, unsigned arena_flag) {
+ tsd_t *tsd = tsd_fetch();
+ assert(tsd != NULL);
+ const size_t usize =
+ (alignment != 0 ? sz_sa2u(size, alignment) : sz_s2u(size));
+ const szind_t ind = sz_size2index(usize);
+ const bin_info_t *bin_info = &bin_infos[ind];
+ const unsigned nregs = bin_info->nregs;
+ assert(nregs > 0);
+ arena_t *arena;
+ if (arena_flag != 0) {
+ arena = arena_get(tsd_tsdn(tsd), MALLOCX_ARENA_GET(arena_flag),
+ false);
+ } else {
+ arena = arena_choose(tsd, NULL);
+ }
+ assert(arena != NULL);
+ int flags = arena_flag;
+ if (alignment != 0) {
+ flags |= MALLOCX_ALIGN(alignment);
+ }
+ if (zero) {
+ flags |= MALLOCX_ZERO;
+ }
+
+ /*
+ * Allocate for the purpose of bootstrapping arena_tdata, so that the
+ * change in bin stats won't contaminate the stats to be verified below.
+ */
+ void *p = mallocx(size, flags | MALLOCX_TCACHE_NONE);
+
+ for (size_t i = 0; i < 4; ++i) {
+ size_t base = 0;
+ if (i == 1) {
+ base = nregs;
+ } else if (i == 2) {
+ base = nregs * 2;
+ } else if (i == 3) {
+ base = (1 << 16);
+ }
+ for (int j = -1; j <= 1; ++j) {
+ if (base == 0 && j == -1) {
+ continue;
+ }
+ size_t batch = base + (size_t)j;
+ assert(batch < BATCH_MAX);
+ size_t filled = batch_alloc_wrapper(global_ptrs, batch,
+ size, flags);
+ assert_zu_eq(filled, batch, "");
+ verify_batch_basic(tsd, global_ptrs, batch, usize,
+ zero);
+ verify_batch_locality(tsd, global_ptrs, batch, usize,
+ arena, nregs);
+ release_batch(global_ptrs, batch, usize);
+ }
+ }
+
+ free(p);
+}
+
+TEST_BEGIN(test_batch_alloc) {
+ test_wrapper(11, 0, false, 0);
+}
+TEST_END
+
+TEST_BEGIN(test_batch_alloc_zero) {
+ test_wrapper(11, 0, true, 0);
+}
+TEST_END
+
+TEST_BEGIN(test_batch_alloc_aligned) {
+ test_wrapper(7, 16, false, 0);
+}
+TEST_END
+
+TEST_BEGIN(test_batch_alloc_manual_arena) {
+ unsigned arena_ind;
+ size_t len_unsigned = sizeof(unsigned);
+ assert_d_eq(mallctl("arenas.create", &arena_ind, &len_unsigned, NULL,
+ 0), 0, "");
+ test_wrapper(11, 0, false, MALLOCX_ARENA(arena_ind));
+}
+TEST_END
+
+TEST_BEGIN(test_batch_alloc_large) {
+ size_t size = SC_LARGE_MINCLASS;
+ for (size_t batch = 0; batch < 4; ++batch) {
+ assert(batch < BATCH_MAX);
+ size_t filled = batch_alloc(global_ptrs, batch, size, 0);
+ assert_zu_eq(filled, batch, "");
+ release_batch(global_ptrs, batch, size);
+ }
+ size = tcache_maxclass + 1;
+ for (size_t batch = 0; batch < 4; ++batch) {
+ assert(batch < BATCH_MAX);
+ size_t filled = batch_alloc(global_ptrs, batch, size, 0);
+ assert_zu_eq(filled, batch, "");
+ release_batch(global_ptrs, batch, size);
+ }
+}
+TEST_END
+
+int
+main(void) {
+ return test(
+ test_batch_alloc,
+ test_batch_alloc_zero,
+ test_batch_alloc_aligned,
+ test_batch_alloc_manual_arena,
+ test_batch_alloc_large);
+}
diff --git a/deps/jemalloc/test/unit/batch_alloc.sh b/deps/jemalloc/test/unit/batch_alloc.sh
new file mode 100644
index 000000000..9d81010ac
--- /dev/null
+++ b/deps/jemalloc/test/unit/batch_alloc.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+export MALLOC_CONF="tcache_gc_incr_bytes:2147483648"
diff --git a/deps/jemalloc/test/unit/batch_alloc_prof.c b/deps/jemalloc/test/unit/batch_alloc_prof.c
new file mode 100644
index 000000000..ef6445861
--- /dev/null
+++ b/deps/jemalloc/test/unit/batch_alloc_prof.c
@@ -0,0 +1 @@
+#include "batch_alloc.c"
diff --git a/deps/jemalloc/test/unit/batch_alloc_prof.sh b/deps/jemalloc/test/unit/batch_alloc_prof.sh
new file mode 100644
index 000000000..a2697a610
--- /dev/null
+++ b/deps/jemalloc/test/unit/batch_alloc_prof.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+export MALLOC_CONF="prof:true,lg_prof_sample:14"
diff --git a/deps/jemalloc/test/unit/binshard.c b/deps/jemalloc/test/unit/binshard.c
index d7a8df8fc..040ea54d2 100644
--- a/deps/jemalloc/test/unit/binshard.c
+++ b/deps/jemalloc/test/unit/binshard.c
@@ -13,7 +13,7 @@ thd_producer(void *varg) {
sz = sizeof(arena);
/* Remote arena. */
- assert_d_eq(mallctl("arenas.create", (void *)&arena, &sz, NULL, 0), 0,
+ expect_d_eq(mallctl("arenas.create", (void *)&arena, &sz, NULL, 0), 0,
"Unexpected mallctl() failure");
for (i = 0; i < REMOTE_NALLOC / 2; i++) {
mem[i] = mallocx(1, MALLOCX_TCACHE_NONE | MALLOCX_ARENA(arena));
@@ -42,7 +42,7 @@ TEST_BEGIN(test_producer_consumer) {
/* Remote deallocation by the current thread. */
for (i = 0; i < NTHREADS; i++) {
for (unsigned j = 0; j < REMOTE_NALLOC; j++) {
- assert_ptr_not_null(mem[i][j],
+ expect_ptr_not_null(mem[i][j],
"Unexpected remote allocation failure");
dallocx(mem[i][j], 0);
}
@@ -53,7 +53,7 @@ TEST_END
static void *
thd_start(void *varg) {
void *ptr, *ptr2;
- extent_t *extent;
+ edata_t *edata;
unsigned shard1, shard2;
tsdn_t *tsdn = tsdn_fetch();
@@ -62,15 +62,15 @@ thd_start(void *varg) {
ptr = mallocx(1, MALLOCX_TCACHE_NONE);
ptr2 = mallocx(129, MALLOCX_TCACHE_NONE);
- extent = iealloc(tsdn, ptr);
- shard1 = extent_binshard_get(extent);
+ edata = emap_edata_lookup(tsdn, &arena_emap_global, ptr);
+ shard1 = edata_binshard_get(edata);
dallocx(ptr, 0);
- assert_u_lt(shard1, 16, "Unexpected bin shard used");
+ expect_u_lt(shard1, 16, "Unexpected bin shard used");
- extent = iealloc(tsdn, ptr2);
- shard2 = extent_binshard_get(extent);
+ edata = emap_edata_lookup(tsdn, &arena_emap_global, ptr2);
+ shard2 = edata_binshard_get(edata);
dallocx(ptr2, 0);
- assert_u_lt(shard2, 4, "Unexpected bin shard used");
+ expect_u_lt(shard2, 4, "Unexpected bin shard used");
if (shard1 > 0 || shard2 > 0) {
/* Triggered sharded bin usage. */
@@ -98,7 +98,7 @@ TEST_BEGIN(test_bin_shard_mt) {
sharded = true;
}
}
- assert_b_eq(sharded, true, "Did not find sharded bins");
+ expect_b_eq(sharded, true, "Did not find sharded bins");
}
TEST_END
@@ -108,14 +108,14 @@ TEST_BEGIN(test_bin_shard) {
size_t miblen, miblen2, len;
len = sizeof(nbins);
- assert_d_eq(mallctl("arenas.nbins", (void *)&nbins, &len, NULL, 0), 0,
+ expect_d_eq(mallctl("arenas.nbins", (void *)&nbins, &len, NULL, 0), 0,
"Unexpected mallctl() failure");
miblen = 4;
- assert_d_eq(mallctlnametomib("arenas.bin.0.nshards", mib, &miblen), 0,
+ expect_d_eq(mallctlnametomib("arenas.bin.0.nshards", mib, &miblen), 0,
"Unexpected mallctlnametomib() failure");
miblen2 = 4;
- assert_d_eq(mallctlnametomib("arenas.bin.0.size", mib2, &miblen2), 0,
+ expect_d_eq(mallctlnametomib("arenas.bin.0.size", mib2, &miblen2), 0,
"Unexpected mallctlnametomib() failure");
for (i = 0; i < nbins; i++) {
@@ -124,22 +124,22 @@ TEST_BEGIN(test_bin_shard) {
mib[2] = i;
sz1 = sizeof(nshards);
- assert_d_eq(mallctlbymib(mib, miblen, (void *)&nshards, &sz1,
+ expect_d_eq(mallctlbymib(mib, miblen, (void *)&nshards, &sz1,
NULL, 0), 0, "Unexpected mallctlbymib() failure");
mib2[2] = i;
sz2 = sizeof(size);
- assert_d_eq(mallctlbymib(mib2, miblen2, (void *)&size, &sz2,
+ expect_d_eq(mallctlbymib(mib2, miblen2, (void *)&size, &sz2,
NULL, 0), 0, "Unexpected mallctlbymib() failure");
if (size >= 1 && size <= 128) {
- assert_u_eq(nshards, 16, "Unexpected nshards");
+ expect_u_eq(nshards, 16, "Unexpected nshards");
} else if (size == 256) {
- assert_u_eq(nshards, 8, "Unexpected nshards");
+ expect_u_eq(nshards, 8, "Unexpected nshards");
} else if (size > 128 && size <= 512) {
- assert_u_eq(nshards, 4, "Unexpected nshards");
+ expect_u_eq(nshards, 4, "Unexpected nshards");
} else {
- assert_u_eq(nshards, 1, "Unexpected nshards");
+ expect_u_eq(nshards, 1, "Unexpected nshards");
}
}
}
diff --git a/deps/jemalloc/test/unit/bit_util.c b/deps/jemalloc/test/unit/bit_util.c
index b747deb43..7d31b2109 100644
--- a/deps/jemalloc/test/unit/bit_util.c
+++ b/deps/jemalloc/test/unit/bit_util.c
@@ -6,27 +6,27 @@
unsigned i, pow2; \
t x; \
\
- assert_##suf##_eq(pow2_ceil_##suf(0), 0, "Unexpected result"); \
+ expect_##suf##_eq(pow2_ceil_##suf(0), 0, "Unexpected result"); \
\
for (i = 0; i < sizeof(t) * 8; i++) { \
- assert_##suf##_eq(pow2_ceil_##suf(((t)1) << i), ((t)1) \
+ expect_##suf##_eq(pow2_ceil_##suf(((t)1) << i), ((t)1) \
<< i, "Unexpected result"); \
} \
\
for (i = 2; i < sizeof(t) * 8; i++) { \
- assert_##suf##_eq(pow2_ceil_##suf((((t)1) << i) - 1), \
+ expect_##suf##_eq(pow2_ceil_##suf((((t)1) << i) - 1), \
((t)1) << i, "Unexpected result"); \
} \
\
for (i = 0; i < sizeof(t) * 8 - 1; i++) { \
- assert_##suf##_eq(pow2_ceil_##suf((((t)1) << i) + 1), \
+ expect_##suf##_eq(pow2_ceil_##suf((((t)1) << i) + 1), \
((t)1) << (i+1), "Unexpected result"); \
} \
\
for (pow2 = 1; pow2 < 25; pow2++) { \
for (x = (((t)1) << (pow2-1)) + 1; x <= ((t)1) << pow2; \
x++) { \
- assert_##suf##_eq(pow2_ceil_##suf(x), \
+ expect_##suf##_eq(pow2_ceil_##suf(x), \
((t)1) << pow2, \
"Unexpected result, x=%"pri, x); \
} \
@@ -49,35 +49,35 @@ TEST_BEGIN(test_pow2_ceil_zu) {
TEST_END
void
-assert_lg_ceil_range(size_t input, unsigned answer) {
+expect_lg_ceil_range(size_t input, unsigned answer) {
if (input == 1) {
- assert_u_eq(0, answer, "Got %u as lg_ceil of 1", answer);
+ expect_u_eq(0, answer, "Got %u as lg_ceil of 1", answer);
return;
}
- assert_zu_le(input, (ZU(1) << answer),
+ expect_zu_le(input, (ZU(1) << answer),
"Got %u as lg_ceil of %zu", answer, input);
- assert_zu_gt(input, (ZU(1) << (answer - 1)),
+ expect_zu_gt(input, (ZU(1) << (answer - 1)),
"Got %u as lg_ceil of %zu", answer, input);
}
void
-assert_lg_floor_range(size_t input, unsigned answer) {
+expect_lg_floor_range(size_t input, unsigned answer) {
if (input == 1) {
- assert_u_eq(0, answer, "Got %u as lg_floor of 1", answer);
+ expect_u_eq(0, answer, "Got %u as lg_floor of 1", answer);
return;
}
- assert_zu_ge(input, (ZU(1) << answer),
+ expect_zu_ge(input, (ZU(1) << answer),
"Got %u as lg_floor of %zu", answer, input);
- assert_zu_lt(input, (ZU(1) << (answer + 1)),
+ expect_zu_lt(input, (ZU(1) << (answer + 1)),
"Got %u as lg_floor of %zu", answer, input);
}
TEST_BEGIN(test_lg_ceil_floor) {
for (size_t i = 1; i < 10 * 1000 * 1000; i++) {
- assert_lg_ceil_range(i, lg_ceil(i));
- assert_lg_ceil_range(i, LG_CEIL(i));
- assert_lg_floor_range(i, lg_floor(i));
- assert_lg_floor_range(i, LG_FLOOR(i));
+ expect_lg_ceil_range(i, lg_ceil(i));
+ expect_lg_ceil_range(i, LG_CEIL(i));
+ expect_lg_floor_range(i, lg_floor(i));
+ expect_lg_floor_range(i, LG_FLOOR(i));
}
for (int i = 10; i < 8 * (1 << LG_SIZEOF_PTR) - 5; i++) {
for (size_t j = 0; j < (1 << 4); j++) {
@@ -85,27 +85,223 @@ TEST_BEGIN(test_lg_ceil_floor) {
- j * ((size_t)1 << (i - 4));
size_t num2 = ((size_t)1 << i)
+ j * ((size_t)1 << (i - 4));
- assert_zu_ne(num1, 0, "Invalid lg argument");
- assert_zu_ne(num2, 0, "Invalid lg argument");
- assert_lg_ceil_range(num1, lg_ceil(num1));
- assert_lg_ceil_range(num1, LG_CEIL(num1));
- assert_lg_ceil_range(num2, lg_ceil(num2));
- assert_lg_ceil_range(num2, LG_CEIL(num2));
-
- assert_lg_floor_range(num1, lg_floor(num1));
- assert_lg_floor_range(num1, LG_FLOOR(num1));
- assert_lg_floor_range(num2, lg_floor(num2));
- assert_lg_floor_range(num2, LG_FLOOR(num2));
+ expect_zu_ne(num1, 0, "Invalid lg argument");
+ expect_zu_ne(num2, 0, "Invalid lg argument");
+ expect_lg_ceil_range(num1, lg_ceil(num1));
+ expect_lg_ceil_range(num1, LG_CEIL(num1));
+ expect_lg_ceil_range(num2, lg_ceil(num2));
+ expect_lg_ceil_range(num2, LG_CEIL(num2));
+
+ expect_lg_floor_range(num1, lg_floor(num1));
+ expect_lg_floor_range(num1, LG_FLOOR(num1));
+ expect_lg_floor_range(num2, lg_floor(num2));
+ expect_lg_floor_range(num2, LG_FLOOR(num2));
+ }
+ }
+}
+TEST_END
+
+#define TEST_FFS(t, suf, test_suf, pri) do { \
+ for (unsigned i = 0; i < sizeof(t) * 8; i++) { \
+ for (unsigned j = 0; j <= i; j++) { \
+ for (unsigned k = 0; k <= j; k++) { \
+ t x = (t)1 << i; \
+ x |= (t)1 << j; \
+ x |= (t)1 << k; \
+ expect_##test_suf##_eq(ffs_##suf(x), k, \
+ "Unexpected result, x=%"pri, x); \
+ } \
+ } \
+ } \
+} while(0)
+
+TEST_BEGIN(test_ffs_u) {
+ TEST_FFS(unsigned, u, u,"u");
+}
+TEST_END
+
+TEST_BEGIN(test_ffs_lu) {
+ TEST_FFS(unsigned long, lu, lu, "lu");
+}
+TEST_END
+
+TEST_BEGIN(test_ffs_llu) {
+ TEST_FFS(unsigned long long, llu, qd, "llu");
+}
+TEST_END
+
+TEST_BEGIN(test_ffs_u32) {
+ TEST_FFS(uint32_t, u32, u32, FMTu32);
+}
+TEST_END
+
+TEST_BEGIN(test_ffs_u64) {
+ TEST_FFS(uint64_t, u64, u64, FMTu64);
+}
+TEST_END
+
+TEST_BEGIN(test_ffs_zu) {
+ TEST_FFS(size_t, zu, zu, "zu");
+}
+TEST_END
+
+#define TEST_FLS(t, suf, test_suf, pri) do { \
+ for (unsigned i = 0; i < sizeof(t) * 8; i++) { \
+ for (unsigned j = 0; j <= i; j++) { \
+ for (unsigned k = 0; k <= j; k++) { \
+ t x = (t)1 << i; \
+ x |= (t)1 << j; \
+ x |= (t)1 << k; \
+ expect_##test_suf##_eq(fls_##suf(x), i, \
+ "Unexpected result, x=%"pri, x); \
+ } \
+ } \
+ } \
+} while(0)
+
+TEST_BEGIN(test_fls_u) {
+ TEST_FLS(unsigned, u, u,"u");
+}
+TEST_END
+
+TEST_BEGIN(test_fls_lu) {
+ TEST_FLS(unsigned long, lu, lu, "lu");
+}
+TEST_END
+
+TEST_BEGIN(test_fls_llu) {
+ TEST_FLS(unsigned long long, llu, qd, "llu");
+}
+TEST_END
+
+TEST_BEGIN(test_fls_u32) {
+ TEST_FLS(uint32_t, u32, u32, FMTu32);
+}
+TEST_END
+
+TEST_BEGIN(test_fls_u64) {
+ TEST_FLS(uint64_t, u64, u64, FMTu64);
+}
+TEST_END
+
+TEST_BEGIN(test_fls_zu) {
+ TEST_FLS(size_t, zu, zu, "zu");
+}
+TEST_END
+
+TEST_BEGIN(test_fls_u_slow) {
+ TEST_FLS(unsigned, u_slow, u,"u");
+}
+TEST_END
+
+TEST_BEGIN(test_fls_lu_slow) {
+ TEST_FLS(unsigned long, lu_slow, lu, "lu");
+}
+TEST_END
+
+TEST_BEGIN(test_fls_llu_slow) {
+ TEST_FLS(unsigned long long, llu_slow, qd, "llu");
+}
+TEST_END
+
+static unsigned
+popcount_byte(unsigned byte) {
+ int count = 0;
+ for (int i = 0; i < 8; i++) {
+ if ((byte & (1 << i)) != 0) {
+ count++;
}
}
+ return count;
+}
+
+static uint64_t
+expand_byte_to_mask(unsigned byte) {
+ uint64_t result = 0;
+ for (int i = 0; i < 8; i++) {
+ if ((byte & (1 << i)) != 0) {
+ result |= ((uint64_t)0xFF << (i * 8));
+ }
+ }
+ return result;
+}
+
+#define TEST_POPCOUNT(t, suf, pri_hex) do { \
+ t bmul = (t)0x0101010101010101ULL; \
+ for (unsigned i = 0; i < (1 << sizeof(t)); i++) { \
+ for (unsigned j = 0; j < 256; j++) { \
+ /* \
+ * Replicate the byte j into various \
+ * bytes of the integer (as indicated by the \
+ * mask in i), and ensure that the popcount of \
+ * the result is popcount(i) * popcount(j) \
+ */ \
+ t mask = (t)expand_byte_to_mask(i); \
+ t x = (bmul * j) & mask; \
+ expect_u_eq( \
+ popcount_byte(i) * popcount_byte(j), \
+ popcount_##suf(x), \
+ "Unexpected result, x=0x%"pri_hex, x); \
+ } \
+ } \
+} while (0)
+
+TEST_BEGIN(test_popcount_u) {
+ TEST_POPCOUNT(unsigned, u, "x");
+}
+TEST_END
+
+TEST_BEGIN(test_popcount_u_slow) {
+ TEST_POPCOUNT(unsigned, u_slow, "x");
+}
+TEST_END
+
+TEST_BEGIN(test_popcount_lu) {
+ TEST_POPCOUNT(unsigned long, lu, "lx");
+}
+TEST_END
+
+TEST_BEGIN(test_popcount_lu_slow) {
+ TEST_POPCOUNT(unsigned long, lu_slow, "lx");
+}
+TEST_END
+
+TEST_BEGIN(test_popcount_llu) {
+ TEST_POPCOUNT(unsigned long long, llu, "llx");
+}
+TEST_END
+
+TEST_BEGIN(test_popcount_llu_slow) {
+ TEST_POPCOUNT(unsigned long long, llu_slow, "llx");
}
TEST_END
int
main(void) {
- return test(
+ return test_no_reentrancy(
test_pow2_ceil_u64,
test_pow2_ceil_u32,
test_pow2_ceil_zu,
- test_lg_ceil_floor);
+ test_lg_ceil_floor,
+ test_ffs_u,
+ test_ffs_lu,
+ test_ffs_llu,
+ test_ffs_u32,
+ test_ffs_u64,
+ test_ffs_zu,
+ test_fls_u,
+ test_fls_lu,
+ test_fls_llu,
+ test_fls_u32,
+ test_fls_u64,
+ test_fls_zu,
+ test_fls_u_slow,
+ test_fls_lu_slow,
+ test_fls_llu_slow,
+ test_popcount_u,
+ test_popcount_u_slow,
+ test_popcount_lu,
+ test_popcount_lu_slow,
+ test_popcount_llu,
+ test_popcount_llu_slow);
}
diff --git a/deps/jemalloc/test/unit/bitmap.c b/deps/jemalloc/test/unit/bitmap.c
index cafb2039e..78e542b67 100644
--- a/deps/jemalloc/test/unit/bitmap.c
+++ b/deps/jemalloc/test/unit/bitmap.c
@@ -1,124 +1,34 @@
#include "test/jemalloc_test.h"
-#define NBITS_TAB \
- NB( 1) \
- NB( 2) \
- NB( 3) \
- NB( 4) \
- NB( 5) \
- NB( 6) \
- NB( 7) \
- NB( 8) \
- NB( 9) \
- NB(10) \
- NB(11) \
- NB(12) \
- NB(13) \
- NB(14) \
- NB(15) \
- NB(16) \
- NB(17) \
- NB(18) \
- NB(19) \
- NB(20) \
- NB(21) \
- NB(22) \
- NB(23) \
- NB(24) \
- NB(25) \
- NB(26) \
- NB(27) \
- NB(28) \
- NB(29) \
- NB(30) \
- NB(31) \
- NB(32) \
- \
- NB(33) \
- NB(34) \
- NB(35) \
- NB(36) \
- NB(37) \
- NB(38) \
- NB(39) \
- NB(40) \
- NB(41) \
- NB(42) \
- NB(43) \
- NB(44) \
- NB(45) \
- NB(46) \
- NB(47) \
- NB(48) \
- NB(49) \
- NB(50) \
- NB(51) \
- NB(52) \
- NB(53) \
- NB(54) \
- NB(55) \
- NB(56) \
- NB(57) \
- NB(58) \
- NB(59) \
- NB(60) \
- NB(61) \
- NB(62) \
- NB(63) \
- NB(64) \
- NB(65) \
- \
- NB(126) \
- NB(127) \
- NB(128) \
- NB(129) \
- NB(130) \
- \
- NB(254) \
- NB(255) \
- NB(256) \
- NB(257) \
- NB(258) \
- \
- NB(510) \
- NB(511) \
- NB(512) \
- NB(513) \
- NB(514) \
- \
- NB(1024) \
- NB(2048) \
- NB(4096) \
- NB(8192) \
- NB(16384) \
+#include "test/nbits.h"
static void
test_bitmap_initializer_body(const bitmap_info_t *binfo, size_t nbits) {
bitmap_info_t binfo_dyn;
bitmap_info_init(&binfo_dyn, nbits);
- assert_zu_eq(bitmap_size(binfo), bitmap_size(&binfo_dyn),
+ expect_zu_eq(bitmap_size(binfo), bitmap_size(&binfo_dyn),
"Unexpected difference between static and dynamic initialization, "
"nbits=%zu", nbits);
- assert_zu_eq(binfo->nbits, binfo_dyn.nbits,
+ expect_zu_eq(binfo->nbits, binfo_dyn.nbits,
"Unexpected difference between static and dynamic initialization, "
"nbits=%zu", nbits);
#ifdef BITMAP_USE_TREE
- assert_u_eq(binfo->nlevels, binfo_dyn.nlevels,
+ expect_u_eq(binfo->nlevels, binfo_dyn.nlevels,
"Unexpected difference between static and dynamic initialization, "
"nbits=%zu", nbits);
{
unsigned i;
for (i = 0; i < binfo->nlevels; i++) {
- assert_zu_eq(binfo->levels[i].group_offset,
+ expect_zu_eq(binfo->levels[i].group_offset,
binfo_dyn.levels[i].group_offset,
"Unexpected difference between static and dynamic "
"initialization, nbits=%zu, level=%u", nbits, i);
}
}
#else
- assert_zu_eq(binfo->ngroups, binfo_dyn.ngroups,
+ expect_zu_eq(binfo->ngroups, binfo_dyn.ngroups,
"Unexpected difference between static and dynamic initialization");
#endif
}
@@ -140,9 +50,9 @@ static size_t
test_bitmap_size_body(const bitmap_info_t *binfo, size_t nbits,
size_t prev_size) {
size_t size = bitmap_size(binfo);
- assert_zu_ge(size, (nbits >> 3),
+ expect_zu_ge(size, (nbits >> 3),
"Bitmap size is smaller than expected");
- assert_zu_ge(size, prev_size, "Bitmap size is smaller than expected");
+ expect_zu_ge(size, prev_size, "Bitmap size is smaller than expected");
return size;
}
@@ -170,17 +80,17 @@ static void
test_bitmap_init_body(const bitmap_info_t *binfo, size_t nbits) {
size_t i;
bitmap_t *bitmap = (bitmap_t *)malloc(bitmap_size(binfo));
- assert_ptr_not_null(bitmap, "Unexpected malloc() failure");
+ expect_ptr_not_null(bitmap, "Unexpected malloc() failure");
bitmap_init(bitmap, binfo, false);
for (i = 0; i < nbits; i++) {
- assert_false(bitmap_get(bitmap, binfo, i),
+ expect_false(bitmap_get(bitmap, binfo, i),
"Bit should be unset");
}
bitmap_init(bitmap, binfo, true);
for (i = 0; i < nbits; i++) {
- assert_true(bitmap_get(bitmap, binfo, i), "Bit should be set");
+ expect_true(bitmap_get(bitmap, binfo, i), "Bit should be set");
}
free(bitmap);
@@ -207,13 +117,13 @@ static void
test_bitmap_set_body(const bitmap_info_t *binfo, size_t nbits) {
size_t i;
bitmap_t *bitmap = (bitmap_t *)malloc(bitmap_size(binfo));
- assert_ptr_not_null(bitmap, "Unexpected malloc() failure");
+ expect_ptr_not_null(bitmap, "Unexpected malloc() failure");
bitmap_init(bitmap, binfo, false);
for (i = 0; i < nbits; i++) {
bitmap_set(bitmap, binfo, i);
}
- assert_true(bitmap_full(bitmap, binfo), "All bits should be set");
+ expect_true(bitmap_full(bitmap, binfo), "All bits should be set");
free(bitmap);
}
@@ -238,20 +148,20 @@ static void
test_bitmap_unset_body(const bitmap_info_t *binfo, size_t nbits) {
size_t i;
bitmap_t *bitmap = (bitmap_t *)malloc(bitmap_size(binfo));
- assert_ptr_not_null(bitmap, "Unexpected malloc() failure");
+ expect_ptr_not_null(bitmap, "Unexpected malloc() failure");
bitmap_init(bitmap, binfo, false);
for (i = 0; i < nbits; i++) {
bitmap_set(bitmap, binfo, i);
}
- assert_true(bitmap_full(bitmap, binfo), "All bits should be set");
+ expect_true(bitmap_full(bitmap, binfo), "All bits should be set");
for (i = 0; i < nbits; i++) {
bitmap_unset(bitmap, binfo, i);
}
for (i = 0; i < nbits; i++) {
bitmap_set(bitmap, binfo, i);
}
- assert_true(bitmap_full(bitmap, binfo), "All bits should be set");
+ expect_true(bitmap_full(bitmap, binfo), "All bits should be set");
free(bitmap);
}
@@ -275,25 +185,25 @@ TEST_END
static void
test_bitmap_xfu_body(const bitmap_info_t *binfo, size_t nbits) {
bitmap_t *bitmap = (bitmap_t *)malloc(bitmap_size(binfo));
- assert_ptr_not_null(bitmap, "Unexpected malloc() failure");
+ expect_ptr_not_null(bitmap, "Unexpected malloc() failure");
bitmap_init(bitmap, binfo, false);
/* Iteratively set bits starting at the beginning. */
for (size_t i = 0; i < nbits; i++) {
- assert_zu_eq(bitmap_ffu(bitmap, binfo, 0), i,
+ expect_zu_eq(bitmap_ffu(bitmap, binfo, 0), i,
"First unset bit should be just after previous first unset "
"bit");
- assert_zu_eq(bitmap_ffu(bitmap, binfo, (i > 0) ? i-1 : i), i,
+ expect_zu_eq(bitmap_ffu(bitmap, binfo, (i > 0) ? i-1 : i), i,
"First unset bit should be just after previous first unset "
"bit");
- assert_zu_eq(bitmap_ffu(bitmap, binfo, i), i,
+ expect_zu_eq(bitmap_ffu(bitmap, binfo, i), i,
"First unset bit should be just after previous first unset "
"bit");
- assert_zu_eq(bitmap_sfu(bitmap, binfo), i,
+ expect_zu_eq(bitmap_sfu(bitmap, binfo), i,
"First unset bit should be just after previous first unset "
"bit");
}
- assert_true(bitmap_full(bitmap, binfo), "All bits should be set");
+ expect_true(bitmap_full(bitmap, binfo), "All bits should be set");
/*
* Iteratively unset bits starting at the end, and verify that
@@ -301,17 +211,17 @@ test_bitmap_xfu_body(const bitmap_info_t *binfo, size_t nbits) {
*/
for (size_t i = nbits - 1; i < nbits; i--) { /* (nbits..0] */
bitmap_unset(bitmap, binfo, i);
- assert_zu_eq(bitmap_ffu(bitmap, binfo, 0), i,
+ expect_zu_eq(bitmap_ffu(bitmap, binfo, 0), i,
"First unset bit should the bit previously unset");
- assert_zu_eq(bitmap_ffu(bitmap, binfo, (i > 0) ? i-1 : i), i,
+ expect_zu_eq(bitmap_ffu(bitmap, binfo, (i > 0) ? i-1 : i), i,
"First unset bit should the bit previously unset");
- assert_zu_eq(bitmap_ffu(bitmap, binfo, i), i,
+ expect_zu_eq(bitmap_ffu(bitmap, binfo, i), i,
"First unset bit should the bit previously unset");
- assert_zu_eq(bitmap_sfu(bitmap, binfo), i,
+ expect_zu_eq(bitmap_sfu(bitmap, binfo), i,
"First unset bit should the bit previously unset");
bitmap_unset(bitmap, binfo, i);
}
- assert_false(bitmap_get(bitmap, binfo, 0), "Bit should be unset");
+ expect_false(bitmap_get(bitmap, binfo, 0), "Bit should be unset");
/*
* Iteratively set bits starting at the beginning, and verify that
@@ -319,29 +229,29 @@ test_bitmap_xfu_body(const bitmap_info_t *binfo, size_t nbits) {
*/
for (size_t i = 1; i < nbits; i++) {
bitmap_set(bitmap, binfo, i - 1);
- assert_zu_eq(bitmap_ffu(bitmap, binfo, 0), i,
+ expect_zu_eq(bitmap_ffu(bitmap, binfo, 0), i,
"First unset bit should be just after the bit previously "
"set");
- assert_zu_eq(bitmap_ffu(bitmap, binfo, (i > 0) ? i-1 : i), i,
+ expect_zu_eq(bitmap_ffu(bitmap, binfo, (i > 0) ? i-1 : i), i,
"First unset bit should be just after the bit previously "
"set");
- assert_zu_eq(bitmap_ffu(bitmap, binfo, i), i,
+ expect_zu_eq(bitmap_ffu(bitmap, binfo, i), i,
"First unset bit should be just after the bit previously "
"set");
- assert_zu_eq(bitmap_sfu(bitmap, binfo), i,
+ expect_zu_eq(bitmap_sfu(bitmap, binfo), i,
"First unset bit should be just after the bit previously "
"set");
bitmap_unset(bitmap, binfo, i);
}
- assert_zu_eq(bitmap_ffu(bitmap, binfo, 0), nbits - 1,
+ expect_zu_eq(bitmap_ffu(bitmap, binfo, 0), nbits - 1,
"First unset bit should be the last bit");
- assert_zu_eq(bitmap_ffu(bitmap, binfo, (nbits > 1) ? nbits-2 : nbits-1),
+ expect_zu_eq(bitmap_ffu(bitmap, binfo, (nbits > 1) ? nbits-2 : nbits-1),
nbits - 1, "First unset bit should be the last bit");
- assert_zu_eq(bitmap_ffu(bitmap, binfo, nbits - 1), nbits - 1,
+ expect_zu_eq(bitmap_ffu(bitmap, binfo, nbits - 1), nbits - 1,
"First unset bit should be the last bit");
- assert_zu_eq(bitmap_sfu(bitmap, binfo), nbits - 1,
+ expect_zu_eq(bitmap_sfu(bitmap, binfo), nbits - 1,
"First unset bit should be the last bit");
- assert_true(bitmap_full(bitmap, binfo), "All bits should be set");
+ expect_true(bitmap_full(bitmap, binfo), "All bits should be set");
/*
* Bubble a "usu" pattern through the bitmap and verify that
@@ -352,22 +262,22 @@ test_bitmap_xfu_body(const bitmap_info_t *binfo, size_t nbits) {
bitmap_unset(bitmap, binfo, i);
bitmap_unset(bitmap, binfo, i+2);
if (i > 0) {
- assert_zu_eq(bitmap_ffu(bitmap, binfo, i-1), i,
+ expect_zu_eq(bitmap_ffu(bitmap, binfo, i-1), i,
"Unexpected first unset bit");
}
- assert_zu_eq(bitmap_ffu(bitmap, binfo, i), i,
+ expect_zu_eq(bitmap_ffu(bitmap, binfo, i), i,
"Unexpected first unset bit");
- assert_zu_eq(bitmap_ffu(bitmap, binfo, i+1), i+2,
+ expect_zu_eq(bitmap_ffu(bitmap, binfo, i+1), i+2,
"Unexpected first unset bit");
- assert_zu_eq(bitmap_ffu(bitmap, binfo, i+2), i+2,
+ expect_zu_eq(bitmap_ffu(bitmap, binfo, i+2), i+2,
"Unexpected first unset bit");
if (i + 3 < nbits) {
- assert_zu_eq(bitmap_ffu(bitmap, binfo, i+3),
+ expect_zu_eq(bitmap_ffu(bitmap, binfo, i+3),
nbits, "Unexpected first unset bit");
}
- assert_zu_eq(bitmap_sfu(bitmap, binfo), i,
+ expect_zu_eq(bitmap_sfu(bitmap, binfo), i,
"Unexpected first unset bit");
- assert_zu_eq(bitmap_sfu(bitmap, binfo), i+2,
+ expect_zu_eq(bitmap_sfu(bitmap, binfo), i+2,
"Unexpected first unset bit");
}
}
@@ -382,20 +292,20 @@ test_bitmap_xfu_body(const bitmap_info_t *binfo, size_t nbits) {
for (size_t i = 0; i < nbits-1; i++) {
bitmap_unset(bitmap, binfo, i);
if (i > 0) {
- assert_zu_eq(bitmap_ffu(bitmap, binfo, i-1), i,
+ expect_zu_eq(bitmap_ffu(bitmap, binfo, i-1), i,
"Unexpected first unset bit");
}
- assert_zu_eq(bitmap_ffu(bitmap, binfo, i), i,
+ expect_zu_eq(bitmap_ffu(bitmap, binfo, i), i,
"Unexpected first unset bit");
- assert_zu_eq(bitmap_ffu(bitmap, binfo, i+1), nbits-1,
+ expect_zu_eq(bitmap_ffu(bitmap, binfo, i+1), nbits-1,
"Unexpected first unset bit");
- assert_zu_eq(bitmap_ffu(bitmap, binfo, nbits-1),
+ expect_zu_eq(bitmap_ffu(bitmap, binfo, nbits-1),
nbits-1, "Unexpected first unset bit");
- assert_zu_eq(bitmap_sfu(bitmap, binfo), i,
+ expect_zu_eq(bitmap_sfu(bitmap, binfo), i,
"Unexpected first unset bit");
}
- assert_zu_eq(bitmap_sfu(bitmap, binfo), nbits-1,
+ expect_zu_eq(bitmap_sfu(bitmap, binfo), nbits-1,
"Unexpected first unset bit");
}
@@ -403,9 +313,11 @@ test_bitmap_xfu_body(const bitmap_info_t *binfo, size_t nbits) {
}
TEST_BEGIN(test_bitmap_xfu) {
- size_t nbits;
+ size_t nbits, nbits_max;
- for (nbits = 1; nbits <= BITMAP_MAXBITS; nbits++) {
+ /* The test is O(n^2); large page sizes may slow down too much. */
+ nbits_max = BITMAP_MAXBITS > 512 ? 512 : BITMAP_MAXBITS;
+ for (nbits = 1; nbits <= nbits_max; nbits++) {
bitmap_info_t binfo;
bitmap_info_init(&binfo, nbits);
test_bitmap_xfu_body(&binfo, nbits);
diff --git a/deps/jemalloc/test/unit/buf_writer.c b/deps/jemalloc/test/unit/buf_writer.c
new file mode 100644
index 000000000..d5e63a0e3
--- /dev/null
+++ b/deps/jemalloc/test/unit/buf_writer.c
@@ -0,0 +1,196 @@
+#include "test/jemalloc_test.h"
+
+#include "jemalloc/internal/buf_writer.h"
+
+#define TEST_BUF_SIZE 16
+#define UNIT_MAX (TEST_BUF_SIZE * 3)
+
+static size_t test_write_len;
+static char test_buf[TEST_BUF_SIZE];
+static uint64_t arg;
+static uint64_t arg_store;
+
+static void
+test_write_cb(void *cbopaque, const char *s) {
+ size_t prev_test_write_len = test_write_len;
+ test_write_len += strlen(s); /* only increase the length */
+ arg_store = *(uint64_t *)cbopaque; /* only pass along the argument */
+ assert_zu_le(prev_test_write_len, test_write_len,
+ "Test write overflowed");
+}
+
+static void
+test_buf_writer_body(tsdn_t *tsdn, buf_writer_t *buf_writer) {
+ char s[UNIT_MAX + 1];
+ size_t n_unit, remain, i;
+ ssize_t unit;
+
+ assert(buf_writer->buf != NULL);
+ memset(s, 'a', UNIT_MAX);
+ arg = 4; /* Starting value of random argument. */
+ arg_store = arg;
+ for (unit = UNIT_MAX; unit >= 0; --unit) {
+ /* unit keeps decreasing, so strlen(s) is always unit. */
+ s[unit] = '\0';
+ for (n_unit = 1; n_unit <= 3; ++n_unit) {
+ test_write_len = 0;
+ remain = 0;
+ for (i = 1; i <= n_unit; ++i) {
+ arg = prng_lg_range_u64(&arg, 64);
+ buf_writer_cb(buf_writer, s);
+ remain += unit;
+ if (remain > buf_writer->buf_size) {
+ /* Flushes should have happened. */
+ assert_u64_eq(arg_store, arg, "Call "
+ "back argument didn't get through");
+ remain %= buf_writer->buf_size;
+ if (remain == 0) {
+ /* Last flush should be lazy. */
+ remain += buf_writer->buf_size;
+ }
+ }
+ assert_zu_eq(test_write_len + remain, i * unit,
+ "Incorrect length after writing %zu strings"
+ " of length %zu", i, unit);
+ }
+ buf_writer_flush(buf_writer);
+ expect_zu_eq(test_write_len, n_unit * unit,
+ "Incorrect length after flushing at the end of"
+ " writing %zu strings of length %zu", n_unit, unit);
+ }
+ }
+ buf_writer_terminate(tsdn, buf_writer);
+}
+
+TEST_BEGIN(test_buf_write_static) {
+ buf_writer_t buf_writer;
+ tsdn_t *tsdn = tsdn_fetch();
+ assert_false(buf_writer_init(tsdn, &buf_writer, test_write_cb, &arg,
+ test_buf, TEST_BUF_SIZE),
+ "buf_writer_init() should not encounter error on static buffer");
+ test_buf_writer_body(tsdn, &buf_writer);
+}
+TEST_END
+
+TEST_BEGIN(test_buf_write_dynamic) {
+ buf_writer_t buf_writer;
+ tsdn_t *tsdn = tsdn_fetch();
+ assert_false(buf_writer_init(tsdn, &buf_writer, test_write_cb, &arg,
+ NULL, TEST_BUF_SIZE), "buf_writer_init() should not OOM");
+ test_buf_writer_body(tsdn, &buf_writer);
+}
+TEST_END
+
+TEST_BEGIN(test_buf_write_oom) {
+ buf_writer_t buf_writer;
+ tsdn_t *tsdn = tsdn_fetch();
+ assert_true(buf_writer_init(tsdn, &buf_writer, test_write_cb, &arg,
+ NULL, SC_LARGE_MAXCLASS + 1), "buf_writer_init() should OOM");
+ assert(buf_writer.buf == NULL);
+
+ char s[UNIT_MAX + 1];
+ size_t n_unit, i;
+ ssize_t unit;
+
+ memset(s, 'a', UNIT_MAX);
+ arg = 4; /* Starting value of random argument. */
+ arg_store = arg;
+ for (unit = UNIT_MAX; unit >= 0; unit -= UNIT_MAX / 4) {
+ /* unit keeps decreasing, so strlen(s) is always unit. */
+ s[unit] = '\0';
+ for (n_unit = 1; n_unit <= 3; ++n_unit) {
+ test_write_len = 0;
+ for (i = 1; i <= n_unit; ++i) {
+ arg = prng_lg_range_u64(&arg, 64);
+ buf_writer_cb(&buf_writer, s);
+ assert_u64_eq(arg_store, arg,
+ "Call back argument didn't get through");
+ assert_zu_eq(test_write_len, i * unit,
+ "Incorrect length after writing %zu strings"
+ " of length %zu", i, unit);
+ }
+ buf_writer_flush(&buf_writer);
+ expect_zu_eq(test_write_len, n_unit * unit,
+ "Incorrect length after flushing at the end of"
+ " writing %zu strings of length %zu", n_unit, unit);
+ }
+ }
+ buf_writer_terminate(tsdn, &buf_writer);
+}
+TEST_END
+
+static int test_read_count;
+static size_t test_read_len;
+static uint64_t arg_sum;
+
+ssize_t
+test_read_cb(void *cbopaque, void *buf, size_t limit) {
+ static uint64_t rand = 4;
+
+ arg_sum += *(uint64_t *)cbopaque;
+ assert_zu_gt(limit, 0, "Limit for read_cb must be positive");
+ --test_read_count;
+ if (test_read_count == 0) {
+ return -1;
+ } else {
+ size_t read_len = limit;
+ if (limit > 1) {
+ rand = prng_range_u64(&rand, (uint64_t)limit);
+ read_len -= (size_t)rand;
+ }
+ assert(read_len > 0);
+ memset(buf, 'a', read_len);
+ size_t prev_test_read_len = test_read_len;
+ test_read_len += read_len;
+ assert_zu_le(prev_test_read_len, test_read_len,
+ "Test read overflowed");
+ return read_len;
+ }
+}
+
+static void
+test_buf_writer_pipe_body(tsdn_t *tsdn, buf_writer_t *buf_writer) {
+ arg = 4; /* Starting value of random argument. */
+ for (int count = 5; count > 0; --count) {
+ arg = prng_lg_range_u64(&arg, 64);
+ arg_sum = 0;
+ test_read_count = count;
+ test_read_len = 0;
+ test_write_len = 0;
+ buf_writer_pipe(buf_writer, test_read_cb, &arg);
+ assert(test_read_count == 0);
+ expect_u64_eq(arg_sum, arg * count, "");
+ expect_zu_eq(test_write_len, test_read_len,
+ "Write length should be equal to read length");
+ }
+ buf_writer_terminate(tsdn, buf_writer);
+}
+
+TEST_BEGIN(test_buf_write_pipe) {
+ buf_writer_t buf_writer;
+ tsdn_t *tsdn = tsdn_fetch();
+ assert_false(buf_writer_init(tsdn, &buf_writer, test_write_cb, &arg,
+ test_buf, TEST_BUF_SIZE),
+ "buf_writer_init() should not encounter error on static buffer");
+ test_buf_writer_pipe_body(tsdn, &buf_writer);
+}
+TEST_END
+
+TEST_BEGIN(test_buf_write_pipe_oom) {
+ buf_writer_t buf_writer;
+ tsdn_t *tsdn = tsdn_fetch();
+ assert_true(buf_writer_init(tsdn, &buf_writer, test_write_cb, &arg,
+ NULL, SC_LARGE_MAXCLASS + 1), "buf_writer_init() should OOM");
+ test_buf_writer_pipe_body(tsdn, &buf_writer);
+}
+TEST_END
+
+int
+main(void) {
+ return test(
+ test_buf_write_static,
+ test_buf_write_dynamic,
+ test_buf_write_oom,
+ test_buf_write_pipe,
+ test_buf_write_pipe_oom);
+}
diff --git a/deps/jemalloc/test/unit/cache_bin.c b/deps/jemalloc/test/unit/cache_bin.c
new file mode 100644
index 000000000..3b6dbab39
--- /dev/null
+++ b/deps/jemalloc/test/unit/cache_bin.c
@@ -0,0 +1,384 @@
+#include "test/jemalloc_test.h"
+
+static void
+do_fill_test(cache_bin_t *bin, cache_bin_info_t *info, void **ptrs,
+ cache_bin_sz_t ncached_max, cache_bin_sz_t nfill_attempt,
+ cache_bin_sz_t nfill_succeed) {
+ bool success;
+ void *ptr;
+ assert_true(cache_bin_ncached_get_local(bin, info) == 0, "");
+ CACHE_BIN_PTR_ARRAY_DECLARE(arr, nfill_attempt);
+ cache_bin_init_ptr_array_for_fill(bin, info, &arr, nfill_attempt);
+ for (cache_bin_sz_t i = 0; i < nfill_succeed; i++) {
+ arr.ptr[i] = &ptrs[i];
+ }
+ cache_bin_finish_fill(bin, info, &arr, nfill_succeed);
+ expect_true(cache_bin_ncached_get_local(bin, info) == nfill_succeed,
+ "");
+ cache_bin_low_water_set(bin);
+
+ for (cache_bin_sz_t i = 0; i < nfill_succeed; i++) {
+ ptr = cache_bin_alloc(bin, &success);
+ expect_true(success, "");
+ expect_ptr_eq(ptr, (void *)&ptrs[i],
+ "Should pop in order filled");
+ expect_true(cache_bin_low_water_get(bin, info)
+ == nfill_succeed - i - 1, "");
+ }
+ expect_true(cache_bin_ncached_get_local(bin, info) == 0, "");
+ expect_true(cache_bin_low_water_get(bin, info) == 0, "");
+}
+
+static void
+do_flush_test(cache_bin_t *bin, cache_bin_info_t *info, void **ptrs,
+ cache_bin_sz_t nfill, cache_bin_sz_t nflush) {
+ bool success;
+ assert_true(cache_bin_ncached_get_local(bin, info) == 0, "");
+
+ for (cache_bin_sz_t i = 0; i < nfill; i++) {
+ success = cache_bin_dalloc_easy(bin, &ptrs[i]);
+ expect_true(success, "");
+ }
+
+ CACHE_BIN_PTR_ARRAY_DECLARE(arr, nflush);
+ cache_bin_init_ptr_array_for_flush(bin, info, &arr, nflush);
+ for (cache_bin_sz_t i = 0; i < nflush; i++) {
+ expect_ptr_eq(arr.ptr[i], &ptrs[nflush - i - 1], "");
+ }
+ cache_bin_finish_flush(bin, info, &arr, nflush);
+
+ expect_true(cache_bin_ncached_get_local(bin, info) == nfill - nflush,
+ "");
+ while (cache_bin_ncached_get_local(bin, info) > 0) {
+ cache_bin_alloc(bin, &success);
+ }
+}
+
+static void
+do_batch_alloc_test(cache_bin_t *bin, cache_bin_info_t *info, void **ptrs,
+ cache_bin_sz_t nfill, size_t batch) {
+ assert_true(cache_bin_ncached_get_local(bin, info) == 0, "");
+ CACHE_BIN_PTR_ARRAY_DECLARE(arr, nfill);
+ cache_bin_init_ptr_array_for_fill(bin, info, &arr, nfill);
+ for (cache_bin_sz_t i = 0; i < nfill; i++) {
+ arr.ptr[i] = &ptrs[i];
+ }
+ cache_bin_finish_fill(bin, info, &arr, nfill);
+ assert_true(cache_bin_ncached_get_local(bin, info) == nfill, "");
+ cache_bin_low_water_set(bin);
+
+ void **out = malloc((batch + 1) * sizeof(void *));
+ size_t n = cache_bin_alloc_batch(bin, batch, out);
+ assert_true(n == ((size_t)nfill < batch ? (size_t)nfill : batch), "");
+ for (cache_bin_sz_t i = 0; i < (cache_bin_sz_t)n; i++) {
+ expect_ptr_eq(out[i], &ptrs[i], "");
+ }
+ expect_true(cache_bin_low_water_get(bin, info) == nfill -
+ (cache_bin_sz_t)n, "");
+ while (cache_bin_ncached_get_local(bin, info) > 0) {
+ bool success;
+ cache_bin_alloc(bin, &success);
+ }
+ free(out);
+}
+
+static void
+test_bin_init(cache_bin_t *bin, cache_bin_info_t *info) {
+ size_t size;
+ size_t alignment;
+ cache_bin_info_compute_alloc(info, 1, &size, &alignment);
+ void *mem = mallocx(size, MALLOCX_ALIGN(alignment));
+ assert_ptr_not_null(mem, "Unexpected mallocx failure");
+
+ size_t cur_offset = 0;
+ cache_bin_preincrement(info, 1, mem, &cur_offset);
+ cache_bin_init(bin, info, mem, &cur_offset);
+ cache_bin_postincrement(info, 1, mem, &cur_offset);
+ assert_zu_eq(cur_offset, size, "Should use all requested memory");
+}
+
+TEST_BEGIN(test_cache_bin) {
+ const int ncached_max = 100;
+ bool success;
+ void *ptr;
+
+ cache_bin_info_t info;
+ cache_bin_info_init(&info, ncached_max);
+ cache_bin_t bin;
+ test_bin_init(&bin, &info);
+
+ /* Initialize to empty; should then have 0 elements. */
+ expect_d_eq(ncached_max, cache_bin_info_ncached_max(&info), "");
+ expect_true(cache_bin_ncached_get_local(&bin, &info) == 0, "");
+ expect_true(cache_bin_low_water_get(&bin, &info) == 0, "");
+
+ ptr = cache_bin_alloc_easy(&bin, &success);
+ expect_false(success, "Shouldn't successfully allocate when empty");
+ expect_ptr_null(ptr, "Shouldn't get a non-null pointer on failure");
+
+ ptr = cache_bin_alloc(&bin, &success);
+ expect_false(success, "Shouldn't successfully allocate when empty");
+ expect_ptr_null(ptr, "Shouldn't get a non-null pointer on failure");
+
+ /*
+ * We allocate one more item than ncached_max, so we can test cache bin
+ * exhaustion.
+ */
+ void **ptrs = mallocx(sizeof(void *) * (ncached_max + 1), 0);
+ assert_ptr_not_null(ptrs, "Unexpected mallocx failure");
+ for (cache_bin_sz_t i = 0; i < ncached_max; i++) {
+ expect_true(cache_bin_ncached_get_local(&bin, &info) == i, "");
+ success = cache_bin_dalloc_easy(&bin, &ptrs[i]);
+ expect_true(success,
+ "Should be able to dalloc into a non-full cache bin.");
+ expect_true(cache_bin_low_water_get(&bin, &info) == 0,
+ "Pushes and pops shouldn't change low water of zero.");
+ }
+ expect_true(cache_bin_ncached_get_local(&bin, &info) == ncached_max,
+ "");
+ success = cache_bin_dalloc_easy(&bin, &ptrs[ncached_max]);
+ expect_false(success, "Shouldn't be able to dalloc into a full bin.");
+
+ cache_bin_low_water_set(&bin);
+
+ for (cache_bin_sz_t i = 0; i < ncached_max; i++) {
+ expect_true(cache_bin_low_water_get(&bin, &info)
+ == ncached_max - i, "");
+ expect_true(cache_bin_ncached_get_local(&bin, &info)
+ == ncached_max - i, "");
+ /*
+ * This should fail -- the easy variant can't change the low
+ * water mark.
+ */
+ ptr = cache_bin_alloc_easy(&bin, &success);
+ expect_ptr_null(ptr, "");
+ expect_false(success, "");
+ expect_true(cache_bin_low_water_get(&bin, &info)
+ == ncached_max - i, "");
+ expect_true(cache_bin_ncached_get_local(&bin, &info)
+ == ncached_max - i, "");
+
+ /* This should succeed, though. */
+ ptr = cache_bin_alloc(&bin, &success);
+ expect_true(success, "");
+ expect_ptr_eq(ptr, &ptrs[ncached_max - i - 1],
+ "Alloc should pop in stack order");
+ expect_true(cache_bin_low_water_get(&bin, &info)
+ == ncached_max - i - 1, "");
+ expect_true(cache_bin_ncached_get_local(&bin, &info)
+ == ncached_max - i - 1, "");
+ }
+ /* Now we're empty -- all alloc attempts should fail. */
+ expect_true(cache_bin_ncached_get_local(&bin, &info) == 0, "");
+ ptr = cache_bin_alloc_easy(&bin, &success);
+ expect_ptr_null(ptr, "");
+ expect_false(success, "");
+ ptr = cache_bin_alloc(&bin, &success);
+ expect_ptr_null(ptr, "");
+ expect_false(success, "");
+
+ for (cache_bin_sz_t i = 0; i < ncached_max / 2; i++) {
+ cache_bin_dalloc_easy(&bin, &ptrs[i]);
+ }
+ cache_bin_low_water_set(&bin);
+
+ for (cache_bin_sz_t i = ncached_max / 2; i < ncached_max; i++) {
+ cache_bin_dalloc_easy(&bin, &ptrs[i]);
+ }
+ expect_true(cache_bin_ncached_get_local(&bin, &info) == ncached_max,
+ "");
+ for (cache_bin_sz_t i = ncached_max - 1; i >= ncached_max / 2; i--) {
+ /*
+ * Size is bigger than low water -- the reduced version should
+ * succeed.
+ */
+ ptr = cache_bin_alloc_easy(&bin, &success);
+ expect_true(success, "");
+ expect_ptr_eq(ptr, &ptrs[i], "");
+ }
+ /* But now, we've hit low-water. */
+ ptr = cache_bin_alloc_easy(&bin, &success);
+ expect_false(success, "");
+ expect_ptr_null(ptr, "");
+
+ /* We're going to test filling -- we must be empty to start. */
+ while (cache_bin_ncached_get_local(&bin, &info)) {
+ cache_bin_alloc(&bin, &success);
+ expect_true(success, "");
+ }
+
+ /* Test fill. */
+ /* Try to fill all, succeed fully. */
+ do_fill_test(&bin, &info, ptrs, ncached_max, ncached_max, ncached_max);
+ /* Try to fill all, succeed partially. */
+ do_fill_test(&bin, &info, ptrs, ncached_max, ncached_max,
+ ncached_max / 2);
+ /* Try to fill all, fail completely. */
+ do_fill_test(&bin, &info, ptrs, ncached_max, ncached_max, 0);
+
+ /* Try to fill some, succeed fully. */
+ do_fill_test(&bin, &info, ptrs, ncached_max, ncached_max / 2,
+ ncached_max / 2);
+ /* Try to fill some, succeed partially. */
+ do_fill_test(&bin, &info, ptrs, ncached_max, ncached_max / 2,
+ ncached_max / 4);
+ /* Try to fill some, fail completely. */
+ do_fill_test(&bin, &info, ptrs, ncached_max, ncached_max / 2, 0);
+
+ do_flush_test(&bin, &info, ptrs, ncached_max, ncached_max);
+ do_flush_test(&bin, &info, ptrs, ncached_max, ncached_max / 2);
+ do_flush_test(&bin, &info, ptrs, ncached_max, 0);
+ do_flush_test(&bin, &info, ptrs, ncached_max / 2, ncached_max / 2);
+ do_flush_test(&bin, &info, ptrs, ncached_max / 2, ncached_max / 4);
+ do_flush_test(&bin, &info, ptrs, ncached_max / 2, 0);
+
+ do_batch_alloc_test(&bin, &info, ptrs, ncached_max, ncached_max);
+ do_batch_alloc_test(&bin, &info, ptrs, ncached_max, ncached_max * 2);
+ do_batch_alloc_test(&bin, &info, ptrs, ncached_max, ncached_max / 2);
+ do_batch_alloc_test(&bin, &info, ptrs, ncached_max, 2);
+ do_batch_alloc_test(&bin, &info, ptrs, ncached_max, 1);
+ do_batch_alloc_test(&bin, &info, ptrs, ncached_max, 0);
+ do_batch_alloc_test(&bin, &info, ptrs, ncached_max / 2,
+ ncached_max / 2);
+ do_batch_alloc_test(&bin, &info, ptrs, ncached_max / 2, ncached_max);
+ do_batch_alloc_test(&bin, &info, ptrs, ncached_max / 2,
+ ncached_max / 4);
+ do_batch_alloc_test(&bin, &info, ptrs, ncached_max / 2, 2);
+ do_batch_alloc_test(&bin, &info, ptrs, ncached_max / 2, 1);
+ do_batch_alloc_test(&bin, &info, ptrs, ncached_max / 2, 0);
+ do_batch_alloc_test(&bin, &info, ptrs, 2, ncached_max);
+ do_batch_alloc_test(&bin, &info, ptrs, 2, 2);
+ do_batch_alloc_test(&bin, &info, ptrs, 2, 1);
+ do_batch_alloc_test(&bin, &info, ptrs, 2, 0);
+ do_batch_alloc_test(&bin, &info, ptrs, 1, 2);
+ do_batch_alloc_test(&bin, &info, ptrs, 1, 1);
+ do_batch_alloc_test(&bin, &info, ptrs, 1, 0);
+ do_batch_alloc_test(&bin, &info, ptrs, 0, 2);
+ do_batch_alloc_test(&bin, &info, ptrs, 0, 1);
+ do_batch_alloc_test(&bin, &info, ptrs, 0, 0);
+
+ free(ptrs);
+}
+TEST_END
+
+static void
+do_flush_stashed_test(cache_bin_t *bin, cache_bin_info_t *info, void **ptrs,
+ cache_bin_sz_t nfill, cache_bin_sz_t nstash) {
+ expect_true(cache_bin_ncached_get_local(bin, info) == 0,
+ "Bin not empty");
+ expect_true(cache_bin_nstashed_get_local(bin, info) == 0,
+ "Bin not empty");
+ expect_true(nfill + nstash <= info->ncached_max, "Exceeded max");
+
+ bool ret;
+ /* Fill */
+ for (cache_bin_sz_t i = 0; i < nfill; i++) {
+ ret = cache_bin_dalloc_easy(bin, &ptrs[i]);
+ expect_true(ret, "Unexpected fill failure");
+ }
+ expect_true(cache_bin_ncached_get_local(bin, info) == nfill,
+ "Wrong cached count");
+
+ /* Stash */
+ for (cache_bin_sz_t i = 0; i < nstash; i++) {
+ ret = cache_bin_stash(bin, &ptrs[i + nfill]);
+ expect_true(ret, "Unexpected stash failure");
+ }
+ expect_true(cache_bin_nstashed_get_local(bin, info) == nstash,
+ "Wrong stashed count");
+
+ if (nfill + nstash == info->ncached_max) {
+ ret = cache_bin_dalloc_easy(bin, &ptrs[0]);
+ expect_false(ret, "Should not dalloc into a full bin");
+ ret = cache_bin_stash(bin, &ptrs[0]);
+ expect_false(ret, "Should not stash into a full bin");
+ }
+
+ /* Alloc filled ones */
+ for (cache_bin_sz_t i = 0; i < nfill; i++) {
+ void *ptr = cache_bin_alloc(bin, &ret);
+ expect_true(ret, "Unexpected alloc failure");
+ /* Verify it's not from the stashed range. */
+ expect_true((uintptr_t)ptr < (uintptr_t)&ptrs[nfill],
+ "Should not alloc stashed ptrs");
+ }
+ expect_true(cache_bin_ncached_get_local(bin, info) == 0,
+ "Wrong cached count");
+ expect_true(cache_bin_nstashed_get_local(bin, info) == nstash,
+ "Wrong stashed count");
+
+ cache_bin_alloc(bin, &ret);
+ expect_false(ret, "Should not alloc stashed");
+
+ /* Clear stashed ones */
+ cache_bin_finish_flush_stashed(bin, info);
+ expect_true(cache_bin_ncached_get_local(bin, info) == 0,
+ "Wrong cached count");
+ expect_true(cache_bin_nstashed_get_local(bin, info) == 0,
+ "Wrong stashed count");
+
+ cache_bin_alloc(bin, &ret);
+ expect_false(ret, "Should not alloc from empty bin");
+}
+
+TEST_BEGIN(test_cache_bin_stash) {
+ const int ncached_max = 100;
+
+ cache_bin_t bin;
+ cache_bin_info_t info;
+ cache_bin_info_init(&info, ncached_max);
+ test_bin_init(&bin, &info);
+
+ /*
+ * The content of this array is not accessed; instead the interior
+ * addresses are used to insert / stash into the bins as test pointers.
+ */
+ void **ptrs = mallocx(sizeof(void *) * (ncached_max + 1), 0);
+ assert_ptr_not_null(ptrs, "Unexpected mallocx failure");
+ bool ret;
+ for (cache_bin_sz_t i = 0; i < ncached_max; i++) {
+ expect_true(cache_bin_ncached_get_local(&bin, &info) ==
+ (i / 2 + i % 2), "Wrong ncached value");
+ expect_true(cache_bin_nstashed_get_local(&bin, &info) == i / 2,
+ "Wrong nstashed value");
+ if (i % 2 == 0) {
+ cache_bin_dalloc_easy(&bin, &ptrs[i]);
+ } else {
+ ret = cache_bin_stash(&bin, &ptrs[i]);
+ expect_true(ret, "Should be able to stash into a "
+ "non-full cache bin");
+ }
+ }
+ ret = cache_bin_dalloc_easy(&bin, &ptrs[0]);
+ expect_false(ret, "Should not dalloc into a full cache bin");
+ ret = cache_bin_stash(&bin, &ptrs[0]);
+ expect_false(ret, "Should not stash into a full cache bin");
+ for (cache_bin_sz_t i = 0; i < ncached_max; i++) {
+ void *ptr = cache_bin_alloc(&bin, &ret);
+ if (i < ncached_max / 2) {
+ expect_true(ret, "Should be able to alloc");
+ uintptr_t diff = ((uintptr_t)ptr - (uintptr_t)&ptrs[0])
+ / sizeof(void *);
+ expect_true(diff % 2 == 0, "Should be able to alloc");
+ } else {
+ expect_false(ret, "Should not alloc stashed");
+ expect_true(cache_bin_nstashed_get_local(&bin, &info) ==
+ ncached_max / 2, "Wrong nstashed value");
+ }
+ }
+
+ test_bin_init(&bin, &info);
+ do_flush_stashed_test(&bin, &info, ptrs, ncached_max, 0);
+ do_flush_stashed_test(&bin, &info, ptrs, 0, ncached_max);
+ do_flush_stashed_test(&bin, &info, ptrs, ncached_max / 2, ncached_max / 2);
+ do_flush_stashed_test(&bin, &info, ptrs, ncached_max / 4, ncached_max / 2);
+ do_flush_stashed_test(&bin, &info, ptrs, ncached_max / 2, ncached_max / 4);
+ do_flush_stashed_test(&bin, &info, ptrs, ncached_max / 4, ncached_max / 4);
+}
+TEST_END
+
+int
+main(void) {
+ return test(test_cache_bin,
+ test_cache_bin_stash);
+}
diff --git a/deps/jemalloc/test/unit/ckh.c b/deps/jemalloc/test/unit/ckh.c
index 707ea5f8c..36142acdd 100644
--- a/deps/jemalloc/test/unit/ckh.c
+++ b/deps/jemalloc/test/unit/ckh.c
@@ -6,11 +6,11 @@ TEST_BEGIN(test_new_delete) {
tsd = tsd_fetch();
- assert_false(ckh_new(tsd, &ckh, 2, ckh_string_hash,
+ expect_false(ckh_new(tsd, &ckh, 2, ckh_string_hash,
ckh_string_keycomp), "Unexpected ckh_new() error");
ckh_delete(tsd, &ckh);
- assert_false(ckh_new(tsd, &ckh, 3, ckh_pointer_hash,
+ expect_false(ckh_new(tsd, &ckh, 3, ckh_pointer_hash,
ckh_pointer_keycomp), "Unexpected ckh_new() error");
ckh_delete(tsd, &ckh);
}
@@ -30,16 +30,16 @@ TEST_BEGIN(test_count_insert_search_remove) {
tsd = tsd_fetch();
- assert_false(ckh_new(tsd, &ckh, 2, ckh_string_hash,
+ expect_false(ckh_new(tsd, &ckh, 2, ckh_string_hash,
ckh_string_keycomp), "Unexpected ckh_new() error");
- assert_zu_eq(ckh_count(&ckh), 0,
+ expect_zu_eq(ckh_count(&ckh), 0,
"ckh_count() should return %zu, but it returned %zu", ZU(0),
ckh_count(&ckh));
/* Insert. */
for (i = 0; i < sizeof(strs)/sizeof(const char *); i++) {
ckh_insert(tsd, &ckh, strs[i], strs[i]);
- assert_zu_eq(ckh_count(&ckh), i+1,
+ expect_zu_eq(ckh_count(&ckh), i+1,
"ckh_count() should return %zu, but it returned %zu", i+1,
ckh_count(&ckh));
}
@@ -57,17 +57,17 @@ TEST_BEGIN(test_count_insert_search_remove) {
vp = (i & 2) ? &v.p : NULL;
k.p = NULL;
v.p = NULL;
- assert_false(ckh_search(&ckh, strs[i], kp, vp),
+ expect_false(ckh_search(&ckh, strs[i], kp, vp),
"Unexpected ckh_search() error");
ks = (i & 1) ? strs[i] : (const char *)NULL;
vs = (i & 2) ? strs[i] : (const char *)NULL;
- assert_ptr_eq((void *)ks, (void *)k.s, "Key mismatch, i=%zu",
+ expect_ptr_eq((void *)ks, (void *)k.s, "Key mismatch, i=%zu",
i);
- assert_ptr_eq((void *)vs, (void *)v.s, "Value mismatch, i=%zu",
+ expect_ptr_eq((void *)vs, (void *)v.s, "Value mismatch, i=%zu",
i);
}
- assert_true(ckh_search(&ckh, missing, NULL, NULL),
+ expect_true(ckh_search(&ckh, missing, NULL, NULL),
"Unexpected ckh_search() success");
/* Remove. */
@@ -83,16 +83,16 @@ TEST_BEGIN(test_count_insert_search_remove) {
vp = (i & 2) ? &v.p : NULL;
k.p = NULL;
v.p = NULL;
- assert_false(ckh_remove(tsd, &ckh, strs[i], kp, vp),
+ expect_false(ckh_remove(tsd, &ckh, strs[i], kp, vp),
"Unexpected ckh_remove() error");
ks = (i & 1) ? strs[i] : (const char *)NULL;
vs = (i & 2) ? strs[i] : (const char *)NULL;
- assert_ptr_eq((void *)ks, (void *)k.s, "Key mismatch, i=%zu",
+ expect_ptr_eq((void *)ks, (void *)k.s, "Key mismatch, i=%zu",
i);
- assert_ptr_eq((void *)vs, (void *)v.s, "Value mismatch, i=%zu",
+ expect_ptr_eq((void *)vs, (void *)v.s, "Value mismatch, i=%zu",
i);
- assert_zu_eq(ckh_count(&ckh),
+ expect_zu_eq(ckh_count(&ckh),
sizeof(strs)/sizeof(const char *) - i - 1,
"ckh_count() should return %zu, but it returned %zu",
sizeof(strs)/sizeof(const char *) - i - 1,
@@ -113,40 +113,40 @@ TEST_BEGIN(test_insert_iter_remove) {
tsd = tsd_fetch();
- assert_false(ckh_new(tsd, &ckh, 2, ckh_pointer_hash,
+ expect_false(ckh_new(tsd, &ckh, 2, ckh_pointer_hash,
ckh_pointer_keycomp), "Unexpected ckh_new() error");
for (i = 0; i < NITEMS; i++) {
p[i] = mallocx(i+1, 0);
- assert_ptr_not_null(p[i], "Unexpected mallocx() failure");
+ expect_ptr_not_null(p[i], "Unexpected mallocx() failure");
}
for (i = 0; i < NITEMS; i++) {
size_t j;
for (j = i; j < NITEMS; j++) {
- assert_false(ckh_insert(tsd, &ckh, p[j], p[j]),
+ expect_false(ckh_insert(tsd, &ckh, p[j], p[j]),
"Unexpected ckh_insert() failure");
- assert_false(ckh_search(&ckh, p[j], &q, &r),
+ expect_false(ckh_search(&ckh, p[j], &q, &r),
"Unexpected ckh_search() failure");
- assert_ptr_eq(p[j], q, "Key pointer mismatch");
- assert_ptr_eq(p[j], r, "Value pointer mismatch");
+ expect_ptr_eq(p[j], q, "Key pointer mismatch");
+ expect_ptr_eq(p[j], r, "Value pointer mismatch");
}
- assert_zu_eq(ckh_count(&ckh), NITEMS,
+ expect_zu_eq(ckh_count(&ckh), NITEMS,
"ckh_count() should return %zu, but it returned %zu",
NITEMS, ckh_count(&ckh));
for (j = i + 1; j < NITEMS; j++) {
- assert_false(ckh_search(&ckh, p[j], NULL, NULL),
+ expect_false(ckh_search(&ckh, p[j], NULL, NULL),
"Unexpected ckh_search() failure");
- assert_false(ckh_remove(tsd, &ckh, p[j], &q, &r),
+ expect_false(ckh_remove(tsd, &ckh, p[j], &q, &r),
"Unexpected ckh_remove() failure");
- assert_ptr_eq(p[j], q, "Key pointer mismatch");
- assert_ptr_eq(p[j], r, "Value pointer mismatch");
- assert_true(ckh_search(&ckh, p[j], NULL, NULL),
+ expect_ptr_eq(p[j], q, "Key pointer mismatch");
+ expect_ptr_eq(p[j], r, "Value pointer mismatch");
+ expect_true(ckh_search(&ckh, p[j], NULL, NULL),
"Unexpected ckh_search() success");
- assert_true(ckh_remove(tsd, &ckh, p[j], &q, &r),
+ expect_true(ckh_remove(tsd, &ckh, p[j], &q, &r),
"Unexpected ckh_remove() success");
}
@@ -159,11 +159,11 @@ TEST_BEGIN(test_insert_iter_remove) {
for (tabind = 0; !ckh_iter(&ckh, &tabind, &q, &r);) {
size_t k;
- assert_ptr_eq(q, r, "Key and val not equal");
+ expect_ptr_eq(q, r, "Key and val not equal");
for (k = 0; k < NITEMS; k++) {
if (p[k] == q) {
- assert_false(seen[k],
+ expect_false(seen[k],
"Item %zu already seen", k);
seen[k] = true;
break;
@@ -172,29 +172,29 @@ TEST_BEGIN(test_insert_iter_remove) {
}
for (j = 0; j < i + 1; j++) {
- assert_true(seen[j], "Item %zu not seen", j);
+ expect_true(seen[j], "Item %zu not seen", j);
}
for (; j < NITEMS; j++) {
- assert_false(seen[j], "Item %zu seen", j);
+ expect_false(seen[j], "Item %zu seen", j);
}
}
}
for (i = 0; i < NITEMS; i++) {
- assert_false(ckh_search(&ckh, p[i], NULL, NULL),
+ expect_false(ckh_search(&ckh, p[i], NULL, NULL),
"Unexpected ckh_search() failure");
- assert_false(ckh_remove(tsd, &ckh, p[i], &q, &r),
+ expect_false(ckh_remove(tsd, &ckh, p[i], &q, &r),
"Unexpected ckh_remove() failure");
- assert_ptr_eq(p[i], q, "Key pointer mismatch");
- assert_ptr_eq(p[i], r, "Value pointer mismatch");
- assert_true(ckh_search(&ckh, p[i], NULL, NULL),
+ expect_ptr_eq(p[i], q, "Key pointer mismatch");
+ expect_ptr_eq(p[i], r, "Value pointer mismatch");
+ expect_true(ckh_search(&ckh, p[i], NULL, NULL),
"Unexpected ckh_search() success");
- assert_true(ckh_remove(tsd, &ckh, p[i], &q, &r),
+ expect_true(ckh_remove(tsd, &ckh, p[i], &q, &r),
"Unexpected ckh_remove() success");
dallocx(p[i], 0);
}
- assert_zu_eq(ckh_count(&ckh), 0,
+ expect_zu_eq(ckh_count(&ckh), 0,
"ckh_count() should return %zu, but it returned %zu",
ZU(0), ckh_count(&ckh));
ckh_delete(tsd, &ckh);
diff --git a/deps/jemalloc/test/unit/counter.c b/deps/jemalloc/test/unit/counter.c
new file mode 100644
index 000000000..277baac16
--- /dev/null
+++ b/deps/jemalloc/test/unit/counter.c
@@ -0,0 +1,80 @@
+#include "test/jemalloc_test.h"
+
+static const uint64_t interval = 1 << 20;
+
+TEST_BEGIN(test_counter_accum) {
+ uint64_t increment = interval >> 4;
+ unsigned n = interval / increment;
+ uint64_t accum = 0;
+
+ counter_accum_t c;
+ counter_accum_init(&c, interval);
+
+ tsd_t *tsd = tsd_fetch();
+ bool trigger;
+ for (unsigned i = 0; i < n; i++) {
+ trigger = counter_accum(tsd_tsdn(tsd), &c, increment);
+ accum += increment;
+ if (accum < interval) {
+ expect_b_eq(trigger, false, "Should not trigger");
+ } else {
+ expect_b_eq(trigger, true, "Should have triggered");
+ }
+ }
+ expect_b_eq(trigger, true, "Should have triggered");
+}
+TEST_END
+
+void
+expect_counter_value(counter_accum_t *c, uint64_t v) {
+ uint64_t accum = locked_read_u64_unsynchronized(&c->accumbytes);
+ expect_u64_eq(accum, v, "Counter value mismatch");
+}
+
+#define N_THDS (16)
+#define N_ITER_THD (1 << 12)
+#define ITER_INCREMENT (interval >> 4)
+
+static void *
+thd_start(void *varg) {
+ counter_accum_t *c = (counter_accum_t *)varg;
+
+ tsd_t *tsd = tsd_fetch();
+ bool trigger;
+ uintptr_t n_triggered = 0;
+ for (unsigned i = 0; i < N_ITER_THD; i++) {
+ trigger = counter_accum(tsd_tsdn(tsd), c, ITER_INCREMENT);
+ n_triggered += trigger ? 1 : 0;
+ }
+
+ return (void *)n_triggered;
+}
+
+
+TEST_BEGIN(test_counter_mt) {
+ counter_accum_t shared_c;
+ counter_accum_init(&shared_c, interval);
+
+ thd_t thds[N_THDS];
+ unsigned i;
+ for (i = 0; i < N_THDS; i++) {
+ thd_create(&thds[i], thd_start, (void *)&shared_c);
+ }
+
+ uint64_t sum = 0;
+ for (i = 0; i < N_THDS; i++) {
+ void *ret;
+ thd_join(thds[i], &ret);
+ sum += (uintptr_t)ret;
+ }
+ expect_u64_eq(sum, N_THDS * N_ITER_THD / (interval / ITER_INCREMENT),
+ "Incorrect number of triggers");
+}
+TEST_END
+
+int
+main(void) {
+ return test(
+ test_counter_accum,
+ test_counter_mt);
+}
diff --git a/deps/jemalloc/test/unit/decay.c b/deps/jemalloc/test/unit/decay.c
index cf3c07960..bdb6d0a39 100644
--- a/deps/jemalloc/test/unit/decay.c
+++ b/deps/jemalloc/test/unit/decay.c
@@ -1,605 +1,283 @@
#include "test/jemalloc_test.h"
-#include "jemalloc/internal/ticker.h"
-
-static nstime_monotonic_t *nstime_monotonic_orig;
-static nstime_update_t *nstime_update_orig;
-
-static unsigned nupdates_mock;
-static nstime_t time_mock;
-static bool monotonic_mock;
-
-static bool
-check_background_thread_enabled(void) {
- bool enabled;
- size_t sz = sizeof(bool);
- int ret = mallctl("background_thread", (void *)&enabled, &sz, NULL,0);
- if (ret == ENOENT) {
- return false;
- }
- assert_d_eq(ret, 0, "Unexpected mallctl error");
- return enabled;
-}
+#include "jemalloc/internal/decay.h"
-static bool
-nstime_monotonic_mock(void) {
- return monotonic_mock;
-}
+TEST_BEGIN(test_decay_init) {
+ decay_t decay;
+ memset(&decay, 0, sizeof(decay));
-static bool
-nstime_update_mock(nstime_t *time) {
- nupdates_mock++;
- if (monotonic_mock) {
- nstime_copy(time, &time_mock);
- }
- return !monotonic_mock;
-}
+ nstime_t curtime;
+ nstime_init(&curtime, 0);
-static unsigned
-do_arena_create(ssize_t dirty_decay_ms, ssize_t muzzy_decay_ms) {
- unsigned arena_ind;
- size_t sz = sizeof(unsigned);
- assert_d_eq(mallctl("arenas.create", (void *)&arena_ind, &sz, NULL, 0),
- 0, "Unexpected mallctl() failure");
- size_t mib[3];
- size_t miblen = sizeof(mib)/sizeof(size_t);
-
- assert_d_eq(mallctlnametomib("arena.0.dirty_decay_ms", mib, &miblen),
- 0, "Unexpected mallctlnametomib() failure");
- mib[1] = (size_t)arena_ind;
- assert_d_eq(mallctlbymib(mib, miblen, NULL, NULL,
- (void *)&dirty_decay_ms, sizeof(dirty_decay_ms)), 0,
- "Unexpected mallctlbymib() failure");
-
- assert_d_eq(mallctlnametomib("arena.0.muzzy_decay_ms", mib, &miblen),
- 0, "Unexpected mallctlnametomib() failure");
- mib[1] = (size_t)arena_ind;
- assert_d_eq(mallctlbymib(mib, miblen, NULL, NULL,
- (void *)&muzzy_decay_ms, sizeof(muzzy_decay_ms)), 0,
- "Unexpected mallctlbymib() failure");
-
- return arena_ind;
-}
+ ssize_t decay_ms = 1000;
+ assert_true(decay_ms_valid(decay_ms), "");
-static void
-do_arena_destroy(unsigned arena_ind) {
- size_t mib[3];
- size_t miblen = sizeof(mib)/sizeof(size_t);
- assert_d_eq(mallctlnametomib("arena.0.destroy", mib, &miblen), 0,
- "Unexpected mallctlnametomib() failure");
- mib[1] = (size_t)arena_ind;
- assert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0,
- "Unexpected mallctlbymib() failure");
+ expect_false(decay_init(&decay, &curtime, decay_ms),
+ "Failed to initialize decay");
+ expect_zd_eq(decay_ms_read(&decay), decay_ms,
+ "Decay_ms was initialized incorrectly");
+ expect_u64_ne(decay_epoch_duration_ns(&decay), 0,
+ "Epoch duration was initialized incorrectly");
}
+TEST_END
-void
-do_epoch(void) {
- uint64_t epoch = 1;
- assert_d_eq(mallctl("epoch", NULL, NULL, (void *)&epoch, sizeof(epoch)),
- 0, "Unexpected mallctl() failure");
+TEST_BEGIN(test_decay_ms_valid) {
+ expect_false(decay_ms_valid(-7),
+ "Misclassified negative decay as valid");
+ expect_true(decay_ms_valid(-1),
+ "Misclassified -1 (never decay) as invalid decay");
+ expect_true(decay_ms_valid(8943),
+ "Misclassified valid decay");
+ if (SSIZE_MAX > NSTIME_SEC_MAX) {
+ expect_false(
+ decay_ms_valid((ssize_t)(NSTIME_SEC_MAX * KQU(1000) + 39)),
+ "Misclassified too large decay");
+ }
}
+TEST_END
-void
-do_purge(unsigned arena_ind) {
- size_t mib[3];
- size_t miblen = sizeof(mib)/sizeof(size_t);
- assert_d_eq(mallctlnametomib("arena.0.purge", mib, &miblen), 0,
- "Unexpected mallctlnametomib() failure");
- mib[1] = (size_t)arena_ind;
- assert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0,
- "Unexpected mallctlbymib() failure");
-}
+TEST_BEGIN(test_decay_npages_purge_in) {
+ decay_t decay;
+ memset(&decay, 0, sizeof(decay));
-void
-do_decay(unsigned arena_ind) {
- size_t mib[3];
- size_t miblen = sizeof(mib)/sizeof(size_t);
- assert_d_eq(mallctlnametomib("arena.0.decay", mib, &miblen), 0,
- "Unexpected mallctlnametomib() failure");
- mib[1] = (size_t)arena_ind;
- assert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0,
- "Unexpected mallctlbymib() failure");
-}
+ nstime_t curtime;
+ nstime_init(&curtime, 0);
-static uint64_t
-get_arena_npurge_impl(const char *mibname, unsigned arena_ind) {
- size_t mib[4];
- size_t miblen = sizeof(mib)/sizeof(size_t);
- assert_d_eq(mallctlnametomib(mibname, mib, &miblen), 0,
- "Unexpected mallctlnametomib() failure");
- mib[2] = (size_t)arena_ind;
- uint64_t npurge = 0;
- size_t sz = sizeof(npurge);
- assert_d_eq(mallctlbymib(mib, miblen, (void *)&npurge, &sz, NULL, 0),
- config_stats ? 0 : ENOENT, "Unexpected mallctlbymib() failure");
- return npurge;
-}
+ uint64_t decay_ms = 1000;
+ nstime_t decay_nstime;
+ nstime_init(&decay_nstime, decay_ms * 1000 * 1000);
+ expect_false(decay_init(&decay, &curtime, (ssize_t)decay_ms),
+ "Failed to initialize decay");
-static uint64_t
-get_arena_dirty_npurge(unsigned arena_ind) {
- do_epoch();
- return get_arena_npurge_impl("stats.arenas.0.dirty_npurge", arena_ind);
-}
+ size_t new_pages = 100;
-static uint64_t
-get_arena_dirty_purged(unsigned arena_ind) {
- do_epoch();
- return get_arena_npurge_impl("stats.arenas.0.dirty_purged", arena_ind);
-}
+ nstime_t time;
+ nstime_copy(&time, &decay_nstime);
+ expect_u64_eq(decay_npages_purge_in(&decay, &time, new_pages),
+ new_pages, "Not all pages are expected to decay in decay_ms");
-static uint64_t
-get_arena_muzzy_npurge(unsigned arena_ind) {
- do_epoch();
- return get_arena_npurge_impl("stats.arenas.0.muzzy_npurge", arena_ind);
-}
+ nstime_init(&time, 0);
+ expect_u64_eq(decay_npages_purge_in(&decay, &time, new_pages), 0,
+ "More than zero pages are expected to instantly decay");
-static uint64_t
-get_arena_npurge(unsigned arena_ind) {
- do_epoch();
- return get_arena_npurge_impl("stats.arenas.0.dirty_npurge", arena_ind) +
- get_arena_npurge_impl("stats.arenas.0.muzzy_npurge", arena_ind);
+ nstime_copy(&time, &decay_nstime);
+ nstime_idivide(&time, 2);
+ expect_u64_eq(decay_npages_purge_in(&decay, &time, new_pages),
+ new_pages / 2, "Not half of pages decay in half the decay period");
}
+TEST_END
-static size_t
-get_arena_pdirty(unsigned arena_ind) {
- do_epoch();
- size_t mib[4];
- size_t miblen = sizeof(mib)/sizeof(size_t);
- assert_d_eq(mallctlnametomib("stats.arenas.0.pdirty", mib, &miblen), 0,
- "Unexpected mallctlnametomib() failure");
- mib[2] = (size_t)arena_ind;
- size_t pdirty;
- size_t sz = sizeof(pdirty);
- assert_d_eq(mallctlbymib(mib, miblen, (void *)&pdirty, &sz, NULL, 0), 0,
- "Unexpected mallctlbymib() failure");
- return pdirty;
-}
+TEST_BEGIN(test_decay_maybe_advance_epoch) {
+ decay_t decay;
+ memset(&decay, 0, sizeof(decay));
-static size_t
-get_arena_pmuzzy(unsigned arena_ind) {
- do_epoch();
- size_t mib[4];
- size_t miblen = sizeof(mib)/sizeof(size_t);
- assert_d_eq(mallctlnametomib("stats.arenas.0.pmuzzy", mib, &miblen), 0,
- "Unexpected mallctlnametomib() failure");
- mib[2] = (size_t)arena_ind;
- size_t pmuzzy;
- size_t sz = sizeof(pmuzzy);
- assert_d_eq(mallctlbymib(mib, miblen, (void *)&pmuzzy, &sz, NULL, 0), 0,
- "Unexpected mallctlbymib() failure");
- return pmuzzy;
-}
+ nstime_t curtime;
+ nstime_init(&curtime, 0);
-static void *
-do_mallocx(size_t size, int flags) {
- void *p = mallocx(size, flags);
- assert_ptr_not_null(p, "Unexpected mallocx() failure");
- return p;
-}
+ uint64_t decay_ms = 1000;
-static void
-generate_dirty(unsigned arena_ind, size_t size) {
- int flags = MALLOCX_ARENA(arena_ind) | MALLOCX_TCACHE_NONE;
- void *p = do_mallocx(size, flags);
- dallocx(p, flags);
-}
+ bool err = decay_init(&decay, &curtime, (ssize_t)decay_ms);
+ expect_false(err, "");
-TEST_BEGIN(test_decay_ticks) {
- test_skip_if(check_background_thread_enabled());
-
- ticker_t *decay_ticker;
- unsigned tick0, tick1, arena_ind;
- size_t sz, large0;
- void *p;
-
- sz = sizeof(size_t);
- assert_d_eq(mallctl("arenas.lextent.0.size", (void *)&large0, &sz, NULL,
- 0), 0, "Unexpected mallctl failure");
-
- /* Set up a manually managed arena for test. */
- arena_ind = do_arena_create(0, 0);
-
- /* Migrate to the new arena, and get the ticker. */
- unsigned old_arena_ind;
- size_t sz_arena_ind = sizeof(old_arena_ind);
- assert_d_eq(mallctl("thread.arena", (void *)&old_arena_ind,
- &sz_arena_ind, (void *)&arena_ind, sizeof(arena_ind)), 0,
- "Unexpected mallctl() failure");
- decay_ticker = decay_ticker_get(tsd_fetch(), arena_ind);
- assert_ptr_not_null(decay_ticker,
- "Unexpected failure getting decay ticker");
-
- /*
- * Test the standard APIs using a large size class, since we can't
- * control tcache interactions for small size classes (except by
- * completely disabling tcache for the entire test program).
- */
-
- /* malloc(). */
- tick0 = ticker_read(decay_ticker);
- p = malloc(large0);
- assert_ptr_not_null(p, "Unexpected malloc() failure");
- tick1 = ticker_read(decay_ticker);
- assert_u32_ne(tick1, tick0, "Expected ticker to tick during malloc()");
- /* free(). */
- tick0 = ticker_read(decay_ticker);
- free(p);
- tick1 = ticker_read(decay_ticker);
- assert_u32_ne(tick1, tick0, "Expected ticker to tick during free()");
-
- /* calloc(). */
- tick0 = ticker_read(decay_ticker);
- p = calloc(1, large0);
- assert_ptr_not_null(p, "Unexpected calloc() failure");
- tick1 = ticker_read(decay_ticker);
- assert_u32_ne(tick1, tick0, "Expected ticker to tick during calloc()");
- free(p);
-
- /* posix_memalign(). */
- tick0 = ticker_read(decay_ticker);
- assert_d_eq(posix_memalign(&p, sizeof(size_t), large0), 0,
- "Unexpected posix_memalign() failure");
- tick1 = ticker_read(decay_ticker);
- assert_u32_ne(tick1, tick0,
- "Expected ticker to tick during posix_memalign()");
- free(p);
-
- /* aligned_alloc(). */
- tick0 = ticker_read(decay_ticker);
- p = aligned_alloc(sizeof(size_t), large0);
- assert_ptr_not_null(p, "Unexpected aligned_alloc() failure");
- tick1 = ticker_read(decay_ticker);
- assert_u32_ne(tick1, tick0,
- "Expected ticker to tick during aligned_alloc()");
- free(p);
-
- /* realloc(). */
- /* Allocate. */
- tick0 = ticker_read(decay_ticker);
- p = realloc(NULL, large0);
- assert_ptr_not_null(p, "Unexpected realloc() failure");
- tick1 = ticker_read(decay_ticker);
- assert_u32_ne(tick1, tick0, "Expected ticker to tick during realloc()");
- /* Reallocate. */
- tick0 = ticker_read(decay_ticker);
- p = realloc(p, large0);
- assert_ptr_not_null(p, "Unexpected realloc() failure");
- tick1 = ticker_read(decay_ticker);
- assert_u32_ne(tick1, tick0, "Expected ticker to tick during realloc()");
- /* Deallocate. */
- tick0 = ticker_read(decay_ticker);
- realloc(p, 0);
- tick1 = ticker_read(decay_ticker);
- assert_u32_ne(tick1, tick0, "Expected ticker to tick during realloc()");
-
- /*
- * Test the *allocx() APIs using large and small size classes, with
- * tcache explicitly disabled.
- */
- {
- unsigned i;
- size_t allocx_sizes[2];
- allocx_sizes[0] = large0;
- allocx_sizes[1] = 1;
-
- for (i = 0; i < sizeof(allocx_sizes) / sizeof(size_t); i++) {
- sz = allocx_sizes[i];
-
- /* mallocx(). */
- tick0 = ticker_read(decay_ticker);
- p = mallocx(sz, MALLOCX_TCACHE_NONE);
- assert_ptr_not_null(p, "Unexpected mallocx() failure");
- tick1 = ticker_read(decay_ticker);
- assert_u32_ne(tick1, tick0,
- "Expected ticker to tick during mallocx() (sz=%zu)",
- sz);
- /* rallocx(). */
- tick0 = ticker_read(decay_ticker);
- p = rallocx(p, sz, MALLOCX_TCACHE_NONE);
- assert_ptr_not_null(p, "Unexpected rallocx() failure");
- tick1 = ticker_read(decay_ticker);
- assert_u32_ne(tick1, tick0,
- "Expected ticker to tick during rallocx() (sz=%zu)",
- sz);
- /* xallocx(). */
- tick0 = ticker_read(decay_ticker);
- xallocx(p, sz, 0, MALLOCX_TCACHE_NONE);
- tick1 = ticker_read(decay_ticker);
- assert_u32_ne(tick1, tick0,
- "Expected ticker to tick during xallocx() (sz=%zu)",
- sz);
- /* dallocx(). */
- tick0 = ticker_read(decay_ticker);
- dallocx(p, MALLOCX_TCACHE_NONE);
- tick1 = ticker_read(decay_ticker);
- assert_u32_ne(tick1, tick0,
- "Expected ticker to tick during dallocx() (sz=%zu)",
- sz);
- /* sdallocx(). */
- p = mallocx(sz, MALLOCX_TCACHE_NONE);
- assert_ptr_not_null(p, "Unexpected mallocx() failure");
- tick0 = ticker_read(decay_ticker);
- sdallocx(p, sz, MALLOCX_TCACHE_NONE);
- tick1 = ticker_read(decay_ticker);
- assert_u32_ne(tick1, tick0,
- "Expected ticker to tick during sdallocx() "
- "(sz=%zu)", sz);
- }
- }
+ bool advanced;
+ advanced = decay_maybe_advance_epoch(&decay, &curtime, 0);
+ expect_false(advanced, "Epoch advanced while time didn't");
- /*
- * Test tcache fill/flush interactions for large and small size classes,
- * using an explicit tcache.
- */
- unsigned tcache_ind, i;
- size_t tcache_sizes[2];
- tcache_sizes[0] = large0;
- tcache_sizes[1] = 1;
-
- size_t tcache_max, sz_tcache_max;
- sz_tcache_max = sizeof(tcache_max);
- assert_d_eq(mallctl("arenas.tcache_max", (void *)&tcache_max,
- &sz_tcache_max, NULL, 0), 0, "Unexpected mallctl() failure");
-
- sz = sizeof(unsigned);
- assert_d_eq(mallctl("tcache.create", (void *)&tcache_ind, &sz,
- NULL, 0), 0, "Unexpected mallctl failure");
-
- for (i = 0; i < sizeof(tcache_sizes) / sizeof(size_t); i++) {
- sz = tcache_sizes[i];
-
- /* tcache fill. */
- tick0 = ticker_read(decay_ticker);
- p = mallocx(sz, MALLOCX_TCACHE(tcache_ind));
- assert_ptr_not_null(p, "Unexpected mallocx() failure");
- tick1 = ticker_read(decay_ticker);
- assert_u32_ne(tick1, tick0,
- "Expected ticker to tick during tcache fill "
- "(sz=%zu)", sz);
- /* tcache flush. */
- dallocx(p, MALLOCX_TCACHE(tcache_ind));
- tick0 = ticker_read(decay_ticker);
- assert_d_eq(mallctl("tcache.flush", NULL, NULL,
- (void *)&tcache_ind, sizeof(unsigned)), 0,
- "Unexpected mallctl failure");
- tick1 = ticker_read(decay_ticker);
-
- /* Will only tick if it's in tcache. */
- if (sz <= tcache_max) {
- assert_u32_ne(tick1, tick0,
- "Expected ticker to tick during tcache "
- "flush (sz=%zu)", sz);
- } else {
- assert_u32_eq(tick1, tick0,
- "Unexpected ticker tick during tcache "
- "flush (sz=%zu)", sz);
- }
- }
+ nstime_t interval;
+ nstime_init(&interval, decay_epoch_duration_ns(&decay));
+
+ nstime_add(&curtime, &interval);
+ advanced = decay_maybe_advance_epoch(&decay, &curtime, 0);
+ expect_false(advanced, "Epoch advanced after first interval");
+
+ nstime_add(&curtime, &interval);
+ advanced = decay_maybe_advance_epoch(&decay, &curtime, 0);
+ expect_true(advanced, "Epoch didn't advance after two intervals");
}
TEST_END
-static void
-decay_ticker_helper(unsigned arena_ind, int flags, bool dirty, ssize_t dt,
- uint64_t dirty_npurge0, uint64_t muzzy_npurge0, bool terminate_asap) {
-#define NINTERVALS 101
- nstime_t time, update_interval, decay_ms, deadline;
-
- nstime_init(&time, 0);
- nstime_update(&time);
-
- nstime_init2(&decay_ms, dt, 0);
- nstime_copy(&deadline, &time);
- nstime_add(&deadline, &decay_ms);
-
- nstime_init2(&update_interval, dt, 0);
- nstime_idivide(&update_interval, NINTERVALS);
-
- /*
- * Keep q's slab from being deallocated during the looping below. If a
- * cached slab were to repeatedly come and go during looping, it could
- * prevent the decay backlog ever becoming empty.
- */
- void *p = do_mallocx(1, flags);
- uint64_t dirty_npurge1, muzzy_npurge1;
- do {
- for (unsigned i = 0; i < DECAY_NTICKS_PER_UPDATE / 2;
- i++) {
- void *q = do_mallocx(1, flags);
- dallocx(q, flags);
+TEST_BEGIN(test_decay_empty) {
+ /* If we never have any decaying pages, npages_limit should be 0. */
+ decay_t decay;
+ memset(&decay, 0, sizeof(decay));
+
+ nstime_t curtime;
+ nstime_init(&curtime, 0);
+
+ uint64_t decay_ms = 1000;
+ uint64_t decay_ns = decay_ms * 1000 * 1000;
+
+ bool err = decay_init(&decay, &curtime, (ssize_t)decay_ms);
+ assert_false(err, "");
+
+ uint64_t time_between_calls = decay_epoch_duration_ns(&decay) / 5;
+ int nepochs = 0;
+ for (uint64_t i = 0; i < decay_ns / time_between_calls * 10; i++) {
+ size_t dirty_pages = 0;
+ nstime_init(&curtime, i * time_between_calls);
+ bool epoch_advanced = decay_maybe_advance_epoch(&decay,
+ &curtime, dirty_pages);
+ if (epoch_advanced) {
+ nepochs++;
+ expect_zu_eq(decay_npages_limit_get(&decay), 0,
+ "Unexpectedly increased npages_limit");
}
- dirty_npurge1 = get_arena_dirty_npurge(arena_ind);
- muzzy_npurge1 = get_arena_muzzy_npurge(arena_ind);
-
- nstime_add(&time_mock, &update_interval);
- nstime_update(&time);
- } while (nstime_compare(&time, &deadline) <= 0 && ((dirty_npurge1 ==
- dirty_npurge0 && muzzy_npurge1 == muzzy_npurge0) ||
- !terminate_asap));
- dallocx(p, flags);
-
- if (config_stats) {
- assert_u64_gt(dirty_npurge1 + muzzy_npurge1, dirty_npurge0 +
- muzzy_npurge0, "Expected purging to occur");
}
-#undef NINTERVALS
+ expect_d_gt(nepochs, 0, "Epochs never advanced");
}
+TEST_END
-TEST_BEGIN(test_decay_ticker) {
- test_skip_if(check_background_thread_enabled());
-#define NPS 2048
- ssize_t ddt = opt_dirty_decay_ms;
- ssize_t mdt = opt_muzzy_decay_ms;
- unsigned arena_ind = do_arena_create(ddt, mdt);
- int flags = (MALLOCX_ARENA(arena_ind) | MALLOCX_TCACHE_NONE);
- void *ps[NPS];
- size_t large;
-
- /*
- * Allocate a bunch of large objects, pause the clock, deallocate every
- * other object (to fragment virtual memory), restore the clock, then
- * [md]allocx() in a tight loop while advancing time rapidly to verify
- * the ticker triggers purging.
- */
-
- size_t tcache_max;
- size_t sz = sizeof(size_t);
- assert_d_eq(mallctl("arenas.tcache_max", (void *)&tcache_max, &sz, NULL,
- 0), 0, "Unexpected mallctl failure");
- large = nallocx(tcache_max + 1, flags);
-
- do_purge(arena_ind);
- uint64_t dirty_npurge0 = get_arena_dirty_npurge(arena_ind);
- uint64_t muzzy_npurge0 = get_arena_muzzy_npurge(arena_ind);
-
- for (unsigned i = 0; i < NPS; i++) {
- ps[i] = do_mallocx(large, flags);
+/*
+ * Verify that npages_limit correctly decays as the time goes.
+ *
+ * During first 'nepoch_init' epochs, add new dirty pages.
+ * After that, let them decay and verify npages_limit decreases.
+ * Then proceed with another 'nepoch_init' epochs and check that
+ * all dirty pages are flushed out of backlog, bringing npages_limit
+ * down to zero.
+ */
+TEST_BEGIN(test_decay) {
+ const uint64_t nepoch_init = 10;
+
+ decay_t decay;
+ memset(&decay, 0, sizeof(decay));
+
+ nstime_t curtime;
+ nstime_init(&curtime, 0);
+
+ uint64_t decay_ms = 1000;
+ uint64_t decay_ns = decay_ms * 1000 * 1000;
+
+ bool err = decay_init(&decay, &curtime, (ssize_t)decay_ms);
+ assert_false(err, "");
+
+ expect_zu_eq(decay_npages_limit_get(&decay), 0,
+ "Empty decay returned nonzero npages_limit");
+
+ nstime_t epochtime;
+ nstime_init(&epochtime, decay_epoch_duration_ns(&decay));
+
+ const size_t dirty_pages_per_epoch = 1000;
+ size_t dirty_pages = 0;
+ uint64_t epoch_ns = decay_epoch_duration_ns(&decay);
+ bool epoch_advanced = false;
+
+ /* Populate backlog with some dirty pages */
+ for (uint64_t i = 0; i < nepoch_init; i++) {
+ nstime_add(&curtime, &epochtime);
+ dirty_pages += dirty_pages_per_epoch;
+ epoch_advanced |= decay_maybe_advance_epoch(&decay, &curtime,
+ dirty_pages);
}
-
- nupdates_mock = 0;
- nstime_init(&time_mock, 0);
- nstime_update(&time_mock);
- monotonic_mock = true;
-
- nstime_monotonic_orig = nstime_monotonic;
- nstime_update_orig = nstime_update;
- nstime_monotonic = nstime_monotonic_mock;
- nstime_update = nstime_update_mock;
-
- for (unsigned i = 0; i < NPS; i += 2) {
- dallocx(ps[i], flags);
- unsigned nupdates0 = nupdates_mock;
- do_decay(arena_ind);
- assert_u_gt(nupdates_mock, nupdates0,
- "Expected nstime_update() to be called");
+ expect_true(epoch_advanced, "Epoch never advanced");
+
+ size_t npages_limit = decay_npages_limit_get(&decay);
+ expect_zu_gt(npages_limit, 0, "npages_limit is incorrectly equal "
+ "to zero after dirty pages have been added");
+
+ /* Keep dirty pages unchanged and verify that npages_limit decreases */
+ for (uint64_t i = nepoch_init; i * epoch_ns < decay_ns; ++i) {
+ nstime_add(&curtime, &epochtime);
+ epoch_advanced = decay_maybe_advance_epoch(&decay, &curtime,
+ dirty_pages);
+ if (epoch_advanced) {
+ size_t npages_limit_new = decay_npages_limit_get(&decay);
+ expect_zu_lt(npages_limit_new, npages_limit,
+ "napges_limit failed to decay");
+
+ npages_limit = npages_limit_new;
+ }
}
- decay_ticker_helper(arena_ind, flags, true, ddt, dirty_npurge0,
- muzzy_npurge0, true);
- decay_ticker_helper(arena_ind, flags, false, ddt+mdt, dirty_npurge0,
- muzzy_npurge0, false);
+ expect_zu_gt(npages_limit, 0, "npages_limit decayed to zero earlier "
+ "than decay_ms since last dirty page was added");
- do_arena_destroy(arena_ind);
+ /* Completely push all dirty pages out of the backlog */
+ epoch_advanced = false;
+ for (uint64_t i = 0; i < nepoch_init; i++) {
+ nstime_add(&curtime, &epochtime);
+ epoch_advanced |= decay_maybe_advance_epoch(&decay, &curtime,
+ dirty_pages);
+ }
+ expect_true(epoch_advanced, "Epoch never advanced");
- nstime_monotonic = nstime_monotonic_orig;
- nstime_update = nstime_update_orig;
-#undef NPS
+ npages_limit = decay_npages_limit_get(&decay);
+ expect_zu_eq(npages_limit, 0, "npages_limit didn't decay to 0 after "
+ "decay_ms since last bump in dirty pages");
}
TEST_END
-TEST_BEGIN(test_decay_nonmonotonic) {
- test_skip_if(check_background_thread_enabled());
-#define NPS (SMOOTHSTEP_NSTEPS + 1)
- int flags = (MALLOCX_ARENA(0) | MALLOCX_TCACHE_NONE);
- void *ps[NPS];
- uint64_t npurge0 = 0;
- uint64_t npurge1 = 0;
- size_t sz, large0;
- unsigned i, nupdates0;
-
- sz = sizeof(size_t);
- assert_d_eq(mallctl("arenas.lextent.0.size", (void *)&large0, &sz, NULL,
- 0), 0, "Unexpected mallctl failure");
-
- assert_d_eq(mallctl("arena.0.purge", NULL, NULL, NULL, 0), 0,
- "Unexpected mallctl failure");
- do_epoch();
- sz = sizeof(uint64_t);
- npurge0 = get_arena_npurge(0);
-
- nupdates_mock = 0;
- nstime_init(&time_mock, 0);
- nstime_update(&time_mock);
- monotonic_mock = false;
-
- nstime_monotonic_orig = nstime_monotonic;
- nstime_update_orig = nstime_update;
- nstime_monotonic = nstime_monotonic_mock;
- nstime_update = nstime_update_mock;
-
- for (i = 0; i < NPS; i++) {
- ps[i] = mallocx(large0, flags);
- assert_ptr_not_null(ps[i], "Unexpected mallocx() failure");
- }
+TEST_BEGIN(test_decay_ns_until_purge) {
+ const uint64_t nepoch_init = 10;
- for (i = 0; i < NPS; i++) {
- dallocx(ps[i], flags);
- nupdates0 = nupdates_mock;
- assert_d_eq(mallctl("arena.0.decay", NULL, NULL, NULL, 0), 0,
- "Unexpected arena.0.decay failure");
- assert_u_gt(nupdates_mock, nupdates0,
- "Expected nstime_update() to be called");
- }
+ decay_t decay;
+ memset(&decay, 0, sizeof(decay));
- do_epoch();
- sz = sizeof(uint64_t);
- npurge1 = get_arena_npurge(0);
+ nstime_t curtime;
+ nstime_init(&curtime, 0);
- if (config_stats) {
- assert_u64_eq(npurge0, npurge1, "Unexpected purging occurred");
- }
+ uint64_t decay_ms = 1000;
+ uint64_t decay_ns = decay_ms * 1000 * 1000;
- nstime_monotonic = nstime_monotonic_orig;
- nstime_update = nstime_update_orig;
-#undef NPS
-}
-TEST_END
+ bool err = decay_init(&decay, &curtime, (ssize_t)decay_ms);
+ assert_false(err, "");
-TEST_BEGIN(test_decay_now) {
- test_skip_if(check_background_thread_enabled());
-
- unsigned arena_ind = do_arena_create(0, 0);
- assert_zu_eq(get_arena_pdirty(arena_ind), 0, "Unexpected dirty pages");
- assert_zu_eq(get_arena_pmuzzy(arena_ind), 0, "Unexpected muzzy pages");
- size_t sizes[] = {16, PAGE<<2, HUGEPAGE<<2};
- /* Verify that dirty/muzzy pages never linger after deallocation. */
- for (unsigned i = 0; i < sizeof(sizes)/sizeof(size_t); i++) {
- size_t size = sizes[i];
- generate_dirty(arena_ind, size);
- assert_zu_eq(get_arena_pdirty(arena_ind), 0,
- "Unexpected dirty pages");
- assert_zu_eq(get_arena_pmuzzy(arena_ind), 0,
- "Unexpected muzzy pages");
- }
- do_arena_destroy(arena_ind);
-}
-TEST_END
+ nstime_t epochtime;
+ nstime_init(&epochtime, decay_epoch_duration_ns(&decay));
-TEST_BEGIN(test_decay_never) {
- test_skip_if(check_background_thread_enabled() || !config_stats);
-
- unsigned arena_ind = do_arena_create(-1, -1);
- int flags = MALLOCX_ARENA(arena_ind) | MALLOCX_TCACHE_NONE;
- assert_zu_eq(get_arena_pdirty(arena_ind), 0, "Unexpected dirty pages");
- assert_zu_eq(get_arena_pmuzzy(arena_ind), 0, "Unexpected muzzy pages");
- size_t sizes[] = {16, PAGE<<2, HUGEPAGE<<2};
- void *ptrs[sizeof(sizes)/sizeof(size_t)];
- for (unsigned i = 0; i < sizeof(sizes)/sizeof(size_t); i++) {
- ptrs[i] = do_mallocx(sizes[i], flags);
- }
- /* Verify that each deallocation generates additional dirty pages. */
- size_t pdirty_prev = get_arena_pdirty(arena_ind);
- size_t pmuzzy_prev = get_arena_pmuzzy(arena_ind);
- assert_zu_eq(pdirty_prev, 0, "Unexpected dirty pages");
- assert_zu_eq(pmuzzy_prev, 0, "Unexpected muzzy pages");
- for (unsigned i = 0; i < sizeof(sizes)/sizeof(size_t); i++) {
- dallocx(ptrs[i], flags);
- size_t pdirty = get_arena_pdirty(arena_ind);
- size_t pmuzzy = get_arena_pmuzzy(arena_ind);
- assert_zu_gt(pdirty + (size_t)get_arena_dirty_purged(arena_ind),
- pdirty_prev, "Expected dirty pages to increase.");
- assert_zu_eq(pmuzzy, 0, "Unexpected muzzy pages");
- pdirty_prev = pdirty;
+ uint64_t ns_until_purge_empty = decay_ns_until_purge(&decay, 0, 0);
+ expect_u64_eq(ns_until_purge_empty, DECAY_UNBOUNDED_TIME_TO_PURGE,
+ "Failed to return unbounded wait time for zero threshold");
+
+ const size_t dirty_pages_per_epoch = 1000;
+ size_t dirty_pages = 0;
+ bool epoch_advanced = false;
+ for (uint64_t i = 0; i < nepoch_init; i++) {
+ nstime_add(&curtime, &epochtime);
+ dirty_pages += dirty_pages_per_epoch;
+ epoch_advanced |= decay_maybe_advance_epoch(&decay, &curtime,
+ dirty_pages);
}
- do_arena_destroy(arena_ind);
+ expect_true(epoch_advanced, "Epoch never advanced");
+
+ uint64_t ns_until_purge_all = decay_ns_until_purge(&decay,
+ dirty_pages, dirty_pages);
+ expect_u64_ge(ns_until_purge_all, decay_ns,
+ "Incorrectly calculated time to purge all pages");
+
+ uint64_t ns_until_purge_none = decay_ns_until_purge(&decay,
+ dirty_pages, 0);
+ expect_u64_eq(ns_until_purge_none, decay_epoch_duration_ns(&decay) * 2,
+ "Incorrectly calculated time to purge 0 pages");
+
+ uint64_t npages_threshold = dirty_pages / 2;
+ uint64_t ns_until_purge_half = decay_ns_until_purge(&decay,
+ dirty_pages, npages_threshold);
+
+ nstime_t waittime;
+ nstime_init(&waittime, ns_until_purge_half);
+ nstime_add(&curtime, &waittime);
+
+ decay_maybe_advance_epoch(&decay, &curtime, dirty_pages);
+ size_t npages_limit = decay_npages_limit_get(&decay);
+ expect_zu_lt(npages_limit, dirty_pages,
+ "npages_limit failed to decrease after waiting");
+ size_t expected = dirty_pages - npages_limit;
+ int deviation = abs((int)expected - (int)(npages_threshold));
+ expect_d_lt(deviation, (int)(npages_threshold / 2),
+ "After waiting, number of pages is out of the expected interval "
+ "[0.5 * npages_threshold .. 1.5 * npages_threshold]");
}
TEST_END
int
main(void) {
return test(
- test_decay_ticks,
- test_decay_ticker,
- test_decay_nonmonotonic,
- test_decay_now,
- test_decay_never);
+ test_decay_init,
+ test_decay_ms_valid,
+ test_decay_npages_purge_in,
+ test_decay_maybe_advance_epoch,
+ test_decay_empty,
+ test_decay,
+ test_decay_ns_until_purge);
}
diff --git a/deps/jemalloc/test/unit/decay.sh b/deps/jemalloc/test/unit/decay.sh
deleted file mode 100644
index 45aeccf42..000000000
--- a/deps/jemalloc/test/unit/decay.sh
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/sh
-
-export MALLOC_CONF="dirty_decay_ms:1000,muzzy_decay_ms:1000,lg_tcache_max:0"
diff --git a/deps/jemalloc/test/unit/div.c b/deps/jemalloc/test/unit/div.c
index b47f10b2b..29aea6659 100644
--- a/deps/jemalloc/test/unit/div.c
+++ b/deps/jemalloc/test/unit/div.c
@@ -14,7 +14,7 @@ TEST_BEGIN(test_div_exhaustive) {
dividend += divisor) {
size_t quotient = div_compute(
&div_info, dividend);
- assert_zu_eq(dividend, quotient * divisor,
+ expect_zu_eq(dividend, quotient * divisor,
"With divisor = %zu, dividend = %zu, "
"got quotient %zu", divisor, dividend, quotient);
}
diff --git a/deps/jemalloc/test/unit/double_free.c b/deps/jemalloc/test/unit/double_free.c
new file mode 100644
index 000000000..12122c1b7
--- /dev/null
+++ b/deps/jemalloc/test/unit/double_free.c
@@ -0,0 +1,77 @@
+#include "test/jemalloc_test.h"
+#include "test/san.h"
+
+#include "jemalloc/internal/safety_check.h"
+
+bool fake_abort_called;
+void fake_abort(const char *message) {
+ (void)message;
+ fake_abort_called = true;
+}
+
+void
+test_large_double_free_pre(void) {
+ safety_check_set_abort(&fake_abort);
+ fake_abort_called = false;
+}
+
+void
+test_large_double_free_post() {
+ expect_b_eq(fake_abort_called, true, "Double-free check didn't fire.");
+ safety_check_set_abort(NULL);
+}
+
+TEST_BEGIN(test_large_double_free_tcache) {
+ test_skip_if(!config_opt_safety_checks);
+ /*
+ * Skip debug builds, since too many assertions will be triggered with
+ * double-free before hitting the one we are interested in.
+ */
+ test_skip_if(config_debug);
+
+ test_large_double_free_pre();
+ char *ptr = malloc(SC_LARGE_MINCLASS);
+ bool guarded = extent_is_guarded(tsdn_fetch(), ptr);
+ free(ptr);
+ if (!guarded) {
+ free(ptr);
+ } else {
+ /*
+ * Skip because guarded extents may unguard immediately on
+ * deallocation, in which case the second free will crash before
+ * reaching the intended safety check.
+ */
+ fake_abort_called = true;
+ }
+ mallctl("thread.tcache.flush", NULL, NULL, NULL, 0);
+ test_large_double_free_post();
+}
+TEST_END
+
+TEST_BEGIN(test_large_double_free_no_tcache) {
+ test_skip_if(!config_opt_safety_checks);
+ test_skip_if(config_debug);
+
+ test_large_double_free_pre();
+ char *ptr = mallocx(SC_LARGE_MINCLASS, MALLOCX_TCACHE_NONE);
+ bool guarded = extent_is_guarded(tsdn_fetch(), ptr);
+ dallocx(ptr, MALLOCX_TCACHE_NONE);
+ if (!guarded) {
+ dallocx(ptr, MALLOCX_TCACHE_NONE);
+ } else {
+ /*
+ * Skip because guarded extents may unguard immediately on
+ * deallocation, in which case the second free will crash before
+ * reaching the intended safety check.
+ */
+ fake_abort_called = true;
+ }
+ test_large_double_free_post();
+}
+TEST_END
+
+int
+main(void) {
+ return test(test_large_double_free_no_tcache,
+ test_large_double_free_tcache);
+}
diff --git a/deps/jemalloc/test/unit/double_free.h b/deps/jemalloc/test/unit/double_free.h
new file mode 100644
index 000000000..8b1378917
--- /dev/null
+++ b/deps/jemalloc/test/unit/double_free.h
@@ -0,0 +1 @@
+
diff --git a/deps/jemalloc/test/unit/edata_cache.c b/deps/jemalloc/test/unit/edata_cache.c
new file mode 100644
index 000000000..af1110a95
--- /dev/null
+++ b/deps/jemalloc/test/unit/edata_cache.c
@@ -0,0 +1,226 @@
+#include "test/jemalloc_test.h"
+
+#include "jemalloc/internal/edata_cache.h"
+
+static void
+test_edata_cache_init(edata_cache_t *edata_cache) {
+ base_t *base = base_new(TSDN_NULL, /* ind */ 1,
+ &ehooks_default_extent_hooks, /* metadata_use_hooks */ true);
+ assert_ptr_not_null(base, "");
+ bool err = edata_cache_init(edata_cache, base);
+ assert_false(err, "");
+}
+
+static void
+test_edata_cache_destroy(edata_cache_t *edata_cache) {
+ base_delete(TSDN_NULL, edata_cache->base);
+}
+
+TEST_BEGIN(test_edata_cache) {
+ edata_cache_t ec;
+ test_edata_cache_init(&ec);
+
+ /* Get one */
+ edata_t *ed1 = edata_cache_get(TSDN_NULL, &ec);
+ assert_ptr_not_null(ed1, "");
+
+ /* Cache should be empty */
+ assert_zu_eq(atomic_load_zu(&ec.count, ATOMIC_RELAXED), 0, "");
+
+ /* Get another */
+ edata_t *ed2 = edata_cache_get(TSDN_NULL, &ec);
+ assert_ptr_not_null(ed2, "");
+
+ /* Still empty */
+ assert_zu_eq(atomic_load_zu(&ec.count, ATOMIC_RELAXED), 0, "");
+
+ /* Put one back, and the cache should now have one item */
+ edata_cache_put(TSDN_NULL, &ec, ed1);
+ assert_zu_eq(atomic_load_zu(&ec.count, ATOMIC_RELAXED), 1, "");
+
+ /* Reallocating should reuse the item, and leave an empty cache. */
+ edata_t *ed1_again = edata_cache_get(TSDN_NULL, &ec);
+ assert_ptr_eq(ed1, ed1_again, "");
+ assert_zu_eq(atomic_load_zu(&ec.count, ATOMIC_RELAXED), 0, "");
+
+ test_edata_cache_destroy(&ec);
+}
+TEST_END
+
+static size_t
+ecf_count(edata_cache_fast_t *ecf) {
+ size_t count = 0;
+ edata_t *cur;
+ ql_foreach(cur, &ecf->list.head, ql_link_inactive) {
+ count++;
+ }
+ return count;
+}
+
+TEST_BEGIN(test_edata_cache_fast_simple) {
+ edata_cache_t ec;
+ edata_cache_fast_t ecf;
+
+ test_edata_cache_init(&ec);
+ edata_cache_fast_init(&ecf, &ec);
+
+ edata_t *ed1 = edata_cache_fast_get(TSDN_NULL, &ecf);
+ expect_ptr_not_null(ed1, "");
+ expect_zu_eq(ecf_count(&ecf), 0, "");
+ expect_zu_eq(atomic_load_zu(&ec.count, ATOMIC_RELAXED), 0, "");
+
+ edata_t *ed2 = edata_cache_fast_get(TSDN_NULL, &ecf);
+ expect_ptr_not_null(ed2, "");
+ expect_zu_eq(ecf_count(&ecf), 0, "");
+ expect_zu_eq(atomic_load_zu(&ec.count, ATOMIC_RELAXED), 0, "");
+
+ edata_cache_fast_put(TSDN_NULL, &ecf, ed1);
+ expect_zu_eq(ecf_count(&ecf), 1, "");
+ expect_zu_eq(atomic_load_zu(&ec.count, ATOMIC_RELAXED), 0, "");
+
+ edata_cache_fast_put(TSDN_NULL, &ecf, ed2);
+ expect_zu_eq(ecf_count(&ecf), 2, "");
+ expect_zu_eq(atomic_load_zu(&ec.count, ATOMIC_RELAXED), 0, "");
+
+ /* LIFO ordering. */
+ expect_ptr_eq(ed2, edata_cache_fast_get(TSDN_NULL, &ecf), "");
+ expect_zu_eq(ecf_count(&ecf), 1, "");
+ expect_zu_eq(atomic_load_zu(&ec.count, ATOMIC_RELAXED), 0, "");
+
+ expect_ptr_eq(ed1, edata_cache_fast_get(TSDN_NULL, &ecf), "");
+ expect_zu_eq(ecf_count(&ecf), 0, "");
+ expect_zu_eq(atomic_load_zu(&ec.count, ATOMIC_RELAXED), 0, "");
+
+ test_edata_cache_destroy(&ec);
+}
+TEST_END
+
+TEST_BEGIN(test_edata_cache_fill) {
+ edata_cache_t ec;
+ edata_cache_fast_t ecf;
+
+ test_edata_cache_init(&ec);
+ edata_cache_fast_init(&ecf, &ec);
+
+ edata_t *allocs[EDATA_CACHE_FAST_FILL * 2];
+
+ /*
+ * If the fallback cache can't satisfy the request, we shouldn't do
+ * extra allocations until compelled to. Put half the fill goal in the
+ * fallback.
+ */
+ for (int i = 0; i < EDATA_CACHE_FAST_FILL / 2; i++) {
+ allocs[i] = edata_cache_get(TSDN_NULL, &ec);
+ }
+ for (int i = 0; i < EDATA_CACHE_FAST_FILL / 2; i++) {
+ edata_cache_put(TSDN_NULL, &ec, allocs[i]);
+ }
+ expect_zu_eq(EDATA_CACHE_FAST_FILL / 2,
+ atomic_load_zu(&ec.count, ATOMIC_RELAXED), "");
+
+ allocs[0] = edata_cache_fast_get(TSDN_NULL, &ecf);
+ expect_zu_eq(EDATA_CACHE_FAST_FILL / 2 - 1, ecf_count(&ecf),
+ "Should have grabbed all edatas available but no more.");
+
+ for (int i = 1; i < EDATA_CACHE_FAST_FILL / 2; i++) {
+ allocs[i] = edata_cache_fast_get(TSDN_NULL, &ecf);
+ expect_ptr_not_null(allocs[i], "");
+ }
+ expect_zu_eq(0, ecf_count(&ecf), "");
+
+ /* When forced, we should alloc from the base. */
+ edata_t *edata = edata_cache_fast_get(TSDN_NULL, &ecf);
+ expect_ptr_not_null(edata, "");
+ expect_zu_eq(0, ecf_count(&ecf), "Allocated more than necessary");
+ expect_zu_eq(0, atomic_load_zu(&ec.count, ATOMIC_RELAXED),
+ "Allocated more than necessary");
+
+ /*
+ * We should correctly fill in the common case where the fallback isn't
+ * exhausted, too.
+ */
+ for (int i = 0; i < EDATA_CACHE_FAST_FILL * 2; i++) {
+ allocs[i] = edata_cache_get(TSDN_NULL, &ec);
+ expect_ptr_not_null(allocs[i], "");
+ }
+ for (int i = 0; i < EDATA_CACHE_FAST_FILL * 2; i++) {
+ edata_cache_put(TSDN_NULL, &ec, allocs[i]);
+ }
+
+ allocs[0] = edata_cache_fast_get(TSDN_NULL, &ecf);
+ expect_zu_eq(EDATA_CACHE_FAST_FILL - 1, ecf_count(&ecf), "");
+ expect_zu_eq(EDATA_CACHE_FAST_FILL,
+ atomic_load_zu(&ec.count, ATOMIC_RELAXED), "");
+ for (int i = 1; i < EDATA_CACHE_FAST_FILL; i++) {
+ expect_zu_eq(EDATA_CACHE_FAST_FILL - i, ecf_count(&ecf), "");
+ expect_zu_eq(EDATA_CACHE_FAST_FILL,
+ atomic_load_zu(&ec.count, ATOMIC_RELAXED), "");
+ allocs[i] = edata_cache_fast_get(TSDN_NULL, &ecf);
+ expect_ptr_not_null(allocs[i], "");
+ }
+ expect_zu_eq(0, ecf_count(&ecf), "");
+ expect_zu_eq(EDATA_CACHE_FAST_FILL,
+ atomic_load_zu(&ec.count, ATOMIC_RELAXED), "");
+
+ allocs[0] = edata_cache_fast_get(TSDN_NULL, &ecf);
+ expect_zu_eq(EDATA_CACHE_FAST_FILL - 1, ecf_count(&ecf), "");
+ expect_zu_eq(0, atomic_load_zu(&ec.count, ATOMIC_RELAXED), "");
+ for (int i = 1; i < EDATA_CACHE_FAST_FILL; i++) {
+ expect_zu_eq(EDATA_CACHE_FAST_FILL - i, ecf_count(&ecf), "");
+ expect_zu_eq(0, atomic_load_zu(&ec.count, ATOMIC_RELAXED), "");
+ allocs[i] = edata_cache_fast_get(TSDN_NULL, &ecf);
+ expect_ptr_not_null(allocs[i], "");
+ }
+ expect_zu_eq(0, ecf_count(&ecf), "");
+ expect_zu_eq(0, atomic_load_zu(&ec.count, ATOMIC_RELAXED), "");
+
+ test_edata_cache_destroy(&ec);
+}
+TEST_END
+
+TEST_BEGIN(test_edata_cache_disable) {
+ edata_cache_t ec;
+ edata_cache_fast_t ecf;
+
+ test_edata_cache_init(&ec);
+ edata_cache_fast_init(&ecf, &ec);
+
+ for (int i = 0; i < EDATA_CACHE_FAST_FILL; i++) {
+ edata_t *edata = edata_cache_get(TSDN_NULL, &ec);
+ expect_ptr_not_null(edata, "");
+ edata_cache_fast_put(TSDN_NULL, &ecf, edata);
+ }
+
+ expect_zu_eq(EDATA_CACHE_FAST_FILL, ecf_count(&ecf), "");
+ expect_zu_eq(0, atomic_load_zu(&ec.count, ATOMIC_RELAXED), "");
+
+ edata_cache_fast_disable(TSDN_NULL, &ecf);
+
+ expect_zu_eq(0, ecf_count(&ecf), "");
+ expect_zu_eq(EDATA_CACHE_FAST_FILL,
+ atomic_load_zu(&ec.count, ATOMIC_RELAXED), "Disabling should flush");
+
+ edata_t *edata = edata_cache_fast_get(TSDN_NULL, &ecf);
+ expect_zu_eq(0, ecf_count(&ecf), "");
+ expect_zu_eq(EDATA_CACHE_FAST_FILL - 1,
+ atomic_load_zu(&ec.count, ATOMIC_RELAXED),
+ "Disabled ecf should forward on get");
+
+ edata_cache_fast_put(TSDN_NULL, &ecf, edata);
+ expect_zu_eq(0, ecf_count(&ecf), "");
+ expect_zu_eq(EDATA_CACHE_FAST_FILL,
+ atomic_load_zu(&ec.count, ATOMIC_RELAXED),
+ "Disabled ecf should forward on put");
+
+ test_edata_cache_destroy(&ec);
+}
+TEST_END
+
+int
+main(void) {
+ return test(
+ test_edata_cache,
+ test_edata_cache_fast_simple,
+ test_edata_cache_fill,
+ test_edata_cache_disable);
+}
diff --git a/deps/jemalloc/test/unit/emitter.c b/deps/jemalloc/test/unit/emitter.c
index b4a693f4b..ef8f9ff58 100644
--- a/deps/jemalloc/test/unit/emitter.c
+++ b/deps/jemalloc/test/unit/emitter.c
@@ -58,15 +58,17 @@ forwarding_cb(void *buf_descriptor_v, const char *str) {
size_t written = malloc_snprintf(buf_descriptor->buf,
buf_descriptor->len, "%s", str);
- assert_zu_eq(written, strlen(str), "Buffer overflow!");
+ expect_zu_eq(written, strlen(str), "Buffer overflow!");
buf_descriptor->buf += written;
buf_descriptor->len -= written;
- assert_zu_gt(buf_descriptor->len, 0, "Buffer out of space!");
+ expect_zu_gt(buf_descriptor->len, 0, "Buffer out of space!");
}
static void
-assert_emit_output(void (*emit_fn)(emitter_t *),
- const char *expected_json_output, const char *expected_table_output) {
+expect_emit_output(void (*emit_fn)(emitter_t *),
+ const char *expected_json_output,
+ const char *expected_json_compact_output,
+ const char *expected_table_output) {
emitter_t emitter;
char buf[MALLOC_PRINTF_BUFSIZE];
buf_descriptor_t buf_descriptor;
@@ -78,7 +80,17 @@ assert_emit_output(void (*emit_fn)(emitter_t *),
emitter_init(&emitter, emitter_output_json, &forwarding_cb,
&buf_descriptor);
(*emit_fn)(&emitter);
- assert_str_eq(expected_json_output, buf, "json output failure");
+ expect_str_eq(expected_json_output, buf, "json output failure");
+
+ buf_descriptor.buf = buf;
+ buf_descriptor.len = MALLOC_PRINTF_BUFSIZE;
+ buf_descriptor.mid_quote = false;
+
+ emitter_init(&emitter, emitter_output_json_compact, &forwarding_cb,
+ &buf_descriptor);
+ (*emit_fn)(&emitter);
+ expect_str_eq(expected_json_compact_output, buf,
+ "compact json output failure");
buf_descriptor.buf = buf;
buf_descriptor.len = MALLOC_PRINTF_BUFSIZE;
@@ -87,7 +99,7 @@ assert_emit_output(void (*emit_fn)(emitter_t *),
emitter_init(&emitter, emitter_output_table, &forwarding_cb,
&buf_descriptor);
(*emit_fn)(&emitter);
- assert_str_eq(expected_table_output, buf, "table output failure");
+ expect_str_eq(expected_table_output, buf, "table output failure");
}
static void
@@ -108,6 +120,7 @@ emit_dict(emitter_t *emitter) {
emitter_dict_end(emitter);
emitter_end(emitter);
}
+
static const char *dict_json =
"{\n"
"\t\"foo\": {\n"
@@ -117,6 +130,15 @@ static const char *dict_json =
"\t\t\"jkl\": \"a string\"\n"
"\t}\n"
"}\n";
+static const char *dict_json_compact =
+"{"
+ "\"foo\":{"
+ "\"abc\":false,"
+ "\"def\":true,"
+ "\"ghi\":123,"
+ "\"jkl\":\"a string\""
+ "}"
+"}";
static const char *dict_table =
"This is the foo table:\n"
" ABC: false\n"
@@ -124,11 +146,6 @@ static const char *dict_table =
" GHI: 123 (note_key1: \"a string\")\n"
" JKL: \"a string\" (note_key2: false)\n";
-TEST_BEGIN(test_dict) {
- assert_emit_output(&emit_dict, dict_json, dict_table);
-}
-TEST_END
-
static void
emit_table_printf(emitter_t *emitter) {
emitter_begin(emitter);
@@ -141,17 +158,11 @@ emit_table_printf(emitter_t *emitter) {
static const char *table_printf_json =
"{\n"
"}\n";
-
+static const char *table_printf_json_compact = "{}";
static const char *table_printf_table =
"Table note 1\n"
"Table note 2 with format string\n";
-TEST_BEGIN(test_table_printf) {
- assert_emit_output(&emit_table_printf, table_printf_json,
- table_printf_table);
-}
-TEST_END
-
static void emit_nested_dict(emitter_t *emitter) {
int val = 123;
emitter_begin(emitter);
@@ -169,7 +180,7 @@ static void emit_nested_dict(emitter_t *emitter) {
emitter_end(emitter);
}
-static const char *nested_object_json =
+static const char *nested_dict_json =
"{\n"
"\t\"json1\": {\n"
"\t\t\"json2\": {\n"
@@ -182,8 +193,20 @@ static const char *nested_object_json =
"\t\t\"primitive\": 123\n"
"\t}\n"
"}\n";
-
-static const char *nested_object_table =
+static const char *nested_dict_json_compact =
+"{"
+ "\"json1\":{"
+ "\"json2\":{"
+ "\"primitive\":123"
+ "},"
+ "\"json3\":{"
+ "}"
+ "},"
+ "\"json4\":{"
+ "\"primitive\":123"
+ "}"
+"}";
+static const char *nested_dict_table =
"Dict 1\n"
" Dict 2\n"
" A primitive: 123\n"
@@ -191,12 +214,6 @@ static const char *nested_object_table =
"Dict 4\n"
" Another primitive: 123\n";
-TEST_BEGIN(test_nested_dict) {
- assert_emit_output(&emit_nested_dict, nested_object_json,
- nested_object_table);
-}
-TEST_END
-
static void
emit_types(emitter_t *emitter) {
bool b = false;
@@ -235,7 +252,17 @@ static const char *types_json =
"\t\"k7\": 789,\n"
"\t\"k8\": 10000000000\n"
"}\n";
-
+static const char *types_json_compact =
+"{"
+ "\"k1\":false,"
+ "\"k2\":-123,"
+ "\"k3\":123,"
+ "\"k4\":-456,"
+ "\"k5\":456,"
+ "\"k6\":\"string\","
+ "\"k7\":789,"
+ "\"k8\":10000000000"
+"}";
static const char *types_table =
"K1: false\n"
"K2: -123\n"
@@ -246,11 +273,6 @@ static const char *types_table =
"K7: 789\n"
"K8: 10000000000\n";
-TEST_BEGIN(test_types) {
- assert_emit_output(&emit_types, types_json, types_table);
-}
-TEST_END
-
static void
emit_modal(emitter_t *emitter) {
int val = 123;
@@ -283,7 +305,18 @@ const char *modal_json =
"\t\t\"i6\": 123\n"
"\t}\n"
"}\n";
-
+const char *modal_json_compact =
+"{"
+ "\"j0\":{"
+ "\"j1\":{"
+ "\"i1\":123,"
+ "\"i2\":123,"
+ "\"i4\":123"
+ "},"
+ "\"i5\":123,"
+ "\"i6\":123"
+ "}"
+"}";
const char *modal_table =
"T0\n"
" I1: 123\n"
@@ -293,13 +326,8 @@ const char *modal_table =
" I5: 123\n"
" I6: 123\n";
-TEST_BEGIN(test_modal) {
- assert_emit_output(&emit_modal, modal_json, modal_table);
-}
-TEST_END
-
static void
-emit_json_arr(emitter_t *emitter) {
+emit_json_array(emitter_t *emitter) {
int ival = 123;
emitter_begin(emitter);
@@ -338,14 +366,24 @@ static const char *json_array_json =
"\t\t]\n"
"\t}\n"
"}\n";
-
+static const char *json_array_json_compact =
+"{"
+ "\"dict\":{"
+ "\"arr\":["
+ "{"
+ "\"foo\":123"
+ "},"
+ "123,"
+ "123,"
+ "{"
+ "\"bar\":123,"
+ "\"baz\":123"
+ "}"
+ "]"
+ "}"
+"}";
static const char *json_array_table = "";
-TEST_BEGIN(test_json_arr) {
- assert_emit_output(&emit_json_arr, json_array_json, json_array_table);
-}
-TEST_END
-
static void
emit_json_nested_array(emitter_t *emitter) {
int ival = 123;
@@ -391,12 +429,27 @@ static const char *json_nested_array_json =
"\t\t]\n"
"\t]\n"
"}\n";
-
-TEST_BEGIN(test_json_nested_arr) {
- assert_emit_output(&emit_json_nested_array, json_nested_array_json,
- json_array_table);
-}
-TEST_END
+static const char *json_nested_array_json_compact =
+"{"
+ "["
+ "["
+ "123,"
+ "\"foo\","
+ "123,"
+ "\"foo\""
+ "],"
+ "["
+ "123"
+ "],"
+ "["
+ "\"foo\","
+ "123"
+ "],"
+ "["
+ "]"
+ "]"
+"}";
+static const char *json_nested_array_table = "";
static void
emit_table_row(emitter_t *emitter) {
@@ -443,18 +496,29 @@ emit_table_row(emitter_t *emitter) {
static const char *table_row_json =
"{\n"
"}\n";
-
+static const char *table_row_json_compact = "{}";
static const char *table_row_table =
"ABC title DEF title GHI\n"
"123 true 456\n"
"789 false 1011\n"
"\"a string\" false ghi\n";
-TEST_BEGIN(test_table_row) {
- assert_emit_output(&emit_table_row, table_row_json, table_row_table);
-}
+#define GENERATE_TEST(feature) \
+TEST_BEGIN(test_##feature) { \
+ expect_emit_output(emit_##feature, feature##_json, \
+ feature##_json_compact, feature##_table); \
+} \
TEST_END
+GENERATE_TEST(dict)
+GENERATE_TEST(table_printf)
+GENERATE_TEST(nested_dict)
+GENERATE_TEST(types)
+GENERATE_TEST(modal)
+GENERATE_TEST(json_array)
+GENERATE_TEST(json_nested_array)
+GENERATE_TEST(table_row)
+
int
main(void) {
return test_no_reentrancy(
@@ -463,7 +527,7 @@ main(void) {
test_nested_dict,
test_types,
test_modal,
- test_json_arr,
- test_json_nested_arr,
+ test_json_array,
+ test_json_nested_array,
test_table_row);
}
diff --git a/deps/jemalloc/test/unit/extent_quantize.c b/deps/jemalloc/test/unit/extent_quantize.c
index 0ca7a75d9..e6bbd539c 100644
--- a/deps/jemalloc/test/unit/extent_quantize.c
+++ b/deps/jemalloc/test/unit/extent_quantize.c
@@ -12,22 +12,22 @@ TEST_BEGIN(test_small_extent_size) {
*/
sz = sizeof(unsigned);
- assert_d_eq(mallctl("arenas.nbins", (void *)&nbins, &sz, NULL, 0), 0,
+ expect_d_eq(mallctl("arenas.nbins", (void *)&nbins, &sz, NULL, 0), 0,
"Unexpected mallctl failure");
- assert_d_eq(mallctlnametomib("arenas.bin.0.slab_size", mib, &miblen), 0,
+ expect_d_eq(mallctlnametomib("arenas.bin.0.slab_size", mib, &miblen), 0,
"Unexpected mallctlnametomib failure");
for (i = 0; i < nbins; i++) {
mib[2] = i;
sz = sizeof(size_t);
- assert_d_eq(mallctlbymib(mib, miblen, (void *)&extent_size, &sz,
+ expect_d_eq(mallctlbymib(mib, miblen, (void *)&extent_size, &sz,
NULL, 0), 0, "Unexpected mallctlbymib failure");
- assert_zu_eq(extent_size,
- extent_size_quantize_floor(extent_size),
+ expect_zu_eq(extent_size,
+ sz_psz_quantize_floor(extent_size),
"Small extent quantization should be a no-op "
"(extent_size=%zu)", extent_size);
- assert_zu_eq(extent_size,
- extent_size_quantize_ceil(extent_size),
+ expect_zu_eq(extent_size,
+ sz_psz_quantize_ceil(extent_size),
"Small extent quantization should be a no-op "
"(extent_size=%zu)", extent_size);
}
@@ -47,42 +47,42 @@ TEST_BEGIN(test_large_extent_size) {
*/
sz = sizeof(bool);
- assert_d_eq(mallctl("config.cache_oblivious", (void *)&cache_oblivious,
+ expect_d_eq(mallctl("opt.cache_oblivious", (void *)&cache_oblivious,
&sz, NULL, 0), 0, "Unexpected mallctl failure");
sz = sizeof(unsigned);
- assert_d_eq(mallctl("arenas.nlextents", (void *)&nlextents, &sz, NULL,
+ expect_d_eq(mallctl("arenas.nlextents", (void *)&nlextents, &sz, NULL,
0), 0, "Unexpected mallctl failure");
- assert_d_eq(mallctlnametomib("arenas.lextent.0.size", mib, &miblen), 0,
+ expect_d_eq(mallctlnametomib("arenas.lextent.0.size", mib, &miblen), 0,
"Unexpected mallctlnametomib failure");
for (i = 0; i < nlextents; i++) {
size_t lextent_size, extent_size, floor, ceil;
mib[2] = i;
sz = sizeof(size_t);
- assert_d_eq(mallctlbymib(mib, miblen, (void *)&lextent_size,
+ expect_d_eq(mallctlbymib(mib, miblen, (void *)&lextent_size,
&sz, NULL, 0), 0, "Unexpected mallctlbymib failure");
extent_size = cache_oblivious ? lextent_size + PAGE :
lextent_size;
- floor = extent_size_quantize_floor(extent_size);
- ceil = extent_size_quantize_ceil(extent_size);
+ floor = sz_psz_quantize_floor(extent_size);
+ ceil = sz_psz_quantize_ceil(extent_size);
- assert_zu_eq(extent_size, floor,
+ expect_zu_eq(extent_size, floor,
"Extent quantization should be a no-op for precise size "
"(lextent_size=%zu, extent_size=%zu)", lextent_size,
extent_size);
- assert_zu_eq(extent_size, ceil,
+ expect_zu_eq(extent_size, ceil,
"Extent quantization should be a no-op for precise size "
"(lextent_size=%zu, extent_size=%zu)", lextent_size,
extent_size);
if (i > 0) {
- assert_zu_eq(extent_size_prev,
- extent_size_quantize_floor(extent_size - PAGE),
+ expect_zu_eq(extent_size_prev,
+ sz_psz_quantize_floor(extent_size - PAGE),
"Floor should be a precise size");
if (extent_size_prev < ceil_prev) {
- assert_zu_eq(ceil_prev, extent_size,
+ expect_zu_eq(ceil_prev, extent_size,
"Ceiling should be a precise size "
"(extent_size_prev=%zu, ceil_prev=%zu, "
"extent_size=%zu)", extent_size_prev,
@@ -91,7 +91,7 @@ TEST_BEGIN(test_large_extent_size) {
}
if (i + 1 < nlextents) {
extent_size_prev = floor;
- ceil_prev = extent_size_quantize_ceil(extent_size +
+ ceil_prev = sz_psz_quantize_ceil(extent_size +
PAGE);
}
}
@@ -109,20 +109,20 @@ TEST_BEGIN(test_monotonic) {
size_t extent_size, floor, ceil;
extent_size = i << LG_PAGE;
- floor = extent_size_quantize_floor(extent_size);
- ceil = extent_size_quantize_ceil(extent_size);
+ floor = sz_psz_quantize_floor(extent_size);
+ ceil = sz_psz_quantize_ceil(extent_size);
- assert_zu_le(floor, extent_size,
+ expect_zu_le(floor, extent_size,
"Floor should be <= (floor=%zu, extent_size=%zu, ceil=%zu)",
floor, extent_size, ceil);
- assert_zu_ge(ceil, extent_size,
+ expect_zu_ge(ceil, extent_size,
"Ceiling should be >= (floor=%zu, extent_size=%zu, "
"ceil=%zu)", floor, extent_size, ceil);
- assert_zu_le(floor_prev, floor, "Floor should be monotonic "
+ expect_zu_le(floor_prev, floor, "Floor should be monotonic "
"(floor_prev=%zu, floor=%zu, extent_size=%zu, ceil=%zu)",
floor_prev, floor, extent_size, ceil);
- assert_zu_le(ceil_prev, ceil, "Ceiling should be monotonic "
+ expect_zu_le(ceil_prev, ceil, "Ceiling should be monotonic "
"(floor=%zu, extent_size=%zu, ceil_prev=%zu, ceil=%zu)",
floor, extent_size, ceil_prev, ceil);
diff --git a/deps/jemalloc/test/unit/fb.c b/deps/jemalloc/test/unit/fb.c
new file mode 100644
index 000000000..ad72c75ad
--- /dev/null
+++ b/deps/jemalloc/test/unit/fb.c
@@ -0,0 +1,954 @@
+#include "test/jemalloc_test.h"
+
+#include "jemalloc/internal/fb.h"
+#include "test/nbits.h"
+
+static void
+do_test_init(size_t nbits) {
+ size_t sz = FB_NGROUPS(nbits) * sizeof(fb_group_t);
+ fb_group_t *fb = malloc(sz);
+ /* Junk fb's contents. */
+ memset(fb, 99, sz);
+ fb_init(fb, nbits);
+ for (size_t i = 0; i < nbits; i++) {
+ expect_false(fb_get(fb, nbits, i),
+ "bitmap should start empty");
+ }
+ free(fb);
+}
+
+TEST_BEGIN(test_fb_init) {
+#define NB(nbits) \
+ do_test_init(nbits);
+ NBITS_TAB
+#undef NB
+}
+TEST_END
+
+static void
+do_test_get_set_unset(size_t nbits) {
+ size_t sz = FB_NGROUPS(nbits) * sizeof(fb_group_t);
+ fb_group_t *fb = malloc(sz);
+ fb_init(fb, nbits);
+ /* Set the bits divisible by 3. */
+ for (size_t i = 0; i < nbits; i++) {
+ if (i % 3 == 0) {
+ fb_set(fb, nbits, i);
+ }
+ }
+ /* Check them. */
+ for (size_t i = 0; i < nbits; i++) {
+ expect_b_eq(i % 3 == 0, fb_get(fb, nbits, i),
+ "Unexpected bit at position %zu", i);
+ }
+ /* Unset those divisible by 5. */
+ for (size_t i = 0; i < nbits; i++) {
+ if (i % 5 == 0) {
+ fb_unset(fb, nbits, i);
+ }
+ }
+ /* Check them. */
+ for (size_t i = 0; i < nbits; i++) {
+ expect_b_eq(i % 3 == 0 && i % 5 != 0, fb_get(fb, nbits, i),
+ "Unexpected bit at position %zu", i);
+ }
+ free(fb);
+}
+
+TEST_BEGIN(test_get_set_unset) {
+#define NB(nbits) \
+ do_test_get_set_unset(nbits);
+ NBITS_TAB
+#undef NB
+}
+TEST_END
+
+static ssize_t
+find_3_5_compute(ssize_t i, size_t nbits, bool bit, bool forward) {
+ for(; i < (ssize_t)nbits && i >= 0; i += (forward ? 1 : -1)) {
+ bool expected_bit = i % 3 == 0 || i % 5 == 0;
+ if (expected_bit == bit) {
+ return i;
+ }
+ }
+ return forward ? (ssize_t)nbits : (ssize_t)-1;
+}
+
+static void
+do_test_search_simple(size_t nbits) {
+ size_t sz = FB_NGROUPS(nbits) * sizeof(fb_group_t);
+ fb_group_t *fb = malloc(sz);
+ fb_init(fb, nbits);
+
+ /* We pick multiples of 3 or 5. */
+ for (size_t i = 0; i < nbits; i++) {
+ if (i % 3 == 0) {
+ fb_set(fb, nbits, i);
+ }
+ /* This tests double-setting a little, too. */
+ if (i % 5 == 0) {
+ fb_set(fb, nbits, i);
+ }
+ }
+ for (size_t i = 0; i < nbits; i++) {
+ size_t ffs_compute = find_3_5_compute(i, nbits, true, true);
+ size_t ffs_search = fb_ffs(fb, nbits, i);
+ expect_zu_eq(ffs_compute, ffs_search, "ffs mismatch at %zu", i);
+
+ ssize_t fls_compute = find_3_5_compute(i, nbits, true, false);
+ size_t fls_search = fb_fls(fb, nbits, i);
+ expect_zu_eq(fls_compute, fls_search, "fls mismatch at %zu", i);
+
+ size_t ffu_compute = find_3_5_compute(i, nbits, false, true);
+ size_t ffu_search = fb_ffu(fb, nbits, i);
+ expect_zu_eq(ffu_compute, ffu_search, "ffu mismatch at %zu", i);
+
+ size_t flu_compute = find_3_5_compute(i, nbits, false, false);
+ size_t flu_search = fb_flu(fb, nbits, i);
+ expect_zu_eq(flu_compute, flu_search, "flu mismatch at %zu", i);
+ }
+
+ free(fb);
+}
+
+TEST_BEGIN(test_search_simple) {
+#define NB(nbits) \
+ do_test_search_simple(nbits);
+ NBITS_TAB
+#undef NB
+}
+TEST_END
+
+static void
+expect_exhaustive_results(fb_group_t *mostly_full, fb_group_t *mostly_empty,
+ size_t nbits, size_t special_bit, size_t position) {
+ if (position < special_bit) {
+ expect_zu_eq(special_bit, fb_ffs(mostly_empty, nbits, position),
+ "mismatch at %zu, %zu", position, special_bit);
+ expect_zd_eq(-1, fb_fls(mostly_empty, nbits, position),
+ "mismatch at %zu, %zu", position, special_bit);
+ expect_zu_eq(position, fb_ffu(mostly_empty, nbits, position),
+ "mismatch at %zu, %zu", position, special_bit);
+ expect_zd_eq(position, fb_flu(mostly_empty, nbits, position),
+ "mismatch at %zu, %zu", position, special_bit);
+
+ expect_zu_eq(position, fb_ffs(mostly_full, nbits, position),
+ "mismatch at %zu, %zu", position, special_bit);
+ expect_zd_eq(position, fb_fls(mostly_full, nbits, position),
+ "mismatch at %zu, %zu", position, special_bit);
+ expect_zu_eq(special_bit, fb_ffu(mostly_full, nbits, position),
+ "mismatch at %zu, %zu", position, special_bit);
+ expect_zd_eq(-1, fb_flu(mostly_full, nbits, position),
+ "mismatch at %zu, %zu", position, special_bit);
+ } else if (position == special_bit) {
+ expect_zu_eq(special_bit, fb_ffs(mostly_empty, nbits, position),
+ "mismatch at %zu, %zu", position, special_bit);
+ expect_zd_eq(special_bit, fb_fls(mostly_empty, nbits, position),
+ "mismatch at %zu, %zu", position, special_bit);
+ expect_zu_eq(position + 1, fb_ffu(mostly_empty, nbits, position),
+ "mismatch at %zu, %zu", position, special_bit);
+ expect_zd_eq(position - 1, fb_flu(mostly_empty, nbits,
+ position), "mismatch at %zu, %zu", position, special_bit);
+
+ expect_zu_eq(position + 1, fb_ffs(mostly_full, nbits, position),
+ "mismatch at %zu, %zu", position, special_bit);
+ expect_zd_eq(position - 1, fb_fls(mostly_full, nbits,
+ position), "mismatch at %zu, %zu", position, special_bit);
+ expect_zu_eq(position, fb_ffu(mostly_full, nbits, position),
+ "mismatch at %zu, %zu", position, special_bit);
+ expect_zd_eq(position, fb_flu(mostly_full, nbits, position),
+ "mismatch at %zu, %zu", position, special_bit);
+ } else {
+ /* position > special_bit. */
+ expect_zu_eq(nbits, fb_ffs(mostly_empty, nbits, position),
+ "mismatch at %zu, %zu", position, special_bit);
+ expect_zd_eq(special_bit, fb_fls(mostly_empty, nbits,
+ position), "mismatch at %zu, %zu", position, special_bit);
+ expect_zu_eq(position, fb_ffu(mostly_empty, nbits, position),
+ "mismatch at %zu, %zu", position, special_bit);
+ expect_zd_eq(position, fb_flu(mostly_empty, nbits, position),
+ "mismatch at %zu, %zu", position, special_bit);
+
+ expect_zu_eq(position, fb_ffs(mostly_full, nbits, position),
+ "mismatch at %zu, %zu", position, special_bit);
+ expect_zd_eq(position, fb_fls(mostly_full, nbits, position),
+ "mismatch at %zu, %zu", position, special_bit);
+ expect_zu_eq(nbits, fb_ffu(mostly_full, nbits, position),
+ "mismatch at %zu, %zu", position, special_bit);
+ expect_zd_eq(special_bit, fb_flu(mostly_full, nbits, position),
+ "mismatch at %zu, %zu", position, special_bit);
+ }
+}
+
+static void
+do_test_search_exhaustive(size_t nbits) {
+ /* This test is quadratic; let's not get too big. */
+ if (nbits > 1000) {
+ return;
+ }
+ size_t sz = FB_NGROUPS(nbits) * sizeof(fb_group_t);
+ fb_group_t *empty = malloc(sz);
+ fb_init(empty, nbits);
+ fb_group_t *full = malloc(sz);
+ fb_init(full, nbits);
+ fb_set_range(full, nbits, 0, nbits);
+
+ for (size_t i = 0; i < nbits; i++) {
+ fb_set(empty, nbits, i);
+ fb_unset(full, nbits, i);
+
+ for (size_t j = 0; j < nbits; j++) {
+ expect_exhaustive_results(full, empty, nbits, i, j);
+ }
+ fb_unset(empty, nbits, i);
+ fb_set(full, nbits, i);
+ }
+
+ free(empty);
+ free(full);
+}
+
+TEST_BEGIN(test_search_exhaustive) {
+#define NB(nbits) \
+ do_test_search_exhaustive(nbits);
+ NBITS_TAB
+#undef NB
+}
+TEST_END
+
+TEST_BEGIN(test_range_simple) {
+ /*
+ * Just pick a constant big enough to have nontrivial middle sizes, and
+ * big enough that usages of things like weirdnum (below) near the
+ * beginning fit comfortably into the beginning of the bitmap.
+ */
+ size_t nbits = 64 * 10;
+ size_t ngroups = FB_NGROUPS(nbits);
+ fb_group_t *fb = malloc(sizeof(fb_group_t) * ngroups);
+ fb_init(fb, nbits);
+ for (size_t i = 0; i < nbits; i++) {
+ if (i % 2 == 0) {
+ fb_set_range(fb, nbits, i, 1);
+ }
+ }
+ for (size_t i = 0; i < nbits; i++) {
+ expect_b_eq(i % 2 == 0, fb_get(fb, nbits, i),
+ "mismatch at position %zu", i);
+ }
+ fb_set_range(fb, nbits, 0, nbits / 2);
+ fb_unset_range(fb, nbits, nbits / 2, nbits / 2);
+ for (size_t i = 0; i < nbits; i++) {
+ expect_b_eq(i < nbits / 2, fb_get(fb, nbits, i),
+ "mismatch at position %zu", i);
+ }
+
+ static const size_t weirdnum = 7;
+ fb_set_range(fb, nbits, 0, nbits);
+ fb_unset_range(fb, nbits, weirdnum, FB_GROUP_BITS + weirdnum);
+ for (size_t i = 0; i < nbits; i++) {
+ expect_b_eq(7 <= i && i <= 2 * weirdnum + FB_GROUP_BITS - 1,
+ !fb_get(fb, nbits, i), "mismatch at position %zu", i);
+ }
+ free(fb);
+}
+TEST_END
+
+static void
+do_test_empty_full_exhaustive(size_t nbits) {
+ size_t sz = FB_NGROUPS(nbits) * sizeof(fb_group_t);
+ fb_group_t *empty = malloc(sz);
+ fb_init(empty, nbits);
+ fb_group_t *full = malloc(sz);
+ fb_init(full, nbits);
+ fb_set_range(full, nbits, 0, nbits);
+
+ expect_true(fb_full(full, nbits), "");
+ expect_false(fb_empty(full, nbits), "");
+ expect_false(fb_full(empty, nbits), "");
+ expect_true(fb_empty(empty, nbits), "");
+
+ for (size_t i = 0; i < nbits; i++) {
+ fb_set(empty, nbits, i);
+ fb_unset(full, nbits, i);
+
+ expect_false(fb_empty(empty, nbits), "error at bit %zu", i);
+ if (nbits != 1) {
+ expect_false(fb_full(empty, nbits),
+ "error at bit %zu", i);
+ expect_false(fb_empty(full, nbits),
+ "error at bit %zu", i);
+ } else {
+ expect_true(fb_full(empty, nbits),
+ "error at bit %zu", i);
+ expect_true(fb_empty(full, nbits),
+ "error at bit %zu", i);
+ }
+ expect_false(fb_full(full, nbits), "error at bit %zu", i);
+
+ fb_unset(empty, nbits, i);
+ fb_set(full, nbits, i);
+ }
+
+ free(empty);
+ free(full);
+}
+
+TEST_BEGIN(test_empty_full) {
+#define NB(nbits) \
+ do_test_empty_full_exhaustive(nbits);
+ NBITS_TAB
+#undef NB
+}
+TEST_END
+
+/*
+ * This tests both iter_range and the longest range functionality, which is
+ * built closely on top of it.
+ */
+TEST_BEGIN(test_iter_range_simple) {
+ size_t set_limit = 30;
+ size_t nbits = 100;
+ fb_group_t fb[FB_NGROUPS(100)];
+
+ fb_init(fb, nbits);
+
+ /*
+ * Failing to initialize these can lead to build failures with -Wall;
+ * the compiler can't prove that they're set.
+ */
+ size_t begin = (size_t)-1;
+ size_t len = (size_t)-1;
+ bool result;
+
+ /* A set of checks with only the first set_limit bits *set*. */
+ fb_set_range(fb, nbits, 0, set_limit);
+ expect_zu_eq(set_limit, fb_srange_longest(fb, nbits),
+ "Incorrect longest set range");
+ expect_zu_eq(nbits - set_limit, fb_urange_longest(fb, nbits),
+ "Incorrect longest unset range");
+ for (size_t i = 0; i < set_limit; i++) {
+ result = fb_srange_iter(fb, nbits, i, &begin, &len);
+ expect_true(result, "Should have found a range at %zu", i);
+ expect_zu_eq(i, begin, "Incorrect begin at %zu", i);
+ expect_zu_eq(set_limit - i, len, "Incorrect len at %zu", i);
+
+ result = fb_urange_iter(fb, nbits, i, &begin, &len);
+ expect_true(result, "Should have found a range at %zu", i);
+ expect_zu_eq(set_limit, begin, "Incorrect begin at %zu", i);
+ expect_zu_eq(nbits - set_limit, len, "Incorrect len at %zu", i);
+
+ result = fb_srange_riter(fb, nbits, i, &begin, &len);
+ expect_true(result, "Should have found a range at %zu", i);
+ expect_zu_eq(0, begin, "Incorrect begin at %zu", i);
+ expect_zu_eq(i + 1, len, "Incorrect len at %zu", i);
+
+ result = fb_urange_riter(fb, nbits, i, &begin, &len);
+ expect_false(result, "Should not have found a range at %zu", i);
+ }
+ for (size_t i = set_limit; i < nbits; i++) {
+ result = fb_srange_iter(fb, nbits, i, &begin, &len);
+ expect_false(result, "Should not have found a range at %zu", i);
+
+ result = fb_urange_iter(fb, nbits, i, &begin, &len);
+ expect_true(result, "Should have found a range at %zu", i);
+ expect_zu_eq(i, begin, "Incorrect begin at %zu", i);
+ expect_zu_eq(nbits - i, len, "Incorrect len at %zu", i);
+
+ result = fb_srange_riter(fb, nbits, i, &begin, &len);
+ expect_true(result, "Should have found a range at %zu", i);
+ expect_zu_eq(0, begin, "Incorrect begin at %zu", i);
+ expect_zu_eq(set_limit, len, "Incorrect len at %zu", i);
+
+ result = fb_urange_riter(fb, nbits, i, &begin, &len);
+ expect_true(result, "Should have found a range at %zu", i);
+ expect_zu_eq(set_limit, begin, "Incorrect begin at %zu", i);
+ expect_zu_eq(i - set_limit + 1, len, "Incorrect len at %zu", i);
+ }
+
+ /* A set of checks with only the first set_limit bits *unset*. */
+ fb_unset_range(fb, nbits, 0, set_limit);
+ fb_set_range(fb, nbits, set_limit, nbits - set_limit);
+ expect_zu_eq(nbits - set_limit, fb_srange_longest(fb, nbits),
+ "Incorrect longest set range");
+ expect_zu_eq(set_limit, fb_urange_longest(fb, nbits),
+ "Incorrect longest unset range");
+ for (size_t i = 0; i < set_limit; i++) {
+ result = fb_srange_iter(fb, nbits, i, &begin, &len);
+ expect_true(result, "Should have found a range at %zu", i);
+ expect_zu_eq(set_limit, begin, "Incorrect begin at %zu", i);
+ expect_zu_eq(nbits - set_limit, len, "Incorrect len at %zu", i);
+
+ result = fb_urange_iter(fb, nbits, i, &begin, &len);
+ expect_true(result, "Should have found a range at %zu", i);
+ expect_zu_eq(i, begin, "Incorrect begin at %zu", i);
+ expect_zu_eq(set_limit - i, len, "Incorrect len at %zu", i);
+
+ result = fb_srange_riter(fb, nbits, i, &begin, &len);
+ expect_false(result, "Should not have found a range at %zu", i);
+
+ result = fb_urange_riter(fb, nbits, i, &begin, &len);
+ expect_true(result, "Should not have found a range at %zu", i);
+ expect_zu_eq(0, begin, "Incorrect begin at %zu", i);
+ expect_zu_eq(i + 1, len, "Incorrect len at %zu", i);
+ }
+ for (size_t i = set_limit; i < nbits; i++) {
+ result = fb_srange_iter(fb, nbits, i, &begin, &len);
+ expect_true(result, "Should have found a range at %zu", i);
+ expect_zu_eq(i, begin, "Incorrect begin at %zu", i);
+ expect_zu_eq(nbits - i, len, "Incorrect len at %zu", i);
+
+ result = fb_urange_iter(fb, nbits, i, &begin, &len);
+ expect_false(result, "Should not have found a range at %zu", i);
+
+ result = fb_srange_riter(fb, nbits, i, &begin, &len);
+ expect_true(result, "Should have found a range at %zu", i);
+ expect_zu_eq(set_limit, begin, "Incorrect begin at %zu", i);
+ expect_zu_eq(i - set_limit + 1, len, "Incorrect len at %zu", i);
+
+ result = fb_urange_riter(fb, nbits, i, &begin, &len);
+ expect_true(result, "Should have found a range at %zu", i);
+ expect_zu_eq(0, begin, "Incorrect begin at %zu", i);
+ expect_zu_eq(set_limit, len, "Incorrect len at %zu", i);
+ }
+
+}
+TEST_END
+
+/*
+ * Doing this bit-by-bit is too slow for a real implementation, but for testing
+ * code, it's easy to get right. In the exhaustive tests, we'll compare the
+ * (fast but tricky) real implementation against the (slow but simple) testing
+ * one.
+ */
+static bool
+fb_iter_simple(fb_group_t *fb, size_t nbits, size_t start, size_t *r_begin,
+ size_t *r_len, bool val, bool forward) {
+ ssize_t stride = (forward ? (ssize_t)1 : (ssize_t)-1);
+ ssize_t range_begin = (ssize_t)start;
+ for (; range_begin != (ssize_t)nbits && range_begin != -1;
+ range_begin += stride) {
+ if (fb_get(fb, nbits, range_begin) == val) {
+ ssize_t range_end = range_begin;
+ for (; range_end != (ssize_t)nbits && range_end != -1;
+ range_end += stride) {
+ if (fb_get(fb, nbits, range_end) != val) {
+ break;
+ }
+ }
+ if (forward) {
+ *r_begin = range_begin;
+ *r_len = range_end - range_begin;
+ } else {
+ *r_begin = range_end + 1;
+ *r_len = range_begin - range_end;
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+/* Similar, but for finding longest ranges. */
+static size_t
+fb_range_longest_simple(fb_group_t *fb, size_t nbits, bool val) {
+ size_t longest_so_far = 0;
+ for (size_t begin = 0; begin < nbits; begin++) {
+ if (fb_get(fb, nbits, begin) != val) {
+ continue;
+ }
+ size_t end = begin + 1;
+ for (; end < nbits; end++) {
+ if (fb_get(fb, nbits, end) != val) {
+ break;
+ }
+ }
+ if (end - begin > longest_so_far) {
+ longest_so_far = end - begin;
+ }
+ }
+ return longest_so_far;
+}
+
+static void
+expect_iter_results_at(fb_group_t *fb, size_t nbits, size_t pos,
+ bool val, bool forward) {
+ bool iter_res;
+ size_t iter_begin JEMALLOC_CC_SILENCE_INIT(0);
+ size_t iter_len JEMALLOC_CC_SILENCE_INIT(0);
+ if (val) {
+ if (forward) {
+ iter_res = fb_srange_iter(fb, nbits, pos,
+ &iter_begin, &iter_len);
+ } else {
+ iter_res = fb_srange_riter(fb, nbits, pos,
+ &iter_begin, &iter_len);
+ }
+ } else {
+ if (forward) {
+ iter_res = fb_urange_iter(fb, nbits, pos,
+ &iter_begin, &iter_len);
+ } else {
+ iter_res = fb_urange_riter(fb, nbits, pos,
+ &iter_begin, &iter_len);
+ }
+ }
+
+ bool simple_iter_res;
+ /*
+ * These are dead stores, but the compiler can't always figure that out
+ * statically, and warns on the uninitialized variable.
+ */
+ size_t simple_iter_begin = 0;
+ size_t simple_iter_len = 0;
+ simple_iter_res = fb_iter_simple(fb, nbits, pos, &simple_iter_begin,
+ &simple_iter_len, val, forward);
+
+ expect_b_eq(iter_res, simple_iter_res, "Result mismatch at %zu", pos);
+ if (iter_res && simple_iter_res) {
+ assert_zu_eq(iter_begin, simple_iter_begin,
+ "Begin mismatch at %zu", pos);
+ expect_zu_eq(iter_len, simple_iter_len,
+ "Length mismatch at %zu", pos);
+ }
+}
+
+static void
+expect_iter_results(fb_group_t *fb, size_t nbits) {
+ for (size_t i = 0; i < nbits; i++) {
+ expect_iter_results_at(fb, nbits, i, false, false);
+ expect_iter_results_at(fb, nbits, i, false, true);
+ expect_iter_results_at(fb, nbits, i, true, false);
+ expect_iter_results_at(fb, nbits, i, true, true);
+ }
+ expect_zu_eq(fb_range_longest_simple(fb, nbits, true),
+ fb_srange_longest(fb, nbits), "Longest range mismatch");
+ expect_zu_eq(fb_range_longest_simple(fb, nbits, false),
+ fb_urange_longest(fb, nbits), "Longest range mismatch");
+}
+
+static void
+set_pattern_3(fb_group_t *fb, size_t nbits, bool zero_val) {
+ for (size_t i = 0; i < nbits; i++) {
+ if ((i % 6 < 3 && zero_val) || (i % 6 >= 3 && !zero_val)) {
+ fb_set(fb, nbits, i);
+ } else {
+ fb_unset(fb, nbits, i);
+ }
+ }
+}
+
+static void
+do_test_iter_range_exhaustive(size_t nbits) {
+ /* This test is also pretty slow. */
+ if (nbits > 1000) {
+ return;
+ }
+ size_t sz = FB_NGROUPS(nbits) * sizeof(fb_group_t);
+ fb_group_t *fb = malloc(sz);
+ fb_init(fb, nbits);
+
+ set_pattern_3(fb, nbits, /* zero_val */ true);
+ expect_iter_results(fb, nbits);
+
+ set_pattern_3(fb, nbits, /* zero_val */ false);
+ expect_iter_results(fb, nbits);
+
+ fb_set_range(fb, nbits, 0, nbits);
+ fb_unset_range(fb, nbits, 0, nbits / 2 == 0 ? 1 : nbits / 2);
+ expect_iter_results(fb, nbits);
+
+ fb_unset_range(fb, nbits, 0, nbits);
+ fb_set_range(fb, nbits, 0, nbits / 2 == 0 ? 1: nbits / 2);
+ expect_iter_results(fb, nbits);
+
+ free(fb);
+}
+
+/*
+ * Like test_iter_range_simple, this tests both iteration and longest-range
+ * computation.
+ */
+TEST_BEGIN(test_iter_range_exhaustive) {
+#define NB(nbits) \
+ do_test_iter_range_exhaustive(nbits);
+ NBITS_TAB
+#undef NB
+}
+TEST_END
+
+/*
+ * If all set bits in the bitmap are contiguous, in [set_start, set_end),
+ * returns the number of set bits in [scount_start, scount_end).
+ */
+static size_t
+scount_contiguous(size_t set_start, size_t set_end, size_t scount_start,
+ size_t scount_end) {
+ /* No overlap. */
+ if (set_end <= scount_start || scount_end <= set_start) {
+ return 0;
+ }
+ /* set range contains scount range */
+ if (set_start <= scount_start && set_end >= scount_end) {
+ return scount_end - scount_start;
+ }
+ /* scount range contains set range. */
+ if (scount_start <= set_start && scount_end >= set_end) {
+ return set_end - set_start;
+ }
+ /* Partial overlap, with set range starting first. */
+ if (set_start < scount_start && set_end < scount_end) {
+ return set_end - scount_start;
+ }
+ /* Partial overlap, with scount range starting first. */
+ if (scount_start < set_start && scount_end < set_end) {
+ return scount_end - set_start;
+ }
+ /*
+ * Trigger an assert failure; the above list should have been
+ * exhaustive.
+ */
+ unreachable();
+}
+
+static size_t
+ucount_contiguous(size_t set_start, size_t set_end, size_t ucount_start,
+ size_t ucount_end) {
+ /* No overlap. */
+ if (set_end <= ucount_start || ucount_end <= set_start) {
+ return ucount_end - ucount_start;
+ }
+ /* set range contains ucount range */
+ if (set_start <= ucount_start && set_end >= ucount_end) {
+ return 0;
+ }
+ /* ucount range contains set range. */
+ if (ucount_start <= set_start && ucount_end >= set_end) {
+ return (ucount_end - ucount_start) - (set_end - set_start);
+ }
+ /* Partial overlap, with set range starting first. */
+ if (set_start < ucount_start && set_end < ucount_end) {
+ return ucount_end - set_end;
+ }
+ /* Partial overlap, with ucount range starting first. */
+ if (ucount_start < set_start && ucount_end < set_end) {
+ return set_start - ucount_start;
+ }
+ /*
+ * Trigger an assert failure; the above list should have been
+ * exhaustive.
+ */
+ unreachable();
+}
+
+static void
+expect_count_match_contiguous(fb_group_t *fb, size_t nbits, size_t set_start,
+ size_t set_end) {
+ for (size_t i = 0; i < nbits; i++) {
+ for (size_t j = i + 1; j <= nbits; j++) {
+ size_t cnt = j - i;
+ size_t scount_expected = scount_contiguous(set_start,
+ set_end, i, j);
+ size_t scount_computed = fb_scount(fb, nbits, i, cnt);
+ expect_zu_eq(scount_expected, scount_computed,
+ "fb_scount error with nbits=%zu, start=%zu, "
+ "cnt=%zu, with bits set in [%zu, %zu)",
+ nbits, i, cnt, set_start, set_end);
+
+ size_t ucount_expected = ucount_contiguous(set_start,
+ set_end, i, j);
+ size_t ucount_computed = fb_ucount(fb, nbits, i, cnt);
+ assert_zu_eq(ucount_expected, ucount_computed,
+ "fb_ucount error with nbits=%zu, start=%zu, "
+ "cnt=%zu, with bits set in [%zu, %zu)",
+ nbits, i, cnt, set_start, set_end);
+
+ }
+ }
+}
+
+static void
+do_test_count_contiguous(size_t nbits) {
+ size_t sz = FB_NGROUPS(nbits) * sizeof(fb_group_t);
+ fb_group_t *fb = malloc(sz);
+
+ fb_init(fb, nbits);
+
+ expect_count_match_contiguous(fb, nbits, 0, 0);
+ for (size_t i = 0; i < nbits; i++) {
+ fb_set(fb, nbits, i);
+ expect_count_match_contiguous(fb, nbits, 0, i + 1);
+ }
+
+ for (size_t i = 0; i < nbits; i++) {
+ fb_unset(fb, nbits, i);
+ expect_count_match_contiguous(fb, nbits, i + 1, nbits);
+ }
+
+ free(fb);
+}
+
+TEST_BEGIN(test_count_contiguous_simple) {
+ enum {nbits = 300};
+ fb_group_t fb[FB_NGROUPS(nbits)];
+ fb_init(fb, nbits);
+ /* Just an arbitrary number. */
+ size_t start = 23;
+
+ fb_set_range(fb, nbits, start, 30 - start);
+ expect_count_match_contiguous(fb, nbits, start, 30);
+
+ fb_set_range(fb, nbits, start, 40 - start);
+ expect_count_match_contiguous(fb, nbits, start, 40);
+
+ fb_set_range(fb, nbits, start, 70 - start);
+ expect_count_match_contiguous(fb, nbits, start, 70);
+
+ fb_set_range(fb, nbits, start, 120 - start);
+ expect_count_match_contiguous(fb, nbits, start, 120);
+
+ fb_set_range(fb, nbits, start, 150 - start);
+ expect_count_match_contiguous(fb, nbits, start, 150);
+
+ fb_set_range(fb, nbits, start, 200 - start);
+ expect_count_match_contiguous(fb, nbits, start, 200);
+
+ fb_set_range(fb, nbits, start, 290 - start);
+ expect_count_match_contiguous(fb, nbits, start, 290);
+}
+TEST_END
+
+TEST_BEGIN(test_count_contiguous) {
+#define NB(nbits) \
+ /* This test is *particularly* slow in debug builds. */ \
+ if ((!config_debug && nbits < 300) || nbits < 150) { \
+ do_test_count_contiguous(nbits); \
+ }
+ NBITS_TAB
+#undef NB
+}
+TEST_END
+
+static void
+expect_count_match_alternating(fb_group_t *fb_even, fb_group_t *fb_odd,
+ size_t nbits) {
+ for (size_t i = 0; i < nbits; i++) {
+ for (size_t j = i + 1; j <= nbits; j++) {
+ size_t cnt = j - i;
+ size_t odd_scount = cnt / 2
+ + (size_t)(cnt % 2 == 1 && i % 2 == 1);
+ size_t odd_scount_computed = fb_scount(fb_odd, nbits,
+ i, j - i);
+ assert_zu_eq(odd_scount, odd_scount_computed,
+ "fb_scount error with nbits=%zu, start=%zu, "
+ "cnt=%zu, with alternating bits set.",
+ nbits, i, j - i);
+
+ size_t odd_ucount = cnt / 2
+ + (size_t)(cnt % 2 == 1 && i % 2 == 0);
+ size_t odd_ucount_computed = fb_ucount(fb_odd, nbits,
+ i, j - i);
+ assert_zu_eq(odd_ucount, odd_ucount_computed,
+ "fb_ucount error with nbits=%zu, start=%zu, "
+ "cnt=%zu, with alternating bits set.",
+ nbits, i, j - i);
+
+ size_t even_scount = cnt / 2
+ + (size_t)(cnt % 2 == 1 && i % 2 == 0);
+ size_t even_scount_computed = fb_scount(fb_even, nbits,
+ i, j - i);
+ assert_zu_eq(even_scount, even_scount_computed,
+ "fb_scount error with nbits=%zu, start=%zu, "
+ "cnt=%zu, with alternating bits set.",
+ nbits, i, j - i);
+
+ size_t even_ucount = cnt / 2
+ + (size_t)(cnt % 2 == 1 && i % 2 == 1);
+ size_t even_ucount_computed = fb_ucount(fb_even, nbits,
+ i, j - i);
+ assert_zu_eq(even_ucount, even_ucount_computed,
+ "fb_ucount error with nbits=%zu, start=%zu, "
+ "cnt=%zu, with alternating bits set.",
+ nbits, i, j - i);
+ }
+ }
+}
+
+static void
+do_test_count_alternating(size_t nbits) {
+ if (nbits > 1000) {
+ return;
+ }
+ size_t sz = FB_NGROUPS(nbits) * sizeof(fb_group_t);
+ fb_group_t *fb_even = malloc(sz);
+ fb_group_t *fb_odd = malloc(sz);
+
+ fb_init(fb_even, nbits);
+ fb_init(fb_odd, nbits);
+
+ for (size_t i = 0; i < nbits; i++) {
+ if (i % 2 == 0) {
+ fb_set(fb_even, nbits, i);
+ } else {
+ fb_set(fb_odd, nbits, i);
+ }
+ }
+
+ expect_count_match_alternating(fb_even, fb_odd, nbits);
+
+ free(fb_even);
+ free(fb_odd);
+}
+
+TEST_BEGIN(test_count_alternating) {
+#define NB(nbits) \
+ do_test_count_alternating(nbits);
+ NBITS_TAB
+#undef NB
+}
+TEST_END
+
+static void
+do_test_bit_op(size_t nbits, bool (*op)(bool a, bool b),
+ void (*fb_op)(fb_group_t *dst, fb_group_t *src1, fb_group_t *src2, size_t nbits)) {
+ size_t sz = FB_NGROUPS(nbits) * sizeof(fb_group_t);
+ fb_group_t *fb1 = malloc(sz);
+ fb_group_t *fb2 = malloc(sz);
+ fb_group_t *fb_result = malloc(sz);
+ fb_init(fb1, nbits);
+ fb_init(fb2, nbits);
+ fb_init(fb_result, nbits);
+
+ /* Just two random numbers. */
+ const uint64_t prng_init1 = (uint64_t)0X4E9A9DE6A35691CDULL;
+ const uint64_t prng_init2 = (uint64_t)0X7856E396B063C36EULL;
+
+ uint64_t prng1 = prng_init1;
+ uint64_t prng2 = prng_init2;
+
+ for (size_t i = 0; i < nbits; i++) {
+ bool bit1 = ((prng1 & (1ULL << (i % 64))) != 0);
+ bool bit2 = ((prng2 & (1ULL << (i % 64))) != 0);
+
+ if (bit1) {
+ fb_set(fb1, nbits, i);
+ }
+ if (bit2) {
+ fb_set(fb2, nbits, i);
+ }
+
+ if (i % 64 == 0) {
+ prng1 = prng_state_next_u64(prng1);
+ prng2 = prng_state_next_u64(prng2);
+ }
+ }
+
+ fb_op(fb_result, fb1, fb2, nbits);
+
+ /* Reset the prngs to replay them. */
+ prng1 = prng_init1;
+ prng2 = prng_init2;
+
+ for (size_t i = 0; i < nbits; i++) {
+ bool bit1 = ((prng1 & (1ULL << (i % 64))) != 0);
+ bool bit2 = ((prng2 & (1ULL << (i % 64))) != 0);
+
+ /* Original bitmaps shouldn't change. */
+ expect_b_eq(bit1, fb_get(fb1, nbits, i), "difference at bit %zu", i);
+ expect_b_eq(bit2, fb_get(fb2, nbits, i), "difference at bit %zu", i);
+
+ /* New one should be bitwise and. */
+ expect_b_eq(op(bit1, bit2), fb_get(fb_result, nbits, i),
+ "difference at bit %zu", i);
+
+ /* Update the same way we did last time. */
+ if (i % 64 == 0) {
+ prng1 = prng_state_next_u64(prng1);
+ prng2 = prng_state_next_u64(prng2);
+ }
+ }
+
+ free(fb1);
+ free(fb2);
+ free(fb_result);
+}
+
+static bool
+binary_and(bool a, bool b) {
+ return a & b;
+}
+
+static void
+do_test_bit_and(size_t nbits) {
+ do_test_bit_op(nbits, &binary_and, &fb_bit_and);
+}
+
+TEST_BEGIN(test_bit_and) {
+#define NB(nbits) \
+ do_test_bit_and(nbits);
+ NBITS_TAB
+#undef NB
+}
+TEST_END
+
+static bool
+binary_or(bool a, bool b) {
+ return a | b;
+}
+
+static void
+do_test_bit_or(size_t nbits) {
+ do_test_bit_op(nbits, &binary_or, &fb_bit_or);
+}
+
+TEST_BEGIN(test_bit_or) {
+#define NB(nbits) \
+ do_test_bit_or(nbits);
+ NBITS_TAB
+#undef NB
+}
+TEST_END
+
+static bool
+binary_not(bool a, bool b) {
+ (void)b;
+ return !a;
+}
+
+static void
+fb_bit_not_shim(fb_group_t *dst, fb_group_t *src1, fb_group_t *src2,
+ size_t nbits) {
+ (void)src2;
+ fb_bit_not(dst, src1, nbits);
+}
+
+static void
+do_test_bit_not(size_t nbits) {
+ do_test_bit_op(nbits, &binary_not, &fb_bit_not_shim);
+}
+
+TEST_BEGIN(test_bit_not) {
+#define NB(nbits) \
+ do_test_bit_not(nbits);
+ NBITS_TAB
+#undef NB
+}
+TEST_END
+
+int
+main(void) {
+ return test_no_reentrancy(
+ test_fb_init,
+ test_get_set_unset,
+ test_search_simple,
+ test_search_exhaustive,
+ test_range_simple,
+ test_empty_full,
+ test_iter_range_simple,
+ test_iter_range_exhaustive,
+ test_count_contiguous_simple,
+ test_count_contiguous,
+ test_count_alternating,
+ test_bit_and,
+ test_bit_or,
+ test_bit_not);
+}
diff --git a/deps/jemalloc/test/unit/fork.c b/deps/jemalloc/test/unit/fork.c
index b1690750a..4137423f0 100644
--- a/deps/jemalloc/test/unit/fork.c
+++ b/deps/jemalloc/test/unit/fork.c
@@ -36,25 +36,25 @@ TEST_BEGIN(test_fork) {
/* Set up a manually managed arena for test. */
unsigned arena_ind;
size_t sz = sizeof(unsigned);
- assert_d_eq(mallctl("arenas.create", (void *)&arena_ind, &sz, NULL, 0),
+ expect_d_eq(mallctl("arenas.create", (void *)&arena_ind, &sz, NULL, 0),
0, "Unexpected mallctl() failure");
/* Migrate to the new arena. */
unsigned old_arena_ind;
sz = sizeof(old_arena_ind);
- assert_d_eq(mallctl("thread.arena", (void *)&old_arena_ind, &sz,
+ expect_d_eq(mallctl("thread.arena", (void *)&old_arena_ind, &sz,
(void *)&arena_ind, sizeof(arena_ind)), 0,
"Unexpected mallctl() failure");
p = malloc(1);
- assert_ptr_not_null(p, "Unexpected malloc() failure");
+ expect_ptr_not_null(p, "Unexpected malloc() failure");
pid = fork();
free(p);
p = malloc(64);
- assert_ptr_not_null(p, "Unexpected malloc() failure");
+ expect_ptr_not_null(p, "Unexpected malloc() failure");
free(p);
if (pid == -1) {
diff --git a/deps/jemalloc/test/unit/fxp.c b/deps/jemalloc/test/unit/fxp.c
new file mode 100644
index 000000000..27f109768
--- /dev/null
+++ b/deps/jemalloc/test/unit/fxp.c
@@ -0,0 +1,394 @@
+#include "test/jemalloc_test.h"
+
+#include "jemalloc/internal/fxp.h"
+
+static double
+fxp2double(fxp_t a) {
+ double intpart = (double)(a >> 16);
+ double fracpart = (double)(a & ((1U << 16) - 1)) / (1U << 16);
+ return intpart + fracpart;
+}
+
+/* Is a close to b? */
+static bool
+double_close(double a, double b) {
+ /*
+ * Our implementation doesn't try for precision. Correspondingly, don't
+ * enforce it too strenuously here; accept values that are close in
+ * either relative or absolute terms.
+ */
+ return fabs(a - b) < 0.01 || fabs(a - b) / a < 0.01;
+}
+
+static bool
+fxp_close(fxp_t a, fxp_t b) {
+ return double_close(fxp2double(a), fxp2double(b));
+}
+
+static fxp_t
+xparse_fxp(const char *str) {
+ fxp_t result;
+ bool err = fxp_parse(&result, str, NULL);
+ assert_false(err, "Invalid fxp string: %s", str);
+ return result;
+}
+
+static void
+expect_parse_accurate(const char *str, const char *parse_str) {
+ double true_val = strtod(str, NULL);
+ fxp_t fxp_val;
+ char *end;
+ bool err = fxp_parse(&fxp_val, parse_str, &end);
+ expect_false(err, "Unexpected parse failure");
+ expect_ptr_eq(parse_str + strlen(str), end,
+ "Didn't parse whole string");
+ expect_true(double_close(fxp2double(fxp_val), true_val),
+ "Misparsed %s", str);
+}
+
+static void
+parse_valid_trial(const char *str) {
+ /* The value it parses should be correct. */
+ expect_parse_accurate(str, str);
+ char buf[100];
+ snprintf(buf, sizeof(buf), "%swith_some_trailing_text", str);
+ expect_parse_accurate(str, buf);
+ snprintf(buf, sizeof(buf), "%s with a space", str);
+ expect_parse_accurate(str, buf);
+ snprintf(buf, sizeof(buf), "%s,in_a_malloc_conf_string:1", str);
+ expect_parse_accurate(str, buf);
+}
+
+TEST_BEGIN(test_parse_valid) {
+ parse_valid_trial("0");
+ parse_valid_trial("1");
+ parse_valid_trial("2");
+ parse_valid_trial("100");
+ parse_valid_trial("345");
+ parse_valid_trial("00000000123");
+ parse_valid_trial("00000000987");
+
+ parse_valid_trial("0.0");
+ parse_valid_trial("0.00000000000456456456");
+ parse_valid_trial("100.00000000000456456456");
+
+ parse_valid_trial("123.1");
+ parse_valid_trial("123.01");
+ parse_valid_trial("123.001");
+ parse_valid_trial("123.0001");
+ parse_valid_trial("123.00001");
+ parse_valid_trial("123.000001");
+ parse_valid_trial("123.0000001");
+
+ parse_valid_trial(".0");
+ parse_valid_trial(".1");
+ parse_valid_trial(".01");
+ parse_valid_trial(".001");
+ parse_valid_trial(".0001");
+ parse_valid_trial(".00001");
+ parse_valid_trial(".000001");
+
+ parse_valid_trial(".1");
+ parse_valid_trial(".10");
+ parse_valid_trial(".100");
+ parse_valid_trial(".1000");
+ parse_valid_trial(".100000");
+}
+TEST_END
+
+static void
+expect_parse_failure(const char *str) {
+ fxp_t result = FXP_INIT_INT(333);
+ char *end = (void *)0x123;
+ bool err = fxp_parse(&result, str, &end);
+ expect_true(err, "Expected a parse error on: %s", str);
+ expect_ptr_eq((void *)0x123, end,
+ "Parse error shouldn't change results");
+ expect_u32_eq(result, FXP_INIT_INT(333),
+ "Parse error shouldn't change results");
+}
+
+TEST_BEGIN(test_parse_invalid) {
+ expect_parse_failure("123.");
+ expect_parse_failure("3.a");
+ expect_parse_failure(".a");
+ expect_parse_failure("a.1");
+ expect_parse_failure("a");
+ /* A valid string, but one that overflows. */
+ expect_parse_failure("123456789");
+ expect_parse_failure("0000000123456789");
+ expect_parse_failure("1000000");
+}
+TEST_END
+
+static void
+expect_init_percent(unsigned percent, const char *str) {
+ fxp_t result_init = FXP_INIT_PERCENT(percent);
+ fxp_t result_parse = xparse_fxp(str);
+ expect_u32_eq(result_init, result_parse,
+ "Expect representations of FXP_INIT_PERCENT(%u) and "
+ "fxp_parse(\"%s\") to be equal; got %x and %x",
+ percent, str, result_init, result_parse);
+
+}
+
+/*
+ * Every other test uses either parsing or FXP_INIT_INT; it gets tested in those
+ * ways. We need a one-off for the percent-based initialization, though.
+ */
+TEST_BEGIN(test_init_percent) {
+ expect_init_percent(100, "1");
+ expect_init_percent(75, ".75");
+ expect_init_percent(1, ".01");
+ expect_init_percent(50, ".5");
+}
+TEST_END
+
+static void
+expect_add(const char *astr, const char *bstr, const char* resultstr) {
+ fxp_t a = xparse_fxp(astr);
+ fxp_t b = xparse_fxp(bstr);
+ fxp_t result = xparse_fxp(resultstr);
+ expect_true(fxp_close(fxp_add(a, b), result),
+ "Expected %s + %s == %s", astr, bstr, resultstr);
+}
+
+TEST_BEGIN(test_add_simple) {
+ expect_add("0", "0", "0");
+ expect_add("0", "1", "1");
+ expect_add("1", "1", "2");
+ expect_add("1.5", "1.5", "3");
+ expect_add("0.1", "0.1", "0.2");
+ expect_add("123", "456", "579");
+}
+TEST_END
+
+static void
+expect_sub(const char *astr, const char *bstr, const char* resultstr) {
+ fxp_t a = xparse_fxp(astr);
+ fxp_t b = xparse_fxp(bstr);
+ fxp_t result = xparse_fxp(resultstr);
+ expect_true(fxp_close(fxp_sub(a, b), result),
+ "Expected %s - %s == %s", astr, bstr, resultstr);
+}
+
+TEST_BEGIN(test_sub_simple) {
+ expect_sub("0", "0", "0");
+ expect_sub("1", "0", "1");
+ expect_sub("1", "1", "0");
+ expect_sub("3.5", "1.5", "2");
+ expect_sub("0.3", "0.1", "0.2");
+ expect_sub("456", "123", "333");
+}
+TEST_END
+
+static void
+expect_mul(const char *astr, const char *bstr, const char* resultstr) {
+ fxp_t a = xparse_fxp(astr);
+ fxp_t b = xparse_fxp(bstr);
+ fxp_t result = xparse_fxp(resultstr);
+ expect_true(fxp_close(fxp_mul(a, b), result),
+ "Expected %s * %s == %s", astr, bstr, resultstr);
+}
+
+TEST_BEGIN(test_mul_simple) {
+ expect_mul("0", "0", "0");
+ expect_mul("1", "0", "0");
+ expect_mul("1", "1", "1");
+ expect_mul("1.5", "1.5", "2.25");
+ expect_mul("100.0", "10", "1000");
+ expect_mul(".1", "10", "1");
+}
+TEST_END
+
+static void
+expect_div(const char *astr, const char *bstr, const char* resultstr) {
+ fxp_t a = xparse_fxp(astr);
+ fxp_t b = xparse_fxp(bstr);
+ fxp_t result = xparse_fxp(resultstr);
+ expect_true(fxp_close(fxp_div(a, b), result),
+ "Expected %s / %s == %s", astr, bstr, resultstr);
+}
+
+TEST_BEGIN(test_div_simple) {
+ expect_div("1", "1", "1");
+ expect_div("0", "1", "0");
+ expect_div("2", "1", "2");
+ expect_div("3", "2", "1.5");
+ expect_div("3", "1.5", "2");
+ expect_div("10", ".1", "100");
+ expect_div("123", "456", ".2697368421");
+}
+TEST_END
+
+static void
+expect_round(const char *str, uint32_t rounded_down, uint32_t rounded_nearest) {
+ fxp_t fxp = xparse_fxp(str);
+ uint32_t fxp_rounded_down = fxp_round_down(fxp);
+ uint32_t fxp_rounded_nearest = fxp_round_nearest(fxp);
+ expect_u32_eq(rounded_down, fxp_rounded_down,
+ "Mistake rounding %s down", str);
+ expect_u32_eq(rounded_nearest, fxp_rounded_nearest,
+ "Mistake rounding %s to nearest", str);
+}
+
+TEST_BEGIN(test_round_simple) {
+ expect_round("1.5", 1, 2);
+ expect_round("0", 0, 0);
+ expect_round("0.1", 0, 0);
+ expect_round("0.4", 0, 0);
+ expect_round("0.40000", 0, 0);
+ expect_round("0.5", 0, 1);
+ expect_round("0.6", 0, 1);
+ expect_round("123", 123, 123);
+ expect_round("123.4", 123, 123);
+ expect_round("123.5", 123, 124);
+}
+TEST_END
+
+static void
+expect_mul_frac(size_t a, const char *fracstr, size_t expected) {
+ fxp_t frac = xparse_fxp(fracstr);
+ size_t result = fxp_mul_frac(a, frac);
+ expect_true(double_close(expected, result),
+ "Expected %zu * %s == %zu (fracmul); got %zu", a, fracstr,
+ expected, result);
+}
+
+TEST_BEGIN(test_mul_frac_simple) {
+ expect_mul_frac(SIZE_MAX, "1.0", SIZE_MAX);
+ expect_mul_frac(SIZE_MAX, ".75", SIZE_MAX / 4 * 3);
+ expect_mul_frac(SIZE_MAX, ".5", SIZE_MAX / 2);
+ expect_mul_frac(SIZE_MAX, ".25", SIZE_MAX / 4);
+ expect_mul_frac(1U << 16, "1.0", 1U << 16);
+ expect_mul_frac(1U << 30, "0.5", 1U << 29);
+ expect_mul_frac(1U << 30, "0.25", 1U << 28);
+ expect_mul_frac(1U << 30, "0.125", 1U << 27);
+ expect_mul_frac((1U << 30) + 1, "0.125", 1U << 27);
+ expect_mul_frac(100, "0.25", 25);
+ expect_mul_frac(1000 * 1000, "0.001", 1000);
+}
+TEST_END
+
+static void
+expect_print(const char *str) {
+ fxp_t fxp = xparse_fxp(str);
+ char buf[FXP_BUF_SIZE];
+ fxp_print(fxp, buf);
+ expect_d_eq(0, strcmp(str, buf), "Couldn't round-trip print %s", str);
+}
+
+TEST_BEGIN(test_print_simple) {
+ expect_print("0.0");
+ expect_print("1.0");
+ expect_print("2.0");
+ expect_print("123.0");
+ /*
+ * We hit the possibility of roundoff errors whenever the fractional
+ * component isn't a round binary number; only check these here (we
+ * round-trip properly in the stress test).
+ */
+ expect_print("1.5");
+ expect_print("3.375");
+ expect_print("0.25");
+ expect_print("0.125");
+ /* 1 / 2**14 */
+ expect_print("0.00006103515625");
+}
+TEST_END
+
+TEST_BEGIN(test_stress) {
+ const char *numbers[] = {
+ "0.0", "0.1", "0.2", "0.3", "0.4",
+ "0.5", "0.6", "0.7", "0.8", "0.9",
+
+ "1.0", "1.1", "1.2", "1.3", "1.4",
+ "1.5", "1.6", "1.7", "1.8", "1.9",
+
+ "2.0", "2.1", "2.2", "2.3", "2.4",
+ "2.5", "2.6", "2.7", "2.8", "2.9",
+
+ "17.0", "17.1", "17.2", "17.3", "17.4",
+ "17.5", "17.6", "17.7", "17.8", "17.9",
+
+ "18.0", "18.1", "18.2", "18.3", "18.4",
+ "18.5", "18.6", "18.7", "18.8", "18.9",
+
+ "123.0", "123.1", "123.2", "123.3", "123.4",
+ "123.5", "123.6", "123.7", "123.8", "123.9",
+
+ "124.0", "124.1", "124.2", "124.3", "124.4",
+ "124.5", "124.6", "124.7", "124.8", "124.9",
+
+ "125.0", "125.1", "125.2", "125.3", "125.4",
+ "125.5", "125.6", "125.7", "125.8", "125.9"};
+ size_t numbers_len = sizeof(numbers)/sizeof(numbers[0]);
+ for (size_t i = 0; i < numbers_len; i++) {
+ fxp_t fxp_a = xparse_fxp(numbers[i]);
+ double double_a = strtod(numbers[i], NULL);
+
+ uint32_t fxp_rounded_down = fxp_round_down(fxp_a);
+ uint32_t fxp_rounded_nearest = fxp_round_nearest(fxp_a);
+ uint32_t double_rounded_down = (uint32_t)double_a;
+ uint32_t double_rounded_nearest = (uint32_t)round(double_a);
+
+ expect_u32_eq(double_rounded_down, fxp_rounded_down,
+ "Incorrectly rounded down %s", numbers[i]);
+ expect_u32_eq(double_rounded_nearest, fxp_rounded_nearest,
+ "Incorrectly rounded-to-nearest %s", numbers[i]);
+
+ for (size_t j = 0; j < numbers_len; j++) {
+ fxp_t fxp_b = xparse_fxp(numbers[j]);
+ double double_b = strtod(numbers[j], NULL);
+
+ fxp_t fxp_sum = fxp_add(fxp_a, fxp_b);
+ double double_sum = double_a + double_b;
+ expect_true(
+ double_close(fxp2double(fxp_sum), double_sum),
+ "Miscomputed %s + %s", numbers[i], numbers[j]);
+
+ if (double_a > double_b) {
+ fxp_t fxp_diff = fxp_sub(fxp_a, fxp_b);
+ double double_diff = double_a - double_b;
+ expect_true(
+ double_close(fxp2double(fxp_diff),
+ double_diff),
+ "Miscomputed %s - %s", numbers[i],
+ numbers[j]);
+ }
+
+ fxp_t fxp_prod = fxp_mul(fxp_a, fxp_b);
+ double double_prod = double_a * double_b;
+ expect_true(
+ double_close(fxp2double(fxp_prod), double_prod),
+ "Miscomputed %s * %s", numbers[i], numbers[j]);
+
+ if (double_b != 0.0) {
+ fxp_t fxp_quot = fxp_div(fxp_a, fxp_b);
+ double double_quot = double_a / double_b;
+ expect_true(
+ double_close(fxp2double(fxp_quot),
+ double_quot),
+ "Miscomputed %s / %s", numbers[i],
+ numbers[j]);
+ }
+ }
+ }
+}
+TEST_END
+
+int
+main(void) {
+ return test_no_reentrancy(
+ test_parse_valid,
+ test_parse_invalid,
+ test_init_percent,
+ test_add_simple,
+ test_sub_simple,
+ test_mul_simple,
+ test_div_simple,
+ test_round_simple,
+ test_mul_frac_simple,
+ test_print_simple,
+ test_stress);
+}
diff --git a/deps/jemalloc/test/unit/hash.c b/deps/jemalloc/test/unit/hash.c
index 7cc034f8d..49f08238d 100644
--- a/deps/jemalloc/test/unit/hash.c
+++ b/deps/jemalloc/test/unit/hash.c
@@ -131,7 +131,7 @@ hash_variant_verify_key(hash_variant_t variant, uint8_t *key) {
default: not_reached();
}
- assert_u32_eq(computed, expected,
+ expect_u32_eq(computed, expected,
"Hash mismatch for %s(): expected %#x but got %#x",
hash_variant_string(variant), expected, computed);
}
diff --git a/deps/jemalloc/test/unit/hook.c b/deps/jemalloc/test/unit/hook.c
index 72fcc433c..16a6f1b03 100644
--- a/deps/jemalloc/test/unit/hook.c
+++ b/deps/jemalloc/test/unit/hook.c
@@ -70,10 +70,10 @@ set_args_raw(uintptr_t *args_raw, int nargs) {
}
static void
-assert_args_raw(uintptr_t *args_raw_expected, int nargs) {
+expect_args_raw(uintptr_t *args_raw_expected, int nargs) {
int cmp = memcmp(args_raw_expected, arg_args_raw,
sizeof(uintptr_t) * nargs);
- assert_d_eq(cmp, 0, "Raw args mismatch");
+ expect_d_eq(cmp, 0, "Raw args mismatch");
}
static void
@@ -132,34 +132,34 @@ TEST_BEGIN(test_hooks_basic) {
reset_args();
hook_invoke_alloc(hook_alloc_posix_memalign, (void *)222, 333,
args_raw);
- assert_ptr_eq(arg_extra, (void *)111, "Passed wrong user pointer");
- assert_d_eq((int)hook_alloc_posix_memalign, arg_type,
+ expect_ptr_eq(arg_extra, (void *)111, "Passed wrong user pointer");
+ expect_d_eq((int)hook_alloc_posix_memalign, arg_type,
"Passed wrong alloc type");
- assert_ptr_eq((void *)222, arg_result, "Passed wrong result address");
- assert_u64_eq(333, arg_result_raw, "Passed wrong result");
- assert_args_raw(args_raw, 3);
+ expect_ptr_eq((void *)222, arg_result, "Passed wrong result address");
+ expect_u64_eq(333, arg_result_raw, "Passed wrong result");
+ expect_args_raw(args_raw, 3);
/* Dalloc */
reset_args();
hook_invoke_dalloc(hook_dalloc_sdallocx, (void *)222, args_raw);
- assert_d_eq((int)hook_dalloc_sdallocx, arg_type,
+ expect_d_eq((int)hook_dalloc_sdallocx, arg_type,
"Passed wrong dalloc type");
- assert_ptr_eq((void *)111, arg_extra, "Passed wrong user pointer");
- assert_ptr_eq((void *)222, arg_address, "Passed wrong address");
- assert_args_raw(args_raw, 3);
+ expect_ptr_eq((void *)111, arg_extra, "Passed wrong user pointer");
+ expect_ptr_eq((void *)222, arg_address, "Passed wrong address");
+ expect_args_raw(args_raw, 3);
/* Expand */
reset_args();
hook_invoke_expand(hook_expand_xallocx, (void *)222, 333, 444, 555,
args_raw);
- assert_d_eq((int)hook_expand_xallocx, arg_type,
+ expect_d_eq((int)hook_expand_xallocx, arg_type,
"Passed wrong expand type");
- assert_ptr_eq((void *)111, arg_extra, "Passed wrong user pointer");
- assert_ptr_eq((void *)222, arg_address, "Passed wrong address");
- assert_zu_eq(333, arg_old_usize, "Passed wrong old usize");
- assert_zu_eq(444, arg_new_usize, "Passed wrong new usize");
- assert_zu_eq(555, arg_result_raw, "Passed wrong result");
- assert_args_raw(args_raw, 4);
+ expect_ptr_eq((void *)111, arg_extra, "Passed wrong user pointer");
+ expect_ptr_eq((void *)222, arg_address, "Passed wrong address");
+ expect_zu_eq(333, arg_old_usize, "Passed wrong old usize");
+ expect_zu_eq(444, arg_new_usize, "Passed wrong new usize");
+ expect_zu_eq(555, arg_result_raw, "Passed wrong result");
+ expect_args_raw(args_raw, 4);
hook_remove(TSDN_NULL, handle);
}
@@ -177,24 +177,24 @@ TEST_BEGIN(test_hooks_null) {
void *handle3 = hook_install(TSDN_NULL, &hooks3);
void *handle4 = hook_install(TSDN_NULL, &hooks4);
- assert_ptr_ne(handle1, NULL, "Hook installation failed");
- assert_ptr_ne(handle2, NULL, "Hook installation failed");
- assert_ptr_ne(handle3, NULL, "Hook installation failed");
- assert_ptr_ne(handle4, NULL, "Hook installation failed");
+ expect_ptr_ne(handle1, NULL, "Hook installation failed");
+ expect_ptr_ne(handle2, NULL, "Hook installation failed");
+ expect_ptr_ne(handle3, NULL, "Hook installation failed");
+ expect_ptr_ne(handle4, NULL, "Hook installation failed");
uintptr_t args_raw[4] = {10, 20, 30, 40};
call_count = 0;
hook_invoke_alloc(hook_alloc_malloc, NULL, 0, args_raw);
- assert_d_eq(call_count, 1, "Called wrong number of times");
+ expect_d_eq(call_count, 1, "Called wrong number of times");
call_count = 0;
hook_invoke_dalloc(hook_dalloc_free, NULL, args_raw);
- assert_d_eq(call_count, 1, "Called wrong number of times");
+ expect_d_eq(call_count, 1, "Called wrong number of times");
call_count = 0;
hook_invoke_expand(hook_expand_realloc, NULL, 0, 0, 0, args_raw);
- assert_d_eq(call_count, 1, "Called wrong number of times");
+ expect_d_eq(call_count, 1, "Called wrong number of times");
hook_remove(TSDN_NULL, handle1);
hook_remove(TSDN_NULL, handle2);
@@ -206,16 +206,16 @@ TEST_END
TEST_BEGIN(test_hooks_remove) {
hooks_t hooks = {&test_alloc_hook, NULL, NULL, NULL};
void *handle = hook_install(TSDN_NULL, &hooks);
- assert_ptr_ne(handle, NULL, "Hook installation failed");
+ expect_ptr_ne(handle, NULL, "Hook installation failed");
call_count = 0;
uintptr_t args_raw[4] = {10, 20, 30, 40};
hook_invoke_alloc(hook_alloc_malloc, NULL, 0, args_raw);
- assert_d_eq(call_count, 1, "Hook not invoked");
+ expect_d_eq(call_count, 1, "Hook not invoked");
call_count = 0;
hook_remove(TSDN_NULL, handle);
hook_invoke_alloc(hook_alloc_malloc, NULL, 0, NULL);
- assert_d_eq(call_count, 0, "Hook invoked after removal");
+ expect_d_eq(call_count, 0, "Hook invoked after removal");
}
TEST_END
@@ -224,7 +224,7 @@ TEST_BEGIN(test_hooks_alloc_simple) {
/* "Simple" in the sense that we're not in a realloc variant. */
hooks_t hooks = {&test_alloc_hook, NULL, NULL, (void *)123};
void *handle = hook_install(TSDN_NULL, &hooks);
- assert_ptr_ne(handle, NULL, "Hook installation failed");
+ expect_ptr_ne(handle, NULL, "Hook installation failed");
/* Stop malloc from being optimized away. */
volatile int err;
@@ -233,69 +233,69 @@ TEST_BEGIN(test_hooks_alloc_simple) {
/* malloc */
reset();
ptr = malloc(1);
- assert_d_eq(call_count, 1, "Hook not called");
- assert_ptr_eq(arg_extra, (void *)123, "Wrong extra");
- assert_d_eq(arg_type, (int)hook_alloc_malloc, "Wrong hook type");
- assert_ptr_eq(ptr, arg_result, "Wrong result");
- assert_u64_eq((uintptr_t)ptr, (uintptr_t)arg_result_raw,
+ expect_d_eq(call_count, 1, "Hook not called");
+ expect_ptr_eq(arg_extra, (void *)123, "Wrong extra");
+ expect_d_eq(arg_type, (int)hook_alloc_malloc, "Wrong hook type");
+ expect_ptr_eq(ptr, arg_result, "Wrong result");
+ expect_u64_eq((uintptr_t)ptr, (uintptr_t)arg_result_raw,
"Wrong raw result");
- assert_u64_eq((uintptr_t)1, arg_args_raw[0], "Wrong argument");
+ expect_u64_eq((uintptr_t)1, arg_args_raw[0], "Wrong argument");
free(ptr);
/* posix_memalign */
reset();
err = posix_memalign((void **)&ptr, 1024, 1);
- assert_d_eq(call_count, 1, "Hook not called");
- assert_ptr_eq(arg_extra, (void *)123, "Wrong extra");
- assert_d_eq(arg_type, (int)hook_alloc_posix_memalign,
+ expect_d_eq(call_count, 1, "Hook not called");
+ expect_ptr_eq(arg_extra, (void *)123, "Wrong extra");
+ expect_d_eq(arg_type, (int)hook_alloc_posix_memalign,
"Wrong hook type");
- assert_ptr_eq(ptr, arg_result, "Wrong result");
- assert_u64_eq((uintptr_t)err, (uintptr_t)arg_result_raw,
+ expect_ptr_eq(ptr, arg_result, "Wrong result");
+ expect_u64_eq((uintptr_t)err, (uintptr_t)arg_result_raw,
"Wrong raw result");
- assert_u64_eq((uintptr_t)&ptr, arg_args_raw[0], "Wrong argument");
- assert_u64_eq((uintptr_t)1024, arg_args_raw[1], "Wrong argument");
- assert_u64_eq((uintptr_t)1, arg_args_raw[2], "Wrong argument");
+ expect_u64_eq((uintptr_t)&ptr, arg_args_raw[0], "Wrong argument");
+ expect_u64_eq((uintptr_t)1024, arg_args_raw[1], "Wrong argument");
+ expect_u64_eq((uintptr_t)1, arg_args_raw[2], "Wrong argument");
free(ptr);
/* aligned_alloc */
reset();
ptr = aligned_alloc(1024, 1);
- assert_d_eq(call_count, 1, "Hook not called");
- assert_ptr_eq(arg_extra, (void *)123, "Wrong extra");
- assert_d_eq(arg_type, (int)hook_alloc_aligned_alloc,
+ expect_d_eq(call_count, 1, "Hook not called");
+ expect_ptr_eq(arg_extra, (void *)123, "Wrong extra");
+ expect_d_eq(arg_type, (int)hook_alloc_aligned_alloc,
"Wrong hook type");
- assert_ptr_eq(ptr, arg_result, "Wrong result");
- assert_u64_eq((uintptr_t)ptr, (uintptr_t)arg_result_raw,
+ expect_ptr_eq(ptr, arg_result, "Wrong result");
+ expect_u64_eq((uintptr_t)ptr, (uintptr_t)arg_result_raw,
"Wrong raw result");
- assert_u64_eq((uintptr_t)1024, arg_args_raw[0], "Wrong argument");
- assert_u64_eq((uintptr_t)1, arg_args_raw[1], "Wrong argument");
+ expect_u64_eq((uintptr_t)1024, arg_args_raw[0], "Wrong argument");
+ expect_u64_eq((uintptr_t)1, arg_args_raw[1], "Wrong argument");
free(ptr);
/* calloc */
reset();
ptr = calloc(11, 13);
- assert_d_eq(call_count, 1, "Hook not called");
- assert_ptr_eq(arg_extra, (void *)123, "Wrong extra");
- assert_d_eq(arg_type, (int)hook_alloc_calloc, "Wrong hook type");
- assert_ptr_eq(ptr, arg_result, "Wrong result");
- assert_u64_eq((uintptr_t)ptr, (uintptr_t)arg_result_raw,
+ expect_d_eq(call_count, 1, "Hook not called");
+ expect_ptr_eq(arg_extra, (void *)123, "Wrong extra");
+ expect_d_eq(arg_type, (int)hook_alloc_calloc, "Wrong hook type");
+ expect_ptr_eq(ptr, arg_result, "Wrong result");
+ expect_u64_eq((uintptr_t)ptr, (uintptr_t)arg_result_raw,
"Wrong raw result");
- assert_u64_eq((uintptr_t)11, arg_args_raw[0], "Wrong argument");
- assert_u64_eq((uintptr_t)13, arg_args_raw[1], "Wrong argument");
+ expect_u64_eq((uintptr_t)11, arg_args_raw[0], "Wrong argument");
+ expect_u64_eq((uintptr_t)13, arg_args_raw[1], "Wrong argument");
free(ptr);
/* memalign */
#ifdef JEMALLOC_OVERRIDE_MEMALIGN
reset();
ptr = memalign(1024, 1);
- assert_d_eq(call_count, 1, "Hook not called");
- assert_ptr_eq(arg_extra, (void *)123, "Wrong extra");
- assert_d_eq(arg_type, (int)hook_alloc_memalign, "Wrong hook type");
- assert_ptr_eq(ptr, arg_result, "Wrong result");
- assert_u64_eq((uintptr_t)ptr, (uintptr_t)arg_result_raw,
+ expect_d_eq(call_count, 1, "Hook not called");
+ expect_ptr_eq(arg_extra, (void *)123, "Wrong extra");
+ expect_d_eq(arg_type, (int)hook_alloc_memalign, "Wrong hook type");
+ expect_ptr_eq(ptr, arg_result, "Wrong result");
+ expect_u64_eq((uintptr_t)ptr, (uintptr_t)arg_result_raw,
"Wrong raw result");
- assert_u64_eq((uintptr_t)1024, arg_args_raw[0], "Wrong argument");
- assert_u64_eq((uintptr_t)1, arg_args_raw[1], "Wrong argument");
+ expect_u64_eq((uintptr_t)1024, arg_args_raw[0], "Wrong argument");
+ expect_u64_eq((uintptr_t)1, arg_args_raw[1], "Wrong argument");
free(ptr);
#endif /* JEMALLOC_OVERRIDE_MEMALIGN */
@@ -303,27 +303,27 @@ TEST_BEGIN(test_hooks_alloc_simple) {
#ifdef JEMALLOC_OVERRIDE_VALLOC
reset();
ptr = valloc(1);
- assert_d_eq(call_count, 1, "Hook not called");
- assert_ptr_eq(arg_extra, (void *)123, "Wrong extra");
- assert_d_eq(arg_type, (int)hook_alloc_valloc, "Wrong hook type");
- assert_ptr_eq(ptr, arg_result, "Wrong result");
- assert_u64_eq((uintptr_t)ptr, (uintptr_t)arg_result_raw,
+ expect_d_eq(call_count, 1, "Hook not called");
+ expect_ptr_eq(arg_extra, (void *)123, "Wrong extra");
+ expect_d_eq(arg_type, (int)hook_alloc_valloc, "Wrong hook type");
+ expect_ptr_eq(ptr, arg_result, "Wrong result");
+ expect_u64_eq((uintptr_t)ptr, (uintptr_t)arg_result_raw,
"Wrong raw result");
- assert_u64_eq((uintptr_t)1, arg_args_raw[0], "Wrong argument");
+ expect_u64_eq((uintptr_t)1, arg_args_raw[0], "Wrong argument");
free(ptr);
#endif /* JEMALLOC_OVERRIDE_VALLOC */
/* mallocx */
reset();
ptr = mallocx(1, MALLOCX_LG_ALIGN(10));
- assert_d_eq(call_count, 1, "Hook not called");
- assert_ptr_eq(arg_extra, (void *)123, "Wrong extra");
- assert_d_eq(arg_type, (int)hook_alloc_mallocx, "Wrong hook type");
- assert_ptr_eq(ptr, arg_result, "Wrong result");
- assert_u64_eq((uintptr_t)ptr, (uintptr_t)arg_result_raw,
+ expect_d_eq(call_count, 1, "Hook not called");
+ expect_ptr_eq(arg_extra, (void *)123, "Wrong extra");
+ expect_d_eq(arg_type, (int)hook_alloc_mallocx, "Wrong hook type");
+ expect_ptr_eq(ptr, arg_result, "Wrong result");
+ expect_u64_eq((uintptr_t)ptr, (uintptr_t)arg_result_raw,
"Wrong raw result");
- assert_u64_eq((uintptr_t)1, arg_args_raw[0], "Wrong argument");
- assert_u64_eq((uintptr_t)MALLOCX_LG_ALIGN(10), arg_args_raw[1],
+ expect_u64_eq((uintptr_t)1, arg_args_raw[0], "Wrong argument");
+ expect_u64_eq((uintptr_t)MALLOCX_LG_ALIGN(10), arg_args_raw[1],
"Wrong flags");
free(ptr);
@@ -335,7 +335,7 @@ TEST_BEGIN(test_hooks_dalloc_simple) {
/* "Simple" in the sense that we're not in a realloc variant. */
hooks_t hooks = {NULL, &test_dalloc_hook, NULL, (void *)123};
void *handle = hook_install(TSDN_NULL, &hooks);
- assert_ptr_ne(handle, NULL, "Hook installation failed");
+ expect_ptr_ne(handle, NULL, "Hook installation failed");
void *volatile ptr;
@@ -343,35 +343,35 @@ TEST_BEGIN(test_hooks_dalloc_simple) {
reset();
ptr = malloc(1);
free(ptr);
- assert_d_eq(call_count, 1, "Hook not called");
- assert_ptr_eq(arg_extra, (void *)123, "Wrong extra");
- assert_d_eq(arg_type, (int)hook_dalloc_free, "Wrong hook type");
- assert_ptr_eq(ptr, arg_address, "Wrong pointer freed");
- assert_u64_eq((uintptr_t)ptr, arg_args_raw[0], "Wrong raw arg");
+ expect_d_eq(call_count, 1, "Hook not called");
+ expect_ptr_eq(arg_extra, (void *)123, "Wrong extra");
+ expect_d_eq(arg_type, (int)hook_dalloc_free, "Wrong hook type");
+ expect_ptr_eq(ptr, arg_address, "Wrong pointer freed");
+ expect_u64_eq((uintptr_t)ptr, arg_args_raw[0], "Wrong raw arg");
/* dallocx() */
reset();
ptr = malloc(1);
dallocx(ptr, MALLOCX_TCACHE_NONE);
- assert_d_eq(call_count, 1, "Hook not called");
- assert_ptr_eq(arg_extra, (void *)123, "Wrong extra");
- assert_d_eq(arg_type, (int)hook_dalloc_dallocx, "Wrong hook type");
- assert_ptr_eq(ptr, arg_address, "Wrong pointer freed");
- assert_u64_eq((uintptr_t)ptr, arg_args_raw[0], "Wrong raw arg");
- assert_u64_eq((uintptr_t)MALLOCX_TCACHE_NONE, arg_args_raw[1],
+ expect_d_eq(call_count, 1, "Hook not called");
+ expect_ptr_eq(arg_extra, (void *)123, "Wrong extra");
+ expect_d_eq(arg_type, (int)hook_dalloc_dallocx, "Wrong hook type");
+ expect_ptr_eq(ptr, arg_address, "Wrong pointer freed");
+ expect_u64_eq((uintptr_t)ptr, arg_args_raw[0], "Wrong raw arg");
+ expect_u64_eq((uintptr_t)MALLOCX_TCACHE_NONE, arg_args_raw[1],
"Wrong raw arg");
/* sdallocx() */
reset();
ptr = malloc(1);
sdallocx(ptr, 1, MALLOCX_TCACHE_NONE);
- assert_d_eq(call_count, 1, "Hook not called");
- assert_ptr_eq(arg_extra, (void *)123, "Wrong extra");
- assert_d_eq(arg_type, (int)hook_dalloc_sdallocx, "Wrong hook type");
- assert_ptr_eq(ptr, arg_address, "Wrong pointer freed");
- assert_u64_eq((uintptr_t)ptr, arg_args_raw[0], "Wrong raw arg");
- assert_u64_eq((uintptr_t)1, arg_args_raw[1], "Wrong raw arg");
- assert_u64_eq((uintptr_t)MALLOCX_TCACHE_NONE, arg_args_raw[2],
+ expect_d_eq(call_count, 1, "Hook not called");
+ expect_ptr_eq(arg_extra, (void *)123, "Wrong extra");
+ expect_d_eq(arg_type, (int)hook_dalloc_sdallocx, "Wrong hook type");
+ expect_ptr_eq(ptr, arg_address, "Wrong pointer freed");
+ expect_u64_eq((uintptr_t)ptr, arg_args_raw[0], "Wrong raw arg");
+ expect_u64_eq((uintptr_t)1, arg_args_raw[1], "Wrong raw arg");
+ expect_u64_eq((uintptr_t)MALLOCX_TCACHE_NONE, arg_args_raw[2],
"Wrong raw arg");
hook_remove(TSDN_NULL, handle);
@@ -382,7 +382,7 @@ TEST_BEGIN(test_hooks_expand_simple) {
/* "Simple" in the sense that we're not in a realloc variant. */
hooks_t hooks = {NULL, NULL, &test_expand_hook, (void *)123};
void *handle = hook_install(TSDN_NULL, &hooks);
- assert_ptr_ne(handle, NULL, "Hook installation failed");
+ expect_ptr_ne(handle, NULL, "Hook installation failed");
void *volatile ptr;
@@ -390,17 +390,17 @@ TEST_BEGIN(test_hooks_expand_simple) {
reset();
ptr = malloc(1);
size_t new_usize = xallocx(ptr, 100, 200, MALLOCX_TCACHE_NONE);
- assert_d_eq(call_count, 1, "Hook not called");
- assert_ptr_eq(arg_extra, (void *)123, "Wrong extra");
- assert_d_eq(arg_type, (int)hook_expand_xallocx, "Wrong hook type");
- assert_ptr_eq(ptr, arg_address, "Wrong pointer expanded");
- assert_u64_eq(arg_old_usize, nallocx(1, 0), "Wrong old usize");
- assert_u64_eq(arg_new_usize, sallocx(ptr, 0), "Wrong new usize");
- assert_u64_eq(new_usize, arg_result_raw, "Wrong result");
- assert_u64_eq((uintptr_t)ptr, arg_args_raw[0], "Wrong arg");
- assert_u64_eq(100, arg_args_raw[1], "Wrong arg");
- assert_u64_eq(200, arg_args_raw[2], "Wrong arg");
- assert_u64_eq(MALLOCX_TCACHE_NONE, arg_args_raw[3], "Wrong arg");
+ expect_d_eq(call_count, 1, "Hook not called");
+ expect_ptr_eq(arg_extra, (void *)123, "Wrong extra");
+ expect_d_eq(arg_type, (int)hook_expand_xallocx, "Wrong hook type");
+ expect_ptr_eq(ptr, arg_address, "Wrong pointer expanded");
+ expect_u64_eq(arg_old_usize, nallocx(1, 0), "Wrong old usize");
+ expect_u64_eq(arg_new_usize, sallocx(ptr, 0), "Wrong new usize");
+ expect_u64_eq(new_usize, arg_result_raw, "Wrong result");
+ expect_u64_eq((uintptr_t)ptr, arg_args_raw[0], "Wrong arg");
+ expect_u64_eq(100, arg_args_raw[1], "Wrong arg");
+ expect_u64_eq(200, arg_args_raw[2], "Wrong arg");
+ expect_u64_eq(MALLOCX_TCACHE_NONE, arg_args_raw[3], "Wrong arg");
hook_remove(TSDN_NULL, handle);
}
@@ -410,45 +410,51 @@ TEST_BEGIN(test_hooks_realloc_as_malloc_or_free) {
hooks_t hooks = {&test_alloc_hook, &test_dalloc_hook,
&test_expand_hook, (void *)123};
void *handle = hook_install(TSDN_NULL, &hooks);
- assert_ptr_ne(handle, NULL, "Hook installation failed");
+ expect_ptr_ne(handle, NULL, "Hook installation failed");
void *volatile ptr;
/* realloc(NULL, size) as malloc */
reset();
ptr = realloc(NULL, 1);
- assert_d_eq(call_count, 1, "Hook not called");
- assert_ptr_eq(arg_extra, (void *)123, "Wrong extra");
- assert_d_eq(arg_type, (int)hook_alloc_realloc, "Wrong hook type");
- assert_ptr_eq(ptr, arg_result, "Wrong result");
- assert_u64_eq((uintptr_t)ptr, (uintptr_t)arg_result_raw,
+ expect_d_eq(call_count, 1, "Hook not called");
+ expect_ptr_eq(arg_extra, (void *)123, "Wrong extra");
+ expect_d_eq(arg_type, (int)hook_alloc_realloc, "Wrong hook type");
+ expect_ptr_eq(ptr, arg_result, "Wrong result");
+ expect_u64_eq((uintptr_t)ptr, (uintptr_t)arg_result_raw,
"Wrong raw result");
- assert_u64_eq((uintptr_t)NULL, arg_args_raw[0], "Wrong argument");
- assert_u64_eq((uintptr_t)1, arg_args_raw[1], "Wrong argument");
+ expect_u64_eq((uintptr_t)NULL, arg_args_raw[0], "Wrong argument");
+ expect_u64_eq((uintptr_t)1, arg_args_raw[1], "Wrong argument");
free(ptr);
/* realloc(ptr, 0) as free */
- ptr = malloc(1);
- reset();
- realloc(ptr, 0);
- assert_d_eq(call_count, 1, "Hook not called");
- assert_ptr_eq(arg_extra, (void *)123, "Wrong extra");
- assert_d_eq(arg_type, (int)hook_dalloc_realloc, "Wrong hook type");
- assert_ptr_eq(ptr, arg_address, "Wrong pointer freed");
- assert_u64_eq((uintptr_t)ptr, arg_args_raw[0], "Wrong raw arg");
- assert_u64_eq((uintptr_t)0, arg_args_raw[1], "Wrong raw arg");
+ if (opt_zero_realloc_action == zero_realloc_action_free) {
+ ptr = malloc(1);
+ reset();
+ realloc(ptr, 0);
+ expect_d_eq(call_count, 1, "Hook not called");
+ expect_ptr_eq(arg_extra, (void *)123, "Wrong extra");
+ expect_d_eq(arg_type, (int)hook_dalloc_realloc,
+ "Wrong hook type");
+ expect_ptr_eq(ptr, arg_address,
+ "Wrong pointer freed");
+ expect_u64_eq((uintptr_t)ptr, arg_args_raw[0],
+ "Wrong raw arg");
+ expect_u64_eq((uintptr_t)0, arg_args_raw[1],
+ "Wrong raw arg");
+ }
/* realloc(NULL, 0) as malloc(0) */
reset();
ptr = realloc(NULL, 0);
- assert_d_eq(call_count, 1, "Hook not called");
- assert_ptr_eq(arg_extra, (void *)123, "Wrong extra");
- assert_d_eq(arg_type, (int)hook_alloc_realloc, "Wrong hook type");
- assert_ptr_eq(ptr, arg_result, "Wrong result");
- assert_u64_eq((uintptr_t)ptr, (uintptr_t)arg_result_raw,
+ expect_d_eq(call_count, 1, "Hook not called");
+ expect_ptr_eq(arg_extra, (void *)123, "Wrong extra");
+ expect_d_eq(arg_type, (int)hook_alloc_realloc, "Wrong hook type");
+ expect_ptr_eq(ptr, arg_result, "Wrong result");
+ expect_u64_eq((uintptr_t)ptr, (uintptr_t)arg_result_raw,
"Wrong raw result");
- assert_u64_eq((uintptr_t)NULL, arg_args_raw[0], "Wrong argument");
- assert_u64_eq((uintptr_t)0, arg_args_raw[1], "Wrong argument");
+ expect_u64_eq((uintptr_t)NULL, arg_args_raw[0], "Wrong argument");
+ expect_u64_eq((uintptr_t)0, arg_args_raw[1], "Wrong argument");
free(ptr);
hook_remove(TSDN_NULL, handle);
@@ -461,7 +467,7 @@ do_realloc_test(void *(*ralloc)(void *, size_t, int), int flags,
hooks_t hooks = {&test_alloc_hook, &test_dalloc_hook,
&test_expand_hook, (void *)123};
void *handle = hook_install(TSDN_NULL, &hooks);
- assert_ptr_ne(handle, NULL, "Hook installation failed");
+ expect_ptr_ne(handle, NULL, "Hook installation failed");
void *volatile ptr;
void *volatile ptr2;
@@ -470,16 +476,16 @@ do_realloc_test(void *(*ralloc)(void *, size_t, int), int flags,
ptr = malloc(129);
reset();
ptr2 = ralloc(ptr, 130, flags);
- assert_ptr_eq(ptr, ptr2, "Small realloc moved");
+ expect_ptr_eq(ptr, ptr2, "Small realloc moved");
- assert_d_eq(call_count, 1, "Hook not called");
- assert_ptr_eq(arg_extra, (void *)123, "Wrong extra");
- assert_d_eq(arg_type, expand_type, "Wrong hook type");
- assert_ptr_eq(ptr, arg_address, "Wrong address");
- assert_u64_eq((uintptr_t)ptr, (uintptr_t)arg_result_raw,
+ expect_d_eq(call_count, 1, "Hook not called");
+ expect_ptr_eq(arg_extra, (void *)123, "Wrong extra");
+ expect_d_eq(arg_type, expand_type, "Wrong hook type");
+ expect_ptr_eq(ptr, arg_address, "Wrong address");
+ expect_u64_eq((uintptr_t)ptr, (uintptr_t)arg_result_raw,
"Wrong raw result");
- assert_u64_eq((uintptr_t)ptr, arg_args_raw[0], "Wrong argument");
- assert_u64_eq((uintptr_t)130, arg_args_raw[1], "Wrong argument");
+ expect_u64_eq((uintptr_t)ptr, arg_args_raw[0], "Wrong argument");
+ expect_u64_eq((uintptr_t)130, arg_args_raw[1], "Wrong argument");
free(ptr);
/*
@@ -493,19 +499,19 @@ do_realloc_test(void *(*ralloc)(void *, size_t, int), int flags,
ptr = ralloc(ptr2, 2 * 1024 * 1024, flags);
/* ptr is the new address, ptr2 is the old address. */
if (ptr == ptr2) {
- assert_d_eq(call_count, 1, "Hook not called");
- assert_d_eq(arg_type, expand_type, "Wrong hook type");
+ expect_d_eq(call_count, 1, "Hook not called");
+ expect_d_eq(arg_type, expand_type, "Wrong hook type");
} else {
- assert_d_eq(call_count, 2, "Wrong hooks called");
- assert_ptr_eq(ptr, arg_result, "Wrong address");
- assert_d_eq(arg_type, dalloc_type, "Wrong hook type");
+ expect_d_eq(call_count, 2, "Wrong hooks called");
+ expect_ptr_eq(ptr, arg_result, "Wrong address");
+ expect_d_eq(arg_type, dalloc_type, "Wrong hook type");
}
- assert_ptr_eq(arg_extra, (void *)123, "Wrong extra");
- assert_ptr_eq(ptr2, arg_address, "Wrong address");
- assert_u64_eq((uintptr_t)ptr, (uintptr_t)arg_result_raw,
+ expect_ptr_eq(arg_extra, (void *)123, "Wrong extra");
+ expect_ptr_eq(ptr2, arg_address, "Wrong address");
+ expect_u64_eq((uintptr_t)ptr, (uintptr_t)arg_result_raw,
"Wrong raw result");
- assert_u64_eq((uintptr_t)ptr2, arg_args_raw[0], "Wrong argument");
- assert_u64_eq((uintptr_t)2 * 1024 * 1024, arg_args_raw[1],
+ expect_u64_eq((uintptr_t)ptr2, arg_args_raw[0], "Wrong argument");
+ expect_u64_eq((uintptr_t)2 * 1024 * 1024, arg_args_raw[1],
"Wrong argument");
free(ptr);
@@ -513,34 +519,34 @@ do_realloc_test(void *(*ralloc)(void *, size_t, int), int flags,
ptr = malloc(8);
reset();
ptr2 = ralloc(ptr, 128, flags);
- assert_ptr_ne(ptr, ptr2, "Small realloc didn't move");
-
- assert_d_eq(call_count, 2, "Hook not called");
- assert_ptr_eq(arg_extra, (void *)123, "Wrong extra");
- assert_d_eq(arg_type, dalloc_type, "Wrong hook type");
- assert_ptr_eq(ptr, arg_address, "Wrong address");
- assert_ptr_eq(ptr2, arg_result, "Wrong address");
- assert_u64_eq((uintptr_t)ptr2, (uintptr_t)arg_result_raw,
+ expect_ptr_ne(ptr, ptr2, "Small realloc didn't move");
+
+ expect_d_eq(call_count, 2, "Hook not called");
+ expect_ptr_eq(arg_extra, (void *)123, "Wrong extra");
+ expect_d_eq(arg_type, dalloc_type, "Wrong hook type");
+ expect_ptr_eq(ptr, arg_address, "Wrong address");
+ expect_ptr_eq(ptr2, arg_result, "Wrong address");
+ expect_u64_eq((uintptr_t)ptr2, (uintptr_t)arg_result_raw,
"Wrong raw result");
- assert_u64_eq((uintptr_t)ptr, arg_args_raw[0], "Wrong argument");
- assert_u64_eq((uintptr_t)128, arg_args_raw[1], "Wrong argument");
+ expect_u64_eq((uintptr_t)ptr, arg_args_raw[0], "Wrong argument");
+ expect_u64_eq((uintptr_t)128, arg_args_raw[1], "Wrong argument");
free(ptr2);
/* Realloc with move, large. */
ptr = malloc(1);
reset();
ptr2 = ralloc(ptr, 2 * 1024 * 1024, flags);
- assert_ptr_ne(ptr, ptr2, "Large realloc didn't move");
-
- assert_d_eq(call_count, 2, "Hook not called");
- assert_ptr_eq(arg_extra, (void *)123, "Wrong extra");
- assert_d_eq(arg_type, dalloc_type, "Wrong hook type");
- assert_ptr_eq(ptr, arg_address, "Wrong address");
- assert_ptr_eq(ptr2, arg_result, "Wrong address");
- assert_u64_eq((uintptr_t)ptr2, (uintptr_t)arg_result_raw,
+ expect_ptr_ne(ptr, ptr2, "Large realloc didn't move");
+
+ expect_d_eq(call_count, 2, "Hook not called");
+ expect_ptr_eq(arg_extra, (void *)123, "Wrong extra");
+ expect_d_eq(arg_type, dalloc_type, "Wrong hook type");
+ expect_ptr_eq(ptr, arg_address, "Wrong address");
+ expect_ptr_eq(ptr2, arg_result, "Wrong address");
+ expect_u64_eq((uintptr_t)ptr2, (uintptr_t)arg_result_raw,
"Wrong raw result");
- assert_u64_eq((uintptr_t)ptr, arg_args_raw[0], "Wrong argument");
- assert_u64_eq((uintptr_t)2 * 1024 * 1024, arg_args_raw[1],
+ expect_u64_eq((uintptr_t)ptr, arg_args_raw[0], "Wrong argument");
+ expect_u64_eq((uintptr_t)2 * 1024 * 1024, arg_args_raw[1],
"Wrong argument");
free(ptr2);
diff --git a/deps/jemalloc/test/unit/hpa.c b/deps/jemalloc/test/unit/hpa.c
new file mode 100644
index 000000000..dfd57f39f
--- /dev/null
+++ b/deps/jemalloc/test/unit/hpa.c
@@ -0,0 +1,459 @@
+#include "test/jemalloc_test.h"
+
+#include "jemalloc/internal/hpa.h"
+#include "jemalloc/internal/nstime.h"
+
+#define SHARD_IND 111
+
+#define ALLOC_MAX (HUGEPAGE / 4)
+
+typedef struct test_data_s test_data_t;
+struct test_data_s {
+ /*
+ * Must be the first member -- we convert back and forth between the
+ * test_data_t and the hpa_shard_t;
+ */
+ hpa_shard_t shard;
+ hpa_central_t central;
+ base_t *base;
+ edata_cache_t shard_edata_cache;
+
+ emap_t emap;
+};
+
+static hpa_shard_opts_t test_hpa_shard_opts_default = {
+ /* slab_max_alloc */
+ ALLOC_MAX,
+ /* hugification threshold */
+ HUGEPAGE,
+ /* dirty_mult */
+ FXP_INIT_PERCENT(25),
+ /* deferral_allowed */
+ false,
+ /* hugify_delay_ms */
+ 10 * 1000,
+};
+
+static hpa_shard_t *
+create_test_data(hpa_hooks_t *hooks, hpa_shard_opts_t *opts) {
+ bool err;
+ base_t *base = base_new(TSDN_NULL, /* ind */ SHARD_IND,
+ &ehooks_default_extent_hooks, /* metadata_use_hooks */ true);
+ assert_ptr_not_null(base, "");
+
+ test_data_t *test_data = malloc(sizeof(test_data_t));
+ assert_ptr_not_null(test_data, "");
+
+ test_data->base = base;
+
+ err = edata_cache_init(&test_data->shard_edata_cache, base);
+ assert_false(err, "");
+
+ err = emap_init(&test_data->emap, test_data->base, /* zeroed */ false);
+ assert_false(err, "");
+
+ err = hpa_central_init(&test_data->central, test_data->base, hooks);
+ assert_false(err, "");
+
+ err = hpa_shard_init(&test_data->shard, &test_data->central,
+ &test_data->emap, test_data->base, &test_data->shard_edata_cache,
+ SHARD_IND, opts);
+ assert_false(err, "");
+
+ return (hpa_shard_t *)test_data;
+}
+
+static void
+destroy_test_data(hpa_shard_t *shard) {
+ test_data_t *test_data = (test_data_t *)shard;
+ base_delete(TSDN_NULL, test_data->base);
+ free(test_data);
+}
+
+TEST_BEGIN(test_alloc_max) {
+ test_skip_if(!hpa_supported());
+
+ hpa_shard_t *shard = create_test_data(&hpa_hooks_default,
+ &test_hpa_shard_opts_default);
+ tsdn_t *tsdn = tsd_tsdn(tsd_fetch());
+
+ edata_t *edata;
+
+ /* Small max */
+ bool deferred_work_generated = false;
+ edata = pai_alloc(tsdn, &shard->pai, ALLOC_MAX, PAGE, false, false,
+ false, &deferred_work_generated);
+ expect_ptr_not_null(edata, "Allocation of small max failed");
+ edata = pai_alloc(tsdn, &shard->pai, ALLOC_MAX + PAGE, PAGE, false,
+ false, false, &deferred_work_generated);
+ expect_ptr_null(edata, "Allocation of larger than small max succeeded");
+
+ destroy_test_data(shard);
+}
+TEST_END
+
+typedef struct mem_contents_s mem_contents_t;
+struct mem_contents_s {
+ uintptr_t my_addr;
+ size_t size;
+ edata_t *my_edata;
+ rb_node(mem_contents_t) link;
+};
+
+static int
+mem_contents_cmp(const mem_contents_t *a, const mem_contents_t *b) {
+ return (a->my_addr > b->my_addr) - (a->my_addr < b->my_addr);
+}
+
+typedef rb_tree(mem_contents_t) mem_tree_t;
+rb_gen(static, mem_tree_, mem_tree_t, mem_contents_t, link,
+ mem_contents_cmp);
+
+static void
+node_assert_ordered(mem_contents_t *a, mem_contents_t *b) {
+ assert_zu_lt(a->my_addr, a->my_addr + a->size, "Overflow");
+ assert_zu_le(a->my_addr + a->size, b->my_addr, "");
+}
+
+static void
+node_check(mem_tree_t *tree, mem_contents_t *contents) {
+ edata_t *edata = contents->my_edata;
+ assert_ptr_eq(contents, (void *)contents->my_addr, "");
+ assert_ptr_eq(contents, edata_base_get(edata), "");
+ assert_zu_eq(contents->size, edata_size_get(edata), "");
+ assert_ptr_eq(contents->my_edata, edata, "");
+
+ mem_contents_t *next = mem_tree_next(tree, contents);
+ if (next != NULL) {
+ node_assert_ordered(contents, next);
+ }
+ mem_contents_t *prev = mem_tree_prev(tree, contents);
+ if (prev != NULL) {
+ node_assert_ordered(prev, contents);
+ }
+}
+
+static void
+node_insert(mem_tree_t *tree, edata_t *edata, size_t npages) {
+ mem_contents_t *contents = (mem_contents_t *)edata_base_get(edata);
+ contents->my_addr = (uintptr_t)edata_base_get(edata);
+ contents->size = edata_size_get(edata);
+ contents->my_edata = edata;
+ mem_tree_insert(tree, contents);
+ node_check(tree, contents);
+}
+
+static void
+node_remove(mem_tree_t *tree, edata_t *edata) {
+ mem_contents_t *contents = (mem_contents_t *)edata_base_get(edata);
+ node_check(tree, contents);
+ mem_tree_remove(tree, contents);
+}
+
+TEST_BEGIN(test_stress) {
+ test_skip_if(!hpa_supported());
+
+ hpa_shard_t *shard = create_test_data(&hpa_hooks_default,
+ &test_hpa_shard_opts_default);
+
+ tsdn_t *tsdn = tsd_tsdn(tsd_fetch());
+
+ const size_t nlive_edatas_max = 500;
+ size_t nlive_edatas = 0;
+ edata_t **live_edatas = calloc(nlive_edatas_max, sizeof(edata_t *));
+ /*
+ * Nothing special about this constant; we're only fixing it for
+ * consistency across runs.
+ */
+ size_t prng_state = (size_t)0x76999ffb014df07c;
+
+ mem_tree_t tree;
+ mem_tree_new(&tree);
+
+ bool deferred_work_generated = false;
+
+ for (size_t i = 0; i < 100 * 1000; i++) {
+ size_t operation = prng_range_zu(&prng_state, 2);
+ if (operation == 0) {
+ /* Alloc */
+ if (nlive_edatas == nlive_edatas_max) {
+ continue;
+ }
+
+ /*
+ * We make sure to get an even balance of small and
+ * large allocations.
+ */
+ size_t npages_min = 1;
+ size_t npages_max = ALLOC_MAX / PAGE;
+ size_t npages = npages_min + prng_range_zu(&prng_state,
+ npages_max - npages_min);
+ edata_t *edata = pai_alloc(tsdn, &shard->pai,
+ npages * PAGE, PAGE, false, false, false,
+ &deferred_work_generated);
+ assert_ptr_not_null(edata,
+ "Unexpected allocation failure");
+ live_edatas[nlive_edatas] = edata;
+ nlive_edatas++;
+ node_insert(&tree, edata, npages);
+ } else {
+ /* Free. */
+ if (nlive_edatas == 0) {
+ continue;
+ }
+ size_t victim = prng_range_zu(&prng_state, nlive_edatas);
+ edata_t *to_free = live_edatas[victim];
+ live_edatas[victim] = live_edatas[nlive_edatas - 1];
+ nlive_edatas--;
+ node_remove(&tree, to_free);
+ pai_dalloc(tsdn, &shard->pai, to_free,
+ &deferred_work_generated);
+ }
+ }
+
+ size_t ntreenodes = 0;
+ for (mem_contents_t *contents = mem_tree_first(&tree); contents != NULL;
+ contents = mem_tree_next(&tree, contents)) {
+ ntreenodes++;
+ node_check(&tree, contents);
+ }
+ expect_zu_eq(ntreenodes, nlive_edatas, "");
+
+ /*
+ * Test hpa_shard_destroy, which requires as a precondition that all its
+ * extents have been deallocated.
+ */
+ for (size_t i = 0; i < nlive_edatas; i++) {
+ edata_t *to_free = live_edatas[i];
+ node_remove(&tree, to_free);
+ pai_dalloc(tsdn, &shard->pai, to_free,
+ &deferred_work_generated);
+ }
+ hpa_shard_destroy(tsdn, shard);
+
+ free(live_edatas);
+ destroy_test_data(shard);
+}
+TEST_END
+
+static void
+expect_contiguous(edata_t **edatas, size_t nedatas) {
+ for (size_t i = 0; i < nedatas; i++) {
+ size_t expected = (size_t)edata_base_get(edatas[0])
+ + i * PAGE;
+ expect_zu_eq(expected, (size_t)edata_base_get(edatas[i]),
+ "Mismatch at index %zu", i);
+ }
+}
+
+TEST_BEGIN(test_alloc_dalloc_batch) {
+ test_skip_if(!hpa_supported());
+
+ hpa_shard_t *shard = create_test_data(&hpa_hooks_default,
+ &test_hpa_shard_opts_default);
+ tsdn_t *tsdn = tsd_tsdn(tsd_fetch());
+
+ bool deferred_work_generated = false;
+
+ enum {NALLOCS = 8};
+
+ edata_t *allocs[NALLOCS];
+ /*
+ * Allocate a mix of ways; first half from regular alloc, second half
+ * from alloc_batch.
+ */
+ for (size_t i = 0; i < NALLOCS / 2; i++) {
+ allocs[i] = pai_alloc(tsdn, &shard->pai, PAGE, PAGE,
+ /* zero */ false, /* guarded */ false,
+ /* frequent_reuse */ false, &deferred_work_generated);
+ expect_ptr_not_null(allocs[i], "Unexpected alloc failure");
+ }
+ edata_list_active_t allocs_list;
+ edata_list_active_init(&allocs_list);
+ size_t nsuccess = pai_alloc_batch(tsdn, &shard->pai, PAGE, NALLOCS / 2,
+ &allocs_list, &deferred_work_generated);
+ expect_zu_eq(NALLOCS / 2, nsuccess, "Unexpected oom");
+ for (size_t i = NALLOCS / 2; i < NALLOCS; i++) {
+ allocs[i] = edata_list_active_first(&allocs_list);
+ edata_list_active_remove(&allocs_list, allocs[i]);
+ }
+
+ /*
+ * Should have allocated them contiguously, despite the differing
+ * methods used.
+ */
+ void *orig_base = edata_base_get(allocs[0]);
+ expect_contiguous(allocs, NALLOCS);
+
+ /*
+ * Batch dalloc the first half, individually deallocate the second half.
+ */
+ for (size_t i = 0; i < NALLOCS / 2; i++) {
+ edata_list_active_append(&allocs_list, allocs[i]);
+ }
+ pai_dalloc_batch(tsdn, &shard->pai, &allocs_list,
+ &deferred_work_generated);
+ for (size_t i = NALLOCS / 2; i < NALLOCS; i++) {
+ pai_dalloc(tsdn, &shard->pai, allocs[i],
+ &deferred_work_generated);
+ }
+
+ /* Reallocate (individually), and ensure reuse and contiguity. */
+ for (size_t i = 0; i < NALLOCS; i++) {
+ allocs[i] = pai_alloc(tsdn, &shard->pai, PAGE, PAGE,
+ /* zero */ false, /* guarded */ false, /* frequent_reuse */
+ false, &deferred_work_generated);
+ expect_ptr_not_null(allocs[i], "Unexpected alloc failure.");
+ }
+ void *new_base = edata_base_get(allocs[0]);
+ expect_ptr_eq(orig_base, new_base,
+ "Failed to reuse the allocated memory.");
+ expect_contiguous(allocs, NALLOCS);
+
+ destroy_test_data(shard);
+}
+TEST_END
+
+static uintptr_t defer_bump_ptr = HUGEPAGE * 123;
+static void *
+defer_test_map(size_t size) {
+ void *result = (void *)defer_bump_ptr;
+ defer_bump_ptr += size;
+ return result;
+}
+
+static void
+defer_test_unmap(void *ptr, size_t size) {
+ (void)ptr;
+ (void)size;
+}
+
+static bool defer_purge_called = false;
+static void
+defer_test_purge(void *ptr, size_t size) {
+ (void)ptr;
+ (void)size;
+ defer_purge_called = true;
+}
+
+static bool defer_hugify_called = false;
+static void
+defer_test_hugify(void *ptr, size_t size) {
+ defer_hugify_called = true;
+}
+
+static bool defer_dehugify_called = false;
+static void
+defer_test_dehugify(void *ptr, size_t size) {
+ defer_dehugify_called = true;
+}
+
+static nstime_t defer_curtime;
+static void
+defer_test_curtime(nstime_t *r_time, bool first_reading) {
+ *r_time = defer_curtime;
+}
+
+static uint64_t
+defer_test_ms_since(nstime_t *past_time) {
+ return (nstime_ns(&defer_curtime) - nstime_ns(past_time)) / 1000 / 1000;
+}
+
+TEST_BEGIN(test_defer_time) {
+ test_skip_if(!hpa_supported());
+
+ hpa_hooks_t hooks;
+ hooks.map = &defer_test_map;
+ hooks.unmap = &defer_test_unmap;
+ hooks.purge = &defer_test_purge;
+ hooks.hugify = &defer_test_hugify;
+ hooks.dehugify = &defer_test_dehugify;
+ hooks.curtime = &defer_test_curtime;
+ hooks.ms_since = &defer_test_ms_since;
+
+ hpa_shard_opts_t opts = test_hpa_shard_opts_default;
+ opts.deferral_allowed = true;
+
+ hpa_shard_t *shard = create_test_data(&hooks, &opts);
+
+ bool deferred_work_generated = false;
+
+ nstime_init(&defer_curtime, 0);
+ tsdn_t *tsdn = tsd_tsdn(tsd_fetch());
+ edata_t *edatas[HUGEPAGE_PAGES];
+ for (int i = 0; i < (int)HUGEPAGE_PAGES; i++) {
+ edatas[i] = pai_alloc(tsdn, &shard->pai, PAGE, PAGE, false,
+ false, false, &deferred_work_generated);
+ expect_ptr_not_null(edatas[i], "Unexpected null edata");
+ }
+ hpa_shard_do_deferred_work(tsdn, shard);
+ expect_false(defer_hugify_called, "Hugified too early");
+
+ /* Hugification delay is set to 10 seconds in options. */
+ nstime_init2(&defer_curtime, 11, 0);
+ hpa_shard_do_deferred_work(tsdn, shard);
+ expect_true(defer_hugify_called, "Failed to hugify");
+
+ defer_hugify_called = false;
+
+ /* Purge. Recall that dirty_mult is .25. */
+ for (int i = 0; i < (int)HUGEPAGE_PAGES / 2; i++) {
+ pai_dalloc(tsdn, &shard->pai, edatas[i],
+ &deferred_work_generated);
+ }
+
+ hpa_shard_do_deferred_work(tsdn, shard);
+
+ expect_false(defer_hugify_called, "Hugified too early");
+ expect_true(defer_dehugify_called, "Should have dehugified");
+ expect_true(defer_purge_called, "Should have purged");
+ defer_hugify_called = false;
+ defer_dehugify_called = false;
+ defer_purge_called = false;
+
+ /*
+ * Refill the page. We now meet the hugification threshold; we should
+ * be marked for pending hugify.
+ */
+ for (int i = 0; i < (int)HUGEPAGE_PAGES / 2; i++) {
+ edatas[i] = pai_alloc(tsdn, &shard->pai, PAGE, PAGE, false,
+ false, false, &deferred_work_generated);
+ expect_ptr_not_null(edatas[i], "Unexpected null edata");
+ }
+ /*
+ * We would be ineligible for hugification, had we not already met the
+ * threshold before dipping below it.
+ */
+ pai_dalloc(tsdn, &shard->pai, edatas[0],
+ &deferred_work_generated);
+ /* Wait for the threshold again. */
+ nstime_init2(&defer_curtime, 22, 0);
+ hpa_shard_do_deferred_work(tsdn, shard);
+ expect_true(defer_hugify_called, "Hugified too early");
+ expect_false(defer_dehugify_called, "Unexpected dehugify");
+ expect_false(defer_purge_called, "Unexpected purge");
+
+ destroy_test_data(shard);
+}
+TEST_END
+
+int
+main(void) {
+ /*
+ * These trigger unused-function warnings on CI runs, even if declared
+ * with static inline.
+ */
+ (void)mem_tree_empty;
+ (void)mem_tree_last;
+ (void)mem_tree_search;
+ (void)mem_tree_nsearch;
+ (void)mem_tree_psearch;
+ (void)mem_tree_iter;
+ (void)mem_tree_reverse_iter;
+ (void)mem_tree_destroy;
+ return test_no_reentrancy(
+ test_alloc_max,
+ test_stress,
+ test_alloc_dalloc_batch,
+ test_defer_time);
+}
diff --git a/deps/jemalloc/test/unit/hpa_background_thread.c b/deps/jemalloc/test/unit/hpa_background_thread.c
new file mode 100644
index 000000000..81c256127
--- /dev/null
+++ b/deps/jemalloc/test/unit/hpa_background_thread.c
@@ -0,0 +1,188 @@
+#include "test/jemalloc_test.h"
+#include "test/sleep.h"
+
+static void
+sleep_for_background_thread_interval() {
+ /*
+ * The sleep interval set in our .sh file is 50ms. So it likely will
+ * run if we sleep for four times that.
+ */
+ sleep_ns(200 * 1000 * 1000);
+}
+
+static unsigned
+create_arena() {
+ unsigned arena_ind;
+ size_t sz;
+
+ sz = sizeof(unsigned);
+ expect_d_eq(mallctl("arenas.create", (void *)&arena_ind, &sz, NULL, 2),
+ 0, "Unexpected mallctl() failure");
+ return arena_ind;
+}
+
+static size_t
+get_empty_ndirty(unsigned arena_ind) {
+ int err;
+ size_t ndirty_huge;
+ size_t ndirty_nonhuge;
+ uint64_t epoch = 1;
+ size_t sz = sizeof(epoch);
+ err = je_mallctl("epoch", (void *)&epoch, &sz, (void *)&epoch,
+ sizeof(epoch));
+ expect_d_eq(0, err, "Unexpected mallctl() failure");
+
+ size_t mib[6];
+ size_t miblen = sizeof(mib)/sizeof(mib[0]);
+ err = mallctlnametomib(
+ "stats.arenas.0.hpa_shard.empty_slabs.ndirty_nonhuge", mib,
+ &miblen);
+ expect_d_eq(0, err, "Unexpected mallctlnametomib() failure");
+
+ sz = sizeof(ndirty_nonhuge);
+ mib[2] = arena_ind;
+ err = mallctlbymib(mib, miblen, &ndirty_nonhuge, &sz, NULL, 0);
+ expect_d_eq(0, err, "Unexpected mallctlbymib() failure");
+
+ err = mallctlnametomib(
+ "stats.arenas.0.hpa_shard.empty_slabs.ndirty_huge", mib,
+ &miblen);
+ expect_d_eq(0, err, "Unexpected mallctlnametomib() failure");
+
+ sz = sizeof(ndirty_huge);
+ mib[2] = arena_ind;
+ err = mallctlbymib(mib, miblen, &ndirty_huge, &sz, NULL, 0);
+ expect_d_eq(0, err, "Unexpected mallctlbymib() failure");
+
+ return ndirty_huge + ndirty_nonhuge;
+}
+
+static void
+set_background_thread_enabled(bool enabled) {
+ int err;
+ err = je_mallctl("background_thread", NULL, NULL, &enabled,
+ sizeof(enabled));
+ expect_d_eq(0, err, "Unexpected mallctl failure");
+}
+
+static void
+wait_until_thread_is_enabled(unsigned arena_id) {
+ tsd_t* tsd = tsd_fetch();
+
+ bool sleeping = false;
+ int iterations = 0;
+ do {
+ background_thread_info_t *info =
+ background_thread_info_get(arena_id);
+ malloc_mutex_lock(tsd_tsdn(tsd), &info->mtx);
+ malloc_mutex_unlock(tsd_tsdn(tsd), &info->mtx);
+ sleeping = background_thread_indefinite_sleep(info);
+ assert_d_lt(iterations, UINT64_C(1000000),
+ "Waiting for a thread to start for too long");
+ } while (!sleeping);
+}
+
+static void
+expect_purging(unsigned arena_ind, bool expect_deferred) {
+ size_t empty_ndirty;
+
+ empty_ndirty = get_empty_ndirty(arena_ind);
+ expect_zu_eq(0, empty_ndirty, "Expected arena to start unused.");
+
+ /*
+ * It's possible that we get unlucky with our stats collection timing,
+ * and the background thread runs in between the deallocation and the
+ * stats collection. So we retry 10 times, and see if we *ever* see
+ * deferred reclamation.
+ */
+ bool observed_dirty_page = false;
+ for (int i = 0; i < 10; i++) {
+ void *ptr = mallocx(PAGE,
+ MALLOCX_TCACHE_NONE | MALLOCX_ARENA(arena_ind));
+ empty_ndirty = get_empty_ndirty(arena_ind);
+ expect_zu_eq(0, empty_ndirty, "All pages should be active");
+ dallocx(ptr, MALLOCX_TCACHE_NONE);
+ empty_ndirty = get_empty_ndirty(arena_ind);
+ if (expect_deferred) {
+ expect_true(empty_ndirty == 0 || empty_ndirty == 1 ||
+ opt_prof, "Unexpected extra dirty page count: %zu",
+ empty_ndirty);
+ } else {
+ assert_zu_eq(0, empty_ndirty,
+ "Saw dirty pages without deferred purging");
+ }
+ if (empty_ndirty > 0) {
+ observed_dirty_page = true;
+ break;
+ }
+ }
+ expect_b_eq(expect_deferred, observed_dirty_page, "");
+
+ /*
+ * Under high concurrency / heavy test load (e.g. using run_test.sh),
+ * the background thread may not get scheduled for a longer period of
+ * time. Retry 100 times max before bailing out.
+ */
+ unsigned retry = 0;
+ while ((empty_ndirty = get_empty_ndirty(arena_ind)) > 0 &&
+ expect_deferred && (retry++ < 100)) {
+ sleep_for_background_thread_interval();
+ }
+
+ expect_zu_eq(0, empty_ndirty, "Should have seen a background purge");
+}
+
+TEST_BEGIN(test_hpa_background_thread_purges) {
+ test_skip_if(!config_stats);
+ test_skip_if(!hpa_supported());
+ test_skip_if(!have_background_thread);
+ /* Skip since guarded pages cannot be allocated from hpa. */
+ test_skip_if(san_guard_enabled());
+
+ unsigned arena_ind = create_arena();
+ /*
+ * Our .sh sets dirty mult to 0, so all dirty pages should get purged
+ * any time any thread frees.
+ */
+ expect_purging(arena_ind, /* expect_deferred */ true);
+}
+TEST_END
+
+TEST_BEGIN(test_hpa_background_thread_enable_disable) {
+ test_skip_if(!config_stats);
+ test_skip_if(!hpa_supported());
+ test_skip_if(!have_background_thread);
+ /* Skip since guarded pages cannot be allocated from hpa. */
+ test_skip_if(san_guard_enabled());
+
+ unsigned arena_ind = create_arena();
+
+ set_background_thread_enabled(false);
+ expect_purging(arena_ind, false);
+
+ set_background_thread_enabled(true);
+ wait_until_thread_is_enabled(arena_ind);
+ expect_purging(arena_ind, true);
+}
+TEST_END
+
+int
+main(void) {
+ /*
+ * OK, this is a sort of nasty hack. We don't want to add *another*
+ * config option for HPA (the intent is that it becomes available on
+ * more platforms over time, and we're trying to prune back config
+ * options generally. But we'll get initialization errors on other
+ * platforms if we set hpa:true in the MALLOC_CONF (even if we set
+ * abort_conf:false as well). So we reach into the internals and set
+ * them directly, but only if we know that we're actually going to do
+ * something nontrivial in the tests.
+ */
+ if (config_stats && hpa_supported() && have_background_thread) {
+ opt_hpa = true;
+ opt_background_thread = true;
+ }
+ return test_no_reentrancy(
+ test_hpa_background_thread_purges,
+ test_hpa_background_thread_enable_disable);
+}
diff --git a/deps/jemalloc/test/unit/hpa_background_thread.sh b/deps/jemalloc/test/unit/hpa_background_thread.sh
new file mode 100644
index 000000000..65a56a089
--- /dev/null
+++ b/deps/jemalloc/test/unit/hpa_background_thread.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+export MALLOC_CONF="hpa_dirty_mult:0,hpa_min_purge_interval_ms:50,hpa_sec_nshards:0"
+
diff --git a/deps/jemalloc/test/unit/hpdata.c b/deps/jemalloc/test/unit/hpdata.c
new file mode 100644
index 000000000..288e71d45
--- /dev/null
+++ b/deps/jemalloc/test/unit/hpdata.c
@@ -0,0 +1,244 @@
+#include "test/jemalloc_test.h"
+
+#define HPDATA_ADDR ((void *)(10 * HUGEPAGE))
+#define HPDATA_AGE 123
+
+TEST_BEGIN(test_reserve_alloc) {
+ hpdata_t hpdata;
+ hpdata_init(&hpdata, HPDATA_ADDR, HPDATA_AGE);
+
+ /* Allocating a page at a time, we should do first fit. */
+ for (size_t i = 0; i < HUGEPAGE_PAGES; i++) {
+ expect_true(hpdata_consistent(&hpdata), "");
+ expect_zu_eq(HUGEPAGE_PAGES - i,
+ hpdata_longest_free_range_get(&hpdata), "");
+ void *alloc = hpdata_reserve_alloc(&hpdata, PAGE);
+ expect_ptr_eq((char *)HPDATA_ADDR + i * PAGE, alloc, "");
+ expect_true(hpdata_consistent(&hpdata), "");
+ }
+ expect_true(hpdata_consistent(&hpdata), "");
+ expect_zu_eq(0, hpdata_longest_free_range_get(&hpdata), "");
+
+ /*
+ * Build up a bigger free-range, 2 pages at a time, until we've got 6
+ * adjacent free pages total. Pages 8-13 should be unreserved after
+ * this.
+ */
+ hpdata_unreserve(&hpdata, (char *)HPDATA_ADDR + 10 * PAGE, 2 * PAGE);
+ expect_true(hpdata_consistent(&hpdata), "");
+ expect_zu_eq(2, hpdata_longest_free_range_get(&hpdata), "");
+
+ hpdata_unreserve(&hpdata, (char *)HPDATA_ADDR + 12 * PAGE, 2 * PAGE);
+ expect_true(hpdata_consistent(&hpdata), "");
+ expect_zu_eq(4, hpdata_longest_free_range_get(&hpdata), "");
+
+ hpdata_unreserve(&hpdata, (char *)HPDATA_ADDR + 8 * PAGE, 2 * PAGE);
+ expect_true(hpdata_consistent(&hpdata), "");
+ expect_zu_eq(6, hpdata_longest_free_range_get(&hpdata), "");
+
+ /*
+ * Leave page 14 reserved, but free page 15 (this test the case where
+ * unreserving combines two ranges).
+ */
+ hpdata_unreserve(&hpdata, (char *)HPDATA_ADDR + 15 * PAGE, PAGE);
+ /*
+ * Longest free range shouldn't change; we've got a free range of size
+ * 6, then a reserved page, then another free range.
+ */
+ expect_true(hpdata_consistent(&hpdata), "");
+ expect_zu_eq(6, hpdata_longest_free_range_get(&hpdata), "");
+
+ /* After freeing page 14, the two ranges get combined. */
+ hpdata_unreserve(&hpdata, (char *)HPDATA_ADDR + 14 * PAGE, PAGE);
+ expect_true(hpdata_consistent(&hpdata), "");
+ expect_zu_eq(8, hpdata_longest_free_range_get(&hpdata), "");
+}
+TEST_END
+
+TEST_BEGIN(test_purge_simple) {
+ hpdata_t hpdata;
+ hpdata_init(&hpdata, HPDATA_ADDR, HPDATA_AGE);
+
+ void *alloc = hpdata_reserve_alloc(&hpdata, HUGEPAGE_PAGES / 2 * PAGE);
+ expect_ptr_eq(alloc, HPDATA_ADDR, "");
+
+ /* Create HUGEPAGE_PAGES / 4 dirty inactive pages at the beginning. */
+ hpdata_unreserve(&hpdata, alloc, HUGEPAGE_PAGES / 4 * PAGE);
+
+ expect_zu_eq(hpdata_ntouched_get(&hpdata), HUGEPAGE_PAGES / 2, "");
+
+ hpdata_alloc_allowed_set(&hpdata, false);
+ hpdata_purge_state_t purge_state;
+ size_t to_purge = hpdata_purge_begin(&hpdata, &purge_state);
+ expect_zu_eq(HUGEPAGE_PAGES / 4, to_purge, "");
+
+ void *purge_addr;
+ size_t purge_size;
+ bool got_result = hpdata_purge_next(&hpdata, &purge_state, &purge_addr,
+ &purge_size);
+ expect_true(got_result, "");
+ expect_ptr_eq(HPDATA_ADDR, purge_addr, "");
+ expect_zu_eq(HUGEPAGE_PAGES / 4 * PAGE, purge_size, "");
+
+ got_result = hpdata_purge_next(&hpdata, &purge_state, &purge_addr,
+ &purge_size);
+ expect_false(got_result, "Unexpected additional purge range: "
+ "extent at %p of size %zu", purge_addr, purge_size);
+
+ hpdata_purge_end(&hpdata, &purge_state);
+ expect_zu_eq(hpdata_ntouched_get(&hpdata), HUGEPAGE_PAGES / 4, "");
+}
+TEST_END
+
+/*
+ * We only test intervening dalloc's not intervening allocs; the latter are
+ * disallowed as a purging precondition (because they interfere with purging
+ * across a retained extent, saving a purge call).
+ */
+TEST_BEGIN(test_purge_intervening_dalloc) {
+ hpdata_t hpdata;
+ hpdata_init(&hpdata, HPDATA_ADDR, HPDATA_AGE);
+
+ /* Allocate the first 3/4 of the pages. */
+ void *alloc = hpdata_reserve_alloc(&hpdata, 3 * HUGEPAGE_PAGES / 4 * PAGE);
+ expect_ptr_eq(alloc, HPDATA_ADDR, "");
+
+ /* Free the first 1/4 and the third 1/4 of the pages. */
+ hpdata_unreserve(&hpdata, alloc, HUGEPAGE_PAGES / 4 * PAGE);
+ hpdata_unreserve(&hpdata,
+ (void *)((uintptr_t)alloc + 2 * HUGEPAGE_PAGES / 4 * PAGE),
+ HUGEPAGE_PAGES / 4 * PAGE);
+
+ expect_zu_eq(hpdata_ntouched_get(&hpdata), 3 * HUGEPAGE_PAGES / 4, "");
+
+ hpdata_alloc_allowed_set(&hpdata, false);
+ hpdata_purge_state_t purge_state;
+ size_t to_purge = hpdata_purge_begin(&hpdata, &purge_state);
+ expect_zu_eq(HUGEPAGE_PAGES / 2, to_purge, "");
+
+ void *purge_addr;
+ size_t purge_size;
+ /* First purge. */
+ bool got_result = hpdata_purge_next(&hpdata, &purge_state, &purge_addr,
+ &purge_size);
+ expect_true(got_result, "");
+ expect_ptr_eq(HPDATA_ADDR, purge_addr, "");
+ expect_zu_eq(HUGEPAGE_PAGES / 4 * PAGE, purge_size, "");
+
+ /* Deallocate the second 1/4 before the second purge occurs. */
+ hpdata_unreserve(&hpdata,
+ (void *)((uintptr_t)alloc + 1 * HUGEPAGE_PAGES / 4 * PAGE),
+ HUGEPAGE_PAGES / 4 * PAGE);
+
+ /* Now continue purging. */
+ got_result = hpdata_purge_next(&hpdata, &purge_state, &purge_addr,
+ &purge_size);
+ expect_true(got_result, "");
+ expect_ptr_eq(
+ (void *)((uintptr_t)alloc + 2 * HUGEPAGE_PAGES / 4 * PAGE),
+ purge_addr, "");
+ expect_zu_ge(HUGEPAGE_PAGES / 4 * PAGE, purge_size, "");
+
+ got_result = hpdata_purge_next(&hpdata, &purge_state, &purge_addr,
+ &purge_size);
+ expect_false(got_result, "Unexpected additional purge range: "
+ "extent at %p of size %zu", purge_addr, purge_size);
+
+ hpdata_purge_end(&hpdata, &purge_state);
+
+ expect_zu_eq(hpdata_ntouched_get(&hpdata), HUGEPAGE_PAGES / 4, "");
+}
+TEST_END
+
+TEST_BEGIN(test_purge_over_retained) {
+ void *purge_addr;
+ size_t purge_size;
+
+ hpdata_t hpdata;
+ hpdata_init(&hpdata, HPDATA_ADDR, HPDATA_AGE);
+
+ /* Allocate the first 3/4 of the pages. */
+ void *alloc = hpdata_reserve_alloc(&hpdata, 3 * HUGEPAGE_PAGES / 4 * PAGE);
+ expect_ptr_eq(alloc, HPDATA_ADDR, "");
+
+ /* Free the second quarter. */
+ void *second_quarter =
+ (void *)((uintptr_t)alloc + HUGEPAGE_PAGES / 4 * PAGE);
+ hpdata_unreserve(&hpdata, second_quarter, HUGEPAGE_PAGES / 4 * PAGE);
+
+ expect_zu_eq(hpdata_ntouched_get(&hpdata), 3 * HUGEPAGE_PAGES / 4, "");
+
+ /* Purge the second quarter. */
+ hpdata_alloc_allowed_set(&hpdata, false);
+ hpdata_purge_state_t purge_state;
+ size_t to_purge_dirty = hpdata_purge_begin(&hpdata, &purge_state);
+ expect_zu_eq(HUGEPAGE_PAGES / 4, to_purge_dirty, "");
+
+ bool got_result = hpdata_purge_next(&hpdata, &purge_state, &purge_addr,
+ &purge_size);
+ expect_true(got_result, "");
+ expect_ptr_eq(second_quarter, purge_addr, "");
+ expect_zu_eq(HUGEPAGE_PAGES / 4 * PAGE, purge_size, "");
+
+ got_result = hpdata_purge_next(&hpdata, &purge_state, &purge_addr,
+ &purge_size);
+ expect_false(got_result, "Unexpected additional purge range: "
+ "extent at %p of size %zu", purge_addr, purge_size);
+ hpdata_purge_end(&hpdata, &purge_state);
+
+ expect_zu_eq(hpdata_ntouched_get(&hpdata), HUGEPAGE_PAGES / 2, "");
+
+ /* Free the first and third quarter. */
+ hpdata_unreserve(&hpdata, HPDATA_ADDR, HUGEPAGE_PAGES / 4 * PAGE);
+ hpdata_unreserve(&hpdata,
+ (void *)((uintptr_t)alloc + 2 * HUGEPAGE_PAGES / 4 * PAGE),
+ HUGEPAGE_PAGES / 4 * PAGE);
+
+ /*
+ * Purge again. The second quarter is retained, so we can safely
+ * re-purge it. We expect a single purge of 3/4 of the hugepage,
+ * purging half its pages.
+ */
+ to_purge_dirty = hpdata_purge_begin(&hpdata, &purge_state);
+ expect_zu_eq(HUGEPAGE_PAGES / 2, to_purge_dirty, "");
+
+ got_result = hpdata_purge_next(&hpdata, &purge_state, &purge_addr,
+ &purge_size);
+ expect_true(got_result, "");
+ expect_ptr_eq(HPDATA_ADDR, purge_addr, "");
+ expect_zu_eq(3 * HUGEPAGE_PAGES / 4 * PAGE, purge_size, "");
+
+ got_result = hpdata_purge_next(&hpdata, &purge_state, &purge_addr,
+ &purge_size);
+ expect_false(got_result, "Unexpected additional purge range: "
+ "extent at %p of size %zu", purge_addr, purge_size);
+ hpdata_purge_end(&hpdata, &purge_state);
+
+ expect_zu_eq(hpdata_ntouched_get(&hpdata), 0, "");
+}
+TEST_END
+
+TEST_BEGIN(test_hugify) {
+ hpdata_t hpdata;
+ hpdata_init(&hpdata, HPDATA_ADDR, HPDATA_AGE);
+
+ void *alloc = hpdata_reserve_alloc(&hpdata, HUGEPAGE / 2);
+ expect_ptr_eq(alloc, HPDATA_ADDR, "");
+
+ expect_zu_eq(HUGEPAGE_PAGES / 2, hpdata_ntouched_get(&hpdata), "");
+
+ hpdata_hugify(&hpdata);
+
+ /* Hugeifying should have increased the dirty page count. */
+ expect_zu_eq(HUGEPAGE_PAGES, hpdata_ntouched_get(&hpdata), "");
+}
+TEST_END
+
+int main(void) {
+ return test_no_reentrancy(
+ test_reserve_alloc,
+ test_purge_simple,
+ test_purge_intervening_dalloc,
+ test_purge_over_retained,
+ test_hugify);
+}
diff --git a/deps/jemalloc/test/unit/huge.c b/deps/jemalloc/test/unit/huge.c
index ab72cf007..ec64e5002 100644
--- a/deps/jemalloc/test/unit/huge.c
+++ b/deps/jemalloc/test/unit/huge.c
@@ -11,37 +11,37 @@ TEST_BEGIN(huge_bind_thread) {
size_t sz = sizeof(unsigned);
/* Bind to a manual arena. */
- assert_d_eq(mallctl("arenas.create", &arena1, &sz, NULL, 0), 0,
+ expect_d_eq(mallctl("arenas.create", &arena1, &sz, NULL, 0), 0,
"Failed to create arena");
- assert_d_eq(mallctl("thread.arena", NULL, NULL, &arena1,
+ expect_d_eq(mallctl("thread.arena", NULL, NULL, &arena1,
sizeof(arena1)), 0, "Fail to bind thread");
void *ptr = mallocx(HUGE_SZ, 0);
- assert_ptr_not_null(ptr, "Fail to allocate huge size");
- assert_d_eq(mallctl("arenas.lookup", &arena2, &sz, &ptr,
+ expect_ptr_not_null(ptr, "Fail to allocate huge size");
+ expect_d_eq(mallctl("arenas.lookup", &arena2, &sz, &ptr,
sizeof(ptr)), 0, "Unexpected mallctl() failure");
- assert_u_eq(arena1, arena2, "Wrong arena used after binding");
+ expect_u_eq(arena1, arena2, "Wrong arena used after binding");
dallocx(ptr, 0);
/* Switch back to arena 0. */
test_skip_if(have_percpu_arena &&
PERCPU_ARENA_ENABLED(opt_percpu_arena));
arena2 = 0;
- assert_d_eq(mallctl("thread.arena", NULL, NULL, &arena2,
+ expect_d_eq(mallctl("thread.arena", NULL, NULL, &arena2,
sizeof(arena2)), 0, "Fail to bind thread");
ptr = mallocx(SMALL_SZ, MALLOCX_TCACHE_NONE);
- assert_d_eq(mallctl("arenas.lookup", &arena2, &sz, &ptr,
+ expect_d_eq(mallctl("arenas.lookup", &arena2, &sz, &ptr,
sizeof(ptr)), 0, "Unexpected mallctl() failure");
- assert_u_eq(arena2, 0, "Wrong arena used after binding");
+ expect_u_eq(arena2, 0, "Wrong arena used after binding");
dallocx(ptr, MALLOCX_TCACHE_NONE);
/* Then huge allocation should use the huge arena. */
ptr = mallocx(HUGE_SZ, 0);
- assert_ptr_not_null(ptr, "Fail to allocate huge size");
- assert_d_eq(mallctl("arenas.lookup", &arena2, &sz, &ptr,
+ expect_ptr_not_null(ptr, "Fail to allocate huge size");
+ expect_d_eq(mallctl("arenas.lookup", &arena2, &sz, &ptr,
sizeof(ptr)), 0, "Unexpected mallctl() failure");
- assert_u_ne(arena2, 0, "Wrong arena used after binding");
- assert_u_ne(arena1, arena2, "Wrong arena used after binding");
+ expect_u_ne(arena2, 0, "Wrong arena used after binding");
+ expect_u_ne(arena1, arena2, "Wrong arena used after binding");
dallocx(ptr, 0);
}
TEST_END
@@ -50,22 +50,22 @@ TEST_BEGIN(huge_mallocx) {
unsigned arena1, arena2;
size_t sz = sizeof(unsigned);
- assert_d_eq(mallctl("arenas.create", &arena1, &sz, NULL, 0), 0,
+ expect_d_eq(mallctl("arenas.create", &arena1, &sz, NULL, 0), 0,
"Failed to create arena");
void *huge = mallocx(HUGE_SZ, MALLOCX_ARENA(arena1));
- assert_ptr_not_null(huge, "Fail to allocate huge size");
- assert_d_eq(mallctl("arenas.lookup", &arena2, &sz, &huge,
+ expect_ptr_not_null(huge, "Fail to allocate huge size");
+ expect_d_eq(mallctl("arenas.lookup", &arena2, &sz, &huge,
sizeof(huge)), 0, "Unexpected mallctl() failure");
- assert_u_eq(arena1, arena2, "Wrong arena used for mallocx");
+ expect_u_eq(arena1, arena2, "Wrong arena used for mallocx");
dallocx(huge, MALLOCX_ARENA(arena1));
void *huge2 = mallocx(HUGE_SZ, 0);
- assert_ptr_not_null(huge, "Fail to allocate huge size");
- assert_d_eq(mallctl("arenas.lookup", &arena2, &sz, &huge2,
+ expect_ptr_not_null(huge, "Fail to allocate huge size");
+ expect_d_eq(mallctl("arenas.lookup", &arena2, &sz, &huge2,
sizeof(huge2)), 0, "Unexpected mallctl() failure");
- assert_u_ne(arena1, arena2,
+ expect_u_ne(arena1, arena2,
"Huge allocation should not come from the manual arena.");
- assert_u_ne(arena2, 0,
+ expect_u_ne(arena2, 0,
"Huge allocation should not come from the arena 0.");
dallocx(huge2, 0);
}
@@ -75,25 +75,25 @@ TEST_BEGIN(huge_allocation) {
unsigned arena1, arena2;
void *ptr = mallocx(HUGE_SZ, 0);
- assert_ptr_not_null(ptr, "Fail to allocate huge size");
+ expect_ptr_not_null(ptr, "Fail to allocate huge size");
size_t sz = sizeof(unsigned);
- assert_d_eq(mallctl("arenas.lookup", &arena1, &sz, &ptr, sizeof(ptr)),
+ expect_d_eq(mallctl("arenas.lookup", &arena1, &sz, &ptr, sizeof(ptr)),
0, "Unexpected mallctl() failure");
- assert_u_gt(arena1, 0, "Huge allocation should not come from arena 0");
+ expect_u_gt(arena1, 0, "Huge allocation should not come from arena 0");
dallocx(ptr, 0);
ptr = mallocx(HUGE_SZ >> 1, 0);
- assert_ptr_not_null(ptr, "Fail to allocate half huge size");
- assert_d_eq(mallctl("arenas.lookup", &arena2, &sz, &ptr,
+ expect_ptr_not_null(ptr, "Fail to allocate half huge size");
+ expect_d_eq(mallctl("arenas.lookup", &arena2, &sz, &ptr,
sizeof(ptr)), 0, "Unexpected mallctl() failure");
- assert_u_ne(arena1, arena2, "Wrong arena used for half huge");
+ expect_u_ne(arena1, arena2, "Wrong arena used for half huge");
dallocx(ptr, 0);
ptr = mallocx(SMALL_SZ, MALLOCX_TCACHE_NONE);
- assert_ptr_not_null(ptr, "Fail to allocate small size");
- assert_d_eq(mallctl("arenas.lookup", &arena2, &sz, &ptr,
+ expect_ptr_not_null(ptr, "Fail to allocate small size");
+ expect_d_eq(mallctl("arenas.lookup", &arena2, &sz, &ptr,
sizeof(ptr)), 0, "Unexpected mallctl() failure");
- assert_u_ne(arena1, arena2,
+ expect_u_ne(arena1, arena2,
"Huge and small should be from different arenas");
dallocx(ptr, 0);
}
diff --git a/deps/jemalloc/test/unit/extent_util.c b/deps/jemalloc/test/unit/inspect.c
index 97e55f0f6..fe59e5971 100644
--- a/deps/jemalloc/test/unit/extent_util.c
+++ b/deps/jemalloc/test/unit/inspect.c
@@ -18,8 +18,8 @@
assert_d_eq(mallctl("experimental.utilization." node, \
out, &out_sz, in, in_sz), 0, \
"Should return 0 on correct arguments"); \
- assert_zu_eq(out_sz, out_sz_ref, "incorrect output size"); \
- assert_d_ne(memcmp(out, out_ref, out_sz_ref), 0, \
+ expect_zu_eq(out_sz, out_sz_ref, "incorrect output size"); \
+ expect_d_ne(memcmp(out, out_ref, out_sz_ref), 0, \
"Output content should be changed"); \
} while (0)
@@ -83,62 +83,67 @@ TEST_BEGIN(test_query) {
/* Examine output for valid call */
TEST_UTIL_VALID("query");
- assert_zu_le(sz, SIZE_READ(out),
+ expect_zu_le(sz, SIZE_READ(out),
"Extent size should be at least allocation size");
- assert_zu_eq(SIZE_READ(out) & (PAGE - 1), 0,
+ expect_zu_eq(SIZE_READ(out) & (PAGE - 1), 0,
"Extent size should be a multiple of page size");
- if (sz <= SC_SMALL_MAXCLASS) {
- assert_zu_le(NFREE_READ(out), NREGS_READ(out),
+
+ /*
+ * We don't do much bin checking if prof is on, since profiling
+ * can produce extents that are for small size classes but not
+ * slabs, which interferes with things like region counts.
+ */
+ if (!opt_prof && sz <= SC_SMALL_MAXCLASS) {
+ expect_zu_le(NFREE_READ(out), NREGS_READ(out),
"Extent free count exceeded region count");
- assert_zu_le(NREGS_READ(out), SIZE_READ(out),
+ expect_zu_le(NREGS_READ(out), SIZE_READ(out),
"Extent region count exceeded size");
- assert_zu_ne(NREGS_READ(out), 0,
+ expect_zu_ne(NREGS_READ(out), 0,
"Extent region count must be positive");
- assert_ptr_not_null(SLABCUR_READ(out),
- "Current slab is null");
- assert_true(NFREE_READ(out) == 0
- || SLABCUR_READ(out) <= p,
+ expect_true(NFREE_READ(out) == 0 || (SLABCUR_READ(out)
+ != NULL && SLABCUR_READ(out) <= p),
"Allocation should follow first fit principle");
+
if (config_stats) {
- assert_zu_le(BIN_NFREE_READ(out),
+ expect_zu_le(BIN_NFREE_READ(out),
BIN_NREGS_READ(out),
"Bin free count exceeded region count");
- assert_zu_ne(BIN_NREGS_READ(out), 0,
+ expect_zu_ne(BIN_NREGS_READ(out), 0,
"Bin region count must be positive");
- assert_zu_le(NFREE_READ(out),
+ expect_zu_le(NFREE_READ(out),
BIN_NFREE_READ(out),
"Extent free count exceeded bin free count");
- assert_zu_le(NREGS_READ(out),
+ expect_zu_le(NREGS_READ(out),
BIN_NREGS_READ(out),
"Extent region count exceeded "
"bin region count");
- assert_zu_eq(BIN_NREGS_READ(out)
+ expect_zu_eq(BIN_NREGS_READ(out)
% NREGS_READ(out), 0,
"Bin region count isn't a multiple of "
"extent region count");
- assert_zu_le(
+ expect_zu_le(
BIN_NFREE_READ(out) - NFREE_READ(out),
BIN_NREGS_READ(out) - NREGS_READ(out),
"Free count in other extents in the bin "
"exceeded region count in other extents "
"in the bin");
- assert_zu_le(NREGS_READ(out) - NFREE_READ(out),
+ expect_zu_le(NREGS_READ(out) - NFREE_READ(out),
BIN_NREGS_READ(out) - BIN_NFREE_READ(out),
"Extent utilized count exceeded "
"bin utilized count");
}
- } else {
- assert_zu_eq(NFREE_READ(out), 0,
+ } else if (sz > SC_SMALL_MAXCLASS) {
+ expect_zu_eq(NFREE_READ(out), 0,
"Extent free count should be zero");
- assert_zu_eq(NREGS_READ(out), 1,
+ expect_zu_eq(NREGS_READ(out), 1,
"Extent region count should be one");
- assert_ptr_null(SLABCUR_READ(out),
+ expect_ptr_null(SLABCUR_READ(out),
"Current slab must be null for large size classes");
if (config_stats) {
- assert_zu_eq(BIN_NFREE_READ(out), 0,
+ expect_zu_eq(BIN_NFREE_READ(out), 0,
"Bin free count must be zero for "
"large sizes");
- assert_zu_eq(BIN_NREGS_READ(out), 0,
+ expect_zu_eq(BIN_NREGS_READ(out), 0,
"Bin region count must be zero for "
"large sizes");
}
@@ -212,21 +217,25 @@ TEST_BEGIN(test_batch) {
out_sz_ref = out_sz /= 2;
in_sz /= 2;
TEST_UTIL_BATCH_VALID;
- assert_zu_le(sz, SIZE_READ(out, 0),
+ expect_zu_le(sz, SIZE_READ(out, 0),
"Extent size should be at least allocation size");
- assert_zu_eq(SIZE_READ(out, 0) & (PAGE - 1), 0,
+ expect_zu_eq(SIZE_READ(out, 0) & (PAGE - 1), 0,
"Extent size should be a multiple of page size");
- if (sz <= SC_SMALL_MAXCLASS) {
- assert_zu_le(NFREE_READ(out, 0), NREGS_READ(out, 0),
+ /*
+ * See the corresponding comment in test_query; profiling breaks
+ * our slab count expectations.
+ */
+ if (sz <= SC_SMALL_MAXCLASS && !opt_prof) {
+ expect_zu_le(NFREE_READ(out, 0), NREGS_READ(out, 0),
"Extent free count exceeded region count");
- assert_zu_le(NREGS_READ(out, 0), SIZE_READ(out, 0),
+ expect_zu_le(NREGS_READ(out, 0), SIZE_READ(out, 0),
"Extent region count exceeded size");
- assert_zu_ne(NREGS_READ(out, 0), 0,
+ expect_zu_ne(NREGS_READ(out, 0), 0,
"Extent region count must be positive");
- } else {
- assert_zu_eq(NFREE_READ(out, 0), 0,
+ } else if (sz > SC_SMALL_MAXCLASS) {
+ expect_zu_eq(NFREE_READ(out, 0), 0,
"Extent free count should be zero");
- assert_zu_eq(NREGS_READ(out, 0), 1,
+ expect_zu_eq(NREGS_READ(out, 0), 1,
"Extent region count should be one");
}
TEST_EQUAL_REF(1,
@@ -238,15 +247,15 @@ TEST_BEGIN(test_batch) {
TEST_UTIL_BATCH_VALID;
TEST_EQUAL_REF(0, "Statistics should be stable across calls");
if (sz <= SC_SMALL_MAXCLASS) {
- assert_zu_le(NFREE_READ(out, 1), NREGS_READ(out, 1),
+ expect_zu_le(NFREE_READ(out, 1), NREGS_READ(out, 1),
"Extent free count exceeded region count");
} else {
- assert_zu_eq(NFREE_READ(out, 0), 0,
+ expect_zu_eq(NFREE_READ(out, 0), 0,
"Extent free count should be zero");
}
- assert_zu_eq(NREGS_READ(out, 0), NREGS_READ(out, 1),
+ expect_zu_eq(NREGS_READ(out, 0), NREGS_READ(out, 1),
"Extent region count should be same for same region size");
- assert_zu_eq(SIZE_READ(out, 0), SIZE_READ(out, 1),
+ expect_zu_eq(SIZE_READ(out, 0), SIZE_READ(out, 1),
"Extent size should be same for same region size");
#undef SIZE_READ
@@ -263,7 +272,7 @@ TEST_END
int
main(void) {
- assert_zu_lt(SC_SMALL_MAXCLASS, TEST_MAX_SIZE,
+ assert_zu_lt(SC_SMALL_MAXCLASS + 100000, TEST_MAX_SIZE,
"Test case cannot cover large classes");
return test(test_query, test_batch);
}
diff --git a/deps/jemalloc/test/unit/inspect.sh b/deps/jemalloc/test/unit/inspect.sh
new file mode 100644
index 000000000..352d11076
--- /dev/null
+++ b/deps/jemalloc/test/unit/inspect.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+if [ "x${enable_prof}" = "x1" ] ; then
+ export MALLOC_CONF="prof:false"
+fi
diff --git a/deps/jemalloc/test/unit/junk.c b/deps/jemalloc/test/unit/junk.c
index 57e3ad431..543092f1d 100644
--- a/deps/jemalloc/test/unit/junk.c
+++ b/deps/jemalloc/test/unit/junk.c
@@ -1,141 +1,195 @@
#include "test/jemalloc_test.h"
-#include "jemalloc/internal/util.h"
-
-static arena_dalloc_junk_small_t *arena_dalloc_junk_small_orig;
-static large_dalloc_junk_t *large_dalloc_junk_orig;
-static large_dalloc_maybe_junk_t *large_dalloc_maybe_junk_orig;
-static void *watch_for_junking;
-static bool saw_junking;
+#define arraylen(arr) (sizeof(arr)/sizeof(arr[0]))
+static size_t ptr_ind;
+static void *volatile ptrs[100];
+static void *last_junked_ptr;
+static size_t last_junked_usize;
static void
-watch_junking(void *p) {
- watch_for_junking = p;
- saw_junking = false;
+reset() {
+ ptr_ind = 0;
+ last_junked_ptr = NULL;
+ last_junked_usize = 0;
}
static void
-arena_dalloc_junk_small_intercept(void *ptr, const bin_info_t *bin_info) {
- size_t i;
-
- arena_dalloc_junk_small_orig(ptr, bin_info);
- for (i = 0; i < bin_info->reg_size; i++) {
- assert_u_eq(((uint8_t *)ptr)[i], JEMALLOC_FREE_JUNK,
- "Missing junk fill for byte %zu/%zu of deallocated region",
- i, bin_info->reg_size);
- }
- if (ptr == watch_for_junking) {
- saw_junking = true;
- }
+test_junk(void *ptr, size_t usize) {
+ last_junked_ptr = ptr;
+ last_junked_usize = usize;
}
static void
-large_dalloc_junk_intercept(void *ptr, size_t usize) {
- size_t i;
-
- large_dalloc_junk_orig(ptr, usize);
- for (i = 0; i < usize; i++) {
- assert_u_eq(((uint8_t *)ptr)[i], JEMALLOC_FREE_JUNK,
- "Missing junk fill for byte %zu/%zu of deallocated region",
- i, usize);
+do_allocs(size_t size, bool zero, size_t lg_align) {
+#define JUNK_ALLOC(...) \
+ do { \
+ assert(ptr_ind + 1 < arraylen(ptrs)); \
+ void *ptr = __VA_ARGS__; \
+ assert_ptr_not_null(ptr, ""); \
+ ptrs[ptr_ind++] = ptr; \
+ if (opt_junk_alloc && !zero) { \
+ expect_ptr_eq(ptr, last_junked_ptr, ""); \
+ expect_zu_eq(last_junked_usize, \
+ TEST_MALLOC_SIZE(ptr), ""); \
+ } \
+ } while (0)
+ if (!zero && lg_align == 0) {
+ JUNK_ALLOC(malloc(size));
}
- if (ptr == watch_for_junking) {
- saw_junking = true;
+ if (!zero) {
+ JUNK_ALLOC(aligned_alloc(1 << lg_align, size));
}
-}
-
-static void
-large_dalloc_maybe_junk_intercept(void *ptr, size_t usize) {
- large_dalloc_maybe_junk_orig(ptr, usize);
- if (ptr == watch_for_junking) {
- saw_junking = true;
+#ifdef JEMALLOC_OVERRIDE_MEMALIGN
+ if (!zero) {
+ JUNK_ALLOC(je_memalign(1 << lg_align, size));
}
-}
-
-static void
-test_junk(size_t sz_min, size_t sz_max) {
- uint8_t *s;
- size_t sz_prev, sz, i;
-
- if (opt_junk_free) {
- arena_dalloc_junk_small_orig = arena_dalloc_junk_small;
- arena_dalloc_junk_small = arena_dalloc_junk_small_intercept;
- large_dalloc_junk_orig = large_dalloc_junk;
- large_dalloc_junk = large_dalloc_junk_intercept;
- large_dalloc_maybe_junk_orig = large_dalloc_maybe_junk;
- large_dalloc_maybe_junk = large_dalloc_maybe_junk_intercept;
+#endif
+#ifdef JEMALLOC_OVERRIDE_VALLOC
+ if (!zero && lg_align == LG_PAGE) {
+ JUNK_ALLOC(je_valloc(size));
}
+#endif
+ int zero_flag = zero ? MALLOCX_ZERO : 0;
+ JUNK_ALLOC(mallocx(size, zero_flag | MALLOCX_LG_ALIGN(lg_align)));
+ JUNK_ALLOC(mallocx(size, zero_flag | MALLOCX_LG_ALIGN(lg_align)
+ | MALLOCX_TCACHE_NONE));
+ if (lg_align >= LG_SIZEOF_PTR) {
+ void *memalign_result;
+ int err = posix_memalign(&memalign_result, (1 << lg_align),
+ size);
+ assert_d_eq(err, 0, "");
+ JUNK_ALLOC(memalign_result);
+ }
+}
- sz_prev = 0;
- s = (uint8_t *)mallocx(sz_min, 0);
- assert_ptr_not_null((void *)s, "Unexpected mallocx() failure");
-
- for (sz = sallocx(s, 0); sz <= sz_max;
- sz_prev = sz, sz = sallocx(s, 0)) {
- if (sz_prev > 0) {
- assert_u_eq(s[0], 'a',
- "Previously allocated byte %zu/%zu is corrupted",
- ZU(0), sz_prev);
- assert_u_eq(s[sz_prev-1], 'a',
- "Previously allocated byte %zu/%zu is corrupted",
- sz_prev-1, sz_prev);
- }
-
- for (i = sz_prev; i < sz; i++) {
- if (opt_junk_alloc) {
- assert_u_eq(s[i], JEMALLOC_ALLOC_JUNK,
- "Newly allocated byte %zu/%zu isn't "
- "junk-filled", i, sz);
- }
- s[i] = 'a';
- }
-
- if (xallocx(s, sz+1, 0, 0) == sz) {
- uint8_t *t;
- watch_junking(s);
- t = (uint8_t *)rallocx(s, sz+1, 0);
- assert_ptr_not_null((void *)t,
- "Unexpected rallocx() failure");
- assert_zu_ge(sallocx(t, 0), sz+1,
- "Unexpectedly small rallocx() result");
- if (!background_thread_enabled()) {
- assert_ptr_ne(s, t,
- "Unexpected in-place rallocx()");
- assert_true(!opt_junk_free || saw_junking,
- "Expected region of size %zu to be "
- "junk-filled", sz);
+TEST_BEGIN(test_junk_alloc_free) {
+ bool zerovals[] = {false, true};
+ size_t sizevals[] = {
+ 1, 8, 100, 1000, 100*1000
+ /*
+ * Memory allocation failure is a real possibility in 32-bit mode.
+ * Rather than try to check in the face of resource exhaustion, we just
+ * rely more on the 64-bit tests. This is a little bit white-box-y in
+ * the sense that this is only a good test strategy if we know that the
+ * junk pathways don't touch interact with the allocation selection
+ * mechanisms; but this is in fact the case.
+ */
+#if LG_SIZEOF_PTR == 3
+ , 10 * 1000 * 1000
+#endif
+ };
+ size_t lg_alignvals[] = {
+ 0, 4, 10, 15, 16, LG_PAGE
+#if LG_SIZEOF_PTR == 3
+ , 20, 24
+#endif
+ };
+
+#define JUNK_FREE(...) \
+ do { \
+ do_allocs(size, zero, lg_align); \
+ for (size_t n = 0; n < ptr_ind; n++) { \
+ void *ptr = ptrs[n]; \
+ __VA_ARGS__; \
+ if (opt_junk_free) { \
+ assert_ptr_eq(ptr, last_junked_ptr, \
+ ""); \
+ assert_zu_eq(usize, last_junked_usize, \
+ ""); \
+ } \
+ reset(); \
+ } \
+ } while (0)
+ for (size_t i = 0; i < arraylen(zerovals); i++) {
+ for (size_t j = 0; j < arraylen(sizevals); j++) {
+ for (size_t k = 0; k < arraylen(lg_alignvals); k++) {
+ bool zero = zerovals[i];
+ size_t size = sizevals[j];
+ size_t lg_align = lg_alignvals[k];
+ size_t usize = nallocx(size,
+ MALLOCX_LG_ALIGN(lg_align));
+
+ JUNK_FREE(free(ptr));
+ JUNK_FREE(dallocx(ptr, 0));
+ JUNK_FREE(dallocx(ptr, MALLOCX_TCACHE_NONE));
+ JUNK_FREE(dallocx(ptr, MALLOCX_LG_ALIGN(
+ lg_align)));
+ JUNK_FREE(sdallocx(ptr, usize, MALLOCX_LG_ALIGN(
+ lg_align)));
+ JUNK_FREE(sdallocx(ptr, usize,
+ MALLOCX_TCACHE_NONE | MALLOCX_LG_ALIGN(lg_align)));
+ if (opt_zero_realloc_action
+ == zero_realloc_action_free) {
+ JUNK_FREE(realloc(ptr, 0));
+ }
}
- s = t;
}
}
-
- watch_junking(s);
- dallocx(s, 0);
- assert_true(!opt_junk_free || saw_junking,
- "Expected region of size %zu to be junk-filled", sz);
-
- if (opt_junk_free) {
- arena_dalloc_junk_small = arena_dalloc_junk_small_orig;
- large_dalloc_junk = large_dalloc_junk_orig;
- large_dalloc_maybe_junk = large_dalloc_maybe_junk_orig;
- }
-}
-
-TEST_BEGIN(test_junk_small) {
- test_skip_if(!config_fill);
- test_junk(1, SC_SMALL_MAXCLASS - 1);
}
TEST_END
-TEST_BEGIN(test_junk_large) {
- test_skip_if(!config_fill);
- test_junk(SC_SMALL_MAXCLASS + 1, (1U << (SC_LG_LARGE_MINCLASS + 1)));
+TEST_BEGIN(test_realloc_expand) {
+ char *volatile ptr;
+ char *volatile expanded;
+
+ test_skip_if(!opt_junk_alloc);
+
+ /* Realloc */
+ ptr = malloc(SC_SMALL_MAXCLASS);
+ expanded = realloc(ptr, SC_LARGE_MINCLASS);
+ expect_ptr_eq(last_junked_ptr, &expanded[SC_SMALL_MAXCLASS], "");
+ expect_zu_eq(last_junked_usize,
+ SC_LARGE_MINCLASS - SC_SMALL_MAXCLASS, "");
+ free(expanded);
+
+ /* rallocx(..., 0) */
+ ptr = malloc(SC_SMALL_MAXCLASS);
+ expanded = rallocx(ptr, SC_LARGE_MINCLASS, 0);
+ expect_ptr_eq(last_junked_ptr, &expanded[SC_SMALL_MAXCLASS], "");
+ expect_zu_eq(last_junked_usize,
+ SC_LARGE_MINCLASS - SC_SMALL_MAXCLASS, "");
+ free(expanded);
+
+ /* rallocx(..., nonzero) */
+ ptr = malloc(SC_SMALL_MAXCLASS);
+ expanded = rallocx(ptr, SC_LARGE_MINCLASS, MALLOCX_TCACHE_NONE);
+ expect_ptr_eq(last_junked_ptr, &expanded[SC_SMALL_MAXCLASS], "");
+ expect_zu_eq(last_junked_usize,
+ SC_LARGE_MINCLASS - SC_SMALL_MAXCLASS, "");
+ free(expanded);
+
+ /* rallocx(..., MALLOCX_ZERO) */
+ ptr = malloc(SC_SMALL_MAXCLASS);
+ last_junked_ptr = (void *)-1;
+ last_junked_usize = (size_t)-1;
+ expanded = rallocx(ptr, SC_LARGE_MINCLASS, MALLOCX_ZERO);
+ expect_ptr_eq(last_junked_ptr, (void *)-1, "");
+ expect_zu_eq(last_junked_usize, (size_t)-1, "");
+ free(expanded);
+
+ /*
+ * Unfortunately, testing xallocx reliably is difficult to do portably
+ * (since allocations can be expanded / not expanded differently on
+ * different platforms. We rely on manual inspection there -- the
+ * xallocx pathway is easy to inspect, though.
+ *
+ * Likewise, we don't test the shrinking pathways. It's difficult to do
+ * so consistently (because of the risk of split failure or memory
+ * exhaustion, in which case no junking should happen). This is fine
+ * -- junking is a best-effort debug mechanism in the first place.
+ */
}
TEST_END
int
main(void) {
- return test(
- test_junk_small,
- test_junk_large);
+ junk_alloc_callback = &test_junk;
+ junk_free_callback = &test_junk;
+ /*
+ * We check the last pointer junked. If a reentrant call happens, that
+ * might be an internal allocation.
+ */
+ return test_no_reentrancy(
+ test_junk_alloc_free,
+ test_realloc_expand);
}
diff --git a/deps/jemalloc/test/unit/log.c b/deps/jemalloc/test/unit/log.c
index a52bd737d..c09b58969 100644
--- a/deps/jemalloc/test/unit/log.c
+++ b/deps/jemalloc/test/unit/log.c
@@ -3,12 +3,17 @@
#include "jemalloc/internal/log.h"
static void
+update_log_var_names(const char *names) {
+ strncpy(log_var_names, names, sizeof(log_var_names));
+}
+
+static void
expect_no_logging(const char *names) {
log_var_t log_l1 = LOG_VAR_INIT("l1");
log_var_t log_l2 = LOG_VAR_INIT("l2");
log_var_t log_l2_a = LOG_VAR_INIT("l2.a");
- strcpy(log_var_names, names);
+ update_log_var_names(names);
int count = 0;
@@ -25,7 +30,7 @@ expect_no_logging(const char *names) {
count++;
log_do_end(log_l2_a)
}
- assert_d_eq(count, 0, "Disabled logging not ignored!");
+ expect_d_eq(count, 0, "Disabled logging not ignored!");
}
TEST_BEGIN(test_log_disabled) {
@@ -50,25 +55,25 @@ TEST_BEGIN(test_log_enabled_direct) {
int count;
count = 0;
- strcpy(log_var_names, "l1");
+ update_log_var_names("l1");
for (int i = 0; i < 10; i++) {
log_do_begin(log_l1)
count++;
log_do_end(log_l1)
}
- assert_d_eq(count, 10, "Mis-logged!");
+ expect_d_eq(count, 10, "Mis-logged!");
count = 0;
- strcpy(log_var_names, "l1.a");
+ update_log_var_names("l1.a");
for (int i = 0; i < 10; i++) {
log_do_begin(log_l1_a)
count++;
log_do_end(log_l1_a)
}
- assert_d_eq(count, 10, "Mis-logged!");
+ expect_d_eq(count, 10, "Mis-logged!");
count = 0;
- strcpy(log_var_names, "l1.a|abc|l2|def");
+ update_log_var_names("l1.a|abc|l2|def");
for (int i = 0; i < 10; i++) {
log_do_begin(log_l1_a)
count++;
@@ -78,14 +83,14 @@ TEST_BEGIN(test_log_enabled_direct) {
count++;
log_do_end(log_l2)
}
- assert_d_eq(count, 20, "Mis-logged!");
+ expect_d_eq(count, 20, "Mis-logged!");
}
TEST_END
TEST_BEGIN(test_log_enabled_indirect) {
test_skip_if(!config_log);
atomic_store_b(&log_init_done, true, ATOMIC_RELAXED);
- strcpy(log_var_names, "l0|l1|abc|l2.b|def");
+ update_log_var_names("l0|l1|abc|l2.b|def");
/* On. */
log_var_t log_l1 = LOG_VAR_INIT("l1");
@@ -128,14 +133,14 @@ TEST_BEGIN(test_log_enabled_indirect) {
log_do_end(log_l2_b_b)
}
- assert_d_eq(count, 40, "Mis-logged!");
+ expect_d_eq(count, 40, "Mis-logged!");
}
TEST_END
TEST_BEGIN(test_log_enabled_global) {
test_skip_if(!config_log);
atomic_store_b(&log_init_done, true, ATOMIC_RELAXED);
- strcpy(log_var_names, "abc|.|def");
+ update_log_var_names("abc|.|def");
log_var_t log_l1 = LOG_VAR_INIT("l1");
log_var_t log_l2_a_a = LOG_VAR_INIT("l2.a.a");
@@ -150,7 +155,7 @@ TEST_BEGIN(test_log_enabled_global) {
count++;
log_do_end(log_l2_a_a)
}
- assert_d_eq(count, 20, "Mis-logged!");
+ expect_d_eq(count, 20, "Mis-logged!");
}
TEST_END
@@ -166,7 +171,7 @@ TEST_BEGIN(test_logs_if_no_init) {
count++;
log_do_end(l)
}
- assert_d_eq(count, 0, "Logging shouldn't happen if not initialized.");
+ expect_d_eq(count, 0, "Logging shouldn't happen if not initialized.");
}
TEST_END
diff --git a/deps/jemalloc/test/unit/mallctl.c b/deps/jemalloc/test/unit/mallctl.c
index 3a75ac040..6efc8f1b7 100644
--- a/deps/jemalloc/test/unit/mallctl.c
+++ b/deps/jemalloc/test/unit/mallctl.c
@@ -1,5 +1,6 @@
#include "test/jemalloc_test.h"
+#include "jemalloc/internal/ctl.h"
#include "jemalloc/internal/hook.h"
#include "jemalloc/internal/util.h"
@@ -7,25 +8,25 @@ TEST_BEGIN(test_mallctl_errors) {
uint64_t epoch;
size_t sz;
- assert_d_eq(mallctl("no_such_name", NULL, NULL, NULL, 0), ENOENT,
+ expect_d_eq(mallctl("no_such_name", NULL, NULL, NULL, 0), ENOENT,
"mallctl() should return ENOENT for non-existent names");
- assert_d_eq(mallctl("version", NULL, NULL, "0.0.0", strlen("0.0.0")),
+ expect_d_eq(mallctl("version", NULL, NULL, "0.0.0", strlen("0.0.0")),
EPERM, "mallctl() should return EPERM on attempt to write "
"read-only value");
- assert_d_eq(mallctl("epoch", NULL, NULL, (void *)&epoch,
+ expect_d_eq(mallctl("epoch", NULL, NULL, (void *)&epoch,
sizeof(epoch)-1), EINVAL,
"mallctl() should return EINVAL for input size mismatch");
- assert_d_eq(mallctl("epoch", NULL, NULL, (void *)&epoch,
+ expect_d_eq(mallctl("epoch", NULL, NULL, (void *)&epoch,
sizeof(epoch)+1), EINVAL,
"mallctl() should return EINVAL for input size mismatch");
sz = sizeof(epoch)-1;
- assert_d_eq(mallctl("epoch", (void *)&epoch, &sz, NULL, 0), EINVAL,
+ expect_d_eq(mallctl("epoch", (void *)&epoch, &sz, NULL, 0), EINVAL,
"mallctl() should return EINVAL for output size mismatch");
sz = sizeof(epoch)+1;
- assert_d_eq(mallctl("epoch", (void *)&epoch, &sz, NULL, 0), EINVAL,
+ expect_d_eq(mallctl("epoch", (void *)&epoch, &sz, NULL, 0), EINVAL,
"mallctl() should return EINVAL for output size mismatch");
}
TEST_END
@@ -35,7 +36,7 @@ TEST_BEGIN(test_mallctlnametomib_errors) {
size_t miblen;
miblen = sizeof(mib)/sizeof(size_t);
- assert_d_eq(mallctlnametomib("no_such_name", mib, &miblen), ENOENT,
+ expect_d_eq(mallctlnametomib("no_such_name", mib, &miblen), ENOENT,
"mallctlnametomib() should return ENOENT for non-existent names");
}
TEST_END
@@ -47,30 +48,30 @@ TEST_BEGIN(test_mallctlbymib_errors) {
size_t miblen;
miblen = sizeof(mib)/sizeof(size_t);
- assert_d_eq(mallctlnametomib("version", mib, &miblen), 0,
+ expect_d_eq(mallctlnametomib("version", mib, &miblen), 0,
"Unexpected mallctlnametomib() failure");
- assert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, "0.0.0",
+ expect_d_eq(mallctlbymib(mib, miblen, NULL, NULL, "0.0.0",
strlen("0.0.0")), EPERM, "mallctl() should return EPERM on "
"attempt to write read-only value");
miblen = sizeof(mib)/sizeof(size_t);
- assert_d_eq(mallctlnametomib("epoch", mib, &miblen), 0,
+ expect_d_eq(mallctlnametomib("epoch", mib, &miblen), 0,
"Unexpected mallctlnametomib() failure");
- assert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, (void *)&epoch,
+ expect_d_eq(mallctlbymib(mib, miblen, NULL, NULL, (void *)&epoch,
sizeof(epoch)-1), EINVAL,
"mallctlbymib() should return EINVAL for input size mismatch");
- assert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, (void *)&epoch,
+ expect_d_eq(mallctlbymib(mib, miblen, NULL, NULL, (void *)&epoch,
sizeof(epoch)+1), EINVAL,
"mallctlbymib() should return EINVAL for input size mismatch");
sz = sizeof(epoch)-1;
- assert_d_eq(mallctlbymib(mib, miblen, (void *)&epoch, &sz, NULL, 0),
+ expect_d_eq(mallctlbymib(mib, miblen, (void *)&epoch, &sz, NULL, 0),
EINVAL,
"mallctlbymib() should return EINVAL for output size mismatch");
sz = sizeof(epoch)+1;
- assert_d_eq(mallctlbymib(mib, miblen, (void *)&epoch, &sz, NULL, 0),
+ expect_d_eq(mallctlbymib(mib, miblen, (void *)&epoch, &sz, NULL, 0),
EINVAL,
"mallctlbymib() should return EINVAL for output size mismatch");
}
@@ -81,25 +82,25 @@ TEST_BEGIN(test_mallctl_read_write) {
size_t sz = sizeof(old_epoch);
/* Blind. */
- assert_d_eq(mallctl("epoch", NULL, NULL, NULL, 0), 0,
+ expect_d_eq(mallctl("epoch", NULL, NULL, NULL, 0), 0,
"Unexpected mallctl() failure");
- assert_zu_eq(sz, sizeof(old_epoch), "Unexpected output size");
+ expect_zu_eq(sz, sizeof(old_epoch), "Unexpected output size");
/* Read. */
- assert_d_eq(mallctl("epoch", (void *)&old_epoch, &sz, NULL, 0), 0,
+ expect_d_eq(mallctl("epoch", (void *)&old_epoch, &sz, NULL, 0), 0,
"Unexpected mallctl() failure");
- assert_zu_eq(sz, sizeof(old_epoch), "Unexpected output size");
+ expect_zu_eq(sz, sizeof(old_epoch), "Unexpected output size");
/* Write. */
- assert_d_eq(mallctl("epoch", NULL, NULL, (void *)&new_epoch,
+ expect_d_eq(mallctl("epoch", NULL, NULL, (void *)&new_epoch,
sizeof(new_epoch)), 0, "Unexpected mallctl() failure");
- assert_zu_eq(sz, sizeof(old_epoch), "Unexpected output size");
+ expect_zu_eq(sz, sizeof(old_epoch), "Unexpected output size");
/* Read+write. */
- assert_d_eq(mallctl("epoch", (void *)&old_epoch, &sz,
+ expect_d_eq(mallctl("epoch", (void *)&old_epoch, &sz,
(void *)&new_epoch, sizeof(new_epoch)), 0,
"Unexpected mallctl() failure");
- assert_zu_eq(sz, sizeof(old_epoch), "Unexpected output size");
+ expect_zu_eq(sz, sizeof(old_epoch), "Unexpected output size");
}
TEST_END
@@ -109,22 +110,141 @@ TEST_BEGIN(test_mallctlnametomib_short_mib) {
miblen = 3;
mib[3] = 42;
- assert_d_eq(mallctlnametomib("arenas.bin.0.nregs", mib, &miblen), 0,
+ expect_d_eq(mallctlnametomib("arenas.bin.0.nregs", mib, &miblen), 0,
"Unexpected mallctlnametomib() failure");
- assert_zu_eq(miblen, 3, "Unexpected mib output length");
- assert_zu_eq(mib[3], 42,
+ expect_zu_eq(miblen, 3, "Unexpected mib output length");
+ expect_zu_eq(mib[3], 42,
"mallctlnametomib() wrote past the end of the input mib");
}
TEST_END
+TEST_BEGIN(test_mallctlnametomib_short_name) {
+ size_t mib[4];
+ size_t miblen;
+
+ miblen = 4;
+ mib[3] = 42;
+ expect_d_eq(mallctlnametomib("arenas.bin.0", mib, &miblen), 0,
+ "Unexpected mallctlnametomib() failure");
+ expect_zu_eq(miblen, 3, "Unexpected mib output length");
+ expect_zu_eq(mib[3], 42,
+ "mallctlnametomib() wrote past the end of the input mib");
+}
+TEST_END
+
+TEST_BEGIN(test_mallctlmibnametomib) {
+ size_t mib[4];
+ size_t miblen = 4;
+ uint32_t result, result_ref;
+ size_t len_result = sizeof(uint32_t);
+
+ tsd_t *tsd = tsd_fetch();
+
+ /* Error cases */
+ assert_d_eq(ctl_mibnametomib(tsd, mib, 0, "bob", &miblen), ENOENT, "");
+ assert_zu_eq(miblen, 4, "");
+ assert_d_eq(ctl_mibnametomib(tsd, mib, 0, "9999", &miblen), ENOENT, "");
+ assert_zu_eq(miblen, 4, "");
+
+ /* Valid case. */
+ assert_d_eq(ctl_mibnametomib(tsd, mib, 0, "arenas", &miblen), 0, "");
+ assert_zu_eq(miblen, 1, "");
+ miblen = 4;
+ assert_d_eq(ctl_mibnametomib(tsd, mib, 1, "bin", &miblen), 0, "");
+ assert_zu_eq(miblen, 2, "");
+ expect_d_eq(mallctlbymib(mib, miblen, &result, &len_result, NULL, 0),
+ ENOENT, "mallctlbymib() should fail on partial path");
+
+ /* Error cases. */
+ miblen = 4;
+ assert_d_eq(ctl_mibnametomib(tsd, mib, 2, "bob", &miblen), ENOENT, "");
+ assert_zu_eq(miblen, 4, "");
+ assert_d_eq(ctl_mibnametomib(tsd, mib, 2, "9999", &miblen), ENOENT, "");
+ assert_zu_eq(miblen, 4, "");
+
+ /* Valid case. */
+ assert_d_eq(ctl_mibnametomib(tsd, mib, 2, "0", &miblen), 0, "");
+ assert_zu_eq(miblen, 3, "");
+ expect_d_eq(mallctlbymib(mib, miblen, &result, &len_result, NULL, 0),
+ ENOENT, "mallctlbymib() should fail on partial path");
+
+ /* Error cases. */
+ miblen = 4;
+ assert_d_eq(ctl_mibnametomib(tsd, mib, 3, "bob", &miblen), ENOENT, "");
+ assert_zu_eq(miblen, 4, "");
+ assert_d_eq(ctl_mibnametomib(tsd, mib, 3, "9999", &miblen), ENOENT, "");
+ assert_zu_eq(miblen, 4, "");
+
+ /* Valid case. */
+ assert_d_eq(ctl_mibnametomib(tsd, mib, 3, "nregs", &miblen), 0, "");
+ assert_zu_eq(miblen, 4, "");
+ assert_d_eq(mallctlbymib(mib, miblen, &result, &len_result, NULL, 0),
+ 0, "Unexpected mallctlbymib() failure");
+ assert_d_eq(mallctl("arenas.bin.0.nregs", &result_ref, &len_result,
+ NULL, 0), 0, "Unexpected mallctl() failure");
+ expect_zu_eq(result, result_ref,
+ "mallctlbymib() and mallctl() returned different result");
+}
+TEST_END
+
+TEST_BEGIN(test_mallctlbymibname) {
+ size_t mib[4];
+ size_t miblen = 4;
+ uint32_t result, result_ref;
+ size_t len_result = sizeof(uint32_t);
+
+ tsd_t *tsd = tsd_fetch();
+
+ /* Error cases. */
+
+ assert_d_eq(mallctlnametomib("arenas", mib, &miblen), 0,
+ "Unexpected mallctlnametomib() failure");
+ assert_zu_eq(miblen, 1, "");
+
+ miblen = 4;
+ assert_d_eq(ctl_bymibname(tsd, mib, 1, "bin.0", &miblen,
+ &result, &len_result, NULL, 0), ENOENT, "");
+ miblen = 4;
+ assert_d_eq(ctl_bymibname(tsd, mib, 1, "bin.0.bob", &miblen,
+ &result, &len_result, NULL, 0), ENOENT, "");
+ assert_zu_eq(miblen, 4, "");
+
+ /* Valid cases. */
+
+ assert_d_eq(mallctl("arenas.bin.0.nregs", &result_ref, &len_result,
+ NULL, 0), 0, "Unexpected mallctl() failure");
+ miblen = 4;
+
+ assert_d_eq(ctl_bymibname(tsd, mib, 0, "arenas.bin.0.nregs", &miblen,
+ &result, &len_result, NULL, 0), 0, "");
+ assert_zu_eq(miblen, 4, "");
+ expect_zu_eq(result, result_ref, "Unexpected result");
+
+ assert_d_eq(ctl_bymibname(tsd, mib, 1, "bin.0.nregs", &miblen, &result,
+ &len_result, NULL, 0), 0, "");
+ assert_zu_eq(miblen, 4, "");
+ expect_zu_eq(result, result_ref, "Unexpected result");
+
+ assert_d_eq(ctl_bymibname(tsd, mib, 2, "0.nregs", &miblen, &result,
+ &len_result, NULL, 0), 0, "");
+ assert_zu_eq(miblen, 4, "");
+ expect_zu_eq(result, result_ref, "Unexpected result");
+
+ assert_d_eq(ctl_bymibname(tsd, mib, 3, "nregs", &miblen, &result,
+ &len_result, NULL, 0), 0, "");
+ assert_zu_eq(miblen, 4, "");
+ expect_zu_eq(result, result_ref, "Unexpected result");
+}
+TEST_END
+
TEST_BEGIN(test_mallctl_config) {
#define TEST_MALLCTL_CONFIG(config, t) do { \
t oldval; \
size_t sz = sizeof(oldval); \
- assert_d_eq(mallctl("config."#config, (void *)&oldval, &sz, \
+ expect_d_eq(mallctl("config."#config, (void *)&oldval, &sz, \
NULL, 0), 0, "Unexpected mallctl() failure"); \
- assert_b_eq(oldval, config_##config, "Incorrect config value"); \
- assert_zu_eq(sz, sizeof(oldval), "Unexpected output size"); \
+ expect_b_eq(oldval, config_##config, "Incorrect config value"); \
+ expect_zu_eq(sz, sizeof(oldval), "Unexpected output size"); \
} while (0)
TEST_MALLCTL_CONFIG(cache_oblivious, bool);
@@ -152,17 +272,26 @@ TEST_BEGIN(test_mallctl_opt) {
int expected = config_##config ? 0 : ENOENT; \
int result = mallctl("opt."#opt, (void *)&oldval, &sz, NULL, \
0); \
- assert_d_eq(result, expected, \
+ expect_d_eq(result, expected, \
"Unexpected mallctl() result for opt."#opt); \
- assert_zu_eq(sz, sizeof(oldval), "Unexpected output size"); \
+ expect_zu_eq(sz, sizeof(oldval), "Unexpected output size"); \
} while (0)
TEST_MALLCTL_OPT(bool, abort, always);
TEST_MALLCTL_OPT(bool, abort_conf, always);
+ TEST_MALLCTL_OPT(bool, cache_oblivious, always);
+ TEST_MALLCTL_OPT(bool, trust_madvise, always);
TEST_MALLCTL_OPT(bool, confirm_conf, always);
TEST_MALLCTL_OPT(const char *, metadata_thp, always);
TEST_MALLCTL_OPT(bool, retain, always);
TEST_MALLCTL_OPT(const char *, dss, always);
+ TEST_MALLCTL_OPT(bool, hpa, always);
+ TEST_MALLCTL_OPT(size_t, hpa_slab_max_alloc, always);
+ TEST_MALLCTL_OPT(size_t, hpa_sec_nshards, always);
+ TEST_MALLCTL_OPT(size_t, hpa_sec_max_alloc, always);
+ TEST_MALLCTL_OPT(size_t, hpa_sec_max_bytes, always);
+ TEST_MALLCTL_OPT(size_t, hpa_sec_bytes_after_flush, always);
+ TEST_MALLCTL_OPT(size_t, hpa_sec_batch_fill_extra, always);
TEST_MALLCTL_OPT(unsigned, narenas, always);
TEST_MALLCTL_OPT(const char *, percpu_arena, always);
TEST_MALLCTL_OPT(size_t, oversize_threshold, always);
@@ -170,14 +299,18 @@ TEST_BEGIN(test_mallctl_opt) {
TEST_MALLCTL_OPT(ssize_t, dirty_decay_ms, always);
TEST_MALLCTL_OPT(ssize_t, muzzy_decay_ms, always);
TEST_MALLCTL_OPT(bool, stats_print, always);
+ TEST_MALLCTL_OPT(const char *, stats_print_opts, always);
+ TEST_MALLCTL_OPT(int64_t, stats_interval, always);
+ TEST_MALLCTL_OPT(const char *, stats_interval_opts, always);
TEST_MALLCTL_OPT(const char *, junk, fill);
TEST_MALLCTL_OPT(bool, zero, fill);
TEST_MALLCTL_OPT(bool, utrace, utrace);
TEST_MALLCTL_OPT(bool, xmalloc, xmalloc);
TEST_MALLCTL_OPT(bool, tcache, always);
TEST_MALLCTL_OPT(size_t, lg_extent_max_active_fit, always);
- TEST_MALLCTL_OPT(size_t, lg_tcache_max, always);
+ TEST_MALLCTL_OPT(size_t, tcache_max, always);
TEST_MALLCTL_OPT(const char *, thp, always);
+ TEST_MALLCTL_OPT(const char *, zero_realloc, always);
TEST_MALLCTL_OPT(bool, prof, prof);
TEST_MALLCTL_OPT(const char *, prof_prefix, prof);
TEST_MALLCTL_OPT(bool, prof_active, prof);
@@ -187,6 +320,11 @@ TEST_BEGIN(test_mallctl_opt) {
TEST_MALLCTL_OPT(bool, prof_gdump, prof);
TEST_MALLCTL_OPT(bool, prof_final, prof);
TEST_MALLCTL_OPT(bool, prof_leak, prof);
+ TEST_MALLCTL_OPT(bool, prof_leak_error, prof);
+ TEST_MALLCTL_OPT(ssize_t, prof_recent_alloc_max, prof);
+ TEST_MALLCTL_OPT(bool, prof_stats, prof);
+ TEST_MALLCTL_OPT(bool, prof_sys_thread_name, prof);
+ TEST_MALLCTL_OPT(ssize_t, lg_san_uaf_align, uaf_detection);
#undef TEST_MALLCTL_OPT
}
@@ -198,18 +336,18 @@ TEST_BEGIN(test_manpage_example) {
size_t len, miblen;
len = sizeof(nbins);
- assert_d_eq(mallctl("arenas.nbins", (void *)&nbins, &len, NULL, 0), 0,
+ expect_d_eq(mallctl("arenas.nbins", (void *)&nbins, &len, NULL, 0), 0,
"Unexpected mallctl() failure");
miblen = 4;
- assert_d_eq(mallctlnametomib("arenas.bin.0.size", mib, &miblen), 0,
+ expect_d_eq(mallctlnametomib("arenas.bin.0.size", mib, &miblen), 0,
"Unexpected mallctlnametomib() failure");
for (i = 0; i < nbins; i++) {
size_t bin_size;
mib[2] = i;
len = sizeof(bin_size);
- assert_d_eq(mallctlbymib(mib, miblen, (void *)&bin_size, &len,
+ expect_d_eq(mallctlbymib(mib, miblen, (void *)&bin_size, &len,
NULL, 0), 0, "Unexpected mallctlbymib() failure");
/* Do something with bin_size... */
}
@@ -221,9 +359,9 @@ TEST_BEGIN(test_tcache_none) {
/* Allocate p and q. */
void *p0 = mallocx(42, 0);
- assert_ptr_not_null(p0, "Unexpected mallocx() failure");
+ expect_ptr_not_null(p0, "Unexpected mallocx() failure");
void *q = mallocx(42, 0);
- assert_ptr_not_null(q, "Unexpected mallocx() failure");
+ expect_ptr_not_null(q, "Unexpected mallocx() failure");
/* Deallocate p and q, but bypass the tcache for q. */
dallocx(p0, 0);
@@ -231,8 +369,11 @@ TEST_BEGIN(test_tcache_none) {
/* Make sure that tcache-based allocation returns p, not q. */
void *p1 = mallocx(42, 0);
- assert_ptr_not_null(p1, "Unexpected mallocx() failure");
- assert_ptr_eq(p0, p1, "Expected tcache to allocate cached region");
+ expect_ptr_not_null(p1, "Unexpected mallocx() failure");
+ if (!opt_prof && !san_uaf_detection_enabled()) {
+ expect_ptr_eq(p0, p1,
+ "Expected tcache to allocate cached region");
+ }
/* Clean up. */
dallocx(p1, MALLOCX_TCACHE_NONE);
@@ -253,25 +394,25 @@ TEST_BEGIN(test_tcache) {
/* Create tcaches. */
for (i = 0; i < NTCACHES; i++) {
sz = sizeof(unsigned);
- assert_d_eq(mallctl("tcache.create", (void *)&tis[i], &sz, NULL,
+ expect_d_eq(mallctl("tcache.create", (void *)&tis[i], &sz, NULL,
0), 0, "Unexpected mallctl() failure, i=%u", i);
}
/* Exercise tcache ID recycling. */
for (i = 0; i < NTCACHES; i++) {
- assert_d_eq(mallctl("tcache.destroy", NULL, NULL,
+ expect_d_eq(mallctl("tcache.destroy", NULL, NULL,
(void *)&tis[i], sizeof(unsigned)), 0,
"Unexpected mallctl() failure, i=%u", i);
}
for (i = 0; i < NTCACHES; i++) {
sz = sizeof(unsigned);
- assert_d_eq(mallctl("tcache.create", (void *)&tis[i], &sz, NULL,
+ expect_d_eq(mallctl("tcache.create", (void *)&tis[i], &sz, NULL,
0), 0, "Unexpected mallctl() failure, i=%u", i);
}
/* Flush empty tcaches. */
for (i = 0; i < NTCACHES; i++) {
- assert_d_eq(mallctl("tcache.flush", NULL, NULL, (void *)&tis[i],
+ expect_d_eq(mallctl("tcache.flush", NULL, NULL, (void *)&tis[i],
sizeof(unsigned)), 0, "Unexpected mallctl() failure, i=%u",
i);
}
@@ -279,12 +420,12 @@ TEST_BEGIN(test_tcache) {
/* Cache some allocations. */
for (i = 0; i < NTCACHES; i++) {
ps[i] = mallocx(psz, MALLOCX_TCACHE(tis[i]));
- assert_ptr_not_null(ps[i], "Unexpected mallocx() failure, i=%u",
+ expect_ptr_not_null(ps[i], "Unexpected mallocx() failure, i=%u",
i);
dallocx(ps[i], MALLOCX_TCACHE(tis[i]));
qs[i] = mallocx(qsz, MALLOCX_TCACHE(tis[i]));
- assert_ptr_not_null(qs[i], "Unexpected mallocx() failure, i=%u",
+ expect_ptr_not_null(qs[i], "Unexpected mallocx() failure, i=%u",
i);
dallocx(qs[i], MALLOCX_TCACHE(tis[i]));
}
@@ -293,20 +434,24 @@ TEST_BEGIN(test_tcache) {
for (i = 0; i < NTCACHES; i++) {
void *p0 = ps[i];
ps[i] = mallocx(psz, MALLOCX_TCACHE(tis[i]));
- assert_ptr_not_null(ps[i], "Unexpected mallocx() failure, i=%u",
+ expect_ptr_not_null(ps[i], "Unexpected mallocx() failure, i=%u",
i);
- assert_ptr_eq(ps[i], p0,
- "Expected mallocx() to allocate cached region, i=%u", i);
+ if (!san_uaf_detection_enabled()) {
+ expect_ptr_eq(ps[i], p0, "Expected mallocx() to "
+ "allocate cached region, i=%u", i);
+ }
}
/* Verify that reallocation uses cached regions. */
for (i = 0; i < NTCACHES; i++) {
void *q0 = qs[i];
qs[i] = rallocx(ps[i], qsz, MALLOCX_TCACHE(tis[i]));
- assert_ptr_not_null(qs[i], "Unexpected rallocx() failure, i=%u",
+ expect_ptr_not_null(qs[i], "Unexpected rallocx() failure, i=%u",
i);
- assert_ptr_eq(qs[i], q0,
- "Expected rallocx() to allocate cached region, i=%u", i);
+ if (!san_uaf_detection_enabled()) {
+ expect_ptr_eq(qs[i], q0, "Expected rallocx() to "
+ "allocate cached region, i=%u", i);
+ }
/* Avoid undefined behavior in case of test failure. */
if (qs[i] == NULL) {
qs[i] = ps[i];
@@ -318,14 +463,14 @@ TEST_BEGIN(test_tcache) {
/* Flush some non-empty tcaches. */
for (i = 0; i < NTCACHES/2; i++) {
- assert_d_eq(mallctl("tcache.flush", NULL, NULL, (void *)&tis[i],
+ expect_d_eq(mallctl("tcache.flush", NULL, NULL, (void *)&tis[i],
sizeof(unsigned)), 0, "Unexpected mallctl() failure, i=%u",
i);
}
/* Destroy tcaches. */
for (i = 0; i < NTCACHES; i++) {
- assert_d_eq(mallctl("tcache.destroy", NULL, NULL,
+ expect_d_eq(mallctl("tcache.destroy", NULL, NULL,
(void *)&tis[i], sizeof(unsigned)), 0,
"Unexpected mallctl() failure, i=%u", i);
}
@@ -337,32 +482,32 @@ TEST_BEGIN(test_thread_arena) {
const char *opa;
size_t sz = sizeof(opa);
- assert_d_eq(mallctl("opt.percpu_arena", (void *)&opa, &sz, NULL, 0), 0,
+ expect_d_eq(mallctl("opt.percpu_arena", (void *)&opa, &sz, NULL, 0), 0,
"Unexpected mallctl() failure");
sz = sizeof(unsigned);
- assert_d_eq(mallctl("arenas.narenas", (void *)&narenas, &sz, NULL, 0),
+ expect_d_eq(mallctl("arenas.narenas", (void *)&narenas, &sz, NULL, 0),
0, "Unexpected mallctl() failure");
if (opt_oversize_threshold != 0) {
narenas--;
}
- assert_u_eq(narenas, opt_narenas, "Number of arenas incorrect");
+ expect_u_eq(narenas, opt_narenas, "Number of arenas incorrect");
if (strcmp(opa, "disabled") == 0) {
new_arena_ind = narenas - 1;
- assert_d_eq(mallctl("thread.arena", (void *)&old_arena_ind, &sz,
+ expect_d_eq(mallctl("thread.arena", (void *)&old_arena_ind, &sz,
(void *)&new_arena_ind, sizeof(unsigned)), 0,
"Unexpected mallctl() failure");
new_arena_ind = 0;
- assert_d_eq(mallctl("thread.arena", (void *)&old_arena_ind, &sz,
+ expect_d_eq(mallctl("thread.arena", (void *)&old_arena_ind, &sz,
(void *)&new_arena_ind, sizeof(unsigned)), 0,
"Unexpected mallctl() failure");
} else {
- assert_d_eq(mallctl("thread.arena", (void *)&old_arena_ind, &sz,
+ expect_d_eq(mallctl("thread.arena", (void *)&old_arena_ind, &sz,
NULL, 0), 0, "Unexpected mallctl() failure");
new_arena_ind = percpu_arena_ind_limit(opt_percpu_arena) - 1;
if (old_arena_ind != new_arena_ind) {
- assert_d_eq(mallctl("thread.arena",
+ expect_d_eq(mallctl("thread.arena",
(void *)&old_arena_ind, &sz, (void *)&new_arena_ind,
sizeof(unsigned)), EPERM, "thread.arena ctl "
"should not be allowed with percpu arena");
@@ -379,32 +524,32 @@ TEST_BEGIN(test_arena_i_initialized) {
bool initialized;
sz = sizeof(narenas);
- assert_d_eq(mallctl("arenas.narenas", (void *)&narenas, &sz, NULL, 0),
+ expect_d_eq(mallctl("arenas.narenas", (void *)&narenas, &sz, NULL, 0),
0, "Unexpected mallctl() failure");
- assert_d_eq(mallctlnametomib("arena.0.initialized", mib, &miblen), 0,
+ expect_d_eq(mallctlnametomib("arena.0.initialized", mib, &miblen), 0,
"Unexpected mallctlnametomib() failure");
for (i = 0; i < narenas; i++) {
mib[1] = i;
sz = sizeof(initialized);
- assert_d_eq(mallctlbymib(mib, miblen, &initialized, &sz, NULL,
+ expect_d_eq(mallctlbymib(mib, miblen, &initialized, &sz, NULL,
0), 0, "Unexpected mallctl() failure");
}
mib[1] = MALLCTL_ARENAS_ALL;
sz = sizeof(initialized);
- assert_d_eq(mallctlbymib(mib, miblen, &initialized, &sz, NULL, 0), 0,
+ expect_d_eq(mallctlbymib(mib, miblen, &initialized, &sz, NULL, 0), 0,
"Unexpected mallctl() failure");
- assert_true(initialized,
+ expect_true(initialized,
"Merged arena statistics should always be initialized");
/* Equivalent to the above but using mallctl() directly. */
sz = sizeof(initialized);
- assert_d_eq(mallctl(
+ expect_d_eq(mallctl(
"arena." STRINGIFY(MALLCTL_ARENAS_ALL) ".initialized",
(void *)&initialized, &sz, NULL, 0), 0,
"Unexpected mallctl() failure");
- assert_true(initialized,
+ expect_true(initialized,
"Merged arena statistics should always be initialized");
}
TEST_END
@@ -413,17 +558,17 @@ TEST_BEGIN(test_arena_i_dirty_decay_ms) {
ssize_t dirty_decay_ms, orig_dirty_decay_ms, prev_dirty_decay_ms;
size_t sz = sizeof(ssize_t);
- assert_d_eq(mallctl("arena.0.dirty_decay_ms",
+ expect_d_eq(mallctl("arena.0.dirty_decay_ms",
(void *)&orig_dirty_decay_ms, &sz, NULL, 0), 0,
"Unexpected mallctl() failure");
dirty_decay_ms = -2;
- assert_d_eq(mallctl("arena.0.dirty_decay_ms", NULL, NULL,
+ expect_d_eq(mallctl("arena.0.dirty_decay_ms", NULL, NULL,
(void *)&dirty_decay_ms, sizeof(ssize_t)), EFAULT,
"Unexpected mallctl() success");
dirty_decay_ms = 0x7fffffff;
- assert_d_eq(mallctl("arena.0.dirty_decay_ms", NULL, NULL,
+ expect_d_eq(mallctl("arena.0.dirty_decay_ms", NULL, NULL,
(void *)&dirty_decay_ms, sizeof(ssize_t)), 0,
"Unexpected mallctl() failure");
@@ -432,10 +577,10 @@ TEST_BEGIN(test_arena_i_dirty_decay_ms) {
dirty_decay_ms++) {
ssize_t old_dirty_decay_ms;
- assert_d_eq(mallctl("arena.0.dirty_decay_ms",
+ expect_d_eq(mallctl("arena.0.dirty_decay_ms",
(void *)&old_dirty_decay_ms, &sz, (void *)&dirty_decay_ms,
sizeof(ssize_t)), 0, "Unexpected mallctl() failure");
- assert_zd_eq(old_dirty_decay_ms, prev_dirty_decay_ms,
+ expect_zd_eq(old_dirty_decay_ms, prev_dirty_decay_ms,
"Unexpected old arena.0.dirty_decay_ms");
}
}
@@ -445,17 +590,17 @@ TEST_BEGIN(test_arena_i_muzzy_decay_ms) {
ssize_t muzzy_decay_ms, orig_muzzy_decay_ms, prev_muzzy_decay_ms;
size_t sz = sizeof(ssize_t);
- assert_d_eq(mallctl("arena.0.muzzy_decay_ms",
+ expect_d_eq(mallctl("arena.0.muzzy_decay_ms",
(void *)&orig_muzzy_decay_ms, &sz, NULL, 0), 0,
"Unexpected mallctl() failure");
muzzy_decay_ms = -2;
- assert_d_eq(mallctl("arena.0.muzzy_decay_ms", NULL, NULL,
+ expect_d_eq(mallctl("arena.0.muzzy_decay_ms", NULL, NULL,
(void *)&muzzy_decay_ms, sizeof(ssize_t)), EFAULT,
"Unexpected mallctl() success");
muzzy_decay_ms = 0x7fffffff;
- assert_d_eq(mallctl("arena.0.muzzy_decay_ms", NULL, NULL,
+ expect_d_eq(mallctl("arena.0.muzzy_decay_ms", NULL, NULL,
(void *)&muzzy_decay_ms, sizeof(ssize_t)), 0,
"Unexpected mallctl() failure");
@@ -464,10 +609,10 @@ TEST_BEGIN(test_arena_i_muzzy_decay_ms) {
muzzy_decay_ms++) {
ssize_t old_muzzy_decay_ms;
- assert_d_eq(mallctl("arena.0.muzzy_decay_ms",
+ expect_d_eq(mallctl("arena.0.muzzy_decay_ms",
(void *)&old_muzzy_decay_ms, &sz, (void *)&muzzy_decay_ms,
sizeof(ssize_t)), 0, "Unexpected mallctl() failure");
- assert_zd_eq(old_muzzy_decay_ms, prev_muzzy_decay_ms,
+ expect_zd_eq(old_muzzy_decay_ms, prev_muzzy_decay_ms,
"Unexpected old arena.0.muzzy_decay_ms");
}
}
@@ -479,19 +624,19 @@ TEST_BEGIN(test_arena_i_purge) {
size_t mib[3];
size_t miblen = 3;
- assert_d_eq(mallctl("arena.0.purge", NULL, NULL, NULL, 0), 0,
+ expect_d_eq(mallctl("arena.0.purge", NULL, NULL, NULL, 0), 0,
"Unexpected mallctl() failure");
- assert_d_eq(mallctl("arenas.narenas", (void *)&narenas, &sz, NULL, 0),
+ expect_d_eq(mallctl("arenas.narenas", (void *)&narenas, &sz, NULL, 0),
0, "Unexpected mallctl() failure");
- assert_d_eq(mallctlnametomib("arena.0.purge", mib, &miblen), 0,
+ expect_d_eq(mallctlnametomib("arena.0.purge", mib, &miblen), 0,
"Unexpected mallctlnametomib() failure");
mib[1] = narenas;
- assert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0,
+ expect_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0,
"Unexpected mallctlbymib() failure");
mib[1] = MALLCTL_ARENAS_ALL;
- assert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0,
+ expect_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0,
"Unexpected mallctlbymib() failure");
}
TEST_END
@@ -502,19 +647,19 @@ TEST_BEGIN(test_arena_i_decay) {
size_t mib[3];
size_t miblen = 3;
- assert_d_eq(mallctl("arena.0.decay", NULL, NULL, NULL, 0), 0,
+ expect_d_eq(mallctl("arena.0.decay", NULL, NULL, NULL, 0), 0,
"Unexpected mallctl() failure");
- assert_d_eq(mallctl("arenas.narenas", (void *)&narenas, &sz, NULL, 0),
+ expect_d_eq(mallctl("arenas.narenas", (void *)&narenas, &sz, NULL, 0),
0, "Unexpected mallctl() failure");
- assert_d_eq(mallctlnametomib("arena.0.decay", mib, &miblen), 0,
+ expect_d_eq(mallctlnametomib("arena.0.decay", mib, &miblen), 0,
"Unexpected mallctlnametomib() failure");
mib[1] = narenas;
- assert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0,
+ expect_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0,
"Unexpected mallctlbymib() failure");
mib[1] = MALLCTL_ARENAS_ALL;
- assert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0,
+ expect_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0,
"Unexpected mallctlbymib() failure");
}
TEST_END
@@ -526,40 +671,40 @@ TEST_BEGIN(test_arena_i_dss) {
size_t miblen;
miblen = sizeof(mib)/sizeof(size_t);
- assert_d_eq(mallctlnametomib("arena.0.dss", mib, &miblen), 0,
+ expect_d_eq(mallctlnametomib("arena.0.dss", mib, &miblen), 0,
"Unexpected mallctlnametomib() error");
dss_prec_new = "disabled";
- assert_d_eq(mallctlbymib(mib, miblen, (void *)&dss_prec_old, &sz,
+ expect_d_eq(mallctlbymib(mib, miblen, (void *)&dss_prec_old, &sz,
(void *)&dss_prec_new, sizeof(dss_prec_new)), 0,
"Unexpected mallctl() failure");
- assert_str_ne(dss_prec_old, "primary",
+ expect_str_ne(dss_prec_old, "primary",
"Unexpected default for dss precedence");
- assert_d_eq(mallctlbymib(mib, miblen, (void *)&dss_prec_new, &sz,
+ expect_d_eq(mallctlbymib(mib, miblen, (void *)&dss_prec_new, &sz,
(void *)&dss_prec_old, sizeof(dss_prec_old)), 0,
"Unexpected mallctl() failure");
- assert_d_eq(mallctlbymib(mib, miblen, (void *)&dss_prec_old, &sz, NULL,
+ expect_d_eq(mallctlbymib(mib, miblen, (void *)&dss_prec_old, &sz, NULL,
0), 0, "Unexpected mallctl() failure");
- assert_str_ne(dss_prec_old, "primary",
+ expect_str_ne(dss_prec_old, "primary",
"Unexpected value for dss precedence");
mib[1] = narenas_total_get();
dss_prec_new = "disabled";
- assert_d_eq(mallctlbymib(mib, miblen, (void *)&dss_prec_old, &sz,
+ expect_d_eq(mallctlbymib(mib, miblen, (void *)&dss_prec_old, &sz,
(void *)&dss_prec_new, sizeof(dss_prec_new)), 0,
"Unexpected mallctl() failure");
- assert_str_ne(dss_prec_old, "primary",
+ expect_str_ne(dss_prec_old, "primary",
"Unexpected default for dss precedence");
- assert_d_eq(mallctlbymib(mib, miblen, (void *)&dss_prec_new, &sz,
+ expect_d_eq(mallctlbymib(mib, miblen, (void *)&dss_prec_new, &sz,
(void *)&dss_prec_old, sizeof(dss_prec_new)), 0,
"Unexpected mallctl() failure");
- assert_d_eq(mallctlbymib(mib, miblen, (void *)&dss_prec_old, &sz, NULL,
+ expect_d_eq(mallctlbymib(mib, miblen, (void *)&dss_prec_old, &sz, NULL,
0), 0, "Unexpected mallctl() failure");
- assert_str_ne(dss_prec_old, "primary",
+ expect_str_ne(dss_prec_old, "primary",
"Unexpected value for dss precedence");
}
TEST_END
@@ -571,43 +716,43 @@ TEST_BEGIN(test_arena_i_retain_grow_limit) {
bool retain_enabled;
size_t sz = sizeof(retain_enabled);
- assert_d_eq(mallctl("opt.retain", &retain_enabled, &sz, NULL, 0),
+ expect_d_eq(mallctl("opt.retain", &retain_enabled, &sz, NULL, 0),
0, "Unexpected mallctl() failure");
test_skip_if(!retain_enabled);
sz = sizeof(default_limit);
miblen = sizeof(mib)/sizeof(size_t);
- assert_d_eq(mallctlnametomib("arena.0.retain_grow_limit", mib, &miblen),
+ expect_d_eq(mallctlnametomib("arena.0.retain_grow_limit", mib, &miblen),
0, "Unexpected mallctlnametomib() error");
- assert_d_eq(mallctlbymib(mib, miblen, &default_limit, &sz, NULL, 0), 0,
+ expect_d_eq(mallctlbymib(mib, miblen, &default_limit, &sz, NULL, 0), 0,
"Unexpected mallctl() failure");
- assert_zu_eq(default_limit, SC_LARGE_MAXCLASS,
+ expect_zu_eq(default_limit, SC_LARGE_MAXCLASS,
"Unexpected default for retain_grow_limit");
new_limit = PAGE - 1;
- assert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, &new_limit,
+ expect_d_eq(mallctlbymib(mib, miblen, NULL, NULL, &new_limit,
sizeof(new_limit)), EFAULT, "Unexpected mallctl() success");
new_limit = PAGE + 1;
- assert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, &new_limit,
+ expect_d_eq(mallctlbymib(mib, miblen, NULL, NULL, &new_limit,
sizeof(new_limit)), 0, "Unexpected mallctl() failure");
- assert_d_eq(mallctlbymib(mib, miblen, &old_limit, &sz, NULL, 0), 0,
+ expect_d_eq(mallctlbymib(mib, miblen, &old_limit, &sz, NULL, 0), 0,
"Unexpected mallctl() failure");
- assert_zu_eq(old_limit, PAGE,
+ expect_zu_eq(old_limit, PAGE,
"Unexpected value for retain_grow_limit");
/* Expect grow less than psize class 10. */
new_limit = sz_pind2sz(10) - 1;
- assert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, &new_limit,
+ expect_d_eq(mallctlbymib(mib, miblen, NULL, NULL, &new_limit,
sizeof(new_limit)), 0, "Unexpected mallctl() failure");
- assert_d_eq(mallctlbymib(mib, miblen, &old_limit, &sz, NULL, 0), 0,
+ expect_d_eq(mallctlbymib(mib, miblen, &old_limit, &sz, NULL, 0), 0,
"Unexpected mallctl() failure");
- assert_zu_eq(old_limit, sz_pind2sz(9),
+ expect_zu_eq(old_limit, sz_pind2sz(9),
"Unexpected value for retain_grow_limit");
/* Restore to default. */
- assert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, &default_limit,
+ expect_d_eq(mallctlbymib(mib, miblen, NULL, NULL, &default_limit,
sizeof(default_limit)), 0, "Unexpected mallctl() failure");
}
TEST_END
@@ -616,17 +761,17 @@ TEST_BEGIN(test_arenas_dirty_decay_ms) {
ssize_t dirty_decay_ms, orig_dirty_decay_ms, prev_dirty_decay_ms;
size_t sz = sizeof(ssize_t);
- assert_d_eq(mallctl("arenas.dirty_decay_ms",
+ expect_d_eq(mallctl("arenas.dirty_decay_ms",
(void *)&orig_dirty_decay_ms, &sz, NULL, 0), 0,
"Unexpected mallctl() failure");
dirty_decay_ms = -2;
- assert_d_eq(mallctl("arenas.dirty_decay_ms", NULL, NULL,
+ expect_d_eq(mallctl("arenas.dirty_decay_ms", NULL, NULL,
(void *)&dirty_decay_ms, sizeof(ssize_t)), EFAULT,
"Unexpected mallctl() success");
dirty_decay_ms = 0x7fffffff;
- assert_d_eq(mallctl("arenas.dirty_decay_ms", NULL, NULL,
+ expect_d_eq(mallctl("arenas.dirty_decay_ms", NULL, NULL,
(void *)&dirty_decay_ms, sizeof(ssize_t)), 0,
"Expected mallctl() failure");
@@ -635,10 +780,10 @@ TEST_BEGIN(test_arenas_dirty_decay_ms) {
dirty_decay_ms++) {
ssize_t old_dirty_decay_ms;
- assert_d_eq(mallctl("arenas.dirty_decay_ms",
+ expect_d_eq(mallctl("arenas.dirty_decay_ms",
(void *)&old_dirty_decay_ms, &sz, (void *)&dirty_decay_ms,
sizeof(ssize_t)), 0, "Unexpected mallctl() failure");
- assert_zd_eq(old_dirty_decay_ms, prev_dirty_decay_ms,
+ expect_zd_eq(old_dirty_decay_ms, prev_dirty_decay_ms,
"Unexpected old arenas.dirty_decay_ms");
}
}
@@ -648,17 +793,17 @@ TEST_BEGIN(test_arenas_muzzy_decay_ms) {
ssize_t muzzy_decay_ms, orig_muzzy_decay_ms, prev_muzzy_decay_ms;
size_t sz = sizeof(ssize_t);
- assert_d_eq(mallctl("arenas.muzzy_decay_ms",
+ expect_d_eq(mallctl("arenas.muzzy_decay_ms",
(void *)&orig_muzzy_decay_ms, &sz, NULL, 0), 0,
"Unexpected mallctl() failure");
muzzy_decay_ms = -2;
- assert_d_eq(mallctl("arenas.muzzy_decay_ms", NULL, NULL,
+ expect_d_eq(mallctl("arenas.muzzy_decay_ms", NULL, NULL,
(void *)&muzzy_decay_ms, sizeof(ssize_t)), EFAULT,
"Unexpected mallctl() success");
muzzy_decay_ms = 0x7fffffff;
- assert_d_eq(mallctl("arenas.muzzy_decay_ms", NULL, NULL,
+ expect_d_eq(mallctl("arenas.muzzy_decay_ms", NULL, NULL,
(void *)&muzzy_decay_ms, sizeof(ssize_t)), 0,
"Expected mallctl() failure");
@@ -667,10 +812,10 @@ TEST_BEGIN(test_arenas_muzzy_decay_ms) {
muzzy_decay_ms++) {
ssize_t old_muzzy_decay_ms;
- assert_d_eq(mallctl("arenas.muzzy_decay_ms",
+ expect_d_eq(mallctl("arenas.muzzy_decay_ms",
(void *)&old_muzzy_decay_ms, &sz, (void *)&muzzy_decay_ms,
sizeof(ssize_t)), 0, "Unexpected mallctl() failure");
- assert_zd_eq(old_muzzy_decay_ms, prev_muzzy_decay_ms,
+ expect_zd_eq(old_muzzy_decay_ms, prev_muzzy_decay_ms,
"Unexpected old arenas.muzzy_decay_ms");
}
}
@@ -680,9 +825,9 @@ TEST_BEGIN(test_arenas_constants) {
#define TEST_ARENAS_CONSTANT(t, name, expected) do { \
t name; \
size_t sz = sizeof(t); \
- assert_d_eq(mallctl("arenas."#name, (void *)&name, &sz, NULL, \
+ expect_d_eq(mallctl("arenas."#name, (void *)&name, &sz, NULL, \
0), 0, "Unexpected mallctl() failure"); \
- assert_zu_eq(name, expected, "Incorrect "#name" size"); \
+ expect_zu_eq(name, expected, "Incorrect "#name" size"); \
} while (0)
TEST_ARENAS_CONSTANT(size_t, quantum, QUANTUM);
@@ -698,9 +843,9 @@ TEST_BEGIN(test_arenas_bin_constants) {
#define TEST_ARENAS_BIN_CONSTANT(t, name, expected) do { \
t name; \
size_t sz = sizeof(t); \
- assert_d_eq(mallctl("arenas.bin.0."#name, (void *)&name, &sz, \
+ expect_d_eq(mallctl("arenas.bin.0."#name, (void *)&name, &sz, \
NULL, 0), 0, "Unexpected mallctl() failure"); \
- assert_zu_eq(name, expected, "Incorrect "#name" size"); \
+ expect_zu_eq(name, expected, "Incorrect "#name" size"); \
} while (0)
TEST_ARENAS_BIN_CONSTANT(size_t, size, bin_infos[0].reg_size);
@@ -717,9 +862,9 @@ TEST_BEGIN(test_arenas_lextent_constants) {
#define TEST_ARENAS_LEXTENT_CONSTANT(t, name, expected) do { \
t name; \
size_t sz = sizeof(t); \
- assert_d_eq(mallctl("arenas.lextent.0."#name, (void *)&name, \
+ expect_d_eq(mallctl("arenas.lextent.0."#name, (void *)&name, \
&sz, NULL, 0), 0, "Unexpected mallctl() failure"); \
- assert_zu_eq(name, expected, "Incorrect "#name" size"); \
+ expect_zu_eq(name, expected, "Incorrect "#name" size"); \
} while (0)
TEST_ARENAS_LEXTENT_CONSTANT(size_t, size,
@@ -733,16 +878,16 @@ TEST_BEGIN(test_arenas_create) {
unsigned narenas_before, arena, narenas_after;
size_t sz = sizeof(unsigned);
- assert_d_eq(mallctl("arenas.narenas", (void *)&narenas_before, &sz,
+ expect_d_eq(mallctl("arenas.narenas", (void *)&narenas_before, &sz,
NULL, 0), 0, "Unexpected mallctl() failure");
- assert_d_eq(mallctl("arenas.create", (void *)&arena, &sz, NULL, 0), 0,
+ expect_d_eq(mallctl("arenas.create", (void *)&arena, &sz, NULL, 0), 0,
"Unexpected mallctl() failure");
- assert_d_eq(mallctl("arenas.narenas", (void *)&narenas_after, &sz, NULL,
+ expect_d_eq(mallctl("arenas.narenas", (void *)&narenas_after, &sz, NULL,
0), 0, "Unexpected mallctl() failure");
- assert_u_eq(narenas_before+1, narenas_after,
+ expect_u_eq(narenas_before+1, narenas_after,
"Unexpected number of arenas before versus after extension");
- assert_u_eq(arena, narenas_after-1, "Unexpected arena index");
+ expect_u_eq(arena, narenas_after-1, "Unexpected arena index");
}
TEST_END
@@ -751,22 +896,49 @@ TEST_BEGIN(test_arenas_lookup) {
void *ptr;
size_t sz = sizeof(unsigned);
- assert_d_eq(mallctl("arenas.create", (void *)&arena, &sz, NULL, 0), 0,
+ expect_d_eq(mallctl("arenas.create", (void *)&arena, &sz, NULL, 0), 0,
"Unexpected mallctl() failure");
ptr = mallocx(42, MALLOCX_ARENA(arena) | MALLOCX_TCACHE_NONE);
- assert_ptr_not_null(ptr, "Unexpected mallocx() failure");
- assert_d_eq(mallctl("arenas.lookup", &arena1, &sz, &ptr, sizeof(ptr)),
+ expect_ptr_not_null(ptr, "Unexpected mallocx() failure");
+ expect_d_eq(mallctl("arenas.lookup", &arena1, &sz, &ptr, sizeof(ptr)),
0, "Unexpected mallctl() failure");
- assert_u_eq(arena, arena1, "Unexpected arena index");
+ expect_u_eq(arena, arena1, "Unexpected arena index");
dallocx(ptr, 0);
}
TEST_END
+TEST_BEGIN(test_prof_active) {
+ /*
+ * If config_prof is off, then the test for prof_active in
+ * test_mallctl_opt was already enough.
+ */
+ test_skip_if(!config_prof);
+ test_skip_if(opt_prof);
+
+ bool active, old;
+ size_t len = sizeof(bool);
+
+ active = true;
+ expect_d_eq(mallctl("prof.active", NULL, NULL, &active, len), ENOENT,
+ "Setting prof_active to true should fail when opt_prof is off");
+ old = true;
+ expect_d_eq(mallctl("prof.active", &old, &len, &active, len), ENOENT,
+ "Setting prof_active to true should fail when opt_prof is off");
+ expect_true(old, "old value should not be touched when mallctl fails");
+ active = false;
+ expect_d_eq(mallctl("prof.active", NULL, NULL, &active, len), 0,
+ "Setting prof_active to false should succeed when opt_prof is off");
+ expect_d_eq(mallctl("prof.active", &old, &len, &active, len), 0,
+ "Setting prof_active to false should succeed when opt_prof is off");
+ expect_false(old, "prof_active should be false when opt_prof is off");
+}
+TEST_END
+
TEST_BEGIN(test_stats_arenas) {
#define TEST_STATS_ARENAS(t, name) do { \
t name; \
size_t sz = sizeof(t); \
- assert_d_eq(mallctl("stats.arenas.0."#name, (void *)&name, &sz, \
+ expect_d_eq(mallctl("stats.arenas.0."#name, (void *)&name, &sz, \
NULL, 0), 0, "Unexpected mallctl() failure"); \
} while (0)
@@ -800,21 +972,21 @@ TEST_BEGIN(test_hooks) {
size_t sz = sizeof(handle);
int err = mallctl("experimental.hooks.install", &handle, &sz, &hooks,
sizeof(hooks));
- assert_d_eq(err, 0, "Hook installation failed");
- assert_ptr_ne(handle, NULL, "Hook installation gave null handle");
+ expect_d_eq(err, 0, "Hook installation failed");
+ expect_ptr_ne(handle, NULL, "Hook installation gave null handle");
void *ptr = mallocx(1, 0);
- assert_true(hook_called, "Alloc hook not called");
+ expect_true(hook_called, "Alloc hook not called");
hook_called = false;
free(ptr);
- assert_true(hook_called, "Free hook not called");
+ expect_true(hook_called, "Free hook not called");
err = mallctl("experimental.hooks.remove", NULL, NULL, &handle,
sizeof(handle));
- assert_d_eq(err, 0, "Hook removal failed");
+ expect_d_eq(err, 0, "Hook removal failed");
hook_called = false;
ptr = mallocx(1, 0);
free(ptr);
- assert_false(hook_called, "Hook called after removal");
+ expect_false(hook_called, "Hook called after removal");
}
TEST_END
@@ -830,27 +1002,234 @@ TEST_BEGIN(test_hooks_exhaustion) {
handle = NULL;
err = mallctl("experimental.hooks.install", &handle, &sz,
&hooks, sizeof(hooks));
- assert_d_eq(err, 0, "Error installation hooks");
- assert_ptr_ne(handle, NULL, "Got NULL handle");
+ expect_d_eq(err, 0, "Error installation hooks");
+ expect_ptr_ne(handle, NULL, "Got NULL handle");
handles[i] = handle;
}
err = mallctl("experimental.hooks.install", &handle, &sz, &hooks,
sizeof(hooks));
- assert_d_eq(err, EAGAIN, "Should have failed hook installation");
+ expect_d_eq(err, EAGAIN, "Should have failed hook installation");
for (int i = 0; i < HOOK_MAX; i++) {
err = mallctl("experimental.hooks.remove", NULL, NULL,
&handles[i], sizeof(handles[i]));
- assert_d_eq(err, 0, "Hook removal failed");
+ expect_d_eq(err, 0, "Hook removal failed");
}
/* Insertion failed, but then we removed some; it should work now. */
handle = NULL;
err = mallctl("experimental.hooks.install", &handle, &sz, &hooks,
sizeof(hooks));
- assert_d_eq(err, 0, "Hook insertion failed");
- assert_ptr_ne(handle, NULL, "Got NULL handle");
+ expect_d_eq(err, 0, "Hook insertion failed");
+ expect_ptr_ne(handle, NULL, "Got NULL handle");
err = mallctl("experimental.hooks.remove", NULL, NULL, &handle,
sizeof(handle));
- assert_d_eq(err, 0, "Hook removal failed");
+ expect_d_eq(err, 0, "Hook removal failed");
+}
+TEST_END
+
+TEST_BEGIN(test_thread_idle) {
+ /*
+ * We're cheating a little bit in this test, and inferring things about
+ * implementation internals (like tcache details). We have to;
+ * thread.idle has no guaranteed effects. We need stats to make these
+ * inferences.
+ */
+ test_skip_if(!config_stats);
+
+ int err;
+ size_t sz;
+ size_t miblen;
+
+ bool tcache_enabled = false;
+ sz = sizeof(tcache_enabled);
+ err = mallctl("thread.tcache.enabled", &tcache_enabled, &sz, NULL, 0);
+ expect_d_eq(err, 0, "");
+ test_skip_if(!tcache_enabled);
+
+ size_t tcache_max;
+ sz = sizeof(tcache_max);
+ err = mallctl("arenas.tcache_max", &tcache_max, &sz, NULL, 0);
+ expect_d_eq(err, 0, "");
+ test_skip_if(tcache_max == 0);
+
+ unsigned arena_ind;
+ sz = sizeof(arena_ind);
+ err = mallctl("thread.arena", &arena_ind, &sz, NULL, 0);
+ expect_d_eq(err, 0, "");
+
+ /* We're going to do an allocation of size 1, which we know is small. */
+ size_t mib[5];
+ miblen = sizeof(mib)/sizeof(mib[0]);
+ err = mallctlnametomib("stats.arenas.0.small.ndalloc", mib, &miblen);
+ expect_d_eq(err, 0, "");
+ mib[2] = arena_ind;
+
+ /*
+ * This alloc and dalloc should leave something in the tcache, in a
+ * small size's cache bin.
+ */
+ void *ptr = mallocx(1, 0);
+ dallocx(ptr, 0);
+
+ uint64_t epoch;
+ err = mallctl("epoch", NULL, NULL, &epoch, sizeof(epoch));
+ expect_d_eq(err, 0, "");
+
+ uint64_t small_dalloc_pre_idle;
+ sz = sizeof(small_dalloc_pre_idle);
+ err = mallctlbymib(mib, miblen, &small_dalloc_pre_idle, &sz, NULL, 0);
+ expect_d_eq(err, 0, "");
+
+ err = mallctl("thread.idle", NULL, NULL, NULL, 0);
+ expect_d_eq(err, 0, "");
+
+ err = mallctl("epoch", NULL, NULL, &epoch, sizeof(epoch));
+ expect_d_eq(err, 0, "");
+
+ uint64_t small_dalloc_post_idle;
+ sz = sizeof(small_dalloc_post_idle);
+ err = mallctlbymib(mib, miblen, &small_dalloc_post_idle, &sz, NULL, 0);
+ expect_d_eq(err, 0, "");
+
+ expect_u64_lt(small_dalloc_pre_idle, small_dalloc_post_idle,
+ "Purge didn't flush the tcache");
+}
+TEST_END
+
+TEST_BEGIN(test_thread_peak) {
+ test_skip_if(!config_stats);
+
+ /*
+ * We don't commit to any stable amount of accuracy for peak tracking
+ * (in practice, when this test was written, we made sure to be within
+ * 100k). But 10MB is big for more or less any definition of big.
+ */
+ size_t big_size = 10 * 1024 * 1024;
+ size_t small_size = 256;
+
+ void *ptr;
+ int err;
+ size_t sz;
+ uint64_t peak;
+ sz = sizeof(uint64_t);
+
+ err = mallctl("thread.peak.reset", NULL, NULL, NULL, 0);
+ expect_d_eq(err, 0, "");
+ ptr = mallocx(SC_SMALL_MAXCLASS, 0);
+ err = mallctl("thread.peak.read", &peak, &sz, NULL, 0);
+ expect_d_eq(err, 0, "");
+ expect_u64_eq(peak, SC_SMALL_MAXCLASS, "Missed an update");
+ free(ptr);
+ err = mallctl("thread.peak.read", &peak, &sz, NULL, 0);
+ expect_d_eq(err, 0, "");
+ expect_u64_eq(peak, SC_SMALL_MAXCLASS, "Freeing changed peak");
+ ptr = mallocx(big_size, 0);
+ free(ptr);
+ /*
+ * The peak should have hit big_size in the last two lines, even though
+ * the net allocated bytes has since dropped back down to zero. We
+ * should have noticed the peak change without having down any mallctl
+ * calls while net allocated bytes was high.
+ */
+ err = mallctl("thread.peak.read", &peak, &sz, NULL, 0);
+ expect_d_eq(err, 0, "");
+ expect_u64_ge(peak, big_size, "Missed a peak change.");
+
+ /* Allocate big_size, but using small allocations. */
+ size_t nallocs = big_size / small_size;
+ void **ptrs = calloc(nallocs, sizeof(void *));
+ err = mallctl("thread.peak.reset", NULL, NULL, NULL, 0);
+ expect_d_eq(err, 0, "");
+ err = mallctl("thread.peak.read", &peak, &sz, NULL, 0);
+ expect_d_eq(err, 0, "");
+ expect_u64_eq(0, peak, "Missed a reset.");
+ for (size_t i = 0; i < nallocs; i++) {
+ ptrs[i] = mallocx(small_size, 0);
+ }
+ for (size_t i = 0; i < nallocs; i++) {
+ free(ptrs[i]);
+ }
+ err = mallctl("thread.peak.read", &peak, &sz, NULL, 0);
+ expect_d_eq(err, 0, "");
+ /*
+ * We don't guarantee exactness; make sure we're within 10% of the peak,
+ * though.
+ */
+ expect_u64_ge(peak, nallocx(small_size, 0) * nallocs * 9 / 10,
+ "Missed some peak changes.");
+ expect_u64_le(peak, nallocx(small_size, 0) * nallocs * 11 / 10,
+ "Overcounted peak changes.");
+ free(ptrs);
+}
+TEST_END
+
+typedef struct activity_test_data_s activity_test_data_t;
+struct activity_test_data_s {
+ uint64_t obtained_alloc;
+ uint64_t obtained_dalloc;
+};
+
+static void
+activity_test_callback(void *uctx, uint64_t alloc, uint64_t dalloc) {
+ activity_test_data_t *test_data = (activity_test_data_t *)uctx;
+ test_data->obtained_alloc = alloc;
+ test_data->obtained_dalloc = dalloc;
+}
+
+TEST_BEGIN(test_thread_activity_callback) {
+ test_skip_if(!config_stats);
+
+ const size_t big_size = 10 * 1024 * 1024;
+ void *ptr;
+ int err;
+ size_t sz;
+
+ uint64_t *allocatedp;
+ uint64_t *deallocatedp;
+ sz = sizeof(allocatedp);
+ err = mallctl("thread.allocatedp", &allocatedp, &sz, NULL, 0);
+ assert_d_eq(0, err, "");
+ err = mallctl("thread.deallocatedp", &deallocatedp, &sz, NULL, 0);
+ assert_d_eq(0, err, "");
+
+ activity_callback_thunk_t old_thunk = {(activity_callback_t)111,
+ (void *)222};
+
+ activity_test_data_t test_data = {333, 444};
+ activity_callback_thunk_t new_thunk =
+ {&activity_test_callback, &test_data};
+
+ sz = sizeof(old_thunk);
+ err = mallctl("experimental.thread.activity_callback", &old_thunk, &sz,
+ &new_thunk, sizeof(new_thunk));
+ assert_d_eq(0, err, "");
+
+ expect_true(old_thunk.callback == NULL, "Callback already installed");
+ expect_true(old_thunk.uctx == NULL, "Callback data already installed");
+
+ ptr = mallocx(big_size, 0);
+ expect_u64_eq(test_data.obtained_alloc, *allocatedp, "");
+ expect_u64_eq(test_data.obtained_dalloc, *deallocatedp, "");
+
+ free(ptr);
+ expect_u64_eq(test_data.obtained_alloc, *allocatedp, "");
+ expect_u64_eq(test_data.obtained_dalloc, *deallocatedp, "");
+
+ sz = sizeof(old_thunk);
+ new_thunk = (activity_callback_thunk_t){ NULL, NULL };
+ err = mallctl("experimental.thread.activity_callback", &old_thunk, &sz,
+ &new_thunk, sizeof(new_thunk));
+ assert_d_eq(0, err, "");
+
+ expect_true(old_thunk.callback == &activity_test_callback, "");
+ expect_true(old_thunk.uctx == &test_data, "");
+
+ /* Inserting NULL should have turned off tracking. */
+ test_data.obtained_alloc = 333;
+ test_data.obtained_dalloc = 444;
+ ptr = mallocx(big_size, 0);
+ free(ptr);
+ expect_u64_eq(333, test_data.obtained_alloc, "");
+ expect_u64_eq(444, test_data.obtained_dalloc, "");
}
TEST_END
@@ -862,6 +1241,9 @@ main(void) {
test_mallctlbymib_errors,
test_mallctl_read_write,
test_mallctlnametomib_short_mib,
+ test_mallctlnametomib_short_name,
+ test_mallctlmibnametomib,
+ test_mallctlbymibname,
test_mallctl_config,
test_mallctl_opt,
test_manpage_example,
@@ -882,7 +1264,11 @@ main(void) {
test_arenas_lextent_constants,
test_arenas_create,
test_arenas_lookup,
+ test_prof_active,
test_stats_arenas,
test_hooks,
- test_hooks_exhaustion);
+ test_hooks_exhaustion,
+ test_thread_idle,
+ test_thread_peak,
+ test_thread_activity_callback);
}
diff --git a/deps/jemalloc/test/unit/malloc_conf_2.c b/deps/jemalloc/test/unit/malloc_conf_2.c
new file mode 100644
index 000000000..ecfa4991c
--- /dev/null
+++ b/deps/jemalloc/test/unit/malloc_conf_2.c
@@ -0,0 +1,29 @@
+#include "test/jemalloc_test.h"
+
+const char *malloc_conf = "dirty_decay_ms:1000";
+const char *malloc_conf_2_conf_harder = "dirty_decay_ms:1234";
+
+TEST_BEGIN(test_malloc_conf_2) {
+#ifdef _WIN32
+ bool windows = true;
+#else
+ bool windows = false;
+#endif
+ /* Windows doesn't support weak symbol linker trickery. */
+ test_skip_if(windows);
+
+ ssize_t dirty_decay_ms;
+ size_t sz = sizeof(dirty_decay_ms);
+
+ int err = mallctl("opt.dirty_decay_ms", &dirty_decay_ms, &sz, NULL, 0);
+ assert_d_eq(err, 0, "Unexpected mallctl failure");
+ expect_zd_eq(dirty_decay_ms, 1234,
+ "malloc_conf_2 setting didn't take effect");
+}
+TEST_END
+
+int
+main(void) {
+ return test(
+ test_malloc_conf_2);
+}
diff --git a/deps/jemalloc/test/unit/malloc_conf_2.sh b/deps/jemalloc/test/unit/malloc_conf_2.sh
new file mode 100644
index 000000000..2c780f1a2
--- /dev/null
+++ b/deps/jemalloc/test/unit/malloc_conf_2.sh
@@ -0,0 +1 @@
+export MALLOC_CONF="dirty_decay_ms:500"
diff --git a/deps/jemalloc/test/unit/malloc_io.c b/deps/jemalloc/test/unit/malloc_io.c
index 79ba7fc53..385f7450e 100644
--- a/deps/jemalloc/test/unit/malloc_io.c
+++ b/deps/jemalloc/test/unit/malloc_io.c
@@ -4,9 +4,9 @@ TEST_BEGIN(test_malloc_strtoumax_no_endptr) {
int err;
set_errno(0);
- assert_ju_eq(malloc_strtoumax("0", NULL, 0), 0, "Unexpected result");
+ expect_ju_eq(malloc_strtoumax("0", NULL, 0), 0, "Unexpected result");
err = get_errno();
- assert_d_eq(err, 0, "Unexpected failure");
+ expect_d_eq(err, 0, "Unexpected failure");
}
TEST_END
@@ -89,14 +89,14 @@ TEST_BEGIN(test_malloc_strtoumax) {
set_errno(0);
result = malloc_strtoumax(test->input, &remainder, test->base);
err = get_errno();
- assert_d_eq(err, test->expected_errno,
+ expect_d_eq(err, test->expected_errno,
"Expected errno %s for \"%s\", base %d",
test->expected_errno_name, test->input, test->base);
- assert_str_eq(remainder, test->expected_remainder,
+ expect_str_eq(remainder, test->expected_remainder,
"Unexpected remainder for \"%s\", base %d",
test->input, test->base);
if (err == 0) {
- assert_ju_eq(result, test->expected_x,
+ expect_ju_eq(result, test->expected_x,
"Unexpected result for \"%s\", base %d",
test->input, test->base);
}
@@ -111,10 +111,10 @@ TEST_BEGIN(test_malloc_snprintf_truncated) {
size_t len;
#define TEST(expected_str_untruncated, ...) do { \
result = malloc_snprintf(buf, len, __VA_ARGS__); \
- assert_d_eq(strncmp(buf, expected_str_untruncated, len-1), 0, \
+ expect_d_eq(strncmp(buf, expected_str_untruncated, len-1), 0, \
"Unexpected string inequality (\"%s\" vs \"%s\")", \
buf, expected_str_untruncated); \
- assert_zu_eq(result, strlen(expected_str_untruncated), \
+ expect_zu_eq(result, strlen(expected_str_untruncated), \
"Unexpected result"); \
} while (0)
@@ -142,8 +142,8 @@ TEST_BEGIN(test_malloc_snprintf) {
size_t result;
#define TEST(expected_str, ...) do { \
result = malloc_snprintf(buf, sizeof(buf), __VA_ARGS__); \
- assert_str_eq(buf, expected_str, "Unexpected output"); \
- assert_zu_eq(result, strlen(expected_str), "Unexpected result");\
+ expect_str_eq(buf, expected_str, "Unexpected output"); \
+ expect_zu_eq(result, strlen(expected_str), "Unexpected result");\
} while (0)
TEST("hello", "hello");
@@ -175,6 +175,7 @@ TEST_BEGIN(test_malloc_snprintf) {
TEST("_1234_", "_%o_", 01234);
TEST("_01234_", "_%#o_", 01234);
TEST("_1234_", "_%u_", 1234);
+ TEST("01234", "%05u", 1234);
TEST("_1234_", "_%d_", 1234);
TEST("_ 1234_", "_% d_", 1234);
@@ -183,6 +184,15 @@ TEST_BEGIN(test_malloc_snprintf) {
TEST("_-1234_", "_% d_", -1234);
TEST("_-1234_", "_%+d_", -1234);
+ /*
+ * Morally, we should test these too, but 0-padded signed types are not
+ * yet supported.
+ *
+ * TEST("01234", "%05", 1234);
+ * TEST("-1234", "%05d", -1234);
+ * TEST("-01234", "%06d", -1234);
+ */
+
TEST("_-1234_", "_%d_", -1234);
TEST("_1234_", "_%d_", 1234);
TEST("_-1234_", "_%i_", -1234);
diff --git a/deps/jemalloc/test/unit/math.c b/deps/jemalloc/test/unit/math.c
index 09ef20c7b..a32767c53 100644
--- a/deps/jemalloc/test/unit/math.c
+++ b/deps/jemalloc/test/unit/math.c
@@ -41,7 +41,7 @@ TEST_BEGIN(test_ln_gamma_factorial) {
/* exp(ln_gamma(x)) == (x-1)! for integer x. */
for (x = 1; x <= 21; x++) {
- assert_true(double_eq_rel(exp(ln_gamma(x)),
+ expect_true(double_eq_rel(exp(ln_gamma(x)),
(double)factorial(x-1), MAX_REL_ERR, MAX_ABS_ERR),
"Incorrect factorial result for x=%u", x);
}
@@ -192,7 +192,7 @@ TEST_BEGIN(test_ln_gamma_misc) {
for (i = 1; i < sizeof(ln_gamma_misc_expected)/sizeof(double); i++) {
double x = (double)i * 0.25;
- assert_true(double_eq_rel(ln_gamma(x),
+ expect_true(double_eq_rel(ln_gamma(x),
ln_gamma_misc_expected[i], MAX_REL_ERR, MAX_ABS_ERR),
"Incorrect ln_gamma result for i=%u", i);
}
@@ -242,7 +242,7 @@ TEST_BEGIN(test_pt_norm) {
for (i = 1; i < sizeof(pt_norm_expected)/sizeof(double); i++) {
double p = (double)i * 0.01;
- assert_true(double_eq_rel(pt_norm(p), pt_norm_expected[i],
+ expect_true(double_eq_rel(pt_norm(p), pt_norm_expected[i],
MAX_REL_ERR, MAX_ABS_ERR),
"Incorrect pt_norm result for i=%u", i);
}
@@ -295,7 +295,7 @@ TEST_BEGIN(test_pt_chi2) {
double ln_gamma_df = ln_gamma(df * 0.5);
for (j = 1; j < 100; j += 7) {
double p = (double)j * 0.01;
- assert_true(double_eq_rel(pt_chi2(p, df, ln_gamma_df),
+ expect_true(double_eq_rel(pt_chi2(p, df, ln_gamma_df),
pt_chi2_expected[e], MAX_REL_ERR, MAX_ABS_ERR),
"Incorrect pt_chi2 result for i=%u, j=%u", i, j);
e++;
@@ -356,7 +356,7 @@ TEST_BEGIN(test_pt_gamma_shape) {
double ln_gamma_shape = ln_gamma(shape);
for (j = 1; j < 100; j += 7) {
double p = (double)j * 0.01;
- assert_true(double_eq_rel(pt_gamma(p, shape, 1.0,
+ expect_true(double_eq_rel(pt_gamma(p, shape, 1.0,
ln_gamma_shape), pt_gamma_expected[e], MAX_REL_ERR,
MAX_ABS_ERR),
"Incorrect pt_gamma result for i=%u, j=%u", i, j);
@@ -370,7 +370,7 @@ TEST_BEGIN(test_pt_gamma_scale) {
double shape = 1.0;
double ln_gamma_shape = ln_gamma(shape);
- assert_true(double_eq_rel(
+ expect_true(double_eq_rel(
pt_gamma(0.5, shape, 1.0, ln_gamma_shape) * 10.0,
pt_gamma(0.5, shape, 10.0, ln_gamma_shape), MAX_REL_ERR,
MAX_ABS_ERR),
diff --git a/deps/jemalloc/test/unit/mpsc_queue.c b/deps/jemalloc/test/unit/mpsc_queue.c
new file mode 100644
index 000000000..895edf840
--- /dev/null
+++ b/deps/jemalloc/test/unit/mpsc_queue.c
@@ -0,0 +1,304 @@
+#include "test/jemalloc_test.h"
+
+#include "jemalloc/internal/mpsc_queue.h"
+
+typedef struct elem_s elem_t;
+typedef ql_head(elem_t) elem_list_t;
+typedef mpsc_queue(elem_t) elem_mpsc_queue_t;
+struct elem_s {
+ int thread;
+ int idx;
+ ql_elm(elem_t) link;
+};
+
+/* Include both proto and gen to make sure they match up. */
+mpsc_queue_proto(static, elem_mpsc_queue_, elem_mpsc_queue_t, elem_t,
+ elem_list_t);
+mpsc_queue_gen(static, elem_mpsc_queue_, elem_mpsc_queue_t, elem_t,
+ elem_list_t, link);
+
+static void
+init_elems_simple(elem_t *elems, int nelems, int thread) {
+ for (int i = 0; i < nelems; i++) {
+ elems[i].thread = thread;
+ elems[i].idx = i;
+ ql_elm_new(&elems[i], link);
+ }
+}
+
+static void
+check_elems_simple(elem_list_t *list, int nelems, int thread) {
+ elem_t *elem;
+ int next_idx = 0;
+ ql_foreach(elem, list, link) {
+ expect_d_lt(next_idx, nelems, "Too many list items");
+ expect_d_eq(thread, elem->thread, "");
+ expect_d_eq(next_idx, elem->idx, "List out of order");
+ next_idx++;
+ }
+}
+
+TEST_BEGIN(test_simple) {
+ enum {NELEMS = 10};
+ elem_t elems[NELEMS];
+ elem_list_t list;
+ elem_mpsc_queue_t queue;
+
+ /* Pop empty queue onto empty list -> empty list */
+ ql_new(&list);
+ elem_mpsc_queue_new(&queue);
+ elem_mpsc_queue_pop_batch(&queue, &list);
+ expect_true(ql_empty(&list), "");
+
+ /* Pop empty queue onto nonempty list -> list unchanged */
+ ql_new(&list);
+ elem_mpsc_queue_new(&queue);
+ init_elems_simple(elems, NELEMS, 0);
+ for (int i = 0; i < NELEMS; i++) {
+ ql_tail_insert(&list, &elems[i], link);
+ }
+ elem_mpsc_queue_pop_batch(&queue, &list);
+ check_elems_simple(&list, NELEMS, 0);
+
+ /* Pop nonempty queue onto empty list -> list takes queue contents */
+ ql_new(&list);
+ elem_mpsc_queue_new(&queue);
+ init_elems_simple(elems, NELEMS, 0);
+ for (int i = 0; i < NELEMS; i++) {
+ elem_mpsc_queue_push(&queue, &elems[i]);
+ }
+ elem_mpsc_queue_pop_batch(&queue, &list);
+ check_elems_simple(&list, NELEMS, 0);
+
+ /* Pop nonempty queue onto nonempty list -> list gains queue contents */
+ ql_new(&list);
+ elem_mpsc_queue_new(&queue);
+ init_elems_simple(elems, NELEMS, 0);
+ for (int i = 0; i < NELEMS / 2; i++) {
+ ql_tail_insert(&list, &elems[i], link);
+ }
+ for (int i = NELEMS / 2; i < NELEMS; i++) {
+ elem_mpsc_queue_push(&queue, &elems[i]);
+ }
+ elem_mpsc_queue_pop_batch(&queue, &list);
+ check_elems_simple(&list, NELEMS, 0);
+
+}
+TEST_END
+
+TEST_BEGIN(test_push_single_or_batch) {
+ enum {
+ BATCH_MAX = 10,
+ /*
+ * We'll push i items one-at-a-time, then i items as a batch,
+ * then i items as a batch again, as i ranges from 1 to
+ * BATCH_MAX. So we need 3 times the sum of the numbers from 1
+ * to BATCH_MAX elements total.
+ */
+ NELEMS = 3 * BATCH_MAX * (BATCH_MAX - 1) / 2
+ };
+ elem_t elems[NELEMS];
+ init_elems_simple(elems, NELEMS, 0);
+ elem_list_t list;
+ ql_new(&list);
+ elem_mpsc_queue_t queue;
+ elem_mpsc_queue_new(&queue);
+ int next_idx = 0;
+ for (int i = 1; i < 10; i++) {
+ /* Push i items 1 at a time. */
+ for (int j = 0; j < i; j++) {
+ elem_mpsc_queue_push(&queue, &elems[next_idx]);
+ next_idx++;
+ }
+ /* Push i items in batch. */
+ for (int j = 0; j < i; j++) {
+ ql_tail_insert(&list, &elems[next_idx], link);
+ next_idx++;
+ }
+ elem_mpsc_queue_push_batch(&queue, &list);
+ expect_true(ql_empty(&list), "Batch push should empty source");
+ /*
+ * Push i items in batch, again. This tests two batches
+ * proceeding one after the other.
+ */
+ for (int j = 0; j < i; j++) {
+ ql_tail_insert(&list, &elems[next_idx], link);
+ next_idx++;
+ }
+ elem_mpsc_queue_push_batch(&queue, &list);
+ expect_true(ql_empty(&list), "Batch push should empty source");
+ }
+ expect_d_eq(NELEMS, next_idx, "Miscomputed number of elems to push.");
+
+ expect_true(ql_empty(&list), "");
+ elem_mpsc_queue_pop_batch(&queue, &list);
+ check_elems_simple(&list, NELEMS, 0);
+}
+TEST_END
+
+TEST_BEGIN(test_multi_op) {
+ enum {NELEMS = 20};
+ elem_t elems[NELEMS];
+ init_elems_simple(elems, NELEMS, 0);
+ elem_list_t push_list;
+ ql_new(&push_list);
+ elem_list_t result_list;
+ ql_new(&result_list);
+ elem_mpsc_queue_t queue;
+ elem_mpsc_queue_new(&queue);
+
+ int next_idx = 0;
+ /* Push first quarter 1-at-a-time. */
+ for (int i = 0; i < NELEMS / 4; i++) {
+ elem_mpsc_queue_push(&queue, &elems[next_idx]);
+ next_idx++;
+ }
+ /* Push second quarter in batch. */
+ for (int i = NELEMS / 4; i < NELEMS / 2; i++) {
+ ql_tail_insert(&push_list, &elems[next_idx], link);
+ next_idx++;
+ }
+ elem_mpsc_queue_push_batch(&queue, &push_list);
+ /* Batch pop all pushed elements. */
+ elem_mpsc_queue_pop_batch(&queue, &result_list);
+ /* Push third quarter in batch. */
+ for (int i = NELEMS / 2; i < 3 * NELEMS / 4; i++) {
+ ql_tail_insert(&push_list, &elems[next_idx], link);
+ next_idx++;
+ }
+ elem_mpsc_queue_push_batch(&queue, &push_list);
+ /* Push last quarter one-at-a-time. */
+ for (int i = 3 * NELEMS / 4; i < NELEMS; i++) {
+ elem_mpsc_queue_push(&queue, &elems[next_idx]);
+ next_idx++;
+ }
+ /* Pop them again. Order of existing list should be preserved. */
+ elem_mpsc_queue_pop_batch(&queue, &result_list);
+
+ check_elems_simple(&result_list, NELEMS, 0);
+
+}
+TEST_END
+
+typedef struct pusher_arg_s pusher_arg_t;
+struct pusher_arg_s {
+ elem_mpsc_queue_t *queue;
+ int thread;
+ elem_t *elems;
+ int nelems;
+};
+
+typedef struct popper_arg_s popper_arg_t;
+struct popper_arg_s {
+ elem_mpsc_queue_t *queue;
+ int npushers;
+ int nelems_per_pusher;
+ int *pusher_counts;
+};
+
+static void *
+thd_pusher(void *void_arg) {
+ pusher_arg_t *arg = (pusher_arg_t *)void_arg;
+ int next_idx = 0;
+ while (next_idx < arg->nelems) {
+ /* Push 10 items in batch. */
+ elem_list_t list;
+ ql_new(&list);
+ int limit = next_idx + 10;
+ while (next_idx < arg->nelems && next_idx < limit) {
+ ql_tail_insert(&list, &arg->elems[next_idx], link);
+ next_idx++;
+ }
+ elem_mpsc_queue_push_batch(arg->queue, &list);
+ /* Push 10 items one-at-a-time. */
+ limit = next_idx + 10;
+ while (next_idx < arg->nelems && next_idx < limit) {
+ elem_mpsc_queue_push(arg->queue, &arg->elems[next_idx]);
+ next_idx++;
+ }
+
+ }
+ return NULL;
+}
+
+static void *
+thd_popper(void *void_arg) {
+ popper_arg_t *arg = (popper_arg_t *)void_arg;
+ int done_pushers = 0;
+ while (done_pushers < arg->npushers) {
+ elem_list_t list;
+ ql_new(&list);
+ elem_mpsc_queue_pop_batch(arg->queue, &list);
+ elem_t *elem;
+ ql_foreach(elem, &list, link) {
+ int thread = elem->thread;
+ int idx = elem->idx;
+ expect_d_eq(arg->pusher_counts[thread], idx,
+ "Thread's pushes reordered");
+ arg->pusher_counts[thread]++;
+ if (arg->pusher_counts[thread]
+ == arg->nelems_per_pusher) {
+ done_pushers++;
+ }
+ }
+ }
+ return NULL;
+}
+
+TEST_BEGIN(test_multiple_threads) {
+ enum {
+ NPUSHERS = 4,
+ NELEMS_PER_PUSHER = 1000*1000,
+ };
+ thd_t pushers[NPUSHERS];
+ pusher_arg_t pusher_arg[NPUSHERS];
+
+ thd_t popper;
+ popper_arg_t popper_arg;
+
+ elem_mpsc_queue_t queue;
+ elem_mpsc_queue_new(&queue);
+
+ elem_t *elems = calloc(NPUSHERS * NELEMS_PER_PUSHER, sizeof(elem_t));
+ elem_t *elem_iter = elems;
+ for (int i = 0; i < NPUSHERS; i++) {
+ pusher_arg[i].queue = &queue;
+ pusher_arg[i].thread = i;
+ pusher_arg[i].elems = elem_iter;
+ pusher_arg[i].nelems = NELEMS_PER_PUSHER;
+
+ init_elems_simple(elem_iter, NELEMS_PER_PUSHER, i);
+ elem_iter += NELEMS_PER_PUSHER;
+ }
+ popper_arg.queue = &queue;
+ popper_arg.npushers = NPUSHERS;
+ popper_arg.nelems_per_pusher = NELEMS_PER_PUSHER;
+ int pusher_counts[NPUSHERS] = {0};
+ popper_arg.pusher_counts = pusher_counts;
+
+ thd_create(&popper, thd_popper, (void *)&popper_arg);
+ for (int i = 0; i < NPUSHERS; i++) {
+ thd_create(&pushers[i], thd_pusher, &pusher_arg[i]);
+ }
+
+ thd_join(popper, NULL);
+ for (int i = 0; i < NPUSHERS; i++) {
+ thd_join(pushers[i], NULL);
+ }
+
+ for (int i = 0; i < NPUSHERS; i++) {
+ expect_d_eq(NELEMS_PER_PUSHER, pusher_counts[i], "");
+ }
+
+ free(elems);
+}
+TEST_END
+
+int
+main(void) {
+ return test_no_reentrancy(
+ test_simple,
+ test_push_single_or_batch,
+ test_multi_op,
+ test_multiple_threads);
+}
diff --git a/deps/jemalloc/test/unit/mq.c b/deps/jemalloc/test/unit/mq.c
index 57a4d54e4..f833f77ce 100644
--- a/deps/jemalloc/test/unit/mq.c
+++ b/deps/jemalloc/test/unit/mq.c
@@ -13,17 +13,17 @@ TEST_BEGIN(test_mq_basic) {
mq_t mq;
mq_msg_t msg;
- assert_false(mq_init(&mq), "Unexpected mq_init() failure");
- assert_u_eq(mq_count(&mq), 0, "mq should be empty");
- assert_ptr_null(mq_tryget(&mq),
+ expect_false(mq_init(&mq), "Unexpected mq_init() failure");
+ expect_u_eq(mq_count(&mq), 0, "mq should be empty");
+ expect_ptr_null(mq_tryget(&mq),
"mq_tryget() should fail when the queue is empty");
mq_put(&mq, &msg);
- assert_u_eq(mq_count(&mq), 1, "mq should contain one message");
- assert_ptr_eq(mq_tryget(&mq), &msg, "mq_tryget() should return msg");
+ expect_u_eq(mq_count(&mq), 1, "mq should contain one message");
+ expect_ptr_eq(mq_tryget(&mq), &msg, "mq_tryget() should return msg");
mq_put(&mq, &msg);
- assert_ptr_eq(mq_get(&mq), &msg, "mq_get() should return msg");
+ expect_ptr_eq(mq_get(&mq), &msg, "mq_get() should return msg");
mq_fini(&mq);
}
@@ -36,7 +36,7 @@ thd_receiver_start(void *arg) {
for (i = 0; i < (NSENDERS * NMSGS); i++) {
mq_msg_t *msg = mq_get(mq);
- assert_ptr_not_null(msg, "mq_get() should never return NULL");
+ expect_ptr_not_null(msg, "mq_get() should never return NULL");
dallocx(msg, 0);
}
return NULL;
@@ -51,7 +51,7 @@ thd_sender_start(void *arg) {
mq_msg_t *msg;
void *p;
p = mallocx(sizeof(mq_msg_t), 0);
- assert_ptr_not_null(p, "Unexpected mallocx() failure");
+ expect_ptr_not_null(p, "Unexpected mallocx() failure");
msg = (mq_msg_t *)p;
mq_put(mq, msg);
}
@@ -64,7 +64,7 @@ TEST_BEGIN(test_mq_threaded) {
thd_t senders[NSENDERS];
unsigned i;
- assert_false(mq_init(&mq), "Unexpected mq_init() failure");
+ expect_false(mq_init(&mq), "Unexpected mq_init() failure");
thd_create(&receiver, thd_receiver_start, (void *)&mq);
for (i = 0; i < NSENDERS; i++) {
diff --git a/deps/jemalloc/test/unit/mtx.c b/deps/jemalloc/test/unit/mtx.c
index 424587b03..4aeebc13f 100644
--- a/deps/jemalloc/test/unit/mtx.c
+++ b/deps/jemalloc/test/unit/mtx.c
@@ -6,7 +6,7 @@
TEST_BEGIN(test_mtx_basic) {
mtx_t mtx;
- assert_false(mtx_init(&mtx), "Unexpected mtx_init() failure");
+ expect_false(mtx_init(&mtx), "Unexpected mtx_init() failure");
mtx_lock(&mtx);
mtx_unlock(&mtx);
mtx_fini(&mtx);
@@ -36,7 +36,7 @@ TEST_BEGIN(test_mtx_race) {
thd_t thds[NTHREADS];
unsigned i;
- assert_false(mtx_init(&arg.mtx), "Unexpected mtx_init() failure");
+ expect_false(mtx_init(&arg.mtx), "Unexpected mtx_init() failure");
arg.x = 0;
for (i = 0; i < NTHREADS; i++) {
thd_create(&thds[i], thd_start, (void *)&arg);
@@ -44,7 +44,7 @@ TEST_BEGIN(test_mtx_race) {
for (i = 0; i < NTHREADS; i++) {
thd_join(thds[i], NULL);
}
- assert_u_eq(arg.x, NTHREADS * NINCRS,
+ expect_u_eq(arg.x, NTHREADS * NINCRS,
"Race-related counter corruption");
}
TEST_END
diff --git a/deps/jemalloc/test/unit/nstime.c b/deps/jemalloc/test/unit/nstime.c
index f31378058..56238ab3b 100644
--- a/deps/jemalloc/test/unit/nstime.c
+++ b/deps/jemalloc/test/unit/nstime.c
@@ -6,9 +6,9 @@ TEST_BEGIN(test_nstime_init) {
nstime_t nst;
nstime_init(&nst, 42000000043);
- assert_u64_eq(nstime_ns(&nst), 42000000043, "ns incorrectly read");
- assert_u64_eq(nstime_sec(&nst), 42, "sec incorrectly read");
- assert_u64_eq(nstime_nsec(&nst), 43, "nsec incorrectly read");
+ expect_u64_eq(nstime_ns(&nst), 42000000043, "ns incorrectly read");
+ expect_u64_eq(nstime_sec(&nst), 42, "sec incorrectly read");
+ expect_u64_eq(nstime_nsec(&nst), 43, "nsec incorrectly read");
}
TEST_END
@@ -16,8 +16,8 @@ TEST_BEGIN(test_nstime_init2) {
nstime_t nst;
nstime_init2(&nst, 42, 43);
- assert_u64_eq(nstime_sec(&nst), 42, "sec incorrectly read");
- assert_u64_eq(nstime_nsec(&nst), 43, "nsec incorrectly read");
+ expect_u64_eq(nstime_sec(&nst), 42, "sec incorrectly read");
+ expect_u64_eq(nstime_nsec(&nst), 43, "nsec incorrectly read");
}
TEST_END
@@ -25,10 +25,10 @@ TEST_BEGIN(test_nstime_copy) {
nstime_t nsta, nstb;
nstime_init2(&nsta, 42, 43);
- nstime_init(&nstb, 0);
+ nstime_init_zero(&nstb);
nstime_copy(&nstb, &nsta);
- assert_u64_eq(nstime_sec(&nstb), 42, "sec incorrectly copied");
- assert_u64_eq(nstime_nsec(&nstb), 43, "nsec incorrectly copied");
+ expect_u64_eq(nstime_sec(&nstb), 42, "sec incorrectly copied");
+ expect_u64_eq(nstime_nsec(&nstb), 43, "nsec incorrectly copied");
}
TEST_END
@@ -37,31 +37,31 @@ TEST_BEGIN(test_nstime_compare) {
nstime_init2(&nsta, 42, 43);
nstime_copy(&nstb, &nsta);
- assert_d_eq(nstime_compare(&nsta, &nstb), 0, "Times should be equal");
- assert_d_eq(nstime_compare(&nstb, &nsta), 0, "Times should be equal");
+ expect_d_eq(nstime_compare(&nsta, &nstb), 0, "Times should be equal");
+ expect_d_eq(nstime_compare(&nstb, &nsta), 0, "Times should be equal");
nstime_init2(&nstb, 42, 42);
- assert_d_eq(nstime_compare(&nsta, &nstb), 1,
+ expect_d_eq(nstime_compare(&nsta, &nstb), 1,
"nsta should be greater than nstb");
- assert_d_eq(nstime_compare(&nstb, &nsta), -1,
+ expect_d_eq(nstime_compare(&nstb, &nsta), -1,
"nstb should be less than nsta");
nstime_init2(&nstb, 42, 44);
- assert_d_eq(nstime_compare(&nsta, &nstb), -1,
+ expect_d_eq(nstime_compare(&nsta, &nstb), -1,
"nsta should be less than nstb");
- assert_d_eq(nstime_compare(&nstb, &nsta), 1,
+ expect_d_eq(nstime_compare(&nstb, &nsta), 1,
"nstb should be greater than nsta");
nstime_init2(&nstb, 41, BILLION - 1);
- assert_d_eq(nstime_compare(&nsta, &nstb), 1,
+ expect_d_eq(nstime_compare(&nsta, &nstb), 1,
"nsta should be greater than nstb");
- assert_d_eq(nstime_compare(&nstb, &nsta), -1,
+ expect_d_eq(nstime_compare(&nstb, &nsta), -1,
"nstb should be less than nsta");
nstime_init2(&nstb, 43, 0);
- assert_d_eq(nstime_compare(&nsta, &nstb), -1,
+ expect_d_eq(nstime_compare(&nsta, &nstb), -1,
"nsta should be less than nstb");
- assert_d_eq(nstime_compare(&nstb, &nsta), 1,
+ expect_d_eq(nstime_compare(&nstb, &nsta), 1,
"nstb should be greater than nsta");
}
TEST_END
@@ -73,14 +73,14 @@ TEST_BEGIN(test_nstime_add) {
nstime_copy(&nstb, &nsta);
nstime_add(&nsta, &nstb);
nstime_init2(&nstb, 84, 86);
- assert_d_eq(nstime_compare(&nsta, &nstb), 0,
+ expect_d_eq(nstime_compare(&nsta, &nstb), 0,
"Incorrect addition result");
nstime_init2(&nsta, 42, BILLION - 1);
nstime_copy(&nstb, &nsta);
nstime_add(&nsta, &nstb);
nstime_init2(&nstb, 85, BILLION - 2);
- assert_d_eq(nstime_compare(&nsta, &nstb), 0,
+ expect_d_eq(nstime_compare(&nsta, &nstb), 0,
"Incorrect addition result");
}
TEST_END
@@ -91,13 +91,13 @@ TEST_BEGIN(test_nstime_iadd) {
nstime_init2(&nsta, 42, BILLION - 1);
nstime_iadd(&nsta, 1);
nstime_init2(&nstb, 43, 0);
- assert_d_eq(nstime_compare(&nsta, &nstb), 0,
+ expect_d_eq(nstime_compare(&nsta, &nstb), 0,
"Incorrect addition result");
nstime_init2(&nsta, 42, 1);
nstime_iadd(&nsta, BILLION + 1);
nstime_init2(&nstb, 43, 2);
- assert_d_eq(nstime_compare(&nsta, &nstb), 0,
+ expect_d_eq(nstime_compare(&nsta, &nstb), 0,
"Incorrect addition result");
}
TEST_END
@@ -108,15 +108,15 @@ TEST_BEGIN(test_nstime_subtract) {
nstime_init2(&nsta, 42, 43);
nstime_copy(&nstb, &nsta);
nstime_subtract(&nsta, &nstb);
- nstime_init(&nstb, 0);
- assert_d_eq(nstime_compare(&nsta, &nstb), 0,
+ nstime_init_zero(&nstb);
+ expect_d_eq(nstime_compare(&nsta, &nstb), 0,
"Incorrect subtraction result");
nstime_init2(&nsta, 42, 43);
nstime_init2(&nstb, 41, 44);
nstime_subtract(&nsta, &nstb);
nstime_init2(&nstb, 0, BILLION - 1);
- assert_d_eq(nstime_compare(&nsta, &nstb), 0,
+ expect_d_eq(nstime_compare(&nsta, &nstb), 0,
"Incorrect subtraction result");
}
TEST_END
@@ -126,14 +126,14 @@ TEST_BEGIN(test_nstime_isubtract) {
nstime_init2(&nsta, 42, 43);
nstime_isubtract(&nsta, 42*BILLION + 43);
- nstime_init(&nstb, 0);
- assert_d_eq(nstime_compare(&nsta, &nstb), 0,
+ nstime_init_zero(&nstb);
+ expect_d_eq(nstime_compare(&nsta, &nstb), 0,
"Incorrect subtraction result");
nstime_init2(&nsta, 42, 43);
nstime_isubtract(&nsta, 41*BILLION + 44);
nstime_init2(&nstb, 0, BILLION - 1);
- assert_d_eq(nstime_compare(&nsta, &nstb), 0,
+ expect_d_eq(nstime_compare(&nsta, &nstb), 0,
"Incorrect subtraction result");
}
TEST_END
@@ -144,13 +144,13 @@ TEST_BEGIN(test_nstime_imultiply) {
nstime_init2(&nsta, 42, 43);
nstime_imultiply(&nsta, 10);
nstime_init2(&nstb, 420, 430);
- assert_d_eq(nstime_compare(&nsta, &nstb), 0,
+ expect_d_eq(nstime_compare(&nsta, &nstb), 0,
"Incorrect multiplication result");
nstime_init2(&nsta, 42, 666666666);
nstime_imultiply(&nsta, 3);
nstime_init2(&nstb, 127, 999999998);
- assert_d_eq(nstime_compare(&nsta, &nstb), 0,
+ expect_d_eq(nstime_compare(&nsta, &nstb), 0,
"Incorrect multiplication result");
}
TEST_END
@@ -162,14 +162,14 @@ TEST_BEGIN(test_nstime_idivide) {
nstime_copy(&nstb, &nsta);
nstime_imultiply(&nsta, 10);
nstime_idivide(&nsta, 10);
- assert_d_eq(nstime_compare(&nsta, &nstb), 0,
+ expect_d_eq(nstime_compare(&nsta, &nstb), 0,
"Incorrect division result");
nstime_init2(&nsta, 42, 666666666);
nstime_copy(&nstb, &nsta);
nstime_imultiply(&nsta, 3);
nstime_idivide(&nsta, 3);
- assert_d_eq(nstime_compare(&nsta, &nstb), 0,
+ expect_d_eq(nstime_compare(&nsta, &nstb), 0,
"Incorrect division result");
}
TEST_END
@@ -180,7 +180,7 @@ TEST_BEGIN(test_nstime_divide) {
nstime_init2(&nsta, 42, 43);
nstime_copy(&nstb, &nsta);
nstime_imultiply(&nsta, 10);
- assert_u64_eq(nstime_divide(&nsta, &nstb), 10,
+ expect_u64_eq(nstime_divide(&nsta, &nstb), 10,
"Incorrect division result");
nstime_init2(&nsta, 42, 43);
@@ -188,7 +188,7 @@ TEST_BEGIN(test_nstime_divide) {
nstime_imultiply(&nsta, 10);
nstime_init(&nstc, 1);
nstime_add(&nsta, &nstc);
- assert_u64_eq(nstime_divide(&nsta, &nstb), 10,
+ expect_u64_eq(nstime_divide(&nsta, &nstb), 10,
"Incorrect division result");
nstime_init2(&nsta, 42, 43);
@@ -196,40 +196,43 @@ TEST_BEGIN(test_nstime_divide) {
nstime_imultiply(&nsta, 10);
nstime_init(&nstc, 1);
nstime_subtract(&nsta, &nstc);
- assert_u64_eq(nstime_divide(&nsta, &nstb), 9,
+ expect_u64_eq(nstime_divide(&nsta, &nstb), 9,
"Incorrect division result");
}
TEST_END
-TEST_BEGIN(test_nstime_monotonic) {
- nstime_monotonic();
-}
-TEST_END
+void
+test_nstime_since_once(nstime_t *t) {
+ nstime_t old_t;
+ nstime_copy(&old_t, t);
-TEST_BEGIN(test_nstime_update) {
- nstime_t nst;
+ uint64_t ns_since = nstime_ns_since(t);
+ nstime_update(t);
- nstime_init(&nst, 0);
+ nstime_t new_t;
+ nstime_copy(&new_t, t);
+ nstime_subtract(&new_t, &old_t);
- assert_false(nstime_update(&nst), "Basic time update failed.");
+ expect_u64_ge(nstime_ns(&new_t), ns_since,
+ "Incorrect time since result");
+}
- /* Only Rip Van Winkle sleeps this long. */
- {
- nstime_t addend;
- nstime_init2(&addend, 631152000, 0);
- nstime_add(&nst, &addend);
- }
- {
- nstime_t nst0;
- nstime_copy(&nst0, &nst);
- assert_true(nstime_update(&nst),
- "Update should detect time roll-back.");
- assert_d_eq(nstime_compare(&nst, &nst0), 0,
- "Time should not have been modified");
+TEST_BEGIN(test_nstime_ns_since) {
+ nstime_t t;
+
+ nstime_init_update(&t);
+ for (uint64_t i = 0; i < 10000; i++) {
+ /* Keeps updating t and verifies ns_since is valid. */
+ test_nstime_since_once(&t);
}
}
TEST_END
+TEST_BEGIN(test_nstime_monotonic) {
+ nstime_monotonic();
+}
+TEST_END
+
int
main(void) {
return test(
@@ -244,6 +247,6 @@ main(void) {
test_nstime_imultiply,
test_nstime_idivide,
test_nstime_divide,
- test_nstime_monotonic,
- test_nstime_update);
+ test_nstime_ns_since,
+ test_nstime_monotonic);
}
diff --git a/deps/jemalloc/test/unit/oversize_threshold.c b/deps/jemalloc/test/unit/oversize_threshold.c
new file mode 100644
index 000000000..44a8f76a4
--- /dev/null
+++ b/deps/jemalloc/test/unit/oversize_threshold.c
@@ -0,0 +1,133 @@
+#include "test/jemalloc_test.h"
+
+#include "jemalloc/internal/ctl.h"
+
+static void
+arena_mallctl(const char *mallctl_str, unsigned arena, void *oldp,
+ size_t *oldlen, void *newp, size_t newlen) {
+ int err;
+ char buf[100];
+ malloc_snprintf(buf, sizeof(buf), mallctl_str, arena);
+
+ err = mallctl(buf, oldp, oldlen, newp, newlen);
+ expect_d_eq(0, err, "Mallctl failed; %s", buf);
+}
+
+TEST_BEGIN(test_oversize_threshold_get_set) {
+ int err;
+ size_t old_threshold;
+ size_t new_threshold;
+ size_t threshold_sz = sizeof(old_threshold);
+
+ unsigned arena;
+ size_t arena_sz = sizeof(arena);
+ err = mallctl("arenas.create", (void *)&arena, &arena_sz, NULL, 0);
+ expect_d_eq(0, err, "Arena creation failed");
+
+ /* Just a write. */
+ new_threshold = 1024 * 1024;
+ arena_mallctl("arena.%u.oversize_threshold", arena, NULL, NULL,
+ &new_threshold, threshold_sz);
+
+ /* Read and write */
+ new_threshold = 2 * 1024 * 1024;
+ arena_mallctl("arena.%u.oversize_threshold", arena, &old_threshold,
+ &threshold_sz, &new_threshold, threshold_sz);
+ expect_zu_eq(1024 * 1024, old_threshold, "Should have read old value");
+
+ /* Just a read */
+ arena_mallctl("arena.%u.oversize_threshold", arena, &old_threshold,
+ &threshold_sz, NULL, 0);
+ expect_zu_eq(2 * 1024 * 1024, old_threshold, "Should have read old value");
+}
+TEST_END
+
+static size_t max_purged = 0;
+static bool
+purge_forced_record_max(extent_hooks_t* hooks, void *addr, size_t sz,
+ size_t offset, size_t length, unsigned arena_ind) {
+ if (length > max_purged) {
+ max_purged = length;
+ }
+ return false;
+}
+
+static bool
+dalloc_record_max(extent_hooks_t *extent_hooks, void *addr, size_t sz,
+ bool comitted, unsigned arena_ind) {
+ if (sz > max_purged) {
+ max_purged = sz;
+ }
+ return false;
+}
+
+extent_hooks_t max_recording_extent_hooks;
+
+TEST_BEGIN(test_oversize_threshold) {
+ max_recording_extent_hooks = ehooks_default_extent_hooks;
+ max_recording_extent_hooks.purge_forced = &purge_forced_record_max;
+ max_recording_extent_hooks.dalloc = &dalloc_record_max;
+
+ extent_hooks_t *extent_hooks = &max_recording_extent_hooks;
+
+ int err;
+
+ unsigned arena;
+ size_t arena_sz = sizeof(arena);
+ err = mallctl("arenas.create", (void *)&arena, &arena_sz, NULL, 0);
+ expect_d_eq(0, err, "Arena creation failed");
+ arena_mallctl("arena.%u.extent_hooks", arena, NULL, NULL, &extent_hooks,
+ sizeof(extent_hooks));
+
+ /*
+ * This test will fundamentally race with purging, since we're going to
+ * check the dirty stats to see if our oversized allocation got purged.
+ * We don't want other purging to happen accidentally. We can't just
+ * disable purging entirely, though, since that will also disable
+ * oversize purging. Just set purging intervals to be very large.
+ */
+ ssize_t decay_ms = 100 * 1000;
+ ssize_t decay_ms_sz = sizeof(decay_ms);
+ arena_mallctl("arena.%u.dirty_decay_ms", arena, NULL, NULL, &decay_ms,
+ decay_ms_sz);
+ arena_mallctl("arena.%u.muzzy_decay_ms", arena, NULL, NULL, &decay_ms,
+ decay_ms_sz);
+
+ /* Clean everything out. */
+ arena_mallctl("arena.%u.purge", arena, NULL, NULL, NULL, 0);
+ max_purged = 0;
+
+ /* Set threshold to 1MB. */
+ size_t threshold = 1024 * 1024;
+ size_t threshold_sz = sizeof(threshold);
+ arena_mallctl("arena.%u.oversize_threshold", arena, NULL, NULL,
+ &threshold, threshold_sz);
+
+ /* Allocating and freeing half a megabyte should leave them dirty. */
+ void *ptr = mallocx(512 * 1024, MALLOCX_ARENA(arena));
+ dallocx(ptr, MALLOCX_TCACHE_NONE);
+ if (!is_background_thread_enabled()) {
+ expect_zu_lt(max_purged, 512 * 1024, "Expected no 512k purge");
+ }
+
+ /* Purge again to reset everything out. */
+ arena_mallctl("arena.%u.purge", arena, NULL, NULL, NULL, 0);
+ max_purged = 0;
+
+ /*
+ * Allocating and freeing 2 megabytes should have them purged because of
+ * the oversize threshold.
+ */
+ ptr = mallocx(2 * 1024 * 1024, MALLOCX_ARENA(arena));
+ dallocx(ptr, MALLOCX_TCACHE_NONE);
+ expect_zu_ge(max_purged, 2 * 1024 * 1024, "Expected a 2MB purge");
+}
+TEST_END
+
+int
+main(void) {
+ return test_no_reentrancy(
+ test_oversize_threshold_get_set,
+ test_oversize_threshold);
+}
+
diff --git a/deps/jemalloc/test/unit/pa.c b/deps/jemalloc/test/unit/pa.c
new file mode 100644
index 000000000..b1e2f6e9e
--- /dev/null
+++ b/deps/jemalloc/test/unit/pa.c
@@ -0,0 +1,126 @@
+#include "test/jemalloc_test.h"
+
+#include "jemalloc/internal/pa.h"
+
+static void *
+alloc_hook(extent_hooks_t *extent_hooks, void *new_addr, size_t size,
+ size_t alignment, bool *zero, bool *commit, unsigned arena_ind) {
+ void *ret = pages_map(new_addr, size, alignment, commit);
+ return ret;
+}
+
+static bool
+merge_hook(extent_hooks_t *extent_hooks, void *addr_a, size_t size_a,
+ void *addr_b, size_t size_b, bool committed, unsigned arena_ind) {
+ return !maps_coalesce;
+}
+
+static bool
+split_hook(extent_hooks_t *extent_hooks, void *addr, size_t size,
+ size_t size_a, size_t size_b, bool committed, unsigned arena_ind) {
+ return !maps_coalesce;
+}
+
+static void
+init_test_extent_hooks(extent_hooks_t *hooks) {
+ /*
+ * The default hooks are mostly fine for testing. A few of them,
+ * though, access globals (alloc for dss setting in an arena, split and
+ * merge touch the global emap to find head state. The first of these
+ * can be fixed by keeping that state with the hooks, where it logically
+ * belongs. The second, though, we can only fix when we use the extent
+ * hook API.
+ */
+ memcpy(hooks, &ehooks_default_extent_hooks, sizeof(extent_hooks_t));
+ hooks->alloc = &alloc_hook;
+ hooks->merge = &merge_hook;
+ hooks->split = &split_hook;
+}
+
+typedef struct test_data_s test_data_t;
+struct test_data_s {
+ pa_shard_t shard;
+ pa_central_t central;
+ base_t *base;
+ emap_t emap;
+ pa_shard_stats_t stats;
+ malloc_mutex_t stats_mtx;
+ extent_hooks_t hooks;
+};
+
+test_data_t *init_test_data(ssize_t dirty_decay_ms, ssize_t muzzy_decay_ms) {
+ test_data_t *test_data = calloc(1, sizeof(test_data_t));
+ assert_ptr_not_null(test_data, "");
+ init_test_extent_hooks(&test_data->hooks);
+
+ base_t *base = base_new(TSDN_NULL, /* ind */ 1, &test_data->hooks,
+ /* metadata_use_hooks */ true);
+ assert_ptr_not_null(base, "");
+
+ test_data->base = base;
+ bool err = emap_init(&test_data->emap, test_data->base,
+ /* zeroed */ true);
+ assert_false(err, "");
+
+ nstime_t time;
+ nstime_init(&time, 0);
+
+ err = pa_central_init(&test_data->central, base, opt_hpa,
+ &hpa_hooks_default);
+ assert_false(err, "");
+
+ const size_t pa_oversize_threshold = 8 * 1024 * 1024;
+ err = pa_shard_init(TSDN_NULL, &test_data->shard, &test_data->central,
+ &test_data->emap, test_data->base, /* ind */ 1, &test_data->stats,
+ &test_data->stats_mtx, &time, pa_oversize_threshold, dirty_decay_ms,
+ muzzy_decay_ms);
+ assert_false(err, "");
+
+ return test_data;
+}
+
+void destroy_test_data(test_data_t *data) {
+ base_delete(TSDN_NULL, data->base);
+ free(data);
+}
+
+static void *
+do_alloc_free_purge(void *arg) {
+ test_data_t *test_data = (test_data_t *)arg;
+ for (int i = 0; i < 10 * 1000; i++) {
+ bool deferred_work_generated = false;
+ edata_t *edata = pa_alloc(TSDN_NULL, &test_data->shard, PAGE,
+ PAGE, /* slab */ false, /* szind */ 0, /* zero */ false,
+ /* guarded */ false, &deferred_work_generated);
+ assert_ptr_not_null(edata, "");
+ pa_dalloc(TSDN_NULL, &test_data->shard, edata,
+ &deferred_work_generated);
+ malloc_mutex_lock(TSDN_NULL,
+ &test_data->shard.pac.decay_dirty.mtx);
+ pac_decay_all(TSDN_NULL, &test_data->shard.pac,
+ &test_data->shard.pac.decay_dirty,
+ &test_data->shard.pac.stats->decay_dirty,
+ &test_data->shard.pac.ecache_dirty, true);
+ malloc_mutex_unlock(TSDN_NULL,
+ &test_data->shard.pac.decay_dirty.mtx);
+ }
+ return NULL;
+}
+
+TEST_BEGIN(test_alloc_free_purge_thds) {
+ test_data_t *test_data = init_test_data(0, 0);
+ thd_t thds[4];
+ for (int i = 0; i < 4; i++) {
+ thd_create(&thds[i], do_alloc_free_purge, test_data);
+ }
+ for (int i = 0; i < 4; i++) {
+ thd_join(thds[i], NULL);
+ }
+}
+TEST_END
+
+int
+main(void) {
+ return test(
+ test_alloc_free_purge_thds);
+}
diff --git a/deps/jemalloc/test/unit/pack.c b/deps/jemalloc/test/unit/pack.c
index fc188b003..e6392825b 100644
--- a/deps/jemalloc/test/unit/pack.c
+++ b/deps/jemalloc/test/unit/pack.c
@@ -22,7 +22,7 @@ binind_compute(void) {
unsigned nbins, i;
sz = sizeof(nbins);
- assert_d_eq(mallctl("arenas.nbins", (void *)&nbins, &sz, NULL, 0), 0,
+ expect_d_eq(mallctl("arenas.nbins", (void *)&nbins, &sz, NULL, 0), 0,
"Unexpected mallctl failure");
for (i = 0; i < nbins; i++) {
@@ -30,12 +30,12 @@ binind_compute(void) {
size_t miblen = sizeof(mib)/sizeof(size_t);
size_t size;
- assert_d_eq(mallctlnametomib("arenas.bin.0.size", mib,
+ expect_d_eq(mallctlnametomib("arenas.bin.0.size", mib,
&miblen), 0, "Unexpected mallctlnametomb failure");
mib[2] = (size_t)i;
sz = sizeof(size);
- assert_d_eq(mallctlbymib(mib, miblen, (void *)&size, &sz, NULL,
+ expect_d_eq(mallctlbymib(mib, miblen, (void *)&size, &sz, NULL,
0), 0, "Unexpected mallctlbymib failure");
if (size == SZ) {
return i;
@@ -54,11 +54,11 @@ nregs_per_run_compute(void) {
size_t mib[4];
size_t miblen = sizeof(mib)/sizeof(size_t);
- assert_d_eq(mallctlnametomib("arenas.bin.0.nregs", mib, &miblen), 0,
+ expect_d_eq(mallctlnametomib("arenas.bin.0.nregs", mib, &miblen), 0,
"Unexpected mallctlnametomb failure");
mib[2] = (size_t)binind;
sz = sizeof(nregs);
- assert_d_eq(mallctlbymib(mib, miblen, (void *)&nregs, &sz, NULL,
+ expect_d_eq(mallctlbymib(mib, miblen, (void *)&nregs, &sz, NULL,
0), 0, "Unexpected mallctlbymib failure");
return nregs;
}
@@ -69,7 +69,7 @@ arenas_create_mallctl(void) {
size_t sz;
sz = sizeof(arena_ind);
- assert_d_eq(mallctl("arenas.create", (void *)&arena_ind, &sz, NULL, 0),
+ expect_d_eq(mallctl("arenas.create", (void *)&arena_ind, &sz, NULL, 0),
0, "Error in arenas.create");
return arena_ind;
@@ -80,10 +80,10 @@ arena_reset_mallctl(unsigned arena_ind) {
size_t mib[3];
size_t miblen = sizeof(mib)/sizeof(size_t);
- assert_d_eq(mallctlnametomib("arena.0.reset", mib, &miblen), 0,
+ expect_d_eq(mallctlnametomib("arena.0.reset", mib, &miblen), 0,
"Unexpected mallctlnametomib() failure");
mib[1] = (size_t)arena_ind;
- assert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0,
+ expect_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0,
"Unexpected mallctlbymib() failure");
}
@@ -105,7 +105,7 @@ TEST_BEGIN(test_pack) {
for (j = 0; j < nregs_per_run; j++) {
void *p = mallocx(SZ, MALLOCX_ARENA(arena_ind) |
MALLOCX_TCACHE_NONE);
- assert_ptr_not_null(p,
+ expect_ptr_not_null(p,
"Unexpected mallocx(%zu, MALLOCX_ARENA(%u) |"
" MALLOCX_TCACHE_NONE) failure, run=%zu, reg=%zu",
SZ, arena_ind, i, j);
@@ -148,7 +148,7 @@ TEST_BEGIN(test_pack) {
}
p = mallocx(SZ, MALLOCX_ARENA(arena_ind) |
MALLOCX_TCACHE_NONE);
- assert_ptr_eq(p, ptrs[(i * nregs_per_run) + j],
+ expect_ptr_eq(p, ptrs[(i * nregs_per_run) + j],
"Unexpected refill discrepancy, run=%zu, reg=%zu\n",
i, j);
}
diff --git a/deps/jemalloc/test/unit/pages.c b/deps/jemalloc/test/unit/pages.c
index ee729eece..8dfd1a72c 100644
--- a/deps/jemalloc/test/unit/pages.c
+++ b/deps/jemalloc/test/unit/pages.c
@@ -8,13 +8,13 @@ TEST_BEGIN(test_pages_huge) {
alloc_size = HUGEPAGE * 2 - PAGE;
commit = true;
pages = pages_map(NULL, alloc_size, PAGE, &commit);
- assert_ptr_not_null(pages, "Unexpected pages_map() error");
+ expect_ptr_not_null(pages, "Unexpected pages_map() error");
if (init_system_thp_mode == thp_mode_default) {
hugepage = (void *)(ALIGNMENT_CEILING((uintptr_t)pages, HUGEPAGE));
- assert_b_ne(pages_huge(hugepage, HUGEPAGE), have_madvise_huge,
+ expect_b_ne(pages_huge(hugepage, HUGEPAGE), have_madvise_huge,
"Unexpected pages_huge() result");
- assert_false(pages_nohuge(hugepage, HUGEPAGE),
+ expect_false(pages_nohuge(hugepage, HUGEPAGE),
"Unexpected pages_nohuge() result");
}
diff --git a/deps/jemalloc/test/unit/peak.c b/deps/jemalloc/test/unit/peak.c
new file mode 100644
index 000000000..11129785f
--- /dev/null
+++ b/deps/jemalloc/test/unit/peak.c
@@ -0,0 +1,47 @@
+#include "test/jemalloc_test.h"
+
+#include "jemalloc/internal/peak.h"
+
+TEST_BEGIN(test_peak) {
+ peak_t peak = PEAK_INITIALIZER;
+ expect_u64_eq(0, peak_max(&peak),
+ "Peak should be zero at initialization");
+ peak_update(&peak, 100, 50);
+ expect_u64_eq(50, peak_max(&peak),
+ "Missed update");
+ peak_update(&peak, 100, 100);
+ expect_u64_eq(50, peak_max(&peak), "Dallocs shouldn't change peak");
+ peak_update(&peak, 100, 200);
+ expect_u64_eq(50, peak_max(&peak), "Dallocs shouldn't change peak");
+ peak_update(&peak, 200, 200);
+ expect_u64_eq(50, peak_max(&peak), "Haven't reached peak again");
+ peak_update(&peak, 300, 200);
+ expect_u64_eq(100, peak_max(&peak), "Missed an update.");
+ peak_set_zero(&peak, 300, 200);
+ expect_u64_eq(0, peak_max(&peak), "No effect from zeroing");
+ peak_update(&peak, 300, 300);
+ expect_u64_eq(0, peak_max(&peak), "Dalloc shouldn't change peak");
+ peak_update(&peak, 400, 300);
+ expect_u64_eq(0, peak_max(&peak), "Should still be net negative");
+ peak_update(&peak, 500, 300);
+ expect_u64_eq(100, peak_max(&peak), "Missed an update.");
+ /*
+ * Above, we set to zero while a net allocator; let's try as a
+ * net-deallocator.
+ */
+ peak_set_zero(&peak, 600, 700);
+ expect_u64_eq(0, peak_max(&peak), "No effect from zeroing.");
+ peak_update(&peak, 600, 800);
+ expect_u64_eq(0, peak_max(&peak), "Dalloc shouldn't change peak.");
+ peak_update(&peak, 700, 800);
+ expect_u64_eq(0, peak_max(&peak), "Should still be net negative.");
+ peak_update(&peak, 800, 800);
+ expect_u64_eq(100, peak_max(&peak), "Missed an update.");
+}
+TEST_END
+
+int
+main(void) {
+ return test_no_reentrancy(
+ test_peak);
+}
diff --git a/deps/jemalloc/test/unit/ph.c b/deps/jemalloc/test/unit/ph.c
index 88bf56f88..28f5e488e 100644
--- a/deps/jemalloc/test/unit/ph.c
+++ b/deps/jemalloc/test/unit/ph.c
@@ -3,11 +3,12 @@
#include "jemalloc/internal/ph.h"
typedef struct node_s node_t;
+ph_structs(heap, node_t);
struct node_s {
#define NODE_MAGIC 0x9823af7e
uint32_t magic;
- phn(node_t) link;
+ heap_link_t link;
uint64_t key;
};
@@ -30,14 +31,28 @@ node_cmp(const node_t *a, const node_t *b) {
static int
node_cmp_magic(const node_t *a, const node_t *b) {
- assert_u32_eq(a->magic, NODE_MAGIC, "Bad magic");
- assert_u32_eq(b->magic, NODE_MAGIC, "Bad magic");
+ expect_u32_eq(a->magic, NODE_MAGIC, "Bad magic");
+ expect_u32_eq(b->magic, NODE_MAGIC, "Bad magic");
return node_cmp(a, b);
}
-typedef ph(node_t) heap_t;
-ph_gen(static, heap_, heap_t, node_t, link, node_cmp_magic);
+ph_gen(static, heap, node_t, link, node_cmp_magic);
+
+static node_t *
+node_next_get(const node_t *node) {
+ return phn_next_get((node_t *)node, offsetof(node_t, link));
+}
+
+static node_t *
+node_prev_get(const node_t *node) {
+ return phn_prev_get((node_t *)node, offsetof(node_t, link));
+}
+
+static node_t *
+node_lchild_get(const node_t *node) {
+ return phn_lchild_get((node_t *)node, offsetof(node_t, link));
+}
static void
node_print(const node_t *node, unsigned depth) {
@@ -49,14 +64,14 @@ node_print(const node_t *node, unsigned depth) {
}
malloc_printf("%2"FMTu64"\n", node->key);
- leftmost_child = phn_lchild_get(node_t, link, node);
+ leftmost_child = node_lchild_get(node);
if (leftmost_child == NULL) {
return;
}
node_print(leftmost_child, depth + 1);
- for (sibling = phn_next_get(node_t, link, leftmost_child); sibling !=
- NULL; sibling = phn_next_get(node_t, link, sibling)) {
+ for (sibling = node_next_get(leftmost_child); sibling !=
+ NULL; sibling = node_next_get(sibling)) {
node_print(sibling, depth + 1);
}
}
@@ -66,16 +81,15 @@ heap_print(const heap_t *heap) {
node_t *auxelm;
malloc_printf("vvv heap %p vvv\n", heap);
- if (heap->ph_root == NULL) {
+ if (heap->ph.root == NULL) {
goto label_return;
}
- node_print(heap->ph_root, 0);
+ node_print(heap->ph.root, 0);
- for (auxelm = phn_next_get(node_t, link, heap->ph_root); auxelm != NULL;
- auxelm = phn_next_get(node_t, link, auxelm)) {
- assert_ptr_eq(phn_next_get(node_t, link, phn_prev_get(node_t,
- link, auxelm)), auxelm,
+ for (auxelm = node_next_get(heap->ph.root); auxelm != NULL;
+ auxelm = node_next_get(auxelm)) {
+ expect_ptr_eq(node_next_get(node_prev_get(auxelm)), auxelm,
"auxelm's prev doesn't link to auxelm");
node_print(auxelm, 0);
}
@@ -90,22 +104,21 @@ node_validate(const node_t *node, const node_t *parent) {
node_t *leftmost_child, *sibling;
if (parent != NULL) {
- assert_d_ge(node_cmp_magic(node, parent), 0,
+ expect_d_ge(node_cmp_magic(node, parent), 0,
"Child is less than parent");
}
- leftmost_child = phn_lchild_get(node_t, link, node);
+ leftmost_child = node_lchild_get(node);
if (leftmost_child == NULL) {
return nnodes;
}
- assert_ptr_eq((void *)phn_prev_get(node_t, link, leftmost_child),
+ expect_ptr_eq(node_prev_get(leftmost_child),
(void *)node, "Leftmost child does not link to node");
nnodes += node_validate(leftmost_child, node);
- for (sibling = phn_next_get(node_t, link, leftmost_child); sibling !=
- NULL; sibling = phn_next_get(node_t, link, sibling)) {
- assert_ptr_eq(phn_next_get(node_t, link, phn_prev_get(node_t,
- link, sibling)), sibling,
+ for (sibling = node_next_get(leftmost_child); sibling !=
+ NULL; sibling = node_next_get(sibling)) {
+ expect_ptr_eq(node_next_get(node_prev_get(sibling)), sibling,
"sibling's prev doesn't link to sibling");
nnodes += node_validate(sibling, node);
}
@@ -117,16 +130,15 @@ heap_validate(const heap_t *heap) {
unsigned nnodes = 0;
node_t *auxelm;
- if (heap->ph_root == NULL) {
+ if (heap->ph.root == NULL) {
goto label_return;
}
- nnodes += node_validate(heap->ph_root, NULL);
+ nnodes += node_validate(heap->ph.root, NULL);
- for (auxelm = phn_next_get(node_t, link, heap->ph_root); auxelm != NULL;
- auxelm = phn_next_get(node_t, link, auxelm)) {
- assert_ptr_eq(phn_next_get(node_t, link, phn_prev_get(node_t,
- link, auxelm)), auxelm,
+ for (auxelm = node_next_get(heap->ph.root); auxelm != NULL;
+ auxelm = node_next_get(auxelm)) {
+ expect_ptr_eq(node_next_get(node_prev_get(auxelm)), auxelm,
"auxelm's prev doesn't link to auxelm");
nnodes += node_validate(auxelm, NULL);
}
@@ -142,9 +154,9 @@ TEST_BEGIN(test_ph_empty) {
heap_t heap;
heap_new(&heap);
- assert_true(heap_empty(&heap), "Heap should be empty");
- assert_ptr_null(heap_first(&heap), "Unexpected node");
- assert_ptr_null(heap_any(&heap), "Unexpected node");
+ expect_true(heap_empty(&heap), "Heap should be empty");
+ expect_ptr_null(heap_first(&heap), "Unexpected node");
+ expect_ptr_null(heap_any(&heap), "Unexpected node");
}
TEST_END
@@ -203,7 +215,7 @@ TEST_BEGIN(test_ph_random) {
for (j = 1; j <= NNODES; j++) {
/* Initialize heap and nodes. */
heap_new(&heap);
- assert_u_eq(heap_validate(&heap), 0,
+ expect_u_eq(heap_validate(&heap), 0,
"Incorrect node count");
for (k = 0; k < j; k++) {
nodes[k].magic = NODE_MAGIC;
@@ -214,34 +226,34 @@ TEST_BEGIN(test_ph_random) {
for (k = 0; k < j; k++) {
heap_insert(&heap, &nodes[k]);
if (i % 13 == 12) {
- assert_ptr_not_null(heap_any(&heap),
+ expect_ptr_not_null(heap_any(&heap),
"Heap should not be empty");
/* Trigger merging. */
- assert_ptr_not_null(heap_first(&heap),
+ expect_ptr_not_null(heap_first(&heap),
"Heap should not be empty");
}
- assert_u_eq(heap_validate(&heap), k + 1,
+ expect_u_eq(heap_validate(&heap), k + 1,
"Incorrect node count");
}
- assert_false(heap_empty(&heap),
+ expect_false(heap_empty(&heap),
"Heap should not be empty");
/* Remove nodes. */
switch (i % 6) {
case 0:
for (k = 0; k < j; k++) {
- assert_u_eq(heap_validate(&heap), j - k,
+ expect_u_eq(heap_validate(&heap), j - k,
"Incorrect node count");
node_remove(&heap, &nodes[k]);
- assert_u_eq(heap_validate(&heap), j - k
+ expect_u_eq(heap_validate(&heap), j - k
- 1, "Incorrect node count");
}
break;
case 1:
for (k = j; k > 0; k--) {
node_remove(&heap, &nodes[k-1]);
- assert_u_eq(heap_validate(&heap), k - 1,
+ expect_u_eq(heap_validate(&heap), k - 1,
"Incorrect node count");
}
break;
@@ -249,10 +261,10 @@ TEST_BEGIN(test_ph_random) {
node_t *prev = NULL;
for (k = 0; k < j; k++) {
node_t *node = node_remove_first(&heap);
- assert_u_eq(heap_validate(&heap), j - k
+ expect_u_eq(heap_validate(&heap), j - k
- 1, "Incorrect node count");
if (prev != NULL) {
- assert_d_ge(node_cmp(node,
+ expect_d_ge(node_cmp(node,
prev), 0,
"Bad removal order");
}
@@ -263,15 +275,15 @@ TEST_BEGIN(test_ph_random) {
node_t *prev = NULL;
for (k = 0; k < j; k++) {
node_t *node = heap_first(&heap);
- assert_u_eq(heap_validate(&heap), j - k,
+ expect_u_eq(heap_validate(&heap), j - k,
"Incorrect node count");
if (prev != NULL) {
- assert_d_ge(node_cmp(node,
+ expect_d_ge(node_cmp(node,
prev), 0,
"Bad removal order");
}
node_remove(&heap, node);
- assert_u_eq(heap_validate(&heap), j - k
+ expect_u_eq(heap_validate(&heap), j - k
- 1, "Incorrect node count");
prev = node;
}
@@ -279,17 +291,17 @@ TEST_BEGIN(test_ph_random) {
} case 4: {
for (k = 0; k < j; k++) {
node_remove_any(&heap);
- assert_u_eq(heap_validate(&heap), j - k
+ expect_u_eq(heap_validate(&heap), j - k
- 1, "Incorrect node count");
}
break;
} case 5: {
for (k = 0; k < j; k++) {
node_t *node = heap_any(&heap);
- assert_u_eq(heap_validate(&heap), j - k,
+ expect_u_eq(heap_validate(&heap), j - k,
"Incorrect node count");
node_remove(&heap, node);
- assert_u_eq(heap_validate(&heap), j - k
+ expect_u_eq(heap_validate(&heap), j - k
- 1, "Incorrect node count");
}
break;
@@ -297,11 +309,11 @@ TEST_BEGIN(test_ph_random) {
not_reached();
}
- assert_ptr_null(heap_first(&heap),
+ expect_ptr_null(heap_first(&heap),
"Heap should be empty");
- assert_ptr_null(heap_any(&heap),
+ expect_ptr_null(heap_any(&heap),
"Heap should be empty");
- assert_true(heap_empty(&heap), "Heap should be empty");
+ expect_true(heap_empty(&heap), "Heap should be empty");
}
}
fini_gen_rand(sfmt);
diff --git a/deps/jemalloc/test/unit/prng.c b/deps/jemalloc/test/unit/prng.c
index b5795c2f4..a6d9b014a 100644
--- a/deps/jemalloc/test/unit/prng.c
+++ b/deps/jemalloc/test/unit/prng.c
@@ -1,44 +1,44 @@
#include "test/jemalloc_test.h"
-static void
-test_prng_lg_range_u32(bool atomic) {
- atomic_u32_t sa, sb;
+TEST_BEGIN(test_prng_lg_range_u32) {
+ uint32_t sa, sb;
uint32_t ra, rb;
unsigned lg_range;
- atomic_store_u32(&sa, 42, ATOMIC_RELAXED);
- ra = prng_lg_range_u32(&sa, 32, atomic);
- atomic_store_u32(&sa, 42, ATOMIC_RELAXED);
- rb = prng_lg_range_u32(&sa, 32, atomic);
- assert_u32_eq(ra, rb,
+ sa = 42;
+ ra = prng_lg_range_u32(&sa, 32);
+ sa = 42;
+ rb = prng_lg_range_u32(&sa, 32);
+ expect_u32_eq(ra, rb,
"Repeated generation should produce repeated results");
- atomic_store_u32(&sb, 42, ATOMIC_RELAXED);
- rb = prng_lg_range_u32(&sb, 32, atomic);
- assert_u32_eq(ra, rb,
+ sb = 42;
+ rb = prng_lg_range_u32(&sb, 32);
+ expect_u32_eq(ra, rb,
"Equivalent generation should produce equivalent results");
- atomic_store_u32(&sa, 42, ATOMIC_RELAXED);
- ra = prng_lg_range_u32(&sa, 32, atomic);
- rb = prng_lg_range_u32(&sa, 32, atomic);
- assert_u32_ne(ra, rb,
+ sa = 42;
+ ra = prng_lg_range_u32(&sa, 32);
+ rb = prng_lg_range_u32(&sa, 32);
+ expect_u32_ne(ra, rb,
"Full-width results must not immediately repeat");
- atomic_store_u32(&sa, 42, ATOMIC_RELAXED);
- ra = prng_lg_range_u32(&sa, 32, atomic);
+ sa = 42;
+ ra = prng_lg_range_u32(&sa, 32);
for (lg_range = 31; lg_range > 0; lg_range--) {
- atomic_store_u32(&sb, 42, ATOMIC_RELAXED);
- rb = prng_lg_range_u32(&sb, lg_range, atomic);
- assert_u32_eq((rb & (UINT32_C(0xffffffff) << lg_range)),
+ sb = 42;
+ rb = prng_lg_range_u32(&sb, lg_range);
+ expect_u32_eq((rb & (UINT32_C(0xffffffff) << lg_range)),
0, "High order bits should be 0, lg_range=%u", lg_range);
- assert_u32_eq(rb, (ra >> (32 - lg_range)),
+ expect_u32_eq(rb, (ra >> (32 - lg_range)),
"Expected high order bits of full-width result, "
"lg_range=%u", lg_range);
}
+
}
+TEST_END
-static void
-test_prng_lg_range_u64(void) {
+TEST_BEGIN(test_prng_lg_range_u64) {
uint64_t sa, sb, ra, rb;
unsigned lg_range;
@@ -46,18 +46,18 @@ test_prng_lg_range_u64(void) {
ra = prng_lg_range_u64(&sa, 64);
sa = 42;
rb = prng_lg_range_u64(&sa, 64);
- assert_u64_eq(ra, rb,
+ expect_u64_eq(ra, rb,
"Repeated generation should produce repeated results");
sb = 42;
rb = prng_lg_range_u64(&sb, 64);
- assert_u64_eq(ra, rb,
+ expect_u64_eq(ra, rb,
"Equivalent generation should produce equivalent results");
sa = 42;
ra = prng_lg_range_u64(&sa, 64);
rb = prng_lg_range_u64(&sa, 64);
- assert_u64_ne(ra, rb,
+ expect_u64_ne(ra, rb,
"Full-width results must not immediately repeat");
sa = 42;
@@ -65,173 +65,125 @@ test_prng_lg_range_u64(void) {
for (lg_range = 63; lg_range > 0; lg_range--) {
sb = 42;
rb = prng_lg_range_u64(&sb, lg_range);
- assert_u64_eq((rb & (UINT64_C(0xffffffffffffffff) << lg_range)),
+ expect_u64_eq((rb & (UINT64_C(0xffffffffffffffff) << lg_range)),
0, "High order bits should be 0, lg_range=%u", lg_range);
- assert_u64_eq(rb, (ra >> (64 - lg_range)),
+ expect_u64_eq(rb, (ra >> (64 - lg_range)),
"Expected high order bits of full-width result, "
"lg_range=%u", lg_range);
}
}
+TEST_END
-static void
-test_prng_lg_range_zu(bool atomic) {
- atomic_zu_t sa, sb;
+TEST_BEGIN(test_prng_lg_range_zu) {
+ size_t sa, sb;
size_t ra, rb;
unsigned lg_range;
- atomic_store_zu(&sa, 42, ATOMIC_RELAXED);
- ra = prng_lg_range_zu(&sa, ZU(1) << (3 + LG_SIZEOF_PTR), atomic);
- atomic_store_zu(&sa, 42, ATOMIC_RELAXED);
- rb = prng_lg_range_zu(&sa, ZU(1) << (3 + LG_SIZEOF_PTR), atomic);
- assert_zu_eq(ra, rb,
+ sa = 42;
+ ra = prng_lg_range_zu(&sa, ZU(1) << (3 + LG_SIZEOF_PTR));
+ sa = 42;
+ rb = prng_lg_range_zu(&sa, ZU(1) << (3 + LG_SIZEOF_PTR));
+ expect_zu_eq(ra, rb,
"Repeated generation should produce repeated results");
- atomic_store_zu(&sb, 42, ATOMIC_RELAXED);
- rb = prng_lg_range_zu(&sb, ZU(1) << (3 + LG_SIZEOF_PTR), atomic);
- assert_zu_eq(ra, rb,
+ sb = 42;
+ rb = prng_lg_range_zu(&sb, ZU(1) << (3 + LG_SIZEOF_PTR));
+ expect_zu_eq(ra, rb,
"Equivalent generation should produce equivalent results");
- atomic_store_zu(&sa, 42, ATOMIC_RELAXED);
- ra = prng_lg_range_zu(&sa, ZU(1) << (3 + LG_SIZEOF_PTR), atomic);
- rb = prng_lg_range_zu(&sa, ZU(1) << (3 + LG_SIZEOF_PTR), atomic);
- assert_zu_ne(ra, rb,
+ sa = 42;
+ ra = prng_lg_range_zu(&sa, ZU(1) << (3 + LG_SIZEOF_PTR));
+ rb = prng_lg_range_zu(&sa, ZU(1) << (3 + LG_SIZEOF_PTR));
+ expect_zu_ne(ra, rb,
"Full-width results must not immediately repeat");
- atomic_store_zu(&sa, 42, ATOMIC_RELAXED);
- ra = prng_lg_range_zu(&sa, ZU(1) << (3 + LG_SIZEOF_PTR), atomic);
+ sa = 42;
+ ra = prng_lg_range_zu(&sa, ZU(1) << (3 + LG_SIZEOF_PTR));
for (lg_range = (ZU(1) << (3 + LG_SIZEOF_PTR)) - 1; lg_range > 0;
lg_range--) {
- atomic_store_zu(&sb, 42, ATOMIC_RELAXED);
- rb = prng_lg_range_zu(&sb, lg_range, atomic);
- assert_zu_eq((rb & (SIZE_T_MAX << lg_range)),
+ sb = 42;
+ rb = prng_lg_range_zu(&sb, lg_range);
+ expect_zu_eq((rb & (SIZE_T_MAX << lg_range)),
0, "High order bits should be 0, lg_range=%u", lg_range);
- assert_zu_eq(rb, (ra >> ((ZU(1) << (3 + LG_SIZEOF_PTR)) -
+ expect_zu_eq(rb, (ra >> ((ZU(1) << (3 + LG_SIZEOF_PTR)) -
lg_range)), "Expected high order bits of full-width "
"result, lg_range=%u", lg_range);
}
-}
-
-TEST_BEGIN(test_prng_lg_range_u32_nonatomic) {
- test_prng_lg_range_u32(false);
-}
-TEST_END
-
-TEST_BEGIN(test_prng_lg_range_u32_atomic) {
- test_prng_lg_range_u32(true);
-}
-TEST_END
-
-TEST_BEGIN(test_prng_lg_range_u64_nonatomic) {
- test_prng_lg_range_u64();
-}
-TEST_END
-TEST_BEGIN(test_prng_lg_range_zu_nonatomic) {
- test_prng_lg_range_zu(false);
}
TEST_END
-TEST_BEGIN(test_prng_lg_range_zu_atomic) {
- test_prng_lg_range_zu(true);
-}
-TEST_END
-
-static void
-test_prng_range_u32(bool atomic) {
+TEST_BEGIN(test_prng_range_u32) {
uint32_t range;
-#define MAX_RANGE 10000000
-#define RANGE_STEP 97
-#define NREPS 10
- for (range = 2; range < MAX_RANGE; range += RANGE_STEP) {
- atomic_u32_t s;
+ const uint32_t max_range = 10000000;
+ const uint32_t range_step = 97;
+ const unsigned nreps = 10;
+
+ for (range = 2; range < max_range; range += range_step) {
+ uint32_t s;
unsigned rep;
- atomic_store_u32(&s, range, ATOMIC_RELAXED);
- for (rep = 0; rep < NREPS; rep++) {
- uint32_t r = prng_range_u32(&s, range, atomic);
+ s = range;
+ for (rep = 0; rep < nreps; rep++) {
+ uint32_t r = prng_range_u32(&s, range);
- assert_u32_lt(r, range, "Out of range");
+ expect_u32_lt(r, range, "Out of range");
}
}
}
+TEST_END
-static void
-test_prng_range_u64(void) {
+TEST_BEGIN(test_prng_range_u64) {
uint64_t range;
-#define MAX_RANGE 10000000
-#define RANGE_STEP 97
-#define NREPS 10
- for (range = 2; range < MAX_RANGE; range += RANGE_STEP) {
+ const uint64_t max_range = 10000000;
+ const uint64_t range_step = 97;
+ const unsigned nreps = 10;
+
+ for (range = 2; range < max_range; range += range_step) {
uint64_t s;
unsigned rep;
s = range;
- for (rep = 0; rep < NREPS; rep++) {
+ for (rep = 0; rep < nreps; rep++) {
uint64_t r = prng_range_u64(&s, range);
- assert_u64_lt(r, range, "Out of range");
+ expect_u64_lt(r, range, "Out of range");
}
}
}
+TEST_END
-static void
-test_prng_range_zu(bool atomic) {
+TEST_BEGIN(test_prng_range_zu) {
size_t range;
-#define MAX_RANGE 10000000
-#define RANGE_STEP 97
-#define NREPS 10
- for (range = 2; range < MAX_RANGE; range += RANGE_STEP) {
- atomic_zu_t s;
+ const size_t max_range = 10000000;
+ const size_t range_step = 97;
+ const unsigned nreps = 10;
+
+
+ for (range = 2; range < max_range; range += range_step) {
+ size_t s;
unsigned rep;
- atomic_store_zu(&s, range, ATOMIC_RELAXED);
- for (rep = 0; rep < NREPS; rep++) {
- size_t r = prng_range_zu(&s, range, atomic);
+ s = range;
+ for (rep = 0; rep < nreps; rep++) {
+ size_t r = prng_range_zu(&s, range);
- assert_zu_lt(r, range, "Out of range");
+ expect_zu_lt(r, range, "Out of range");
}
}
}
-
-TEST_BEGIN(test_prng_range_u32_nonatomic) {
- test_prng_range_u32(false);
-}
-TEST_END
-
-TEST_BEGIN(test_prng_range_u32_atomic) {
- test_prng_range_u32(true);
-}
-TEST_END
-
-TEST_BEGIN(test_prng_range_u64_nonatomic) {
- test_prng_range_u64();
-}
-TEST_END
-
-TEST_BEGIN(test_prng_range_zu_nonatomic) {
- test_prng_range_zu(false);
-}
-TEST_END
-
-TEST_BEGIN(test_prng_range_zu_atomic) {
- test_prng_range_zu(true);
-}
TEST_END
int
main(void) {
- return test(
- test_prng_lg_range_u32_nonatomic,
- test_prng_lg_range_u32_atomic,
- test_prng_lg_range_u64_nonatomic,
- test_prng_lg_range_zu_nonatomic,
- test_prng_lg_range_zu_atomic,
- test_prng_range_u32_nonatomic,
- test_prng_range_u32_atomic,
- test_prng_range_u64_nonatomic,
- test_prng_range_zu_nonatomic,
- test_prng_range_zu_atomic);
+ return test_no_reentrancy(
+ test_prng_lg_range_u32,
+ test_prng_lg_range_u64,
+ test_prng_lg_range_zu,
+ test_prng_range_u32,
+ test_prng_range_u64,
+ test_prng_range_zu);
}
diff --git a/deps/jemalloc/test/unit/prof_accum.c b/deps/jemalloc/test/unit/prof_accum.c
index 252200635..ef392acda 100644
--- a/deps/jemalloc/test/unit/prof_accum.c
+++ b/deps/jemalloc/test/unit/prof_accum.c
@@ -1,12 +1,15 @@
#include "test/jemalloc_test.h"
+#include "jemalloc/internal/prof_data.h"
+#include "jemalloc/internal/prof_sys.h"
+
#define NTHREADS 4
#define NALLOCS_PER_THREAD 50
#define DUMP_INTERVAL 1
#define BT_COUNT_CHECK_INTERVAL 5
static int
-prof_dump_open_intercept(bool propagate_err, const char *filename) {
+prof_dump_open_file_intercept(const char *filename, int mode) {
int fd;
fd = open("/dev/null", O_WRONLY);
@@ -32,14 +35,14 @@ thd_start(void *varg) {
void *p = alloc_from_permuted_backtrace(thd_ind, i);
dallocx(p, 0);
if (i % DUMP_INTERVAL == 0) {
- assert_d_eq(mallctl("prof.dump", NULL, NULL, NULL, 0),
+ expect_d_eq(mallctl("prof.dump", NULL, NULL, NULL, 0),
0, "Unexpected error while dumping heap profile");
}
if (i % BT_COUNT_CHECK_INTERVAL == 0 ||
i+1 == NALLOCS_PER_THREAD) {
bt_count = prof_bt_count();
- assert_zu_le(bt_count_prev+(i-i_prev), bt_count,
+ expect_zu_le(bt_count_prev+(i-i_prev), bt_count,
"Expected larger backtrace count increase");
i_prev = i;
bt_count_prev = bt_count;
@@ -58,11 +61,11 @@ TEST_BEGIN(test_idump) {
test_skip_if(!config_prof);
active = true;
- assert_d_eq(mallctl("prof.active", NULL, NULL, (void *)&active,
+ expect_d_eq(mallctl("prof.active", NULL, NULL, (void *)&active,
sizeof(active)), 0,
"Unexpected mallctl failure while activating profiling");
- prof_dump_open = prof_dump_open_intercept;
+ prof_dump_open_file = prof_dump_open_file_intercept;
for (i = 0; i < NTHREADS; i++) {
thd_args[i] = i;
diff --git a/deps/jemalloc/test/unit/prof_active.c b/deps/jemalloc/test/unit/prof_active.c
index 850a24a77..af29e7ad2 100644
--- a/deps/jemalloc/test/unit/prof_active.c
+++ b/deps/jemalloc/test/unit/prof_active.c
@@ -1,14 +1,16 @@
#include "test/jemalloc_test.h"
+#include "jemalloc/internal/prof_data.h"
+
static void
mallctl_bool_get(const char *name, bool expected, const char *func, int line) {
bool old;
size_t sz;
sz = sizeof(old);
- assert_d_eq(mallctl(name, (void *)&old, &sz, NULL, 0), 0,
+ expect_d_eq(mallctl(name, (void *)&old, &sz, NULL, 0), 0,
"%s():%d: Unexpected mallctl failure reading %s", func, line, name);
- assert_b_eq(old, expected, "%s():%d: Unexpected %s value", func, line,
+ expect_b_eq(old, expected, "%s():%d: Unexpected %s value", func, line,
name);
}
@@ -19,11 +21,11 @@ mallctl_bool_set(const char *name, bool old_expected, bool val_new,
size_t sz;
sz = sizeof(old);
- assert_d_eq(mallctl(name, (void *)&old, &sz, (void *)&val_new,
+ expect_d_eq(mallctl(name, (void *)&old, &sz, (void *)&val_new,
sizeof(val_new)), 0,
"%s():%d: Unexpected mallctl failure reading/writing %s", func,
line, name);
- assert_b_eq(old, old_expected, "%s():%d: Unexpected %s value", func,
+ expect_b_eq(old, old_expected, "%s():%d: Unexpected %s value", func,
line, name);
}
@@ -67,11 +69,11 @@ prof_sampling_probe_impl(bool expect_sample, const char *func, int line) {
void *p;
size_t expected_backtraces = expect_sample ? 1 : 0;
- assert_zu_eq(prof_bt_count(), 0, "%s():%d: Expected 0 backtraces", func,
+ expect_zu_eq(prof_bt_count(), 0, "%s():%d: Expected 0 backtraces", func,
line);
p = mallocx(1, 0);
- assert_ptr_not_null(p, "Unexpected mallocx() failure");
- assert_zu_eq(prof_bt_count(), expected_backtraces,
+ expect_ptr_not_null(p, "Unexpected mallocx() failure");
+ expect_zu_eq(prof_bt_count(), expected_backtraces,
"%s():%d: Unexpected backtrace count", func, line);
dallocx(p, 0);
}
diff --git a/deps/jemalloc/test/unit/prof_active.sh b/deps/jemalloc/test/unit/prof_active.sh
index 0167cb10b..9749674af 100644
--- a/deps/jemalloc/test/unit/prof_active.sh
+++ b/deps/jemalloc/test/unit/prof_active.sh
@@ -1,5 +1,5 @@
#!/bin/sh
if [ "x${enable_prof}" = "x1" ] ; then
- export MALLOC_CONF="prof:true,prof_thread_active_init:false,lg_prof_sample:0"
+ export MALLOC_CONF="prof:true,prof_active:true,prof_thread_active_init:false,lg_prof_sample:0"
fi
diff --git a/deps/jemalloc/test/unit/prof_gdump.c b/deps/jemalloc/test/unit/prof_gdump.c
index f7e0aac76..46e45036a 100644
--- a/deps/jemalloc/test/unit/prof_gdump.c
+++ b/deps/jemalloc/test/unit/prof_gdump.c
@@ -1,9 +1,11 @@
#include "test/jemalloc_test.h"
+#include "jemalloc/internal/prof_sys.h"
+
static bool did_prof_dump_open;
static int
-prof_dump_open_intercept(bool propagate_err, const char *filename) {
+prof_dump_open_file_intercept(const char *filename, int mode) {
int fd;
did_prof_dump_open = true;
@@ -15,6 +17,7 @@ prof_dump_open_intercept(bool propagate_err, const char *filename) {
}
TEST_BEGIN(test_gdump) {
+ test_skip_if(opt_hpa);
bool active, gdump, gdump_old;
void *p, *q, *r, *s;
size_t sz;
@@ -22,43 +25,43 @@ TEST_BEGIN(test_gdump) {
test_skip_if(!config_prof);
active = true;
- assert_d_eq(mallctl("prof.active", NULL, NULL, (void *)&active,
+ expect_d_eq(mallctl("prof.active", NULL, NULL, (void *)&active,
sizeof(active)), 0,
"Unexpected mallctl failure while activating profiling");
- prof_dump_open = prof_dump_open_intercept;
+ prof_dump_open_file = prof_dump_open_file_intercept;
did_prof_dump_open = false;
p = mallocx((1U << SC_LG_LARGE_MINCLASS), 0);
- assert_ptr_not_null(p, "Unexpected mallocx() failure");
- assert_true(did_prof_dump_open, "Expected a profile dump");
+ expect_ptr_not_null(p, "Unexpected mallocx() failure");
+ expect_true(did_prof_dump_open, "Expected a profile dump");
did_prof_dump_open = false;
q = mallocx((1U << SC_LG_LARGE_MINCLASS), 0);
- assert_ptr_not_null(q, "Unexpected mallocx() failure");
- assert_true(did_prof_dump_open, "Expected a profile dump");
+ expect_ptr_not_null(q, "Unexpected mallocx() failure");
+ expect_true(did_prof_dump_open, "Expected a profile dump");
gdump = false;
sz = sizeof(gdump_old);
- assert_d_eq(mallctl("prof.gdump", (void *)&gdump_old, &sz,
+ expect_d_eq(mallctl("prof.gdump", (void *)&gdump_old, &sz,
(void *)&gdump, sizeof(gdump)), 0,
"Unexpected mallctl failure while disabling prof.gdump");
assert(gdump_old);
did_prof_dump_open = false;
r = mallocx((1U << SC_LG_LARGE_MINCLASS), 0);
- assert_ptr_not_null(q, "Unexpected mallocx() failure");
- assert_false(did_prof_dump_open, "Unexpected profile dump");
+ expect_ptr_not_null(q, "Unexpected mallocx() failure");
+ expect_false(did_prof_dump_open, "Unexpected profile dump");
gdump = true;
sz = sizeof(gdump_old);
- assert_d_eq(mallctl("prof.gdump", (void *)&gdump_old, &sz,
+ expect_d_eq(mallctl("prof.gdump", (void *)&gdump_old, &sz,
(void *)&gdump, sizeof(gdump)), 0,
"Unexpected mallctl failure while enabling prof.gdump");
assert(!gdump_old);
did_prof_dump_open = false;
s = mallocx((1U << SC_LG_LARGE_MINCLASS), 0);
- assert_ptr_not_null(q, "Unexpected mallocx() failure");
- assert_true(did_prof_dump_open, "Expected a profile dump");
+ expect_ptr_not_null(q, "Unexpected mallocx() failure");
+ expect_true(did_prof_dump_open, "Expected a profile dump");
dallocx(p, 0);
dallocx(q, 0);
diff --git a/deps/jemalloc/test/unit/prof_hook.c b/deps/jemalloc/test/unit/prof_hook.c
new file mode 100644
index 000000000..6480d9303
--- /dev/null
+++ b/deps/jemalloc/test/unit/prof_hook.c
@@ -0,0 +1,169 @@
+#include "test/jemalloc_test.h"
+
+const char *dump_filename = "/dev/null";
+
+prof_backtrace_hook_t default_hook;
+
+bool mock_bt_hook_called = false;
+bool mock_dump_hook_called = false;
+
+void
+mock_bt_hook(void **vec, unsigned *len, unsigned max_len) {
+ *len = max_len;
+ for (unsigned i = 0; i < max_len; ++i) {
+ vec[i] = (void *)((uintptr_t)i);
+ }
+ mock_bt_hook_called = true;
+}
+
+void
+mock_bt_augmenting_hook(void **vec, unsigned *len, unsigned max_len) {
+ default_hook(vec, len, max_len);
+ expect_u_gt(*len, 0, "Default backtrace hook returned empty backtrace");
+ expect_u_lt(*len, max_len,
+ "Default backtrace hook returned too large backtrace");
+
+ /* Add a separator between default frames and augmented */
+ vec[*len] = (void *)0x030303030;
+ (*len)++;
+
+ /* Add more stack frames */
+ for (unsigned i = 0; i < 3; ++i) {
+ if (*len == max_len) {
+ break;
+ }
+ vec[*len] = (void *)((uintptr_t)i);
+ (*len)++;
+ }
+
+
+ mock_bt_hook_called = true;
+}
+
+void
+mock_dump_hook(const char *filename) {
+ mock_dump_hook_called = true;
+ expect_str_eq(filename, dump_filename,
+ "Incorrect file name passed to the dump hook");
+}
+
+TEST_BEGIN(test_prof_backtrace_hook_replace) {
+
+ test_skip_if(!config_prof);
+
+ mock_bt_hook_called = false;
+
+ void *p0 = mallocx(1, 0);
+ assert_ptr_not_null(p0, "Failed to allocate");
+
+ expect_false(mock_bt_hook_called, "Called mock hook before it's set");
+
+ prof_backtrace_hook_t null_hook = NULL;
+ expect_d_eq(mallctl("experimental.hooks.prof_backtrace",
+ NULL, 0, (void *)&null_hook, sizeof(null_hook)),
+ EINVAL, "Incorrectly allowed NULL backtrace hook");
+
+ size_t default_hook_sz = sizeof(prof_backtrace_hook_t);
+ prof_backtrace_hook_t hook = &mock_bt_hook;
+ expect_d_eq(mallctl("experimental.hooks.prof_backtrace",
+ (void *)&default_hook, &default_hook_sz, (void *)&hook,
+ sizeof(hook)), 0, "Unexpected mallctl failure setting hook");
+
+ void *p1 = mallocx(1, 0);
+ assert_ptr_not_null(p1, "Failed to allocate");
+
+ expect_true(mock_bt_hook_called, "Didn't call mock hook");
+
+ prof_backtrace_hook_t current_hook;
+ size_t current_hook_sz = sizeof(prof_backtrace_hook_t);
+ expect_d_eq(mallctl("experimental.hooks.prof_backtrace",
+ (void *)&current_hook, &current_hook_sz, (void *)&default_hook,
+ sizeof(default_hook)), 0,
+ "Unexpected mallctl failure resetting hook to default");
+
+ expect_ptr_eq(current_hook, hook,
+ "Hook returned by mallctl is not equal to mock hook");
+
+ dallocx(p1, 0);
+ dallocx(p0, 0);
+}
+TEST_END
+
+TEST_BEGIN(test_prof_backtrace_hook_augment) {
+
+ test_skip_if(!config_prof);
+
+ mock_bt_hook_called = false;
+
+ void *p0 = mallocx(1, 0);
+ assert_ptr_not_null(p0, "Failed to allocate");
+
+ expect_false(mock_bt_hook_called, "Called mock hook before it's set");
+
+ size_t default_hook_sz = sizeof(prof_backtrace_hook_t);
+ prof_backtrace_hook_t hook = &mock_bt_augmenting_hook;
+ expect_d_eq(mallctl("experimental.hooks.prof_backtrace",
+ (void *)&default_hook, &default_hook_sz, (void *)&hook,
+ sizeof(hook)), 0, "Unexpected mallctl failure setting hook");
+
+ void *p1 = mallocx(1, 0);
+ assert_ptr_not_null(p1, "Failed to allocate");
+
+ expect_true(mock_bt_hook_called, "Didn't call mock hook");
+
+ prof_backtrace_hook_t current_hook;
+ size_t current_hook_sz = sizeof(prof_backtrace_hook_t);
+ expect_d_eq(mallctl("experimental.hooks.prof_backtrace",
+ (void *)&current_hook, &current_hook_sz, (void *)&default_hook,
+ sizeof(default_hook)), 0,
+ "Unexpected mallctl failure resetting hook to default");
+
+ expect_ptr_eq(current_hook, hook,
+ "Hook returned by mallctl is not equal to mock hook");
+
+ dallocx(p1, 0);
+ dallocx(p0, 0);
+}
+TEST_END
+
+TEST_BEGIN(test_prof_dump_hook) {
+
+ test_skip_if(!config_prof);
+
+ mock_dump_hook_called = false;
+
+ expect_d_eq(mallctl("prof.dump", NULL, NULL, (void *)&dump_filename,
+ sizeof(dump_filename)), 0, "Failed to dump heap profile");
+
+ expect_false(mock_dump_hook_called, "Called dump hook before it's set");
+
+ size_t default_hook_sz = sizeof(prof_dump_hook_t);
+ prof_dump_hook_t hook = &mock_dump_hook;
+ expect_d_eq(mallctl("experimental.hooks.prof_dump",
+ (void *)&default_hook, &default_hook_sz, (void *)&hook,
+ sizeof(hook)), 0, "Unexpected mallctl failure setting hook");
+
+ expect_d_eq(mallctl("prof.dump", NULL, NULL, (void *)&dump_filename,
+ sizeof(dump_filename)), 0, "Failed to dump heap profile");
+
+ expect_true(mock_dump_hook_called, "Didn't call mock hook");
+
+ prof_dump_hook_t current_hook;
+ size_t current_hook_sz = sizeof(prof_dump_hook_t);
+ expect_d_eq(mallctl("experimental.hooks.prof_dump",
+ (void *)&current_hook, &current_hook_sz, (void *)&default_hook,
+ sizeof(default_hook)), 0,
+ "Unexpected mallctl failure resetting hook to default");
+
+ expect_ptr_eq(current_hook, hook,
+ "Hook returned by mallctl is not equal to mock hook");
+}
+TEST_END
+
+int
+main(void) {
+ return test(
+ test_prof_backtrace_hook_replace,
+ test_prof_backtrace_hook_augment,
+ test_prof_dump_hook);
+}
diff --git a/deps/jemalloc/test/unit/prof_hook.sh b/deps/jemalloc/test/unit/prof_hook.sh
new file mode 100644
index 000000000..c7ebd8f98
--- /dev/null
+++ b/deps/jemalloc/test/unit/prof_hook.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+if [ "x${enable_prof}" = "x1" ] ; then
+ export MALLOC_CONF="prof:true,prof_active:true,lg_prof_sample:0"
+fi
+
diff --git a/deps/jemalloc/test/unit/prof_idump.c b/deps/jemalloc/test/unit/prof_idump.c
index 1cc6c98cd..455ac5297 100644
--- a/deps/jemalloc/test/unit/prof_idump.c
+++ b/deps/jemalloc/test/unit/prof_idump.c
@@ -1,13 +1,21 @@
#include "test/jemalloc_test.h"
+#include "jemalloc/internal/prof_sys.h"
+
+#define TEST_PREFIX "test_prefix"
+
static bool did_prof_dump_open;
static int
-prof_dump_open_intercept(bool propagate_err, const char *filename) {
+prof_dump_open_file_intercept(const char *filename, int mode) {
int fd;
did_prof_dump_open = true;
+ const char filename_prefix[] = TEST_PREFIX ".";
+ expect_d_eq(strncmp(filename_prefix, filename, sizeof(filename_prefix)
+ - 1), 0, "Dump file name should start with \"" TEST_PREFIX ".\"");
+
fd = open("/dev/null", O_WRONLY);
assert_d_ne(fd, -1, "Unexpected open() failure");
@@ -18,20 +26,27 @@ TEST_BEGIN(test_idump) {
bool active;
void *p;
+ const char *test_prefix = TEST_PREFIX;
+
test_skip_if(!config_prof);
active = true;
- assert_d_eq(mallctl("prof.active", NULL, NULL, (void *)&active,
+
+ expect_d_eq(mallctl("prof.prefix", NULL, NULL, (void *)&test_prefix,
+ sizeof(test_prefix)), 0,
+ "Unexpected mallctl failure while overwriting dump prefix");
+
+ expect_d_eq(mallctl("prof.active", NULL, NULL, (void *)&active,
sizeof(active)), 0,
"Unexpected mallctl failure while activating profiling");
- prof_dump_open = prof_dump_open_intercept;
+ prof_dump_open_file = prof_dump_open_file_intercept;
did_prof_dump_open = false;
p = mallocx(1, 0);
- assert_ptr_not_null(p, "Unexpected mallocx() failure");
+ expect_ptr_not_null(p, "Unexpected mallocx() failure");
dallocx(p, 0);
- assert_true(did_prof_dump_open, "Expected a profile dump");
+ expect_true(did_prof_dump_open, "Expected a profile dump");
}
TEST_END
diff --git a/deps/jemalloc/test/unit/prof_log.c b/deps/jemalloc/test/unit/prof_log.c
index 92fbd7cea..5ff208e2d 100644
--- a/deps/jemalloc/test/unit/prof_log.c
+++ b/deps/jemalloc/test/unit/prof_log.c
@@ -1,18 +1,19 @@
#include "test/jemalloc_test.h"
+#include "jemalloc/internal/prof_log.h"
#define N_PARAM 100
#define N_THREADS 10
-static void assert_rep() {
- assert_b_eq(prof_log_rep_check(), false, "Rep check failed");
+static void expect_rep() {
+ expect_b_eq(prof_log_rep_check(), false, "Rep check failed");
}
-static void assert_log_empty() {
- assert_zu_eq(prof_log_bt_count(), 0,
+static void expect_log_empty() {
+ expect_zu_eq(prof_log_bt_count(), 0,
"The log has backtraces; it isn't empty");
- assert_zu_eq(prof_log_thr_count(), 0,
+ expect_zu_eq(prof_log_thr_count(), 0,
"The log has threads; it isn't empty");
- assert_zu_eq(prof_log_alloc_count(), 0,
+ expect_zu_eq(prof_log_alloc_count(), 0,
"The log has allocations; it isn't empty");
}
@@ -34,22 +35,22 @@ TEST_BEGIN(test_prof_log_many_logs) {
test_skip_if(!config_prof);
for (i = 0; i < N_PARAM; i++) {
- assert_b_eq(prof_log_is_logging(), false,
+ expect_b_eq(prof_log_is_logging(), false,
"Logging shouldn't have started yet");
- assert_d_eq(mallctl("prof.log_start", NULL, NULL, NULL, 0), 0,
+ expect_d_eq(mallctl("prof.log_start", NULL, NULL, NULL, 0), 0,
"Unexpected mallctl failure when starting logging");
- assert_b_eq(prof_log_is_logging(), true,
+ expect_b_eq(prof_log_is_logging(), true,
"Logging should be started by now");
- assert_log_empty();
- assert_rep();
+ expect_log_empty();
+ expect_rep();
f();
- assert_zu_eq(prof_log_thr_count(), 1, "Wrong thread count");
- assert_rep();
- assert_b_eq(prof_log_is_logging(), true,
+ expect_zu_eq(prof_log_thr_count(), 1, "Wrong thread count");
+ expect_rep();
+ expect_b_eq(prof_log_is_logging(), true,
"Logging should still be on");
- assert_d_eq(mallctl("prof.log_stop", NULL, NULL, NULL, 0), 0,
+ expect_d_eq(mallctl("prof.log_stop", NULL, NULL, NULL, 0), 0,
"Unexpected mallctl failure when stopping logging");
- assert_b_eq(prof_log_is_logging(), false,
+ expect_b_eq(prof_log_is_logging(), false,
"Logging should have turned off");
}
}
@@ -61,7 +62,7 @@ static void *f_thread(void *unused) {
int i;
for (i = 0; i < N_PARAM; i++) {
void *p = malloc(100);
- memset(p, 100, sizeof(char));
+ memset(p, 100, 1);
free(p);
}
@@ -73,7 +74,7 @@ TEST_BEGIN(test_prof_log_many_threads) {
test_skip_if(!config_prof);
int i;
- assert_d_eq(mallctl("prof.log_start", NULL, NULL, NULL, 0), 0,
+ expect_d_eq(mallctl("prof.log_start", NULL, NULL, NULL, 0), 0,
"Unexpected mallctl failure when starting logging");
for (i = 0; i < N_THREADS; i++) {
thd_create(&thr_buf[i], &f_thread, NULL);
@@ -82,10 +83,10 @@ TEST_BEGIN(test_prof_log_many_threads) {
for (i = 0; i < N_THREADS; i++) {
thd_join(thr_buf[i], NULL);
}
- assert_zu_eq(prof_log_thr_count(), N_THREADS,
+ expect_zu_eq(prof_log_thr_count(), N_THREADS,
"Wrong number of thread entries");
- assert_rep();
- assert_d_eq(mallctl("prof.log_stop", NULL, NULL, NULL, 0), 0,
+ expect_rep();
+ expect_d_eq(mallctl("prof.log_stop", NULL, NULL, NULL, 0), 0,
"Unexpected mallctl failure when stopping logging");
}
TEST_END
@@ -110,19 +111,19 @@ TEST_BEGIN(test_prof_log_many_traces) {
test_skip_if(!config_prof);
- assert_d_eq(mallctl("prof.log_start", NULL, NULL, NULL, 0), 0,
+ expect_d_eq(mallctl("prof.log_start", NULL, NULL, NULL, 0), 0,
"Unexpected mallctl failure when starting logging");
int i;
- assert_rep();
- assert_log_empty();
+ expect_rep();
+ expect_log_empty();
for (i = 0; i < N_PARAM; i++) {
- assert_rep();
+ expect_rep();
f1();
- assert_rep();
+ expect_rep();
f2();
- assert_rep();
+ expect_rep();
f3();
- assert_rep();
+ expect_rep();
}
/*
* There should be 8 total backtraces: two for malloc/free in f1(), two
@@ -131,16 +132,18 @@ TEST_BEGIN(test_prof_log_many_traces) {
* optimizations such as loop unrolling might generate more call sites.
* So >= 8 traces are expected.
*/
- assert_zu_ge(prof_log_bt_count(), 8,
+ expect_zu_ge(prof_log_bt_count(), 8,
"Expect at least 8 backtraces given sample workload");
- assert_d_eq(mallctl("prof.log_stop", NULL, NULL, NULL, 0), 0,
+ expect_d_eq(mallctl("prof.log_stop", NULL, NULL, NULL, 0), 0,
"Unexpected mallctl failure when stopping logging");
}
TEST_END
int
main(void) {
- prof_log_dummy_set(true);
+ if (config_prof) {
+ prof_log_dummy_set(true);
+ }
return test_no_reentrancy(
test_prof_log_many_logs,
test_prof_log_many_traces,
diff --git a/deps/jemalloc/test/unit/prof_log.sh b/deps/jemalloc/test/unit/prof_log.sh
index 8fcc7d8a7..485f9bf0a 100644
--- a/deps/jemalloc/test/unit/prof_log.sh
+++ b/deps/jemalloc/test/unit/prof_log.sh
@@ -1,5 +1,5 @@
#!/bin/sh
if [ "x${enable_prof}" = "x1" ] ; then
- export MALLOC_CONF="prof:true,lg_prof_sample:0"
+ export MALLOC_CONF="prof:true,prof_active:true,lg_prof_sample:0"
fi
diff --git a/deps/jemalloc/test/unit/prof_mdump.c b/deps/jemalloc/test/unit/prof_mdump.c
new file mode 100644
index 000000000..75b3a5159
--- /dev/null
+++ b/deps/jemalloc/test/unit/prof_mdump.c
@@ -0,0 +1,216 @@
+#include "test/jemalloc_test.h"
+
+#include "jemalloc/internal/prof_sys.h"
+
+static const char *test_filename = "test_filename";
+static bool did_prof_dump_open;
+
+static int
+prof_dump_open_file_intercept(const char *filename, int mode) {
+ int fd;
+
+ did_prof_dump_open = true;
+
+ /*
+ * Stronger than a strcmp() - verifying that we internally directly use
+ * the caller supplied char pointer.
+ */
+ expect_ptr_eq(filename, test_filename,
+ "Dump file name should be \"%s\"", test_filename);
+
+ fd = open("/dev/null", O_WRONLY);
+ assert_d_ne(fd, -1, "Unexpected open() failure");
+
+ return fd;
+}
+
+TEST_BEGIN(test_mdump_normal) {
+ test_skip_if(!config_prof);
+
+ prof_dump_open_file_t *open_file_orig = prof_dump_open_file;
+
+ void *p = mallocx(1, 0);
+ assert_ptr_not_null(p, "Unexpected mallocx() failure");
+
+ prof_dump_open_file = prof_dump_open_file_intercept;
+ did_prof_dump_open = false;
+ expect_d_eq(mallctl("prof.dump", NULL, NULL, (void *)&test_filename,
+ sizeof(test_filename)), 0,
+ "Unexpected mallctl failure while dumping");
+ expect_true(did_prof_dump_open, "Expected a profile dump");
+
+ dallocx(p, 0);
+
+ prof_dump_open_file = open_file_orig;
+}
+TEST_END
+
+static int
+prof_dump_open_file_error(const char *filename, int mode) {
+ return -1;
+}
+
+/*
+ * In the context of test_mdump_output_error, prof_dump_write_file_count is the
+ * total number of times prof_dump_write_file_error() is expected to be called.
+ * In the context of test_mdump_maps_error, prof_dump_write_file_count is the
+ * total number of times prof_dump_write_file_error() is expected to be called
+ * starting from the one that contains an 'M' (beginning the "MAPPED_LIBRARIES"
+ * header).
+ */
+static int prof_dump_write_file_count;
+
+static ssize_t
+prof_dump_write_file_error(int fd, const void *s, size_t len) {
+ --prof_dump_write_file_count;
+
+ expect_d_ge(prof_dump_write_file_count, 0,
+ "Write is called after error occurs");
+
+ if (prof_dump_write_file_count == 0) {
+ return -1;
+ } else {
+ /*
+ * Any non-negative number indicates success, and for
+ * simplicity we just use 0. When prof_dump_write_file_count
+ * is positive, it means that we haven't reached the write that
+ * we want to fail; when prof_dump_write_file_count is
+ * negative, it means that we've already violated the
+ * expect_d_ge(prof_dump_write_file_count, 0) statement above,
+ * but instead of aborting, we continue the rest of the test,
+ * and we indicate that all the writes after the failed write
+ * are successful.
+ */
+ return 0;
+ }
+}
+
+static void
+expect_write_failure(int count) {
+ prof_dump_write_file_count = count;
+ expect_d_eq(mallctl("prof.dump", NULL, NULL, (void *)&test_filename,
+ sizeof(test_filename)), EFAULT, "Dump should err");
+ expect_d_eq(prof_dump_write_file_count, 0,
+ "Dumping stopped after a wrong number of writes");
+}
+
+TEST_BEGIN(test_mdump_output_error) {
+ test_skip_if(!config_prof);
+ test_skip_if(!config_debug);
+
+ prof_dump_open_file_t *open_file_orig = prof_dump_open_file;
+ prof_dump_write_file_t *write_file_orig = prof_dump_write_file;
+
+ prof_dump_write_file = prof_dump_write_file_error;
+
+ void *p = mallocx(1, 0);
+ assert_ptr_not_null(p, "Unexpected mallocx() failure");
+
+ /*
+ * When opening the dump file fails, there shouldn't be any write, and
+ * mallctl() should return failure.
+ */
+ prof_dump_open_file = prof_dump_open_file_error;
+ expect_write_failure(0);
+
+ /*
+ * When the n-th write fails, there shouldn't be any more write, and
+ * mallctl() should return failure.
+ */
+ prof_dump_open_file = prof_dump_open_file_intercept;
+ expect_write_failure(1); /* First write fails. */
+ expect_write_failure(2); /* Second write fails. */
+
+ dallocx(p, 0);
+
+ prof_dump_open_file = open_file_orig;
+ prof_dump_write_file = write_file_orig;
+}
+TEST_END
+
+static int
+prof_dump_open_maps_error() {
+ return -1;
+}
+
+static bool started_piping_maps_file;
+
+static ssize_t
+prof_dump_write_maps_file_error(int fd, const void *s, size_t len) {
+ /* The main dump doesn't contain any capital 'M'. */
+ if (!started_piping_maps_file && strchr(s, 'M') != NULL) {
+ started_piping_maps_file = true;
+ }
+
+ if (started_piping_maps_file) {
+ return prof_dump_write_file_error(fd, s, len);
+ } else {
+ /* Return success when we haven't started piping maps. */
+ return 0;
+ }
+}
+
+static void
+expect_maps_write_failure(int count) {
+ int mfd = prof_dump_open_maps();
+ if (mfd == -1) {
+ /* No need to continue if we just can't find the maps file. */
+ return;
+ }
+ close(mfd);
+ started_piping_maps_file = false;
+ expect_write_failure(count);
+ expect_true(started_piping_maps_file, "Should start piping maps");
+}
+
+TEST_BEGIN(test_mdump_maps_error) {
+ test_skip_if(!config_prof);
+ test_skip_if(!config_debug);
+
+ prof_dump_open_file_t *open_file_orig = prof_dump_open_file;
+ prof_dump_write_file_t *write_file_orig = prof_dump_write_file;
+ prof_dump_open_maps_t *open_maps_orig = prof_dump_open_maps;
+
+ prof_dump_open_file = prof_dump_open_file_intercept;
+ prof_dump_write_file = prof_dump_write_maps_file_error;
+
+ void *p = mallocx(1, 0);
+ assert_ptr_not_null(p, "Unexpected mallocx() failure");
+
+ /*
+ * When opening the maps file fails, there shouldn't be any maps write,
+ * and mallctl() should return success.
+ */
+ prof_dump_open_maps = prof_dump_open_maps_error;
+ started_piping_maps_file = false;
+ prof_dump_write_file_count = 0;
+ expect_d_eq(mallctl("prof.dump", NULL, NULL, (void *)&test_filename,
+ sizeof(test_filename)), 0,
+ "mallctl should not fail in case of maps file opening failure");
+ expect_false(started_piping_maps_file, "Shouldn't start piping maps");
+ expect_d_eq(prof_dump_write_file_count, 0,
+ "Dumping stopped after a wrong number of writes");
+
+ /*
+ * When the n-th maps write fails (given that we are able to find the
+ * maps file), there shouldn't be any more maps write, and mallctl()
+ * should return failure.
+ */
+ prof_dump_open_maps = open_maps_orig;
+ expect_maps_write_failure(1); /* First write fails. */
+ expect_maps_write_failure(2); /* Second write fails. */
+
+ dallocx(p, 0);
+
+ prof_dump_open_file = open_file_orig;
+ prof_dump_write_file = write_file_orig;
+}
+TEST_END
+
+int
+main(void) {
+ return test(
+ test_mdump_normal,
+ test_mdump_output_error,
+ test_mdump_maps_error);
+}
diff --git a/deps/jemalloc/test/unit/prof_mdump.sh b/deps/jemalloc/test/unit/prof_mdump.sh
new file mode 100644
index 000000000..d14cb8c5e
--- /dev/null
+++ b/deps/jemalloc/test/unit/prof_mdump.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+if [ "x${enable_prof}" = "x1" ] ; then
+ export MALLOC_CONF="prof:true,lg_prof_sample:0"
+fi
+
diff --git a/deps/jemalloc/test/unit/prof_recent.c b/deps/jemalloc/test/unit/prof_recent.c
new file mode 100644
index 000000000..4fb37236f
--- /dev/null
+++ b/deps/jemalloc/test/unit/prof_recent.c
@@ -0,0 +1,678 @@
+#include "test/jemalloc_test.h"
+
+#include "jemalloc/internal/prof_recent.h"
+
+/* As specified in the shell script */
+#define OPT_ALLOC_MAX 3
+
+/* Invariant before and after every test (when config_prof is on) */
+static void
+confirm_prof_setup() {
+ /* Options */
+ assert_true(opt_prof, "opt_prof not on");
+ assert_true(opt_prof_active, "opt_prof_active not on");
+ assert_zd_eq(opt_prof_recent_alloc_max, OPT_ALLOC_MAX,
+ "opt_prof_recent_alloc_max not set correctly");
+
+ /* Dynamics */
+ assert_true(prof_active_state, "prof_active not on");
+ assert_zd_eq(prof_recent_alloc_max_ctl_read(), OPT_ALLOC_MAX,
+ "prof_recent_alloc_max not set correctly");
+}
+
+TEST_BEGIN(test_confirm_setup) {
+ test_skip_if(!config_prof);
+ confirm_prof_setup();
+}
+TEST_END
+
+TEST_BEGIN(test_prof_recent_off) {
+ test_skip_if(config_prof);
+
+ const ssize_t past_ref = 0, future_ref = 0;
+ const size_t len_ref = sizeof(ssize_t);
+
+ ssize_t past = past_ref, future = future_ref;
+ size_t len = len_ref;
+
+#define ASSERT_SHOULD_FAIL(opt, a, b, c, d) do { \
+ assert_d_eq(mallctl("experimental.prof_recent." opt, a, b, c, \
+ d), ENOENT, "Should return ENOENT when config_prof is off");\
+ assert_zd_eq(past, past_ref, "output was touched"); \
+ assert_zu_eq(len, len_ref, "output length was touched"); \
+ assert_zd_eq(future, future_ref, "input was touched"); \
+} while (0)
+
+ ASSERT_SHOULD_FAIL("alloc_max", NULL, NULL, NULL, 0);
+ ASSERT_SHOULD_FAIL("alloc_max", &past, &len, NULL, 0);
+ ASSERT_SHOULD_FAIL("alloc_max", NULL, NULL, &future, len);
+ ASSERT_SHOULD_FAIL("alloc_max", &past, &len, &future, len);
+
+#undef ASSERT_SHOULD_FAIL
+}
+TEST_END
+
+TEST_BEGIN(test_prof_recent_on) {
+ test_skip_if(!config_prof);
+
+ ssize_t past, future;
+ size_t len = sizeof(ssize_t);
+
+ confirm_prof_setup();
+
+ assert_d_eq(mallctl("experimental.prof_recent.alloc_max",
+ NULL, NULL, NULL, 0), 0, "no-op mallctl should be allowed");
+ confirm_prof_setup();
+
+ assert_d_eq(mallctl("experimental.prof_recent.alloc_max",
+ &past, &len, NULL, 0), 0, "Read error");
+ expect_zd_eq(past, OPT_ALLOC_MAX, "Wrong read result");
+ future = OPT_ALLOC_MAX + 1;
+ assert_d_eq(mallctl("experimental.prof_recent.alloc_max",
+ NULL, NULL, &future, len), 0, "Write error");
+ future = -1;
+ assert_d_eq(mallctl("experimental.prof_recent.alloc_max",
+ &past, &len, &future, len), 0, "Read/write error");
+ expect_zd_eq(past, OPT_ALLOC_MAX + 1, "Wrong read result");
+ future = -2;
+ assert_d_eq(mallctl("experimental.prof_recent.alloc_max",
+ &past, &len, &future, len), EINVAL,
+ "Invalid write should return EINVAL");
+ expect_zd_eq(past, OPT_ALLOC_MAX + 1,
+ "Output should not be touched given invalid write");
+ future = OPT_ALLOC_MAX;
+ assert_d_eq(mallctl("experimental.prof_recent.alloc_max",
+ &past, &len, &future, len), 0, "Read/write error");
+ expect_zd_eq(past, -1, "Wrong read result");
+ future = OPT_ALLOC_MAX + 2;
+ assert_d_eq(mallctl("experimental.prof_recent.alloc_max",
+ &past, &len, &future, len * 2), EINVAL,
+ "Invalid write should return EINVAL");
+ expect_zd_eq(past, -1,
+ "Output should not be touched given invalid write");
+
+ confirm_prof_setup();
+}
+TEST_END
+
+/* Reproducible sequence of request sizes */
+#define NTH_REQ_SIZE(n) ((n) * 97 + 101)
+
+static void
+confirm_malloc(void *p) {
+ assert_ptr_not_null(p, "malloc failed unexpectedly");
+ edata_t *e = emap_edata_lookup(TSDN_NULL, &arena_emap_global, p);
+ assert_ptr_not_null(e, "NULL edata for living pointer");
+ prof_recent_t *n = edata_prof_recent_alloc_get_no_lock_test(e);
+ assert_ptr_not_null(n, "Record in edata should not be NULL");
+ expect_ptr_not_null(n->alloc_tctx,
+ "alloc_tctx in record should not be NULL");
+ expect_ptr_eq(e, prof_recent_alloc_edata_get_no_lock_test(n),
+ "edata pointer in record is not correct");
+ expect_ptr_null(n->dalloc_tctx, "dalloc_tctx in record should be NULL");
+}
+
+static void
+confirm_record_size(prof_recent_t *n, unsigned kth) {
+ expect_zu_eq(n->size, NTH_REQ_SIZE(kth),
+ "Recorded allocation size is wrong");
+}
+
+static void
+confirm_record_living(prof_recent_t *n) {
+ expect_ptr_not_null(n->alloc_tctx,
+ "alloc_tctx in record should not be NULL");
+ edata_t *edata = prof_recent_alloc_edata_get_no_lock_test(n);
+ assert_ptr_not_null(edata,
+ "Recorded edata should not be NULL for living pointer");
+ expect_ptr_eq(n, edata_prof_recent_alloc_get_no_lock_test(edata),
+ "Record in edata is not correct");
+ expect_ptr_null(n->dalloc_tctx, "dalloc_tctx in record should be NULL");
+}
+
+static void
+confirm_record_released(prof_recent_t *n) {
+ expect_ptr_not_null(n->alloc_tctx,
+ "alloc_tctx in record should not be NULL");
+ expect_ptr_null(prof_recent_alloc_edata_get_no_lock_test(n),
+ "Recorded edata should be NULL for released pointer");
+ expect_ptr_not_null(n->dalloc_tctx,
+ "dalloc_tctx in record should not be NULL for released pointer");
+}
+
+TEST_BEGIN(test_prof_recent_alloc) {
+ test_skip_if(!config_prof);
+
+ bool b;
+ unsigned i, c;
+ size_t req_size;
+ void *p;
+ prof_recent_t *n;
+ ssize_t future;
+
+ confirm_prof_setup();
+
+ /*
+ * First batch of 2 * OPT_ALLOC_MAX allocations. After the
+ * (OPT_ALLOC_MAX - 1)'th allocation the recorded allocations should
+ * always be the last OPT_ALLOC_MAX allocations coming from here.
+ */
+ for (i = 0; i < 2 * OPT_ALLOC_MAX; ++i) {
+ req_size = NTH_REQ_SIZE(i);
+ p = malloc(req_size);
+ confirm_malloc(p);
+ if (i < OPT_ALLOC_MAX - 1) {
+ assert_false(ql_empty(&prof_recent_alloc_list),
+ "Empty recent allocation");
+ free(p);
+ /*
+ * The recorded allocations may still include some
+ * other allocations before the test run started,
+ * so keep allocating without checking anything.
+ */
+ continue;
+ }
+ c = 0;
+ ql_foreach(n, &prof_recent_alloc_list, link) {
+ ++c;
+ confirm_record_size(n, i + c - OPT_ALLOC_MAX);
+ if (c == OPT_ALLOC_MAX) {
+ confirm_record_living(n);
+ } else {
+ confirm_record_released(n);
+ }
+ }
+ assert_u_eq(c, OPT_ALLOC_MAX,
+ "Incorrect total number of allocations");
+ free(p);
+ }
+
+ confirm_prof_setup();
+
+ b = false;
+ assert_d_eq(mallctl("prof.active", NULL, NULL, &b, sizeof(bool)), 0,
+ "mallctl for turning off prof_active failed");
+
+ /*
+ * Second batch of OPT_ALLOC_MAX allocations. Since prof_active is
+ * turned off, this batch shouldn't be recorded.
+ */
+ for (; i < 3 * OPT_ALLOC_MAX; ++i) {
+ req_size = NTH_REQ_SIZE(i);
+ p = malloc(req_size);
+ assert_ptr_not_null(p, "malloc failed unexpectedly");
+ c = 0;
+ ql_foreach(n, &prof_recent_alloc_list, link) {
+ confirm_record_size(n, c + OPT_ALLOC_MAX);
+ confirm_record_released(n);
+ ++c;
+ }
+ assert_u_eq(c, OPT_ALLOC_MAX,
+ "Incorrect total number of allocations");
+ free(p);
+ }
+
+ b = true;
+ assert_d_eq(mallctl("prof.active", NULL, NULL, &b, sizeof(bool)), 0,
+ "mallctl for turning on prof_active failed");
+
+ confirm_prof_setup();
+
+ /*
+ * Third batch of OPT_ALLOC_MAX allocations. Since prof_active is
+ * turned back on, they should be recorded, and in the list of recorded
+ * allocations they should follow the first batch rather than the
+ * second batch.
+ */
+ for (; i < 4 * OPT_ALLOC_MAX; ++i) {
+ req_size = NTH_REQ_SIZE(i);
+ p = malloc(req_size);
+ confirm_malloc(p);
+ c = 0;
+ ql_foreach(n, &prof_recent_alloc_list, link) {
+ ++c;
+ confirm_record_size(n,
+ /* Is the allocation from the third batch? */
+ i + c - OPT_ALLOC_MAX >= 3 * OPT_ALLOC_MAX ?
+ /* If yes, then it's just recorded. */
+ i + c - OPT_ALLOC_MAX :
+ /*
+ * Otherwise, it should come from the first batch
+ * instead of the second batch.
+ */
+ i + c - 2 * OPT_ALLOC_MAX);
+ if (c == OPT_ALLOC_MAX) {
+ confirm_record_living(n);
+ } else {
+ confirm_record_released(n);
+ }
+ }
+ assert_u_eq(c, OPT_ALLOC_MAX,
+ "Incorrect total number of allocations");
+ free(p);
+ }
+
+ /* Increasing the limit shouldn't alter the list of records. */
+ future = OPT_ALLOC_MAX + 1;
+ assert_d_eq(mallctl("experimental.prof_recent.alloc_max",
+ NULL, NULL, &future, sizeof(ssize_t)), 0, "Write error");
+ c = 0;
+ ql_foreach(n, &prof_recent_alloc_list, link) {
+ confirm_record_size(n, c + 3 * OPT_ALLOC_MAX);
+ confirm_record_released(n);
+ ++c;
+ }
+ assert_u_eq(c, OPT_ALLOC_MAX,
+ "Incorrect total number of allocations");
+
+ /*
+ * Decreasing the limit shouldn't alter the list of records as long as
+ * the new limit is still no less than the length of the list.
+ */
+ future = OPT_ALLOC_MAX;
+ assert_d_eq(mallctl("experimental.prof_recent.alloc_max",
+ NULL, NULL, &future, sizeof(ssize_t)), 0, "Write error");
+ c = 0;
+ ql_foreach(n, &prof_recent_alloc_list, link) {
+ confirm_record_size(n, c + 3 * OPT_ALLOC_MAX);
+ confirm_record_released(n);
+ ++c;
+ }
+ assert_u_eq(c, OPT_ALLOC_MAX,
+ "Incorrect total number of allocations");
+
+ /*
+ * Decreasing the limit should shorten the list of records if the new
+ * limit is less than the length of the list.
+ */
+ future = OPT_ALLOC_MAX - 1;
+ assert_d_eq(mallctl("experimental.prof_recent.alloc_max",
+ NULL, NULL, &future, sizeof(ssize_t)), 0, "Write error");
+ c = 0;
+ ql_foreach(n, &prof_recent_alloc_list, link) {
+ ++c;
+ confirm_record_size(n, c + 3 * OPT_ALLOC_MAX);
+ confirm_record_released(n);
+ }
+ assert_u_eq(c, OPT_ALLOC_MAX - 1,
+ "Incorrect total number of allocations");
+
+ /* Setting to unlimited shouldn't alter the list of records. */
+ future = -1;
+ assert_d_eq(mallctl("experimental.prof_recent.alloc_max",
+ NULL, NULL, &future, sizeof(ssize_t)), 0, "Write error");
+ c = 0;
+ ql_foreach(n, &prof_recent_alloc_list, link) {
+ ++c;
+ confirm_record_size(n, c + 3 * OPT_ALLOC_MAX);
+ confirm_record_released(n);
+ }
+ assert_u_eq(c, OPT_ALLOC_MAX - 1,
+ "Incorrect total number of allocations");
+
+ /* Downshift to only one record. */
+ future = 1;
+ assert_d_eq(mallctl("experimental.prof_recent.alloc_max",
+ NULL, NULL, &future, sizeof(ssize_t)), 0, "Write error");
+ assert_false(ql_empty(&prof_recent_alloc_list), "Recent list is empty");
+ n = ql_first(&prof_recent_alloc_list);
+ confirm_record_size(n, 4 * OPT_ALLOC_MAX - 1);
+ confirm_record_released(n);
+ n = ql_next(&prof_recent_alloc_list, n, link);
+ assert_ptr_null(n, "Recent list should only contain one record");
+
+ /* Completely turn off. */
+ future = 0;
+ assert_d_eq(mallctl("experimental.prof_recent.alloc_max",
+ NULL, NULL, &future, sizeof(ssize_t)), 0, "Write error");
+ assert_true(ql_empty(&prof_recent_alloc_list),
+ "Recent list should be empty");
+
+ /* Restore the settings. */
+ future = OPT_ALLOC_MAX;
+ assert_d_eq(mallctl("experimental.prof_recent.alloc_max",
+ NULL, NULL, &future, sizeof(ssize_t)), 0, "Write error");
+ assert_true(ql_empty(&prof_recent_alloc_list),
+ "Recent list should be empty");
+
+ confirm_prof_setup();
+}
+TEST_END
+
+#undef NTH_REQ_SIZE
+
+#define DUMP_OUT_SIZE 4096
+static char dump_out[DUMP_OUT_SIZE];
+static size_t dump_out_len = 0;
+
+static void
+test_dump_write_cb(void *not_used, const char *str) {
+ size_t len = strlen(str);
+ assert(dump_out_len + len < DUMP_OUT_SIZE);
+ memcpy(dump_out + dump_out_len, str, len + 1);
+ dump_out_len += len;
+}
+
+static void
+call_dump() {
+ static void *in[2] = {test_dump_write_cb, NULL};
+ dump_out_len = 0;
+ assert_d_eq(mallctl("experimental.prof_recent.alloc_dump",
+ NULL, NULL, in, sizeof(in)), 0, "Dump mallctl raised error");
+}
+
+typedef struct {
+ size_t size;
+ size_t usize;
+ bool released;
+} confirm_record_t;
+
+#define DUMP_ERROR "Dump output is wrong"
+
+static void
+confirm_record(const char *template, const confirm_record_t *records,
+ const size_t n_records) {
+ static const char *types[2] = {"alloc", "dalloc"};
+ static char buf[64];
+
+ /*
+ * The template string would be in the form of:
+ * "{...,\"recent_alloc\":[]}",
+ * and dump_out would be in the form of:
+ * "{...,\"recent_alloc\":[...]}".
+ * Using "- 2" serves to cut right before the ending "]}".
+ */
+ assert_d_eq(memcmp(dump_out, template, strlen(template) - 2), 0,
+ DUMP_ERROR);
+ assert_d_eq(memcmp(dump_out + strlen(dump_out) - 2,
+ template + strlen(template) - 2, 2), 0, DUMP_ERROR);
+
+ const char *start = dump_out + strlen(template) - 2;
+ const char *end = dump_out + strlen(dump_out) - 2;
+ const confirm_record_t *record;
+ for (record = records; record < records + n_records; ++record) {
+
+#define ASSERT_CHAR(c) do { \
+ assert_true(start < end, DUMP_ERROR); \
+ assert_c_eq(*start++, c, DUMP_ERROR); \
+} while (0)
+
+#define ASSERT_STR(s) do { \
+ const size_t len = strlen(s); \
+ assert_true(start + len <= end, DUMP_ERROR); \
+ assert_d_eq(memcmp(start, s, len), 0, DUMP_ERROR); \
+ start += len; \
+} while (0)
+
+#define ASSERT_FORMATTED_STR(s, ...) do { \
+ malloc_snprintf(buf, sizeof(buf), s, __VA_ARGS__); \
+ ASSERT_STR(buf); \
+} while (0)
+
+ if (record != records) {
+ ASSERT_CHAR(',');
+ }
+
+ ASSERT_CHAR('{');
+
+ ASSERT_STR("\"size\"");
+ ASSERT_CHAR(':');
+ ASSERT_FORMATTED_STR("%zu", record->size);
+ ASSERT_CHAR(',');
+
+ ASSERT_STR("\"usize\"");
+ ASSERT_CHAR(':');
+ ASSERT_FORMATTED_STR("%zu", record->usize);
+ ASSERT_CHAR(',');
+
+ ASSERT_STR("\"released\"");
+ ASSERT_CHAR(':');
+ ASSERT_STR(record->released ? "true" : "false");
+ ASSERT_CHAR(',');
+
+ const char **type = types;
+ while (true) {
+ ASSERT_FORMATTED_STR("\"%s_thread_uid\"", *type);
+ ASSERT_CHAR(':');
+ while (isdigit(*start)) {
+ ++start;
+ }
+ ASSERT_CHAR(',');
+
+ if (opt_prof_sys_thread_name) {
+ ASSERT_FORMATTED_STR("\"%s_thread_name\"",
+ *type);
+ ASSERT_CHAR(':');
+ ASSERT_CHAR('"');
+ while (*start != '"') {
+ ++start;
+ }
+ ASSERT_CHAR('"');
+ ASSERT_CHAR(',');
+ }
+
+ ASSERT_FORMATTED_STR("\"%s_time\"", *type);
+ ASSERT_CHAR(':');
+ while (isdigit(*start)) {
+ ++start;
+ }
+ ASSERT_CHAR(',');
+
+ ASSERT_FORMATTED_STR("\"%s_trace\"", *type);
+ ASSERT_CHAR(':');
+ ASSERT_CHAR('[');
+ while (isdigit(*start) || *start == 'x' ||
+ (*start >= 'a' && *start <= 'f') ||
+ *start == '\"' || *start == ',') {
+ ++start;
+ }
+ ASSERT_CHAR(']');
+
+ if (strcmp(*type, "dalloc") == 0) {
+ break;
+ }
+
+ assert(strcmp(*type, "alloc") == 0);
+ if (!record->released) {
+ break;
+ }
+
+ ASSERT_CHAR(',');
+ ++type;
+ }
+
+ ASSERT_CHAR('}');
+
+#undef ASSERT_FORMATTED_STR
+#undef ASSERT_STR
+#undef ASSERT_CHAR
+
+ }
+ assert_ptr_eq(record, records + n_records, DUMP_ERROR);
+ assert_ptr_eq(start, end, DUMP_ERROR);
+}
+
+TEST_BEGIN(test_prof_recent_alloc_dump) {
+ test_skip_if(!config_prof);
+
+ confirm_prof_setup();
+
+ ssize_t future;
+ void *p, *q;
+ confirm_record_t records[2];
+
+ assert_zu_eq(lg_prof_sample, (size_t)0,
+ "lg_prof_sample not set correctly");
+
+ future = 0;
+ assert_d_eq(mallctl("experimental.prof_recent.alloc_max",
+ NULL, NULL, &future, sizeof(ssize_t)), 0, "Write error");
+ call_dump();
+ expect_str_eq(dump_out, "{\"sample_interval\":1,"
+ "\"recent_alloc_max\":0,\"recent_alloc\":[]}", DUMP_ERROR);
+
+ future = 2;
+ assert_d_eq(mallctl("experimental.prof_recent.alloc_max",
+ NULL, NULL, &future, sizeof(ssize_t)), 0, "Write error");
+ call_dump();
+ const char *template = "{\"sample_interval\":1,"
+ "\"recent_alloc_max\":2,\"recent_alloc\":[]}";
+ expect_str_eq(dump_out, template, DUMP_ERROR);
+
+ p = malloc(7);
+ call_dump();
+ records[0].size = 7;
+ records[0].usize = sz_s2u(7);
+ records[0].released = false;
+ confirm_record(template, records, 1);
+
+ q = mallocx(17, MALLOCX_ALIGN(128));
+ call_dump();
+ records[1].size = 17;
+ records[1].usize = sz_sa2u(17, 128);
+ records[1].released = false;
+ confirm_record(template, records, 2);
+
+ free(q);
+ call_dump();
+ records[1].released = true;
+ confirm_record(template, records, 2);
+
+ free(p);
+ call_dump();
+ records[0].released = true;
+ confirm_record(template, records, 2);
+
+ future = OPT_ALLOC_MAX;
+ assert_d_eq(mallctl("experimental.prof_recent.alloc_max",
+ NULL, NULL, &future, sizeof(ssize_t)), 0, "Write error");
+ confirm_prof_setup();
+}
+TEST_END
+
+#undef DUMP_ERROR
+#undef DUMP_OUT_SIZE
+
+#define N_THREADS 8
+#define N_PTRS 512
+#define N_CTLS 8
+#define N_ITERS 2048
+#define STRESS_ALLOC_MAX 4096
+
+typedef struct {
+ thd_t thd;
+ size_t id;
+ void *ptrs[N_PTRS];
+ size_t count;
+} thd_data_t;
+
+static thd_data_t thd_data[N_THREADS];
+static ssize_t test_max;
+
+static void
+test_write_cb(void *cbopaque, const char *str) {
+ sleep_ns(1000 * 1000);
+}
+
+static void *
+f_thread(void *arg) {
+ const size_t thd_id = *(size_t *)arg;
+ thd_data_t *data_p = thd_data + thd_id;
+ assert(data_p->id == thd_id);
+ data_p->count = 0;
+ uint64_t rand = (uint64_t)thd_id;
+ tsd_t *tsd = tsd_fetch();
+ assert(test_max > 1);
+ ssize_t last_max = -1;
+ for (int i = 0; i < N_ITERS; i++) {
+ rand = prng_range_u64(&rand, N_PTRS + N_CTLS * 5);
+ assert(data_p->count <= N_PTRS);
+ if (rand < data_p->count) {
+ assert(data_p->count > 0);
+ if (rand != data_p->count - 1) {
+ assert(data_p->count > 1);
+ void *temp = data_p->ptrs[rand];
+ data_p->ptrs[rand] =
+ data_p->ptrs[data_p->count - 1];
+ data_p->ptrs[data_p->count - 1] = temp;
+ }
+ free(data_p->ptrs[--data_p->count]);
+ } else if (rand < N_PTRS) {
+ assert(data_p->count < N_PTRS);
+ data_p->ptrs[data_p->count++] = malloc(1);
+ } else if (rand % 5 == 0) {
+ prof_recent_alloc_dump(tsd, test_write_cb, NULL);
+ } else if (rand % 5 == 1) {
+ last_max = prof_recent_alloc_max_ctl_read();
+ } else if (rand % 5 == 2) {
+ last_max =
+ prof_recent_alloc_max_ctl_write(tsd, test_max * 2);
+ } else if (rand % 5 == 3) {
+ last_max =
+ prof_recent_alloc_max_ctl_write(tsd, test_max);
+ } else {
+ assert(rand % 5 == 4);
+ last_max =
+ prof_recent_alloc_max_ctl_write(tsd, test_max / 2);
+ }
+ assert_zd_ge(last_max, -1, "Illegal last-N max");
+ }
+
+ while (data_p->count > 0) {
+ free(data_p->ptrs[--data_p->count]);
+ }
+
+ return NULL;
+}
+
+TEST_BEGIN(test_prof_recent_stress) {
+ test_skip_if(!config_prof);
+
+ confirm_prof_setup();
+
+ test_max = OPT_ALLOC_MAX;
+ for (size_t i = 0; i < N_THREADS; i++) {
+ thd_data_t *data_p = thd_data + i;
+ data_p->id = i;
+ thd_create(&data_p->thd, &f_thread, &data_p->id);
+ }
+ for (size_t i = 0; i < N_THREADS; i++) {
+ thd_data_t *data_p = thd_data + i;
+ thd_join(data_p->thd, NULL);
+ }
+
+ test_max = STRESS_ALLOC_MAX;
+ assert_d_eq(mallctl("experimental.prof_recent.alloc_max",
+ NULL, NULL, &test_max, sizeof(ssize_t)), 0, "Write error");
+ for (size_t i = 0; i < N_THREADS; i++) {
+ thd_data_t *data_p = thd_data + i;
+ data_p->id = i;
+ thd_create(&data_p->thd, &f_thread, &data_p->id);
+ }
+ for (size_t i = 0; i < N_THREADS; i++) {
+ thd_data_t *data_p = thd_data + i;
+ thd_join(data_p->thd, NULL);
+ }
+
+ test_max = OPT_ALLOC_MAX;
+ assert_d_eq(mallctl("experimental.prof_recent.alloc_max",
+ NULL, NULL, &test_max, sizeof(ssize_t)), 0, "Write error");
+ confirm_prof_setup();
+}
+TEST_END
+
+#undef STRESS_ALLOC_MAX
+#undef N_ITERS
+#undef N_PTRS
+#undef N_THREADS
+
+int
+main(void) {
+ return test(
+ test_confirm_setup,
+ test_prof_recent_off,
+ test_prof_recent_on,
+ test_prof_recent_alloc,
+ test_prof_recent_alloc_dump,
+ test_prof_recent_stress);
+}
diff --git a/deps/jemalloc/test/unit/prof_recent.sh b/deps/jemalloc/test/unit/prof_recent.sh
new file mode 100644
index 000000000..58a54a47b
--- /dev/null
+++ b/deps/jemalloc/test/unit/prof_recent.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+if [ "x${enable_prof}" = "x1" ] ; then
+ export MALLOC_CONF="prof:true,prof_active:true,lg_prof_sample:0,prof_recent_alloc_max:3"
+fi
diff --git a/deps/jemalloc/test/unit/prof_reset.c b/deps/jemalloc/test/unit/prof_reset.c
index 7cce42d27..9b33b2051 100644
--- a/deps/jemalloc/test/unit/prof_reset.c
+++ b/deps/jemalloc/test/unit/prof_reset.c
@@ -1,7 +1,10 @@
#include "test/jemalloc_test.h"
+#include "jemalloc/internal/prof_data.h"
+#include "jemalloc/internal/prof_sys.h"
+
static int
-prof_dump_open_intercept(bool propagate_err, const char *filename) {
+prof_dump_open_file_intercept(const char *filename, int mode) {
int fd;
fd = open("/dev/null", O_WRONLY);
@@ -12,54 +15,53 @@ prof_dump_open_intercept(bool propagate_err, const char *filename) {
static void
set_prof_active(bool active) {
- assert_d_eq(mallctl("prof.active", NULL, NULL, (void *)&active,
+ expect_d_eq(mallctl("prof.active", NULL, NULL, (void *)&active,
sizeof(active)), 0, "Unexpected mallctl failure");
}
static size_t
get_lg_prof_sample(void) {
- size_t lg_prof_sample;
+ size_t ret;
size_t sz = sizeof(size_t);
- assert_d_eq(mallctl("prof.lg_sample", (void *)&lg_prof_sample, &sz,
- NULL, 0), 0,
+ expect_d_eq(mallctl("prof.lg_sample", (void *)&ret, &sz, NULL, 0), 0,
"Unexpected mallctl failure while reading profiling sample rate");
- return lg_prof_sample;
+ return ret;
}
static void
-do_prof_reset(size_t lg_prof_sample) {
- assert_d_eq(mallctl("prof.reset", NULL, NULL,
- (void *)&lg_prof_sample, sizeof(size_t)), 0,
+do_prof_reset(size_t lg_prof_sample_input) {
+ expect_d_eq(mallctl("prof.reset", NULL, NULL,
+ (void *)&lg_prof_sample_input, sizeof(size_t)), 0,
"Unexpected mallctl failure while resetting profile data");
- assert_zu_eq(lg_prof_sample, get_lg_prof_sample(),
+ expect_zu_eq(lg_prof_sample_input, get_lg_prof_sample(),
"Expected profile sample rate change");
}
TEST_BEGIN(test_prof_reset_basic) {
- size_t lg_prof_sample_orig, lg_prof_sample, lg_prof_sample_next;
+ size_t lg_prof_sample_orig, lg_prof_sample_cur, lg_prof_sample_next;
size_t sz;
unsigned i;
test_skip_if(!config_prof);
sz = sizeof(size_t);
- assert_d_eq(mallctl("opt.lg_prof_sample", (void *)&lg_prof_sample_orig,
+ expect_d_eq(mallctl("opt.lg_prof_sample", (void *)&lg_prof_sample_orig,
&sz, NULL, 0), 0,
"Unexpected mallctl failure while reading profiling sample rate");
- assert_zu_eq(lg_prof_sample_orig, 0,
+ expect_zu_eq(lg_prof_sample_orig, 0,
"Unexpected profiling sample rate");
- lg_prof_sample = get_lg_prof_sample();
- assert_zu_eq(lg_prof_sample_orig, lg_prof_sample,
+ lg_prof_sample_cur = get_lg_prof_sample();
+ expect_zu_eq(lg_prof_sample_orig, lg_prof_sample_cur,
"Unexpected disagreement between \"opt.lg_prof_sample\" and "
"\"prof.lg_sample\"");
/* Test simple resets. */
for (i = 0; i < 2; i++) {
- assert_d_eq(mallctl("prof.reset", NULL, NULL, NULL, 0), 0,
+ expect_d_eq(mallctl("prof.reset", NULL, NULL, NULL, 0), 0,
"Unexpected mallctl failure while resetting profile data");
- lg_prof_sample = get_lg_prof_sample();
- assert_zu_eq(lg_prof_sample_orig, lg_prof_sample,
+ lg_prof_sample_cur = get_lg_prof_sample();
+ expect_zu_eq(lg_prof_sample_orig, lg_prof_sample_cur,
"Unexpected profile sample rate change");
}
@@ -67,64 +69,42 @@ TEST_BEGIN(test_prof_reset_basic) {
lg_prof_sample_next = 1;
for (i = 0; i < 2; i++) {
do_prof_reset(lg_prof_sample_next);
- lg_prof_sample = get_lg_prof_sample();
- assert_zu_eq(lg_prof_sample, lg_prof_sample_next,
+ lg_prof_sample_cur = get_lg_prof_sample();
+ expect_zu_eq(lg_prof_sample_cur, lg_prof_sample_next,
"Expected profile sample rate change");
lg_prof_sample_next = lg_prof_sample_orig;
}
/* Make sure the test code restored prof.lg_sample. */
- lg_prof_sample = get_lg_prof_sample();
- assert_zu_eq(lg_prof_sample_orig, lg_prof_sample,
+ lg_prof_sample_cur = get_lg_prof_sample();
+ expect_zu_eq(lg_prof_sample_orig, lg_prof_sample_cur,
"Unexpected disagreement between \"opt.lg_prof_sample\" and "
"\"prof.lg_sample\"");
}
TEST_END
-bool prof_dump_header_intercepted = false;
-prof_cnt_t cnt_all_copy = {0, 0, 0, 0};
-static bool
-prof_dump_header_intercept(tsdn_t *tsdn, bool propagate_err,
- const prof_cnt_t *cnt_all) {
- prof_dump_header_intercepted = true;
- memcpy(&cnt_all_copy, cnt_all, sizeof(prof_cnt_t));
-
- return false;
-}
-
TEST_BEGIN(test_prof_reset_cleanup) {
- void *p;
- prof_dump_header_t *prof_dump_header_orig;
-
test_skip_if(!config_prof);
set_prof_active(true);
- assert_zu_eq(prof_bt_count(), 0, "Expected 0 backtraces");
- p = mallocx(1, 0);
- assert_ptr_not_null(p, "Unexpected mallocx() failure");
- assert_zu_eq(prof_bt_count(), 1, "Expected 1 backtrace");
-
- prof_dump_header_orig = prof_dump_header;
- prof_dump_header = prof_dump_header_intercept;
- assert_false(prof_dump_header_intercepted, "Unexpected intercept");
+ expect_zu_eq(prof_bt_count(), 0, "Expected 0 backtraces");
+ void *p = mallocx(1, 0);
+ expect_ptr_not_null(p, "Unexpected mallocx() failure");
+ expect_zu_eq(prof_bt_count(), 1, "Expected 1 backtrace");
- assert_d_eq(mallctl("prof.dump", NULL, NULL, NULL, 0),
- 0, "Unexpected error while dumping heap profile");
- assert_true(prof_dump_header_intercepted, "Expected intercept");
- assert_u64_eq(cnt_all_copy.curobjs, 1, "Expected 1 allocation");
+ prof_cnt_t cnt_all;
+ prof_cnt_all(&cnt_all);
+ expect_u64_eq(cnt_all.curobjs, 1, "Expected 1 allocation");
- assert_d_eq(mallctl("prof.reset", NULL, NULL, NULL, 0), 0,
+ expect_d_eq(mallctl("prof.reset", NULL, NULL, NULL, 0), 0,
"Unexpected error while resetting heap profile data");
- assert_d_eq(mallctl("prof.dump", NULL, NULL, NULL, 0),
- 0, "Unexpected error while dumping heap profile");
- assert_u64_eq(cnt_all_copy.curobjs, 0, "Expected 0 allocations");
- assert_zu_eq(prof_bt_count(), 1, "Expected 1 backtrace");
-
- prof_dump_header = prof_dump_header_orig;
+ prof_cnt_all(&cnt_all);
+ expect_u64_eq(cnt_all.curobjs, 0, "Expected 0 allocations");
+ expect_zu_eq(prof_bt_count(), 1, "Expected 1 backtrace");
dallocx(p, 0);
- assert_zu_eq(prof_bt_count(), 0, "Expected 0 backtraces");
+ expect_zu_eq(prof_bt_count(), 0, "Expected 0 backtraces");
set_prof_active(false);
}
@@ -145,13 +125,13 @@ thd_start(void *varg) {
for (i = 0; i < NALLOCS_PER_THREAD; i++) {
if (i % RESET_INTERVAL == 0) {
- assert_d_eq(mallctl("prof.reset", NULL, NULL, NULL, 0),
+ expect_d_eq(mallctl("prof.reset", NULL, NULL, NULL, 0),
0, "Unexpected error while resetting heap profile "
"data");
}
if (i % DUMP_INTERVAL == 0) {
- assert_d_eq(mallctl("prof.dump", NULL, NULL, NULL, 0),
+ expect_d_eq(mallctl("prof.dump", NULL, NULL, NULL, 0),
0, "Unexpected error while dumping heap profile");
}
@@ -162,7 +142,7 @@ thd_start(void *varg) {
*pp = NULL;
}
*pp = btalloc(1, thd_ind*NALLOCS_PER_THREAD + i);
- assert_ptr_not_null(*pp,
+ expect_ptr_not_null(*pp,
"Unexpected btalloc() failure");
}
}
@@ -189,7 +169,7 @@ TEST_BEGIN(test_prof_reset) {
test_skip_if(!config_prof);
bt_count = prof_bt_count();
- assert_zu_eq(bt_count, 0,
+ expect_zu_eq(bt_count, 0,
"Unexpected pre-existing tdata structures");
tdata_count = prof_tdata_count();
@@ -206,9 +186,9 @@ TEST_BEGIN(test_prof_reset) {
thd_join(thds[i], NULL);
}
- assert_zu_eq(prof_bt_count(), bt_count,
+ expect_zu_eq(prof_bt_count(), bt_count,
"Unexpected bactrace count change");
- assert_zu_eq(prof_tdata_count(), tdata_count,
+ expect_zu_eq(prof_tdata_count(), tdata_count,
"Unexpected remaining tdata structures");
set_prof_active(false);
@@ -246,19 +226,19 @@ TEST_BEGIN(test_xallocx) {
/* Allocate small object (which will be promoted). */
p = ptrs[i] = mallocx(1, 0);
- assert_ptr_not_null(p, "Unexpected mallocx() failure");
+ expect_ptr_not_null(p, "Unexpected mallocx() failure");
/* Reset profiling. */
do_prof_reset(0);
/* Perform successful xallocx(). */
sz = sallocx(p, 0);
- assert_zu_eq(xallocx(p, sz, 0, 0), sz,
+ expect_zu_eq(xallocx(p, sz, 0, 0), sz,
"Unexpected xallocx() failure");
/* Perform unsuccessful xallocx(). */
nsz = nallocx(sz+1, 0);
- assert_zu_eq(xallocx(p, nsz, 0, 0), sz,
+ expect_zu_eq(xallocx(p, nsz, 0, 0), sz,
"Unexpected xallocx() success");
}
@@ -276,7 +256,7 @@ TEST_END
int
main(void) {
/* Intercept dumping prior to running any tests. */
- prof_dump_open = prof_dump_open_intercept;
+ prof_dump_open_file = prof_dump_open_file_intercept;
return test_no_reentrancy(
test_prof_reset_basic,
diff --git a/deps/jemalloc/test/unit/prof_reset.sh b/deps/jemalloc/test/unit/prof_reset.sh
index 43c516a08..daefeb70c 100644
--- a/deps/jemalloc/test/unit/prof_reset.sh
+++ b/deps/jemalloc/test/unit/prof_reset.sh
@@ -1,5 +1,5 @@
#!/bin/sh
if [ "x${enable_prof}" = "x1" ] ; then
- export MALLOC_CONF="prof:true,prof_active:false,lg_prof_sample:0"
+ export MALLOC_CONF="prof:true,prof_active:false,lg_prof_sample:0,prof_recent_alloc_max:0"
fi
diff --git a/deps/jemalloc/test/unit/prof_stats.c b/deps/jemalloc/test/unit/prof_stats.c
new file mode 100644
index 000000000..c88c4ae0f
--- /dev/null
+++ b/deps/jemalloc/test/unit/prof_stats.c
@@ -0,0 +1,151 @@
+#include "test/jemalloc_test.h"
+
+#define N_PTRS 3
+
+static void
+test_combinations(szind_t ind, size_t sizes_array[N_PTRS],
+ int flags_array[N_PTRS]) {
+#define MALLCTL_STR_LEN 64
+ assert(opt_prof && opt_prof_stats);
+
+ char mallctl_live_str[MALLCTL_STR_LEN];
+ char mallctl_accum_str[MALLCTL_STR_LEN];
+ if (ind < SC_NBINS) {
+ malloc_snprintf(mallctl_live_str, MALLCTL_STR_LEN,
+ "prof.stats.bins.%u.live", (unsigned)ind);
+ malloc_snprintf(mallctl_accum_str, MALLCTL_STR_LEN,
+ "prof.stats.bins.%u.accum", (unsigned)ind);
+ } else {
+ malloc_snprintf(mallctl_live_str, MALLCTL_STR_LEN,
+ "prof.stats.lextents.%u.live", (unsigned)(ind - SC_NBINS));
+ malloc_snprintf(mallctl_accum_str, MALLCTL_STR_LEN,
+ "prof.stats.lextents.%u.accum", (unsigned)(ind - SC_NBINS));
+ }
+
+ size_t stats_len = 2 * sizeof(uint64_t);
+
+ uint64_t live_stats_orig[2];
+ assert_d_eq(mallctl(mallctl_live_str, &live_stats_orig, &stats_len,
+ NULL, 0), 0, "");
+ uint64_t accum_stats_orig[2];
+ assert_d_eq(mallctl(mallctl_accum_str, &accum_stats_orig, &stats_len,
+ NULL, 0), 0, "");
+
+ void *ptrs[N_PTRS];
+
+ uint64_t live_req_sum = 0;
+ uint64_t live_count = 0;
+ uint64_t accum_req_sum = 0;
+ uint64_t accum_count = 0;
+
+ for (size_t i = 0; i < N_PTRS; ++i) {
+ size_t sz = sizes_array[i];
+ int flags = flags_array[i];
+ void *p = mallocx(sz, flags);
+ assert_ptr_not_null(p, "malloc() failed");
+ assert(TEST_MALLOC_SIZE(p) == sz_index2size(ind));
+ ptrs[i] = p;
+ live_req_sum += sz;
+ live_count++;
+ accum_req_sum += sz;
+ accum_count++;
+ uint64_t live_stats[2];
+ assert_d_eq(mallctl(mallctl_live_str, &live_stats, &stats_len,
+ NULL, 0), 0, "");
+ expect_u64_eq(live_stats[0] - live_stats_orig[0],
+ live_req_sum, "");
+ expect_u64_eq(live_stats[1] - live_stats_orig[1],
+ live_count, "");
+ uint64_t accum_stats[2];
+ assert_d_eq(mallctl(mallctl_accum_str, &accum_stats, &stats_len,
+ NULL, 0), 0, "");
+ expect_u64_eq(accum_stats[0] - accum_stats_orig[0],
+ accum_req_sum, "");
+ expect_u64_eq(accum_stats[1] - accum_stats_orig[1],
+ accum_count, "");
+ }
+
+ for (size_t i = 0; i < N_PTRS; ++i) {
+ size_t sz = sizes_array[i];
+ int flags = flags_array[i];
+ sdallocx(ptrs[i], sz, flags);
+ live_req_sum -= sz;
+ live_count--;
+ uint64_t live_stats[2];
+ assert_d_eq(mallctl(mallctl_live_str, &live_stats, &stats_len,
+ NULL, 0), 0, "");
+ expect_u64_eq(live_stats[0] - live_stats_orig[0],
+ live_req_sum, "");
+ expect_u64_eq(live_stats[1] - live_stats_orig[1],
+ live_count, "");
+ uint64_t accum_stats[2];
+ assert_d_eq(mallctl(mallctl_accum_str, &accum_stats, &stats_len,
+ NULL, 0), 0, "");
+ expect_u64_eq(accum_stats[0] - accum_stats_orig[0],
+ accum_req_sum, "");
+ expect_u64_eq(accum_stats[1] - accum_stats_orig[1],
+ accum_count, "");
+ }
+#undef MALLCTL_STR_LEN
+}
+
+static void
+test_szind_wrapper(szind_t ind) {
+ size_t sizes_array[N_PTRS];
+ int flags_array[N_PTRS];
+ for (size_t i = 0, sz = sz_index2size(ind) - N_PTRS; i < N_PTRS;
+ ++i, ++sz) {
+ sizes_array[i] = sz;
+ flags_array[i] = 0;
+ }
+ test_combinations(ind, sizes_array, flags_array);
+}
+
+TEST_BEGIN(test_prof_stats) {
+ test_skip_if(!config_prof);
+ test_szind_wrapper(0);
+ test_szind_wrapper(1);
+ test_szind_wrapper(2);
+ test_szind_wrapper(SC_NBINS);
+ test_szind_wrapper(SC_NBINS + 1);
+ test_szind_wrapper(SC_NBINS + 2);
+}
+TEST_END
+
+static void
+test_szind_aligned_wrapper(szind_t ind, unsigned lg_align) {
+ size_t sizes_array[N_PTRS];
+ int flags_array[N_PTRS];
+ int flags = MALLOCX_LG_ALIGN(lg_align);
+ for (size_t i = 0, sz = sz_index2size(ind) - N_PTRS; i < N_PTRS;
+ ++i, ++sz) {
+ sizes_array[i] = sz;
+ flags_array[i] = flags;
+ }
+ test_combinations(
+ sz_size2index(sz_sa2u(sz_index2size(ind), 1 << lg_align)),
+ sizes_array, flags_array);
+}
+
+TEST_BEGIN(test_prof_stats_aligned) {
+ test_skip_if(!config_prof);
+ for (szind_t ind = 0; ind < 10; ++ind) {
+ for (unsigned lg_align = 0; lg_align < 10; ++lg_align) {
+ test_szind_aligned_wrapper(ind, lg_align);
+ }
+ }
+ for (szind_t ind = SC_NBINS - 5; ind < SC_NBINS + 5; ++ind) {
+ for (unsigned lg_align = SC_LG_LARGE_MINCLASS - 5;
+ lg_align < SC_LG_LARGE_MINCLASS + 5; ++lg_align) {
+ test_szind_aligned_wrapper(ind, lg_align);
+ }
+ }
+}
+TEST_END
+
+int
+main(void) {
+ return test(
+ test_prof_stats,
+ test_prof_stats_aligned);
+}
diff --git a/deps/jemalloc/test/unit/prof_stats.sh b/deps/jemalloc/test/unit/prof_stats.sh
new file mode 100644
index 000000000..f3c819b57
--- /dev/null
+++ b/deps/jemalloc/test/unit/prof_stats.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+if [ "x${enable_prof}" = "x1" ] ; then
+ export MALLOC_CONF="prof:true,prof_active:true,lg_prof_sample:0,prof_stats:true"
+fi
diff --git a/deps/jemalloc/test/unit/prof_sys_thread_name.c b/deps/jemalloc/test/unit/prof_sys_thread_name.c
new file mode 100644
index 000000000..affc788aa
--- /dev/null
+++ b/deps/jemalloc/test/unit/prof_sys_thread_name.c
@@ -0,0 +1,77 @@
+#include "test/jemalloc_test.h"
+
+#include "jemalloc/internal/prof_sys.h"
+
+static const char *test_thread_name = "test_name";
+
+static int
+test_prof_sys_thread_name_read_error(char *buf, size_t limit) {
+ return ENOSYS;
+}
+
+static int
+test_prof_sys_thread_name_read(char *buf, size_t limit) {
+ assert(strlen(test_thread_name) < limit);
+ strncpy(buf, test_thread_name, limit);
+ return 0;
+}
+
+static int
+test_prof_sys_thread_name_read_clear(char *buf, size_t limit) {
+ assert(limit > 0);
+ buf[0] = '\0';
+ return 0;
+}
+
+TEST_BEGIN(test_prof_sys_thread_name) {
+ test_skip_if(!config_prof);
+
+ bool oldval;
+ size_t sz = sizeof(oldval);
+ assert_d_eq(mallctl("opt.prof_sys_thread_name", &oldval, &sz, NULL, 0),
+ 0, "mallctl failed");
+ assert_true(oldval, "option was not set correctly");
+
+ const char *thread_name;
+ sz = sizeof(thread_name);
+ assert_d_eq(mallctl("thread.prof.name", &thread_name, &sz, NULL, 0), 0,
+ "mallctl read for thread name should not fail");
+ expect_str_eq(thread_name, "", "Initial thread name should be empty");
+
+ thread_name = test_thread_name;
+ assert_d_eq(mallctl("thread.prof.name", NULL, NULL, &thread_name, sz),
+ ENOENT, "mallctl write for thread name should fail");
+ assert_ptr_eq(thread_name, test_thread_name,
+ "Thread name should not be touched");
+
+ prof_sys_thread_name_read = test_prof_sys_thread_name_read_error;
+ void *p = malloc(1);
+ free(p);
+ assert_d_eq(mallctl("thread.prof.name", &thread_name, &sz, NULL, 0), 0,
+ "mallctl read for thread name should not fail");
+ assert_str_eq(thread_name, "",
+ "Thread name should stay the same if the system call fails");
+
+ prof_sys_thread_name_read = test_prof_sys_thread_name_read;
+ p = malloc(1);
+ free(p);
+ assert_d_eq(mallctl("thread.prof.name", &thread_name, &sz, NULL, 0), 0,
+ "mallctl read for thread name should not fail");
+ assert_str_eq(thread_name, test_thread_name,
+ "Thread name should be changed if the system call succeeds");
+
+ prof_sys_thread_name_read = test_prof_sys_thread_name_read_clear;
+ p = malloc(1);
+ free(p);
+ assert_d_eq(mallctl("thread.prof.name", &thread_name, &sz, NULL, 0), 0,
+ "mallctl read for thread name should not fail");
+ expect_str_eq(thread_name, "", "Thread name should be updated if the "
+ "system call returns a different name");
+}
+TEST_END
+
+int
+main(void) {
+ return test(
+ test_prof_sys_thread_name);
+}
diff --git a/deps/jemalloc/test/unit/prof_sys_thread_name.sh b/deps/jemalloc/test/unit/prof_sys_thread_name.sh
new file mode 100644
index 000000000..1f02a8a80
--- /dev/null
+++ b/deps/jemalloc/test/unit/prof_sys_thread_name.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+if [ "x${enable_prof}" = "x1" ] ; then
+ export MALLOC_CONF="prof:true,prof_active:true,lg_prof_sample:0,prof_sys_thread_name:true"
+fi
diff --git a/deps/jemalloc/test/unit/prof_tctx.c b/deps/jemalloc/test/unit/prof_tctx.c
index ff3b2b0ca..e0efdc36a 100644
--- a/deps/jemalloc/test/unit/prof_tctx.c
+++ b/deps/jemalloc/test/unit/prof_tctx.c
@@ -1,40 +1,42 @@
#include "test/jemalloc_test.h"
+#include "jemalloc/internal/prof_data.h"
+
TEST_BEGIN(test_prof_realloc) {
- tsdn_t *tsdn;
+ tsd_t *tsd;
int flags;
void *p, *q;
- prof_tctx_t *tctx_p, *tctx_q;
- uint64_t curobjs_0, curobjs_1, curobjs_2, curobjs_3;
+ prof_info_t prof_info_p, prof_info_q;
+ prof_cnt_t cnt_0, cnt_1, cnt_2, cnt_3;
test_skip_if(!config_prof);
- tsdn = tsdn_fetch();
+ tsd = tsd_fetch();
flags = MALLOCX_TCACHE_NONE;
- prof_cnt_all(&curobjs_0, NULL, NULL, NULL);
+ prof_cnt_all(&cnt_0);
p = mallocx(1024, flags);
- assert_ptr_not_null(p, "Unexpected mallocx() failure");
- tctx_p = prof_tctx_get(tsdn, p, NULL);
- assert_ptr_ne(tctx_p, (prof_tctx_t *)(uintptr_t)1U,
+ expect_ptr_not_null(p, "Unexpected mallocx() failure");
+ prof_info_get(tsd, p, NULL, &prof_info_p);
+ expect_ptr_ne(prof_info_p.alloc_tctx, (prof_tctx_t *)(uintptr_t)1U,
"Expected valid tctx");
- prof_cnt_all(&curobjs_1, NULL, NULL, NULL);
- assert_u64_eq(curobjs_0 + 1, curobjs_1,
+ prof_cnt_all(&cnt_1);
+ expect_u64_eq(cnt_0.curobjs + 1, cnt_1.curobjs,
"Allocation should have increased sample size");
q = rallocx(p, 2048, flags);
- assert_ptr_ne(p, q, "Expected move");
- assert_ptr_not_null(p, "Unexpected rmallocx() failure");
- tctx_q = prof_tctx_get(tsdn, q, NULL);
- assert_ptr_ne(tctx_q, (prof_tctx_t *)(uintptr_t)1U,
+ expect_ptr_ne(p, q, "Expected move");
+ expect_ptr_not_null(p, "Unexpected rmallocx() failure");
+ prof_info_get(tsd, q, NULL, &prof_info_q);
+ expect_ptr_ne(prof_info_q.alloc_tctx, (prof_tctx_t *)(uintptr_t)1U,
"Expected valid tctx");
- prof_cnt_all(&curobjs_2, NULL, NULL, NULL);
- assert_u64_eq(curobjs_1, curobjs_2,
+ prof_cnt_all(&cnt_2);
+ expect_u64_eq(cnt_1.curobjs, cnt_2.curobjs,
"Reallocation should not have changed sample size");
dallocx(q, flags);
- prof_cnt_all(&curobjs_3, NULL, NULL, NULL);
- assert_u64_eq(curobjs_0, curobjs_3,
+ prof_cnt_all(&cnt_3);
+ expect_u64_eq(cnt_0.curobjs, cnt_3.curobjs,
"Sample size should have returned to base level");
}
TEST_END
diff --git a/deps/jemalloc/test/unit/prof_tctx.sh b/deps/jemalloc/test/unit/prof_tctx.sh
index 8fcc7d8a7..485f9bf0a 100644
--- a/deps/jemalloc/test/unit/prof_tctx.sh
+++ b/deps/jemalloc/test/unit/prof_tctx.sh
@@ -1,5 +1,5 @@
#!/bin/sh
if [ "x${enable_prof}" = "x1" ] ; then
- export MALLOC_CONF="prof:true,lg_prof_sample:0"
+ export MALLOC_CONF="prof:true,prof_active:true,lg_prof_sample:0"
fi
diff --git a/deps/jemalloc/test/unit/prof_thread_name.c b/deps/jemalloc/test/unit/prof_thread_name.c
index c9c2a2b76..3c4614fca 100644
--- a/deps/jemalloc/test/unit/prof_thread_name.c
+++ b/deps/jemalloc/test/unit/prof_thread_name.c
@@ -7,11 +7,11 @@ mallctl_thread_name_get_impl(const char *thread_name_expected, const char *func,
size_t sz;
sz = sizeof(thread_name_old);
- assert_d_eq(mallctl("thread.prof.name", (void *)&thread_name_old, &sz,
+ expect_d_eq(mallctl("thread.prof.name", (void *)&thread_name_old, &sz,
NULL, 0), 0,
"%s():%d: Unexpected mallctl failure reading thread.prof.name",
func, line);
- assert_str_eq(thread_name_old, thread_name_expected,
+ expect_str_eq(thread_name_old, thread_name_expected,
"%s():%d: Unexpected thread.prof.name value", func, line);
}
#define mallctl_thread_name_get(a) \
@@ -20,9 +20,9 @@ mallctl_thread_name_get_impl(const char *thread_name_expected, const char *func,
static void
mallctl_thread_name_set_impl(const char *thread_name, const char *func,
int line) {
- assert_d_eq(mallctl("thread.prof.name", NULL, NULL,
+ expect_d_eq(mallctl("thread.prof.name", NULL, NULL,
(void *)&thread_name, sizeof(thread_name)), 0,
- "%s():%d: Unexpected mallctl failure reading thread.prof.name",
+ "%s():%d: Unexpected mallctl failure writing thread.prof.name",
func, line);
mallctl_thread_name_get_impl(thread_name, func, line);
}
@@ -33,20 +33,21 @@ TEST_BEGIN(test_prof_thread_name_validation) {
const char *thread_name;
test_skip_if(!config_prof);
+ test_skip_if(opt_prof_sys_thread_name);
mallctl_thread_name_get("");
mallctl_thread_name_set("hi there");
/* NULL input shouldn't be allowed. */
thread_name = NULL;
- assert_d_eq(mallctl("thread.prof.name", NULL, NULL,
+ expect_d_eq(mallctl("thread.prof.name", NULL, NULL,
(void *)&thread_name, sizeof(thread_name)), EFAULT,
"Unexpected mallctl result writing \"%s\" to thread.prof.name",
thread_name);
/* '\n' shouldn't be allowed. */
thread_name = "hi\nthere";
- assert_d_eq(mallctl("thread.prof.name", NULL, NULL,
+ expect_d_eq(mallctl("thread.prof.name", NULL, NULL,
(void *)&thread_name, sizeof(thread_name)), EFAULT,
"Unexpected mallctl result writing \"%s\" to thread.prof.name",
thread_name);
@@ -57,7 +58,7 @@ TEST_BEGIN(test_prof_thread_name_validation) {
size_t sz;
sz = sizeof(thread_name_old);
- assert_d_eq(mallctl("thread.prof.name",
+ expect_d_eq(mallctl("thread.prof.name",
(void *)&thread_name_old, &sz, (void *)&thread_name,
sizeof(thread_name)), EPERM,
"Unexpected mallctl result writing \"%s\" to "
@@ -82,7 +83,7 @@ thd_start(void *varg) {
mallctl_thread_name_set(thread_name);
for (i = 0; i < NRESET; i++) {
- assert_d_eq(mallctl("prof.reset", NULL, NULL, NULL, 0), 0,
+ expect_d_eq(mallctl("prof.reset", NULL, NULL, NULL, 0), 0,
"Unexpected error while resetting heap profile data");
mallctl_thread_name_get(thread_name);
}
@@ -94,12 +95,13 @@ thd_start(void *varg) {
}
TEST_BEGIN(test_prof_thread_name_threaded) {
+ test_skip_if(!config_prof);
+ test_skip_if(opt_prof_sys_thread_name);
+
thd_t thds[NTHREADS];
unsigned thd_args[NTHREADS];
unsigned i;
- test_skip_if(!config_prof);
-
for (i = 0; i < NTHREADS; i++) {
thd_args[i] = i;
thd_create(&thds[i], thd_start, (void *)&thd_args[i]);
diff --git a/deps/jemalloc/test/unit/psset.c b/deps/jemalloc/test/unit/psset.c
new file mode 100644
index 000000000..6ff720129
--- /dev/null
+++ b/deps/jemalloc/test/unit/psset.c
@@ -0,0 +1,748 @@
+#include "test/jemalloc_test.h"
+
+#include "jemalloc/internal/psset.h"
+
+#define PAGESLAB_ADDR ((void *)(1234 * HUGEPAGE))
+#define PAGESLAB_AGE 5678
+
+#define ALLOC_ARENA_IND 111
+#define ALLOC_ESN 222
+
+static void
+edata_init_test(edata_t *edata) {
+ memset(edata, 0, sizeof(*edata));
+ edata_arena_ind_set(edata, ALLOC_ARENA_IND);
+ edata_esn_set(edata, ALLOC_ESN);
+}
+
+static void
+test_psset_fake_purge(hpdata_t *ps) {
+ hpdata_purge_state_t purge_state;
+ hpdata_alloc_allowed_set(ps, false);
+ hpdata_purge_begin(ps, &purge_state);
+ void *addr;
+ size_t size;
+ while (hpdata_purge_next(ps, &purge_state, &addr, &size)) {
+ }
+ hpdata_purge_end(ps, &purge_state);
+ hpdata_alloc_allowed_set(ps, true);
+}
+
+static void
+test_psset_alloc_new(psset_t *psset, hpdata_t *ps, edata_t *r_edata,
+ size_t size) {
+ hpdata_assert_empty(ps);
+
+ test_psset_fake_purge(ps);
+
+ psset_insert(psset, ps);
+ psset_update_begin(psset, ps);
+
+ void *addr = hpdata_reserve_alloc(ps, size);
+ edata_init(r_edata, edata_arena_ind_get(r_edata), addr, size,
+ /* slab */ false, SC_NSIZES, /* sn */ 0, extent_state_active,
+ /* zeroed */ false, /* committed */ true, EXTENT_PAI_HPA,
+ EXTENT_NOT_HEAD);
+ edata_ps_set(r_edata, ps);
+ psset_update_end(psset, ps);
+}
+
+static bool
+test_psset_alloc_reuse(psset_t *psset, edata_t *r_edata, size_t size) {
+ hpdata_t *ps = psset_pick_alloc(psset, size);
+ if (ps == NULL) {
+ return true;
+ }
+ psset_update_begin(psset, ps);
+ void *addr = hpdata_reserve_alloc(ps, size);
+ edata_init(r_edata, edata_arena_ind_get(r_edata), addr, size,
+ /* slab */ false, SC_NSIZES, /* sn */ 0, extent_state_active,
+ /* zeroed */ false, /* committed */ true, EXTENT_PAI_HPA,
+ EXTENT_NOT_HEAD);
+ edata_ps_set(r_edata, ps);
+ psset_update_end(psset, ps);
+ return false;
+}
+
+static hpdata_t *
+test_psset_dalloc(psset_t *psset, edata_t *edata) {
+ hpdata_t *ps = edata_ps_get(edata);
+ psset_update_begin(psset, ps);
+ hpdata_unreserve(ps, edata_addr_get(edata), edata_size_get(edata));
+ psset_update_end(psset, ps);
+ if (hpdata_empty(ps)) {
+ psset_remove(psset, ps);
+ return ps;
+ } else {
+ return NULL;
+ }
+}
+
+static void
+edata_expect(edata_t *edata, size_t page_offset, size_t page_cnt) {
+ /*
+ * Note that allocations should get the arena ind of their home
+ * arena, *not* the arena ind of the pageslab allocator.
+ */
+ expect_u_eq(ALLOC_ARENA_IND, edata_arena_ind_get(edata),
+ "Arena ind changed");
+ expect_ptr_eq(
+ (void *)((uintptr_t)PAGESLAB_ADDR + (page_offset << LG_PAGE)),
+ edata_addr_get(edata), "Didn't allocate in order");
+ expect_zu_eq(page_cnt << LG_PAGE, edata_size_get(edata), "");
+ expect_false(edata_slab_get(edata), "");
+ expect_u_eq(SC_NSIZES, edata_szind_get_maybe_invalid(edata),
+ "");
+ expect_u64_eq(0, edata_sn_get(edata), "");
+ expect_d_eq(edata_state_get(edata), extent_state_active, "");
+ expect_false(edata_zeroed_get(edata), "");
+ expect_true(edata_committed_get(edata), "");
+ expect_d_eq(EXTENT_PAI_HPA, edata_pai_get(edata), "");
+ expect_false(edata_is_head_get(edata), "");
+}
+
+TEST_BEGIN(test_empty) {
+ bool err;
+ hpdata_t pageslab;
+ hpdata_init(&pageslab, PAGESLAB_ADDR, PAGESLAB_AGE);
+
+ edata_t alloc;
+ edata_init_test(&alloc);
+
+ psset_t psset;
+ psset_init(&psset);
+
+ /* Empty psset should return fail allocations. */
+ err = test_psset_alloc_reuse(&psset, &alloc, PAGE);
+ expect_true(err, "Empty psset succeeded in an allocation.");
+}
+TEST_END
+
+TEST_BEGIN(test_fill) {
+ bool err;
+
+ hpdata_t pageslab;
+ hpdata_init(&pageslab, PAGESLAB_ADDR, PAGESLAB_AGE);
+
+ edata_t alloc[HUGEPAGE_PAGES];
+
+ psset_t psset;
+ psset_init(&psset);
+
+ edata_init_test(&alloc[0]);
+ test_psset_alloc_new(&psset, &pageslab, &alloc[0], PAGE);
+ for (size_t i = 1; i < HUGEPAGE_PAGES; i++) {
+ edata_init_test(&alloc[i]);
+ err = test_psset_alloc_reuse(&psset, &alloc[i], PAGE);
+ expect_false(err, "Nonempty psset failed page allocation.");
+ }
+
+ for (size_t i = 0; i < HUGEPAGE_PAGES; i++) {
+ edata_t *edata = &alloc[i];
+ edata_expect(edata, i, 1);
+ }
+
+ /* The pageslab, and thus psset, should now have no allocations. */
+ edata_t extra_alloc;
+ edata_init_test(&extra_alloc);
+ err = test_psset_alloc_reuse(&psset, &extra_alloc, PAGE);
+ expect_true(err, "Alloc succeeded even though psset should be empty");
+}
+TEST_END
+
+TEST_BEGIN(test_reuse) {
+ bool err;
+ hpdata_t *ps;
+
+ hpdata_t pageslab;
+ hpdata_init(&pageslab, PAGESLAB_ADDR, PAGESLAB_AGE);
+
+ edata_t alloc[HUGEPAGE_PAGES];
+
+ psset_t psset;
+ psset_init(&psset);
+
+ edata_init_test(&alloc[0]);
+ test_psset_alloc_new(&psset, &pageslab, &alloc[0], PAGE);
+ for (size_t i = 1; i < HUGEPAGE_PAGES; i++) {
+ edata_init_test(&alloc[i]);
+ err = test_psset_alloc_reuse(&psset, &alloc[i], PAGE);
+ expect_false(err, "Nonempty psset failed page allocation.");
+ }
+
+ /* Free odd indices. */
+ for (size_t i = 0; i < HUGEPAGE_PAGES; i ++) {
+ if (i % 2 == 0) {
+ continue;
+ }
+ ps = test_psset_dalloc(&psset, &alloc[i]);
+ expect_ptr_null(ps, "Nonempty pageslab evicted");
+ }
+ /* Realloc into them. */
+ for (size_t i = 0; i < HUGEPAGE_PAGES; i++) {
+ if (i % 2 == 0) {
+ continue;
+ }
+ err = test_psset_alloc_reuse(&psset, &alloc[i], PAGE);
+ expect_false(err, "Nonempty psset failed page allocation.");
+ edata_expect(&alloc[i], i, 1);
+ }
+ /* Now, free the pages at indices 0 or 1 mod 2. */
+ for (size_t i = 0; i < HUGEPAGE_PAGES; i++) {
+ if (i % 4 > 1) {
+ continue;
+ }
+ ps = test_psset_dalloc(&psset, &alloc[i]);
+ expect_ptr_null(ps, "Nonempty pageslab evicted");
+ }
+ /* And realloc 2-page allocations into them. */
+ for (size_t i = 0; i < HUGEPAGE_PAGES; i++) {
+ if (i % 4 != 0) {
+ continue;
+ }
+ err = test_psset_alloc_reuse(&psset, &alloc[i], 2 * PAGE);
+ expect_false(err, "Nonempty psset failed page allocation.");
+ edata_expect(&alloc[i], i, 2);
+ }
+ /* Free all the 2-page allocations. */
+ for (size_t i = 0; i < HUGEPAGE_PAGES; i++) {
+ if (i % 4 != 0) {
+ continue;
+ }
+ ps = test_psset_dalloc(&psset, &alloc[i]);
+ expect_ptr_null(ps, "Nonempty pageslab evicted");
+ }
+ /*
+ * Free up a 1-page hole next to a 2-page hole, but somewhere in the
+ * middle of the pageslab. Index 11 should be right before such a hole
+ * (since 12 % 4 == 0).
+ */
+ size_t index_of_3 = 11;
+ ps = test_psset_dalloc(&psset, &alloc[index_of_3]);
+ expect_ptr_null(ps, "Nonempty pageslab evicted");
+ err = test_psset_alloc_reuse(&psset, &alloc[index_of_3], 3 * PAGE);
+ expect_false(err, "Should have been able to find alloc.");
+ edata_expect(&alloc[index_of_3], index_of_3, 3);
+
+ /*
+ * Free up a 4-page hole at the end. Recall that the pages at offsets 0
+ * and 1 mod 4 were freed above, so we just have to free the last
+ * allocations.
+ */
+ ps = test_psset_dalloc(&psset, &alloc[HUGEPAGE_PAGES - 1]);
+ expect_ptr_null(ps, "Nonempty pageslab evicted");
+ ps = test_psset_dalloc(&psset, &alloc[HUGEPAGE_PAGES - 2]);
+ expect_ptr_null(ps, "Nonempty pageslab evicted");
+
+ /* Make sure we can satisfy an allocation at the very end of a slab. */
+ size_t index_of_4 = HUGEPAGE_PAGES - 4;
+ err = test_psset_alloc_reuse(&psset, &alloc[index_of_4], 4 * PAGE);
+ expect_false(err, "Should have been able to find alloc.");
+ edata_expect(&alloc[index_of_4], index_of_4, 4);
+}
+TEST_END
+
+TEST_BEGIN(test_evict) {
+ bool err;
+ hpdata_t *ps;
+
+ hpdata_t pageslab;
+ hpdata_init(&pageslab, PAGESLAB_ADDR, PAGESLAB_AGE);
+
+ edata_t alloc[HUGEPAGE_PAGES];
+
+ psset_t psset;
+ psset_init(&psset);
+
+ /* Alloc the whole slab. */
+ edata_init_test(&alloc[0]);
+ test_psset_alloc_new(&psset, &pageslab, &alloc[0], PAGE);
+ for (size_t i = 1; i < HUGEPAGE_PAGES; i++) {
+ edata_init_test(&alloc[i]);
+ err = test_psset_alloc_reuse(&psset, &alloc[i], PAGE);
+ expect_false(err, "Unxpected allocation failure");
+ }
+
+ /* Dealloc the whole slab, going forwards. */
+ for (size_t i = 0; i < HUGEPAGE_PAGES - 1; i++) {
+ ps = test_psset_dalloc(&psset, &alloc[i]);
+ expect_ptr_null(ps, "Nonempty pageslab evicted");
+ }
+ ps = test_psset_dalloc(&psset, &alloc[HUGEPAGE_PAGES - 1]);
+ expect_ptr_eq(&pageslab, ps, "Empty pageslab not evicted.");
+
+ err = test_psset_alloc_reuse(&psset, &alloc[0], PAGE);
+ expect_true(err, "psset should be empty.");
+}
+TEST_END
+
+TEST_BEGIN(test_multi_pageslab) {
+ bool err;
+ hpdata_t *ps;
+
+ hpdata_t pageslab[2];
+ hpdata_init(&pageslab[0], PAGESLAB_ADDR, PAGESLAB_AGE);
+ hpdata_init(&pageslab[1],
+ (void *)((uintptr_t)PAGESLAB_ADDR + HUGEPAGE),
+ PAGESLAB_AGE + 1);
+
+ edata_t alloc[2][HUGEPAGE_PAGES];
+
+ psset_t psset;
+ psset_init(&psset);
+
+ /* Insert both slabs. */
+ edata_init_test(&alloc[0][0]);
+ test_psset_alloc_new(&psset, &pageslab[0], &alloc[0][0], PAGE);
+ edata_init_test(&alloc[1][0]);
+ test_psset_alloc_new(&psset, &pageslab[1], &alloc[1][0], PAGE);
+
+ /* Fill them both up; make sure we do so in first-fit order. */
+ for (size_t i = 0; i < 2; i++) {
+ for (size_t j = 1; j < HUGEPAGE_PAGES; j++) {
+ edata_init_test(&alloc[i][j]);
+ err = test_psset_alloc_reuse(&psset, &alloc[i][j], PAGE);
+ expect_false(err,
+ "Nonempty psset failed page allocation.");
+ assert_ptr_eq(&pageslab[i], edata_ps_get(&alloc[i][j]),
+ "Didn't pick pageslabs in first-fit");
+ }
+ }
+
+ /*
+ * Free up a 2-page hole in the earlier slab, and a 1-page one in the
+ * later one. We should still pick the later one.
+ */
+ ps = test_psset_dalloc(&psset, &alloc[0][0]);
+ expect_ptr_null(ps, "Unexpected eviction");
+ ps = test_psset_dalloc(&psset, &alloc[0][1]);
+ expect_ptr_null(ps, "Unexpected eviction");
+ ps = test_psset_dalloc(&psset, &alloc[1][0]);
+ expect_ptr_null(ps, "Unexpected eviction");
+ err = test_psset_alloc_reuse(&psset, &alloc[0][0], PAGE);
+ expect_ptr_eq(&pageslab[1], edata_ps_get(&alloc[0][0]),
+ "Should have picked the fuller pageslab");
+
+ /*
+ * Now both slabs have 1-page holes. Free up a second one in the later
+ * slab.
+ */
+ ps = test_psset_dalloc(&psset, &alloc[1][1]);
+ expect_ptr_null(ps, "Unexpected eviction");
+
+ /*
+ * We should be able to allocate a 2-page object, even though an earlier
+ * size class is nonempty.
+ */
+ err = test_psset_alloc_reuse(&psset, &alloc[1][0], 2 * PAGE);
+ expect_false(err, "Allocation should have succeeded");
+}
+TEST_END
+
+static void
+stats_expect_empty(psset_bin_stats_t *stats) {
+ assert_zu_eq(0, stats->npageslabs,
+ "Supposedly empty bin had positive npageslabs");
+ expect_zu_eq(0, stats->nactive, "Unexpected nonempty bin"
+ "Supposedly empty bin had positive nactive");
+}
+
+static void
+stats_expect(psset_t *psset, size_t nactive) {
+ if (nactive == HUGEPAGE_PAGES) {
+ expect_zu_eq(1, psset->stats.full_slabs[0].npageslabs,
+ "Expected a full slab");
+ expect_zu_eq(HUGEPAGE_PAGES,
+ psset->stats.full_slabs[0].nactive,
+ "Should have exactly filled the bin");
+ } else {
+ stats_expect_empty(&psset->stats.full_slabs[0]);
+ }
+ size_t ninactive = HUGEPAGE_PAGES - nactive;
+ pszind_t nonempty_pind = PSSET_NPSIZES;
+ if (ninactive != 0 && ninactive < HUGEPAGE_PAGES) {
+ nonempty_pind = sz_psz2ind(sz_psz_quantize_floor(
+ ninactive << LG_PAGE));
+ }
+ for (pszind_t i = 0; i < PSSET_NPSIZES; i++) {
+ if (i == nonempty_pind) {
+ assert_zu_eq(1,
+ psset->stats.nonfull_slabs[i][0].npageslabs,
+ "Should have found a slab");
+ expect_zu_eq(nactive,
+ psset->stats.nonfull_slabs[i][0].nactive,
+ "Mismatch in active pages");
+ } else {
+ stats_expect_empty(&psset->stats.nonfull_slabs[i][0]);
+ }
+ }
+ expect_zu_eq(nactive, psset_nactive(psset), "");
+}
+
+TEST_BEGIN(test_stats) {
+ bool err;
+
+ hpdata_t pageslab;
+ hpdata_init(&pageslab, PAGESLAB_ADDR, PAGESLAB_AGE);
+
+ edata_t alloc[HUGEPAGE_PAGES];
+
+ psset_t psset;
+ psset_init(&psset);
+ stats_expect(&psset, 0);
+
+ edata_init_test(&alloc[0]);
+ test_psset_alloc_new(&psset, &pageslab, &alloc[0], PAGE);
+ for (size_t i = 1; i < HUGEPAGE_PAGES; i++) {
+ stats_expect(&psset, i);
+ edata_init_test(&alloc[i]);
+ err = test_psset_alloc_reuse(&psset, &alloc[i], PAGE);
+ expect_false(err, "Nonempty psset failed page allocation.");
+ }
+ stats_expect(&psset, HUGEPAGE_PAGES);
+ hpdata_t *ps;
+ for (ssize_t i = HUGEPAGE_PAGES - 1; i >= 0; i--) {
+ ps = test_psset_dalloc(&psset, &alloc[i]);
+ expect_true((ps == NULL) == (i != 0),
+ "test_psset_dalloc should only evict a slab on the last "
+ "free");
+ stats_expect(&psset, i);
+ }
+
+ test_psset_alloc_new(&psset, &pageslab, &alloc[0], PAGE);
+ stats_expect(&psset, 1);
+ psset_update_begin(&psset, &pageslab);
+ stats_expect(&psset, 0);
+ psset_update_end(&psset, &pageslab);
+ stats_expect(&psset, 1);
+}
+TEST_END
+
+/*
+ * Fills in and inserts two pageslabs, with the first better than the second,
+ * and each fully allocated (into the allocations in allocs and worse_allocs,
+ * each of which should be HUGEPAGE_PAGES long), except for a single free page
+ * at the end.
+ *
+ * (There's nothing magic about these numbers; it's just useful to share the
+ * setup between the oldest fit and the insert/remove test).
+ */
+static void
+init_test_pageslabs(psset_t *psset, hpdata_t *pageslab,
+ hpdata_t *worse_pageslab, edata_t *alloc, edata_t *worse_alloc) {
+ bool err;
+
+ hpdata_init(pageslab, (void *)(10 * HUGEPAGE), PAGESLAB_AGE);
+ /*
+ * This pageslab would be better from an address-first-fit POV, but
+ * worse from an age POV.
+ */
+ hpdata_init(worse_pageslab, (void *)(9 * HUGEPAGE), PAGESLAB_AGE + 1);
+
+ psset_init(psset);
+
+ edata_init_test(&alloc[0]);
+ test_psset_alloc_new(psset, pageslab, &alloc[0], PAGE);
+ for (size_t i = 1; i < HUGEPAGE_PAGES; i++) {
+ edata_init_test(&alloc[i]);
+ err = test_psset_alloc_reuse(psset, &alloc[i], PAGE);
+ expect_false(err, "Nonempty psset failed page allocation.");
+ expect_ptr_eq(pageslab, edata_ps_get(&alloc[i]),
+ "Allocated from the wrong pageslab");
+ }
+
+ edata_init_test(&worse_alloc[0]);
+ test_psset_alloc_new(psset, worse_pageslab, &worse_alloc[0], PAGE);
+ expect_ptr_eq(worse_pageslab, edata_ps_get(&worse_alloc[0]),
+ "Allocated from the wrong pageslab");
+ /*
+ * Make the two pssets otherwise indistinguishable; all full except for
+ * a single page.
+ */
+ for (size_t i = 1; i < HUGEPAGE_PAGES - 1; i++) {
+ edata_init_test(&worse_alloc[i]);
+ err = test_psset_alloc_reuse(psset, &alloc[i], PAGE);
+ expect_false(err, "Nonempty psset failed page allocation.");
+ expect_ptr_eq(worse_pageslab, edata_ps_get(&alloc[i]),
+ "Allocated from the wrong pageslab");
+ }
+
+ /* Deallocate the last page from the older pageslab. */
+ hpdata_t *evicted = test_psset_dalloc(psset,
+ &alloc[HUGEPAGE_PAGES - 1]);
+ expect_ptr_null(evicted, "Unexpected eviction");
+}
+
+TEST_BEGIN(test_oldest_fit) {
+ bool err;
+ edata_t alloc[HUGEPAGE_PAGES];
+ edata_t worse_alloc[HUGEPAGE_PAGES];
+
+ hpdata_t pageslab;
+ hpdata_t worse_pageslab;
+
+ psset_t psset;
+
+ init_test_pageslabs(&psset, &pageslab, &worse_pageslab, alloc,
+ worse_alloc);
+
+ /* The edata should come from the better pageslab. */
+ edata_t test_edata;
+ edata_init_test(&test_edata);
+ err = test_psset_alloc_reuse(&psset, &test_edata, PAGE);
+ expect_false(err, "Nonempty psset failed page allocation");
+ expect_ptr_eq(&pageslab, edata_ps_get(&test_edata),
+ "Allocated from the wrong pageslab");
+}
+TEST_END
+
+TEST_BEGIN(test_insert_remove) {
+ bool err;
+ hpdata_t *ps;
+ edata_t alloc[HUGEPAGE_PAGES];
+ edata_t worse_alloc[HUGEPAGE_PAGES];
+
+ hpdata_t pageslab;
+ hpdata_t worse_pageslab;
+
+ psset_t psset;
+
+ init_test_pageslabs(&psset, &pageslab, &worse_pageslab, alloc,
+ worse_alloc);
+
+ /* Remove better; should still be able to alloc from worse. */
+ psset_update_begin(&psset, &pageslab);
+ err = test_psset_alloc_reuse(&psset, &worse_alloc[HUGEPAGE_PAGES - 1],
+ PAGE);
+ expect_false(err, "Removal should still leave an empty page");
+ expect_ptr_eq(&worse_pageslab,
+ edata_ps_get(&worse_alloc[HUGEPAGE_PAGES - 1]),
+ "Allocated out of wrong ps");
+
+ /*
+ * After deallocating the previous alloc and reinserting better, it
+ * should be preferred for future allocations.
+ */
+ ps = test_psset_dalloc(&psset, &worse_alloc[HUGEPAGE_PAGES - 1]);
+ expect_ptr_null(ps, "Incorrect eviction of nonempty pageslab");
+ psset_update_end(&psset, &pageslab);
+ err = test_psset_alloc_reuse(&psset, &alloc[HUGEPAGE_PAGES - 1], PAGE);
+ expect_false(err, "psset should be nonempty");
+ expect_ptr_eq(&pageslab, edata_ps_get(&alloc[HUGEPAGE_PAGES - 1]),
+ "Removal/reinsertion shouldn't change ordering");
+ /*
+ * After deallocating and removing both, allocations should fail.
+ */
+ ps = test_psset_dalloc(&psset, &alloc[HUGEPAGE_PAGES - 1]);
+ expect_ptr_null(ps, "Incorrect eviction");
+ psset_update_begin(&psset, &pageslab);
+ psset_update_begin(&psset, &worse_pageslab);
+ err = test_psset_alloc_reuse(&psset, &alloc[HUGEPAGE_PAGES - 1], PAGE);
+ expect_true(err, "psset should be empty, but an alloc succeeded");
+}
+TEST_END
+
+TEST_BEGIN(test_purge_prefers_nonhuge) {
+ /*
+ * All else being equal, we should prefer purging non-huge pages over
+ * huge ones for non-empty extents.
+ */
+
+ /* Nothing magic about this constant. */
+ enum {
+ NHP = 23,
+ };
+ hpdata_t *hpdata;
+
+ psset_t psset;
+ psset_init(&psset);
+
+ hpdata_t hpdata_huge[NHP];
+ uintptr_t huge_begin = (uintptr_t)&hpdata_huge[0];
+ uintptr_t huge_end = (uintptr_t)&hpdata_huge[NHP];
+ hpdata_t hpdata_nonhuge[NHP];
+ uintptr_t nonhuge_begin = (uintptr_t)&hpdata_nonhuge[0];
+ uintptr_t nonhuge_end = (uintptr_t)&hpdata_nonhuge[NHP];
+
+ for (size_t i = 0; i < NHP; i++) {
+ hpdata_init(&hpdata_huge[i], (void *)((10 + i) * HUGEPAGE),
+ 123 + i);
+ psset_insert(&psset, &hpdata_huge[i]);
+
+ hpdata_init(&hpdata_nonhuge[i],
+ (void *)((10 + NHP + i) * HUGEPAGE),
+ 456 + i);
+ psset_insert(&psset, &hpdata_nonhuge[i]);
+
+ }
+ for (int i = 0; i < 2 * NHP; i++) {
+ hpdata = psset_pick_alloc(&psset, HUGEPAGE * 3 / 4);
+ psset_update_begin(&psset, hpdata);
+ void *ptr;
+ ptr = hpdata_reserve_alloc(hpdata, HUGEPAGE * 3 / 4);
+ /* Ignore the first alloc, which will stick around. */
+ (void)ptr;
+ /*
+ * The second alloc is to dirty the pages; free it immediately
+ * after allocating.
+ */
+ ptr = hpdata_reserve_alloc(hpdata, HUGEPAGE / 4);
+ hpdata_unreserve(hpdata, ptr, HUGEPAGE / 4);
+
+ if (huge_begin <= (uintptr_t)hpdata
+ && (uintptr_t)hpdata < huge_end) {
+ hpdata_hugify(hpdata);
+ }
+
+ hpdata_purge_allowed_set(hpdata, true);
+ psset_update_end(&psset, hpdata);
+ }
+
+ /*
+ * We've got a bunch of 1/8th dirty hpdatas. It should give us all the
+ * non-huge ones to purge, then all the huge ones, then refuse to purge
+ * further.
+ */
+ for (int i = 0; i < NHP; i++) {
+ hpdata = psset_pick_purge(&psset);
+ assert_true(nonhuge_begin <= (uintptr_t)hpdata
+ && (uintptr_t)hpdata < nonhuge_end, "");
+ psset_update_begin(&psset, hpdata);
+ test_psset_fake_purge(hpdata);
+ hpdata_purge_allowed_set(hpdata, false);
+ psset_update_end(&psset, hpdata);
+ }
+ for (int i = 0; i < NHP; i++) {
+ hpdata = psset_pick_purge(&psset);
+ expect_true(huge_begin <= (uintptr_t)hpdata
+ && (uintptr_t)hpdata < huge_end, "");
+ psset_update_begin(&psset, hpdata);
+ hpdata_dehugify(hpdata);
+ test_psset_fake_purge(hpdata);
+ hpdata_purge_allowed_set(hpdata, false);
+ psset_update_end(&psset, hpdata);
+ }
+}
+TEST_END
+
+TEST_BEGIN(test_purge_prefers_empty) {
+ void *ptr;
+
+ psset_t psset;
+ psset_init(&psset);
+
+ hpdata_t hpdata_empty;
+ hpdata_t hpdata_nonempty;
+ hpdata_init(&hpdata_empty, (void *)(10 * HUGEPAGE), 123);
+ psset_insert(&psset, &hpdata_empty);
+ hpdata_init(&hpdata_nonempty, (void *)(11 * HUGEPAGE), 456);
+ psset_insert(&psset, &hpdata_nonempty);
+
+ psset_update_begin(&psset, &hpdata_empty);
+ ptr = hpdata_reserve_alloc(&hpdata_empty, PAGE);
+ expect_ptr_eq(hpdata_addr_get(&hpdata_empty), ptr, "");
+ hpdata_unreserve(&hpdata_empty, ptr, PAGE);
+ hpdata_purge_allowed_set(&hpdata_empty, true);
+ psset_update_end(&psset, &hpdata_empty);
+
+ psset_update_begin(&psset, &hpdata_nonempty);
+ ptr = hpdata_reserve_alloc(&hpdata_nonempty, 10 * PAGE);
+ expect_ptr_eq(hpdata_addr_get(&hpdata_nonempty), ptr, "");
+ hpdata_unreserve(&hpdata_nonempty, ptr, 9 * PAGE);
+ hpdata_purge_allowed_set(&hpdata_nonempty, true);
+ psset_update_end(&psset, &hpdata_nonempty);
+
+ /*
+ * The nonempty slab has 9 dirty pages, while the empty one has only 1.
+ * We should still pick the empty one for purging.
+ */
+ hpdata_t *to_purge = psset_pick_purge(&psset);
+ expect_ptr_eq(&hpdata_empty, to_purge, "");
+}
+TEST_END
+
+TEST_BEGIN(test_purge_prefers_empty_huge) {
+ void *ptr;
+
+ psset_t psset;
+ psset_init(&psset);
+
+ enum {NHP = 10 };
+
+ hpdata_t hpdata_huge[NHP];
+ hpdata_t hpdata_nonhuge[NHP];
+
+ uintptr_t cur_addr = 100 * HUGEPAGE;
+ uint64_t cur_age = 123;
+ for (int i = 0; i < NHP; i++) {
+ hpdata_init(&hpdata_huge[i], (void *)cur_addr, cur_age);
+ cur_addr += HUGEPAGE;
+ cur_age++;
+ psset_insert(&psset, &hpdata_huge[i]);
+
+ hpdata_init(&hpdata_nonhuge[i], (void *)cur_addr, cur_age);
+ cur_addr += HUGEPAGE;
+ cur_age++;
+ psset_insert(&psset, &hpdata_nonhuge[i]);
+
+ /*
+ * Make the hpdata_huge[i] fully dirty, empty, purgable, and
+ * huge.
+ */
+ psset_update_begin(&psset, &hpdata_huge[i]);
+ ptr = hpdata_reserve_alloc(&hpdata_huge[i], HUGEPAGE);
+ expect_ptr_eq(hpdata_addr_get(&hpdata_huge[i]), ptr, "");
+ hpdata_hugify(&hpdata_huge[i]);
+ hpdata_unreserve(&hpdata_huge[i], ptr, HUGEPAGE);
+ hpdata_purge_allowed_set(&hpdata_huge[i], true);
+ psset_update_end(&psset, &hpdata_huge[i]);
+
+ /*
+ * Make hpdata_nonhuge[i] fully dirty, empty, purgable, and
+ * non-huge.
+ */
+ psset_update_begin(&psset, &hpdata_nonhuge[i]);
+ ptr = hpdata_reserve_alloc(&hpdata_nonhuge[i], HUGEPAGE);
+ expect_ptr_eq(hpdata_addr_get(&hpdata_nonhuge[i]), ptr, "");
+ hpdata_unreserve(&hpdata_nonhuge[i], ptr, HUGEPAGE);
+ hpdata_purge_allowed_set(&hpdata_nonhuge[i], true);
+ psset_update_end(&psset, &hpdata_nonhuge[i]);
+ }
+
+ /*
+ * We have a bunch of empty slabs, half huge, half nonhuge, inserted in
+ * alternating order. We should pop all the huge ones before popping
+ * any of the non-huge ones for purging.
+ */
+ for (int i = 0; i < NHP; i++) {
+ hpdata_t *to_purge = psset_pick_purge(&psset);
+ expect_ptr_eq(&hpdata_huge[i], to_purge, "");
+ psset_update_begin(&psset, to_purge);
+ hpdata_purge_allowed_set(to_purge, false);
+ psset_update_end(&psset, to_purge);
+ }
+ for (int i = 0; i < NHP; i++) {
+ hpdata_t *to_purge = psset_pick_purge(&psset);
+ expect_ptr_eq(&hpdata_nonhuge[i], to_purge, "");
+ psset_update_begin(&psset, to_purge);
+ hpdata_purge_allowed_set(to_purge, false);
+ psset_update_end(&psset, to_purge);
+ }
+}
+TEST_END
+
+int
+main(void) {
+ return test_no_reentrancy(
+ test_empty,
+ test_fill,
+ test_reuse,
+ test_evict,
+ test_multi_pageslab,
+ test_stats,
+ test_oldest_fit,
+ test_insert_remove,
+ test_purge_prefers_nonhuge,
+ test_purge_prefers_empty,
+ test_purge_prefers_empty_huge);
+}
diff --git a/deps/jemalloc/test/unit/ql.c b/deps/jemalloc/test/unit/ql.c
index b76c24c41..f9130582f 100644
--- a/deps/jemalloc/test/unit/ql.c
+++ b/deps/jemalloc/test/unit/ql.c
@@ -18,21 +18,22 @@ test_empty_list(list_head_t *head) {
list_t *t;
unsigned i;
- assert_ptr_null(ql_first(head), "Unexpected element for empty list");
- assert_ptr_null(ql_last(head, link),
+ expect_true(ql_empty(head), "Unexpected element for empty list");
+ expect_ptr_null(ql_first(head), "Unexpected element for empty list");
+ expect_ptr_null(ql_last(head, link),
"Unexpected element for empty list");
i = 0;
ql_foreach(t, head, link) {
i++;
}
- assert_u_eq(i, 0, "Unexpected element for empty list");
+ expect_u_eq(i, 0, "Unexpected element for empty list");
i = 0;
ql_reverse_foreach(t, head, link) {
i++;
}
- assert_u_eq(i, 0, "Unexpected element for empty list");
+ expect_u_eq(i, 0, "Unexpected element for empty list");
}
TEST_BEGIN(test_ql_empty) {
@@ -58,34 +59,35 @@ test_entries_list(list_head_t *head, list_t *entries, unsigned nentries) {
list_t *t;
unsigned i;
- assert_c_eq(ql_first(head)->id, entries[0].id, "Element id mismatch");
- assert_c_eq(ql_last(head, link)->id, entries[nentries-1].id,
+ expect_false(ql_empty(head), "List should not be empty");
+ expect_c_eq(ql_first(head)->id, entries[0].id, "Element id mismatch");
+ expect_c_eq(ql_last(head, link)->id, entries[nentries-1].id,
"Element id mismatch");
i = 0;
ql_foreach(t, head, link) {
- assert_c_eq(t->id, entries[i].id, "Element id mismatch");
+ expect_c_eq(t->id, entries[i].id, "Element id mismatch");
i++;
}
i = 0;
ql_reverse_foreach(t, head, link) {
- assert_c_eq(t->id, entries[nentries-i-1].id,
+ expect_c_eq(t->id, entries[nentries-i-1].id,
"Element id mismatch");
i++;
}
for (i = 0; i < nentries-1; i++) {
t = ql_next(head, &entries[i], link);
- assert_c_eq(t->id, entries[i+1].id, "Element id mismatch");
+ expect_c_eq(t->id, entries[i+1].id, "Element id mismatch");
}
- assert_ptr_null(ql_next(head, &entries[nentries-1], link),
+ expect_ptr_null(ql_next(head, &entries[nentries-1], link),
"Unexpected element");
- assert_ptr_null(ql_prev(head, &entries[0], link), "Unexpected element");
+ expect_ptr_null(ql_prev(head, &entries[0], link), "Unexpected element");
for (i = 1; i < nentries; i++) {
t = ql_prev(head, &entries[i], link);
- assert_c_eq(t->id, entries[i-1].id, "Element id mismatch");
+ expect_c_eq(t->id, entries[i-1].id, "Element id mismatch");
}
}
@@ -192,6 +194,114 @@ TEST_BEGIN(test_ql_insert) {
}
TEST_END
+static void
+test_concat_split_entries(list_t *entries, unsigned nentries_a,
+ unsigned nentries_b) {
+ init_entries(entries, nentries_a + nentries_b);
+
+ list_head_t head_a;
+ ql_new(&head_a);
+ for (unsigned i = 0; i < nentries_a; i++) {
+ ql_tail_insert(&head_a, &entries[i], link);
+ }
+ if (nentries_a == 0) {
+ test_empty_list(&head_a);
+ } else {
+ test_entries_list(&head_a, entries, nentries_a);
+ }
+
+ list_head_t head_b;
+ ql_new(&head_b);
+ for (unsigned i = 0; i < nentries_b; i++) {
+ ql_tail_insert(&head_b, &entries[nentries_a + i], link);
+ }
+ if (nentries_b == 0) {
+ test_empty_list(&head_b);
+ } else {
+ test_entries_list(&head_b, entries + nentries_a, nentries_b);
+ }
+
+ ql_concat(&head_a, &head_b, link);
+ if (nentries_a + nentries_b == 0) {
+ test_empty_list(&head_a);
+ } else {
+ test_entries_list(&head_a, entries, nentries_a + nentries_b);
+ }
+ test_empty_list(&head_b);
+
+ if (nentries_b == 0) {
+ return;
+ }
+
+ list_head_t head_c;
+ ql_split(&head_a, &entries[nentries_a], &head_c, link);
+ if (nentries_a == 0) {
+ test_empty_list(&head_a);
+ } else {
+ test_entries_list(&head_a, entries, nentries_a);
+ }
+ test_entries_list(&head_c, entries + nentries_a, nentries_b);
+}
+
+TEST_BEGIN(test_ql_concat_split) {
+ list_t entries[NENTRIES];
+
+ test_concat_split_entries(entries, 0, 0);
+
+ test_concat_split_entries(entries, 0, 1);
+ test_concat_split_entries(entries, 1, 0);
+
+ test_concat_split_entries(entries, 0, NENTRIES);
+ test_concat_split_entries(entries, 1, NENTRIES - 1);
+ test_concat_split_entries(entries, NENTRIES / 2,
+ NENTRIES - NENTRIES / 2);
+ test_concat_split_entries(entries, NENTRIES - 1, 1);
+ test_concat_split_entries(entries, NENTRIES, 0);
+}
+TEST_END
+
+TEST_BEGIN(test_ql_rotate) {
+ list_head_t head;
+ list_t entries[NENTRIES];
+ unsigned i;
+
+ ql_new(&head);
+ init_entries(entries, sizeof(entries)/sizeof(list_t));
+ for (i = 0; i < NENTRIES; i++) {
+ ql_tail_insert(&head, &entries[i], link);
+ }
+
+ char head_id = ql_first(&head)->id;
+ for (i = 0; i < NENTRIES; i++) {
+ assert_c_eq(ql_first(&head)->id, head_id, "");
+ ql_rotate(&head, link);
+ assert_c_eq(ql_last(&head, link)->id, head_id, "");
+ head_id++;
+ }
+ test_entries_list(&head, entries, NENTRIES);
+}
+TEST_END
+
+TEST_BEGIN(test_ql_move) {
+ list_head_t head_dest, head_src;
+ list_t entries[NENTRIES];
+ unsigned i;
+
+ ql_new(&head_src);
+ ql_move(&head_dest, &head_src);
+ test_empty_list(&head_src);
+ test_empty_list(&head_dest);
+
+ init_entries(entries, sizeof(entries)/sizeof(list_t));
+ for (i = 0; i < NENTRIES; i++) {
+ ql_tail_insert(&head_src, &entries[i], link);
+ }
+ ql_move(&head_dest, &head_src);
+ test_empty_list(&head_src);
+ test_entries_list(&head_dest, entries, NENTRIES);
+}
+TEST_END
+
int
main(void) {
return test(
@@ -200,5 +310,8 @@ main(void) {
test_ql_tail_remove,
test_ql_head_insert,
test_ql_head_remove,
- test_ql_insert);
+ test_ql_insert,
+ test_ql_concat_split,
+ test_ql_rotate,
+ test_ql_move);
}
diff --git a/deps/jemalloc/test/unit/qr.c b/deps/jemalloc/test/unit/qr.c
index 271a10953..16eed0e92 100644
--- a/deps/jemalloc/test/unit/qr.c
+++ b/deps/jemalloc/test/unit/qr.c
@@ -34,7 +34,7 @@ test_independent_entries(ring_t *entries) {
qr_foreach(t, &entries[i], link) {
j++;
}
- assert_u_eq(j, 1,
+ expect_u_eq(j, 1,
"Iteration over single-element ring should visit precisely "
"one element");
}
@@ -43,19 +43,19 @@ test_independent_entries(ring_t *entries) {
qr_reverse_foreach(t, &entries[i], link) {
j++;
}
- assert_u_eq(j, 1,
+ expect_u_eq(j, 1,
"Iteration over single-element ring should visit precisely "
"one element");
}
for (i = 0; i < NENTRIES; i++) {
t = qr_next(&entries[i], link);
- assert_ptr_eq(t, &entries[i],
+ expect_ptr_eq(t, &entries[i],
"Next element in single-element ring should be same as "
"current element");
}
for (i = 0; i < NENTRIES; i++) {
t = qr_prev(&entries[i], link);
- assert_ptr_eq(t, &entries[i],
+ expect_ptr_eq(t, &entries[i],
"Previous element in single-element ring should be same as "
"current element");
}
@@ -77,7 +77,7 @@ test_entries_ring(ring_t *entries) {
for (i = 0; i < NENTRIES; i++) {
j = 0;
qr_foreach(t, &entries[i], link) {
- assert_c_eq(t->id, entries[(i+j) % NENTRIES].id,
+ expect_c_eq(t->id, entries[(i+j) % NENTRIES].id,
"Element id mismatch");
j++;
}
@@ -85,19 +85,19 @@ test_entries_ring(ring_t *entries) {
for (i = 0; i < NENTRIES; i++) {
j = 0;
qr_reverse_foreach(t, &entries[i], link) {
- assert_c_eq(t->id, entries[(NENTRIES+i-j-1) %
+ expect_c_eq(t->id, entries[(NENTRIES+i-j-1) %
NENTRIES].id, "Element id mismatch");
j++;
}
}
for (i = 0; i < NENTRIES; i++) {
t = qr_next(&entries[i], link);
- assert_c_eq(t->id, entries[(i+1) % NENTRIES].id,
+ expect_c_eq(t->id, entries[(i+1) % NENTRIES].id,
"Element id mismatch");
}
for (i = 0; i < NENTRIES; i++) {
t = qr_prev(&entries[i], link);
- assert_c_eq(t->id, entries[(NENTRIES+i-1) % NENTRIES].id,
+ expect_c_eq(t->id, entries[(NENTRIES+i-1) % NENTRIES].id,
"Element id mismatch");
}
}
@@ -127,13 +127,13 @@ TEST_BEGIN(test_qr_remove) {
for (i = 0; i < NENTRIES; i++) {
j = 0;
qr_foreach(t, &entries[i], link) {
- assert_c_eq(t->id, entries[i+j].id,
+ expect_c_eq(t->id, entries[i+j].id,
"Element id mismatch");
j++;
}
j = 0;
qr_reverse_foreach(t, &entries[i], link) {
- assert_c_eq(t->id, entries[NENTRIES - 1 - j].id,
+ expect_c_eq(t->id, entries[NENTRIES - 1 - j].id,
"Element id mismatch");
j++;
}
@@ -155,7 +155,7 @@ TEST_BEGIN(test_qr_before_insert) {
for (i = 0; i < NENTRIES; i++) {
j = 0;
qr_foreach(t, &entries[i], link) {
- assert_c_eq(t->id, entries[(NENTRIES+i-j) %
+ expect_c_eq(t->id, entries[(NENTRIES+i-j) %
NENTRIES].id, "Element id mismatch");
j++;
}
@@ -163,19 +163,19 @@ TEST_BEGIN(test_qr_before_insert) {
for (i = 0; i < NENTRIES; i++) {
j = 0;
qr_reverse_foreach(t, &entries[i], link) {
- assert_c_eq(t->id, entries[(i+j+1) % NENTRIES].id,
+ expect_c_eq(t->id, entries[(i+j+1) % NENTRIES].id,
"Element id mismatch");
j++;
}
}
for (i = 0; i < NENTRIES; i++) {
t = qr_next(&entries[i], link);
- assert_c_eq(t->id, entries[(NENTRIES+i-1) % NENTRIES].id,
+ expect_c_eq(t->id, entries[(NENTRIES+i-1) % NENTRIES].id,
"Element id mismatch");
}
for (i = 0; i < NENTRIES; i++) {
t = qr_prev(&entries[i], link);
- assert_c_eq(t->id, entries[(i+1) % NENTRIES].id,
+ expect_c_eq(t->id, entries[(i+1) % NENTRIES].id,
"Element id mismatch");
}
}
@@ -190,11 +190,11 @@ test_split_entries(ring_t *entries) {
j = 0;
qr_foreach(t, &entries[i], link) {
if (i < SPLIT_INDEX) {
- assert_c_eq(t->id,
+ expect_c_eq(t->id,
entries[(i+j) % SPLIT_INDEX].id,
"Element id mismatch");
} else {
- assert_c_eq(t->id, entries[(i+j-SPLIT_INDEX) %
+ expect_c_eq(t->id, entries[(i+j-SPLIT_INDEX) %
(NENTRIES-SPLIT_INDEX) + SPLIT_INDEX].id,
"Element id mismatch");
}
@@ -212,22 +212,22 @@ TEST_BEGIN(test_qr_meld_split) {
qr_after_insert(&entries[i - 1], &entries[i], link);
}
- qr_split(&entries[0], &entries[SPLIT_INDEX], ring_t, link);
+ qr_split(&entries[0], &entries[SPLIT_INDEX], link);
test_split_entries(entries);
- qr_meld(&entries[0], &entries[SPLIT_INDEX], ring_t, link);
+ qr_meld(&entries[0], &entries[SPLIT_INDEX], link);
test_entries_ring(entries);
- qr_meld(&entries[0], &entries[SPLIT_INDEX], ring_t, link);
+ qr_meld(&entries[0], &entries[SPLIT_INDEX], link);
test_split_entries(entries);
- qr_split(&entries[0], &entries[SPLIT_INDEX], ring_t, link);
+ qr_split(&entries[0], &entries[SPLIT_INDEX], link);
test_entries_ring(entries);
- qr_split(&entries[0], &entries[0], ring_t, link);
+ qr_split(&entries[0], &entries[0], link);
test_entries_ring(entries);
- qr_meld(&entries[0], &entries[0], ring_t, link);
+ qr_meld(&entries[0], &entries[0], link);
test_entries_ring(entries);
}
TEST_END
diff --git a/deps/jemalloc/test/unit/rb.c b/deps/jemalloc/test/unit/rb.c
index 65c049207..827ec510f 100644
--- a/deps/jemalloc/test/unit/rb.c
+++ b/deps/jemalloc/test/unit/rb.c
@@ -1,5 +1,7 @@
#include "test/jemalloc_test.h"
+#include <stdlib.h>
+
#include "jemalloc/internal/rb.h"
#define rbtn_black_height(a_type, a_field, a_rbt, r_height) do { \
@@ -13,27 +15,63 @@
} \
} while (0)
-typedef struct node_s node_t;
+static bool summarize_always_returns_true = false;
+typedef struct node_s node_t;
struct node_s {
#define NODE_MAGIC 0x9823af7e
uint32_t magic;
rb_node(node_t) link;
+ /* Order used by nodes. */
uint64_t key;
+ /*
+ * Our made-up summary property is "specialness", with summarization
+ * taking the max.
+ */
+ uint64_t specialness;
+
+ /*
+ * Used by some of the test randomization to avoid double-removing
+ * nodes.
+ */
+ bool mid_remove;
+
+ /*
+ * To test searching functionality, we want to temporarily weaken the
+ * ordering to allow non-equal nodes that nevertheless compare equal.
+ */
+ bool allow_duplicates;
+
+ /*
+ * In check_consistency, it's handy to know a node's rank in the tree;
+ * this tracks it (but only there; not all tests use this).
+ */
+ int rank;
+ int filtered_rank;
+
+ /*
+ * Replicate the internal structure of the tree, to make sure the
+ * implementation doesn't miss any updates.
+ */
+ const node_t *summary_lchild;
+ const node_t *summary_rchild;
+ uint64_t summary_max_specialness;
};
static int
node_cmp(const node_t *a, const node_t *b) {
int ret;
- assert_u32_eq(a->magic, NODE_MAGIC, "Bad magic");
- assert_u32_eq(b->magic, NODE_MAGIC, "Bad magic");
+ expect_u32_eq(a->magic, NODE_MAGIC, "Bad magic");
+ expect_u32_eq(b->magic, NODE_MAGIC, "Bad magic");
ret = (a->key > b->key) - (a->key < b->key);
- if (ret == 0) {
+ if (ret == 0 && !a->allow_duplicates) {
/*
* Duplicates are not allowed in the tree, so force an
- * arbitrary ordering for non-identical items with equal keys.
+ * arbitrary ordering for non-identical items with equal keys,
+ * unless the user is searching and wants to allow the
+ * duplicate.
*/
ret = (((uintptr_t)a) > ((uintptr_t)b))
- (((uintptr_t)a) < ((uintptr_t)b));
@@ -41,8 +79,77 @@ node_cmp(const node_t *a, const node_t *b) {
return ret;
}
+static uint64_t
+node_subtree_specialness(node_t *n, const node_t *lchild,
+ const node_t *rchild) {
+ uint64_t subtree_specialness = n->specialness;
+ if (lchild != NULL
+ && lchild->summary_max_specialness > subtree_specialness) {
+ subtree_specialness = lchild->summary_max_specialness;
+ }
+ if (rchild != NULL
+ && rchild->summary_max_specialness > subtree_specialness) {
+ subtree_specialness = rchild->summary_max_specialness;
+ }
+ return subtree_specialness;
+}
+
+static bool
+node_summarize(node_t *a, const node_t *lchild, const node_t *rchild) {
+ uint64_t new_summary_max_specialness = node_subtree_specialness(
+ a, lchild, rchild);
+ bool changed = (a->summary_lchild != lchild)
+ || (a->summary_rchild != rchild)
+ || (new_summary_max_specialness != a->summary_max_specialness);
+ a->summary_max_specialness = new_summary_max_specialness;
+ a->summary_lchild = lchild;
+ a->summary_rchild = rchild;
+ return changed || summarize_always_returns_true;
+}
+
typedef rb_tree(node_t) tree_t;
-rb_gen(static, tree_, tree_t, node_t, link, node_cmp);
+rb_summarized_proto(static, tree_, tree_t, node_t);
+rb_summarized_gen(static, tree_, tree_t, node_t, link, node_cmp,
+ node_summarize);
+
+static bool
+specialness_filter_node(void *ctx, node_t *node) {
+ uint64_t specialness = *(uint64_t *)ctx;
+ return node->specialness >= specialness;
+}
+
+static bool
+specialness_filter_subtree(void *ctx, node_t *node) {
+ uint64_t specialness = *(uint64_t *)ctx;
+ return node->summary_max_specialness >= specialness;
+}
+
+static node_t *
+tree_iterate_cb(tree_t *tree, node_t *node, void *data) {
+ unsigned *i = (unsigned *)data;
+ node_t *search_node;
+
+ expect_u32_eq(node->magic, NODE_MAGIC, "Bad magic");
+
+ /* Test rb_search(). */
+ search_node = tree_search(tree, node);
+ expect_ptr_eq(search_node, node,
+ "tree_search() returned unexpected node");
+
+ /* Test rb_nsearch(). */
+ search_node = tree_nsearch(tree, node);
+ expect_ptr_eq(search_node, node,
+ "tree_nsearch() returned unexpected node");
+
+ /* Test rb_psearch(). */
+ search_node = tree_psearch(tree, node);
+ expect_ptr_eq(search_node, node,
+ "tree_psearch() returned unexpected node");
+
+ (*i)++;
+
+ return NULL;
+}
TEST_BEGIN(test_rb_empty) {
tree_t tree;
@@ -50,21 +157,47 @@ TEST_BEGIN(test_rb_empty) {
tree_new(&tree);
- assert_true(tree_empty(&tree), "Tree should be empty");
- assert_ptr_null(tree_first(&tree), "Unexpected node");
- assert_ptr_null(tree_last(&tree), "Unexpected node");
+ expect_true(tree_empty(&tree), "Tree should be empty");
+ expect_ptr_null(tree_first(&tree), "Unexpected node");
+ expect_ptr_null(tree_last(&tree), "Unexpected node");
key.key = 0;
key.magic = NODE_MAGIC;
- assert_ptr_null(tree_search(&tree, &key), "Unexpected node");
+ expect_ptr_null(tree_search(&tree, &key), "Unexpected node");
key.key = 0;
key.magic = NODE_MAGIC;
- assert_ptr_null(tree_nsearch(&tree, &key), "Unexpected node");
+ expect_ptr_null(tree_nsearch(&tree, &key), "Unexpected node");
+
+ key.key = 0;
+ key.magic = NODE_MAGIC;
+ expect_ptr_null(tree_psearch(&tree, &key), "Unexpected node");
+
+ unsigned nodes = 0;
+ tree_iter_filtered(&tree, NULL, &tree_iterate_cb,
+ &nodes, &specialness_filter_node, &specialness_filter_subtree,
+ NULL);
+ expect_u_eq(0, nodes, "");
+
+ nodes = 0;
+ tree_reverse_iter_filtered(&tree, NULL, &tree_iterate_cb,
+ &nodes, &specialness_filter_node, &specialness_filter_subtree,
+ NULL);
+ expect_u_eq(0, nodes, "");
+
+ expect_ptr_null(tree_first_filtered(&tree, &specialness_filter_node,
+ &specialness_filter_subtree, NULL), "");
+ expect_ptr_null(tree_last_filtered(&tree, &specialness_filter_node,
+ &specialness_filter_subtree, NULL), "");
key.key = 0;
key.magic = NODE_MAGIC;
- assert_ptr_null(tree_psearch(&tree, &key), "Unexpected node");
+ expect_ptr_null(tree_search_filtered(&tree, &key,
+ &specialness_filter_node, &specialness_filter_subtree, NULL), "");
+ expect_ptr_null(tree_nsearch_filtered(&tree, &key,
+ &specialness_filter_node, &specialness_filter_subtree, NULL), "");
+ expect_ptr_null(tree_psearch_filtered(&tree, &key,
+ &specialness_filter_node, &specialness_filter_subtree, NULL), "");
}
TEST_END
@@ -81,6 +214,16 @@ tree_recurse(node_t *node, unsigned black_height, unsigned black_depth) {
left_node = rbtn_left_get(node_t, link, node);
right_node = rbtn_right_get(node_t, link, node);
+ expect_ptr_eq(left_node, node->summary_lchild,
+ "summary missed a tree update");
+ expect_ptr_eq(right_node, node->summary_rchild,
+ "summary missed a tree update");
+
+ uint64_t expected_subtree_specialness = node_subtree_specialness(node,
+ left_node, right_node);
+ expect_u64_eq(expected_subtree_specialness,
+ node->summary_max_specialness, "Incorrect summary");
+
if (!rbtn_red_get(node_t, link, node)) {
black_depth++;
}
@@ -88,17 +231,17 @@ tree_recurse(node_t *node, unsigned black_height, unsigned black_depth) {
/* Red nodes must be interleaved with black nodes. */
if (rbtn_red_get(node_t, link, node)) {
if (left_node != NULL) {
- assert_false(rbtn_red_get(node_t, link, left_node),
+ expect_false(rbtn_red_get(node_t, link, left_node),
"Node should be black");
}
if (right_node != NULL) {
- assert_false(rbtn_red_get(node_t, link, right_node),
+ expect_false(rbtn_red_get(node_t, link, right_node),
"Node should be black");
}
}
/* Self. */
- assert_u32_eq(node->magic, NODE_MAGIC, "Bad magic");
+ expect_u32_eq(node->magic, NODE_MAGIC, "Bad magic");
/* Left subtree. */
if (left_node != NULL) {
@@ -117,33 +260,6 @@ tree_recurse(node_t *node, unsigned black_height, unsigned black_depth) {
return ret;
}
-static node_t *
-tree_iterate_cb(tree_t *tree, node_t *node, void *data) {
- unsigned *i = (unsigned *)data;
- node_t *search_node;
-
- assert_u32_eq(node->magic, NODE_MAGIC, "Bad magic");
-
- /* Test rb_search(). */
- search_node = tree_search(tree, node);
- assert_ptr_eq(search_node, node,
- "tree_search() returned unexpected node");
-
- /* Test rb_nsearch(). */
- search_node = tree_nsearch(tree, node);
- assert_ptr_eq(search_node, node,
- "tree_nsearch() returned unexpected node");
-
- /* Test rb_psearch(). */
- search_node = tree_psearch(tree, node);
- assert_ptr_eq(search_node, node,
- "tree_psearch() returned unexpected node");
-
- (*i)++;
-
- return NULL;
-}
-
static unsigned
tree_iterate(tree_t *tree) {
unsigned i;
@@ -174,14 +290,14 @@ node_remove(tree_t *tree, node_t *node, unsigned nnodes) {
/* Test rb_nsearch(). */
search_node = tree_nsearch(tree, node);
if (search_node != NULL) {
- assert_u64_ge(search_node->key, node->key,
+ expect_u64_ge(search_node->key, node->key,
"Key ordering error");
}
/* Test rb_psearch(). */
search_node = tree_psearch(tree, node);
if (search_node != NULL) {
- assert_u64_le(search_node->key, node->key,
+ expect_u64_le(search_node->key, node->key,
"Key ordering error");
}
@@ -189,10 +305,10 @@ node_remove(tree_t *tree, node_t *node, unsigned nnodes) {
rbtn_black_height(node_t, link, tree, black_height);
imbalances = tree_recurse(tree->rbt_root, black_height, 0);
- assert_u_eq(imbalances, 0, "Tree is unbalanced");
- assert_u_eq(tree_iterate(tree), nnodes-1,
+ expect_u_eq(imbalances, 0, "Tree is unbalanced");
+ expect_u_eq(tree_iterate(tree), nnodes-1,
"Unexpected node iteration count");
- assert_u_eq(tree_iterate_reverse(tree), nnodes-1,
+ expect_u_eq(tree_iterate_reverse(tree), nnodes-1,
"Unexpected node iteration count");
}
@@ -220,14 +336,16 @@ static void
destroy_cb(node_t *node, void *data) {
unsigned *nnodes = (unsigned *)data;
- assert_u_gt(*nnodes, 0, "Destruction removed too many nodes");
+ expect_u_gt(*nnodes, 0, "Destruction removed too many nodes");
(*nnodes)--;
}
TEST_BEGIN(test_rb_random) {
-#define NNODES 25
-#define NBAGS 250
-#define SEED 42
+ enum {
+ NNODES = 25,
+ NBAGS = 500,
+ SEED = 42
+ };
sfmt_t *sfmt;
uint64_t bag[NNODES];
tree_t tree;
@@ -255,12 +373,26 @@ TEST_BEGIN(test_rb_random) {
}
}
+ /*
+ * We alternate test behavior with a period of 2 here, and a
+ * period of 5 down below, so there's no cycle in which certain
+ * combinations get omitted.
+ */
+ summarize_always_returns_true = (i % 2 == 0);
+
for (j = 1; j <= NNODES; j++) {
/* Initialize tree and nodes. */
tree_new(&tree);
for (k = 0; k < j; k++) {
nodes[k].magic = NODE_MAGIC;
nodes[k].key = bag[k];
+ nodes[k].specialness = gen_rand64_range(sfmt,
+ NNODES);
+ nodes[k].mid_remove = false;
+ nodes[k].allow_duplicates = false;
+ nodes[k].summary_lchild = NULL;
+ nodes[k].summary_rchild = NULL;
+ nodes[k].summary_max_specialness = 0;
}
/* Insert nodes. */
@@ -271,19 +403,19 @@ TEST_BEGIN(test_rb_random) {
black_height);
imbalances = tree_recurse(tree.rbt_root,
black_height, 0);
- assert_u_eq(imbalances, 0,
+ expect_u_eq(imbalances, 0,
"Tree is unbalanced");
- assert_u_eq(tree_iterate(&tree), k+1,
+ expect_u_eq(tree_iterate(&tree), k+1,
"Unexpected node iteration count");
- assert_u_eq(tree_iterate_reverse(&tree), k+1,
+ expect_u_eq(tree_iterate_reverse(&tree), k+1,
"Unexpected node iteration count");
- assert_false(tree_empty(&tree),
+ expect_false(tree_empty(&tree),
"Tree should not be empty");
- assert_ptr_not_null(tree_first(&tree),
+ expect_ptr_not_null(tree_first(&tree),
"Tree should not be empty");
- assert_ptr_not_null(tree_last(&tree),
+ expect_ptr_not_null(tree_last(&tree),
"Tree should not be empty");
tree_next(&tree, &nodes[k]);
@@ -312,7 +444,7 @@ TEST_BEGIN(test_rb_random) {
remove_iterate_cb, (void *)&nnodes);
nnodes--;
} while (start != NULL);
- assert_u_eq(nnodes, 0,
+ expect_u_eq(nnodes, 0,
"Removal terminated early");
break;
} case 3: {
@@ -326,13 +458,13 @@ TEST_BEGIN(test_rb_random) {
(void *)&nnodes);
nnodes--;
} while (start != NULL);
- assert_u_eq(nnodes, 0,
+ expect_u_eq(nnodes, 0,
"Removal terminated early");
break;
} case 4: {
unsigned nnodes = j;
tree_destroy(&tree, destroy_cb, &nnodes);
- assert_u_eq(nnodes, 0,
+ expect_u_eq(nnodes, 0,
"Destruction terminated early");
break;
} default:
@@ -341,15 +473,547 @@ TEST_BEGIN(test_rb_random) {
}
}
fini_gen_rand(sfmt);
-#undef NNODES
-#undef NBAGS
-#undef SEED
+}
+TEST_END
+
+static void
+expect_simple_consistency(tree_t *tree, uint64_t specialness,
+ bool expected_empty, node_t *expected_first, node_t *expected_last) {
+ bool empty;
+ node_t *first;
+ node_t *last;
+
+ empty = tree_empty_filtered(tree, &specialness_filter_node,
+ &specialness_filter_subtree, &specialness);
+ expect_b_eq(expected_empty, empty, "");
+
+ first = tree_first_filtered(tree,
+ &specialness_filter_node, &specialness_filter_subtree,
+ (void *)&specialness);
+ expect_ptr_eq(expected_first, first, "");
+
+ last = tree_last_filtered(tree,
+ &specialness_filter_node, &specialness_filter_subtree,
+ (void *)&specialness);
+ expect_ptr_eq(expected_last, last, "");
+}
+
+TEST_BEGIN(test_rb_filter_simple) {
+ enum {FILTER_NODES = 10};
+ node_t nodes[FILTER_NODES];
+ for (unsigned i = 0; i < FILTER_NODES; i++) {
+ nodes[i].magic = NODE_MAGIC;
+ nodes[i].key = i;
+ if (i == 0) {
+ nodes[i].specialness = 0;
+ } else {
+ nodes[i].specialness = ffs_u(i);
+ }
+ nodes[i].mid_remove = false;
+ nodes[i].allow_duplicates = false;
+ nodes[i].summary_lchild = NULL;
+ nodes[i].summary_rchild = NULL;
+ nodes[i].summary_max_specialness = 0;
+ }
+
+ summarize_always_returns_true = false;
+
+ tree_t tree;
+ tree_new(&tree);
+
+ /* Should be empty */
+ expect_simple_consistency(&tree, /* specialness */ 0, /* empty */ true,
+ /* first */ NULL, /* last */ NULL);
+
+ /* Fill in just the odd nodes. */
+ for (int i = 1; i < FILTER_NODES; i += 2) {
+ tree_insert(&tree, &nodes[i]);
+ }
+
+ /* A search for an odd node should succeed. */
+ expect_simple_consistency(&tree, /* specialness */ 0, /* empty */ false,
+ /* first */ &nodes[1], /* last */ &nodes[9]);
+
+ /* But a search for an even one should fail. */
+ expect_simple_consistency(&tree, /* specialness */ 1, /* empty */ true,
+ /* first */ NULL, /* last */ NULL);
+
+ /* Now we add an even. */
+ tree_insert(&tree, &nodes[4]);
+ expect_simple_consistency(&tree, /* specialness */ 1, /* empty */ false,
+ /* first */ &nodes[4], /* last */ &nodes[4]);
+
+ /* A smaller even, and a larger even. */
+ tree_insert(&tree, &nodes[2]);
+ tree_insert(&tree, &nodes[8]);
+
+ /*
+ * A first-search (resp. last-search) for an even should switch to the
+ * lower (higher) one, now that it's been added.
+ */
+ expect_simple_consistency(&tree, /* specialness */ 1, /* empty */ false,
+ /* first */ &nodes[2], /* last */ &nodes[8]);
+
+ /*
+ * If we remove 2, a first-search we should go back to 4, while a
+ * last-search should remain unchanged.
+ */
+ tree_remove(&tree, &nodes[2]);
+ expect_simple_consistency(&tree, /* specialness */ 1, /* empty */ false,
+ /* first */ &nodes[4], /* last */ &nodes[8]);
+
+ /* Reinsert 2, then find it again. */
+ tree_insert(&tree, &nodes[2]);
+ expect_simple_consistency(&tree, /* specialness */ 1, /* empty */ false,
+ /* first */ &nodes[2], /* last */ &nodes[8]);
+
+ /* Searching for a multiple of 4 should not have changed. */
+ expect_simple_consistency(&tree, /* specialness */ 2, /* empty */ false,
+ /* first */ &nodes[4], /* last */ &nodes[8]);
+
+ /* And a multiple of 8 */
+ expect_simple_consistency(&tree, /* specialness */ 3, /* empty */ false,
+ /* first */ &nodes[8], /* last */ &nodes[8]);
+
+ /* But not a multiple of 16 */
+ expect_simple_consistency(&tree, /* specialness */ 4, /* empty */ true,
+ /* first */ NULL, /* last */ NULL);
+}
+TEST_END
+
+typedef struct iter_ctx_s iter_ctx_t;
+struct iter_ctx_s {
+ int ncalls;
+ node_t *last_node;
+
+ int ncalls_max;
+ bool forward;
+};
+
+static node_t *
+tree_iterate_filtered_cb(tree_t *tree, node_t *node, void *arg) {
+ iter_ctx_t *ctx = (iter_ctx_t *)arg;
+ ctx->ncalls++;
+ expect_u64_ge(node->specialness, 1,
+ "Should only invoke cb on nodes that pass the filter");
+ if (ctx->last_node != NULL) {
+ if (ctx->forward) {
+ expect_d_lt(node_cmp(ctx->last_node, node), 0,
+ "Incorrect iteration order");
+ } else {
+ expect_d_gt(node_cmp(ctx->last_node, node), 0,
+ "Incorrect iteration order");
+ }
+ }
+ ctx->last_node = node;
+ if (ctx->ncalls == ctx->ncalls_max) {
+ return node;
+ }
+ return NULL;
+}
+
+static int
+qsort_node_cmp(const void *ap, const void *bp) {
+ node_t *a = *(node_t **)ap;
+ node_t *b = *(node_t **)bp;
+ return node_cmp(a, b);
+}
+
+#define UPDATE_TEST_MAX 100
+static void
+check_consistency(tree_t *tree, node_t nodes[UPDATE_TEST_MAX], int nnodes) {
+ uint64_t specialness = 1;
+
+ bool empty;
+ bool real_empty = true;
+ node_t *first;
+ node_t *real_first = NULL;
+ node_t *last;
+ node_t *real_last = NULL;
+ for (int i = 0; i < nnodes; i++) {
+ if (nodes[i].specialness >= specialness) {
+ real_empty = false;
+ if (real_first == NULL
+ || node_cmp(&nodes[i], real_first) < 0) {
+ real_first = &nodes[i];
+ }
+ if (real_last == NULL
+ || node_cmp(&nodes[i], real_last) > 0) {
+ real_last = &nodes[i];
+ }
+ }
+ }
+
+ empty = tree_empty_filtered(tree, &specialness_filter_node,
+ &specialness_filter_subtree, &specialness);
+ expect_b_eq(real_empty, empty, "");
+
+ first = tree_first_filtered(tree, &specialness_filter_node,
+ &specialness_filter_subtree, &specialness);
+ expect_ptr_eq(real_first, first, "");
+
+ last = tree_last_filtered(tree, &specialness_filter_node,
+ &specialness_filter_subtree, &specialness);
+ expect_ptr_eq(real_last, last, "");
+
+ for (int i = 0; i < nnodes; i++) {
+ node_t *next_filtered;
+ node_t *real_next_filtered = NULL;
+ node_t *prev_filtered;
+ node_t *real_prev_filtered = NULL;
+ for (int j = 0; j < nnodes; j++) {
+ if (nodes[j].specialness < specialness) {
+ continue;
+ }
+ if (node_cmp(&nodes[j], &nodes[i]) < 0
+ && (real_prev_filtered == NULL
+ || node_cmp(&nodes[j], real_prev_filtered) > 0)) {
+ real_prev_filtered = &nodes[j];
+ }
+ if (node_cmp(&nodes[j], &nodes[i]) > 0
+ && (real_next_filtered == NULL
+ || node_cmp(&nodes[j], real_next_filtered) < 0)) {
+ real_next_filtered = &nodes[j];
+ }
+ }
+ next_filtered = tree_next_filtered(tree, &nodes[i],
+ &specialness_filter_node, &specialness_filter_subtree,
+ &specialness);
+ expect_ptr_eq(real_next_filtered, next_filtered, "");
+
+ prev_filtered = tree_prev_filtered(tree, &nodes[i],
+ &specialness_filter_node, &specialness_filter_subtree,
+ &specialness);
+ expect_ptr_eq(real_prev_filtered, prev_filtered, "");
+
+ node_t *search_filtered;
+ node_t *real_search_filtered;
+ node_t *nsearch_filtered;
+ node_t *real_nsearch_filtered;
+ node_t *psearch_filtered;
+ node_t *real_psearch_filtered;
+
+ /*
+ * search, nsearch, psearch from a node before nodes[i] in the
+ * ordering.
+ */
+ node_t before;
+ before.magic = NODE_MAGIC;
+ before.key = nodes[i].key - 1;
+ before.allow_duplicates = false;
+ real_search_filtered = NULL;
+ search_filtered = tree_search_filtered(tree, &before,
+ &specialness_filter_node, &specialness_filter_subtree,
+ &specialness);
+ expect_ptr_eq(real_search_filtered, search_filtered, "");
+
+ real_nsearch_filtered = (nodes[i].specialness >= specialness ?
+ &nodes[i] : real_next_filtered);
+ nsearch_filtered = tree_nsearch_filtered(tree, &before,
+ &specialness_filter_node, &specialness_filter_subtree,
+ &specialness);
+ expect_ptr_eq(real_nsearch_filtered, nsearch_filtered, "");
+
+ real_psearch_filtered = real_prev_filtered;
+ psearch_filtered = tree_psearch_filtered(tree, &before,
+ &specialness_filter_node, &specialness_filter_subtree,
+ &specialness);
+ expect_ptr_eq(real_psearch_filtered, psearch_filtered, "");
+
+ /* search, nsearch, psearch from nodes[i] */
+ real_search_filtered = (nodes[i].specialness >= specialness ?
+ &nodes[i] : NULL);
+ search_filtered = tree_search_filtered(tree, &nodes[i],
+ &specialness_filter_node, &specialness_filter_subtree,
+ &specialness);
+ expect_ptr_eq(real_search_filtered, search_filtered, "");
+
+ real_nsearch_filtered = (nodes[i].specialness >= specialness ?
+ &nodes[i] : real_next_filtered);
+ nsearch_filtered = tree_nsearch_filtered(tree, &nodes[i],
+ &specialness_filter_node, &specialness_filter_subtree,
+ &specialness);
+ expect_ptr_eq(real_nsearch_filtered, nsearch_filtered, "");
+
+ real_psearch_filtered = (nodes[i].specialness >= specialness ?
+ &nodes[i] : real_prev_filtered);
+ psearch_filtered = tree_psearch_filtered(tree, &nodes[i],
+ &specialness_filter_node, &specialness_filter_subtree,
+ &specialness);
+ expect_ptr_eq(real_psearch_filtered, psearch_filtered, "");
+
+ /*
+ * search, nsearch, psearch from a node equivalent to but
+ * distinct from nodes[i].
+ */
+ node_t equiv;
+ equiv.magic = NODE_MAGIC;
+ equiv.key = nodes[i].key;
+ equiv.allow_duplicates = true;
+ real_search_filtered = (nodes[i].specialness >= specialness ?
+ &nodes[i] : NULL);
+ search_filtered = tree_search_filtered(tree, &equiv,
+ &specialness_filter_node, &specialness_filter_subtree,
+ &specialness);
+ expect_ptr_eq(real_search_filtered, search_filtered, "");
+
+ real_nsearch_filtered = (nodes[i].specialness >= specialness ?
+ &nodes[i] : real_next_filtered);
+ nsearch_filtered = tree_nsearch_filtered(tree, &equiv,
+ &specialness_filter_node, &specialness_filter_subtree,
+ &specialness);
+ expect_ptr_eq(real_nsearch_filtered, nsearch_filtered, "");
+
+ real_psearch_filtered = (nodes[i].specialness >= specialness ?
+ &nodes[i] : real_prev_filtered);
+ psearch_filtered = tree_psearch_filtered(tree, &equiv,
+ &specialness_filter_node, &specialness_filter_subtree,
+ &specialness);
+ expect_ptr_eq(real_psearch_filtered, psearch_filtered, "");
+
+ /*
+ * search, nsearch, psearch from a node after nodes[i] in the
+ * ordering.
+ */
+ node_t after;
+ after.magic = NODE_MAGIC;
+ after.key = nodes[i].key + 1;
+ after.allow_duplicates = false;
+ real_search_filtered = NULL;
+ search_filtered = tree_search_filtered(tree, &after,
+ &specialness_filter_node, &specialness_filter_subtree,
+ &specialness);
+ expect_ptr_eq(real_search_filtered, search_filtered, "");
+
+ real_nsearch_filtered = real_next_filtered;
+ nsearch_filtered = tree_nsearch_filtered(tree, &after,
+ &specialness_filter_node, &specialness_filter_subtree,
+ &specialness);
+ expect_ptr_eq(real_nsearch_filtered, nsearch_filtered, "");
+
+ real_psearch_filtered = (nodes[i].specialness >= specialness ?
+ &nodes[i] : real_prev_filtered);
+ psearch_filtered = tree_psearch_filtered(tree, &after,
+ &specialness_filter_node, &specialness_filter_subtree,
+ &specialness);
+ expect_ptr_eq(real_psearch_filtered, psearch_filtered, "");
+ }
+
+ /* Filtered iteration test setup. */
+ int nspecial = 0;
+ node_t *sorted_nodes[UPDATE_TEST_MAX];
+ node_t *sorted_filtered_nodes[UPDATE_TEST_MAX];
+ for (int i = 0; i < nnodes; i++) {
+ sorted_nodes[i] = &nodes[i];
+ }
+ qsort(sorted_nodes, nnodes, sizeof(node_t *), &qsort_node_cmp);
+ for (int i = 0; i < nnodes; i++) {
+ sorted_nodes[i]->rank = i;
+ sorted_nodes[i]->filtered_rank = nspecial;
+ if (sorted_nodes[i]->specialness >= 1) {
+ sorted_filtered_nodes[nspecial] = sorted_nodes[i];
+ nspecial++;
+ }
+ }
+
+ node_t *iter_result;
+
+ iter_ctx_t ctx;
+ ctx.ncalls = 0;
+ ctx.last_node = NULL;
+ ctx.ncalls_max = INT_MAX;
+ ctx.forward = true;
+
+ /* Filtered forward iteration from the beginning. */
+ iter_result = tree_iter_filtered(tree, NULL, &tree_iterate_filtered_cb,
+ &ctx, &specialness_filter_node, &specialness_filter_subtree,
+ &specialness);
+ expect_ptr_null(iter_result, "");
+ expect_d_eq(nspecial, ctx.ncalls, "");
+ /* Filtered forward iteration from a starting point. */
+ for (int i = 0; i < nnodes; i++) {
+ ctx.ncalls = 0;
+ ctx.last_node = NULL;
+ iter_result = tree_iter_filtered(tree, &nodes[i],
+ &tree_iterate_filtered_cb, &ctx, &specialness_filter_node,
+ &specialness_filter_subtree, &specialness);
+ expect_ptr_null(iter_result, "");
+ expect_d_eq(nspecial - nodes[i].filtered_rank, ctx.ncalls, "");
+ }
+ /* Filtered forward iteration from the beginning, with stopping */
+ for (int i = 0; i < nspecial; i++) {
+ ctx.ncalls = 0;
+ ctx.last_node = NULL;
+ ctx.ncalls_max = i + 1;
+ iter_result = tree_iter_filtered(tree, NULL,
+ &tree_iterate_filtered_cb, &ctx, &specialness_filter_node,
+ &specialness_filter_subtree, &specialness);
+ expect_ptr_eq(sorted_filtered_nodes[i], iter_result, "");
+ expect_d_eq(ctx.ncalls, i + 1, "");
+ }
+ /* Filtered forward iteration from a starting point, with stopping. */
+ for (int i = 0; i < nnodes; i++) {
+ for (int j = 0; j < nspecial - nodes[i].filtered_rank; j++) {
+ ctx.ncalls = 0;
+ ctx.last_node = NULL;
+ ctx.ncalls_max = j + 1;
+ iter_result = tree_iter_filtered(tree, &nodes[i],
+ &tree_iterate_filtered_cb, &ctx,
+ &specialness_filter_node,
+ &specialness_filter_subtree, &specialness);
+ expect_d_eq(j + 1, ctx.ncalls, "");
+ expect_ptr_eq(sorted_filtered_nodes[
+ nodes[i].filtered_rank + j], iter_result, "");
+ }
+ }
+
+ /* Backwards iteration. */
+ ctx.ncalls = 0;
+ ctx.last_node = NULL;
+ ctx.ncalls_max = INT_MAX;
+ ctx.forward = false;
+
+ /* Filtered backward iteration from the end. */
+ iter_result = tree_reverse_iter_filtered(tree, NULL,
+ &tree_iterate_filtered_cb, &ctx, &specialness_filter_node,
+ &specialness_filter_subtree, &specialness);
+ expect_ptr_null(iter_result, "");
+ expect_d_eq(nspecial, ctx.ncalls, "");
+ /* Filtered backward iteration from a starting point. */
+ for (int i = 0; i < nnodes; i++) {
+ ctx.ncalls = 0;
+ ctx.last_node = NULL;
+ iter_result = tree_reverse_iter_filtered(tree, &nodes[i],
+ &tree_iterate_filtered_cb, &ctx, &specialness_filter_node,
+ &specialness_filter_subtree, &specialness);
+ expect_ptr_null(iter_result, "");
+ int surplus_rank = (nodes[i].specialness >= 1 ? 1 : 0);
+ expect_d_eq(nodes[i].filtered_rank + surplus_rank, ctx.ncalls,
+ "");
+ }
+ /* Filtered backward iteration from the end, with stopping */
+ for (int i = 0; i < nspecial; i++) {
+ ctx.ncalls = 0;
+ ctx.last_node = NULL;
+ ctx.ncalls_max = i + 1;
+ iter_result = tree_reverse_iter_filtered(tree, NULL,
+ &tree_iterate_filtered_cb, &ctx, &specialness_filter_node,
+ &specialness_filter_subtree, &specialness);
+ expect_ptr_eq(sorted_filtered_nodes[nspecial - i - 1],
+ iter_result, "");
+ expect_d_eq(ctx.ncalls, i + 1, "");
+ }
+ /* Filtered backward iteration from a starting point, with stopping. */
+ for (int i = 0; i < nnodes; i++) {
+ int surplus_rank = (nodes[i].specialness >= 1 ? 1 : 0);
+ for (int j = 0; j < nodes[i].filtered_rank + surplus_rank;
+ j++) {
+ ctx.ncalls = 0;
+ ctx.last_node = NULL;
+ ctx.ncalls_max = j + 1;
+ iter_result = tree_reverse_iter_filtered(tree,
+ &nodes[i], &tree_iterate_filtered_cb, &ctx,
+ &specialness_filter_node,
+ &specialness_filter_subtree, &specialness);
+ expect_d_eq(j + 1, ctx.ncalls, "");
+ expect_ptr_eq(sorted_filtered_nodes[
+ nodes[i].filtered_rank - j - 1 + surplus_rank],
+ iter_result, "");
+ }
+ }
+}
+
+static void
+do_update_search_test(int nnodes, int ntrees, int nremovals,
+ int nupdates) {
+ node_t nodes[UPDATE_TEST_MAX];
+ assert(nnodes <= UPDATE_TEST_MAX);
+
+ sfmt_t *sfmt = init_gen_rand(12345);
+ for (int i = 0; i < ntrees; i++) {
+ tree_t tree;
+ tree_new(&tree);
+ for (int j = 0; j < nnodes; j++) {
+ nodes[j].magic = NODE_MAGIC;
+ /*
+ * In consistency checking, we increment or decrement a
+ * key and assume that the result is not a key in the
+ * tree. This isn't a *real* concern with 64-bit keys
+ * and a good PRNG, but why not be correct anyways?
+ */
+ nodes[j].key = 2 * gen_rand64(sfmt);
+ nodes[j].specialness = 0;
+ nodes[j].mid_remove = false;
+ nodes[j].allow_duplicates = false;
+ nodes[j].summary_lchild = NULL;
+ nodes[j].summary_rchild = NULL;
+ nodes[j].summary_max_specialness = 0;
+ tree_insert(&tree, &nodes[j]);
+ }
+ for (int j = 0; j < nremovals; j++) {
+ int victim = (int)gen_rand64_range(sfmt, nnodes);
+ if (!nodes[victim].mid_remove) {
+ tree_remove(&tree, &nodes[victim]);
+ nodes[victim].mid_remove = true;
+ }
+ }
+ for (int j = 0; j < nnodes; j++) {
+ if (nodes[j].mid_remove) {
+ nodes[j].mid_remove = false;
+ nodes[j].key = 2 * gen_rand64(sfmt);
+ tree_insert(&tree, &nodes[j]);
+ }
+ }
+ for (int j = 0; j < nupdates; j++) {
+ uint32_t ind = gen_rand32_range(sfmt, nnodes);
+ nodes[ind].specialness = 1 - nodes[ind].specialness;
+ tree_update_summaries(&tree, &nodes[ind]);
+ check_consistency(&tree, nodes, nnodes);
+ }
+ }
+}
+
+TEST_BEGIN(test_rb_update_search) {
+ summarize_always_returns_true = false;
+ do_update_search_test(2, 100, 3, 50);
+ do_update_search_test(5, 100, 3, 50);
+ do_update_search_test(12, 100, 5, 1000);
+ do_update_search_test(100, 1, 50, 500);
+}
+TEST_END
+
+typedef rb_tree(node_t) unsummarized_tree_t;
+rb_gen(static UNUSED, unsummarized_tree_, unsummarized_tree_t, node_t, link,
+ node_cmp);
+
+static node_t *
+unsummarized_tree_iterate_cb(unsummarized_tree_t *tree, node_t *node,
+ void *data) {
+ unsigned *i = (unsigned *)data;
+ (*i)++;
+ return NULL;
+}
+/*
+ * The unsummarized and summarized funtionality is implemented via the same
+ * functions; we don't really need to do much more than test that we can exclude
+ * the filtered functionality without anything breaking.
+ */
+TEST_BEGIN(test_rb_unsummarized) {
+ unsummarized_tree_t tree;
+ unsummarized_tree_new(&tree);
+ unsigned nnodes = 0;
+ unsummarized_tree_iter(&tree, NULL, &unsummarized_tree_iterate_cb,
+ &nnodes);
+ expect_u_eq(0, nnodes, "");
}
TEST_END
int
main(void) {
- return test(
+ return test_no_reentrancy(
test_rb_empty,
- test_rb_random);
+ test_rb_random,
+ test_rb_filter_simple,
+ test_rb_update_search,
+ test_rb_unsummarized);
}
diff --git a/deps/jemalloc/test/unit/retained.c b/deps/jemalloc/test/unit/retained.c
index 7993fd3d9..aa9f6847b 100644
--- a/deps/jemalloc/test/unit/retained.c
+++ b/deps/jemalloc/test/unit/retained.c
@@ -1,5 +1,6 @@
#include "test/jemalloc_test.h"
+#include "jemalloc/internal/san.h"
#include "jemalloc/internal/spin.h"
static unsigned arena_ind;
@@ -12,58 +13,58 @@ static atomic_u_t nfinished;
static unsigned
do_arena_create(extent_hooks_t *h) {
- unsigned arena_ind;
- size_t sz = sizeof(unsigned);
- assert_d_eq(mallctl("arenas.create", (void *)&arena_ind, &sz,
+ unsigned new_arena_ind;
+ size_t ind_sz = sizeof(unsigned);
+ expect_d_eq(mallctl("arenas.create", (void *)&new_arena_ind, &ind_sz,
(void *)(h != NULL ? &h : NULL), (h != NULL ? sizeof(h) : 0)), 0,
"Unexpected mallctl() failure");
- return arena_ind;
+ return new_arena_ind;
}
static void
-do_arena_destroy(unsigned arena_ind) {
+do_arena_destroy(unsigned ind) {
size_t mib[3];
size_t miblen;
miblen = sizeof(mib)/sizeof(size_t);
- assert_d_eq(mallctlnametomib("arena.0.destroy", mib, &miblen), 0,
+ expect_d_eq(mallctlnametomib("arena.0.destroy", mib, &miblen), 0,
"Unexpected mallctlnametomib() failure");
- mib[1] = (size_t)arena_ind;
- assert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0,
+ mib[1] = (size_t)ind;
+ expect_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL, 0), 0,
"Unexpected mallctlbymib() failure");
}
static void
do_refresh(void) {
- uint64_t epoch = 1;
- assert_d_eq(mallctl("epoch", NULL, NULL, (void *)&epoch,
- sizeof(epoch)), 0, "Unexpected mallctl() failure");
+ uint64_t refresh_epoch = 1;
+ expect_d_eq(mallctl("epoch", NULL, NULL, (void *)&refresh_epoch,
+ sizeof(refresh_epoch)), 0, "Unexpected mallctl() failure");
}
static size_t
-do_get_size_impl(const char *cmd, unsigned arena_ind) {
+do_get_size_impl(const char *cmd, unsigned ind) {
size_t mib[4];
size_t miblen = sizeof(mib) / sizeof(size_t);
size_t z = sizeof(size_t);
- assert_d_eq(mallctlnametomib(cmd, mib, &miblen),
+ expect_d_eq(mallctlnametomib(cmd, mib, &miblen),
0, "Unexpected mallctlnametomib(\"%s\", ...) failure", cmd);
- mib[2] = arena_ind;
+ mib[2] = ind;
size_t size;
- assert_d_eq(mallctlbymib(mib, miblen, (void *)&size, &z, NULL, 0),
+ expect_d_eq(mallctlbymib(mib, miblen, (void *)&size, &z, NULL, 0),
0, "Unexpected mallctlbymib([\"%s\"], ...) failure", cmd);
return size;
}
static size_t
-do_get_active(unsigned arena_ind) {
- return do_get_size_impl("stats.arenas.0.pactive", arena_ind) * PAGE;
+do_get_active(unsigned ind) {
+ return do_get_size_impl("stats.arenas.0.pactive", ind) * PAGE;
}
static size_t
-do_get_mapped(unsigned arena_ind) {
- return do_get_size_impl("stats.arenas.0.mapped", arena_ind);
+do_get_mapped(unsigned ind) {
+ return do_get_size_impl("stats.arenas.0.mapped", ind);
}
static void *
@@ -76,7 +77,7 @@ thd_start(void *arg) {
next_epoch) {
spin_adaptive(&spinner);
}
- assert_u_eq(cur_epoch, next_epoch, "Unexpected epoch");
+ expect_u_eq(cur_epoch, next_epoch, "Unexpected epoch");
/*
* Allocate. The main thread will reset the arena, so there's
@@ -86,7 +87,7 @@ thd_start(void *arg) {
void *p = mallocx(sz, MALLOCX_ARENA(arena_ind) |
MALLOCX_TCACHE_NONE
);
- assert_ptr_not_null(p,
+ expect_ptr_not_null(p,
"Unexpected mallocx() failure\n");
}
@@ -99,10 +100,12 @@ thd_start(void *arg) {
TEST_BEGIN(test_retained) {
test_skip_if(!config_stats);
+ test_skip_if(opt_hpa);
arena_ind = do_arena_create(NULL);
sz = nallocx(HUGEPAGE, 0);
- esz = sz + sz_large_pad;
+ size_t guard_sz = san_guard_enabled() ? SAN_PAGE_GUARDS_SIZE : 0;
+ esz = sz + sz_large_pad + guard_sz;
atomic_store_u(&epoch, 0, ATOMIC_RELAXED);
@@ -132,17 +135,18 @@ TEST_BEGIN(test_retained) {
*/
do_refresh();
- size_t allocated = esz * nthreads * PER_THD_NALLOCS;
+ size_t allocated = (esz - guard_sz) * nthreads *
+ PER_THD_NALLOCS;
size_t active = do_get_active(arena_ind);
- assert_zu_le(allocated, active, "Unexpected active memory");
+ expect_zu_le(allocated, active, "Unexpected active memory");
size_t mapped = do_get_mapped(arena_ind);
- assert_zu_le(active, mapped, "Unexpected mapped memory");
+ expect_zu_le(active, mapped, "Unexpected mapped memory");
arena_t *arena = arena_get(tsdn_fetch(), arena_ind, false);
size_t usable = 0;
size_t fragmented = 0;
for (pszind_t pind = sz_psz2ind(HUGEPAGE); pind <
- arena->extent_grow_next; pind++) {
+ arena->pa_shard.pac.exp_grow.next; pind++) {
size_t psz = sz_pind2sz(pind);
size_t psz_fragmented = psz % esz;
size_t psz_usable = psz - psz_fragmented;
@@ -150,7 +154,7 @@ TEST_BEGIN(test_retained) {
* Only consider size classes that wouldn't be skipped.
*/
if (psz_usable > 0) {
- assert_zu_lt(usable, allocated,
+ expect_zu_lt(usable, allocated,
"Excessive retained memory "
"(%#zx[+%#zx] > %#zx)", usable, psz_usable,
allocated);
@@ -165,7 +169,7 @@ TEST_BEGIN(test_retained) {
* (rather than retaining) during reset.
*/
do_arena_destroy(arena_ind);
- assert_u_eq(do_arena_create(NULL), arena_ind,
+ expect_u_eq(do_arena_create(NULL), arena_ind,
"Unexpected arena index");
}
diff --git a/deps/jemalloc/test/unit/rtree.c b/deps/jemalloc/test/unit/rtree.c
index 90adca134..4101b72be 100644
--- a/deps/jemalloc/test/unit/rtree.c
+++ b/deps/jemalloc/test/unit/rtree.c
@@ -2,80 +2,30 @@
#include "jemalloc/internal/rtree.h"
-rtree_node_alloc_t *rtree_node_alloc_orig;
-rtree_node_dalloc_t *rtree_node_dalloc_orig;
-rtree_leaf_alloc_t *rtree_leaf_alloc_orig;
-rtree_leaf_dalloc_t *rtree_leaf_dalloc_orig;
+#define INVALID_ARENA_IND ((1U << MALLOCX_ARENA_BITS) - 1)
/* Potentially too large to safely place on the stack. */
rtree_t test_rtree;
-static rtree_node_elm_t *
-rtree_node_alloc_intercept(tsdn_t *tsdn, rtree_t *rtree, size_t nelms) {
- rtree_node_elm_t *node;
-
- if (rtree != &test_rtree) {
- return rtree_node_alloc_orig(tsdn, rtree, nelms);
- }
-
- malloc_mutex_unlock(tsdn, &rtree->init_lock);
- node = (rtree_node_elm_t *)calloc(nelms, sizeof(rtree_node_elm_t));
- assert_ptr_not_null(node, "Unexpected calloc() failure");
- malloc_mutex_lock(tsdn, &rtree->init_lock);
-
- return node;
-}
-
-static void
-rtree_node_dalloc_intercept(tsdn_t *tsdn, rtree_t *rtree,
- rtree_node_elm_t *node) {
- if (rtree != &test_rtree) {
- rtree_node_dalloc_orig(tsdn, rtree, node);
- return;
- }
-
- free(node);
-}
-
-static rtree_leaf_elm_t *
-rtree_leaf_alloc_intercept(tsdn_t *tsdn, rtree_t *rtree, size_t nelms) {
- rtree_leaf_elm_t *leaf;
-
- if (rtree != &test_rtree) {
- return rtree_leaf_alloc_orig(tsdn, rtree, nelms);
- }
-
- malloc_mutex_unlock(tsdn, &rtree->init_lock);
- leaf = (rtree_leaf_elm_t *)calloc(nelms, sizeof(rtree_leaf_elm_t));
- assert_ptr_not_null(leaf, "Unexpected calloc() failure");
- malloc_mutex_lock(tsdn, &rtree->init_lock);
-
- return leaf;
-}
-
-static void
-rtree_leaf_dalloc_intercept(tsdn_t *tsdn, rtree_t *rtree,
- rtree_leaf_elm_t *leaf) {
- if (rtree != &test_rtree) {
- rtree_leaf_dalloc_orig(tsdn, rtree, leaf);
- return;
- }
-
- free(leaf);
-}
-
TEST_BEGIN(test_rtree_read_empty) {
tsdn_t *tsdn;
tsdn = tsdn_fetch();
+ base_t *base = base_new(tsdn, 0, &ehooks_default_extent_hooks,
+ /* metadata_use_hooks */ true);
+ expect_ptr_not_null(base, "Unexpected base_new failure");
+
rtree_t *rtree = &test_rtree;
rtree_ctx_t rtree_ctx;
rtree_ctx_data_init(&rtree_ctx);
- assert_false(rtree_new(rtree, false), "Unexpected rtree_new() failure");
- assert_ptr_null(rtree_extent_read(tsdn, rtree, &rtree_ctx, PAGE,
- false), "rtree_extent_read() should return NULL for empty tree");
- rtree_delete(tsdn, rtree);
+ expect_false(rtree_new(rtree, base, false),
+ "Unexpected rtree_new() failure");
+ rtree_contents_t contents;
+ expect_true(rtree_read_independent(tsdn, rtree, &rtree_ctx, PAGE,
+ &contents), "rtree_read_independent() should fail on empty rtree.");
+
+ base_delete(tsdn, base);
}
TEST_END
@@ -83,75 +33,119 @@ TEST_END
#undef NITERS
#undef SEED
+static edata_t *
+alloc_edata(void) {
+ void *ret = mallocx(sizeof(edata_t), MALLOCX_ALIGN(EDATA_ALIGNMENT));
+ assert_ptr_not_null(ret, "Unexpected mallocx() failure");
+
+ return ret;
+}
+
TEST_BEGIN(test_rtree_extrema) {
- extent_t extent_a, extent_b;
- extent_init(&extent_a, NULL, NULL, SC_LARGE_MINCLASS, false,
- sz_size2index(SC_LARGE_MINCLASS), 0,
- extent_state_active, false, false, true, EXTENT_NOT_HEAD);
- extent_init(&extent_b, NULL, NULL, 0, false, SC_NSIZES, 0,
- extent_state_active, false, false, true, EXTENT_NOT_HEAD);
+ edata_t *edata_a, *edata_b;
+ edata_a = alloc_edata();
+ edata_b = alloc_edata();
+ edata_init(edata_a, INVALID_ARENA_IND, NULL, SC_LARGE_MINCLASS,
+ false, sz_size2index(SC_LARGE_MINCLASS), 0,
+ extent_state_active, false, false, EXTENT_PAI_PAC, EXTENT_NOT_HEAD);
+ edata_init(edata_b, INVALID_ARENA_IND, NULL, 0, false, SC_NSIZES, 0,
+ extent_state_active, false, false, EXTENT_PAI_PAC, EXTENT_NOT_HEAD);
tsdn_t *tsdn = tsdn_fetch();
+ base_t *base = base_new(tsdn, 0, &ehooks_default_extent_hooks,
+ /* metadata_use_hooks */ true);
+ expect_ptr_not_null(base, "Unexpected base_new failure");
+
rtree_t *rtree = &test_rtree;
rtree_ctx_t rtree_ctx;
rtree_ctx_data_init(&rtree_ctx);
- assert_false(rtree_new(rtree, false), "Unexpected rtree_new() failure");
-
- assert_false(rtree_write(tsdn, rtree, &rtree_ctx, PAGE, &extent_a,
- extent_szind_get(&extent_a), extent_slab_get(&extent_a)),
+ expect_false(rtree_new(rtree, base, false),
+ "Unexpected rtree_new() failure");
+
+ rtree_contents_t contents_a;
+ contents_a.edata = edata_a;
+ contents_a.metadata.szind = edata_szind_get(edata_a);
+ contents_a.metadata.slab = edata_slab_get(edata_a);
+ contents_a.metadata.is_head = edata_is_head_get(edata_a);
+ contents_a.metadata.state = edata_state_get(edata_a);
+ expect_false(rtree_write(tsdn, rtree, &rtree_ctx, PAGE, contents_a),
+ "Unexpected rtree_write() failure");
+ expect_false(rtree_write(tsdn, rtree, &rtree_ctx, PAGE, contents_a),
"Unexpected rtree_write() failure");
- rtree_szind_slab_update(tsdn, rtree, &rtree_ctx, PAGE,
- extent_szind_get(&extent_a), extent_slab_get(&extent_a));
- assert_ptr_eq(rtree_extent_read(tsdn, rtree, &rtree_ctx, PAGE, true),
- &extent_a,
- "rtree_extent_read() should return previously set value");
-
- assert_false(rtree_write(tsdn, rtree, &rtree_ctx, ~((uintptr_t)0),
- &extent_b, extent_szind_get_maybe_invalid(&extent_b),
- extent_slab_get(&extent_b)), "Unexpected rtree_write() failure");
- assert_ptr_eq(rtree_extent_read(tsdn, rtree, &rtree_ctx,
- ~((uintptr_t)0), true), &extent_b,
- "rtree_extent_read() should return previously set value");
-
- rtree_delete(tsdn, rtree);
+ rtree_contents_t read_contents_a = rtree_read(tsdn, rtree, &rtree_ctx,
+ PAGE);
+ expect_true(contents_a.edata == read_contents_a.edata
+ && contents_a.metadata.szind == read_contents_a.metadata.szind
+ && contents_a.metadata.slab == read_contents_a.metadata.slab
+ && contents_a.metadata.is_head == read_contents_a.metadata.is_head
+ && contents_a.metadata.state == read_contents_a.metadata.state,
+ "rtree_read() should return previously set value");
+
+ rtree_contents_t contents_b;
+ contents_b.edata = edata_b;
+ contents_b.metadata.szind = edata_szind_get_maybe_invalid(edata_b);
+ contents_b.metadata.slab = edata_slab_get(edata_b);
+ contents_b.metadata.is_head = edata_is_head_get(edata_b);
+ contents_b.metadata.state = edata_state_get(edata_b);
+ expect_false(rtree_write(tsdn, rtree, &rtree_ctx, ~((uintptr_t)0),
+ contents_b), "Unexpected rtree_write() failure");
+ rtree_contents_t read_contents_b = rtree_read(tsdn, rtree, &rtree_ctx,
+ ~((uintptr_t)0));
+ assert_true(contents_b.edata == read_contents_b.edata
+ && contents_b.metadata.szind == read_contents_b.metadata.szind
+ && contents_b.metadata.slab == read_contents_b.metadata.slab
+ && contents_b.metadata.is_head == read_contents_b.metadata.is_head
+ && contents_b.metadata.state == read_contents_b.metadata.state,
+ "rtree_read() should return previously set value");
+
+ base_delete(tsdn, base);
}
TEST_END
TEST_BEGIN(test_rtree_bits) {
tsdn_t *tsdn = tsdn_fetch();
+ base_t *base = base_new(tsdn, 0, &ehooks_default_extent_hooks,
+ /* metadata_use_hooks */ true);
+ expect_ptr_not_null(base, "Unexpected base_new failure");
uintptr_t keys[] = {PAGE, PAGE + 1,
PAGE + (((uintptr_t)1) << LG_PAGE) - 1};
-
- extent_t extent;
- extent_init(&extent, NULL, NULL, 0, false, SC_NSIZES, 0,
- extent_state_active, false, false, true, EXTENT_NOT_HEAD);
+ edata_t *edata_c = alloc_edata();
+ edata_init(edata_c, INVALID_ARENA_IND, NULL, 0, false, SC_NSIZES, 0,
+ extent_state_active, false, false, EXTENT_PAI_PAC, EXTENT_NOT_HEAD);
rtree_t *rtree = &test_rtree;
rtree_ctx_t rtree_ctx;
rtree_ctx_data_init(&rtree_ctx);
- assert_false(rtree_new(rtree, false), "Unexpected rtree_new() failure");
+ expect_false(rtree_new(rtree, base, false),
+ "Unexpected rtree_new() failure");
for (unsigned i = 0; i < sizeof(keys)/sizeof(uintptr_t); i++) {
- assert_false(rtree_write(tsdn, rtree, &rtree_ctx, keys[i],
- &extent, SC_NSIZES, false),
- "Unexpected rtree_write() failure");
+ rtree_contents_t contents;
+ contents.edata = edata_c;
+ contents.metadata.szind = SC_NSIZES;
+ contents.metadata.slab = false;
+ contents.metadata.is_head = false;
+ contents.metadata.state = extent_state_active;
+
+ expect_false(rtree_write(tsdn, rtree, &rtree_ctx, keys[i],
+ contents), "Unexpected rtree_write() failure");
for (unsigned j = 0; j < sizeof(keys)/sizeof(uintptr_t); j++) {
- assert_ptr_eq(rtree_extent_read(tsdn, rtree, &rtree_ctx,
- keys[j], true), &extent,
- "rtree_extent_read() should return previously set "
+ expect_ptr_eq(rtree_read(tsdn, rtree, &rtree_ctx,
+ keys[j]).edata, edata_c,
+ "rtree_edata_read() should return previously set "
"value and ignore insignificant key bits; i=%u, "
"j=%u, set key=%#"FMTxPTR", get key=%#"FMTxPTR, i,
j, keys[i], keys[j]);
}
- assert_ptr_null(rtree_extent_read(tsdn, rtree, &rtree_ctx,
- (((uintptr_t)2) << LG_PAGE), false),
+ expect_ptr_null(rtree_read(tsdn, rtree, &rtree_ctx,
+ (((uintptr_t)2) << LG_PAGE)).edata,
"Only leftmost rtree leaf should be set; i=%u", i);
rtree_clear(tsdn, rtree, &rtree_ctx, keys[i]);
}
- rtree_delete(tsdn, rtree);
+ base_delete(tsdn, base);
}
TEST_END
@@ -160,69 +154,136 @@ TEST_BEGIN(test_rtree_random) {
#define SEED 42
sfmt_t *sfmt = init_gen_rand(SEED);
tsdn_t *tsdn = tsdn_fetch();
+
+ base_t *base = base_new(tsdn, 0, &ehooks_default_extent_hooks,
+ /* metadata_use_hooks */ true);
+ expect_ptr_not_null(base, "Unexpected base_new failure");
+
uintptr_t keys[NSET];
rtree_t *rtree = &test_rtree;
rtree_ctx_t rtree_ctx;
rtree_ctx_data_init(&rtree_ctx);
- extent_t extent;
- extent_init(&extent, NULL, NULL, 0, false, SC_NSIZES, 0,
- extent_state_active, false, false, true, EXTENT_NOT_HEAD);
+ edata_t *edata_d = alloc_edata();
+ edata_init(edata_d, INVALID_ARENA_IND, NULL, 0, false, SC_NSIZES, 0,
+ extent_state_active, false, false, EXTENT_PAI_PAC, EXTENT_NOT_HEAD);
- assert_false(rtree_new(rtree, false), "Unexpected rtree_new() failure");
+ expect_false(rtree_new(rtree, base, false),
+ "Unexpected rtree_new() failure");
for (unsigned i = 0; i < NSET; i++) {
keys[i] = (uintptr_t)gen_rand64(sfmt);
rtree_leaf_elm_t *elm = rtree_leaf_elm_lookup(tsdn, rtree,
&rtree_ctx, keys[i], false, true);
- assert_ptr_not_null(elm,
+ expect_ptr_not_null(elm,
"Unexpected rtree_leaf_elm_lookup() failure");
- rtree_leaf_elm_write(tsdn, rtree, elm, &extent, SC_NSIZES,
- false);
- assert_ptr_eq(rtree_extent_read(tsdn, rtree, &rtree_ctx,
- keys[i], true), &extent,
- "rtree_extent_read() should return previously set value");
+ rtree_contents_t contents;
+ contents.edata = edata_d;
+ contents.metadata.szind = SC_NSIZES;
+ contents.metadata.slab = false;
+ contents.metadata.is_head = false;
+ contents.metadata.state = edata_state_get(edata_d);
+ rtree_leaf_elm_write(tsdn, rtree, elm, contents);
+ expect_ptr_eq(rtree_read(tsdn, rtree, &rtree_ctx,
+ keys[i]).edata, edata_d,
+ "rtree_edata_read() should return previously set value");
}
for (unsigned i = 0; i < NSET; i++) {
- assert_ptr_eq(rtree_extent_read(tsdn, rtree, &rtree_ctx,
- keys[i], true), &extent,
- "rtree_extent_read() should return previously set value, "
+ expect_ptr_eq(rtree_read(tsdn, rtree, &rtree_ctx,
+ keys[i]).edata, edata_d,
+ "rtree_edata_read() should return previously set value, "
"i=%u", i);
}
for (unsigned i = 0; i < NSET; i++) {
rtree_clear(tsdn, rtree, &rtree_ctx, keys[i]);
- assert_ptr_null(rtree_extent_read(tsdn, rtree, &rtree_ctx,
- keys[i], true),
- "rtree_extent_read() should return previously set value");
+ expect_ptr_null(rtree_read(tsdn, rtree, &rtree_ctx,
+ keys[i]).edata,
+ "rtree_edata_read() should return previously set value");
}
for (unsigned i = 0; i < NSET; i++) {
- assert_ptr_null(rtree_extent_read(tsdn, rtree, &rtree_ctx,
- keys[i], true),
- "rtree_extent_read() should return previously set value");
+ expect_ptr_null(rtree_read(tsdn, rtree, &rtree_ctx,
+ keys[i]).edata,
+ "rtree_edata_read() should return previously set value");
}
- rtree_delete(tsdn, rtree);
+ base_delete(tsdn, base);
fini_gen_rand(sfmt);
#undef NSET
#undef SEED
}
TEST_END
+static void
+test_rtree_range_write(tsdn_t *tsdn, rtree_t *rtree, uintptr_t start,
+ uintptr_t end) {
+ rtree_ctx_t rtree_ctx;
+ rtree_ctx_data_init(&rtree_ctx);
+
+ edata_t *edata_e = alloc_edata();
+ edata_init(edata_e, INVALID_ARENA_IND, NULL, 0, false, SC_NSIZES, 0,
+ extent_state_active, false, false, EXTENT_PAI_PAC, EXTENT_NOT_HEAD);
+ rtree_contents_t contents;
+ contents.edata = edata_e;
+ contents.metadata.szind = SC_NSIZES;
+ contents.metadata.slab = false;
+ contents.metadata.is_head = false;
+ contents.metadata.state = extent_state_active;
+
+ expect_false(rtree_write(tsdn, rtree, &rtree_ctx, start,
+ contents), "Unexpected rtree_write() failure");
+ expect_false(rtree_write(tsdn, rtree, &rtree_ctx, end,
+ contents), "Unexpected rtree_write() failure");
+
+ rtree_write_range(tsdn, rtree, &rtree_ctx, start, end, contents);
+ for (uintptr_t i = 0; i < ((end - start) >> LG_PAGE); i++) {
+ expect_ptr_eq(rtree_read(tsdn, rtree, &rtree_ctx,
+ start + (i << LG_PAGE)).edata, edata_e,
+ "rtree_edata_read() should return previously set value");
+ }
+ rtree_clear_range(tsdn, rtree, &rtree_ctx, start, end);
+ rtree_leaf_elm_t *elm;
+ for (uintptr_t i = 0; i < ((end - start) >> LG_PAGE); i++) {
+ elm = rtree_leaf_elm_lookup(tsdn, rtree, &rtree_ctx,
+ start + (i << LG_PAGE), false, false);
+ expect_ptr_not_null(elm, "Should have been initialized.");
+ expect_ptr_null(rtree_leaf_elm_read(tsdn, rtree, elm,
+ false).edata, "Should have been cleared.");
+ }
+}
+
+TEST_BEGIN(test_rtree_range) {
+ tsdn_t *tsdn = tsdn_fetch();
+ base_t *base = base_new(tsdn, 0, &ehooks_default_extent_hooks,
+ /* metadata_use_hooks */ true);
+ expect_ptr_not_null(base, "Unexpected base_new failure");
+
+ rtree_t *rtree = &test_rtree;
+ expect_false(rtree_new(rtree, base, false),
+ "Unexpected rtree_new() failure");
+
+ /* Not crossing rtree node boundary first. */
+ uintptr_t start = ZU(1) << rtree_leaf_maskbits();
+ uintptr_t end = start + (ZU(100) << LG_PAGE);
+ test_rtree_range_write(tsdn, rtree, start, end);
+
+ /* Crossing rtree node boundary. */
+ start = (ZU(1) << rtree_leaf_maskbits()) - (ZU(10) << LG_PAGE);
+ end = start + (ZU(100) << LG_PAGE);
+ assert_ptr_ne((void *)rtree_leafkey(start), (void *)rtree_leafkey(end),
+ "The range should span across two rtree nodes");
+ test_rtree_range_write(tsdn, rtree, start, end);
+
+ base_delete(tsdn, base);
+}
+TEST_END
+
int
main(void) {
- rtree_node_alloc_orig = rtree_node_alloc;
- rtree_node_alloc = rtree_node_alloc_intercept;
- rtree_node_dalloc_orig = rtree_node_dalloc;
- rtree_node_dalloc = rtree_node_dalloc_intercept;
- rtree_leaf_alloc_orig = rtree_leaf_alloc;
- rtree_leaf_alloc = rtree_leaf_alloc_intercept;
- rtree_leaf_dalloc_orig = rtree_leaf_dalloc;
- rtree_leaf_dalloc = rtree_leaf_dalloc_intercept;
-
return test(
test_rtree_read_empty,
test_rtree_extrema,
test_rtree_bits,
- test_rtree_random);
+ test_rtree_random,
+ test_rtree_range);
}
diff --git a/deps/jemalloc/test/unit/safety_check.c b/deps/jemalloc/test/unit/safety_check.c
index bf4bd86d6..84726675f 100644
--- a/deps/jemalloc/test/unit/safety_check.c
+++ b/deps/jemalloc/test/unit/safety_check.c
@@ -13,6 +13,13 @@ void fake_abort(const char *message) {
fake_abort_called = true;
}
+static void
+buffer_overflow_write(char *ptr, size_t size) {
+ /* Avoid overflow warnings. */
+ volatile size_t idx = size;
+ ptr[idx] = 0;
+}
+
TEST_BEGIN(test_malloc_free_overflow) {
test_skip_if(!config_prof);
test_skip_if(!config_opt_safety_checks);
@@ -20,11 +27,11 @@ TEST_BEGIN(test_malloc_free_overflow) {
safety_check_set_abort(&fake_abort);
/* Buffer overflow! */
char* ptr = malloc(128);
- ptr[128] = 0;
+ buffer_overflow_write(ptr, 128);
free(ptr);
safety_check_set_abort(NULL);
- assert_b_eq(fake_abort_called, true, "Redzone check didn't fire.");
+ expect_b_eq(fake_abort_called, true, "Redzone check didn't fire.");
fake_abort_called = false;
}
TEST_END
@@ -36,11 +43,11 @@ TEST_BEGIN(test_mallocx_dallocx_overflow) {
safety_check_set_abort(&fake_abort);
/* Buffer overflow! */
char* ptr = mallocx(128, 0);
- ptr[128] = 0;
+ buffer_overflow_write(ptr, 128);
dallocx(ptr, 0);
safety_check_set_abort(NULL);
- assert_b_eq(fake_abort_called, true, "Redzone check didn't fire.");
+ expect_b_eq(fake_abort_called, true, "Redzone check didn't fire.");
fake_abort_called = false;
}
TEST_END
@@ -52,11 +59,11 @@ TEST_BEGIN(test_malloc_sdallocx_overflow) {
safety_check_set_abort(&fake_abort);
/* Buffer overflow! */
char* ptr = malloc(128);
- ptr[128] = 0;
+ buffer_overflow_write(ptr, 128);
sdallocx(ptr, 128, 0);
safety_check_set_abort(NULL);
- assert_b_eq(fake_abort_called, true, "Redzone check didn't fire.");
+ expect_b_eq(fake_abort_called, true, "Redzone check didn't fire.");
fake_abort_called = false;
}
TEST_END
@@ -68,12 +75,12 @@ TEST_BEGIN(test_realloc_overflow) {
safety_check_set_abort(&fake_abort);
/* Buffer overflow! */
char* ptr = malloc(128);
- ptr[128] = 0;
+ buffer_overflow_write(ptr, 128);
ptr = realloc(ptr, 129);
safety_check_set_abort(NULL);
free(ptr);
- assert_b_eq(fake_abort_called, true, "Redzone check didn't fire.");
+ expect_b_eq(fake_abort_called, true, "Redzone check didn't fire.");
fake_abort_called = false;
}
TEST_END
@@ -85,12 +92,12 @@ TEST_BEGIN(test_rallocx_overflow) {
safety_check_set_abort(&fake_abort);
/* Buffer overflow! */
char* ptr = malloc(128);
- ptr[128] = 0;
+ buffer_overflow_write(ptr, 128);
ptr = rallocx(ptr, 129, 0);
safety_check_set_abort(NULL);
free(ptr);
- assert_b_eq(fake_abort_called, true, "Redzone check didn't fire.");
+ expect_b_eq(fake_abort_called, true, "Redzone check didn't fire.");
fake_abort_called = false;
}
TEST_END
@@ -102,11 +109,11 @@ TEST_BEGIN(test_xallocx_overflow) {
safety_check_set_abort(&fake_abort);
/* Buffer overflow! */
char* ptr = malloc(128);
- ptr[128] = 0;
+ buffer_overflow_write(ptr, 128);
size_t result = xallocx(ptr, 129, 0, 0);
- assert_zu_eq(result, 128, "");
+ expect_zu_eq(result, 128, "");
free(ptr);
- assert_b_eq(fake_abort_called, true, "Redzone check didn't fire.");
+ expect_b_eq(fake_abort_called, true, "Redzone check didn't fire.");
fake_abort_called = false;
safety_check_set_abort(NULL);
}
diff --git a/deps/jemalloc/test/unit/safety_check.sh b/deps/jemalloc/test/unit/safety_check.sh
index 8fcc7d8a7..485f9bf0a 100644
--- a/deps/jemalloc/test/unit/safety_check.sh
+++ b/deps/jemalloc/test/unit/safety_check.sh
@@ -1,5 +1,5 @@
#!/bin/sh
if [ "x${enable_prof}" = "x1" ] ; then
- export MALLOC_CONF="prof:true,lg_prof_sample:0"
+ export MALLOC_CONF="prof:true,prof_active:true,lg_prof_sample:0"
fi
diff --git a/deps/jemalloc/test/unit/san.c b/deps/jemalloc/test/unit/san.c
new file mode 100644
index 000000000..5b98f52e6
--- /dev/null
+++ b/deps/jemalloc/test/unit/san.c
@@ -0,0 +1,207 @@
+#include "test/jemalloc_test.h"
+#include "test/arena_util.h"
+#include "test/san.h"
+
+#include "jemalloc/internal/san.h"
+
+static void
+verify_extent_guarded(tsdn_t *tsdn, void *ptr) {
+ expect_true(extent_is_guarded(tsdn, ptr),
+ "All extents should be guarded.");
+}
+
+#define MAX_SMALL_ALLOCATIONS 4096
+void *small_alloc[MAX_SMALL_ALLOCATIONS];
+
+/*
+ * This test allocates page sized slabs and checks that every two slabs have
+ * at least one page in between them. That page is supposed to be the guard
+ * page.
+ */
+TEST_BEGIN(test_guarded_small) {
+ test_skip_if(opt_prof);
+
+ tsdn_t *tsdn = tsd_tsdn(tsd_fetch());
+ unsigned npages = 16, pages_found = 0, ends_found = 0;
+ VARIABLE_ARRAY(uintptr_t, pages, npages);
+
+ /* Allocate to get sanitized pointers. */
+ size_t slab_sz = PAGE;
+ size_t sz = slab_sz / 8;
+ unsigned n_alloc = 0;
+ while (n_alloc < MAX_SMALL_ALLOCATIONS) {
+ void *ptr = malloc(sz);
+ expect_ptr_not_null(ptr, "Unexpected malloc() failure");
+ small_alloc[n_alloc] = ptr;
+ verify_extent_guarded(tsdn, ptr);
+ if ((uintptr_t)ptr % PAGE == 0) {
+ assert_u_lt(pages_found, npages,
+ "Unexpectedly large number of page aligned allocs");
+ pages[pages_found++] = (uintptr_t)ptr;
+ }
+ if (((uintptr_t)ptr + (uintptr_t)sz) % PAGE == 0) {
+ ends_found++;
+ }
+ n_alloc++;
+ if (pages_found == npages && ends_found == npages) {
+ break;
+ }
+ }
+ /* Should found the ptrs being checked for overflow and underflow. */
+ expect_u_eq(pages_found, npages, "Could not found the expected pages.");
+ expect_u_eq(ends_found, npages, "Could not found the expected pages.");
+
+ /* Verify the pages are not continuous, i.e. separated by guards. */
+ for (unsigned i = 0; i < npages - 1; i++) {
+ for (unsigned j = i + 1; j < npages; j++) {
+ uintptr_t ptr_diff = pages[i] > pages[j] ?
+ pages[i] - pages[j] : pages[j] - pages[i];
+ expect_zu_ge((size_t)ptr_diff, slab_sz + PAGE,
+ "There should be at least one pages between "
+ "guarded slabs");
+ }
+ }
+
+ for (unsigned i = 0; i < n_alloc + 1; i++) {
+ free(small_alloc[i]);
+ }
+}
+TEST_END
+
+TEST_BEGIN(test_guarded_large) {
+ tsdn_t *tsdn = tsd_tsdn(tsd_fetch());
+ unsigned nlarge = 32;
+ VARIABLE_ARRAY(uintptr_t, large, nlarge);
+
+ /* Allocate to get sanitized pointers. */
+ size_t large_sz = SC_LARGE_MINCLASS;
+ for (unsigned i = 0; i < nlarge; i++) {
+ void *ptr = malloc(large_sz);
+ verify_extent_guarded(tsdn, ptr);
+ expect_ptr_not_null(ptr, "Unexpected malloc() failure");
+ large[i] = (uintptr_t)ptr;
+ }
+
+ /* Verify the pages are not continuous, i.e. separated by guards. */
+ for (unsigned i = 0; i < nlarge; i++) {
+ for (unsigned j = i + 1; j < nlarge; j++) {
+ uintptr_t ptr_diff = large[i] > large[j] ?
+ large[i] - large[j] : large[j] - large[i];
+ expect_zu_ge((size_t)ptr_diff, large_sz + 2 * PAGE,
+ "There should be at least two pages between "
+ " guarded large allocations");
+ }
+ }
+
+ for (unsigned i = 0; i < nlarge; i++) {
+ free((void *)large[i]);
+ }
+}
+TEST_END
+
+static void
+verify_pdirty(unsigned arena_ind, uint64_t expected) {
+ uint64_t pdirty = get_arena_pdirty(arena_ind);
+ expect_u64_eq(pdirty, expected / PAGE,
+ "Unexpected dirty page amount.");
+}
+
+static void
+verify_pmuzzy(unsigned arena_ind, uint64_t expected) {
+ uint64_t pmuzzy = get_arena_pmuzzy(arena_ind);
+ expect_u64_eq(pmuzzy, expected / PAGE,
+ "Unexpected muzzy page amount.");
+}
+
+TEST_BEGIN(test_guarded_decay) {
+ unsigned arena_ind = do_arena_create(-1, -1);
+ do_decay(arena_ind);
+ do_purge(arena_ind);
+
+ verify_pdirty(arena_ind, 0);
+ verify_pmuzzy(arena_ind, 0);
+
+ /* Verify that guarded extents as dirty. */
+ size_t sz1 = PAGE, sz2 = PAGE * 2;
+ /* W/o maps_coalesce, guarded extents are unguarded eagerly. */
+ size_t add_guard_size = maps_coalesce ? 0 : SAN_PAGE_GUARDS_SIZE;
+ generate_dirty(arena_ind, sz1);
+ verify_pdirty(arena_ind, sz1 + add_guard_size);
+ verify_pmuzzy(arena_ind, 0);
+
+ /* Should reuse the first extent. */
+ generate_dirty(arena_ind, sz1);
+ verify_pdirty(arena_ind, sz1 + add_guard_size);
+ verify_pmuzzy(arena_ind, 0);
+
+ /* Should not reuse; expect new dirty pages. */
+ generate_dirty(arena_ind, sz2);
+ verify_pdirty(arena_ind, sz1 + sz2 + 2 * add_guard_size);
+ verify_pmuzzy(arena_ind, 0);
+
+ tsdn_t *tsdn = tsd_tsdn(tsd_fetch());
+ int flags = MALLOCX_ARENA(arena_ind) | MALLOCX_TCACHE_NONE;
+
+ /* Should reuse dirty extents for the two mallocx. */
+ void *p1 = do_mallocx(sz1, flags);
+ verify_extent_guarded(tsdn, p1);
+ verify_pdirty(arena_ind, sz2 + add_guard_size);
+
+ void *p2 = do_mallocx(sz2, flags);
+ verify_extent_guarded(tsdn, p2);
+ verify_pdirty(arena_ind, 0);
+ verify_pmuzzy(arena_ind, 0);
+
+ dallocx(p1, flags);
+ verify_pdirty(arena_ind, sz1 + add_guard_size);
+ dallocx(p2, flags);
+ verify_pdirty(arena_ind, sz1 + sz2 + 2 * add_guard_size);
+ verify_pmuzzy(arena_ind, 0);
+
+ do_purge(arena_ind);
+ verify_pdirty(arena_ind, 0);
+ verify_pmuzzy(arena_ind, 0);
+
+ if (config_stats) {
+ expect_u64_eq(get_arena_npurge(arena_ind), 1,
+ "Expected purging to occur");
+ expect_u64_eq(get_arena_dirty_npurge(arena_ind), 1,
+ "Expected purging to occur");
+ expect_u64_eq(get_arena_dirty_purged(arena_ind),
+ (sz1 + sz2 + 2 * add_guard_size) / PAGE,
+ "Expected purging to occur");
+ expect_u64_eq(get_arena_muzzy_npurge(arena_ind), 0,
+ "Expected purging to occur");
+ }
+
+ if (opt_retain) {
+ /*
+ * With retain, guarded extents are not mergable and will be
+ * cached in ecache_retained. They should be reused.
+ */
+ void *new_p1 = do_mallocx(sz1, flags);
+ verify_extent_guarded(tsdn, p1);
+ expect_ptr_eq(p1, new_p1, "Expect to reuse p1");
+
+ void *new_p2 = do_mallocx(sz2, flags);
+ verify_extent_guarded(tsdn, p2);
+ expect_ptr_eq(p2, new_p2, "Expect to reuse p2");
+
+ dallocx(new_p1, flags);
+ verify_pdirty(arena_ind, sz1 + add_guard_size);
+ dallocx(new_p2, flags);
+ verify_pdirty(arena_ind, sz1 + sz2 + 2 * add_guard_size);
+ verify_pmuzzy(arena_ind, 0);
+ }
+
+ do_arena_destroy(arena_ind);
+}
+TEST_END
+
+int
+main(void) {
+ return test(
+ test_guarded_small,
+ test_guarded_large,
+ test_guarded_decay);
+}
diff --git a/deps/jemalloc/test/unit/san.sh b/deps/jemalloc/test/unit/san.sh
new file mode 100644
index 000000000..933b4a4d6
--- /dev/null
+++ b/deps/jemalloc/test/unit/san.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+export MALLOC_CONF="san_guard_large:1,san_guard_small:1"
diff --git a/deps/jemalloc/test/unit/san_bump.c b/deps/jemalloc/test/unit/san_bump.c
new file mode 100644
index 000000000..cafa37fee
--- /dev/null
+++ b/deps/jemalloc/test/unit/san_bump.c
@@ -0,0 +1,111 @@
+#include "test/jemalloc_test.h"
+#include "test/arena_util.h"
+
+#include "jemalloc/internal/arena_structs.h"
+#include "jemalloc/internal/san_bump.h"
+
+TEST_BEGIN(test_san_bump_alloc) {
+ test_skip_if(!maps_coalesce || !opt_retain);
+
+ tsdn_t *tsdn = tsdn_fetch();
+
+ san_bump_alloc_t sba;
+ san_bump_alloc_init(&sba);
+
+ unsigned arena_ind = do_arena_create(0, 0);
+ assert_u_ne(arena_ind, UINT_MAX, "Failed to create an arena");
+
+ arena_t *arena = arena_get(tsdn, arena_ind, false);
+ pac_t *pac = &arena->pa_shard.pac;
+
+ size_t alloc_size = PAGE * 16;
+ size_t alloc_n = alloc_size / sizeof(unsigned);
+ edata_t* edata = san_bump_alloc(tsdn, &sba, pac, pac_ehooks_get(pac),
+ alloc_size, /* zero */ false);
+
+ expect_ptr_not_null(edata, "Failed to allocate edata");
+ expect_u_eq(edata_arena_ind_get(edata), arena_ind,
+ "Edata was assigned an incorrect arena id");
+ expect_zu_eq(edata_size_get(edata), alloc_size,
+ "Allocated edata of incorrect size");
+ expect_false(edata_slab_get(edata),
+ "Bump allocator incorrectly assigned 'slab' to true");
+ expect_true(edata_committed_get(edata), "Edata is not committed");
+
+ void *ptr = edata_addr_get(edata);
+ expect_ptr_not_null(ptr, "Edata was assigned an invalid address");
+ /* Test that memory is allocated; no guard pages are misplaced */
+ for (unsigned i = 0; i < alloc_n; ++i) {
+ ((unsigned *)ptr)[i] = 1;
+ }
+
+ size_t alloc_size2 = PAGE * 28;
+ size_t alloc_n2 = alloc_size / sizeof(unsigned);
+ edata_t *edata2 = san_bump_alloc(tsdn, &sba, pac, pac_ehooks_get(pac),
+ alloc_size2, /* zero */ true);
+
+ expect_ptr_not_null(edata2, "Failed to allocate edata");
+ expect_u_eq(edata_arena_ind_get(edata2), arena_ind,
+ "Edata was assigned an incorrect arena id");
+ expect_zu_eq(edata_size_get(edata2), alloc_size2,
+ "Allocated edata of incorrect size");
+ expect_false(edata_slab_get(edata2),
+ "Bump allocator incorrectly assigned 'slab' to true");
+ expect_true(edata_committed_get(edata2), "Edata is not committed");
+
+ void *ptr2 = edata_addr_get(edata2);
+ expect_ptr_not_null(ptr, "Edata was assigned an invalid address");
+
+ uintptr_t ptrdiff = ptr2 > ptr ? (uintptr_t)ptr2 - (uintptr_t)ptr
+ : (uintptr_t)ptr - (uintptr_t)ptr2;
+ size_t between_allocs = (size_t)ptrdiff - alloc_size;
+
+ expect_zu_ge(between_allocs, PAGE,
+ "Guard page between allocs is missing");
+
+ for (unsigned i = 0; i < alloc_n2; ++i) {
+ expect_u_eq(((unsigned *)ptr2)[i], 0, "Memory is not zeroed");
+ }
+}
+TEST_END
+
+TEST_BEGIN(test_large_alloc_size) {
+ test_skip_if(!maps_coalesce || !opt_retain);
+
+ tsdn_t *tsdn = tsdn_fetch();
+
+ san_bump_alloc_t sba;
+ san_bump_alloc_init(&sba);
+
+ unsigned arena_ind = do_arena_create(0, 0);
+ assert_u_ne(arena_ind, UINT_MAX, "Failed to create an arena");
+
+ arena_t *arena = arena_get(tsdn, arena_ind, false);
+ pac_t *pac = &arena->pa_shard.pac;
+
+ size_t alloc_size = SBA_RETAINED_ALLOC_SIZE * 2;
+ edata_t* edata = san_bump_alloc(tsdn, &sba, pac, pac_ehooks_get(pac),
+ alloc_size, /* zero */ false);
+ expect_u_eq(edata_arena_ind_get(edata), arena_ind,
+ "Edata was assigned an incorrect arena id");
+ expect_zu_eq(edata_size_get(edata), alloc_size,
+ "Allocated edata of incorrect size");
+ expect_false(edata_slab_get(edata),
+ "Bump allocator incorrectly assigned 'slab' to true");
+ expect_true(edata_committed_get(edata), "Edata is not committed");
+
+ void *ptr = edata_addr_get(edata);
+ expect_ptr_not_null(ptr, "Edata was assigned an invalid address");
+ /* Test that memory is allocated; no guard pages are misplaced */
+ for (unsigned i = 0; i < alloc_size / PAGE; ++i) {
+ *((char *)ptr + PAGE * i) = 1;
+ }
+}
+TEST_END
+
+int
+main(void) {
+ return test(
+ test_san_bump_alloc,
+ test_large_alloc_size);
+}
diff --git a/deps/jemalloc/test/unit/sc.c b/deps/jemalloc/test/unit/sc.c
index bf51d8e59..d207481c3 100644
--- a/deps/jemalloc/test/unit/sc.c
+++ b/deps/jemalloc/test/unit/sc.c
@@ -9,7 +9,7 @@ TEST_BEGIN(test_update_slab_size) {
+ (ZU(tiny->ndelta) << tiny->lg_delta);
size_t pgs_too_big = (tiny_size * BITMAP_MAXBITS + PAGE - 1) / PAGE + 1;
sc_data_update_slab_size(&data, tiny_size, tiny_size, (int)pgs_too_big);
- assert_zu_lt((size_t)tiny->pgs, pgs_too_big, "Allowed excessive pages");
+ expect_zu_lt((size_t)tiny->pgs, pgs_too_big, "Allowed excessive pages");
sc_data_update_slab_size(&data, 1, 10 * PAGE, 1);
for (int i = 0; i < data.nbins; i++) {
@@ -17,9 +17,9 @@ TEST_BEGIN(test_update_slab_size) {
size_t reg_size = (ZU(1) << sc->lg_base)
+ (ZU(sc->ndelta) << sc->lg_delta);
if (reg_size <= PAGE) {
- assert_d_eq(sc->pgs, 1, "Ignored valid page size hint");
+ expect_d_eq(sc->pgs, 1, "Ignored valid page size hint");
} else {
- assert_d_gt(sc->pgs, 1,
+ expect_d_gt(sc->pgs, 1,
"Allowed invalid page size hint");
}
}
diff --git a/deps/jemalloc/test/unit/sec.c b/deps/jemalloc/test/unit/sec.c
new file mode 100644
index 000000000..f3ec403da
--- /dev/null
+++ b/deps/jemalloc/test/unit/sec.c
@@ -0,0 +1,634 @@
+#include "test/jemalloc_test.h"
+
+#include "jemalloc/internal/sec.h"
+
+typedef struct pai_test_allocator_s pai_test_allocator_t;
+struct pai_test_allocator_s {
+ pai_t pai;
+ bool alloc_fail;
+ size_t alloc_count;
+ size_t alloc_batch_count;
+ size_t dalloc_count;
+ size_t dalloc_batch_count;
+ /*
+ * We use a simple bump allocator as the implementation. This isn't
+ * *really* correct, since we may allow expansion into a subsequent
+ * allocation, but it's not like the SEC is really examining the
+ * pointers it gets back; this is mostly just helpful for debugging.
+ */
+ uintptr_t next_ptr;
+ size_t expand_count;
+ bool expand_return_value;
+ size_t shrink_count;
+ bool shrink_return_value;
+};
+
+static void
+test_sec_init(sec_t *sec, pai_t *fallback, size_t nshards, size_t max_alloc,
+ size_t max_bytes) {
+ sec_opts_t opts;
+ opts.nshards = 1;
+ opts.max_alloc = max_alloc;
+ opts.max_bytes = max_bytes;
+ /*
+ * Just choose reasonable defaults for these; most tests don't care so
+ * long as they're something reasonable.
+ */
+ opts.bytes_after_flush = max_bytes / 2;
+ opts.batch_fill_extra = 4;
+
+ /*
+ * We end up leaking this base, but that's fine; this test is
+ * short-running, and SECs are arena-scoped in reality.
+ */
+ base_t *base = base_new(TSDN_NULL, /* ind */ 123,
+ &ehooks_default_extent_hooks, /* metadata_use_hooks */ true);
+
+ bool err = sec_init(TSDN_NULL, sec, base, fallback, &opts);
+ assert_false(err, "Unexpected initialization failure");
+ assert_u_ge(sec->npsizes, 0, "Zero size classes allowed for caching");
+}
+
+static inline edata_t *
+pai_test_allocator_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(!guarded);
+ pai_test_allocator_t *ta = (pai_test_allocator_t *)self;
+ if (ta->alloc_fail) {
+ return NULL;
+ }
+ edata_t *edata = malloc(sizeof(edata_t));
+ assert_ptr_not_null(edata, "");
+ ta->next_ptr += alignment - 1;
+ edata_init(edata, /* arena_ind */ 0,
+ (void *)(ta->next_ptr & ~(alignment - 1)), size,
+ /* slab */ false,
+ /* szind */ 0, /* sn */ 1, extent_state_active, /* zero */ zero,
+ /* comitted */ true, /* ranged */ false, EXTENT_NOT_HEAD);
+ ta->next_ptr += size;
+ ta->alloc_count++;
+ return edata;
+}
+
+static inline size_t
+pai_test_allocator_alloc_batch(tsdn_t *tsdn, pai_t *self, size_t size,
+ size_t nallocs, edata_list_active_t *results,
+ bool *deferred_work_generated) {
+ pai_test_allocator_t *ta = (pai_test_allocator_t *)self;
+ if (ta->alloc_fail) {
+ return 0;
+ }
+ for (size_t i = 0; i < nallocs; i++) {
+ edata_t *edata = malloc(sizeof(edata_t));
+ assert_ptr_not_null(edata, "");
+ edata_init(edata, /* arena_ind */ 0,
+ (void *)ta->next_ptr, size,
+ /* slab */ false, /* szind */ 0, /* sn */ 1,
+ extent_state_active, /* zero */ false, /* comitted */ true,
+ /* ranged */ false, EXTENT_NOT_HEAD);
+ ta->next_ptr += size;
+ ta->alloc_batch_count++;
+ edata_list_active_append(results, edata);
+ }
+ return nallocs;
+}
+
+static bool
+pai_test_allocator_expand(tsdn_t *tsdn, pai_t *self, edata_t *edata,
+ size_t old_size, size_t new_size, bool zero,
+ bool *deferred_work_generated) {
+ pai_test_allocator_t *ta = (pai_test_allocator_t *)self;
+ ta->expand_count++;
+ return ta->expand_return_value;
+}
+
+static bool
+pai_test_allocator_shrink(tsdn_t *tsdn, pai_t *self, edata_t *edata,
+ size_t old_size, size_t new_size, bool *deferred_work_generated) {
+ pai_test_allocator_t *ta = (pai_test_allocator_t *)self;
+ ta->shrink_count++;
+ return ta->shrink_return_value;
+}
+
+static void
+pai_test_allocator_dalloc(tsdn_t *tsdn, pai_t *self, edata_t *edata,
+ bool *deferred_work_generated) {
+ pai_test_allocator_t *ta = (pai_test_allocator_t *)self;
+ ta->dalloc_count++;
+ free(edata);
+}
+
+static void
+pai_test_allocator_dalloc_batch(tsdn_t *tsdn, pai_t *self,
+ edata_list_active_t *list, bool *deferred_work_generated) {
+ pai_test_allocator_t *ta = (pai_test_allocator_t *)self;
+
+ edata_t *edata;
+ while ((edata = edata_list_active_first(list)) != NULL) {
+ edata_list_active_remove(list, edata);
+ ta->dalloc_batch_count++;
+ free(edata);
+ }
+}
+
+static inline void
+pai_test_allocator_init(pai_test_allocator_t *ta) {
+ ta->alloc_fail = false;
+ ta->alloc_count = 0;
+ ta->alloc_batch_count = 0;
+ ta->dalloc_count = 0;
+ ta->dalloc_batch_count = 0;
+ /* Just don't start the edata at 0. */
+ ta->next_ptr = 10 * PAGE;
+ ta->expand_count = 0;
+ ta->expand_return_value = false;
+ ta->shrink_count = 0;
+ ta->shrink_return_value = false;
+ ta->pai.alloc = &pai_test_allocator_alloc;
+ ta->pai.alloc_batch = &pai_test_allocator_alloc_batch;
+ ta->pai.expand = &pai_test_allocator_expand;
+ ta->pai.shrink = &pai_test_allocator_shrink;
+ ta->pai.dalloc = &pai_test_allocator_dalloc;
+ ta->pai.dalloc_batch = &pai_test_allocator_dalloc_batch;
+}
+
+TEST_BEGIN(test_reuse) {
+ pai_test_allocator_t ta;
+ pai_test_allocator_init(&ta);
+ sec_t sec;
+ /*
+ * We can't use the "real" tsd, since we malloc within the test
+ * allocator hooks; we'd get lock inversion crashes. Eventually, we
+ * should have a way to mock tsds, but for now just don't do any
+ * lock-order checking.
+ */
+ tsdn_t *tsdn = TSDN_NULL;
+ /*
+ * 11 allocs apiece of 1-PAGE and 2-PAGE objects means that we should be
+ * able to get to 33 pages in the cache before triggering a flush. We
+ * set the flush liimt to twice this amount, to avoid accidentally
+ * triggering a flush caused by the batch-allocation down the cache fill
+ * pathway disrupting ordering.
+ */
+ enum { NALLOCS = 11 };
+ edata_t *one_page[NALLOCS];
+ edata_t *two_page[NALLOCS];
+ bool deferred_work_generated = false;
+ test_sec_init(&sec, &ta.pai, /* nshards */ 1, /* max_alloc */ 2 * PAGE,
+ /* max_bytes */ 2 * (NALLOCS * PAGE + NALLOCS * 2 * PAGE));
+ for (int i = 0; i < NALLOCS; i++) {
+ one_page[i] = pai_alloc(tsdn, &sec.pai, PAGE, PAGE,
+ /* zero */ false, /* guarded */ false, /* frequent_reuse */
+ false, &deferred_work_generated);
+ expect_ptr_not_null(one_page[i], "Unexpected alloc failure");
+ two_page[i] = pai_alloc(tsdn, &sec.pai, 2 * PAGE, PAGE,
+ /* zero */ false, /* guarded */ false, /* frequent_reuse */
+ false, &deferred_work_generated);
+ expect_ptr_not_null(one_page[i], "Unexpected alloc failure");
+ }
+ expect_zu_eq(0, ta.alloc_count, "Should be using batch allocs");
+ size_t max_allocs = ta.alloc_count + ta.alloc_batch_count;
+ expect_zu_le(2 * NALLOCS, max_allocs,
+ "Incorrect number of allocations");
+ expect_zu_eq(0, ta.dalloc_count,
+ "Incorrect number of allocations");
+ /*
+ * Free in a different order than we allocated, to make sure free-list
+ * separation works correctly.
+ */
+ for (int i = NALLOCS - 1; i >= 0; i--) {
+ pai_dalloc(tsdn, &sec.pai, one_page[i],
+ &deferred_work_generated);
+ }
+ for (int i = NALLOCS - 1; i >= 0; i--) {
+ pai_dalloc(tsdn, &sec.pai, two_page[i],
+ &deferred_work_generated);
+ }
+ expect_zu_eq(max_allocs, ta.alloc_count + ta.alloc_batch_count,
+ "Incorrect number of allocations");
+ expect_zu_eq(0, ta.dalloc_count,
+ "Incorrect number of allocations");
+ /*
+ * Check that the n'th most recent deallocated extent is returned for
+ * the n'th alloc request of a given size.
+ */
+ for (int i = 0; i < NALLOCS; i++) {
+ edata_t *alloc1 = pai_alloc(tsdn, &sec.pai, PAGE, PAGE,
+ /* zero */ false, /* guarded */ false, /* frequent_reuse */
+ false, &deferred_work_generated);
+ edata_t *alloc2 = pai_alloc(tsdn, &sec.pai, 2 * PAGE, PAGE,
+ /* zero */ false, /* guarded */ false, /* frequent_reuse */
+ false, &deferred_work_generated);
+ expect_ptr_eq(one_page[i], alloc1,
+ "Got unexpected allocation");
+ expect_ptr_eq(two_page[i], alloc2,
+ "Got unexpected allocation");
+ }
+ expect_zu_eq(max_allocs, ta.alloc_count + ta.alloc_batch_count,
+ "Incorrect number of allocations");
+ expect_zu_eq(0, ta.dalloc_count,
+ "Incorrect number of allocations");
+}
+TEST_END
+
+
+TEST_BEGIN(test_auto_flush) {
+ pai_test_allocator_t ta;
+ pai_test_allocator_init(&ta);
+ sec_t sec;
+ /* See the note above -- we can't use the real tsd. */
+ tsdn_t *tsdn = TSDN_NULL;
+ /*
+ * 10-allocs apiece of 1-PAGE and 2-PAGE objects means that we should be
+ * able to get to 30 pages in the cache before triggering a flush. The
+ * choice of NALLOCS here is chosen to match the batch allocation
+ * default (4 extra + 1 == 5; so 10 allocations leaves the cache exactly
+ * empty, even in the presence of batch allocation on fill).
+ * Eventually, once our allocation batching strategies become smarter,
+ * this should change.
+ */
+ enum { NALLOCS = 10 };
+ edata_t *extra_alloc;
+ edata_t *allocs[NALLOCS];
+ bool deferred_work_generated = false;
+ test_sec_init(&sec, &ta.pai, /* nshards */ 1, /* max_alloc */ PAGE,
+ /* max_bytes */ NALLOCS * PAGE);
+ for (int i = 0; i < NALLOCS; i++) {
+ allocs[i] = pai_alloc(tsdn, &sec.pai, PAGE, PAGE,
+ /* zero */ false, /* guarded */ false, /* frequent_reuse */
+ false, &deferred_work_generated);
+ expect_ptr_not_null(allocs[i], "Unexpected alloc failure");
+ }
+ extra_alloc = pai_alloc(tsdn, &sec.pai, PAGE, PAGE, /* zero */ false,
+ /* guarded */ false, /* frequent_reuse */ false,
+ &deferred_work_generated);
+ expect_ptr_not_null(extra_alloc, "Unexpected alloc failure");
+ size_t max_allocs = ta.alloc_count + ta.alloc_batch_count;
+ expect_zu_le(NALLOCS + 1, max_allocs,
+ "Incorrect number of allocations");
+ expect_zu_eq(0, ta.dalloc_count,
+ "Incorrect number of allocations");
+ /* Free until the SEC is full, but should not have flushed yet. */
+ for (int i = 0; i < NALLOCS; i++) {
+ pai_dalloc(tsdn, &sec.pai, allocs[i], &deferred_work_generated);
+ }
+ expect_zu_le(NALLOCS + 1, max_allocs,
+ "Incorrect number of allocations");
+ expect_zu_eq(0, ta.dalloc_count,
+ "Incorrect number of allocations");
+ /*
+ * Free the extra allocation; this should trigger a flush. The internal
+ * flushing logic is allowed to get complicated; for now, we rely on our
+ * whitebox knowledge of the fact that the SEC flushes bins in their
+ * entirety when it decides to do so, and it has only one bin active
+ * right now.
+ */
+ pai_dalloc(tsdn, &sec.pai, extra_alloc, &deferred_work_generated);
+ expect_zu_eq(max_allocs, ta.alloc_count + ta.alloc_batch_count,
+ "Incorrect number of allocations");
+ expect_zu_eq(0, ta.dalloc_count,
+ "Incorrect number of (non-batch) deallocations");
+ expect_zu_eq(NALLOCS + 1, ta.dalloc_batch_count,
+ "Incorrect number of batch deallocations");
+}
+TEST_END
+
+/*
+ * A disable and a flush are *almost* equivalent; the only difference is what
+ * happens afterwards; disabling disallows all future caching as well.
+ */
+static void
+do_disable_flush_test(bool is_disable) {
+ pai_test_allocator_t ta;
+ pai_test_allocator_init(&ta);
+ sec_t sec;
+ /* See the note above -- we can't use the real tsd. */
+ tsdn_t *tsdn = TSDN_NULL;
+
+ enum { NALLOCS = 11 };
+ edata_t *allocs[NALLOCS];
+ bool deferred_work_generated = false;
+ test_sec_init(&sec, &ta.pai, /* nshards */ 1, /* max_alloc */ PAGE,
+ /* max_bytes */ NALLOCS * PAGE);
+ for (int i = 0; i < NALLOCS; i++) {
+ allocs[i] = pai_alloc(tsdn, &sec.pai, PAGE, PAGE,
+ /* zero */ false, /* guarded */ false, /* frequent_reuse */
+ false, &deferred_work_generated);
+ expect_ptr_not_null(allocs[i], "Unexpected alloc failure");
+ }
+ /* Free all but the last aloc. */
+ for (int i = 0; i < NALLOCS - 1; i++) {
+ pai_dalloc(tsdn, &sec.pai, allocs[i], &deferred_work_generated);
+ }
+ size_t max_allocs = ta.alloc_count + ta.alloc_batch_count;
+
+ expect_zu_le(NALLOCS, max_allocs, "Incorrect number of allocations");
+ expect_zu_eq(0, ta.dalloc_count,
+ "Incorrect number of allocations");
+
+ if (is_disable) {
+ sec_disable(tsdn, &sec);
+ } else {
+ sec_flush(tsdn, &sec);
+ }
+
+ expect_zu_eq(max_allocs, ta.alloc_count + ta.alloc_batch_count,
+ "Incorrect number of allocations");
+ expect_zu_eq(0, ta.dalloc_count,
+ "Incorrect number of (non-batch) deallocations");
+ expect_zu_le(NALLOCS - 1, ta.dalloc_batch_count,
+ "Incorrect number of batch deallocations");
+ size_t old_dalloc_batch_count = ta.dalloc_batch_count;
+
+ /*
+ * If we free into a disabled SEC, it should forward to the fallback.
+ * Otherwise, the SEC should accept the allocation.
+ */
+ pai_dalloc(tsdn, &sec.pai, allocs[NALLOCS - 1],
+ &deferred_work_generated);
+
+ expect_zu_eq(max_allocs, ta.alloc_count + ta.alloc_batch_count,
+ "Incorrect number of allocations");
+ expect_zu_eq(is_disable ? 1 : 0, ta.dalloc_count,
+ "Incorrect number of (non-batch) deallocations");
+ expect_zu_eq(old_dalloc_batch_count, ta.dalloc_batch_count,
+ "Incorrect number of batch deallocations");
+}
+
+TEST_BEGIN(test_disable) {
+ do_disable_flush_test(/* is_disable */ true);
+}
+TEST_END
+
+TEST_BEGIN(test_flush) {
+ do_disable_flush_test(/* is_disable */ false);
+}
+TEST_END
+
+TEST_BEGIN(test_max_alloc_respected) {
+ pai_test_allocator_t ta;
+ pai_test_allocator_init(&ta);
+ sec_t sec;
+ /* See the note above -- we can't use the real tsd. */
+ tsdn_t *tsdn = TSDN_NULL;
+
+ size_t max_alloc = 2 * PAGE;
+ size_t attempted_alloc = 3 * PAGE;
+
+ bool deferred_work_generated = false;
+
+ test_sec_init(&sec, &ta.pai, /* nshards */ 1, max_alloc,
+ /* max_bytes */ 1000 * PAGE);
+
+ for (size_t i = 0; i < 100; i++) {
+ expect_zu_eq(i, ta.alloc_count,
+ "Incorrect number of allocations");
+ expect_zu_eq(i, ta.dalloc_count,
+ "Incorrect number of deallocations");
+ edata_t *edata = pai_alloc(tsdn, &sec.pai, attempted_alloc,
+ PAGE, /* zero */ false, /* guarded */ false,
+ /* frequent_reuse */ false, &deferred_work_generated);
+ expect_ptr_not_null(edata, "Unexpected alloc failure");
+ expect_zu_eq(i + 1, ta.alloc_count,
+ "Incorrect number of allocations");
+ expect_zu_eq(i, ta.dalloc_count,
+ "Incorrect number of deallocations");
+ pai_dalloc(tsdn, &sec.pai, edata, &deferred_work_generated);
+ }
+}
+TEST_END
+
+TEST_BEGIN(test_expand_shrink_delegate) {
+ /*
+ * Expand and shrink shouldn't affect sec state; they should just
+ * delegate to the fallback PAI.
+ */
+ pai_test_allocator_t ta;
+ pai_test_allocator_init(&ta);
+ sec_t sec;
+ /* See the note above -- we can't use the real tsd. */
+ tsdn_t *tsdn = TSDN_NULL;
+
+ bool deferred_work_generated = false;
+
+ test_sec_init(&sec, &ta.pai, /* nshards */ 1, /* max_alloc */ 10 * PAGE,
+ /* max_bytes */ 1000 * PAGE);
+ edata_t *edata = pai_alloc(tsdn, &sec.pai, PAGE, PAGE,
+ /* zero */ false, /* guarded */ false, /* frequent_reuse */ false,
+ &deferred_work_generated);
+ expect_ptr_not_null(edata, "Unexpected alloc failure");
+
+ bool err = pai_expand(tsdn, &sec.pai, edata, PAGE, 4 * PAGE,
+ /* zero */ false, &deferred_work_generated);
+ expect_false(err, "Unexpected expand failure");
+ expect_zu_eq(1, ta.expand_count, "");
+ ta.expand_return_value = true;
+ err = pai_expand(tsdn, &sec.pai, edata, 4 * PAGE, 3 * PAGE,
+ /* zero */ false, &deferred_work_generated);
+ expect_true(err, "Unexpected expand success");
+ expect_zu_eq(2, ta.expand_count, "");
+
+ err = pai_shrink(tsdn, &sec.pai, edata, 4 * PAGE, 2 * PAGE,
+ &deferred_work_generated);
+ expect_false(err, "Unexpected shrink failure");
+ expect_zu_eq(1, ta.shrink_count, "");
+ ta.shrink_return_value = true;
+ err = pai_shrink(tsdn, &sec.pai, edata, 2 * PAGE, PAGE,
+ &deferred_work_generated);
+ expect_true(err, "Unexpected shrink success");
+ expect_zu_eq(2, ta.shrink_count, "");
+}
+TEST_END
+
+TEST_BEGIN(test_nshards_0) {
+ pai_test_allocator_t ta;
+ pai_test_allocator_init(&ta);
+ sec_t sec;
+ /* See the note above -- we can't use the real tsd. */
+ tsdn_t *tsdn = TSDN_NULL;
+ base_t *base = base_new(TSDN_NULL, /* ind */ 123,
+ &ehooks_default_extent_hooks, /* metadata_use_hooks */ true);
+
+ sec_opts_t opts = SEC_OPTS_DEFAULT;
+ opts.nshards = 0;
+ sec_init(TSDN_NULL, &sec, base, &ta.pai, &opts);
+
+ bool deferred_work_generated = false;
+ edata_t *edata = pai_alloc(tsdn, &sec.pai, PAGE, PAGE,
+ /* zero */ false, /* guarded */ false, /* frequent_reuse */ false,
+ &deferred_work_generated);
+ pai_dalloc(tsdn, &sec.pai, edata, &deferred_work_generated);
+
+ /* Both operations should have gone directly to the fallback. */
+ expect_zu_eq(1, ta.alloc_count, "");
+ expect_zu_eq(1, ta.dalloc_count, "");
+}
+TEST_END
+
+static void
+expect_stats_pages(tsdn_t *tsdn, sec_t *sec, size_t npages) {
+ sec_stats_t stats;
+ /*
+ * Check that the stats merging accumulates rather than overwrites by
+ * putting some (made up) data there to begin with.
+ */
+ stats.bytes = 123;
+ sec_stats_merge(tsdn, sec, &stats);
+ assert_zu_le(npages * PAGE + 123, stats.bytes, "");
+}
+
+TEST_BEGIN(test_stats_simple) {
+ pai_test_allocator_t ta;
+ pai_test_allocator_init(&ta);
+ sec_t sec;
+
+ /* See the note above -- we can't use the real tsd. */
+ tsdn_t *tsdn = TSDN_NULL;
+
+ enum {
+ NITERS = 100,
+ FLUSH_PAGES = 20,
+ };
+
+ bool deferred_work_generated = false;
+
+ test_sec_init(&sec, &ta.pai, /* nshards */ 1, /* max_alloc */ PAGE,
+ /* max_bytes */ FLUSH_PAGES * PAGE);
+
+ edata_t *allocs[FLUSH_PAGES];
+ for (size_t i = 0; i < FLUSH_PAGES; i++) {
+ allocs[i] = pai_alloc(tsdn, &sec.pai, PAGE, PAGE,
+ /* zero */ false, /* guarded */ false, /* frequent_reuse */
+ false, &deferred_work_generated);
+ expect_stats_pages(tsdn, &sec, 0);
+ }
+
+ /* Increase and decrease, without flushing. */
+ for (size_t i = 0; i < NITERS; i++) {
+ for (size_t j = 0; j < FLUSH_PAGES / 2; j++) {
+ pai_dalloc(tsdn, &sec.pai, allocs[j],
+ &deferred_work_generated);
+ expect_stats_pages(tsdn, &sec, j + 1);
+ }
+ for (size_t j = 0; j < FLUSH_PAGES / 2; j++) {
+ allocs[j] = pai_alloc(tsdn, &sec.pai, PAGE, PAGE,
+ /* zero */ false, /* guarded */ false,
+ /* frequent_reuse */ false,
+ &deferred_work_generated);
+ expect_stats_pages(tsdn, &sec, FLUSH_PAGES / 2 - j - 1);
+ }
+ }
+}
+TEST_END
+
+TEST_BEGIN(test_stats_auto_flush) {
+ pai_test_allocator_t ta;
+ pai_test_allocator_init(&ta);
+ sec_t sec;
+
+ /* See the note above -- we can't use the real tsd. */
+ tsdn_t *tsdn = TSDN_NULL;
+
+ enum {
+ FLUSH_PAGES = 10,
+ };
+
+ test_sec_init(&sec, &ta.pai, /* nshards */ 1, /* max_alloc */ PAGE,
+ /* max_bytes */ FLUSH_PAGES * PAGE);
+
+ edata_t *extra_alloc0;
+ edata_t *extra_alloc1;
+ edata_t *allocs[2 * FLUSH_PAGES];
+
+ bool deferred_work_generated = false;
+
+ extra_alloc0 = pai_alloc(tsdn, &sec.pai, PAGE, PAGE, /* zero */ false,
+ /* guarded */ false, /* frequent_reuse */ false,
+ &deferred_work_generated);
+ extra_alloc1 = pai_alloc(tsdn, &sec.pai, PAGE, PAGE, /* zero */ false,
+ /* guarded */ false, /* frequent_reuse */ false,
+ &deferred_work_generated);
+
+ for (size_t i = 0; i < 2 * FLUSH_PAGES; i++) {
+ allocs[i] = pai_alloc(tsdn, &sec.pai, PAGE, PAGE,
+ /* zero */ false, /* guarded */ false, /* frequent_reuse */
+ false, &deferred_work_generated);
+ }
+
+ for (size_t i = 0; i < FLUSH_PAGES; i++) {
+ pai_dalloc(tsdn, &sec.pai, allocs[i], &deferred_work_generated);
+ }
+ pai_dalloc(tsdn, &sec.pai, extra_alloc0, &deferred_work_generated);
+
+ /* Flush the remaining pages; stats should still work. */
+ for (size_t i = 0; i < FLUSH_PAGES; i++) {
+ pai_dalloc(tsdn, &sec.pai, allocs[FLUSH_PAGES + i],
+ &deferred_work_generated);
+ }
+
+ pai_dalloc(tsdn, &sec.pai, extra_alloc1, &deferred_work_generated);
+
+ expect_stats_pages(tsdn, &sec, ta.alloc_count + ta.alloc_batch_count
+ - ta.dalloc_count - ta.dalloc_batch_count);
+}
+TEST_END
+
+TEST_BEGIN(test_stats_manual_flush) {
+ pai_test_allocator_t ta;
+ pai_test_allocator_init(&ta);
+ sec_t sec;
+
+ /* See the note above -- we can't use the real tsd. */
+ tsdn_t *tsdn = TSDN_NULL;
+
+ enum {
+ FLUSH_PAGES = 10,
+ };
+
+ test_sec_init(&sec, &ta.pai, /* nshards */ 1, /* max_alloc */ PAGE,
+ /* max_bytes */ FLUSH_PAGES * PAGE);
+
+ bool deferred_work_generated = false;
+ edata_t *allocs[FLUSH_PAGES];
+ for (size_t i = 0; i < FLUSH_PAGES; i++) {
+ allocs[i] = pai_alloc(tsdn, &sec.pai, PAGE, PAGE,
+ /* zero */ false, /* guarded */ false, /* frequent_reuse */
+ false, &deferred_work_generated);
+ expect_stats_pages(tsdn, &sec, 0);
+ }
+
+ /* Dalloc the first half of the allocations. */
+ for (size_t i = 0; i < FLUSH_PAGES / 2; i++) {
+ pai_dalloc(tsdn, &sec.pai, allocs[i], &deferred_work_generated);
+ expect_stats_pages(tsdn, &sec, i + 1);
+ }
+
+ sec_flush(tsdn, &sec);
+ expect_stats_pages(tsdn, &sec, 0);
+
+ /* Flush the remaining pages. */
+ for (size_t i = 0; i < FLUSH_PAGES / 2; i++) {
+ pai_dalloc(tsdn, &sec.pai, allocs[FLUSH_PAGES / 2 + i],
+ &deferred_work_generated);
+ expect_stats_pages(tsdn, &sec, i + 1);
+ }
+ sec_disable(tsdn, &sec);
+ expect_stats_pages(tsdn, &sec, 0);
+}
+TEST_END
+
+int
+main(void) {
+ return test(
+ test_reuse,
+ test_auto_flush,
+ test_disable,
+ test_flush,
+ test_max_alloc_respected,
+ test_expand_shrink_delegate,
+ test_nshards_0,
+ test_stats_simple,
+ test_stats_auto_flush,
+ test_stats_manual_flush);
+}
diff --git a/deps/jemalloc/test/unit/seq.c b/deps/jemalloc/test/unit/seq.c
index 19613b0b2..06ed68345 100644
--- a/deps/jemalloc/test/unit/seq.c
+++ b/deps/jemalloc/test/unit/seq.c
@@ -15,10 +15,10 @@ set_data(data_t *data, int num) {
}
static void
-assert_data(data_t *data) {
+expect_data(data_t *data) {
int num = data->arr[0];
for (int i = 0; i < 10; i++) {
- assert_d_eq(num, data->arr[i], "Data consistency error");
+ expect_d_eq(num, data->arr[i], "Data consistency error");
}
}
@@ -37,8 +37,8 @@ seq_reader_thd(void *arg) {
while (iter < 1000 * 1000 - 1) {
bool success = seq_try_load_data(&local_data, &thd_data->data);
if (success) {
- assert_data(&local_data);
- assert_d_le(iter, local_data.arr[0],
+ expect_data(&local_data);
+ expect_d_le(iter, local_data.arr[0],
"Seq read went back in time.");
iter = local_data.arr[0];
}
@@ -82,8 +82,8 @@ TEST_BEGIN(test_seq_simple) {
seq_store_data(&seq, &data);
set_data(&data, 0);
bool success = seq_try_load_data(&data, &seq);
- assert_b_eq(success, true, "Failed non-racing read");
- assert_data(&data);
+ expect_b_eq(success, true, "Failed non-racing read");
+ expect_data(&data);
}
}
TEST_END
diff --git a/deps/jemalloc/test/unit/size_check.c b/deps/jemalloc/test/unit/size_check.c
new file mode 100644
index 000000000..accdc405b
--- /dev/null
+++ b/deps/jemalloc/test/unit/size_check.c
@@ -0,0 +1,79 @@
+#include "test/jemalloc_test.h"
+
+#include "jemalloc/internal/safety_check.h"
+
+bool fake_abort_called;
+void fake_abort(const char *message) {
+ (void)message;
+ fake_abort_called = true;
+}
+
+#define SMALL_SIZE1 SC_SMALL_MAXCLASS
+#define SMALL_SIZE2 (SC_SMALL_MAXCLASS / 2)
+
+#define LARGE_SIZE1 SC_LARGE_MINCLASS
+#define LARGE_SIZE2 (LARGE_SIZE1 * 2)
+
+void *
+test_invalid_size_pre(size_t sz) {
+ safety_check_set_abort(&fake_abort);
+
+ fake_abort_called = false;
+ void *ptr = malloc(sz);
+ assert_ptr_not_null(ptr, "Unexpected failure");
+
+ return ptr;
+}
+
+void
+test_invalid_size_post(void) {
+ expect_true(fake_abort_called, "Safety check didn't fire");
+ safety_check_set_abort(NULL);
+}
+
+TEST_BEGIN(test_invalid_size_sdallocx) {
+ test_skip_if(!config_opt_size_checks);
+
+ void *ptr = test_invalid_size_pre(SMALL_SIZE1);
+ sdallocx(ptr, SMALL_SIZE2, 0);
+ test_invalid_size_post();
+
+ ptr = test_invalid_size_pre(LARGE_SIZE1);
+ sdallocx(ptr, LARGE_SIZE2, 0);
+ test_invalid_size_post();
+}
+TEST_END
+
+TEST_BEGIN(test_invalid_size_sdallocx_nonzero_flag) {
+ test_skip_if(!config_opt_size_checks);
+
+ void *ptr = test_invalid_size_pre(SMALL_SIZE1);
+ sdallocx(ptr, SMALL_SIZE2, MALLOCX_TCACHE_NONE);
+ test_invalid_size_post();
+
+ ptr = test_invalid_size_pre(LARGE_SIZE1);
+ sdallocx(ptr, LARGE_SIZE2, MALLOCX_TCACHE_NONE);
+ test_invalid_size_post();
+}
+TEST_END
+
+TEST_BEGIN(test_invalid_size_sdallocx_noflags) {
+ test_skip_if(!config_opt_size_checks);
+
+ void *ptr = test_invalid_size_pre(SMALL_SIZE1);
+ je_sdallocx_noflags(ptr, SMALL_SIZE2);
+ test_invalid_size_post();
+
+ ptr = test_invalid_size_pre(LARGE_SIZE1);
+ je_sdallocx_noflags(ptr, LARGE_SIZE2);
+ test_invalid_size_post();
+}
+TEST_END
+
+int
+main(void) {
+ return test(
+ test_invalid_size_sdallocx,
+ test_invalid_size_sdallocx_nonzero_flag,
+ test_invalid_size_sdallocx_noflags);
+}
diff --git a/deps/jemalloc/test/unit/size_check.sh b/deps/jemalloc/test/unit/size_check.sh
new file mode 100644
index 000000000..352d11076
--- /dev/null
+++ b/deps/jemalloc/test/unit/size_check.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+if [ "x${enable_prof}" = "x1" ] ; then
+ export MALLOC_CONF="prof:false"
+fi
diff --git a/deps/jemalloc/test/unit/size_classes.c b/deps/jemalloc/test/unit/size_classes.c
index 694733635..c70eb592d 100644
--- a/deps/jemalloc/test/unit/size_classes.c
+++ b/deps/jemalloc/test/unit/size_classes.c
@@ -7,16 +7,16 @@ get_max_size_class(void) {
size_t sz, miblen, max_size_class;
sz = sizeof(unsigned);
- assert_d_eq(mallctl("arenas.nlextents", (void *)&nlextents, &sz, NULL,
+ expect_d_eq(mallctl("arenas.nlextents", (void *)&nlextents, &sz, NULL,
0), 0, "Unexpected mallctl() error");
miblen = sizeof(mib) / sizeof(size_t);
- assert_d_eq(mallctlnametomib("arenas.lextent.0.size", mib, &miblen), 0,
+ expect_d_eq(mallctlnametomib("arenas.lextent.0.size", mib, &miblen), 0,
"Unexpected mallctlnametomib() error");
mib[2] = nlextents - 1;
sz = sizeof(size_t);
- assert_d_eq(mallctlbymib(mib, miblen, (void *)&max_size_class, &sz,
+ expect_d_eq(mallctlbymib(mib, miblen, (void *)&max_size_class, &sz,
NULL, 0), 0, "Unexpected mallctlbymib() error");
return max_size_class;
@@ -32,50 +32,50 @@ TEST_BEGIN(test_size_classes) {
for (index = 0, size_class = sz_index2size(index); index < max_index ||
size_class < max_size_class; index++, size_class =
sz_index2size(index)) {
- assert_true(index < max_index,
+ expect_true(index < max_index,
"Loop conditionals should be equivalent; index=%u, "
"size_class=%zu (%#zx)", index, size_class, size_class);
- assert_true(size_class < max_size_class,
+ expect_true(size_class < max_size_class,
"Loop conditionals should be equivalent; index=%u, "
"size_class=%zu (%#zx)", index, size_class, size_class);
- assert_u_eq(index, sz_size2index(size_class),
+ expect_u_eq(index, sz_size2index(size_class),
"sz_size2index() does not reverse sz_index2size(): index=%u"
" --> size_class=%zu --> index=%u --> size_class=%zu",
index, size_class, sz_size2index(size_class),
sz_index2size(sz_size2index(size_class)));
- assert_zu_eq(size_class,
+ expect_zu_eq(size_class,
sz_index2size(sz_size2index(size_class)),
"sz_index2size() does not reverse sz_size2index(): index=%u"
" --> size_class=%zu --> index=%u --> size_class=%zu",
index, size_class, sz_size2index(size_class),
sz_index2size(sz_size2index(size_class)));
- assert_u_eq(index+1, sz_size2index(size_class+1),
+ expect_u_eq(index+1, sz_size2index(size_class+1),
"Next size_class does not round up properly");
- assert_zu_eq(size_class, (index > 0) ?
+ expect_zu_eq(size_class, (index > 0) ?
sz_s2u(sz_index2size(index-1)+1) : sz_s2u(1),
"sz_s2u() does not round up to size class");
- assert_zu_eq(size_class, sz_s2u(size_class-1),
+ expect_zu_eq(size_class, sz_s2u(size_class-1),
"sz_s2u() does not round up to size class");
- assert_zu_eq(size_class, sz_s2u(size_class),
+ expect_zu_eq(size_class, sz_s2u(size_class),
"sz_s2u() does not compute same size class");
- assert_zu_eq(sz_s2u(size_class+1), sz_index2size(index+1),
+ expect_zu_eq(sz_s2u(size_class+1), sz_index2size(index+1),
"sz_s2u() does not round up to next size class");
}
- assert_u_eq(index, sz_size2index(sz_index2size(index)),
+ expect_u_eq(index, sz_size2index(sz_index2size(index)),
"sz_size2index() does not reverse sz_index2size()");
- assert_zu_eq(max_size_class, sz_index2size(
+ expect_zu_eq(max_size_class, sz_index2size(
sz_size2index(max_size_class)),
"sz_index2size() does not reverse sz_size2index()");
- assert_zu_eq(size_class, sz_s2u(sz_index2size(index-1)+1),
+ expect_zu_eq(size_class, sz_s2u(sz_index2size(index-1)+1),
"sz_s2u() does not round up to size class");
- assert_zu_eq(size_class, sz_s2u(size_class-1),
+ expect_zu_eq(size_class, sz_s2u(size_class-1),
"sz_s2u() does not round up to size class");
- assert_zu_eq(size_class, sz_s2u(size_class),
+ expect_zu_eq(size_class, sz_s2u(size_class),
"sz_s2u() does not compute same size class");
}
TEST_END
@@ -90,53 +90,53 @@ TEST_BEGIN(test_psize_classes) {
for (pind = 0, size_class = sz_pind2sz(pind);
pind < max_pind || size_class < max_psz;
pind++, size_class = sz_pind2sz(pind)) {
- assert_true(pind < max_pind,
+ expect_true(pind < max_pind,
"Loop conditionals should be equivalent; pind=%u, "
"size_class=%zu (%#zx)", pind, size_class, size_class);
- assert_true(size_class < max_psz,
+ expect_true(size_class < max_psz,
"Loop conditionals should be equivalent; pind=%u, "
"size_class=%zu (%#zx)", pind, size_class, size_class);
- assert_u_eq(pind, sz_psz2ind(size_class),
+ expect_u_eq(pind, sz_psz2ind(size_class),
"sz_psz2ind() does not reverse sz_pind2sz(): pind=%u -->"
" size_class=%zu --> pind=%u --> size_class=%zu", pind,
size_class, sz_psz2ind(size_class),
sz_pind2sz(sz_psz2ind(size_class)));
- assert_zu_eq(size_class, sz_pind2sz(sz_psz2ind(size_class)),
+ expect_zu_eq(size_class, sz_pind2sz(sz_psz2ind(size_class)),
"sz_pind2sz() does not reverse sz_psz2ind(): pind=%u -->"
" size_class=%zu --> pind=%u --> size_class=%zu", pind,
size_class, sz_psz2ind(size_class),
sz_pind2sz(sz_psz2ind(size_class)));
if (size_class == SC_LARGE_MAXCLASS) {
- assert_u_eq(SC_NPSIZES, sz_psz2ind(size_class + 1),
+ expect_u_eq(SC_NPSIZES, sz_psz2ind(size_class + 1),
"Next size_class does not round up properly");
} else {
- assert_u_eq(pind + 1, sz_psz2ind(size_class + 1),
+ expect_u_eq(pind + 1, sz_psz2ind(size_class + 1),
"Next size_class does not round up properly");
}
- assert_zu_eq(size_class, (pind > 0) ?
+ expect_zu_eq(size_class, (pind > 0) ?
sz_psz2u(sz_pind2sz(pind-1)+1) : sz_psz2u(1),
"sz_psz2u() does not round up to size class");
- assert_zu_eq(size_class, sz_psz2u(size_class-1),
+ expect_zu_eq(size_class, sz_psz2u(size_class-1),
"sz_psz2u() does not round up to size class");
- assert_zu_eq(size_class, sz_psz2u(size_class),
+ expect_zu_eq(size_class, sz_psz2u(size_class),
"sz_psz2u() does not compute same size class");
- assert_zu_eq(sz_psz2u(size_class+1), sz_pind2sz(pind+1),
+ expect_zu_eq(sz_psz2u(size_class+1), sz_pind2sz(pind+1),
"sz_psz2u() does not round up to next size class");
}
- assert_u_eq(pind, sz_psz2ind(sz_pind2sz(pind)),
+ expect_u_eq(pind, sz_psz2ind(sz_pind2sz(pind)),
"sz_psz2ind() does not reverse sz_pind2sz()");
- assert_zu_eq(max_psz, sz_pind2sz(sz_psz2ind(max_psz)),
+ expect_zu_eq(max_psz, sz_pind2sz(sz_psz2ind(max_psz)),
"sz_pind2sz() does not reverse sz_psz2ind()");
- assert_zu_eq(size_class, sz_psz2u(sz_pind2sz(pind-1)+1),
+ expect_zu_eq(size_class, sz_psz2u(sz_pind2sz(pind-1)+1),
"sz_psz2u() does not round up to size class");
- assert_zu_eq(size_class, sz_psz2u(size_class-1),
+ expect_zu_eq(size_class, sz_psz2u(size_class-1),
"sz_psz2u() does not round up to size class");
- assert_zu_eq(size_class, sz_psz2u(size_class),
+ expect_zu_eq(size_class, sz_psz2u(size_class),
"sz_psz2u() does not compute same size class");
}
TEST_END
@@ -147,34 +147,34 @@ TEST_BEGIN(test_overflow) {
max_size_class = get_max_size_class();
max_psz = max_size_class + PAGE;
- assert_u_eq(sz_size2index(max_size_class+1), SC_NSIZES,
+ expect_u_eq(sz_size2index(max_size_class+1), SC_NSIZES,
"sz_size2index() should return NSIZES on overflow");
- assert_u_eq(sz_size2index(ZU(PTRDIFF_MAX)+1), SC_NSIZES,
+ expect_u_eq(sz_size2index(ZU(PTRDIFF_MAX)+1), SC_NSIZES,
"sz_size2index() should return NSIZES on overflow");
- assert_u_eq(sz_size2index(SIZE_T_MAX), SC_NSIZES,
+ expect_u_eq(sz_size2index(SIZE_T_MAX), SC_NSIZES,
"sz_size2index() should return NSIZES on overflow");
- assert_zu_eq(sz_s2u(max_size_class+1), 0,
+ expect_zu_eq(sz_s2u(max_size_class+1), 0,
"sz_s2u() should return 0 for unsupported size");
- assert_zu_eq(sz_s2u(ZU(PTRDIFF_MAX)+1), 0,
+ expect_zu_eq(sz_s2u(ZU(PTRDIFF_MAX)+1), 0,
"sz_s2u() should return 0 for unsupported size");
- assert_zu_eq(sz_s2u(SIZE_T_MAX), 0,
+ expect_zu_eq(sz_s2u(SIZE_T_MAX), 0,
"sz_s2u() should return 0 on overflow");
- assert_u_eq(sz_psz2ind(max_size_class+1), SC_NPSIZES,
+ expect_u_eq(sz_psz2ind(max_size_class+1), SC_NPSIZES,
"sz_psz2ind() should return NPSIZES on overflow");
- assert_u_eq(sz_psz2ind(ZU(PTRDIFF_MAX)+1), SC_NPSIZES,
+ expect_u_eq(sz_psz2ind(ZU(PTRDIFF_MAX)+1), SC_NPSIZES,
"sz_psz2ind() should return NPSIZES on overflow");
- assert_u_eq(sz_psz2ind(SIZE_T_MAX), SC_NPSIZES,
+ expect_u_eq(sz_psz2ind(SIZE_T_MAX), SC_NPSIZES,
"sz_psz2ind() should return NPSIZES on overflow");
- assert_zu_eq(sz_psz2u(max_size_class+1), max_psz,
+ expect_zu_eq(sz_psz2u(max_size_class+1), max_psz,
"sz_psz2u() should return (LARGE_MAXCLASS + PAGE) for unsupported"
" size");
- assert_zu_eq(sz_psz2u(ZU(PTRDIFF_MAX)+1), max_psz,
+ expect_zu_eq(sz_psz2u(ZU(PTRDIFF_MAX)+1), max_psz,
"sz_psz2u() should return (LARGE_MAXCLASS + PAGE) for unsupported "
"size");
- assert_zu_eq(sz_psz2u(SIZE_T_MAX), max_psz,
+ expect_zu_eq(sz_psz2u(SIZE_T_MAX), max_psz,
"sz_psz2u() should return (LARGE_MAXCLASS + PAGE) on overflow");
}
TEST_END
diff --git a/deps/jemalloc/test/unit/slab.c b/deps/jemalloc/test/unit/slab.c
index c56af25fe..70fc5c7d1 100644
--- a/deps/jemalloc/test/unit/slab.c
+++ b/deps/jemalloc/test/unit/slab.c
@@ -1,27 +1,33 @@
#include "test/jemalloc_test.h"
+#define INVALID_ARENA_IND ((1U << MALLOCX_ARENA_BITS) - 1)
+
TEST_BEGIN(test_arena_slab_regind) {
szind_t binind;
for (binind = 0; binind < SC_NBINS; binind++) {
size_t regind;
- extent_t slab;
+ edata_t slab;
const bin_info_t *bin_info = &bin_infos[binind];
- extent_init(&slab, NULL, mallocx(bin_info->slab_size,
- MALLOCX_LG_ALIGN(LG_PAGE)), bin_info->slab_size, true,
- binind, 0, extent_state_active, false, true, true,
+ edata_init(&slab, INVALID_ARENA_IND,
+ mallocx(bin_info->slab_size, MALLOCX_LG_ALIGN(LG_PAGE)),
+ bin_info->slab_size, true,
+ binind, 0, extent_state_active, false, true, EXTENT_PAI_PAC,
EXTENT_NOT_HEAD);
- assert_ptr_not_null(extent_addr_get(&slab),
+ expect_ptr_not_null(edata_addr_get(&slab),
"Unexpected malloc() failure");
+ arena_dalloc_bin_locked_info_t dalloc_info;
+ arena_dalloc_bin_locked_begin(&dalloc_info, binind);
for (regind = 0; regind < bin_info->nregs; regind++) {
- void *reg = (void *)((uintptr_t)extent_addr_get(&slab) +
+ void *reg = (void *)((uintptr_t)edata_addr_get(&slab) +
(bin_info->reg_size * regind));
- assert_zu_eq(arena_slab_regind(&slab, binind, reg),
+ expect_zu_eq(arena_slab_regind(&dalloc_info, binind,
+ &slab, reg),
regind,
"Incorrect region index computed for size %zu",
bin_info->reg_size);
}
- free(extent_addr_get(&slab));
+ free(edata_addr_get(&slab));
}
}
TEST_END
diff --git a/deps/jemalloc/test/unit/smoothstep.c b/deps/jemalloc/test/unit/smoothstep.c
index 7c5dbb7e0..588c9f44e 100644
--- a/deps/jemalloc/test/unit/smoothstep.c
+++ b/deps/jemalloc/test/unit/smoothstep.c
@@ -26,9 +26,9 @@ TEST_BEGIN(test_smoothstep_integral) {
max = (KQU(1) << (SMOOTHSTEP_BFP-1)) * (SMOOTHSTEP_NSTEPS+1);
min = max - SMOOTHSTEP_NSTEPS;
- assert_u64_ge(sum, min,
+ expect_u64_ge(sum, min,
"Integral too small, even accounting for truncation");
- assert_u64_le(sum, max, "Integral exceeds 1/2");
+ expect_u64_le(sum, max, "Integral exceeds 1/2");
if (false) {
malloc_printf("%"FMTu64" ulps under 1/2 (limit %d)\n",
max - sum, SMOOTHSTEP_NSTEPS);
@@ -49,10 +49,10 @@ TEST_BEGIN(test_smoothstep_monotonic) {
prev_h = 0;
for (i = 0; i < SMOOTHSTEP_NSTEPS; i++) {
uint64_t h = smoothstep_tab[i];
- assert_u64_ge(h, prev_h, "Piecewise non-monotonic, i=%u", i);
+ expect_u64_ge(h, prev_h, "Piecewise non-monotonic, i=%u", i);
prev_h = h;
}
- assert_u64_eq(smoothstep_tab[SMOOTHSTEP_NSTEPS-1],
+ expect_u64_eq(smoothstep_tab[SMOOTHSTEP_NSTEPS-1],
(KQU(1) << SMOOTHSTEP_BFP), "Last step must equal 1");
}
TEST_END
@@ -72,7 +72,7 @@ TEST_BEGIN(test_smoothstep_slope) {
for (i = 0; i < SMOOTHSTEP_NSTEPS / 2 + SMOOTHSTEP_NSTEPS % 2; i++) {
uint64_t h = smoothstep_tab[i];
uint64_t delta = h - prev_h;
- assert_u64_ge(delta, prev_delta,
+ expect_u64_ge(delta, prev_delta,
"Slope must monotonically increase in 0.0 <= x <= 0.5, "
"i=%u", i);
prev_h = h;
@@ -84,7 +84,7 @@ TEST_BEGIN(test_smoothstep_slope) {
for (i = SMOOTHSTEP_NSTEPS-1; i >= SMOOTHSTEP_NSTEPS / 2; i--) {
uint64_t h = smoothstep_tab[i];
uint64_t delta = prev_h - h;
- assert_u64_ge(delta, prev_delta,
+ expect_u64_ge(delta, prev_delta,
"Slope must monotonically decrease in 0.5 <= x <= 1.0, "
"i=%u", i);
prev_h = h;
diff --git a/deps/jemalloc/test/unit/stats.c b/deps/jemalloc/test/unit/stats.c
index 646768e88..bbdbd1809 100644
--- a/deps/jemalloc/test/unit/stats.c
+++ b/deps/jemalloc/test/unit/stats.c
@@ -1,25 +1,28 @@
#include "test/jemalloc_test.h"
+#define STRINGIFY_HELPER(x) #x
+#define STRINGIFY(x) STRINGIFY_HELPER(x)
+
TEST_BEGIN(test_stats_summary) {
size_t sz, allocated, active, resident, mapped;
int expected = config_stats ? 0 : ENOENT;
sz = sizeof(size_t);
- assert_d_eq(mallctl("stats.allocated", (void *)&allocated, &sz, NULL,
+ expect_d_eq(mallctl("stats.allocated", (void *)&allocated, &sz, NULL,
0), expected, "Unexpected mallctl() result");
- assert_d_eq(mallctl("stats.active", (void *)&active, &sz, NULL, 0),
+ expect_d_eq(mallctl("stats.active", (void *)&active, &sz, NULL, 0),
expected, "Unexpected mallctl() result");
- assert_d_eq(mallctl("stats.resident", (void *)&resident, &sz, NULL, 0),
+ expect_d_eq(mallctl("stats.resident", (void *)&resident, &sz, NULL, 0),
expected, "Unexpected mallctl() result");
- assert_d_eq(mallctl("stats.mapped", (void *)&mapped, &sz, NULL, 0),
+ expect_d_eq(mallctl("stats.mapped", (void *)&mapped, &sz, NULL, 0),
expected, "Unexpected mallctl() result");
if (config_stats) {
- assert_zu_le(allocated, active,
+ expect_zu_le(allocated, active,
"allocated should be no larger than active");
- assert_zu_lt(active, resident,
+ expect_zu_lt(active, resident,
"active should be less than resident");
- assert_zu_lt(active, mapped,
+ expect_zu_lt(active, mapped,
"active should be less than mapped");
}
}
@@ -34,30 +37,30 @@ TEST_BEGIN(test_stats_large) {
int expected = config_stats ? 0 : ENOENT;
p = mallocx(SC_SMALL_MAXCLASS + 1, MALLOCX_ARENA(0));
- assert_ptr_not_null(p, "Unexpected mallocx() failure");
+ expect_ptr_not_null(p, "Unexpected mallocx() failure");
- assert_d_eq(mallctl("epoch", NULL, NULL, (void *)&epoch, sizeof(epoch)),
+ expect_d_eq(mallctl("epoch", NULL, NULL, (void *)&epoch, sizeof(epoch)),
0, "Unexpected mallctl() failure");
sz = sizeof(size_t);
- assert_d_eq(mallctl("stats.arenas.0.large.allocated",
+ expect_d_eq(mallctl("stats.arenas.0.large.allocated",
(void *)&allocated, &sz, NULL, 0), expected,
"Unexpected mallctl() result");
sz = sizeof(uint64_t);
- assert_d_eq(mallctl("stats.arenas.0.large.nmalloc", (void *)&nmalloc,
+ expect_d_eq(mallctl("stats.arenas.0.large.nmalloc", (void *)&nmalloc,
&sz, NULL, 0), expected, "Unexpected mallctl() result");
- assert_d_eq(mallctl("stats.arenas.0.large.ndalloc", (void *)&ndalloc,
+ expect_d_eq(mallctl("stats.arenas.0.large.ndalloc", (void *)&ndalloc,
&sz, NULL, 0), expected, "Unexpected mallctl() result");
- assert_d_eq(mallctl("stats.arenas.0.large.nrequests",
+ expect_d_eq(mallctl("stats.arenas.0.large.nrequests",
(void *)&nrequests, &sz, NULL, 0), expected,
"Unexpected mallctl() result");
if (config_stats) {
- assert_zu_gt(allocated, 0,
+ expect_zu_gt(allocated, 0,
"allocated should be greater than zero");
- assert_u64_ge(nmalloc, ndalloc,
+ expect_u64_ge(nmalloc, ndalloc,
"nmalloc should be at least as large as ndalloc");
- assert_u64_le(nmalloc, nrequests,
+ expect_u64_le(nmalloc, nrequests,
"nmalloc should no larger than nrequests");
}
@@ -75,54 +78,54 @@ TEST_BEGIN(test_stats_arenas_summary) {
uint64_t muzzy_npurge, muzzy_nmadvise, muzzy_purged;
little = mallocx(SC_SMALL_MAXCLASS, MALLOCX_ARENA(0));
- assert_ptr_not_null(little, "Unexpected mallocx() failure");
+ expect_ptr_not_null(little, "Unexpected mallocx() failure");
large = mallocx((1U << SC_LG_LARGE_MINCLASS),
MALLOCX_ARENA(0));
- assert_ptr_not_null(large, "Unexpected mallocx() failure");
+ expect_ptr_not_null(large, "Unexpected mallocx() failure");
dallocx(little, 0);
dallocx(large, 0);
- assert_d_eq(mallctl("thread.tcache.flush", NULL, NULL, NULL, 0),
+ expect_d_eq(mallctl("thread.tcache.flush", NULL, NULL, NULL, 0),
opt_tcache ? 0 : EFAULT, "Unexpected mallctl() result");
- assert_d_eq(mallctl("arena.0.purge", NULL, NULL, NULL, 0), 0,
+ expect_d_eq(mallctl("arena.0.purge", NULL, NULL, NULL, 0), 0,
"Unexpected mallctl() failure");
- assert_d_eq(mallctl("epoch", NULL, NULL, (void *)&epoch, sizeof(epoch)),
+ expect_d_eq(mallctl("epoch", NULL, NULL, (void *)&epoch, sizeof(epoch)),
0, "Unexpected mallctl() failure");
sz = sizeof(size_t);
- assert_d_eq(mallctl("stats.arenas.0.mapped", (void *)&mapped, &sz, NULL,
+ expect_d_eq(mallctl("stats.arenas.0.mapped", (void *)&mapped, &sz, NULL,
0), expected, "Unexepected mallctl() result");
sz = sizeof(uint64_t);
- assert_d_eq(mallctl("stats.arenas.0.dirty_npurge",
+ expect_d_eq(mallctl("stats.arenas.0.dirty_npurge",
(void *)&dirty_npurge, &sz, NULL, 0), expected,
"Unexepected mallctl() result");
- assert_d_eq(mallctl("stats.arenas.0.dirty_nmadvise",
+ expect_d_eq(mallctl("stats.arenas.0.dirty_nmadvise",
(void *)&dirty_nmadvise, &sz, NULL, 0), expected,
"Unexepected mallctl() result");
- assert_d_eq(mallctl("stats.arenas.0.dirty_purged",
+ expect_d_eq(mallctl("stats.arenas.0.dirty_purged",
(void *)&dirty_purged, &sz, NULL, 0), expected,
"Unexepected mallctl() result");
- assert_d_eq(mallctl("stats.arenas.0.muzzy_npurge",
+ expect_d_eq(mallctl("stats.arenas.0.muzzy_npurge",
(void *)&muzzy_npurge, &sz, NULL, 0), expected,
"Unexepected mallctl() result");
- assert_d_eq(mallctl("stats.arenas.0.muzzy_nmadvise",
+ expect_d_eq(mallctl("stats.arenas.0.muzzy_nmadvise",
(void *)&muzzy_nmadvise, &sz, NULL, 0), expected,
"Unexepected mallctl() result");
- assert_d_eq(mallctl("stats.arenas.0.muzzy_purged",
+ expect_d_eq(mallctl("stats.arenas.0.muzzy_purged",
(void *)&muzzy_purged, &sz, NULL, 0), expected,
"Unexepected mallctl() result");
if (config_stats) {
- if (!background_thread_enabled()) {
- assert_u64_gt(dirty_npurge + muzzy_npurge, 0,
+ if (!is_background_thread_enabled() && !opt_hpa) {
+ expect_u64_gt(dirty_npurge + muzzy_npurge, 0,
"At least one purge should have occurred");
}
- assert_u64_le(dirty_nmadvise, dirty_purged,
+ expect_u64_le(dirty_nmadvise, dirty_purged,
"dirty_nmadvise should be no greater than dirty_purged");
- assert_u64_le(muzzy_nmadvise, muzzy_purged,
+ expect_u64_le(muzzy_nmadvise, muzzy_purged,
"muzzy_nmadvise should be no greater than muzzy_purged");
}
}
@@ -150,35 +153,35 @@ TEST_BEGIN(test_stats_arenas_small) {
no_lazy_lock(); /* Lazy locking would dodge tcache testing. */
p = mallocx(SC_SMALL_MAXCLASS, MALLOCX_ARENA(0));
- assert_ptr_not_null(p, "Unexpected mallocx() failure");
+ expect_ptr_not_null(p, "Unexpected mallocx() failure");
- assert_d_eq(mallctl("thread.tcache.flush", NULL, NULL, NULL, 0),
+ expect_d_eq(mallctl("thread.tcache.flush", NULL, NULL, NULL, 0),
opt_tcache ? 0 : EFAULT, "Unexpected mallctl() result");
- assert_d_eq(mallctl("epoch", NULL, NULL, (void *)&epoch, sizeof(epoch)),
+ expect_d_eq(mallctl("epoch", NULL, NULL, (void *)&epoch, sizeof(epoch)),
0, "Unexpected mallctl() failure");
sz = sizeof(size_t);
- assert_d_eq(mallctl("stats.arenas.0.small.allocated",
+ expect_d_eq(mallctl("stats.arenas.0.small.allocated",
(void *)&allocated, &sz, NULL, 0), expected,
"Unexpected mallctl() result");
sz = sizeof(uint64_t);
- assert_d_eq(mallctl("stats.arenas.0.small.nmalloc", (void *)&nmalloc,
+ expect_d_eq(mallctl("stats.arenas.0.small.nmalloc", (void *)&nmalloc,
&sz, NULL, 0), expected, "Unexpected mallctl() result");
- assert_d_eq(mallctl("stats.arenas.0.small.ndalloc", (void *)&ndalloc,
+ expect_d_eq(mallctl("stats.arenas.0.small.ndalloc", (void *)&ndalloc,
&sz, NULL, 0), expected, "Unexpected mallctl() result");
- assert_d_eq(mallctl("stats.arenas.0.small.nrequests",
+ expect_d_eq(mallctl("stats.arenas.0.small.nrequests",
(void *)&nrequests, &sz, NULL, 0), expected,
"Unexpected mallctl() result");
if (config_stats) {
- assert_zu_gt(allocated, 0,
+ expect_zu_gt(allocated, 0,
"allocated should be greater than zero");
- assert_u64_gt(nmalloc, 0,
+ expect_u64_gt(nmalloc, 0,
"nmalloc should be no greater than zero");
- assert_u64_ge(nmalloc, ndalloc,
+ expect_u64_ge(nmalloc, ndalloc,
"nmalloc should be at least as large as ndalloc");
- assert_u64_gt(nrequests, 0,
+ expect_u64_gt(nrequests, 0,
"nrequests should be greater than zero");
}
@@ -193,27 +196,27 @@ TEST_BEGIN(test_stats_arenas_large) {
int expected = config_stats ? 0 : ENOENT;
p = mallocx((1U << SC_LG_LARGE_MINCLASS), MALLOCX_ARENA(0));
- assert_ptr_not_null(p, "Unexpected mallocx() failure");
+ expect_ptr_not_null(p, "Unexpected mallocx() failure");
- assert_d_eq(mallctl("epoch", NULL, NULL, (void *)&epoch, sizeof(epoch)),
+ expect_d_eq(mallctl("epoch", NULL, NULL, (void *)&epoch, sizeof(epoch)),
0, "Unexpected mallctl() failure");
sz = sizeof(size_t);
- assert_d_eq(mallctl("stats.arenas.0.large.allocated",
+ expect_d_eq(mallctl("stats.arenas.0.large.allocated",
(void *)&allocated, &sz, NULL, 0), expected,
"Unexpected mallctl() result");
sz = sizeof(uint64_t);
- assert_d_eq(mallctl("stats.arenas.0.large.nmalloc", (void *)&nmalloc,
+ expect_d_eq(mallctl("stats.arenas.0.large.nmalloc", (void *)&nmalloc,
&sz, NULL, 0), expected, "Unexpected mallctl() result");
- assert_d_eq(mallctl("stats.arenas.0.large.ndalloc", (void *)&ndalloc,
+ expect_d_eq(mallctl("stats.arenas.0.large.ndalloc", (void *)&ndalloc,
&sz, NULL, 0), expected, "Unexpected mallctl() result");
if (config_stats) {
- assert_zu_gt(allocated, 0,
+ expect_zu_gt(allocated, 0,
"allocated should be greater than zero");
- assert_u64_gt(nmalloc, 0,
+ expect_u64_gt(nmalloc, 0,
"nmalloc should be greater than zero");
- assert_u64_ge(nmalloc, ndalloc,
+ expect_u64_ge(nmalloc, ndalloc,
"nmalloc should be at least as large as ndalloc");
}
@@ -234,85 +237,85 @@ TEST_BEGIN(test_stats_arenas_bins) {
int expected = config_stats ? 0 : ENOENT;
/* Make sure allocation below isn't satisfied by tcache. */
- assert_d_eq(mallctl("thread.tcache.flush", NULL, NULL, NULL, 0),
+ expect_d_eq(mallctl("thread.tcache.flush", NULL, NULL, NULL, 0),
opt_tcache ? 0 : EFAULT, "Unexpected mallctl() result");
unsigned arena_ind, old_arena_ind;
sz = sizeof(unsigned);
- assert_d_eq(mallctl("arenas.create", (void *)&arena_ind, &sz, NULL, 0),
+ expect_d_eq(mallctl("arenas.create", (void *)&arena_ind, &sz, NULL, 0),
0, "Arena creation failure");
sz = sizeof(arena_ind);
- assert_d_eq(mallctl("thread.arena", (void *)&old_arena_ind, &sz,
+ expect_d_eq(mallctl("thread.arena", (void *)&old_arena_ind, &sz,
(void *)&arena_ind, sizeof(arena_ind)), 0,
"Unexpected mallctl() failure");
p = malloc(bin_infos[0].reg_size);
- assert_ptr_not_null(p, "Unexpected malloc() failure");
+ expect_ptr_not_null(p, "Unexpected malloc() failure");
- assert_d_eq(mallctl("thread.tcache.flush", NULL, NULL, NULL, 0),
+ expect_d_eq(mallctl("thread.tcache.flush", NULL, NULL, NULL, 0),
opt_tcache ? 0 : EFAULT, "Unexpected mallctl() result");
- assert_d_eq(mallctl("epoch", NULL, NULL, (void *)&epoch, sizeof(epoch)),
+ expect_d_eq(mallctl("epoch", NULL, NULL, (void *)&epoch, sizeof(epoch)),
0, "Unexpected mallctl() failure");
char cmd[128];
sz = sizeof(uint64_t);
gen_mallctl_str(cmd, "nmalloc", arena_ind);
- assert_d_eq(mallctl(cmd, (void *)&nmalloc, &sz, NULL, 0), expected,
+ expect_d_eq(mallctl(cmd, (void *)&nmalloc, &sz, NULL, 0), expected,
"Unexpected mallctl() result");
gen_mallctl_str(cmd, "ndalloc", arena_ind);
- assert_d_eq(mallctl(cmd, (void *)&ndalloc, &sz, NULL, 0), expected,
+ expect_d_eq(mallctl(cmd, (void *)&ndalloc, &sz, NULL, 0), expected,
"Unexpected mallctl() result");
gen_mallctl_str(cmd, "nrequests", arena_ind);
- assert_d_eq(mallctl(cmd, (void *)&nrequests, &sz, NULL, 0), expected,
+ expect_d_eq(mallctl(cmd, (void *)&nrequests, &sz, NULL, 0), expected,
"Unexpected mallctl() result");
sz = sizeof(size_t);
gen_mallctl_str(cmd, "curregs", arena_ind);
- assert_d_eq(mallctl(cmd, (void *)&curregs, &sz, NULL, 0), expected,
+ expect_d_eq(mallctl(cmd, (void *)&curregs, &sz, NULL, 0), expected,
"Unexpected mallctl() result");
sz = sizeof(uint64_t);
gen_mallctl_str(cmd, "nfills", arena_ind);
- assert_d_eq(mallctl(cmd, (void *)&nfills, &sz, NULL, 0), expected,
+ expect_d_eq(mallctl(cmd, (void *)&nfills, &sz, NULL, 0), expected,
"Unexpected mallctl() result");
gen_mallctl_str(cmd, "nflushes", arena_ind);
- assert_d_eq(mallctl(cmd, (void *)&nflushes, &sz, NULL, 0), expected,
+ expect_d_eq(mallctl(cmd, (void *)&nflushes, &sz, NULL, 0), expected,
"Unexpected mallctl() result");
gen_mallctl_str(cmd, "nslabs", arena_ind);
- assert_d_eq(mallctl(cmd, (void *)&nslabs, &sz, NULL, 0), expected,
+ expect_d_eq(mallctl(cmd, (void *)&nslabs, &sz, NULL, 0), expected,
"Unexpected mallctl() result");
gen_mallctl_str(cmd, "nreslabs", arena_ind);
- assert_d_eq(mallctl(cmd, (void *)&nreslabs, &sz, NULL, 0), expected,
+ expect_d_eq(mallctl(cmd, (void *)&nreslabs, &sz, NULL, 0), expected,
"Unexpected mallctl() result");
sz = sizeof(size_t);
gen_mallctl_str(cmd, "curslabs", arena_ind);
- assert_d_eq(mallctl(cmd, (void *)&curslabs, &sz, NULL, 0), expected,
+ expect_d_eq(mallctl(cmd, (void *)&curslabs, &sz, NULL, 0), expected,
"Unexpected mallctl() result");
gen_mallctl_str(cmd, "nonfull_slabs", arena_ind);
- assert_d_eq(mallctl(cmd, (void *)&nonfull_slabs, &sz, NULL, 0),
+ expect_d_eq(mallctl(cmd, (void *)&nonfull_slabs, &sz, NULL, 0),
expected, "Unexpected mallctl() result");
if (config_stats) {
- assert_u64_gt(nmalloc, 0,
+ expect_u64_gt(nmalloc, 0,
"nmalloc should be greater than zero");
- assert_u64_ge(nmalloc, ndalloc,
+ expect_u64_ge(nmalloc, ndalloc,
"nmalloc should be at least as large as ndalloc");
- assert_u64_gt(nrequests, 0,
+ expect_u64_gt(nrequests, 0,
"nrequests should be greater than zero");
- assert_zu_gt(curregs, 0,
+ expect_zu_gt(curregs, 0,
"allocated should be greater than zero");
if (opt_tcache) {
- assert_u64_gt(nfills, 0,
+ expect_u64_gt(nfills, 0,
"At least one fill should have occurred");
- assert_u64_gt(nflushes, 0,
+ expect_u64_gt(nflushes, 0,
"At least one flush should have occurred");
}
- assert_u64_gt(nslabs, 0,
+ expect_u64_gt(nslabs, 0,
"At least one slab should have been allocated");
- assert_zu_gt(curslabs, 0,
+ expect_zu_gt(curslabs, 0,
"At least one slab should be currently allocated");
- assert_zu_eq(nonfull_slabs, 0,
+ expect_zu_eq(nonfull_slabs, 0,
"slabs_nonfull should be empty");
}
@@ -327,33 +330,33 @@ TEST_BEGIN(test_stats_arenas_lextents) {
int expected = config_stats ? 0 : ENOENT;
sz = sizeof(size_t);
- assert_d_eq(mallctl("arenas.lextent.0.size", (void *)&hsize, &sz, NULL,
+ expect_d_eq(mallctl("arenas.lextent.0.size", (void *)&hsize, &sz, NULL,
0), 0, "Unexpected mallctl() failure");
p = mallocx(hsize, MALLOCX_ARENA(0));
- assert_ptr_not_null(p, "Unexpected mallocx() failure");
+ expect_ptr_not_null(p, "Unexpected mallocx() failure");
- assert_d_eq(mallctl("epoch", NULL, NULL, (void *)&epoch, sizeof(epoch)),
+ expect_d_eq(mallctl("epoch", NULL, NULL, (void *)&epoch, sizeof(epoch)),
0, "Unexpected mallctl() failure");
sz = sizeof(uint64_t);
- assert_d_eq(mallctl("stats.arenas.0.lextents.0.nmalloc",
+ expect_d_eq(mallctl("stats.arenas.0.lextents.0.nmalloc",
(void *)&nmalloc, &sz, NULL, 0), expected,
"Unexpected mallctl() result");
- assert_d_eq(mallctl("stats.arenas.0.lextents.0.ndalloc",
+ expect_d_eq(mallctl("stats.arenas.0.lextents.0.ndalloc",
(void *)&ndalloc, &sz, NULL, 0), expected,
"Unexpected mallctl() result");
sz = sizeof(size_t);
- assert_d_eq(mallctl("stats.arenas.0.lextents.0.curlextents",
+ expect_d_eq(mallctl("stats.arenas.0.lextents.0.curlextents",
(void *)&curlextents, &sz, NULL, 0), expected,
"Unexpected mallctl() result");
if (config_stats) {
- assert_u64_gt(nmalloc, 0,
+ expect_u64_gt(nmalloc, 0,
"nmalloc should be greater than zero");
- assert_u64_ge(nmalloc, ndalloc,
+ expect_u64_ge(nmalloc, ndalloc,
"nmalloc should be at least as large as ndalloc");
- assert_u64_gt(curlextents, 0,
+ expect_u64_gt(curlextents, 0,
"At least one extent should be currently allocated");
}
@@ -361,6 +364,58 @@ TEST_BEGIN(test_stats_arenas_lextents) {
}
TEST_END
+static void
+test_tcache_bytes_for_usize(size_t usize) {
+ uint64_t epoch;
+ size_t tcache_bytes, tcache_stashed_bytes;
+ size_t sz = sizeof(tcache_bytes);
+
+ void *ptr = mallocx(usize, 0);
+
+ expect_d_eq(mallctl("epoch", NULL, NULL, (void *)&epoch, sizeof(epoch)),
+ 0, "Unexpected mallctl() failure");
+ assert_d_eq(mallctl(
+ "stats.arenas." STRINGIFY(MALLCTL_ARENAS_ALL) ".tcache_bytes",
+ &tcache_bytes, &sz, NULL, 0), 0, "Unexpected mallctl failure");
+ assert_d_eq(mallctl(
+ "stats.arenas." STRINGIFY(MALLCTL_ARENAS_ALL)
+ ".tcache_stashed_bytes", &tcache_stashed_bytes, &sz, NULL, 0), 0,
+ "Unexpected mallctl failure");
+ size_t tcache_bytes_before = tcache_bytes + tcache_stashed_bytes;
+ dallocx(ptr, 0);
+
+ expect_d_eq(mallctl("epoch", NULL, NULL, (void *)&epoch, sizeof(epoch)),
+ 0, "Unexpected mallctl() failure");
+ assert_d_eq(mallctl(
+ "stats.arenas." STRINGIFY(MALLCTL_ARENAS_ALL) ".tcache_bytes",
+ &tcache_bytes, &sz, NULL, 0), 0, "Unexpected mallctl failure");
+ assert_d_eq(mallctl(
+ "stats.arenas." STRINGIFY(MALLCTL_ARENAS_ALL)
+ ".tcache_stashed_bytes", &tcache_stashed_bytes, &sz, NULL, 0), 0,
+ "Unexpected mallctl failure");
+ size_t tcache_bytes_after = tcache_bytes + tcache_stashed_bytes;
+ assert_zu_eq(tcache_bytes_after - tcache_bytes_before,
+ usize, "Incorrectly attributed a free");
+}
+
+TEST_BEGIN(test_stats_tcache_bytes_small) {
+ test_skip_if(!config_stats);
+ test_skip_if(!opt_tcache);
+ test_skip_if(opt_tcache_max < SC_SMALL_MAXCLASS);
+
+ test_tcache_bytes_for_usize(SC_SMALL_MAXCLASS);
+}
+TEST_END
+
+TEST_BEGIN(test_stats_tcache_bytes_large) {
+ test_skip_if(!config_stats);
+ test_skip_if(!opt_tcache);
+ test_skip_if(opt_tcache_max < SC_LARGE_MINCLASS);
+
+ test_tcache_bytes_for_usize(SC_LARGE_MINCLASS);
+}
+TEST_END
+
int
main(void) {
return test_no_reentrancy(
@@ -370,5 +425,7 @@ main(void) {
test_stats_arenas_small,
test_stats_arenas_large,
test_stats_arenas_bins,
- test_stats_arenas_lextents);
+ test_stats_arenas_lextents,
+ test_stats_tcache_bytes_small,
+ test_stats_tcache_bytes_large);
}
diff --git a/deps/jemalloc/test/unit/stats_print.c b/deps/jemalloc/test/unit/stats_print.c
index 014d002fd..3b3177534 100644
--- a/deps/jemalloc/test/unit/stats_print.c
+++ b/deps/jemalloc/test/unit/stats_print.c
@@ -136,7 +136,7 @@ parser_tokenize(parser_t *parser) {
size_t token_line JEMALLOC_CC_SILENCE_INIT(1);
size_t token_col JEMALLOC_CC_SILENCE_INIT(0);
- assert_zu_le(parser->pos, parser->len,
+ expect_zu_le(parser->pos, parser->len,
"Position is past end of buffer");
while (state != STATE_ACCEPT) {
@@ -686,7 +686,7 @@ parser_parse_value(parser_t *parser) {
static bool
parser_parse_pair(parser_t *parser) {
- assert_d_eq(parser->token.token_type, TOKEN_TYPE_STRING,
+ expect_d_eq(parser->token.token_type, TOKEN_TYPE_STRING,
"Pair should start with string");
if (parser_tokenize(parser)) {
return true;
@@ -731,7 +731,7 @@ parser_parse_values(parser_t *parser) {
static bool
parser_parse_array(parser_t *parser) {
- assert_d_eq(parser->token.token_type, TOKEN_TYPE_LBRACKET,
+ expect_d_eq(parser->token.token_type, TOKEN_TYPE_LBRACKET,
"Array should start with [");
if (parser_tokenize(parser)) {
return true;
@@ -747,7 +747,7 @@ parser_parse_array(parser_t *parser) {
static bool
parser_parse_pairs(parser_t *parser) {
- assert_d_eq(parser->token.token_type, TOKEN_TYPE_STRING,
+ expect_d_eq(parser->token.token_type, TOKEN_TYPE_STRING,
"Object should start with string");
if (parser_parse_pair(parser)) {
return true;
@@ -782,7 +782,7 @@ parser_parse_pairs(parser_t *parser) {
static bool
parser_parse_object(parser_t *parser) {
- assert_d_eq(parser->token.token_type, TOKEN_TYPE_LBRACE,
+ expect_d_eq(parser->token.token_type, TOKEN_TYPE_LBRACE,
"Object should start with {");
if (parser_tokenize(parser)) {
return true;
@@ -899,9 +899,9 @@ TEST_BEGIN(test_json_parser) {
const char *input = invalid_inputs[i];
parser_t parser;
parser_init(&parser, false);
- assert_false(parser_append(&parser, input),
+ expect_false(parser_append(&parser, input),
"Unexpected input appending failure");
- assert_true(parser_parse(&parser),
+ expect_true(parser_parse(&parser),
"Unexpected parse success for input: %s", input);
parser_fini(&parser);
}
@@ -910,9 +910,9 @@ TEST_BEGIN(test_json_parser) {
const char *input = valid_inputs[i];
parser_t parser;
parser_init(&parser, true);
- assert_false(parser_append(&parser, input),
+ expect_false(parser_append(&parser, input),
"Unexpected input appending failure");
- assert_false(parser_parse(&parser),
+ expect_false(parser_parse(&parser),
"Unexpected parse error for input: %s", input);
parser_fini(&parser);
}
@@ -961,17 +961,17 @@ TEST_BEGIN(test_stats_print_json) {
break;
case 1: {
size_t sz = sizeof(arena_ind);
- assert_d_eq(mallctl("arenas.create", (void *)&arena_ind,
+ expect_d_eq(mallctl("arenas.create", (void *)&arena_ind,
&sz, NULL, 0), 0, "Unexpected mallctl failure");
break;
} case 2: {
size_t mib[3];
size_t miblen = sizeof(mib)/sizeof(size_t);
- assert_d_eq(mallctlnametomib("arena.0.destroy",
+ expect_d_eq(mallctlnametomib("arena.0.destroy",
mib, &miblen), 0,
"Unexpected mallctlnametomib failure");
mib[1] = arena_ind;
- assert_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL,
+ expect_d_eq(mallctlbymib(mib, miblen, NULL, NULL, NULL,
0), 0, "Unexpected mallctlbymib failure");
break;
} default:
@@ -983,7 +983,7 @@ TEST_BEGIN(test_stats_print_json) {
parser_init(&parser, true);
malloc_stats_print(write_cb, (void *)&parser, opts[j]);
- assert_false(parser_parse(&parser),
+ expect_false(parser_parse(&parser),
"Unexpected parse error, opts=\"%s\"", opts[j]);
parser_fini(&parser);
}
diff --git a/deps/jemalloc/test/unit/sz.c b/deps/jemalloc/test/unit/sz.c
new file mode 100644
index 000000000..8ae04b921
--- /dev/null
+++ b/deps/jemalloc/test/unit/sz.c
@@ -0,0 +1,66 @@
+#include "test/jemalloc_test.h"
+
+TEST_BEGIN(test_sz_psz2ind) {
+ /*
+ * Testing page size classes which reside prior to the regular group
+ * with all size classes divisible by page size.
+ * For x86_64 Linux, it's 4096, 8192, 12288, 16384, with corresponding
+ * pszind 0, 1, 2 and 3.
+ */
+ for (size_t i = 0; i < SC_NGROUP; i++) {
+ for (size_t psz = i * PAGE + 1; psz <= (i + 1) * PAGE; psz++) {
+ pszind_t ind = sz_psz2ind(psz);
+ expect_zu_eq(ind, i, "Got %u as sz_psz2ind of %zu", ind,
+ psz);
+ }
+ }
+
+ sc_data_t data;
+ memset(&data, 0, sizeof(data));
+ sc_data_init(&data);
+ /*
+ * 'base' is the base of the first regular group with all size classes
+ * divisible by page size.
+ * For x86_64 Linux, it's 16384, and base_ind is 36.
+ */
+ size_t base_psz = 1 << (SC_LG_NGROUP + LG_PAGE);
+ size_t base_ind = 0;
+ while (base_ind < SC_NSIZES &&
+ reg_size_compute(data.sc[base_ind].lg_base,
+ data.sc[base_ind].lg_delta,
+ data.sc[base_ind].ndelta) < base_psz) {
+ base_ind++;
+ }
+ expect_zu_eq(
+ reg_size_compute(data.sc[base_ind].lg_base,
+ data.sc[base_ind].lg_delta, data.sc[base_ind].ndelta),
+ base_psz, "Size class equal to %zu not found", base_psz);
+ /*
+ * Test different sizes falling into groups after the 'base'. The
+ * increment is PAGE / 3 for the execution speed purpose.
+ */
+ base_ind -= SC_NGROUP;
+ for (size_t psz = base_psz; psz <= 64 * 1024 * 1024; psz += PAGE / 3) {
+ pszind_t ind = sz_psz2ind(psz);
+ sc_t gt_sc = data.sc[ind + base_ind];
+ expect_zu_gt(psz,
+ reg_size_compute(gt_sc.lg_base, gt_sc.lg_delta,
+ gt_sc.ndelta),
+ "Got %u as sz_psz2ind of %zu", ind, psz);
+ sc_t le_sc = data.sc[ind + base_ind + 1];
+ expect_zu_le(psz,
+ reg_size_compute(le_sc.lg_base, le_sc.lg_delta,
+ le_sc.ndelta),
+ "Got %u as sz_psz2ind of %zu", ind, psz);
+ }
+
+ pszind_t max_ind = sz_psz2ind(SC_LARGE_MAXCLASS + 1);
+ expect_lu_eq(max_ind, SC_NPSIZES,
+ "Got %u as sz_psz2ind of %llu", max_ind, SC_LARGE_MAXCLASS);
+}
+TEST_END
+
+int
+main(void) {
+ return test(test_sz_psz2ind);
+}
diff --git a/deps/jemalloc/test/unit/tcache_max.c b/deps/jemalloc/test/unit/tcache_max.c
new file mode 100644
index 000000000..1f657c859
--- /dev/null
+++ b/deps/jemalloc/test/unit/tcache_max.c
@@ -0,0 +1,175 @@
+#include "test/jemalloc_test.h"
+#include "test/san.h"
+
+const char *malloc_conf = TEST_SAN_UAF_ALIGN_DISABLE;
+
+enum {
+ alloc_option_start = 0,
+ use_malloc = 0,
+ use_mallocx,
+ alloc_option_end
+};
+
+enum {
+ dalloc_option_start = 0,
+ use_free = 0,
+ use_dallocx,
+ use_sdallocx,
+ dalloc_option_end
+};
+
+static unsigned alloc_option, dalloc_option;
+static size_t tcache_max;
+
+static void *
+alloc_func(size_t sz) {
+ void *ret;
+
+ switch (alloc_option) {
+ case use_malloc:
+ ret = malloc(sz);
+ break;
+ case use_mallocx:
+ ret = mallocx(sz, 0);
+ break;
+ default:
+ unreachable();
+ }
+ expect_ptr_not_null(ret, "Unexpected malloc / mallocx failure");
+
+ return ret;
+}
+
+static void
+dalloc_func(void *ptr, size_t sz) {
+ switch (dalloc_option) {
+ case use_free:
+ free(ptr);
+ break;
+ case use_dallocx:
+ dallocx(ptr, 0);
+ break;
+ case use_sdallocx:
+ sdallocx(ptr, sz, 0);
+ break;
+ default:
+ unreachable();
+ }
+}
+
+static size_t
+tcache_bytes_read(void) {
+ uint64_t epoch;
+ assert_d_eq(mallctl("epoch", NULL, NULL, (void *)&epoch, sizeof(epoch)),
+ 0, "Unexpected mallctl() failure");
+
+ size_t tcache_bytes;
+ size_t sz = sizeof(tcache_bytes);
+ assert_d_eq(mallctl(
+ "stats.arenas." STRINGIFY(MALLCTL_ARENAS_ALL) ".tcache_bytes",
+ &tcache_bytes, &sz, NULL, 0), 0, "Unexpected mallctl failure");
+
+ return tcache_bytes;
+}
+
+static void
+tcache_bytes_check_update(size_t *prev, ssize_t diff) {
+ size_t tcache_bytes = tcache_bytes_read();
+ expect_zu_eq(tcache_bytes, *prev + diff, "tcache bytes not expected");
+
+ *prev += diff;
+}
+
+static void
+test_tcache_bytes_alloc(size_t alloc_size) {
+ expect_d_eq(mallctl("thread.tcache.flush", NULL, NULL, NULL, 0), 0,
+ "Unexpected tcache flush failure");
+
+ size_t usize = sz_s2u(alloc_size);
+ /* No change is expected if usize is outside of tcache_max range. */
+ bool cached = (usize <= tcache_max);
+ ssize_t diff = cached ? usize : 0;
+
+ void *ptr1 = alloc_func(alloc_size);
+ void *ptr2 = alloc_func(alloc_size);
+
+ size_t bytes = tcache_bytes_read();
+ dalloc_func(ptr2, alloc_size);
+ /* Expect tcache_bytes increase after dalloc */
+ tcache_bytes_check_update(&bytes, diff);
+
+ dalloc_func(ptr1, alloc_size);
+ /* Expect tcache_bytes increase again */
+ tcache_bytes_check_update(&bytes, diff);
+
+ void *ptr3 = alloc_func(alloc_size);
+ if (cached) {
+ expect_ptr_eq(ptr1, ptr3, "Unexpected cached ptr");
+ }
+ /* Expect tcache_bytes decrease after alloc */
+ tcache_bytes_check_update(&bytes, -diff);
+
+ void *ptr4 = alloc_func(alloc_size);
+ if (cached) {
+ expect_ptr_eq(ptr2, ptr4, "Unexpected cached ptr");
+ }
+ /* Expect tcache_bytes decrease again */
+ tcache_bytes_check_update(&bytes, -diff);
+
+ dalloc_func(ptr3, alloc_size);
+ tcache_bytes_check_update(&bytes, diff);
+ dalloc_func(ptr4, alloc_size);
+ tcache_bytes_check_update(&bytes, diff);
+}
+
+static void
+test_tcache_max_impl(void) {
+ size_t sz;
+ sz = sizeof(tcache_max);
+ assert_d_eq(mallctl("arenas.tcache_max", (void *)&tcache_max,
+ &sz, NULL, 0), 0, "Unexpected mallctl() failure");
+
+ /* opt.tcache_max set to 1024 in tcache_max.sh */
+ expect_zu_eq(tcache_max, 1024, "tcache_max not expected");
+
+ test_tcache_bytes_alloc(1);
+ test_tcache_bytes_alloc(tcache_max - 1);
+ test_tcache_bytes_alloc(tcache_max);
+ test_tcache_bytes_alloc(tcache_max + 1);
+
+ test_tcache_bytes_alloc(PAGE - 1);
+ test_tcache_bytes_alloc(PAGE);
+ test_tcache_bytes_alloc(PAGE + 1);
+
+ size_t large;
+ sz = sizeof(large);
+ assert_d_eq(mallctl("arenas.lextent.0.size", (void *)&large, &sz, NULL,
+ 0), 0, "Unexpected mallctl() failure");
+
+ test_tcache_bytes_alloc(large - 1);
+ test_tcache_bytes_alloc(large);
+ test_tcache_bytes_alloc(large + 1);
+}
+
+TEST_BEGIN(test_tcache_max) {
+ test_skip_if(!config_stats);
+ test_skip_if(!opt_tcache);
+ test_skip_if(opt_prof);
+ test_skip_if(san_uaf_detection_enabled());
+
+ for (alloc_option = alloc_option_start;
+ alloc_option < alloc_option_end;
+ alloc_option++) {
+ for (dalloc_option = dalloc_option_start;
+ dalloc_option < dalloc_option_end;
+ dalloc_option++) {
+ test_tcache_max_impl();
+ }
+ }
+}
+TEST_END
+
+int
+main(void) {
+ return test(test_tcache_max);
+}
diff --git a/deps/jemalloc/test/unit/tcache_max.sh b/deps/jemalloc/test/unit/tcache_max.sh
new file mode 100644
index 000000000..4480d733c
--- /dev/null
+++ b/deps/jemalloc/test/unit/tcache_max.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+export MALLOC_CONF="tcache_max:1024"
diff --git a/deps/jemalloc/test/unit/test_hooks.c b/deps/jemalloc/test/unit/test_hooks.c
index ded8698bc..8cd2b3bb1 100644
--- a/deps/jemalloc/test/unit/test_hooks.c
+++ b/deps/jemalloc/test/unit/test_hooks.c
@@ -12,21 +12,21 @@ func_to_hook(int arg1, int arg2) {
return arg1 + arg2;
}
-#define func_to_hook JEMALLOC_HOOK(func_to_hook, test_hooks_libc_hook)
+#define func_to_hook JEMALLOC_TEST_HOOK(func_to_hook, test_hooks_libc_hook)
TEST_BEGIN(unhooked_call) {
test_hooks_libc_hook = NULL;
hook_called = false;
- assert_d_eq(3, func_to_hook(1, 2), "Hooking changed return value.");
- assert_false(hook_called, "Nulling out hook didn't take.");
+ expect_d_eq(3, func_to_hook(1, 2), "Hooking changed return value.");
+ expect_false(hook_called, "Nulling out hook didn't take.");
}
TEST_END
TEST_BEGIN(hooked_call) {
test_hooks_libc_hook = &hook;
hook_called = false;
- assert_d_eq(3, func_to_hook(1, 2), "Hooking changed return value.");
- assert_true(hook_called, "Hook should have executed.");
+ expect_d_eq(3, func_to_hook(1, 2), "Hooking changed return value.");
+ expect_true(hook_called, "Hook should have executed.");
}
TEST_END
diff --git a/deps/jemalloc/test/unit/thread_event.c b/deps/jemalloc/test/unit/thread_event.c
new file mode 100644
index 000000000..e0b88a92d
--- /dev/null
+++ b/deps/jemalloc/test/unit/thread_event.c
@@ -0,0 +1,34 @@
+#include "test/jemalloc_test.h"
+
+TEST_BEGIN(test_next_event_fast) {
+ tsd_t *tsd = tsd_fetch();
+ te_ctx_t ctx;
+ te_ctx_get(tsd, &ctx, true);
+
+ te_ctx_last_event_set(&ctx, 0);
+ te_ctx_current_bytes_set(&ctx, TE_NEXT_EVENT_FAST_MAX - 8U);
+ te_ctx_next_event_set(tsd, &ctx, TE_NEXT_EVENT_FAST_MAX);
+#define E(event, condition, is_alloc) \
+ if (is_alloc && condition) { \
+ event##_event_wait_set(tsd, TE_NEXT_EVENT_FAST_MAX); \
+ }
+ ITERATE_OVER_ALL_EVENTS
+#undef E
+
+ /* Test next_event_fast rolling back to 0. */
+ void *p = malloc(16U);
+ assert_ptr_not_null(p, "malloc() failed");
+ free(p);
+
+ /* Test next_event_fast resuming to be equal to next_event. */
+ void *q = malloc(SC_LOOKUP_MAXCLASS);
+ assert_ptr_not_null(q, "malloc() failed");
+ free(q);
+}
+TEST_END
+
+int
+main(void) {
+ return test(
+ test_next_event_fast);
+}
diff --git a/deps/jemalloc/test/unit/thread_event.sh b/deps/jemalloc/test/unit/thread_event.sh
new file mode 100644
index 000000000..8fcc7d8a7
--- /dev/null
+++ b/deps/jemalloc/test/unit/thread_event.sh
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+if [ "x${enable_prof}" = "x1" ] ; then
+ export MALLOC_CONF="prof:true,lg_prof_sample:0"
+fi
diff --git a/deps/jemalloc/test/unit/ticker.c b/deps/jemalloc/test/unit/ticker.c
index e5790a316..0dd778619 100644
--- a/deps/jemalloc/test/unit/ticker.c
+++ b/deps/jemalloc/test/unit/ticker.c
@@ -11,16 +11,16 @@ TEST_BEGIN(test_ticker_tick) {
ticker_init(&ticker, NTICKS);
for (i = 0; i < NREPS; i++) {
for (j = 0; j < NTICKS; j++) {
- assert_u_eq(ticker_read(&ticker), NTICKS - j,
+ expect_u_eq(ticker_read(&ticker), NTICKS - j,
"Unexpected ticker value (i=%d, j=%d)", i, j);
- assert_false(ticker_tick(&ticker),
+ expect_false(ticker_tick(&ticker),
"Unexpected ticker fire (i=%d, j=%d)", i, j);
}
- assert_u32_eq(ticker_read(&ticker), 0,
+ expect_u32_eq(ticker_read(&ticker), 0,
"Expected ticker depletion");
- assert_true(ticker_tick(&ticker),
+ expect_true(ticker_tick(&ticker),
"Expected ticker fire (i=%d)", i);
- assert_u32_eq(ticker_read(&ticker), NTICKS,
+ expect_u32_eq(ticker_read(&ticker), NTICKS,
"Expected ticker reset");
}
#undef NTICKS
@@ -33,14 +33,14 @@ TEST_BEGIN(test_ticker_ticks) {
ticker_init(&ticker, NTICKS);
- assert_u_eq(ticker_read(&ticker), NTICKS, "Unexpected ticker value");
- assert_false(ticker_ticks(&ticker, NTICKS), "Unexpected ticker fire");
- assert_u_eq(ticker_read(&ticker), 0, "Unexpected ticker value");
- assert_true(ticker_ticks(&ticker, NTICKS), "Expected ticker fire");
- assert_u_eq(ticker_read(&ticker), NTICKS, "Unexpected ticker value");
+ expect_u_eq(ticker_read(&ticker), NTICKS, "Unexpected ticker value");
+ expect_false(ticker_ticks(&ticker, NTICKS), "Unexpected ticker fire");
+ expect_u_eq(ticker_read(&ticker), 0, "Unexpected ticker value");
+ expect_true(ticker_ticks(&ticker, NTICKS), "Expected ticker fire");
+ expect_u_eq(ticker_read(&ticker), NTICKS, "Unexpected ticker value");
- assert_true(ticker_ticks(&ticker, NTICKS + 1), "Expected ticker fire");
- assert_u_eq(ticker_read(&ticker), NTICKS, "Unexpected ticker value");
+ expect_true(ticker_ticks(&ticker, NTICKS + 1), "Expected ticker fire");
+ expect_u_eq(ticker_read(&ticker), NTICKS, "Unexpected ticker value");
#undef NTICKS
}
TEST_END
@@ -51,23 +51,50 @@ TEST_BEGIN(test_ticker_copy) {
ticker_init(&ta, NTICKS);
ticker_copy(&tb, &ta);
- assert_u_eq(ticker_read(&tb), NTICKS, "Unexpected ticker value");
- assert_true(ticker_ticks(&tb, NTICKS + 1), "Expected ticker fire");
- assert_u_eq(ticker_read(&tb), NTICKS, "Unexpected ticker value");
+ expect_u_eq(ticker_read(&tb), NTICKS, "Unexpected ticker value");
+ expect_true(ticker_ticks(&tb, NTICKS + 1), "Expected ticker fire");
+ expect_u_eq(ticker_read(&tb), NTICKS, "Unexpected ticker value");
ticker_tick(&ta);
ticker_copy(&tb, &ta);
- assert_u_eq(ticker_read(&tb), NTICKS - 1, "Unexpected ticker value");
- assert_true(ticker_ticks(&tb, NTICKS), "Expected ticker fire");
- assert_u_eq(ticker_read(&tb), NTICKS, "Unexpected ticker value");
+ expect_u_eq(ticker_read(&tb), NTICKS - 1, "Unexpected ticker value");
+ expect_true(ticker_ticks(&tb, NTICKS), "Expected ticker fire");
+ expect_u_eq(ticker_read(&tb), NTICKS, "Unexpected ticker value");
#undef NTICKS
}
TEST_END
+TEST_BEGIN(test_ticker_geom) {
+ const int32_t ticks = 100;
+ const uint64_t niters = 100 * 1000;
+
+ ticker_geom_t ticker;
+ ticker_geom_init(&ticker, ticks);
+ uint64_t total_ticks = 0;
+ /* Just some random constant. */
+ uint64_t prng_state = 0x343219f93496db9fULL;
+ for (uint64_t i = 0; i < niters; i++) {
+ while(!ticker_geom_tick(&ticker, &prng_state)) {
+ total_ticks++;
+ }
+ }
+ /*
+ * In fact, with this choice of random seed and the PRNG implementation
+ * used at the time this was tested, total_ticks is 95.1% of the
+ * expected ticks.
+ */
+ expect_u64_ge(total_ticks , niters * ticks * 9 / 10,
+ "Mean off by > 10%%");
+ expect_u64_le(total_ticks , niters * ticks * 11 / 10,
+ "Mean off by > 10%%");
+}
+TEST_END
+
int
main(void) {
return test(
test_ticker_tick,
test_ticker_ticks,
- test_ticker_copy);
+ test_ticker_copy,
+ test_ticker_geom);
}
diff --git a/deps/jemalloc/test/unit/tsd.c b/deps/jemalloc/test/unit/tsd.c
index 917884dcf..205d87089 100644
--- a/deps/jemalloc/test/unit/tsd.c
+++ b/deps/jemalloc/test/unit/tsd.c
@@ -10,7 +10,7 @@ static int data_cleanup_count;
void
data_cleanup(int *data) {
if (data_cleanup_count == 0) {
- assert_x_eq(*data, MALLOC_TSD_TEST_DATA_INIT,
+ expect_x_eq(*data, MALLOC_TSD_TEST_DATA_INIT,
"Argument passed into cleanup function should match tsd "
"value");
}
@@ -38,7 +38,7 @@ data_cleanup(int *data) {
if (reincarnate) {
void *p = mallocx(1, 0);
- assert_ptr_not_null(p, "Unexpeced mallocx() failure");
+ expect_ptr_not_null(p, "Unexpeced mallocx() failure");
dallocx(p, 0);
}
}
@@ -48,19 +48,26 @@ thd_start(void *arg) {
int d = (int)(uintptr_t)arg;
void *p;
+ /*
+ * Test free before tsd init -- the free fast path (which does not
+ * explicitly check for NULL) has to tolerate this case, and fall back
+ * to free_default.
+ */
+ free(NULL);
+
tsd_t *tsd = tsd_fetch();
- assert_x_eq(tsd_test_data_get(tsd), MALLOC_TSD_TEST_DATA_INIT,
+ expect_x_eq(tsd_test_data_get(tsd), MALLOC_TSD_TEST_DATA_INIT,
"Initial tsd get should return initialization value");
p = malloc(1);
- assert_ptr_not_null(p, "Unexpected malloc() failure");
+ expect_ptr_not_null(p, "Unexpected malloc() failure");
tsd_test_data_set(tsd, d);
- assert_x_eq(tsd_test_data_get(tsd), d,
+ expect_x_eq(tsd_test_data_get(tsd), d,
"After tsd set, tsd get should return value that was set");
d = 0;
- assert_x_eq(tsd_test_data_get(tsd), (int)(uintptr_t)arg,
+ expect_x_eq(tsd_test_data_get(tsd), (int)(uintptr_t)arg,
"Resetting local data should have no effect on tsd");
tsd_test_callback_set(tsd, &data_cleanup);
@@ -84,7 +91,7 @@ TEST_BEGIN(test_tsd_sub_thread) {
* We reincarnate twice in the data cleanup, so it should execute at
* least 3 times.
*/
- assert_x_ge(data_cleanup_count, 3,
+ expect_x_ge(data_cleanup_count, 3,
"Cleanup function should have executed multiple times.");
}
TEST_END
@@ -95,28 +102,28 @@ thd_start_reincarnated(void *arg) {
assert(tsd);
void *p = malloc(1);
- assert_ptr_not_null(p, "Unexpected malloc() failure");
+ expect_ptr_not_null(p, "Unexpected malloc() failure");
/* Manually trigger reincarnation. */
- assert_ptr_not_null(tsd_arena_get(tsd),
+ expect_ptr_not_null(tsd_arena_get(tsd),
"Should have tsd arena set.");
tsd_cleanup((void *)tsd);
- assert_ptr_null(*tsd_arenap_get_unsafe(tsd),
+ expect_ptr_null(*tsd_arenap_get_unsafe(tsd),
"TSD arena should have been cleared.");
- assert_u_eq(tsd_state_get(tsd), tsd_state_purgatory,
+ expect_u_eq(tsd_state_get(tsd), tsd_state_purgatory,
"TSD state should be purgatory\n");
free(p);
- assert_u_eq(tsd_state_get(tsd), tsd_state_reincarnated,
+ expect_u_eq(tsd_state_get(tsd), tsd_state_reincarnated,
"TSD state should be reincarnated\n");
p = mallocx(1, MALLOCX_TCACHE_NONE);
- assert_ptr_not_null(p, "Unexpected malloc() failure");
- assert_ptr_null(*tsd_arenap_get_unsafe(tsd),
+ expect_ptr_not_null(p, "Unexpected malloc() failure");
+ expect_ptr_null(*tsd_arenap_get_unsafe(tsd),
"Should not have tsd arena set after reincarnation.");
free(p);
tsd_cleanup((void *)tsd);
- assert_ptr_null(*tsd_arenap_get_unsafe(tsd),
+ expect_ptr_null(*tsd_arenap_get_unsafe(tsd),
"TSD arena should have been cleared after 2nd cleanup.");
return NULL;
@@ -206,46 +213,46 @@ TEST_BEGIN(test_tsd_global_slow) {
* Spin-wait.
*/
}
- assert_false(atomic_load_b(&data.error, ATOMIC_SEQ_CST), "");
+ expect_false(atomic_load_b(&data.error, ATOMIC_SEQ_CST), "");
tsd_global_slow_inc(tsd_tsdn(tsd));
free(mallocx(1, 0));
- assert_false(tsd_fast(tsd), "");
+ expect_false(tsd_fast(tsd), "");
atomic_store_u32(&data.phase, 2, ATOMIC_SEQ_CST);
/* PHASE 3 */
while (atomic_load_u32(&data.phase, ATOMIC_SEQ_CST) != 3) {
}
- assert_false(atomic_load_b(&data.error, ATOMIC_SEQ_CST), "");
+ expect_false(atomic_load_b(&data.error, ATOMIC_SEQ_CST), "");
/* Increase again, so that we can test multiple fast/slow changes. */
tsd_global_slow_inc(tsd_tsdn(tsd));
atomic_store_u32(&data.phase, 4, ATOMIC_SEQ_CST);
free(mallocx(1, 0));
- assert_false(tsd_fast(tsd), "");
+ expect_false(tsd_fast(tsd), "");
/* PHASE 5 */
while (atomic_load_u32(&data.phase, ATOMIC_SEQ_CST) != 5) {
}
- assert_false(atomic_load_b(&data.error, ATOMIC_SEQ_CST), "");
+ expect_false(atomic_load_b(&data.error, ATOMIC_SEQ_CST), "");
tsd_global_slow_dec(tsd_tsdn(tsd));
atomic_store_u32(&data.phase, 6, ATOMIC_SEQ_CST);
/* We only decreased once; things should still be slow. */
free(mallocx(1, 0));
- assert_false(tsd_fast(tsd), "");
+ expect_false(tsd_fast(tsd), "");
/* PHASE 7 */
while (atomic_load_u32(&data.phase, ATOMIC_SEQ_CST) != 7) {
}
- assert_false(atomic_load_b(&data.error, ATOMIC_SEQ_CST), "");
+ expect_false(atomic_load_b(&data.error, ATOMIC_SEQ_CST), "");
tsd_global_slow_dec(tsd_tsdn(tsd));
atomic_store_u32(&data.phase, 8, ATOMIC_SEQ_CST);
/* We incremented and then decremented twice; we should be fast now. */
free(mallocx(1, 0));
- assert_true(!originally_fast || tsd_fast(tsd), "");
+ expect_true(!originally_fast || tsd_fast(tsd), "");
/* PHASE 9 */
while (atomic_load_u32(&data.phase, ATOMIC_SEQ_CST) != 9) {
}
- assert_false(atomic_load_b(&data.error, ATOMIC_SEQ_CST), "");
+ expect_false(atomic_load_b(&data.error, ATOMIC_SEQ_CST), "");
thd_join(thd, NULL);
}
diff --git a/deps/jemalloc/test/unit/uaf.c b/deps/jemalloc/test/unit/uaf.c
new file mode 100644
index 000000000..a8433c298
--- /dev/null
+++ b/deps/jemalloc/test/unit/uaf.c
@@ -0,0 +1,262 @@
+#include "test/jemalloc_test.h"
+#include "test/arena_util.h"
+#include "test/san.h"
+
+#include "jemalloc/internal/cache_bin.h"
+#include "jemalloc/internal/san.h"
+#include "jemalloc/internal/safety_check.h"
+
+const char *malloc_conf = TEST_SAN_UAF_ALIGN_ENABLE;
+
+static size_t san_uaf_align;
+
+static bool fake_abort_called;
+void fake_abort(const char *message) {
+ (void)message;
+ fake_abort_called = true;
+}
+
+static void
+test_write_after_free_pre(void) {
+ safety_check_set_abort(&fake_abort);
+ fake_abort_called = false;
+}
+
+static void
+test_write_after_free_post(void) {
+ assert_d_eq(mallctl("thread.tcache.flush", NULL, NULL, NULL, 0),
+ 0, "Unexpected tcache flush failure");
+ expect_true(fake_abort_called, "Use-after-free check didn't fire.");
+ safety_check_set_abort(NULL);
+}
+
+static bool
+uaf_detection_enabled(void) {
+ if (!config_uaf_detection || !san_uaf_detection_enabled()) {
+ return false;
+ }
+
+ ssize_t lg_san_uaf_align;
+ size_t sz = sizeof(lg_san_uaf_align);
+ assert_d_eq(mallctl("opt.lg_san_uaf_align", &lg_san_uaf_align, &sz,
+ NULL, 0), 0, "Unexpected mallctl failure");
+ if (lg_san_uaf_align < 0) {
+ return false;
+ }
+ assert_zd_ge(lg_san_uaf_align, LG_PAGE, "san_uaf_align out of range");
+ san_uaf_align = (size_t)1 << lg_san_uaf_align;
+
+ bool tcache_enabled;
+ sz = sizeof(tcache_enabled);
+ assert_d_eq(mallctl("thread.tcache.enabled", &tcache_enabled, &sz, NULL,
+ 0), 0, "Unexpected mallctl failure");
+ if (!tcache_enabled) {
+ return false;
+ }
+
+ return true;
+}
+
+static size_t
+read_tcache_stashed_bytes(unsigned arena_ind) {
+ if (!config_stats) {
+ return 0;
+ }
+
+ uint64_t epoch;
+ assert_d_eq(mallctl("epoch", NULL, NULL, (void *)&epoch, sizeof(epoch)),
+ 0, "Unexpected mallctl() failure");
+
+ size_t tcache_stashed_bytes;
+ size_t sz = sizeof(tcache_stashed_bytes);
+ assert_d_eq(mallctl(
+ "stats.arenas." STRINGIFY(MALLCTL_ARENAS_ALL)
+ ".tcache_stashed_bytes", &tcache_stashed_bytes, &sz, NULL, 0), 0,
+ "Unexpected mallctl failure");
+
+ return tcache_stashed_bytes;
+}
+
+static void
+test_use_after_free(size_t alloc_size, bool write_after_free) {
+ void *ptr = (void *)(uintptr_t)san_uaf_align;
+ assert_true(cache_bin_nonfast_aligned(ptr), "Wrong alignment");
+ ptr = (void *)((uintptr_t)123 * (uintptr_t)san_uaf_align);
+ assert_true(cache_bin_nonfast_aligned(ptr), "Wrong alignment");
+ ptr = (void *)((uintptr_t)san_uaf_align + 1);
+ assert_false(cache_bin_nonfast_aligned(ptr), "Wrong alignment");
+
+ /*
+ * Disable purging (-1) so that all dirty pages remain committed, to
+ * make use-after-free tolerable.
+ */
+ unsigned arena_ind = do_arena_create(-1, -1);
+ int flags = MALLOCX_ARENA(arena_ind) | MALLOCX_TCACHE_NONE;
+
+ size_t n_max = san_uaf_align * 2;
+ void **items = mallocx(n_max * sizeof(void *), flags);
+ assert_ptr_not_null(items, "Unexpected mallocx failure");
+
+ bool found = false;
+ size_t iter = 0;
+ char magic = 's';
+ assert_d_eq(mallctl("thread.tcache.flush", NULL, NULL, NULL, 0),
+ 0, "Unexpected tcache flush failure");
+ while (!found) {
+ ptr = mallocx(alloc_size, flags);
+ assert_ptr_not_null(ptr, "Unexpected mallocx failure");
+
+ found = cache_bin_nonfast_aligned(ptr);
+ *(char *)ptr = magic;
+ items[iter] = ptr;
+ assert_zu_lt(iter++, n_max, "No aligned ptr found");
+ }
+
+ if (write_after_free) {
+ test_write_after_free_pre();
+ }
+ bool junked = false;
+ while (iter-- != 0) {
+ char *volatile mem = items[iter];
+ assert_c_eq(*mem, magic, "Unexpected memory content");
+ size_t stashed_before = read_tcache_stashed_bytes(arena_ind);
+ free(mem);
+ if (*mem != magic) {
+ junked = true;
+ assert_c_eq(*mem, (char)uaf_detect_junk,
+ "Unexpected junk-filling bytes");
+ if (write_after_free) {
+ *(char *)mem = magic + 1;
+ }
+
+ size_t stashed_after = read_tcache_stashed_bytes(
+ arena_ind);
+ /*
+ * An edge case is the deallocation above triggering the
+ * tcache GC event, in which case the stashed pointers
+ * may get flushed immediately, before returning from
+ * free(). Treat these cases as checked already.
+ */
+ if (stashed_after <= stashed_before) {
+ fake_abort_called = true;
+ }
+ }
+ /* Flush tcache (including stashed). */
+ assert_d_eq(mallctl("thread.tcache.flush", NULL, NULL, NULL, 0),
+ 0, "Unexpected tcache flush failure");
+ }
+ expect_true(junked, "Aligned ptr not junked");
+ if (write_after_free) {
+ test_write_after_free_post();
+ }
+
+ dallocx(items, flags);
+ do_arena_destroy(arena_ind);
+}
+
+TEST_BEGIN(test_read_after_free) {
+ test_skip_if(!uaf_detection_enabled());
+
+ test_use_after_free(sizeof(void *), /* write_after_free */ false);
+ test_use_after_free(sizeof(void *) + 1, /* write_after_free */ false);
+ test_use_after_free(16, /* write_after_free */ false);
+ test_use_after_free(20, /* write_after_free */ false);
+ test_use_after_free(32, /* write_after_free */ false);
+ test_use_after_free(33, /* write_after_free */ false);
+ test_use_after_free(48, /* write_after_free */ false);
+ test_use_after_free(64, /* write_after_free */ false);
+ test_use_after_free(65, /* write_after_free */ false);
+ test_use_after_free(129, /* write_after_free */ false);
+ test_use_after_free(255, /* write_after_free */ false);
+ test_use_after_free(256, /* write_after_free */ false);
+}
+TEST_END
+
+TEST_BEGIN(test_write_after_free) {
+ test_skip_if(!uaf_detection_enabled());
+
+ test_use_after_free(sizeof(void *), /* write_after_free */ true);
+ test_use_after_free(sizeof(void *) + 1, /* write_after_free */ true);
+ test_use_after_free(16, /* write_after_free */ true);
+ test_use_after_free(20, /* write_after_free */ true);
+ test_use_after_free(32, /* write_after_free */ true);
+ test_use_after_free(33, /* write_after_free */ true);
+ test_use_after_free(48, /* write_after_free */ true);
+ test_use_after_free(64, /* write_after_free */ true);
+ test_use_after_free(65, /* write_after_free */ true);
+ test_use_after_free(129, /* write_after_free */ true);
+ test_use_after_free(255, /* write_after_free */ true);
+ test_use_after_free(256, /* write_after_free */ true);
+}
+TEST_END
+
+static bool
+check_allocated_intact(void **allocated, size_t n_alloc) {
+ for (unsigned i = 0; i < n_alloc; i++) {
+ void *ptr = *(void **)allocated[i];
+ bool found = false;
+ for (unsigned j = 0; j < n_alloc; j++) {
+ if (ptr == allocated[j]) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+TEST_BEGIN(test_use_after_free_integration) {
+ test_skip_if(!uaf_detection_enabled());
+
+ unsigned arena_ind = do_arena_create(-1, -1);
+ int flags = MALLOCX_ARENA(arena_ind);
+
+ size_t n_alloc = san_uaf_align * 2;
+ void **allocated = mallocx(n_alloc * sizeof(void *), flags);
+ assert_ptr_not_null(allocated, "Unexpected mallocx failure");
+
+ for (unsigned i = 0; i < n_alloc; i++) {
+ allocated[i] = mallocx(sizeof(void *) * 8, flags);
+ assert_ptr_not_null(allocated[i], "Unexpected mallocx failure");
+ if (i > 0) {
+ /* Emulate a circular list. */
+ *(void **)allocated[i] = allocated[i - 1];
+ }
+ }
+ *(void **)allocated[0] = allocated[n_alloc - 1];
+ expect_true(check_allocated_intact(allocated, n_alloc),
+ "Allocated data corrupted");
+
+ for (unsigned i = 0; i < n_alloc; i++) {
+ free(allocated[i]);
+ }
+ /* Read-after-free */
+ expect_false(check_allocated_intact(allocated, n_alloc),
+ "Junk-filling not detected");
+
+ test_write_after_free_pre();
+ for (unsigned i = 0; i < n_alloc; i++) {
+ allocated[i] = mallocx(sizeof(void *), flags);
+ assert_ptr_not_null(allocated[i], "Unexpected mallocx failure");
+ *(void **)allocated[i] = (void *)(uintptr_t)i;
+ }
+ /* Write-after-free */
+ for (unsigned i = 0; i < n_alloc; i++) {
+ free(allocated[i]);
+ *(void **)allocated[i] = NULL;
+ }
+ test_write_after_free_post();
+}
+TEST_END
+
+int
+main(void) {
+ return test(
+ test_read_after_free,
+ test_write_after_free,
+ test_use_after_free_integration);
+}
diff --git a/deps/jemalloc/test/unit/witness.c b/deps/jemalloc/test/unit/witness.c
index 5986da400..5a6c44827 100644
--- a/deps/jemalloc/test/unit/witness.c
+++ b/deps/jemalloc/test/unit/witness.c
@@ -34,7 +34,7 @@ witness_depth_error_intercept(const witness_list_t *witnesses,
static int
witness_comp(const witness_t *a, void *oa, const witness_t *b, void *ob) {
- assert_u_eq(a->rank, b->rank, "Witnesses should have equal rank");
+ expect_u_eq(a->rank, b->rank, "Witnesses should have equal rank");
assert(oa == (void *)a);
assert(ob == (void *)b);
@@ -45,7 +45,7 @@ witness_comp(const witness_t *a, void *oa, const witness_t *b, void *ob) {
static int
witness_comp_reverse(const witness_t *a, void *oa, const witness_t *b,
void *ob) {
- assert_u_eq(a->rank, b->rank, "Witnesses should have equal rank");
+ expect_u_eq(a->rank, b->rank, "Witnesses should have equal rank");
assert(oa == (void *)a);
assert(ob == (void *)b);
@@ -121,9 +121,9 @@ TEST_BEGIN(test_witness_comp) {
witness_init(&c, "c", 1, witness_comp_reverse, &c);
witness_assert_not_owner(&witness_tsdn, &c);
- assert_false(saw_lock_error, "Unexpected witness lock error");
+ expect_false(saw_lock_error, "Unexpected witness lock error");
witness_lock(&witness_tsdn, &c);
- assert_true(saw_lock_error, "Expected witness lock error");
+ expect_true(saw_lock_error, "Expected witness lock error");
witness_unlock(&witness_tsdn, &c);
witness_assert_depth(&witness_tsdn, 1);
@@ -131,9 +131,9 @@ TEST_BEGIN(test_witness_comp) {
witness_init(&d, "d", 1, NULL, NULL);
witness_assert_not_owner(&witness_tsdn, &d);
- assert_false(saw_lock_error, "Unexpected witness lock error");
+ expect_false(saw_lock_error, "Unexpected witness lock error");
witness_lock(&witness_tsdn, &d);
- assert_true(saw_lock_error, "Expected witness lock error");
+ expect_true(saw_lock_error, "Expected witness lock error");
witness_unlock(&witness_tsdn, &d);
witness_assert_depth(&witness_tsdn, 1);
@@ -162,9 +162,9 @@ TEST_BEGIN(test_witness_reversal) {
witness_lock(&witness_tsdn, &b);
witness_assert_depth(&witness_tsdn, 1);
- assert_false(saw_lock_error, "Unexpected witness lock error");
+ expect_false(saw_lock_error, "Unexpected witness lock error");
witness_lock(&witness_tsdn, &a);
- assert_true(saw_lock_error, "Expected witness lock error");
+ expect_true(saw_lock_error, "Expected witness lock error");
witness_unlock(&witness_tsdn, &a);
witness_assert_depth(&witness_tsdn, 1);
@@ -195,11 +195,11 @@ TEST_BEGIN(test_witness_recursive) {
witness_init(&a, "a", 1, NULL, NULL);
witness_lock(&witness_tsdn, &a);
- assert_false(saw_lock_error, "Unexpected witness lock error");
- assert_false(saw_not_owner_error, "Unexpected witness not owner error");
+ expect_false(saw_lock_error, "Unexpected witness lock error");
+ expect_false(saw_not_owner_error, "Unexpected witness not owner error");
witness_lock(&witness_tsdn, &a);
- assert_true(saw_lock_error, "Expected witness lock error");
- assert_true(saw_not_owner_error, "Expected witness not owner error");
+ expect_true(saw_lock_error, "Expected witness lock error");
+ expect_true(saw_not_owner_error, "Expected witness not owner error");
witness_unlock(&witness_tsdn, &a);
@@ -225,9 +225,9 @@ TEST_BEGIN(test_witness_unlock_not_owned) {
witness_init(&a, "a", 1, NULL, NULL);
- assert_false(saw_owner_error, "Unexpected owner error");
+ expect_false(saw_owner_error, "Unexpected owner error");
witness_unlock(&witness_tsdn, &a);
- assert_true(saw_owner_error, "Expected owner error");
+ expect_true(saw_owner_error, "Expected owner error");
witness_assert_lockless(&witness_tsdn);
@@ -250,14 +250,14 @@ TEST_BEGIN(test_witness_depth) {
witness_init(&a, "a", 1, NULL, NULL);
- assert_false(saw_depth_error, "Unexpected depth error");
+ expect_false(saw_depth_error, "Unexpected depth error");
witness_assert_lockless(&witness_tsdn);
witness_assert_depth(&witness_tsdn, 0);
witness_lock(&witness_tsdn, &a);
witness_assert_lockless(&witness_tsdn);
witness_assert_depth(&witness_tsdn, 0);
- assert_true(saw_depth_error, "Expected depth error");
+ expect_true(saw_depth_error, "Expected depth error");
witness_unlock(&witness_tsdn, &a);
diff --git a/deps/jemalloc/test/unit/zero.c b/deps/jemalloc/test/unit/zero.c
index 271fd5cba..d3e81f1bc 100644
--- a/deps/jemalloc/test/unit/zero.c
+++ b/deps/jemalloc/test/unit/zero.c
@@ -8,21 +8,21 @@ test_zero(size_t sz_min, size_t sz_max) {
sz_prev = 0;
s = (uint8_t *)mallocx(sz_min, 0);
- assert_ptr_not_null((void *)s, "Unexpected mallocx() failure");
+ expect_ptr_not_null((void *)s, "Unexpected mallocx() failure");
for (sz = sallocx(s, 0); sz <= sz_max;
sz_prev = sz, sz = sallocx(s, 0)) {
if (sz_prev > 0) {
- assert_u_eq(s[0], MAGIC,
+ expect_u_eq(s[0], MAGIC,
"Previously allocated byte %zu/%zu is corrupted",
ZU(0), sz_prev);
- assert_u_eq(s[sz_prev-1], MAGIC,
+ expect_u_eq(s[sz_prev-1], MAGIC,
"Previously allocated byte %zu/%zu is corrupted",
sz_prev-1, sz_prev);
}
for (i = sz_prev; i < sz; i++) {
- assert_u_eq(s[i], 0x0,
+ expect_u_eq(s[i], 0x0,
"Newly allocated byte %zu/%zu isn't zero-filled",
i, sz);
s[i] = MAGIC;
@@ -30,7 +30,7 @@ test_zero(size_t sz_min, size_t sz_max) {
if (xallocx(s, sz+1, 0, 0) == sz) {
s = (uint8_t *)rallocx(s, sz+1, 0);
- assert_ptr_not_null((void *)s,
+ expect_ptr_not_null((void *)s,
"Unexpected rallocx() failure");
}
}
diff --git a/deps/jemalloc/test/unit/zero_realloc_abort.c b/deps/jemalloc/test/unit/zero_realloc_abort.c
new file mode 100644
index 000000000..a880d104b
--- /dev/null
+++ b/deps/jemalloc/test/unit/zero_realloc_abort.c
@@ -0,0 +1,26 @@
+#include "test/jemalloc_test.h"
+
+#include <signal.h>
+
+static bool abort_called = false;
+
+void set_abort_called() {
+ abort_called = true;
+};
+
+TEST_BEGIN(test_realloc_abort) {
+ abort_called = false;
+ safety_check_set_abort(&set_abort_called);
+ void *ptr = mallocx(42, 0);
+ expect_ptr_not_null(ptr, "Unexpected mallocx error");
+ ptr = realloc(ptr, 0);
+ expect_true(abort_called, "Realloc with zero size didn't abort");
+}
+TEST_END
+
+int
+main(void) {
+ return test(
+ test_realloc_abort);
+}
+
diff --git a/deps/jemalloc/test/unit/zero_realloc_abort.sh b/deps/jemalloc/test/unit/zero_realloc_abort.sh
new file mode 100644
index 000000000..37daeeaa1
--- /dev/null
+++ b/deps/jemalloc/test/unit/zero_realloc_abort.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+export MALLOC_CONF="zero_realloc:abort"
diff --git a/deps/jemalloc/test/unit/zero_realloc_alloc.c b/deps/jemalloc/test/unit/zero_realloc_alloc.c
new file mode 100644
index 000000000..65e07bdbe
--- /dev/null
+++ b/deps/jemalloc/test/unit/zero_realloc_alloc.c
@@ -0,0 +1,48 @@
+#include "test/jemalloc_test.h"
+
+static uint64_t
+allocated() {
+ if (!config_stats) {
+ return 0;
+ }
+ uint64_t allocated;
+ size_t sz = sizeof(allocated);
+ expect_d_eq(mallctl("thread.allocated", (void *)&allocated, &sz, NULL,
+ 0), 0, "Unexpected mallctl failure");
+ return allocated;
+}
+
+static uint64_t
+deallocated() {
+ if (!config_stats) {
+ return 0;
+ }
+ uint64_t deallocated;
+ size_t sz = sizeof(deallocated);
+ expect_d_eq(mallctl("thread.deallocated", (void *)&deallocated, &sz,
+ NULL, 0), 0, "Unexpected mallctl failure");
+ return deallocated;
+}
+
+TEST_BEGIN(test_realloc_alloc) {
+ void *ptr = mallocx(1, 0);
+ expect_ptr_not_null(ptr, "Unexpected mallocx error");
+ uint64_t allocated_before = allocated();
+ uint64_t deallocated_before = deallocated();
+ ptr = realloc(ptr, 0);
+ uint64_t allocated_after = allocated();
+ uint64_t deallocated_after = deallocated();
+ if (config_stats) {
+ expect_u64_lt(allocated_before, allocated_after,
+ "Unexpected stats change");
+ expect_u64_lt(deallocated_before, deallocated_after,
+ "Unexpected stats change");
+ }
+ dallocx(ptr, 0);
+}
+TEST_END
+int
+main(void) {
+ return test(
+ test_realloc_alloc);
+}
diff --git a/deps/jemalloc/test/unit/zero_realloc_alloc.sh b/deps/jemalloc/test/unit/zero_realloc_alloc.sh
new file mode 100644
index 000000000..802687cff
--- /dev/null
+++ b/deps/jemalloc/test/unit/zero_realloc_alloc.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+export MALLOC_CONF="zero_realloc:alloc"
diff --git a/deps/jemalloc/test/unit/zero_realloc_free.c b/deps/jemalloc/test/unit/zero_realloc_free.c
new file mode 100644
index 000000000..baed86c92
--- /dev/null
+++ b/deps/jemalloc/test/unit/zero_realloc_free.c
@@ -0,0 +1,33 @@
+#include "test/jemalloc_test.h"
+
+static uint64_t
+deallocated() {
+ if (!config_stats) {
+ return 0;
+ }
+ uint64_t deallocated;
+ size_t sz = sizeof(deallocated);
+ expect_d_eq(mallctl("thread.deallocated", (void *)&deallocated, &sz,
+ NULL, 0), 0, "Unexpected mallctl failure");
+ return deallocated;
+}
+
+TEST_BEGIN(test_realloc_free) {
+ void *ptr = mallocx(42, 0);
+ expect_ptr_not_null(ptr, "Unexpected mallocx error");
+ uint64_t deallocated_before = deallocated();
+ ptr = realloc(ptr, 0);
+ uint64_t deallocated_after = deallocated();
+ expect_ptr_null(ptr, "Realloc didn't free");
+ if (config_stats) {
+ expect_u64_gt(deallocated_after, deallocated_before,
+ "Realloc didn't free");
+ }
+}
+TEST_END
+
+int
+main(void) {
+ return test(
+ test_realloc_free);
+}
diff --git a/deps/jemalloc/test/unit/zero_realloc_free.sh b/deps/jemalloc/test/unit/zero_realloc_free.sh
new file mode 100644
index 000000000..51b01c915
--- /dev/null
+++ b/deps/jemalloc/test/unit/zero_realloc_free.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+export MALLOC_CONF="zero_realloc:free"
diff --git a/deps/jemalloc/test/unit/zero_reallocs.c b/deps/jemalloc/test/unit/zero_reallocs.c
new file mode 100644
index 000000000..66c7a404a
--- /dev/null
+++ b/deps/jemalloc/test/unit/zero_reallocs.c
@@ -0,0 +1,40 @@
+#include "test/jemalloc_test.h"
+
+static size_t
+zero_reallocs() {
+ if (!config_stats) {
+ return 0;
+ }
+ size_t count = 12345;
+ size_t sz = sizeof(count);
+
+ expect_d_eq(mallctl("stats.zero_reallocs", (void *)&count, &sz,
+ NULL, 0), 0, "Unexpected mallctl failure");
+ return count;
+}
+
+TEST_BEGIN(test_zero_reallocs) {
+ test_skip_if(!config_stats);
+
+ for (size_t i = 0; i < 100; ++i) {
+ void *ptr = mallocx(i * i + 1, 0);
+ expect_ptr_not_null(ptr, "Unexpected mallocx error");
+ size_t count = zero_reallocs();
+ expect_zu_eq(i, count, "Incorrect zero realloc count");
+ ptr = realloc(ptr, 0);
+ expect_ptr_null(ptr, "Realloc didn't free");
+ count = zero_reallocs();
+ expect_zu_eq(i + 1, count, "Realloc didn't adjust count");
+ }
+}
+TEST_END
+
+int
+main(void) {
+ /*
+ * We expect explicit counts; reentrant tests run multiple times, so
+ * counts leak across runs.
+ */
+ return test_no_reentrancy(
+ test_zero_reallocs);
+}
diff --git a/deps/jemalloc/test/unit/zero_reallocs.sh b/deps/jemalloc/test/unit/zero_reallocs.sh
new file mode 100644
index 000000000..51b01c915
--- /dev/null
+++ b/deps/jemalloc/test/unit/zero_reallocs.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+export MALLOC_CONF="zero_realloc:free"
diff --git a/runtest-moduleapi b/runtest-moduleapi
index d5010edc1..ff685afb6 100755
--- a/runtest-moduleapi
+++ b/runtest-moduleapi
@@ -54,4 +54,5 @@ $TCLSH tests/test_helper.tcl \
--single unit/moduleapi/postnotifications \
--single unit/moduleapi/async_rm_call \
--single unit/moduleapi/moduleauth \
+--single unit/moduleapi/rdbloadsave \
"${@}"
diff --git a/sentinel.conf b/sentinel.conf
index 0f0265c97..60eb9ff4d 100644
--- a/sentinel.conf
+++ b/sentinel.conf
@@ -32,6 +32,16 @@ loglevel notice
# output for logging but daemonize, logs will be sent to /dev/null
logfile ""
+# To enable logging to the system logger, just set 'syslog-enabled' to yes,
+# and optionally update the other syslog parameters to suit your needs.
+# syslog-enabled no
+
+# Specify the syslog identity.
+# syslog-ident sentinel
+
+# Specify the syslog facility. Must be USER or between LOCAL0-LOCAL7.
+# syslog-facility local0
+
# sentinel announce-ip <ip>
# sentinel announce-port <port>
#
diff --git a/src/Makefile b/src/Makefile
index b0727db2c..4708d1a0a 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -15,9 +15,14 @@
release_hdr := $(shell sh -c './mkreleasehdr.sh')
uname_S := $(shell sh -c 'uname -s 2>/dev/null || echo not')
uname_M := $(shell sh -c 'uname -m 2>/dev/null || echo not')
+CLANG := $(findstring clang,$(shell sh -c '$(CC) --version | head -1'))
OPTIMIZATION?=-O3
ifeq ($(OPTIMIZATION),-O3)
- REDIS_CFLAGS+=-flto=auto
+ ifeq (clang,$(CLANG))
+ REDIS_CFLAGS+=-flto
+ else
+ REDIS_CFLAGS+=-flto=auto
+ endif
REDIS_LDFLAGS+=-flto
endif
DEPENDENCY_TARGETS=hiredis linenoise lua hdr_histogram fpconv
@@ -28,14 +33,14 @@ STD=-pedantic -DREDIS_STATIC=''
# Use -Wno-c11-extensions on clang, either where explicitly used or on
# platforms we can assume it's being used.
-ifneq (,$(findstring clang,$(CC)))
+ifeq (clang,$(CLANG))
STD+=-Wno-c11-extensions
else
ifneq (,$(findstring FreeBSD,$(uname_S)))
STD+=-Wno-c11-extensions
endif
endif
-WARN=-Wall -W -Wno-missing-field-initializers -Werror=deprecated-declarations
+WARN=-Wall -W -Wno-missing-field-initializers -Werror=deprecated-declarations -Wstrict-prototypes
OPT=$(OPTIMIZATION)
# Detect if the compiler supports C11 _Atomic.
@@ -331,18 +336,18 @@ QUIET_INSTALL = @printf ' %b %b\n' $(LINKCOLOR)INSTALL$(ENDCOLOR) $(BINCOLOR)
endif
ifneq (, $(findstring LOG_REQ_RES, $(REDIS_CFLAGS)))
- COMMANDS_FILENAME=commands_with_reply_schema
+ COMMANDS_DEF_FILENAME=commands_with_reply_schema
GEN_COMMANDS_FLAGS=--with-reply-schema
else
- COMMANDS_FILENAME=commands
+ COMMANDS_DEF_FILENAME=commands
GEN_COMMANDS_FLAGS=
endif
REDIS_SERVER_NAME=redis-server$(PROG_SUFFIX)
REDIS_SENTINEL_NAME=redis-sentinel$(PROG_SUFFIX)
-REDIS_SERVER_OBJ=adlist.o quicklist.o ae.o anet.o dict.o server.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o cluster.o crc16.o endianconv.o slowlog.o eval.o bio.o rio.o rand.o memtest.o syscheck.o crcspeed.o crc64.o bitops.o sentinel.o notify.o setproctitle.o blocked.o hyperloglog.o latency.o sparkline.o redis-check-rdb.o redis-check-aof.o geo.o lazyfree.o module.o evict.o expire.o geohash.o geohash_helper.o childinfo.o defrag.o siphash.o rax.o t_stream.o listpack.o localtime.o lolwut.o lolwut5.o lolwut6.o acl.o tracking.o socket.o tls.o sha256.o timeout.o setcpuaffinity.o monotonic.o mt19937-64.o resp_parser.o call_reply.o script_lua.o script.o functions.o function_lua.o $(COMMANDS_FILENAME).o strl.o connection.o unix.o logreqres.o
+REDIS_SERVER_OBJ=adlist.o quicklist.o ae.o anet.o dict.o server.o sds.o zmalloc.o lzf_c.o lzf_d.o pqsort.o zipmap.o sha1.o ziplist.o release.o networking.o util.o object.o db.o replication.o rdb.o t_string.o t_list.o t_set.o t_zset.o t_hash.o config.o aof.o pubsub.o multi.o debug.o sort.o intset.o syncio.o cluster.o crc16.o endianconv.o slowlog.o eval.o bio.o rio.o rand.o memtest.o syscheck.o crcspeed.o crc64.o bitops.o sentinel.o notify.o setproctitle.o blocked.o hyperloglog.o latency.o sparkline.o redis-check-rdb.o redis-check-aof.o geo.o lazyfree.o module.o evict.o expire.o geohash.o geohash_helper.o childinfo.o defrag.o siphash.o rax.o t_stream.o listpack.o localtime.o lolwut.o lolwut5.o lolwut6.o acl.o tracking.o socket.o tls.o sha256.o timeout.o setcpuaffinity.o monotonic.o mt19937-64.o resp_parser.o call_reply.o script_lua.o script.o functions.o function_lua.o commands.o strl.o connection.o unix.o logreqres.o
REDIS_CLI_NAME=redis-cli$(PROG_SUFFIX)
-REDIS_CLI_OBJ=anet.o adlist.o dict.o redis-cli.o zmalloc.o release.o ae.o redisassert.o crcspeed.o crc64.o siphash.o crc16.o monotonic.o cli_common.o mt19937-64.o strl.o
+REDIS_CLI_OBJ=anet.o adlist.o dict.o redis-cli.o zmalloc.o release.o ae.o redisassert.o crcspeed.o crc64.o siphash.o crc16.o monotonic.o cli_common.o mt19937-64.o strl.o cli_commands.o
REDIS_BENCHMARK_NAME=redis-benchmark$(PROG_SUFFIX)
REDIS_BENCHMARK_OBJ=ae.o anet.o redis-benchmark.o adlist.o dict.o zmalloc.o redisassert.o release.o crcspeed.o crc64.o siphash.o crc16.o monotonic.o cli_common.o mt19937-64.o strl.o
REDIS_CHECK_RDB_NAME=redis-check-rdb$(PROG_SUFFIX)
@@ -430,13 +435,15 @@ DEP = $(REDIS_SERVER_OBJ:%.o=%.d) $(REDIS_CLI_OBJ:%.o=%.d) $(REDIS_BENCHMARK_OBJ
%.o: %.c .make-prerequisites
$(REDIS_CC) -MMD -o $@ -c $<
-# The file commands.c is checked in and doesn't normally need to be rebuilt. It
+# The file commands.def is checked in and doesn't normally need to be rebuilt. It
# is built only if python is available and its prereqs are modified.
ifneq (,$(PYTHON))
-$(COMMANDS_FILENAME).c: commands/*.json ../utils/generate-command-code.py
+$(COMMANDS_DEF_FILENAME).def: commands/*.json ../utils/generate-command-code.py
$(QUIET_GEN)$(PYTHON) ../utils/generate-command-code.py $(GEN_COMMANDS_FLAGS)
endif
+commands.c: $(COMMANDS_DEF_FILENAME).def
+
clean:
rm -rf $(REDIS_SERVER_NAME) $(REDIS_SENTINEL_NAME) $(REDIS_CLI_NAME) $(REDIS_BENCHMARK_NAME) $(REDIS_CHECK_RDB_NAME) $(REDIS_CHECK_AOF_NAME) *.o *.gcda *.gcno *.gcov redis.info lcov-html Makefile.dep *.so
rm -f $(DEP)
diff --git a/src/acl.c b/src/acl.c
index 8b571d86e..ebf152aaa 100644
--- a/src/acl.c
+++ b/src/acl.c
@@ -642,7 +642,7 @@ void ACLSetSelectorCommandBitsForCategory(dict *commands, aclSelector *selector,
/* This function is responsible for recomputing the command bits for all selectors of the existing users.
* It uses the 'command_rules', a string representation of the ordered categories and commands,
* to recompute the command bits. */
-void ACLRecomputeCommandBitsFromCommandRulesAllUsers() {
+void ACLRecomputeCommandBitsFromCommandRulesAllUsers(void) {
raxIterator ri;
raxStart(&ri,Users);
raxSeek(&ri,"^",NULL,0);
@@ -1891,6 +1891,13 @@ void ACLKillPubsubClientsIfNeeded(user *new, user *original) {
listRewind(original->selectors,&li);
while((ln = listNext(&li)) && match) {
aclSelector *s = (aclSelector *) listNodeValue(ln);
+ /* If any of the original selectors has the all-channels permission, but
+ * the new ones don't (this is checked earlier in this function), then the
+ * new list is not a strict superset of the original. */
+ if (s->flags & SELECTOR_FLAG_ALLCHANNELS) {
+ match = 0;
+ break;
+ }
listRewind(s->channels, &lpi);
while((lpn = listNext(&lpi)) && match) {
if (!listSearchKey(upcoming, listNodeValue(lpn))) {
diff --git a/src/ae.c b/src/ae.c
index 468859c06..1b6422b2d 100644
--- a/src/ae.c
+++ b/src/ae.c
@@ -103,7 +103,11 @@ int aeGetSetSize(aeEventLoop *eventLoop) {
return eventLoop->setsize;
}
-/* Tells the next iteration/s of the event processing to set timeout of 0. */
+/*
+ * Tell the event processing to change the wait timeout as soon as possible.
+ *
+ * Note: it just means you turn on/off the global AE_DONT_WAIT.
+ */
void aeSetDontWait(aeEventLoop *eventLoop, int noWait) {
if (noWait)
eventLoop->flags |= AE_DONT_WAIT;
@@ -361,44 +365,35 @@ int aeProcessEvents(aeEventLoop *eventLoop, int flags)
/* Nothing to do? return ASAP */
if (!(flags & AE_TIME_EVENTS) && !(flags & AE_FILE_EVENTS)) return 0;
- /* Note that we want to call select() even if there are no
+ /* Note that we want to call aeApiPoll() even if there are no
* file events to process as long as we want to process time
* events, in order to sleep until the next time event is ready
* to fire. */
if (eventLoop->maxfd != -1 ||
((flags & AE_TIME_EVENTS) && !(flags & AE_DONT_WAIT))) {
int j;
- struct timeval tv, *tvp;
- int64_t usUntilTimer = -1;
+ struct timeval tv, *tvp = NULL; /* NULL means infinite wait. */
+ int64_t usUntilTimer;
- if (flags & AE_TIME_EVENTS && !(flags & AE_DONT_WAIT))
- usUntilTimer = usUntilEarliestTimer(eventLoop);
+ if (eventLoop->beforesleep != NULL && (flags & AE_CALL_BEFORE_SLEEP))
+ eventLoop->beforesleep(eventLoop);
- if (usUntilTimer >= 0) {
- tv.tv_sec = usUntilTimer / 1000000;
- tv.tv_usec = usUntilTimer % 1000000;
+ /* The eventLoop->flags may be changed inside beforesleep.
+ * So we should check it after beforesleep be called. At the same time,
+ * the parameter flags always should have the highest priority.
+ * That is to say, once the parameter flag is set to AE_DONT_WAIT,
+ * no matter what value eventLoop->flags is set to, we should ignore it. */
+ if ((flags & AE_DONT_WAIT) || (eventLoop->flags & AE_DONT_WAIT)) {
+ tv.tv_sec = tv.tv_usec = 0;
tvp = &tv;
- } else {
- /* If we have to check for events but need to return
- * ASAP because of AE_DONT_WAIT we need to set the timeout
- * to zero */
- if (flags & AE_DONT_WAIT) {
- tv.tv_sec = tv.tv_usec = 0;
+ } else if (flags & AE_TIME_EVENTS) {
+ usUntilTimer = usUntilEarliestTimer(eventLoop);
+ if (usUntilTimer >= 0) {
+ tv.tv_sec = usUntilTimer / 1000000;
+ tv.tv_usec = usUntilTimer % 1000000;
tvp = &tv;
- } else {
- /* Otherwise we can block */
- tvp = NULL; /* wait forever */
}
}
-
- if (eventLoop->flags & AE_DONT_WAIT) {
- tv.tv_sec = tv.tv_usec = 0;
- tvp = &tv;
- }
-
- if (eventLoop->beforesleep != NULL && flags & AE_CALL_BEFORE_SLEEP)
- eventLoop->beforesleep(eventLoop);
-
/* Call the multiplexing API, will return only on timeout or when
* some event fires. */
numevents = aeApiPoll(eventLoop, tvp);
diff --git a/src/anet.c b/src/anet.c
index 00c30b83d..790ea7e0a 100644
--- a/src/anet.c
+++ b/src/anet.c
@@ -697,3 +697,9 @@ int anetSetSockMarkId(char *err, int fd, uint32_t id) {
return ANET_OK;
#endif
}
+
+int anetIsFifo(char *filepath) {
+ struct stat sb;
+ if (stat(filepath, &sb) == -1) return 0;
+ return S_ISFIFO(sb.st_mode);
+}
diff --git a/src/anet.h b/src/anet.h
index b571e52c1..b13c14f77 100644
--- a/src/anet.h
+++ b/src/anet.h
@@ -70,5 +70,6 @@ int anetFormatAddr(char *fmt, size_t fmt_len, char *ip, int port);
int anetPipe(int fds[2], int read_flags, int write_flags);
int anetSetSockMarkId(char *err, int fd, uint32_t id);
int anetGetError(int fd);
+int anetIsFifo(char *filepath);
#endif
diff --git a/src/aof.c b/src/aof.c
index 12bd74376..468d577f8 100644
--- a/src/aof.c
+++ b/src/aof.c
@@ -164,12 +164,12 @@ void aofManifestFree(aofManifest *am) {
zfree(am);
}
-sds getAofManifestFileName() {
+sds getAofManifestFileName(void) {
return sdscatprintf(sdsempty(), "%s%s", server.aof_filename,
MANIFEST_NAME_SUFFIX);
}
-sds getTempAofManifestFileName() {
+sds getTempAofManifestFileName(void) {
return sdscatprintf(sdsempty(), "%s%s%s", TEMP_FILE_NAME_PREFIX,
server.aof_filename, MANIFEST_NAME_SUFFIX);
}
@@ -464,7 +464,7 @@ sds getNewIncrAofName(aofManifest *am) {
}
/* Get temp INCR type AOF name. */
-sds getTempIncrAofName() {
+sds getTempIncrAofName(void) {
return sdscatprintf(sdsempty(), "%s%s%s", TEMP_FILE_NAME_PREFIX, server.aof_filename,
INCR_FILE_SUFFIX);
}
@@ -692,7 +692,7 @@ int aofDelHistoryFiles(void) {
}
/* Used to clean up temp INCR AOF when AOFRW fails. */
-void aofDelTempIncrAofFile() {
+void aofDelTempIncrAofFile(void) {
sds aof_filename = getTempIncrAofName();
sds aof_filepath = makePath(server.aof_dirname, aof_filename);
serverLog(LL_NOTICE, "Removing the temp incr aof file %s in the background", aof_filename);
@@ -758,6 +758,7 @@ void aofOpenIfNeededOnServerStart(void) {
}
server.aof_last_incr_size = getAppendOnlyFileSize(aof_name, NULL);
+ server.aof_last_incr_fsync_offset = server.aof_last_incr_size;
if (incr_aof_len) {
serverLog(LL_NOTICE, "Opening AOF incr file %s on server start", aof_name);
@@ -832,13 +833,14 @@ int openNewIncrAofForAppend(void) {
* is already synced at this point so fsync doesn't matter. */
if (server.aof_fd != -1) {
aof_background_fsync_and_close(server.aof_fd);
- server.aof_fsync_offset = server.aof_current_size;
server.aof_last_fsync = server.unixtime;
}
server.aof_fd = newfd;
/* Reset the aof_last_incr_size. */
server.aof_last_incr_size = 0;
+ /* Reset the aof_last_incr_fsync_offset. */
+ server.aof_last_incr_fsync_offset = 0;
/* Update `server.aof_manifest`. */
if (temp_am) aofManifestFreeAndUpdate(temp_am);
return C_OK;
@@ -952,7 +954,6 @@ void stopAppendOnly(void) {
if (redis_fsync(server.aof_fd) == -1) {
serverLog(LL_WARNING,"Fail to fsync the AOF file: %s",strerror(errno));
} else {
- server.aof_fsync_offset = server.aof_current_size;
server.aof_last_fsync = server.unixtime;
}
close(server.aof_fd);
@@ -962,6 +963,7 @@ void stopAppendOnly(void) {
server.aof_state = AOF_OFF;
server.aof_rewrite_scheduled = 0;
server.aof_last_incr_size = 0;
+ server.aof_last_incr_fsync_offset = 0;
server.fsynced_reploff = -1;
atomicSet(server.fsynced_reploff_pending, 0);
killAppendOnlyChild();
@@ -1083,10 +1085,19 @@ void flushAppendOnlyFile(int force) {
* stop write commands before fsync called in one second,
* the data in page cache cannot be flushed in time. */
if (server.aof_fsync == AOF_FSYNC_EVERYSEC &&
- server.aof_fsync_offset != server.aof_current_size &&
+ server.aof_last_incr_fsync_offset != server.aof_last_incr_size &&
server.unixtime > server.aof_last_fsync &&
!(sync_in_progress = aofFsyncInProgress())) {
goto try_fsync;
+
+ /* Check if we need to do fsync even the aof buffer is empty,
+ * the reason is described in the previous AOF_FSYNC_EVERYSEC block,
+ * and AOF_FSYNC_ALWAYS is also checked here to handle a case where
+ * aof_fsync is changed from everysec to always. */
+ } else if (server.aof_fsync == AOF_FSYNC_ALWAYS &&
+ server.aof_last_incr_fsync_offset != server.aof_last_incr_size)
+ {
+ goto try_fsync;
} else {
return;
}
@@ -1253,14 +1264,14 @@ try_fsync:
}
latencyEndMonitor(latency);
latencyAddSampleIfNeeded("aof-fsync-always",latency);
- server.aof_fsync_offset = server.aof_current_size;
+ server.aof_last_incr_fsync_offset = server.aof_last_incr_size;
server.aof_last_fsync = server.unixtime;
atomicSet(server.fsynced_reploff_pending, server.master_repl_offset);
- } else if ((server.aof_fsync == AOF_FSYNC_EVERYSEC &&
- server.unixtime > server.aof_last_fsync)) {
+ } else if (server.aof_fsync == AOF_FSYNC_EVERYSEC &&
+ server.unixtime > server.aof_last_fsync) {
if (!sync_in_progress) {
aof_background_fsync(server.aof_fd);
- server.aof_fsync_offset = server.aof_current_size;
+ server.aof_last_incr_fsync_offset = server.aof_last_incr_size;
}
server.aof_last_fsync = server.unixtime;
}
@@ -1766,7 +1777,6 @@ int loadAppendOnlyFiles(aofManifest *am) {
* executed early, but that shouldn't be a problem since everything will be
* fine after the first AOFRW. */
server.aof_rewrite_base_size = base_size;
- server.aof_fsync_offset = server.aof_current_size;
cleanup:
stopLoading(ret == AOF_OK || ret == AOF_TRUNCATED);
@@ -2666,13 +2676,10 @@ void backgroundRewriteDoneHandler(int exitcode, int bysignal) {
/* We can safely let `server.aof_manifest` point to 'temp_am' and free the previous one. */
aofManifestFreeAndUpdate(temp_am);
- if (server.aof_fd != -1) {
+ if (server.aof_state != AOF_OFF) {
/* AOF enabled. */
- server.aof_selected_db = -1; /* Make sure SELECT is re-issued */
server.aof_current_size = getAppendOnlyFileSize(new_base_filename, NULL) + server.aof_last_incr_size;
server.aof_rewrite_base_size = server.aof_current_size;
- server.aof_fsync_offset = server.aof_current_size;
- server.aof_last_fsync = server.unixtime;
}
/* We don't care about the return value of `aofDelHistoryFiles`, because the history
diff --git a/src/blocked.c b/src/blocked.c
index 7c10460b1..1b3a804b1 100644
--- a/src/blocked.c
+++ b/src/blocked.c
@@ -114,6 +114,7 @@ void updateStatsOnUnblock(client *c, long blocked_us, long reply_us, int had_err
updateCommandLatencyHistogram(&(c->lastcmd->latency_histogram), total_cmd_duration*1000);
/* Log the command into the Slow log if needed. */
slowlogPushCurrentCommand(c, c->lastcmd, total_cmd_duration);
+ c->duration = 0;
/* Log the reply duration event. */
latencyAddSampleIfNeeded("command-unblocking",reply_us/1000);
}
@@ -649,7 +650,7 @@ static void unblockClientOnKey(client *c, robj *key) {
* to run atomically, this is why we must enter the execution unit here before
* running the command, and exit the execution unit after calling the unblock handler (if exists).
* Notice that we also must set the current client so it will be available
- * when we will try to send the the client side caching notification (done on 'afterCommand'). */
+ * when we will try to send the client side caching notification (done on 'afterCommand'). */
client *old_client = server.current_client;
server.current_client = c;
enterExecutionUnit(1, 0);
diff --git a/src/cli_commands.c b/src/cli_commands.c
new file mode 100644
index 000000000..e56d48cfa
--- /dev/null
+++ b/src/cli_commands.c
@@ -0,0 +1,13 @@
+#include <stddef.h>
+#include "cli_commands.h"
+
+/* Definitions to configure commands.c to generate the above structs. */
+#define MAKE_CMD(name,summary,complexity,since,doc_flags,replaced,deprecated,group,group_enum,history,num_history,tips,num_tips,function,arity,flags,acl,key_specs,key_specs_num,get_keys,numargs) name,summary,group,since,numargs
+#define MAKE_ARG(name,type,key_spec_index,token,summary,since,flags,numsubargs,deprecated_since) name,type,token,since,flags,numsubargs
+#define COMMAND_ARG cliCommandArg
+#define COMMAND_STRUCT commandDocs
+#define SKIP_CMD_HISTORY_TABLE
+#define SKIP_CMD_TIPS_TABLE
+#define SKIP_CMD_KEY_SPECS_TABLE
+
+#include "commands.def"
diff --git a/src/cli_commands.h b/src/cli_commands.h
new file mode 100644
index 000000000..eb5a476e3
--- /dev/null
+++ b/src/cli_commands.h
@@ -0,0 +1,46 @@
+/* This file is used by redis-cli in place of server.h when including commands.c
+ * It contains alternative structs which omit the parts of the commands table
+ * that are not suitable for redis-cli, e.g. the command proc. */
+
+#ifndef __REDIS_CLI_COMMANDS_H
+#define __REDIS_CLI_COMMANDS_H
+
+#include <stddef.h>
+#include "commands.h"
+
+/* Syntax specifications for a command argument. */
+typedef struct cliCommandArg {
+ char *name;
+ redisCommandArgType type;
+ char *token;
+ char *since;
+ int flags;
+ int numsubargs;
+ struct cliCommandArg *subargs;
+ const char *display_text;
+
+ /*
+ * For use at runtime.
+ * Fields used to keep track of input word matches for command-line hinting.
+ */
+ int matched; /* How many input words have been matched by this argument? */
+ int matched_token; /* Has the token been matched? */
+ int matched_name; /* Has the name been matched? */
+ int matched_all; /* Has the whole argument been consumed (no hint needed)? */
+} cliCommandArg;
+
+/* Command documentation info used for help output */
+struct commandDocs {
+ char *name;
+ char *summary;
+ char *group;
+ char *since;
+ int numargs;
+ cliCommandArg *args; /* An array of the command arguments. */
+ struct commandDocs *subcommands;
+ char *params; /* A string describing the syntax of the command arguments. */
+};
+
+extern struct commandDocs redisCommandTable[];
+
+#endif
diff --git a/src/cli_common.c b/src/cli_common.c
index 7b4775cde..0b13db5dc 100644
--- a/src/cli_common.c
+++ b/src/cli_common.c
@@ -191,7 +191,7 @@ ssize_t cliWriteConn(redisContext *c, const char *buf, size_t buf_len)
/* Wrapper around OpenSSL (libssl and libcrypto) initialisation
*/
-int cliSecureInit()
+int cliSecureInit(void)
{
#ifdef USE_OPENSSL
ERR_load_crypto_strings();
diff --git a/src/cli_common.h b/src/cli_common.h
index c5c4c11aa..cffdee61d 100644
--- a/src/cli_common.h
+++ b/src/cli_common.h
@@ -37,7 +37,7 @@ int cliSecureConnection(redisContext *c, cliSSLconfig config, const char **err);
ssize_t cliWriteConn(redisContext *c, const char *buf, size_t buf_len);
-int cliSecureInit();
+int cliSecureInit(void);
sds readArgFromStdin(void);
diff --git a/src/cluster.c b/src/cluster.c
index b20225c8b..69029164e 100644
--- a/src/cluster.c
+++ b/src/cluster.c
@@ -126,7 +126,7 @@ dictType clusterNodesBlackListDictType = {
NULL /* allow to expand */
};
-static ConnectionType *connTypeOfCluster() {
+static ConnectionType *connTypeOfCluster(void) {
if (server.tls_cluster) {
return connectionTypeTls();
}
@@ -2321,18 +2321,18 @@ uint32_t getAlignedPingExtSize(uint32_t dataSize) {
return sizeof(clusterMsgPingExt) + EIGHT_BYTE_ALIGN(dataSize);
}
-uint32_t getHostnamePingExtSize() {
+uint32_t getHostnamePingExtSize(void) {
if (sdslen(myself->hostname) == 0) {
return 0;
}
return getAlignedPingExtSize(sdslen(myself->hostname) + 1);
}
-uint32_t getShardIdPingExtSize() {
+uint32_t getShardIdPingExtSize(void) {
return getAlignedPingExtSize(sizeof(clusterMsgPingExtShardId));
}
-uint32_t getForgottenNodeExtSize() {
+uint32_t getForgottenNodeExtSize(void) {
return getAlignedPingExtSize(sizeof(clusterMsgPingExtForgottenNode));
}
@@ -5559,7 +5559,7 @@ void clusterReplyMultiBulkSlots(client * c) {
setDeferredArrayLen(c, slot_replylen, num_masters);
}
-sds genClusterInfoString() {
+sds genClusterInfoString(void) {
sds info = sdsempty();
char *statestr[] = {"ok","fail"};
int slots_assigned = 0, slots_ok = 0, slots_pfail = 0, slots_fail = 0;
diff --git a/src/cluster.h b/src/cluster.h
index cabc9273f..b5ab7d5a5 100644
--- a/src/cluster.h
+++ b/src/cluster.h
@@ -423,7 +423,7 @@ void slotToChannelDel(sds channel);
void clusterUpdateMyselfHostname(void);
void clusterUpdateMyselfAnnouncedPorts(void);
sds clusterGenNodesDescription(int filter, int use_pport);
-sds genClusterInfoString();
+sds genClusterInfoString(void);
void freeClusterLink(clusterLink *link);
#endif /* __CLUSTER_H */
diff --git a/src/commands.c b/src/commands.c
index 6ccd8c18e..5dcfe1973 100644
--- a/src/commands.c
+++ b/src/commands.c
@@ -1,7502 +1,13 @@
-/* Automatically generated by generate-command-code.py, do not edit. */
-
+#include "commands.h"
#include "server.h"
-/* We have fabulous commands from
- * the fantastic
- * Redis Command Table! */
-
-/********** BITCOUNT ********************/
-
-/* BITCOUNT history */
-commandHistory BITCOUNT_History[] = {
-{"7.0.0","Added the `BYTE|BIT` option."},
-{0}
-};
-
-/* BITCOUNT tips */
-#define BITCOUNT_tips NULL
-
-/* BITCOUNT range unit argument table */
-struct redisCommandArg BITCOUNT_range_unit_Subargs[] = {
-{"byte",ARG_TYPE_PURE_TOKEN,-1,"BYTE",NULL,NULL,CMD_ARG_NONE},
-{"bit",ARG_TYPE_PURE_TOKEN,-1,"BIT",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* BITCOUNT range argument table */
-struct redisCommandArg BITCOUNT_range_Subargs[] = {
-{"start",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"end",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"unit",ARG_TYPE_ONEOF,-1,NULL,NULL,"7.0.0",CMD_ARG_OPTIONAL,.subargs=BITCOUNT_range_unit_Subargs},
-{0}
-};
-
-/* BITCOUNT argument table */
-struct redisCommandArg BITCOUNT_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"range",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,.subargs=BITCOUNT_range_Subargs},
-{0}
-};
-
-/********** BITFIELD ********************/
-
-/* BITFIELD history */
-#define BITFIELD_History NULL
-
-/* BITFIELD tips */
-#define BITFIELD_tips NULL
-
-/* BITFIELD operation get_block argument table */
-struct redisCommandArg BITFIELD_operation_get_block_Subargs[] = {
-{"encoding",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"offset",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* BITFIELD operation write overflow_block argument table */
-struct redisCommandArg BITFIELD_operation_write_overflow_block_Subargs[] = {
-{"wrap",ARG_TYPE_PURE_TOKEN,-1,"WRAP",NULL,NULL,CMD_ARG_NONE},
-{"sat",ARG_TYPE_PURE_TOKEN,-1,"SAT",NULL,NULL,CMD_ARG_NONE},
-{"fail",ARG_TYPE_PURE_TOKEN,-1,"FAIL",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* BITFIELD operation write write_operation set_block argument table */
-struct redisCommandArg BITFIELD_operation_write_write_operation_set_block_Subargs[] = {
-{"encoding",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"offset",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"value",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* BITFIELD operation write write_operation incrby_block argument table */
-struct redisCommandArg BITFIELD_operation_write_write_operation_incrby_block_Subargs[] = {
-{"encoding",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"offset",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"increment",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* BITFIELD operation write write_operation argument table */
-struct redisCommandArg BITFIELD_operation_write_write_operation_Subargs[] = {
-{"set-block",ARG_TYPE_BLOCK,-1,"SET",NULL,NULL,CMD_ARG_NONE,.subargs=BITFIELD_operation_write_write_operation_set_block_Subargs},
-{"incrby-block",ARG_TYPE_BLOCK,-1,"INCRBY",NULL,NULL,CMD_ARG_NONE,.subargs=BITFIELD_operation_write_write_operation_incrby_block_Subargs},
-{0}
-};
-
-/* BITFIELD operation write argument table */
-struct redisCommandArg BITFIELD_operation_write_Subargs[] = {
-{"overflow-block",ARG_TYPE_ONEOF,-1,"OVERFLOW",NULL,NULL,CMD_ARG_OPTIONAL,.subargs=BITFIELD_operation_write_overflow_block_Subargs},
-{"write-operation",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,.subargs=BITFIELD_operation_write_write_operation_Subargs},
-{0}
-};
-
-/* BITFIELD operation argument table */
-struct redisCommandArg BITFIELD_operation_Subargs[] = {
-{"get-block",ARG_TYPE_BLOCK,-1,"GET",NULL,NULL,CMD_ARG_NONE,.subargs=BITFIELD_operation_get_block_Subargs},
-{"write",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_NONE,.subargs=BITFIELD_operation_write_Subargs},
-{0}
-};
-
-/* BITFIELD argument table */
-struct redisCommandArg BITFIELD_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"operation",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE,.subargs=BITFIELD_operation_Subargs},
-{0}
-};
-
-/********** BITFIELD_RO ********************/
-
-/* BITFIELD_RO history */
-#define BITFIELD_RO_History NULL
-
-/* BITFIELD_RO tips */
-#define BITFIELD_RO_tips NULL
-
-/* BITFIELD_RO get_block argument table */
-struct redisCommandArg BITFIELD_RO_get_block_Subargs[] = {
-{"encoding",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"offset",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* BITFIELD_RO argument table */
-struct redisCommandArg BITFIELD_RO_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"get-block",ARG_TYPE_BLOCK,-1,"GET",NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE|CMD_ARG_MULTIPLE_TOKEN,.subargs=BITFIELD_RO_get_block_Subargs},
-{0}
-};
-
-/********** BITOP ********************/
-
-/* BITOP history */
-#define BITOP_History NULL
-
-/* BITOP tips */
-#define BITOP_tips NULL
-
-/* BITOP operation argument table */
-struct redisCommandArg BITOP_operation_Subargs[] = {
-{"and",ARG_TYPE_PURE_TOKEN,-1,"AND",NULL,NULL,CMD_ARG_NONE},
-{"or",ARG_TYPE_PURE_TOKEN,-1,"OR",NULL,NULL,CMD_ARG_NONE},
-{"xor",ARG_TYPE_PURE_TOKEN,-1,"XOR",NULL,NULL,CMD_ARG_NONE},
-{"not",ARG_TYPE_PURE_TOKEN,-1,"NOT",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* BITOP argument table */
-struct redisCommandArg BITOP_Args[] = {
-{"operation",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,.subargs=BITOP_operation_Subargs},
-{"destkey",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"key",ARG_TYPE_KEY,1,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** BITPOS ********************/
-
-/* BITPOS history */
-commandHistory BITPOS_History[] = {
-{"7.0.0","Added the `BYTE|BIT` option."},
-{0}
-};
-
-/* BITPOS tips */
-#define BITPOS_tips NULL
-
-/* BITPOS range end_unit_block unit argument table */
-struct redisCommandArg BITPOS_range_end_unit_block_unit_Subargs[] = {
-{"byte",ARG_TYPE_PURE_TOKEN,-1,"BYTE",NULL,NULL,CMD_ARG_NONE},
-{"bit",ARG_TYPE_PURE_TOKEN,-1,"BIT",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* BITPOS range end_unit_block argument table */
-struct redisCommandArg BITPOS_range_end_unit_block_Subargs[] = {
-{"end",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"unit",ARG_TYPE_ONEOF,-1,NULL,NULL,"7.0.0",CMD_ARG_OPTIONAL,.subargs=BITPOS_range_end_unit_block_unit_Subargs},
-{0}
-};
-
-/* BITPOS range argument table */
-struct redisCommandArg BITPOS_range_Subargs[] = {
-{"start",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"end-unit-block",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,.subargs=BITPOS_range_end_unit_block_Subargs},
-{0}
-};
-
-/* BITPOS argument table */
-struct redisCommandArg BITPOS_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"bit",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"range",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,.subargs=BITPOS_range_Subargs},
-{0}
-};
-
-/********** GETBIT ********************/
-
-/* GETBIT history */
-#define GETBIT_History NULL
-
-/* GETBIT tips */
-#define GETBIT_tips NULL
-
-/* GETBIT argument table */
-struct redisCommandArg GETBIT_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"offset",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** SETBIT ********************/
-
-/* SETBIT history */
-#define SETBIT_History NULL
-
-/* SETBIT tips */
-#define SETBIT_tips NULL
-
-/* SETBIT argument table */
-struct redisCommandArg SETBIT_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"offset",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"value",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** ASKING ********************/
-
-/* ASKING history */
-#define ASKING_History NULL
-
-/* ASKING tips */
-#define ASKING_tips NULL
-
-/********** CLUSTER ADDSLOTS ********************/
-
-/* CLUSTER ADDSLOTS history */
-#define CLUSTER_ADDSLOTS_History NULL
-
-/* CLUSTER ADDSLOTS tips */
-#define CLUSTER_ADDSLOTS_tips NULL
-
-/* CLUSTER ADDSLOTS argument table */
-struct redisCommandArg CLUSTER_ADDSLOTS_Args[] = {
-{"slot",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** CLUSTER ADDSLOTSRANGE ********************/
-
-/* CLUSTER ADDSLOTSRANGE history */
-#define CLUSTER_ADDSLOTSRANGE_History NULL
-
-/* CLUSTER ADDSLOTSRANGE tips */
-#define CLUSTER_ADDSLOTSRANGE_tips NULL
-
-/* CLUSTER ADDSLOTSRANGE range argument table */
-struct redisCommandArg CLUSTER_ADDSLOTSRANGE_range_Subargs[] = {
-{"start-slot",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"end-slot",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* CLUSTER ADDSLOTSRANGE argument table */
-struct redisCommandArg CLUSTER_ADDSLOTSRANGE_Args[] = {
-{"range",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,.subargs=CLUSTER_ADDSLOTSRANGE_range_Subargs},
-{0}
-};
-
-/********** CLUSTER BUMPEPOCH ********************/
-
-/* CLUSTER BUMPEPOCH history */
-#define CLUSTER_BUMPEPOCH_History NULL
-
-/* CLUSTER BUMPEPOCH tips */
-const char *CLUSTER_BUMPEPOCH_tips[] = {
-"nondeterministic_output",
-NULL
-};
-
-/********** CLUSTER COUNT_FAILURE_REPORTS ********************/
-
-/* CLUSTER COUNT_FAILURE_REPORTS history */
-#define CLUSTER_COUNT_FAILURE_REPORTS_History NULL
-
-/* CLUSTER COUNT_FAILURE_REPORTS tips */
-const char *CLUSTER_COUNT_FAILURE_REPORTS_tips[] = {
-"nondeterministic_output",
-NULL
-};
-
-/* CLUSTER COUNT_FAILURE_REPORTS argument table */
-struct redisCommandArg CLUSTER_COUNT_FAILURE_REPORTS_Args[] = {
-{"node-id",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** CLUSTER COUNTKEYSINSLOT ********************/
-
-/* CLUSTER COUNTKEYSINSLOT history */
-#define CLUSTER_COUNTKEYSINSLOT_History NULL
-
-/* CLUSTER COUNTKEYSINSLOT tips */
-#define CLUSTER_COUNTKEYSINSLOT_tips NULL
-
-/* CLUSTER COUNTKEYSINSLOT argument table */
-struct redisCommandArg CLUSTER_COUNTKEYSINSLOT_Args[] = {
-{"slot",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** CLUSTER DELSLOTS ********************/
-
-/* CLUSTER DELSLOTS history */
-#define CLUSTER_DELSLOTS_History NULL
-
-/* CLUSTER DELSLOTS tips */
-#define CLUSTER_DELSLOTS_tips NULL
-
-/* CLUSTER DELSLOTS argument table */
-struct redisCommandArg CLUSTER_DELSLOTS_Args[] = {
-{"slot",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** CLUSTER DELSLOTSRANGE ********************/
-
-/* CLUSTER DELSLOTSRANGE history */
-#define CLUSTER_DELSLOTSRANGE_History NULL
-
-/* CLUSTER DELSLOTSRANGE tips */
-#define CLUSTER_DELSLOTSRANGE_tips NULL
-
-/* CLUSTER DELSLOTSRANGE range argument table */
-struct redisCommandArg CLUSTER_DELSLOTSRANGE_range_Subargs[] = {
-{"start-slot",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"end-slot",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* CLUSTER DELSLOTSRANGE argument table */
-struct redisCommandArg CLUSTER_DELSLOTSRANGE_Args[] = {
-{"range",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,.subargs=CLUSTER_DELSLOTSRANGE_range_Subargs},
-{0}
-};
-
-/********** CLUSTER FAILOVER ********************/
-
-/* CLUSTER FAILOVER history */
-#define CLUSTER_FAILOVER_History NULL
-
-/* CLUSTER FAILOVER tips */
-#define CLUSTER_FAILOVER_tips NULL
-
-/* CLUSTER FAILOVER options argument table */
-struct redisCommandArg CLUSTER_FAILOVER_options_Subargs[] = {
-{"force",ARG_TYPE_PURE_TOKEN,-1,"FORCE",NULL,NULL,CMD_ARG_NONE},
-{"takeover",ARG_TYPE_PURE_TOKEN,-1,"TAKEOVER",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* CLUSTER FAILOVER argument table */
-struct redisCommandArg CLUSTER_FAILOVER_Args[] = {
-{"options",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,.subargs=CLUSTER_FAILOVER_options_Subargs},
-{0}
-};
-
-/********** CLUSTER FLUSHSLOTS ********************/
-
-/* CLUSTER FLUSHSLOTS history */
-#define CLUSTER_FLUSHSLOTS_History NULL
-
-/* CLUSTER FLUSHSLOTS tips */
-#define CLUSTER_FLUSHSLOTS_tips NULL
-
-/********** CLUSTER FORGET ********************/
-
-/* CLUSTER FORGET history */
-#define CLUSTER_FORGET_History NULL
-
-/* CLUSTER FORGET tips */
-#define CLUSTER_FORGET_tips NULL
-
-/* CLUSTER FORGET argument table */
-struct redisCommandArg CLUSTER_FORGET_Args[] = {
-{"node-id",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** CLUSTER GETKEYSINSLOT ********************/
-
-/* CLUSTER GETKEYSINSLOT history */
-#define CLUSTER_GETKEYSINSLOT_History NULL
-
-/* CLUSTER GETKEYSINSLOT tips */
-const char *CLUSTER_GETKEYSINSLOT_tips[] = {
-"nondeterministic_output",
-NULL
-};
-
-/* CLUSTER GETKEYSINSLOT argument table */
-struct redisCommandArg CLUSTER_GETKEYSINSLOT_Args[] = {
-{"slot",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"count",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** CLUSTER HELP ********************/
-
-/* CLUSTER HELP history */
-#define CLUSTER_HELP_History NULL
-
-/* CLUSTER HELP tips */
-#define CLUSTER_HELP_tips NULL
-
-/********** CLUSTER INFO ********************/
-
-/* CLUSTER INFO history */
-#define CLUSTER_INFO_History NULL
-
-/* CLUSTER INFO tips */
-const char *CLUSTER_INFO_tips[] = {
-"nondeterministic_output",
-NULL
-};
-
-/********** CLUSTER KEYSLOT ********************/
-
-/* CLUSTER KEYSLOT history */
-#define CLUSTER_KEYSLOT_History NULL
-
-/* CLUSTER KEYSLOT tips */
-#define CLUSTER_KEYSLOT_tips NULL
-
-/* CLUSTER KEYSLOT argument table */
-struct redisCommandArg CLUSTER_KEYSLOT_Args[] = {
-{"key",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** CLUSTER LINKS ********************/
-
-/* CLUSTER LINKS history */
-#define CLUSTER_LINKS_History NULL
-
-/* CLUSTER LINKS tips */
-const char *CLUSTER_LINKS_tips[] = {
-"nondeterministic_output",
-NULL
-};
-
-/********** CLUSTER MEET ********************/
-
-/* CLUSTER MEET history */
-commandHistory CLUSTER_MEET_History[] = {
-{"4.0.0","Added the optional `cluster_bus_port` argument."},
-{0}
-};
-
-/* CLUSTER MEET tips */
-#define CLUSTER_MEET_tips NULL
-
-/* CLUSTER MEET argument table */
-struct redisCommandArg CLUSTER_MEET_Args[] = {
-{"ip",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"port",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"cluster-bus-port",ARG_TYPE_INTEGER,-1,NULL,NULL,"4.0.0",CMD_ARG_OPTIONAL},
-{0}
-};
-
-/********** CLUSTER MYID ********************/
-
-/* CLUSTER MYID history */
-#define CLUSTER_MYID_History NULL
-
-/* CLUSTER MYID tips */
-#define CLUSTER_MYID_tips NULL
-
-/********** CLUSTER MYSHARDID ********************/
-
-/* CLUSTER MYSHARDID history */
-#define CLUSTER_MYSHARDID_History NULL
-
-/* CLUSTER MYSHARDID tips */
-const char *CLUSTER_MYSHARDID_tips[] = {
-"nondeterministic_output",
-NULL
-};
-
-/********** CLUSTER NODES ********************/
-
-/* CLUSTER NODES history */
-#define CLUSTER_NODES_History NULL
-
-/* CLUSTER NODES tips */
-const char *CLUSTER_NODES_tips[] = {
-"nondeterministic_output",
-NULL
-};
-
-/********** CLUSTER REPLICAS ********************/
-
-/* CLUSTER REPLICAS history */
-#define CLUSTER_REPLICAS_History NULL
-
-/* CLUSTER REPLICAS tips */
-const char *CLUSTER_REPLICAS_tips[] = {
-"nondeterministic_output",
-NULL
-};
-
-/* CLUSTER REPLICAS argument table */
-struct redisCommandArg CLUSTER_REPLICAS_Args[] = {
-{"node-id",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** CLUSTER REPLICATE ********************/
-
-/* CLUSTER REPLICATE history */
-#define CLUSTER_REPLICATE_History NULL
-
-/* CLUSTER REPLICATE tips */
-#define CLUSTER_REPLICATE_tips NULL
-
-/* CLUSTER REPLICATE argument table */
-struct redisCommandArg CLUSTER_REPLICATE_Args[] = {
-{"node-id",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** CLUSTER RESET ********************/
-
-/* CLUSTER RESET history */
-#define CLUSTER_RESET_History NULL
-
-/* CLUSTER RESET tips */
-#define CLUSTER_RESET_tips NULL
-
-/* CLUSTER RESET reset_type argument table */
-struct redisCommandArg CLUSTER_RESET_reset_type_Subargs[] = {
-{"hard",ARG_TYPE_PURE_TOKEN,-1,"HARD",NULL,NULL,CMD_ARG_NONE},
-{"soft",ARG_TYPE_PURE_TOKEN,-1,"SOFT",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* CLUSTER RESET argument table */
-struct redisCommandArg CLUSTER_RESET_Args[] = {
-{"reset-type",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,.subargs=CLUSTER_RESET_reset_type_Subargs},
-{0}
-};
-
-/********** CLUSTER SAVECONFIG ********************/
-
-/* CLUSTER SAVECONFIG history */
-#define CLUSTER_SAVECONFIG_History NULL
-
-/* CLUSTER SAVECONFIG tips */
-#define CLUSTER_SAVECONFIG_tips NULL
-
-/********** CLUSTER SET_CONFIG_EPOCH ********************/
-
-/* CLUSTER SET_CONFIG_EPOCH history */
-#define CLUSTER_SET_CONFIG_EPOCH_History NULL
-
-/* CLUSTER SET_CONFIG_EPOCH tips */
-#define CLUSTER_SET_CONFIG_EPOCH_tips NULL
-
-/* CLUSTER SET_CONFIG_EPOCH argument table */
-struct redisCommandArg CLUSTER_SET_CONFIG_EPOCH_Args[] = {
-{"config-epoch",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** CLUSTER SETSLOT ********************/
-
-/* CLUSTER SETSLOT history */
-#define CLUSTER_SETSLOT_History NULL
-
-/* CLUSTER SETSLOT tips */
-#define CLUSTER_SETSLOT_tips NULL
-
-/* CLUSTER SETSLOT subcommand argument table */
-struct redisCommandArg CLUSTER_SETSLOT_subcommand_Subargs[] = {
-{"importing",ARG_TYPE_STRING,-1,"IMPORTING",NULL,NULL,CMD_ARG_NONE,.display_text="node-id"},
-{"migrating",ARG_TYPE_STRING,-1,"MIGRATING",NULL,NULL,CMD_ARG_NONE,.display_text="node-id"},
-{"node",ARG_TYPE_STRING,-1,"NODE",NULL,NULL,CMD_ARG_NONE,.display_text="node-id"},
-{"stable",ARG_TYPE_PURE_TOKEN,-1,"STABLE",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* CLUSTER SETSLOT argument table */
-struct redisCommandArg CLUSTER_SETSLOT_Args[] = {
-{"slot",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"subcommand",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,.subargs=CLUSTER_SETSLOT_subcommand_Subargs},
-{0}
-};
-
-/********** CLUSTER SHARDS ********************/
-
-/* CLUSTER SHARDS history */
-#define CLUSTER_SHARDS_History NULL
-
-/* CLUSTER SHARDS tips */
-const char *CLUSTER_SHARDS_tips[] = {
-"nondeterministic_output",
-NULL
-};
-
-/********** CLUSTER SLAVES ********************/
-
-/* CLUSTER SLAVES history */
-#define CLUSTER_SLAVES_History NULL
-
-/* CLUSTER SLAVES tips */
-const char *CLUSTER_SLAVES_tips[] = {
-"nondeterministic_output",
-NULL
-};
-
-/* CLUSTER SLAVES argument table */
-struct redisCommandArg CLUSTER_SLAVES_Args[] = {
-{"node-id",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** CLUSTER SLOTS ********************/
-
-/* CLUSTER SLOTS history */
-commandHistory CLUSTER_SLOTS_History[] = {
-{"4.0.0","Added node IDs."},
-{"7.0.0","Added additional networking metadata field."},
-{0}
-};
-
-/* CLUSTER SLOTS tips */
-const char *CLUSTER_SLOTS_tips[] = {
-"nondeterministic_output",
-NULL
-};
-
-/* CLUSTER command table */
-struct redisCommand CLUSTER_Subcommands[] = {
-{"addslots","Assign new hash slots to receiving node","O(N) where N is the total number of hash slot arguments","3.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CLUSTER,CLUSTER_ADDSLOTS_History,CLUSTER_ADDSLOTS_tips,clusterCommand,-3,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_STALE,0,.args=CLUSTER_ADDSLOTS_Args},
-{"addslotsrange","Assign new hash slots to receiving node","O(N) where N is the total number of the slots between the start slot and end slot arguments.","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CLUSTER,CLUSTER_ADDSLOTSRANGE_History,CLUSTER_ADDSLOTSRANGE_tips,clusterCommand,-4,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_STALE,0,.args=CLUSTER_ADDSLOTSRANGE_Args},
-{"bumpepoch","Advance the cluster config epoch","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CLUSTER,CLUSTER_BUMPEPOCH_History,CLUSTER_BUMPEPOCH_tips,clusterCommand,2,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_STALE,0},
-{"count-failure-reports","Return the number of failure reports active for a given node","O(N) where N is the number of failure reports","3.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CLUSTER,CLUSTER_COUNT_FAILURE_REPORTS_History,CLUSTER_COUNT_FAILURE_REPORTS_tips,clusterCommand,3,CMD_ADMIN|CMD_STALE,0,.args=CLUSTER_COUNT_FAILURE_REPORTS_Args},
-{"countkeysinslot","Return the number of local keys in the specified hash slot","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CLUSTER,CLUSTER_COUNTKEYSINSLOT_History,CLUSTER_COUNTKEYSINSLOT_tips,clusterCommand,3,CMD_STALE,0,.args=CLUSTER_COUNTKEYSINSLOT_Args},
-{"delslots","Set hash slots as unbound in receiving node","O(N) where N is the total number of hash slot arguments","3.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CLUSTER,CLUSTER_DELSLOTS_History,CLUSTER_DELSLOTS_tips,clusterCommand,-3,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_STALE,0,.args=CLUSTER_DELSLOTS_Args},
-{"delslotsrange","Set hash slots as unbound in receiving node","O(N) where N is the total number of the slots between the start slot and end slot arguments.","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CLUSTER,CLUSTER_DELSLOTSRANGE_History,CLUSTER_DELSLOTSRANGE_tips,clusterCommand,-4,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_STALE,0,.args=CLUSTER_DELSLOTSRANGE_Args},
-{"failover","Forces a replica to perform a manual failover of its master.","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CLUSTER,CLUSTER_FAILOVER_History,CLUSTER_FAILOVER_tips,clusterCommand,-2,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_STALE,0,.args=CLUSTER_FAILOVER_Args},
-{"flushslots","Delete a node's own slots information","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CLUSTER,CLUSTER_FLUSHSLOTS_History,CLUSTER_FLUSHSLOTS_tips,clusterCommand,2,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_STALE,0},
-{"forget","Remove a node from the nodes table","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CLUSTER,CLUSTER_FORGET_History,CLUSTER_FORGET_tips,clusterCommand,3,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_STALE,0,.args=CLUSTER_FORGET_Args},
-{"getkeysinslot","Return local key names in the specified hash slot","O(N) where N is the number of requested keys","3.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CLUSTER,CLUSTER_GETKEYSINSLOT_History,CLUSTER_GETKEYSINSLOT_tips,clusterCommand,4,CMD_STALE,0,.args=CLUSTER_GETKEYSINSLOT_Args},
-{"help","Show helpful text about the different subcommands","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CLUSTER,CLUSTER_HELP_History,CLUSTER_HELP_tips,clusterCommand,2,CMD_LOADING|CMD_STALE,0},
-{"info","Provides info about Redis Cluster node state","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CLUSTER,CLUSTER_INFO_History,CLUSTER_INFO_tips,clusterCommand,2,CMD_STALE,0},
-{"keyslot","Returns the hash slot of the specified key","O(N) where N is the number of bytes in the key","3.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CLUSTER,CLUSTER_KEYSLOT_History,CLUSTER_KEYSLOT_tips,clusterCommand,3,CMD_STALE,0,.args=CLUSTER_KEYSLOT_Args},
-{"links","Returns a list of all TCP links to and from peer nodes in cluster","O(N) where N is the total number of Cluster nodes","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CLUSTER,CLUSTER_LINKS_History,CLUSTER_LINKS_tips,clusterCommand,2,CMD_STALE,0},
-{"meet","Force a node cluster to handshake with another node","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CLUSTER,CLUSTER_MEET_History,CLUSTER_MEET_tips,clusterCommand,-4,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_STALE,0,.args=CLUSTER_MEET_Args},
-{"myid","Return the node id","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CLUSTER,CLUSTER_MYID_History,CLUSTER_MYID_tips,clusterCommand,2,CMD_STALE,0},
-{"myshardid","Return the node shard id","O(1)","7.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CLUSTER,CLUSTER_MYSHARDID_History,CLUSTER_MYSHARDID_tips,clusterCommand,2,CMD_STALE,0},
-{"nodes","Get Cluster config for the node","O(N) where N is the total number of Cluster nodes","3.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CLUSTER,CLUSTER_NODES_History,CLUSTER_NODES_tips,clusterCommand,2,CMD_STALE,0},
-{"replicas","List replica nodes of the specified master node","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CLUSTER,CLUSTER_REPLICAS_History,CLUSTER_REPLICAS_tips,clusterCommand,3,CMD_ADMIN|CMD_STALE,0,.args=CLUSTER_REPLICAS_Args},
-{"replicate","Reconfigure a node as a replica of the specified master node","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CLUSTER,CLUSTER_REPLICATE_History,CLUSTER_REPLICATE_tips,clusterCommand,3,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_STALE,0,.args=CLUSTER_REPLICATE_Args},
-{"reset","Reset a Redis Cluster node","O(N) where N is the number of known nodes. The command may execute a FLUSHALL as a side effect.","3.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CLUSTER,CLUSTER_RESET_History,CLUSTER_RESET_tips,clusterCommand,-2,CMD_ADMIN|CMD_STALE|CMD_NOSCRIPT,0,.args=CLUSTER_RESET_Args},
-{"saveconfig","Forces the node to save cluster state on disk","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CLUSTER,CLUSTER_SAVECONFIG_History,CLUSTER_SAVECONFIG_tips,clusterCommand,2,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_STALE,0},
-{"set-config-epoch","Set the configuration epoch in a new node","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CLUSTER,CLUSTER_SET_CONFIG_EPOCH_History,CLUSTER_SET_CONFIG_EPOCH_tips,clusterCommand,3,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_STALE,0,.args=CLUSTER_SET_CONFIG_EPOCH_Args},
-{"setslot","Bind a hash slot to a specific node","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CLUSTER,CLUSTER_SETSLOT_History,CLUSTER_SETSLOT_tips,clusterCommand,-4,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_STALE,0,.args=CLUSTER_SETSLOT_Args},
-{"shards","Get array of cluster slots to node mappings","O(N) where N is the total number of cluster nodes","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CLUSTER,CLUSTER_SHARDS_History,CLUSTER_SHARDS_tips,clusterCommand,2,CMD_STALE,0},
-{"slaves","List replica nodes of the specified master node","O(1)","3.0.0",CMD_DOC_DEPRECATED,"`CLUSTER REPLICAS`","5.0.0",COMMAND_GROUP_CLUSTER,CLUSTER_SLAVES_History,CLUSTER_SLAVES_tips,clusterCommand,3,CMD_ADMIN|CMD_STALE,0,.args=CLUSTER_SLAVES_Args},
-{"slots","Get array of Cluster slot to node mappings","O(N) where N is the total number of Cluster nodes","3.0.0",CMD_DOC_DEPRECATED,"`CLUSTER SHARDS`","7.0.0",COMMAND_GROUP_CLUSTER,CLUSTER_SLOTS_History,CLUSTER_SLOTS_tips,clusterCommand,2,CMD_STALE,0},
-{0}
-};
-
-/********** CLUSTER ********************/
-
-/* CLUSTER history */
-#define CLUSTER_History NULL
-
-/* CLUSTER tips */
-#define CLUSTER_tips NULL
-
-/********** READONLY ********************/
-
-/* READONLY history */
-#define READONLY_History NULL
-
-/* READONLY tips */
-#define READONLY_tips NULL
-
-/********** READWRITE ********************/
-
-/* READWRITE history */
-#define READWRITE_History NULL
-
-/* READWRITE tips */
-#define READWRITE_tips NULL
-
-/********** AUTH ********************/
-
-/* AUTH history */
-commandHistory AUTH_History[] = {
-{"6.0.0","Added ACL style (username and password)."},
-{0}
-};
-
-/* AUTH tips */
-#define AUTH_tips NULL
-
-/* AUTH argument table */
-struct redisCommandArg AUTH_Args[] = {
-{"username",ARG_TYPE_STRING,-1,NULL,NULL,"6.0.0",CMD_ARG_OPTIONAL},
-{"password",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** CLIENT CACHING ********************/
-
-/* CLIENT CACHING history */
-#define CLIENT_CACHING_History NULL
-
-/* CLIENT CACHING tips */
-#define CLIENT_CACHING_tips NULL
-
-/* CLIENT CACHING mode argument table */
-struct redisCommandArg CLIENT_CACHING_mode_Subargs[] = {
-{"yes",ARG_TYPE_PURE_TOKEN,-1,"YES",NULL,NULL,CMD_ARG_NONE},
-{"no",ARG_TYPE_PURE_TOKEN,-1,"NO",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* CLIENT CACHING argument table */
-struct redisCommandArg CLIENT_CACHING_Args[] = {
-{"mode",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,.subargs=CLIENT_CACHING_mode_Subargs},
-{0}
-};
-
-/********** CLIENT GETNAME ********************/
-
-/* CLIENT GETNAME history */
-#define CLIENT_GETNAME_History NULL
-
-/* CLIENT GETNAME tips */
-#define CLIENT_GETNAME_tips NULL
-
-/********** CLIENT GETREDIR ********************/
-
-/* CLIENT GETREDIR history */
-#define CLIENT_GETREDIR_History NULL
-
-/* CLIENT GETREDIR tips */
-#define CLIENT_GETREDIR_tips NULL
-
-/********** CLIENT HELP ********************/
-
-/* CLIENT HELP history */
-#define CLIENT_HELP_History NULL
-
-/* CLIENT HELP tips */
-#define CLIENT_HELP_tips NULL
-
-/********** CLIENT ID ********************/
-
-/* CLIENT ID history */
-#define CLIENT_ID_History NULL
-
-/* CLIENT ID tips */
-#define CLIENT_ID_tips NULL
-
-/********** CLIENT INFO ********************/
-
-/* CLIENT INFO history */
-#define CLIENT_INFO_History NULL
-
-/* CLIENT INFO tips */
-const char *CLIENT_INFO_tips[] = {
-"nondeterministic_output",
-NULL
-};
-
-/********** CLIENT KILL ********************/
-
-/* CLIENT KILL history */
-commandHistory CLIENT_KILL_History[] = {
-{"2.8.12","Added new filter format."},
-{"2.8.12","`ID` option."},
-{"3.2.0","Added `master` type in for `TYPE` option."},
-{"5.0.0","Replaced `slave` `TYPE` with `replica`. `slave` still supported for backward compatibility."},
-{"6.2.0","`LADDR` option."},
-{0}
-};
-
-/* CLIENT KILL tips */
-#define CLIENT_KILL_tips NULL
-
-/* CLIENT KILL filter new_format client_type argument table */
-struct redisCommandArg CLIENT_KILL_filter_new_format_client_type_Subargs[] = {
-{"normal",ARG_TYPE_PURE_TOKEN,-1,"NORMAL",NULL,NULL,CMD_ARG_NONE},
-{"master",ARG_TYPE_PURE_TOKEN,-1,"MASTER",NULL,"3.2.0",CMD_ARG_NONE},
-{"slave",ARG_TYPE_PURE_TOKEN,-1,"SLAVE",NULL,NULL,CMD_ARG_NONE},
-{"replica",ARG_TYPE_PURE_TOKEN,-1,"REPLICA",NULL,"5.0.0",CMD_ARG_NONE},
-{"pubsub",ARG_TYPE_PURE_TOKEN,-1,"PUBSUB",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* CLIENT KILL filter new_format skipme argument table */
-struct redisCommandArg CLIENT_KILL_filter_new_format_skipme_Subargs[] = {
-{"yes",ARG_TYPE_PURE_TOKEN,-1,"YES",NULL,NULL,CMD_ARG_NONE},
-{"no",ARG_TYPE_PURE_TOKEN,-1,"NO",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* CLIENT KILL filter new_format argument table */
-struct redisCommandArg CLIENT_KILL_filter_new_format_Subargs[] = {
-{"client-id",ARG_TYPE_INTEGER,-1,"ID",NULL,"2.8.12",CMD_ARG_OPTIONAL},
-{"client-type",ARG_TYPE_ONEOF,-1,"TYPE",NULL,"2.8.12",CMD_ARG_OPTIONAL,.subargs=CLIENT_KILL_filter_new_format_client_type_Subargs},
-{"username",ARG_TYPE_STRING,-1,"USER",NULL,NULL,CMD_ARG_OPTIONAL},
-{"addr",ARG_TYPE_STRING,-1,"ADDR",NULL,NULL,CMD_ARG_OPTIONAL,.display_text="ip:port"},
-{"laddr",ARG_TYPE_STRING,-1,"LADDR",NULL,"6.2.0",CMD_ARG_OPTIONAL,.display_text="ip:port"},
-{"skipme",ARG_TYPE_ONEOF,-1,"SKIPME",NULL,NULL,CMD_ARG_OPTIONAL,.subargs=CLIENT_KILL_filter_new_format_skipme_Subargs},
-{0}
-};
-
-/* CLIENT KILL filter argument table */
-struct redisCommandArg CLIENT_KILL_filter_Subargs[] = {
-{"old-format",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,.deprecated_since="2.8.12",.display_text="ip:port"},
-{"new-format",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,.subargs=CLIENT_KILL_filter_new_format_Subargs},
-{0}
-};
-
-/* CLIENT KILL argument table */
-struct redisCommandArg CLIENT_KILL_Args[] = {
-{"filter",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,.subargs=CLIENT_KILL_filter_Subargs},
-{0}
-};
-
-/********** CLIENT LIST ********************/
-
-/* CLIENT LIST history */
-commandHistory CLIENT_LIST_History[] = {
-{"2.8.12","Added unique client `id` field."},
-{"5.0.0","Added optional `TYPE` filter."},
-{"6.0.0","Added `user` field."},
-{"6.2.0","Added `argv-mem`, `tot-mem`, `laddr` and `redir` fields and the optional `ID` filter."},
-{"7.0.0","Added `resp`, `multi-mem`, `rbs` and `rbp` fields."},
-{"7.0.3","Added `ssub` field."},
-{0}
-};
-
-/* CLIENT LIST tips */
-const char *CLIENT_LIST_tips[] = {
-"nondeterministic_output",
-NULL
-};
-
-/* CLIENT LIST client_type argument table */
-struct redisCommandArg CLIENT_LIST_client_type_Subargs[] = {
-{"normal",ARG_TYPE_PURE_TOKEN,-1,"NORMAL",NULL,NULL,CMD_ARG_NONE},
-{"master",ARG_TYPE_PURE_TOKEN,-1,"MASTER",NULL,NULL,CMD_ARG_NONE},
-{"replica",ARG_TYPE_PURE_TOKEN,-1,"REPLICA",NULL,NULL,CMD_ARG_NONE},
-{"pubsub",ARG_TYPE_PURE_TOKEN,-1,"PUBSUB",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* CLIENT LIST argument table */
-struct redisCommandArg CLIENT_LIST_Args[] = {
-{"client-type",ARG_TYPE_ONEOF,-1,"TYPE",NULL,"5.0.0",CMD_ARG_OPTIONAL,.subargs=CLIENT_LIST_client_type_Subargs},
-{"client-id",ARG_TYPE_INTEGER,-1,"ID",NULL,"6.2.0",CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** CLIENT NO_EVICT ********************/
-
-/* CLIENT NO_EVICT history */
-#define CLIENT_NO_EVICT_History NULL
-
-/* CLIENT NO_EVICT tips */
-#define CLIENT_NO_EVICT_tips NULL
-
-/* CLIENT NO_EVICT enabled argument table */
-struct redisCommandArg CLIENT_NO_EVICT_enabled_Subargs[] = {
-{"on",ARG_TYPE_PURE_TOKEN,-1,"ON",NULL,NULL,CMD_ARG_NONE},
-{"off",ARG_TYPE_PURE_TOKEN,-1,"OFF",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* CLIENT NO_EVICT argument table */
-struct redisCommandArg CLIENT_NO_EVICT_Args[] = {
-{"enabled",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,.subargs=CLIENT_NO_EVICT_enabled_Subargs},
-{0}
-};
-
-/********** CLIENT NO_TOUCH ********************/
-
-/* CLIENT NO_TOUCH history */
-#define CLIENT_NO_TOUCH_History NULL
-
-/* CLIENT NO_TOUCH tips */
-#define CLIENT_NO_TOUCH_tips NULL
-
-/* CLIENT NO_TOUCH enabled argument table */
-struct redisCommandArg CLIENT_NO_TOUCH_enabled_Subargs[] = {
-{"on",ARG_TYPE_PURE_TOKEN,-1,"ON",NULL,NULL,CMD_ARG_NONE},
-{"off",ARG_TYPE_PURE_TOKEN,-1,"OFF",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* CLIENT NO_TOUCH argument table */
-struct redisCommandArg CLIENT_NO_TOUCH_Args[] = {
-{"enabled",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,.subargs=CLIENT_NO_TOUCH_enabled_Subargs},
-{0}
-};
-
-/********** CLIENT PAUSE ********************/
-
-/* CLIENT PAUSE history */
-commandHistory CLIENT_PAUSE_History[] = {
-{"6.2.0","`CLIENT PAUSE WRITE` mode added along with the `mode` option."},
-{0}
-};
-
-/* CLIENT PAUSE tips */
-#define CLIENT_PAUSE_tips NULL
-
-/* CLIENT PAUSE mode argument table */
-struct redisCommandArg CLIENT_PAUSE_mode_Subargs[] = {
-{"write",ARG_TYPE_PURE_TOKEN,-1,"WRITE",NULL,NULL,CMD_ARG_NONE},
-{"all",ARG_TYPE_PURE_TOKEN,-1,"ALL",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* CLIENT PAUSE argument table */
-struct redisCommandArg CLIENT_PAUSE_Args[] = {
-{"timeout",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"mode",ARG_TYPE_ONEOF,-1,NULL,NULL,"6.2.0",CMD_ARG_OPTIONAL,.subargs=CLIENT_PAUSE_mode_Subargs},
-{0}
-};
-
-/********** CLIENT REPLY ********************/
-
-/* CLIENT REPLY history */
-#define CLIENT_REPLY_History NULL
-
-/* CLIENT REPLY tips */
-#define CLIENT_REPLY_tips NULL
-
-/* CLIENT REPLY action argument table */
-struct redisCommandArg CLIENT_REPLY_action_Subargs[] = {
-{"on",ARG_TYPE_PURE_TOKEN,-1,"ON",NULL,NULL,CMD_ARG_NONE},
-{"off",ARG_TYPE_PURE_TOKEN,-1,"OFF",NULL,NULL,CMD_ARG_NONE},
-{"skip",ARG_TYPE_PURE_TOKEN,-1,"SKIP",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* CLIENT REPLY argument table */
-struct redisCommandArg CLIENT_REPLY_Args[] = {
-{"action",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,.subargs=CLIENT_REPLY_action_Subargs},
-{0}
-};
-
-/********** CLIENT SETINFO ********************/
-
-/* CLIENT SETINFO history */
-#define CLIENT_SETINFO_History NULL
-
-/* CLIENT SETINFO tips */
-#define CLIENT_SETINFO_tips NULL
-
-/* CLIENT SETINFO attr argument table */
-struct redisCommandArg CLIENT_SETINFO_attr_Subargs[] = {
-{"libname",ARG_TYPE_STRING,-1,"LIB-NAME",NULL,NULL,CMD_ARG_NONE},
-{"libver",ARG_TYPE_STRING,-1,"LIB-VER",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* CLIENT SETINFO argument table */
-struct redisCommandArg CLIENT_SETINFO_Args[] = {
-{"attr",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,.subargs=CLIENT_SETINFO_attr_Subargs},
-{0}
-};
-
-/********** CLIENT SETNAME ********************/
-
-/* CLIENT SETNAME history */
-#define CLIENT_SETNAME_History NULL
-
-/* CLIENT SETNAME tips */
-#define CLIENT_SETNAME_tips NULL
-
-/* CLIENT SETNAME argument table */
-struct redisCommandArg CLIENT_SETNAME_Args[] = {
-{"connection-name",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** CLIENT TRACKING ********************/
-
-/* CLIENT TRACKING history */
-#define CLIENT_TRACKING_History NULL
-
-/* CLIENT TRACKING tips */
-#define CLIENT_TRACKING_tips NULL
-
-/* CLIENT TRACKING status argument table */
-struct redisCommandArg CLIENT_TRACKING_status_Subargs[] = {
-{"on",ARG_TYPE_PURE_TOKEN,-1,"ON",NULL,NULL,CMD_ARG_NONE},
-{"off",ARG_TYPE_PURE_TOKEN,-1,"OFF",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* CLIENT TRACKING argument table */
-struct redisCommandArg CLIENT_TRACKING_Args[] = {
-{"status",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,.subargs=CLIENT_TRACKING_status_Subargs},
-{"client-id",ARG_TYPE_INTEGER,-1,"REDIRECT",NULL,NULL,CMD_ARG_OPTIONAL},
-{"prefix",ARG_TYPE_STRING,-1,"PREFIX",NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE|CMD_ARG_MULTIPLE_TOKEN},
-{"bcast",ARG_TYPE_PURE_TOKEN,-1,"BCAST",NULL,NULL,CMD_ARG_OPTIONAL},
-{"optin",ARG_TYPE_PURE_TOKEN,-1,"OPTIN",NULL,NULL,CMD_ARG_OPTIONAL},
-{"optout",ARG_TYPE_PURE_TOKEN,-1,"OPTOUT",NULL,NULL,CMD_ARG_OPTIONAL},
-{"noloop",ARG_TYPE_PURE_TOKEN,-1,"NOLOOP",NULL,NULL,CMD_ARG_OPTIONAL},
-{0}
-};
-
-/********** CLIENT TRACKINGINFO ********************/
-
-/* CLIENT TRACKINGINFO history */
-#define CLIENT_TRACKINGINFO_History NULL
-
-/* CLIENT TRACKINGINFO tips */
-#define CLIENT_TRACKINGINFO_tips NULL
-
-/********** CLIENT UNBLOCK ********************/
-
-/* CLIENT UNBLOCK history */
-#define CLIENT_UNBLOCK_History NULL
-
-/* CLIENT UNBLOCK tips */
-#define CLIENT_UNBLOCK_tips NULL
-
-/* CLIENT UNBLOCK unblock_type argument table */
-struct redisCommandArg CLIENT_UNBLOCK_unblock_type_Subargs[] = {
-{"timeout",ARG_TYPE_PURE_TOKEN,-1,"TIMEOUT",NULL,NULL,CMD_ARG_NONE},
-{"error",ARG_TYPE_PURE_TOKEN,-1,"ERROR",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* CLIENT UNBLOCK argument table */
-struct redisCommandArg CLIENT_UNBLOCK_Args[] = {
-{"client-id",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"unblock-type",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,.subargs=CLIENT_UNBLOCK_unblock_type_Subargs},
-{0}
-};
-
-/********** CLIENT UNPAUSE ********************/
-
-/* CLIENT UNPAUSE history */
-#define CLIENT_UNPAUSE_History NULL
-
-/* CLIENT UNPAUSE tips */
-#define CLIENT_UNPAUSE_tips NULL
-
-/* CLIENT command table */
-struct redisCommand CLIENT_Subcommands[] = {
-{"caching","Instruct the server about tracking or not keys in the next request","O(1)","6.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_CACHING_History,CLIENT_CACHING_tips,clientCommand,3,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,.args=CLIENT_CACHING_Args},
-{"getname","Get the current connection name","O(1)","2.6.9",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_GETNAME_History,CLIENT_GETNAME_tips,clientCommand,2,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION},
-{"getredir","Get tracking notifications redirection client ID if any","O(1)","6.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_GETREDIR_History,CLIENT_GETREDIR_tips,clientCommand,2,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION},
-{"help","Show helpful text about the different subcommands","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_HELP_History,CLIENT_HELP_tips,clientCommand,2,CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION},
-{"id","Returns the client ID for the current connection","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_ID_History,CLIENT_ID_tips,clientCommand,2,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION},
-{"info","Returns information about the current client connection.","O(1)","6.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_INFO_History,CLIENT_INFO_tips,clientCommand,2,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION},
-{"kill","Kill the connection of a client","O(N) where N is the number of client connections","2.4.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_KILL_History,CLIENT_KILL_tips,clientCommand,-3,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,.args=CLIENT_KILL_Args},
-{"list","Get the list of client connections","O(N) where N is the number of client connections","2.4.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_LIST_History,CLIENT_LIST_tips,clientCommand,-2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,.args=CLIENT_LIST_Args},
-{"no-evict","Set client eviction mode for the current connection","O(1)","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_NO_EVICT_History,CLIENT_NO_EVICT_tips,clientCommand,3,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,.args=CLIENT_NO_EVICT_Args},
-{"no-touch","Controls whether commands sent by the client will alter the LRU/LFU of the keys they access.","O(1)","7.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_NO_TOUCH_History,CLIENT_NO_TOUCH_tips,clientCommand,3,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,ACL_CATEGORY_CONNECTION,.args=CLIENT_NO_TOUCH_Args},
-{"pause","Stop processing commands from clients for some time","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_PAUSE_History,CLIENT_PAUSE_tips,clientCommand,-3,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,.args=CLIENT_PAUSE_Args},
-{"reply","Instruct the server whether to reply to commands","O(1)","3.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_REPLY_History,CLIENT_REPLY_tips,clientCommand,3,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,.args=CLIENT_REPLY_Args},
-{"setinfo","Set client or connection specific info","O(1)","7.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_SETINFO_History,CLIENT_SETINFO_tips,clientSetinfoCommand,4,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,.args=CLIENT_SETINFO_Args},
-{"setname","Set the current connection name","O(1)","2.6.9",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_SETNAME_History,CLIENT_SETNAME_tips,clientCommand,3,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,.args=CLIENT_SETNAME_Args},
-{"tracking","Enable or disable server assisted client side caching support","O(1). Some options may introduce additional complexity.","6.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_TRACKING_History,CLIENT_TRACKING_tips,clientCommand,-3,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,.args=CLIENT_TRACKING_Args},
-{"trackinginfo","Return information about server assisted client side caching for the current connection","O(1)","6.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_TRACKINGINFO_History,CLIENT_TRACKINGINFO_tips,clientCommand,2,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION},
-{"unblock","Unblock a client blocked in a blocking command from a different connection","O(log N) where N is the number of client connections","5.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_UNBLOCK_History,CLIENT_UNBLOCK_tips,clientCommand,-3,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,.args=CLIENT_UNBLOCK_Args},
-{"unpause","Resume processing of clients that were paused","O(N) Where N is the number of paused clients","6.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_UNPAUSE_History,CLIENT_UNPAUSE_tips,clientCommand,2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION},
-{0}
-};
-
-/********** CLIENT ********************/
-
-/* CLIENT history */
-#define CLIENT_History NULL
-
-/* CLIENT tips */
-#define CLIENT_tips NULL
-
-/********** ECHO ********************/
-
-/* ECHO history */
-#define ECHO_History NULL
-
-/* ECHO tips */
-#define ECHO_tips NULL
-
-/* ECHO argument table */
-struct redisCommandArg ECHO_Args[] = {
-{"message",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** HELLO ********************/
-
-/* HELLO history */
-commandHistory HELLO_History[] = {
-{"6.2.0","`protover` made optional; when called without arguments the command reports the current connection's context."},
-{0}
-};
-
-/* HELLO tips */
-#define HELLO_tips NULL
-
-/* HELLO arguments auth argument table */
-struct redisCommandArg HELLO_arguments_auth_Subargs[] = {
-{"username",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"password",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* HELLO arguments argument table */
-struct redisCommandArg HELLO_arguments_Subargs[] = {
-{"protover",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"auth",ARG_TYPE_BLOCK,-1,"AUTH",NULL,NULL,CMD_ARG_OPTIONAL,.subargs=HELLO_arguments_auth_Subargs},
-{"clientname",ARG_TYPE_STRING,-1,"SETNAME",NULL,NULL,CMD_ARG_OPTIONAL},
-{0}
-};
-
-/* HELLO argument table */
-struct redisCommandArg HELLO_Args[] = {
-{"arguments",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,.subargs=HELLO_arguments_Subargs},
-{0}
-};
-
-/********** PING ********************/
-
-/* PING history */
-#define PING_History NULL
-
-/* PING tips */
-const char *PING_tips[] = {
-"request_policy:all_shards",
-"response_policy:all_succeeded",
-NULL
-};
-
-/* PING argument table */
-struct redisCommandArg PING_Args[] = {
-{"message",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL},
-{0}
-};
-
-/********** QUIT ********************/
-
-/* QUIT history */
-#define QUIT_History NULL
-
-/* QUIT tips */
-#define QUIT_tips NULL
-
-/********** RESET ********************/
-
-/* RESET history */
-#define RESET_History NULL
-
-/* RESET tips */
-#define RESET_tips NULL
-
-/********** SELECT ********************/
-
-/* SELECT history */
-#define SELECT_History NULL
-
-/* SELECT tips */
-#define SELECT_tips NULL
-
-/* SELECT argument table */
-struct redisCommandArg SELECT_Args[] = {
-{"index",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** COPY ********************/
-
-/* COPY history */
-#define COPY_History NULL
-
-/* COPY tips */
-#define COPY_tips NULL
-
-/* COPY argument table */
-struct redisCommandArg COPY_Args[] = {
-{"source",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"destination",ARG_TYPE_KEY,1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"destination-db",ARG_TYPE_INTEGER,-1,"DB",NULL,NULL,CMD_ARG_OPTIONAL},
-{"replace",ARG_TYPE_PURE_TOKEN,-1,"REPLACE",NULL,NULL,CMD_ARG_OPTIONAL},
-{0}
-};
-
-/********** DEL ********************/
-
-/* DEL history */
-#define DEL_History NULL
-
-/* DEL tips */
-const char *DEL_tips[] = {
-"request_policy:multi_shard",
-"response_policy:agg_sum",
-NULL
-};
-
-/* DEL argument table */
-struct redisCommandArg DEL_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** DUMP ********************/
-
-/* DUMP history */
-#define DUMP_History NULL
-
-/* DUMP tips */
-const char *DUMP_tips[] = {
-"nondeterministic_output",
-NULL
-};
-
-/* DUMP argument table */
-struct redisCommandArg DUMP_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** EXISTS ********************/
-
-/* EXISTS history */
-commandHistory EXISTS_History[] = {
-{"3.0.3","Accepts multiple `key` arguments."},
-{0}
-};
-
-/* EXISTS tips */
-const char *EXISTS_tips[] = {
-"request_policy:multi_shard",
-"response_policy:agg_sum",
-NULL
-};
-
-/* EXISTS argument table */
-struct redisCommandArg EXISTS_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** EXPIRE ********************/
-
-/* EXPIRE history */
-commandHistory EXPIRE_History[] = {
-{"7.0.0","Added options: `NX`, `XX`, `GT` and `LT`."},
-{0}
-};
-
-/* EXPIRE tips */
-#define EXPIRE_tips NULL
-
-/* EXPIRE condition argument table */
-struct redisCommandArg EXPIRE_condition_Subargs[] = {
-{"nx",ARG_TYPE_PURE_TOKEN,-1,"NX",NULL,NULL,CMD_ARG_NONE},
-{"xx",ARG_TYPE_PURE_TOKEN,-1,"XX",NULL,NULL,CMD_ARG_NONE},
-{"gt",ARG_TYPE_PURE_TOKEN,-1,"GT",NULL,NULL,CMD_ARG_NONE},
-{"lt",ARG_TYPE_PURE_TOKEN,-1,"LT",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* EXPIRE argument table */
-struct redisCommandArg EXPIRE_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"seconds",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"condition",ARG_TYPE_ONEOF,-1,NULL,NULL,"7.0.0",CMD_ARG_OPTIONAL,.subargs=EXPIRE_condition_Subargs},
-{0}
-};
-
-/********** EXPIREAT ********************/
-
-/* EXPIREAT history */
-commandHistory EXPIREAT_History[] = {
-{"7.0.0","Added options: `NX`, `XX`, `GT` and `LT`."},
-{0}
-};
-
-/* EXPIREAT tips */
-#define EXPIREAT_tips NULL
-
-/* EXPIREAT condition argument table */
-struct redisCommandArg EXPIREAT_condition_Subargs[] = {
-{"nx",ARG_TYPE_PURE_TOKEN,-1,"NX",NULL,NULL,CMD_ARG_NONE},
-{"xx",ARG_TYPE_PURE_TOKEN,-1,"XX",NULL,NULL,CMD_ARG_NONE},
-{"gt",ARG_TYPE_PURE_TOKEN,-1,"GT",NULL,NULL,CMD_ARG_NONE},
-{"lt",ARG_TYPE_PURE_TOKEN,-1,"LT",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* EXPIREAT argument table */
-struct redisCommandArg EXPIREAT_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"unix-time-seconds",ARG_TYPE_UNIX_TIME,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"condition",ARG_TYPE_ONEOF,-1,NULL,NULL,"7.0.0",CMD_ARG_OPTIONAL,.subargs=EXPIREAT_condition_Subargs},
-{0}
-};
-
-/********** EXPIRETIME ********************/
-
-/* EXPIRETIME history */
-#define EXPIRETIME_History NULL
-
-/* EXPIRETIME tips */
-#define EXPIRETIME_tips NULL
-
-/* EXPIRETIME argument table */
-struct redisCommandArg EXPIRETIME_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** KEYS ********************/
-
-/* KEYS history */
-#define KEYS_History NULL
-
-/* KEYS tips */
-const char *KEYS_tips[] = {
-"request_policy:all_shards",
-"nondeterministic_output_order",
-NULL
-};
-
-/* KEYS argument table */
-struct redisCommandArg KEYS_Args[] = {
-{"pattern",ARG_TYPE_PATTERN,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** MIGRATE ********************/
-
-/* MIGRATE history */
-commandHistory MIGRATE_History[] = {
-{"3.0.0","Added the `COPY` and `REPLACE` options."},
-{"3.0.6","Added the `KEYS` option."},
-{"4.0.7","Added the `AUTH` option."},
-{"6.0.0","Added the `AUTH2` option."},
-{0}
-};
-
-/* MIGRATE tips */
-const char *MIGRATE_tips[] = {
-"nondeterministic_output",
-NULL
-};
-
-/* MIGRATE key_selector argument table */
-struct redisCommandArg MIGRATE_key_selector_Subargs[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"empty-string",ARG_TYPE_PURE_TOKEN,-1,"""",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* MIGRATE authentication auth2 argument table */
-struct redisCommandArg MIGRATE_authentication_auth2_Subargs[] = {
-{"username",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"password",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* MIGRATE authentication argument table */
-struct redisCommandArg MIGRATE_authentication_Subargs[] = {
-{"auth",ARG_TYPE_STRING,-1,"AUTH",NULL,"4.0.7",CMD_ARG_NONE,.display_text="password"},
-{"auth2",ARG_TYPE_BLOCK,-1,"AUTH2",NULL,"6.0.0",CMD_ARG_NONE,.subargs=MIGRATE_authentication_auth2_Subargs},
-{0}
-};
-
-/* MIGRATE argument table */
-struct redisCommandArg MIGRATE_Args[] = {
-{"host",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"port",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"key-selector",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,.subargs=MIGRATE_key_selector_Subargs},
-{"destination-db",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"timeout",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"copy",ARG_TYPE_PURE_TOKEN,-1,"COPY",NULL,"3.0.0",CMD_ARG_OPTIONAL},
-{"replace",ARG_TYPE_PURE_TOKEN,-1,"REPLACE",NULL,"3.0.0",CMD_ARG_OPTIONAL},
-{"authentication",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,.subargs=MIGRATE_authentication_Subargs},
-{"keys",ARG_TYPE_KEY,1,"KEYS",NULL,"3.0.6",CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE,.display_text="key"},
-{0}
-};
-
-/********** MOVE ********************/
-
-/* MOVE history */
-#define MOVE_History NULL
-
-/* MOVE tips */
-#define MOVE_tips NULL
-
-/* MOVE argument table */
-struct redisCommandArg MOVE_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"db",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** OBJECT ENCODING ********************/
-
-/* OBJECT ENCODING history */
-#define OBJECT_ENCODING_History NULL
-
-/* OBJECT ENCODING tips */
-const char *OBJECT_ENCODING_tips[] = {
-"nondeterministic_output",
-NULL
-};
-
-/* OBJECT ENCODING argument table */
-struct redisCommandArg OBJECT_ENCODING_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** OBJECT FREQ ********************/
-
-/* OBJECT FREQ history */
-#define OBJECT_FREQ_History NULL
-
-/* OBJECT FREQ tips */
-const char *OBJECT_FREQ_tips[] = {
-"nondeterministic_output",
-NULL
-};
-
-/* OBJECT FREQ argument table */
-struct redisCommandArg OBJECT_FREQ_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** OBJECT HELP ********************/
-
-/* OBJECT HELP history */
-#define OBJECT_HELP_History NULL
-
-/* OBJECT HELP tips */
-#define OBJECT_HELP_tips NULL
-
-/********** OBJECT IDLETIME ********************/
-
-/* OBJECT IDLETIME history */
-#define OBJECT_IDLETIME_History NULL
-
-/* OBJECT IDLETIME tips */
-const char *OBJECT_IDLETIME_tips[] = {
-"nondeterministic_output",
-NULL
-};
-
-/* OBJECT IDLETIME argument table */
-struct redisCommandArg OBJECT_IDLETIME_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** OBJECT REFCOUNT ********************/
-
-/* OBJECT REFCOUNT history */
-#define OBJECT_REFCOUNT_History NULL
-
-/* OBJECT REFCOUNT tips */
-const char *OBJECT_REFCOUNT_tips[] = {
-"nondeterministic_output",
-NULL
-};
-
-/* OBJECT REFCOUNT argument table */
-struct redisCommandArg OBJECT_REFCOUNT_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* OBJECT command table */
-struct redisCommand OBJECT_Subcommands[] = {
-{"encoding","Inspect the internal encoding of a Redis object","O(1)","2.2.3",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_GENERIC,OBJECT_ENCODING_History,OBJECT_ENCODING_tips,objectCommand,3,CMD_READONLY,ACL_CATEGORY_KEYSPACE,{{NULL,CMD_KEY_RO,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=OBJECT_ENCODING_Args},
-{"freq","Get the logarithmic access frequency counter of a Redis object","O(1)","4.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_GENERIC,OBJECT_FREQ_History,OBJECT_FREQ_tips,objectCommand,3,CMD_READONLY,ACL_CATEGORY_KEYSPACE,{{NULL,CMD_KEY_RO,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=OBJECT_FREQ_Args},
-{"help","Show helpful text about the different subcommands","O(1)","6.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_GENERIC,OBJECT_HELP_History,OBJECT_HELP_tips,objectCommand,2,CMD_LOADING|CMD_STALE,ACL_CATEGORY_KEYSPACE},
-{"idletime","Get the time since a Redis object was last accessed","O(1)","2.2.3",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_GENERIC,OBJECT_IDLETIME_History,OBJECT_IDLETIME_tips,objectCommand,3,CMD_READONLY,ACL_CATEGORY_KEYSPACE,{{NULL,CMD_KEY_RO,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=OBJECT_IDLETIME_Args},
-{"refcount","Get the number of references to the value of the key","O(1)","2.2.3",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_GENERIC,OBJECT_REFCOUNT_History,OBJECT_REFCOUNT_tips,objectCommand,3,CMD_READONLY,ACL_CATEGORY_KEYSPACE,{{NULL,CMD_KEY_RO,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=OBJECT_REFCOUNT_Args},
-{0}
-};
-
-/********** OBJECT ********************/
-
-/* OBJECT history */
-#define OBJECT_History NULL
-
-/* OBJECT tips */
-#define OBJECT_tips NULL
-
-/********** PERSIST ********************/
-
-/* PERSIST history */
-#define PERSIST_History NULL
-
-/* PERSIST tips */
-#define PERSIST_tips NULL
-
-/* PERSIST argument table */
-struct redisCommandArg PERSIST_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** PEXPIRE ********************/
-
-/* PEXPIRE history */
-commandHistory PEXPIRE_History[] = {
-{"7.0.0","Added options: `NX`, `XX`, `GT` and `LT`."},
-{0}
-};
-
-/* PEXPIRE tips */
-#define PEXPIRE_tips NULL
-
-/* PEXPIRE condition argument table */
-struct redisCommandArg PEXPIRE_condition_Subargs[] = {
-{"nx",ARG_TYPE_PURE_TOKEN,-1,"NX",NULL,NULL,CMD_ARG_NONE},
-{"xx",ARG_TYPE_PURE_TOKEN,-1,"XX",NULL,NULL,CMD_ARG_NONE},
-{"gt",ARG_TYPE_PURE_TOKEN,-1,"GT",NULL,NULL,CMD_ARG_NONE},
-{"lt",ARG_TYPE_PURE_TOKEN,-1,"LT",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* PEXPIRE argument table */
-struct redisCommandArg PEXPIRE_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"milliseconds",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"condition",ARG_TYPE_ONEOF,-1,NULL,NULL,"7.0.0",CMD_ARG_OPTIONAL,.subargs=PEXPIRE_condition_Subargs},
-{0}
-};
-
-/********** PEXPIREAT ********************/
-
-/* PEXPIREAT history */
-commandHistory PEXPIREAT_History[] = {
-{"7.0.0","Added options: `NX`, `XX`, `GT` and `LT`."},
-{0}
-};
-
-/* PEXPIREAT tips */
-#define PEXPIREAT_tips NULL
-
-/* PEXPIREAT condition argument table */
-struct redisCommandArg PEXPIREAT_condition_Subargs[] = {
-{"nx",ARG_TYPE_PURE_TOKEN,-1,"NX",NULL,NULL,CMD_ARG_NONE},
-{"xx",ARG_TYPE_PURE_TOKEN,-1,"XX",NULL,NULL,CMD_ARG_NONE},
-{"gt",ARG_TYPE_PURE_TOKEN,-1,"GT",NULL,NULL,CMD_ARG_NONE},
-{"lt",ARG_TYPE_PURE_TOKEN,-1,"LT",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* PEXPIREAT argument table */
-struct redisCommandArg PEXPIREAT_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"unix-time-milliseconds",ARG_TYPE_UNIX_TIME,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"condition",ARG_TYPE_ONEOF,-1,NULL,NULL,"7.0.0",CMD_ARG_OPTIONAL,.subargs=PEXPIREAT_condition_Subargs},
-{0}
-};
-
-/********** PEXPIRETIME ********************/
-
-/* PEXPIRETIME history */
-#define PEXPIRETIME_History NULL
-
-/* PEXPIRETIME tips */
-#define PEXPIRETIME_tips NULL
-
-/* PEXPIRETIME argument table */
-struct redisCommandArg PEXPIRETIME_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** PTTL ********************/
-
-/* PTTL history */
-commandHistory PTTL_History[] = {
-{"2.8.0","Added the -2 reply."},
-{0}
-};
-
-/* PTTL tips */
-const char *PTTL_tips[] = {
-"nondeterministic_output",
-NULL
-};
-
-/* PTTL argument table */
-struct redisCommandArg PTTL_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** RANDOMKEY ********************/
-
-/* RANDOMKEY history */
-#define RANDOMKEY_History NULL
-
-/* RANDOMKEY tips */
-const char *RANDOMKEY_tips[] = {
-"request_policy:all_shards",
-"nondeterministic_output",
-NULL
-};
-
-/********** RENAME ********************/
-
-/* RENAME history */
-#define RENAME_History NULL
-
-/* RENAME tips */
-#define RENAME_tips NULL
-
-/* RENAME argument table */
-struct redisCommandArg RENAME_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"newkey",ARG_TYPE_KEY,1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** RENAMENX ********************/
-
-/* RENAMENX history */
-commandHistory RENAMENX_History[] = {
-{"3.2.0","The command no longer returns an error when source and destination names are the same."},
-{0}
-};
-
-/* RENAMENX tips */
-#define RENAMENX_tips NULL
-
-/* RENAMENX argument table */
-struct redisCommandArg RENAMENX_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"newkey",ARG_TYPE_KEY,1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** RESTORE ********************/
-
-/* RESTORE history */
-commandHistory RESTORE_History[] = {
-{"3.0.0","Added the `REPLACE` modifier."},
-{"5.0.0","Added the `ABSTTL` modifier."},
-{"5.0.0","Added the `IDLETIME` and `FREQ` options."},
-{0}
-};
-
-/* RESTORE tips */
-#define RESTORE_tips NULL
-
-/* RESTORE argument table */
-struct redisCommandArg RESTORE_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"ttl",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"serialized-value",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"replace",ARG_TYPE_PURE_TOKEN,-1,"REPLACE",NULL,"3.0.0",CMD_ARG_OPTIONAL},
-{"absttl",ARG_TYPE_PURE_TOKEN,-1,"ABSTTL",NULL,"5.0.0",CMD_ARG_OPTIONAL},
-{"seconds",ARG_TYPE_INTEGER,-1,"IDLETIME",NULL,"5.0.0",CMD_ARG_OPTIONAL},
-{"frequency",ARG_TYPE_INTEGER,-1,"FREQ",NULL,"5.0.0",CMD_ARG_OPTIONAL},
-{0}
-};
-
-/********** SCAN ********************/
-
-/* SCAN history */
-commandHistory SCAN_History[] = {
-{"6.0.0","Added the `TYPE` subcommand."},
-{0}
-};
-
-/* SCAN tips */
-const char *SCAN_tips[] = {
-"nondeterministic_output",
-"request_policy:special",
-NULL
-};
-
-/* SCAN argument table */
-struct redisCommandArg SCAN_Args[] = {
-{"cursor",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"pattern",ARG_TYPE_PATTERN,-1,"MATCH",NULL,NULL,CMD_ARG_OPTIONAL},
-{"count",ARG_TYPE_INTEGER,-1,"COUNT",NULL,NULL,CMD_ARG_OPTIONAL},
-{"type",ARG_TYPE_STRING,-1,"TYPE",NULL,"6.0.0",CMD_ARG_OPTIONAL},
-{0}
-};
-
-/********** SORT ********************/
-
-/* SORT history */
-#define SORT_History NULL
-
-/* SORT tips */
-#define SORT_tips NULL
-
-/* SORT limit argument table */
-struct redisCommandArg SORT_limit_Subargs[] = {
-{"offset",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"count",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* SORT order argument table */
-struct redisCommandArg SORT_order_Subargs[] = {
-{"asc",ARG_TYPE_PURE_TOKEN,-1,"ASC",NULL,NULL,CMD_ARG_NONE},
-{"desc",ARG_TYPE_PURE_TOKEN,-1,"DESC",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* SORT argument table */
-struct redisCommandArg SORT_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"by-pattern",ARG_TYPE_PATTERN,1,"BY",NULL,NULL,CMD_ARG_OPTIONAL,.display_text="pattern"},
-{"limit",ARG_TYPE_BLOCK,-1,"LIMIT",NULL,NULL,CMD_ARG_OPTIONAL,.subargs=SORT_limit_Subargs},
-{"get-pattern",ARG_TYPE_PATTERN,1,"GET",NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE|CMD_ARG_MULTIPLE_TOKEN,.display_text="pattern"},
-{"order",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,.subargs=SORT_order_Subargs},
-{"sorting",ARG_TYPE_PURE_TOKEN,-1,"ALPHA",NULL,NULL,CMD_ARG_OPTIONAL},
-{"destination",ARG_TYPE_KEY,2,"STORE",NULL,NULL,CMD_ARG_OPTIONAL},
-{0}
-};
-
-/********** SORT_RO ********************/
-
-/* SORT_RO history */
-#define SORT_RO_History NULL
-
-/* SORT_RO tips */
-#define SORT_RO_tips NULL
-
-/* SORT_RO limit argument table */
-struct redisCommandArg SORT_RO_limit_Subargs[] = {
-{"offset",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"count",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* SORT_RO order argument table */
-struct redisCommandArg SORT_RO_order_Subargs[] = {
-{"asc",ARG_TYPE_PURE_TOKEN,-1,"ASC",NULL,NULL,CMD_ARG_NONE},
-{"desc",ARG_TYPE_PURE_TOKEN,-1,"DESC",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* SORT_RO argument table */
-struct redisCommandArg SORT_RO_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"by-pattern",ARG_TYPE_PATTERN,1,"BY",NULL,NULL,CMD_ARG_OPTIONAL,.display_text="pattern"},
-{"limit",ARG_TYPE_BLOCK,-1,"LIMIT",NULL,NULL,CMD_ARG_OPTIONAL,.subargs=SORT_RO_limit_Subargs},
-{"get-pattern",ARG_TYPE_PATTERN,1,"GET",NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE|CMD_ARG_MULTIPLE_TOKEN,.display_text="pattern"},
-{"order",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,.subargs=SORT_RO_order_Subargs},
-{"sorting",ARG_TYPE_PURE_TOKEN,-1,"ALPHA",NULL,NULL,CMD_ARG_OPTIONAL},
-{0}
-};
-
-/********** TOUCH ********************/
-
-/* TOUCH history */
-#define TOUCH_History NULL
-
-/* TOUCH tips */
-const char *TOUCH_tips[] = {
-"request_policy:multi_shard",
-"response_policy:agg_sum",
-NULL
-};
-
-/* TOUCH argument table */
-struct redisCommandArg TOUCH_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** TTL ********************/
-
-/* TTL history */
-commandHistory TTL_History[] = {
-{"2.8.0","Added the -2 reply."},
-{0}
-};
-
-/* TTL tips */
-const char *TTL_tips[] = {
-"nondeterministic_output",
-NULL
-};
-
-/* TTL argument table */
-struct redisCommandArg TTL_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** TYPE ********************/
-
-/* TYPE history */
-#define TYPE_History NULL
-
-/* TYPE tips */
-#define TYPE_tips NULL
-
-/* TYPE argument table */
-struct redisCommandArg TYPE_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** UNLINK ********************/
-
-/* UNLINK history */
-#define UNLINK_History NULL
-
-/* UNLINK tips */
-const char *UNLINK_tips[] = {
-"request_policy:multi_shard",
-"response_policy:agg_sum",
-NULL
-};
-
-/* UNLINK argument table */
-struct redisCommandArg UNLINK_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** WAIT ********************/
-
-/* WAIT history */
-#define WAIT_History NULL
-
-/* WAIT tips */
-const char *WAIT_tips[] = {
-"request_policy:all_shards",
-"response_policy:agg_min",
-NULL
-};
-
-/* WAIT argument table */
-struct redisCommandArg WAIT_Args[] = {
-{"numreplicas",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"timeout",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** WAITAOF ********************/
-
-/* WAITAOF history */
-#define WAITAOF_History NULL
-
-/* WAITAOF tips */
-const char *WAITAOF_tips[] = {
-"request_policy:all_shards",
-"response_policy:agg_min",
-NULL
-};
-
-/* WAITAOF argument table */
-struct redisCommandArg WAITAOF_Args[] = {
-{"numlocal",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"numreplicas",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"timeout",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** GEOADD ********************/
-
-/* GEOADD history */
-commandHistory GEOADD_History[] = {
-{"6.2.0","Added the `CH`, `NX` and `XX` options."},
-{0}
-};
-
-/* GEOADD tips */
-#define GEOADD_tips NULL
-
-/* GEOADD condition argument table */
-struct redisCommandArg GEOADD_condition_Subargs[] = {
-{"nx",ARG_TYPE_PURE_TOKEN,-1,"NX",NULL,NULL,CMD_ARG_NONE},
-{"xx",ARG_TYPE_PURE_TOKEN,-1,"XX",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* GEOADD data argument table */
-struct redisCommandArg GEOADD_data_Subargs[] = {
-{"longitude",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"latitude",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"member",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* GEOADD argument table */
-struct redisCommandArg GEOADD_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"condition",ARG_TYPE_ONEOF,-1,NULL,NULL,"6.2.0",CMD_ARG_OPTIONAL,.subargs=GEOADD_condition_Subargs},
-{"change",ARG_TYPE_PURE_TOKEN,-1,"CH",NULL,"6.2.0",CMD_ARG_OPTIONAL},
-{"data",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,.subargs=GEOADD_data_Subargs},
-{0}
-};
-
-/********** GEODIST ********************/
-
-/* GEODIST history */
-#define GEODIST_History NULL
-
-/* GEODIST tips */
-#define GEODIST_tips NULL
-
-/* GEODIST unit argument table */
-struct redisCommandArg GEODIST_unit_Subargs[] = {
-{"m",ARG_TYPE_PURE_TOKEN,-1,"M",NULL,NULL,CMD_ARG_NONE},
-{"km",ARG_TYPE_PURE_TOKEN,-1,"KM",NULL,NULL,CMD_ARG_NONE},
-{"ft",ARG_TYPE_PURE_TOKEN,-1,"FT",NULL,NULL,CMD_ARG_NONE},
-{"mi",ARG_TYPE_PURE_TOKEN,-1,"MI",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* GEODIST argument table */
-struct redisCommandArg GEODIST_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"member1",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"member2",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"unit",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,.subargs=GEODIST_unit_Subargs},
-{0}
-};
-
-/********** GEOHASH ********************/
-
-/* GEOHASH history */
-#define GEOHASH_History NULL
-
-/* GEOHASH tips */
-#define GEOHASH_tips NULL
-
-/* GEOHASH argument table */
-struct redisCommandArg GEOHASH_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"member",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** GEOPOS ********************/
-
-/* GEOPOS history */
-#define GEOPOS_History NULL
-
-/* GEOPOS tips */
-#define GEOPOS_tips NULL
-
-/* GEOPOS argument table */
-struct redisCommandArg GEOPOS_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"member",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** GEORADIUS ********************/
-
-/* GEORADIUS history */
-commandHistory GEORADIUS_History[] = {
-{"6.2.0","Added the `ANY` option for `COUNT`."},
-{"7.0.0","Added support for uppercase unit names."},
-{0}
-};
-
-/* GEORADIUS tips */
-#define GEORADIUS_tips NULL
-
-/* GEORADIUS unit argument table */
-struct redisCommandArg GEORADIUS_unit_Subargs[] = {
-{"m",ARG_TYPE_PURE_TOKEN,-1,"M",NULL,NULL,CMD_ARG_NONE},
-{"km",ARG_TYPE_PURE_TOKEN,-1,"KM",NULL,NULL,CMD_ARG_NONE},
-{"ft",ARG_TYPE_PURE_TOKEN,-1,"FT",NULL,NULL,CMD_ARG_NONE},
-{"mi",ARG_TYPE_PURE_TOKEN,-1,"MI",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* GEORADIUS count_block argument table */
-struct redisCommandArg GEORADIUS_count_block_Subargs[] = {
-{"count",ARG_TYPE_INTEGER,-1,"COUNT",NULL,NULL,CMD_ARG_NONE},
-{"any",ARG_TYPE_PURE_TOKEN,-1,"ANY",NULL,"6.2.0",CMD_ARG_OPTIONAL},
-{0}
-};
-
-/* GEORADIUS order argument table */
-struct redisCommandArg GEORADIUS_order_Subargs[] = {
-{"asc",ARG_TYPE_PURE_TOKEN,-1,"ASC",NULL,NULL,CMD_ARG_NONE},
-{"desc",ARG_TYPE_PURE_TOKEN,-1,"DESC",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* GEORADIUS argument table */
-struct redisCommandArg GEORADIUS_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"longitude",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"latitude",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"radius",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"unit",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,.subargs=GEORADIUS_unit_Subargs},
-{"withcoord",ARG_TYPE_PURE_TOKEN,-1,"WITHCOORD",NULL,NULL,CMD_ARG_OPTIONAL},
-{"withdist",ARG_TYPE_PURE_TOKEN,-1,"WITHDIST",NULL,NULL,CMD_ARG_OPTIONAL},
-{"withhash",ARG_TYPE_PURE_TOKEN,-1,"WITHHASH",NULL,NULL,CMD_ARG_OPTIONAL},
-{"count-block",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,.subargs=GEORADIUS_count_block_Subargs},
-{"order",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,.subargs=GEORADIUS_order_Subargs},
-{"storekey",ARG_TYPE_KEY,1,"STORE",NULL,NULL,CMD_ARG_OPTIONAL,.display_text="key"},
-{"storedistkey",ARG_TYPE_KEY,2,"STOREDIST",NULL,NULL,CMD_ARG_OPTIONAL,.display_text="key"},
-{0}
-};
-
-/********** GEORADIUSBYMEMBER ********************/
-
-/* GEORADIUSBYMEMBER history */
-commandHistory GEORADIUSBYMEMBER_History[] = {
-{"7.0.0","Added support for uppercase unit names."},
-{0}
-};
-
-/* GEORADIUSBYMEMBER tips */
-#define GEORADIUSBYMEMBER_tips NULL
-
-/* GEORADIUSBYMEMBER unit argument table */
-struct redisCommandArg GEORADIUSBYMEMBER_unit_Subargs[] = {
-{"m",ARG_TYPE_PURE_TOKEN,-1,"M",NULL,NULL,CMD_ARG_NONE},
-{"km",ARG_TYPE_PURE_TOKEN,-1,"KM",NULL,NULL,CMD_ARG_NONE},
-{"ft",ARG_TYPE_PURE_TOKEN,-1,"FT",NULL,NULL,CMD_ARG_NONE},
-{"mi",ARG_TYPE_PURE_TOKEN,-1,"MI",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* GEORADIUSBYMEMBER count_block argument table */
-struct redisCommandArg GEORADIUSBYMEMBER_count_block_Subargs[] = {
-{"count",ARG_TYPE_INTEGER,-1,"COUNT",NULL,NULL,CMD_ARG_NONE},
-{"any",ARG_TYPE_PURE_TOKEN,-1,"ANY",NULL,NULL,CMD_ARG_OPTIONAL},
-{0}
-};
-
-/* GEORADIUSBYMEMBER order argument table */
-struct redisCommandArg GEORADIUSBYMEMBER_order_Subargs[] = {
-{"asc",ARG_TYPE_PURE_TOKEN,-1,"ASC",NULL,NULL,CMD_ARG_NONE},
-{"desc",ARG_TYPE_PURE_TOKEN,-1,"DESC",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* GEORADIUSBYMEMBER argument table */
-struct redisCommandArg GEORADIUSBYMEMBER_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"member",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"radius",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"unit",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,.subargs=GEORADIUSBYMEMBER_unit_Subargs},
-{"withcoord",ARG_TYPE_PURE_TOKEN,-1,"WITHCOORD",NULL,NULL,CMD_ARG_OPTIONAL},
-{"withdist",ARG_TYPE_PURE_TOKEN,-1,"WITHDIST",NULL,NULL,CMD_ARG_OPTIONAL},
-{"withhash",ARG_TYPE_PURE_TOKEN,-1,"WITHHASH",NULL,NULL,CMD_ARG_OPTIONAL},
-{"count-block",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,.subargs=GEORADIUSBYMEMBER_count_block_Subargs},
-{"order",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,.subargs=GEORADIUSBYMEMBER_order_Subargs},
-{"storekey",ARG_TYPE_KEY,1,"STORE",NULL,NULL,CMD_ARG_OPTIONAL,.display_text="key"},
-{"storedistkey",ARG_TYPE_KEY,2,"STOREDIST",NULL,NULL,CMD_ARG_OPTIONAL,.display_text="key"},
-{0}
-};
-
-/********** GEORADIUSBYMEMBER_RO ********************/
-
-/* GEORADIUSBYMEMBER_RO history */
-#define GEORADIUSBYMEMBER_RO_History NULL
-
-/* GEORADIUSBYMEMBER_RO tips */
-#define GEORADIUSBYMEMBER_RO_tips NULL
-
-/* GEORADIUSBYMEMBER_RO unit argument table */
-struct redisCommandArg GEORADIUSBYMEMBER_RO_unit_Subargs[] = {
-{"m",ARG_TYPE_PURE_TOKEN,-1,"M",NULL,NULL,CMD_ARG_NONE},
-{"km",ARG_TYPE_PURE_TOKEN,-1,"KM",NULL,NULL,CMD_ARG_NONE},
-{"ft",ARG_TYPE_PURE_TOKEN,-1,"FT",NULL,NULL,CMD_ARG_NONE},
-{"mi",ARG_TYPE_PURE_TOKEN,-1,"MI",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* GEORADIUSBYMEMBER_RO count_block argument table */
-struct redisCommandArg GEORADIUSBYMEMBER_RO_count_block_Subargs[] = {
-{"count",ARG_TYPE_INTEGER,-1,"COUNT",NULL,NULL,CMD_ARG_NONE},
-{"any",ARG_TYPE_PURE_TOKEN,-1,"ANY",NULL,NULL,CMD_ARG_OPTIONAL},
-{0}
-};
-
-/* GEORADIUSBYMEMBER_RO order argument table */
-struct redisCommandArg GEORADIUSBYMEMBER_RO_order_Subargs[] = {
-{"asc",ARG_TYPE_PURE_TOKEN,-1,"ASC",NULL,NULL,CMD_ARG_NONE},
-{"desc",ARG_TYPE_PURE_TOKEN,-1,"DESC",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* GEORADIUSBYMEMBER_RO argument table */
-struct redisCommandArg GEORADIUSBYMEMBER_RO_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"member",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"radius",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"unit",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,.subargs=GEORADIUSBYMEMBER_RO_unit_Subargs},
-{"withcoord",ARG_TYPE_PURE_TOKEN,-1,"WITHCOORD",NULL,NULL,CMD_ARG_OPTIONAL},
-{"withdist",ARG_TYPE_PURE_TOKEN,-1,"WITHDIST",NULL,NULL,CMD_ARG_OPTIONAL},
-{"withhash",ARG_TYPE_PURE_TOKEN,-1,"WITHHASH",NULL,NULL,CMD_ARG_OPTIONAL},
-{"count-block",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,.subargs=GEORADIUSBYMEMBER_RO_count_block_Subargs},
-{"order",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,.subargs=GEORADIUSBYMEMBER_RO_order_Subargs},
-{0}
-};
-
-/********** GEORADIUS_RO ********************/
-
-/* GEORADIUS_RO history */
-commandHistory GEORADIUS_RO_History[] = {
-{"6.2.0","Added the `ANY` option for `COUNT`."},
-{0}
-};
-
-/* GEORADIUS_RO tips */
-#define GEORADIUS_RO_tips NULL
-
-/* GEORADIUS_RO unit argument table */
-struct redisCommandArg GEORADIUS_RO_unit_Subargs[] = {
-{"m",ARG_TYPE_PURE_TOKEN,-1,"M",NULL,NULL,CMD_ARG_NONE},
-{"km",ARG_TYPE_PURE_TOKEN,-1,"KM",NULL,NULL,CMD_ARG_NONE},
-{"ft",ARG_TYPE_PURE_TOKEN,-1,"FT",NULL,NULL,CMD_ARG_NONE},
-{"mi",ARG_TYPE_PURE_TOKEN,-1,"MI",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* GEORADIUS_RO count_block argument table */
-struct redisCommandArg GEORADIUS_RO_count_block_Subargs[] = {
-{"count",ARG_TYPE_INTEGER,-1,"COUNT",NULL,NULL,CMD_ARG_NONE},
-{"any",ARG_TYPE_PURE_TOKEN,-1,"ANY",NULL,"6.2.0",CMD_ARG_OPTIONAL},
-{0}
-};
-
-/* GEORADIUS_RO order argument table */
-struct redisCommandArg GEORADIUS_RO_order_Subargs[] = {
-{"asc",ARG_TYPE_PURE_TOKEN,-1,"ASC",NULL,NULL,CMD_ARG_NONE},
-{"desc",ARG_TYPE_PURE_TOKEN,-1,"DESC",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* GEORADIUS_RO argument table */
-struct redisCommandArg GEORADIUS_RO_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"longitude",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"latitude",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"radius",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"unit",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,.subargs=GEORADIUS_RO_unit_Subargs},
-{"withcoord",ARG_TYPE_PURE_TOKEN,-1,"WITHCOORD",NULL,NULL,CMD_ARG_OPTIONAL},
-{"withdist",ARG_TYPE_PURE_TOKEN,-1,"WITHDIST",NULL,NULL,CMD_ARG_OPTIONAL},
-{"withhash",ARG_TYPE_PURE_TOKEN,-1,"WITHHASH",NULL,NULL,CMD_ARG_OPTIONAL},
-{"count-block",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,.subargs=GEORADIUS_RO_count_block_Subargs},
-{"order",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,.subargs=GEORADIUS_RO_order_Subargs},
-{0}
-};
-
-/********** GEOSEARCH ********************/
-
-/* GEOSEARCH history */
-commandHistory GEOSEARCH_History[] = {
-{"7.0.0","Added support for uppercase unit names."},
-{0}
-};
-
-/* GEOSEARCH tips */
-#define GEOSEARCH_tips NULL
-
-/* GEOSEARCH from fromlonlat argument table */
-struct redisCommandArg GEOSEARCH_from_fromlonlat_Subargs[] = {
-{"longitude",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"latitude",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* GEOSEARCH from argument table */
-struct redisCommandArg GEOSEARCH_from_Subargs[] = {
-{"member",ARG_TYPE_STRING,-1,"FROMMEMBER",NULL,NULL,CMD_ARG_NONE},
-{"fromlonlat",ARG_TYPE_BLOCK,-1,"FROMLONLAT",NULL,NULL,CMD_ARG_NONE,.subargs=GEOSEARCH_from_fromlonlat_Subargs},
-{0}
-};
-
-/* GEOSEARCH by circle unit argument table */
-struct redisCommandArg GEOSEARCH_by_circle_unit_Subargs[] = {
-{"m",ARG_TYPE_PURE_TOKEN,-1,"M",NULL,NULL,CMD_ARG_NONE},
-{"km",ARG_TYPE_PURE_TOKEN,-1,"KM",NULL,NULL,CMD_ARG_NONE},
-{"ft",ARG_TYPE_PURE_TOKEN,-1,"FT",NULL,NULL,CMD_ARG_NONE},
-{"mi",ARG_TYPE_PURE_TOKEN,-1,"MI",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* GEOSEARCH by circle argument table */
-struct redisCommandArg GEOSEARCH_by_circle_Subargs[] = {
-{"radius",ARG_TYPE_DOUBLE,-1,"BYRADIUS",NULL,NULL,CMD_ARG_NONE},
-{"unit",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,.subargs=GEOSEARCH_by_circle_unit_Subargs},
-{0}
-};
-
-/* GEOSEARCH by box unit argument table */
-struct redisCommandArg GEOSEARCH_by_box_unit_Subargs[] = {
-{"m",ARG_TYPE_PURE_TOKEN,-1,"M",NULL,NULL,CMD_ARG_NONE},
-{"km",ARG_TYPE_PURE_TOKEN,-1,"KM",NULL,NULL,CMD_ARG_NONE},
-{"ft",ARG_TYPE_PURE_TOKEN,-1,"FT",NULL,NULL,CMD_ARG_NONE},
-{"mi",ARG_TYPE_PURE_TOKEN,-1,"MI",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* GEOSEARCH by box argument table */
-struct redisCommandArg GEOSEARCH_by_box_Subargs[] = {
-{"width",ARG_TYPE_DOUBLE,-1,"BYBOX",NULL,NULL,CMD_ARG_NONE},
-{"height",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"unit",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,.subargs=GEOSEARCH_by_box_unit_Subargs},
-{0}
-};
-
-/* GEOSEARCH by argument table */
-struct redisCommandArg GEOSEARCH_by_Subargs[] = {
-{"circle",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_NONE,.subargs=GEOSEARCH_by_circle_Subargs},
-{"box",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_NONE,.subargs=GEOSEARCH_by_box_Subargs},
-{0}
-};
-
-/* GEOSEARCH order argument table */
-struct redisCommandArg GEOSEARCH_order_Subargs[] = {
-{"asc",ARG_TYPE_PURE_TOKEN,-1,"ASC",NULL,NULL,CMD_ARG_NONE},
-{"desc",ARG_TYPE_PURE_TOKEN,-1,"DESC",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* GEOSEARCH count_block argument table */
-struct redisCommandArg GEOSEARCH_count_block_Subargs[] = {
-{"count",ARG_TYPE_INTEGER,-1,"COUNT",NULL,NULL,CMD_ARG_NONE},
-{"any",ARG_TYPE_PURE_TOKEN,-1,"ANY",NULL,NULL,CMD_ARG_OPTIONAL},
-{0}
-};
-
-/* GEOSEARCH argument table */
-struct redisCommandArg GEOSEARCH_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"from",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,.subargs=GEOSEARCH_from_Subargs},
-{"by",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,.subargs=GEOSEARCH_by_Subargs},
-{"order",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,.subargs=GEOSEARCH_order_Subargs},
-{"count-block",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,.subargs=GEOSEARCH_count_block_Subargs},
-{"withcoord",ARG_TYPE_PURE_TOKEN,-1,"WITHCOORD",NULL,NULL,CMD_ARG_OPTIONAL},
-{"withdist",ARG_TYPE_PURE_TOKEN,-1,"WITHDIST",NULL,NULL,CMD_ARG_OPTIONAL},
-{"withhash",ARG_TYPE_PURE_TOKEN,-1,"WITHHASH",NULL,NULL,CMD_ARG_OPTIONAL},
-{0}
-};
-
-/********** GEOSEARCHSTORE ********************/
-
-/* GEOSEARCHSTORE history */
-commandHistory GEOSEARCHSTORE_History[] = {
-{"7.0.0","Added support for uppercase unit names."},
-{0}
-};
-
-/* GEOSEARCHSTORE tips */
-#define GEOSEARCHSTORE_tips NULL
-
-/* GEOSEARCHSTORE from fromlonlat argument table */
-struct redisCommandArg GEOSEARCHSTORE_from_fromlonlat_Subargs[] = {
-{"longitude",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"latitude",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* GEOSEARCHSTORE from argument table */
-struct redisCommandArg GEOSEARCHSTORE_from_Subargs[] = {
-{"member",ARG_TYPE_STRING,-1,"FROMMEMBER",NULL,NULL,CMD_ARG_NONE},
-{"fromlonlat",ARG_TYPE_BLOCK,-1,"FROMLONLAT",NULL,NULL,CMD_ARG_NONE,.subargs=GEOSEARCHSTORE_from_fromlonlat_Subargs},
-{0}
-};
-
-/* GEOSEARCHSTORE by circle unit argument table */
-struct redisCommandArg GEOSEARCHSTORE_by_circle_unit_Subargs[] = {
-{"m",ARG_TYPE_PURE_TOKEN,-1,"M",NULL,NULL,CMD_ARG_NONE},
-{"km",ARG_TYPE_PURE_TOKEN,-1,"KM",NULL,NULL,CMD_ARG_NONE},
-{"ft",ARG_TYPE_PURE_TOKEN,-1,"FT",NULL,NULL,CMD_ARG_NONE},
-{"mi",ARG_TYPE_PURE_TOKEN,-1,"MI",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* GEOSEARCHSTORE by circle argument table */
-struct redisCommandArg GEOSEARCHSTORE_by_circle_Subargs[] = {
-{"radius",ARG_TYPE_DOUBLE,-1,"BYRADIUS",NULL,NULL,CMD_ARG_NONE},
-{"unit",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,.subargs=GEOSEARCHSTORE_by_circle_unit_Subargs},
-{0}
-};
-
-/* GEOSEARCHSTORE by box unit argument table */
-struct redisCommandArg GEOSEARCHSTORE_by_box_unit_Subargs[] = {
-{"m",ARG_TYPE_PURE_TOKEN,-1,"M",NULL,NULL,CMD_ARG_NONE},
-{"km",ARG_TYPE_PURE_TOKEN,-1,"KM",NULL,NULL,CMD_ARG_NONE},
-{"ft",ARG_TYPE_PURE_TOKEN,-1,"FT",NULL,NULL,CMD_ARG_NONE},
-{"mi",ARG_TYPE_PURE_TOKEN,-1,"MI",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* GEOSEARCHSTORE by box argument table */
-struct redisCommandArg GEOSEARCHSTORE_by_box_Subargs[] = {
-{"width",ARG_TYPE_DOUBLE,-1,"BYBOX",NULL,NULL,CMD_ARG_NONE},
-{"height",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"unit",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,.subargs=GEOSEARCHSTORE_by_box_unit_Subargs},
-{0}
-};
-
-/* GEOSEARCHSTORE by argument table */
-struct redisCommandArg GEOSEARCHSTORE_by_Subargs[] = {
-{"circle",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_NONE,.subargs=GEOSEARCHSTORE_by_circle_Subargs},
-{"box",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_NONE,.subargs=GEOSEARCHSTORE_by_box_Subargs},
-{0}
-};
-
-/* GEOSEARCHSTORE order argument table */
-struct redisCommandArg GEOSEARCHSTORE_order_Subargs[] = {
-{"asc",ARG_TYPE_PURE_TOKEN,-1,"ASC",NULL,NULL,CMD_ARG_NONE},
-{"desc",ARG_TYPE_PURE_TOKEN,-1,"DESC",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* GEOSEARCHSTORE count_block argument table */
-struct redisCommandArg GEOSEARCHSTORE_count_block_Subargs[] = {
-{"count",ARG_TYPE_INTEGER,-1,"COUNT",NULL,NULL,CMD_ARG_NONE},
-{"any",ARG_TYPE_PURE_TOKEN,-1,"ANY",NULL,NULL,CMD_ARG_OPTIONAL},
-{0}
-};
-
-/* GEOSEARCHSTORE argument table */
-struct redisCommandArg GEOSEARCHSTORE_Args[] = {
-{"destination",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"source",ARG_TYPE_KEY,1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"from",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,.subargs=GEOSEARCHSTORE_from_Subargs},
-{"by",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,.subargs=GEOSEARCHSTORE_by_Subargs},
-{"order",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,.subargs=GEOSEARCHSTORE_order_Subargs},
-{"count-block",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,.subargs=GEOSEARCHSTORE_count_block_Subargs},
-{"storedist",ARG_TYPE_PURE_TOKEN,-1,"STOREDIST",NULL,NULL,CMD_ARG_OPTIONAL},
-{0}
-};
-
-/********** HDEL ********************/
-
-/* HDEL history */
-commandHistory HDEL_History[] = {
-{"2.4.0","Accepts multiple `field` arguments."},
-{0}
-};
-
-/* HDEL tips */
-#define HDEL_tips NULL
-
-/* HDEL argument table */
-struct redisCommandArg HDEL_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"field",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** HEXISTS ********************/
-
-/* HEXISTS history */
-#define HEXISTS_History NULL
-
-/* HEXISTS tips */
-#define HEXISTS_tips NULL
-
-/* HEXISTS argument table */
-struct redisCommandArg HEXISTS_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"field",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** HGET ********************/
-
-/* HGET history */
-#define HGET_History NULL
-
-/* HGET tips */
-#define HGET_tips NULL
-
-/* HGET argument table */
-struct redisCommandArg HGET_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"field",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** HGETALL ********************/
-
-/* HGETALL history */
-#define HGETALL_History NULL
-
-/* HGETALL tips */
-const char *HGETALL_tips[] = {
-"nondeterministic_output_order",
-NULL
-};
-
-/* HGETALL argument table */
-struct redisCommandArg HGETALL_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** HINCRBY ********************/
-
-/* HINCRBY history */
-#define HINCRBY_History NULL
-
-/* HINCRBY tips */
-#define HINCRBY_tips NULL
-
-/* HINCRBY argument table */
-struct redisCommandArg HINCRBY_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"field",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"increment",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** HINCRBYFLOAT ********************/
-
-/* HINCRBYFLOAT history */
-#define HINCRBYFLOAT_History NULL
-
-/* HINCRBYFLOAT tips */
-#define HINCRBYFLOAT_tips NULL
-
-/* HINCRBYFLOAT argument table */
-struct redisCommandArg HINCRBYFLOAT_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"field",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"increment",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** HKEYS ********************/
-
-/* HKEYS history */
-#define HKEYS_History NULL
-
-/* HKEYS tips */
-const char *HKEYS_tips[] = {
-"nondeterministic_output_order",
-NULL
-};
-
-/* HKEYS argument table */
-struct redisCommandArg HKEYS_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** HLEN ********************/
-
-/* HLEN history */
-#define HLEN_History NULL
-
-/* HLEN tips */
-#define HLEN_tips NULL
-
-/* HLEN argument table */
-struct redisCommandArg HLEN_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** HMGET ********************/
-
-/* HMGET history */
-#define HMGET_History NULL
-
-/* HMGET tips */
-#define HMGET_tips NULL
-
-/* HMGET argument table */
-struct redisCommandArg HMGET_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"field",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** HMSET ********************/
-
-/* HMSET history */
-#define HMSET_History NULL
-
-/* HMSET tips */
-#define HMSET_tips NULL
-
-/* HMSET data argument table */
-struct redisCommandArg HMSET_data_Subargs[] = {
-{"field",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"value",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* HMSET argument table */
-struct redisCommandArg HMSET_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"data",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,.subargs=HMSET_data_Subargs},
-{0}
-};
-
-/********** HRANDFIELD ********************/
-
-/* HRANDFIELD history */
-#define HRANDFIELD_History NULL
-
-/* HRANDFIELD tips */
-const char *HRANDFIELD_tips[] = {
-"nondeterministic_output",
-NULL
-};
-
-/* HRANDFIELD options argument table */
-struct redisCommandArg HRANDFIELD_options_Subargs[] = {
-{"count",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"withvalues",ARG_TYPE_PURE_TOKEN,-1,"WITHVALUES",NULL,NULL,CMD_ARG_OPTIONAL},
-{0}
-};
-
-/* HRANDFIELD argument table */
-struct redisCommandArg HRANDFIELD_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"options",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,.subargs=HRANDFIELD_options_Subargs},
-{0}
-};
-
-/********** HSCAN ********************/
-
-/* HSCAN history */
-#define HSCAN_History NULL
-
-/* HSCAN tips */
-const char *HSCAN_tips[] = {
-"nondeterministic_output",
-NULL
-};
-
-/* HSCAN argument table */
-struct redisCommandArg HSCAN_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"cursor",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"pattern",ARG_TYPE_PATTERN,-1,"MATCH",NULL,NULL,CMD_ARG_OPTIONAL},
-{"count",ARG_TYPE_INTEGER,-1,"COUNT",NULL,NULL,CMD_ARG_OPTIONAL},
-{0}
-};
-
-/********** HSET ********************/
-
-/* HSET history */
-commandHistory HSET_History[] = {
-{"4.0.0","Accepts multiple `field` and `value` arguments."},
-{0}
-};
-
-/* HSET tips */
-#define HSET_tips NULL
-
-/* HSET data argument table */
-struct redisCommandArg HSET_data_Subargs[] = {
-{"field",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"value",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* HSET argument table */
-struct redisCommandArg HSET_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"data",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,.subargs=HSET_data_Subargs},
-{0}
-};
-
-/********** HSETNX ********************/
-
-/* HSETNX history */
-#define HSETNX_History NULL
-
-/* HSETNX tips */
-#define HSETNX_tips NULL
-
-/* HSETNX argument table */
-struct redisCommandArg HSETNX_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"field",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"value",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** HSTRLEN ********************/
-
-/* HSTRLEN history */
-#define HSTRLEN_History NULL
-
-/* HSTRLEN tips */
-#define HSTRLEN_tips NULL
-
-/* HSTRLEN argument table */
-struct redisCommandArg HSTRLEN_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"field",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** HVALS ********************/
-
-/* HVALS history */
-#define HVALS_History NULL
-
-/* HVALS tips */
-const char *HVALS_tips[] = {
-"nondeterministic_output_order",
-NULL
-};
-
-/* HVALS argument table */
-struct redisCommandArg HVALS_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** PFADD ********************/
-
-/* PFADD history */
-#define PFADD_History NULL
-
-/* PFADD tips */
-#define PFADD_tips NULL
-
-/* PFADD argument table */
-struct redisCommandArg PFADD_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"element",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** PFCOUNT ********************/
-
-/* PFCOUNT history */
-#define PFCOUNT_History NULL
-
-/* PFCOUNT tips */
-#define PFCOUNT_tips NULL
-
-/* PFCOUNT argument table */
-struct redisCommandArg PFCOUNT_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** PFDEBUG ********************/
-
-/* PFDEBUG history */
-#define PFDEBUG_History NULL
-
-/* PFDEBUG tips */
-#define PFDEBUG_tips NULL
-
-/* PFDEBUG argument table */
-struct redisCommandArg PFDEBUG_Args[] = {
-{"subcommand",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** PFMERGE ********************/
-
-/* PFMERGE history */
-#define PFMERGE_History NULL
-
-/* PFMERGE tips */
-#define PFMERGE_tips NULL
-
-/* PFMERGE argument table */
-struct redisCommandArg PFMERGE_Args[] = {
-{"destkey",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"sourcekey",ARG_TYPE_KEY,1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** PFSELFTEST ********************/
-
-/* PFSELFTEST history */
-#define PFSELFTEST_History NULL
-
-/* PFSELFTEST tips */
-#define PFSELFTEST_tips NULL
-
-/********** BLMOVE ********************/
-
-/* BLMOVE history */
-#define BLMOVE_History NULL
-
-/* BLMOVE tips */
-#define BLMOVE_tips NULL
-
-/* BLMOVE wherefrom argument table */
-struct redisCommandArg BLMOVE_wherefrom_Subargs[] = {
-{"left",ARG_TYPE_PURE_TOKEN,-1,"LEFT",NULL,NULL,CMD_ARG_NONE},
-{"right",ARG_TYPE_PURE_TOKEN,-1,"RIGHT",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* BLMOVE whereto argument table */
-struct redisCommandArg BLMOVE_whereto_Subargs[] = {
-{"left",ARG_TYPE_PURE_TOKEN,-1,"LEFT",NULL,NULL,CMD_ARG_NONE},
-{"right",ARG_TYPE_PURE_TOKEN,-1,"RIGHT",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* BLMOVE argument table */
-struct redisCommandArg BLMOVE_Args[] = {
-{"source",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"destination",ARG_TYPE_KEY,1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"wherefrom",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,.subargs=BLMOVE_wherefrom_Subargs},
-{"whereto",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,.subargs=BLMOVE_whereto_Subargs},
-{"timeout",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** BLMPOP ********************/
-
-/* BLMPOP history */
-#define BLMPOP_History NULL
-
-/* BLMPOP tips */
-#define BLMPOP_tips NULL
-
-/* BLMPOP where argument table */
-struct redisCommandArg BLMPOP_where_Subargs[] = {
-{"left",ARG_TYPE_PURE_TOKEN,-1,"LEFT",NULL,NULL,CMD_ARG_NONE},
-{"right",ARG_TYPE_PURE_TOKEN,-1,"RIGHT",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* BLMPOP argument table */
-struct redisCommandArg BLMPOP_Args[] = {
-{"timeout",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"numkeys",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{"where",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,.subargs=BLMPOP_where_Subargs},
-{"count",ARG_TYPE_INTEGER,-1,"COUNT",NULL,NULL,CMD_ARG_OPTIONAL},
-{0}
-};
-
-/********** BLPOP ********************/
-
-/* BLPOP history */
-commandHistory BLPOP_History[] = {
-{"6.0.0","`timeout` is interpreted as a double instead of an integer."},
-{0}
-};
-
-/* BLPOP tips */
-#define BLPOP_tips NULL
-
-/* BLPOP argument table */
-struct redisCommandArg BLPOP_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{"timeout",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** BRPOP ********************/
-
-/* BRPOP history */
-commandHistory BRPOP_History[] = {
-{"6.0.0","`timeout` is interpreted as a double instead of an integer."},
-{0}
-};
-
-/* BRPOP tips */
-#define BRPOP_tips NULL
-
-/* BRPOP argument table */
-struct redisCommandArg BRPOP_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{"timeout",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** BRPOPLPUSH ********************/
-
-/* BRPOPLPUSH history */
-commandHistory BRPOPLPUSH_History[] = {
-{"6.0.0","`timeout` is interpreted as a double instead of an integer."},
-{0}
-};
-
-/* BRPOPLPUSH tips */
-#define BRPOPLPUSH_tips NULL
-
-/* BRPOPLPUSH argument table */
-struct redisCommandArg BRPOPLPUSH_Args[] = {
-{"source",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"destination",ARG_TYPE_KEY,1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"timeout",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** LINDEX ********************/
-
-/* LINDEX history */
-#define LINDEX_History NULL
-
-/* LINDEX tips */
-#define LINDEX_tips NULL
-
-/* LINDEX argument table */
-struct redisCommandArg LINDEX_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"index",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** LINSERT ********************/
-
-/* LINSERT history */
-#define LINSERT_History NULL
-
-/* LINSERT tips */
-#define LINSERT_tips NULL
-
-/* LINSERT where argument table */
-struct redisCommandArg LINSERT_where_Subargs[] = {
-{"before",ARG_TYPE_PURE_TOKEN,-1,"BEFORE",NULL,NULL,CMD_ARG_NONE},
-{"after",ARG_TYPE_PURE_TOKEN,-1,"AFTER",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* LINSERT argument table */
-struct redisCommandArg LINSERT_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"where",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,.subargs=LINSERT_where_Subargs},
-{"pivot",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"element",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** LLEN ********************/
-
-/* LLEN history */
-#define LLEN_History NULL
-
-/* LLEN tips */
-#define LLEN_tips NULL
-
-/* LLEN argument table */
-struct redisCommandArg LLEN_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** LMOVE ********************/
-
-/* LMOVE history */
-#define LMOVE_History NULL
-
-/* LMOVE tips */
-#define LMOVE_tips NULL
-
-/* LMOVE wherefrom argument table */
-struct redisCommandArg LMOVE_wherefrom_Subargs[] = {
-{"left",ARG_TYPE_PURE_TOKEN,-1,"LEFT",NULL,NULL,CMD_ARG_NONE},
-{"right",ARG_TYPE_PURE_TOKEN,-1,"RIGHT",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* LMOVE whereto argument table */
-struct redisCommandArg LMOVE_whereto_Subargs[] = {
-{"left",ARG_TYPE_PURE_TOKEN,-1,"LEFT",NULL,NULL,CMD_ARG_NONE},
-{"right",ARG_TYPE_PURE_TOKEN,-1,"RIGHT",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* LMOVE argument table */
-struct redisCommandArg LMOVE_Args[] = {
-{"source",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"destination",ARG_TYPE_KEY,1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"wherefrom",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,.subargs=LMOVE_wherefrom_Subargs},
-{"whereto",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,.subargs=LMOVE_whereto_Subargs},
-{0}
-};
-
-/********** LMPOP ********************/
-
-/* LMPOP history */
-#define LMPOP_History NULL
-
-/* LMPOP tips */
-#define LMPOP_tips NULL
-
-/* LMPOP where argument table */
-struct redisCommandArg LMPOP_where_Subargs[] = {
-{"left",ARG_TYPE_PURE_TOKEN,-1,"LEFT",NULL,NULL,CMD_ARG_NONE},
-{"right",ARG_TYPE_PURE_TOKEN,-1,"RIGHT",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* LMPOP argument table */
-struct redisCommandArg LMPOP_Args[] = {
-{"numkeys",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{"where",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,.subargs=LMPOP_where_Subargs},
-{"count",ARG_TYPE_INTEGER,-1,"COUNT",NULL,NULL,CMD_ARG_OPTIONAL},
-{0}
-};
-
-/********** LPOP ********************/
-
-/* LPOP history */
-commandHistory LPOP_History[] = {
-{"6.2.0","Added the `count` argument."},
-{0}
-};
-
-/* LPOP tips */
-#define LPOP_tips NULL
-
-/* LPOP argument table */
-struct redisCommandArg LPOP_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"count",ARG_TYPE_INTEGER,-1,NULL,NULL,"6.2.0",CMD_ARG_OPTIONAL},
-{0}
-};
-
-/********** LPOS ********************/
-
-/* LPOS history */
-#define LPOS_History NULL
-
-/* LPOS tips */
-#define LPOS_tips NULL
-
-/* LPOS argument table */
-struct redisCommandArg LPOS_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"element",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"rank",ARG_TYPE_INTEGER,-1,"RANK",NULL,NULL,CMD_ARG_OPTIONAL},
-{"num-matches",ARG_TYPE_INTEGER,-1,"COUNT",NULL,NULL,CMD_ARG_OPTIONAL},
-{"len",ARG_TYPE_INTEGER,-1,"MAXLEN",NULL,NULL,CMD_ARG_OPTIONAL},
-{0}
-};
-
-/********** LPUSH ********************/
-
-/* LPUSH history */
-commandHistory LPUSH_History[] = {
-{"2.4.0","Accepts multiple `element` arguments."},
-{0}
-};
-
-/* LPUSH tips */
-#define LPUSH_tips NULL
-
-/* LPUSH argument table */
-struct redisCommandArg LPUSH_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"element",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** LPUSHX ********************/
-
-/* LPUSHX history */
-commandHistory LPUSHX_History[] = {
-{"4.0.0","Accepts multiple `element` arguments."},
-{0}
-};
-
-/* LPUSHX tips */
-#define LPUSHX_tips NULL
-
-/* LPUSHX argument table */
-struct redisCommandArg LPUSHX_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"element",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** LRANGE ********************/
-
-/* LRANGE history */
-#define LRANGE_History NULL
-
-/* LRANGE tips */
-#define LRANGE_tips NULL
-
-/* LRANGE argument table */
-struct redisCommandArg LRANGE_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"start",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"stop",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** LREM ********************/
-
-/* LREM history */
-#define LREM_History NULL
-
-/* LREM tips */
-#define LREM_tips NULL
-
-/* LREM argument table */
-struct redisCommandArg LREM_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"count",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"element",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** LSET ********************/
-
-/* LSET history */
-#define LSET_History NULL
-
-/* LSET tips */
-#define LSET_tips NULL
-
-/* LSET argument table */
-struct redisCommandArg LSET_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"index",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"element",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** LTRIM ********************/
-
-/* LTRIM history */
-#define LTRIM_History NULL
-
-/* LTRIM tips */
-#define LTRIM_tips NULL
-
-/* LTRIM argument table */
-struct redisCommandArg LTRIM_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"start",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"stop",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** RPOP ********************/
-
-/* RPOP history */
-commandHistory RPOP_History[] = {
-{"6.2.0","Added the `count` argument."},
-{0}
-};
-
-/* RPOP tips */
-#define RPOP_tips NULL
-
-/* RPOP argument table */
-struct redisCommandArg RPOP_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"count",ARG_TYPE_INTEGER,-1,NULL,NULL,"6.2.0",CMD_ARG_OPTIONAL},
-{0}
-};
-
-/********** RPOPLPUSH ********************/
-
-/* RPOPLPUSH history */
-#define RPOPLPUSH_History NULL
-
-/* RPOPLPUSH tips */
-#define RPOPLPUSH_tips NULL
-
-/* RPOPLPUSH argument table */
-struct redisCommandArg RPOPLPUSH_Args[] = {
-{"source",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"destination",ARG_TYPE_KEY,1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** RPUSH ********************/
-
-/* RPUSH history */
-commandHistory RPUSH_History[] = {
-{"2.4.0","Accepts multiple `element` arguments."},
-{0}
-};
-
-/* RPUSH tips */
-#define RPUSH_tips NULL
-
-/* RPUSH argument table */
-struct redisCommandArg RPUSH_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"element",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** RPUSHX ********************/
-
-/* RPUSHX history */
-commandHistory RPUSHX_History[] = {
-{"4.0.0","Accepts multiple `element` arguments."},
-{0}
-};
-
-/* RPUSHX tips */
-#define RPUSHX_tips NULL
-
-/* RPUSHX argument table */
-struct redisCommandArg RPUSHX_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"element",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** PSUBSCRIBE ********************/
-
-/* PSUBSCRIBE history */
-#define PSUBSCRIBE_History NULL
-
-/* PSUBSCRIBE tips */
-#define PSUBSCRIBE_tips NULL
-
-/* PSUBSCRIBE argument table */
-struct redisCommandArg PSUBSCRIBE_Args[] = {
-{"pattern",ARG_TYPE_PATTERN,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** PUBLISH ********************/
-
-/* PUBLISH history */
-#define PUBLISH_History NULL
-
-/* PUBLISH tips */
-#define PUBLISH_tips NULL
-
-/* PUBLISH argument table */
-struct redisCommandArg PUBLISH_Args[] = {
-{"channel",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"message",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** PUBSUB CHANNELS ********************/
-
-/* PUBSUB CHANNELS history */
-#define PUBSUB_CHANNELS_History NULL
-
-/* PUBSUB CHANNELS tips */
-#define PUBSUB_CHANNELS_tips NULL
-
-/* PUBSUB CHANNELS argument table */
-struct redisCommandArg PUBSUB_CHANNELS_Args[] = {
-{"pattern",ARG_TYPE_PATTERN,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL},
-{0}
-};
-
-/********** PUBSUB HELP ********************/
-
-/* PUBSUB HELP history */
-#define PUBSUB_HELP_History NULL
-
-/* PUBSUB HELP tips */
-#define PUBSUB_HELP_tips NULL
-
-/********** PUBSUB NUMPAT ********************/
-
-/* PUBSUB NUMPAT history */
-#define PUBSUB_NUMPAT_History NULL
-
-/* PUBSUB NUMPAT tips */
-#define PUBSUB_NUMPAT_tips NULL
-
-/********** PUBSUB NUMSUB ********************/
-
-/* PUBSUB NUMSUB history */
-#define PUBSUB_NUMSUB_History NULL
-
-/* PUBSUB NUMSUB tips */
-#define PUBSUB_NUMSUB_tips NULL
-
-/* PUBSUB NUMSUB argument table */
-struct redisCommandArg PUBSUB_NUMSUB_Args[] = {
-{"channel",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** PUBSUB SHARDCHANNELS ********************/
-
-/* PUBSUB SHARDCHANNELS history */
-#define PUBSUB_SHARDCHANNELS_History NULL
-
-/* PUBSUB SHARDCHANNELS tips */
-#define PUBSUB_SHARDCHANNELS_tips NULL
-
-/* PUBSUB SHARDCHANNELS argument table */
-struct redisCommandArg PUBSUB_SHARDCHANNELS_Args[] = {
-{"pattern",ARG_TYPE_PATTERN,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL},
-{0}
-};
-
-/********** PUBSUB SHARDNUMSUB ********************/
-
-/* PUBSUB SHARDNUMSUB history */
-#define PUBSUB_SHARDNUMSUB_History NULL
-
-/* PUBSUB SHARDNUMSUB tips */
-#define PUBSUB_SHARDNUMSUB_tips NULL
-
-/* PUBSUB SHARDNUMSUB argument table */
-struct redisCommandArg PUBSUB_SHARDNUMSUB_Args[] = {
-{"shardchannel",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE},
-{0}
-};
-
-/* PUBSUB command table */
-struct redisCommand PUBSUB_Subcommands[] = {
-{"channels","List active channels","O(N) where N is the number of active channels, and assuming constant time pattern matching (relatively short channels and patterns)","2.8.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_PUBSUB,PUBSUB_CHANNELS_History,PUBSUB_CHANNELS_tips,pubsubCommand,-2,CMD_PUBSUB|CMD_LOADING|CMD_STALE,0,.args=PUBSUB_CHANNELS_Args},
-{"help","Show helpful text about the different subcommands","O(1)","6.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_PUBSUB,PUBSUB_HELP_History,PUBSUB_HELP_tips,pubsubCommand,2,CMD_LOADING|CMD_STALE,0},
-{"numpat","Get the count of unique patterns pattern subscriptions","O(1)","2.8.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_PUBSUB,PUBSUB_NUMPAT_History,PUBSUB_NUMPAT_tips,pubsubCommand,2,CMD_PUBSUB|CMD_LOADING|CMD_STALE,0},
-{"numsub","Get the count of subscribers for channels","O(N) for the NUMSUB subcommand, where N is the number of requested channels","2.8.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_PUBSUB,PUBSUB_NUMSUB_History,PUBSUB_NUMSUB_tips,pubsubCommand,-2,CMD_PUBSUB|CMD_LOADING|CMD_STALE,0,.args=PUBSUB_NUMSUB_Args},
-{"shardchannels","List active shard channels","O(N) where N is the number of active shard channels, and assuming constant time pattern matching (relatively short shard channels).","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_PUBSUB,PUBSUB_SHARDCHANNELS_History,PUBSUB_SHARDCHANNELS_tips,pubsubCommand,-2,CMD_PUBSUB|CMD_LOADING|CMD_STALE,0,.args=PUBSUB_SHARDCHANNELS_Args},
-{"shardnumsub","Get the count of subscribers for shard channels","O(N) for the SHARDNUMSUB subcommand, where N is the number of requested shard channels","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_PUBSUB,PUBSUB_SHARDNUMSUB_History,PUBSUB_SHARDNUMSUB_tips,pubsubCommand,-2,CMD_PUBSUB|CMD_LOADING|CMD_STALE,0,.args=PUBSUB_SHARDNUMSUB_Args},
-{0}
-};
-
-/********** PUBSUB ********************/
-
-/* PUBSUB history */
-#define PUBSUB_History NULL
-
-/* PUBSUB tips */
-#define PUBSUB_tips NULL
-
-/********** PUNSUBSCRIBE ********************/
-
-/* PUNSUBSCRIBE history */
-#define PUNSUBSCRIBE_History NULL
-
-/* PUNSUBSCRIBE tips */
-#define PUNSUBSCRIBE_tips NULL
-
-/* PUNSUBSCRIBE argument table */
-struct redisCommandArg PUNSUBSCRIBE_Args[] = {
-{"pattern",ARG_TYPE_PATTERN,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** SPUBLISH ********************/
-
-/* SPUBLISH history */
-#define SPUBLISH_History NULL
-
-/* SPUBLISH tips */
-#define SPUBLISH_tips NULL
-
-/* SPUBLISH argument table */
-struct redisCommandArg SPUBLISH_Args[] = {
-{"shardchannel",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"message",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** SSUBSCRIBE ********************/
-
-/* SSUBSCRIBE history */
-#define SSUBSCRIBE_History NULL
-
-/* SSUBSCRIBE tips */
-#define SSUBSCRIBE_tips NULL
-
-/* SSUBSCRIBE argument table */
-struct redisCommandArg SSUBSCRIBE_Args[] = {
-{"shardchannel",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** SUBSCRIBE ********************/
-
-/* SUBSCRIBE history */
-#define SUBSCRIBE_History NULL
-
-/* SUBSCRIBE tips */
-#define SUBSCRIBE_tips NULL
-
-/* SUBSCRIBE argument table */
-struct redisCommandArg SUBSCRIBE_Args[] = {
-{"channel",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** SUNSUBSCRIBE ********************/
-
-/* SUNSUBSCRIBE history */
-#define SUNSUBSCRIBE_History NULL
-
-/* SUNSUBSCRIBE tips */
-#define SUNSUBSCRIBE_tips NULL
-
-/* SUNSUBSCRIBE argument table */
-struct redisCommandArg SUNSUBSCRIBE_Args[] = {
-{"shardchannel",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** UNSUBSCRIBE ********************/
-
-/* UNSUBSCRIBE history */
-#define UNSUBSCRIBE_History NULL
-
-/* UNSUBSCRIBE tips */
-#define UNSUBSCRIBE_tips NULL
-
-/* UNSUBSCRIBE argument table */
-struct redisCommandArg UNSUBSCRIBE_Args[] = {
-{"channel",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** EVAL ********************/
-
-/* EVAL history */
-#define EVAL_History NULL
-
-/* EVAL tips */
-#define EVAL_tips NULL
-
-/* EVAL argument table */
-struct redisCommandArg EVAL_Args[] = {
-{"script",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"numkeys",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE},
-{"arg",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** EVALSHA ********************/
-
-/* EVALSHA history */
-#define EVALSHA_History NULL
-
-/* EVALSHA tips */
-#define EVALSHA_tips NULL
-
-/* EVALSHA argument table */
-struct redisCommandArg EVALSHA_Args[] = {
-{"sha1",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"numkeys",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE},
-{"arg",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** EVALSHA_RO ********************/
-
-/* EVALSHA_RO history */
-#define EVALSHA_RO_History NULL
-
-/* EVALSHA_RO tips */
-#define EVALSHA_RO_tips NULL
-
-/* EVALSHA_RO argument table */
-struct redisCommandArg EVALSHA_RO_Args[] = {
-{"sha1",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"numkeys",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE},
-{"arg",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** EVAL_RO ********************/
-
-/* EVAL_RO history */
-#define EVAL_RO_History NULL
-
-/* EVAL_RO tips */
-#define EVAL_RO_tips NULL
-
-/* EVAL_RO argument table */
-struct redisCommandArg EVAL_RO_Args[] = {
-{"script",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"numkeys",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE},
-{"arg",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** FCALL ********************/
-
-/* FCALL history */
-#define FCALL_History NULL
-
-/* FCALL tips */
-#define FCALL_tips NULL
-
-/* FCALL argument table */
-struct redisCommandArg FCALL_Args[] = {
-{"function",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"numkeys",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE},
-{"arg",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** FCALL_RO ********************/
-
-/* FCALL_RO history */
-#define FCALL_RO_History NULL
-
-/* FCALL_RO tips */
-#define FCALL_RO_tips NULL
-
-/* FCALL_RO argument table */
-struct redisCommandArg FCALL_RO_Args[] = {
-{"function",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"numkeys",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE},
-{"arg",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** FUNCTION DELETE ********************/
-
-/* FUNCTION DELETE history */
-#define FUNCTION_DELETE_History NULL
-
-/* FUNCTION DELETE tips */
-const char *FUNCTION_DELETE_tips[] = {
-"request_policy:all_shards",
-"response_policy:all_succeeded",
-NULL
-};
-
-/* FUNCTION DELETE argument table */
-struct redisCommandArg FUNCTION_DELETE_Args[] = {
-{"library-name",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** FUNCTION DUMP ********************/
-
-/* FUNCTION DUMP history */
-#define FUNCTION_DUMP_History NULL
-
-/* FUNCTION DUMP tips */
-#define FUNCTION_DUMP_tips NULL
-
-/********** FUNCTION FLUSH ********************/
-
-/* FUNCTION FLUSH history */
-#define FUNCTION_FLUSH_History NULL
-
-/* FUNCTION FLUSH tips */
-const char *FUNCTION_FLUSH_tips[] = {
-"request_policy:all_shards",
-"response_policy:all_succeeded",
-NULL
-};
-
-/* FUNCTION FLUSH flush_type argument table */
-struct redisCommandArg FUNCTION_FLUSH_flush_type_Subargs[] = {
-{"async",ARG_TYPE_PURE_TOKEN,-1,"ASYNC",NULL,NULL,CMD_ARG_NONE},
-{"sync",ARG_TYPE_PURE_TOKEN,-1,"SYNC",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* FUNCTION FLUSH argument table */
-struct redisCommandArg FUNCTION_FLUSH_Args[] = {
-{"flush-type",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,.subargs=FUNCTION_FLUSH_flush_type_Subargs},
-{0}
-};
-
-/********** FUNCTION HELP ********************/
-
-/* FUNCTION HELP history */
-#define FUNCTION_HELP_History NULL
-
-/* FUNCTION HELP tips */
-#define FUNCTION_HELP_tips NULL
-
-/********** FUNCTION KILL ********************/
-
-/* FUNCTION KILL history */
-#define FUNCTION_KILL_History NULL
-
-/* FUNCTION KILL tips */
-const char *FUNCTION_KILL_tips[] = {
-"request_policy:all_shards",
-"response_policy:one_succeeded",
-NULL
-};
-
-/********** FUNCTION LIST ********************/
-
-/* FUNCTION LIST history */
-#define FUNCTION_LIST_History NULL
-
-/* FUNCTION LIST tips */
-const char *FUNCTION_LIST_tips[] = {
-"nondeterministic_output_order",
-NULL
-};
-
-/* FUNCTION LIST argument table */
-struct redisCommandArg FUNCTION_LIST_Args[] = {
-{"library-name-pattern",ARG_TYPE_STRING,-1,"LIBRARYNAME",NULL,NULL,CMD_ARG_OPTIONAL},
-{"withcode",ARG_TYPE_PURE_TOKEN,-1,"WITHCODE",NULL,NULL,CMD_ARG_OPTIONAL},
-{0}
-};
-
-/********** FUNCTION LOAD ********************/
-
-/* FUNCTION LOAD history */
-#define FUNCTION_LOAD_History NULL
-
-/* FUNCTION LOAD tips */
-const char *FUNCTION_LOAD_tips[] = {
-"request_policy:all_shards",
-"response_policy:all_succeeded",
-NULL
-};
-
-/* FUNCTION LOAD argument table */
-struct redisCommandArg FUNCTION_LOAD_Args[] = {
-{"replace",ARG_TYPE_PURE_TOKEN,-1,"REPLACE",NULL,NULL,CMD_ARG_OPTIONAL},
-{"function-code",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** FUNCTION RESTORE ********************/
-
-/* FUNCTION RESTORE history */
-#define FUNCTION_RESTORE_History NULL
-
-/* FUNCTION RESTORE tips */
-const char *FUNCTION_RESTORE_tips[] = {
-"request_policy:all_shards",
-"response_policy:all_succeeded",
-NULL
-};
-
-/* FUNCTION RESTORE policy argument table */
-struct redisCommandArg FUNCTION_RESTORE_policy_Subargs[] = {
-{"flush",ARG_TYPE_PURE_TOKEN,-1,"FLUSH",NULL,NULL,CMD_ARG_NONE},
-{"append",ARG_TYPE_PURE_TOKEN,-1,"APPEND",NULL,NULL,CMD_ARG_NONE},
-{"replace",ARG_TYPE_PURE_TOKEN,-1,"REPLACE",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* FUNCTION RESTORE argument table */
-struct redisCommandArg FUNCTION_RESTORE_Args[] = {
-{"serialized-value",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"policy",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,.subargs=FUNCTION_RESTORE_policy_Subargs},
-{0}
-};
-
-/********** FUNCTION STATS ********************/
-
-/* FUNCTION STATS history */
-#define FUNCTION_STATS_History NULL
-
-/* FUNCTION STATS tips */
-const char *FUNCTION_STATS_tips[] = {
-"nondeterministic_output",
-"request_policy:all_shards",
-"response_policy:special",
-NULL
-};
-
-/* FUNCTION command table */
-struct redisCommand FUNCTION_Subcommands[] = {
-{"delete","Delete a function by name","O(1)","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SCRIPTING,FUNCTION_DELETE_History,FUNCTION_DELETE_tips,functionDeleteCommand,3,CMD_NOSCRIPT|CMD_WRITE,ACL_CATEGORY_SCRIPTING,.args=FUNCTION_DELETE_Args},
-{"dump","Dump all functions into a serialized binary payload","O(N) where N is the number of functions","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SCRIPTING,FUNCTION_DUMP_History,FUNCTION_DUMP_tips,functionDumpCommand,2,CMD_NOSCRIPT,ACL_CATEGORY_SCRIPTING},
-{"flush","Deleting all functions","O(N) where N is the number of functions deleted","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SCRIPTING,FUNCTION_FLUSH_History,FUNCTION_FLUSH_tips,functionFlushCommand,-2,CMD_NOSCRIPT|CMD_WRITE,ACL_CATEGORY_SCRIPTING,.args=FUNCTION_FLUSH_Args},
-{"help","Show helpful text about the different subcommands","O(1)","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SCRIPTING,FUNCTION_HELP_History,FUNCTION_HELP_tips,functionHelpCommand,2,CMD_LOADING|CMD_STALE,ACL_CATEGORY_SCRIPTING},
-{"kill","Kill the function currently in execution.","O(1)","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SCRIPTING,FUNCTION_KILL_History,FUNCTION_KILL_tips,functionKillCommand,2,CMD_NOSCRIPT|CMD_ALLOW_BUSY,ACL_CATEGORY_SCRIPTING},
-{"list","List information about all the functions","O(N) where N is the number of functions","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SCRIPTING,FUNCTION_LIST_History,FUNCTION_LIST_tips,functionListCommand,-2,CMD_NOSCRIPT,ACL_CATEGORY_SCRIPTING,.args=FUNCTION_LIST_Args},
-{"load","Create a function with the given arguments (name, code, description)","O(1) (considering compilation time is redundant)","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SCRIPTING,FUNCTION_LOAD_History,FUNCTION_LOAD_tips,functionLoadCommand,-3,CMD_NOSCRIPT|CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_SCRIPTING,.args=FUNCTION_LOAD_Args},
-{"restore","Restore all the functions on the given payload","O(N) where N is the number of functions on the payload","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SCRIPTING,FUNCTION_RESTORE_History,FUNCTION_RESTORE_tips,functionRestoreCommand,-3,CMD_NOSCRIPT|CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_SCRIPTING,.args=FUNCTION_RESTORE_Args},
-{"stats","Return information about the function currently running (name, description, duration)","O(1)","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SCRIPTING,FUNCTION_STATS_History,FUNCTION_STATS_tips,functionStatsCommand,2,CMD_NOSCRIPT|CMD_ALLOW_BUSY,ACL_CATEGORY_SCRIPTING},
-{0}
-};
-
-/********** FUNCTION ********************/
-
-/* FUNCTION history */
-#define FUNCTION_History NULL
-
-/* FUNCTION tips */
-#define FUNCTION_tips NULL
-
-/********** SCRIPT DEBUG ********************/
-
-/* SCRIPT DEBUG history */
-#define SCRIPT_DEBUG_History NULL
-
-/* SCRIPT DEBUG tips */
-#define SCRIPT_DEBUG_tips NULL
-
-/* SCRIPT DEBUG mode argument table */
-struct redisCommandArg SCRIPT_DEBUG_mode_Subargs[] = {
-{"yes",ARG_TYPE_PURE_TOKEN,-1,"YES",NULL,NULL,CMD_ARG_NONE},
-{"sync",ARG_TYPE_PURE_TOKEN,-1,"SYNC",NULL,NULL,CMD_ARG_NONE},
-{"no",ARG_TYPE_PURE_TOKEN,-1,"NO",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* SCRIPT DEBUG argument table */
-struct redisCommandArg SCRIPT_DEBUG_Args[] = {
-{"mode",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,.subargs=SCRIPT_DEBUG_mode_Subargs},
-{0}
-};
-
-/********** SCRIPT EXISTS ********************/
-
-/* SCRIPT EXISTS history */
-#define SCRIPT_EXISTS_History NULL
-
-/* SCRIPT EXISTS tips */
-const char *SCRIPT_EXISTS_tips[] = {
-"request_policy:all_shards",
-"response_policy:agg_logical_and",
-NULL
-};
-
-/* SCRIPT EXISTS argument table */
-struct redisCommandArg SCRIPT_EXISTS_Args[] = {
-{"sha1",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** SCRIPT FLUSH ********************/
-
-/* SCRIPT FLUSH history */
-commandHistory SCRIPT_FLUSH_History[] = {
-{"6.2.0","Added the `ASYNC` and `SYNC` flushing mode modifiers."},
-{0}
-};
-
-/* SCRIPT FLUSH tips */
-const char *SCRIPT_FLUSH_tips[] = {
-"request_policy:all_nodes",
-"response_policy:all_succeeded",
-NULL
-};
-
-/* SCRIPT FLUSH flush_type argument table */
-struct redisCommandArg SCRIPT_FLUSH_flush_type_Subargs[] = {
-{"async",ARG_TYPE_PURE_TOKEN,-1,"ASYNC",NULL,NULL,CMD_ARG_NONE},
-{"sync",ARG_TYPE_PURE_TOKEN,-1,"SYNC",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* SCRIPT FLUSH argument table */
-struct redisCommandArg SCRIPT_FLUSH_Args[] = {
-{"flush-type",ARG_TYPE_ONEOF,-1,NULL,NULL,"6.2.0",CMD_ARG_OPTIONAL,.subargs=SCRIPT_FLUSH_flush_type_Subargs},
-{0}
-};
-
-/********** SCRIPT HELP ********************/
-
-/* SCRIPT HELP history */
-#define SCRIPT_HELP_History NULL
-
-/* SCRIPT HELP tips */
-#define SCRIPT_HELP_tips NULL
-
-/********** SCRIPT KILL ********************/
-
-/* SCRIPT KILL history */
-#define SCRIPT_KILL_History NULL
-
-/* SCRIPT KILL tips */
-const char *SCRIPT_KILL_tips[] = {
-"request_policy:all_shards",
-"response_policy:one_succeeded",
-NULL
-};
-
-/********** SCRIPT LOAD ********************/
-
-/* SCRIPT LOAD history */
-#define SCRIPT_LOAD_History NULL
-
-/* SCRIPT LOAD tips */
-const char *SCRIPT_LOAD_tips[] = {
-"request_policy:all_nodes",
-"response_policy:all_succeeded",
-NULL
-};
-
-/* SCRIPT LOAD argument table */
-struct redisCommandArg SCRIPT_LOAD_Args[] = {
-{"script",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* SCRIPT command table */
-struct redisCommand SCRIPT_Subcommands[] = {
-{"debug","Set the debug mode for executed scripts.","O(1)","3.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SCRIPTING,SCRIPT_DEBUG_History,SCRIPT_DEBUG_tips,scriptCommand,3,CMD_NOSCRIPT,ACL_CATEGORY_SCRIPTING,.args=SCRIPT_DEBUG_Args},
-{"exists","Check existence of scripts in the script cache.","O(N) with N being the number of scripts to check (so checking a single script is an O(1) operation).","2.6.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SCRIPTING,SCRIPT_EXISTS_History,SCRIPT_EXISTS_tips,scriptCommand,-3,CMD_NOSCRIPT,ACL_CATEGORY_SCRIPTING,.args=SCRIPT_EXISTS_Args},
-{"flush","Remove all the scripts from the script cache.","O(N) with N being the number of scripts in cache","2.6.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SCRIPTING,SCRIPT_FLUSH_History,SCRIPT_FLUSH_tips,scriptCommand,-2,CMD_NOSCRIPT,ACL_CATEGORY_SCRIPTING,.args=SCRIPT_FLUSH_Args},
-{"help","Show helpful text about the different subcommands","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SCRIPTING,SCRIPT_HELP_History,SCRIPT_HELP_tips,scriptCommand,2,CMD_LOADING|CMD_STALE,ACL_CATEGORY_SCRIPTING},
-{"kill","Kill the script currently in execution.","O(1)","2.6.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SCRIPTING,SCRIPT_KILL_History,SCRIPT_KILL_tips,scriptCommand,2,CMD_NOSCRIPT|CMD_ALLOW_BUSY,ACL_CATEGORY_SCRIPTING},
-{"load","Load the specified Lua script into the script cache.","O(N) with N being the length in bytes of the script body.","2.6.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SCRIPTING,SCRIPT_LOAD_History,SCRIPT_LOAD_tips,scriptCommand,3,CMD_NOSCRIPT|CMD_STALE,ACL_CATEGORY_SCRIPTING,.args=SCRIPT_LOAD_Args},
-{0}
-};
-
-/********** SCRIPT ********************/
-
-/* SCRIPT history */
-#define SCRIPT_History NULL
-
-/* SCRIPT tips */
-#define SCRIPT_tips NULL
-
-/********** SENTINEL CKQUORUM ********************/
-
-/* SENTINEL CKQUORUM history */
-#define SENTINEL_CKQUORUM_History NULL
-
-/* SENTINEL CKQUORUM tips */
-#define SENTINEL_CKQUORUM_tips NULL
-
-/* SENTINEL CKQUORUM argument table */
-struct redisCommandArg SENTINEL_CKQUORUM_Args[] = {
-{"master-name",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** SENTINEL CONFIG ********************/
-
-/* SENTINEL CONFIG history */
-#define SENTINEL_CONFIG_History NULL
-
-/* SENTINEL CONFIG tips */
-#define SENTINEL_CONFIG_tips NULL
-
-/* SENTINEL CONFIG action set argument table */
-struct redisCommandArg SENTINEL_CONFIG_action_set_Subargs[] = {
-{"parameter",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"value",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* SENTINEL CONFIG action argument table */
-struct redisCommandArg SENTINEL_CONFIG_action_Subargs[] = {
-{"set",ARG_TYPE_BLOCK,-1,"SET",NULL,NULL,CMD_ARG_NONE,.subargs=SENTINEL_CONFIG_action_set_Subargs},
-{"parameter",ARG_TYPE_STRING,-1,"GET",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* SENTINEL CONFIG argument table */
-struct redisCommandArg SENTINEL_CONFIG_Args[] = {
-{"action",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,.subargs=SENTINEL_CONFIG_action_Subargs},
-{0}
-};
-
-/********** SENTINEL DEBUG ********************/
-
-/* SENTINEL DEBUG history */
-#define SENTINEL_DEBUG_History NULL
-
-/* SENTINEL DEBUG tips */
-#define SENTINEL_DEBUG_tips NULL
-
-/* SENTINEL DEBUG data argument table */
-struct redisCommandArg SENTINEL_DEBUG_data_Subargs[] = {
-{"parameter",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"value",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* SENTINEL DEBUG argument table */
-struct redisCommandArg SENTINEL_DEBUG_Args[] = {
-{"data",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE,.subargs=SENTINEL_DEBUG_data_Subargs},
-{0}
-};
-
-/********** SENTINEL FAILOVER ********************/
-
-/* SENTINEL FAILOVER history */
-#define SENTINEL_FAILOVER_History NULL
-
-/* SENTINEL FAILOVER tips */
-#define SENTINEL_FAILOVER_tips NULL
-
-/* SENTINEL FAILOVER argument table */
-struct redisCommandArg SENTINEL_FAILOVER_Args[] = {
-{"master-name",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** SENTINEL FLUSHCONFIG ********************/
-
-/* SENTINEL FLUSHCONFIG history */
-#define SENTINEL_FLUSHCONFIG_History NULL
-
-/* SENTINEL FLUSHCONFIG tips */
-#define SENTINEL_FLUSHCONFIG_tips NULL
-
-/********** SENTINEL GET_MASTER_ADDR_BY_NAME ********************/
-
-/* SENTINEL GET_MASTER_ADDR_BY_NAME history */
-#define SENTINEL_GET_MASTER_ADDR_BY_NAME_History NULL
-
-/* SENTINEL GET_MASTER_ADDR_BY_NAME tips */
-#define SENTINEL_GET_MASTER_ADDR_BY_NAME_tips NULL
-
-/* SENTINEL GET_MASTER_ADDR_BY_NAME argument table */
-struct redisCommandArg SENTINEL_GET_MASTER_ADDR_BY_NAME_Args[] = {
-{"master-name",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** SENTINEL HELP ********************/
-
-/* SENTINEL HELP history */
-#define SENTINEL_HELP_History NULL
-
-/* SENTINEL HELP tips */
-#define SENTINEL_HELP_tips NULL
-
-/********** SENTINEL INFO_CACHE ********************/
-
-/* SENTINEL INFO_CACHE history */
-#define SENTINEL_INFO_CACHE_History NULL
-
-/* SENTINEL INFO_CACHE tips */
-#define SENTINEL_INFO_CACHE_tips NULL
-
-/* SENTINEL INFO_CACHE argument table */
-struct redisCommandArg SENTINEL_INFO_CACHE_Args[] = {
-{"nodename",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** SENTINEL IS_MASTER_DOWN_BY_ADDR ********************/
-
-/* SENTINEL IS_MASTER_DOWN_BY_ADDR history */
-#define SENTINEL_IS_MASTER_DOWN_BY_ADDR_History NULL
-
-/* SENTINEL IS_MASTER_DOWN_BY_ADDR tips */
-#define SENTINEL_IS_MASTER_DOWN_BY_ADDR_tips NULL
-
-/* SENTINEL IS_MASTER_DOWN_BY_ADDR argument table */
-struct redisCommandArg SENTINEL_IS_MASTER_DOWN_BY_ADDR_Args[] = {
-{"ip",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"port",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"current-epoch",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"runid",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** SENTINEL MASTER ********************/
-
-/* SENTINEL MASTER history */
-#define SENTINEL_MASTER_History NULL
-
-/* SENTINEL MASTER tips */
-#define SENTINEL_MASTER_tips NULL
-
-/* SENTINEL MASTER argument table */
-struct redisCommandArg SENTINEL_MASTER_Args[] = {
-{"master-name",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** SENTINEL MASTERS ********************/
-
-/* SENTINEL MASTERS history */
-#define SENTINEL_MASTERS_History NULL
-
-/* SENTINEL MASTERS tips */
-#define SENTINEL_MASTERS_tips NULL
-
-/********** SENTINEL MONITOR ********************/
-
-/* SENTINEL MONITOR history */
-#define SENTINEL_MONITOR_History NULL
-
-/* SENTINEL MONITOR tips */
-#define SENTINEL_MONITOR_tips NULL
-
-/* SENTINEL MONITOR argument table */
-struct redisCommandArg SENTINEL_MONITOR_Args[] = {
-{"name",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"ip",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"port",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"quorum",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** SENTINEL MYID ********************/
-
-/* SENTINEL MYID history */
-#define SENTINEL_MYID_History NULL
-
-/* SENTINEL MYID tips */
-#define SENTINEL_MYID_tips NULL
-
-/********** SENTINEL PENDING_SCRIPTS ********************/
-
-/* SENTINEL PENDING_SCRIPTS history */
-#define SENTINEL_PENDING_SCRIPTS_History NULL
-
-/* SENTINEL PENDING_SCRIPTS tips */
-#define SENTINEL_PENDING_SCRIPTS_tips NULL
-
-/********** SENTINEL REMOVE ********************/
-
-/* SENTINEL REMOVE history */
-#define SENTINEL_REMOVE_History NULL
-
-/* SENTINEL REMOVE tips */
-#define SENTINEL_REMOVE_tips NULL
-
-/* SENTINEL REMOVE argument table */
-struct redisCommandArg SENTINEL_REMOVE_Args[] = {
-{"master-name",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** SENTINEL REPLICAS ********************/
-
-/* SENTINEL REPLICAS history */
-#define SENTINEL_REPLICAS_History NULL
-
-/* SENTINEL REPLICAS tips */
-#define SENTINEL_REPLICAS_tips NULL
-
-/* SENTINEL REPLICAS argument table */
-struct redisCommandArg SENTINEL_REPLICAS_Args[] = {
-{"master-name",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** SENTINEL RESET ********************/
-
-/* SENTINEL RESET history */
-#define SENTINEL_RESET_History NULL
-
-/* SENTINEL RESET tips */
-#define SENTINEL_RESET_tips NULL
-
-/* SENTINEL RESET argument table */
-struct redisCommandArg SENTINEL_RESET_Args[] = {
-{"pattern",ARG_TYPE_PATTERN,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** SENTINEL SENTINELS ********************/
-
-/* SENTINEL SENTINELS history */
-#define SENTINEL_SENTINELS_History NULL
-
-/* SENTINEL SENTINELS tips */
-#define SENTINEL_SENTINELS_tips NULL
-
-/* SENTINEL SENTINELS argument table */
-struct redisCommandArg SENTINEL_SENTINELS_Args[] = {
-{"master-name",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** SENTINEL SET ********************/
-
-/* SENTINEL SET history */
-#define SENTINEL_SET_History NULL
-
-/* SENTINEL SET tips */
-#define SENTINEL_SET_tips NULL
-
-/* SENTINEL SET data argument table */
-struct redisCommandArg SENTINEL_SET_data_Subargs[] = {
-{"option",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"value",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* SENTINEL SET argument table */
-struct redisCommandArg SENTINEL_SET_Args[] = {
-{"master-name",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"data",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,.subargs=SENTINEL_SET_data_Subargs},
-{0}
-};
-
-/********** SENTINEL SIMULATE_FAILURE ********************/
-
-/* SENTINEL SIMULATE_FAILURE history */
-#define SENTINEL_SIMULATE_FAILURE_History NULL
-
-/* SENTINEL SIMULATE_FAILURE tips */
-#define SENTINEL_SIMULATE_FAILURE_tips NULL
-
-/* SENTINEL SIMULATE_FAILURE mode argument table */
-struct redisCommandArg SENTINEL_SIMULATE_FAILURE_mode_Subargs[] = {
-{"crash-after-election",ARG_TYPE_PURE_TOKEN,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"crash-after-promotion",ARG_TYPE_PURE_TOKEN,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"help",ARG_TYPE_PURE_TOKEN,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* SENTINEL SIMULATE_FAILURE argument table */
-struct redisCommandArg SENTINEL_SIMULATE_FAILURE_Args[] = {
-{"mode",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE,.subargs=SENTINEL_SIMULATE_FAILURE_mode_Subargs},
-{0}
-};
-
-/********** SENTINEL SLAVES ********************/
-
-/* SENTINEL SLAVES history */
-#define SENTINEL_SLAVES_History NULL
-
-/* SENTINEL SLAVES tips */
-#define SENTINEL_SLAVES_tips NULL
-
-/* SENTINEL SLAVES argument table */
-struct redisCommandArg SENTINEL_SLAVES_Args[] = {
-{"master-name",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* SENTINEL command table */
-struct redisCommand SENTINEL_Subcommands[] = {
-{"ckquorum","Check for a Sentinel quorum",NULL,"2.8.4",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_CKQUORUM_History,SENTINEL_CKQUORUM_tips,sentinelCommand,3,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,.args=SENTINEL_CKQUORUM_Args},
-{"config","Configure Sentinel","O(1)","6.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_CONFIG_History,SENTINEL_CONFIG_tips,sentinelCommand,-4,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,.args=SENTINEL_CONFIG_Args},
-{"debug","List or update the current configurable parameters","O(N) where N is the number of configurable parameters","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_DEBUG_History,SENTINEL_DEBUG_tips,sentinelCommand,-2,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,.args=SENTINEL_DEBUG_Args},
-{"failover","Force a failover",NULL,"2.8.4",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_FAILOVER_History,SENTINEL_FAILOVER_tips,sentinelCommand,3,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,.args=SENTINEL_FAILOVER_Args},
-{"flushconfig","Rewrite configuration file","O(1)","2.8.4",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_FLUSHCONFIG_History,SENTINEL_FLUSHCONFIG_tips,sentinelCommand,2,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0},
-{"get-master-addr-by-name","Get port and address of a master","O(1)","2.8.4",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_GET_MASTER_ADDR_BY_NAME_History,SENTINEL_GET_MASTER_ADDR_BY_NAME_tips,sentinelCommand,3,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,.args=SENTINEL_GET_MASTER_ADDR_BY_NAME_Args},
-{"help","Show helpful text about the different subcommands","O(1)","6.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_HELP_History,SENTINEL_HELP_tips,sentinelCommand,2,CMD_LOADING|CMD_STALE|CMD_SENTINEL|CMD_ONLY_SENTINEL,0},
-{"info-cache","Get cached INFO from the instances in the deployment","O(N) where N is the number of instances","3.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_INFO_CACHE_History,SENTINEL_INFO_CACHE_tips,sentinelCommand,-3,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,.args=SENTINEL_INFO_CACHE_Args},
-{"is-master-down-by-addr","Check if a master is down","O(1)","2.8.4",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_IS_MASTER_DOWN_BY_ADDR_History,SENTINEL_IS_MASTER_DOWN_BY_ADDR_tips,sentinelCommand,6,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,.args=SENTINEL_IS_MASTER_DOWN_BY_ADDR_Args},
-{"master","Shows the state of a master","O(1)","2.8.4",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_MASTER_History,SENTINEL_MASTER_tips,sentinelCommand,3,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,.args=SENTINEL_MASTER_Args},
-{"masters","List the monitored masters","O(N) where N is the number of masters","2.8.4",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_MASTERS_History,SENTINEL_MASTERS_tips,sentinelCommand,2,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0},
-{"monitor","Start monitoring","O(1)","2.8.4",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_MONITOR_History,SENTINEL_MONITOR_tips,sentinelCommand,6,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,.args=SENTINEL_MONITOR_Args},
-{"myid","Get the Sentinel instance ID","O(1)","6.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_MYID_History,SENTINEL_MYID_tips,sentinelCommand,2,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0},
-{"pending-scripts","Get information about pending scripts",NULL,"2.8.4",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_PENDING_SCRIPTS_History,SENTINEL_PENDING_SCRIPTS_tips,sentinelCommand,2,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0},
-{"remove","Stop monitoring","O(1)","2.8.4",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_REMOVE_History,SENTINEL_REMOVE_tips,sentinelCommand,3,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,.args=SENTINEL_REMOVE_Args},
-{"replicas","List the monitored replicas","O(N) where N is the number of replicas","5.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_REPLICAS_History,SENTINEL_REPLICAS_tips,sentinelCommand,3,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,.args=SENTINEL_REPLICAS_Args},
-{"reset","Reset masters by name pattern","O(N) where N is the number of monitored masters","2.8.4",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_RESET_History,SENTINEL_RESET_tips,sentinelCommand,3,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,.args=SENTINEL_RESET_Args},
-{"sentinels","List the Sentinel instances","O(N) where N is the number of Sentinels","2.8.4",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_SENTINELS_History,SENTINEL_SENTINELS_tips,sentinelCommand,3,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,.args=SENTINEL_SENTINELS_Args},
-{"set","Change the configuration of a monitored master","O(1)","2.8.4",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_SET_History,SENTINEL_SET_tips,sentinelCommand,-5,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,.args=SENTINEL_SET_Args},
-{"simulate-failure","Simulate failover scenarios",NULL,"3.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_SIMULATE_FAILURE_History,SENTINEL_SIMULATE_FAILURE_tips,sentinelCommand,-3,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,.args=SENTINEL_SIMULATE_FAILURE_Args},
-{"slaves","List the monitored slaves","O(N) where N is the number of slaves","2.8.0",CMD_DOC_DEPRECATED,"`SENTINEL REPLICAS`","5.0.0",COMMAND_GROUP_SENTINEL,SENTINEL_SLAVES_History,SENTINEL_SLAVES_tips,sentinelCommand,3,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,.args=SENTINEL_SLAVES_Args},
-{0}
-};
-
-/********** SENTINEL ********************/
-
-/* SENTINEL history */
-#define SENTINEL_History NULL
-
-/* SENTINEL tips */
-#define SENTINEL_tips NULL
-
-/********** ACL CAT ********************/
-
-/* ACL CAT history */
-#define ACL_CAT_History NULL
-
-/* ACL CAT tips */
-#define ACL_CAT_tips NULL
-
-/* ACL CAT argument table */
-struct redisCommandArg ACL_CAT_Args[] = {
-{"categoryname",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL},
-{0}
-};
-
-/********** ACL DELUSER ********************/
-
-/* ACL DELUSER history */
-#define ACL_DELUSER_History NULL
-
-/* ACL DELUSER tips */
-#define ACL_DELUSER_tips NULL
-
-/* ACL DELUSER argument table */
-struct redisCommandArg ACL_DELUSER_Args[] = {
-{"username",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** ACL DRYRUN ********************/
-
-/* ACL DRYRUN history */
-#define ACL_DRYRUN_History NULL
-
-/* ACL DRYRUN tips */
-#define ACL_DRYRUN_tips NULL
-
-/* ACL DRYRUN argument table */
-struct redisCommandArg ACL_DRYRUN_Args[] = {
-{"username",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"command",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"arg",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** ACL GENPASS ********************/
-
-/* ACL GENPASS history */
-#define ACL_GENPASS_History NULL
-
-/* ACL GENPASS tips */
-#define ACL_GENPASS_tips NULL
-
-/* ACL GENPASS argument table */
-struct redisCommandArg ACL_GENPASS_Args[] = {
-{"bits",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL},
-{0}
-};
-
-/********** ACL GETUSER ********************/
-
-/* ACL GETUSER history */
-commandHistory ACL_GETUSER_History[] = {
-{"6.2.0","Added Pub/Sub channel patterns."},
-{"7.0.0","Added selectors and changed the format of key and channel patterns from a list to their rule representation."},
-{0}
-};
-
-/* ACL GETUSER tips */
-#define ACL_GETUSER_tips NULL
-
-/* ACL GETUSER argument table */
-struct redisCommandArg ACL_GETUSER_Args[] = {
-{"username",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** ACL HELP ********************/
-
-/* ACL HELP history */
-#define ACL_HELP_History NULL
-
-/* ACL HELP tips */
-#define ACL_HELP_tips NULL
-
-/********** ACL LIST ********************/
-
-/* ACL LIST history */
-#define ACL_LIST_History NULL
-
-/* ACL LIST tips */
-#define ACL_LIST_tips NULL
-
-/********** ACL LOAD ********************/
-
-/* ACL LOAD history */
-#define ACL_LOAD_History NULL
-
-/* ACL LOAD tips */
-#define ACL_LOAD_tips NULL
-
-/********** ACL LOG ********************/
-
-/* ACL LOG history */
-commandHistory ACL_LOG_History[] = {
-{"7.2.0","Added entry ID, timestamp created, and timestamp last updated."},
-{0}
-};
-
-/* ACL LOG tips */
-#define ACL_LOG_tips NULL
-
-/* ACL LOG operation argument table */
-struct redisCommandArg ACL_LOG_operation_Subargs[] = {
-{"count",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"reset",ARG_TYPE_PURE_TOKEN,-1,"RESET",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* ACL LOG argument table */
-struct redisCommandArg ACL_LOG_Args[] = {
-{"operation",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,.subargs=ACL_LOG_operation_Subargs},
-{0}
-};
-
-/********** ACL SAVE ********************/
-
-/* ACL SAVE history */
-#define ACL_SAVE_History NULL
-
-/* ACL SAVE tips */
-#define ACL_SAVE_tips NULL
-
-/********** ACL SETUSER ********************/
-
-/* ACL SETUSER history */
-commandHistory ACL_SETUSER_History[] = {
-{"6.2.0","Added Pub/Sub channel patterns."},
-{"7.0.0","Added selectors and key based permissions."},
-{0}
-};
-
-/* ACL SETUSER tips */
-#define ACL_SETUSER_tips NULL
-
-/* ACL SETUSER argument table */
-struct redisCommandArg ACL_SETUSER_Args[] = {
-{"username",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"rule",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** ACL USERS ********************/
-
-/* ACL USERS history */
-#define ACL_USERS_History NULL
-
-/* ACL USERS tips */
-#define ACL_USERS_tips NULL
-
-/********** ACL WHOAMI ********************/
-
-/* ACL WHOAMI history */
-#define ACL_WHOAMI_History NULL
-
-/* ACL WHOAMI tips */
-#define ACL_WHOAMI_tips NULL
-
-/* ACL command table */
-struct redisCommand ACL_Subcommands[] = {
-{"cat","List the ACL categories or the commands inside a category","O(1) since the categories and commands are a fixed set.","6.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,ACL_CAT_History,ACL_CAT_tips,aclCommand,-2,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,.args=ACL_CAT_Args},
-{"deluser","Remove the specified ACL users and the associated rules","O(1) amortized time considering the typical user.","6.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,ACL_DELUSER_History,ACL_DELUSER_tips,aclCommand,-3,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,.args=ACL_DELUSER_Args},
-{"dryrun","Returns whether the user can execute the given command without executing the command.","O(1).","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,ACL_DRYRUN_History,ACL_DRYRUN_tips,aclCommand,-4,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,.args=ACL_DRYRUN_Args},
-{"genpass","Generate a pseudorandom secure password to use for ACL users","O(1)","6.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,ACL_GENPASS_History,ACL_GENPASS_tips,aclCommand,-2,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,.args=ACL_GENPASS_Args},
-{"getuser","Get the rules for a specific ACL user","O(N). Where N is the number of password, command and pattern rules that the user has.","6.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,ACL_GETUSER_History,ACL_GETUSER_tips,aclCommand,3,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,.args=ACL_GETUSER_Args},
-{"help","Show helpful text about the different subcommands","O(1)","6.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,ACL_HELP_History,ACL_HELP_tips,aclCommand,2,CMD_LOADING|CMD_STALE|CMD_SENTINEL,0},
-{"list","List the current ACL rules in ACL config file format","O(N). Where N is the number of configured users.","6.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,ACL_LIST_History,ACL_LIST_tips,aclCommand,2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0},
-{"load","Reload the ACLs from the configured ACL file","O(N). Where N is the number of configured users.","6.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,ACL_LOAD_History,ACL_LOAD_tips,aclCommand,2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0},
-{"log","List latest events denied because of ACLs in place","O(N) with N being the number of entries shown.","6.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,ACL_LOG_History,ACL_LOG_tips,aclCommand,-2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,.args=ACL_LOG_Args},
-{"save","Save the current ACL rules in the configured ACL file","O(N). Where N is the number of configured users.","6.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,ACL_SAVE_History,ACL_SAVE_tips,aclCommand,2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0},
-{"setuser","Modify or create the rules for a specific ACL user","O(N). Where N is the number of rules provided.","6.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,ACL_SETUSER_History,ACL_SETUSER_tips,aclCommand,-3,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,.args=ACL_SETUSER_Args},
-{"users","List the username of all the configured ACL rules","O(N). Where N is the number of configured users.","6.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,ACL_USERS_History,ACL_USERS_tips,aclCommand,2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0},
-{"whoami","Return the name of the user associated to the current connection","O(1)","6.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,ACL_WHOAMI_History,ACL_WHOAMI_tips,aclCommand,2,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0},
-{0}
-};
-
-/********** ACL ********************/
-
-/* ACL history */
-#define ACL_History NULL
-
-/* ACL tips */
-#define ACL_tips NULL
-
-/********** BGREWRITEAOF ********************/
-
-/* BGREWRITEAOF history */
-#define BGREWRITEAOF_History NULL
-
-/* BGREWRITEAOF tips */
-#define BGREWRITEAOF_tips NULL
-
-/********** BGSAVE ********************/
-
-/* BGSAVE history */
-commandHistory BGSAVE_History[] = {
-{"3.2.2","Added the `SCHEDULE` option."},
-{0}
-};
-
-/* BGSAVE tips */
-#define BGSAVE_tips NULL
-
-/* BGSAVE argument table */
-struct redisCommandArg BGSAVE_Args[] = {
-{"schedule",ARG_TYPE_PURE_TOKEN,-1,"SCHEDULE",NULL,"3.2.2",CMD_ARG_OPTIONAL},
-{0}
-};
-
-/********** COMMAND COUNT ********************/
-
-/* COMMAND COUNT history */
-#define COMMAND_COUNT_History NULL
-
-/* COMMAND COUNT tips */
-#define COMMAND_COUNT_tips NULL
-
-/********** COMMAND DOCS ********************/
-
-/* COMMAND DOCS history */
-#define COMMAND_DOCS_History NULL
-
-/* COMMAND DOCS tips */
-const char *COMMAND_DOCS_tips[] = {
-"nondeterministic_output_order",
-NULL
-};
-
-/* COMMAND DOCS argument table */
-struct redisCommandArg COMMAND_DOCS_Args[] = {
-{"command-name",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** COMMAND GETKEYS ********************/
-
-/* COMMAND GETKEYS history */
-#define COMMAND_GETKEYS_History NULL
-
-/* COMMAND GETKEYS tips */
-#define COMMAND_GETKEYS_tips NULL
-
-/* COMMAND GETKEYS argument table */
-struct redisCommandArg COMMAND_GETKEYS_Args[] = {
-{"command",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"arg",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** COMMAND GETKEYSANDFLAGS ********************/
-
-/* COMMAND GETKEYSANDFLAGS history */
-#define COMMAND_GETKEYSANDFLAGS_History NULL
-
-/* COMMAND GETKEYSANDFLAGS tips */
-#define COMMAND_GETKEYSANDFLAGS_tips NULL
-
-/* COMMAND GETKEYSANDFLAGS argument table */
-struct redisCommandArg COMMAND_GETKEYSANDFLAGS_Args[] = {
-{"command",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"arg",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** COMMAND HELP ********************/
-
-/* COMMAND HELP history */
-#define COMMAND_HELP_History NULL
-
-/* COMMAND HELP tips */
-#define COMMAND_HELP_tips NULL
-
-/********** COMMAND INFO ********************/
-
-/* COMMAND INFO history */
-commandHistory COMMAND_INFO_History[] = {
-{"7.0.0","Allowed to be called with no argument to get info on all commands."},
-{0}
-};
-
-/* COMMAND INFO tips */
-const char *COMMAND_INFO_tips[] = {
-"nondeterministic_output_order",
-NULL
-};
-
-/* COMMAND INFO argument table */
-struct redisCommandArg COMMAND_INFO_Args[] = {
-{"command-name",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** COMMAND LIST ********************/
-
-/* COMMAND LIST history */
-#define COMMAND_LIST_History NULL
-
-/* COMMAND LIST tips */
-const char *COMMAND_LIST_tips[] = {
-"nondeterministic_output_order",
-NULL
-};
-
-/* COMMAND LIST filterby argument table */
-struct redisCommandArg COMMAND_LIST_filterby_Subargs[] = {
-{"module-name",ARG_TYPE_STRING,-1,"MODULE",NULL,NULL,CMD_ARG_NONE},
-{"category",ARG_TYPE_STRING,-1,"ACLCAT",NULL,NULL,CMD_ARG_NONE},
-{"pattern",ARG_TYPE_PATTERN,-1,"PATTERN",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* COMMAND LIST argument table */
-struct redisCommandArg COMMAND_LIST_Args[] = {
-{"filterby",ARG_TYPE_ONEOF,-1,"FILTERBY",NULL,NULL,CMD_ARG_OPTIONAL,.subargs=COMMAND_LIST_filterby_Subargs},
-{0}
-};
-
-/* COMMAND command table */
-struct redisCommand COMMAND_Subcommands[] = {
-{"count","Get total number of Redis commands","O(1)","2.8.13",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,COMMAND_COUNT_History,COMMAND_COUNT_tips,commandCountCommand,2,CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION},
-{"docs","Get array of specific Redis command documentation","O(N) where N is the number of commands to look up","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,COMMAND_DOCS_History,COMMAND_DOCS_tips,commandDocsCommand,-2,CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,.args=COMMAND_DOCS_Args},
-{"getkeys","Extract keys given a full Redis command","O(N) where N is the number of arguments to the command","2.8.13",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,COMMAND_GETKEYS_History,COMMAND_GETKEYS_tips,commandGetKeysCommand,-3,CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,.args=COMMAND_GETKEYS_Args},
-{"getkeysandflags","Extract keys and access flags given a full Redis command","O(N) where N is the number of arguments to the command","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,COMMAND_GETKEYSANDFLAGS_History,COMMAND_GETKEYSANDFLAGS_tips,commandGetKeysAndFlagsCommand,-3,CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,.args=COMMAND_GETKEYSANDFLAGS_Args},
-{"help","Show helpful text about the different subcommands","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,COMMAND_HELP_History,COMMAND_HELP_tips,commandHelpCommand,2,CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION},
-{"info","Get array of specific Redis command details, or all when no argument is given.","O(N) where N is the number of commands to look up","2.8.13",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,COMMAND_INFO_History,COMMAND_INFO_tips,commandInfoCommand,-2,CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,.args=COMMAND_INFO_Args},
-{"list","Get an array of Redis command names","O(N) where N is the total number of Redis commands","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,COMMAND_LIST_History,COMMAND_LIST_tips,commandListCommand,-2,CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,.args=COMMAND_LIST_Args},
-{0}
-};
-
-/********** COMMAND ********************/
-
-/* COMMAND history */
-#define COMMAND_History NULL
-
-/* COMMAND tips */
-const char *COMMAND_tips[] = {
-"nondeterministic_output_order",
-NULL
-};
-
-/********** CONFIG GET ********************/
-
-/* CONFIG GET history */
-commandHistory CONFIG_GET_History[] = {
-{"7.0.0","Added the ability to pass multiple pattern parameters in one call"},
-{0}
-};
-
-/* CONFIG GET tips */
-#define CONFIG_GET_tips NULL
-
-/* CONFIG GET argument table */
-struct redisCommandArg CONFIG_GET_Args[] = {
-{"parameter",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** CONFIG HELP ********************/
-
-/* CONFIG HELP history */
-#define CONFIG_HELP_History NULL
-
-/* CONFIG HELP tips */
-#define CONFIG_HELP_tips NULL
-
-/********** CONFIG RESETSTAT ********************/
-
-/* CONFIG RESETSTAT history */
-#define CONFIG_RESETSTAT_History NULL
-
-/* CONFIG RESETSTAT tips */
-#define CONFIG_RESETSTAT_tips NULL
-
-/********** CONFIG REWRITE ********************/
-
-/* CONFIG REWRITE history */
-#define CONFIG_REWRITE_History NULL
-
-/* CONFIG REWRITE tips */
-#define CONFIG_REWRITE_tips NULL
-
-/********** CONFIG SET ********************/
-
-/* CONFIG SET history */
-commandHistory CONFIG_SET_History[] = {
-{"7.0.0","Added the ability to set multiple parameters in one call."},
-{0}
-};
-
-/* CONFIG SET tips */
-const char *CONFIG_SET_tips[] = {
-"request_policy:all_nodes",
-"response_policy:all_succeeded",
-NULL
-};
-
-/* CONFIG SET data argument table */
-struct redisCommandArg CONFIG_SET_data_Subargs[] = {
-{"parameter",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"value",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* CONFIG SET argument table */
-struct redisCommandArg CONFIG_SET_Args[] = {
-{"data",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,.subargs=CONFIG_SET_data_Subargs},
-{0}
-};
-
-/* CONFIG command table */
-struct redisCommand CONFIG_Subcommands[] = {
-{"get","Get the values of configuration parameters","O(N) when N is the number of configuration parameters provided","2.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,CONFIG_GET_History,CONFIG_GET_tips,configGetCommand,-3,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,0,.args=CONFIG_GET_Args},
-{"help","Show helpful text about the different subcommands","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,CONFIG_HELP_History,CONFIG_HELP_tips,configHelpCommand,2,CMD_LOADING|CMD_STALE,0},
-{"resetstat","Reset the stats returned by INFO","O(1)","2.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,CONFIG_RESETSTAT_History,CONFIG_RESETSTAT_tips,configResetStatCommand,2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,0},
-{"rewrite","Rewrite the configuration file with the in memory configuration","O(1)","2.8.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,CONFIG_REWRITE_History,CONFIG_REWRITE_tips,configRewriteCommand,2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,0},
-{"set","Set configuration parameters to the given values","O(N) when N is the number of configuration parameters provided","2.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,CONFIG_SET_History,CONFIG_SET_tips,configSetCommand,-4,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,0,.args=CONFIG_SET_Args},
-{0}
-};
-
-/********** CONFIG ********************/
-
-/* CONFIG history */
-#define CONFIG_History NULL
-
-/* CONFIG tips */
-#define CONFIG_tips NULL
-
-/********** DBSIZE ********************/
-
-/* DBSIZE history */
-#define DBSIZE_History NULL
-
-/* DBSIZE tips */
-const char *DBSIZE_tips[] = {
-"request_policy:all_shards",
-"response_policy:agg_sum",
-NULL
-};
-
-/********** DEBUG ********************/
-
-/* DEBUG history */
-#define DEBUG_History NULL
-
-/* DEBUG tips */
-#define DEBUG_tips NULL
-
-/********** FAILOVER ********************/
-
-/* FAILOVER history */
-#define FAILOVER_History NULL
-
-/* FAILOVER tips */
-#define FAILOVER_tips NULL
-
-/* FAILOVER target argument table */
-struct redisCommandArg FAILOVER_target_Subargs[] = {
-{"host",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"port",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"force",ARG_TYPE_PURE_TOKEN,-1,"FORCE",NULL,NULL,CMD_ARG_OPTIONAL},
-{0}
-};
-
-/* FAILOVER argument table */
-struct redisCommandArg FAILOVER_Args[] = {
-{"target",ARG_TYPE_BLOCK,-1,"TO",NULL,NULL,CMD_ARG_OPTIONAL,.subargs=FAILOVER_target_Subargs},
-{"abort",ARG_TYPE_PURE_TOKEN,-1,"ABORT",NULL,NULL,CMD_ARG_OPTIONAL},
-{"milliseconds",ARG_TYPE_INTEGER,-1,"TIMEOUT",NULL,NULL,CMD_ARG_OPTIONAL},
-{0}
-};
-
-/********** FLUSHALL ********************/
-
-/* FLUSHALL history */
-commandHistory FLUSHALL_History[] = {
-{"4.0.0","Added the `ASYNC` flushing mode modifier."},
-{"6.2.0","Added the `SYNC` flushing mode modifier."},
-{0}
-};
-
-/* FLUSHALL tips */
-const char *FLUSHALL_tips[] = {
-"request_policy:all_shards",
-"response_policy:all_succeeded",
-NULL
-};
-
-/* FLUSHALL flush_type argument table */
-struct redisCommandArg FLUSHALL_flush_type_Subargs[] = {
-{"async",ARG_TYPE_PURE_TOKEN,-1,"ASYNC",NULL,"4.0.0",CMD_ARG_NONE},
-{"sync",ARG_TYPE_PURE_TOKEN,-1,"SYNC",NULL,"6.2.0",CMD_ARG_NONE},
-{0}
-};
-
-/* FLUSHALL argument table */
-struct redisCommandArg FLUSHALL_Args[] = {
-{"flush-type",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,.subargs=FLUSHALL_flush_type_Subargs},
-{0}
-};
-
-/********** FLUSHDB ********************/
-
-/* FLUSHDB history */
-commandHistory FLUSHDB_History[] = {
-{"4.0.0","Added the `ASYNC` flushing mode modifier."},
-{"6.2.0","Added the `SYNC` flushing mode modifier."},
-{0}
-};
-
-/* FLUSHDB tips */
-const char *FLUSHDB_tips[] = {
-"request_policy:all_shards",
-"response_policy:all_succeeded",
-NULL
-};
-
-/* FLUSHDB flush_type argument table */
-struct redisCommandArg FLUSHDB_flush_type_Subargs[] = {
-{"async",ARG_TYPE_PURE_TOKEN,-1,"ASYNC",NULL,"4.0.0",CMD_ARG_NONE},
-{"sync",ARG_TYPE_PURE_TOKEN,-1,"SYNC",NULL,"6.2.0",CMD_ARG_NONE},
-{0}
-};
-
-/* FLUSHDB argument table */
-struct redisCommandArg FLUSHDB_Args[] = {
-{"flush-type",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,.subargs=FLUSHDB_flush_type_Subargs},
-{0}
-};
-
-/********** INFO ********************/
-
-/* INFO history */
-commandHistory INFO_History[] = {
-{"7.0.0","Added support for taking multiple section arguments."},
-{0}
-};
-
-/* INFO tips */
-const char *INFO_tips[] = {
-"nondeterministic_output",
-"request_policy:all_shards",
-"response_policy:special",
-NULL
-};
-
-/* INFO argument table */
-struct redisCommandArg INFO_Args[] = {
-{"section",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** LASTSAVE ********************/
-
-/* LASTSAVE history */
-#define LASTSAVE_History NULL
-
-/* LASTSAVE tips */
-const char *LASTSAVE_tips[] = {
-"nondeterministic_output",
-NULL
-};
-
-/********** LATENCY DOCTOR ********************/
-
-/* LATENCY DOCTOR history */
-#define LATENCY_DOCTOR_History NULL
-
-/* LATENCY DOCTOR tips */
-const char *LATENCY_DOCTOR_tips[] = {
-"nondeterministic_output",
-"request_policy:all_nodes",
-"response_policy:special",
-NULL
-};
-
-/********** LATENCY GRAPH ********************/
-
-/* LATENCY GRAPH history */
-#define LATENCY_GRAPH_History NULL
-
-/* LATENCY GRAPH tips */
-const char *LATENCY_GRAPH_tips[] = {
-"nondeterministic_output",
-"request_policy:all_nodes",
-"response_policy:special",
-NULL
-};
-
-/* LATENCY GRAPH argument table */
-struct redisCommandArg LATENCY_GRAPH_Args[] = {
-{"event",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** LATENCY HELP ********************/
-
-/* LATENCY HELP history */
-#define LATENCY_HELP_History NULL
-
-/* LATENCY HELP tips */
-#define LATENCY_HELP_tips NULL
-
-/********** LATENCY HISTOGRAM ********************/
-
-/* LATENCY HISTOGRAM history */
-#define LATENCY_HISTOGRAM_History NULL
-
-/* LATENCY HISTOGRAM tips */
-const char *LATENCY_HISTOGRAM_tips[] = {
-"nondeterministic_output",
-"request_policy:all_nodes",
-"response_policy:special",
-NULL
-};
-
-/* LATENCY HISTOGRAM argument table */
-struct redisCommandArg LATENCY_HISTOGRAM_Args[] = {
-{"command",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** LATENCY HISTORY ********************/
-
-/* LATENCY HISTORY history */
-#define LATENCY_HISTORY_History NULL
-
-/* LATENCY HISTORY tips */
-const char *LATENCY_HISTORY_tips[] = {
-"nondeterministic_output",
-"request_policy:all_nodes",
-"response_policy:special",
-NULL
-};
-
-/* LATENCY HISTORY argument table */
-struct redisCommandArg LATENCY_HISTORY_Args[] = {
-{"event",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** LATENCY LATEST ********************/
-
-/* LATENCY LATEST history */
-#define LATENCY_LATEST_History NULL
-
-/* LATENCY LATEST tips */
-const char *LATENCY_LATEST_tips[] = {
-"nondeterministic_output",
-"request_policy:all_nodes",
-"response_policy:special",
-NULL
-};
-
-/********** LATENCY RESET ********************/
-
-/* LATENCY RESET history */
-#define LATENCY_RESET_History NULL
-
-/* LATENCY RESET tips */
-const char *LATENCY_RESET_tips[] = {
-"request_policy:all_nodes",
-"response_policy:all_succeeded",
-NULL
-};
-
-/* LATENCY RESET argument table */
-struct redisCommandArg LATENCY_RESET_Args[] = {
-{"event",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE},
-{0}
-};
-
-/* LATENCY command table */
-struct redisCommand LATENCY_Subcommands[] = {
-{"doctor","Return a human readable latency analysis report.","O(1)","2.8.13",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,LATENCY_DOCTOR_History,LATENCY_DOCTOR_tips,latencyCommand,2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,0},
-{"graph","Return a latency graph for the event.","O(1)","2.8.13",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,LATENCY_GRAPH_History,LATENCY_GRAPH_tips,latencyCommand,3,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,0,.args=LATENCY_GRAPH_Args},
-{"help","Show helpful text about the different subcommands.","O(1)","2.8.13",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,LATENCY_HELP_History,LATENCY_HELP_tips,latencyCommand,2,CMD_LOADING|CMD_STALE,0},
-{"histogram","Return the cumulative distribution of latencies of a subset of commands or all.","O(N) where N is the number of commands with latency information being retrieved.","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,LATENCY_HISTOGRAM_History,LATENCY_HISTOGRAM_tips,latencyCommand,-2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,0,.args=LATENCY_HISTOGRAM_Args},
-{"history","Return timestamp-latency samples for the event.","O(1)","2.8.13",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,LATENCY_HISTORY_History,LATENCY_HISTORY_tips,latencyCommand,3,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,0,.args=LATENCY_HISTORY_Args},
-{"latest","Return the latest latency samples for all events.","O(1)","2.8.13",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,LATENCY_LATEST_History,LATENCY_LATEST_tips,latencyCommand,2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,0},
-{"reset","Reset latency data for one or more events.","O(1)","2.8.13",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,LATENCY_RESET_History,LATENCY_RESET_tips,latencyCommand,-2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,0,.args=LATENCY_RESET_Args},
-{0}
-};
-
-/********** LATENCY ********************/
-
-/* LATENCY history */
-#define LATENCY_History NULL
-
-/* LATENCY tips */
-#define LATENCY_tips NULL
-
-/********** LOLWUT ********************/
-
-/* LOLWUT history */
-#define LOLWUT_History NULL
-
-/* LOLWUT tips */
-#define LOLWUT_tips NULL
-
-/* LOLWUT argument table */
-struct redisCommandArg LOLWUT_Args[] = {
-{"version",ARG_TYPE_INTEGER,-1,"VERSION",NULL,NULL,CMD_ARG_OPTIONAL},
-{0}
-};
-
-/********** MEMORY DOCTOR ********************/
-
-/* MEMORY DOCTOR history */
-#define MEMORY_DOCTOR_History NULL
-
-/* MEMORY DOCTOR tips */
-const char *MEMORY_DOCTOR_tips[] = {
-"nondeterministic_output",
-"request_policy:all_shards",
-"response_policy:special",
-NULL
-};
-
-/********** MEMORY HELP ********************/
-
-/* MEMORY HELP history */
-#define MEMORY_HELP_History NULL
-
-/* MEMORY HELP tips */
-#define MEMORY_HELP_tips NULL
-
-/********** MEMORY MALLOC_STATS ********************/
-
-/* MEMORY MALLOC_STATS history */
-#define MEMORY_MALLOC_STATS_History NULL
-
-/* MEMORY MALLOC_STATS tips */
-const char *MEMORY_MALLOC_STATS_tips[] = {
-"nondeterministic_output",
-"request_policy:all_shards",
-"response_policy:special",
-NULL
-};
-
-/********** MEMORY PURGE ********************/
-
-/* MEMORY PURGE history */
-#define MEMORY_PURGE_History NULL
-
-/* MEMORY PURGE tips */
-const char *MEMORY_PURGE_tips[] = {
-"request_policy:all_shards",
-"response_policy:all_succeeded",
-NULL
-};
-
-/********** MEMORY STATS ********************/
-
-/* MEMORY STATS history */
-#define MEMORY_STATS_History NULL
-
-/* MEMORY STATS tips */
-const char *MEMORY_STATS_tips[] = {
-"nondeterministic_output",
-"request_policy:all_shards",
-"response_policy:special",
-NULL
-};
-
-/********** MEMORY USAGE ********************/
-
-/* MEMORY USAGE history */
-#define MEMORY_USAGE_History NULL
-
-/* MEMORY USAGE tips */
-#define MEMORY_USAGE_tips NULL
-
-/* MEMORY USAGE argument table */
-struct redisCommandArg MEMORY_USAGE_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"count",ARG_TYPE_INTEGER,-1,"SAMPLES",NULL,NULL,CMD_ARG_OPTIONAL},
-{0}
-};
-
-/* MEMORY command table */
-struct redisCommand MEMORY_Subcommands[] = {
-{"doctor","Outputs memory problems report","O(1)","4.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,MEMORY_DOCTOR_History,MEMORY_DOCTOR_tips,memoryCommand,2,0,0},
-{"help","Show helpful text about the different subcommands","O(1)","4.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,MEMORY_HELP_History,MEMORY_HELP_tips,memoryCommand,2,CMD_LOADING|CMD_STALE,0},
-{"malloc-stats","Show allocator internal stats","Depends on how much memory is allocated, could be slow","4.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,MEMORY_MALLOC_STATS_History,MEMORY_MALLOC_STATS_tips,memoryCommand,2,0,0},
-{"purge","Ask the allocator to release memory","Depends on how much memory is allocated, could be slow","4.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,MEMORY_PURGE_History,MEMORY_PURGE_tips,memoryCommand,2,0,0},
-{"stats","Show memory usage details","O(1)","4.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,MEMORY_STATS_History,MEMORY_STATS_tips,memoryCommand,2,0,0},
-{"usage","Estimate the memory usage of a key","O(N) where N is the number of samples.","4.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,MEMORY_USAGE_History,MEMORY_USAGE_tips,memoryCommand,-3,CMD_READONLY,0,{{NULL,CMD_KEY_RO,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=MEMORY_USAGE_Args},
-{0}
-};
-
-/********** MEMORY ********************/
-
-/* MEMORY history */
-#define MEMORY_History NULL
-
-/* MEMORY tips */
-#define MEMORY_tips NULL
-
-/********** MODULE HELP ********************/
-
-/* MODULE HELP history */
-#define MODULE_HELP_History NULL
-
-/* MODULE HELP tips */
-#define MODULE_HELP_tips NULL
-
-/********** MODULE LIST ********************/
-
-/* MODULE LIST history */
-#define MODULE_LIST_History NULL
-
-/* MODULE LIST tips */
-const char *MODULE_LIST_tips[] = {
-"nondeterministic_output_order",
-NULL
-};
-
-/********** MODULE LOAD ********************/
-
-/* MODULE LOAD history */
-#define MODULE_LOAD_History NULL
-
-/* MODULE LOAD tips */
-#define MODULE_LOAD_tips NULL
-
-/* MODULE LOAD argument table */
-struct redisCommandArg MODULE_LOAD_Args[] = {
-{"path",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"arg",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** MODULE LOADEX ********************/
-
-/* MODULE LOADEX history */
-#define MODULE_LOADEX_History NULL
-
-/* MODULE LOADEX tips */
-#define MODULE_LOADEX_tips NULL
-
-/* MODULE LOADEX configs argument table */
-struct redisCommandArg MODULE_LOADEX_configs_Subargs[] = {
-{"name",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"value",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* MODULE LOADEX argument table */
-struct redisCommandArg MODULE_LOADEX_Args[] = {
-{"path",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"configs",ARG_TYPE_BLOCK,-1,"CONFIG",NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE|CMD_ARG_MULTIPLE_TOKEN,.subargs=MODULE_LOADEX_configs_Subargs},
-{"args",ARG_TYPE_STRING,-1,"ARGS",NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** MODULE UNLOAD ********************/
-
-/* MODULE UNLOAD history */
-#define MODULE_UNLOAD_History NULL
-
-/* MODULE UNLOAD tips */
-#define MODULE_UNLOAD_tips NULL
-
-/* MODULE UNLOAD argument table */
-struct redisCommandArg MODULE_UNLOAD_Args[] = {
-{"name",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* MODULE command table */
-struct redisCommand MODULE_Subcommands[] = {
-{"help","Show helpful text about the different subcommands","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,MODULE_HELP_History,MODULE_HELP_tips,moduleCommand,2,CMD_LOADING|CMD_STALE,0},
-{"list","List all modules loaded by the server","O(N) where N is the number of loaded modules.","4.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,MODULE_LIST_History,MODULE_LIST_tips,moduleCommand,2,CMD_ADMIN|CMD_NOSCRIPT,0},
-{"load","Load a module","O(1)","4.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,MODULE_LOAD_History,MODULE_LOAD_tips,moduleCommand,-3,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_NOSCRIPT|CMD_PROTECTED,0,.args=MODULE_LOAD_Args},
-{"loadex","Load a module with extended parameters","O(1)","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,MODULE_LOADEX_History,MODULE_LOADEX_tips,moduleCommand,-3,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_NOSCRIPT|CMD_PROTECTED,0,.args=MODULE_LOADEX_Args},
-{"unload","Unload a module","O(1)","4.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,MODULE_UNLOAD_History,MODULE_UNLOAD_tips,moduleCommand,3,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_NOSCRIPT|CMD_PROTECTED,0,.args=MODULE_UNLOAD_Args},
-{0}
-};
-
-/********** MODULE ********************/
-
-/* MODULE history */
-#define MODULE_History NULL
-
-/* MODULE tips */
-#define MODULE_tips NULL
-
-/********** MONITOR ********************/
-
-/* MONITOR history */
-#define MONITOR_History NULL
-
-/* MONITOR tips */
-#define MONITOR_tips NULL
-
-/********** PSYNC ********************/
-
-/* PSYNC history */
-#define PSYNC_History NULL
-
-/* PSYNC tips */
-#define PSYNC_tips NULL
-
-/* PSYNC argument table */
-struct redisCommandArg PSYNC_Args[] = {
-{"replicationid",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"offset",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** REPLCONF ********************/
-
-/* REPLCONF history */
-#define REPLCONF_History NULL
-
-/* REPLCONF tips */
-#define REPLCONF_tips NULL
-
-/********** REPLICAOF ********************/
-
-/* REPLICAOF history */
-#define REPLICAOF_History NULL
-
-/* REPLICAOF tips */
-#define REPLICAOF_tips NULL
-
-/* REPLICAOF argument table */
-struct redisCommandArg REPLICAOF_Args[] = {
-{"host",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"port",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** RESTORE_ASKING ********************/
-
-/* RESTORE_ASKING history */
-commandHistory RESTORE_ASKING_History[] = {
-{"3.0.0","Added the `REPLACE` modifier."},
-{"5.0.0","Added the `ABSTTL` modifier."},
-{"5.0.0","Added the `IDLETIME` and `FREQ` options."},
-{0}
-};
-
-/* RESTORE_ASKING tips */
-#define RESTORE_ASKING_tips NULL
-
-/* RESTORE_ASKING argument table */
-struct redisCommandArg RESTORE_ASKING_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"ttl",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"serialized-value",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"replace",ARG_TYPE_PURE_TOKEN,-1,"REPLACE",NULL,"3.0.0",CMD_ARG_OPTIONAL},
-{"absttl",ARG_TYPE_PURE_TOKEN,-1,"ABSTTL",NULL,"5.0.0",CMD_ARG_OPTIONAL},
-{"seconds",ARG_TYPE_INTEGER,-1,"IDLETIME",NULL,"5.0.0",CMD_ARG_OPTIONAL},
-{"frequency",ARG_TYPE_INTEGER,-1,"FREQ",NULL,"5.0.0",CMD_ARG_OPTIONAL},
-{0}
-};
-
-/********** ROLE ********************/
-
-/* ROLE history */
-#define ROLE_History NULL
-
-/* ROLE tips */
-#define ROLE_tips NULL
-
-/********** SAVE ********************/
-
-/* SAVE history */
-#define SAVE_History NULL
-
-/* SAVE tips */
-#define SAVE_tips NULL
-
-/********** SHUTDOWN ********************/
-
-/* SHUTDOWN history */
-commandHistory SHUTDOWN_History[] = {
-{"7.0.0","Added the `NOW`, `FORCE` and `ABORT` modifiers."},
-{0}
-};
-
-/* SHUTDOWN tips */
-#define SHUTDOWN_tips NULL
-
-/* SHUTDOWN save_selector argument table */
-struct redisCommandArg SHUTDOWN_save_selector_Subargs[] = {
-{"nosave",ARG_TYPE_PURE_TOKEN,-1,"NOSAVE",NULL,NULL,CMD_ARG_NONE},
-{"save",ARG_TYPE_PURE_TOKEN,-1,"SAVE",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* SHUTDOWN argument table */
-struct redisCommandArg SHUTDOWN_Args[] = {
-{"save-selector",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,.subargs=SHUTDOWN_save_selector_Subargs},
-{"now",ARG_TYPE_PURE_TOKEN,-1,"NOW",NULL,"7.0.0",CMD_ARG_OPTIONAL},
-{"force",ARG_TYPE_PURE_TOKEN,-1,"FORCE",NULL,"7.0.0",CMD_ARG_OPTIONAL},
-{"abort",ARG_TYPE_PURE_TOKEN,-1,"ABORT",NULL,"7.0.0",CMD_ARG_OPTIONAL},
-{0}
-};
-
-/********** SLAVEOF ********************/
-
-/* SLAVEOF history */
-#define SLAVEOF_History NULL
-
-/* SLAVEOF tips */
-#define SLAVEOF_tips NULL
-
-/* SLAVEOF argument table */
-struct redisCommandArg SLAVEOF_Args[] = {
-{"host",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"port",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** SLOWLOG GET ********************/
-
-/* SLOWLOG GET history */
-commandHistory SLOWLOG_GET_History[] = {
-{"4.0.0","Added client IP address, port and name to the reply."},
-{0}
-};
-
-/* SLOWLOG GET tips */
-const char *SLOWLOG_GET_tips[] = {
-"request_policy:all_nodes",
-"nondeterministic_output",
-NULL
-};
-
-/* SLOWLOG GET argument table */
-struct redisCommandArg SLOWLOG_GET_Args[] = {
-{"count",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL},
-{0}
-};
-
-/********** SLOWLOG HELP ********************/
-
-/* SLOWLOG HELP history */
-#define SLOWLOG_HELP_History NULL
-
-/* SLOWLOG HELP tips */
-#define SLOWLOG_HELP_tips NULL
-
-/********** SLOWLOG LEN ********************/
-
-/* SLOWLOG LEN history */
-#define SLOWLOG_LEN_History NULL
-
-/* SLOWLOG LEN tips */
-const char *SLOWLOG_LEN_tips[] = {
-"request_policy:all_nodes",
-"response_policy:agg_sum",
-"nondeterministic_output",
-NULL
-};
-
-/********** SLOWLOG RESET ********************/
-
-/* SLOWLOG RESET history */
-#define SLOWLOG_RESET_History NULL
-
-/* SLOWLOG RESET tips */
-const char *SLOWLOG_RESET_tips[] = {
-"request_policy:all_nodes",
-"response_policy:all_succeeded",
-NULL
-};
-
-/* SLOWLOG command table */
-struct redisCommand SLOWLOG_Subcommands[] = {
-{"get","Get the slow log's entries","O(N) where N is the number of entries returned","2.2.12",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,SLOWLOG_GET_History,SLOWLOG_GET_tips,slowlogCommand,-2,CMD_ADMIN|CMD_LOADING|CMD_STALE,0,.args=SLOWLOG_GET_Args},
-{"help","Show helpful text about the different subcommands","O(1)","6.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,SLOWLOG_HELP_History,SLOWLOG_HELP_tips,slowlogCommand,2,CMD_LOADING|CMD_STALE,0},
-{"len","Get the slow log's length","O(1)","2.2.12",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,SLOWLOG_LEN_History,SLOWLOG_LEN_tips,slowlogCommand,2,CMD_ADMIN|CMD_LOADING|CMD_STALE,0},
-{"reset","Clear all entries from the slow log","O(N) where N is the number of entries in the slowlog","2.2.12",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,SLOWLOG_RESET_History,SLOWLOG_RESET_tips,slowlogCommand,2,CMD_ADMIN|CMD_LOADING|CMD_STALE,0},
-{0}
-};
-
-/********** SLOWLOG ********************/
-
-/* SLOWLOG history */
-#define SLOWLOG_History NULL
-
-/* SLOWLOG tips */
-#define SLOWLOG_tips NULL
-
-/********** SWAPDB ********************/
-
-/* SWAPDB history */
-#define SWAPDB_History NULL
-
-/* SWAPDB tips */
-#define SWAPDB_tips NULL
-
-/* SWAPDB argument table */
-struct redisCommandArg SWAPDB_Args[] = {
-{"index1",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"index2",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** SYNC ********************/
-
-/* SYNC history */
-#define SYNC_History NULL
-
-/* SYNC tips */
-#define SYNC_tips NULL
-
-/********** TIME ********************/
-
-/* TIME history */
-#define TIME_History NULL
-
-/* TIME tips */
-const char *TIME_tips[] = {
-"nondeterministic_output",
-NULL
-};
-
-/********** SADD ********************/
-
-/* SADD history */
-commandHistory SADD_History[] = {
-{"2.4.0","Accepts multiple `member` arguments."},
-{0}
-};
-
-/* SADD tips */
-#define SADD_tips NULL
-
-/* SADD argument table */
-struct redisCommandArg SADD_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"member",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** SCARD ********************/
-
-/* SCARD history */
-#define SCARD_History NULL
-
-/* SCARD tips */
-#define SCARD_tips NULL
-
-/* SCARD argument table */
-struct redisCommandArg SCARD_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** SDIFF ********************/
-
-/* SDIFF history */
-#define SDIFF_History NULL
-
-/* SDIFF tips */
-const char *SDIFF_tips[] = {
-"nondeterministic_output_order",
-NULL
-};
-
-/* SDIFF argument table */
-struct redisCommandArg SDIFF_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** SDIFFSTORE ********************/
-
-/* SDIFFSTORE history */
-#define SDIFFSTORE_History NULL
-
-/* SDIFFSTORE tips */
-#define SDIFFSTORE_tips NULL
-
-/* SDIFFSTORE argument table */
-struct redisCommandArg SDIFFSTORE_Args[] = {
-{"destination",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"key",ARG_TYPE_KEY,1,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** SINTER ********************/
-
-/* SINTER history */
-#define SINTER_History NULL
-
-/* SINTER tips */
-const char *SINTER_tips[] = {
-"nondeterministic_output_order",
-NULL
-};
-
-/* SINTER argument table */
-struct redisCommandArg SINTER_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** SINTERCARD ********************/
-
-/* SINTERCARD history */
-#define SINTERCARD_History NULL
-
-/* SINTERCARD tips */
-#define SINTERCARD_tips NULL
-
-/* SINTERCARD argument table */
-struct redisCommandArg SINTERCARD_Args[] = {
-{"numkeys",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{"limit",ARG_TYPE_INTEGER,-1,"LIMIT",NULL,NULL,CMD_ARG_OPTIONAL},
-{0}
-};
-
-/********** SINTERSTORE ********************/
-
-/* SINTERSTORE history */
-#define SINTERSTORE_History NULL
-
-/* SINTERSTORE tips */
-#define SINTERSTORE_tips NULL
-
-/* SINTERSTORE argument table */
-struct redisCommandArg SINTERSTORE_Args[] = {
-{"destination",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"key",ARG_TYPE_KEY,1,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** SISMEMBER ********************/
-
-/* SISMEMBER history */
-#define SISMEMBER_History NULL
-
-/* SISMEMBER tips */
-#define SISMEMBER_tips NULL
-
-/* SISMEMBER argument table */
-struct redisCommandArg SISMEMBER_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"member",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** SMEMBERS ********************/
-
-/* SMEMBERS history */
-#define SMEMBERS_History NULL
-
-/* SMEMBERS tips */
-const char *SMEMBERS_tips[] = {
-"nondeterministic_output_order",
-NULL
-};
-
-/* SMEMBERS argument table */
-struct redisCommandArg SMEMBERS_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** SMISMEMBER ********************/
-
-/* SMISMEMBER history */
-#define SMISMEMBER_History NULL
-
-/* SMISMEMBER tips */
-#define SMISMEMBER_tips NULL
-
-/* SMISMEMBER argument table */
-struct redisCommandArg SMISMEMBER_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"member",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** SMOVE ********************/
-
-/* SMOVE history */
-#define SMOVE_History NULL
-
-/* SMOVE tips */
-#define SMOVE_tips NULL
-
-/* SMOVE argument table */
-struct redisCommandArg SMOVE_Args[] = {
-{"source",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"destination",ARG_TYPE_KEY,1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"member",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** SPOP ********************/
-
-/* SPOP history */
-commandHistory SPOP_History[] = {
-{"3.2.0","Added the `count` argument."},
-{0}
-};
-
-/* SPOP tips */
-const char *SPOP_tips[] = {
-"nondeterministic_output",
-NULL
-};
-
-/* SPOP argument table */
-struct redisCommandArg SPOP_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"count",ARG_TYPE_INTEGER,-1,NULL,NULL,"3.2.0",CMD_ARG_OPTIONAL},
-{0}
-};
-
-/********** SRANDMEMBER ********************/
-
-/* SRANDMEMBER history */
-commandHistory SRANDMEMBER_History[] = {
-{"2.6.0","Added the optional `count` argument."},
-{0}
-};
-
-/* SRANDMEMBER tips */
-const char *SRANDMEMBER_tips[] = {
-"nondeterministic_output",
-NULL
-};
-
-/* SRANDMEMBER argument table */
-struct redisCommandArg SRANDMEMBER_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"count",ARG_TYPE_INTEGER,-1,NULL,NULL,"2.6.0",CMD_ARG_OPTIONAL},
-{0}
-};
-
-/********** SREM ********************/
-
-/* SREM history */
-commandHistory SREM_History[] = {
-{"2.4.0","Accepts multiple `member` arguments."},
-{0}
-};
-
-/* SREM tips */
-#define SREM_tips NULL
-
-/* SREM argument table */
-struct redisCommandArg SREM_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"member",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** SSCAN ********************/
-
-/* SSCAN history */
-#define SSCAN_History NULL
-
-/* SSCAN tips */
-const char *SSCAN_tips[] = {
-"nondeterministic_output",
-NULL
-};
-
-/* SSCAN argument table */
-struct redisCommandArg SSCAN_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"cursor",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"pattern",ARG_TYPE_PATTERN,-1,"MATCH",NULL,NULL,CMD_ARG_OPTIONAL},
-{"count",ARG_TYPE_INTEGER,-1,"COUNT",NULL,NULL,CMD_ARG_OPTIONAL},
-{0}
-};
-
-/********** SUNION ********************/
-
-/* SUNION history */
-#define SUNION_History NULL
-
-/* SUNION tips */
-const char *SUNION_tips[] = {
-"nondeterministic_output_order",
-NULL
-};
-
-/* SUNION argument table */
-struct redisCommandArg SUNION_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** SUNIONSTORE ********************/
-
-/* SUNIONSTORE history */
-#define SUNIONSTORE_History NULL
-
-/* SUNIONSTORE tips */
-#define SUNIONSTORE_tips NULL
-
-/* SUNIONSTORE argument table */
-struct redisCommandArg SUNIONSTORE_Args[] = {
-{"destination",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"key",ARG_TYPE_KEY,1,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** BZMPOP ********************/
-
-/* BZMPOP history */
-#define BZMPOP_History NULL
-
-/* BZMPOP tips */
-#define BZMPOP_tips NULL
-
-/* BZMPOP where argument table */
-struct redisCommandArg BZMPOP_where_Subargs[] = {
-{"min",ARG_TYPE_PURE_TOKEN,-1,"MIN",NULL,NULL,CMD_ARG_NONE},
-{"max",ARG_TYPE_PURE_TOKEN,-1,"MAX",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* BZMPOP argument table */
-struct redisCommandArg BZMPOP_Args[] = {
-{"timeout",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"numkeys",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{"where",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,.subargs=BZMPOP_where_Subargs},
-{"count",ARG_TYPE_INTEGER,-1,"COUNT",NULL,NULL,CMD_ARG_OPTIONAL},
-{0}
-};
-
-/********** BZPOPMAX ********************/
-
-/* BZPOPMAX history */
-commandHistory BZPOPMAX_History[] = {
-{"6.0.0","`timeout` is interpreted as a double instead of an integer."},
-{0}
-};
-
-/* BZPOPMAX tips */
-#define BZPOPMAX_tips NULL
-
-/* BZPOPMAX argument table */
-struct redisCommandArg BZPOPMAX_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{"timeout",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** BZPOPMIN ********************/
-
-/* BZPOPMIN history */
-commandHistory BZPOPMIN_History[] = {
-{"6.0.0","`timeout` is interpreted as a double instead of an integer."},
-{0}
-};
-
-/* BZPOPMIN tips */
-#define BZPOPMIN_tips NULL
-
-/* BZPOPMIN argument table */
-struct redisCommandArg BZPOPMIN_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{"timeout",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** ZADD ********************/
-
-/* ZADD history */
-commandHistory ZADD_History[] = {
-{"2.4.0","Accepts multiple elements."},
-{"3.0.2","Added the `XX`, `NX`, `CH` and `INCR` options."},
-{"6.2.0","Added the `GT` and `LT` options."},
-{0}
-};
-
-/* ZADD tips */
-#define ZADD_tips NULL
-
-/* ZADD condition argument table */
-struct redisCommandArg ZADD_condition_Subargs[] = {
-{"nx",ARG_TYPE_PURE_TOKEN,-1,"NX",NULL,NULL,CMD_ARG_NONE},
-{"xx",ARG_TYPE_PURE_TOKEN,-1,"XX",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* ZADD comparison argument table */
-struct redisCommandArg ZADD_comparison_Subargs[] = {
-{"gt",ARG_TYPE_PURE_TOKEN,-1,"GT",NULL,NULL,CMD_ARG_NONE},
-{"lt",ARG_TYPE_PURE_TOKEN,-1,"LT",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* ZADD data argument table */
-struct redisCommandArg ZADD_data_Subargs[] = {
-{"score",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"member",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* ZADD argument table */
-struct redisCommandArg ZADD_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"condition",ARG_TYPE_ONEOF,-1,NULL,NULL,"3.0.2",CMD_ARG_OPTIONAL,.subargs=ZADD_condition_Subargs},
-{"comparison",ARG_TYPE_ONEOF,-1,NULL,NULL,"6.2.0",CMD_ARG_OPTIONAL,.subargs=ZADD_comparison_Subargs},
-{"change",ARG_TYPE_PURE_TOKEN,-1,"CH",NULL,"3.0.2",CMD_ARG_OPTIONAL},
-{"increment",ARG_TYPE_PURE_TOKEN,-1,"INCR",NULL,"3.0.2",CMD_ARG_OPTIONAL},
-{"data",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,.subargs=ZADD_data_Subargs},
-{0}
-};
-
-/********** ZCARD ********************/
-
-/* ZCARD history */
-#define ZCARD_History NULL
-
-/* ZCARD tips */
-#define ZCARD_tips NULL
-
-/* ZCARD argument table */
-struct redisCommandArg ZCARD_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** ZCOUNT ********************/
-
-/* ZCOUNT history */
-#define ZCOUNT_History NULL
-
-/* ZCOUNT tips */
-#define ZCOUNT_tips NULL
-
-/* ZCOUNT argument table */
-struct redisCommandArg ZCOUNT_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"min",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"max",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** ZDIFF ********************/
-
-/* ZDIFF history */
-#define ZDIFF_History NULL
-
-/* ZDIFF tips */
-#define ZDIFF_tips NULL
-
-/* ZDIFF argument table */
-struct redisCommandArg ZDIFF_Args[] = {
-{"numkeys",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{"withscores",ARG_TYPE_PURE_TOKEN,-1,"WITHSCORES",NULL,NULL,CMD_ARG_OPTIONAL},
-{0}
-};
-
-/********** ZDIFFSTORE ********************/
-
-/* ZDIFFSTORE history */
-#define ZDIFFSTORE_History NULL
-
-/* ZDIFFSTORE tips */
-#define ZDIFFSTORE_tips NULL
-
-/* ZDIFFSTORE argument table */
-struct redisCommandArg ZDIFFSTORE_Args[] = {
-{"destination",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"numkeys",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"key",ARG_TYPE_KEY,1,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** ZINCRBY ********************/
-
-/* ZINCRBY history */
-#define ZINCRBY_History NULL
-
-/* ZINCRBY tips */
-#define ZINCRBY_tips NULL
-
-/* ZINCRBY argument table */
-struct redisCommandArg ZINCRBY_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"increment",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"member",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** ZINTER ********************/
-
-/* ZINTER history */
-#define ZINTER_History NULL
-
-/* ZINTER tips */
-#define ZINTER_tips NULL
-
-/* ZINTER aggregate argument table */
-struct redisCommandArg ZINTER_aggregate_Subargs[] = {
-{"sum",ARG_TYPE_PURE_TOKEN,-1,"SUM",NULL,NULL,CMD_ARG_NONE},
-{"min",ARG_TYPE_PURE_TOKEN,-1,"MIN",NULL,NULL,CMD_ARG_NONE},
-{"max",ARG_TYPE_PURE_TOKEN,-1,"MAX",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* ZINTER argument table */
-struct redisCommandArg ZINTER_Args[] = {
-{"numkeys",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{"weight",ARG_TYPE_INTEGER,-1,"WEIGHTS",NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE},
-{"aggregate",ARG_TYPE_ONEOF,-1,"AGGREGATE",NULL,NULL,CMD_ARG_OPTIONAL,.subargs=ZINTER_aggregate_Subargs},
-{"withscores",ARG_TYPE_PURE_TOKEN,-1,"WITHSCORES",NULL,NULL,CMD_ARG_OPTIONAL},
-{0}
-};
-
-/********** ZINTERCARD ********************/
-
-/* ZINTERCARD history */
-#define ZINTERCARD_History NULL
-
-/* ZINTERCARD tips */
-#define ZINTERCARD_tips NULL
-
-/* ZINTERCARD argument table */
-struct redisCommandArg ZINTERCARD_Args[] = {
-{"numkeys",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{"limit",ARG_TYPE_INTEGER,-1,"LIMIT",NULL,NULL,CMD_ARG_OPTIONAL},
-{0}
-};
-
-/********** ZINTERSTORE ********************/
-
-/* ZINTERSTORE history */
-#define ZINTERSTORE_History NULL
-
-/* ZINTERSTORE tips */
-#define ZINTERSTORE_tips NULL
-
-/* ZINTERSTORE aggregate argument table */
-struct redisCommandArg ZINTERSTORE_aggregate_Subargs[] = {
-{"sum",ARG_TYPE_PURE_TOKEN,-1,"SUM",NULL,NULL,CMD_ARG_NONE},
-{"min",ARG_TYPE_PURE_TOKEN,-1,"MIN",NULL,NULL,CMD_ARG_NONE},
-{"max",ARG_TYPE_PURE_TOKEN,-1,"MAX",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* ZINTERSTORE argument table */
-struct redisCommandArg ZINTERSTORE_Args[] = {
-{"destination",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"numkeys",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"key",ARG_TYPE_KEY,1,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{"weight",ARG_TYPE_INTEGER,-1,"WEIGHTS",NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE},
-{"aggregate",ARG_TYPE_ONEOF,-1,"AGGREGATE",NULL,NULL,CMD_ARG_OPTIONAL,.subargs=ZINTERSTORE_aggregate_Subargs},
-{0}
-};
-
-/********** ZLEXCOUNT ********************/
-
-/* ZLEXCOUNT history */
-#define ZLEXCOUNT_History NULL
-
-/* ZLEXCOUNT tips */
-#define ZLEXCOUNT_tips NULL
-
-/* ZLEXCOUNT argument table */
-struct redisCommandArg ZLEXCOUNT_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"min",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"max",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** ZMPOP ********************/
-
-/* ZMPOP history */
-#define ZMPOP_History NULL
-
-/* ZMPOP tips */
-#define ZMPOP_tips NULL
-
-/* ZMPOP where argument table */
-struct redisCommandArg ZMPOP_where_Subargs[] = {
-{"min",ARG_TYPE_PURE_TOKEN,-1,"MIN",NULL,NULL,CMD_ARG_NONE},
-{"max",ARG_TYPE_PURE_TOKEN,-1,"MAX",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* ZMPOP argument table */
-struct redisCommandArg ZMPOP_Args[] = {
-{"numkeys",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{"where",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,.subargs=ZMPOP_where_Subargs},
-{"count",ARG_TYPE_INTEGER,-1,"COUNT",NULL,NULL,CMD_ARG_OPTIONAL},
-{0}
-};
-
-/********** ZMSCORE ********************/
-
-/* ZMSCORE history */
-#define ZMSCORE_History NULL
-
-/* ZMSCORE tips */
-#define ZMSCORE_tips NULL
-
-/* ZMSCORE argument table */
-struct redisCommandArg ZMSCORE_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"member",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** ZPOPMAX ********************/
-
-/* ZPOPMAX history */
-#define ZPOPMAX_History NULL
-
-/* ZPOPMAX tips */
-#define ZPOPMAX_tips NULL
-
-/* ZPOPMAX argument table */
-struct redisCommandArg ZPOPMAX_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"count",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL},
-{0}
-};
-
-/********** ZPOPMIN ********************/
-
-/* ZPOPMIN history */
-#define ZPOPMIN_History NULL
-
-/* ZPOPMIN tips */
-#define ZPOPMIN_tips NULL
-
-/* ZPOPMIN argument table */
-struct redisCommandArg ZPOPMIN_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"count",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL},
-{0}
-};
-
-/********** ZRANDMEMBER ********************/
-
-/* ZRANDMEMBER history */
-#define ZRANDMEMBER_History NULL
-
-/* ZRANDMEMBER tips */
-const char *ZRANDMEMBER_tips[] = {
-"nondeterministic_output",
-NULL
-};
-
-/* ZRANDMEMBER options argument table */
-struct redisCommandArg ZRANDMEMBER_options_Subargs[] = {
-{"count",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"withscores",ARG_TYPE_PURE_TOKEN,-1,"WITHSCORES",NULL,NULL,CMD_ARG_OPTIONAL},
-{0}
-};
-
-/* ZRANDMEMBER argument table */
-struct redisCommandArg ZRANDMEMBER_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"options",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,.subargs=ZRANDMEMBER_options_Subargs},
-{0}
-};
-
-/********** ZRANGE ********************/
-
-/* ZRANGE history */
-commandHistory ZRANGE_History[] = {
-{"6.2.0","Added the `REV`, `BYSCORE`, `BYLEX` and `LIMIT` options."},
-{0}
-};
-
-/* ZRANGE tips */
-#define ZRANGE_tips NULL
-
-/* ZRANGE sortby argument table */
-struct redisCommandArg ZRANGE_sortby_Subargs[] = {
-{"byscore",ARG_TYPE_PURE_TOKEN,-1,"BYSCORE",NULL,NULL,CMD_ARG_NONE},
-{"bylex",ARG_TYPE_PURE_TOKEN,-1,"BYLEX",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* ZRANGE limit argument table */
-struct redisCommandArg ZRANGE_limit_Subargs[] = {
-{"offset",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"count",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* ZRANGE argument table */
-struct redisCommandArg ZRANGE_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"start",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"stop",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"sortby",ARG_TYPE_ONEOF,-1,NULL,NULL,"6.2.0",CMD_ARG_OPTIONAL,.subargs=ZRANGE_sortby_Subargs},
-{"rev",ARG_TYPE_PURE_TOKEN,-1,"REV",NULL,"6.2.0",CMD_ARG_OPTIONAL},
-{"limit",ARG_TYPE_BLOCK,-1,"LIMIT",NULL,"6.2.0",CMD_ARG_OPTIONAL,.subargs=ZRANGE_limit_Subargs},
-{"withscores",ARG_TYPE_PURE_TOKEN,-1,"WITHSCORES",NULL,NULL,CMD_ARG_OPTIONAL},
-{0}
-};
-
-/********** ZRANGEBYLEX ********************/
-
-/* ZRANGEBYLEX history */
-#define ZRANGEBYLEX_History NULL
-
-/* ZRANGEBYLEX tips */
-#define ZRANGEBYLEX_tips NULL
-
-/* ZRANGEBYLEX limit argument table */
-struct redisCommandArg ZRANGEBYLEX_limit_Subargs[] = {
-{"offset",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"count",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* ZRANGEBYLEX argument table */
-struct redisCommandArg ZRANGEBYLEX_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"min",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"max",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"limit",ARG_TYPE_BLOCK,-1,"LIMIT",NULL,NULL,CMD_ARG_OPTIONAL,.subargs=ZRANGEBYLEX_limit_Subargs},
-{0}
-};
-
-/********** ZRANGEBYSCORE ********************/
-
-/* ZRANGEBYSCORE history */
-commandHistory ZRANGEBYSCORE_History[] = {
-{"2.0.0","Added the `WITHSCORES` modifier."},
-{0}
-};
-
-/* ZRANGEBYSCORE tips */
-#define ZRANGEBYSCORE_tips NULL
-
-/* ZRANGEBYSCORE limit argument table */
-struct redisCommandArg ZRANGEBYSCORE_limit_Subargs[] = {
-{"offset",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"count",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* ZRANGEBYSCORE argument table */
-struct redisCommandArg ZRANGEBYSCORE_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"min",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"max",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"withscores",ARG_TYPE_PURE_TOKEN,-1,"WITHSCORES",NULL,"2.0.0",CMD_ARG_OPTIONAL},
-{"limit",ARG_TYPE_BLOCK,-1,"LIMIT",NULL,NULL,CMD_ARG_OPTIONAL,.subargs=ZRANGEBYSCORE_limit_Subargs},
-{0}
-};
-
-/********** ZRANGESTORE ********************/
-
-/* ZRANGESTORE history */
-#define ZRANGESTORE_History NULL
-
-/* ZRANGESTORE tips */
-#define ZRANGESTORE_tips NULL
-
-/* ZRANGESTORE sortby argument table */
-struct redisCommandArg ZRANGESTORE_sortby_Subargs[] = {
-{"byscore",ARG_TYPE_PURE_TOKEN,-1,"BYSCORE",NULL,NULL,CMD_ARG_NONE},
-{"bylex",ARG_TYPE_PURE_TOKEN,-1,"BYLEX",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* ZRANGESTORE limit argument table */
-struct redisCommandArg ZRANGESTORE_limit_Subargs[] = {
-{"offset",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"count",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* ZRANGESTORE argument table */
-struct redisCommandArg ZRANGESTORE_Args[] = {
-{"dst",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"src",ARG_TYPE_KEY,1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"min",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"max",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"sortby",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,.subargs=ZRANGESTORE_sortby_Subargs},
-{"rev",ARG_TYPE_PURE_TOKEN,-1,"REV",NULL,NULL,CMD_ARG_OPTIONAL},
-{"limit",ARG_TYPE_BLOCK,-1,"LIMIT",NULL,NULL,CMD_ARG_OPTIONAL,.subargs=ZRANGESTORE_limit_Subargs},
-{0}
-};
-
-/********** ZRANK ********************/
-
-/* ZRANK history */
-commandHistory ZRANK_History[] = {
-{"7.2.0","Added the optional `WITHSCORE` argument."},
-{0}
-};
-
-/* ZRANK tips */
-#define ZRANK_tips NULL
-
-/* ZRANK argument table */
-struct redisCommandArg ZRANK_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"member",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"withscore",ARG_TYPE_PURE_TOKEN,-1,"WITHSCORE",NULL,NULL,CMD_ARG_OPTIONAL},
-{0}
-};
-
-/********** ZREM ********************/
-
-/* ZREM history */
-commandHistory ZREM_History[] = {
-{"2.4.0","Accepts multiple elements."},
-{0}
-};
-
-/* ZREM tips */
-#define ZREM_tips NULL
-
-/* ZREM argument table */
-struct redisCommandArg ZREM_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"member",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** ZREMRANGEBYLEX ********************/
-
-/* ZREMRANGEBYLEX history */
-#define ZREMRANGEBYLEX_History NULL
-
-/* ZREMRANGEBYLEX tips */
-#define ZREMRANGEBYLEX_tips NULL
-
-/* ZREMRANGEBYLEX argument table */
-struct redisCommandArg ZREMRANGEBYLEX_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"min",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"max",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** ZREMRANGEBYRANK ********************/
-
-/* ZREMRANGEBYRANK history */
-#define ZREMRANGEBYRANK_History NULL
-
-/* ZREMRANGEBYRANK tips */
-#define ZREMRANGEBYRANK_tips NULL
-
-/* ZREMRANGEBYRANK argument table */
-struct redisCommandArg ZREMRANGEBYRANK_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"start",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"stop",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** ZREMRANGEBYSCORE ********************/
-
-/* ZREMRANGEBYSCORE history */
-#define ZREMRANGEBYSCORE_History NULL
-
-/* ZREMRANGEBYSCORE tips */
-#define ZREMRANGEBYSCORE_tips NULL
-
-/* ZREMRANGEBYSCORE argument table */
-struct redisCommandArg ZREMRANGEBYSCORE_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"min",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"max",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** ZREVRANGE ********************/
-
-/* ZREVRANGE history */
-#define ZREVRANGE_History NULL
-
-/* ZREVRANGE tips */
-#define ZREVRANGE_tips NULL
-
-/* ZREVRANGE argument table */
-struct redisCommandArg ZREVRANGE_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"start",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"stop",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"withscores",ARG_TYPE_PURE_TOKEN,-1,"WITHSCORES",NULL,NULL,CMD_ARG_OPTIONAL},
-{0}
-};
-
-/********** ZREVRANGEBYLEX ********************/
-
-/* ZREVRANGEBYLEX history */
-#define ZREVRANGEBYLEX_History NULL
-
-/* ZREVRANGEBYLEX tips */
-#define ZREVRANGEBYLEX_tips NULL
-
-/* ZREVRANGEBYLEX limit argument table */
-struct redisCommandArg ZREVRANGEBYLEX_limit_Subargs[] = {
-{"offset",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"count",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* ZREVRANGEBYLEX argument table */
-struct redisCommandArg ZREVRANGEBYLEX_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"max",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"min",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"limit",ARG_TYPE_BLOCK,-1,"LIMIT",NULL,NULL,CMD_ARG_OPTIONAL,.subargs=ZREVRANGEBYLEX_limit_Subargs},
-{0}
-};
-
-/********** ZREVRANGEBYSCORE ********************/
-
-/* ZREVRANGEBYSCORE history */
-commandHistory ZREVRANGEBYSCORE_History[] = {
-{"2.1.6","`min` and `max` can be exclusive."},
-{0}
-};
-
-/* ZREVRANGEBYSCORE tips */
-#define ZREVRANGEBYSCORE_tips NULL
-
-/* ZREVRANGEBYSCORE limit argument table */
-struct redisCommandArg ZREVRANGEBYSCORE_limit_Subargs[] = {
-{"offset",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"count",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* ZREVRANGEBYSCORE argument table */
-struct redisCommandArg ZREVRANGEBYSCORE_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"max",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"min",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"withscores",ARG_TYPE_PURE_TOKEN,-1,"WITHSCORES",NULL,NULL,CMD_ARG_OPTIONAL},
-{"limit",ARG_TYPE_BLOCK,-1,"LIMIT",NULL,NULL,CMD_ARG_OPTIONAL,.subargs=ZREVRANGEBYSCORE_limit_Subargs},
-{0}
-};
-
-/********** ZREVRANK ********************/
-
-/* ZREVRANK history */
-commandHistory ZREVRANK_History[] = {
-{"7.2.0","Added the optional `WITHSCORE` argument."},
-{0}
-};
-
-/* ZREVRANK tips */
-#define ZREVRANK_tips NULL
-
-/* ZREVRANK argument table */
-struct redisCommandArg ZREVRANK_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"member",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"withscore",ARG_TYPE_PURE_TOKEN,-1,"WITHSCORE",NULL,NULL,CMD_ARG_OPTIONAL},
-{0}
-};
-
-/********** ZSCAN ********************/
-
-/* ZSCAN history */
-#define ZSCAN_History NULL
-
-/* ZSCAN tips */
-const char *ZSCAN_tips[] = {
-"nondeterministic_output",
-NULL
-};
-
-/* ZSCAN argument table */
-struct redisCommandArg ZSCAN_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"cursor",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"pattern",ARG_TYPE_PATTERN,-1,"MATCH",NULL,NULL,CMD_ARG_OPTIONAL},
-{"count",ARG_TYPE_INTEGER,-1,"COUNT",NULL,NULL,CMD_ARG_OPTIONAL},
-{0}
-};
-
-/********** ZSCORE ********************/
-
-/* ZSCORE history */
-#define ZSCORE_History NULL
-
-/* ZSCORE tips */
-#define ZSCORE_tips NULL
-
-/* ZSCORE argument table */
-struct redisCommandArg ZSCORE_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"member",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** ZUNION ********************/
-
-/* ZUNION history */
-#define ZUNION_History NULL
-
-/* ZUNION tips */
-#define ZUNION_tips NULL
-
-/* ZUNION aggregate argument table */
-struct redisCommandArg ZUNION_aggregate_Subargs[] = {
-{"sum",ARG_TYPE_PURE_TOKEN,-1,"SUM",NULL,NULL,CMD_ARG_NONE},
-{"min",ARG_TYPE_PURE_TOKEN,-1,"MIN",NULL,NULL,CMD_ARG_NONE},
-{"max",ARG_TYPE_PURE_TOKEN,-1,"MAX",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* ZUNION argument table */
-struct redisCommandArg ZUNION_Args[] = {
-{"numkeys",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{"weight",ARG_TYPE_INTEGER,-1,"WEIGHTS",NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE},
-{"aggregate",ARG_TYPE_ONEOF,-1,"AGGREGATE",NULL,NULL,CMD_ARG_OPTIONAL,.subargs=ZUNION_aggregate_Subargs},
-{"withscores",ARG_TYPE_PURE_TOKEN,-1,"WITHSCORES",NULL,NULL,CMD_ARG_OPTIONAL},
-{0}
-};
-
-/********** ZUNIONSTORE ********************/
-
-/* ZUNIONSTORE history */
-#define ZUNIONSTORE_History NULL
-
-/* ZUNIONSTORE tips */
-#define ZUNIONSTORE_tips NULL
-
-/* ZUNIONSTORE aggregate argument table */
-struct redisCommandArg ZUNIONSTORE_aggregate_Subargs[] = {
-{"sum",ARG_TYPE_PURE_TOKEN,-1,"SUM",NULL,NULL,CMD_ARG_NONE},
-{"min",ARG_TYPE_PURE_TOKEN,-1,"MIN",NULL,NULL,CMD_ARG_NONE},
-{"max",ARG_TYPE_PURE_TOKEN,-1,"MAX",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* ZUNIONSTORE argument table */
-struct redisCommandArg ZUNIONSTORE_Args[] = {
-{"destination",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"numkeys",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"key",ARG_TYPE_KEY,1,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{"weight",ARG_TYPE_INTEGER,-1,"WEIGHTS",NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE},
-{"aggregate",ARG_TYPE_ONEOF,-1,"AGGREGATE",NULL,NULL,CMD_ARG_OPTIONAL,.subargs=ZUNIONSTORE_aggregate_Subargs},
-{0}
-};
-
-/********** XACK ********************/
-
-/* XACK history */
-#define XACK_History NULL
-
-/* XACK tips */
-#define XACK_tips NULL
-
-/* XACK argument table */
-struct redisCommandArg XACK_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"group",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"id",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** XADD ********************/
-
-/* XADD history */
-commandHistory XADD_History[] = {
-{"6.2.0","Added the `NOMKSTREAM` option, `MINID` trimming strategy and the `LIMIT` option."},
-{"7.0.0","Added support for the `<ms>-*` explicit ID form."},
-{0}
-};
-
-/* XADD tips */
-const char *XADD_tips[] = {
-"nondeterministic_output",
-NULL
-};
-
-/* XADD trim strategy argument table */
-struct redisCommandArg XADD_trim_strategy_Subargs[] = {
-{"maxlen",ARG_TYPE_PURE_TOKEN,-1,"MAXLEN",NULL,NULL,CMD_ARG_NONE},
-{"minid",ARG_TYPE_PURE_TOKEN,-1,"MINID",NULL,"6.2.0",CMD_ARG_NONE},
-{0}
-};
-
-/* XADD trim operator argument table */
-struct redisCommandArg XADD_trim_operator_Subargs[] = {
-{"equal",ARG_TYPE_PURE_TOKEN,-1,"=",NULL,NULL,CMD_ARG_NONE},
-{"approximately",ARG_TYPE_PURE_TOKEN,-1,"~",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* XADD trim argument table */
-struct redisCommandArg XADD_trim_Subargs[] = {
-{"strategy",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,.subargs=XADD_trim_strategy_Subargs},
-{"operator",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,.subargs=XADD_trim_operator_Subargs},
-{"threshold",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"count",ARG_TYPE_INTEGER,-1,"LIMIT",NULL,"6.2.0",CMD_ARG_OPTIONAL},
-{0}
-};
-
-/* XADD id_selector argument table */
-struct redisCommandArg XADD_id_selector_Subargs[] = {
-{"auto-id",ARG_TYPE_PURE_TOKEN,-1,"*",NULL,NULL,CMD_ARG_NONE},
-{"id",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* XADD data argument table */
-struct redisCommandArg XADD_data_Subargs[] = {
-{"field",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"value",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* XADD argument table */
-struct redisCommandArg XADD_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"nomkstream",ARG_TYPE_PURE_TOKEN,-1,"NOMKSTREAM",NULL,"6.2.0",CMD_ARG_OPTIONAL},
-{"trim",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,.subargs=XADD_trim_Subargs},
-{"id-selector",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,.subargs=XADD_id_selector_Subargs},
-{"data",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,.subargs=XADD_data_Subargs},
-{0}
-};
-
-/********** XAUTOCLAIM ********************/
-
-/* XAUTOCLAIM history */
-commandHistory XAUTOCLAIM_History[] = {
-{"7.0.0","Added an element to the reply array, containing deleted entries the command cleared from the PEL"},
-{0}
-};
-
-/* XAUTOCLAIM tips */
-const char *XAUTOCLAIM_tips[] = {
-"nondeterministic_output",
-NULL
-};
-
-/* XAUTOCLAIM argument table */
-struct redisCommandArg XAUTOCLAIM_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"group",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"consumer",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"min-idle-time",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"start",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"count",ARG_TYPE_INTEGER,-1,"COUNT",NULL,NULL,CMD_ARG_OPTIONAL},
-{"justid",ARG_TYPE_PURE_TOKEN,-1,"JUSTID",NULL,NULL,CMD_ARG_OPTIONAL},
-{0}
-};
-
-/********** XCLAIM ********************/
-
-/* XCLAIM history */
-#define XCLAIM_History NULL
-
-/* XCLAIM tips */
-const char *XCLAIM_tips[] = {
-"nondeterministic_output",
-NULL
-};
-
-/* XCLAIM argument table */
-struct redisCommandArg XCLAIM_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"group",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"consumer",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"min-idle-time",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"id",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{"ms",ARG_TYPE_INTEGER,-1,"IDLE",NULL,NULL,CMD_ARG_OPTIONAL},
-{"unix-time-milliseconds",ARG_TYPE_UNIX_TIME,-1,"TIME",NULL,NULL,CMD_ARG_OPTIONAL},
-{"count",ARG_TYPE_INTEGER,-1,"RETRYCOUNT",NULL,NULL,CMD_ARG_OPTIONAL},
-{"force",ARG_TYPE_PURE_TOKEN,-1,"FORCE",NULL,NULL,CMD_ARG_OPTIONAL},
-{"justid",ARG_TYPE_PURE_TOKEN,-1,"JUSTID",NULL,NULL,CMD_ARG_OPTIONAL},
-{"lastid",ARG_TYPE_STRING,-1,"LASTID",NULL,NULL,CMD_ARG_OPTIONAL},
-{0}
-};
-
-/********** XDEL ********************/
-
-/* XDEL history */
-#define XDEL_History NULL
-
-/* XDEL tips */
-#define XDEL_tips NULL
-
-/* XDEL argument table */
-struct redisCommandArg XDEL_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"id",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** XGROUP CREATE ********************/
-
-/* XGROUP CREATE history */
-commandHistory XGROUP_CREATE_History[] = {
-{"7.0.0","Added the `entries_read` named argument."},
-{0}
-};
-
-/* XGROUP CREATE tips */
-#define XGROUP_CREATE_tips NULL
-
-/* XGROUP CREATE id_selector argument table */
-struct redisCommandArg XGROUP_CREATE_id_selector_Subargs[] = {
-{"id",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"new-id",ARG_TYPE_PURE_TOKEN,-1,"$",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* XGROUP CREATE argument table */
-struct redisCommandArg XGROUP_CREATE_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"group",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"id-selector",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,.subargs=XGROUP_CREATE_id_selector_Subargs},
-{"mkstream",ARG_TYPE_PURE_TOKEN,-1,"MKSTREAM",NULL,NULL,CMD_ARG_OPTIONAL},
-{"entries-read",ARG_TYPE_INTEGER,-1,"ENTRIESREAD",NULL,NULL,CMD_ARG_OPTIONAL},
-{0}
-};
-
-/********** XGROUP CREATECONSUMER ********************/
-
-/* XGROUP CREATECONSUMER history */
-#define XGROUP_CREATECONSUMER_History NULL
-
-/* XGROUP CREATECONSUMER tips */
-#define XGROUP_CREATECONSUMER_tips NULL
-
-/* XGROUP CREATECONSUMER argument table */
-struct redisCommandArg XGROUP_CREATECONSUMER_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"group",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"consumer",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** XGROUP DELCONSUMER ********************/
-
-/* XGROUP DELCONSUMER history */
-#define XGROUP_DELCONSUMER_History NULL
-
-/* XGROUP DELCONSUMER tips */
-#define XGROUP_DELCONSUMER_tips NULL
-
-/* XGROUP DELCONSUMER argument table */
-struct redisCommandArg XGROUP_DELCONSUMER_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"group",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"consumer",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** XGROUP DESTROY ********************/
-
-/* XGROUP DESTROY history */
-#define XGROUP_DESTROY_History NULL
-
-/* XGROUP DESTROY tips */
-#define XGROUP_DESTROY_tips NULL
-
-/* XGROUP DESTROY argument table */
-struct redisCommandArg XGROUP_DESTROY_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"group",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** XGROUP HELP ********************/
-
-/* XGROUP HELP history */
-#define XGROUP_HELP_History NULL
-
-/* XGROUP HELP tips */
-#define XGROUP_HELP_tips NULL
-
-/********** XGROUP SETID ********************/
-
-/* XGROUP SETID history */
-commandHistory XGROUP_SETID_History[] = {
-{"7.0.0","Added the optional `entries_read` argument."},
-{0}
-};
-
-/* XGROUP SETID tips */
-#define XGROUP_SETID_tips NULL
-
-/* XGROUP SETID id_selector argument table */
-struct redisCommandArg XGROUP_SETID_id_selector_Subargs[] = {
-{"id",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"new-id",ARG_TYPE_PURE_TOKEN,-1,"$",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* XGROUP SETID argument table */
-struct redisCommandArg XGROUP_SETID_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"group",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"id-selector",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,.subargs=XGROUP_SETID_id_selector_Subargs},
-{"entriesread",ARG_TYPE_INTEGER,-1,"ENTRIESREAD",NULL,NULL,CMD_ARG_OPTIONAL,.display_text="entries-read"},
-{0}
-};
-
-/* XGROUP command table */
-struct redisCommand XGROUP_Subcommands[] = {
-{"create","Create a consumer group.","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_STREAM,XGROUP_CREATE_History,XGROUP_CREATE_tips,xgroupCommand,-5,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_STREAM,{{NULL,CMD_KEY_RW|CMD_KEY_INSERT,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=XGROUP_CREATE_Args},
-{"createconsumer","Create a consumer in a consumer group.","O(1)","6.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_STREAM,XGROUP_CREATECONSUMER_History,XGROUP_CREATECONSUMER_tips,xgroupCommand,5,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_STREAM,{{NULL,CMD_KEY_RW|CMD_KEY_INSERT,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=XGROUP_CREATECONSUMER_Args},
-{"delconsumer","Delete a consumer from a consumer group.","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_STREAM,XGROUP_DELCONSUMER_History,XGROUP_DELCONSUMER_tips,xgroupCommand,5,CMD_WRITE,ACL_CATEGORY_STREAM,{{NULL,CMD_KEY_RW|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=XGROUP_DELCONSUMER_Args},
-{"destroy","Destroy a consumer group.","O(N) where N is the number of entries in the group's pending entries list (PEL).","5.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_STREAM,XGROUP_DESTROY_History,XGROUP_DESTROY_tips,xgroupCommand,4,CMD_WRITE,ACL_CATEGORY_STREAM,{{NULL,CMD_KEY_RW|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=XGROUP_DESTROY_Args},
-{"help","Show helpful text about the different subcommands","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_STREAM,XGROUP_HELP_History,XGROUP_HELP_tips,xgroupCommand,2,CMD_LOADING|CMD_STALE,ACL_CATEGORY_STREAM},
-{"setid","Set a consumer group to an arbitrary last delivered ID value.","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_STREAM,XGROUP_SETID_History,XGROUP_SETID_tips,xgroupCommand,-5,CMD_WRITE,ACL_CATEGORY_STREAM,{{NULL,CMD_KEY_RW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=XGROUP_SETID_Args},
-{0}
-};
-
-/********** XGROUP ********************/
-
-/* XGROUP history */
-#define XGROUP_History NULL
-
-/* XGROUP tips */
-#define XGROUP_tips NULL
-
-/********** XINFO CONSUMERS ********************/
-
-/* XINFO CONSUMERS history */
-commandHistory XINFO_CONSUMERS_History[] = {
-{"7.2.0","Added the `inactive` field."},
-{0}
-};
-
-/* XINFO CONSUMERS tips */
-const char *XINFO_CONSUMERS_tips[] = {
-"nondeterministic_output",
-NULL
-};
-
-/* XINFO CONSUMERS argument table */
-struct redisCommandArg XINFO_CONSUMERS_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"group",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** XINFO GROUPS ********************/
-
-/* XINFO GROUPS history */
-commandHistory XINFO_GROUPS_History[] = {
-{"7.0.0","Added the `entries-read` and `lag` fields"},
-{0}
-};
-
-/* XINFO GROUPS tips */
-#define XINFO_GROUPS_tips NULL
-
-/* XINFO GROUPS argument table */
-struct redisCommandArg XINFO_GROUPS_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** XINFO HELP ********************/
-
-/* XINFO HELP history */
-#define XINFO_HELP_History NULL
-
-/* XINFO HELP tips */
-#define XINFO_HELP_tips NULL
-
-/********** XINFO STREAM ********************/
-
-/* XINFO STREAM history */
-commandHistory XINFO_STREAM_History[] = {
-{"6.0.0","Added the `FULL` modifier."},
-{"7.0.0","Added the `max-deleted-entry-id`, `entries-added`, `recorded-first-entry-id`, `entries-read` and `lag` fields"},
-{"7.2.0","Added the `active-time` field, and changed the meaning of `seen-time`."},
-{0}
-};
-
-/* XINFO STREAM tips */
-#define XINFO_STREAM_tips NULL
-
-/* XINFO STREAM full_block argument table */
-struct redisCommandArg XINFO_STREAM_full_block_Subargs[] = {
-{"full",ARG_TYPE_PURE_TOKEN,-1,"FULL",NULL,NULL,CMD_ARG_NONE},
-{"count",ARG_TYPE_INTEGER,-1,"COUNT",NULL,NULL,CMD_ARG_OPTIONAL},
-{0}
-};
-
-/* XINFO STREAM argument table */
-struct redisCommandArg XINFO_STREAM_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"full-block",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,.subargs=XINFO_STREAM_full_block_Subargs},
-{0}
-};
-
-/* XINFO command table */
-struct redisCommand XINFO_Subcommands[] = {
-{"consumers","List the consumers in a consumer group","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_STREAM,XINFO_CONSUMERS_History,XINFO_CONSUMERS_tips,xinfoCommand,4,CMD_READONLY,ACL_CATEGORY_STREAM,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=XINFO_CONSUMERS_Args},
-{"groups","List the consumer groups of a stream","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_STREAM,XINFO_GROUPS_History,XINFO_GROUPS_tips,xinfoCommand,3,CMD_READONLY,ACL_CATEGORY_STREAM,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=XINFO_GROUPS_Args},
-{"help","Show helpful text about the different subcommands","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_STREAM,XINFO_HELP_History,XINFO_HELP_tips,xinfoCommand,2,CMD_LOADING|CMD_STALE,ACL_CATEGORY_STREAM},
-{"stream","Get information about a stream","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_STREAM,XINFO_STREAM_History,XINFO_STREAM_tips,xinfoCommand,-3,CMD_READONLY,ACL_CATEGORY_STREAM,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=XINFO_STREAM_Args},
-{0}
-};
-
-/********** XINFO ********************/
-
-/* XINFO history */
-#define XINFO_History NULL
-
-/* XINFO tips */
-#define XINFO_tips NULL
-
-/********** XLEN ********************/
-
-/* XLEN history */
-#define XLEN_History NULL
-
-/* XLEN tips */
-#define XLEN_tips NULL
-
-/* XLEN argument table */
-struct redisCommandArg XLEN_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** XPENDING ********************/
-
-/* XPENDING history */
-commandHistory XPENDING_History[] = {
-{"6.2.0","Added the `IDLE` option and exclusive range intervals."},
-{0}
-};
-
-/* XPENDING tips */
-const char *XPENDING_tips[] = {
-"nondeterministic_output",
-NULL
-};
-
-/* XPENDING filters argument table */
-struct redisCommandArg XPENDING_filters_Subargs[] = {
-{"min-idle-time",ARG_TYPE_INTEGER,-1,"IDLE",NULL,"6.2.0",CMD_ARG_OPTIONAL},
-{"start",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"end",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"count",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"consumer",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL},
-{0}
-};
-
-/* XPENDING argument table */
-struct redisCommandArg XPENDING_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"group",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"filters",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,.subargs=XPENDING_filters_Subargs},
-{0}
-};
-
-/********** XRANGE ********************/
-
-/* XRANGE history */
-commandHistory XRANGE_History[] = {
-{"6.2.0","Added exclusive ranges."},
-{0}
-};
-
-/* XRANGE tips */
-#define XRANGE_tips NULL
-
-/* XRANGE argument table */
-struct redisCommandArg XRANGE_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"start",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"end",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"count",ARG_TYPE_INTEGER,-1,"COUNT",NULL,NULL,CMD_ARG_OPTIONAL},
-{0}
-};
-
-/********** XREAD ********************/
-
-/* XREAD history */
-#define XREAD_History NULL
-
-/* XREAD tips */
-#define XREAD_tips NULL
-
-/* XREAD streams argument table */
-struct redisCommandArg XREAD_streams_Subargs[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{"id",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{0}
-};
-
-/* XREAD argument table */
-struct redisCommandArg XREAD_Args[] = {
-{"count",ARG_TYPE_INTEGER,-1,"COUNT",NULL,NULL,CMD_ARG_OPTIONAL},
-{"milliseconds",ARG_TYPE_INTEGER,-1,"BLOCK",NULL,NULL,CMD_ARG_OPTIONAL},
-{"streams",ARG_TYPE_BLOCK,-1,"STREAMS",NULL,NULL,CMD_ARG_NONE,.subargs=XREAD_streams_Subargs},
-{0}
-};
-
-/********** XREADGROUP ********************/
-
-/* XREADGROUP history */
-#define XREADGROUP_History NULL
-
-/* XREADGROUP tips */
-#define XREADGROUP_tips NULL
-
-/* XREADGROUP group_block argument table */
-struct redisCommandArg XREADGROUP_group_block_Subargs[] = {
-{"group",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"consumer",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* XREADGROUP streams argument table */
-struct redisCommandArg XREADGROUP_streams_Subargs[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{"id",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{0}
-};
-
-/* XREADGROUP argument table */
-struct redisCommandArg XREADGROUP_Args[] = {
-{"group-block",ARG_TYPE_BLOCK,-1,"GROUP",NULL,NULL,CMD_ARG_NONE,.subargs=XREADGROUP_group_block_Subargs},
-{"count",ARG_TYPE_INTEGER,-1,"COUNT",NULL,NULL,CMD_ARG_OPTIONAL},
-{"milliseconds",ARG_TYPE_INTEGER,-1,"BLOCK",NULL,NULL,CMD_ARG_OPTIONAL},
-{"noack",ARG_TYPE_PURE_TOKEN,-1,"NOACK",NULL,NULL,CMD_ARG_OPTIONAL},
-{"streams",ARG_TYPE_BLOCK,-1,"STREAMS",NULL,NULL,CMD_ARG_NONE,.subargs=XREADGROUP_streams_Subargs},
-{0}
-};
-
-/********** XREVRANGE ********************/
-
-/* XREVRANGE history */
-commandHistory XREVRANGE_History[] = {
-{"6.2.0","Added exclusive ranges."},
-{0}
-};
-
-/* XREVRANGE tips */
-#define XREVRANGE_tips NULL
-
-/* XREVRANGE argument table */
-struct redisCommandArg XREVRANGE_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"end",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"start",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"count",ARG_TYPE_INTEGER,-1,"COUNT",NULL,NULL,CMD_ARG_OPTIONAL},
-{0}
-};
-
-/********** XSETID ********************/
-
-/* XSETID history */
-commandHistory XSETID_History[] = {
-{"7.0.0","Added the `entries_added` and `max_deleted_entry_id` arguments."},
-{0}
-};
-
-/* XSETID tips */
-#define XSETID_tips NULL
-
-/* XSETID argument table */
-struct redisCommandArg XSETID_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"last-id",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"entries-added",ARG_TYPE_INTEGER,-1,"ENTRIESADDED",NULL,"7.0.0",CMD_ARG_OPTIONAL},
-{"max-deleted-id",ARG_TYPE_STRING,-1,"MAXDELETEDID",NULL,"7.0.0",CMD_ARG_OPTIONAL},
-{0}
-};
-
-/********** XTRIM ********************/
-
-/* XTRIM history */
-commandHistory XTRIM_History[] = {
-{"6.2.0","Added the `MINID` trimming strategy and the `LIMIT` option."},
-{0}
-};
-
-/* XTRIM tips */
-const char *XTRIM_tips[] = {
-"nondeterministic_output",
-NULL
-};
-
-/* XTRIM trim strategy argument table */
-struct redisCommandArg XTRIM_trim_strategy_Subargs[] = {
-{"maxlen",ARG_TYPE_PURE_TOKEN,-1,"MAXLEN",NULL,NULL,CMD_ARG_NONE},
-{"minid",ARG_TYPE_PURE_TOKEN,-1,"MINID",NULL,"6.2.0",CMD_ARG_NONE},
-{0}
-};
-
-/* XTRIM trim operator argument table */
-struct redisCommandArg XTRIM_trim_operator_Subargs[] = {
-{"equal",ARG_TYPE_PURE_TOKEN,-1,"=",NULL,NULL,CMD_ARG_NONE},
-{"approximately",ARG_TYPE_PURE_TOKEN,-1,"~",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* XTRIM trim argument table */
-struct redisCommandArg XTRIM_trim_Subargs[] = {
-{"strategy",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,.subargs=XTRIM_trim_strategy_Subargs},
-{"operator",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,.subargs=XTRIM_trim_operator_Subargs},
-{"threshold",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"count",ARG_TYPE_INTEGER,-1,"LIMIT",NULL,"6.2.0",CMD_ARG_OPTIONAL},
-{0}
-};
-
-/* XTRIM argument table */
-struct redisCommandArg XTRIM_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"trim",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_NONE,.subargs=XTRIM_trim_Subargs},
-{0}
-};
-
-/********** APPEND ********************/
-
-/* APPEND history */
-#define APPEND_History NULL
-
-/* APPEND tips */
-#define APPEND_tips NULL
-
-/* APPEND argument table */
-struct redisCommandArg APPEND_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"value",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** DECR ********************/
-
-/* DECR history */
-#define DECR_History NULL
-
-/* DECR tips */
-#define DECR_tips NULL
-
-/* DECR argument table */
-struct redisCommandArg DECR_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** DECRBY ********************/
-
-/* DECRBY history */
-#define DECRBY_History NULL
-
-/* DECRBY tips */
-#define DECRBY_tips NULL
-
-/* DECRBY argument table */
-struct redisCommandArg DECRBY_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"decrement",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** GET ********************/
-
-/* GET history */
-#define GET_History NULL
-
-/* GET tips */
-#define GET_tips NULL
-
-/* GET argument table */
-struct redisCommandArg GET_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** GETDEL ********************/
-
-/* GETDEL history */
-#define GETDEL_History NULL
-
-/* GETDEL tips */
-#define GETDEL_tips NULL
-
-/* GETDEL argument table */
-struct redisCommandArg GETDEL_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** GETEX ********************/
-
-/* GETEX history */
-#define GETEX_History NULL
-
-/* GETEX tips */
-#define GETEX_tips NULL
-
-/* GETEX expiration argument table */
-struct redisCommandArg GETEX_expiration_Subargs[] = {
-{"seconds",ARG_TYPE_INTEGER,-1,"EX",NULL,NULL,CMD_ARG_NONE},
-{"milliseconds",ARG_TYPE_INTEGER,-1,"PX",NULL,NULL,CMD_ARG_NONE},
-{"unix-time-seconds",ARG_TYPE_UNIX_TIME,-1,"EXAT",NULL,NULL,CMD_ARG_NONE},
-{"unix-time-milliseconds",ARG_TYPE_UNIX_TIME,-1,"PXAT",NULL,NULL,CMD_ARG_NONE},
-{"persist",ARG_TYPE_PURE_TOKEN,-1,"PERSIST",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* GETEX argument table */
-struct redisCommandArg GETEX_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"expiration",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,.subargs=GETEX_expiration_Subargs},
-{0}
-};
-
-/********** GETRANGE ********************/
-
-/* GETRANGE history */
-#define GETRANGE_History NULL
-
-/* GETRANGE tips */
-#define GETRANGE_tips NULL
-
-/* GETRANGE argument table */
-struct redisCommandArg GETRANGE_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"start",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"end",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** GETSET ********************/
-
-/* GETSET history */
-#define GETSET_History NULL
-
-/* GETSET tips */
-#define GETSET_tips NULL
-
-/* GETSET argument table */
-struct redisCommandArg GETSET_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"value",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** INCR ********************/
-
-/* INCR history */
-#define INCR_History NULL
-
-/* INCR tips */
-#define INCR_tips NULL
-
-/* INCR argument table */
-struct redisCommandArg INCR_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** INCRBY ********************/
-
-/* INCRBY history */
-#define INCRBY_History NULL
-
-/* INCRBY tips */
-#define INCRBY_tips NULL
-
-/* INCRBY argument table */
-struct redisCommandArg INCRBY_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"increment",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** INCRBYFLOAT ********************/
-
-/* INCRBYFLOAT history */
-#define INCRBYFLOAT_History NULL
-
-/* INCRBYFLOAT tips */
-#define INCRBYFLOAT_tips NULL
-
-/* INCRBYFLOAT argument table */
-struct redisCommandArg INCRBYFLOAT_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"increment",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** LCS ********************/
-
-/* LCS history */
-#define LCS_History NULL
-
-/* LCS tips */
-#define LCS_tips NULL
-
-/* LCS argument table */
-struct redisCommandArg LCS_Args[] = {
-{"key1",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"key2",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"len",ARG_TYPE_PURE_TOKEN,-1,"LEN",NULL,NULL,CMD_ARG_OPTIONAL},
-{"idx",ARG_TYPE_PURE_TOKEN,-1,"IDX",NULL,NULL,CMD_ARG_OPTIONAL},
-{"min-match-len",ARG_TYPE_INTEGER,-1,"MINMATCHLEN",NULL,NULL,CMD_ARG_OPTIONAL},
-{"withmatchlen",ARG_TYPE_PURE_TOKEN,-1,"WITHMATCHLEN",NULL,NULL,CMD_ARG_OPTIONAL},
-{0}
-};
-
-/********** MGET ********************/
-
-/* MGET history */
-#define MGET_History NULL
-
-/* MGET tips */
-const char *MGET_tips[] = {
-"request_policy:multi_shard",
-NULL
-};
-
-/* MGET argument table */
-struct redisCommandArg MGET_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{0}
-};
-
-/********** MSET ********************/
-
-/* MSET history */
-#define MSET_History NULL
-
-/* MSET tips */
-const char *MSET_tips[] = {
-"request_policy:multi_shard",
-"response_policy:all_succeeded",
-NULL
-};
-
-/* MSET data argument table */
-struct redisCommandArg MSET_data_Subargs[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"value",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* MSET argument table */
-struct redisCommandArg MSET_Args[] = {
-{"data",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,.subargs=MSET_data_Subargs},
-{0}
-};
-
-/********** MSETNX ********************/
-
-/* MSETNX history */
-#define MSETNX_History NULL
-
-/* MSETNX tips */
-const char *MSETNX_tips[] = {
-"request_policy:multi_shard",
-"response_policy:agg_min",
-NULL
-};
-
-/* MSETNX data argument table */
-struct redisCommandArg MSETNX_data_Subargs[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"value",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* MSETNX argument table */
-struct redisCommandArg MSETNX_Args[] = {
-{"data",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,.subargs=MSETNX_data_Subargs},
-{0}
-};
-
-/********** PSETEX ********************/
-
-/* PSETEX history */
-#define PSETEX_History NULL
-
-/* PSETEX tips */
-#define PSETEX_tips NULL
-
-/* PSETEX argument table */
-struct redisCommandArg PSETEX_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"milliseconds",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"value",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** SET ********************/
-
-/* SET history */
-commandHistory SET_History[] = {
-{"2.6.12","Added the `EX`, `PX`, `NX` and `XX` options."},
-{"6.0.0","Added the `KEEPTTL` option."},
-{"6.2.0","Added the `GET`, `EXAT` and `PXAT` option."},
-{"7.0.0","Allowed the `NX` and `GET` options to be used together."},
-{0}
-};
-
-/* SET tips */
-#define SET_tips NULL
-
-/* SET condition argument table */
-struct redisCommandArg SET_condition_Subargs[] = {
-{"nx",ARG_TYPE_PURE_TOKEN,-1,"NX",NULL,NULL,CMD_ARG_NONE},
-{"xx",ARG_TYPE_PURE_TOKEN,-1,"XX",NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/* SET expiration argument table */
-struct redisCommandArg SET_expiration_Subargs[] = {
-{"seconds",ARG_TYPE_INTEGER,-1,"EX",NULL,"2.6.12",CMD_ARG_NONE},
-{"milliseconds",ARG_TYPE_INTEGER,-1,"PX",NULL,"2.6.12",CMD_ARG_NONE},
-{"unix-time-seconds",ARG_TYPE_UNIX_TIME,-1,"EXAT",NULL,"6.2.0",CMD_ARG_NONE},
-{"unix-time-milliseconds",ARG_TYPE_UNIX_TIME,-1,"PXAT",NULL,"6.2.0",CMD_ARG_NONE},
-{"keepttl",ARG_TYPE_PURE_TOKEN,-1,"KEEPTTL",NULL,"6.0.0",CMD_ARG_NONE},
-{0}
-};
-
-/* SET argument table */
-struct redisCommandArg SET_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"value",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"condition",ARG_TYPE_ONEOF,-1,NULL,NULL,"2.6.12",CMD_ARG_OPTIONAL,.subargs=SET_condition_Subargs},
-{"get",ARG_TYPE_PURE_TOKEN,-1,"GET",NULL,"6.2.0",CMD_ARG_OPTIONAL},
-{"expiration",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,.subargs=SET_expiration_Subargs},
-{0}
-};
-
-/********** SETEX ********************/
-
-/* SETEX history */
-#define SETEX_History NULL
-
-/* SETEX tips */
-#define SETEX_tips NULL
-
-/* SETEX argument table */
-struct redisCommandArg SETEX_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"seconds",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"value",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** SETNX ********************/
-
-/* SETNX history */
-#define SETNX_History NULL
-
-/* SETNX tips */
-#define SETNX_tips NULL
-
-/* SETNX argument table */
-struct redisCommandArg SETNX_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"value",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** SETRANGE ********************/
-
-/* SETRANGE history */
-#define SETRANGE_History NULL
-
-/* SETRANGE tips */
-#define SETRANGE_tips NULL
-
-/* SETRANGE argument table */
-struct redisCommandArg SETRANGE_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"offset",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"value",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** STRLEN ********************/
-
-/* STRLEN history */
-#define STRLEN_History NULL
-
-/* STRLEN tips */
-#define STRLEN_tips NULL
-
-/* STRLEN argument table */
-struct redisCommandArg STRLEN_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** SUBSTR ********************/
-
-/* SUBSTR history */
-#define SUBSTR_History NULL
-
-/* SUBSTR tips */
-#define SUBSTR_tips NULL
-
-/* SUBSTR argument table */
-struct redisCommandArg SUBSTR_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE},
-{"start",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{"end",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE},
-{0}
-};
-
-/********** DISCARD ********************/
-
-/* DISCARD history */
-#define DISCARD_History NULL
-
-/* DISCARD tips */
-#define DISCARD_tips NULL
-
-/********** EXEC ********************/
-
-/* EXEC history */
-#define EXEC_History NULL
-
-/* EXEC tips */
-#define EXEC_tips NULL
-
-/********** MULTI ********************/
-
-/* MULTI history */
-#define MULTI_History NULL
-
-/* MULTI tips */
-#define MULTI_tips NULL
-
-/********** UNWATCH ********************/
-
-/* UNWATCH history */
-#define UNWATCH_History NULL
-
-/* UNWATCH tips */
-#define UNWATCH_tips NULL
-
-/********** WATCH ********************/
-
-/* WATCH history */
-#define WATCH_History NULL
-
-/* WATCH tips */
-#define WATCH_tips NULL
-
-/* WATCH argument table */
-struct redisCommandArg WATCH_Args[] = {
-{"key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_MULTIPLE},
-{0}
-};
+#define MAKE_CMD(name,summary,complexity,since,doc_flags,replaced,deprecated,group,group_enum,history,num_history,tips,num_tips,function,arity,flags,acl,key_specs,key_specs_num,get_keys,numargs) name,summary,complexity,since,doc_flags,replaced,deprecated,group_enum,history,num_history,tips,num_tips,function,arity,flags,acl,key_specs,key_specs_num,get_keys,numargs
+#define MAKE_ARG(name,type,key_spec_index,token,summary,since,flags,numsubargs,deprecated_since) name,type,key_spec_index,token,summary,since,flags,deprecated_since,numsubargs
+#define COMMAND_STRUCT redisCommand
+#define COMMAND_ARG redisCommandArg
-/* Main command table */
-struct redisCommand redisCommandTable[] = {
-/* bitmap */
-{"bitcount","Count set bits in a string","O(N)","2.6.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_BITMAP,BITCOUNT_History,BITCOUNT_tips,bitcountCommand,-2,CMD_READONLY,ACL_CATEGORY_BITMAP,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=BITCOUNT_Args},
-{"bitfield","Perform arbitrary bitfield integer operations on strings","O(1) for each subcommand specified","3.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_BITMAP,BITFIELD_History,BITFIELD_tips,bitfieldCommand,-2,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_BITMAP,{{"This command allows both access and modification of the key",CMD_KEY_RW|CMD_KEY_UPDATE|CMD_KEY_ACCESS|CMD_KEY_VARIABLE_FLAGS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},bitfieldGetKeys,.args=BITFIELD_Args},
-{"bitfield_ro","Perform arbitrary bitfield integer operations on strings. Read-only variant of BITFIELD","O(1) for each subcommand specified","6.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_BITMAP,BITFIELD_RO_History,BITFIELD_RO_tips,bitfieldroCommand,-2,CMD_READONLY|CMD_FAST,ACL_CATEGORY_BITMAP,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=BITFIELD_RO_Args},
-{"bitop","Perform bitwise operations between strings","O(N)","2.6.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_BITMAP,BITOP_History,BITOP_tips,bitopCommand,-4,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_BITMAP,{{NULL,CMD_KEY_OW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={0,1,0}},{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={3},KSPEC_FK_RANGE,.fk.range={-1,1,0}}},.args=BITOP_Args},
-{"bitpos","Find first bit set or clear in a string","O(N)","2.8.7",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_BITMAP,BITPOS_History,BITPOS_tips,bitposCommand,-3,CMD_READONLY,ACL_CATEGORY_BITMAP,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=BITPOS_Args},
-{"getbit","Returns the bit value at offset in the string value stored at key","O(1)","2.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_BITMAP,GETBIT_History,GETBIT_tips,getbitCommand,3,CMD_READONLY|CMD_FAST,ACL_CATEGORY_BITMAP,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=GETBIT_Args},
-{"setbit","Sets or clears the bit at offset in the string value stored at key","O(1)","2.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_BITMAP,SETBIT_History,SETBIT_tips,setbitCommand,4,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_BITMAP,{{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=SETBIT_Args},
-/* cluster */
-{"asking","Sent by cluster clients after an -ASK redirect","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CLUSTER,ASKING_History,ASKING_tips,askingCommand,1,CMD_FAST,ACL_CATEGORY_CONNECTION},
-{"cluster","A container for cluster commands","Depends on subcommand.","3.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CLUSTER,CLUSTER_History,CLUSTER_tips,NULL,-2,0,0,.subcommands=CLUSTER_Subcommands},
-{"readonly","Enables read queries for a connection to a cluster replica node","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CLUSTER,READONLY_History,READONLY_tips,readonlyCommand,1,CMD_FAST|CMD_LOADING|CMD_STALE,ACL_CATEGORY_CONNECTION},
-{"readwrite","Disables read queries for a connection to a cluster replica node","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CLUSTER,READWRITE_History,READWRITE_tips,readwriteCommand,1,CMD_FAST|CMD_LOADING|CMD_STALE,ACL_CATEGORY_CONNECTION},
-/* connection */
-{"auth","Authenticate to the server","O(N) where N is the number of passwords defined for the user","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,AUTH_History,AUTH_tips,authCommand,-2,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_FAST|CMD_NO_AUTH|CMD_SENTINEL|CMD_ALLOW_BUSY,ACL_CATEGORY_CONNECTION,.args=AUTH_Args},
-{"client","A container for client connection commands","Depends on subcommand.","2.4.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,CLIENT_History,CLIENT_tips,NULL,-2,CMD_SENTINEL,0,.subcommands=CLIENT_Subcommands},
-{"echo","Echo the given string","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,ECHO_History,ECHO_tips,echoCommand,2,CMD_LOADING|CMD_STALE|CMD_FAST,ACL_CATEGORY_CONNECTION,.args=ECHO_Args},
-{"hello","Handshake with Redis","O(1)","6.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,HELLO_History,HELLO_tips,helloCommand,-1,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_FAST|CMD_NO_AUTH|CMD_SENTINEL|CMD_ALLOW_BUSY,ACL_CATEGORY_CONNECTION,.args=HELLO_Args},
-{"ping","Ping the server","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,PING_History,PING_tips,pingCommand,-1,CMD_FAST|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,.args=PING_Args},
-{"quit","Close the connection","O(1)","1.0.0",CMD_DOC_DEPRECATED,"just closing the connection","7.2.0",COMMAND_GROUP_CONNECTION,QUIT_History,QUIT_tips,quitCommand,-1,CMD_ALLOW_BUSY|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_FAST|CMD_NO_AUTH,ACL_CATEGORY_CONNECTION},
-{"reset","Reset the connection","O(1)","6.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,RESET_History,RESET_tips,resetCommand,1,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_FAST|CMD_NO_AUTH|CMD_ALLOW_BUSY,ACL_CATEGORY_CONNECTION},
-{"select","Change the selected database for the current connection","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_CONNECTION,SELECT_History,SELECT_tips,selectCommand,2,CMD_LOADING|CMD_STALE|CMD_FAST,ACL_CATEGORY_CONNECTION,.args=SELECT_Args},
-/* generic */
-{"copy","Copy a key","O(N) worst case for collections, where N is the number of nested items. O(1) for string values.","6.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_GENERIC,COPY_History,COPY_tips,copyCommand,-3,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_KEYSPACE,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}},{NULL,CMD_KEY_OW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=COPY_Args},
-{"del","Delete a key","O(N) where N is the number of keys that will be removed. When a key to remove holds a value other than a string, the individual complexity for this key is O(M) where M is the number of elements in the list, set, sorted set or hash. Removing a single key that holds a string value is O(1).","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_GENERIC,DEL_History,DEL_tips,delCommand,-2,CMD_WRITE,ACL_CATEGORY_KEYSPACE,{{NULL,CMD_KEY_RM|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={-1,1,0}}},.args=DEL_Args},
-{"dump","Return a serialized version of the value stored at the specified key.","O(1) to access the key and additional O(N*M) to serialize it, where N is the number of Redis objects composing the value and M their average size. For small string values the time complexity is thus O(1)+O(1*M) where M is small, so simply O(1).","2.6.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_GENERIC,DUMP_History,DUMP_tips,dumpCommand,2,CMD_READONLY,ACL_CATEGORY_KEYSPACE,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=DUMP_Args},
-{"exists","Determine if a key exists","O(N) where N is the number of keys to check.","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_GENERIC,EXISTS_History,EXISTS_tips,existsCommand,-2,CMD_READONLY|CMD_FAST,ACL_CATEGORY_KEYSPACE,{{NULL,CMD_KEY_RO,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={-1,1,0}}},.args=EXISTS_Args},
-{"expire","Set a key's time to live in seconds","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_GENERIC,EXPIRE_History,EXPIRE_tips,expireCommand,-3,CMD_WRITE|CMD_FAST,ACL_CATEGORY_KEYSPACE,{{NULL,CMD_KEY_RW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=EXPIRE_Args},
-{"expireat","Set the expiration for a key as a UNIX timestamp","O(1)","1.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_GENERIC,EXPIREAT_History,EXPIREAT_tips,expireatCommand,-3,CMD_WRITE|CMD_FAST,ACL_CATEGORY_KEYSPACE,{{NULL,CMD_KEY_RW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=EXPIREAT_Args},
-{"expiretime","Get the expiration Unix timestamp for a key","O(1)","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_GENERIC,EXPIRETIME_History,EXPIRETIME_tips,expiretimeCommand,2,CMD_READONLY|CMD_FAST,ACL_CATEGORY_KEYSPACE,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=EXPIRETIME_Args},
-{"keys","Find all keys matching the given pattern","O(N) with N being the number of keys in the database, under the assumption that the key names in the database and the given pattern have limited length.","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_GENERIC,KEYS_History,KEYS_tips,keysCommand,2,CMD_READONLY,ACL_CATEGORY_KEYSPACE|ACL_CATEGORY_DANGEROUS,.args=KEYS_Args},
-{"migrate","Atomically transfer a key from a Redis instance to another one.","This command actually executes a DUMP+DEL in the source instance, and a RESTORE in the target instance. See the pages of these commands for time complexity. Also an O(N) data transfer between the two instances is performed.","2.6.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_GENERIC,MIGRATE_History,MIGRATE_tips,migrateCommand,-6,CMD_WRITE,ACL_CATEGORY_KEYSPACE|ACL_CATEGORY_DANGEROUS,{{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={3},KSPEC_FK_RANGE,.fk.range={0,1,0}},{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_DELETE|CMD_KEY_INCOMPLETE,KSPEC_BS_KEYWORD,.bs.keyword={"KEYS",-2},KSPEC_FK_RANGE,.fk.range={-1,1,0}}},migrateGetKeys,.args=MIGRATE_Args},
-{"move","Move a key to another database","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_GENERIC,MOVE_History,MOVE_tips,moveCommand,3,CMD_WRITE|CMD_FAST,ACL_CATEGORY_KEYSPACE,{{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=MOVE_Args},
-{"object","A container for object introspection commands","Depends on subcommand.","2.2.3",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_GENERIC,OBJECT_History,OBJECT_tips,NULL,-2,0,0,.subcommands=OBJECT_Subcommands},
-{"persist","Remove the expiration from a key","O(1)","2.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_GENERIC,PERSIST_History,PERSIST_tips,persistCommand,2,CMD_WRITE|CMD_FAST,ACL_CATEGORY_KEYSPACE,{{NULL,CMD_KEY_RW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=PERSIST_Args},
-{"pexpire","Set a key's time to live in milliseconds","O(1)","2.6.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_GENERIC,PEXPIRE_History,PEXPIRE_tips,pexpireCommand,-3,CMD_WRITE|CMD_FAST,ACL_CATEGORY_KEYSPACE,{{NULL,CMD_KEY_RW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=PEXPIRE_Args},
-{"pexpireat","Set the expiration for a key as a UNIX timestamp specified in milliseconds","O(1)","2.6.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_GENERIC,PEXPIREAT_History,PEXPIREAT_tips,pexpireatCommand,-3,CMD_WRITE|CMD_FAST,ACL_CATEGORY_KEYSPACE,{{NULL,CMD_KEY_RW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=PEXPIREAT_Args},
-{"pexpiretime","Get the expiration Unix timestamp for a key in milliseconds","O(1)","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_GENERIC,PEXPIRETIME_History,PEXPIRETIME_tips,pexpiretimeCommand,2,CMD_READONLY|CMD_FAST,ACL_CATEGORY_KEYSPACE,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=PEXPIRETIME_Args},
-{"pttl","Get the time to live for a key in milliseconds","O(1)","2.6.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_GENERIC,PTTL_History,PTTL_tips,pttlCommand,2,CMD_READONLY|CMD_FAST,ACL_CATEGORY_KEYSPACE,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=PTTL_Args},
-{"randomkey","Return a random key from the keyspace","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_GENERIC,RANDOMKEY_History,RANDOMKEY_tips,randomkeyCommand,1,CMD_READONLY|CMD_TOUCHES_ARBITRARY_KEYS,ACL_CATEGORY_KEYSPACE},
-{"rename","Rename a key","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_GENERIC,RENAME_History,RENAME_tips,renameCommand,3,CMD_WRITE,ACL_CATEGORY_KEYSPACE,{{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}},{NULL,CMD_KEY_OW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=RENAME_Args},
-{"renamenx","Rename a key, only if the new key does not exist","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_GENERIC,RENAMENX_History,RENAMENX_tips,renamenxCommand,3,CMD_WRITE|CMD_FAST,ACL_CATEGORY_KEYSPACE,{{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}},{NULL,CMD_KEY_OW|CMD_KEY_INSERT,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=RENAMENX_Args},
-{"restore","Create a key using the provided serialized value, previously obtained using DUMP.","O(1) to create the new key and additional O(N*M) to reconstruct the serialized value, where N is the number of Redis objects composing the value and M their average size. For small string values the time complexity is thus O(1)+O(1*M) where M is small, so simply O(1). However for sorted set values the complexity is O(N*M*log(N)) because inserting values into sorted sets is O(log(N)).","2.6.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_GENERIC,RESTORE_History,RESTORE_tips,restoreCommand,-4,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_KEYSPACE|ACL_CATEGORY_DANGEROUS,{{NULL,CMD_KEY_OW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=RESTORE_Args},
-{"scan","Incrementally iterate the keys space","O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.","2.8.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_GENERIC,SCAN_History,SCAN_tips,scanCommand,-2,CMD_READONLY|CMD_TOUCHES_ARBITRARY_KEYS,ACL_CATEGORY_KEYSPACE,.args=SCAN_Args},
-{"sort","Sort the elements in a list, set or sorted set","O(N+M*log(M)) where N is the number of elements in the list or set to sort, and M the number of returned elements. When the elements are not sorted, complexity is O(N).","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_GENERIC,SORT_History,SORT_tips,sortCommand,-2,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_SET|ACL_CATEGORY_SORTEDSET|ACL_CATEGORY_LIST|ACL_CATEGORY_DANGEROUS,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}},{"For the optional BY/GET keyword. It is marked 'unknown' because the key names derive from the content of the key we sort",CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_UNKNOWN,{{0}},KSPEC_FK_UNKNOWN,{{0}}},{"For the optional STORE keyword. It is marked 'unknown' because the keyword can appear anywhere in the argument array",CMD_KEY_OW|CMD_KEY_UPDATE,KSPEC_BS_UNKNOWN,{{0}},KSPEC_FK_UNKNOWN,{{0}}}},sortGetKeys,.args=SORT_Args},
-{"sort_ro","Sort the elements in a list, set or sorted set. Read-only variant of SORT.","O(N+M*log(M)) where N is the number of elements in the list or set to sort, and M the number of returned elements. When the elements are not sorted, complexity is O(N).","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_GENERIC,SORT_RO_History,SORT_RO_tips,sortroCommand,-2,CMD_READONLY,ACL_CATEGORY_SET|ACL_CATEGORY_SORTEDSET|ACL_CATEGORY_LIST|ACL_CATEGORY_DANGEROUS,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}},{"For the optional BY/GET keyword. It is marked 'unknown' because the key names derive from the content of the key we sort",CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_UNKNOWN,{{0}},KSPEC_FK_UNKNOWN,{{0}}}},sortROGetKeys,.args=SORT_RO_Args},
-{"touch","Alters the last access time of a key(s). Returns the number of existing keys specified.","O(N) where N is the number of keys that will be touched.","3.2.1",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_GENERIC,TOUCH_History,TOUCH_tips,touchCommand,-2,CMD_READONLY|CMD_FAST,ACL_CATEGORY_KEYSPACE,{{NULL,CMD_KEY_RO,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={-1,1,0}}},.args=TOUCH_Args},
-{"ttl","Get the time to live for a key in seconds","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_GENERIC,TTL_History,TTL_tips,ttlCommand,2,CMD_READONLY|CMD_FAST,ACL_CATEGORY_KEYSPACE,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=TTL_Args},
-{"type","Determine the type stored at key","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_GENERIC,TYPE_History,TYPE_tips,typeCommand,2,CMD_READONLY|CMD_FAST,ACL_CATEGORY_KEYSPACE,{{NULL,CMD_KEY_RO,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=TYPE_Args},
-{"unlink","Delete a key asynchronously in another thread. Otherwise it is just as DEL, but non blocking.","O(1) for each key removed regardless of its size. Then the command does O(N) work in a different thread in order to reclaim memory, where N is the number of allocations the deleted objects where composed of.","4.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_GENERIC,UNLINK_History,UNLINK_tips,unlinkCommand,-2,CMD_WRITE|CMD_FAST,ACL_CATEGORY_KEYSPACE,{{NULL,CMD_KEY_RM|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={-1,1,0}}},.args=UNLINK_Args},
-{"wait","Wait for the synchronous replication of all the write commands sent in the context of the current connection","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_GENERIC,WAIT_History,WAIT_tips,waitCommand,3,0,ACL_CATEGORY_CONNECTION,.args=WAIT_Args},
-{"waitaof","Wait for all write commands sent in the context of the current connection to be synced to AOF of local host and/or replicas","O(1)","7.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_GENERIC,WAITAOF_History,WAITAOF_tips,waitaofCommand,4,CMD_NOSCRIPT,ACL_CATEGORY_CONNECTION,.args=WAITAOF_Args},
-/* geo */
-{"geoadd","Add one or more geospatial items in the geospatial index represented using a sorted set","O(log(N)) for each item added, where N is the number of elements in the sorted set.","3.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_GEO,GEOADD_History,GEOADD_tips,geoaddCommand,-5,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_GEO,{{NULL,CMD_KEY_RW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=GEOADD_Args},
-{"geodist","Returns the distance between two members of a geospatial index","O(log(N))","3.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_GEO,GEODIST_History,GEODIST_tips,geodistCommand,-4,CMD_READONLY,ACL_CATEGORY_GEO,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=GEODIST_Args},
-{"geohash","Returns members of a geospatial index as standard geohash strings","O(log(N)) for each member requested, where N is the number of elements in the sorted set.","3.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_GEO,GEOHASH_History,GEOHASH_tips,geohashCommand,-2,CMD_READONLY,ACL_CATEGORY_GEO,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=GEOHASH_Args},
-{"geopos","Returns longitude and latitude of members of a geospatial index","O(N) where N is the number of members requested.","3.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_GEO,GEOPOS_History,GEOPOS_tips,geoposCommand,-2,CMD_READONLY,ACL_CATEGORY_GEO,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=GEOPOS_Args},
-{"georadius","Query a sorted set representing a geospatial index to fetch members matching a given maximum distance from a point","O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.","3.2.0",CMD_DOC_DEPRECATED,"`GEOSEARCH` and `GEOSEARCHSTORE` with the `BYRADIUS` argument","6.2.0",COMMAND_GROUP_GEO,GEORADIUS_History,GEORADIUS_tips,georadiusCommand,-6,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_GEO,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}},{NULL,CMD_KEY_OW|CMD_KEY_UPDATE,KSPEC_BS_KEYWORD,.bs.keyword={"STORE",6},KSPEC_FK_RANGE,.fk.range={0,1,0}},{NULL,CMD_KEY_OW|CMD_KEY_UPDATE,KSPEC_BS_KEYWORD,.bs.keyword={"STOREDIST",6},KSPEC_FK_RANGE,.fk.range={0,1,0}}},georadiusGetKeys,.args=GEORADIUS_Args},
-{"georadiusbymember","Query a sorted set representing a geospatial index to fetch members matching a given maximum distance from a member","O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.","3.2.0",CMD_DOC_DEPRECATED,"`GEOSEARCH` and `GEOSEARCHSTORE` with the `BYRADIUS` and `FROMMEMBER` arguments","6.2.0",COMMAND_GROUP_GEO,GEORADIUSBYMEMBER_History,GEORADIUSBYMEMBER_tips,georadiusbymemberCommand,-5,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_GEO,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}},{NULL,CMD_KEY_OW|CMD_KEY_UPDATE,KSPEC_BS_KEYWORD,.bs.keyword={"STORE",5},KSPEC_FK_RANGE,.fk.range={0,1,0}},{NULL,CMD_KEY_OW|CMD_KEY_UPDATE,KSPEC_BS_KEYWORD,.bs.keyword={"STOREDIST",5},KSPEC_FK_RANGE,.fk.range={0,1,0}}},georadiusGetKeys,.args=GEORADIUSBYMEMBER_Args},
-{"georadiusbymember_ro","A read-only variant for GEORADIUSBYMEMBER","O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.","3.2.10",CMD_DOC_DEPRECATED,"`GEOSEARCH` with the `BYRADIUS` and `FROMMEMBER` arguments","6.2.0",COMMAND_GROUP_GEO,GEORADIUSBYMEMBER_RO_History,GEORADIUSBYMEMBER_RO_tips,georadiusbymemberroCommand,-5,CMD_READONLY,ACL_CATEGORY_GEO,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=GEORADIUSBYMEMBER_RO_Args},
-{"georadius_ro","A read-only variant for GEORADIUS","O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.","3.2.10",CMD_DOC_DEPRECATED,"`GEOSEARCH` with the `BYRADIUS` argument","6.2.0",COMMAND_GROUP_GEO,GEORADIUS_RO_History,GEORADIUS_RO_tips,georadiusroCommand,-6,CMD_READONLY,ACL_CATEGORY_GEO,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=GEORADIUS_RO_Args},
-{"geosearch","Query a sorted set representing a geospatial index to fetch members inside an area of a box or a circle.","O(N+log(M)) where N is the number of elements in the grid-aligned bounding box area around the shape provided as the filter and M is the number of items inside the shape","6.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_GEO,GEOSEARCH_History,GEOSEARCH_tips,geosearchCommand,-7,CMD_READONLY,ACL_CATEGORY_GEO,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=GEOSEARCH_Args},
-{"geosearchstore","Query a sorted set representing a geospatial index to fetch members inside an area of a box or a circle, and store the result in another key.","O(N+log(M)) where N is the number of elements in the grid-aligned bounding box area around the shape provided as the filter and M is the number of items inside the shape","6.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_GEO,GEOSEARCHSTORE_History,GEOSEARCHSTORE_tips,geosearchstoreCommand,-8,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_GEO,{{NULL,CMD_KEY_OW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}},{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=GEOSEARCHSTORE_Args},
-/* hash */
-{"hdel","Delete one or more hash fields","O(N) where N is the number of fields to be removed.","2.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_HASH,HDEL_History,HDEL_tips,hdelCommand,-3,CMD_WRITE|CMD_FAST,ACL_CATEGORY_HASH,{{NULL,CMD_KEY_RW|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=HDEL_Args},
-{"hexists","Determine if a hash field exists","O(1)","2.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_HASH,HEXISTS_History,HEXISTS_tips,hexistsCommand,3,CMD_READONLY|CMD_FAST,ACL_CATEGORY_HASH,{{NULL,CMD_KEY_RO,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=HEXISTS_Args},
-{"hget","Get the value of a hash field","O(1)","2.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_HASH,HGET_History,HGET_tips,hgetCommand,3,CMD_READONLY|CMD_FAST,ACL_CATEGORY_HASH,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=HGET_Args},
-{"hgetall","Get all the fields and values in a hash","O(N) where N is the size of the hash.","2.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_HASH,HGETALL_History,HGETALL_tips,hgetallCommand,2,CMD_READONLY,ACL_CATEGORY_HASH,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=HGETALL_Args},
-{"hincrby","Increment the integer value of a hash field by the given number","O(1)","2.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_HASH,HINCRBY_History,HINCRBY_tips,hincrbyCommand,4,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_HASH,{{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=HINCRBY_Args},
-{"hincrbyfloat","Increment the float value of a hash field by the given amount","O(1)","2.6.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_HASH,HINCRBYFLOAT_History,HINCRBYFLOAT_tips,hincrbyfloatCommand,4,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_HASH,{{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=HINCRBYFLOAT_Args},
-{"hkeys","Get all the fields in a hash","O(N) where N is the size of the hash.","2.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_HASH,HKEYS_History,HKEYS_tips,hkeysCommand,2,CMD_READONLY,ACL_CATEGORY_HASH,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=HKEYS_Args},
-{"hlen","Get the number of fields in a hash","O(1)","2.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_HASH,HLEN_History,HLEN_tips,hlenCommand,2,CMD_READONLY|CMD_FAST,ACL_CATEGORY_HASH,{{NULL,CMD_KEY_RO,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=HLEN_Args},
-{"hmget","Get the values of all the given hash fields","O(N) where N is the number of fields being requested.","2.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_HASH,HMGET_History,HMGET_tips,hmgetCommand,-3,CMD_READONLY|CMD_FAST,ACL_CATEGORY_HASH,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=HMGET_Args},
-{"hmset","Set multiple hash fields to multiple values","O(N) where N is the number of fields being set.","2.0.0",CMD_DOC_DEPRECATED,"`HSET` with multiple field-value pairs","4.0.0",COMMAND_GROUP_HASH,HMSET_History,HMSET_tips,hsetCommand,-4,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_HASH,{{NULL,CMD_KEY_RW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=HMSET_Args},
-{"hrandfield","Get one or multiple random fields from a hash","O(N) where N is the number of fields returned","6.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_HASH,HRANDFIELD_History,HRANDFIELD_tips,hrandfieldCommand,-2,CMD_READONLY,ACL_CATEGORY_HASH,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=HRANDFIELD_Args},
-{"hscan","Incrementally iterate hash fields and associated values","O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.","2.8.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_HASH,HSCAN_History,HSCAN_tips,hscanCommand,-3,CMD_READONLY,ACL_CATEGORY_HASH,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=HSCAN_Args},
-{"hset","Set the string value of a hash field","O(1) for each field/value pair added, so O(N) to add N field/value pairs when the command is called with multiple field/value pairs.","2.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_HASH,HSET_History,HSET_tips,hsetCommand,-4,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_HASH,{{NULL,CMD_KEY_RW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=HSET_Args},
-{"hsetnx","Set the value of a hash field, only if the field does not exist","O(1)","2.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_HASH,HSETNX_History,HSETNX_tips,hsetnxCommand,4,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_HASH,{{NULL,CMD_KEY_RW|CMD_KEY_INSERT,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=HSETNX_Args},
-{"hstrlen","Get the length of the value of a hash field","O(1)","3.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_HASH,HSTRLEN_History,HSTRLEN_tips,hstrlenCommand,3,CMD_READONLY|CMD_FAST,ACL_CATEGORY_HASH,{{NULL,CMD_KEY_RO,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=HSTRLEN_Args},
-{"hvals","Get all the values in a hash","O(N) where N is the size of the hash.","2.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_HASH,HVALS_History,HVALS_tips,hvalsCommand,2,CMD_READONLY,ACL_CATEGORY_HASH,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=HVALS_Args},
-/* hyperloglog */
-{"pfadd","Adds the specified elements to the specified HyperLogLog.","O(1) to add every element.","2.8.9",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_HYPERLOGLOG,PFADD_History,PFADD_tips,pfaddCommand,-2,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_HYPERLOGLOG,{{NULL,CMD_KEY_RW|CMD_KEY_INSERT,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=PFADD_Args},
-{"pfcount","Return the approximated cardinality of the set(s) observed by the HyperLogLog at key(s).","O(1) with a very small average constant time when called with a single key. O(N) with N being the number of keys, and much bigger constant times, when called with multiple keys.","2.8.9",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_HYPERLOGLOG,PFCOUNT_History,PFCOUNT_tips,pfcountCommand,-2,CMD_READONLY|CMD_MAY_REPLICATE,ACL_CATEGORY_HYPERLOGLOG,{{"RW because it may change the internal representation of the key, and propagate to replicas",CMD_KEY_RW|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={-1,1,0}}},.args=PFCOUNT_Args},
-{"pfdebug","Internal commands for debugging HyperLogLog values","N/A","2.8.9",CMD_DOC_SYSCMD,NULL,NULL,COMMAND_GROUP_HYPERLOGLOG,PFDEBUG_History,PFDEBUG_tips,pfdebugCommand,3,CMD_WRITE|CMD_DENYOOM|CMD_ADMIN,ACL_CATEGORY_HYPERLOGLOG,{{NULL,CMD_KEY_RW|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=PFDEBUG_Args},
-{"pfmerge","Merge N different HyperLogLogs into a single one.","O(N) to merge N HyperLogLogs, but with high constant times.","2.8.9",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_HYPERLOGLOG,PFMERGE_History,PFMERGE_tips,pfmergeCommand,-2,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_HYPERLOGLOG,{{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_INSERT,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}},{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={-1,1,0}}},.args=PFMERGE_Args},
-{"pfselftest","An internal command for testing HyperLogLog values","N/A","2.8.9",CMD_DOC_SYSCMD,NULL,NULL,COMMAND_GROUP_HYPERLOGLOG,PFSELFTEST_History,PFSELFTEST_tips,pfselftestCommand,1,CMD_ADMIN,ACL_CATEGORY_HYPERLOGLOG},
-/* list */
-{"blmove","Pop an element from a list, push it to another list and return it; or block until one is available","O(1)","6.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_LIST,BLMOVE_History,BLMOVE_tips,blmoveCommand,6,CMD_WRITE|CMD_DENYOOM|CMD_BLOCKING,ACL_CATEGORY_LIST,{{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}},{NULL,CMD_KEY_RW|CMD_KEY_INSERT,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=BLMOVE_Args},
-{"blmpop","Pop elements from a list, or block until one is available","O(N+M) where N is the number of provided keys and M is the number of elements returned.","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_LIST,BLMPOP_History,BLMPOP_tips,blmpopCommand,-5,CMD_WRITE|CMD_BLOCKING,ACL_CATEGORY_LIST,{{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_KEYNUM,.fk.keynum={0,1,1}}},blmpopGetKeys,.args=BLMPOP_Args},
-{"blpop","Remove and get the first element in a list, or block until one is available","O(N) where N is the number of provided keys.","2.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_LIST,BLPOP_History,BLPOP_tips,blpopCommand,-3,CMD_WRITE|CMD_BLOCKING,ACL_CATEGORY_LIST,{{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={-2,1,0}}},.args=BLPOP_Args},
-{"brpop","Remove and get the last element in a list, or block until one is available","O(N) where N is the number of provided keys.","2.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_LIST,BRPOP_History,BRPOP_tips,brpopCommand,-3,CMD_WRITE|CMD_BLOCKING,ACL_CATEGORY_LIST,{{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={-2,1,0}}},.args=BRPOP_Args},
-{"brpoplpush","Pop an element from a list, push it to another list and return it; or block until one is available","O(1)","2.2.0",CMD_DOC_DEPRECATED,"`BLMOVE` with the `RIGHT` and `LEFT` arguments","6.2.0",COMMAND_GROUP_LIST,BRPOPLPUSH_History,BRPOPLPUSH_tips,brpoplpushCommand,4,CMD_WRITE|CMD_DENYOOM|CMD_BLOCKING,ACL_CATEGORY_LIST,{{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}},{NULL,CMD_KEY_RW|CMD_KEY_INSERT,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=BRPOPLPUSH_Args},
-{"lindex","Get an element from a list by its index","O(N) where N is the number of elements to traverse to get to the element at index. This makes asking for the first or the last element of the list O(1).","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_LIST,LINDEX_History,LINDEX_tips,lindexCommand,3,CMD_READONLY,ACL_CATEGORY_LIST,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=LINDEX_Args},
-{"linsert","Insert an element before or after another element in a list","O(N) where N is the number of elements to traverse before seeing the value pivot. This means that inserting somewhere on the left end on the list (head) can be considered O(1) and inserting somewhere on the right end (tail) is O(N).","2.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_LIST,LINSERT_History,LINSERT_tips,linsertCommand,5,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_LIST,{{NULL,CMD_KEY_RW|CMD_KEY_INSERT,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=LINSERT_Args},
-{"llen","Get the length of a list","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_LIST,LLEN_History,LLEN_tips,llenCommand,2,CMD_READONLY|CMD_FAST,ACL_CATEGORY_LIST,{{NULL,CMD_KEY_RO,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=LLEN_Args},
-{"lmove","Pop an element from a list, push it to another list and return it","O(1)","6.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_LIST,LMOVE_History,LMOVE_tips,lmoveCommand,5,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_LIST,{{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}},{NULL,CMD_KEY_RW|CMD_KEY_INSERT,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=LMOVE_Args},
-{"lmpop","Pop elements from a list","O(N+M) where N is the number of provided keys and M is the number of elements returned.","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_LIST,LMPOP_History,LMPOP_tips,lmpopCommand,-4,CMD_WRITE,ACL_CATEGORY_LIST,{{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_KEYNUM,.fk.keynum={0,1,1}}},lmpopGetKeys,.args=LMPOP_Args},
-{"lpop","Remove and get the first elements in a list","O(N) where N is the number of elements returned","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_LIST,LPOP_History,LPOP_tips,lpopCommand,-2,CMD_WRITE|CMD_FAST,ACL_CATEGORY_LIST,{{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=LPOP_Args},
-{"lpos","Return the index of matching elements on a list","O(N) where N is the number of elements in the list, for the average case. When searching for elements near the head or the tail of the list, or when the MAXLEN option is provided, the command may run in constant time.","6.0.6",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_LIST,LPOS_History,LPOS_tips,lposCommand,-3,CMD_READONLY,ACL_CATEGORY_LIST,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=LPOS_Args},
-{"lpush","Prepend one or multiple elements to a list","O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_LIST,LPUSH_History,LPUSH_tips,lpushCommand,-3,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_LIST,{{NULL,CMD_KEY_RW|CMD_KEY_INSERT,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=LPUSH_Args},
-{"lpushx","Prepend an element to a list, only if the list exists","O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.","2.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_LIST,LPUSHX_History,LPUSHX_tips,lpushxCommand,-3,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_LIST,{{NULL,CMD_KEY_RW|CMD_KEY_INSERT,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=LPUSHX_Args},
-{"lrange","Get a range of elements from a list","O(S+N) where S is the distance of start offset from HEAD for small lists, from nearest end (HEAD or TAIL) for large lists; and N is the number of elements in the specified range.","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_LIST,LRANGE_History,LRANGE_tips,lrangeCommand,4,CMD_READONLY,ACL_CATEGORY_LIST,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=LRANGE_Args},
-{"lrem","Remove elements from a list","O(N+M) where N is the length of the list and M is the number of elements removed.","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_LIST,LREM_History,LREM_tips,lremCommand,4,CMD_WRITE,ACL_CATEGORY_LIST,{{NULL,CMD_KEY_RW|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=LREM_Args},
-{"lset","Set the value of an element in a list by its index","O(N) where N is the length of the list. Setting either the first or the last element of the list is O(1).","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_LIST,LSET_History,LSET_tips,lsetCommand,4,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_LIST,{{NULL,CMD_KEY_RW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=LSET_Args},
-{"ltrim","Trim a list to the specified range","O(N) where N is the number of elements to be removed by the operation.","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_LIST,LTRIM_History,LTRIM_tips,ltrimCommand,4,CMD_WRITE,ACL_CATEGORY_LIST,{{NULL,CMD_KEY_RW|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=LTRIM_Args},
-{"rpop","Remove and get the last elements in a list","O(N) where N is the number of elements returned","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_LIST,RPOP_History,RPOP_tips,rpopCommand,-2,CMD_WRITE|CMD_FAST,ACL_CATEGORY_LIST,{{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=RPOP_Args},
-{"rpoplpush","Remove the last element in a list, prepend it to another list and return it","O(1)","1.2.0",CMD_DOC_DEPRECATED,"`LMOVE` with the `RIGHT` and `LEFT` arguments","6.2.0",COMMAND_GROUP_LIST,RPOPLPUSH_History,RPOPLPUSH_tips,rpoplpushCommand,3,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_LIST,{{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}},{NULL,CMD_KEY_RW|CMD_KEY_INSERT,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=RPOPLPUSH_Args},
-{"rpush","Append one or multiple elements to a list","O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_LIST,RPUSH_History,RPUSH_tips,rpushCommand,-3,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_LIST,{{NULL,CMD_KEY_RW|CMD_KEY_INSERT,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=RPUSH_Args},
-{"rpushx","Append an element to a list, only if the list exists","O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.","2.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_LIST,RPUSHX_History,RPUSHX_tips,rpushxCommand,-3,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_LIST,{{NULL,CMD_KEY_RW|CMD_KEY_INSERT,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=RPUSHX_Args},
-/* pubsub */
-{"psubscribe","Listen for messages published to channels matching the given patterns","O(N) where N is the number of patterns the client is already subscribed to.","2.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_PUBSUB,PSUBSCRIBE_History,PSUBSCRIBE_tips,psubscribeCommand,-2,CMD_PUBSUB|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,.args=PSUBSCRIBE_Args},
-{"publish","Post a message to a channel","O(N+M) where N is the number of clients subscribed to the receiving channel and M is the total number of subscribed patterns (by any client).","2.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_PUBSUB,PUBLISH_History,PUBLISH_tips,publishCommand,3,CMD_PUBSUB|CMD_LOADING|CMD_STALE|CMD_FAST|CMD_MAY_REPLICATE|CMD_SENTINEL,0,.args=PUBLISH_Args},
-{"pubsub","A container for Pub/Sub commands","Depends on subcommand.","2.8.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_PUBSUB,PUBSUB_History,PUBSUB_tips,NULL,-2,0,0,.subcommands=PUBSUB_Subcommands},
-{"punsubscribe","Stop listening for messages posted to channels matching the given patterns","O(N+M) where N is the number of patterns the client is already subscribed and M is the number of total patterns subscribed in the system (by any client).","2.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_PUBSUB,PUNSUBSCRIBE_History,PUNSUBSCRIBE_tips,punsubscribeCommand,-1,CMD_PUBSUB|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,.args=PUNSUBSCRIBE_Args},
-{"spublish","Post a message to a shard channel","O(N) where N is the number of clients subscribed to the receiving shard channel.","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_PUBSUB,SPUBLISH_History,SPUBLISH_tips,spublishCommand,3,CMD_PUBSUB|CMD_LOADING|CMD_STALE|CMD_FAST|CMD_MAY_REPLICATE,0,{{NULL,CMD_KEY_NOT_KEY,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=SPUBLISH_Args},
-{"ssubscribe","Listen for messages published to the given shard channels","O(N) where N is the number of shard channels to subscribe to.","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_PUBSUB,SSUBSCRIBE_History,SSUBSCRIBE_tips,ssubscribeCommand,-2,CMD_PUBSUB|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,0,{{NULL,CMD_KEY_NOT_KEY,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={-1,1,0}}},.args=SSUBSCRIBE_Args},
-{"subscribe","Listen for messages published to the given channels","O(N) where N is the number of channels to subscribe to.","2.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_PUBSUB,SUBSCRIBE_History,SUBSCRIBE_tips,subscribeCommand,-2,CMD_PUBSUB|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,.args=SUBSCRIBE_Args},
-{"sunsubscribe","Stop listening for messages posted to the given shard channels","O(N) where N is the number of clients already subscribed to a shard channel.","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_PUBSUB,SUNSUBSCRIBE_History,SUNSUBSCRIBE_tips,sunsubscribeCommand,-1,CMD_PUBSUB|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,0,{{NULL,CMD_KEY_NOT_KEY,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={-1,1,0}}},.args=SUNSUBSCRIBE_Args},
-{"unsubscribe","Stop listening for messages posted to the given channels","O(N) where N is the number of clients already subscribed to a channel.","2.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_PUBSUB,UNSUBSCRIBE_History,UNSUBSCRIBE_tips,unsubscribeCommand,-1,CMD_PUBSUB|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,.args=UNSUBSCRIBE_Args},
-/* scripting */
-{"eval","Execute a Lua script server side","Depends on the script that is executed.","2.6.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SCRIPTING,EVAL_History,EVAL_tips,evalCommand,-3,CMD_NOSCRIPT|CMD_SKIP_MONITOR|CMD_MAY_REPLICATE|CMD_NO_MANDATORY_KEYS|CMD_STALE,ACL_CATEGORY_SCRIPTING,{{"We cannot tell how the keys will be used so we assume the worst, RW and UPDATE",CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_KEYNUM,.fk.keynum={0,1,1}}},evalGetKeys,.args=EVAL_Args},
-{"evalsha","Execute a Lua script server side","Depends on the script that is executed.","2.6.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SCRIPTING,EVALSHA_History,EVALSHA_tips,evalShaCommand,-3,CMD_NOSCRIPT|CMD_SKIP_MONITOR|CMD_MAY_REPLICATE|CMD_NO_MANDATORY_KEYS|CMD_STALE,ACL_CATEGORY_SCRIPTING,{{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_KEYNUM,.fk.keynum={0,1,1}}},evalGetKeys,.args=EVALSHA_Args},
-{"evalsha_ro","Execute a read-only Lua script server side","Depends on the script that is executed.","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SCRIPTING,EVALSHA_RO_History,EVALSHA_RO_tips,evalShaRoCommand,-3,CMD_NOSCRIPT|CMD_SKIP_MONITOR|CMD_NO_MANDATORY_KEYS|CMD_STALE|CMD_READONLY,ACL_CATEGORY_SCRIPTING,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_KEYNUM,.fk.keynum={0,1,1}}},evalGetKeys,.args=EVALSHA_RO_Args},
-{"eval_ro","Execute a read-only Lua script server side","Depends on the script that is executed.","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SCRIPTING,EVAL_RO_History,EVAL_RO_tips,evalRoCommand,-3,CMD_NOSCRIPT|CMD_SKIP_MONITOR|CMD_NO_MANDATORY_KEYS|CMD_STALE|CMD_READONLY,ACL_CATEGORY_SCRIPTING,{{"We cannot tell how the keys will be used so we assume the worst, RO and ACCESS",CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_KEYNUM,.fk.keynum={0,1,1}}},evalGetKeys,.args=EVAL_RO_Args},
-{"fcall","Invoke a function","Depends on the function that is executed.","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SCRIPTING,FCALL_History,FCALL_tips,fcallCommand,-3,CMD_NOSCRIPT|CMD_SKIP_MONITOR|CMD_MAY_REPLICATE|CMD_NO_MANDATORY_KEYS|CMD_STALE,ACL_CATEGORY_SCRIPTING,{{"We cannot tell how the keys will be used so we assume the worst, RW and UPDATE",CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_KEYNUM,.fk.keynum={0,1,1}}},functionGetKeys,.args=FCALL_Args},
-{"fcall_ro","Invoke a read-only function","Depends on the function that is executed.","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SCRIPTING,FCALL_RO_History,FCALL_RO_tips,fcallroCommand,-3,CMD_NOSCRIPT|CMD_SKIP_MONITOR|CMD_NO_MANDATORY_KEYS|CMD_STALE|CMD_READONLY,ACL_CATEGORY_SCRIPTING,{{"We cannot tell how the keys will be used so we assume the worst, RO and ACCESS",CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_KEYNUM,.fk.keynum={0,1,1}}},functionGetKeys,.args=FCALL_RO_Args},
-{"function","A container for function commands","Depends on subcommand.","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SCRIPTING,FUNCTION_History,FUNCTION_tips,NULL,-2,0,0,.subcommands=FUNCTION_Subcommands},
-{"script","A container for Lua scripts management commands","Depends on subcommand.","2.6.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SCRIPTING,SCRIPT_History,SCRIPT_tips,NULL,-2,0,0,.subcommands=SCRIPT_Subcommands},
-/* sentinel */
-{"sentinel","A container for Sentinel commands","Depends on subcommand.","2.8.4",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SENTINEL,SENTINEL_History,SENTINEL_tips,NULL,-2,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,.subcommands=SENTINEL_Subcommands},
-/* server */
-{"acl","A container for Access List Control commands ","Depends on subcommand.","6.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,ACL_History,ACL_tips,NULL,-2,CMD_SENTINEL,0,.subcommands=ACL_Subcommands},
-{"bgrewriteaof","Asynchronously rewrite the append-only file","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,BGREWRITEAOF_History,BGREWRITEAOF_tips,bgrewriteaofCommand,1,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_NOSCRIPT,0},
-{"bgsave","Asynchronously save the dataset to disk","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,BGSAVE_History,BGSAVE_tips,bgsaveCommand,-1,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_NOSCRIPT,0,.args=BGSAVE_Args},
-{"command","Get array of Redis command details","O(N) where N is the total number of Redis commands","2.8.13",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,COMMAND_History,COMMAND_tips,commandCommand,-1,CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,.subcommands=COMMAND_Subcommands},
-{"config","A container for server configuration commands","Depends on subcommand.","2.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,CONFIG_History,CONFIG_tips,NULL,-2,0,0,.subcommands=CONFIG_Subcommands},
-{"dbsize","Return the number of keys in the selected database","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,DBSIZE_History,DBSIZE_tips,dbsizeCommand,1,CMD_READONLY|CMD_FAST,ACL_CATEGORY_KEYSPACE},
-{"debug","A container for debugging commands","Depends on subcommand.","1.0.0",CMD_DOC_SYSCMD,NULL,NULL,COMMAND_GROUP_SERVER,DEBUG_History,DEBUG_tips,debugCommand,-2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_PROTECTED,0},
-{"failover","Start a coordinated failover between this server and one of its replicas.","O(1)","6.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,FAILOVER_History,FAILOVER_tips,failoverCommand,-1,CMD_ADMIN|CMD_NOSCRIPT|CMD_STALE,0,.args=FAILOVER_Args},
-{"flushall","Remove all keys from all databases","O(N) where N is the total number of keys in all databases","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,FLUSHALL_History,FLUSHALL_tips,flushallCommand,-1,CMD_WRITE,ACL_CATEGORY_KEYSPACE|ACL_CATEGORY_DANGEROUS,.args=FLUSHALL_Args},
-{"flushdb","Remove all keys from the current database","O(N) where N is the number of keys in the selected database","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,FLUSHDB_History,FLUSHDB_tips,flushdbCommand,-1,CMD_WRITE,ACL_CATEGORY_KEYSPACE|ACL_CATEGORY_DANGEROUS,.args=FLUSHDB_Args},
-{"info","Get information and statistics about the server","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,INFO_History,INFO_tips,infoCommand,-1,CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_DANGEROUS,.args=INFO_Args},
-{"lastsave","Get the UNIX time stamp of the last successful save to disk","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,LASTSAVE_History,LASTSAVE_tips,lastsaveCommand,1,CMD_LOADING|CMD_STALE|CMD_FAST,ACL_CATEGORY_ADMIN|ACL_CATEGORY_DANGEROUS},
-{"latency","A container for latency diagnostics commands","Depends on subcommand.","2.8.13",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,LATENCY_History,LATENCY_tips,NULL,-2,0,0,.subcommands=LATENCY_Subcommands},
-{"lolwut","Display some computer art and the Redis version",NULL,"5.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,LOLWUT_History,LOLWUT_tips,lolwutCommand,-1,CMD_READONLY|CMD_FAST,0,.args=LOLWUT_Args},
-{"memory","A container for memory diagnostics commands","Depends on subcommand.","4.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,MEMORY_History,MEMORY_tips,NULL,-2,0,0,.subcommands=MEMORY_Subcommands},
-{"module","A container for module commands","Depends on subcommand.","4.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,MODULE_History,MODULE_tips,NULL,-2,0,0,.subcommands=MODULE_Subcommands},
-{"monitor","Listen for all requests received by the server in real time",NULL,"1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,MONITOR_History,MONITOR_tips,monitorCommand,1,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,0},
-{"psync","Internal command used for replication",NULL,"2.8.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,PSYNC_History,PSYNC_tips,syncCommand,-3,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_NO_MULTI|CMD_NOSCRIPT,0,.args=PSYNC_Args},
-{"replconf","An internal command for configuring the replication stream","O(1)","3.0.0",CMD_DOC_SYSCMD,NULL,NULL,COMMAND_GROUP_SERVER,REPLCONF_History,REPLCONF_tips,replconfCommand,-1,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_ALLOW_BUSY,0},
-{"replicaof","Make the server a replica of another instance, or promote it as master.","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,REPLICAOF_History,REPLICAOF_tips,replicaofCommand,3,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_NOSCRIPT|CMD_STALE,0,.args=REPLICAOF_Args},
-{"restore-asking","An internal command for migrating keys in a cluster","O(1) to create the new key and additional O(N*M) to reconstruct the serialized value, where N is the number of Redis objects composing the value and M their average size. For small string values the time complexity is thus O(1)+O(1*M) where M is small, so simply O(1). However for sorted set values the complexity is O(N*M*log(N)) because inserting values into sorted sets is O(log(N)).","3.0.0",CMD_DOC_SYSCMD,NULL,NULL,COMMAND_GROUP_SERVER,RESTORE_ASKING_History,RESTORE_ASKING_tips,restoreCommand,-4,CMD_WRITE|CMD_DENYOOM|CMD_ASKING,ACL_CATEGORY_KEYSPACE|ACL_CATEGORY_DANGEROUS,{{NULL,CMD_KEY_OW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=RESTORE_ASKING_Args},
-{"role","Return the role of the instance in the context of replication","O(1)","2.8.12",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,ROLE_History,ROLE_tips,roleCommand,1,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_FAST|CMD_SENTINEL,ACL_CATEGORY_ADMIN|ACL_CATEGORY_DANGEROUS},
-{"save","Synchronously save the dataset to disk","O(N) where N is the total number of keys in all databases","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,SAVE_History,SAVE_tips,saveCommand,1,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_NOSCRIPT|CMD_NO_MULTI,0},
-{"shutdown","Synchronously save the dataset to disk and then shut down the server","O(N) when saving, where N is the total number of keys in all databases when saving data, otherwise O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,SHUTDOWN_History,SHUTDOWN_tips,shutdownCommand,-1,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_NO_MULTI|CMD_SENTINEL|CMD_ALLOW_BUSY,0,.args=SHUTDOWN_Args},
-{"slaveof","Make the server a replica of another instance, or promote it as master.","O(1)","1.0.0",CMD_DOC_DEPRECATED,"`REPLICAOF`","5.0.0",COMMAND_GROUP_SERVER,SLAVEOF_History,SLAVEOF_tips,replicaofCommand,3,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_NOSCRIPT|CMD_STALE,0,.args=SLAVEOF_Args},
-{"slowlog","A container for slow log commands","Depends on subcommand.","2.2.12",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,SLOWLOG_History,SLOWLOG_tips,NULL,-2,0,0,.subcommands=SLOWLOG_Subcommands},
-{"swapdb","Swaps two Redis databases","O(N) where N is the count of clients watching or blocking on keys from both databases.","4.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,SWAPDB_History,SWAPDB_tips,swapdbCommand,3,CMD_WRITE|CMD_FAST,ACL_CATEGORY_KEYSPACE|ACL_CATEGORY_DANGEROUS,.args=SWAPDB_Args},
-{"sync","Internal command used for replication",NULL,"1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,SYNC_History,SYNC_tips,syncCommand,1,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_NO_MULTI|CMD_NOSCRIPT,0},
-{"time","Return the current server time","O(1)","2.6.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SERVER,TIME_History,TIME_tips,timeCommand,1,CMD_LOADING|CMD_STALE|CMD_FAST,0},
-/* set */
-{"sadd","Add one or more members to a set","O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SET,SADD_History,SADD_tips,saddCommand,-3,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_SET,{{NULL,CMD_KEY_RW|CMD_KEY_INSERT,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=SADD_Args},
-{"scard","Get the number of members in a set","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SET,SCARD_History,SCARD_tips,scardCommand,2,CMD_READONLY|CMD_FAST,ACL_CATEGORY_SET,{{NULL,CMD_KEY_RO,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=SCARD_Args},
-{"sdiff","Subtract multiple sets","O(N) where N is the total number of elements in all given sets.","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SET,SDIFF_History,SDIFF_tips,sdiffCommand,-2,CMD_READONLY,ACL_CATEGORY_SET,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={-1,1,0}}},.args=SDIFF_Args},
-{"sdiffstore","Subtract multiple sets and store the resulting set in a key","O(N) where N is the total number of elements in all given sets.","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SET,SDIFFSTORE_History,SDIFFSTORE_tips,sdiffstoreCommand,-3,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_SET,{{NULL,CMD_KEY_OW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}},{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={-1,1,0}}},.args=SDIFFSTORE_Args},
-{"sinter","Intersect multiple sets","O(N*M) worst case where N is the cardinality of the smallest set and M is the number of sets.","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SET,SINTER_History,SINTER_tips,sinterCommand,-2,CMD_READONLY,ACL_CATEGORY_SET,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={-1,1,0}}},.args=SINTER_Args},
-{"sintercard","Intersect multiple sets and return the cardinality of the result","O(N*M) worst case where N is the cardinality of the smallest set and M is the number of sets.","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SET,SINTERCARD_History,SINTERCARD_tips,sinterCardCommand,-3,CMD_READONLY,ACL_CATEGORY_SET,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_KEYNUM,.fk.keynum={0,1,1}}},sintercardGetKeys,.args=SINTERCARD_Args},
-{"sinterstore","Intersect multiple sets and store the resulting set in a key","O(N*M) worst case where N is the cardinality of the smallest set and M is the number of sets.","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SET,SINTERSTORE_History,SINTERSTORE_tips,sinterstoreCommand,-3,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_SET,{{NULL,CMD_KEY_RW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}},{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={-1,1,0}}},.args=SINTERSTORE_Args},
-{"sismember","Determine if a given value is a member of a set","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SET,SISMEMBER_History,SISMEMBER_tips,sismemberCommand,3,CMD_READONLY|CMD_FAST,ACL_CATEGORY_SET,{{NULL,CMD_KEY_RO,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=SISMEMBER_Args},
-{"smembers","Get all the members in a set","O(N) where N is the set cardinality.","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SET,SMEMBERS_History,SMEMBERS_tips,sinterCommand,2,CMD_READONLY,ACL_CATEGORY_SET,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=SMEMBERS_Args},
-{"smismember","Returns the membership associated with the given elements for a set","O(N) where N is the number of elements being checked for membership","6.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SET,SMISMEMBER_History,SMISMEMBER_tips,smismemberCommand,-3,CMD_READONLY|CMD_FAST,ACL_CATEGORY_SET,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=SMISMEMBER_Args},
-{"smove","Move a member from one set to another","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SET,SMOVE_History,SMOVE_tips,smoveCommand,4,CMD_WRITE|CMD_FAST,ACL_CATEGORY_SET,{{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}},{NULL,CMD_KEY_RW|CMD_KEY_INSERT,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=SMOVE_Args},
-{"spop","Remove and return one or multiple random members from a set","Without the count argument O(1), otherwise O(N) where N is the value of the passed count.","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SET,SPOP_History,SPOP_tips,spopCommand,-2,CMD_WRITE|CMD_FAST,ACL_CATEGORY_SET,{{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=SPOP_Args},
-{"srandmember","Get one or multiple random members from a set","Without the count argument O(1), otherwise O(N) where N is the absolute value of the passed count.","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SET,SRANDMEMBER_History,SRANDMEMBER_tips,srandmemberCommand,-2,CMD_READONLY,ACL_CATEGORY_SET,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=SRANDMEMBER_Args},
-{"srem","Remove one or more members from a set","O(N) where N is the number of members to be removed.","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SET,SREM_History,SREM_tips,sremCommand,-3,CMD_WRITE|CMD_FAST,ACL_CATEGORY_SET,{{NULL,CMD_KEY_RW|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=SREM_Args},
-{"sscan","Incrementally iterate Set elements","O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.","2.8.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SET,SSCAN_History,SSCAN_tips,sscanCommand,-3,CMD_READONLY,ACL_CATEGORY_SET,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=SSCAN_Args},
-{"sunion","Add multiple sets","O(N) where N is the total number of elements in all given sets.","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SET,SUNION_History,SUNION_tips,sunionCommand,-2,CMD_READONLY,ACL_CATEGORY_SET,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={-1,1,0}}},.args=SUNION_Args},
-{"sunionstore","Add multiple sets and store the resulting set in a key","O(N) where N is the total number of elements in all given sets.","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SET,SUNIONSTORE_History,SUNIONSTORE_tips,sunionstoreCommand,-3,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_SET,{{NULL,CMD_KEY_OW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}},{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={-1,1,0}}},.args=SUNIONSTORE_Args},
-/* sorted_set */
-{"bzmpop","Remove and return members with scores in a sorted set or block until one is available","O(K) + O(M*log(N)) where K is the number of provided keys, N being the number of elements in the sorted set, and M being the number of elements popped.","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SORTED_SET,BZMPOP_History,BZMPOP_tips,bzmpopCommand,-5,CMD_WRITE|CMD_BLOCKING,ACL_CATEGORY_SORTEDSET,{{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_KEYNUM,.fk.keynum={0,1,1}}},blmpopGetKeys,.args=BZMPOP_Args},
-{"bzpopmax","Remove and return the member with the highest score from one or more sorted sets, or block until one is available","O(log(N)) with N being the number of elements in the sorted set.","5.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SORTED_SET,BZPOPMAX_History,BZPOPMAX_tips,bzpopmaxCommand,-3,CMD_WRITE|CMD_FAST|CMD_BLOCKING,ACL_CATEGORY_SORTEDSET,{{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={-2,1,0}}},.args=BZPOPMAX_Args},
-{"bzpopmin","Remove and return the member with the lowest score from one or more sorted sets, or block until one is available","O(log(N)) with N being the number of elements in the sorted set.","5.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SORTED_SET,BZPOPMIN_History,BZPOPMIN_tips,bzpopminCommand,-3,CMD_WRITE|CMD_FAST|CMD_BLOCKING,ACL_CATEGORY_SORTEDSET,{{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={-2,1,0}}},.args=BZPOPMIN_Args},
-{"zadd","Add one or more members to a sorted set, or update its score if it already exists","O(log(N)) for each item added, where N is the number of elements in the sorted set.","1.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SORTED_SET,ZADD_History,ZADD_tips,zaddCommand,-4,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_SORTEDSET,{{NULL,CMD_KEY_RW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=ZADD_Args},
-{"zcard","Get the number of members in a sorted set","O(1)","1.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SORTED_SET,ZCARD_History,ZCARD_tips,zcardCommand,2,CMD_READONLY|CMD_FAST,ACL_CATEGORY_SORTEDSET,{{NULL,CMD_KEY_RO,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=ZCARD_Args},
-{"zcount","Count the members in a sorted set with scores within the given values","O(log(N)) with N being the number of elements in the sorted set.","2.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SORTED_SET,ZCOUNT_History,ZCOUNT_tips,zcountCommand,4,CMD_READONLY|CMD_FAST,ACL_CATEGORY_SORTEDSET,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=ZCOUNT_Args},
-{"zdiff","Subtract multiple sorted sets","O(L + (N-K)log(N)) worst case where L is the total number of elements in all the sets, N is the size of the first set, and K is the size of the result set.","6.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SORTED_SET,ZDIFF_History,ZDIFF_tips,zdiffCommand,-3,CMD_READONLY,ACL_CATEGORY_SORTEDSET,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_KEYNUM,.fk.keynum={0,1,1}}},zunionInterDiffGetKeys,.args=ZDIFF_Args},
-{"zdiffstore","Subtract multiple sorted sets and store the resulting sorted set in a new key","O(L + (N-K)log(N)) worst case where L is the total number of elements in all the sets, N is the size of the first set, and K is the size of the result set.","6.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SORTED_SET,ZDIFFSTORE_History,ZDIFFSTORE_tips,zdiffstoreCommand,-4,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_SORTEDSET,{{NULL,CMD_KEY_OW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}},{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_KEYNUM,.fk.keynum={0,1,1}}},zunionInterDiffStoreGetKeys,.args=ZDIFFSTORE_Args},
-{"zincrby","Increment the score of a member in a sorted set","O(log(N)) where N is the number of elements in the sorted set.","1.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SORTED_SET,ZINCRBY_History,ZINCRBY_tips,zincrbyCommand,4,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_SORTEDSET,{{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=ZINCRBY_Args},
-{"zinter","Intersect multiple sorted sets","O(N*K)+O(M*log(M)) worst case with N being the smallest input sorted set, K being the number of input sorted sets and M being the number of elements in the resulting sorted set.","6.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SORTED_SET,ZINTER_History,ZINTER_tips,zinterCommand,-3,CMD_READONLY,ACL_CATEGORY_SORTEDSET,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_KEYNUM,.fk.keynum={0,1,1}}},zunionInterDiffGetKeys,.args=ZINTER_Args},
-{"zintercard","Intersect multiple sorted sets and return the cardinality of the result","O(N*K) worst case with N being the smallest input sorted set, K being the number of input sorted sets.","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SORTED_SET,ZINTERCARD_History,ZINTERCARD_tips,zinterCardCommand,-3,CMD_READONLY,ACL_CATEGORY_SORTEDSET,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_KEYNUM,.fk.keynum={0,1,1}}},zunionInterDiffGetKeys,.args=ZINTERCARD_Args},
-{"zinterstore","Intersect multiple sorted sets and store the resulting sorted set in a new key","O(N*K)+O(M*log(M)) worst case with N being the smallest input sorted set, K being the number of input sorted sets and M being the number of elements in the resulting sorted set.","2.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SORTED_SET,ZINTERSTORE_History,ZINTERSTORE_tips,zinterstoreCommand,-4,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_SORTEDSET,{{NULL,CMD_KEY_OW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}},{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_KEYNUM,.fk.keynum={0,1,1}}},zunionInterDiffStoreGetKeys,.args=ZINTERSTORE_Args},
-{"zlexcount","Count the number of members in a sorted set between a given lexicographical range","O(log(N)) with N being the number of elements in the sorted set.","2.8.9",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SORTED_SET,ZLEXCOUNT_History,ZLEXCOUNT_tips,zlexcountCommand,4,CMD_READONLY|CMD_FAST,ACL_CATEGORY_SORTEDSET,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=ZLEXCOUNT_Args},
-{"zmpop","Remove and return members with scores in a sorted set","O(K) + O(M*log(N)) where K is the number of provided keys, N being the number of elements in the sorted set, and M being the number of elements popped.","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SORTED_SET,ZMPOP_History,ZMPOP_tips,zmpopCommand,-4,CMD_WRITE,ACL_CATEGORY_SORTEDSET,{{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_KEYNUM,.fk.keynum={0,1,1}}},zmpopGetKeys,.args=ZMPOP_Args},
-{"zmscore","Get the score associated with the given members in a sorted set","O(N) where N is the number of members being requested.","6.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SORTED_SET,ZMSCORE_History,ZMSCORE_tips,zmscoreCommand,-3,CMD_READONLY|CMD_FAST,ACL_CATEGORY_SORTEDSET,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=ZMSCORE_Args},
-{"zpopmax","Remove and return members with the highest scores in a sorted set","O(log(N)*M) with N being the number of elements in the sorted set, and M being the number of elements popped.","5.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SORTED_SET,ZPOPMAX_History,ZPOPMAX_tips,zpopmaxCommand,-2,CMD_WRITE|CMD_FAST,ACL_CATEGORY_SORTEDSET,{{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=ZPOPMAX_Args},
-{"zpopmin","Remove and return members with the lowest scores in a sorted set","O(log(N)*M) with N being the number of elements in the sorted set, and M being the number of elements popped.","5.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SORTED_SET,ZPOPMIN_History,ZPOPMIN_tips,zpopminCommand,-2,CMD_WRITE|CMD_FAST,ACL_CATEGORY_SORTEDSET,{{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=ZPOPMIN_Args},
-{"zrandmember","Get one or multiple random elements from a sorted set","O(N) where N is the number of elements returned","6.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SORTED_SET,ZRANDMEMBER_History,ZRANDMEMBER_tips,zrandmemberCommand,-2,CMD_READONLY,ACL_CATEGORY_SORTEDSET,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=ZRANDMEMBER_Args},
-{"zrange","Return a range of members in a sorted set","O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements returned.","1.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SORTED_SET,ZRANGE_History,ZRANGE_tips,zrangeCommand,-4,CMD_READONLY,ACL_CATEGORY_SORTEDSET,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=ZRANGE_Args},
-{"zrangebylex","Return a range of members in a sorted set, by lexicographical range","O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).","2.8.9",CMD_DOC_DEPRECATED,"`ZRANGE` with the `BYLEX` argument","6.2.0",COMMAND_GROUP_SORTED_SET,ZRANGEBYLEX_History,ZRANGEBYLEX_tips,zrangebylexCommand,-4,CMD_READONLY,ACL_CATEGORY_SORTEDSET,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=ZRANGEBYLEX_Args},
-{"zrangebyscore","Return a range of members in a sorted set, by score","O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).","1.0.5",CMD_DOC_DEPRECATED,"`ZRANGE` with the `BYSCORE` argument","6.2.0",COMMAND_GROUP_SORTED_SET,ZRANGEBYSCORE_History,ZRANGEBYSCORE_tips,zrangebyscoreCommand,-4,CMD_READONLY,ACL_CATEGORY_SORTEDSET,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=ZRANGEBYSCORE_Args},
-{"zrangestore","Store a range of members from sorted set into another key","O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements stored into the destination key.","6.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SORTED_SET,ZRANGESTORE_History,ZRANGESTORE_tips,zrangestoreCommand,-5,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_SORTEDSET,{{NULL,CMD_KEY_OW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}},{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=ZRANGESTORE_Args},
-{"zrank","Determine the index of a member in a sorted set","O(log(N))","2.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SORTED_SET,ZRANK_History,ZRANK_tips,zrankCommand,-3,CMD_READONLY|CMD_FAST,ACL_CATEGORY_SORTEDSET,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=ZRANK_Args},
-{"zrem","Remove one or more members from a sorted set","O(M*log(N)) with N being the number of elements in the sorted set and M the number of elements to be removed.","1.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SORTED_SET,ZREM_History,ZREM_tips,zremCommand,-3,CMD_WRITE|CMD_FAST,ACL_CATEGORY_SORTEDSET,{{NULL,CMD_KEY_RW|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=ZREM_Args},
-{"zremrangebylex","Remove all members in a sorted set between the given lexicographical range","O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements removed by the operation.","2.8.9",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SORTED_SET,ZREMRANGEBYLEX_History,ZREMRANGEBYLEX_tips,zremrangebylexCommand,4,CMD_WRITE,ACL_CATEGORY_SORTEDSET,{{NULL,CMD_KEY_RW|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=ZREMRANGEBYLEX_Args},
-{"zremrangebyrank","Remove all members in a sorted set within the given indexes","O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements removed by the operation.","2.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SORTED_SET,ZREMRANGEBYRANK_History,ZREMRANGEBYRANK_tips,zremrangebyrankCommand,4,CMD_WRITE,ACL_CATEGORY_SORTEDSET,{{NULL,CMD_KEY_RW|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=ZREMRANGEBYRANK_Args},
-{"zremrangebyscore","Remove all members in a sorted set within the given scores","O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements removed by the operation.","1.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SORTED_SET,ZREMRANGEBYSCORE_History,ZREMRANGEBYSCORE_tips,zremrangebyscoreCommand,4,CMD_WRITE,ACL_CATEGORY_SORTEDSET,{{NULL,CMD_KEY_RW|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=ZREMRANGEBYSCORE_Args},
-{"zrevrange","Return a range of members in a sorted set, by index, with scores ordered from high to low","O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements returned.","1.2.0",CMD_DOC_DEPRECATED,"`ZRANGE` with the `REV` argument","6.2.0",COMMAND_GROUP_SORTED_SET,ZREVRANGE_History,ZREVRANGE_tips,zrevrangeCommand,-4,CMD_READONLY,ACL_CATEGORY_SORTEDSET,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=ZREVRANGE_Args},
-{"zrevrangebylex","Return a range of members in a sorted set, by lexicographical range, ordered from higher to lower strings.","O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).","2.8.9",CMD_DOC_DEPRECATED,"`ZRANGE` with the `REV` and `BYLEX` arguments","6.2.0",COMMAND_GROUP_SORTED_SET,ZREVRANGEBYLEX_History,ZREVRANGEBYLEX_tips,zrevrangebylexCommand,-4,CMD_READONLY,ACL_CATEGORY_SORTEDSET,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=ZREVRANGEBYLEX_Args},
-{"zrevrangebyscore","Return a range of members in a sorted set, by score, with scores ordered from high to low","O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).","2.2.0",CMD_DOC_DEPRECATED,"`ZRANGE` with the `REV` and `BYSCORE` arguments","6.2.0",COMMAND_GROUP_SORTED_SET,ZREVRANGEBYSCORE_History,ZREVRANGEBYSCORE_tips,zrevrangebyscoreCommand,-4,CMD_READONLY,ACL_CATEGORY_SORTEDSET,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=ZREVRANGEBYSCORE_Args},
-{"zrevrank","Determine the index of a member in a sorted set, with scores ordered from high to low","O(log(N))","2.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SORTED_SET,ZREVRANK_History,ZREVRANK_tips,zrevrankCommand,-3,CMD_READONLY|CMD_FAST,ACL_CATEGORY_SORTEDSET,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=ZREVRANK_Args},
-{"zscan","Incrementally iterate sorted sets elements and associated scores","O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.","2.8.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SORTED_SET,ZSCAN_History,ZSCAN_tips,zscanCommand,-3,CMD_READONLY,ACL_CATEGORY_SORTEDSET,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=ZSCAN_Args},
-{"zscore","Get the score associated with the given member in a sorted set","O(1)","1.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SORTED_SET,ZSCORE_History,ZSCORE_tips,zscoreCommand,3,CMD_READONLY|CMD_FAST,ACL_CATEGORY_SORTEDSET,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=ZSCORE_Args},
-{"zunion","Add multiple sorted sets","O(N)+O(M*log(M)) with N being the sum of the sizes of the input sorted sets, and M being the number of elements in the resulting sorted set.","6.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SORTED_SET,ZUNION_History,ZUNION_tips,zunionCommand,-3,CMD_READONLY,ACL_CATEGORY_SORTEDSET,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_KEYNUM,.fk.keynum={0,1,1}}},zunionInterDiffGetKeys,.args=ZUNION_Args},
-{"zunionstore","Add multiple sorted sets and store the resulting sorted set in a new key","O(N)+O(M log(M)) with N being the sum of the sizes of the input sorted sets, and M being the number of elements in the resulting sorted set.","2.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_SORTED_SET,ZUNIONSTORE_History,ZUNIONSTORE_tips,zunionstoreCommand,-4,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_SORTEDSET,{{NULL,CMD_KEY_OW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}},{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_KEYNUM,.fk.keynum={0,1,1}}},zunionInterDiffStoreGetKeys,.args=ZUNIONSTORE_Args},
-/* stream */
-{"xack","Marks a pending message as correctly processed, effectively removing it from the pending entries list of the consumer group. Return value of the command is the number of messages successfully acknowledged, that is, the IDs we were actually able to resolve in the PEL.","O(1) for each message ID processed.","5.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_STREAM,XACK_History,XACK_tips,xackCommand,-4,CMD_WRITE|CMD_FAST,ACL_CATEGORY_STREAM,{{NULL,CMD_KEY_RW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=XACK_Args},
-{"xadd","Appends a new entry to a stream","O(1) when adding a new entry, O(N) when trimming where N being the number of entries evicted.","5.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_STREAM,XADD_History,XADD_tips,xaddCommand,-5,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_STREAM,{{"UPDATE instead of INSERT because of the optional trimming feature",CMD_KEY_RW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=XADD_Args},
-{"xautoclaim","Changes (or acquires) ownership of messages in a consumer group, as if the messages were delivered to the specified consumer.","O(1) if COUNT is small.","6.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_STREAM,XAUTOCLAIM_History,XAUTOCLAIM_tips,xautoclaimCommand,-6,CMD_WRITE|CMD_FAST,ACL_CATEGORY_STREAM,{{NULL,CMD_KEY_RW|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=XAUTOCLAIM_Args},
-{"xclaim","Changes (or acquires) ownership of a message in a consumer group, as if the message was delivered to the specified consumer.","O(log N) with N being the number of messages in the PEL of the consumer group.","5.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_STREAM,XCLAIM_History,XCLAIM_tips,xclaimCommand,-6,CMD_WRITE|CMD_FAST,ACL_CATEGORY_STREAM,{{NULL,CMD_KEY_RW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=XCLAIM_Args},
-{"xdel","Removes the specified entries from the stream. Returns the number of items actually deleted, that may be different from the number of IDs passed in case certain IDs do not exist.","O(1) for each single item to delete in the stream, regardless of the stream size.","5.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_STREAM,XDEL_History,XDEL_tips,xdelCommand,-3,CMD_WRITE|CMD_FAST,ACL_CATEGORY_STREAM,{{NULL,CMD_KEY_RW|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=XDEL_Args},
-{"xgroup","A container for consumer groups commands","Depends on subcommand.","5.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_STREAM,XGROUP_History,XGROUP_tips,NULL,-2,0,0,.subcommands=XGROUP_Subcommands},
-{"xinfo","A container for stream introspection commands","Depends on subcommand.","5.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_STREAM,XINFO_History,XINFO_tips,NULL,-2,0,0,.subcommands=XINFO_Subcommands},
-{"xlen","Return the number of entries in a stream","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_STREAM,XLEN_History,XLEN_tips,xlenCommand,2,CMD_READONLY|CMD_FAST,ACL_CATEGORY_STREAM,{{NULL,CMD_KEY_RO,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=XLEN_Args},
-{"xpending","Return information and entries from a stream consumer group pending entries list, that are messages fetched but never acknowledged.","O(N) with N being the number of elements returned, so asking for a small fixed number of entries per call is O(1). O(M), where M is the total number of entries scanned when used with the IDLE filter. When the command returns just the summary and the list of consumers is small, it runs in O(1) time; otherwise, an additional O(N) time for iterating every consumer.","5.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_STREAM,XPENDING_History,XPENDING_tips,xpendingCommand,-3,CMD_READONLY,ACL_CATEGORY_STREAM,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=XPENDING_Args},
-{"xrange","Return a range of elements in a stream, with IDs matching the specified IDs interval","O(N) with N being the number of elements being returned. If N is constant (e.g. always asking for the first 10 elements with COUNT), you can consider it O(1).","5.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_STREAM,XRANGE_History,XRANGE_tips,xrangeCommand,-4,CMD_READONLY,ACL_CATEGORY_STREAM,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=XRANGE_Args},
-{"xread","Return never seen elements in multiple streams, with IDs greater than the ones reported by the caller for each stream. Can block.","For each stream mentioned: O(N) with N being the number of elements being returned, it means that XREAD-ing with a fixed COUNT is O(1). Note that when the BLOCK option is used, XADD will pay O(M) time in order to serve the M clients blocked on the stream getting new data.","5.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_STREAM,XREAD_History,XREAD_tips,xreadCommand,-4,CMD_BLOCKING|CMD_READONLY|CMD_BLOCKING,ACL_CATEGORY_STREAM,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_KEYWORD,.bs.keyword={"STREAMS",1},KSPEC_FK_RANGE,.fk.range={-1,1,2}}},xreadGetKeys,.args=XREAD_Args},
-{"xreadgroup","Return new entries from a stream using a consumer group, or access the history of the pending entries for a given consumer. Can block.","For each stream mentioned: O(M) with M being the number of elements returned. If M is constant (e.g. always asking for the first 10 elements with COUNT), you can consider it O(1). On the other side when XREADGROUP blocks, XADD will pay the O(N) time in order to serve the N clients blocked on the stream getting new data.","5.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_STREAM,XREADGROUP_History,XREADGROUP_tips,xreadCommand,-7,CMD_BLOCKING|CMD_WRITE,ACL_CATEGORY_STREAM,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_KEYWORD,.bs.keyword={"STREAMS",4},KSPEC_FK_RANGE,.fk.range={-1,1,2}}},xreadGetKeys,.args=XREADGROUP_Args},
-{"xrevrange","Return a range of elements in a stream, with IDs matching the specified IDs interval, in reverse order (from greater to smaller IDs) compared to XRANGE","O(N) with N being the number of elements returned. If N is constant (e.g. always asking for the first 10 elements with COUNT), you can consider it O(1).","5.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_STREAM,XREVRANGE_History,XREVRANGE_tips,xrevrangeCommand,-4,CMD_READONLY,ACL_CATEGORY_STREAM,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=XREVRANGE_Args},
-{"xsetid","An internal command for replicating stream values","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_STREAM,XSETID_History,XSETID_tips,xsetidCommand,-3,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_STREAM,{{NULL,CMD_KEY_RW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=XSETID_Args},
-{"xtrim","Trims the stream to (approximately if '~' is passed) a certain size","O(N), with N being the number of evicted entries. Constant times are very small however, since entries are organized in macro nodes containing multiple entries that can be released with a single deallocation.","5.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_STREAM,XTRIM_History,XTRIM_tips,xtrimCommand,-4,CMD_WRITE,ACL_CATEGORY_STREAM,{{NULL,CMD_KEY_RW|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=XTRIM_Args},
-/* string */
-{"append","Append a value to a key","O(1). The amortized time complexity is O(1) assuming the appended value is small and the already present value is of any size, since the dynamic string library used by Redis will double the free space available on every reallocation.","2.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_STRING,APPEND_History,APPEND_tips,appendCommand,3,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_STRING,{{NULL,CMD_KEY_RW|CMD_KEY_INSERT,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=APPEND_Args},
-{"decr","Decrement the integer value of a key by one","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_STRING,DECR_History,DECR_tips,decrCommand,2,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_STRING,{{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=DECR_Args},
-{"decrby","Decrement the integer value of a key by the given number","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_STRING,DECRBY_History,DECRBY_tips,decrbyCommand,3,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_STRING,{{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=DECRBY_Args},
-{"get","Get the value of a key","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_STRING,GET_History,GET_tips,getCommand,2,CMD_READONLY|CMD_FAST,ACL_CATEGORY_STRING,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=GET_Args},
-{"getdel","Get the value of a key and delete the key","O(1)","6.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_STRING,GETDEL_History,GETDEL_tips,getdelCommand,2,CMD_WRITE|CMD_FAST,ACL_CATEGORY_STRING,{{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=GETDEL_Args},
-{"getex","Get the value of a key and optionally set its expiration","O(1)","6.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_STRING,GETEX_History,GETEX_tips,getexCommand,-2,CMD_WRITE|CMD_FAST,ACL_CATEGORY_STRING,{{"RW and UPDATE because it changes the TTL",CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=GETEX_Args},
-{"getrange","Get a substring of the string stored at a key","O(N) where N is the length of the returned string. The complexity is ultimately determined by the returned length, but because creating a substring from an existing string is very cheap, it can be considered O(1) for small strings.","2.4.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_STRING,GETRANGE_History,GETRANGE_tips,getrangeCommand,4,CMD_READONLY,ACL_CATEGORY_STRING,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=GETRANGE_Args},
-{"getset","Set the string value of a key and return its old value","O(1)","1.0.0",CMD_DOC_DEPRECATED,"`SET` with the `!GET` argument","6.2.0",COMMAND_GROUP_STRING,GETSET_History,GETSET_tips,getsetCommand,3,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_STRING,{{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=GETSET_Args},
-{"incr","Increment the integer value of a key by one","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_STRING,INCR_History,INCR_tips,incrCommand,2,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_STRING,{{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=INCR_Args},
-{"incrby","Increment the integer value of a key by the given amount","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_STRING,INCRBY_History,INCRBY_tips,incrbyCommand,3,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_STRING,{{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=INCRBY_Args},
-{"incrbyfloat","Increment the float value of a key by the given amount","O(1)","2.6.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_STRING,INCRBYFLOAT_History,INCRBYFLOAT_tips,incrbyfloatCommand,3,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_STRING,{{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=INCRBYFLOAT_Args},
-{"lcs","Find longest common substring","O(N*M) where N and M are the lengths of s1 and s2, respectively","7.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_STRING,LCS_History,LCS_tips,lcsCommand,-3,CMD_READONLY,ACL_CATEGORY_STRING,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={1,1,0}}},.args=LCS_Args},
-{"mget","Get the values of all the given keys","O(N) where N is the number of keys to retrieve.","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_STRING,MGET_History,MGET_tips,mgetCommand,-2,CMD_READONLY|CMD_FAST,ACL_CATEGORY_STRING,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={-1,1,0}}},.args=MGET_Args},
-{"mset","Set multiple keys to multiple values","O(N) where N is the number of keys to set.","1.0.1",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_STRING,MSET_History,MSET_tips,msetCommand,-3,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_STRING,{{NULL,CMD_KEY_OW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={-1,2,0}}},.args=MSET_Args},
-{"msetnx","Set multiple keys to multiple values, only if none of the keys exist","O(N) where N is the number of keys to set.","1.0.1",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_STRING,MSETNX_History,MSETNX_tips,msetnxCommand,-3,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_STRING,{{NULL,CMD_KEY_OW|CMD_KEY_INSERT,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={-1,2,0}}},.args=MSETNX_Args},
-{"psetex","Set the value and expiration in milliseconds of a key","O(1)","2.6.0",CMD_DOC_DEPRECATED,"`SET` with the `PX` argument","2.6.12",COMMAND_GROUP_STRING,PSETEX_History,PSETEX_tips,psetexCommand,4,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_STRING,{{NULL,CMD_KEY_OW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=PSETEX_Args},
-{"set","Set the string value of a key","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_STRING,SET_History,SET_tips,setCommand,-3,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_STRING,{{"RW and ACCESS due to the optional `GET` argument",CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_UPDATE|CMD_KEY_VARIABLE_FLAGS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},setGetKeys,.args=SET_Args},
-{"setex","Set the value and expiration of a key","O(1)","2.0.0",CMD_DOC_DEPRECATED,"`SET` with the `EX` argument","2.6.12",COMMAND_GROUP_STRING,SETEX_History,SETEX_tips,setexCommand,4,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_STRING,{{NULL,CMD_KEY_OW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=SETEX_Args},
-{"setnx","Set the value of a key, only if the key does not exist","O(1)","1.0.0",CMD_DOC_DEPRECATED,"`SET` with the `NX` argument","2.6.12",COMMAND_GROUP_STRING,SETNX_History,SETNX_tips,setnxCommand,3,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_STRING,{{NULL,CMD_KEY_OW|CMD_KEY_INSERT,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=SETNX_Args},
-{"setrange","Overwrite part of a string at key starting at the specified offset","O(1), not counting the time taken to copy the new string in place. Usually, this string is very small so the amortized complexity is O(1). Otherwise, complexity is O(M) with M being the length of the value argument.","2.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_STRING,SETRANGE_History,SETRANGE_tips,setrangeCommand,4,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_STRING,{{NULL,CMD_KEY_RW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=SETRANGE_Args},
-{"strlen","Get the length of the value stored in a key","O(1)","2.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_STRING,STRLEN_History,STRLEN_tips,strlenCommand,2,CMD_READONLY|CMD_FAST,ACL_CATEGORY_STRING,{{NULL,CMD_KEY_RO,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=STRLEN_Args},
-{"substr","Get a substring of the string stored at a key","O(N) where N is the length of the returned string. The complexity is ultimately determined by the returned length, but because creating a substring from an existing string is very cheap, it can be considered O(1) for small strings.","1.0.0",CMD_DOC_DEPRECATED,"`GETRANGE`","2.0.0",COMMAND_GROUP_STRING,SUBSTR_History,SUBSTR_tips,getrangeCommand,4,CMD_READONLY,ACL_CATEGORY_STRING,{{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=SUBSTR_Args},
-/* transactions */
-{"discard","Discard all commands issued after MULTI","O(N), when N is the number of queued commands","2.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_TRANSACTIONS,DISCARD_History,DISCARD_tips,discardCommand,1,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_FAST|CMD_ALLOW_BUSY,ACL_CATEGORY_TRANSACTION},
-{"exec","Execute all commands issued after MULTI","Depends on commands in the transaction","1.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_TRANSACTIONS,EXEC_History,EXEC_tips,execCommand,1,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SKIP_SLOWLOG,ACL_CATEGORY_TRANSACTION},
-{"multi","Mark the start of a transaction block","O(1)","1.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_TRANSACTIONS,MULTI_History,MULTI_tips,multiCommand,1,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_FAST|CMD_ALLOW_BUSY,ACL_CATEGORY_TRANSACTION},
-{"unwatch","Forget about all watched keys","O(1)","2.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_TRANSACTIONS,UNWATCH_History,UNWATCH_tips,unwatchCommand,1,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_FAST|CMD_ALLOW_BUSY,ACL_CATEGORY_TRANSACTION},
-{"watch","Watch the given keys to determine execution of the MULTI/EXEC block","O(1) for every key.","2.2.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_TRANSACTIONS,WATCH_History,WATCH_tips,watchCommand,-2,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_FAST|CMD_ALLOW_BUSY,ACL_CATEGORY_TRANSACTION,{{NULL,CMD_KEY_RO,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={-1,1,0}}},.args=WATCH_Args},
-{0}
-};
+#ifdef LOG_REQ_RES
+#include "commands_with_reply_schema.def"
+#else
+#include "commands.def"
+#endif
diff --git a/src/commands.def b/src/commands.def
new file mode 100644
index 000000000..33e309bfb
--- /dev/null
+++ b/src/commands.def
@@ -0,0 +1,10843 @@
+/* Automatically generated by generate-command-code.py, do not edit. */
+
+
+/* We have fabulous commands from
+ * the fantastic
+ * Redis Command Table! */
+
+/* Must match redisCommandGroup */
+const char *COMMAND_GROUP_STR[] = {
+ "generic",
+ "string",
+ "list",
+ "set",
+ "sorted-set",
+ "hash",
+ "pubsub",
+ "transactions",
+ "connection",
+ "server",
+ "scripting",
+ "hyperloglog",
+ "cluster",
+ "sentinel",
+ "geo",
+ "stream",
+ "bitmap",
+ "module"
+};
+
+const char *commandGroupStr(int index) {
+ return COMMAND_GROUP_STR[index];
+}
+/********** BITCOUNT ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* BITCOUNT history */
+commandHistory BITCOUNT_History[] = {
+{"7.0.0","Added the `BYTE|BIT` option."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* BITCOUNT tips */
+#define BITCOUNT_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* BITCOUNT key specs */
+keySpec BITCOUNT_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* BITCOUNT range unit argument table */
+struct COMMAND_ARG BITCOUNT_range_unit_Subargs[] = {
+{MAKE_ARG("byte",ARG_TYPE_PURE_TOKEN,-1,"BYTE",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("bit",ARG_TYPE_PURE_TOKEN,-1,"BIT",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* BITCOUNT range argument table */
+struct COMMAND_ARG BITCOUNT_range_Subargs[] = {
+{MAKE_ARG("start",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("end",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("unit",ARG_TYPE_ONEOF,-1,NULL,NULL,"7.0.0",CMD_ARG_OPTIONAL,2,NULL),.subargs=BITCOUNT_range_unit_Subargs},
+};
+
+/* BITCOUNT argument table */
+struct COMMAND_ARG BITCOUNT_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("range",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,3,NULL),.subargs=BITCOUNT_range_Subargs},
+};
+
+/********** BITFIELD ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* BITFIELD history */
+#define BITFIELD_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* BITFIELD tips */
+#define BITFIELD_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* BITFIELD key specs */
+keySpec BITFIELD_Keyspecs[1] = {
+{"This command allows both access and modification of the key",CMD_KEY_RW|CMD_KEY_UPDATE|CMD_KEY_ACCESS|CMD_KEY_VARIABLE_FLAGS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* BITFIELD operation get_block argument table */
+struct COMMAND_ARG BITFIELD_operation_get_block_Subargs[] = {
+{MAKE_ARG("encoding",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("offset",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* BITFIELD operation write overflow_block argument table */
+struct COMMAND_ARG BITFIELD_operation_write_overflow_block_Subargs[] = {
+{MAKE_ARG("wrap",ARG_TYPE_PURE_TOKEN,-1,"WRAP",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("sat",ARG_TYPE_PURE_TOKEN,-1,"SAT",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("fail",ARG_TYPE_PURE_TOKEN,-1,"FAIL",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* BITFIELD operation write write_operation set_block argument table */
+struct COMMAND_ARG BITFIELD_operation_write_write_operation_set_block_Subargs[] = {
+{MAKE_ARG("encoding",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("offset",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("value",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* BITFIELD operation write write_operation incrby_block argument table */
+struct COMMAND_ARG BITFIELD_operation_write_write_operation_incrby_block_Subargs[] = {
+{MAKE_ARG("encoding",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("offset",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("increment",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* BITFIELD operation write write_operation argument table */
+struct COMMAND_ARG BITFIELD_operation_write_write_operation_Subargs[] = {
+{MAKE_ARG("set-block",ARG_TYPE_BLOCK,-1,"SET",NULL,NULL,CMD_ARG_NONE,3,NULL),.subargs=BITFIELD_operation_write_write_operation_set_block_Subargs},
+{MAKE_ARG("incrby-block",ARG_TYPE_BLOCK,-1,"INCRBY",NULL,NULL,CMD_ARG_NONE,3,NULL),.subargs=BITFIELD_operation_write_write_operation_incrby_block_Subargs},
+};
+
+/* BITFIELD operation write argument table */
+struct COMMAND_ARG BITFIELD_operation_write_Subargs[] = {
+{MAKE_ARG("overflow-block",ARG_TYPE_ONEOF,-1,"OVERFLOW",NULL,NULL,CMD_ARG_OPTIONAL,3,NULL),.subargs=BITFIELD_operation_write_overflow_block_Subargs},
+{MAKE_ARG("write-operation",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=BITFIELD_operation_write_write_operation_Subargs},
+};
+
+/* BITFIELD operation argument table */
+struct COMMAND_ARG BITFIELD_operation_Subargs[] = {
+{MAKE_ARG("get-block",ARG_TYPE_BLOCK,-1,"GET",NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=BITFIELD_operation_get_block_Subargs},
+{MAKE_ARG("write",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=BITFIELD_operation_write_Subargs},
+};
+
+/* BITFIELD argument table */
+struct COMMAND_ARG BITFIELD_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("operation",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE,2,NULL),.subargs=BITFIELD_operation_Subargs},
+};
+
+/********** BITFIELD_RO ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* BITFIELD_RO history */
+#define BITFIELD_RO_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* BITFIELD_RO tips */
+#define BITFIELD_RO_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* BITFIELD_RO key specs */
+keySpec BITFIELD_RO_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* BITFIELD_RO get_block argument table */
+struct COMMAND_ARG BITFIELD_RO_get_block_Subargs[] = {
+{MAKE_ARG("encoding",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("offset",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* BITFIELD_RO argument table */
+struct COMMAND_ARG BITFIELD_RO_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("get-block",ARG_TYPE_BLOCK,-1,"GET",NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE|CMD_ARG_MULTIPLE_TOKEN,2,NULL),.subargs=BITFIELD_RO_get_block_Subargs},
+};
+
+/********** BITOP ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* BITOP history */
+#define BITOP_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* BITOP tips */
+#define BITOP_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* BITOP key specs */
+keySpec BITOP_Keyspecs[2] = {
+{NULL,CMD_KEY_OW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={0,1,0}},{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={3},KSPEC_FK_RANGE,.fk.range={-1,1,0}}
+};
+#endif
+
+/* BITOP operation argument table */
+struct COMMAND_ARG BITOP_operation_Subargs[] = {
+{MAKE_ARG("and",ARG_TYPE_PURE_TOKEN,-1,"AND",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("or",ARG_TYPE_PURE_TOKEN,-1,"OR",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("xor",ARG_TYPE_PURE_TOKEN,-1,"XOR",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("not",ARG_TYPE_PURE_TOKEN,-1,"NOT",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* BITOP argument table */
+struct COMMAND_ARG BITOP_Args[] = {
+{MAKE_ARG("operation",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,4,NULL),.subargs=BITOP_operation_Subargs},
+{MAKE_ARG("destkey",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("key",ARG_TYPE_KEY,1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** BITPOS ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* BITPOS history */
+commandHistory BITPOS_History[] = {
+{"7.0.0","Added the `BYTE|BIT` option."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* BITPOS tips */
+#define BITPOS_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* BITPOS key specs */
+keySpec BITPOS_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* BITPOS range end_unit_block unit argument table */
+struct COMMAND_ARG BITPOS_range_end_unit_block_unit_Subargs[] = {
+{MAKE_ARG("byte",ARG_TYPE_PURE_TOKEN,-1,"BYTE",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("bit",ARG_TYPE_PURE_TOKEN,-1,"BIT",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* BITPOS range end_unit_block argument table */
+struct COMMAND_ARG BITPOS_range_end_unit_block_Subargs[] = {
+{MAKE_ARG("end",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("unit",ARG_TYPE_ONEOF,-1,NULL,NULL,"7.0.0",CMD_ARG_OPTIONAL,2,NULL),.subargs=BITPOS_range_end_unit_block_unit_Subargs},
+};
+
+/* BITPOS range argument table */
+struct COMMAND_ARG BITPOS_range_Subargs[] = {
+{MAKE_ARG("start",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("end-unit-block",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,2,NULL),.subargs=BITPOS_range_end_unit_block_Subargs},
+};
+
+/* BITPOS argument table */
+struct COMMAND_ARG BITPOS_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("bit",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("range",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,2,NULL),.subargs=BITPOS_range_Subargs},
+};
+
+/********** GETBIT ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* GETBIT history */
+#define GETBIT_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* GETBIT tips */
+#define GETBIT_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* GETBIT key specs */
+keySpec GETBIT_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* GETBIT argument table */
+struct COMMAND_ARG GETBIT_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("offset",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** SETBIT ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SETBIT history */
+#define SETBIT_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SETBIT tips */
+#define SETBIT_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SETBIT key specs */
+keySpec SETBIT_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* SETBIT argument table */
+struct COMMAND_ARG SETBIT_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("offset",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("value",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** ASKING ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* ASKING history */
+#define ASKING_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* ASKING tips */
+#define ASKING_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* ASKING key specs */
+#define ASKING_Keyspecs NULL
+#endif
+
+/********** CLUSTER ADDSLOTS ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* CLUSTER ADDSLOTS history */
+#define CLUSTER_ADDSLOTS_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* CLUSTER ADDSLOTS tips */
+#define CLUSTER_ADDSLOTS_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* CLUSTER ADDSLOTS key specs */
+#define CLUSTER_ADDSLOTS_Keyspecs NULL
+#endif
+
+/* CLUSTER ADDSLOTS argument table */
+struct COMMAND_ARG CLUSTER_ADDSLOTS_Args[] = {
+{MAKE_ARG("slot",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** CLUSTER ADDSLOTSRANGE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* CLUSTER ADDSLOTSRANGE history */
+#define CLUSTER_ADDSLOTSRANGE_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* CLUSTER ADDSLOTSRANGE tips */
+#define CLUSTER_ADDSLOTSRANGE_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* CLUSTER ADDSLOTSRANGE key specs */
+#define CLUSTER_ADDSLOTSRANGE_Keyspecs NULL
+#endif
+
+/* CLUSTER ADDSLOTSRANGE range argument table */
+struct COMMAND_ARG CLUSTER_ADDSLOTSRANGE_range_Subargs[] = {
+{MAKE_ARG("start-slot",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("end-slot",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* CLUSTER ADDSLOTSRANGE argument table */
+struct COMMAND_ARG CLUSTER_ADDSLOTSRANGE_Args[] = {
+{MAKE_ARG("range",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,2,NULL),.subargs=CLUSTER_ADDSLOTSRANGE_range_Subargs},
+};
+
+/********** CLUSTER BUMPEPOCH ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* CLUSTER BUMPEPOCH history */
+#define CLUSTER_BUMPEPOCH_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* CLUSTER BUMPEPOCH tips */
+const char *CLUSTER_BUMPEPOCH_Tips[] = {
+"nondeterministic_output",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* CLUSTER BUMPEPOCH key specs */
+#define CLUSTER_BUMPEPOCH_Keyspecs NULL
+#endif
+
+/********** CLUSTER COUNT_FAILURE_REPORTS ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* CLUSTER COUNT_FAILURE_REPORTS history */
+#define CLUSTER_COUNT_FAILURE_REPORTS_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* CLUSTER COUNT_FAILURE_REPORTS tips */
+const char *CLUSTER_COUNT_FAILURE_REPORTS_Tips[] = {
+"nondeterministic_output",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* CLUSTER COUNT_FAILURE_REPORTS key specs */
+#define CLUSTER_COUNT_FAILURE_REPORTS_Keyspecs NULL
+#endif
+
+/* CLUSTER COUNT_FAILURE_REPORTS argument table */
+struct COMMAND_ARG CLUSTER_COUNT_FAILURE_REPORTS_Args[] = {
+{MAKE_ARG("node-id",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** CLUSTER COUNTKEYSINSLOT ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* CLUSTER COUNTKEYSINSLOT history */
+#define CLUSTER_COUNTKEYSINSLOT_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* CLUSTER COUNTKEYSINSLOT tips */
+#define CLUSTER_COUNTKEYSINSLOT_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* CLUSTER COUNTKEYSINSLOT key specs */
+#define CLUSTER_COUNTKEYSINSLOT_Keyspecs NULL
+#endif
+
+/* CLUSTER COUNTKEYSINSLOT argument table */
+struct COMMAND_ARG CLUSTER_COUNTKEYSINSLOT_Args[] = {
+{MAKE_ARG("slot",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** CLUSTER DELSLOTS ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* CLUSTER DELSLOTS history */
+#define CLUSTER_DELSLOTS_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* CLUSTER DELSLOTS tips */
+#define CLUSTER_DELSLOTS_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* CLUSTER DELSLOTS key specs */
+#define CLUSTER_DELSLOTS_Keyspecs NULL
+#endif
+
+/* CLUSTER DELSLOTS argument table */
+struct COMMAND_ARG CLUSTER_DELSLOTS_Args[] = {
+{MAKE_ARG("slot",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** CLUSTER DELSLOTSRANGE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* CLUSTER DELSLOTSRANGE history */
+#define CLUSTER_DELSLOTSRANGE_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* CLUSTER DELSLOTSRANGE tips */
+#define CLUSTER_DELSLOTSRANGE_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* CLUSTER DELSLOTSRANGE key specs */
+#define CLUSTER_DELSLOTSRANGE_Keyspecs NULL
+#endif
+
+/* CLUSTER DELSLOTSRANGE range argument table */
+struct COMMAND_ARG CLUSTER_DELSLOTSRANGE_range_Subargs[] = {
+{MAKE_ARG("start-slot",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("end-slot",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* CLUSTER DELSLOTSRANGE argument table */
+struct COMMAND_ARG CLUSTER_DELSLOTSRANGE_Args[] = {
+{MAKE_ARG("range",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,2,NULL),.subargs=CLUSTER_DELSLOTSRANGE_range_Subargs},
+};
+
+/********** CLUSTER FAILOVER ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* CLUSTER FAILOVER history */
+#define CLUSTER_FAILOVER_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* CLUSTER FAILOVER tips */
+#define CLUSTER_FAILOVER_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* CLUSTER FAILOVER key specs */
+#define CLUSTER_FAILOVER_Keyspecs NULL
+#endif
+
+/* CLUSTER FAILOVER options argument table */
+struct COMMAND_ARG CLUSTER_FAILOVER_options_Subargs[] = {
+{MAKE_ARG("force",ARG_TYPE_PURE_TOKEN,-1,"FORCE",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("takeover",ARG_TYPE_PURE_TOKEN,-1,"TAKEOVER",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* CLUSTER FAILOVER argument table */
+struct COMMAND_ARG CLUSTER_FAILOVER_Args[] = {
+{MAKE_ARG("options",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,2,NULL),.subargs=CLUSTER_FAILOVER_options_Subargs},
+};
+
+/********** CLUSTER FLUSHSLOTS ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* CLUSTER FLUSHSLOTS history */
+#define CLUSTER_FLUSHSLOTS_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* CLUSTER FLUSHSLOTS tips */
+#define CLUSTER_FLUSHSLOTS_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* CLUSTER FLUSHSLOTS key specs */
+#define CLUSTER_FLUSHSLOTS_Keyspecs NULL
+#endif
+
+/********** CLUSTER FORGET ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* CLUSTER FORGET history */
+#define CLUSTER_FORGET_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* CLUSTER FORGET tips */
+#define CLUSTER_FORGET_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* CLUSTER FORGET key specs */
+#define CLUSTER_FORGET_Keyspecs NULL
+#endif
+
+/* CLUSTER FORGET argument table */
+struct COMMAND_ARG CLUSTER_FORGET_Args[] = {
+{MAKE_ARG("node-id",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** CLUSTER GETKEYSINSLOT ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* CLUSTER GETKEYSINSLOT history */
+#define CLUSTER_GETKEYSINSLOT_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* CLUSTER GETKEYSINSLOT tips */
+const char *CLUSTER_GETKEYSINSLOT_Tips[] = {
+"nondeterministic_output",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* CLUSTER GETKEYSINSLOT key specs */
+#define CLUSTER_GETKEYSINSLOT_Keyspecs NULL
+#endif
+
+/* CLUSTER GETKEYSINSLOT argument table */
+struct COMMAND_ARG CLUSTER_GETKEYSINSLOT_Args[] = {
+{MAKE_ARG("slot",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("count",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** CLUSTER HELP ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* CLUSTER HELP history */
+#define CLUSTER_HELP_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* CLUSTER HELP tips */
+#define CLUSTER_HELP_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* CLUSTER HELP key specs */
+#define CLUSTER_HELP_Keyspecs NULL
+#endif
+
+/********** CLUSTER INFO ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* CLUSTER INFO history */
+#define CLUSTER_INFO_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* CLUSTER INFO tips */
+const char *CLUSTER_INFO_Tips[] = {
+"nondeterministic_output",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* CLUSTER INFO key specs */
+#define CLUSTER_INFO_Keyspecs NULL
+#endif
+
+/********** CLUSTER KEYSLOT ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* CLUSTER KEYSLOT history */
+#define CLUSTER_KEYSLOT_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* CLUSTER KEYSLOT tips */
+#define CLUSTER_KEYSLOT_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* CLUSTER KEYSLOT key specs */
+#define CLUSTER_KEYSLOT_Keyspecs NULL
+#endif
+
+/* CLUSTER KEYSLOT argument table */
+struct COMMAND_ARG CLUSTER_KEYSLOT_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** CLUSTER LINKS ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* CLUSTER LINKS history */
+#define CLUSTER_LINKS_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* CLUSTER LINKS tips */
+const char *CLUSTER_LINKS_Tips[] = {
+"nondeterministic_output",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* CLUSTER LINKS key specs */
+#define CLUSTER_LINKS_Keyspecs NULL
+#endif
+
+/********** CLUSTER MEET ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* CLUSTER MEET history */
+commandHistory CLUSTER_MEET_History[] = {
+{"4.0.0","Added the optional `cluster_bus_port` argument."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* CLUSTER MEET tips */
+#define CLUSTER_MEET_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* CLUSTER MEET key specs */
+#define CLUSTER_MEET_Keyspecs NULL
+#endif
+
+/* CLUSTER MEET argument table */
+struct COMMAND_ARG CLUSTER_MEET_Args[] = {
+{MAKE_ARG("ip",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("port",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("cluster-bus-port",ARG_TYPE_INTEGER,-1,NULL,NULL,"4.0.0",CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/********** CLUSTER MYID ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* CLUSTER MYID history */
+#define CLUSTER_MYID_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* CLUSTER MYID tips */
+#define CLUSTER_MYID_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* CLUSTER MYID key specs */
+#define CLUSTER_MYID_Keyspecs NULL
+#endif
+
+/********** CLUSTER MYSHARDID ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* CLUSTER MYSHARDID history */
+#define CLUSTER_MYSHARDID_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* CLUSTER MYSHARDID tips */
+const char *CLUSTER_MYSHARDID_Tips[] = {
+"nondeterministic_output",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* CLUSTER MYSHARDID key specs */
+#define CLUSTER_MYSHARDID_Keyspecs NULL
+#endif
+
+/********** CLUSTER NODES ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* CLUSTER NODES history */
+#define CLUSTER_NODES_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* CLUSTER NODES tips */
+const char *CLUSTER_NODES_Tips[] = {
+"nondeterministic_output",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* CLUSTER NODES key specs */
+#define CLUSTER_NODES_Keyspecs NULL
+#endif
+
+/********** CLUSTER REPLICAS ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* CLUSTER REPLICAS history */
+#define CLUSTER_REPLICAS_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* CLUSTER REPLICAS tips */
+const char *CLUSTER_REPLICAS_Tips[] = {
+"nondeterministic_output",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* CLUSTER REPLICAS key specs */
+#define CLUSTER_REPLICAS_Keyspecs NULL
+#endif
+
+/* CLUSTER REPLICAS argument table */
+struct COMMAND_ARG CLUSTER_REPLICAS_Args[] = {
+{MAKE_ARG("node-id",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** CLUSTER REPLICATE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* CLUSTER REPLICATE history */
+#define CLUSTER_REPLICATE_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* CLUSTER REPLICATE tips */
+#define CLUSTER_REPLICATE_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* CLUSTER REPLICATE key specs */
+#define CLUSTER_REPLICATE_Keyspecs NULL
+#endif
+
+/* CLUSTER REPLICATE argument table */
+struct COMMAND_ARG CLUSTER_REPLICATE_Args[] = {
+{MAKE_ARG("node-id",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** CLUSTER RESET ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* CLUSTER RESET history */
+#define CLUSTER_RESET_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* CLUSTER RESET tips */
+#define CLUSTER_RESET_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* CLUSTER RESET key specs */
+#define CLUSTER_RESET_Keyspecs NULL
+#endif
+
+/* CLUSTER RESET reset_type argument table */
+struct COMMAND_ARG CLUSTER_RESET_reset_type_Subargs[] = {
+{MAKE_ARG("hard",ARG_TYPE_PURE_TOKEN,-1,"HARD",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("soft",ARG_TYPE_PURE_TOKEN,-1,"SOFT",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* CLUSTER RESET argument table */
+struct COMMAND_ARG CLUSTER_RESET_Args[] = {
+{MAKE_ARG("reset-type",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,2,NULL),.subargs=CLUSTER_RESET_reset_type_Subargs},
+};
+
+/********** CLUSTER SAVECONFIG ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* CLUSTER SAVECONFIG history */
+#define CLUSTER_SAVECONFIG_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* CLUSTER SAVECONFIG tips */
+#define CLUSTER_SAVECONFIG_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* CLUSTER SAVECONFIG key specs */
+#define CLUSTER_SAVECONFIG_Keyspecs NULL
+#endif
+
+/********** CLUSTER SET_CONFIG_EPOCH ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* CLUSTER SET_CONFIG_EPOCH history */
+#define CLUSTER_SET_CONFIG_EPOCH_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* CLUSTER SET_CONFIG_EPOCH tips */
+#define CLUSTER_SET_CONFIG_EPOCH_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* CLUSTER SET_CONFIG_EPOCH key specs */
+#define CLUSTER_SET_CONFIG_EPOCH_Keyspecs NULL
+#endif
+
+/* CLUSTER SET_CONFIG_EPOCH argument table */
+struct COMMAND_ARG CLUSTER_SET_CONFIG_EPOCH_Args[] = {
+{MAKE_ARG("config-epoch",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** CLUSTER SETSLOT ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* CLUSTER SETSLOT history */
+#define CLUSTER_SETSLOT_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* CLUSTER SETSLOT tips */
+#define CLUSTER_SETSLOT_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* CLUSTER SETSLOT key specs */
+#define CLUSTER_SETSLOT_Keyspecs NULL
+#endif
+
+/* CLUSTER SETSLOT subcommand argument table */
+struct COMMAND_ARG CLUSTER_SETSLOT_subcommand_Subargs[] = {
+{MAKE_ARG("importing",ARG_TYPE_STRING,-1,"IMPORTING",NULL,NULL,CMD_ARG_NONE,0,NULL),.display_text="node-id"},
+{MAKE_ARG("migrating",ARG_TYPE_STRING,-1,"MIGRATING",NULL,NULL,CMD_ARG_NONE,0,NULL),.display_text="node-id"},
+{MAKE_ARG("node",ARG_TYPE_STRING,-1,"NODE",NULL,NULL,CMD_ARG_NONE,0,NULL),.display_text="node-id"},
+{MAKE_ARG("stable",ARG_TYPE_PURE_TOKEN,-1,"STABLE",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* CLUSTER SETSLOT argument table */
+struct COMMAND_ARG CLUSTER_SETSLOT_Args[] = {
+{MAKE_ARG("slot",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("subcommand",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,4,NULL),.subargs=CLUSTER_SETSLOT_subcommand_Subargs},
+};
+
+/********** CLUSTER SHARDS ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* CLUSTER SHARDS history */
+#define CLUSTER_SHARDS_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* CLUSTER SHARDS tips */
+const char *CLUSTER_SHARDS_Tips[] = {
+"nondeterministic_output",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* CLUSTER SHARDS key specs */
+#define CLUSTER_SHARDS_Keyspecs NULL
+#endif
+
+/********** CLUSTER SLAVES ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* CLUSTER SLAVES history */
+#define CLUSTER_SLAVES_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* CLUSTER SLAVES tips */
+const char *CLUSTER_SLAVES_Tips[] = {
+"nondeterministic_output",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* CLUSTER SLAVES key specs */
+#define CLUSTER_SLAVES_Keyspecs NULL
+#endif
+
+/* CLUSTER SLAVES argument table */
+struct COMMAND_ARG CLUSTER_SLAVES_Args[] = {
+{MAKE_ARG("node-id",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** CLUSTER SLOTS ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* CLUSTER SLOTS history */
+commandHistory CLUSTER_SLOTS_History[] = {
+{"4.0.0","Added node IDs."},
+{"7.0.0","Added additional networking metadata field."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* CLUSTER SLOTS tips */
+const char *CLUSTER_SLOTS_Tips[] = {
+"nondeterministic_output",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* CLUSTER SLOTS key specs */
+#define CLUSTER_SLOTS_Keyspecs NULL
+#endif
+
+/* CLUSTER command table */
+struct COMMAND_STRUCT CLUSTER_Subcommands[] = {
+{MAKE_CMD("addslots","Assigns new hash slots to a node.","O(N) where N is the total number of hash slot arguments","3.0.0",CMD_DOC_NONE,NULL,NULL,"cluster",COMMAND_GROUP_CLUSTER,CLUSTER_ADDSLOTS_History,0,CLUSTER_ADDSLOTS_Tips,0,clusterCommand,-3,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_STALE,0,CLUSTER_ADDSLOTS_Keyspecs,0,NULL,1),.args=CLUSTER_ADDSLOTS_Args},
+{MAKE_CMD("addslotsrange","Assigns new hash slot ranges to a node.","O(N) where N is the total number of the slots between the start slot and end slot arguments.","7.0.0",CMD_DOC_NONE,NULL,NULL,"cluster",COMMAND_GROUP_CLUSTER,CLUSTER_ADDSLOTSRANGE_History,0,CLUSTER_ADDSLOTSRANGE_Tips,0,clusterCommand,-4,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_STALE,0,CLUSTER_ADDSLOTSRANGE_Keyspecs,0,NULL,1),.args=CLUSTER_ADDSLOTSRANGE_Args},
+{MAKE_CMD("bumpepoch","Advances the cluster config epoch.","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,"cluster",COMMAND_GROUP_CLUSTER,CLUSTER_BUMPEPOCH_History,0,CLUSTER_BUMPEPOCH_Tips,1,clusterCommand,2,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_STALE,0,CLUSTER_BUMPEPOCH_Keyspecs,0,NULL,0)},
+{MAKE_CMD("count-failure-reports","Returns the number of active failure reports active for a node.","O(N) where N is the number of failure reports","3.0.0",CMD_DOC_NONE,NULL,NULL,"cluster",COMMAND_GROUP_CLUSTER,CLUSTER_COUNT_FAILURE_REPORTS_History,0,CLUSTER_COUNT_FAILURE_REPORTS_Tips,1,clusterCommand,3,CMD_ADMIN|CMD_STALE,0,CLUSTER_COUNT_FAILURE_REPORTS_Keyspecs,0,NULL,1),.args=CLUSTER_COUNT_FAILURE_REPORTS_Args},
+{MAKE_CMD("countkeysinslot","Returns the number of keys in a hash slot.","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,"cluster",COMMAND_GROUP_CLUSTER,CLUSTER_COUNTKEYSINSLOT_History,0,CLUSTER_COUNTKEYSINSLOT_Tips,0,clusterCommand,3,CMD_STALE,0,CLUSTER_COUNTKEYSINSLOT_Keyspecs,0,NULL,1),.args=CLUSTER_COUNTKEYSINSLOT_Args},
+{MAKE_CMD("delslots","Sets hash slots as unbound for a node.","O(N) where N is the total number of hash slot arguments","3.0.0",CMD_DOC_NONE,NULL,NULL,"cluster",COMMAND_GROUP_CLUSTER,CLUSTER_DELSLOTS_History,0,CLUSTER_DELSLOTS_Tips,0,clusterCommand,-3,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_STALE,0,CLUSTER_DELSLOTS_Keyspecs,0,NULL,1),.args=CLUSTER_DELSLOTS_Args},
+{MAKE_CMD("delslotsrange","Sets hash slot ranges as unbound for a node.","O(N) where N is the total number of the slots between the start slot and end slot arguments.","7.0.0",CMD_DOC_NONE,NULL,NULL,"cluster",COMMAND_GROUP_CLUSTER,CLUSTER_DELSLOTSRANGE_History,0,CLUSTER_DELSLOTSRANGE_Tips,0,clusterCommand,-4,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_STALE,0,CLUSTER_DELSLOTSRANGE_Keyspecs,0,NULL,1),.args=CLUSTER_DELSLOTSRANGE_Args},
+{MAKE_CMD("failover","Forces a replica to perform a manual failover of its master.","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,"cluster",COMMAND_GROUP_CLUSTER,CLUSTER_FAILOVER_History,0,CLUSTER_FAILOVER_Tips,0,clusterCommand,-2,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_STALE,0,CLUSTER_FAILOVER_Keyspecs,0,NULL,1),.args=CLUSTER_FAILOVER_Args},
+{MAKE_CMD("flushslots","Deletes all slots information from a node.","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,"cluster",COMMAND_GROUP_CLUSTER,CLUSTER_FLUSHSLOTS_History,0,CLUSTER_FLUSHSLOTS_Tips,0,clusterCommand,2,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_STALE,0,CLUSTER_FLUSHSLOTS_Keyspecs,0,NULL,0)},
+{MAKE_CMD("forget","Removes a node from the nodes table.","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,"cluster",COMMAND_GROUP_CLUSTER,CLUSTER_FORGET_History,0,CLUSTER_FORGET_Tips,0,clusterCommand,3,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_STALE,0,CLUSTER_FORGET_Keyspecs,0,NULL,1),.args=CLUSTER_FORGET_Args},
+{MAKE_CMD("getkeysinslot","Returns the key names in a hash slot.","O(N) where N is the number of requested keys","3.0.0",CMD_DOC_NONE,NULL,NULL,"cluster",COMMAND_GROUP_CLUSTER,CLUSTER_GETKEYSINSLOT_History,0,CLUSTER_GETKEYSINSLOT_Tips,1,clusterCommand,4,CMD_STALE,0,CLUSTER_GETKEYSINSLOT_Keyspecs,0,NULL,2),.args=CLUSTER_GETKEYSINSLOT_Args},
+{MAKE_CMD("help","Returns helpful text about the different subcommands.","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,"cluster",COMMAND_GROUP_CLUSTER,CLUSTER_HELP_History,0,CLUSTER_HELP_Tips,0,clusterCommand,2,CMD_LOADING|CMD_STALE,0,CLUSTER_HELP_Keyspecs,0,NULL,0)},
+{MAKE_CMD("info","Returns information about the state of a node.","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,"cluster",COMMAND_GROUP_CLUSTER,CLUSTER_INFO_History,0,CLUSTER_INFO_Tips,1,clusterCommand,2,CMD_STALE,0,CLUSTER_INFO_Keyspecs,0,NULL,0)},
+{MAKE_CMD("keyslot","Returns the hash slot for a key.","O(N) where N is the number of bytes in the key","3.0.0",CMD_DOC_NONE,NULL,NULL,"cluster",COMMAND_GROUP_CLUSTER,CLUSTER_KEYSLOT_History,0,CLUSTER_KEYSLOT_Tips,0,clusterCommand,3,CMD_STALE,0,CLUSTER_KEYSLOT_Keyspecs,0,NULL,1),.args=CLUSTER_KEYSLOT_Args},
+{MAKE_CMD("links","Returns a list of all TCP links to and from peer nodes.","O(N) where N is the total number of Cluster nodes","7.0.0",CMD_DOC_NONE,NULL,NULL,"cluster",COMMAND_GROUP_CLUSTER,CLUSTER_LINKS_History,0,CLUSTER_LINKS_Tips,1,clusterCommand,2,CMD_STALE,0,CLUSTER_LINKS_Keyspecs,0,NULL,0)},
+{MAKE_CMD("meet","Forces a node to handshake with another node.","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,"cluster",COMMAND_GROUP_CLUSTER,CLUSTER_MEET_History,1,CLUSTER_MEET_Tips,0,clusterCommand,-4,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_STALE,0,CLUSTER_MEET_Keyspecs,0,NULL,3),.args=CLUSTER_MEET_Args},
+{MAKE_CMD("myid","Returns the ID of a node.","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,"cluster",COMMAND_GROUP_CLUSTER,CLUSTER_MYID_History,0,CLUSTER_MYID_Tips,0,clusterCommand,2,CMD_STALE,0,CLUSTER_MYID_Keyspecs,0,NULL,0)},
+{MAKE_CMD("myshardid","Returns the shard ID of a node.","O(1)","7.2.0",CMD_DOC_NONE,NULL,NULL,"cluster",COMMAND_GROUP_CLUSTER,CLUSTER_MYSHARDID_History,0,CLUSTER_MYSHARDID_Tips,1,clusterCommand,2,CMD_STALE,0,CLUSTER_MYSHARDID_Keyspecs,0,NULL,0)},
+{MAKE_CMD("nodes","Returns the cluster configuration for a node.","O(N) where N is the total number of Cluster nodes","3.0.0",CMD_DOC_NONE,NULL,NULL,"cluster",COMMAND_GROUP_CLUSTER,CLUSTER_NODES_History,0,CLUSTER_NODES_Tips,1,clusterCommand,2,CMD_STALE,0,CLUSTER_NODES_Keyspecs,0,NULL,0)},
+{MAKE_CMD("replicas","Lists the replica nodes of a master node.","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,"cluster",COMMAND_GROUP_CLUSTER,CLUSTER_REPLICAS_History,0,CLUSTER_REPLICAS_Tips,1,clusterCommand,3,CMD_ADMIN|CMD_STALE,0,CLUSTER_REPLICAS_Keyspecs,0,NULL,1),.args=CLUSTER_REPLICAS_Args},
+{MAKE_CMD("replicate","Configure a node as replica of a master node.","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,"cluster",COMMAND_GROUP_CLUSTER,CLUSTER_REPLICATE_History,0,CLUSTER_REPLICATE_Tips,0,clusterCommand,3,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_STALE,0,CLUSTER_REPLICATE_Keyspecs,0,NULL,1),.args=CLUSTER_REPLICATE_Args},
+{MAKE_CMD("reset","Resets a node.","O(N) where N is the number of known nodes. The command may execute a FLUSHALL as a side effect.","3.0.0",CMD_DOC_NONE,NULL,NULL,"cluster",COMMAND_GROUP_CLUSTER,CLUSTER_RESET_History,0,CLUSTER_RESET_Tips,0,clusterCommand,-2,CMD_ADMIN|CMD_STALE|CMD_NOSCRIPT,0,CLUSTER_RESET_Keyspecs,0,NULL,1),.args=CLUSTER_RESET_Args},
+{MAKE_CMD("saveconfig","Forces a node to save the cluster configuration to disk.","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,"cluster",COMMAND_GROUP_CLUSTER,CLUSTER_SAVECONFIG_History,0,CLUSTER_SAVECONFIG_Tips,0,clusterCommand,2,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_STALE,0,CLUSTER_SAVECONFIG_Keyspecs,0,NULL,0)},
+{MAKE_CMD("set-config-epoch","Sets the configuration epoch for a new node.","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,"cluster",COMMAND_GROUP_CLUSTER,CLUSTER_SET_CONFIG_EPOCH_History,0,CLUSTER_SET_CONFIG_EPOCH_Tips,0,clusterCommand,3,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_STALE,0,CLUSTER_SET_CONFIG_EPOCH_Keyspecs,0,NULL,1),.args=CLUSTER_SET_CONFIG_EPOCH_Args},
+{MAKE_CMD("setslot","Binds a hash slot to a node.","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,"cluster",COMMAND_GROUP_CLUSTER,CLUSTER_SETSLOT_History,0,CLUSTER_SETSLOT_Tips,0,clusterCommand,-4,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_STALE,0,CLUSTER_SETSLOT_Keyspecs,0,NULL,2),.args=CLUSTER_SETSLOT_Args},
+{MAKE_CMD("shards","Returns the mapping of cluster slots to shards.","O(N) where N is the total number of cluster nodes","7.0.0",CMD_DOC_NONE,NULL,NULL,"cluster",COMMAND_GROUP_CLUSTER,CLUSTER_SHARDS_History,0,CLUSTER_SHARDS_Tips,1,clusterCommand,2,CMD_STALE,0,CLUSTER_SHARDS_Keyspecs,0,NULL,0)},
+{MAKE_CMD("slaves","Lists the replica nodes of a master node.","O(1)","3.0.0",CMD_DOC_DEPRECATED,"`CLUSTER REPLICAS`","5.0.0","cluster",COMMAND_GROUP_CLUSTER,CLUSTER_SLAVES_History,0,CLUSTER_SLAVES_Tips,1,clusterCommand,3,CMD_ADMIN|CMD_STALE,0,CLUSTER_SLAVES_Keyspecs,0,NULL,1),.args=CLUSTER_SLAVES_Args},
+{MAKE_CMD("slots","Returns the mapping of cluster slots to nodes.","O(N) where N is the total number of Cluster nodes","3.0.0",CMD_DOC_DEPRECATED,"`CLUSTER SHARDS`","7.0.0","cluster",COMMAND_GROUP_CLUSTER,CLUSTER_SLOTS_History,2,CLUSTER_SLOTS_Tips,1,clusterCommand,2,CMD_STALE,0,CLUSTER_SLOTS_Keyspecs,0,NULL,0)},
+{0}
+};
+
+/********** CLUSTER ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* CLUSTER history */
+#define CLUSTER_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* CLUSTER tips */
+#define CLUSTER_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* CLUSTER key specs */
+#define CLUSTER_Keyspecs NULL
+#endif
+
+/********** READONLY ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* READONLY history */
+#define READONLY_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* READONLY tips */
+#define READONLY_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* READONLY key specs */
+#define READONLY_Keyspecs NULL
+#endif
+
+/********** READWRITE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* READWRITE history */
+#define READWRITE_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* READWRITE tips */
+#define READWRITE_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* READWRITE key specs */
+#define READWRITE_Keyspecs NULL
+#endif
+
+/********** AUTH ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* AUTH history */
+commandHistory AUTH_History[] = {
+{"6.0.0","Added ACL style (username and password)."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* AUTH tips */
+#define AUTH_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* AUTH key specs */
+#define AUTH_Keyspecs NULL
+#endif
+
+/* AUTH argument table */
+struct COMMAND_ARG AUTH_Args[] = {
+{MAKE_ARG("username",ARG_TYPE_STRING,-1,NULL,NULL,"6.0.0",CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("password",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** CLIENT CACHING ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* CLIENT CACHING history */
+#define CLIENT_CACHING_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* CLIENT CACHING tips */
+#define CLIENT_CACHING_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* CLIENT CACHING key specs */
+#define CLIENT_CACHING_Keyspecs NULL
+#endif
+
+/* CLIENT CACHING mode argument table */
+struct COMMAND_ARG CLIENT_CACHING_mode_Subargs[] = {
+{MAKE_ARG("yes",ARG_TYPE_PURE_TOKEN,-1,"YES",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("no",ARG_TYPE_PURE_TOKEN,-1,"NO",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* CLIENT CACHING argument table */
+struct COMMAND_ARG CLIENT_CACHING_Args[] = {
+{MAKE_ARG("mode",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=CLIENT_CACHING_mode_Subargs},
+};
+
+/********** CLIENT GETNAME ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* CLIENT GETNAME history */
+#define CLIENT_GETNAME_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* CLIENT GETNAME tips */
+#define CLIENT_GETNAME_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* CLIENT GETNAME key specs */
+#define CLIENT_GETNAME_Keyspecs NULL
+#endif
+
+/********** CLIENT GETREDIR ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* CLIENT GETREDIR history */
+#define CLIENT_GETREDIR_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* CLIENT GETREDIR tips */
+#define CLIENT_GETREDIR_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* CLIENT GETREDIR key specs */
+#define CLIENT_GETREDIR_Keyspecs NULL
+#endif
+
+/********** CLIENT HELP ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* CLIENT HELP history */
+#define CLIENT_HELP_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* CLIENT HELP tips */
+#define CLIENT_HELP_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* CLIENT HELP key specs */
+#define CLIENT_HELP_Keyspecs NULL
+#endif
+
+/********** CLIENT ID ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* CLIENT ID history */
+#define CLIENT_ID_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* CLIENT ID tips */
+#define CLIENT_ID_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* CLIENT ID key specs */
+#define CLIENT_ID_Keyspecs NULL
+#endif
+
+/********** CLIENT INFO ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* CLIENT INFO history */
+#define CLIENT_INFO_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* CLIENT INFO tips */
+const char *CLIENT_INFO_Tips[] = {
+"nondeterministic_output",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* CLIENT INFO key specs */
+#define CLIENT_INFO_Keyspecs NULL
+#endif
+
+/********** CLIENT KILL ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* CLIENT KILL history */
+commandHistory CLIENT_KILL_History[] = {
+{"2.8.12","Added new filter format."},
+{"2.8.12","`ID` option."},
+{"3.2.0","Added `master` type in for `TYPE` option."},
+{"5.0.0","Replaced `slave` `TYPE` with `replica`. `slave` still supported for backward compatibility."},
+{"6.2.0","`LADDR` option."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* CLIENT KILL tips */
+#define CLIENT_KILL_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* CLIENT KILL key specs */
+#define CLIENT_KILL_Keyspecs NULL
+#endif
+
+/* CLIENT KILL filter new_format client_type argument table */
+struct COMMAND_ARG CLIENT_KILL_filter_new_format_client_type_Subargs[] = {
+{MAKE_ARG("normal",ARG_TYPE_PURE_TOKEN,-1,"NORMAL",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("master",ARG_TYPE_PURE_TOKEN,-1,"MASTER",NULL,"3.2.0",CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("slave",ARG_TYPE_PURE_TOKEN,-1,"SLAVE",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("replica",ARG_TYPE_PURE_TOKEN,-1,"REPLICA",NULL,"5.0.0",CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("pubsub",ARG_TYPE_PURE_TOKEN,-1,"PUBSUB",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* CLIENT KILL filter new_format skipme argument table */
+struct COMMAND_ARG CLIENT_KILL_filter_new_format_skipme_Subargs[] = {
+{MAKE_ARG("yes",ARG_TYPE_PURE_TOKEN,-1,"YES",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("no",ARG_TYPE_PURE_TOKEN,-1,"NO",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* CLIENT KILL filter new_format argument table */
+struct COMMAND_ARG CLIENT_KILL_filter_new_format_Subargs[] = {
+{MAKE_ARG("client-id",ARG_TYPE_INTEGER,-1,"ID",NULL,"2.8.12",CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("client-type",ARG_TYPE_ONEOF,-1,"TYPE",NULL,"2.8.12",CMD_ARG_OPTIONAL,5,NULL),.subargs=CLIENT_KILL_filter_new_format_client_type_Subargs},
+{MAKE_ARG("username",ARG_TYPE_STRING,-1,"USER",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("addr",ARG_TYPE_STRING,-1,"ADDR",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL),.display_text="ip:port"},
+{MAKE_ARG("laddr",ARG_TYPE_STRING,-1,"LADDR",NULL,"6.2.0",CMD_ARG_OPTIONAL,0,NULL),.display_text="ip:port"},
+{MAKE_ARG("skipme",ARG_TYPE_ONEOF,-1,"SKIPME",NULL,NULL,CMD_ARG_OPTIONAL,2,NULL),.subargs=CLIENT_KILL_filter_new_format_skipme_Subargs},
+};
+
+/* CLIENT KILL filter argument table */
+struct COMMAND_ARG CLIENT_KILL_filter_Subargs[] = {
+{MAKE_ARG("old-format",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,"2.8.12"),.display_text="ip:port"},
+{MAKE_ARG("new-format",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,6,NULL),.subargs=CLIENT_KILL_filter_new_format_Subargs},
+};
+
+/* CLIENT KILL argument table */
+struct COMMAND_ARG CLIENT_KILL_Args[] = {
+{MAKE_ARG("filter",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=CLIENT_KILL_filter_Subargs},
+};
+
+/********** CLIENT LIST ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* CLIENT LIST history */
+commandHistory CLIENT_LIST_History[] = {
+{"2.8.12","Added unique client `id` field."},
+{"5.0.0","Added optional `TYPE` filter."},
+{"6.0.0","Added `user` field."},
+{"6.2.0","Added `argv-mem`, `tot-mem`, `laddr` and `redir` fields and the optional `ID` filter."},
+{"7.0.0","Added `resp`, `multi-mem`, `rbs` and `rbp` fields."},
+{"7.0.3","Added `ssub` field."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* CLIENT LIST tips */
+const char *CLIENT_LIST_Tips[] = {
+"nondeterministic_output",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* CLIENT LIST key specs */
+#define CLIENT_LIST_Keyspecs NULL
+#endif
+
+/* CLIENT LIST client_type argument table */
+struct COMMAND_ARG CLIENT_LIST_client_type_Subargs[] = {
+{MAKE_ARG("normal",ARG_TYPE_PURE_TOKEN,-1,"NORMAL",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("master",ARG_TYPE_PURE_TOKEN,-1,"MASTER",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("replica",ARG_TYPE_PURE_TOKEN,-1,"REPLICA",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("pubsub",ARG_TYPE_PURE_TOKEN,-1,"PUBSUB",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* CLIENT LIST argument table */
+struct COMMAND_ARG CLIENT_LIST_Args[] = {
+{MAKE_ARG("client-type",ARG_TYPE_ONEOF,-1,"TYPE",NULL,"5.0.0",CMD_ARG_OPTIONAL,4,NULL),.subargs=CLIENT_LIST_client_type_Subargs},
+{MAKE_ARG("client-id",ARG_TYPE_INTEGER,-1,"ID",NULL,"6.2.0",CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** CLIENT NO_EVICT ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* CLIENT NO_EVICT history */
+#define CLIENT_NO_EVICT_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* CLIENT NO_EVICT tips */
+#define CLIENT_NO_EVICT_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* CLIENT NO_EVICT key specs */
+#define CLIENT_NO_EVICT_Keyspecs NULL
+#endif
+
+/* CLIENT NO_EVICT enabled argument table */
+struct COMMAND_ARG CLIENT_NO_EVICT_enabled_Subargs[] = {
+{MAKE_ARG("on",ARG_TYPE_PURE_TOKEN,-1,"ON",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("off",ARG_TYPE_PURE_TOKEN,-1,"OFF",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* CLIENT NO_EVICT argument table */
+struct COMMAND_ARG CLIENT_NO_EVICT_Args[] = {
+{MAKE_ARG("enabled",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=CLIENT_NO_EVICT_enabled_Subargs},
+};
+
+/********** CLIENT NO_TOUCH ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* CLIENT NO_TOUCH history */
+#define CLIENT_NO_TOUCH_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* CLIENT NO_TOUCH tips */
+#define CLIENT_NO_TOUCH_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* CLIENT NO_TOUCH key specs */
+#define CLIENT_NO_TOUCH_Keyspecs NULL
+#endif
+
+/* CLIENT NO_TOUCH enabled argument table */
+struct COMMAND_ARG CLIENT_NO_TOUCH_enabled_Subargs[] = {
+{MAKE_ARG("on",ARG_TYPE_PURE_TOKEN,-1,"ON",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("off",ARG_TYPE_PURE_TOKEN,-1,"OFF",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* CLIENT NO_TOUCH argument table */
+struct COMMAND_ARG CLIENT_NO_TOUCH_Args[] = {
+{MAKE_ARG("enabled",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=CLIENT_NO_TOUCH_enabled_Subargs},
+};
+
+/********** CLIENT PAUSE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* CLIENT PAUSE history */
+commandHistory CLIENT_PAUSE_History[] = {
+{"6.2.0","`CLIENT PAUSE WRITE` mode added along with the `mode` option."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* CLIENT PAUSE tips */
+#define CLIENT_PAUSE_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* CLIENT PAUSE key specs */
+#define CLIENT_PAUSE_Keyspecs NULL
+#endif
+
+/* CLIENT PAUSE mode argument table */
+struct COMMAND_ARG CLIENT_PAUSE_mode_Subargs[] = {
+{MAKE_ARG("write",ARG_TYPE_PURE_TOKEN,-1,"WRITE",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("all",ARG_TYPE_PURE_TOKEN,-1,"ALL",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* CLIENT PAUSE argument table */
+struct COMMAND_ARG CLIENT_PAUSE_Args[] = {
+{MAKE_ARG("timeout",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("mode",ARG_TYPE_ONEOF,-1,NULL,NULL,"6.2.0",CMD_ARG_OPTIONAL,2,NULL),.subargs=CLIENT_PAUSE_mode_Subargs},
+};
+
+/********** CLIENT REPLY ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* CLIENT REPLY history */
+#define CLIENT_REPLY_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* CLIENT REPLY tips */
+#define CLIENT_REPLY_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* CLIENT REPLY key specs */
+#define CLIENT_REPLY_Keyspecs NULL
+#endif
+
+/* CLIENT REPLY action argument table */
+struct COMMAND_ARG CLIENT_REPLY_action_Subargs[] = {
+{MAKE_ARG("on",ARG_TYPE_PURE_TOKEN,-1,"ON",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("off",ARG_TYPE_PURE_TOKEN,-1,"OFF",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("skip",ARG_TYPE_PURE_TOKEN,-1,"SKIP",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* CLIENT REPLY argument table */
+struct COMMAND_ARG CLIENT_REPLY_Args[] = {
+{MAKE_ARG("action",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,3,NULL),.subargs=CLIENT_REPLY_action_Subargs},
+};
+
+/********** CLIENT SETINFO ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* CLIENT SETINFO history */
+#define CLIENT_SETINFO_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* CLIENT SETINFO tips */
+#define CLIENT_SETINFO_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* CLIENT SETINFO key specs */
+#define CLIENT_SETINFO_Keyspecs NULL
+#endif
+
+/* CLIENT SETINFO attr argument table */
+struct COMMAND_ARG CLIENT_SETINFO_attr_Subargs[] = {
+{MAKE_ARG("libname",ARG_TYPE_STRING,-1,"LIB-NAME",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("libver",ARG_TYPE_STRING,-1,"LIB-VER",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* CLIENT SETINFO argument table */
+struct COMMAND_ARG CLIENT_SETINFO_Args[] = {
+{MAKE_ARG("attr",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=CLIENT_SETINFO_attr_Subargs},
+};
+
+/********** CLIENT SETNAME ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* CLIENT SETNAME history */
+#define CLIENT_SETNAME_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* CLIENT SETNAME tips */
+#define CLIENT_SETNAME_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* CLIENT SETNAME key specs */
+#define CLIENT_SETNAME_Keyspecs NULL
+#endif
+
+/* CLIENT SETNAME argument table */
+struct COMMAND_ARG CLIENT_SETNAME_Args[] = {
+{MAKE_ARG("connection-name",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** CLIENT TRACKING ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* CLIENT TRACKING history */
+#define CLIENT_TRACKING_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* CLIENT TRACKING tips */
+#define CLIENT_TRACKING_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* CLIENT TRACKING key specs */
+#define CLIENT_TRACKING_Keyspecs NULL
+#endif
+
+/* CLIENT TRACKING status argument table */
+struct COMMAND_ARG CLIENT_TRACKING_status_Subargs[] = {
+{MAKE_ARG("on",ARG_TYPE_PURE_TOKEN,-1,"ON",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("off",ARG_TYPE_PURE_TOKEN,-1,"OFF",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* CLIENT TRACKING argument table */
+struct COMMAND_ARG CLIENT_TRACKING_Args[] = {
+{MAKE_ARG("status",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=CLIENT_TRACKING_status_Subargs},
+{MAKE_ARG("client-id",ARG_TYPE_INTEGER,-1,"REDIRECT",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("prefix",ARG_TYPE_STRING,-1,"PREFIX",NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE|CMD_ARG_MULTIPLE_TOKEN,0,NULL)},
+{MAKE_ARG("bcast",ARG_TYPE_PURE_TOKEN,-1,"BCAST",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("optin",ARG_TYPE_PURE_TOKEN,-1,"OPTIN",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("optout",ARG_TYPE_PURE_TOKEN,-1,"OPTOUT",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("noloop",ARG_TYPE_PURE_TOKEN,-1,"NOLOOP",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/********** CLIENT TRACKINGINFO ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* CLIENT TRACKINGINFO history */
+#define CLIENT_TRACKINGINFO_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* CLIENT TRACKINGINFO tips */
+#define CLIENT_TRACKINGINFO_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* CLIENT TRACKINGINFO key specs */
+#define CLIENT_TRACKINGINFO_Keyspecs NULL
+#endif
+
+/********** CLIENT UNBLOCK ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* CLIENT UNBLOCK history */
+#define CLIENT_UNBLOCK_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* CLIENT UNBLOCK tips */
+#define CLIENT_UNBLOCK_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* CLIENT UNBLOCK key specs */
+#define CLIENT_UNBLOCK_Keyspecs NULL
+#endif
+
+/* CLIENT UNBLOCK unblock_type argument table */
+struct COMMAND_ARG CLIENT_UNBLOCK_unblock_type_Subargs[] = {
+{MAKE_ARG("timeout",ARG_TYPE_PURE_TOKEN,-1,"TIMEOUT",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("error",ARG_TYPE_PURE_TOKEN,-1,"ERROR",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* CLIENT UNBLOCK argument table */
+struct COMMAND_ARG CLIENT_UNBLOCK_Args[] = {
+{MAKE_ARG("client-id",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("unblock-type",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,2,NULL),.subargs=CLIENT_UNBLOCK_unblock_type_Subargs},
+};
+
+/********** CLIENT UNPAUSE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* CLIENT UNPAUSE history */
+#define CLIENT_UNPAUSE_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* CLIENT UNPAUSE tips */
+#define CLIENT_UNPAUSE_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* CLIENT UNPAUSE key specs */
+#define CLIENT_UNPAUSE_Keyspecs NULL
+#endif
+
+/* CLIENT command table */
+struct COMMAND_STRUCT CLIENT_Subcommands[] = {
+{MAKE_CMD("caching","Instructs the server whether to track the keys in the next request.","O(1)","6.0.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,CLIENT_CACHING_History,0,CLIENT_CACHING_Tips,0,clientCommand,3,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,CLIENT_CACHING_Keyspecs,0,NULL,1),.args=CLIENT_CACHING_Args},
+{MAKE_CMD("getname","Returns the name of the connection.","O(1)","2.6.9",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,CLIENT_GETNAME_History,0,CLIENT_GETNAME_Tips,0,clientCommand,2,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,CLIENT_GETNAME_Keyspecs,0,NULL,0)},
+{MAKE_CMD("getredir","Returns the client ID to which the connection's tracking notifications are redirected.","O(1)","6.0.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,CLIENT_GETREDIR_History,0,CLIENT_GETREDIR_Tips,0,clientCommand,2,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,CLIENT_GETREDIR_Keyspecs,0,NULL,0)},
+{MAKE_CMD("help","Returns helpful text about the different subcommands.","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,CLIENT_HELP_History,0,CLIENT_HELP_Tips,0,clientCommand,2,CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,CLIENT_HELP_Keyspecs,0,NULL,0)},
+{MAKE_CMD("id","Returns the unique client ID of the connection.","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,CLIENT_ID_History,0,CLIENT_ID_Tips,0,clientCommand,2,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,CLIENT_ID_Keyspecs,0,NULL,0)},
+{MAKE_CMD("info","Returns information about the connection.","O(1)","6.2.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,CLIENT_INFO_History,0,CLIENT_INFO_Tips,1,clientCommand,2,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,CLIENT_INFO_Keyspecs,0,NULL,0)},
+{MAKE_CMD("kill","Terminates open connections.","O(N) where N is the number of client connections","2.4.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,CLIENT_KILL_History,5,CLIENT_KILL_Tips,0,clientCommand,-3,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,CLIENT_KILL_Keyspecs,0,NULL,1),.args=CLIENT_KILL_Args},
+{MAKE_CMD("list","Lists open connections.","O(N) where N is the number of client connections","2.4.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,CLIENT_LIST_History,6,CLIENT_LIST_Tips,1,clientCommand,-2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,CLIENT_LIST_Keyspecs,0,NULL,2),.args=CLIENT_LIST_Args},
+{MAKE_CMD("no-evict","Sets the client eviction mode of the connection.","O(1)","7.0.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,CLIENT_NO_EVICT_History,0,CLIENT_NO_EVICT_Tips,0,clientCommand,3,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,CLIENT_NO_EVICT_Keyspecs,0,NULL,1),.args=CLIENT_NO_EVICT_Args},
+{MAKE_CMD("no-touch","Controls whether commands sent by the client affect the LRU/LFU of accessed keys.","O(1)","7.2.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,CLIENT_NO_TOUCH_History,0,CLIENT_NO_TOUCH_Tips,0,clientCommand,3,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,ACL_CATEGORY_CONNECTION,CLIENT_NO_TOUCH_Keyspecs,0,NULL,1),.args=CLIENT_NO_TOUCH_Args},
+{MAKE_CMD("pause","Suspends commands processing.","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,CLIENT_PAUSE_History,1,CLIENT_PAUSE_Tips,0,clientCommand,-3,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,CLIENT_PAUSE_Keyspecs,0,NULL,2),.args=CLIENT_PAUSE_Args},
+{MAKE_CMD("reply","Instructs the server whether to reply to commands.","O(1)","3.2.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,CLIENT_REPLY_History,0,CLIENT_REPLY_Tips,0,clientCommand,3,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,CLIENT_REPLY_Keyspecs,0,NULL,1),.args=CLIENT_REPLY_Args},
+{MAKE_CMD("setinfo","Sets information specific to the client or connection.","O(1)","7.2.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,CLIENT_SETINFO_History,0,CLIENT_SETINFO_Tips,0,clientSetinfoCommand,4,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,CLIENT_SETINFO_Keyspecs,0,NULL,1),.args=CLIENT_SETINFO_Args},
+{MAKE_CMD("setname","Sets the connection name.","O(1)","2.6.9",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,CLIENT_SETNAME_History,0,CLIENT_SETNAME_Tips,0,clientCommand,3,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,CLIENT_SETNAME_Keyspecs,0,NULL,1),.args=CLIENT_SETNAME_Args},
+{MAKE_CMD("tracking","Controls server-assisted client-side caching for the connection.","O(1). Some options may introduce additional complexity.","6.0.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,CLIENT_TRACKING_History,0,CLIENT_TRACKING_Tips,0,clientCommand,-3,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,CLIENT_TRACKING_Keyspecs,0,NULL,7),.args=CLIENT_TRACKING_Args},
+{MAKE_CMD("trackinginfo","Returns information about server-assisted client-side caching for the connection.","O(1)","6.2.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,CLIENT_TRACKINGINFO_History,0,CLIENT_TRACKINGINFO_Tips,0,clientCommand,2,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,CLIENT_TRACKINGINFO_Keyspecs,0,NULL,0)},
+{MAKE_CMD("unblock","Unblocks a client blocked by a blocking command from a different connection.","O(log N) where N is the number of client connections","5.0.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,CLIENT_UNBLOCK_History,0,CLIENT_UNBLOCK_Tips,0,clientCommand,-3,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,CLIENT_UNBLOCK_Keyspecs,0,NULL,2),.args=CLIENT_UNBLOCK_Args},
+{MAKE_CMD("unpause","Resumes processing commands from paused clients.","O(N) Where N is the number of paused clients","6.2.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,CLIENT_UNPAUSE_History,0,CLIENT_UNPAUSE_Tips,0,clientCommand,2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,CLIENT_UNPAUSE_Keyspecs,0,NULL,0)},
+{0}
+};
+
+/********** CLIENT ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* CLIENT history */
+#define CLIENT_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* CLIENT tips */
+#define CLIENT_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* CLIENT key specs */
+#define CLIENT_Keyspecs NULL
+#endif
+
+/********** ECHO ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* ECHO history */
+#define ECHO_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* ECHO tips */
+#define ECHO_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* ECHO key specs */
+#define ECHO_Keyspecs NULL
+#endif
+
+/* ECHO argument table */
+struct COMMAND_ARG ECHO_Args[] = {
+{MAKE_ARG("message",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** HELLO ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* HELLO history */
+commandHistory HELLO_History[] = {
+{"6.2.0","`protover` made optional; when called without arguments the command reports the current connection's context."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* HELLO tips */
+#define HELLO_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* HELLO key specs */
+#define HELLO_Keyspecs NULL
+#endif
+
+/* HELLO arguments auth argument table */
+struct COMMAND_ARG HELLO_arguments_auth_Subargs[] = {
+{MAKE_ARG("username",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("password",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* HELLO arguments argument table */
+struct COMMAND_ARG HELLO_arguments_Subargs[] = {
+{MAKE_ARG("protover",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("auth",ARG_TYPE_BLOCK,-1,"AUTH",NULL,NULL,CMD_ARG_OPTIONAL,2,NULL),.subargs=HELLO_arguments_auth_Subargs},
+{MAKE_ARG("clientname",ARG_TYPE_STRING,-1,"SETNAME",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/* HELLO argument table */
+struct COMMAND_ARG HELLO_Args[] = {
+{MAKE_ARG("arguments",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,3,NULL),.subargs=HELLO_arguments_Subargs},
+};
+
+/********** PING ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* PING history */
+#define PING_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* PING tips */
+const char *PING_Tips[] = {
+"request_policy:all_shards",
+"response_policy:all_succeeded",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* PING key specs */
+#define PING_Keyspecs NULL
+#endif
+
+/* PING argument table */
+struct COMMAND_ARG PING_Args[] = {
+{MAKE_ARG("message",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/********** QUIT ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* QUIT history */
+#define QUIT_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* QUIT tips */
+#define QUIT_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* QUIT key specs */
+#define QUIT_Keyspecs NULL
+#endif
+
+/********** RESET ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* RESET history */
+#define RESET_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* RESET tips */
+#define RESET_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* RESET key specs */
+#define RESET_Keyspecs NULL
+#endif
+
+/********** SELECT ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SELECT history */
+#define SELECT_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SELECT tips */
+#define SELECT_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SELECT key specs */
+#define SELECT_Keyspecs NULL
+#endif
+
+/* SELECT argument table */
+struct COMMAND_ARG SELECT_Args[] = {
+{MAKE_ARG("index",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** COPY ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* COPY history */
+#define COPY_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* COPY tips */
+#define COPY_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* COPY key specs */
+keySpec COPY_Keyspecs[2] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}},{NULL,CMD_KEY_OW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* COPY argument table */
+struct COMMAND_ARG COPY_Args[] = {
+{MAKE_ARG("source",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("destination",ARG_TYPE_KEY,1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("destination-db",ARG_TYPE_INTEGER,-1,"DB",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("replace",ARG_TYPE_PURE_TOKEN,-1,"REPLACE",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/********** DEL ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* DEL history */
+#define DEL_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* DEL tips */
+const char *DEL_Tips[] = {
+"request_policy:multi_shard",
+"response_policy:agg_sum",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* DEL key specs */
+keySpec DEL_Keyspecs[1] = {
+{NULL,CMD_KEY_RM|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={-1,1,0}}
+};
+#endif
+
+/* DEL argument table */
+struct COMMAND_ARG DEL_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** DUMP ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* DUMP history */
+#define DUMP_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* DUMP tips */
+const char *DUMP_Tips[] = {
+"nondeterministic_output",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* DUMP key specs */
+keySpec DUMP_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* DUMP argument table */
+struct COMMAND_ARG DUMP_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** EXISTS ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* EXISTS history */
+commandHistory EXISTS_History[] = {
+{"3.0.3","Accepts multiple `key` arguments."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* EXISTS tips */
+const char *EXISTS_Tips[] = {
+"request_policy:multi_shard",
+"response_policy:agg_sum",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* EXISTS key specs */
+keySpec EXISTS_Keyspecs[1] = {
+{NULL,CMD_KEY_RO,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={-1,1,0}}
+};
+#endif
+
+/* EXISTS argument table */
+struct COMMAND_ARG EXISTS_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** EXPIRE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* EXPIRE history */
+commandHistory EXPIRE_History[] = {
+{"7.0.0","Added options: `NX`, `XX`, `GT` and `LT`."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* EXPIRE tips */
+#define EXPIRE_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* EXPIRE key specs */
+keySpec EXPIRE_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* EXPIRE condition argument table */
+struct COMMAND_ARG EXPIRE_condition_Subargs[] = {
+{MAKE_ARG("nx",ARG_TYPE_PURE_TOKEN,-1,"NX",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("xx",ARG_TYPE_PURE_TOKEN,-1,"XX",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("gt",ARG_TYPE_PURE_TOKEN,-1,"GT",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("lt",ARG_TYPE_PURE_TOKEN,-1,"LT",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* EXPIRE argument table */
+struct COMMAND_ARG EXPIRE_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("seconds",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("condition",ARG_TYPE_ONEOF,-1,NULL,NULL,"7.0.0",CMD_ARG_OPTIONAL,4,NULL),.subargs=EXPIRE_condition_Subargs},
+};
+
+/********** EXPIREAT ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* EXPIREAT history */
+commandHistory EXPIREAT_History[] = {
+{"7.0.0","Added options: `NX`, `XX`, `GT` and `LT`."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* EXPIREAT tips */
+#define EXPIREAT_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* EXPIREAT key specs */
+keySpec EXPIREAT_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* EXPIREAT condition argument table */
+struct COMMAND_ARG EXPIREAT_condition_Subargs[] = {
+{MAKE_ARG("nx",ARG_TYPE_PURE_TOKEN,-1,"NX",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("xx",ARG_TYPE_PURE_TOKEN,-1,"XX",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("gt",ARG_TYPE_PURE_TOKEN,-1,"GT",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("lt",ARG_TYPE_PURE_TOKEN,-1,"LT",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* EXPIREAT argument table */
+struct COMMAND_ARG EXPIREAT_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("unix-time-seconds",ARG_TYPE_UNIX_TIME,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("condition",ARG_TYPE_ONEOF,-1,NULL,NULL,"7.0.0",CMD_ARG_OPTIONAL,4,NULL),.subargs=EXPIREAT_condition_Subargs},
+};
+
+/********** EXPIRETIME ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* EXPIRETIME history */
+#define EXPIRETIME_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* EXPIRETIME tips */
+#define EXPIRETIME_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* EXPIRETIME key specs */
+keySpec EXPIRETIME_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* EXPIRETIME argument table */
+struct COMMAND_ARG EXPIRETIME_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** KEYS ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* KEYS history */
+#define KEYS_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* KEYS tips */
+const char *KEYS_Tips[] = {
+"request_policy:all_shards",
+"nondeterministic_output_order",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* KEYS key specs */
+#define KEYS_Keyspecs NULL
+#endif
+
+/* KEYS argument table */
+struct COMMAND_ARG KEYS_Args[] = {
+{MAKE_ARG("pattern",ARG_TYPE_PATTERN,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** MIGRATE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* MIGRATE history */
+commandHistory MIGRATE_History[] = {
+{"3.0.0","Added the `COPY` and `REPLACE` options."},
+{"3.0.6","Added the `KEYS` option."},
+{"4.0.7","Added the `AUTH` option."},
+{"6.0.0","Added the `AUTH2` option."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* MIGRATE tips */
+const char *MIGRATE_Tips[] = {
+"nondeterministic_output",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* MIGRATE key specs */
+keySpec MIGRATE_Keyspecs[2] = {
+{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={3},KSPEC_FK_RANGE,.fk.range={0,1,0}},{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_DELETE|CMD_KEY_INCOMPLETE,KSPEC_BS_KEYWORD,.bs.keyword={"KEYS",-2},KSPEC_FK_RANGE,.fk.range={-1,1,0}}
+};
+#endif
+
+/* MIGRATE key_selector argument table */
+struct COMMAND_ARG MIGRATE_key_selector_Subargs[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("empty-string",ARG_TYPE_PURE_TOKEN,-1,"""",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* MIGRATE authentication auth2 argument table */
+struct COMMAND_ARG MIGRATE_authentication_auth2_Subargs[] = {
+{MAKE_ARG("username",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("password",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* MIGRATE authentication argument table */
+struct COMMAND_ARG MIGRATE_authentication_Subargs[] = {
+{MAKE_ARG("auth",ARG_TYPE_STRING,-1,"AUTH",NULL,"4.0.7",CMD_ARG_NONE,0,NULL),.display_text="password"},
+{MAKE_ARG("auth2",ARG_TYPE_BLOCK,-1,"AUTH2",NULL,"6.0.0",CMD_ARG_NONE,2,NULL),.subargs=MIGRATE_authentication_auth2_Subargs},
+};
+
+/* MIGRATE argument table */
+struct COMMAND_ARG MIGRATE_Args[] = {
+{MAKE_ARG("host",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("port",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("key-selector",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=MIGRATE_key_selector_Subargs},
+{MAKE_ARG("destination-db",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("timeout",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("copy",ARG_TYPE_PURE_TOKEN,-1,"COPY",NULL,"3.0.0",CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("replace",ARG_TYPE_PURE_TOKEN,-1,"REPLACE",NULL,"3.0.0",CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("authentication",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,2,NULL),.subargs=MIGRATE_authentication_Subargs},
+{MAKE_ARG("keys",ARG_TYPE_KEY,1,"KEYS",NULL,"3.0.6",CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE,0,NULL),.display_text="key"},
+};
+
+/********** MOVE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* MOVE history */
+#define MOVE_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* MOVE tips */
+#define MOVE_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* MOVE key specs */
+keySpec MOVE_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* MOVE argument table */
+struct COMMAND_ARG MOVE_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("db",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** OBJECT ENCODING ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* OBJECT ENCODING history */
+#define OBJECT_ENCODING_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* OBJECT ENCODING tips */
+const char *OBJECT_ENCODING_Tips[] = {
+"nondeterministic_output",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* OBJECT ENCODING key specs */
+keySpec OBJECT_ENCODING_Keyspecs[1] = {
+{NULL,CMD_KEY_RO,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* OBJECT ENCODING argument table */
+struct COMMAND_ARG OBJECT_ENCODING_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** OBJECT FREQ ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* OBJECT FREQ history */
+#define OBJECT_FREQ_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* OBJECT FREQ tips */
+const char *OBJECT_FREQ_Tips[] = {
+"nondeterministic_output",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* OBJECT FREQ key specs */
+keySpec OBJECT_FREQ_Keyspecs[1] = {
+{NULL,CMD_KEY_RO,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* OBJECT FREQ argument table */
+struct COMMAND_ARG OBJECT_FREQ_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** OBJECT HELP ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* OBJECT HELP history */
+#define OBJECT_HELP_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* OBJECT HELP tips */
+#define OBJECT_HELP_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* OBJECT HELP key specs */
+#define OBJECT_HELP_Keyspecs NULL
+#endif
+
+/********** OBJECT IDLETIME ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* OBJECT IDLETIME history */
+#define OBJECT_IDLETIME_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* OBJECT IDLETIME tips */
+const char *OBJECT_IDLETIME_Tips[] = {
+"nondeterministic_output",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* OBJECT IDLETIME key specs */
+keySpec OBJECT_IDLETIME_Keyspecs[1] = {
+{NULL,CMD_KEY_RO,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* OBJECT IDLETIME argument table */
+struct COMMAND_ARG OBJECT_IDLETIME_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** OBJECT REFCOUNT ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* OBJECT REFCOUNT history */
+#define OBJECT_REFCOUNT_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* OBJECT REFCOUNT tips */
+const char *OBJECT_REFCOUNT_Tips[] = {
+"nondeterministic_output",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* OBJECT REFCOUNT key specs */
+keySpec OBJECT_REFCOUNT_Keyspecs[1] = {
+{NULL,CMD_KEY_RO,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* OBJECT REFCOUNT argument table */
+struct COMMAND_ARG OBJECT_REFCOUNT_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* OBJECT command table */
+struct COMMAND_STRUCT OBJECT_Subcommands[] = {
+{MAKE_CMD("encoding","Returns the internal encoding of a Redis object.","O(1)","2.2.3",CMD_DOC_NONE,NULL,NULL,"generic",COMMAND_GROUP_GENERIC,OBJECT_ENCODING_History,0,OBJECT_ENCODING_Tips,1,objectCommand,3,CMD_READONLY,ACL_CATEGORY_KEYSPACE,OBJECT_ENCODING_Keyspecs,1,NULL,1),.args=OBJECT_ENCODING_Args},
+{MAKE_CMD("freq","Returns the logarithmic access frequency counter of a Redis object.","O(1)","4.0.0",CMD_DOC_NONE,NULL,NULL,"generic",COMMAND_GROUP_GENERIC,OBJECT_FREQ_History,0,OBJECT_FREQ_Tips,1,objectCommand,3,CMD_READONLY,ACL_CATEGORY_KEYSPACE,OBJECT_FREQ_Keyspecs,1,NULL,1),.args=OBJECT_FREQ_Args},
+{MAKE_CMD("help","Returns helpful text about the different subcommands.","O(1)","6.2.0",CMD_DOC_NONE,NULL,NULL,"generic",COMMAND_GROUP_GENERIC,OBJECT_HELP_History,0,OBJECT_HELP_Tips,0,objectCommand,2,CMD_LOADING|CMD_STALE,ACL_CATEGORY_KEYSPACE,OBJECT_HELP_Keyspecs,0,NULL,0)},
+{MAKE_CMD("idletime","Returns the time since the last access to a Redis object.","O(1)","2.2.3",CMD_DOC_NONE,NULL,NULL,"generic",COMMAND_GROUP_GENERIC,OBJECT_IDLETIME_History,0,OBJECT_IDLETIME_Tips,1,objectCommand,3,CMD_READONLY,ACL_CATEGORY_KEYSPACE,OBJECT_IDLETIME_Keyspecs,1,NULL,1),.args=OBJECT_IDLETIME_Args},
+{MAKE_CMD("refcount","Returns the reference count of a value of a key.","O(1)","2.2.3",CMD_DOC_NONE,NULL,NULL,"generic",COMMAND_GROUP_GENERIC,OBJECT_REFCOUNT_History,0,OBJECT_REFCOUNT_Tips,1,objectCommand,3,CMD_READONLY,ACL_CATEGORY_KEYSPACE,OBJECT_REFCOUNT_Keyspecs,1,NULL,1),.args=OBJECT_REFCOUNT_Args},
+{0}
+};
+
+/********** OBJECT ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* OBJECT history */
+#define OBJECT_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* OBJECT tips */
+#define OBJECT_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* OBJECT key specs */
+#define OBJECT_Keyspecs NULL
+#endif
+
+/********** PERSIST ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* PERSIST history */
+#define PERSIST_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* PERSIST tips */
+#define PERSIST_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* PERSIST key specs */
+keySpec PERSIST_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* PERSIST argument table */
+struct COMMAND_ARG PERSIST_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** PEXPIRE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* PEXPIRE history */
+commandHistory PEXPIRE_History[] = {
+{"7.0.0","Added options: `NX`, `XX`, `GT` and `LT`."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* PEXPIRE tips */
+#define PEXPIRE_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* PEXPIRE key specs */
+keySpec PEXPIRE_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* PEXPIRE condition argument table */
+struct COMMAND_ARG PEXPIRE_condition_Subargs[] = {
+{MAKE_ARG("nx",ARG_TYPE_PURE_TOKEN,-1,"NX",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("xx",ARG_TYPE_PURE_TOKEN,-1,"XX",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("gt",ARG_TYPE_PURE_TOKEN,-1,"GT",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("lt",ARG_TYPE_PURE_TOKEN,-1,"LT",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* PEXPIRE argument table */
+struct COMMAND_ARG PEXPIRE_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("milliseconds",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("condition",ARG_TYPE_ONEOF,-1,NULL,NULL,"7.0.0",CMD_ARG_OPTIONAL,4,NULL),.subargs=PEXPIRE_condition_Subargs},
+};
+
+/********** PEXPIREAT ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* PEXPIREAT history */
+commandHistory PEXPIREAT_History[] = {
+{"7.0.0","Added options: `NX`, `XX`, `GT` and `LT`."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* PEXPIREAT tips */
+#define PEXPIREAT_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* PEXPIREAT key specs */
+keySpec PEXPIREAT_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* PEXPIREAT condition argument table */
+struct COMMAND_ARG PEXPIREAT_condition_Subargs[] = {
+{MAKE_ARG("nx",ARG_TYPE_PURE_TOKEN,-1,"NX",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("xx",ARG_TYPE_PURE_TOKEN,-1,"XX",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("gt",ARG_TYPE_PURE_TOKEN,-1,"GT",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("lt",ARG_TYPE_PURE_TOKEN,-1,"LT",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* PEXPIREAT argument table */
+struct COMMAND_ARG PEXPIREAT_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("unix-time-milliseconds",ARG_TYPE_UNIX_TIME,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("condition",ARG_TYPE_ONEOF,-1,NULL,NULL,"7.0.0",CMD_ARG_OPTIONAL,4,NULL),.subargs=PEXPIREAT_condition_Subargs},
+};
+
+/********** PEXPIRETIME ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* PEXPIRETIME history */
+#define PEXPIRETIME_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* PEXPIRETIME tips */
+#define PEXPIRETIME_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* PEXPIRETIME key specs */
+keySpec PEXPIRETIME_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* PEXPIRETIME argument table */
+struct COMMAND_ARG PEXPIRETIME_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** PTTL ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* PTTL history */
+commandHistory PTTL_History[] = {
+{"2.8.0","Added the -2 reply."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* PTTL tips */
+const char *PTTL_Tips[] = {
+"nondeterministic_output",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* PTTL key specs */
+keySpec PTTL_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* PTTL argument table */
+struct COMMAND_ARG PTTL_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** RANDOMKEY ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* RANDOMKEY history */
+#define RANDOMKEY_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* RANDOMKEY tips */
+const char *RANDOMKEY_Tips[] = {
+"request_policy:all_shards",
+"nondeterministic_output",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* RANDOMKEY key specs */
+#define RANDOMKEY_Keyspecs NULL
+#endif
+
+/********** RENAME ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* RENAME history */
+#define RENAME_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* RENAME tips */
+#define RENAME_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* RENAME key specs */
+keySpec RENAME_Keyspecs[2] = {
+{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}},{NULL,CMD_KEY_OW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* RENAME argument table */
+struct COMMAND_ARG RENAME_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("newkey",ARG_TYPE_KEY,1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** RENAMENX ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* RENAMENX history */
+commandHistory RENAMENX_History[] = {
+{"3.2.0","The command no longer returns an error when source and destination names are the same."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* RENAMENX tips */
+#define RENAMENX_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* RENAMENX key specs */
+keySpec RENAMENX_Keyspecs[2] = {
+{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}},{NULL,CMD_KEY_OW|CMD_KEY_INSERT,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* RENAMENX argument table */
+struct COMMAND_ARG RENAMENX_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("newkey",ARG_TYPE_KEY,1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** RESTORE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* RESTORE history */
+commandHistory RESTORE_History[] = {
+{"3.0.0","Added the `REPLACE` modifier."},
+{"5.0.0","Added the `ABSTTL` modifier."},
+{"5.0.0","Added the `IDLETIME` and `FREQ` options."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* RESTORE tips */
+#define RESTORE_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* RESTORE key specs */
+keySpec RESTORE_Keyspecs[1] = {
+{NULL,CMD_KEY_OW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* RESTORE argument table */
+struct COMMAND_ARG RESTORE_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("ttl",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("serialized-value",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("replace",ARG_TYPE_PURE_TOKEN,-1,"REPLACE",NULL,"3.0.0",CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("absttl",ARG_TYPE_PURE_TOKEN,-1,"ABSTTL",NULL,"5.0.0",CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("seconds",ARG_TYPE_INTEGER,-1,"IDLETIME",NULL,"5.0.0",CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("frequency",ARG_TYPE_INTEGER,-1,"FREQ",NULL,"5.0.0",CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/********** SCAN ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SCAN history */
+commandHistory SCAN_History[] = {
+{"6.0.0","Added the `TYPE` subcommand."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SCAN tips */
+const char *SCAN_Tips[] = {
+"nondeterministic_output",
+"request_policy:special",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SCAN key specs */
+#define SCAN_Keyspecs NULL
+#endif
+
+/* SCAN argument table */
+struct COMMAND_ARG SCAN_Args[] = {
+{MAKE_ARG("cursor",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("pattern",ARG_TYPE_PATTERN,-1,"MATCH",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("count",ARG_TYPE_INTEGER,-1,"COUNT",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("type",ARG_TYPE_STRING,-1,"TYPE",NULL,"6.0.0",CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/********** SORT ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SORT history */
+#define SORT_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SORT tips */
+#define SORT_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SORT key specs */
+keySpec SORT_Keyspecs[3] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}},{"For the optional BY/GET keyword. It is marked 'unknown' because the key names derive from the content of the key we sort",CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_UNKNOWN,{{0}},KSPEC_FK_UNKNOWN,{{0}}},{"For the optional STORE keyword. It is marked 'unknown' because the keyword can appear anywhere in the argument array",CMD_KEY_OW|CMD_KEY_UPDATE,KSPEC_BS_UNKNOWN,{{0}},KSPEC_FK_UNKNOWN,{{0}}}
+};
+#endif
+
+/* SORT limit argument table */
+struct COMMAND_ARG SORT_limit_Subargs[] = {
+{MAKE_ARG("offset",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("count",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* SORT order argument table */
+struct COMMAND_ARG SORT_order_Subargs[] = {
+{MAKE_ARG("asc",ARG_TYPE_PURE_TOKEN,-1,"ASC",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("desc",ARG_TYPE_PURE_TOKEN,-1,"DESC",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* SORT argument table */
+struct COMMAND_ARG SORT_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("by-pattern",ARG_TYPE_PATTERN,1,"BY",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL),.display_text="pattern"},
+{MAKE_ARG("limit",ARG_TYPE_BLOCK,-1,"LIMIT",NULL,NULL,CMD_ARG_OPTIONAL,2,NULL),.subargs=SORT_limit_Subargs},
+{MAKE_ARG("get-pattern",ARG_TYPE_PATTERN,1,"GET",NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE|CMD_ARG_MULTIPLE_TOKEN,0,NULL),.display_text="pattern"},
+{MAKE_ARG("order",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,2,NULL),.subargs=SORT_order_Subargs},
+{MAKE_ARG("sorting",ARG_TYPE_PURE_TOKEN,-1,"ALPHA",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("destination",ARG_TYPE_KEY,2,"STORE",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/********** SORT_RO ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SORT_RO history */
+#define SORT_RO_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SORT_RO tips */
+#define SORT_RO_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SORT_RO key specs */
+keySpec SORT_RO_Keyspecs[2] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}},{"For the optional BY/GET keyword. It is marked 'unknown' because the key names derive from the content of the key we sort",CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_UNKNOWN,{{0}},KSPEC_FK_UNKNOWN,{{0}}}
+};
+#endif
+
+/* SORT_RO limit argument table */
+struct COMMAND_ARG SORT_RO_limit_Subargs[] = {
+{MAKE_ARG("offset",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("count",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* SORT_RO order argument table */
+struct COMMAND_ARG SORT_RO_order_Subargs[] = {
+{MAKE_ARG("asc",ARG_TYPE_PURE_TOKEN,-1,"ASC",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("desc",ARG_TYPE_PURE_TOKEN,-1,"DESC",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* SORT_RO argument table */
+struct COMMAND_ARG SORT_RO_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("by-pattern",ARG_TYPE_PATTERN,1,"BY",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL),.display_text="pattern"},
+{MAKE_ARG("limit",ARG_TYPE_BLOCK,-1,"LIMIT",NULL,NULL,CMD_ARG_OPTIONAL,2,NULL),.subargs=SORT_RO_limit_Subargs},
+{MAKE_ARG("get-pattern",ARG_TYPE_PATTERN,1,"GET",NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE|CMD_ARG_MULTIPLE_TOKEN,0,NULL),.display_text="pattern"},
+{MAKE_ARG("order",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,2,NULL),.subargs=SORT_RO_order_Subargs},
+{MAKE_ARG("sorting",ARG_TYPE_PURE_TOKEN,-1,"ALPHA",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/********** TOUCH ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* TOUCH history */
+#define TOUCH_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* TOUCH tips */
+const char *TOUCH_Tips[] = {
+"request_policy:multi_shard",
+"response_policy:agg_sum",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* TOUCH key specs */
+keySpec TOUCH_Keyspecs[1] = {
+{NULL,CMD_KEY_RO,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={-1,1,0}}
+};
+#endif
+
+/* TOUCH argument table */
+struct COMMAND_ARG TOUCH_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** TTL ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* TTL history */
+commandHistory TTL_History[] = {
+{"2.8.0","Added the -2 reply."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* TTL tips */
+const char *TTL_Tips[] = {
+"nondeterministic_output",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* TTL key specs */
+keySpec TTL_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* TTL argument table */
+struct COMMAND_ARG TTL_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** TYPE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* TYPE history */
+#define TYPE_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* TYPE tips */
+#define TYPE_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* TYPE key specs */
+keySpec TYPE_Keyspecs[1] = {
+{NULL,CMD_KEY_RO,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* TYPE argument table */
+struct COMMAND_ARG TYPE_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** UNLINK ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* UNLINK history */
+#define UNLINK_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* UNLINK tips */
+const char *UNLINK_Tips[] = {
+"request_policy:multi_shard",
+"response_policy:agg_sum",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* UNLINK key specs */
+keySpec UNLINK_Keyspecs[1] = {
+{NULL,CMD_KEY_RM|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={-1,1,0}}
+};
+#endif
+
+/* UNLINK argument table */
+struct COMMAND_ARG UNLINK_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** WAIT ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* WAIT history */
+#define WAIT_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* WAIT tips */
+const char *WAIT_Tips[] = {
+"request_policy:all_shards",
+"response_policy:agg_min",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* WAIT key specs */
+#define WAIT_Keyspecs NULL
+#endif
+
+/* WAIT argument table */
+struct COMMAND_ARG WAIT_Args[] = {
+{MAKE_ARG("numreplicas",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("timeout",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** WAITAOF ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* WAITAOF history */
+#define WAITAOF_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* WAITAOF tips */
+const char *WAITAOF_Tips[] = {
+"request_policy:all_shards",
+"response_policy:agg_min",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* WAITAOF key specs */
+#define WAITAOF_Keyspecs NULL
+#endif
+
+/* WAITAOF argument table */
+struct COMMAND_ARG WAITAOF_Args[] = {
+{MAKE_ARG("numlocal",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("numreplicas",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("timeout",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** GEOADD ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* GEOADD history */
+commandHistory GEOADD_History[] = {
+{"6.2.0","Added the `CH`, `NX` and `XX` options."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* GEOADD tips */
+#define GEOADD_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* GEOADD key specs */
+keySpec GEOADD_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* GEOADD condition argument table */
+struct COMMAND_ARG GEOADD_condition_Subargs[] = {
+{MAKE_ARG("nx",ARG_TYPE_PURE_TOKEN,-1,"NX",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("xx",ARG_TYPE_PURE_TOKEN,-1,"XX",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* GEOADD data argument table */
+struct COMMAND_ARG GEOADD_data_Subargs[] = {
+{MAKE_ARG("longitude",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("latitude",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("member",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* GEOADD argument table */
+struct COMMAND_ARG GEOADD_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("condition",ARG_TYPE_ONEOF,-1,NULL,NULL,"6.2.0",CMD_ARG_OPTIONAL,2,NULL),.subargs=GEOADD_condition_Subargs},
+{MAKE_ARG("change",ARG_TYPE_PURE_TOKEN,-1,"CH",NULL,"6.2.0",CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("data",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,3,NULL),.subargs=GEOADD_data_Subargs},
+};
+
+/********** GEODIST ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* GEODIST history */
+#define GEODIST_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* GEODIST tips */
+#define GEODIST_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* GEODIST key specs */
+keySpec GEODIST_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* GEODIST unit argument table */
+struct COMMAND_ARG GEODIST_unit_Subargs[] = {
+{MAKE_ARG("m",ARG_TYPE_PURE_TOKEN,-1,"M",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("km",ARG_TYPE_PURE_TOKEN,-1,"KM",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("ft",ARG_TYPE_PURE_TOKEN,-1,"FT",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("mi",ARG_TYPE_PURE_TOKEN,-1,"MI",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* GEODIST argument table */
+struct COMMAND_ARG GEODIST_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("member1",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("member2",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("unit",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,4,NULL),.subargs=GEODIST_unit_Subargs},
+};
+
+/********** GEOHASH ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* GEOHASH history */
+#define GEOHASH_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* GEOHASH tips */
+#define GEOHASH_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* GEOHASH key specs */
+keySpec GEOHASH_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* GEOHASH argument table */
+struct COMMAND_ARG GEOHASH_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("member",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** GEOPOS ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* GEOPOS history */
+#define GEOPOS_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* GEOPOS tips */
+#define GEOPOS_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* GEOPOS key specs */
+keySpec GEOPOS_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* GEOPOS argument table */
+struct COMMAND_ARG GEOPOS_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("member",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** GEORADIUS ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* GEORADIUS history */
+commandHistory GEORADIUS_History[] = {
+{"6.2.0","Added the `ANY` option for `COUNT`."},
+{"7.0.0","Added support for uppercase unit names."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* GEORADIUS tips */
+#define GEORADIUS_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* GEORADIUS key specs */
+keySpec GEORADIUS_Keyspecs[3] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}},{NULL,CMD_KEY_OW|CMD_KEY_UPDATE,KSPEC_BS_KEYWORD,.bs.keyword={"STORE",6},KSPEC_FK_RANGE,.fk.range={0,1,0}},{NULL,CMD_KEY_OW|CMD_KEY_UPDATE,KSPEC_BS_KEYWORD,.bs.keyword={"STOREDIST",6},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* GEORADIUS unit argument table */
+struct COMMAND_ARG GEORADIUS_unit_Subargs[] = {
+{MAKE_ARG("m",ARG_TYPE_PURE_TOKEN,-1,"M",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("km",ARG_TYPE_PURE_TOKEN,-1,"KM",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("ft",ARG_TYPE_PURE_TOKEN,-1,"FT",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("mi",ARG_TYPE_PURE_TOKEN,-1,"MI",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* GEORADIUS count_block argument table */
+struct COMMAND_ARG GEORADIUS_count_block_Subargs[] = {
+{MAKE_ARG("count",ARG_TYPE_INTEGER,-1,"COUNT",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("any",ARG_TYPE_PURE_TOKEN,-1,"ANY",NULL,"6.2.0",CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/* GEORADIUS order argument table */
+struct COMMAND_ARG GEORADIUS_order_Subargs[] = {
+{MAKE_ARG("asc",ARG_TYPE_PURE_TOKEN,-1,"ASC",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("desc",ARG_TYPE_PURE_TOKEN,-1,"DESC",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* GEORADIUS store argument table */
+struct COMMAND_ARG GEORADIUS_store_Subargs[] = {
+{MAKE_ARG("storekey",ARG_TYPE_KEY,1,"STORE",NULL,NULL,CMD_ARG_NONE,0,NULL),.display_text="key"},
+{MAKE_ARG("storedistkey",ARG_TYPE_KEY,2,"STOREDIST",NULL,NULL,CMD_ARG_NONE,0,NULL),.display_text="key"},
+};
+
+/* GEORADIUS argument table */
+struct COMMAND_ARG GEORADIUS_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("longitude",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("latitude",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("radius",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("unit",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,4,NULL),.subargs=GEORADIUS_unit_Subargs},
+{MAKE_ARG("withcoord",ARG_TYPE_PURE_TOKEN,-1,"WITHCOORD",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("withdist",ARG_TYPE_PURE_TOKEN,-1,"WITHDIST",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("withhash",ARG_TYPE_PURE_TOKEN,-1,"WITHHASH",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("count-block",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,2,NULL),.subargs=GEORADIUS_count_block_Subargs},
+{MAKE_ARG("order",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,2,NULL),.subargs=GEORADIUS_order_Subargs},
+{MAKE_ARG("store",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,2,NULL),.subargs=GEORADIUS_store_Subargs},
+};
+
+/********** GEORADIUSBYMEMBER ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* GEORADIUSBYMEMBER history */
+commandHistory GEORADIUSBYMEMBER_History[] = {
+{"7.0.0","Added support for uppercase unit names."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* GEORADIUSBYMEMBER tips */
+#define GEORADIUSBYMEMBER_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* GEORADIUSBYMEMBER key specs */
+keySpec GEORADIUSBYMEMBER_Keyspecs[3] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}},{NULL,CMD_KEY_OW|CMD_KEY_UPDATE,KSPEC_BS_KEYWORD,.bs.keyword={"STORE",5},KSPEC_FK_RANGE,.fk.range={0,1,0}},{NULL,CMD_KEY_OW|CMD_KEY_UPDATE,KSPEC_BS_KEYWORD,.bs.keyword={"STOREDIST",5},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* GEORADIUSBYMEMBER unit argument table */
+struct COMMAND_ARG GEORADIUSBYMEMBER_unit_Subargs[] = {
+{MAKE_ARG("m",ARG_TYPE_PURE_TOKEN,-1,"M",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("km",ARG_TYPE_PURE_TOKEN,-1,"KM",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("ft",ARG_TYPE_PURE_TOKEN,-1,"FT",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("mi",ARG_TYPE_PURE_TOKEN,-1,"MI",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* GEORADIUSBYMEMBER count_block argument table */
+struct COMMAND_ARG GEORADIUSBYMEMBER_count_block_Subargs[] = {
+{MAKE_ARG("count",ARG_TYPE_INTEGER,-1,"COUNT",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("any",ARG_TYPE_PURE_TOKEN,-1,"ANY",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/* GEORADIUSBYMEMBER order argument table */
+struct COMMAND_ARG GEORADIUSBYMEMBER_order_Subargs[] = {
+{MAKE_ARG("asc",ARG_TYPE_PURE_TOKEN,-1,"ASC",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("desc",ARG_TYPE_PURE_TOKEN,-1,"DESC",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* GEORADIUSBYMEMBER store argument table */
+struct COMMAND_ARG GEORADIUSBYMEMBER_store_Subargs[] = {
+{MAKE_ARG("storekey",ARG_TYPE_KEY,1,"STORE",NULL,NULL,CMD_ARG_NONE,0,NULL),.display_text="key"},
+{MAKE_ARG("storedistkey",ARG_TYPE_KEY,2,"STOREDIST",NULL,NULL,CMD_ARG_NONE,0,NULL),.display_text="key"},
+};
+
+/* GEORADIUSBYMEMBER argument table */
+struct COMMAND_ARG GEORADIUSBYMEMBER_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("member",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("radius",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("unit",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,4,NULL),.subargs=GEORADIUSBYMEMBER_unit_Subargs},
+{MAKE_ARG("withcoord",ARG_TYPE_PURE_TOKEN,-1,"WITHCOORD",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("withdist",ARG_TYPE_PURE_TOKEN,-1,"WITHDIST",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("withhash",ARG_TYPE_PURE_TOKEN,-1,"WITHHASH",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("count-block",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,2,NULL),.subargs=GEORADIUSBYMEMBER_count_block_Subargs},
+{MAKE_ARG("order",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,2,NULL),.subargs=GEORADIUSBYMEMBER_order_Subargs},
+{MAKE_ARG("store",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,2,NULL),.subargs=GEORADIUSBYMEMBER_store_Subargs},
+};
+
+/********** GEORADIUSBYMEMBER_RO ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* GEORADIUSBYMEMBER_RO history */
+#define GEORADIUSBYMEMBER_RO_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* GEORADIUSBYMEMBER_RO tips */
+#define GEORADIUSBYMEMBER_RO_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* GEORADIUSBYMEMBER_RO key specs */
+keySpec GEORADIUSBYMEMBER_RO_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* GEORADIUSBYMEMBER_RO unit argument table */
+struct COMMAND_ARG GEORADIUSBYMEMBER_RO_unit_Subargs[] = {
+{MAKE_ARG("m",ARG_TYPE_PURE_TOKEN,-1,"M",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("km",ARG_TYPE_PURE_TOKEN,-1,"KM",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("ft",ARG_TYPE_PURE_TOKEN,-1,"FT",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("mi",ARG_TYPE_PURE_TOKEN,-1,"MI",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* GEORADIUSBYMEMBER_RO count_block argument table */
+struct COMMAND_ARG GEORADIUSBYMEMBER_RO_count_block_Subargs[] = {
+{MAKE_ARG("count",ARG_TYPE_INTEGER,-1,"COUNT",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("any",ARG_TYPE_PURE_TOKEN,-1,"ANY",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/* GEORADIUSBYMEMBER_RO order argument table */
+struct COMMAND_ARG GEORADIUSBYMEMBER_RO_order_Subargs[] = {
+{MAKE_ARG("asc",ARG_TYPE_PURE_TOKEN,-1,"ASC",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("desc",ARG_TYPE_PURE_TOKEN,-1,"DESC",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* GEORADIUSBYMEMBER_RO argument table */
+struct COMMAND_ARG GEORADIUSBYMEMBER_RO_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("member",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("radius",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("unit",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,4,NULL),.subargs=GEORADIUSBYMEMBER_RO_unit_Subargs},
+{MAKE_ARG("withcoord",ARG_TYPE_PURE_TOKEN,-1,"WITHCOORD",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("withdist",ARG_TYPE_PURE_TOKEN,-1,"WITHDIST",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("withhash",ARG_TYPE_PURE_TOKEN,-1,"WITHHASH",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("count-block",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,2,NULL),.subargs=GEORADIUSBYMEMBER_RO_count_block_Subargs},
+{MAKE_ARG("order",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,2,NULL),.subargs=GEORADIUSBYMEMBER_RO_order_Subargs},
+};
+
+/********** GEORADIUS_RO ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* GEORADIUS_RO history */
+commandHistory GEORADIUS_RO_History[] = {
+{"6.2.0","Added the `ANY` option for `COUNT`."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* GEORADIUS_RO tips */
+#define GEORADIUS_RO_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* GEORADIUS_RO key specs */
+keySpec GEORADIUS_RO_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* GEORADIUS_RO unit argument table */
+struct COMMAND_ARG GEORADIUS_RO_unit_Subargs[] = {
+{MAKE_ARG("m",ARG_TYPE_PURE_TOKEN,-1,"M",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("km",ARG_TYPE_PURE_TOKEN,-1,"KM",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("ft",ARG_TYPE_PURE_TOKEN,-1,"FT",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("mi",ARG_TYPE_PURE_TOKEN,-1,"MI",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* GEORADIUS_RO count_block argument table */
+struct COMMAND_ARG GEORADIUS_RO_count_block_Subargs[] = {
+{MAKE_ARG("count",ARG_TYPE_INTEGER,-1,"COUNT",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("any",ARG_TYPE_PURE_TOKEN,-1,"ANY",NULL,"6.2.0",CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/* GEORADIUS_RO order argument table */
+struct COMMAND_ARG GEORADIUS_RO_order_Subargs[] = {
+{MAKE_ARG("asc",ARG_TYPE_PURE_TOKEN,-1,"ASC",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("desc",ARG_TYPE_PURE_TOKEN,-1,"DESC",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* GEORADIUS_RO argument table */
+struct COMMAND_ARG GEORADIUS_RO_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("longitude",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("latitude",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("radius",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("unit",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,4,NULL),.subargs=GEORADIUS_RO_unit_Subargs},
+{MAKE_ARG("withcoord",ARG_TYPE_PURE_TOKEN,-1,"WITHCOORD",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("withdist",ARG_TYPE_PURE_TOKEN,-1,"WITHDIST",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("withhash",ARG_TYPE_PURE_TOKEN,-1,"WITHHASH",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("count-block",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,2,NULL),.subargs=GEORADIUS_RO_count_block_Subargs},
+{MAKE_ARG("order",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,2,NULL),.subargs=GEORADIUS_RO_order_Subargs},
+};
+
+/********** GEOSEARCH ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* GEOSEARCH history */
+commandHistory GEOSEARCH_History[] = {
+{"7.0.0","Added support for uppercase unit names."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* GEOSEARCH tips */
+#define GEOSEARCH_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* GEOSEARCH key specs */
+keySpec GEOSEARCH_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* GEOSEARCH from fromlonlat argument table */
+struct COMMAND_ARG GEOSEARCH_from_fromlonlat_Subargs[] = {
+{MAKE_ARG("longitude",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("latitude",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* GEOSEARCH from argument table */
+struct COMMAND_ARG GEOSEARCH_from_Subargs[] = {
+{MAKE_ARG("member",ARG_TYPE_STRING,-1,"FROMMEMBER",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("fromlonlat",ARG_TYPE_BLOCK,-1,"FROMLONLAT",NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=GEOSEARCH_from_fromlonlat_Subargs},
+};
+
+/* GEOSEARCH by circle unit argument table */
+struct COMMAND_ARG GEOSEARCH_by_circle_unit_Subargs[] = {
+{MAKE_ARG("m",ARG_TYPE_PURE_TOKEN,-1,"M",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("km",ARG_TYPE_PURE_TOKEN,-1,"KM",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("ft",ARG_TYPE_PURE_TOKEN,-1,"FT",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("mi",ARG_TYPE_PURE_TOKEN,-1,"MI",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* GEOSEARCH by circle argument table */
+struct COMMAND_ARG GEOSEARCH_by_circle_Subargs[] = {
+{MAKE_ARG("radius",ARG_TYPE_DOUBLE,-1,"BYRADIUS",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("unit",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,4,NULL),.subargs=GEOSEARCH_by_circle_unit_Subargs},
+};
+
+/* GEOSEARCH by box unit argument table */
+struct COMMAND_ARG GEOSEARCH_by_box_unit_Subargs[] = {
+{MAKE_ARG("m",ARG_TYPE_PURE_TOKEN,-1,"M",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("km",ARG_TYPE_PURE_TOKEN,-1,"KM",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("ft",ARG_TYPE_PURE_TOKEN,-1,"FT",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("mi",ARG_TYPE_PURE_TOKEN,-1,"MI",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* GEOSEARCH by box argument table */
+struct COMMAND_ARG GEOSEARCH_by_box_Subargs[] = {
+{MAKE_ARG("width",ARG_TYPE_DOUBLE,-1,"BYBOX",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("height",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("unit",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,4,NULL),.subargs=GEOSEARCH_by_box_unit_Subargs},
+};
+
+/* GEOSEARCH by argument table */
+struct COMMAND_ARG GEOSEARCH_by_Subargs[] = {
+{MAKE_ARG("circle",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=GEOSEARCH_by_circle_Subargs},
+{MAKE_ARG("box",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_NONE,3,NULL),.subargs=GEOSEARCH_by_box_Subargs},
+};
+
+/* GEOSEARCH order argument table */
+struct COMMAND_ARG GEOSEARCH_order_Subargs[] = {
+{MAKE_ARG("asc",ARG_TYPE_PURE_TOKEN,-1,"ASC",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("desc",ARG_TYPE_PURE_TOKEN,-1,"DESC",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* GEOSEARCH count_block argument table */
+struct COMMAND_ARG GEOSEARCH_count_block_Subargs[] = {
+{MAKE_ARG("count",ARG_TYPE_INTEGER,-1,"COUNT",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("any",ARG_TYPE_PURE_TOKEN,-1,"ANY",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/* GEOSEARCH argument table */
+struct COMMAND_ARG GEOSEARCH_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("from",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=GEOSEARCH_from_Subargs},
+{MAKE_ARG("by",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=GEOSEARCH_by_Subargs},
+{MAKE_ARG("order",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,2,NULL),.subargs=GEOSEARCH_order_Subargs},
+{MAKE_ARG("count-block",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,2,NULL),.subargs=GEOSEARCH_count_block_Subargs},
+{MAKE_ARG("withcoord",ARG_TYPE_PURE_TOKEN,-1,"WITHCOORD",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("withdist",ARG_TYPE_PURE_TOKEN,-1,"WITHDIST",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("withhash",ARG_TYPE_PURE_TOKEN,-1,"WITHHASH",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/********** GEOSEARCHSTORE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* GEOSEARCHSTORE history */
+commandHistory GEOSEARCHSTORE_History[] = {
+{"7.0.0","Added support for uppercase unit names."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* GEOSEARCHSTORE tips */
+#define GEOSEARCHSTORE_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* GEOSEARCHSTORE key specs */
+keySpec GEOSEARCHSTORE_Keyspecs[2] = {
+{NULL,CMD_KEY_OW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}},{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* GEOSEARCHSTORE from fromlonlat argument table */
+struct COMMAND_ARG GEOSEARCHSTORE_from_fromlonlat_Subargs[] = {
+{MAKE_ARG("longitude",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("latitude",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* GEOSEARCHSTORE from argument table */
+struct COMMAND_ARG GEOSEARCHSTORE_from_Subargs[] = {
+{MAKE_ARG("member",ARG_TYPE_STRING,-1,"FROMMEMBER",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("fromlonlat",ARG_TYPE_BLOCK,-1,"FROMLONLAT",NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=GEOSEARCHSTORE_from_fromlonlat_Subargs},
+};
+
+/* GEOSEARCHSTORE by circle unit argument table */
+struct COMMAND_ARG GEOSEARCHSTORE_by_circle_unit_Subargs[] = {
+{MAKE_ARG("m",ARG_TYPE_PURE_TOKEN,-1,"M",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("km",ARG_TYPE_PURE_TOKEN,-1,"KM",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("ft",ARG_TYPE_PURE_TOKEN,-1,"FT",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("mi",ARG_TYPE_PURE_TOKEN,-1,"MI",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* GEOSEARCHSTORE by circle argument table */
+struct COMMAND_ARG GEOSEARCHSTORE_by_circle_Subargs[] = {
+{MAKE_ARG("radius",ARG_TYPE_DOUBLE,-1,"BYRADIUS",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("unit",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,4,NULL),.subargs=GEOSEARCHSTORE_by_circle_unit_Subargs},
+};
+
+/* GEOSEARCHSTORE by box unit argument table */
+struct COMMAND_ARG GEOSEARCHSTORE_by_box_unit_Subargs[] = {
+{MAKE_ARG("m",ARG_TYPE_PURE_TOKEN,-1,"M",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("km",ARG_TYPE_PURE_TOKEN,-1,"KM",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("ft",ARG_TYPE_PURE_TOKEN,-1,"FT",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("mi",ARG_TYPE_PURE_TOKEN,-1,"MI",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* GEOSEARCHSTORE by box argument table */
+struct COMMAND_ARG GEOSEARCHSTORE_by_box_Subargs[] = {
+{MAKE_ARG("width",ARG_TYPE_DOUBLE,-1,"BYBOX",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("height",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("unit",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,4,NULL),.subargs=GEOSEARCHSTORE_by_box_unit_Subargs},
+};
+
+/* GEOSEARCHSTORE by argument table */
+struct COMMAND_ARG GEOSEARCHSTORE_by_Subargs[] = {
+{MAKE_ARG("circle",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=GEOSEARCHSTORE_by_circle_Subargs},
+{MAKE_ARG("box",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_NONE,3,NULL),.subargs=GEOSEARCHSTORE_by_box_Subargs},
+};
+
+/* GEOSEARCHSTORE order argument table */
+struct COMMAND_ARG GEOSEARCHSTORE_order_Subargs[] = {
+{MAKE_ARG("asc",ARG_TYPE_PURE_TOKEN,-1,"ASC",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("desc",ARG_TYPE_PURE_TOKEN,-1,"DESC",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* GEOSEARCHSTORE count_block argument table */
+struct COMMAND_ARG GEOSEARCHSTORE_count_block_Subargs[] = {
+{MAKE_ARG("count",ARG_TYPE_INTEGER,-1,"COUNT",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("any",ARG_TYPE_PURE_TOKEN,-1,"ANY",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/* GEOSEARCHSTORE argument table */
+struct COMMAND_ARG GEOSEARCHSTORE_Args[] = {
+{MAKE_ARG("destination",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("source",ARG_TYPE_KEY,1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("from",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=GEOSEARCHSTORE_from_Subargs},
+{MAKE_ARG("by",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=GEOSEARCHSTORE_by_Subargs},
+{MAKE_ARG("order",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,2,NULL),.subargs=GEOSEARCHSTORE_order_Subargs},
+{MAKE_ARG("count-block",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,2,NULL),.subargs=GEOSEARCHSTORE_count_block_Subargs},
+{MAKE_ARG("storedist",ARG_TYPE_PURE_TOKEN,-1,"STOREDIST",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/********** HDEL ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* HDEL history */
+commandHistory HDEL_History[] = {
+{"2.4.0","Accepts multiple `field` arguments."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* HDEL tips */
+#define HDEL_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* HDEL key specs */
+keySpec HDEL_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* HDEL argument table */
+struct COMMAND_ARG HDEL_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("field",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** HEXISTS ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* HEXISTS history */
+#define HEXISTS_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* HEXISTS tips */
+#define HEXISTS_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* HEXISTS key specs */
+keySpec HEXISTS_Keyspecs[1] = {
+{NULL,CMD_KEY_RO,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* HEXISTS argument table */
+struct COMMAND_ARG HEXISTS_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("field",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** HGET ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* HGET history */
+#define HGET_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* HGET tips */
+#define HGET_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* HGET key specs */
+keySpec HGET_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* HGET argument table */
+struct COMMAND_ARG HGET_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("field",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** HGETALL ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* HGETALL history */
+#define HGETALL_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* HGETALL tips */
+const char *HGETALL_Tips[] = {
+"nondeterministic_output_order",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* HGETALL key specs */
+keySpec HGETALL_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* HGETALL argument table */
+struct COMMAND_ARG HGETALL_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** HINCRBY ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* HINCRBY history */
+#define HINCRBY_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* HINCRBY tips */
+#define HINCRBY_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* HINCRBY key specs */
+keySpec HINCRBY_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* HINCRBY argument table */
+struct COMMAND_ARG HINCRBY_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("field",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("increment",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** HINCRBYFLOAT ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* HINCRBYFLOAT history */
+#define HINCRBYFLOAT_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* HINCRBYFLOAT tips */
+#define HINCRBYFLOAT_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* HINCRBYFLOAT key specs */
+keySpec HINCRBYFLOAT_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* HINCRBYFLOAT argument table */
+struct COMMAND_ARG HINCRBYFLOAT_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("field",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("increment",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** HKEYS ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* HKEYS history */
+#define HKEYS_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* HKEYS tips */
+const char *HKEYS_Tips[] = {
+"nondeterministic_output_order",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* HKEYS key specs */
+keySpec HKEYS_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* HKEYS argument table */
+struct COMMAND_ARG HKEYS_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** HLEN ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* HLEN history */
+#define HLEN_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* HLEN tips */
+#define HLEN_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* HLEN key specs */
+keySpec HLEN_Keyspecs[1] = {
+{NULL,CMD_KEY_RO,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* HLEN argument table */
+struct COMMAND_ARG HLEN_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** HMGET ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* HMGET history */
+#define HMGET_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* HMGET tips */
+#define HMGET_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* HMGET key specs */
+keySpec HMGET_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* HMGET argument table */
+struct COMMAND_ARG HMGET_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("field",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** HMSET ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* HMSET history */
+#define HMSET_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* HMSET tips */
+#define HMSET_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* HMSET key specs */
+keySpec HMSET_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* HMSET data argument table */
+struct COMMAND_ARG HMSET_data_Subargs[] = {
+{MAKE_ARG("field",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("value",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* HMSET argument table */
+struct COMMAND_ARG HMSET_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("data",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,2,NULL),.subargs=HMSET_data_Subargs},
+};
+
+/********** HRANDFIELD ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* HRANDFIELD history */
+#define HRANDFIELD_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* HRANDFIELD tips */
+const char *HRANDFIELD_Tips[] = {
+"nondeterministic_output",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* HRANDFIELD key specs */
+keySpec HRANDFIELD_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* HRANDFIELD options argument table */
+struct COMMAND_ARG HRANDFIELD_options_Subargs[] = {
+{MAKE_ARG("count",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("withvalues",ARG_TYPE_PURE_TOKEN,-1,"WITHVALUES",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/* HRANDFIELD argument table */
+struct COMMAND_ARG HRANDFIELD_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("options",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,2,NULL),.subargs=HRANDFIELD_options_Subargs},
+};
+
+/********** HSCAN ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* HSCAN history */
+#define HSCAN_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* HSCAN tips */
+const char *HSCAN_Tips[] = {
+"nondeterministic_output",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* HSCAN key specs */
+keySpec HSCAN_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* HSCAN argument table */
+struct COMMAND_ARG HSCAN_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("cursor",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("pattern",ARG_TYPE_PATTERN,-1,"MATCH",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("count",ARG_TYPE_INTEGER,-1,"COUNT",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/********** HSET ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* HSET history */
+commandHistory HSET_History[] = {
+{"4.0.0","Accepts multiple `field` and `value` arguments."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* HSET tips */
+#define HSET_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* HSET key specs */
+keySpec HSET_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* HSET data argument table */
+struct COMMAND_ARG HSET_data_Subargs[] = {
+{MAKE_ARG("field",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("value",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* HSET argument table */
+struct COMMAND_ARG HSET_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("data",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,2,NULL),.subargs=HSET_data_Subargs},
+};
+
+/********** HSETNX ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* HSETNX history */
+#define HSETNX_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* HSETNX tips */
+#define HSETNX_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* HSETNX key specs */
+keySpec HSETNX_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_INSERT,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* HSETNX argument table */
+struct COMMAND_ARG HSETNX_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("field",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("value",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** HSTRLEN ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* HSTRLEN history */
+#define HSTRLEN_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* HSTRLEN tips */
+#define HSTRLEN_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* HSTRLEN key specs */
+keySpec HSTRLEN_Keyspecs[1] = {
+{NULL,CMD_KEY_RO,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* HSTRLEN argument table */
+struct COMMAND_ARG HSTRLEN_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("field",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** HVALS ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* HVALS history */
+#define HVALS_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* HVALS tips */
+const char *HVALS_Tips[] = {
+"nondeterministic_output_order",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* HVALS key specs */
+keySpec HVALS_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* HVALS argument table */
+struct COMMAND_ARG HVALS_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** PFADD ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* PFADD history */
+#define PFADD_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* PFADD tips */
+#define PFADD_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* PFADD key specs */
+keySpec PFADD_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_INSERT,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* PFADD argument table */
+struct COMMAND_ARG PFADD_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("element",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** PFCOUNT ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* PFCOUNT history */
+#define PFCOUNT_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* PFCOUNT tips */
+#define PFCOUNT_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* PFCOUNT key specs */
+keySpec PFCOUNT_Keyspecs[1] = {
+{"RW because it may change the internal representation of the key, and propagate to replicas",CMD_KEY_RW|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={-1,1,0}}
+};
+#endif
+
+/* PFCOUNT argument table */
+struct COMMAND_ARG PFCOUNT_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** PFDEBUG ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* PFDEBUG history */
+#define PFDEBUG_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* PFDEBUG tips */
+#define PFDEBUG_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* PFDEBUG key specs */
+keySpec PFDEBUG_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* PFDEBUG argument table */
+struct COMMAND_ARG PFDEBUG_Args[] = {
+{MAKE_ARG("subcommand",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** PFMERGE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* PFMERGE history */
+#define PFMERGE_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* PFMERGE tips */
+#define PFMERGE_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* PFMERGE key specs */
+keySpec PFMERGE_Keyspecs[2] = {
+{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_INSERT,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}},{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={-1,1,0}}
+};
+#endif
+
+/* PFMERGE argument table */
+struct COMMAND_ARG PFMERGE_Args[] = {
+{MAKE_ARG("destkey",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("sourcekey",ARG_TYPE_KEY,1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** PFSELFTEST ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* PFSELFTEST history */
+#define PFSELFTEST_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* PFSELFTEST tips */
+#define PFSELFTEST_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* PFSELFTEST key specs */
+#define PFSELFTEST_Keyspecs NULL
+#endif
+
+/********** BLMOVE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* BLMOVE history */
+#define BLMOVE_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* BLMOVE tips */
+#define BLMOVE_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* BLMOVE key specs */
+keySpec BLMOVE_Keyspecs[2] = {
+{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}},{NULL,CMD_KEY_RW|CMD_KEY_INSERT,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* BLMOVE wherefrom argument table */
+struct COMMAND_ARG BLMOVE_wherefrom_Subargs[] = {
+{MAKE_ARG("left",ARG_TYPE_PURE_TOKEN,-1,"LEFT",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("right",ARG_TYPE_PURE_TOKEN,-1,"RIGHT",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* BLMOVE whereto argument table */
+struct COMMAND_ARG BLMOVE_whereto_Subargs[] = {
+{MAKE_ARG("left",ARG_TYPE_PURE_TOKEN,-1,"LEFT",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("right",ARG_TYPE_PURE_TOKEN,-1,"RIGHT",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* BLMOVE argument table */
+struct COMMAND_ARG BLMOVE_Args[] = {
+{MAKE_ARG("source",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("destination",ARG_TYPE_KEY,1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("wherefrom",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=BLMOVE_wherefrom_Subargs},
+{MAKE_ARG("whereto",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=BLMOVE_whereto_Subargs},
+{MAKE_ARG("timeout",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** BLMPOP ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* BLMPOP history */
+#define BLMPOP_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* BLMPOP tips */
+#define BLMPOP_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* BLMPOP key specs */
+keySpec BLMPOP_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_KEYNUM,.fk.keynum={0,1,1}}
+};
+#endif
+
+/* BLMPOP where argument table */
+struct COMMAND_ARG BLMPOP_where_Subargs[] = {
+{MAKE_ARG("left",ARG_TYPE_PURE_TOKEN,-1,"LEFT",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("right",ARG_TYPE_PURE_TOKEN,-1,"RIGHT",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* BLMPOP argument table */
+struct COMMAND_ARG BLMPOP_Args[] = {
+{MAKE_ARG("timeout",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("numkeys",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+{MAKE_ARG("where",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=BLMPOP_where_Subargs},
+{MAKE_ARG("count",ARG_TYPE_INTEGER,-1,"COUNT",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/********** BLPOP ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* BLPOP history */
+commandHistory BLPOP_History[] = {
+{"6.0.0","`timeout` is interpreted as a double instead of an integer."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* BLPOP tips */
+#define BLPOP_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* BLPOP key specs */
+keySpec BLPOP_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={-2,1,0}}
+};
+#endif
+
+/* BLPOP argument table */
+struct COMMAND_ARG BLPOP_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+{MAKE_ARG("timeout",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** BRPOP ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* BRPOP history */
+commandHistory BRPOP_History[] = {
+{"6.0.0","`timeout` is interpreted as a double instead of an integer."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* BRPOP tips */
+#define BRPOP_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* BRPOP key specs */
+keySpec BRPOP_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={-2,1,0}}
+};
+#endif
+
+/* BRPOP argument table */
+struct COMMAND_ARG BRPOP_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+{MAKE_ARG("timeout",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** BRPOPLPUSH ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* BRPOPLPUSH history */
+commandHistory BRPOPLPUSH_History[] = {
+{"6.0.0","`timeout` is interpreted as a double instead of an integer."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* BRPOPLPUSH tips */
+#define BRPOPLPUSH_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* BRPOPLPUSH key specs */
+keySpec BRPOPLPUSH_Keyspecs[2] = {
+{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}},{NULL,CMD_KEY_RW|CMD_KEY_INSERT,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* BRPOPLPUSH argument table */
+struct COMMAND_ARG BRPOPLPUSH_Args[] = {
+{MAKE_ARG("source",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("destination",ARG_TYPE_KEY,1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("timeout",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** LINDEX ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* LINDEX history */
+#define LINDEX_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* LINDEX tips */
+#define LINDEX_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* LINDEX key specs */
+keySpec LINDEX_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* LINDEX argument table */
+struct COMMAND_ARG LINDEX_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("index",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** LINSERT ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* LINSERT history */
+#define LINSERT_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* LINSERT tips */
+#define LINSERT_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* LINSERT key specs */
+keySpec LINSERT_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_INSERT,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* LINSERT where argument table */
+struct COMMAND_ARG LINSERT_where_Subargs[] = {
+{MAKE_ARG("before",ARG_TYPE_PURE_TOKEN,-1,"BEFORE",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("after",ARG_TYPE_PURE_TOKEN,-1,"AFTER",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* LINSERT argument table */
+struct COMMAND_ARG LINSERT_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("where",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=LINSERT_where_Subargs},
+{MAKE_ARG("pivot",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("element",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** LLEN ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* LLEN history */
+#define LLEN_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* LLEN tips */
+#define LLEN_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* LLEN key specs */
+keySpec LLEN_Keyspecs[1] = {
+{NULL,CMD_KEY_RO,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* LLEN argument table */
+struct COMMAND_ARG LLEN_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** LMOVE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* LMOVE history */
+#define LMOVE_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* LMOVE tips */
+#define LMOVE_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* LMOVE key specs */
+keySpec LMOVE_Keyspecs[2] = {
+{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}},{NULL,CMD_KEY_RW|CMD_KEY_INSERT,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* LMOVE wherefrom argument table */
+struct COMMAND_ARG LMOVE_wherefrom_Subargs[] = {
+{MAKE_ARG("left",ARG_TYPE_PURE_TOKEN,-1,"LEFT",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("right",ARG_TYPE_PURE_TOKEN,-1,"RIGHT",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* LMOVE whereto argument table */
+struct COMMAND_ARG LMOVE_whereto_Subargs[] = {
+{MAKE_ARG("left",ARG_TYPE_PURE_TOKEN,-1,"LEFT",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("right",ARG_TYPE_PURE_TOKEN,-1,"RIGHT",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* LMOVE argument table */
+struct COMMAND_ARG LMOVE_Args[] = {
+{MAKE_ARG("source",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("destination",ARG_TYPE_KEY,1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("wherefrom",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=LMOVE_wherefrom_Subargs},
+{MAKE_ARG("whereto",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=LMOVE_whereto_Subargs},
+};
+
+/********** LMPOP ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* LMPOP history */
+#define LMPOP_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* LMPOP tips */
+#define LMPOP_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* LMPOP key specs */
+keySpec LMPOP_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_KEYNUM,.fk.keynum={0,1,1}}
+};
+#endif
+
+/* LMPOP where argument table */
+struct COMMAND_ARG LMPOP_where_Subargs[] = {
+{MAKE_ARG("left",ARG_TYPE_PURE_TOKEN,-1,"LEFT",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("right",ARG_TYPE_PURE_TOKEN,-1,"RIGHT",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* LMPOP argument table */
+struct COMMAND_ARG LMPOP_Args[] = {
+{MAKE_ARG("numkeys",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+{MAKE_ARG("where",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=LMPOP_where_Subargs},
+{MAKE_ARG("count",ARG_TYPE_INTEGER,-1,"COUNT",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/********** LPOP ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* LPOP history */
+commandHistory LPOP_History[] = {
+{"6.2.0","Added the `count` argument."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* LPOP tips */
+#define LPOP_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* LPOP key specs */
+keySpec LPOP_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* LPOP argument table */
+struct COMMAND_ARG LPOP_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("count",ARG_TYPE_INTEGER,-1,NULL,NULL,"6.2.0",CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/********** LPOS ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* LPOS history */
+#define LPOS_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* LPOS tips */
+#define LPOS_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* LPOS key specs */
+keySpec LPOS_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* LPOS argument table */
+struct COMMAND_ARG LPOS_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("element",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("rank",ARG_TYPE_INTEGER,-1,"RANK",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("num-matches",ARG_TYPE_INTEGER,-1,"COUNT",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("len",ARG_TYPE_INTEGER,-1,"MAXLEN",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/********** LPUSH ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* LPUSH history */
+commandHistory LPUSH_History[] = {
+{"2.4.0","Accepts multiple `element` arguments."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* LPUSH tips */
+#define LPUSH_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* LPUSH key specs */
+keySpec LPUSH_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_INSERT,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* LPUSH argument table */
+struct COMMAND_ARG LPUSH_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("element",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** LPUSHX ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* LPUSHX history */
+commandHistory LPUSHX_History[] = {
+{"4.0.0","Accepts multiple `element` arguments."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* LPUSHX tips */
+#define LPUSHX_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* LPUSHX key specs */
+keySpec LPUSHX_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_INSERT,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* LPUSHX argument table */
+struct COMMAND_ARG LPUSHX_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("element",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** LRANGE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* LRANGE history */
+#define LRANGE_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* LRANGE tips */
+#define LRANGE_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* LRANGE key specs */
+keySpec LRANGE_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* LRANGE argument table */
+struct COMMAND_ARG LRANGE_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("start",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("stop",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** LREM ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* LREM history */
+#define LREM_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* LREM tips */
+#define LREM_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* LREM key specs */
+keySpec LREM_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* LREM argument table */
+struct COMMAND_ARG LREM_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("count",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("element",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** LSET ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* LSET history */
+#define LSET_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* LSET tips */
+#define LSET_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* LSET key specs */
+keySpec LSET_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* LSET argument table */
+struct COMMAND_ARG LSET_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("index",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("element",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** LTRIM ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* LTRIM history */
+#define LTRIM_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* LTRIM tips */
+#define LTRIM_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* LTRIM key specs */
+keySpec LTRIM_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* LTRIM argument table */
+struct COMMAND_ARG LTRIM_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("start",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("stop",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** RPOP ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* RPOP history */
+commandHistory RPOP_History[] = {
+{"6.2.0","Added the `count` argument."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* RPOP tips */
+#define RPOP_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* RPOP key specs */
+keySpec RPOP_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* RPOP argument table */
+struct COMMAND_ARG RPOP_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("count",ARG_TYPE_INTEGER,-1,NULL,NULL,"6.2.0",CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/********** RPOPLPUSH ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* RPOPLPUSH history */
+#define RPOPLPUSH_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* RPOPLPUSH tips */
+#define RPOPLPUSH_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* RPOPLPUSH key specs */
+keySpec RPOPLPUSH_Keyspecs[2] = {
+{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}},{NULL,CMD_KEY_RW|CMD_KEY_INSERT,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* RPOPLPUSH argument table */
+struct COMMAND_ARG RPOPLPUSH_Args[] = {
+{MAKE_ARG("source",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("destination",ARG_TYPE_KEY,1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** RPUSH ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* RPUSH history */
+commandHistory RPUSH_History[] = {
+{"2.4.0","Accepts multiple `element` arguments."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* RPUSH tips */
+#define RPUSH_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* RPUSH key specs */
+keySpec RPUSH_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_INSERT,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* RPUSH argument table */
+struct COMMAND_ARG RPUSH_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("element",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** RPUSHX ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* RPUSHX history */
+commandHistory RPUSHX_History[] = {
+{"4.0.0","Accepts multiple `element` arguments."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* RPUSHX tips */
+#define RPUSHX_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* RPUSHX key specs */
+keySpec RPUSHX_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_INSERT,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* RPUSHX argument table */
+struct COMMAND_ARG RPUSHX_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("element",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** PSUBSCRIBE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* PSUBSCRIBE history */
+#define PSUBSCRIBE_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* PSUBSCRIBE tips */
+#define PSUBSCRIBE_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* PSUBSCRIBE key specs */
+#define PSUBSCRIBE_Keyspecs NULL
+#endif
+
+/* PSUBSCRIBE argument table */
+struct COMMAND_ARG PSUBSCRIBE_Args[] = {
+{MAKE_ARG("pattern",ARG_TYPE_PATTERN,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** PUBLISH ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* PUBLISH history */
+#define PUBLISH_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* PUBLISH tips */
+#define PUBLISH_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* PUBLISH key specs */
+#define PUBLISH_Keyspecs NULL
+#endif
+
+/* PUBLISH argument table */
+struct COMMAND_ARG PUBLISH_Args[] = {
+{MAKE_ARG("channel",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("message",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** PUBSUB CHANNELS ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* PUBSUB CHANNELS history */
+#define PUBSUB_CHANNELS_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* PUBSUB CHANNELS tips */
+#define PUBSUB_CHANNELS_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* PUBSUB CHANNELS key specs */
+#define PUBSUB_CHANNELS_Keyspecs NULL
+#endif
+
+/* PUBSUB CHANNELS argument table */
+struct COMMAND_ARG PUBSUB_CHANNELS_Args[] = {
+{MAKE_ARG("pattern",ARG_TYPE_PATTERN,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/********** PUBSUB HELP ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* PUBSUB HELP history */
+#define PUBSUB_HELP_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* PUBSUB HELP tips */
+#define PUBSUB_HELP_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* PUBSUB HELP key specs */
+#define PUBSUB_HELP_Keyspecs NULL
+#endif
+
+/********** PUBSUB NUMPAT ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* PUBSUB NUMPAT history */
+#define PUBSUB_NUMPAT_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* PUBSUB NUMPAT tips */
+#define PUBSUB_NUMPAT_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* PUBSUB NUMPAT key specs */
+#define PUBSUB_NUMPAT_Keyspecs NULL
+#endif
+
+/********** PUBSUB NUMSUB ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* PUBSUB NUMSUB history */
+#define PUBSUB_NUMSUB_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* PUBSUB NUMSUB tips */
+#define PUBSUB_NUMSUB_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* PUBSUB NUMSUB key specs */
+#define PUBSUB_NUMSUB_Keyspecs NULL
+#endif
+
+/* PUBSUB NUMSUB argument table */
+struct COMMAND_ARG PUBSUB_NUMSUB_Args[] = {
+{MAKE_ARG("channel",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** PUBSUB SHARDCHANNELS ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* PUBSUB SHARDCHANNELS history */
+#define PUBSUB_SHARDCHANNELS_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* PUBSUB SHARDCHANNELS tips */
+#define PUBSUB_SHARDCHANNELS_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* PUBSUB SHARDCHANNELS key specs */
+#define PUBSUB_SHARDCHANNELS_Keyspecs NULL
+#endif
+
+/* PUBSUB SHARDCHANNELS argument table */
+struct COMMAND_ARG PUBSUB_SHARDCHANNELS_Args[] = {
+{MAKE_ARG("pattern",ARG_TYPE_PATTERN,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/********** PUBSUB SHARDNUMSUB ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* PUBSUB SHARDNUMSUB history */
+#define PUBSUB_SHARDNUMSUB_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* PUBSUB SHARDNUMSUB tips */
+#define PUBSUB_SHARDNUMSUB_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* PUBSUB SHARDNUMSUB key specs */
+#define PUBSUB_SHARDNUMSUB_Keyspecs NULL
+#endif
+
+/* PUBSUB SHARDNUMSUB argument table */
+struct COMMAND_ARG PUBSUB_SHARDNUMSUB_Args[] = {
+{MAKE_ARG("shardchannel",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/* PUBSUB command table */
+struct COMMAND_STRUCT PUBSUB_Subcommands[] = {
+{MAKE_CMD("channels","Returns the active channels.","O(N) where N is the number of active channels, and assuming constant time pattern matching (relatively short channels and patterns)","2.8.0",CMD_DOC_NONE,NULL,NULL,"pubsub",COMMAND_GROUP_PUBSUB,PUBSUB_CHANNELS_History,0,PUBSUB_CHANNELS_Tips,0,pubsubCommand,-2,CMD_PUBSUB|CMD_LOADING|CMD_STALE,0,PUBSUB_CHANNELS_Keyspecs,0,NULL,1),.args=PUBSUB_CHANNELS_Args},
+{MAKE_CMD("help","Returns helpful text about the different subcommands.","O(1)","6.2.0",CMD_DOC_NONE,NULL,NULL,"pubsub",COMMAND_GROUP_PUBSUB,PUBSUB_HELP_History,0,PUBSUB_HELP_Tips,0,pubsubCommand,2,CMD_LOADING|CMD_STALE,0,PUBSUB_HELP_Keyspecs,0,NULL,0)},
+{MAKE_CMD("numpat","Returns a count of unique pattern subscriptions.","O(1)","2.8.0",CMD_DOC_NONE,NULL,NULL,"pubsub",COMMAND_GROUP_PUBSUB,PUBSUB_NUMPAT_History,0,PUBSUB_NUMPAT_Tips,0,pubsubCommand,2,CMD_PUBSUB|CMD_LOADING|CMD_STALE,0,PUBSUB_NUMPAT_Keyspecs,0,NULL,0)},
+{MAKE_CMD("numsub","Returns a count of subscribers to channels.","O(N) for the NUMSUB subcommand, where N is the number of requested channels","2.8.0",CMD_DOC_NONE,NULL,NULL,"pubsub",COMMAND_GROUP_PUBSUB,PUBSUB_NUMSUB_History,0,PUBSUB_NUMSUB_Tips,0,pubsubCommand,-2,CMD_PUBSUB|CMD_LOADING|CMD_STALE,0,PUBSUB_NUMSUB_Keyspecs,0,NULL,1),.args=PUBSUB_NUMSUB_Args},
+{MAKE_CMD("shardchannels","Returns the active shard channels.","O(N) where N is the number of active shard channels, and assuming constant time pattern matching (relatively short shard channels).","7.0.0",CMD_DOC_NONE,NULL,NULL,"pubsub",COMMAND_GROUP_PUBSUB,PUBSUB_SHARDCHANNELS_History,0,PUBSUB_SHARDCHANNELS_Tips,0,pubsubCommand,-2,CMD_PUBSUB|CMD_LOADING|CMD_STALE,0,PUBSUB_SHARDCHANNELS_Keyspecs,0,NULL,1),.args=PUBSUB_SHARDCHANNELS_Args},
+{MAKE_CMD("shardnumsub","Returns the count of subscribers of shard channels.","O(N) for the SHARDNUMSUB subcommand, where N is the number of requested shard channels","7.0.0",CMD_DOC_NONE,NULL,NULL,"pubsub",COMMAND_GROUP_PUBSUB,PUBSUB_SHARDNUMSUB_History,0,PUBSUB_SHARDNUMSUB_Tips,0,pubsubCommand,-2,CMD_PUBSUB|CMD_LOADING|CMD_STALE,0,PUBSUB_SHARDNUMSUB_Keyspecs,0,NULL,1),.args=PUBSUB_SHARDNUMSUB_Args},
+{0}
+};
+
+/********** PUBSUB ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* PUBSUB history */
+#define PUBSUB_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* PUBSUB tips */
+#define PUBSUB_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* PUBSUB key specs */
+#define PUBSUB_Keyspecs NULL
+#endif
+
+/********** PUNSUBSCRIBE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* PUNSUBSCRIBE history */
+#define PUNSUBSCRIBE_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* PUNSUBSCRIBE tips */
+#define PUNSUBSCRIBE_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* PUNSUBSCRIBE key specs */
+#define PUNSUBSCRIBE_Keyspecs NULL
+#endif
+
+/* PUNSUBSCRIBE argument table */
+struct COMMAND_ARG PUNSUBSCRIBE_Args[] = {
+{MAKE_ARG("pattern",ARG_TYPE_PATTERN,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** SPUBLISH ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SPUBLISH history */
+#define SPUBLISH_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SPUBLISH tips */
+#define SPUBLISH_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SPUBLISH key specs */
+keySpec SPUBLISH_Keyspecs[1] = {
+{NULL,CMD_KEY_NOT_KEY,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* SPUBLISH argument table */
+struct COMMAND_ARG SPUBLISH_Args[] = {
+{MAKE_ARG("shardchannel",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("message",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** SSUBSCRIBE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SSUBSCRIBE history */
+#define SSUBSCRIBE_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SSUBSCRIBE tips */
+#define SSUBSCRIBE_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SSUBSCRIBE key specs */
+keySpec SSUBSCRIBE_Keyspecs[1] = {
+{NULL,CMD_KEY_NOT_KEY,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={-1,1,0}}
+};
+#endif
+
+/* SSUBSCRIBE argument table */
+struct COMMAND_ARG SSUBSCRIBE_Args[] = {
+{MAKE_ARG("shardchannel",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** SUBSCRIBE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SUBSCRIBE history */
+#define SUBSCRIBE_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SUBSCRIBE tips */
+#define SUBSCRIBE_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SUBSCRIBE key specs */
+#define SUBSCRIBE_Keyspecs NULL
+#endif
+
+/* SUBSCRIBE argument table */
+struct COMMAND_ARG SUBSCRIBE_Args[] = {
+{MAKE_ARG("channel",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** SUNSUBSCRIBE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SUNSUBSCRIBE history */
+#define SUNSUBSCRIBE_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SUNSUBSCRIBE tips */
+#define SUNSUBSCRIBE_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SUNSUBSCRIBE key specs */
+keySpec SUNSUBSCRIBE_Keyspecs[1] = {
+{NULL,CMD_KEY_NOT_KEY,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={-1,1,0}}
+};
+#endif
+
+/* SUNSUBSCRIBE argument table */
+struct COMMAND_ARG SUNSUBSCRIBE_Args[] = {
+{MAKE_ARG("shardchannel",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** UNSUBSCRIBE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* UNSUBSCRIBE history */
+#define UNSUBSCRIBE_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* UNSUBSCRIBE tips */
+#define UNSUBSCRIBE_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* UNSUBSCRIBE key specs */
+#define UNSUBSCRIBE_Keyspecs NULL
+#endif
+
+/* UNSUBSCRIBE argument table */
+struct COMMAND_ARG UNSUBSCRIBE_Args[] = {
+{MAKE_ARG("channel",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** EVAL ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* EVAL history */
+#define EVAL_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* EVAL tips */
+#define EVAL_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* EVAL key specs */
+keySpec EVAL_Keyspecs[1] = {
+{"We cannot tell how the keys will be used so we assume the worst, RW and UPDATE",CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_KEYNUM,.fk.keynum={0,1,1}}
+};
+#endif
+
+/* EVAL argument table */
+struct COMMAND_ARG EVAL_Args[] = {
+{MAKE_ARG("script",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("numkeys",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE,0,NULL)},
+{MAKE_ARG("arg",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** EVALSHA ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* EVALSHA history */
+#define EVALSHA_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* EVALSHA tips */
+#define EVALSHA_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* EVALSHA key specs */
+keySpec EVALSHA_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_KEYNUM,.fk.keynum={0,1,1}}
+};
+#endif
+
+/* EVALSHA argument table */
+struct COMMAND_ARG EVALSHA_Args[] = {
+{MAKE_ARG("sha1",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("numkeys",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE,0,NULL)},
+{MAKE_ARG("arg",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** EVALSHA_RO ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* EVALSHA_RO history */
+#define EVALSHA_RO_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* EVALSHA_RO tips */
+#define EVALSHA_RO_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* EVALSHA_RO key specs */
+keySpec EVALSHA_RO_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_KEYNUM,.fk.keynum={0,1,1}}
+};
+#endif
+
+/* EVALSHA_RO argument table */
+struct COMMAND_ARG EVALSHA_RO_Args[] = {
+{MAKE_ARG("sha1",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("numkeys",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE,0,NULL)},
+{MAKE_ARG("arg",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** EVAL_RO ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* EVAL_RO history */
+#define EVAL_RO_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* EVAL_RO tips */
+#define EVAL_RO_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* EVAL_RO key specs */
+keySpec EVAL_RO_Keyspecs[1] = {
+{"We cannot tell how the keys will be used so we assume the worst, RO and ACCESS",CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_KEYNUM,.fk.keynum={0,1,1}}
+};
+#endif
+
+/* EVAL_RO argument table */
+struct COMMAND_ARG EVAL_RO_Args[] = {
+{MAKE_ARG("script",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("numkeys",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE,0,NULL)},
+{MAKE_ARG("arg",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** FCALL ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* FCALL history */
+#define FCALL_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* FCALL tips */
+#define FCALL_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* FCALL key specs */
+keySpec FCALL_Keyspecs[1] = {
+{"We cannot tell how the keys will be used so we assume the worst, RW and UPDATE",CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_KEYNUM,.fk.keynum={0,1,1}}
+};
+#endif
+
+/* FCALL argument table */
+struct COMMAND_ARG FCALL_Args[] = {
+{MAKE_ARG("function",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("numkeys",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE,0,NULL)},
+{MAKE_ARG("arg",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** FCALL_RO ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* FCALL_RO history */
+#define FCALL_RO_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* FCALL_RO tips */
+#define FCALL_RO_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* FCALL_RO key specs */
+keySpec FCALL_RO_Keyspecs[1] = {
+{"We cannot tell how the keys will be used so we assume the worst, RO and ACCESS",CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_KEYNUM,.fk.keynum={0,1,1}}
+};
+#endif
+
+/* FCALL_RO argument table */
+struct COMMAND_ARG FCALL_RO_Args[] = {
+{MAKE_ARG("function",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("numkeys",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE,0,NULL)},
+{MAKE_ARG("arg",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** FUNCTION DELETE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* FUNCTION DELETE history */
+#define FUNCTION_DELETE_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* FUNCTION DELETE tips */
+const char *FUNCTION_DELETE_Tips[] = {
+"request_policy:all_shards",
+"response_policy:all_succeeded",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* FUNCTION DELETE key specs */
+#define FUNCTION_DELETE_Keyspecs NULL
+#endif
+
+/* FUNCTION DELETE argument table */
+struct COMMAND_ARG FUNCTION_DELETE_Args[] = {
+{MAKE_ARG("library-name",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** FUNCTION DUMP ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* FUNCTION DUMP history */
+#define FUNCTION_DUMP_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* FUNCTION DUMP tips */
+#define FUNCTION_DUMP_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* FUNCTION DUMP key specs */
+#define FUNCTION_DUMP_Keyspecs NULL
+#endif
+
+/********** FUNCTION FLUSH ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* FUNCTION FLUSH history */
+#define FUNCTION_FLUSH_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* FUNCTION FLUSH tips */
+const char *FUNCTION_FLUSH_Tips[] = {
+"request_policy:all_shards",
+"response_policy:all_succeeded",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* FUNCTION FLUSH key specs */
+#define FUNCTION_FLUSH_Keyspecs NULL
+#endif
+
+/* FUNCTION FLUSH flush_type argument table */
+struct COMMAND_ARG FUNCTION_FLUSH_flush_type_Subargs[] = {
+{MAKE_ARG("async",ARG_TYPE_PURE_TOKEN,-1,"ASYNC",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("sync",ARG_TYPE_PURE_TOKEN,-1,"SYNC",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* FUNCTION FLUSH argument table */
+struct COMMAND_ARG FUNCTION_FLUSH_Args[] = {
+{MAKE_ARG("flush-type",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,2,NULL),.subargs=FUNCTION_FLUSH_flush_type_Subargs},
+};
+
+/********** FUNCTION HELP ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* FUNCTION HELP history */
+#define FUNCTION_HELP_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* FUNCTION HELP tips */
+#define FUNCTION_HELP_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* FUNCTION HELP key specs */
+#define FUNCTION_HELP_Keyspecs NULL
+#endif
+
+/********** FUNCTION KILL ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* FUNCTION KILL history */
+#define FUNCTION_KILL_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* FUNCTION KILL tips */
+const char *FUNCTION_KILL_Tips[] = {
+"request_policy:all_shards",
+"response_policy:one_succeeded",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* FUNCTION KILL key specs */
+#define FUNCTION_KILL_Keyspecs NULL
+#endif
+
+/********** FUNCTION LIST ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* FUNCTION LIST history */
+#define FUNCTION_LIST_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* FUNCTION LIST tips */
+const char *FUNCTION_LIST_Tips[] = {
+"nondeterministic_output_order",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* FUNCTION LIST key specs */
+#define FUNCTION_LIST_Keyspecs NULL
+#endif
+
+/* FUNCTION LIST argument table */
+struct COMMAND_ARG FUNCTION_LIST_Args[] = {
+{MAKE_ARG("library-name-pattern",ARG_TYPE_STRING,-1,"LIBRARYNAME",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("withcode",ARG_TYPE_PURE_TOKEN,-1,"WITHCODE",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/********** FUNCTION LOAD ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* FUNCTION LOAD history */
+#define FUNCTION_LOAD_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* FUNCTION LOAD tips */
+const char *FUNCTION_LOAD_Tips[] = {
+"request_policy:all_shards",
+"response_policy:all_succeeded",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* FUNCTION LOAD key specs */
+#define FUNCTION_LOAD_Keyspecs NULL
+#endif
+
+/* FUNCTION LOAD argument table */
+struct COMMAND_ARG FUNCTION_LOAD_Args[] = {
+{MAKE_ARG("replace",ARG_TYPE_PURE_TOKEN,-1,"REPLACE",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("function-code",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** FUNCTION RESTORE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* FUNCTION RESTORE history */
+#define FUNCTION_RESTORE_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* FUNCTION RESTORE tips */
+const char *FUNCTION_RESTORE_Tips[] = {
+"request_policy:all_shards",
+"response_policy:all_succeeded",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* FUNCTION RESTORE key specs */
+#define FUNCTION_RESTORE_Keyspecs NULL
+#endif
+
+/* FUNCTION RESTORE policy argument table */
+struct COMMAND_ARG FUNCTION_RESTORE_policy_Subargs[] = {
+{MAKE_ARG("flush",ARG_TYPE_PURE_TOKEN,-1,"FLUSH",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("append",ARG_TYPE_PURE_TOKEN,-1,"APPEND",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("replace",ARG_TYPE_PURE_TOKEN,-1,"REPLACE",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* FUNCTION RESTORE argument table */
+struct COMMAND_ARG FUNCTION_RESTORE_Args[] = {
+{MAKE_ARG("serialized-value",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("policy",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,3,NULL),.subargs=FUNCTION_RESTORE_policy_Subargs},
+};
+
+/********** FUNCTION STATS ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* FUNCTION STATS history */
+#define FUNCTION_STATS_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* FUNCTION STATS tips */
+const char *FUNCTION_STATS_Tips[] = {
+"nondeterministic_output",
+"request_policy:all_shards",
+"response_policy:special",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* FUNCTION STATS key specs */
+#define FUNCTION_STATS_Keyspecs NULL
+#endif
+
+/* FUNCTION command table */
+struct COMMAND_STRUCT FUNCTION_Subcommands[] = {
+{MAKE_CMD("delete","Deletes a library and its functions.","O(1)","7.0.0",CMD_DOC_NONE,NULL,NULL,"scripting",COMMAND_GROUP_SCRIPTING,FUNCTION_DELETE_History,0,FUNCTION_DELETE_Tips,2,functionDeleteCommand,3,CMD_NOSCRIPT|CMD_WRITE,ACL_CATEGORY_SCRIPTING,FUNCTION_DELETE_Keyspecs,0,NULL,1),.args=FUNCTION_DELETE_Args},
+{MAKE_CMD("dump","Dumps all libraries into a serialized binary payload.","O(N) where N is the number of functions","7.0.0",CMD_DOC_NONE,NULL,NULL,"scripting",COMMAND_GROUP_SCRIPTING,FUNCTION_DUMP_History,0,FUNCTION_DUMP_Tips,0,functionDumpCommand,2,CMD_NOSCRIPT,ACL_CATEGORY_SCRIPTING,FUNCTION_DUMP_Keyspecs,0,NULL,0)},
+{MAKE_CMD("flush","Deletes all libraries and functions.","O(N) where N is the number of functions deleted","7.0.0",CMD_DOC_NONE,NULL,NULL,"scripting",COMMAND_GROUP_SCRIPTING,FUNCTION_FLUSH_History,0,FUNCTION_FLUSH_Tips,2,functionFlushCommand,-2,CMD_NOSCRIPT|CMD_WRITE,ACL_CATEGORY_SCRIPTING,FUNCTION_FLUSH_Keyspecs,0,NULL,1),.args=FUNCTION_FLUSH_Args},
+{MAKE_CMD("help","Returns helpful text about the different subcommands.","O(1)","7.0.0",CMD_DOC_NONE,NULL,NULL,"scripting",COMMAND_GROUP_SCRIPTING,FUNCTION_HELP_History,0,FUNCTION_HELP_Tips,0,functionHelpCommand,2,CMD_LOADING|CMD_STALE,ACL_CATEGORY_SCRIPTING,FUNCTION_HELP_Keyspecs,0,NULL,0)},
+{MAKE_CMD("kill","Terminates a function during execution.","O(1)","7.0.0",CMD_DOC_NONE,NULL,NULL,"scripting",COMMAND_GROUP_SCRIPTING,FUNCTION_KILL_History,0,FUNCTION_KILL_Tips,2,functionKillCommand,2,CMD_NOSCRIPT|CMD_ALLOW_BUSY,ACL_CATEGORY_SCRIPTING,FUNCTION_KILL_Keyspecs,0,NULL,0)},
+{MAKE_CMD("list","Returns information about all libraries.","O(N) where N is the number of functions","7.0.0",CMD_DOC_NONE,NULL,NULL,"scripting",COMMAND_GROUP_SCRIPTING,FUNCTION_LIST_History,0,FUNCTION_LIST_Tips,1,functionListCommand,-2,CMD_NOSCRIPT,ACL_CATEGORY_SCRIPTING,FUNCTION_LIST_Keyspecs,0,NULL,2),.args=FUNCTION_LIST_Args},
+{MAKE_CMD("load","Creates a library.","O(1) (considering compilation time is redundant)","7.0.0",CMD_DOC_NONE,NULL,NULL,"scripting",COMMAND_GROUP_SCRIPTING,FUNCTION_LOAD_History,0,FUNCTION_LOAD_Tips,2,functionLoadCommand,-3,CMD_NOSCRIPT|CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_SCRIPTING,FUNCTION_LOAD_Keyspecs,0,NULL,2),.args=FUNCTION_LOAD_Args},
+{MAKE_CMD("restore","Restores all libraries from a payload.","O(N) where N is the number of functions on the payload","7.0.0",CMD_DOC_NONE,NULL,NULL,"scripting",COMMAND_GROUP_SCRIPTING,FUNCTION_RESTORE_History,0,FUNCTION_RESTORE_Tips,2,functionRestoreCommand,-3,CMD_NOSCRIPT|CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_SCRIPTING,FUNCTION_RESTORE_Keyspecs,0,NULL,2),.args=FUNCTION_RESTORE_Args},
+{MAKE_CMD("stats","Returns information about a function during execution.","O(1)","7.0.0",CMD_DOC_NONE,NULL,NULL,"scripting",COMMAND_GROUP_SCRIPTING,FUNCTION_STATS_History,0,FUNCTION_STATS_Tips,3,functionStatsCommand,2,CMD_NOSCRIPT|CMD_ALLOW_BUSY,ACL_CATEGORY_SCRIPTING,FUNCTION_STATS_Keyspecs,0,NULL,0)},
+{0}
+};
+
+/********** FUNCTION ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* FUNCTION history */
+#define FUNCTION_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* FUNCTION tips */
+#define FUNCTION_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* FUNCTION key specs */
+#define FUNCTION_Keyspecs NULL
+#endif
+
+/********** SCRIPT DEBUG ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SCRIPT DEBUG history */
+#define SCRIPT_DEBUG_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SCRIPT DEBUG tips */
+#define SCRIPT_DEBUG_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SCRIPT DEBUG key specs */
+#define SCRIPT_DEBUG_Keyspecs NULL
+#endif
+
+/* SCRIPT DEBUG mode argument table */
+struct COMMAND_ARG SCRIPT_DEBUG_mode_Subargs[] = {
+{MAKE_ARG("yes",ARG_TYPE_PURE_TOKEN,-1,"YES",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("sync",ARG_TYPE_PURE_TOKEN,-1,"SYNC",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("no",ARG_TYPE_PURE_TOKEN,-1,"NO",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* SCRIPT DEBUG argument table */
+struct COMMAND_ARG SCRIPT_DEBUG_Args[] = {
+{MAKE_ARG("mode",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,3,NULL),.subargs=SCRIPT_DEBUG_mode_Subargs},
+};
+
+/********** SCRIPT EXISTS ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SCRIPT EXISTS history */
+#define SCRIPT_EXISTS_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SCRIPT EXISTS tips */
+const char *SCRIPT_EXISTS_Tips[] = {
+"request_policy:all_shards",
+"response_policy:agg_logical_and",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SCRIPT EXISTS key specs */
+#define SCRIPT_EXISTS_Keyspecs NULL
+#endif
+
+/* SCRIPT EXISTS argument table */
+struct COMMAND_ARG SCRIPT_EXISTS_Args[] = {
+{MAKE_ARG("sha1",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** SCRIPT FLUSH ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SCRIPT FLUSH history */
+commandHistory SCRIPT_FLUSH_History[] = {
+{"6.2.0","Added the `ASYNC` and `SYNC` flushing mode modifiers."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SCRIPT FLUSH tips */
+const char *SCRIPT_FLUSH_Tips[] = {
+"request_policy:all_nodes",
+"response_policy:all_succeeded",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SCRIPT FLUSH key specs */
+#define SCRIPT_FLUSH_Keyspecs NULL
+#endif
+
+/* SCRIPT FLUSH flush_type argument table */
+struct COMMAND_ARG SCRIPT_FLUSH_flush_type_Subargs[] = {
+{MAKE_ARG("async",ARG_TYPE_PURE_TOKEN,-1,"ASYNC",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("sync",ARG_TYPE_PURE_TOKEN,-1,"SYNC",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* SCRIPT FLUSH argument table */
+struct COMMAND_ARG SCRIPT_FLUSH_Args[] = {
+{MAKE_ARG("flush-type",ARG_TYPE_ONEOF,-1,NULL,NULL,"6.2.0",CMD_ARG_OPTIONAL,2,NULL),.subargs=SCRIPT_FLUSH_flush_type_Subargs},
+};
+
+/********** SCRIPT HELP ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SCRIPT HELP history */
+#define SCRIPT_HELP_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SCRIPT HELP tips */
+#define SCRIPT_HELP_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SCRIPT HELP key specs */
+#define SCRIPT_HELP_Keyspecs NULL
+#endif
+
+/********** SCRIPT KILL ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SCRIPT KILL history */
+#define SCRIPT_KILL_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SCRIPT KILL tips */
+const char *SCRIPT_KILL_Tips[] = {
+"request_policy:all_shards",
+"response_policy:one_succeeded",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SCRIPT KILL key specs */
+#define SCRIPT_KILL_Keyspecs NULL
+#endif
+
+/********** SCRIPT LOAD ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SCRIPT LOAD history */
+#define SCRIPT_LOAD_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SCRIPT LOAD tips */
+const char *SCRIPT_LOAD_Tips[] = {
+"request_policy:all_nodes",
+"response_policy:all_succeeded",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SCRIPT LOAD key specs */
+#define SCRIPT_LOAD_Keyspecs NULL
+#endif
+
+/* SCRIPT LOAD argument table */
+struct COMMAND_ARG SCRIPT_LOAD_Args[] = {
+{MAKE_ARG("script",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* SCRIPT command table */
+struct COMMAND_STRUCT SCRIPT_Subcommands[] = {
+{MAKE_CMD("debug","Sets the debug mode of server-side Lua scripts.","O(1)","3.2.0",CMD_DOC_NONE,NULL,NULL,"scripting",COMMAND_GROUP_SCRIPTING,SCRIPT_DEBUG_History,0,SCRIPT_DEBUG_Tips,0,scriptCommand,3,CMD_NOSCRIPT,ACL_CATEGORY_SCRIPTING,SCRIPT_DEBUG_Keyspecs,0,NULL,1),.args=SCRIPT_DEBUG_Args},
+{MAKE_CMD("exists","Determines whether server-side Lua scripts exist in the script cache.","O(N) with N being the number of scripts to check (so checking a single script is an O(1) operation).","2.6.0",CMD_DOC_NONE,NULL,NULL,"scripting",COMMAND_GROUP_SCRIPTING,SCRIPT_EXISTS_History,0,SCRIPT_EXISTS_Tips,2,scriptCommand,-3,CMD_NOSCRIPT,ACL_CATEGORY_SCRIPTING,SCRIPT_EXISTS_Keyspecs,0,NULL,1),.args=SCRIPT_EXISTS_Args},
+{MAKE_CMD("flush","Removes all server-side Lua scripts from the script cache.","O(N) with N being the number of scripts in cache","2.6.0",CMD_DOC_NONE,NULL,NULL,"scripting",COMMAND_GROUP_SCRIPTING,SCRIPT_FLUSH_History,1,SCRIPT_FLUSH_Tips,2,scriptCommand,-2,CMD_NOSCRIPT,ACL_CATEGORY_SCRIPTING,SCRIPT_FLUSH_Keyspecs,0,NULL,1),.args=SCRIPT_FLUSH_Args},
+{MAKE_CMD("help","Returns helpful text about the different subcommands.","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,"scripting",COMMAND_GROUP_SCRIPTING,SCRIPT_HELP_History,0,SCRIPT_HELP_Tips,0,scriptCommand,2,CMD_LOADING|CMD_STALE,ACL_CATEGORY_SCRIPTING,SCRIPT_HELP_Keyspecs,0,NULL,0)},
+{MAKE_CMD("kill","Terminates a server-side Lua script during execution.","O(1)","2.6.0",CMD_DOC_NONE,NULL,NULL,"scripting",COMMAND_GROUP_SCRIPTING,SCRIPT_KILL_History,0,SCRIPT_KILL_Tips,2,scriptCommand,2,CMD_NOSCRIPT|CMD_ALLOW_BUSY,ACL_CATEGORY_SCRIPTING,SCRIPT_KILL_Keyspecs,0,NULL,0)},
+{MAKE_CMD("load","Loads a server-side Lua script to the script cache.","O(N) with N being the length in bytes of the script body.","2.6.0",CMD_DOC_NONE,NULL,NULL,"scripting",COMMAND_GROUP_SCRIPTING,SCRIPT_LOAD_History,0,SCRIPT_LOAD_Tips,2,scriptCommand,3,CMD_NOSCRIPT|CMD_STALE,ACL_CATEGORY_SCRIPTING,SCRIPT_LOAD_Keyspecs,0,NULL,1),.args=SCRIPT_LOAD_Args},
+{0}
+};
+
+/********** SCRIPT ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SCRIPT history */
+#define SCRIPT_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SCRIPT tips */
+#define SCRIPT_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SCRIPT key specs */
+#define SCRIPT_Keyspecs NULL
+#endif
+
+/********** SENTINEL CKQUORUM ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SENTINEL CKQUORUM history */
+#define SENTINEL_CKQUORUM_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SENTINEL CKQUORUM tips */
+#define SENTINEL_CKQUORUM_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SENTINEL CKQUORUM key specs */
+#define SENTINEL_CKQUORUM_Keyspecs NULL
+#endif
+
+/* SENTINEL CKQUORUM argument table */
+struct COMMAND_ARG SENTINEL_CKQUORUM_Args[] = {
+{MAKE_ARG("master-name",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** SENTINEL CONFIG ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SENTINEL CONFIG history */
+#define SENTINEL_CONFIG_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SENTINEL CONFIG tips */
+#define SENTINEL_CONFIG_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SENTINEL CONFIG key specs */
+#define SENTINEL_CONFIG_Keyspecs NULL
+#endif
+
+/* SENTINEL CONFIG action set argument table */
+struct COMMAND_ARG SENTINEL_CONFIG_action_set_Subargs[] = {
+{MAKE_ARG("parameter",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("value",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* SENTINEL CONFIG action argument table */
+struct COMMAND_ARG SENTINEL_CONFIG_action_Subargs[] = {
+{MAKE_ARG("set",ARG_TYPE_BLOCK,-1,"SET",NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=SENTINEL_CONFIG_action_set_Subargs},
+{MAKE_ARG("parameter",ARG_TYPE_STRING,-1,"GET",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* SENTINEL CONFIG argument table */
+struct COMMAND_ARG SENTINEL_CONFIG_Args[] = {
+{MAKE_ARG("action",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=SENTINEL_CONFIG_action_Subargs},
+};
+
+/********** SENTINEL DEBUG ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SENTINEL DEBUG history */
+#define SENTINEL_DEBUG_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SENTINEL DEBUG tips */
+#define SENTINEL_DEBUG_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SENTINEL DEBUG key specs */
+#define SENTINEL_DEBUG_Keyspecs NULL
+#endif
+
+/* SENTINEL DEBUG data argument table */
+struct COMMAND_ARG SENTINEL_DEBUG_data_Subargs[] = {
+{MAKE_ARG("parameter",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("value",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* SENTINEL DEBUG argument table */
+struct COMMAND_ARG SENTINEL_DEBUG_Args[] = {
+{MAKE_ARG("data",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE,2,NULL),.subargs=SENTINEL_DEBUG_data_Subargs},
+};
+
+/********** SENTINEL FAILOVER ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SENTINEL FAILOVER history */
+#define SENTINEL_FAILOVER_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SENTINEL FAILOVER tips */
+#define SENTINEL_FAILOVER_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SENTINEL FAILOVER key specs */
+#define SENTINEL_FAILOVER_Keyspecs NULL
+#endif
+
+/* SENTINEL FAILOVER argument table */
+struct COMMAND_ARG SENTINEL_FAILOVER_Args[] = {
+{MAKE_ARG("master-name",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** SENTINEL FLUSHCONFIG ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SENTINEL FLUSHCONFIG history */
+#define SENTINEL_FLUSHCONFIG_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SENTINEL FLUSHCONFIG tips */
+#define SENTINEL_FLUSHCONFIG_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SENTINEL FLUSHCONFIG key specs */
+#define SENTINEL_FLUSHCONFIG_Keyspecs NULL
+#endif
+
+/********** SENTINEL GET_MASTER_ADDR_BY_NAME ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SENTINEL GET_MASTER_ADDR_BY_NAME history */
+#define SENTINEL_GET_MASTER_ADDR_BY_NAME_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SENTINEL GET_MASTER_ADDR_BY_NAME tips */
+#define SENTINEL_GET_MASTER_ADDR_BY_NAME_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SENTINEL GET_MASTER_ADDR_BY_NAME key specs */
+#define SENTINEL_GET_MASTER_ADDR_BY_NAME_Keyspecs NULL
+#endif
+
+/* SENTINEL GET_MASTER_ADDR_BY_NAME argument table */
+struct COMMAND_ARG SENTINEL_GET_MASTER_ADDR_BY_NAME_Args[] = {
+{MAKE_ARG("master-name",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** SENTINEL HELP ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SENTINEL HELP history */
+#define SENTINEL_HELP_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SENTINEL HELP tips */
+#define SENTINEL_HELP_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SENTINEL HELP key specs */
+#define SENTINEL_HELP_Keyspecs NULL
+#endif
+
+/********** SENTINEL INFO_CACHE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SENTINEL INFO_CACHE history */
+#define SENTINEL_INFO_CACHE_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SENTINEL INFO_CACHE tips */
+#define SENTINEL_INFO_CACHE_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SENTINEL INFO_CACHE key specs */
+#define SENTINEL_INFO_CACHE_Keyspecs NULL
+#endif
+
+/* SENTINEL INFO_CACHE argument table */
+struct COMMAND_ARG SENTINEL_INFO_CACHE_Args[] = {
+{MAKE_ARG("nodename",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** SENTINEL IS_MASTER_DOWN_BY_ADDR ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SENTINEL IS_MASTER_DOWN_BY_ADDR history */
+#define SENTINEL_IS_MASTER_DOWN_BY_ADDR_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SENTINEL IS_MASTER_DOWN_BY_ADDR tips */
+#define SENTINEL_IS_MASTER_DOWN_BY_ADDR_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SENTINEL IS_MASTER_DOWN_BY_ADDR key specs */
+#define SENTINEL_IS_MASTER_DOWN_BY_ADDR_Keyspecs NULL
+#endif
+
+/* SENTINEL IS_MASTER_DOWN_BY_ADDR argument table */
+struct COMMAND_ARG SENTINEL_IS_MASTER_DOWN_BY_ADDR_Args[] = {
+{MAKE_ARG("ip",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("port",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("current-epoch",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("runid",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** SENTINEL MASTER ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SENTINEL MASTER history */
+#define SENTINEL_MASTER_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SENTINEL MASTER tips */
+#define SENTINEL_MASTER_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SENTINEL MASTER key specs */
+#define SENTINEL_MASTER_Keyspecs NULL
+#endif
+
+/* SENTINEL MASTER argument table */
+struct COMMAND_ARG SENTINEL_MASTER_Args[] = {
+{MAKE_ARG("master-name",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** SENTINEL MASTERS ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SENTINEL MASTERS history */
+#define SENTINEL_MASTERS_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SENTINEL MASTERS tips */
+#define SENTINEL_MASTERS_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SENTINEL MASTERS key specs */
+#define SENTINEL_MASTERS_Keyspecs NULL
+#endif
+
+/********** SENTINEL MONITOR ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SENTINEL MONITOR history */
+#define SENTINEL_MONITOR_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SENTINEL MONITOR tips */
+#define SENTINEL_MONITOR_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SENTINEL MONITOR key specs */
+#define SENTINEL_MONITOR_Keyspecs NULL
+#endif
+
+/* SENTINEL MONITOR argument table */
+struct COMMAND_ARG SENTINEL_MONITOR_Args[] = {
+{MAKE_ARG("name",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("ip",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("port",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("quorum",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** SENTINEL MYID ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SENTINEL MYID history */
+#define SENTINEL_MYID_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SENTINEL MYID tips */
+#define SENTINEL_MYID_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SENTINEL MYID key specs */
+#define SENTINEL_MYID_Keyspecs NULL
+#endif
+
+/********** SENTINEL PENDING_SCRIPTS ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SENTINEL PENDING_SCRIPTS history */
+#define SENTINEL_PENDING_SCRIPTS_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SENTINEL PENDING_SCRIPTS tips */
+#define SENTINEL_PENDING_SCRIPTS_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SENTINEL PENDING_SCRIPTS key specs */
+#define SENTINEL_PENDING_SCRIPTS_Keyspecs NULL
+#endif
+
+/********** SENTINEL REMOVE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SENTINEL REMOVE history */
+#define SENTINEL_REMOVE_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SENTINEL REMOVE tips */
+#define SENTINEL_REMOVE_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SENTINEL REMOVE key specs */
+#define SENTINEL_REMOVE_Keyspecs NULL
+#endif
+
+/* SENTINEL REMOVE argument table */
+struct COMMAND_ARG SENTINEL_REMOVE_Args[] = {
+{MAKE_ARG("master-name",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** SENTINEL REPLICAS ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SENTINEL REPLICAS history */
+#define SENTINEL_REPLICAS_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SENTINEL REPLICAS tips */
+#define SENTINEL_REPLICAS_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SENTINEL REPLICAS key specs */
+#define SENTINEL_REPLICAS_Keyspecs NULL
+#endif
+
+/* SENTINEL REPLICAS argument table */
+struct COMMAND_ARG SENTINEL_REPLICAS_Args[] = {
+{MAKE_ARG("master-name",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** SENTINEL RESET ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SENTINEL RESET history */
+#define SENTINEL_RESET_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SENTINEL RESET tips */
+#define SENTINEL_RESET_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SENTINEL RESET key specs */
+#define SENTINEL_RESET_Keyspecs NULL
+#endif
+
+/* SENTINEL RESET argument table */
+struct COMMAND_ARG SENTINEL_RESET_Args[] = {
+{MAKE_ARG("pattern",ARG_TYPE_PATTERN,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** SENTINEL SENTINELS ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SENTINEL SENTINELS history */
+#define SENTINEL_SENTINELS_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SENTINEL SENTINELS tips */
+#define SENTINEL_SENTINELS_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SENTINEL SENTINELS key specs */
+#define SENTINEL_SENTINELS_Keyspecs NULL
+#endif
+
+/* SENTINEL SENTINELS argument table */
+struct COMMAND_ARG SENTINEL_SENTINELS_Args[] = {
+{MAKE_ARG("master-name",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** SENTINEL SET ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SENTINEL SET history */
+#define SENTINEL_SET_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SENTINEL SET tips */
+#define SENTINEL_SET_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SENTINEL SET key specs */
+#define SENTINEL_SET_Keyspecs NULL
+#endif
+
+/* SENTINEL SET data argument table */
+struct COMMAND_ARG SENTINEL_SET_data_Subargs[] = {
+{MAKE_ARG("option",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("value",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* SENTINEL SET argument table */
+struct COMMAND_ARG SENTINEL_SET_Args[] = {
+{MAKE_ARG("master-name",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("data",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,2,NULL),.subargs=SENTINEL_SET_data_Subargs},
+};
+
+/********** SENTINEL SIMULATE_FAILURE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SENTINEL SIMULATE_FAILURE history */
+#define SENTINEL_SIMULATE_FAILURE_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SENTINEL SIMULATE_FAILURE tips */
+#define SENTINEL_SIMULATE_FAILURE_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SENTINEL SIMULATE_FAILURE key specs */
+#define SENTINEL_SIMULATE_FAILURE_Keyspecs NULL
+#endif
+
+/* SENTINEL SIMULATE_FAILURE mode argument table */
+struct COMMAND_ARG SENTINEL_SIMULATE_FAILURE_mode_Subargs[] = {
+{MAKE_ARG("crash-after-election",ARG_TYPE_PURE_TOKEN,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("crash-after-promotion",ARG_TYPE_PURE_TOKEN,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("help",ARG_TYPE_PURE_TOKEN,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* SENTINEL SIMULATE_FAILURE argument table */
+struct COMMAND_ARG SENTINEL_SIMULATE_FAILURE_Args[] = {
+{MAKE_ARG("mode",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE,3,NULL),.subargs=SENTINEL_SIMULATE_FAILURE_mode_Subargs},
+};
+
+/********** SENTINEL SLAVES ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SENTINEL SLAVES history */
+#define SENTINEL_SLAVES_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SENTINEL SLAVES tips */
+#define SENTINEL_SLAVES_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SENTINEL SLAVES key specs */
+#define SENTINEL_SLAVES_Keyspecs NULL
+#endif
+
+/* SENTINEL SLAVES argument table */
+struct COMMAND_ARG SENTINEL_SLAVES_Args[] = {
+{MAKE_ARG("master-name",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* SENTINEL command table */
+struct COMMAND_STRUCT SENTINEL_Subcommands[] = {
+{MAKE_CMD("ckquorum","Checks for a Redis Sentinel quorum.",NULL,"2.8.4",CMD_DOC_NONE,NULL,NULL,"sentinel",COMMAND_GROUP_SENTINEL,SENTINEL_CKQUORUM_History,0,SENTINEL_CKQUORUM_Tips,0,sentinelCommand,3,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,SENTINEL_CKQUORUM_Keyspecs,0,NULL,1),.args=SENTINEL_CKQUORUM_Args},
+{MAKE_CMD("config","Configures Redis Sentinel.","O(1)","6.2.0",CMD_DOC_NONE,NULL,NULL,"sentinel",COMMAND_GROUP_SENTINEL,SENTINEL_CONFIG_History,0,SENTINEL_CONFIG_Tips,0,sentinelCommand,-4,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,SENTINEL_CONFIG_Keyspecs,0,NULL,1),.args=SENTINEL_CONFIG_Args},
+{MAKE_CMD("debug","Lists or updates the current configurable parameters of Redis Sentinel.","O(N) where N is the number of configurable parameters","7.0.0",CMD_DOC_NONE,NULL,NULL,"sentinel",COMMAND_GROUP_SENTINEL,SENTINEL_DEBUG_History,0,SENTINEL_DEBUG_Tips,0,sentinelCommand,-2,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,SENTINEL_DEBUG_Keyspecs,0,NULL,1),.args=SENTINEL_DEBUG_Args},
+{MAKE_CMD("failover","Forces a Redis Sentinel failover.",NULL,"2.8.4",CMD_DOC_NONE,NULL,NULL,"sentinel",COMMAND_GROUP_SENTINEL,SENTINEL_FAILOVER_History,0,SENTINEL_FAILOVER_Tips,0,sentinelCommand,3,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,SENTINEL_FAILOVER_Keyspecs,0,NULL,1),.args=SENTINEL_FAILOVER_Args},
+{MAKE_CMD("flushconfig","Rewrites the Redis Sentinel configuration file.","O(1)","2.8.4",CMD_DOC_NONE,NULL,NULL,"sentinel",COMMAND_GROUP_SENTINEL,SENTINEL_FLUSHCONFIG_History,0,SENTINEL_FLUSHCONFIG_Tips,0,sentinelCommand,2,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,SENTINEL_FLUSHCONFIG_Keyspecs,0,NULL,0)},
+{MAKE_CMD("get-master-addr-by-name","Returns the port and address of a master Redis instance.","O(1)","2.8.4",CMD_DOC_NONE,NULL,NULL,"sentinel",COMMAND_GROUP_SENTINEL,SENTINEL_GET_MASTER_ADDR_BY_NAME_History,0,SENTINEL_GET_MASTER_ADDR_BY_NAME_Tips,0,sentinelCommand,3,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,SENTINEL_GET_MASTER_ADDR_BY_NAME_Keyspecs,0,NULL,1),.args=SENTINEL_GET_MASTER_ADDR_BY_NAME_Args},
+{MAKE_CMD("help","Returns helpful text about the different subcommands.","O(1)","6.2.0",CMD_DOC_NONE,NULL,NULL,"sentinel",COMMAND_GROUP_SENTINEL,SENTINEL_HELP_History,0,SENTINEL_HELP_Tips,0,sentinelCommand,2,CMD_LOADING|CMD_STALE|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,SENTINEL_HELP_Keyspecs,0,NULL,0)},
+{MAKE_CMD("info-cache","Returns the cached `INFO` replies from the deployment's instances.","O(N) where N is the number of instances","3.2.0",CMD_DOC_NONE,NULL,NULL,"sentinel",COMMAND_GROUP_SENTINEL,SENTINEL_INFO_CACHE_History,0,SENTINEL_INFO_CACHE_Tips,0,sentinelCommand,-3,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,SENTINEL_INFO_CACHE_Keyspecs,0,NULL,1),.args=SENTINEL_INFO_CACHE_Args},
+{MAKE_CMD("is-master-down-by-addr","Determines whether a master Redis instance is down.","O(1)","2.8.4",CMD_DOC_NONE,NULL,NULL,"sentinel",COMMAND_GROUP_SENTINEL,SENTINEL_IS_MASTER_DOWN_BY_ADDR_History,0,SENTINEL_IS_MASTER_DOWN_BY_ADDR_Tips,0,sentinelCommand,6,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,SENTINEL_IS_MASTER_DOWN_BY_ADDR_Keyspecs,0,NULL,4),.args=SENTINEL_IS_MASTER_DOWN_BY_ADDR_Args},
+{MAKE_CMD("master","Returns the state of a master Redis instance.","O(1)","2.8.4",CMD_DOC_NONE,NULL,NULL,"sentinel",COMMAND_GROUP_SENTINEL,SENTINEL_MASTER_History,0,SENTINEL_MASTER_Tips,0,sentinelCommand,3,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,SENTINEL_MASTER_Keyspecs,0,NULL,1),.args=SENTINEL_MASTER_Args},
+{MAKE_CMD("masters","Returns a list of monitored Redis masters.","O(N) where N is the number of masters","2.8.4",CMD_DOC_NONE,NULL,NULL,"sentinel",COMMAND_GROUP_SENTINEL,SENTINEL_MASTERS_History,0,SENTINEL_MASTERS_Tips,0,sentinelCommand,2,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,SENTINEL_MASTERS_Keyspecs,0,NULL,0)},
+{MAKE_CMD("monitor","Starts monitoring.","O(1)","2.8.4",CMD_DOC_NONE,NULL,NULL,"sentinel",COMMAND_GROUP_SENTINEL,SENTINEL_MONITOR_History,0,SENTINEL_MONITOR_Tips,0,sentinelCommand,6,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,SENTINEL_MONITOR_Keyspecs,0,NULL,4),.args=SENTINEL_MONITOR_Args},
+{MAKE_CMD("myid","Returns the Redis Sentinel instance ID.","O(1)","6.2.0",CMD_DOC_NONE,NULL,NULL,"sentinel",COMMAND_GROUP_SENTINEL,SENTINEL_MYID_History,0,SENTINEL_MYID_Tips,0,sentinelCommand,2,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,SENTINEL_MYID_Keyspecs,0,NULL,0)},
+{MAKE_CMD("pending-scripts","Returns information about pending scripts for Redis Sentinel.",NULL,"2.8.4",CMD_DOC_NONE,NULL,NULL,"sentinel",COMMAND_GROUP_SENTINEL,SENTINEL_PENDING_SCRIPTS_History,0,SENTINEL_PENDING_SCRIPTS_Tips,0,sentinelCommand,2,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,SENTINEL_PENDING_SCRIPTS_Keyspecs,0,NULL,0)},
+{MAKE_CMD("remove","Stops monitoring.","O(1)","2.8.4",CMD_DOC_NONE,NULL,NULL,"sentinel",COMMAND_GROUP_SENTINEL,SENTINEL_REMOVE_History,0,SENTINEL_REMOVE_Tips,0,sentinelCommand,3,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,SENTINEL_REMOVE_Keyspecs,0,NULL,1),.args=SENTINEL_REMOVE_Args},
+{MAKE_CMD("replicas","Returns a list of the monitored Redis replicas.","O(N) where N is the number of replicas","5.0.0",CMD_DOC_NONE,NULL,NULL,"sentinel",COMMAND_GROUP_SENTINEL,SENTINEL_REPLICAS_History,0,SENTINEL_REPLICAS_Tips,0,sentinelCommand,3,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,SENTINEL_REPLICAS_Keyspecs,0,NULL,1),.args=SENTINEL_REPLICAS_Args},
+{MAKE_CMD("reset","Resets Redis masters by name matching a pattern.","O(N) where N is the number of monitored masters","2.8.4",CMD_DOC_NONE,NULL,NULL,"sentinel",COMMAND_GROUP_SENTINEL,SENTINEL_RESET_History,0,SENTINEL_RESET_Tips,0,sentinelCommand,3,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,SENTINEL_RESET_Keyspecs,0,NULL,1),.args=SENTINEL_RESET_Args},
+{MAKE_CMD("sentinels","Returns a list of Sentinel instances.","O(N) where N is the number of Sentinels","2.8.4",CMD_DOC_NONE,NULL,NULL,"sentinel",COMMAND_GROUP_SENTINEL,SENTINEL_SENTINELS_History,0,SENTINEL_SENTINELS_Tips,0,sentinelCommand,3,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,SENTINEL_SENTINELS_Keyspecs,0,NULL,1),.args=SENTINEL_SENTINELS_Args},
+{MAKE_CMD("set","Changes the configuration of a monitored Redis master.","O(1)","2.8.4",CMD_DOC_NONE,NULL,NULL,"sentinel",COMMAND_GROUP_SENTINEL,SENTINEL_SET_History,0,SENTINEL_SET_Tips,0,sentinelCommand,-5,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,SENTINEL_SET_Keyspecs,0,NULL,2),.args=SENTINEL_SET_Args},
+{MAKE_CMD("simulate-failure","Simulates failover scenarios.",NULL,"3.2.0",CMD_DOC_NONE,NULL,NULL,"sentinel",COMMAND_GROUP_SENTINEL,SENTINEL_SIMULATE_FAILURE_History,0,SENTINEL_SIMULATE_FAILURE_Tips,0,sentinelCommand,-3,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,SENTINEL_SIMULATE_FAILURE_Keyspecs,0,NULL,1),.args=SENTINEL_SIMULATE_FAILURE_Args},
+{MAKE_CMD("slaves","Returns a list of the monitored replicas.","O(N) where N is the number of replicas.","2.8.0",CMD_DOC_DEPRECATED,"`SENTINEL REPLICAS`","5.0.0","sentinel",COMMAND_GROUP_SENTINEL,SENTINEL_SLAVES_History,0,SENTINEL_SLAVES_Tips,0,sentinelCommand,3,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,SENTINEL_SLAVES_Keyspecs,0,NULL,1),.args=SENTINEL_SLAVES_Args},
+{0}
+};
+
+/********** SENTINEL ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SENTINEL history */
+#define SENTINEL_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SENTINEL tips */
+#define SENTINEL_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SENTINEL key specs */
+#define SENTINEL_Keyspecs NULL
+#endif
+
+/********** ACL CAT ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* ACL CAT history */
+#define ACL_CAT_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* ACL CAT tips */
+#define ACL_CAT_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* ACL CAT key specs */
+#define ACL_CAT_Keyspecs NULL
+#endif
+
+/* ACL CAT argument table */
+struct COMMAND_ARG ACL_CAT_Args[] = {
+{MAKE_ARG("category",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/********** ACL DELUSER ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* ACL DELUSER history */
+#define ACL_DELUSER_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* ACL DELUSER tips */
+#define ACL_DELUSER_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* ACL DELUSER key specs */
+#define ACL_DELUSER_Keyspecs NULL
+#endif
+
+/* ACL DELUSER argument table */
+struct COMMAND_ARG ACL_DELUSER_Args[] = {
+{MAKE_ARG("username",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** ACL DRYRUN ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* ACL DRYRUN history */
+#define ACL_DRYRUN_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* ACL DRYRUN tips */
+#define ACL_DRYRUN_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* ACL DRYRUN key specs */
+#define ACL_DRYRUN_Keyspecs NULL
+#endif
+
+/* ACL DRYRUN argument table */
+struct COMMAND_ARG ACL_DRYRUN_Args[] = {
+{MAKE_ARG("username",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("command",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("arg",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** ACL GENPASS ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* ACL GENPASS history */
+#define ACL_GENPASS_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* ACL GENPASS tips */
+#define ACL_GENPASS_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* ACL GENPASS key specs */
+#define ACL_GENPASS_Keyspecs NULL
+#endif
+
+/* ACL GENPASS argument table */
+struct COMMAND_ARG ACL_GENPASS_Args[] = {
+{MAKE_ARG("bits",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/********** ACL GETUSER ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* ACL GETUSER history */
+commandHistory ACL_GETUSER_History[] = {
+{"6.2.0","Added Pub/Sub channel patterns."},
+{"7.0.0","Added selectors and changed the format of key and channel patterns from a list to their rule representation."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* ACL GETUSER tips */
+#define ACL_GETUSER_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* ACL GETUSER key specs */
+#define ACL_GETUSER_Keyspecs NULL
+#endif
+
+/* ACL GETUSER argument table */
+struct COMMAND_ARG ACL_GETUSER_Args[] = {
+{MAKE_ARG("username",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** ACL HELP ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* ACL HELP history */
+#define ACL_HELP_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* ACL HELP tips */
+#define ACL_HELP_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* ACL HELP key specs */
+#define ACL_HELP_Keyspecs NULL
+#endif
+
+/********** ACL LIST ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* ACL LIST history */
+#define ACL_LIST_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* ACL LIST tips */
+#define ACL_LIST_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* ACL LIST key specs */
+#define ACL_LIST_Keyspecs NULL
+#endif
+
+/********** ACL LOAD ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* ACL LOAD history */
+#define ACL_LOAD_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* ACL LOAD tips */
+#define ACL_LOAD_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* ACL LOAD key specs */
+#define ACL_LOAD_Keyspecs NULL
+#endif
+
+/********** ACL LOG ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* ACL LOG history */
+commandHistory ACL_LOG_History[] = {
+{"7.2.0","Added entry ID, timestamp created, and timestamp last updated."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* ACL LOG tips */
+#define ACL_LOG_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* ACL LOG key specs */
+#define ACL_LOG_Keyspecs NULL
+#endif
+
+/* ACL LOG operation argument table */
+struct COMMAND_ARG ACL_LOG_operation_Subargs[] = {
+{MAKE_ARG("count",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("reset",ARG_TYPE_PURE_TOKEN,-1,"RESET",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* ACL LOG argument table */
+struct COMMAND_ARG ACL_LOG_Args[] = {
+{MAKE_ARG("operation",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,2,NULL),.subargs=ACL_LOG_operation_Subargs},
+};
+
+/********** ACL SAVE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* ACL SAVE history */
+#define ACL_SAVE_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* ACL SAVE tips */
+#define ACL_SAVE_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* ACL SAVE key specs */
+#define ACL_SAVE_Keyspecs NULL
+#endif
+
+/********** ACL SETUSER ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* ACL SETUSER history */
+commandHistory ACL_SETUSER_History[] = {
+{"6.2.0","Added Pub/Sub channel patterns."},
+{"7.0.0","Added selectors and key based permissions."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* ACL SETUSER tips */
+#define ACL_SETUSER_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* ACL SETUSER key specs */
+#define ACL_SETUSER_Keyspecs NULL
+#endif
+
+/* ACL SETUSER argument table */
+struct COMMAND_ARG ACL_SETUSER_Args[] = {
+{MAKE_ARG("username",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("rule",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** ACL USERS ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* ACL USERS history */
+#define ACL_USERS_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* ACL USERS tips */
+#define ACL_USERS_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* ACL USERS key specs */
+#define ACL_USERS_Keyspecs NULL
+#endif
+
+/********** ACL WHOAMI ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* ACL WHOAMI history */
+#define ACL_WHOAMI_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* ACL WHOAMI tips */
+#define ACL_WHOAMI_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* ACL WHOAMI key specs */
+#define ACL_WHOAMI_Keyspecs NULL
+#endif
+
+/* ACL command table */
+struct COMMAND_STRUCT ACL_Subcommands[] = {
+{MAKE_CMD("cat","Lists the ACL categories, or the commands inside a category.","O(1) since the categories and commands are a fixed set.","6.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,ACL_CAT_History,0,ACL_CAT_Tips,0,aclCommand,-2,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,ACL_CAT_Keyspecs,0,NULL,1),.args=ACL_CAT_Args},
+{MAKE_CMD("deluser","Deletes ACL users, and terminates their connections.","O(1) amortized time considering the typical user.","6.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,ACL_DELUSER_History,0,ACL_DELUSER_Tips,0,aclCommand,-3,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,ACL_DELUSER_Keyspecs,0,NULL,1),.args=ACL_DELUSER_Args},
+{MAKE_CMD("dryrun","Simulates the execution of a command by a user, without executing the command.","O(1).","7.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,ACL_DRYRUN_History,0,ACL_DRYRUN_Tips,0,aclCommand,-4,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,ACL_DRYRUN_Keyspecs,0,NULL,3),.args=ACL_DRYRUN_Args},
+{MAKE_CMD("genpass","Generates a pseudorandom, secure password that can be used to identify ACL users.","O(1)","6.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,ACL_GENPASS_History,0,ACL_GENPASS_Tips,0,aclCommand,-2,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,ACL_GENPASS_Keyspecs,0,NULL,1),.args=ACL_GENPASS_Args},
+{MAKE_CMD("getuser","Lists the ACL rules of a user.","O(N). Where N is the number of password, command and pattern rules that the user has.","6.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,ACL_GETUSER_History,2,ACL_GETUSER_Tips,0,aclCommand,3,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,ACL_GETUSER_Keyspecs,0,NULL,1),.args=ACL_GETUSER_Args},
+{MAKE_CMD("help","Returns helpful text about the different subcommands.","O(1)","6.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,ACL_HELP_History,0,ACL_HELP_Tips,0,aclCommand,2,CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,ACL_HELP_Keyspecs,0,NULL,0)},
+{MAKE_CMD("list","Dumps the effective rules in ACL file format.","O(N). Where N is the number of configured users.","6.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,ACL_LIST_History,0,ACL_LIST_Tips,0,aclCommand,2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,ACL_LIST_Keyspecs,0,NULL,0)},
+{MAKE_CMD("load","Reloads the rules from the configured ACL file.","O(N). Where N is the number of configured users.","6.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,ACL_LOAD_History,0,ACL_LOAD_Tips,0,aclCommand,2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,ACL_LOAD_Keyspecs,0,NULL,0)},
+{MAKE_CMD("log","Lists recent security events generated due to ACL rules.","O(N) with N being the number of entries shown.","6.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,ACL_LOG_History,1,ACL_LOG_Tips,0,aclCommand,-2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,ACL_LOG_Keyspecs,0,NULL,1),.args=ACL_LOG_Args},
+{MAKE_CMD("save","Saves the effective ACL rules in the configured ACL file.","O(N). Where N is the number of configured users.","6.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,ACL_SAVE_History,0,ACL_SAVE_Tips,0,aclCommand,2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,ACL_SAVE_Keyspecs,0,NULL,0)},
+{MAKE_CMD("setuser","Creates and modifies an ACL user and its rules.","O(N). Where N is the number of rules provided.","6.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,ACL_SETUSER_History,2,ACL_SETUSER_Tips,0,aclCommand,-3,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,ACL_SETUSER_Keyspecs,0,NULL,2),.args=ACL_SETUSER_Args},
+{MAKE_CMD("users","Lists all ACL users.","O(N). Where N is the number of configured users.","6.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,ACL_USERS_History,0,ACL_USERS_Tips,0,aclCommand,2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,ACL_USERS_Keyspecs,0,NULL,0)},
+{MAKE_CMD("whoami","Returns the authenticated username of the current connection.","O(1)","6.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,ACL_WHOAMI_History,0,ACL_WHOAMI_Tips,0,aclCommand,2,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,ACL_WHOAMI_Keyspecs,0,NULL,0)},
+{0}
+};
+
+/********** ACL ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* ACL history */
+#define ACL_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* ACL tips */
+#define ACL_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* ACL key specs */
+#define ACL_Keyspecs NULL
+#endif
+
+/********** BGREWRITEAOF ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* BGREWRITEAOF history */
+#define BGREWRITEAOF_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* BGREWRITEAOF tips */
+#define BGREWRITEAOF_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* BGREWRITEAOF key specs */
+#define BGREWRITEAOF_Keyspecs NULL
+#endif
+
+/********** BGSAVE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* BGSAVE history */
+commandHistory BGSAVE_History[] = {
+{"3.2.2","Added the `SCHEDULE` option."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* BGSAVE tips */
+#define BGSAVE_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* BGSAVE key specs */
+#define BGSAVE_Keyspecs NULL
+#endif
+
+/* BGSAVE argument table */
+struct COMMAND_ARG BGSAVE_Args[] = {
+{MAKE_ARG("schedule",ARG_TYPE_PURE_TOKEN,-1,"SCHEDULE",NULL,"3.2.2",CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/********** COMMAND COUNT ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* COMMAND COUNT history */
+#define COMMAND_COUNT_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* COMMAND COUNT tips */
+#define COMMAND_COUNT_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* COMMAND COUNT key specs */
+#define COMMAND_COUNT_Keyspecs NULL
+#endif
+
+/********** COMMAND DOCS ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* COMMAND DOCS history */
+#define COMMAND_DOCS_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* COMMAND DOCS tips */
+const char *COMMAND_DOCS_Tips[] = {
+"nondeterministic_output_order",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* COMMAND DOCS key specs */
+#define COMMAND_DOCS_Keyspecs NULL
+#endif
+
+/* COMMAND DOCS argument table */
+struct COMMAND_ARG COMMAND_DOCS_Args[] = {
+{MAKE_ARG("command-name",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** COMMAND GETKEYS ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* COMMAND GETKEYS history */
+#define COMMAND_GETKEYS_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* COMMAND GETKEYS tips */
+#define COMMAND_GETKEYS_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* COMMAND GETKEYS key specs */
+#define COMMAND_GETKEYS_Keyspecs NULL
+#endif
+
+/* COMMAND GETKEYS argument table */
+struct COMMAND_ARG COMMAND_GETKEYS_Args[] = {
+{MAKE_ARG("command",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("arg",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** COMMAND GETKEYSANDFLAGS ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* COMMAND GETKEYSANDFLAGS history */
+#define COMMAND_GETKEYSANDFLAGS_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* COMMAND GETKEYSANDFLAGS tips */
+#define COMMAND_GETKEYSANDFLAGS_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* COMMAND GETKEYSANDFLAGS key specs */
+#define COMMAND_GETKEYSANDFLAGS_Keyspecs NULL
+#endif
+
+/* COMMAND GETKEYSANDFLAGS argument table */
+struct COMMAND_ARG COMMAND_GETKEYSANDFLAGS_Args[] = {
+{MAKE_ARG("command",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("arg",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** COMMAND HELP ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* COMMAND HELP history */
+#define COMMAND_HELP_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* COMMAND HELP tips */
+#define COMMAND_HELP_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* COMMAND HELP key specs */
+#define COMMAND_HELP_Keyspecs NULL
+#endif
+
+/********** COMMAND INFO ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* COMMAND INFO history */
+commandHistory COMMAND_INFO_History[] = {
+{"7.0.0","Allowed to be called with no argument to get info on all commands."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* COMMAND INFO tips */
+const char *COMMAND_INFO_Tips[] = {
+"nondeterministic_output_order",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* COMMAND INFO key specs */
+#define COMMAND_INFO_Keyspecs NULL
+#endif
+
+/* COMMAND INFO argument table */
+struct COMMAND_ARG COMMAND_INFO_Args[] = {
+{MAKE_ARG("command-name",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** COMMAND LIST ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* COMMAND LIST history */
+#define COMMAND_LIST_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* COMMAND LIST tips */
+const char *COMMAND_LIST_Tips[] = {
+"nondeterministic_output_order",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* COMMAND LIST key specs */
+#define COMMAND_LIST_Keyspecs NULL
+#endif
+
+/* COMMAND LIST filterby argument table */
+struct COMMAND_ARG COMMAND_LIST_filterby_Subargs[] = {
+{MAKE_ARG("module-name",ARG_TYPE_STRING,-1,"MODULE",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("category",ARG_TYPE_STRING,-1,"ACLCAT",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("pattern",ARG_TYPE_PATTERN,-1,"PATTERN",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* COMMAND LIST argument table */
+struct COMMAND_ARG COMMAND_LIST_Args[] = {
+{MAKE_ARG("filterby",ARG_TYPE_ONEOF,-1,"FILTERBY",NULL,NULL,CMD_ARG_OPTIONAL,3,NULL),.subargs=COMMAND_LIST_filterby_Subargs},
+};
+
+/* COMMAND command table */
+struct COMMAND_STRUCT COMMAND_Subcommands[] = {
+{MAKE_CMD("count","Returns a count of commands.","O(1)","2.8.13",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,COMMAND_COUNT_History,0,COMMAND_COUNT_Tips,0,commandCountCommand,2,CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,COMMAND_COUNT_Keyspecs,0,NULL,0)},
+{MAKE_CMD("docs","Returns documentary information about one, multiple or all commands.","O(N) where N is the number of commands to look up","7.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,COMMAND_DOCS_History,0,COMMAND_DOCS_Tips,1,commandDocsCommand,-2,CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,COMMAND_DOCS_Keyspecs,0,NULL,1),.args=COMMAND_DOCS_Args},
+{MAKE_CMD("getkeys","Extracts the key names from an arbitrary command.","O(N) where N is the number of arguments to the command","2.8.13",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,COMMAND_GETKEYS_History,0,COMMAND_GETKEYS_Tips,0,commandGetKeysCommand,-3,CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,COMMAND_GETKEYS_Keyspecs,0,NULL,2),.args=COMMAND_GETKEYS_Args},
+{MAKE_CMD("getkeysandflags","Extracts the key names and access flags for an arbitrary command.","O(N) where N is the number of arguments to the command","7.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,COMMAND_GETKEYSANDFLAGS_History,0,COMMAND_GETKEYSANDFLAGS_Tips,0,commandGetKeysAndFlagsCommand,-3,CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,COMMAND_GETKEYSANDFLAGS_Keyspecs,0,NULL,2),.args=COMMAND_GETKEYSANDFLAGS_Args},
+{MAKE_CMD("help","Returns helpful text about the different subcommands.","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,COMMAND_HELP_History,0,COMMAND_HELP_Tips,0,commandHelpCommand,2,CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,COMMAND_HELP_Keyspecs,0,NULL,0)},
+{MAKE_CMD("info","Returns information about one, multiple or all commands.","O(N) where N is the number of commands to look up","2.8.13",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,COMMAND_INFO_History,1,COMMAND_INFO_Tips,1,commandInfoCommand,-2,CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,COMMAND_INFO_Keyspecs,0,NULL,1),.args=COMMAND_INFO_Args},
+{MAKE_CMD("list","Returns a list of command names.","O(N) where N is the total number of Redis commands","7.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,COMMAND_LIST_History,0,COMMAND_LIST_Tips,1,commandListCommand,-2,CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,COMMAND_LIST_Keyspecs,0,NULL,1),.args=COMMAND_LIST_Args},
+{0}
+};
+
+/********** COMMAND ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* COMMAND history */
+#define COMMAND_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* COMMAND tips */
+const char *COMMAND_Tips[] = {
+"nondeterministic_output_order",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* COMMAND key specs */
+#define COMMAND_Keyspecs NULL
+#endif
+
+/********** CONFIG GET ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* CONFIG GET history */
+commandHistory CONFIG_GET_History[] = {
+{"7.0.0","Added the ability to pass multiple pattern parameters in one call"},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* CONFIG GET tips */
+#define CONFIG_GET_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* CONFIG GET key specs */
+#define CONFIG_GET_Keyspecs NULL
+#endif
+
+/* CONFIG GET argument table */
+struct COMMAND_ARG CONFIG_GET_Args[] = {
+{MAKE_ARG("parameter",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** CONFIG HELP ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* CONFIG HELP history */
+#define CONFIG_HELP_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* CONFIG HELP tips */
+#define CONFIG_HELP_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* CONFIG HELP key specs */
+#define CONFIG_HELP_Keyspecs NULL
+#endif
+
+/********** CONFIG RESETSTAT ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* CONFIG RESETSTAT history */
+#define CONFIG_RESETSTAT_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* CONFIG RESETSTAT tips */
+#define CONFIG_RESETSTAT_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* CONFIG RESETSTAT key specs */
+#define CONFIG_RESETSTAT_Keyspecs NULL
+#endif
+
+/********** CONFIG REWRITE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* CONFIG REWRITE history */
+#define CONFIG_REWRITE_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* CONFIG REWRITE tips */
+#define CONFIG_REWRITE_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* CONFIG REWRITE key specs */
+#define CONFIG_REWRITE_Keyspecs NULL
+#endif
+
+/********** CONFIG SET ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* CONFIG SET history */
+commandHistory CONFIG_SET_History[] = {
+{"7.0.0","Added the ability to set multiple parameters in one call."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* CONFIG SET tips */
+const char *CONFIG_SET_Tips[] = {
+"request_policy:all_nodes",
+"response_policy:all_succeeded",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* CONFIG SET key specs */
+#define CONFIG_SET_Keyspecs NULL
+#endif
+
+/* CONFIG SET data argument table */
+struct COMMAND_ARG CONFIG_SET_data_Subargs[] = {
+{MAKE_ARG("parameter",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("value",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* CONFIG SET argument table */
+struct COMMAND_ARG CONFIG_SET_Args[] = {
+{MAKE_ARG("data",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,2,NULL),.subargs=CONFIG_SET_data_Subargs},
+};
+
+/* CONFIG command table */
+struct COMMAND_STRUCT CONFIG_Subcommands[] = {
+{MAKE_CMD("get","Returns the effective values of configuration parameters.","O(N) when N is the number of configuration parameters provided","2.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,CONFIG_GET_History,1,CONFIG_GET_Tips,0,configGetCommand,-3,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,0,CONFIG_GET_Keyspecs,0,NULL,1),.args=CONFIG_GET_Args},
+{MAKE_CMD("help","Returns helpful text about the different subcommands.","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,CONFIG_HELP_History,0,CONFIG_HELP_Tips,0,configHelpCommand,2,CMD_LOADING|CMD_STALE,0,CONFIG_HELP_Keyspecs,0,NULL,0)},
+{MAKE_CMD("resetstat","Resets the server's statistics.","O(1)","2.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,CONFIG_RESETSTAT_History,0,CONFIG_RESETSTAT_Tips,0,configResetStatCommand,2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,0,CONFIG_RESETSTAT_Keyspecs,0,NULL,0)},
+{MAKE_CMD("rewrite","Persists the effective configuration to file.","O(1)","2.8.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,CONFIG_REWRITE_History,0,CONFIG_REWRITE_Tips,0,configRewriteCommand,2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,0,CONFIG_REWRITE_Keyspecs,0,NULL,0)},
+{MAKE_CMD("set","Sets configuration parameters in-flight.","O(N) when N is the number of configuration parameters provided","2.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,CONFIG_SET_History,1,CONFIG_SET_Tips,2,configSetCommand,-4,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,0,CONFIG_SET_Keyspecs,0,NULL,1),.args=CONFIG_SET_Args},
+{0}
+};
+
+/********** CONFIG ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* CONFIG history */
+#define CONFIG_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* CONFIG tips */
+#define CONFIG_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* CONFIG key specs */
+#define CONFIG_Keyspecs NULL
+#endif
+
+/********** DBSIZE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* DBSIZE history */
+#define DBSIZE_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* DBSIZE tips */
+const char *DBSIZE_Tips[] = {
+"request_policy:all_shards",
+"response_policy:agg_sum",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* DBSIZE key specs */
+#define DBSIZE_Keyspecs NULL
+#endif
+
+/********** DEBUG ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* DEBUG history */
+#define DEBUG_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* DEBUG tips */
+#define DEBUG_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* DEBUG key specs */
+#define DEBUG_Keyspecs NULL
+#endif
+
+/********** FAILOVER ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* FAILOVER history */
+#define FAILOVER_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* FAILOVER tips */
+#define FAILOVER_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* FAILOVER key specs */
+#define FAILOVER_Keyspecs NULL
+#endif
+
+/* FAILOVER target argument table */
+struct COMMAND_ARG FAILOVER_target_Subargs[] = {
+{MAKE_ARG("host",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("port",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("force",ARG_TYPE_PURE_TOKEN,-1,"FORCE",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/* FAILOVER argument table */
+struct COMMAND_ARG FAILOVER_Args[] = {
+{MAKE_ARG("target",ARG_TYPE_BLOCK,-1,"TO",NULL,NULL,CMD_ARG_OPTIONAL,3,NULL),.subargs=FAILOVER_target_Subargs},
+{MAKE_ARG("abort",ARG_TYPE_PURE_TOKEN,-1,"ABORT",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("milliseconds",ARG_TYPE_INTEGER,-1,"TIMEOUT",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/********** FLUSHALL ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* FLUSHALL history */
+commandHistory FLUSHALL_History[] = {
+{"4.0.0","Added the `ASYNC` flushing mode modifier."},
+{"6.2.0","Added the `SYNC` flushing mode modifier."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* FLUSHALL tips */
+const char *FLUSHALL_Tips[] = {
+"request_policy:all_shards",
+"response_policy:all_succeeded",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* FLUSHALL key specs */
+#define FLUSHALL_Keyspecs NULL
+#endif
+
+/* FLUSHALL flush_type argument table */
+struct COMMAND_ARG FLUSHALL_flush_type_Subargs[] = {
+{MAKE_ARG("async",ARG_TYPE_PURE_TOKEN,-1,"ASYNC",NULL,"4.0.0",CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("sync",ARG_TYPE_PURE_TOKEN,-1,"SYNC",NULL,"6.2.0",CMD_ARG_NONE,0,NULL)},
+};
+
+/* FLUSHALL argument table */
+struct COMMAND_ARG FLUSHALL_Args[] = {
+{MAKE_ARG("flush-type",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,2,NULL),.subargs=FLUSHALL_flush_type_Subargs},
+};
+
+/********** FLUSHDB ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* FLUSHDB history */
+commandHistory FLUSHDB_History[] = {
+{"4.0.0","Added the `ASYNC` flushing mode modifier."},
+{"6.2.0","Added the `SYNC` flushing mode modifier."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* FLUSHDB tips */
+const char *FLUSHDB_Tips[] = {
+"request_policy:all_shards",
+"response_policy:all_succeeded",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* FLUSHDB key specs */
+#define FLUSHDB_Keyspecs NULL
+#endif
+
+/* FLUSHDB flush_type argument table */
+struct COMMAND_ARG FLUSHDB_flush_type_Subargs[] = {
+{MAKE_ARG("async",ARG_TYPE_PURE_TOKEN,-1,"ASYNC",NULL,"4.0.0",CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("sync",ARG_TYPE_PURE_TOKEN,-1,"SYNC",NULL,"6.2.0",CMD_ARG_NONE,0,NULL)},
+};
+
+/* FLUSHDB argument table */
+struct COMMAND_ARG FLUSHDB_Args[] = {
+{MAKE_ARG("flush-type",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,2,NULL),.subargs=FLUSHDB_flush_type_Subargs},
+};
+
+/********** INFO ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* INFO history */
+commandHistory INFO_History[] = {
+{"7.0.0","Added support for taking multiple section arguments."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* INFO tips */
+const char *INFO_Tips[] = {
+"nondeterministic_output",
+"request_policy:all_shards",
+"response_policy:special",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* INFO key specs */
+#define INFO_Keyspecs NULL
+#endif
+
+/* INFO argument table */
+struct COMMAND_ARG INFO_Args[] = {
+{MAKE_ARG("section",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** LASTSAVE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* LASTSAVE history */
+#define LASTSAVE_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* LASTSAVE tips */
+const char *LASTSAVE_Tips[] = {
+"nondeterministic_output",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* LASTSAVE key specs */
+#define LASTSAVE_Keyspecs NULL
+#endif
+
+/********** LATENCY DOCTOR ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* LATENCY DOCTOR history */
+#define LATENCY_DOCTOR_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* LATENCY DOCTOR tips */
+const char *LATENCY_DOCTOR_Tips[] = {
+"nondeterministic_output",
+"request_policy:all_nodes",
+"response_policy:special",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* LATENCY DOCTOR key specs */
+#define LATENCY_DOCTOR_Keyspecs NULL
+#endif
+
+/********** LATENCY GRAPH ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* LATENCY GRAPH history */
+#define LATENCY_GRAPH_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* LATENCY GRAPH tips */
+const char *LATENCY_GRAPH_Tips[] = {
+"nondeterministic_output",
+"request_policy:all_nodes",
+"response_policy:special",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* LATENCY GRAPH key specs */
+#define LATENCY_GRAPH_Keyspecs NULL
+#endif
+
+/* LATENCY GRAPH argument table */
+struct COMMAND_ARG LATENCY_GRAPH_Args[] = {
+{MAKE_ARG("event",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** LATENCY HELP ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* LATENCY HELP history */
+#define LATENCY_HELP_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* LATENCY HELP tips */
+#define LATENCY_HELP_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* LATENCY HELP key specs */
+#define LATENCY_HELP_Keyspecs NULL
+#endif
+
+/********** LATENCY HISTOGRAM ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* LATENCY HISTOGRAM history */
+#define LATENCY_HISTOGRAM_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* LATENCY HISTOGRAM tips */
+const char *LATENCY_HISTOGRAM_Tips[] = {
+"nondeterministic_output",
+"request_policy:all_nodes",
+"response_policy:special",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* LATENCY HISTOGRAM key specs */
+#define LATENCY_HISTOGRAM_Keyspecs NULL
+#endif
+
+/* LATENCY HISTOGRAM argument table */
+struct COMMAND_ARG LATENCY_HISTOGRAM_Args[] = {
+{MAKE_ARG("command",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** LATENCY HISTORY ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* LATENCY HISTORY history */
+#define LATENCY_HISTORY_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* LATENCY HISTORY tips */
+const char *LATENCY_HISTORY_Tips[] = {
+"nondeterministic_output",
+"request_policy:all_nodes",
+"response_policy:special",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* LATENCY HISTORY key specs */
+#define LATENCY_HISTORY_Keyspecs NULL
+#endif
+
+/* LATENCY HISTORY argument table */
+struct COMMAND_ARG LATENCY_HISTORY_Args[] = {
+{MAKE_ARG("event",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** LATENCY LATEST ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* LATENCY LATEST history */
+#define LATENCY_LATEST_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* LATENCY LATEST tips */
+const char *LATENCY_LATEST_Tips[] = {
+"nondeterministic_output",
+"request_policy:all_nodes",
+"response_policy:special",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* LATENCY LATEST key specs */
+#define LATENCY_LATEST_Keyspecs NULL
+#endif
+
+/********** LATENCY RESET ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* LATENCY RESET history */
+#define LATENCY_RESET_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* LATENCY RESET tips */
+const char *LATENCY_RESET_Tips[] = {
+"request_policy:all_nodes",
+"response_policy:all_succeeded",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* LATENCY RESET key specs */
+#define LATENCY_RESET_Keyspecs NULL
+#endif
+
+/* LATENCY RESET argument table */
+struct COMMAND_ARG LATENCY_RESET_Args[] = {
+{MAKE_ARG("event",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/* LATENCY command table */
+struct COMMAND_STRUCT LATENCY_Subcommands[] = {
+{MAKE_CMD("doctor","Returns a human-readable latency analysis report.","O(1)","2.8.13",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,LATENCY_DOCTOR_History,0,LATENCY_DOCTOR_Tips,3,latencyCommand,2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,0,LATENCY_DOCTOR_Keyspecs,0,NULL,0)},
+{MAKE_CMD("graph","Returns a latency graph for an event.","O(1)","2.8.13",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,LATENCY_GRAPH_History,0,LATENCY_GRAPH_Tips,3,latencyCommand,3,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,0,LATENCY_GRAPH_Keyspecs,0,NULL,1),.args=LATENCY_GRAPH_Args},
+{MAKE_CMD("help","Returns helpful text about the different subcommands.","O(1)","2.8.13",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,LATENCY_HELP_History,0,LATENCY_HELP_Tips,0,latencyCommand,2,CMD_LOADING|CMD_STALE,0,LATENCY_HELP_Keyspecs,0,NULL,0)},
+{MAKE_CMD("histogram","Returns the cumulative distribution of latencies of a subset or all commands.","O(N) where N is the number of commands with latency information being retrieved.","7.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,LATENCY_HISTOGRAM_History,0,LATENCY_HISTOGRAM_Tips,3,latencyCommand,-2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,0,LATENCY_HISTOGRAM_Keyspecs,0,NULL,1),.args=LATENCY_HISTOGRAM_Args},
+{MAKE_CMD("history","Returns timestamp-latency samples for an event.","O(1)","2.8.13",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,LATENCY_HISTORY_History,0,LATENCY_HISTORY_Tips,3,latencyCommand,3,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,0,LATENCY_HISTORY_Keyspecs,0,NULL,1),.args=LATENCY_HISTORY_Args},
+{MAKE_CMD("latest","Returns the latest latency samples for all events.","O(1)","2.8.13",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,LATENCY_LATEST_History,0,LATENCY_LATEST_Tips,3,latencyCommand,2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,0,LATENCY_LATEST_Keyspecs,0,NULL,0)},
+{MAKE_CMD("reset","Resets the latency data for one or more events.","O(1)","2.8.13",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,LATENCY_RESET_History,0,LATENCY_RESET_Tips,2,latencyCommand,-2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,0,LATENCY_RESET_Keyspecs,0,NULL,1),.args=LATENCY_RESET_Args},
+{0}
+};
+
+/********** LATENCY ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* LATENCY history */
+#define LATENCY_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* LATENCY tips */
+#define LATENCY_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* LATENCY key specs */
+#define LATENCY_Keyspecs NULL
+#endif
+
+/********** LOLWUT ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* LOLWUT history */
+#define LOLWUT_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* LOLWUT tips */
+#define LOLWUT_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* LOLWUT key specs */
+#define LOLWUT_Keyspecs NULL
+#endif
+
+/* LOLWUT argument table */
+struct COMMAND_ARG LOLWUT_Args[] = {
+{MAKE_ARG("version",ARG_TYPE_INTEGER,-1,"VERSION",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/********** MEMORY DOCTOR ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* MEMORY DOCTOR history */
+#define MEMORY_DOCTOR_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* MEMORY DOCTOR tips */
+const char *MEMORY_DOCTOR_Tips[] = {
+"nondeterministic_output",
+"request_policy:all_shards",
+"response_policy:special",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* MEMORY DOCTOR key specs */
+#define MEMORY_DOCTOR_Keyspecs NULL
+#endif
+
+/********** MEMORY HELP ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* MEMORY HELP history */
+#define MEMORY_HELP_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* MEMORY HELP tips */
+#define MEMORY_HELP_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* MEMORY HELP key specs */
+#define MEMORY_HELP_Keyspecs NULL
+#endif
+
+/********** MEMORY MALLOC_STATS ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* MEMORY MALLOC_STATS history */
+#define MEMORY_MALLOC_STATS_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* MEMORY MALLOC_STATS tips */
+const char *MEMORY_MALLOC_STATS_Tips[] = {
+"nondeterministic_output",
+"request_policy:all_shards",
+"response_policy:special",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* MEMORY MALLOC_STATS key specs */
+#define MEMORY_MALLOC_STATS_Keyspecs NULL
+#endif
+
+/********** MEMORY PURGE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* MEMORY PURGE history */
+#define MEMORY_PURGE_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* MEMORY PURGE tips */
+const char *MEMORY_PURGE_Tips[] = {
+"request_policy:all_shards",
+"response_policy:all_succeeded",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* MEMORY PURGE key specs */
+#define MEMORY_PURGE_Keyspecs NULL
+#endif
+
+/********** MEMORY STATS ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* MEMORY STATS history */
+#define MEMORY_STATS_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* MEMORY STATS tips */
+const char *MEMORY_STATS_Tips[] = {
+"nondeterministic_output",
+"request_policy:all_shards",
+"response_policy:special",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* MEMORY STATS key specs */
+#define MEMORY_STATS_Keyspecs NULL
+#endif
+
+/********** MEMORY USAGE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* MEMORY USAGE history */
+#define MEMORY_USAGE_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* MEMORY USAGE tips */
+#define MEMORY_USAGE_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* MEMORY USAGE key specs */
+keySpec MEMORY_USAGE_Keyspecs[1] = {
+{NULL,CMD_KEY_RO,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* MEMORY USAGE argument table */
+struct COMMAND_ARG MEMORY_USAGE_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("count",ARG_TYPE_INTEGER,-1,"SAMPLES",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/* MEMORY command table */
+struct COMMAND_STRUCT MEMORY_Subcommands[] = {
+{MAKE_CMD("doctor","Outputs a memory problems report.","O(1)","4.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,MEMORY_DOCTOR_History,0,MEMORY_DOCTOR_Tips,3,memoryCommand,2,0,0,MEMORY_DOCTOR_Keyspecs,0,NULL,0)},
+{MAKE_CMD("help","Returns helpful text about the different subcommands.","O(1)","4.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,MEMORY_HELP_History,0,MEMORY_HELP_Tips,0,memoryCommand,2,CMD_LOADING|CMD_STALE,0,MEMORY_HELP_Keyspecs,0,NULL,0)},
+{MAKE_CMD("malloc-stats","Returns the allocator statistics.","Depends on how much memory is allocated, could be slow","4.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,MEMORY_MALLOC_STATS_History,0,MEMORY_MALLOC_STATS_Tips,3,memoryCommand,2,0,0,MEMORY_MALLOC_STATS_Keyspecs,0,NULL,0)},
+{MAKE_CMD("purge","Asks the allocator to release memory.","Depends on how much memory is allocated, could be slow","4.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,MEMORY_PURGE_History,0,MEMORY_PURGE_Tips,2,memoryCommand,2,0,0,MEMORY_PURGE_Keyspecs,0,NULL,0)},
+{MAKE_CMD("stats","Returns details about memory usage.","O(1)","4.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,MEMORY_STATS_History,0,MEMORY_STATS_Tips,3,memoryCommand,2,0,0,MEMORY_STATS_Keyspecs,0,NULL,0)},
+{MAKE_CMD("usage","Estimates the memory usage of a key.","O(N) where N is the number of samples.","4.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,MEMORY_USAGE_History,0,MEMORY_USAGE_Tips,0,memoryCommand,-3,CMD_READONLY,0,MEMORY_USAGE_Keyspecs,1,NULL,2),.args=MEMORY_USAGE_Args},
+{0}
+};
+
+/********** MEMORY ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* MEMORY history */
+#define MEMORY_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* MEMORY tips */
+#define MEMORY_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* MEMORY key specs */
+#define MEMORY_Keyspecs NULL
+#endif
+
+/********** MODULE HELP ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* MODULE HELP history */
+#define MODULE_HELP_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* MODULE HELP tips */
+#define MODULE_HELP_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* MODULE HELP key specs */
+#define MODULE_HELP_Keyspecs NULL
+#endif
+
+/********** MODULE LIST ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* MODULE LIST history */
+#define MODULE_LIST_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* MODULE LIST tips */
+const char *MODULE_LIST_Tips[] = {
+"nondeterministic_output_order",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* MODULE LIST key specs */
+#define MODULE_LIST_Keyspecs NULL
+#endif
+
+/********** MODULE LOAD ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* MODULE LOAD history */
+#define MODULE_LOAD_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* MODULE LOAD tips */
+#define MODULE_LOAD_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* MODULE LOAD key specs */
+#define MODULE_LOAD_Keyspecs NULL
+#endif
+
+/* MODULE LOAD argument table */
+struct COMMAND_ARG MODULE_LOAD_Args[] = {
+{MAKE_ARG("path",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("arg",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** MODULE LOADEX ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* MODULE LOADEX history */
+#define MODULE_LOADEX_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* MODULE LOADEX tips */
+#define MODULE_LOADEX_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* MODULE LOADEX key specs */
+#define MODULE_LOADEX_Keyspecs NULL
+#endif
+
+/* MODULE LOADEX configs argument table */
+struct COMMAND_ARG MODULE_LOADEX_configs_Subargs[] = {
+{MAKE_ARG("name",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("value",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* MODULE LOADEX argument table */
+struct COMMAND_ARG MODULE_LOADEX_Args[] = {
+{MAKE_ARG("path",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("configs",ARG_TYPE_BLOCK,-1,"CONFIG",NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE|CMD_ARG_MULTIPLE_TOKEN,2,NULL),.subargs=MODULE_LOADEX_configs_Subargs},
+{MAKE_ARG("args",ARG_TYPE_STRING,-1,"ARGS",NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** MODULE UNLOAD ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* MODULE UNLOAD history */
+#define MODULE_UNLOAD_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* MODULE UNLOAD tips */
+#define MODULE_UNLOAD_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* MODULE UNLOAD key specs */
+#define MODULE_UNLOAD_Keyspecs NULL
+#endif
+
+/* MODULE UNLOAD argument table */
+struct COMMAND_ARG MODULE_UNLOAD_Args[] = {
+{MAKE_ARG("name",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* MODULE command table */
+struct COMMAND_STRUCT MODULE_Subcommands[] = {
+{MAKE_CMD("help","Returns helpful text about the different subcommands.","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,MODULE_HELP_History,0,MODULE_HELP_Tips,0,moduleCommand,2,CMD_LOADING|CMD_STALE,0,MODULE_HELP_Keyspecs,0,NULL,0)},
+{MAKE_CMD("list","Returns all loaded modules.","O(N) where N is the number of loaded modules.","4.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,MODULE_LIST_History,0,MODULE_LIST_Tips,1,moduleCommand,2,CMD_ADMIN|CMD_NOSCRIPT,0,MODULE_LIST_Keyspecs,0,NULL,0)},
+{MAKE_CMD("load","Loads a module.","O(1)","4.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,MODULE_LOAD_History,0,MODULE_LOAD_Tips,0,moduleCommand,-3,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_NOSCRIPT|CMD_PROTECTED,0,MODULE_LOAD_Keyspecs,0,NULL,2),.args=MODULE_LOAD_Args},
+{MAKE_CMD("loadex","Loads a module using extended parameters.","O(1)","7.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,MODULE_LOADEX_History,0,MODULE_LOADEX_Tips,0,moduleCommand,-3,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_NOSCRIPT|CMD_PROTECTED,0,MODULE_LOADEX_Keyspecs,0,NULL,3),.args=MODULE_LOADEX_Args},
+{MAKE_CMD("unload","Unloads a module.","O(1)","4.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,MODULE_UNLOAD_History,0,MODULE_UNLOAD_Tips,0,moduleCommand,3,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_NOSCRIPT|CMD_PROTECTED,0,MODULE_UNLOAD_Keyspecs,0,NULL,1),.args=MODULE_UNLOAD_Args},
+{0}
+};
+
+/********** MODULE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* MODULE history */
+#define MODULE_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* MODULE tips */
+#define MODULE_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* MODULE key specs */
+#define MODULE_Keyspecs NULL
+#endif
+
+/********** MONITOR ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* MONITOR history */
+#define MONITOR_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* MONITOR tips */
+#define MONITOR_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* MONITOR key specs */
+#define MONITOR_Keyspecs NULL
+#endif
+
+/********** PSYNC ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* PSYNC history */
+#define PSYNC_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* PSYNC tips */
+#define PSYNC_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* PSYNC key specs */
+#define PSYNC_Keyspecs NULL
+#endif
+
+/* PSYNC argument table */
+struct COMMAND_ARG PSYNC_Args[] = {
+{MAKE_ARG("replicationid",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("offset",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** REPLCONF ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* REPLCONF history */
+#define REPLCONF_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* REPLCONF tips */
+#define REPLCONF_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* REPLCONF key specs */
+#define REPLCONF_Keyspecs NULL
+#endif
+
+/********** REPLICAOF ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* REPLICAOF history */
+#define REPLICAOF_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* REPLICAOF tips */
+#define REPLICAOF_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* REPLICAOF key specs */
+#define REPLICAOF_Keyspecs NULL
+#endif
+
+/* REPLICAOF argument table */
+struct COMMAND_ARG REPLICAOF_Args[] = {
+{MAKE_ARG("host",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("port",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** RESTORE_ASKING ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* RESTORE_ASKING history */
+commandHistory RESTORE_ASKING_History[] = {
+{"3.0.0","Added the `REPLACE` modifier."},
+{"5.0.0","Added the `ABSTTL` modifier."},
+{"5.0.0","Added the `IDLETIME` and `FREQ` options."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* RESTORE_ASKING tips */
+#define RESTORE_ASKING_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* RESTORE_ASKING key specs */
+keySpec RESTORE_ASKING_Keyspecs[1] = {
+{NULL,CMD_KEY_OW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* RESTORE_ASKING argument table */
+struct COMMAND_ARG RESTORE_ASKING_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("ttl",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("serialized-value",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("replace",ARG_TYPE_PURE_TOKEN,-1,"REPLACE",NULL,"3.0.0",CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("absttl",ARG_TYPE_PURE_TOKEN,-1,"ABSTTL",NULL,"5.0.0",CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("seconds",ARG_TYPE_INTEGER,-1,"IDLETIME",NULL,"5.0.0",CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("frequency",ARG_TYPE_INTEGER,-1,"FREQ",NULL,"5.0.0",CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/********** ROLE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* ROLE history */
+#define ROLE_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* ROLE tips */
+#define ROLE_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* ROLE key specs */
+#define ROLE_Keyspecs NULL
+#endif
+
+/********** SAVE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SAVE history */
+#define SAVE_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SAVE tips */
+#define SAVE_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SAVE key specs */
+#define SAVE_Keyspecs NULL
+#endif
+
+/********** SHUTDOWN ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SHUTDOWN history */
+commandHistory SHUTDOWN_History[] = {
+{"7.0.0","Added the `NOW`, `FORCE` and `ABORT` modifiers."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SHUTDOWN tips */
+#define SHUTDOWN_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SHUTDOWN key specs */
+#define SHUTDOWN_Keyspecs NULL
+#endif
+
+/* SHUTDOWN save_selector argument table */
+struct COMMAND_ARG SHUTDOWN_save_selector_Subargs[] = {
+{MAKE_ARG("nosave",ARG_TYPE_PURE_TOKEN,-1,"NOSAVE",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("save",ARG_TYPE_PURE_TOKEN,-1,"SAVE",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* SHUTDOWN argument table */
+struct COMMAND_ARG SHUTDOWN_Args[] = {
+{MAKE_ARG("save-selector",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,2,NULL),.subargs=SHUTDOWN_save_selector_Subargs},
+{MAKE_ARG("now",ARG_TYPE_PURE_TOKEN,-1,"NOW",NULL,"7.0.0",CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("force",ARG_TYPE_PURE_TOKEN,-1,"FORCE",NULL,"7.0.0",CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("abort",ARG_TYPE_PURE_TOKEN,-1,"ABORT",NULL,"7.0.0",CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/********** SLAVEOF ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SLAVEOF history */
+#define SLAVEOF_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SLAVEOF tips */
+#define SLAVEOF_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SLAVEOF key specs */
+#define SLAVEOF_Keyspecs NULL
+#endif
+
+/* SLAVEOF argument table */
+struct COMMAND_ARG SLAVEOF_Args[] = {
+{MAKE_ARG("host",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("port",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** SLOWLOG GET ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SLOWLOG GET history */
+commandHistory SLOWLOG_GET_History[] = {
+{"4.0.0","Added client IP address, port and name to the reply."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SLOWLOG GET tips */
+const char *SLOWLOG_GET_Tips[] = {
+"request_policy:all_nodes",
+"nondeterministic_output",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SLOWLOG GET key specs */
+#define SLOWLOG_GET_Keyspecs NULL
+#endif
+
+/* SLOWLOG GET argument table */
+struct COMMAND_ARG SLOWLOG_GET_Args[] = {
+{MAKE_ARG("count",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/********** SLOWLOG HELP ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SLOWLOG HELP history */
+#define SLOWLOG_HELP_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SLOWLOG HELP tips */
+#define SLOWLOG_HELP_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SLOWLOG HELP key specs */
+#define SLOWLOG_HELP_Keyspecs NULL
+#endif
+
+/********** SLOWLOG LEN ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SLOWLOG LEN history */
+#define SLOWLOG_LEN_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SLOWLOG LEN tips */
+const char *SLOWLOG_LEN_Tips[] = {
+"request_policy:all_nodes",
+"response_policy:agg_sum",
+"nondeterministic_output",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SLOWLOG LEN key specs */
+#define SLOWLOG_LEN_Keyspecs NULL
+#endif
+
+/********** SLOWLOG RESET ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SLOWLOG RESET history */
+#define SLOWLOG_RESET_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SLOWLOG RESET tips */
+const char *SLOWLOG_RESET_Tips[] = {
+"request_policy:all_nodes",
+"response_policy:all_succeeded",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SLOWLOG RESET key specs */
+#define SLOWLOG_RESET_Keyspecs NULL
+#endif
+
+/* SLOWLOG command table */
+struct COMMAND_STRUCT SLOWLOG_Subcommands[] = {
+{MAKE_CMD("get","Returns the slow log's entries.","O(N) where N is the number of entries returned","2.2.12",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,SLOWLOG_GET_History,1,SLOWLOG_GET_Tips,2,slowlogCommand,-2,CMD_ADMIN|CMD_LOADING|CMD_STALE,0,SLOWLOG_GET_Keyspecs,0,NULL,1),.args=SLOWLOG_GET_Args},
+{MAKE_CMD("help","Show helpful text about the different subcommands","O(1)","6.2.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,SLOWLOG_HELP_History,0,SLOWLOG_HELP_Tips,0,slowlogCommand,2,CMD_LOADING|CMD_STALE,0,SLOWLOG_HELP_Keyspecs,0,NULL,0)},
+{MAKE_CMD("len","Returns the number of entries in the slow log.","O(1)","2.2.12",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,SLOWLOG_LEN_History,0,SLOWLOG_LEN_Tips,3,slowlogCommand,2,CMD_ADMIN|CMD_LOADING|CMD_STALE,0,SLOWLOG_LEN_Keyspecs,0,NULL,0)},
+{MAKE_CMD("reset","Clears all entries from the slow log.","O(N) where N is the number of entries in the slowlog","2.2.12",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,SLOWLOG_RESET_History,0,SLOWLOG_RESET_Tips,2,slowlogCommand,2,CMD_ADMIN|CMD_LOADING|CMD_STALE,0,SLOWLOG_RESET_Keyspecs,0,NULL,0)},
+{0}
+};
+
+/********** SLOWLOG ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SLOWLOG history */
+#define SLOWLOG_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SLOWLOG tips */
+#define SLOWLOG_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SLOWLOG key specs */
+#define SLOWLOG_Keyspecs NULL
+#endif
+
+/********** SWAPDB ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SWAPDB history */
+#define SWAPDB_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SWAPDB tips */
+#define SWAPDB_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SWAPDB key specs */
+#define SWAPDB_Keyspecs NULL
+#endif
+
+/* SWAPDB argument table */
+struct COMMAND_ARG SWAPDB_Args[] = {
+{MAKE_ARG("index1",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("index2",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** SYNC ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SYNC history */
+#define SYNC_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SYNC tips */
+#define SYNC_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SYNC key specs */
+#define SYNC_Keyspecs NULL
+#endif
+
+/********** TIME ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* TIME history */
+#define TIME_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* TIME tips */
+const char *TIME_Tips[] = {
+"nondeterministic_output",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* TIME key specs */
+#define TIME_Keyspecs NULL
+#endif
+
+/********** SADD ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SADD history */
+commandHistory SADD_History[] = {
+{"2.4.0","Accepts multiple `member` arguments."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SADD tips */
+#define SADD_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SADD key specs */
+keySpec SADD_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_INSERT,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* SADD argument table */
+struct COMMAND_ARG SADD_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("member",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** SCARD ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SCARD history */
+#define SCARD_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SCARD tips */
+#define SCARD_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SCARD key specs */
+keySpec SCARD_Keyspecs[1] = {
+{NULL,CMD_KEY_RO,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* SCARD argument table */
+struct COMMAND_ARG SCARD_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** SDIFF ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SDIFF history */
+#define SDIFF_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SDIFF tips */
+const char *SDIFF_Tips[] = {
+"nondeterministic_output_order",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SDIFF key specs */
+keySpec SDIFF_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={-1,1,0}}
+};
+#endif
+
+/* SDIFF argument table */
+struct COMMAND_ARG SDIFF_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** SDIFFSTORE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SDIFFSTORE history */
+#define SDIFFSTORE_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SDIFFSTORE tips */
+#define SDIFFSTORE_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SDIFFSTORE key specs */
+keySpec SDIFFSTORE_Keyspecs[2] = {
+{NULL,CMD_KEY_OW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}},{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={-1,1,0}}
+};
+#endif
+
+/* SDIFFSTORE argument table */
+struct COMMAND_ARG SDIFFSTORE_Args[] = {
+{MAKE_ARG("destination",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("key",ARG_TYPE_KEY,1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** SINTER ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SINTER history */
+#define SINTER_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SINTER tips */
+const char *SINTER_Tips[] = {
+"nondeterministic_output_order",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SINTER key specs */
+keySpec SINTER_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={-1,1,0}}
+};
+#endif
+
+/* SINTER argument table */
+struct COMMAND_ARG SINTER_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** SINTERCARD ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SINTERCARD history */
+#define SINTERCARD_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SINTERCARD tips */
+#define SINTERCARD_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SINTERCARD key specs */
+keySpec SINTERCARD_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_KEYNUM,.fk.keynum={0,1,1}}
+};
+#endif
+
+/* SINTERCARD argument table */
+struct COMMAND_ARG SINTERCARD_Args[] = {
+{MAKE_ARG("numkeys",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+{MAKE_ARG("limit",ARG_TYPE_INTEGER,-1,"LIMIT",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/********** SINTERSTORE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SINTERSTORE history */
+#define SINTERSTORE_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SINTERSTORE tips */
+#define SINTERSTORE_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SINTERSTORE key specs */
+keySpec SINTERSTORE_Keyspecs[2] = {
+{NULL,CMD_KEY_RW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}},{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={-1,1,0}}
+};
+#endif
+
+/* SINTERSTORE argument table */
+struct COMMAND_ARG SINTERSTORE_Args[] = {
+{MAKE_ARG("destination",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("key",ARG_TYPE_KEY,1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** SISMEMBER ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SISMEMBER history */
+#define SISMEMBER_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SISMEMBER tips */
+#define SISMEMBER_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SISMEMBER key specs */
+keySpec SISMEMBER_Keyspecs[1] = {
+{NULL,CMD_KEY_RO,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* SISMEMBER argument table */
+struct COMMAND_ARG SISMEMBER_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("member",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** SMEMBERS ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SMEMBERS history */
+#define SMEMBERS_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SMEMBERS tips */
+const char *SMEMBERS_Tips[] = {
+"nondeterministic_output_order",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SMEMBERS key specs */
+keySpec SMEMBERS_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* SMEMBERS argument table */
+struct COMMAND_ARG SMEMBERS_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** SMISMEMBER ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SMISMEMBER history */
+#define SMISMEMBER_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SMISMEMBER tips */
+#define SMISMEMBER_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SMISMEMBER key specs */
+keySpec SMISMEMBER_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* SMISMEMBER argument table */
+struct COMMAND_ARG SMISMEMBER_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("member",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** SMOVE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SMOVE history */
+#define SMOVE_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SMOVE tips */
+#define SMOVE_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SMOVE key specs */
+keySpec SMOVE_Keyspecs[2] = {
+{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}},{NULL,CMD_KEY_RW|CMD_KEY_INSERT,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* SMOVE argument table */
+struct COMMAND_ARG SMOVE_Args[] = {
+{MAKE_ARG("source",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("destination",ARG_TYPE_KEY,1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("member",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** SPOP ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SPOP history */
+commandHistory SPOP_History[] = {
+{"3.2.0","Added the `count` argument."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SPOP tips */
+const char *SPOP_Tips[] = {
+"nondeterministic_output",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SPOP key specs */
+keySpec SPOP_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* SPOP argument table */
+struct COMMAND_ARG SPOP_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("count",ARG_TYPE_INTEGER,-1,NULL,NULL,"3.2.0",CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/********** SRANDMEMBER ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SRANDMEMBER history */
+commandHistory SRANDMEMBER_History[] = {
+{"2.6.0","Added the optional `count` argument."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SRANDMEMBER tips */
+const char *SRANDMEMBER_Tips[] = {
+"nondeterministic_output",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SRANDMEMBER key specs */
+keySpec SRANDMEMBER_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* SRANDMEMBER argument table */
+struct COMMAND_ARG SRANDMEMBER_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("count",ARG_TYPE_INTEGER,-1,NULL,NULL,"2.6.0",CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/********** SREM ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SREM history */
+commandHistory SREM_History[] = {
+{"2.4.0","Accepts multiple `member` arguments."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SREM tips */
+#define SREM_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SREM key specs */
+keySpec SREM_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* SREM argument table */
+struct COMMAND_ARG SREM_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("member",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** SSCAN ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SSCAN history */
+#define SSCAN_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SSCAN tips */
+const char *SSCAN_Tips[] = {
+"nondeterministic_output",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SSCAN key specs */
+keySpec SSCAN_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* SSCAN argument table */
+struct COMMAND_ARG SSCAN_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("cursor",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("pattern",ARG_TYPE_PATTERN,-1,"MATCH",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("count",ARG_TYPE_INTEGER,-1,"COUNT",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/********** SUNION ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SUNION history */
+#define SUNION_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SUNION tips */
+const char *SUNION_Tips[] = {
+"nondeterministic_output_order",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SUNION key specs */
+keySpec SUNION_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={-1,1,0}}
+};
+#endif
+
+/* SUNION argument table */
+struct COMMAND_ARG SUNION_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** SUNIONSTORE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SUNIONSTORE history */
+#define SUNIONSTORE_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SUNIONSTORE tips */
+#define SUNIONSTORE_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SUNIONSTORE key specs */
+keySpec SUNIONSTORE_Keyspecs[2] = {
+{NULL,CMD_KEY_OW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}},{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={-1,1,0}}
+};
+#endif
+
+/* SUNIONSTORE argument table */
+struct COMMAND_ARG SUNIONSTORE_Args[] = {
+{MAKE_ARG("destination",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("key",ARG_TYPE_KEY,1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** BZMPOP ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* BZMPOP history */
+#define BZMPOP_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* BZMPOP tips */
+#define BZMPOP_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* BZMPOP key specs */
+keySpec BZMPOP_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_KEYNUM,.fk.keynum={0,1,1}}
+};
+#endif
+
+/* BZMPOP where argument table */
+struct COMMAND_ARG BZMPOP_where_Subargs[] = {
+{MAKE_ARG("min",ARG_TYPE_PURE_TOKEN,-1,"MIN",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("max",ARG_TYPE_PURE_TOKEN,-1,"MAX",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* BZMPOP argument table */
+struct COMMAND_ARG BZMPOP_Args[] = {
+{MAKE_ARG("timeout",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("numkeys",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+{MAKE_ARG("where",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=BZMPOP_where_Subargs},
+{MAKE_ARG("count",ARG_TYPE_INTEGER,-1,"COUNT",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/********** BZPOPMAX ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* BZPOPMAX history */
+commandHistory BZPOPMAX_History[] = {
+{"6.0.0","`timeout` is interpreted as a double instead of an integer."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* BZPOPMAX tips */
+#define BZPOPMAX_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* BZPOPMAX key specs */
+keySpec BZPOPMAX_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={-2,1,0}}
+};
+#endif
+
+/* BZPOPMAX argument table */
+struct COMMAND_ARG BZPOPMAX_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+{MAKE_ARG("timeout",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** BZPOPMIN ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* BZPOPMIN history */
+commandHistory BZPOPMIN_History[] = {
+{"6.0.0","`timeout` is interpreted as a double instead of an integer."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* BZPOPMIN tips */
+#define BZPOPMIN_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* BZPOPMIN key specs */
+keySpec BZPOPMIN_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={-2,1,0}}
+};
+#endif
+
+/* BZPOPMIN argument table */
+struct COMMAND_ARG BZPOPMIN_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+{MAKE_ARG("timeout",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** ZADD ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* ZADD history */
+commandHistory ZADD_History[] = {
+{"2.4.0","Accepts multiple elements."},
+{"3.0.2","Added the `XX`, `NX`, `CH` and `INCR` options."},
+{"6.2.0","Added the `GT` and `LT` options."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* ZADD tips */
+#define ZADD_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* ZADD key specs */
+keySpec ZADD_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* ZADD condition argument table */
+struct COMMAND_ARG ZADD_condition_Subargs[] = {
+{MAKE_ARG("nx",ARG_TYPE_PURE_TOKEN,-1,"NX",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("xx",ARG_TYPE_PURE_TOKEN,-1,"XX",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* ZADD comparison argument table */
+struct COMMAND_ARG ZADD_comparison_Subargs[] = {
+{MAKE_ARG("gt",ARG_TYPE_PURE_TOKEN,-1,"GT",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("lt",ARG_TYPE_PURE_TOKEN,-1,"LT",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* ZADD data argument table */
+struct COMMAND_ARG ZADD_data_Subargs[] = {
+{MAKE_ARG("score",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("member",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* ZADD argument table */
+struct COMMAND_ARG ZADD_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("condition",ARG_TYPE_ONEOF,-1,NULL,NULL,"3.0.2",CMD_ARG_OPTIONAL,2,NULL),.subargs=ZADD_condition_Subargs},
+{MAKE_ARG("comparison",ARG_TYPE_ONEOF,-1,NULL,NULL,"6.2.0",CMD_ARG_OPTIONAL,2,NULL),.subargs=ZADD_comparison_Subargs},
+{MAKE_ARG("change",ARG_TYPE_PURE_TOKEN,-1,"CH",NULL,"3.0.2",CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("increment",ARG_TYPE_PURE_TOKEN,-1,"INCR",NULL,"3.0.2",CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("data",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,2,NULL),.subargs=ZADD_data_Subargs},
+};
+
+/********** ZCARD ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* ZCARD history */
+#define ZCARD_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* ZCARD tips */
+#define ZCARD_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* ZCARD key specs */
+keySpec ZCARD_Keyspecs[1] = {
+{NULL,CMD_KEY_RO,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* ZCARD argument table */
+struct COMMAND_ARG ZCARD_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** ZCOUNT ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* ZCOUNT history */
+#define ZCOUNT_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* ZCOUNT tips */
+#define ZCOUNT_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* ZCOUNT key specs */
+keySpec ZCOUNT_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* ZCOUNT argument table */
+struct COMMAND_ARG ZCOUNT_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("min",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("max",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** ZDIFF ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* ZDIFF history */
+#define ZDIFF_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* ZDIFF tips */
+#define ZDIFF_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* ZDIFF key specs */
+keySpec ZDIFF_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_KEYNUM,.fk.keynum={0,1,1}}
+};
+#endif
+
+/* ZDIFF argument table */
+struct COMMAND_ARG ZDIFF_Args[] = {
+{MAKE_ARG("numkeys",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+{MAKE_ARG("withscores",ARG_TYPE_PURE_TOKEN,-1,"WITHSCORES",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/********** ZDIFFSTORE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* ZDIFFSTORE history */
+#define ZDIFFSTORE_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* ZDIFFSTORE tips */
+#define ZDIFFSTORE_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* ZDIFFSTORE key specs */
+keySpec ZDIFFSTORE_Keyspecs[2] = {
+{NULL,CMD_KEY_OW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}},{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_KEYNUM,.fk.keynum={0,1,1}}
+};
+#endif
+
+/* ZDIFFSTORE argument table */
+struct COMMAND_ARG ZDIFFSTORE_Args[] = {
+{MAKE_ARG("destination",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("numkeys",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("key",ARG_TYPE_KEY,1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** ZINCRBY ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* ZINCRBY history */
+#define ZINCRBY_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* ZINCRBY tips */
+#define ZINCRBY_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* ZINCRBY key specs */
+keySpec ZINCRBY_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* ZINCRBY argument table */
+struct COMMAND_ARG ZINCRBY_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("increment",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("member",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** ZINTER ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* ZINTER history */
+#define ZINTER_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* ZINTER tips */
+#define ZINTER_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* ZINTER key specs */
+keySpec ZINTER_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_KEYNUM,.fk.keynum={0,1,1}}
+};
+#endif
+
+/* ZINTER aggregate argument table */
+struct COMMAND_ARG ZINTER_aggregate_Subargs[] = {
+{MAKE_ARG("sum",ARG_TYPE_PURE_TOKEN,-1,"SUM",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("min",ARG_TYPE_PURE_TOKEN,-1,"MIN",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("max",ARG_TYPE_PURE_TOKEN,-1,"MAX",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* ZINTER argument table */
+struct COMMAND_ARG ZINTER_Args[] = {
+{MAKE_ARG("numkeys",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+{MAKE_ARG("weight",ARG_TYPE_INTEGER,-1,"WEIGHTS",NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE,0,NULL)},
+{MAKE_ARG("aggregate",ARG_TYPE_ONEOF,-1,"AGGREGATE",NULL,NULL,CMD_ARG_OPTIONAL,3,NULL),.subargs=ZINTER_aggregate_Subargs},
+{MAKE_ARG("withscores",ARG_TYPE_PURE_TOKEN,-1,"WITHSCORES",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/********** ZINTERCARD ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* ZINTERCARD history */
+#define ZINTERCARD_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* ZINTERCARD tips */
+#define ZINTERCARD_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* ZINTERCARD key specs */
+keySpec ZINTERCARD_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_KEYNUM,.fk.keynum={0,1,1}}
+};
+#endif
+
+/* ZINTERCARD argument table */
+struct COMMAND_ARG ZINTERCARD_Args[] = {
+{MAKE_ARG("numkeys",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+{MAKE_ARG("limit",ARG_TYPE_INTEGER,-1,"LIMIT",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/********** ZINTERSTORE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* ZINTERSTORE history */
+#define ZINTERSTORE_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* ZINTERSTORE tips */
+#define ZINTERSTORE_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* ZINTERSTORE key specs */
+keySpec ZINTERSTORE_Keyspecs[2] = {
+{NULL,CMD_KEY_OW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}},{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_KEYNUM,.fk.keynum={0,1,1}}
+};
+#endif
+
+/* ZINTERSTORE aggregate argument table */
+struct COMMAND_ARG ZINTERSTORE_aggregate_Subargs[] = {
+{MAKE_ARG("sum",ARG_TYPE_PURE_TOKEN,-1,"SUM",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("min",ARG_TYPE_PURE_TOKEN,-1,"MIN",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("max",ARG_TYPE_PURE_TOKEN,-1,"MAX",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* ZINTERSTORE argument table */
+struct COMMAND_ARG ZINTERSTORE_Args[] = {
+{MAKE_ARG("destination",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("numkeys",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("key",ARG_TYPE_KEY,1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+{MAKE_ARG("weight",ARG_TYPE_INTEGER,-1,"WEIGHTS",NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE,0,NULL)},
+{MAKE_ARG("aggregate",ARG_TYPE_ONEOF,-1,"AGGREGATE",NULL,NULL,CMD_ARG_OPTIONAL,3,NULL),.subargs=ZINTERSTORE_aggregate_Subargs},
+};
+
+/********** ZLEXCOUNT ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* ZLEXCOUNT history */
+#define ZLEXCOUNT_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* ZLEXCOUNT tips */
+#define ZLEXCOUNT_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* ZLEXCOUNT key specs */
+keySpec ZLEXCOUNT_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* ZLEXCOUNT argument table */
+struct COMMAND_ARG ZLEXCOUNT_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("min",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("max",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** ZMPOP ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* ZMPOP history */
+#define ZMPOP_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* ZMPOP tips */
+#define ZMPOP_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* ZMPOP key specs */
+keySpec ZMPOP_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_KEYNUM,.fk.keynum={0,1,1}}
+};
+#endif
+
+/* ZMPOP where argument table */
+struct COMMAND_ARG ZMPOP_where_Subargs[] = {
+{MAKE_ARG("min",ARG_TYPE_PURE_TOKEN,-1,"MIN",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("max",ARG_TYPE_PURE_TOKEN,-1,"MAX",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* ZMPOP argument table */
+struct COMMAND_ARG ZMPOP_Args[] = {
+{MAKE_ARG("numkeys",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+{MAKE_ARG("where",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=ZMPOP_where_Subargs},
+{MAKE_ARG("count",ARG_TYPE_INTEGER,-1,"COUNT",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/********** ZMSCORE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* ZMSCORE history */
+#define ZMSCORE_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* ZMSCORE tips */
+#define ZMSCORE_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* ZMSCORE key specs */
+keySpec ZMSCORE_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* ZMSCORE argument table */
+struct COMMAND_ARG ZMSCORE_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("member",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** ZPOPMAX ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* ZPOPMAX history */
+#define ZPOPMAX_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* ZPOPMAX tips */
+#define ZPOPMAX_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* ZPOPMAX key specs */
+keySpec ZPOPMAX_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* ZPOPMAX argument table */
+struct COMMAND_ARG ZPOPMAX_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("count",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/********** ZPOPMIN ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* ZPOPMIN history */
+#define ZPOPMIN_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* ZPOPMIN tips */
+#define ZPOPMIN_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* ZPOPMIN key specs */
+keySpec ZPOPMIN_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* ZPOPMIN argument table */
+struct COMMAND_ARG ZPOPMIN_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("count",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/********** ZRANDMEMBER ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* ZRANDMEMBER history */
+#define ZRANDMEMBER_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* ZRANDMEMBER tips */
+const char *ZRANDMEMBER_Tips[] = {
+"nondeterministic_output",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* ZRANDMEMBER key specs */
+keySpec ZRANDMEMBER_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* ZRANDMEMBER options argument table */
+struct COMMAND_ARG ZRANDMEMBER_options_Subargs[] = {
+{MAKE_ARG("count",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("withscores",ARG_TYPE_PURE_TOKEN,-1,"WITHSCORES",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/* ZRANDMEMBER argument table */
+struct COMMAND_ARG ZRANDMEMBER_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("options",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,2,NULL),.subargs=ZRANDMEMBER_options_Subargs},
+};
+
+/********** ZRANGE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* ZRANGE history */
+commandHistory ZRANGE_History[] = {
+{"6.2.0","Added the `REV`, `BYSCORE`, `BYLEX` and `LIMIT` options."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* ZRANGE tips */
+#define ZRANGE_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* ZRANGE key specs */
+keySpec ZRANGE_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* ZRANGE sortby argument table */
+struct COMMAND_ARG ZRANGE_sortby_Subargs[] = {
+{MAKE_ARG("byscore",ARG_TYPE_PURE_TOKEN,-1,"BYSCORE",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("bylex",ARG_TYPE_PURE_TOKEN,-1,"BYLEX",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* ZRANGE limit argument table */
+struct COMMAND_ARG ZRANGE_limit_Subargs[] = {
+{MAKE_ARG("offset",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("count",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* ZRANGE argument table */
+struct COMMAND_ARG ZRANGE_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("start",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("stop",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("sortby",ARG_TYPE_ONEOF,-1,NULL,NULL,"6.2.0",CMD_ARG_OPTIONAL,2,NULL),.subargs=ZRANGE_sortby_Subargs},
+{MAKE_ARG("rev",ARG_TYPE_PURE_TOKEN,-1,"REV",NULL,"6.2.0",CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("limit",ARG_TYPE_BLOCK,-1,"LIMIT",NULL,"6.2.0",CMD_ARG_OPTIONAL,2,NULL),.subargs=ZRANGE_limit_Subargs},
+{MAKE_ARG("withscores",ARG_TYPE_PURE_TOKEN,-1,"WITHSCORES",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/********** ZRANGEBYLEX ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* ZRANGEBYLEX history */
+#define ZRANGEBYLEX_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* ZRANGEBYLEX tips */
+#define ZRANGEBYLEX_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* ZRANGEBYLEX key specs */
+keySpec ZRANGEBYLEX_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* ZRANGEBYLEX limit argument table */
+struct COMMAND_ARG ZRANGEBYLEX_limit_Subargs[] = {
+{MAKE_ARG("offset",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("count",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* ZRANGEBYLEX argument table */
+struct COMMAND_ARG ZRANGEBYLEX_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("min",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("max",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("limit",ARG_TYPE_BLOCK,-1,"LIMIT",NULL,NULL,CMD_ARG_OPTIONAL,2,NULL),.subargs=ZRANGEBYLEX_limit_Subargs},
+};
+
+/********** ZRANGEBYSCORE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* ZRANGEBYSCORE history */
+commandHistory ZRANGEBYSCORE_History[] = {
+{"2.0.0","Added the `WITHSCORES` modifier."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* ZRANGEBYSCORE tips */
+#define ZRANGEBYSCORE_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* ZRANGEBYSCORE key specs */
+keySpec ZRANGEBYSCORE_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* ZRANGEBYSCORE limit argument table */
+struct COMMAND_ARG ZRANGEBYSCORE_limit_Subargs[] = {
+{MAKE_ARG("offset",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("count",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* ZRANGEBYSCORE argument table */
+struct COMMAND_ARG ZRANGEBYSCORE_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("min",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("max",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("withscores",ARG_TYPE_PURE_TOKEN,-1,"WITHSCORES",NULL,"2.0.0",CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("limit",ARG_TYPE_BLOCK,-1,"LIMIT",NULL,NULL,CMD_ARG_OPTIONAL,2,NULL),.subargs=ZRANGEBYSCORE_limit_Subargs},
+};
+
+/********** ZRANGESTORE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* ZRANGESTORE history */
+#define ZRANGESTORE_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* ZRANGESTORE tips */
+#define ZRANGESTORE_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* ZRANGESTORE key specs */
+keySpec ZRANGESTORE_Keyspecs[2] = {
+{NULL,CMD_KEY_OW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}},{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* ZRANGESTORE sortby argument table */
+struct COMMAND_ARG ZRANGESTORE_sortby_Subargs[] = {
+{MAKE_ARG("byscore",ARG_TYPE_PURE_TOKEN,-1,"BYSCORE",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("bylex",ARG_TYPE_PURE_TOKEN,-1,"BYLEX",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* ZRANGESTORE limit argument table */
+struct COMMAND_ARG ZRANGESTORE_limit_Subargs[] = {
+{MAKE_ARG("offset",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("count",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* ZRANGESTORE argument table */
+struct COMMAND_ARG ZRANGESTORE_Args[] = {
+{MAKE_ARG("dst",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("src",ARG_TYPE_KEY,1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("min",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("max",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("sortby",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,2,NULL),.subargs=ZRANGESTORE_sortby_Subargs},
+{MAKE_ARG("rev",ARG_TYPE_PURE_TOKEN,-1,"REV",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("limit",ARG_TYPE_BLOCK,-1,"LIMIT",NULL,NULL,CMD_ARG_OPTIONAL,2,NULL),.subargs=ZRANGESTORE_limit_Subargs},
+};
+
+/********** ZRANK ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* ZRANK history */
+commandHistory ZRANK_History[] = {
+{"7.2.0","Added the optional `WITHSCORE` argument."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* ZRANK tips */
+#define ZRANK_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* ZRANK key specs */
+keySpec ZRANK_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* ZRANK argument table */
+struct COMMAND_ARG ZRANK_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("member",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("withscore",ARG_TYPE_PURE_TOKEN,-1,"WITHSCORE",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/********** ZREM ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* ZREM history */
+commandHistory ZREM_History[] = {
+{"2.4.0","Accepts multiple elements."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* ZREM tips */
+#define ZREM_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* ZREM key specs */
+keySpec ZREM_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* ZREM argument table */
+struct COMMAND_ARG ZREM_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("member",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** ZREMRANGEBYLEX ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* ZREMRANGEBYLEX history */
+#define ZREMRANGEBYLEX_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* ZREMRANGEBYLEX tips */
+#define ZREMRANGEBYLEX_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* ZREMRANGEBYLEX key specs */
+keySpec ZREMRANGEBYLEX_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* ZREMRANGEBYLEX argument table */
+struct COMMAND_ARG ZREMRANGEBYLEX_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("min",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("max",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** ZREMRANGEBYRANK ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* ZREMRANGEBYRANK history */
+#define ZREMRANGEBYRANK_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* ZREMRANGEBYRANK tips */
+#define ZREMRANGEBYRANK_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* ZREMRANGEBYRANK key specs */
+keySpec ZREMRANGEBYRANK_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* ZREMRANGEBYRANK argument table */
+struct COMMAND_ARG ZREMRANGEBYRANK_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("start",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("stop",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** ZREMRANGEBYSCORE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* ZREMRANGEBYSCORE history */
+#define ZREMRANGEBYSCORE_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* ZREMRANGEBYSCORE tips */
+#define ZREMRANGEBYSCORE_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* ZREMRANGEBYSCORE key specs */
+keySpec ZREMRANGEBYSCORE_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* ZREMRANGEBYSCORE argument table */
+struct COMMAND_ARG ZREMRANGEBYSCORE_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("min",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("max",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** ZREVRANGE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* ZREVRANGE history */
+#define ZREVRANGE_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* ZREVRANGE tips */
+#define ZREVRANGE_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* ZREVRANGE key specs */
+keySpec ZREVRANGE_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* ZREVRANGE argument table */
+struct COMMAND_ARG ZREVRANGE_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("start",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("stop",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("withscores",ARG_TYPE_PURE_TOKEN,-1,"WITHSCORES",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/********** ZREVRANGEBYLEX ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* ZREVRANGEBYLEX history */
+#define ZREVRANGEBYLEX_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* ZREVRANGEBYLEX tips */
+#define ZREVRANGEBYLEX_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* ZREVRANGEBYLEX key specs */
+keySpec ZREVRANGEBYLEX_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* ZREVRANGEBYLEX limit argument table */
+struct COMMAND_ARG ZREVRANGEBYLEX_limit_Subargs[] = {
+{MAKE_ARG("offset",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("count",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* ZREVRANGEBYLEX argument table */
+struct COMMAND_ARG ZREVRANGEBYLEX_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("max",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("min",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("limit",ARG_TYPE_BLOCK,-1,"LIMIT",NULL,NULL,CMD_ARG_OPTIONAL,2,NULL),.subargs=ZREVRANGEBYLEX_limit_Subargs},
+};
+
+/********** ZREVRANGEBYSCORE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* ZREVRANGEBYSCORE history */
+commandHistory ZREVRANGEBYSCORE_History[] = {
+{"2.1.6","`min` and `max` can be exclusive."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* ZREVRANGEBYSCORE tips */
+#define ZREVRANGEBYSCORE_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* ZREVRANGEBYSCORE key specs */
+keySpec ZREVRANGEBYSCORE_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* ZREVRANGEBYSCORE limit argument table */
+struct COMMAND_ARG ZREVRANGEBYSCORE_limit_Subargs[] = {
+{MAKE_ARG("offset",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("count",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* ZREVRANGEBYSCORE argument table */
+struct COMMAND_ARG ZREVRANGEBYSCORE_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("max",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("min",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("withscores",ARG_TYPE_PURE_TOKEN,-1,"WITHSCORES",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("limit",ARG_TYPE_BLOCK,-1,"LIMIT",NULL,NULL,CMD_ARG_OPTIONAL,2,NULL),.subargs=ZREVRANGEBYSCORE_limit_Subargs},
+};
+
+/********** ZREVRANK ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* ZREVRANK history */
+commandHistory ZREVRANK_History[] = {
+{"7.2.0","Added the optional `WITHSCORE` argument."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* ZREVRANK tips */
+#define ZREVRANK_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* ZREVRANK key specs */
+keySpec ZREVRANK_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* ZREVRANK argument table */
+struct COMMAND_ARG ZREVRANK_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("member",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("withscore",ARG_TYPE_PURE_TOKEN,-1,"WITHSCORE",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/********** ZSCAN ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* ZSCAN history */
+#define ZSCAN_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* ZSCAN tips */
+const char *ZSCAN_Tips[] = {
+"nondeterministic_output",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* ZSCAN key specs */
+keySpec ZSCAN_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* ZSCAN argument table */
+struct COMMAND_ARG ZSCAN_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("cursor",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("pattern",ARG_TYPE_PATTERN,-1,"MATCH",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("count",ARG_TYPE_INTEGER,-1,"COUNT",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/********** ZSCORE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* ZSCORE history */
+#define ZSCORE_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* ZSCORE tips */
+#define ZSCORE_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* ZSCORE key specs */
+keySpec ZSCORE_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* ZSCORE argument table */
+struct COMMAND_ARG ZSCORE_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("member",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** ZUNION ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* ZUNION history */
+#define ZUNION_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* ZUNION tips */
+#define ZUNION_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* ZUNION key specs */
+keySpec ZUNION_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_KEYNUM,.fk.keynum={0,1,1}}
+};
+#endif
+
+/* ZUNION aggregate argument table */
+struct COMMAND_ARG ZUNION_aggregate_Subargs[] = {
+{MAKE_ARG("sum",ARG_TYPE_PURE_TOKEN,-1,"SUM",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("min",ARG_TYPE_PURE_TOKEN,-1,"MIN",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("max",ARG_TYPE_PURE_TOKEN,-1,"MAX",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* ZUNION argument table */
+struct COMMAND_ARG ZUNION_Args[] = {
+{MAKE_ARG("numkeys",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+{MAKE_ARG("weight",ARG_TYPE_INTEGER,-1,"WEIGHTS",NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE,0,NULL)},
+{MAKE_ARG("aggregate",ARG_TYPE_ONEOF,-1,"AGGREGATE",NULL,NULL,CMD_ARG_OPTIONAL,3,NULL),.subargs=ZUNION_aggregate_Subargs},
+{MAKE_ARG("withscores",ARG_TYPE_PURE_TOKEN,-1,"WITHSCORES",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/********** ZUNIONSTORE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* ZUNIONSTORE history */
+#define ZUNIONSTORE_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* ZUNIONSTORE tips */
+#define ZUNIONSTORE_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* ZUNIONSTORE key specs */
+keySpec ZUNIONSTORE_Keyspecs[2] = {
+{NULL,CMD_KEY_OW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}},{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_KEYNUM,.fk.keynum={0,1,1}}
+};
+#endif
+
+/* ZUNIONSTORE aggregate argument table */
+struct COMMAND_ARG ZUNIONSTORE_aggregate_Subargs[] = {
+{MAKE_ARG("sum",ARG_TYPE_PURE_TOKEN,-1,"SUM",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("min",ARG_TYPE_PURE_TOKEN,-1,"MIN",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("max",ARG_TYPE_PURE_TOKEN,-1,"MAX",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* ZUNIONSTORE argument table */
+struct COMMAND_ARG ZUNIONSTORE_Args[] = {
+{MAKE_ARG("destination",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("numkeys",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("key",ARG_TYPE_KEY,1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+{MAKE_ARG("weight",ARG_TYPE_INTEGER,-1,"WEIGHTS",NULL,NULL,CMD_ARG_OPTIONAL|CMD_ARG_MULTIPLE,0,NULL)},
+{MAKE_ARG("aggregate",ARG_TYPE_ONEOF,-1,"AGGREGATE",NULL,NULL,CMD_ARG_OPTIONAL,3,NULL),.subargs=ZUNIONSTORE_aggregate_Subargs},
+};
+
+/********** XACK ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* XACK history */
+#define XACK_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* XACK tips */
+#define XACK_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* XACK key specs */
+keySpec XACK_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* XACK argument table */
+struct COMMAND_ARG XACK_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("group",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("id",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** XADD ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* XADD history */
+commandHistory XADD_History[] = {
+{"6.2.0","Added the `NOMKSTREAM` option, `MINID` trimming strategy and the `LIMIT` option."},
+{"7.0.0","Added support for the `<ms>-*` explicit ID form."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* XADD tips */
+const char *XADD_Tips[] = {
+"nondeterministic_output",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* XADD key specs */
+keySpec XADD_Keyspecs[1] = {
+{"UPDATE instead of INSERT because of the optional trimming feature",CMD_KEY_RW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* XADD trim strategy argument table */
+struct COMMAND_ARG XADD_trim_strategy_Subargs[] = {
+{MAKE_ARG("maxlen",ARG_TYPE_PURE_TOKEN,-1,"MAXLEN",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("minid",ARG_TYPE_PURE_TOKEN,-1,"MINID",NULL,"6.2.0",CMD_ARG_NONE,0,NULL)},
+};
+
+/* XADD trim operator argument table */
+struct COMMAND_ARG XADD_trim_operator_Subargs[] = {
+{MAKE_ARG("equal",ARG_TYPE_PURE_TOKEN,-1,"=",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("approximately",ARG_TYPE_PURE_TOKEN,-1,"~",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* XADD trim argument table */
+struct COMMAND_ARG XADD_trim_Subargs[] = {
+{MAKE_ARG("strategy",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=XADD_trim_strategy_Subargs},
+{MAKE_ARG("operator",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,2,NULL),.subargs=XADD_trim_operator_Subargs},
+{MAKE_ARG("threshold",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("count",ARG_TYPE_INTEGER,-1,"LIMIT",NULL,"6.2.0",CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/* XADD id_selector argument table */
+struct COMMAND_ARG XADD_id_selector_Subargs[] = {
+{MAKE_ARG("auto-id",ARG_TYPE_PURE_TOKEN,-1,"*",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("id",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* XADD data argument table */
+struct COMMAND_ARG XADD_data_Subargs[] = {
+{MAKE_ARG("field",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("value",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* XADD argument table */
+struct COMMAND_ARG XADD_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("nomkstream",ARG_TYPE_PURE_TOKEN,-1,"NOMKSTREAM",NULL,"6.2.0",CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("trim",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,4,NULL),.subargs=XADD_trim_Subargs},
+{MAKE_ARG("id-selector",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=XADD_id_selector_Subargs},
+{MAKE_ARG("data",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,2,NULL),.subargs=XADD_data_Subargs},
+};
+
+/********** XAUTOCLAIM ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* XAUTOCLAIM history */
+commandHistory XAUTOCLAIM_History[] = {
+{"7.0.0","Added an element to the reply array, containing deleted entries the command cleared from the PEL"},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* XAUTOCLAIM tips */
+const char *XAUTOCLAIM_Tips[] = {
+"nondeterministic_output",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* XAUTOCLAIM key specs */
+keySpec XAUTOCLAIM_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* XAUTOCLAIM argument table */
+struct COMMAND_ARG XAUTOCLAIM_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("group",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("consumer",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("min-idle-time",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("start",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("count",ARG_TYPE_INTEGER,-1,"COUNT",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("justid",ARG_TYPE_PURE_TOKEN,-1,"JUSTID",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/********** XCLAIM ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* XCLAIM history */
+#define XCLAIM_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* XCLAIM tips */
+const char *XCLAIM_Tips[] = {
+"nondeterministic_output",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* XCLAIM key specs */
+keySpec XCLAIM_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* XCLAIM argument table */
+struct COMMAND_ARG XCLAIM_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("group",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("consumer",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("min-idle-time",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("id",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+{MAKE_ARG("ms",ARG_TYPE_INTEGER,-1,"IDLE",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("unix-time-milliseconds",ARG_TYPE_UNIX_TIME,-1,"TIME",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("count",ARG_TYPE_INTEGER,-1,"RETRYCOUNT",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("force",ARG_TYPE_PURE_TOKEN,-1,"FORCE",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("justid",ARG_TYPE_PURE_TOKEN,-1,"JUSTID",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("lastid",ARG_TYPE_STRING,-1,"LASTID",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/********** XDEL ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* XDEL history */
+#define XDEL_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* XDEL tips */
+#define XDEL_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* XDEL key specs */
+keySpec XDEL_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* XDEL argument table */
+struct COMMAND_ARG XDEL_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("id",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** XGROUP CREATE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* XGROUP CREATE history */
+commandHistory XGROUP_CREATE_History[] = {
+{"7.0.0","Added the `entries_read` named argument."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* XGROUP CREATE tips */
+#define XGROUP_CREATE_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* XGROUP CREATE key specs */
+keySpec XGROUP_CREATE_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_INSERT,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* XGROUP CREATE id_selector argument table */
+struct COMMAND_ARG XGROUP_CREATE_id_selector_Subargs[] = {
+{MAKE_ARG("id",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("new-id",ARG_TYPE_PURE_TOKEN,-1,"$",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* XGROUP CREATE argument table */
+struct COMMAND_ARG XGROUP_CREATE_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("group",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("id-selector",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=XGROUP_CREATE_id_selector_Subargs},
+{MAKE_ARG("mkstream",ARG_TYPE_PURE_TOKEN,-1,"MKSTREAM",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("entries-read",ARG_TYPE_INTEGER,-1,"ENTRIESREAD",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/********** XGROUP CREATECONSUMER ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* XGROUP CREATECONSUMER history */
+#define XGROUP_CREATECONSUMER_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* XGROUP CREATECONSUMER tips */
+#define XGROUP_CREATECONSUMER_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* XGROUP CREATECONSUMER key specs */
+keySpec XGROUP_CREATECONSUMER_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_INSERT,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* XGROUP CREATECONSUMER argument table */
+struct COMMAND_ARG XGROUP_CREATECONSUMER_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("group",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("consumer",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** XGROUP DELCONSUMER ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* XGROUP DELCONSUMER history */
+#define XGROUP_DELCONSUMER_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* XGROUP DELCONSUMER tips */
+#define XGROUP_DELCONSUMER_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* XGROUP DELCONSUMER key specs */
+keySpec XGROUP_DELCONSUMER_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* XGROUP DELCONSUMER argument table */
+struct COMMAND_ARG XGROUP_DELCONSUMER_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("group",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("consumer",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** XGROUP DESTROY ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* XGROUP DESTROY history */
+#define XGROUP_DESTROY_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* XGROUP DESTROY tips */
+#define XGROUP_DESTROY_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* XGROUP DESTROY key specs */
+keySpec XGROUP_DESTROY_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* XGROUP DESTROY argument table */
+struct COMMAND_ARG XGROUP_DESTROY_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("group",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** XGROUP HELP ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* XGROUP HELP history */
+#define XGROUP_HELP_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* XGROUP HELP tips */
+#define XGROUP_HELP_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* XGROUP HELP key specs */
+#define XGROUP_HELP_Keyspecs NULL
+#endif
+
+/********** XGROUP SETID ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* XGROUP SETID history */
+commandHistory XGROUP_SETID_History[] = {
+{"7.0.0","Added the optional `entries_read` argument."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* XGROUP SETID tips */
+#define XGROUP_SETID_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* XGROUP SETID key specs */
+keySpec XGROUP_SETID_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* XGROUP SETID id_selector argument table */
+struct COMMAND_ARG XGROUP_SETID_id_selector_Subargs[] = {
+{MAKE_ARG("id",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("new-id",ARG_TYPE_PURE_TOKEN,-1,"$",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* XGROUP SETID argument table */
+struct COMMAND_ARG XGROUP_SETID_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("group",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("id-selector",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=XGROUP_SETID_id_selector_Subargs},
+{MAKE_ARG("entriesread",ARG_TYPE_INTEGER,-1,"ENTRIESREAD",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL),.display_text="entries-read"},
+};
+
+/* XGROUP command table */
+struct COMMAND_STRUCT XGROUP_Subcommands[] = {
+{MAKE_CMD("create","Creates a consumer group.","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,"stream",COMMAND_GROUP_STREAM,XGROUP_CREATE_History,1,XGROUP_CREATE_Tips,0,xgroupCommand,-5,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_STREAM,XGROUP_CREATE_Keyspecs,1,NULL,5),.args=XGROUP_CREATE_Args},
+{MAKE_CMD("createconsumer","Creates a consumer in a consumer group.","O(1)","6.2.0",CMD_DOC_NONE,NULL,NULL,"stream",COMMAND_GROUP_STREAM,XGROUP_CREATECONSUMER_History,0,XGROUP_CREATECONSUMER_Tips,0,xgroupCommand,5,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_STREAM,XGROUP_CREATECONSUMER_Keyspecs,1,NULL,3),.args=XGROUP_CREATECONSUMER_Args},
+{MAKE_CMD("delconsumer","Deletes a consumer from a consumer group.","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,"stream",COMMAND_GROUP_STREAM,XGROUP_DELCONSUMER_History,0,XGROUP_DELCONSUMER_Tips,0,xgroupCommand,5,CMD_WRITE,ACL_CATEGORY_STREAM,XGROUP_DELCONSUMER_Keyspecs,1,NULL,3),.args=XGROUP_DELCONSUMER_Args},
+{MAKE_CMD("destroy","Destroys a consumer group.","O(N) where N is the number of entries in the group's pending entries list (PEL).","5.0.0",CMD_DOC_NONE,NULL,NULL,"stream",COMMAND_GROUP_STREAM,XGROUP_DESTROY_History,0,XGROUP_DESTROY_Tips,0,xgroupCommand,4,CMD_WRITE,ACL_CATEGORY_STREAM,XGROUP_DESTROY_Keyspecs,1,NULL,2),.args=XGROUP_DESTROY_Args},
+{MAKE_CMD("help","Returns helpful text about the different subcommands.","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,"stream",COMMAND_GROUP_STREAM,XGROUP_HELP_History,0,XGROUP_HELP_Tips,0,xgroupCommand,2,CMD_LOADING|CMD_STALE,ACL_CATEGORY_STREAM,XGROUP_HELP_Keyspecs,0,NULL,0)},
+{MAKE_CMD("setid","Sets the last-delivered ID of a consumer group.","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,"stream",COMMAND_GROUP_STREAM,XGROUP_SETID_History,1,XGROUP_SETID_Tips,0,xgroupCommand,-5,CMD_WRITE,ACL_CATEGORY_STREAM,XGROUP_SETID_Keyspecs,1,NULL,4),.args=XGROUP_SETID_Args},
+{0}
+};
+
+/********** XGROUP ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* XGROUP history */
+#define XGROUP_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* XGROUP tips */
+#define XGROUP_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* XGROUP key specs */
+#define XGROUP_Keyspecs NULL
+#endif
+
+/********** XINFO CONSUMERS ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* XINFO CONSUMERS history */
+commandHistory XINFO_CONSUMERS_History[] = {
+{"7.2.0","Added the `inactive` field."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* XINFO CONSUMERS tips */
+const char *XINFO_CONSUMERS_Tips[] = {
+"nondeterministic_output",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* XINFO CONSUMERS key specs */
+keySpec XINFO_CONSUMERS_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* XINFO CONSUMERS argument table */
+struct COMMAND_ARG XINFO_CONSUMERS_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("group",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** XINFO GROUPS ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* XINFO GROUPS history */
+commandHistory XINFO_GROUPS_History[] = {
+{"7.0.0","Added the `entries-read` and `lag` fields"},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* XINFO GROUPS tips */
+#define XINFO_GROUPS_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* XINFO GROUPS key specs */
+keySpec XINFO_GROUPS_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* XINFO GROUPS argument table */
+struct COMMAND_ARG XINFO_GROUPS_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** XINFO HELP ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* XINFO HELP history */
+#define XINFO_HELP_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* XINFO HELP tips */
+#define XINFO_HELP_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* XINFO HELP key specs */
+#define XINFO_HELP_Keyspecs NULL
+#endif
+
+/********** XINFO STREAM ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* XINFO STREAM history */
+commandHistory XINFO_STREAM_History[] = {
+{"6.0.0","Added the `FULL` modifier."},
+{"7.0.0","Added the `max-deleted-entry-id`, `entries-added`, `recorded-first-entry-id`, `entries-read` and `lag` fields"},
+{"7.2.0","Added the `active-time` field, and changed the meaning of `seen-time`."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* XINFO STREAM tips */
+#define XINFO_STREAM_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* XINFO STREAM key specs */
+keySpec XINFO_STREAM_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={2},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* XINFO STREAM full_block argument table */
+struct COMMAND_ARG XINFO_STREAM_full_block_Subargs[] = {
+{MAKE_ARG("full",ARG_TYPE_PURE_TOKEN,-1,"FULL",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("count",ARG_TYPE_INTEGER,-1,"COUNT",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/* XINFO STREAM argument table */
+struct COMMAND_ARG XINFO_STREAM_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("full-block",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,2,NULL),.subargs=XINFO_STREAM_full_block_Subargs},
+};
+
+/* XINFO command table */
+struct COMMAND_STRUCT XINFO_Subcommands[] = {
+{MAKE_CMD("consumers","Returns a list of the consumers in a consumer group.","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,"stream",COMMAND_GROUP_STREAM,XINFO_CONSUMERS_History,1,XINFO_CONSUMERS_Tips,1,xinfoCommand,4,CMD_READONLY,ACL_CATEGORY_STREAM,XINFO_CONSUMERS_Keyspecs,1,NULL,2),.args=XINFO_CONSUMERS_Args},
+{MAKE_CMD("groups","Returns a list of the consumer groups of a stream.","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,"stream",COMMAND_GROUP_STREAM,XINFO_GROUPS_History,1,XINFO_GROUPS_Tips,0,xinfoCommand,3,CMD_READONLY,ACL_CATEGORY_STREAM,XINFO_GROUPS_Keyspecs,1,NULL,1),.args=XINFO_GROUPS_Args},
+{MAKE_CMD("help","Returns helpful text about the different subcommands.","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,"stream",COMMAND_GROUP_STREAM,XINFO_HELP_History,0,XINFO_HELP_Tips,0,xinfoCommand,2,CMD_LOADING|CMD_STALE,ACL_CATEGORY_STREAM,XINFO_HELP_Keyspecs,0,NULL,0)},
+{MAKE_CMD("stream","Returns information about a stream.","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,"stream",COMMAND_GROUP_STREAM,XINFO_STREAM_History,3,XINFO_STREAM_Tips,0,xinfoCommand,-3,CMD_READONLY,ACL_CATEGORY_STREAM,XINFO_STREAM_Keyspecs,1,NULL,2),.args=XINFO_STREAM_Args},
+{0}
+};
+
+/********** XINFO ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* XINFO history */
+#define XINFO_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* XINFO tips */
+#define XINFO_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* XINFO key specs */
+#define XINFO_Keyspecs NULL
+#endif
+
+/********** XLEN ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* XLEN history */
+#define XLEN_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* XLEN tips */
+#define XLEN_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* XLEN key specs */
+keySpec XLEN_Keyspecs[1] = {
+{NULL,CMD_KEY_RO,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* XLEN argument table */
+struct COMMAND_ARG XLEN_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** XPENDING ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* XPENDING history */
+commandHistory XPENDING_History[] = {
+{"6.2.0","Added the `IDLE` option and exclusive range intervals."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* XPENDING tips */
+const char *XPENDING_Tips[] = {
+"nondeterministic_output",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* XPENDING key specs */
+keySpec XPENDING_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* XPENDING filters argument table */
+struct COMMAND_ARG XPENDING_filters_Subargs[] = {
+{MAKE_ARG("min-idle-time",ARG_TYPE_INTEGER,-1,"IDLE",NULL,"6.2.0",CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("start",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("end",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("count",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("consumer",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/* XPENDING argument table */
+struct COMMAND_ARG XPENDING_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("group",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("filters",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,5,NULL),.subargs=XPENDING_filters_Subargs},
+};
+
+/********** XRANGE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* XRANGE history */
+commandHistory XRANGE_History[] = {
+{"6.2.0","Added exclusive ranges."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* XRANGE tips */
+#define XRANGE_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* XRANGE key specs */
+keySpec XRANGE_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* XRANGE argument table */
+struct COMMAND_ARG XRANGE_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("start",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("end",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("count",ARG_TYPE_INTEGER,-1,"COUNT",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/********** XREAD ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* XREAD history */
+#define XREAD_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* XREAD tips */
+#define XREAD_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* XREAD key specs */
+keySpec XREAD_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_KEYWORD,.bs.keyword={"STREAMS",1},KSPEC_FK_RANGE,.fk.range={-1,1,2}}
+};
+#endif
+
+/* XREAD streams argument table */
+struct COMMAND_ARG XREAD_streams_Subargs[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+{MAKE_ARG("id",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/* XREAD argument table */
+struct COMMAND_ARG XREAD_Args[] = {
+{MAKE_ARG("count",ARG_TYPE_INTEGER,-1,"COUNT",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("milliseconds",ARG_TYPE_INTEGER,-1,"BLOCK",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("streams",ARG_TYPE_BLOCK,-1,"STREAMS",NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=XREAD_streams_Subargs},
+};
+
+/********** XREADGROUP ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* XREADGROUP history */
+#define XREADGROUP_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* XREADGROUP tips */
+#define XREADGROUP_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* XREADGROUP key specs */
+keySpec XREADGROUP_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_KEYWORD,.bs.keyword={"STREAMS",4},KSPEC_FK_RANGE,.fk.range={-1,1,2}}
+};
+#endif
+
+/* XREADGROUP group_block argument table */
+struct COMMAND_ARG XREADGROUP_group_block_Subargs[] = {
+{MAKE_ARG("group",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("consumer",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* XREADGROUP streams argument table */
+struct COMMAND_ARG XREADGROUP_streams_Subargs[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+{MAKE_ARG("id",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/* XREADGROUP argument table */
+struct COMMAND_ARG XREADGROUP_Args[] = {
+{MAKE_ARG("group-block",ARG_TYPE_BLOCK,-1,"GROUP",NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=XREADGROUP_group_block_Subargs},
+{MAKE_ARG("count",ARG_TYPE_INTEGER,-1,"COUNT",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("milliseconds",ARG_TYPE_INTEGER,-1,"BLOCK",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("noack",ARG_TYPE_PURE_TOKEN,-1,"NOACK",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("streams",ARG_TYPE_BLOCK,-1,"STREAMS",NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=XREADGROUP_streams_Subargs},
+};
+
+/********** XREVRANGE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* XREVRANGE history */
+commandHistory XREVRANGE_History[] = {
+{"6.2.0","Added exclusive ranges."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* XREVRANGE tips */
+#define XREVRANGE_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* XREVRANGE key specs */
+keySpec XREVRANGE_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* XREVRANGE argument table */
+struct COMMAND_ARG XREVRANGE_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("end",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("start",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("count",ARG_TYPE_INTEGER,-1,"COUNT",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/********** XSETID ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* XSETID history */
+commandHistory XSETID_History[] = {
+{"7.0.0","Added the `entries_added` and `max_deleted_entry_id` arguments."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* XSETID tips */
+#define XSETID_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* XSETID key specs */
+keySpec XSETID_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* XSETID argument table */
+struct COMMAND_ARG XSETID_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("last-id",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("entries-added",ARG_TYPE_INTEGER,-1,"ENTRIESADDED",NULL,"7.0.0",CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("max-deleted-id",ARG_TYPE_STRING,-1,"MAXDELETEDID",NULL,"7.0.0",CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/********** XTRIM ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* XTRIM history */
+commandHistory XTRIM_History[] = {
+{"6.2.0","Added the `MINID` trimming strategy and the `LIMIT` option."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* XTRIM tips */
+const char *XTRIM_Tips[] = {
+"nondeterministic_output",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* XTRIM key specs */
+keySpec XTRIM_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* XTRIM trim strategy argument table */
+struct COMMAND_ARG XTRIM_trim_strategy_Subargs[] = {
+{MAKE_ARG("maxlen",ARG_TYPE_PURE_TOKEN,-1,"MAXLEN",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("minid",ARG_TYPE_PURE_TOKEN,-1,"MINID",NULL,"6.2.0",CMD_ARG_NONE,0,NULL)},
+};
+
+/* XTRIM trim operator argument table */
+struct COMMAND_ARG XTRIM_trim_operator_Subargs[] = {
+{MAKE_ARG("equal",ARG_TYPE_PURE_TOKEN,-1,"=",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("approximately",ARG_TYPE_PURE_TOKEN,-1,"~",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* XTRIM trim argument table */
+struct COMMAND_ARG XTRIM_trim_Subargs[] = {
+{MAKE_ARG("strategy",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_NONE,2,NULL),.subargs=XTRIM_trim_strategy_Subargs},
+{MAKE_ARG("operator",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,2,NULL),.subargs=XTRIM_trim_operator_Subargs},
+{MAKE_ARG("threshold",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("count",ARG_TYPE_INTEGER,-1,"LIMIT",NULL,"6.2.0",CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/* XTRIM argument table */
+struct COMMAND_ARG XTRIM_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("trim",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_NONE,4,NULL),.subargs=XTRIM_trim_Subargs},
+};
+
+/********** APPEND ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* APPEND history */
+#define APPEND_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* APPEND tips */
+#define APPEND_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* APPEND key specs */
+keySpec APPEND_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_INSERT,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* APPEND argument table */
+struct COMMAND_ARG APPEND_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("value",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** DECR ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* DECR history */
+#define DECR_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* DECR tips */
+#define DECR_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* DECR key specs */
+keySpec DECR_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* DECR argument table */
+struct COMMAND_ARG DECR_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** DECRBY ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* DECRBY history */
+#define DECRBY_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* DECRBY tips */
+#define DECRBY_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* DECRBY key specs */
+keySpec DECRBY_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* DECRBY argument table */
+struct COMMAND_ARG DECRBY_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("decrement",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** GET ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* GET history */
+#define GET_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* GET tips */
+#define GET_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* GET key specs */
+keySpec GET_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* GET argument table */
+struct COMMAND_ARG GET_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** GETDEL ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* GETDEL history */
+#define GETDEL_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* GETDEL tips */
+#define GETDEL_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* GETDEL key specs */
+keySpec GETDEL_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_DELETE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* GETDEL argument table */
+struct COMMAND_ARG GETDEL_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** GETEX ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* GETEX history */
+#define GETEX_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* GETEX tips */
+#define GETEX_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* GETEX key specs */
+keySpec GETEX_Keyspecs[1] = {
+{"RW and UPDATE because it changes the TTL",CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* GETEX expiration argument table */
+struct COMMAND_ARG GETEX_expiration_Subargs[] = {
+{MAKE_ARG("seconds",ARG_TYPE_INTEGER,-1,"EX",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("milliseconds",ARG_TYPE_INTEGER,-1,"PX",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("unix-time-seconds",ARG_TYPE_UNIX_TIME,-1,"EXAT",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("unix-time-milliseconds",ARG_TYPE_UNIX_TIME,-1,"PXAT",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("persist",ARG_TYPE_PURE_TOKEN,-1,"PERSIST",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* GETEX argument table */
+struct COMMAND_ARG GETEX_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("expiration",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,5,NULL),.subargs=GETEX_expiration_Subargs},
+};
+
+/********** GETRANGE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* GETRANGE history */
+#define GETRANGE_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* GETRANGE tips */
+#define GETRANGE_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* GETRANGE key specs */
+keySpec GETRANGE_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* GETRANGE argument table */
+struct COMMAND_ARG GETRANGE_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("start",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("end",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** GETSET ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* GETSET history */
+#define GETSET_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* GETSET tips */
+#define GETSET_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* GETSET key specs */
+keySpec GETSET_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* GETSET argument table */
+struct COMMAND_ARG GETSET_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("value",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** INCR ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* INCR history */
+#define INCR_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* INCR tips */
+#define INCR_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* INCR key specs */
+keySpec INCR_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* INCR argument table */
+struct COMMAND_ARG INCR_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** INCRBY ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* INCRBY history */
+#define INCRBY_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* INCRBY tips */
+#define INCRBY_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* INCRBY key specs */
+keySpec INCRBY_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* INCRBY argument table */
+struct COMMAND_ARG INCRBY_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("increment",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** INCRBYFLOAT ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* INCRBYFLOAT history */
+#define INCRBYFLOAT_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* INCRBYFLOAT tips */
+#define INCRBYFLOAT_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* INCRBYFLOAT key specs */
+keySpec INCRBYFLOAT_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* INCRBYFLOAT argument table */
+struct COMMAND_ARG INCRBYFLOAT_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("increment",ARG_TYPE_DOUBLE,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** LCS ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* LCS history */
+#define LCS_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* LCS tips */
+#define LCS_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* LCS key specs */
+keySpec LCS_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={1,1,0}}
+};
+#endif
+
+/* LCS argument table */
+struct COMMAND_ARG LCS_Args[] = {
+{MAKE_ARG("key1",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("key2",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("len",ARG_TYPE_PURE_TOKEN,-1,"LEN",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("idx",ARG_TYPE_PURE_TOKEN,-1,"IDX",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("min-match-len",ARG_TYPE_INTEGER,-1,"MINMATCHLEN",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("withmatchlen",ARG_TYPE_PURE_TOKEN,-1,"WITHMATCHLEN",NULL,NULL,CMD_ARG_OPTIONAL,0,NULL)},
+};
+
+/********** MGET ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* MGET history */
+#define MGET_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* MGET tips */
+const char *MGET_Tips[] = {
+"request_policy:multi_shard",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* MGET key specs */
+keySpec MGET_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={-1,1,0}}
+};
+#endif
+
+/* MGET argument table */
+struct COMMAND_ARG MGET_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/********** MSET ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* MSET history */
+#define MSET_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* MSET tips */
+const char *MSET_Tips[] = {
+"request_policy:multi_shard",
+"response_policy:all_succeeded",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* MSET key specs */
+keySpec MSET_Keyspecs[1] = {
+{NULL,CMD_KEY_OW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={-1,2,0}}
+};
+#endif
+
+/* MSET data argument table */
+struct COMMAND_ARG MSET_data_Subargs[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("value",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* MSET argument table */
+struct COMMAND_ARG MSET_Args[] = {
+{MAKE_ARG("data",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,2,NULL),.subargs=MSET_data_Subargs},
+};
+
+/********** MSETNX ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* MSETNX history */
+#define MSETNX_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* MSETNX tips */
+const char *MSETNX_Tips[] = {
+"request_policy:multi_shard",
+"response_policy:agg_min",
+};
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* MSETNX key specs */
+keySpec MSETNX_Keyspecs[1] = {
+{NULL,CMD_KEY_OW|CMD_KEY_INSERT,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={-1,2,0}}
+};
+#endif
+
+/* MSETNX data argument table */
+struct COMMAND_ARG MSETNX_data_Subargs[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("value",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* MSETNX argument table */
+struct COMMAND_ARG MSETNX_Args[] = {
+{MAKE_ARG("data",ARG_TYPE_BLOCK,-1,NULL,NULL,NULL,CMD_ARG_MULTIPLE,2,NULL),.subargs=MSETNX_data_Subargs},
+};
+
+/********** PSETEX ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* PSETEX history */
+#define PSETEX_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* PSETEX tips */
+#define PSETEX_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* PSETEX key specs */
+keySpec PSETEX_Keyspecs[1] = {
+{NULL,CMD_KEY_OW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* PSETEX argument table */
+struct COMMAND_ARG PSETEX_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("milliseconds",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("value",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** SET ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SET history */
+commandHistory SET_History[] = {
+{"2.6.12","Added the `EX`, `PX`, `NX` and `XX` options."},
+{"6.0.0","Added the `KEEPTTL` option."},
+{"6.2.0","Added the `GET`, `EXAT` and `PXAT` option."},
+{"7.0.0","Allowed the `NX` and `GET` options to be used together."},
+};
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SET tips */
+#define SET_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SET key specs */
+keySpec SET_Keyspecs[1] = {
+{"RW and ACCESS due to the optional `GET` argument",CMD_KEY_RW|CMD_KEY_ACCESS|CMD_KEY_UPDATE|CMD_KEY_VARIABLE_FLAGS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* SET condition argument table */
+struct COMMAND_ARG SET_condition_Subargs[] = {
+{MAKE_ARG("nx",ARG_TYPE_PURE_TOKEN,-1,"NX",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("xx",ARG_TYPE_PURE_TOKEN,-1,"XX",NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/* SET expiration argument table */
+struct COMMAND_ARG SET_expiration_Subargs[] = {
+{MAKE_ARG("seconds",ARG_TYPE_INTEGER,-1,"EX",NULL,"2.6.12",CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("milliseconds",ARG_TYPE_INTEGER,-1,"PX",NULL,"2.6.12",CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("unix-time-seconds",ARG_TYPE_UNIX_TIME,-1,"EXAT",NULL,"6.2.0",CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("unix-time-milliseconds",ARG_TYPE_UNIX_TIME,-1,"PXAT",NULL,"6.2.0",CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("keepttl",ARG_TYPE_PURE_TOKEN,-1,"KEEPTTL",NULL,"6.0.0",CMD_ARG_NONE,0,NULL)},
+};
+
+/* SET argument table */
+struct COMMAND_ARG SET_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("value",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("condition",ARG_TYPE_ONEOF,-1,NULL,NULL,"2.6.12",CMD_ARG_OPTIONAL,2,NULL),.subargs=SET_condition_Subargs},
+{MAKE_ARG("get",ARG_TYPE_PURE_TOKEN,-1,"GET",NULL,"6.2.0",CMD_ARG_OPTIONAL,0,NULL)},
+{MAKE_ARG("expiration",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,5,NULL),.subargs=SET_expiration_Subargs},
+};
+
+/********** SETEX ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SETEX history */
+#define SETEX_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SETEX tips */
+#define SETEX_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SETEX key specs */
+keySpec SETEX_Keyspecs[1] = {
+{NULL,CMD_KEY_OW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* SETEX argument table */
+struct COMMAND_ARG SETEX_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("seconds",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("value",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** SETNX ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SETNX history */
+#define SETNX_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SETNX tips */
+#define SETNX_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SETNX key specs */
+keySpec SETNX_Keyspecs[1] = {
+{NULL,CMD_KEY_OW|CMD_KEY_INSERT,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* SETNX argument table */
+struct COMMAND_ARG SETNX_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("value",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** SETRANGE ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SETRANGE history */
+#define SETRANGE_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SETRANGE tips */
+#define SETRANGE_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SETRANGE key specs */
+keySpec SETRANGE_Keyspecs[1] = {
+{NULL,CMD_KEY_RW|CMD_KEY_UPDATE,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* SETRANGE argument table */
+struct COMMAND_ARG SETRANGE_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("offset",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("value",ARG_TYPE_STRING,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** STRLEN ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* STRLEN history */
+#define STRLEN_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* STRLEN tips */
+#define STRLEN_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* STRLEN key specs */
+keySpec STRLEN_Keyspecs[1] = {
+{NULL,CMD_KEY_RO,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* STRLEN argument table */
+struct COMMAND_ARG STRLEN_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** SUBSTR ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* SUBSTR history */
+#define SUBSTR_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* SUBSTR tips */
+#define SUBSTR_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* SUBSTR key specs */
+keySpec SUBSTR_Keyspecs[1] = {
+{NULL,CMD_KEY_RO|CMD_KEY_ACCESS,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}
+};
+#endif
+
+/* SUBSTR argument table */
+struct COMMAND_ARG SUBSTR_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("start",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+{MAKE_ARG("end",ARG_TYPE_INTEGER,-1,NULL,NULL,NULL,CMD_ARG_NONE,0,NULL)},
+};
+
+/********** DISCARD ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* DISCARD history */
+#define DISCARD_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* DISCARD tips */
+#define DISCARD_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* DISCARD key specs */
+#define DISCARD_Keyspecs NULL
+#endif
+
+/********** EXEC ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* EXEC history */
+#define EXEC_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* EXEC tips */
+#define EXEC_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* EXEC key specs */
+#define EXEC_Keyspecs NULL
+#endif
+
+/********** MULTI ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* MULTI history */
+#define MULTI_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* MULTI tips */
+#define MULTI_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* MULTI key specs */
+#define MULTI_Keyspecs NULL
+#endif
+
+/********** UNWATCH ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* UNWATCH history */
+#define UNWATCH_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* UNWATCH tips */
+#define UNWATCH_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* UNWATCH key specs */
+#define UNWATCH_Keyspecs NULL
+#endif
+
+/********** WATCH ********************/
+
+#ifndef SKIP_CMD_HISTORY_TABLE
+/* WATCH history */
+#define WATCH_History NULL
+#endif
+
+#ifndef SKIP_CMD_TIPS_TABLE
+/* WATCH tips */
+#define WATCH_Tips NULL
+#endif
+
+#ifndef SKIP_CMD_KEY_SPECS_TABLE
+/* WATCH key specs */
+keySpec WATCH_Keyspecs[1] = {
+{NULL,CMD_KEY_RO,KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={-1,1,0}}
+};
+#endif
+
+/* WATCH argument table */
+struct COMMAND_ARG WATCH_Args[] = {
+{MAKE_ARG("key",ARG_TYPE_KEY,0,NULL,NULL,NULL,CMD_ARG_MULTIPLE,0,NULL)},
+};
+
+/* Main command table */
+struct COMMAND_STRUCT redisCommandTable[] = {
+/* bitmap */
+{MAKE_CMD("bitcount","Counts the number of set bits (population counting) in a string.","O(N)","2.6.0",CMD_DOC_NONE,NULL,NULL,"bitmap",COMMAND_GROUP_BITMAP,BITCOUNT_History,1,BITCOUNT_Tips,0,bitcountCommand,-2,CMD_READONLY,ACL_CATEGORY_BITMAP,BITCOUNT_Keyspecs,1,NULL,2),.args=BITCOUNT_Args},
+{MAKE_CMD("bitfield","Performs arbitrary bitfield integer operations on strings.","O(1) for each subcommand specified","3.2.0",CMD_DOC_NONE,NULL,NULL,"bitmap",COMMAND_GROUP_BITMAP,BITFIELD_History,0,BITFIELD_Tips,0,bitfieldCommand,-2,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_BITMAP,BITFIELD_Keyspecs,1,bitfieldGetKeys,2),.args=BITFIELD_Args},
+{MAKE_CMD("bitfield_ro","Performs arbitrary read-only bitfield integer operations on strings.","O(1) for each subcommand specified","6.0.0",CMD_DOC_NONE,NULL,NULL,"bitmap",COMMAND_GROUP_BITMAP,BITFIELD_RO_History,0,BITFIELD_RO_Tips,0,bitfieldroCommand,-2,CMD_READONLY|CMD_FAST,ACL_CATEGORY_BITMAP,BITFIELD_RO_Keyspecs,1,NULL,2),.args=BITFIELD_RO_Args},
+{MAKE_CMD("bitop","Performs bitwise operations on multiple strings, and stores the result.","O(N)","2.6.0",CMD_DOC_NONE,NULL,NULL,"bitmap",COMMAND_GROUP_BITMAP,BITOP_History,0,BITOP_Tips,0,bitopCommand,-4,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_BITMAP,BITOP_Keyspecs,2,NULL,3),.args=BITOP_Args},
+{MAKE_CMD("bitpos","Finds the first set (1) or clear (0) bit in a string.","O(N)","2.8.7",CMD_DOC_NONE,NULL,NULL,"bitmap",COMMAND_GROUP_BITMAP,BITPOS_History,1,BITPOS_Tips,0,bitposCommand,-3,CMD_READONLY,ACL_CATEGORY_BITMAP,BITPOS_Keyspecs,1,NULL,3),.args=BITPOS_Args},
+{MAKE_CMD("getbit","Returns a bit value by offset.","O(1)","2.2.0",CMD_DOC_NONE,NULL,NULL,"bitmap",COMMAND_GROUP_BITMAP,GETBIT_History,0,GETBIT_Tips,0,getbitCommand,3,CMD_READONLY|CMD_FAST,ACL_CATEGORY_BITMAP,GETBIT_Keyspecs,1,NULL,2),.args=GETBIT_Args},
+{MAKE_CMD("setbit","Sets or clears the bit at offset of the string value. Creates the key if it doesn't exist.","O(1)","2.2.0",CMD_DOC_NONE,NULL,NULL,"bitmap",COMMAND_GROUP_BITMAP,SETBIT_History,0,SETBIT_Tips,0,setbitCommand,4,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_BITMAP,SETBIT_Keyspecs,1,NULL,3),.args=SETBIT_Args},
+/* cluster */
+{MAKE_CMD("asking","Signals that a cluster client is following an -ASK redirect.","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,"cluster",COMMAND_GROUP_CLUSTER,ASKING_History,0,ASKING_Tips,0,askingCommand,1,CMD_FAST,ACL_CATEGORY_CONNECTION,ASKING_Keyspecs,0,NULL,0)},
+{MAKE_CMD("cluster","A container for Redis Cluster commands.","Depends on subcommand.","3.0.0",CMD_DOC_NONE,NULL,NULL,"cluster",COMMAND_GROUP_CLUSTER,CLUSTER_History,0,CLUSTER_Tips,0,NULL,-2,0,0,CLUSTER_Keyspecs,0,NULL,0),.subcommands=CLUSTER_Subcommands},
+{MAKE_CMD("readonly","Enables read-only queries for a connection to a Redis Cluster replica node.","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,"cluster",COMMAND_GROUP_CLUSTER,READONLY_History,0,READONLY_Tips,0,readonlyCommand,1,CMD_FAST|CMD_LOADING|CMD_STALE,ACL_CATEGORY_CONNECTION,READONLY_Keyspecs,0,NULL,0)},
+{MAKE_CMD("readwrite","Enables read-write queries for a connection to a Reids Cluster replica node.","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,"cluster",COMMAND_GROUP_CLUSTER,READWRITE_History,0,READWRITE_Tips,0,readwriteCommand,1,CMD_FAST|CMD_LOADING|CMD_STALE,ACL_CATEGORY_CONNECTION,READWRITE_Keyspecs,0,NULL,0)},
+/* connection */
+{MAKE_CMD("auth","Authenticates the connection.","O(N) where N is the number of passwords defined for the user","1.0.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,AUTH_History,1,AUTH_Tips,0,authCommand,-2,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_FAST|CMD_NO_AUTH|CMD_SENTINEL|CMD_ALLOW_BUSY,ACL_CATEGORY_CONNECTION,AUTH_Keyspecs,0,NULL,2),.args=AUTH_Args},
+{MAKE_CMD("client","A container for client connection commands.","Depends on subcommand.","2.4.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,CLIENT_History,0,CLIENT_Tips,0,NULL,-2,CMD_SENTINEL,0,CLIENT_Keyspecs,0,NULL,0),.subcommands=CLIENT_Subcommands},
+{MAKE_CMD("echo","Returns the given string.","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,ECHO_History,0,ECHO_Tips,0,echoCommand,2,CMD_LOADING|CMD_STALE|CMD_FAST,ACL_CATEGORY_CONNECTION,ECHO_Keyspecs,0,NULL,1),.args=ECHO_Args},
+{MAKE_CMD("hello","Handshakes with the Redis server.","O(1)","6.0.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,HELLO_History,1,HELLO_Tips,0,helloCommand,-1,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_FAST|CMD_NO_AUTH|CMD_SENTINEL|CMD_ALLOW_BUSY,ACL_CATEGORY_CONNECTION,HELLO_Keyspecs,0,NULL,1),.args=HELLO_Args},
+{MAKE_CMD("ping","Returns the server's liveliness response.","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,PING_History,0,PING_Tips,2,pingCommand,-1,CMD_FAST|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,PING_Keyspecs,0,NULL,1),.args=PING_Args},
+{MAKE_CMD("quit","Closes the connection.","O(1)","1.0.0",CMD_DOC_DEPRECATED,"just closing the connection","7.2.0","connection",COMMAND_GROUP_CONNECTION,QUIT_History,0,QUIT_Tips,0,quitCommand,-1,CMD_ALLOW_BUSY|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_FAST|CMD_NO_AUTH,ACL_CATEGORY_CONNECTION,QUIT_Keyspecs,0,NULL,0)},
+{MAKE_CMD("reset","Resets the connection.","O(1)","6.2.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,RESET_History,0,RESET_Tips,0,resetCommand,1,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_FAST|CMD_NO_AUTH|CMD_ALLOW_BUSY,ACL_CATEGORY_CONNECTION,RESET_Keyspecs,0,NULL,0)},
+{MAKE_CMD("select","Changes the selected database.","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,"connection",COMMAND_GROUP_CONNECTION,SELECT_History,0,SELECT_Tips,0,selectCommand,2,CMD_LOADING|CMD_STALE|CMD_FAST,ACL_CATEGORY_CONNECTION,SELECT_Keyspecs,0,NULL,1),.args=SELECT_Args},
+/* generic */
+{MAKE_CMD("copy","Copies the value of a key to a new key.","O(N) worst case for collections, where N is the number of nested items. O(1) for string values.","6.2.0",CMD_DOC_NONE,NULL,NULL,"generic",COMMAND_GROUP_GENERIC,COPY_History,0,COPY_Tips,0,copyCommand,-3,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_KEYSPACE,COPY_Keyspecs,2,NULL,4),.args=COPY_Args},
+{MAKE_CMD("del","Deletes one or more keys.","O(N) where N is the number of keys that will be removed. When a key to remove holds a value other than a string, the individual complexity for this key is O(M) where M is the number of elements in the list, set, sorted set or hash. Removing a single key that holds a string value is O(1).","1.0.0",CMD_DOC_NONE,NULL,NULL,"generic",COMMAND_GROUP_GENERIC,DEL_History,0,DEL_Tips,2,delCommand,-2,CMD_WRITE,ACL_CATEGORY_KEYSPACE,DEL_Keyspecs,1,NULL,1),.args=DEL_Args},
+{MAKE_CMD("dump","Returns a serialized representation of the value stored at a key.","O(1) to access the key and additional O(N*M) to serialize it, where N is the number of Redis objects composing the value and M their average size. For small string values the time complexity is thus O(1)+O(1*M) where M is small, so simply O(1).","2.6.0",CMD_DOC_NONE,NULL,NULL,"generic",COMMAND_GROUP_GENERIC,DUMP_History,0,DUMP_Tips,1,dumpCommand,2,CMD_READONLY,ACL_CATEGORY_KEYSPACE,DUMP_Keyspecs,1,NULL,1),.args=DUMP_Args},
+{MAKE_CMD("exists","Determines whether one or more keys exist.","O(N) where N is the number of keys to check.","1.0.0",CMD_DOC_NONE,NULL,NULL,"generic",COMMAND_GROUP_GENERIC,EXISTS_History,1,EXISTS_Tips,2,existsCommand,-2,CMD_READONLY|CMD_FAST,ACL_CATEGORY_KEYSPACE,EXISTS_Keyspecs,1,NULL,1),.args=EXISTS_Args},
+{MAKE_CMD("expire","Sets the expiration time of a key in seconds.","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,"generic",COMMAND_GROUP_GENERIC,EXPIRE_History,1,EXPIRE_Tips,0,expireCommand,-3,CMD_WRITE|CMD_FAST,ACL_CATEGORY_KEYSPACE,EXPIRE_Keyspecs,1,NULL,3),.args=EXPIRE_Args},
+{MAKE_CMD("expireat","Sets the expiration time of a key to a Unix timestamp.","O(1)","1.2.0",CMD_DOC_NONE,NULL,NULL,"generic",COMMAND_GROUP_GENERIC,EXPIREAT_History,1,EXPIREAT_Tips,0,expireatCommand,-3,CMD_WRITE|CMD_FAST,ACL_CATEGORY_KEYSPACE,EXPIREAT_Keyspecs,1,NULL,3),.args=EXPIREAT_Args},
+{MAKE_CMD("expiretime","Returns the expiration time of a key as a Unix timestamp.","O(1)","7.0.0",CMD_DOC_NONE,NULL,NULL,"generic",COMMAND_GROUP_GENERIC,EXPIRETIME_History,0,EXPIRETIME_Tips,0,expiretimeCommand,2,CMD_READONLY|CMD_FAST,ACL_CATEGORY_KEYSPACE,EXPIRETIME_Keyspecs,1,NULL,1),.args=EXPIRETIME_Args},
+{MAKE_CMD("keys","Returns all key names that match a pattern.","O(N) with N being the number of keys in the database, under the assumption that the key names in the database and the given pattern have limited length.","1.0.0",CMD_DOC_NONE,NULL,NULL,"generic",COMMAND_GROUP_GENERIC,KEYS_History,0,KEYS_Tips,2,keysCommand,2,CMD_READONLY,ACL_CATEGORY_KEYSPACE|ACL_CATEGORY_DANGEROUS,KEYS_Keyspecs,0,NULL,1),.args=KEYS_Args},
+{MAKE_CMD("migrate","Atomically transfers a key from one Redis instance to another.","This command actually executes a DUMP+DEL in the source instance, and a RESTORE in the target instance. See the pages of these commands for time complexity. Also an O(N) data transfer between the two instances is performed.","2.6.0",CMD_DOC_NONE,NULL,NULL,"generic",COMMAND_GROUP_GENERIC,MIGRATE_History,4,MIGRATE_Tips,1,migrateCommand,-6,CMD_WRITE,ACL_CATEGORY_KEYSPACE|ACL_CATEGORY_DANGEROUS,MIGRATE_Keyspecs,2,migrateGetKeys,9),.args=MIGRATE_Args},
+{MAKE_CMD("move","Moves a key to another database.","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,"generic",COMMAND_GROUP_GENERIC,MOVE_History,0,MOVE_Tips,0,moveCommand,3,CMD_WRITE|CMD_FAST,ACL_CATEGORY_KEYSPACE,MOVE_Keyspecs,1,NULL,2),.args=MOVE_Args},
+{MAKE_CMD("object","A container for object introspection commands.","Depends on subcommand.","2.2.3",CMD_DOC_NONE,NULL,NULL,"generic",COMMAND_GROUP_GENERIC,OBJECT_History,0,OBJECT_Tips,0,NULL,-2,0,0,OBJECT_Keyspecs,0,NULL,0),.subcommands=OBJECT_Subcommands},
+{MAKE_CMD("persist","Removes the expiration time of a key.","O(1)","2.2.0",CMD_DOC_NONE,NULL,NULL,"generic",COMMAND_GROUP_GENERIC,PERSIST_History,0,PERSIST_Tips,0,persistCommand,2,CMD_WRITE|CMD_FAST,ACL_CATEGORY_KEYSPACE,PERSIST_Keyspecs,1,NULL,1),.args=PERSIST_Args},
+{MAKE_CMD("pexpire","Sets the expiration time of a key in milliseconds.","O(1)","2.6.0",CMD_DOC_NONE,NULL,NULL,"generic",COMMAND_GROUP_GENERIC,PEXPIRE_History,1,PEXPIRE_Tips,0,pexpireCommand,-3,CMD_WRITE|CMD_FAST,ACL_CATEGORY_KEYSPACE,PEXPIRE_Keyspecs,1,NULL,3),.args=PEXPIRE_Args},
+{MAKE_CMD("pexpireat","Sets the expiration time of a key to a Unix milliseconds timestamp.","O(1)","2.6.0",CMD_DOC_NONE,NULL,NULL,"generic",COMMAND_GROUP_GENERIC,PEXPIREAT_History,1,PEXPIREAT_Tips,0,pexpireatCommand,-3,CMD_WRITE|CMD_FAST,ACL_CATEGORY_KEYSPACE,PEXPIREAT_Keyspecs,1,NULL,3),.args=PEXPIREAT_Args},
+{MAKE_CMD("pexpiretime","Returns the expiration time of a key as a Unix milliseconds timestamp.","O(1)","7.0.0",CMD_DOC_NONE,NULL,NULL,"generic",COMMAND_GROUP_GENERIC,PEXPIRETIME_History,0,PEXPIRETIME_Tips,0,pexpiretimeCommand,2,CMD_READONLY|CMD_FAST,ACL_CATEGORY_KEYSPACE,PEXPIRETIME_Keyspecs,1,NULL,1),.args=PEXPIRETIME_Args},
+{MAKE_CMD("pttl","Returns the expiration time in milliseconds of a key.","O(1)","2.6.0",CMD_DOC_NONE,NULL,NULL,"generic",COMMAND_GROUP_GENERIC,PTTL_History,1,PTTL_Tips,1,pttlCommand,2,CMD_READONLY|CMD_FAST,ACL_CATEGORY_KEYSPACE,PTTL_Keyspecs,1,NULL,1),.args=PTTL_Args},
+{MAKE_CMD("randomkey","Returns a random key name from the database.","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,"generic",COMMAND_GROUP_GENERIC,RANDOMKEY_History,0,RANDOMKEY_Tips,2,randomkeyCommand,1,CMD_READONLY|CMD_TOUCHES_ARBITRARY_KEYS,ACL_CATEGORY_KEYSPACE,RANDOMKEY_Keyspecs,0,NULL,0)},
+{MAKE_CMD("rename","Renames a key and overwrites the destination.","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,"generic",COMMAND_GROUP_GENERIC,RENAME_History,0,RENAME_Tips,0,renameCommand,3,CMD_WRITE,ACL_CATEGORY_KEYSPACE,RENAME_Keyspecs,2,NULL,2),.args=RENAME_Args},
+{MAKE_CMD("renamenx","Renames a key only when the target key name doesn't exist.","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,"generic",COMMAND_GROUP_GENERIC,RENAMENX_History,1,RENAMENX_Tips,0,renamenxCommand,3,CMD_WRITE|CMD_FAST,ACL_CATEGORY_KEYSPACE,RENAMENX_Keyspecs,2,NULL,2),.args=RENAMENX_Args},
+{MAKE_CMD("restore","Creates a key from the serialized representation of a value.","O(1) to create the new key and additional O(N*M) to reconstruct the serialized value, where N is the number of Redis objects composing the value and M their average size. For small string values the time complexity is thus O(1)+O(1*M) where M is small, so simply O(1). However for sorted set values the complexity is O(N*M*log(N)) because inserting values into sorted sets is O(log(N)).","2.6.0",CMD_DOC_NONE,NULL,NULL,"generic",COMMAND_GROUP_GENERIC,RESTORE_History,3,RESTORE_Tips,0,restoreCommand,-4,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_KEYSPACE|ACL_CATEGORY_DANGEROUS,RESTORE_Keyspecs,1,NULL,7),.args=RESTORE_Args},
+{MAKE_CMD("scan","Iterates over the key names in the database.","O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.","2.8.0",CMD_DOC_NONE,NULL,NULL,"generic",COMMAND_GROUP_GENERIC,SCAN_History,1,SCAN_Tips,2,scanCommand,-2,CMD_READONLY|CMD_TOUCHES_ARBITRARY_KEYS,ACL_CATEGORY_KEYSPACE,SCAN_Keyspecs,0,NULL,4),.args=SCAN_Args},
+{MAKE_CMD("sort","Sorts the elements in a list, a set, or a sorted set, optionally storing the result.","O(N+M*log(M)) where N is the number of elements in the list or set to sort, and M the number of returned elements. When the elements are not sorted, complexity is O(N).","1.0.0",CMD_DOC_NONE,NULL,NULL,"generic",COMMAND_GROUP_GENERIC,SORT_History,0,SORT_Tips,0,sortCommand,-2,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_SET|ACL_CATEGORY_SORTEDSET|ACL_CATEGORY_LIST|ACL_CATEGORY_DANGEROUS,SORT_Keyspecs,3,sortGetKeys,7),.args=SORT_Args},
+{MAKE_CMD("sort_ro","Returns the sorted elements of a list, a set, or a sorted set.","O(N+M*log(M)) where N is the number of elements in the list or set to sort, and M the number of returned elements. When the elements are not sorted, complexity is O(N).","7.0.0",CMD_DOC_NONE,NULL,NULL,"generic",COMMAND_GROUP_GENERIC,SORT_RO_History,0,SORT_RO_Tips,0,sortroCommand,-2,CMD_READONLY,ACL_CATEGORY_SET|ACL_CATEGORY_SORTEDSET|ACL_CATEGORY_LIST|ACL_CATEGORY_DANGEROUS,SORT_RO_Keyspecs,2,sortROGetKeys,6),.args=SORT_RO_Args},
+{MAKE_CMD("touch","Returns the number of existing keys out of those specified after updating the time they were last accessed.","O(N) where N is the number of keys that will be touched.","3.2.1",CMD_DOC_NONE,NULL,NULL,"generic",COMMAND_GROUP_GENERIC,TOUCH_History,0,TOUCH_Tips,2,touchCommand,-2,CMD_READONLY|CMD_FAST,ACL_CATEGORY_KEYSPACE,TOUCH_Keyspecs,1,NULL,1),.args=TOUCH_Args},
+{MAKE_CMD("ttl","Returns the expiration time in seconds of a key.","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,"generic",COMMAND_GROUP_GENERIC,TTL_History,1,TTL_Tips,1,ttlCommand,2,CMD_READONLY|CMD_FAST,ACL_CATEGORY_KEYSPACE,TTL_Keyspecs,1,NULL,1),.args=TTL_Args},
+{MAKE_CMD("type","Determines the type of value stored at a key.","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,"generic",COMMAND_GROUP_GENERIC,TYPE_History,0,TYPE_Tips,0,typeCommand,2,CMD_READONLY|CMD_FAST,ACL_CATEGORY_KEYSPACE,TYPE_Keyspecs,1,NULL,1),.args=TYPE_Args},
+{MAKE_CMD("unlink","Asynchronously deletes one or more keys.","O(1) for each key removed regardless of its size. Then the command does O(N) work in a different thread in order to reclaim memory, where N is the number of allocations the deleted objects where composed of.","4.0.0",CMD_DOC_NONE,NULL,NULL,"generic",COMMAND_GROUP_GENERIC,UNLINK_History,0,UNLINK_Tips,2,unlinkCommand,-2,CMD_WRITE|CMD_FAST,ACL_CATEGORY_KEYSPACE,UNLINK_Keyspecs,1,NULL,1),.args=UNLINK_Args},
+{MAKE_CMD("wait","Blocks until the asynchronous replication of all preceding write commands sent by the connection is completed.","O(1)","3.0.0",CMD_DOC_NONE,NULL,NULL,"generic",COMMAND_GROUP_GENERIC,WAIT_History,0,WAIT_Tips,2,waitCommand,3,0,ACL_CATEGORY_CONNECTION,WAIT_Keyspecs,0,NULL,2),.args=WAIT_Args},
+{MAKE_CMD("waitaof","Blocks until all of the preceding write commands sent by the connection are written to the append-only file of the master and/or replicas.","O(1)","7.2.0",CMD_DOC_NONE,NULL,NULL,"generic",COMMAND_GROUP_GENERIC,WAITAOF_History,0,WAITAOF_Tips,2,waitaofCommand,4,CMD_NOSCRIPT,ACL_CATEGORY_CONNECTION,WAITAOF_Keyspecs,0,NULL,3),.args=WAITAOF_Args},
+/* geo */
+{MAKE_CMD("geoadd","Adds one or more members to a geospatial index. The key is created if it doesn't exist.","O(log(N)) for each item added, where N is the number of elements in the sorted set.","3.2.0",CMD_DOC_NONE,NULL,NULL,"geo",COMMAND_GROUP_GEO,GEOADD_History,1,GEOADD_Tips,0,geoaddCommand,-5,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_GEO,GEOADD_Keyspecs,1,NULL,4),.args=GEOADD_Args},
+{MAKE_CMD("geodist","Returns the distance between two members of a geospatial index.","O(log(N))","3.2.0",CMD_DOC_NONE,NULL,NULL,"geo",COMMAND_GROUP_GEO,GEODIST_History,0,GEODIST_Tips,0,geodistCommand,-4,CMD_READONLY,ACL_CATEGORY_GEO,GEODIST_Keyspecs,1,NULL,4),.args=GEODIST_Args},
+{MAKE_CMD("geohash","Returns members from a geospatial index as geohash strings.","O(log(N)) for each member requested, where N is the number of elements in the sorted set.","3.2.0",CMD_DOC_NONE,NULL,NULL,"geo",COMMAND_GROUP_GEO,GEOHASH_History,0,GEOHASH_Tips,0,geohashCommand,-2,CMD_READONLY,ACL_CATEGORY_GEO,GEOHASH_Keyspecs,1,NULL,2),.args=GEOHASH_Args},
+{MAKE_CMD("geopos","Returns the longitude and latitude of members from a geospatial index.","O(N) where N is the number of members requested.","3.2.0",CMD_DOC_NONE,NULL,NULL,"geo",COMMAND_GROUP_GEO,GEOPOS_History,0,GEOPOS_Tips,0,geoposCommand,-2,CMD_READONLY,ACL_CATEGORY_GEO,GEOPOS_Keyspecs,1,NULL,2),.args=GEOPOS_Args},
+{MAKE_CMD("georadius","Queries a geospatial index for members within a distance from a coordinate, optionally stores the result.","O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.","3.2.0",CMD_DOC_DEPRECATED,"`GEOSEARCH` and `GEOSEARCHSTORE` with the `BYRADIUS` argument","6.2.0","geo",COMMAND_GROUP_GEO,GEORADIUS_History,2,GEORADIUS_Tips,0,georadiusCommand,-6,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_GEO,GEORADIUS_Keyspecs,3,georadiusGetKeys,11),.args=GEORADIUS_Args},
+{MAKE_CMD("georadiusbymember","Queries a geospatial index for members within a distance from a member, optionally stores the result.","O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.","3.2.0",CMD_DOC_DEPRECATED,"`GEOSEARCH` and `GEOSEARCHSTORE` with the `BYRADIUS` and `FROMMEMBER` arguments","6.2.0","geo",COMMAND_GROUP_GEO,GEORADIUSBYMEMBER_History,1,GEORADIUSBYMEMBER_Tips,0,georadiusbymemberCommand,-5,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_GEO,GEORADIUSBYMEMBER_Keyspecs,3,georadiusGetKeys,10),.args=GEORADIUSBYMEMBER_Args},
+{MAKE_CMD("georadiusbymember_ro","Returns members from a geospatial index that are within a distance from a member.","O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.","3.2.10",CMD_DOC_DEPRECATED,"`GEOSEARCH` with the `BYRADIUS` and `FROMMEMBER` arguments","6.2.0","geo",COMMAND_GROUP_GEO,GEORADIUSBYMEMBER_RO_History,0,GEORADIUSBYMEMBER_RO_Tips,0,georadiusbymemberroCommand,-5,CMD_READONLY,ACL_CATEGORY_GEO,GEORADIUSBYMEMBER_RO_Keyspecs,1,NULL,9),.args=GEORADIUSBYMEMBER_RO_Args},
+{MAKE_CMD("georadius_ro","Returns members from a geospatial index that are within a distance from a coordinate.","O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.","3.2.10",CMD_DOC_DEPRECATED,"`GEOSEARCH` with the `BYRADIUS` argument","6.2.0","geo",COMMAND_GROUP_GEO,GEORADIUS_RO_History,1,GEORADIUS_RO_Tips,0,georadiusroCommand,-6,CMD_READONLY,ACL_CATEGORY_GEO,GEORADIUS_RO_Keyspecs,1,NULL,10),.args=GEORADIUS_RO_Args},
+{MAKE_CMD("geosearch","Queries a geospatial index for members inside an area of a box or a circle.","O(N+log(M)) where N is the number of elements in the grid-aligned bounding box area around the shape provided as the filter and M is the number of items inside the shape","6.2.0",CMD_DOC_NONE,NULL,NULL,"geo",COMMAND_GROUP_GEO,GEOSEARCH_History,1,GEOSEARCH_Tips,0,geosearchCommand,-7,CMD_READONLY,ACL_CATEGORY_GEO,GEOSEARCH_Keyspecs,1,NULL,8),.args=GEOSEARCH_Args},
+{MAKE_CMD("geosearchstore","Queries a geospatial index for members inside an area of a box or a circle, optionally stores the result.","O(N+log(M)) where N is the number of elements in the grid-aligned bounding box area around the shape provided as the filter and M is the number of items inside the shape","6.2.0",CMD_DOC_NONE,NULL,NULL,"geo",COMMAND_GROUP_GEO,GEOSEARCHSTORE_History,1,GEOSEARCHSTORE_Tips,0,geosearchstoreCommand,-8,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_GEO,GEOSEARCHSTORE_Keyspecs,2,NULL,7),.args=GEOSEARCHSTORE_Args},
+/* hash */
+{MAKE_CMD("hdel","Deletes one or more fields and their values from a hash. Deletes the hash if no fields remain.","O(N) where N is the number of fields to be removed.","2.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HDEL_History,1,HDEL_Tips,0,hdelCommand,-3,CMD_WRITE|CMD_FAST,ACL_CATEGORY_HASH,HDEL_Keyspecs,1,NULL,2),.args=HDEL_Args},
+{MAKE_CMD("hexists","Determines whether a field exists in a hash.","O(1)","2.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HEXISTS_History,0,HEXISTS_Tips,0,hexistsCommand,3,CMD_READONLY|CMD_FAST,ACL_CATEGORY_HASH,HEXISTS_Keyspecs,1,NULL,2),.args=HEXISTS_Args},
+{MAKE_CMD("hget","Returns the value of a field in a hash.","O(1)","2.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HGET_History,0,HGET_Tips,0,hgetCommand,3,CMD_READONLY|CMD_FAST,ACL_CATEGORY_HASH,HGET_Keyspecs,1,NULL,2),.args=HGET_Args},
+{MAKE_CMD("hgetall","Returns all fields and values in a hash.","O(N) where N is the size of the hash.","2.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HGETALL_History,0,HGETALL_Tips,1,hgetallCommand,2,CMD_READONLY,ACL_CATEGORY_HASH,HGETALL_Keyspecs,1,NULL,1),.args=HGETALL_Args},
+{MAKE_CMD("hincrby","Increments the integer value of a field in a hash by a number. Uses 0 as initial value if the field doesn't exist.","O(1)","2.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HINCRBY_History,0,HINCRBY_Tips,0,hincrbyCommand,4,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_HASH,HINCRBY_Keyspecs,1,NULL,3),.args=HINCRBY_Args},
+{MAKE_CMD("hincrbyfloat","Increments the floating point value of a field by a number. Uses 0 as initial value if the field doesn't exist.","O(1)","2.6.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HINCRBYFLOAT_History,0,HINCRBYFLOAT_Tips,0,hincrbyfloatCommand,4,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_HASH,HINCRBYFLOAT_Keyspecs,1,NULL,3),.args=HINCRBYFLOAT_Args},
+{MAKE_CMD("hkeys","Returns all fields in a hash.","O(N) where N is the size of the hash.","2.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HKEYS_History,0,HKEYS_Tips,1,hkeysCommand,2,CMD_READONLY,ACL_CATEGORY_HASH,HKEYS_Keyspecs,1,NULL,1),.args=HKEYS_Args},
+{MAKE_CMD("hlen","Returns the number of fields in a hash.","O(1)","2.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HLEN_History,0,HLEN_Tips,0,hlenCommand,2,CMD_READONLY|CMD_FAST,ACL_CATEGORY_HASH,HLEN_Keyspecs,1,NULL,1),.args=HLEN_Args},
+{MAKE_CMD("hmget","Returns the values of all fields in a hash.","O(N) where N is the number of fields being requested.","2.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HMGET_History,0,HMGET_Tips,0,hmgetCommand,-3,CMD_READONLY|CMD_FAST,ACL_CATEGORY_HASH,HMGET_Keyspecs,1,NULL,2),.args=HMGET_Args},
+{MAKE_CMD("hmset","Sets the values of multiple fields.","O(N) where N is the number of fields being set.","2.0.0",CMD_DOC_DEPRECATED,"`HSET` with multiple field-value pairs","4.0.0","hash",COMMAND_GROUP_HASH,HMSET_History,0,HMSET_Tips,0,hsetCommand,-4,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_HASH,HMSET_Keyspecs,1,NULL,2),.args=HMSET_Args},
+{MAKE_CMD("hrandfield","Returns one or more random fields from a hash.","O(N) where N is the number of fields returned","6.2.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HRANDFIELD_History,0,HRANDFIELD_Tips,1,hrandfieldCommand,-2,CMD_READONLY,ACL_CATEGORY_HASH,HRANDFIELD_Keyspecs,1,NULL,2),.args=HRANDFIELD_Args},
+{MAKE_CMD("hscan","Iterates over fields and values of a hash.","O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.","2.8.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HSCAN_History,0,HSCAN_Tips,1,hscanCommand,-3,CMD_READONLY,ACL_CATEGORY_HASH,HSCAN_Keyspecs,1,NULL,4),.args=HSCAN_Args},
+{MAKE_CMD("hset","Creates or modifies the value of a field in a hash.","O(1) for each field/value pair added, so O(N) to add N field/value pairs when the command is called with multiple field/value pairs.","2.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HSET_History,1,HSET_Tips,0,hsetCommand,-4,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_HASH,HSET_Keyspecs,1,NULL,2),.args=HSET_Args},
+{MAKE_CMD("hsetnx","Sets the value of a field in a hash only when the field doesn't exist.","O(1)","2.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HSETNX_History,0,HSETNX_Tips,0,hsetnxCommand,4,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_HASH,HSETNX_Keyspecs,1,NULL,3),.args=HSETNX_Args},
+{MAKE_CMD("hstrlen","Returns the length of the value of a field.","O(1)","3.2.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HSTRLEN_History,0,HSTRLEN_Tips,0,hstrlenCommand,3,CMD_READONLY|CMD_FAST,ACL_CATEGORY_HASH,HSTRLEN_Keyspecs,1,NULL,2),.args=HSTRLEN_Args},
+{MAKE_CMD("hvals","Returns all values in a hash.","O(N) where N is the size of the hash.","2.0.0",CMD_DOC_NONE,NULL,NULL,"hash",COMMAND_GROUP_HASH,HVALS_History,0,HVALS_Tips,1,hvalsCommand,2,CMD_READONLY,ACL_CATEGORY_HASH,HVALS_Keyspecs,1,NULL,1),.args=HVALS_Args},
+/* hyperloglog */
+{MAKE_CMD("pfadd","Adds elements to a HyperLogLog key. Creates the key if it doesn't exist.","O(1) to add every element.","2.8.9",CMD_DOC_NONE,NULL,NULL,"hyperloglog",COMMAND_GROUP_HYPERLOGLOG,PFADD_History,0,PFADD_Tips,0,pfaddCommand,-2,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_HYPERLOGLOG,PFADD_Keyspecs,1,NULL,2),.args=PFADD_Args},
+{MAKE_CMD("pfcount","Returns the approximated cardinality of the set(s) observed by the HyperLogLog key(s).","O(1) with a very small average constant time when called with a single key. O(N) with N being the number of keys, and much bigger constant times, when called with multiple keys.","2.8.9",CMD_DOC_NONE,NULL,NULL,"hyperloglog",COMMAND_GROUP_HYPERLOGLOG,PFCOUNT_History,0,PFCOUNT_Tips,0,pfcountCommand,-2,CMD_READONLY|CMD_MAY_REPLICATE,ACL_CATEGORY_HYPERLOGLOG,PFCOUNT_Keyspecs,1,NULL,1),.args=PFCOUNT_Args},
+{MAKE_CMD("pfdebug","Internal commands for debugging HyperLogLog values.","N/A","2.8.9",CMD_DOC_SYSCMD,NULL,NULL,"hyperloglog",COMMAND_GROUP_HYPERLOGLOG,PFDEBUG_History,0,PFDEBUG_Tips,0,pfdebugCommand,3,CMD_WRITE|CMD_DENYOOM|CMD_ADMIN,ACL_CATEGORY_HYPERLOGLOG,PFDEBUG_Keyspecs,1,NULL,2),.args=PFDEBUG_Args},
+{MAKE_CMD("pfmerge","Merges one or more HyperLogLog values into a single key.","O(N) to merge N HyperLogLogs, but with high constant times.","2.8.9",CMD_DOC_NONE,NULL,NULL,"hyperloglog",COMMAND_GROUP_HYPERLOGLOG,PFMERGE_History,0,PFMERGE_Tips,0,pfmergeCommand,-2,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_HYPERLOGLOG,PFMERGE_Keyspecs,2,NULL,2),.args=PFMERGE_Args},
+{MAKE_CMD("pfselftest","An internal command for testing HyperLogLog values.","N/A","2.8.9",CMD_DOC_SYSCMD,NULL,NULL,"hyperloglog",COMMAND_GROUP_HYPERLOGLOG,PFSELFTEST_History,0,PFSELFTEST_Tips,0,pfselftestCommand,1,CMD_ADMIN,ACL_CATEGORY_HYPERLOGLOG,PFSELFTEST_Keyspecs,0,NULL,0)},
+/* list */
+{MAKE_CMD("blmove","Pops an element from a list, pushes it to another list and returns it. Blocks until an element is available otherwise. Deletes the list if the last element was moved.","O(1)","6.2.0",CMD_DOC_NONE,NULL,NULL,"list",COMMAND_GROUP_LIST,BLMOVE_History,0,BLMOVE_Tips,0,blmoveCommand,6,CMD_WRITE|CMD_DENYOOM|CMD_BLOCKING,ACL_CATEGORY_LIST,BLMOVE_Keyspecs,2,NULL,5),.args=BLMOVE_Args},
+{MAKE_CMD("blmpop","Pops the first element from one of multiple lists. Blocks until an element is available otherwise. Deletes the list if the last element was popped.","O(N+M) where N is the number of provided keys and M is the number of elements returned.","7.0.0",CMD_DOC_NONE,NULL,NULL,"list",COMMAND_GROUP_LIST,BLMPOP_History,0,BLMPOP_Tips,0,blmpopCommand,-5,CMD_WRITE|CMD_BLOCKING,ACL_CATEGORY_LIST,BLMPOP_Keyspecs,1,blmpopGetKeys,5),.args=BLMPOP_Args},
+{MAKE_CMD("blpop","Removes and returns the first element in a list. Blocks until an element is available otherwise. Deletes the list if the last element was popped.","O(N) where N is the number of provided keys.","2.0.0",CMD_DOC_NONE,NULL,NULL,"list",COMMAND_GROUP_LIST,BLPOP_History,1,BLPOP_Tips,0,blpopCommand,-3,CMD_WRITE|CMD_BLOCKING,ACL_CATEGORY_LIST,BLPOP_Keyspecs,1,NULL,2),.args=BLPOP_Args},
+{MAKE_CMD("brpop","Removes and returns the last element in a list. Blocks until an element is available otherwise. Deletes the list if the last element was popped.","O(N) where N is the number of provided keys.","2.0.0",CMD_DOC_NONE,NULL,NULL,"list",COMMAND_GROUP_LIST,BRPOP_History,1,BRPOP_Tips,0,brpopCommand,-3,CMD_WRITE|CMD_BLOCKING,ACL_CATEGORY_LIST,BRPOP_Keyspecs,1,NULL,2),.args=BRPOP_Args},
+{MAKE_CMD("brpoplpush","Pops an element from a list, pushes it to another list and returns it. Block until an element is available otherwise. Deletes the list if the last element was popped.","O(1)","2.2.0",CMD_DOC_DEPRECATED,"`BLMOVE` with the `RIGHT` and `LEFT` arguments","6.2.0","list",COMMAND_GROUP_LIST,BRPOPLPUSH_History,1,BRPOPLPUSH_Tips,0,brpoplpushCommand,4,CMD_WRITE|CMD_DENYOOM|CMD_BLOCKING,ACL_CATEGORY_LIST,BRPOPLPUSH_Keyspecs,2,NULL,3),.args=BRPOPLPUSH_Args},
+{MAKE_CMD("lindex","Returns an element from a list by its index.","O(N) where N is the number of elements to traverse to get to the element at index. This makes asking for the first or the last element of the list O(1).","1.0.0",CMD_DOC_NONE,NULL,NULL,"list",COMMAND_GROUP_LIST,LINDEX_History,0,LINDEX_Tips,0,lindexCommand,3,CMD_READONLY,ACL_CATEGORY_LIST,LINDEX_Keyspecs,1,NULL,2),.args=LINDEX_Args},
+{MAKE_CMD("linsert","Inserts an element before or after another element in a list.","O(N) where N is the number of elements to traverse before seeing the value pivot. This means that inserting somewhere on the left end on the list (head) can be considered O(1) and inserting somewhere on the right end (tail) is O(N).","2.2.0",CMD_DOC_NONE,NULL,NULL,"list",COMMAND_GROUP_LIST,LINSERT_History,0,LINSERT_Tips,0,linsertCommand,5,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_LIST,LINSERT_Keyspecs,1,NULL,4),.args=LINSERT_Args},
+{MAKE_CMD("llen","Returns the length of a list.","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,"list",COMMAND_GROUP_LIST,LLEN_History,0,LLEN_Tips,0,llenCommand,2,CMD_READONLY|CMD_FAST,ACL_CATEGORY_LIST,LLEN_Keyspecs,1,NULL,1),.args=LLEN_Args},
+{MAKE_CMD("lmove","Returns an element after popping it from one list and pushing it to another. Deletes the list if the last element was moved.","O(1)","6.2.0",CMD_DOC_NONE,NULL,NULL,"list",COMMAND_GROUP_LIST,LMOVE_History,0,LMOVE_Tips,0,lmoveCommand,5,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_LIST,LMOVE_Keyspecs,2,NULL,4),.args=LMOVE_Args},
+{MAKE_CMD("lmpop","Returns multiple elements from a list after removing them. Deletes the list if the last element was popped.","O(N+M) where N is the number of provided keys and M is the number of elements returned.","7.0.0",CMD_DOC_NONE,NULL,NULL,"list",COMMAND_GROUP_LIST,LMPOP_History,0,LMPOP_Tips,0,lmpopCommand,-4,CMD_WRITE,ACL_CATEGORY_LIST,LMPOP_Keyspecs,1,lmpopGetKeys,4),.args=LMPOP_Args},
+{MAKE_CMD("lpop","Returns the first elements in a list after removing it. Deletes the list if the last element was popped.","O(N) where N is the number of elements returned","1.0.0",CMD_DOC_NONE,NULL,NULL,"list",COMMAND_GROUP_LIST,LPOP_History,1,LPOP_Tips,0,lpopCommand,-2,CMD_WRITE|CMD_FAST,ACL_CATEGORY_LIST,LPOP_Keyspecs,1,NULL,2),.args=LPOP_Args},
+{MAKE_CMD("lpos","Returns the index of matching elements in a list.","O(N) where N is the number of elements in the list, for the average case. When searching for elements near the head or the tail of the list, or when the MAXLEN option is provided, the command may run in constant time.","6.0.6",CMD_DOC_NONE,NULL,NULL,"list",COMMAND_GROUP_LIST,LPOS_History,0,LPOS_Tips,0,lposCommand,-3,CMD_READONLY,ACL_CATEGORY_LIST,LPOS_Keyspecs,1,NULL,5),.args=LPOS_Args},
+{MAKE_CMD("lpush","Prepends one or more elements to a list. Creates the key if it doesn't exist.","O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.","1.0.0",CMD_DOC_NONE,NULL,NULL,"list",COMMAND_GROUP_LIST,LPUSH_History,1,LPUSH_Tips,0,lpushCommand,-3,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_LIST,LPUSH_Keyspecs,1,NULL,2),.args=LPUSH_Args},
+{MAKE_CMD("lpushx","Prepends one or more elements to a list only when the list exists.","O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.","2.2.0",CMD_DOC_NONE,NULL,NULL,"list",COMMAND_GROUP_LIST,LPUSHX_History,1,LPUSHX_Tips,0,lpushxCommand,-3,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_LIST,LPUSHX_Keyspecs,1,NULL,2),.args=LPUSHX_Args},
+{MAKE_CMD("lrange","Returns a range of elements from a list.","O(S+N) where S is the distance of start offset from HEAD for small lists, from nearest end (HEAD or TAIL) for large lists; and N is the number of elements in the specified range.","1.0.0",CMD_DOC_NONE,NULL,NULL,"list",COMMAND_GROUP_LIST,LRANGE_History,0,LRANGE_Tips,0,lrangeCommand,4,CMD_READONLY,ACL_CATEGORY_LIST,LRANGE_Keyspecs,1,NULL,3),.args=LRANGE_Args},
+{MAKE_CMD("lrem","Removes elements from a list. Deletes the list if the last element was removed.","O(N+M) where N is the length of the list and M is the number of elements removed.","1.0.0",CMD_DOC_NONE,NULL,NULL,"list",COMMAND_GROUP_LIST,LREM_History,0,LREM_Tips,0,lremCommand,4,CMD_WRITE,ACL_CATEGORY_LIST,LREM_Keyspecs,1,NULL,3),.args=LREM_Args},
+{MAKE_CMD("lset","Sets the value of an element in a list by its index.","O(N) where N is the length of the list. Setting either the first or the last element of the list is O(1).","1.0.0",CMD_DOC_NONE,NULL,NULL,"list",COMMAND_GROUP_LIST,LSET_History,0,LSET_Tips,0,lsetCommand,4,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_LIST,LSET_Keyspecs,1,NULL,3),.args=LSET_Args},
+{MAKE_CMD("ltrim","Removes elements from both ends a list. Deletes the list if all elements were trimmed.","O(N) where N is the number of elements to be removed by the operation.","1.0.0",CMD_DOC_NONE,NULL,NULL,"list",COMMAND_GROUP_LIST,LTRIM_History,0,LTRIM_Tips,0,ltrimCommand,4,CMD_WRITE,ACL_CATEGORY_LIST,LTRIM_Keyspecs,1,NULL,3),.args=LTRIM_Args},
+{MAKE_CMD("rpop","Returns and removes the last elements of a list. Deletes the list if the last element was popped.","O(N) where N is the number of elements returned","1.0.0",CMD_DOC_NONE,NULL,NULL,"list",COMMAND_GROUP_LIST,RPOP_History,1,RPOP_Tips,0,rpopCommand,-2,CMD_WRITE|CMD_FAST,ACL_CATEGORY_LIST,RPOP_Keyspecs,1,NULL,2),.args=RPOP_Args},
+{MAKE_CMD("rpoplpush","Returns the last element of a list after removing and pushing it to another list. Deletes the list if the last element was popped.","O(1)","1.2.0",CMD_DOC_DEPRECATED,"`LMOVE` with the `RIGHT` and `LEFT` arguments","6.2.0","list",COMMAND_GROUP_LIST,RPOPLPUSH_History,0,RPOPLPUSH_Tips,0,rpoplpushCommand,3,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_LIST,RPOPLPUSH_Keyspecs,2,NULL,2),.args=RPOPLPUSH_Args},
+{MAKE_CMD("rpush","Appends one or more elements to a list. Creates the key if it doesn't exist.","O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.","1.0.0",CMD_DOC_NONE,NULL,NULL,"list",COMMAND_GROUP_LIST,RPUSH_History,1,RPUSH_Tips,0,rpushCommand,-3,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_LIST,RPUSH_Keyspecs,1,NULL,2),.args=RPUSH_Args},
+{MAKE_CMD("rpushx","Appends an element to a list only when the list exists.","O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.","2.2.0",CMD_DOC_NONE,NULL,NULL,"list",COMMAND_GROUP_LIST,RPUSHX_History,1,RPUSHX_Tips,0,rpushxCommand,-3,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_LIST,RPUSHX_Keyspecs,1,NULL,2),.args=RPUSHX_Args},
+/* pubsub */
+{MAKE_CMD("psubscribe","Listens for messages published to channels that match one or more patterns.","O(N) where N is the number of patterns the client is already subscribed to.","2.0.0",CMD_DOC_NONE,NULL,NULL,"pubsub",COMMAND_GROUP_PUBSUB,PSUBSCRIBE_History,0,PSUBSCRIBE_Tips,0,psubscribeCommand,-2,CMD_PUBSUB|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,PSUBSCRIBE_Keyspecs,0,NULL,1),.args=PSUBSCRIBE_Args},
+{MAKE_CMD("publish","Posts a message to a channel.","O(N+M) where N is the number of clients subscribed to the receiving channel and M is the total number of subscribed patterns (by any client).","2.0.0",CMD_DOC_NONE,NULL,NULL,"pubsub",COMMAND_GROUP_PUBSUB,PUBLISH_History,0,PUBLISH_Tips,0,publishCommand,3,CMD_PUBSUB|CMD_LOADING|CMD_STALE|CMD_FAST|CMD_MAY_REPLICATE|CMD_SENTINEL,0,PUBLISH_Keyspecs,0,NULL,2),.args=PUBLISH_Args},
+{MAKE_CMD("pubsub","A container for Pub/Sub commands.","Depends on subcommand.","2.8.0",CMD_DOC_NONE,NULL,NULL,"pubsub",COMMAND_GROUP_PUBSUB,PUBSUB_History,0,PUBSUB_Tips,0,NULL,-2,0,0,PUBSUB_Keyspecs,0,NULL,0),.subcommands=PUBSUB_Subcommands},
+{MAKE_CMD("punsubscribe","Stops listening to messages published to channels that match one or more patterns.","O(N+M) where N is the number of patterns the client is already subscribed and M is the number of total patterns subscribed in the system (by any client).","2.0.0",CMD_DOC_NONE,NULL,NULL,"pubsub",COMMAND_GROUP_PUBSUB,PUNSUBSCRIBE_History,0,PUNSUBSCRIBE_Tips,0,punsubscribeCommand,-1,CMD_PUBSUB|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,PUNSUBSCRIBE_Keyspecs,0,NULL,1),.args=PUNSUBSCRIBE_Args},
+{MAKE_CMD("spublish","Post a message to a shard channel","O(N) where N is the number of clients subscribed to the receiving shard channel.","7.0.0",CMD_DOC_NONE,NULL,NULL,"pubsub",COMMAND_GROUP_PUBSUB,SPUBLISH_History,0,SPUBLISH_Tips,0,spublishCommand,3,CMD_PUBSUB|CMD_LOADING|CMD_STALE|CMD_FAST|CMD_MAY_REPLICATE,0,SPUBLISH_Keyspecs,1,NULL,2),.args=SPUBLISH_Args},
+{MAKE_CMD("ssubscribe","Listens for messages published to shard channels.","O(N) where N is the number of shard channels to subscribe to.","7.0.0",CMD_DOC_NONE,NULL,NULL,"pubsub",COMMAND_GROUP_PUBSUB,SSUBSCRIBE_History,0,SSUBSCRIBE_Tips,0,ssubscribeCommand,-2,CMD_PUBSUB|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,0,SSUBSCRIBE_Keyspecs,1,NULL,1),.args=SSUBSCRIBE_Args},
+{MAKE_CMD("subscribe","Listens for messages published to channels.","O(N) where N is the number of channels to subscribe to.","2.0.0",CMD_DOC_NONE,NULL,NULL,"pubsub",COMMAND_GROUP_PUBSUB,SUBSCRIBE_History,0,SUBSCRIBE_Tips,0,subscribeCommand,-2,CMD_PUBSUB|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,SUBSCRIBE_Keyspecs,0,NULL,1),.args=SUBSCRIBE_Args},
+{MAKE_CMD("sunsubscribe","Stops listening to messages posted to shard channels.","O(N) where N is the number of clients already subscribed to a shard channel.","7.0.0",CMD_DOC_NONE,NULL,NULL,"pubsub",COMMAND_GROUP_PUBSUB,SUNSUBSCRIBE_History,0,SUNSUBSCRIBE_Tips,0,sunsubscribeCommand,-1,CMD_PUBSUB|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,0,SUNSUBSCRIBE_Keyspecs,1,NULL,1),.args=SUNSUBSCRIBE_Args},
+{MAKE_CMD("unsubscribe","Stops listening to messages posted to channels.","O(N) where N is the number of clients already subscribed to a channel.","2.0.0",CMD_DOC_NONE,NULL,NULL,"pubsub",COMMAND_GROUP_PUBSUB,UNSUBSCRIBE_History,0,UNSUBSCRIBE_Tips,0,unsubscribeCommand,-1,CMD_PUBSUB|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SENTINEL,0,UNSUBSCRIBE_Keyspecs,0,NULL,1),.args=UNSUBSCRIBE_Args},
+/* scripting */
+{MAKE_CMD("eval","Executes a server-side Lua script.","Depends on the script that is executed.","2.6.0",CMD_DOC_NONE,NULL,NULL,"scripting",COMMAND_GROUP_SCRIPTING,EVAL_History,0,EVAL_Tips,0,evalCommand,-3,CMD_NOSCRIPT|CMD_SKIP_MONITOR|CMD_MAY_REPLICATE|CMD_NO_MANDATORY_KEYS|CMD_STALE,ACL_CATEGORY_SCRIPTING,EVAL_Keyspecs,1,evalGetKeys,4),.args=EVAL_Args},
+{MAKE_CMD("evalsha","Executes a server-side Lua script by SHA1 digest.","Depends on the script that is executed.","2.6.0",CMD_DOC_NONE,NULL,NULL,"scripting",COMMAND_GROUP_SCRIPTING,EVALSHA_History,0,EVALSHA_Tips,0,evalShaCommand,-3,CMD_NOSCRIPT|CMD_SKIP_MONITOR|CMD_MAY_REPLICATE|CMD_NO_MANDATORY_KEYS|CMD_STALE,ACL_CATEGORY_SCRIPTING,EVALSHA_Keyspecs,1,evalGetKeys,4),.args=EVALSHA_Args},
+{MAKE_CMD("evalsha_ro","Executes a read-only server-side Lua script by SHA1 digest.","Depends on the script that is executed.","7.0.0",CMD_DOC_NONE,NULL,NULL,"scripting",COMMAND_GROUP_SCRIPTING,EVALSHA_RO_History,0,EVALSHA_RO_Tips,0,evalShaRoCommand,-3,CMD_NOSCRIPT|CMD_SKIP_MONITOR|CMD_NO_MANDATORY_KEYS|CMD_STALE|CMD_READONLY,ACL_CATEGORY_SCRIPTING,EVALSHA_RO_Keyspecs,1,evalGetKeys,4),.args=EVALSHA_RO_Args},
+{MAKE_CMD("eval_ro","Executes a read-only server-side Lua script.","Depends on the script that is executed.","7.0.0",CMD_DOC_NONE,NULL,NULL,"scripting",COMMAND_GROUP_SCRIPTING,EVAL_RO_History,0,EVAL_RO_Tips,0,evalRoCommand,-3,CMD_NOSCRIPT|CMD_SKIP_MONITOR|CMD_NO_MANDATORY_KEYS|CMD_STALE|CMD_READONLY,ACL_CATEGORY_SCRIPTING,EVAL_RO_Keyspecs,1,evalGetKeys,4),.args=EVAL_RO_Args},
+{MAKE_CMD("fcall","Invokes a function.","Depends on the function that is executed.","7.0.0",CMD_DOC_NONE,NULL,NULL,"scripting",COMMAND_GROUP_SCRIPTING,FCALL_History,0,FCALL_Tips,0,fcallCommand,-3,CMD_NOSCRIPT|CMD_SKIP_MONITOR|CMD_MAY_REPLICATE|CMD_NO_MANDATORY_KEYS|CMD_STALE,ACL_CATEGORY_SCRIPTING,FCALL_Keyspecs,1,functionGetKeys,4),.args=FCALL_Args},
+{MAKE_CMD("fcall_ro","Invokes a read-only function.","Depends on the function that is executed.","7.0.0",CMD_DOC_NONE,NULL,NULL,"scripting",COMMAND_GROUP_SCRIPTING,FCALL_RO_History,0,FCALL_RO_Tips,0,fcallroCommand,-3,CMD_NOSCRIPT|CMD_SKIP_MONITOR|CMD_NO_MANDATORY_KEYS|CMD_STALE|CMD_READONLY,ACL_CATEGORY_SCRIPTING,FCALL_RO_Keyspecs,1,functionGetKeys,4),.args=FCALL_RO_Args},
+{MAKE_CMD("function","A container for function commands.","Depends on subcommand.","7.0.0",CMD_DOC_NONE,NULL,NULL,"scripting",COMMAND_GROUP_SCRIPTING,FUNCTION_History,0,FUNCTION_Tips,0,NULL,-2,0,0,FUNCTION_Keyspecs,0,NULL,0),.subcommands=FUNCTION_Subcommands},
+{MAKE_CMD("script","A container for Lua scripts management commands.","Depends on subcommand.","2.6.0",CMD_DOC_NONE,NULL,NULL,"scripting",COMMAND_GROUP_SCRIPTING,SCRIPT_History,0,SCRIPT_Tips,0,NULL,-2,0,0,SCRIPT_Keyspecs,0,NULL,0),.subcommands=SCRIPT_Subcommands},
+/* sentinel */
+{MAKE_CMD("sentinel","A container for Redis Sentinel commands.","Depends on subcommand.","2.8.4",CMD_DOC_NONE,NULL,NULL,"sentinel",COMMAND_GROUP_SENTINEL,SENTINEL_History,0,SENTINEL_Tips,0,NULL,-2,CMD_ADMIN|CMD_SENTINEL|CMD_ONLY_SENTINEL,0,SENTINEL_Keyspecs,0,NULL,0),.subcommands=SENTINEL_Subcommands},
+/* server */
+{MAKE_CMD("acl","A container for Access List Control commands.","Depends on subcommand.","6.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,ACL_History,0,ACL_Tips,0,NULL,-2,CMD_SENTINEL,0,ACL_Keyspecs,0,NULL,0),.subcommands=ACL_Subcommands},
+{MAKE_CMD("bgrewriteaof","Asynchronously rewrites the append-only file to disk.","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,BGREWRITEAOF_History,0,BGREWRITEAOF_Tips,0,bgrewriteaofCommand,1,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_NOSCRIPT,0,BGREWRITEAOF_Keyspecs,0,NULL,0)},
+{MAKE_CMD("bgsave","Asynchronously saves the database(s) to disk.","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,BGSAVE_History,1,BGSAVE_Tips,0,bgsaveCommand,-1,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_NOSCRIPT,0,BGSAVE_Keyspecs,0,NULL,1),.args=BGSAVE_Args},
+{MAKE_CMD("command","Returns detailed information about all commands.","O(N) where N is the total number of Redis commands","2.8.13",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,COMMAND_History,0,COMMAND_Tips,1,commandCommand,-1,CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_CONNECTION,COMMAND_Keyspecs,0,NULL,0),.subcommands=COMMAND_Subcommands},
+{MAKE_CMD("config","A container for server configuration commands.","Depends on subcommand.","2.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,CONFIG_History,0,CONFIG_Tips,0,NULL,-2,0,0,CONFIG_Keyspecs,0,NULL,0),.subcommands=CONFIG_Subcommands},
+{MAKE_CMD("dbsize","Returns the number of keys in the database.","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,DBSIZE_History,0,DBSIZE_Tips,2,dbsizeCommand,1,CMD_READONLY|CMD_FAST,ACL_CATEGORY_KEYSPACE,DBSIZE_Keyspecs,0,NULL,0)},
+{MAKE_CMD("debug","A container for debugging commands.","Depends on subcommand.","1.0.0",CMD_DOC_SYSCMD,NULL,NULL,"server",COMMAND_GROUP_SERVER,DEBUG_History,0,DEBUG_Tips,0,debugCommand,-2,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_PROTECTED,0,DEBUG_Keyspecs,0,NULL,0)},
+{MAKE_CMD("failover","Starts a coordinated failover from a server to one of its replicas.","O(1)","6.2.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,FAILOVER_History,0,FAILOVER_Tips,0,failoverCommand,-1,CMD_ADMIN|CMD_NOSCRIPT|CMD_STALE,0,FAILOVER_Keyspecs,0,NULL,3),.args=FAILOVER_Args},
+{MAKE_CMD("flushall","Removes all keys from all databases.","O(N) where N is the total number of keys in all databases","1.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,FLUSHALL_History,2,FLUSHALL_Tips,2,flushallCommand,-1,CMD_WRITE,ACL_CATEGORY_KEYSPACE|ACL_CATEGORY_DANGEROUS,FLUSHALL_Keyspecs,0,NULL,1),.args=FLUSHALL_Args},
+{MAKE_CMD("flushdb","Remove all keys from the current database.","O(N) where N is the number of keys in the selected database","1.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,FLUSHDB_History,2,FLUSHDB_Tips,2,flushdbCommand,-1,CMD_WRITE,ACL_CATEGORY_KEYSPACE|ACL_CATEGORY_DANGEROUS,FLUSHDB_Keyspecs,0,NULL,1),.args=FLUSHDB_Args},
+{MAKE_CMD("info","Returns information and statistics about the server.","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,INFO_History,1,INFO_Tips,3,infoCommand,-1,CMD_LOADING|CMD_STALE|CMD_SENTINEL,ACL_CATEGORY_DANGEROUS,INFO_Keyspecs,0,NULL,1),.args=INFO_Args},
+{MAKE_CMD("lastsave","Returns the Unix timestamp of the last successful save to disk.","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,LASTSAVE_History,0,LASTSAVE_Tips,1,lastsaveCommand,1,CMD_LOADING|CMD_STALE|CMD_FAST,ACL_CATEGORY_ADMIN|ACL_CATEGORY_DANGEROUS,LASTSAVE_Keyspecs,0,NULL,0)},
+{MAKE_CMD("latency","A container for latency diagnostics commands.","Depends on subcommand.","2.8.13",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,LATENCY_History,0,LATENCY_Tips,0,NULL,-2,0,0,LATENCY_Keyspecs,0,NULL,0),.subcommands=LATENCY_Subcommands},
+{MAKE_CMD("lolwut","Displays computer art and the Redis version",NULL,"5.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,LOLWUT_History,0,LOLWUT_Tips,0,lolwutCommand,-1,CMD_READONLY|CMD_FAST,0,LOLWUT_Keyspecs,0,NULL,1),.args=LOLWUT_Args},
+{MAKE_CMD("memory","A container for memory diagnostics commands.","Depends on subcommand.","4.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,MEMORY_History,0,MEMORY_Tips,0,NULL,-2,0,0,MEMORY_Keyspecs,0,NULL,0),.subcommands=MEMORY_Subcommands},
+{MAKE_CMD("module","A container for module commands.","Depends on subcommand.","4.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,MODULE_History,0,MODULE_Tips,0,NULL,-2,0,0,MODULE_Keyspecs,0,NULL,0),.subcommands=MODULE_Subcommands},
+{MAKE_CMD("monitor","Listens for all requests received by the server in real-time.",NULL,"1.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,MONITOR_History,0,MONITOR_Tips,0,monitorCommand,1,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE,0,MONITOR_Keyspecs,0,NULL,0)},
+{MAKE_CMD("psync","An internal command used in replication.",NULL,"2.8.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,PSYNC_History,0,PSYNC_Tips,0,syncCommand,-3,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_NO_MULTI|CMD_NOSCRIPT,0,PSYNC_Keyspecs,0,NULL,2),.args=PSYNC_Args},
+{MAKE_CMD("replconf","An internal command for configuring the replication stream.","O(1)","3.0.0",CMD_DOC_SYSCMD,NULL,NULL,"server",COMMAND_GROUP_SERVER,REPLCONF_History,0,REPLCONF_Tips,0,replconfCommand,-1,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_ALLOW_BUSY,0,REPLCONF_Keyspecs,0,NULL,0)},
+{MAKE_CMD("replicaof","Configures a server as replica of another, or promotes it to a master.","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,REPLICAOF_History,0,REPLICAOF_Tips,0,replicaofCommand,3,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_NOSCRIPT|CMD_STALE,0,REPLICAOF_Keyspecs,0,NULL,2),.args=REPLICAOF_Args},
+{MAKE_CMD("restore-asking","An internal command for migrating keys in a cluster.","O(1) to create the new key and additional O(N*M) to reconstruct the serialized value, where N is the number of Redis objects composing the value and M their average size. For small string values the time complexity is thus O(1)+O(1*M) where M is small, so simply O(1). However for sorted set values the complexity is O(N*M*log(N)) because inserting values into sorted sets is O(log(N)).","3.0.0",CMD_DOC_SYSCMD,NULL,NULL,"server",COMMAND_GROUP_SERVER,RESTORE_ASKING_History,3,RESTORE_ASKING_Tips,0,restoreCommand,-4,CMD_WRITE|CMD_DENYOOM|CMD_ASKING,ACL_CATEGORY_KEYSPACE|ACL_CATEGORY_DANGEROUS,RESTORE_ASKING_Keyspecs,1,NULL,7),.args=RESTORE_ASKING_Args},
+{MAKE_CMD("role","Returns the replication role.","O(1)","2.8.12",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,ROLE_History,0,ROLE_Tips,0,roleCommand,1,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_FAST|CMD_SENTINEL,ACL_CATEGORY_ADMIN|ACL_CATEGORY_DANGEROUS,ROLE_Keyspecs,0,NULL,0)},
+{MAKE_CMD("save","Synchronously saves the database(s) to disk.","O(N) where N is the total number of keys in all databases","1.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,SAVE_History,0,SAVE_Tips,0,saveCommand,1,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_NOSCRIPT|CMD_NO_MULTI,0,SAVE_Keyspecs,0,NULL,0)},
+{MAKE_CMD("shutdown","Synchronously saves the database(s) to disk and shuts down the Redis server.","O(N) when saving, where N is the total number of keys in all databases when saving data, otherwise O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,SHUTDOWN_History,1,SHUTDOWN_Tips,0,shutdownCommand,-1,CMD_ADMIN|CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_NO_MULTI|CMD_SENTINEL|CMD_ALLOW_BUSY,0,SHUTDOWN_Keyspecs,0,NULL,4),.args=SHUTDOWN_Args},
+{MAKE_CMD("slaveof","Sets a Redis server as a replica of another, or promotes it to being a master.","O(1)","1.0.0",CMD_DOC_DEPRECATED,"`REPLICAOF`","5.0.0","server",COMMAND_GROUP_SERVER,SLAVEOF_History,0,SLAVEOF_Tips,0,replicaofCommand,3,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_NOSCRIPT|CMD_STALE,0,SLAVEOF_Keyspecs,0,NULL,2),.args=SLAVEOF_Args},
+{MAKE_CMD("slowlog","A container for slow log commands.","Depends on subcommand.","2.2.12",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,SLOWLOG_History,0,SLOWLOG_Tips,0,NULL,-2,0,0,SLOWLOG_Keyspecs,0,NULL,0),.subcommands=SLOWLOG_Subcommands},
+{MAKE_CMD("swapdb","Swaps two Redis databases.","O(N) where N is the count of clients watching or blocking on keys from both databases.","4.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,SWAPDB_History,0,SWAPDB_Tips,0,swapdbCommand,3,CMD_WRITE|CMD_FAST,ACL_CATEGORY_KEYSPACE|ACL_CATEGORY_DANGEROUS,SWAPDB_Keyspecs,0,NULL,2),.args=SWAPDB_Args},
+{MAKE_CMD("sync","An internal command used in replication.",NULL,"1.0.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,SYNC_History,0,SYNC_Tips,0,syncCommand,1,CMD_NO_ASYNC_LOADING|CMD_ADMIN|CMD_NO_MULTI|CMD_NOSCRIPT,0,SYNC_Keyspecs,0,NULL,0)},
+{MAKE_CMD("time","Returns the server time.","O(1)","2.6.0",CMD_DOC_NONE,NULL,NULL,"server",COMMAND_GROUP_SERVER,TIME_History,0,TIME_Tips,1,timeCommand,1,CMD_LOADING|CMD_STALE|CMD_FAST,0,TIME_Keyspecs,0,NULL,0)},
+/* set */
+{MAKE_CMD("sadd","Adds one or more members to a set. Creates the key if it doesn't exist.","O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.","1.0.0",CMD_DOC_NONE,NULL,NULL,"set",COMMAND_GROUP_SET,SADD_History,1,SADD_Tips,0,saddCommand,-3,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_SET,SADD_Keyspecs,1,NULL,2),.args=SADD_Args},
+{MAKE_CMD("scard","Returns the number of members in a set.","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,"set",COMMAND_GROUP_SET,SCARD_History,0,SCARD_Tips,0,scardCommand,2,CMD_READONLY|CMD_FAST,ACL_CATEGORY_SET,SCARD_Keyspecs,1,NULL,1),.args=SCARD_Args},
+{MAKE_CMD("sdiff","Returns the difference of multiple sets.","O(N) where N is the total number of elements in all given sets.","1.0.0",CMD_DOC_NONE,NULL,NULL,"set",COMMAND_GROUP_SET,SDIFF_History,0,SDIFF_Tips,1,sdiffCommand,-2,CMD_READONLY,ACL_CATEGORY_SET,SDIFF_Keyspecs,1,NULL,1),.args=SDIFF_Args},
+{MAKE_CMD("sdiffstore","Stores the difference of multiple sets in a key.","O(N) where N is the total number of elements in all given sets.","1.0.0",CMD_DOC_NONE,NULL,NULL,"set",COMMAND_GROUP_SET,SDIFFSTORE_History,0,SDIFFSTORE_Tips,0,sdiffstoreCommand,-3,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_SET,SDIFFSTORE_Keyspecs,2,NULL,2),.args=SDIFFSTORE_Args},
+{MAKE_CMD("sinter","Returns the intersect of multiple sets.","O(N*M) worst case where N is the cardinality of the smallest set and M is the number of sets.","1.0.0",CMD_DOC_NONE,NULL,NULL,"set",COMMAND_GROUP_SET,SINTER_History,0,SINTER_Tips,1,sinterCommand,-2,CMD_READONLY,ACL_CATEGORY_SET,SINTER_Keyspecs,1,NULL,1),.args=SINTER_Args},
+{MAKE_CMD("sintercard","Returns the number of members of the intersect of multiple sets.","O(N*M) worst case where N is the cardinality of the smallest set and M is the number of sets.","7.0.0",CMD_DOC_NONE,NULL,NULL,"set",COMMAND_GROUP_SET,SINTERCARD_History,0,SINTERCARD_Tips,0,sinterCardCommand,-3,CMD_READONLY,ACL_CATEGORY_SET,SINTERCARD_Keyspecs,1,sintercardGetKeys,3),.args=SINTERCARD_Args},
+{MAKE_CMD("sinterstore","Stores the intersect of multiple sets in a key.","O(N*M) worst case where N is the cardinality of the smallest set and M is the number of sets.","1.0.0",CMD_DOC_NONE,NULL,NULL,"set",COMMAND_GROUP_SET,SINTERSTORE_History,0,SINTERSTORE_Tips,0,sinterstoreCommand,-3,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_SET,SINTERSTORE_Keyspecs,2,NULL,2),.args=SINTERSTORE_Args},
+{MAKE_CMD("sismember","Determines whether a member belongs to a set.","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,"set",COMMAND_GROUP_SET,SISMEMBER_History,0,SISMEMBER_Tips,0,sismemberCommand,3,CMD_READONLY|CMD_FAST,ACL_CATEGORY_SET,SISMEMBER_Keyspecs,1,NULL,2),.args=SISMEMBER_Args},
+{MAKE_CMD("smembers","Returns all members of a set.","O(N) where N is the set cardinality.","1.0.0",CMD_DOC_NONE,NULL,NULL,"set",COMMAND_GROUP_SET,SMEMBERS_History,0,SMEMBERS_Tips,1,sinterCommand,2,CMD_READONLY,ACL_CATEGORY_SET,SMEMBERS_Keyspecs,1,NULL,1),.args=SMEMBERS_Args},
+{MAKE_CMD("smismember","Determines whether multiple members belong to a set.","O(N) where N is the number of elements being checked for membership","6.2.0",CMD_DOC_NONE,NULL,NULL,"set",COMMAND_GROUP_SET,SMISMEMBER_History,0,SMISMEMBER_Tips,0,smismemberCommand,-3,CMD_READONLY|CMD_FAST,ACL_CATEGORY_SET,SMISMEMBER_Keyspecs,1,NULL,2),.args=SMISMEMBER_Args},
+{MAKE_CMD("smove","Moves a member from one set to another.","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,"set",COMMAND_GROUP_SET,SMOVE_History,0,SMOVE_Tips,0,smoveCommand,4,CMD_WRITE|CMD_FAST,ACL_CATEGORY_SET,SMOVE_Keyspecs,2,NULL,3),.args=SMOVE_Args},
+{MAKE_CMD("spop","Returns one or more random members from a set after removing them. Deletes the set if the last member was popped.","Without the count argument O(1), otherwise O(N) where N is the value of the passed count.","1.0.0",CMD_DOC_NONE,NULL,NULL,"set",COMMAND_GROUP_SET,SPOP_History,1,SPOP_Tips,1,spopCommand,-2,CMD_WRITE|CMD_FAST,ACL_CATEGORY_SET,SPOP_Keyspecs,1,NULL,2),.args=SPOP_Args},
+{MAKE_CMD("srandmember","Get one or multiple random members from a set","Without the count argument O(1), otherwise O(N) where N is the absolute value of the passed count.","1.0.0",CMD_DOC_NONE,NULL,NULL,"set",COMMAND_GROUP_SET,SRANDMEMBER_History,1,SRANDMEMBER_Tips,1,srandmemberCommand,-2,CMD_READONLY,ACL_CATEGORY_SET,SRANDMEMBER_Keyspecs,1,NULL,2),.args=SRANDMEMBER_Args},
+{MAKE_CMD("srem","Removes one or more members from a set. Deletes the set if the last member was removed.","O(N) where N is the number of members to be removed.","1.0.0",CMD_DOC_NONE,NULL,NULL,"set",COMMAND_GROUP_SET,SREM_History,1,SREM_Tips,0,sremCommand,-3,CMD_WRITE|CMD_FAST,ACL_CATEGORY_SET,SREM_Keyspecs,1,NULL,2),.args=SREM_Args},
+{MAKE_CMD("sscan","Iterates over members of a set.","O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.","2.8.0",CMD_DOC_NONE,NULL,NULL,"set",COMMAND_GROUP_SET,SSCAN_History,0,SSCAN_Tips,1,sscanCommand,-3,CMD_READONLY,ACL_CATEGORY_SET,SSCAN_Keyspecs,1,NULL,4),.args=SSCAN_Args},
+{MAKE_CMD("sunion","Returns the union of multiple sets.","O(N) where N is the total number of elements in all given sets.","1.0.0",CMD_DOC_NONE,NULL,NULL,"set",COMMAND_GROUP_SET,SUNION_History,0,SUNION_Tips,1,sunionCommand,-2,CMD_READONLY,ACL_CATEGORY_SET,SUNION_Keyspecs,1,NULL,1),.args=SUNION_Args},
+{MAKE_CMD("sunionstore","Stores the union of multiple sets in a key.","O(N) where N is the total number of elements in all given sets.","1.0.0",CMD_DOC_NONE,NULL,NULL,"set",COMMAND_GROUP_SET,SUNIONSTORE_History,0,SUNIONSTORE_Tips,0,sunionstoreCommand,-3,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_SET,SUNIONSTORE_Keyspecs,2,NULL,2),.args=SUNIONSTORE_Args},
+/* sorted_set */
+{MAKE_CMD("bzmpop","Removes and returns a member by score from one or more sorted sets. Blocks until a member is available otherwise. Deletes the sorted set if the last element was popped.","O(K) + O(M*log(N)) where K is the number of provided keys, N being the number of elements in the sorted set, and M being the number of elements popped.","7.0.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,BZMPOP_History,0,BZMPOP_Tips,0,bzmpopCommand,-5,CMD_WRITE|CMD_BLOCKING,ACL_CATEGORY_SORTEDSET,BZMPOP_Keyspecs,1,blmpopGetKeys,5),.args=BZMPOP_Args},
+{MAKE_CMD("bzpopmax","Removes and returns the member with the highest score from one or more sorted sets. Blocks until a member available otherwise. Deletes the sorted set if the last element was popped.","O(log(N)) with N being the number of elements in the sorted set.","5.0.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,BZPOPMAX_History,1,BZPOPMAX_Tips,0,bzpopmaxCommand,-3,CMD_WRITE|CMD_FAST|CMD_BLOCKING,ACL_CATEGORY_SORTEDSET,BZPOPMAX_Keyspecs,1,NULL,2),.args=BZPOPMAX_Args},
+{MAKE_CMD("bzpopmin","Removes and returns the member with the lowest score from one or more sorted sets. Blocks until a member is available otherwise. Deletes the sorted set if the last element was popped.","O(log(N)) with N being the number of elements in the sorted set.","5.0.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,BZPOPMIN_History,1,BZPOPMIN_Tips,0,bzpopminCommand,-3,CMD_WRITE|CMD_FAST|CMD_BLOCKING,ACL_CATEGORY_SORTEDSET,BZPOPMIN_Keyspecs,1,NULL,2),.args=BZPOPMIN_Args},
+{MAKE_CMD("zadd","Adds one or more members to a sorted set, or updates their scores. Creates the key if it doesn't exist.","O(log(N)) for each item added, where N is the number of elements in the sorted set.","1.2.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZADD_History,3,ZADD_Tips,0,zaddCommand,-4,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_SORTEDSET,ZADD_Keyspecs,1,NULL,6),.args=ZADD_Args},
+{MAKE_CMD("zcard","Returns the number of members in a sorted set.","O(1)","1.2.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZCARD_History,0,ZCARD_Tips,0,zcardCommand,2,CMD_READONLY|CMD_FAST,ACL_CATEGORY_SORTEDSET,ZCARD_Keyspecs,1,NULL,1),.args=ZCARD_Args},
+{MAKE_CMD("zcount","Returns the count of members in a sorted set that have scores within a range.","O(log(N)) with N being the number of elements in the sorted set.","2.0.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZCOUNT_History,0,ZCOUNT_Tips,0,zcountCommand,4,CMD_READONLY|CMD_FAST,ACL_CATEGORY_SORTEDSET,ZCOUNT_Keyspecs,1,NULL,3),.args=ZCOUNT_Args},
+{MAKE_CMD("zdiff","Returns the difference between multiple sorted sets.","O(L + (N-K)log(N)) worst case where L is the total number of elements in all the sets, N is the size of the first set, and K is the size of the result set.","6.2.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZDIFF_History,0,ZDIFF_Tips,0,zdiffCommand,-3,CMD_READONLY,ACL_CATEGORY_SORTEDSET,ZDIFF_Keyspecs,1,zunionInterDiffGetKeys,3),.args=ZDIFF_Args},
+{MAKE_CMD("zdiffstore","Stores the difference of multiple sorted sets in a key.","O(L + (N-K)log(N)) worst case where L is the total number of elements in all the sets, N is the size of the first set, and K is the size of the result set.","6.2.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZDIFFSTORE_History,0,ZDIFFSTORE_Tips,0,zdiffstoreCommand,-4,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_SORTEDSET,ZDIFFSTORE_Keyspecs,2,zunionInterDiffStoreGetKeys,3),.args=ZDIFFSTORE_Args},
+{MAKE_CMD("zincrby","Increments the score of a member in a sorted set.","O(log(N)) where N is the number of elements in the sorted set.","1.2.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZINCRBY_History,0,ZINCRBY_Tips,0,zincrbyCommand,4,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_SORTEDSET,ZINCRBY_Keyspecs,1,NULL,3),.args=ZINCRBY_Args},
+{MAKE_CMD("zinter","Returns the intersect of multiple sorted sets.","O(N*K)+O(M*log(M)) worst case with N being the smallest input sorted set, K being the number of input sorted sets and M being the number of elements in the resulting sorted set.","6.2.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZINTER_History,0,ZINTER_Tips,0,zinterCommand,-3,CMD_READONLY,ACL_CATEGORY_SORTEDSET,ZINTER_Keyspecs,1,zunionInterDiffGetKeys,5),.args=ZINTER_Args},
+{MAKE_CMD("zintercard","Returns the number of members of the intersect of multiple sorted sets.","O(N*K) worst case with N being the smallest input sorted set, K being the number of input sorted sets.","7.0.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZINTERCARD_History,0,ZINTERCARD_Tips,0,zinterCardCommand,-3,CMD_READONLY,ACL_CATEGORY_SORTEDSET,ZINTERCARD_Keyspecs,1,zunionInterDiffGetKeys,3),.args=ZINTERCARD_Args},
+{MAKE_CMD("zinterstore","Stores the intersect of multiple sorted sets in a key.","O(N*K)+O(M*log(M)) worst case with N being the smallest input sorted set, K being the number of input sorted sets and M being the number of elements in the resulting sorted set.","2.0.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZINTERSTORE_History,0,ZINTERSTORE_Tips,0,zinterstoreCommand,-4,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_SORTEDSET,ZINTERSTORE_Keyspecs,2,zunionInterDiffStoreGetKeys,5),.args=ZINTERSTORE_Args},
+{MAKE_CMD("zlexcount","Returns the number of members in a sorted set within a lexicographical range.","O(log(N)) with N being the number of elements in the sorted set.","2.8.9",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZLEXCOUNT_History,0,ZLEXCOUNT_Tips,0,zlexcountCommand,4,CMD_READONLY|CMD_FAST,ACL_CATEGORY_SORTEDSET,ZLEXCOUNT_Keyspecs,1,NULL,3),.args=ZLEXCOUNT_Args},
+{MAKE_CMD("zmpop","Returns the highest- or lowest-scoring members from one or more sorted sets after removing them. Deletes the sorted set if the last member was popped.","O(K) + O(M*log(N)) where K is the number of provided keys, N being the number of elements in the sorted set, and M being the number of elements popped.","7.0.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZMPOP_History,0,ZMPOP_Tips,0,zmpopCommand,-4,CMD_WRITE,ACL_CATEGORY_SORTEDSET,ZMPOP_Keyspecs,1,zmpopGetKeys,4),.args=ZMPOP_Args},
+{MAKE_CMD("zmscore","Returns the score of one or more members in a sorted set.","O(N) where N is the number of members being requested.","6.2.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZMSCORE_History,0,ZMSCORE_Tips,0,zmscoreCommand,-3,CMD_READONLY|CMD_FAST,ACL_CATEGORY_SORTEDSET,ZMSCORE_Keyspecs,1,NULL,2),.args=ZMSCORE_Args},
+{MAKE_CMD("zpopmax","Returns the highest-scoring members from a sorted set after removing them. Deletes the sorted set if the last member was popped.","O(log(N)*M) with N being the number of elements in the sorted set, and M being the number of elements popped.","5.0.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZPOPMAX_History,0,ZPOPMAX_Tips,0,zpopmaxCommand,-2,CMD_WRITE|CMD_FAST,ACL_CATEGORY_SORTEDSET,ZPOPMAX_Keyspecs,1,NULL,2),.args=ZPOPMAX_Args},
+{MAKE_CMD("zpopmin","Returns the lowest-scoring members from a sorted set after removing them. Deletes the sorted set if the last member was popped.","O(log(N)*M) with N being the number of elements in the sorted set, and M being the number of elements popped.","5.0.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZPOPMIN_History,0,ZPOPMIN_Tips,0,zpopminCommand,-2,CMD_WRITE|CMD_FAST,ACL_CATEGORY_SORTEDSET,ZPOPMIN_Keyspecs,1,NULL,2),.args=ZPOPMIN_Args},
+{MAKE_CMD("zrandmember","Returns one or more random members from a sorted set.","O(N) where N is the number of members returned","6.2.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZRANDMEMBER_History,0,ZRANDMEMBER_Tips,1,zrandmemberCommand,-2,CMD_READONLY,ACL_CATEGORY_SORTEDSET,ZRANDMEMBER_Keyspecs,1,NULL,2),.args=ZRANDMEMBER_Args},
+{MAKE_CMD("zrange","Returns members in a sorted set within a range of indexes.","O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements returned.","1.2.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZRANGE_History,1,ZRANGE_Tips,0,zrangeCommand,-4,CMD_READONLY,ACL_CATEGORY_SORTEDSET,ZRANGE_Keyspecs,1,NULL,7),.args=ZRANGE_Args},
+{MAKE_CMD("zrangebylex","Returns members in a sorted set within a lexicographical range.","O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).","2.8.9",CMD_DOC_DEPRECATED,"`ZRANGE` with the `BYLEX` argument","6.2.0","sorted_set",COMMAND_GROUP_SORTED_SET,ZRANGEBYLEX_History,0,ZRANGEBYLEX_Tips,0,zrangebylexCommand,-4,CMD_READONLY,ACL_CATEGORY_SORTEDSET,ZRANGEBYLEX_Keyspecs,1,NULL,4),.args=ZRANGEBYLEX_Args},
+{MAKE_CMD("zrangebyscore","Returns members in a sorted set within a range of scores.","O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).","1.0.5",CMD_DOC_DEPRECATED,"`ZRANGE` with the `BYSCORE` argument","6.2.0","sorted_set",COMMAND_GROUP_SORTED_SET,ZRANGEBYSCORE_History,1,ZRANGEBYSCORE_Tips,0,zrangebyscoreCommand,-4,CMD_READONLY,ACL_CATEGORY_SORTEDSET,ZRANGEBYSCORE_Keyspecs,1,NULL,5),.args=ZRANGEBYSCORE_Args},
+{MAKE_CMD("zrangestore","Stores a range of members from sorted set in a key.","O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements stored into the destination key.","6.2.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZRANGESTORE_History,0,ZRANGESTORE_Tips,0,zrangestoreCommand,-5,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_SORTEDSET,ZRANGESTORE_Keyspecs,2,NULL,7),.args=ZRANGESTORE_Args},
+{MAKE_CMD("zrank","Returns the index of a member in a sorted set ordered by ascending scores.","O(log(N))","2.0.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZRANK_History,1,ZRANK_Tips,0,zrankCommand,-3,CMD_READONLY|CMD_FAST,ACL_CATEGORY_SORTEDSET,ZRANK_Keyspecs,1,NULL,3),.args=ZRANK_Args},
+{MAKE_CMD("zrem","Removes one or more members from a sorted set. Deletes the sorted set if all members were removed.","O(M*log(N)) with N being the number of elements in the sorted set and M the number of elements to be removed.","1.2.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZREM_History,1,ZREM_Tips,0,zremCommand,-3,CMD_WRITE|CMD_FAST,ACL_CATEGORY_SORTEDSET,ZREM_Keyspecs,1,NULL,2),.args=ZREM_Args},
+{MAKE_CMD("zremrangebylex","Removes members in a sorted set within a lexicographical range. Deletes the sorted set if all members were removed.","O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements removed by the operation.","2.8.9",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZREMRANGEBYLEX_History,0,ZREMRANGEBYLEX_Tips,0,zremrangebylexCommand,4,CMD_WRITE,ACL_CATEGORY_SORTEDSET,ZREMRANGEBYLEX_Keyspecs,1,NULL,3),.args=ZREMRANGEBYLEX_Args},
+{MAKE_CMD("zremrangebyrank","Removes members in a sorted set within a range of indexes. Deletes the sorted set if all members were removed.","O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements removed by the operation.","2.0.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZREMRANGEBYRANK_History,0,ZREMRANGEBYRANK_Tips,0,zremrangebyrankCommand,4,CMD_WRITE,ACL_CATEGORY_SORTEDSET,ZREMRANGEBYRANK_Keyspecs,1,NULL,3),.args=ZREMRANGEBYRANK_Args},
+{MAKE_CMD("zremrangebyscore","Removes members in a sorted set within a range of scores. Deletes the sorted set if all members were removed.","O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements removed by the operation.","1.2.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZREMRANGEBYSCORE_History,0,ZREMRANGEBYSCORE_Tips,0,zremrangebyscoreCommand,4,CMD_WRITE,ACL_CATEGORY_SORTEDSET,ZREMRANGEBYSCORE_Keyspecs,1,NULL,3),.args=ZREMRANGEBYSCORE_Args},
+{MAKE_CMD("zrevrange","Returns members in a sorted set within a range of indexes in reverse order.","O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements returned.","1.2.0",CMD_DOC_DEPRECATED,"`ZRANGE` with the `REV` argument","6.2.0","sorted_set",COMMAND_GROUP_SORTED_SET,ZREVRANGE_History,0,ZREVRANGE_Tips,0,zrevrangeCommand,-4,CMD_READONLY,ACL_CATEGORY_SORTEDSET,ZREVRANGE_Keyspecs,1,NULL,4),.args=ZREVRANGE_Args},
+{MAKE_CMD("zrevrangebylex","Returns members in a sorted set within a lexicographical range in reverse order.","O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).","2.8.9",CMD_DOC_DEPRECATED,"`ZRANGE` with the `REV` and `BYLEX` arguments","6.2.0","sorted_set",COMMAND_GROUP_SORTED_SET,ZREVRANGEBYLEX_History,0,ZREVRANGEBYLEX_Tips,0,zrevrangebylexCommand,-4,CMD_READONLY,ACL_CATEGORY_SORTEDSET,ZREVRANGEBYLEX_Keyspecs,1,NULL,4),.args=ZREVRANGEBYLEX_Args},
+{MAKE_CMD("zrevrangebyscore","Returns members in a sorted set within a range of scores in reverse order.","O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).","2.2.0",CMD_DOC_DEPRECATED,"`ZRANGE` with the `REV` and `BYSCORE` arguments","6.2.0","sorted_set",COMMAND_GROUP_SORTED_SET,ZREVRANGEBYSCORE_History,1,ZREVRANGEBYSCORE_Tips,0,zrevrangebyscoreCommand,-4,CMD_READONLY,ACL_CATEGORY_SORTEDSET,ZREVRANGEBYSCORE_Keyspecs,1,NULL,5),.args=ZREVRANGEBYSCORE_Args},
+{MAKE_CMD("zrevrank","Returns the index of a member in a sorted set ordered by descending scores.","O(log(N))","2.0.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZREVRANK_History,1,ZREVRANK_Tips,0,zrevrankCommand,-3,CMD_READONLY|CMD_FAST,ACL_CATEGORY_SORTEDSET,ZREVRANK_Keyspecs,1,NULL,3),.args=ZREVRANK_Args},
+{MAKE_CMD("zscan","Iterates over members and scores of a sorted set.","O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.","2.8.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZSCAN_History,0,ZSCAN_Tips,1,zscanCommand,-3,CMD_READONLY,ACL_CATEGORY_SORTEDSET,ZSCAN_Keyspecs,1,NULL,4),.args=ZSCAN_Args},
+{MAKE_CMD("zscore","Returns the score of a member in a sorted set.","O(1)","1.2.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZSCORE_History,0,ZSCORE_Tips,0,zscoreCommand,3,CMD_READONLY|CMD_FAST,ACL_CATEGORY_SORTEDSET,ZSCORE_Keyspecs,1,NULL,2),.args=ZSCORE_Args},
+{MAKE_CMD("zunion","Returns the union of multiple sorted sets.","O(N)+O(M*log(M)) with N being the sum of the sizes of the input sorted sets, and M being the number of elements in the resulting sorted set.","6.2.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZUNION_History,0,ZUNION_Tips,0,zunionCommand,-3,CMD_READONLY,ACL_CATEGORY_SORTEDSET,ZUNION_Keyspecs,1,zunionInterDiffGetKeys,5),.args=ZUNION_Args},
+{MAKE_CMD("zunionstore","Stores the union of multiple sorted sets in a key.","O(N)+O(M log(M)) with N being the sum of the sizes of the input sorted sets, and M being the number of elements in the resulting sorted set.","2.0.0",CMD_DOC_NONE,NULL,NULL,"sorted_set",COMMAND_GROUP_SORTED_SET,ZUNIONSTORE_History,0,ZUNIONSTORE_Tips,0,zunionstoreCommand,-4,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_SORTEDSET,ZUNIONSTORE_Keyspecs,2,zunionInterDiffStoreGetKeys,5),.args=ZUNIONSTORE_Args},
+/* stream */
+{MAKE_CMD("xack","Returns the number of messages that were successfully acknowledged by the consumer group member of a stream.","O(1) for each message ID processed.","5.0.0",CMD_DOC_NONE,NULL,NULL,"stream",COMMAND_GROUP_STREAM,XACK_History,0,XACK_Tips,0,xackCommand,-4,CMD_WRITE|CMD_FAST,ACL_CATEGORY_STREAM,XACK_Keyspecs,1,NULL,3),.args=XACK_Args},
+{MAKE_CMD("xadd","Appends a new message to a stream. Creates the key if it doesn't exist.","O(1) when adding a new entry, O(N) when trimming where N being the number of entries evicted.","5.0.0",CMD_DOC_NONE,NULL,NULL,"stream",COMMAND_GROUP_STREAM,XADD_History,2,XADD_Tips,1,xaddCommand,-5,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_STREAM,XADD_Keyspecs,1,NULL,5),.args=XADD_Args},
+{MAKE_CMD("xautoclaim","Changes, or acquires, ownership of messages in a consumer group, as if the messages were delivered to as consumer group member.","O(1) if COUNT is small.","6.2.0",CMD_DOC_NONE,NULL,NULL,"stream",COMMAND_GROUP_STREAM,XAUTOCLAIM_History,1,XAUTOCLAIM_Tips,1,xautoclaimCommand,-6,CMD_WRITE|CMD_FAST,ACL_CATEGORY_STREAM,XAUTOCLAIM_Keyspecs,1,NULL,7),.args=XAUTOCLAIM_Args},
+{MAKE_CMD("xclaim","Changes, or acquires, ownership of a message in a consumer group, as if the message was delivered a consumer group member.","O(log N) with N being the number of messages in the PEL of the consumer group.","5.0.0",CMD_DOC_NONE,NULL,NULL,"stream",COMMAND_GROUP_STREAM,XCLAIM_History,0,XCLAIM_Tips,1,xclaimCommand,-6,CMD_WRITE|CMD_FAST,ACL_CATEGORY_STREAM,XCLAIM_Keyspecs,1,NULL,11),.args=XCLAIM_Args},
+{MAKE_CMD("xdel","Returns the number of messages after removing them from a stream.","O(1) for each single item to delete in the stream, regardless of the stream size.","5.0.0",CMD_DOC_NONE,NULL,NULL,"stream",COMMAND_GROUP_STREAM,XDEL_History,0,XDEL_Tips,0,xdelCommand,-3,CMD_WRITE|CMD_FAST,ACL_CATEGORY_STREAM,XDEL_Keyspecs,1,NULL,2),.args=XDEL_Args},
+{MAKE_CMD("xgroup","A container for consumer groups commands.","Depends on subcommand.","5.0.0",CMD_DOC_NONE,NULL,NULL,"stream",COMMAND_GROUP_STREAM,XGROUP_History,0,XGROUP_Tips,0,NULL,-2,0,0,XGROUP_Keyspecs,0,NULL,0),.subcommands=XGROUP_Subcommands},
+{MAKE_CMD("xinfo","A container for stream introspection commands.","Depends on subcommand.","5.0.0",CMD_DOC_NONE,NULL,NULL,"stream",COMMAND_GROUP_STREAM,XINFO_History,0,XINFO_Tips,0,NULL,-2,0,0,XINFO_Keyspecs,0,NULL,0),.subcommands=XINFO_Subcommands},
+{MAKE_CMD("xlen","Return the number of messages in a stream.","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,"stream",COMMAND_GROUP_STREAM,XLEN_History,0,XLEN_Tips,0,xlenCommand,2,CMD_READONLY|CMD_FAST,ACL_CATEGORY_STREAM,XLEN_Keyspecs,1,NULL,1),.args=XLEN_Args},
+{MAKE_CMD("xpending","Returns the information and entries from a stream consumer group's pending entries list.","O(N) with N being the number of elements returned, so asking for a small fixed number of entries per call is O(1). O(M), where M is the total number of entries scanned when used with the IDLE filter. When the command returns just the summary and the list of consumers is small, it runs in O(1) time; otherwise, an additional O(N) time for iterating every consumer.","5.0.0",CMD_DOC_NONE,NULL,NULL,"stream",COMMAND_GROUP_STREAM,XPENDING_History,1,XPENDING_Tips,1,xpendingCommand,-3,CMD_READONLY,ACL_CATEGORY_STREAM,XPENDING_Keyspecs,1,NULL,3),.args=XPENDING_Args},
+{MAKE_CMD("xrange","Returns the messages from a stream within a range of IDs.","O(N) with N being the number of elements being returned. If N is constant (e.g. always asking for the first 10 elements with COUNT), you can consider it O(1).","5.0.0",CMD_DOC_NONE,NULL,NULL,"stream",COMMAND_GROUP_STREAM,XRANGE_History,1,XRANGE_Tips,0,xrangeCommand,-4,CMD_READONLY,ACL_CATEGORY_STREAM,XRANGE_Keyspecs,1,NULL,4),.args=XRANGE_Args},
+{MAKE_CMD("xread","Returns messages from multiple streams with IDs greater than the ones requested. Blocks until a message is available otherwise.",NULL,"5.0.0",CMD_DOC_NONE,NULL,NULL,"stream",COMMAND_GROUP_STREAM,XREAD_History,0,XREAD_Tips,0,xreadCommand,-4,CMD_BLOCKING|CMD_READONLY|CMD_BLOCKING,ACL_CATEGORY_STREAM,XREAD_Keyspecs,1,xreadGetKeys,3),.args=XREAD_Args},
+{MAKE_CMD("xreadgroup","Returns new or historical messages from a stream for a consumer in a group. Blocks until a message is available otherwise.","For each stream mentioned: O(M) with M being the number of elements returned. If M is constant (e.g. always asking for the first 10 elements with COUNT), you can consider it O(1). On the other side when XREADGROUP blocks, XADD will pay the O(N) time in order to serve the N clients blocked on the stream getting new data.","5.0.0",CMD_DOC_NONE,NULL,NULL,"stream",COMMAND_GROUP_STREAM,XREADGROUP_History,0,XREADGROUP_Tips,0,xreadCommand,-7,CMD_BLOCKING|CMD_WRITE,ACL_CATEGORY_STREAM,XREADGROUP_Keyspecs,1,xreadGetKeys,5),.args=XREADGROUP_Args},
+{MAKE_CMD("xrevrange","Returns the messages from a stream within a range of IDs in reverse order.","O(N) with N being the number of elements returned. If N is constant (e.g. always asking for the first 10 elements with COUNT), you can consider it O(1).","5.0.0",CMD_DOC_NONE,NULL,NULL,"stream",COMMAND_GROUP_STREAM,XREVRANGE_History,1,XREVRANGE_Tips,0,xrevrangeCommand,-4,CMD_READONLY,ACL_CATEGORY_STREAM,XREVRANGE_Keyspecs,1,NULL,4),.args=XREVRANGE_Args},
+{MAKE_CMD("xsetid","An internal command for replicating stream values.","O(1)","5.0.0",CMD_DOC_NONE,NULL,NULL,"stream",COMMAND_GROUP_STREAM,XSETID_History,1,XSETID_Tips,0,xsetidCommand,-3,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_STREAM,XSETID_Keyspecs,1,NULL,4),.args=XSETID_Args},
+{MAKE_CMD("xtrim","Deletes messages from the beginning of a stream.","O(N), with N being the number of evicted entries. Constant times are very small however, since entries are organized in macro nodes containing multiple entries that can be released with a single deallocation.","5.0.0",CMD_DOC_NONE,NULL,NULL,"stream",COMMAND_GROUP_STREAM,XTRIM_History,1,XTRIM_Tips,1,xtrimCommand,-4,CMD_WRITE,ACL_CATEGORY_STREAM,XTRIM_Keyspecs,1,NULL,2),.args=XTRIM_Args},
+/* string */
+{MAKE_CMD("append","Appends a string to the value of a key. Creates the key if it doesn't exist.","O(1). The amortized time complexity is O(1) assuming the appended value is small and the already present value is of any size, since the dynamic string library used by Redis will double the free space available on every reallocation.","2.0.0",CMD_DOC_NONE,NULL,NULL,"string",COMMAND_GROUP_STRING,APPEND_History,0,APPEND_Tips,0,appendCommand,3,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_STRING,APPEND_Keyspecs,1,NULL,2),.args=APPEND_Args},
+{MAKE_CMD("decr","Decrements the integer value of a key by one. Uses 0 as initial value if the key doesn't exist.","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,"string",COMMAND_GROUP_STRING,DECR_History,0,DECR_Tips,0,decrCommand,2,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_STRING,DECR_Keyspecs,1,NULL,1),.args=DECR_Args},
+{MAKE_CMD("decrby","Decrements a number from the integer value of a key. Uses 0 as initial value if the key doesn't exist.","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,"string",COMMAND_GROUP_STRING,DECRBY_History,0,DECRBY_Tips,0,decrbyCommand,3,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_STRING,DECRBY_Keyspecs,1,NULL,2),.args=DECRBY_Args},
+{MAKE_CMD("get","Returns the string value of a key.","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,"string",COMMAND_GROUP_STRING,GET_History,0,GET_Tips,0,getCommand,2,CMD_READONLY|CMD_FAST,ACL_CATEGORY_STRING,GET_Keyspecs,1,NULL,1),.args=GET_Args},
+{MAKE_CMD("getdel","Returns the string value of a key after deleting the key.","O(1)","6.2.0",CMD_DOC_NONE,NULL,NULL,"string",COMMAND_GROUP_STRING,GETDEL_History,0,GETDEL_Tips,0,getdelCommand,2,CMD_WRITE|CMD_FAST,ACL_CATEGORY_STRING,GETDEL_Keyspecs,1,NULL,1),.args=GETDEL_Args},
+{MAKE_CMD("getex","Returns the string value of a key after setting its expiration time.","O(1)","6.2.0",CMD_DOC_NONE,NULL,NULL,"string",COMMAND_GROUP_STRING,GETEX_History,0,GETEX_Tips,0,getexCommand,-2,CMD_WRITE|CMD_FAST,ACL_CATEGORY_STRING,GETEX_Keyspecs,1,NULL,2),.args=GETEX_Args},
+{MAKE_CMD("getrange","Returns a substring of the string stored at a key.","O(N) where N is the length of the returned string. The complexity is ultimately determined by the returned length, but because creating a substring from an existing string is very cheap, it can be considered O(1) for small strings.","2.4.0",CMD_DOC_NONE,NULL,NULL,"string",COMMAND_GROUP_STRING,GETRANGE_History,0,GETRANGE_Tips,0,getrangeCommand,4,CMD_READONLY,ACL_CATEGORY_STRING,GETRANGE_Keyspecs,1,NULL,3),.args=GETRANGE_Args},
+{MAKE_CMD("getset","Returns the previous string value of a key after setting it to a new value.","O(1)","1.0.0",CMD_DOC_DEPRECATED,"`SET` with the `!GET` argument","6.2.0","string",COMMAND_GROUP_STRING,GETSET_History,0,GETSET_Tips,0,getsetCommand,3,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_STRING,GETSET_Keyspecs,1,NULL,2),.args=GETSET_Args},
+{MAKE_CMD("incr","Increments the integer value of a key by one. Uses 0 as initial value if the key doesn't exist.","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,"string",COMMAND_GROUP_STRING,INCR_History,0,INCR_Tips,0,incrCommand,2,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_STRING,INCR_Keyspecs,1,NULL,1),.args=INCR_Args},
+{MAKE_CMD("incrby","Increments the integer value of a key by a number. Uses 0 as initial value if the key doesn't exist.","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,"string",COMMAND_GROUP_STRING,INCRBY_History,0,INCRBY_Tips,0,incrbyCommand,3,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_STRING,INCRBY_Keyspecs,1,NULL,2),.args=INCRBY_Args},
+{MAKE_CMD("incrbyfloat","Increment the floating point value of a key by a number. Uses 0 as initial value if the key doesn't exist.","O(1)","2.6.0",CMD_DOC_NONE,NULL,NULL,"string",COMMAND_GROUP_STRING,INCRBYFLOAT_History,0,INCRBYFLOAT_Tips,0,incrbyfloatCommand,3,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_STRING,INCRBYFLOAT_Keyspecs,1,NULL,2),.args=INCRBYFLOAT_Args},
+{MAKE_CMD("lcs","Finds the longest common substring.","O(N*M) where N and M are the lengths of s1 and s2, respectively","7.0.0",CMD_DOC_NONE,NULL,NULL,"string",COMMAND_GROUP_STRING,LCS_History,0,LCS_Tips,0,lcsCommand,-3,CMD_READONLY,ACL_CATEGORY_STRING,LCS_Keyspecs,1,NULL,6),.args=LCS_Args},
+{MAKE_CMD("mget","Atomically returns the string values of one or more keys.","O(N) where N is the number of keys to retrieve.","1.0.0",CMD_DOC_NONE,NULL,NULL,"string",COMMAND_GROUP_STRING,MGET_History,0,MGET_Tips,1,mgetCommand,-2,CMD_READONLY|CMD_FAST,ACL_CATEGORY_STRING,MGET_Keyspecs,1,NULL,1),.args=MGET_Args},
+{MAKE_CMD("mset","Atomically creates or modifies the string values of one or more keys.","O(N) where N is the number of keys to set.","1.0.1",CMD_DOC_NONE,NULL,NULL,"string",COMMAND_GROUP_STRING,MSET_History,0,MSET_Tips,2,msetCommand,-3,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_STRING,MSET_Keyspecs,1,NULL,1),.args=MSET_Args},
+{MAKE_CMD("msetnx","Atomically modifies the string values of one or more keys only when all keys don't exist.","O(N) where N is the number of keys to set.","1.0.1",CMD_DOC_NONE,NULL,NULL,"string",COMMAND_GROUP_STRING,MSETNX_History,0,MSETNX_Tips,2,msetnxCommand,-3,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_STRING,MSETNX_Keyspecs,1,NULL,1),.args=MSETNX_Args},
+{MAKE_CMD("psetex","Sets both string value and expiration time in milliseconds of a key. The key is created if it doesn't exist.","O(1)","2.6.0",CMD_DOC_DEPRECATED,"`SET` with the `PX` argument","2.6.12","string",COMMAND_GROUP_STRING,PSETEX_History,0,PSETEX_Tips,0,psetexCommand,4,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_STRING,PSETEX_Keyspecs,1,NULL,3),.args=PSETEX_Args},
+{MAKE_CMD("set","Sets the string value of a key, ignoring its type. The key is created if it doesn't exist.","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,"string",COMMAND_GROUP_STRING,SET_History,4,SET_Tips,0,setCommand,-3,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_STRING,SET_Keyspecs,1,setGetKeys,5),.args=SET_Args},
+{MAKE_CMD("setex","Sets the string value and expiration time of a key. Creates the key if it doesn't exist.","O(1)","2.0.0",CMD_DOC_DEPRECATED,"`SET` with the `EX` argument","2.6.12","string",COMMAND_GROUP_STRING,SETEX_History,0,SETEX_Tips,0,setexCommand,4,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_STRING,SETEX_Keyspecs,1,NULL,3),.args=SETEX_Args},
+{MAKE_CMD("setnx","Set the string value of a key only when the key doesn't exist.","O(1)","1.0.0",CMD_DOC_DEPRECATED,"`SET` with the `NX` argument","2.6.12","string",COMMAND_GROUP_STRING,SETNX_History,0,SETNX_Tips,0,setnxCommand,3,CMD_WRITE|CMD_DENYOOM|CMD_FAST,ACL_CATEGORY_STRING,SETNX_Keyspecs,1,NULL,2),.args=SETNX_Args},
+{MAKE_CMD("setrange","Overwrites a part of a string value with another by an offset. Creates the key if it doesn't exist.","O(1), not counting the time taken to copy the new string in place. Usually, this string is very small so the amortized complexity is O(1). Otherwise, complexity is O(M) with M being the length of the value argument.","2.2.0",CMD_DOC_NONE,NULL,NULL,"string",COMMAND_GROUP_STRING,SETRANGE_History,0,SETRANGE_Tips,0,setrangeCommand,4,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_STRING,SETRANGE_Keyspecs,1,NULL,3),.args=SETRANGE_Args},
+{MAKE_CMD("strlen","Returns the length of a string value.","O(1)","2.2.0",CMD_DOC_NONE,NULL,NULL,"string",COMMAND_GROUP_STRING,STRLEN_History,0,STRLEN_Tips,0,strlenCommand,2,CMD_READONLY|CMD_FAST,ACL_CATEGORY_STRING,STRLEN_Keyspecs,1,NULL,1),.args=STRLEN_Args},
+{MAKE_CMD("substr","Returns a substring from a string value.","O(N) where N is the length of the returned string. The complexity is ultimately determined by the returned length, but because creating a substring from an existing string is very cheap, it can be considered O(1) for small strings.","1.0.0",CMD_DOC_DEPRECATED,"`GETRANGE`","2.0.0","string",COMMAND_GROUP_STRING,SUBSTR_History,0,SUBSTR_Tips,0,getrangeCommand,4,CMD_READONLY,ACL_CATEGORY_STRING,SUBSTR_Keyspecs,1,NULL,3),.args=SUBSTR_Args},
+/* transactions */
+{MAKE_CMD("discard","Discards a transaction.","O(N), when N is the number of queued commands","2.0.0",CMD_DOC_NONE,NULL,NULL,"transactions",COMMAND_GROUP_TRANSACTIONS,DISCARD_History,0,DISCARD_Tips,0,discardCommand,1,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_FAST|CMD_ALLOW_BUSY,ACL_CATEGORY_TRANSACTION,DISCARD_Keyspecs,0,NULL,0)},
+{MAKE_CMD("exec","Executes all commands in a transaction.","Depends on commands in the transaction","1.2.0",CMD_DOC_NONE,NULL,NULL,"transactions",COMMAND_GROUP_TRANSACTIONS,EXEC_History,0,EXEC_Tips,0,execCommand,1,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_SKIP_SLOWLOG,ACL_CATEGORY_TRANSACTION,EXEC_Keyspecs,0,NULL,0)},
+{MAKE_CMD("multi","Starts a transaction.","O(1)","1.2.0",CMD_DOC_NONE,NULL,NULL,"transactions",COMMAND_GROUP_TRANSACTIONS,MULTI_History,0,MULTI_Tips,0,multiCommand,1,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_FAST|CMD_ALLOW_BUSY,ACL_CATEGORY_TRANSACTION,MULTI_Keyspecs,0,NULL,0)},
+{MAKE_CMD("unwatch","Forgets about watched keys of a transaction.","O(1)","2.2.0",CMD_DOC_NONE,NULL,NULL,"transactions",COMMAND_GROUP_TRANSACTIONS,UNWATCH_History,0,UNWATCH_Tips,0,unwatchCommand,1,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_FAST|CMD_ALLOW_BUSY,ACL_CATEGORY_TRANSACTION,UNWATCH_Keyspecs,0,NULL,0)},
+{MAKE_CMD("watch","Monitors changes to keys to determine the execution of a transaction.","O(1) for every key.","2.2.0",CMD_DOC_NONE,NULL,NULL,"transactions",COMMAND_GROUP_TRANSACTIONS,WATCH_History,0,WATCH_Tips,0,watchCommand,-2,CMD_NOSCRIPT|CMD_LOADING|CMD_STALE|CMD_FAST|CMD_ALLOW_BUSY,ACL_CATEGORY_TRANSACTION,WATCH_Keyspecs,1,NULL,1),.args=WATCH_Args},
+{0}
+};
diff --git a/src/commands.h b/src/commands.h
new file mode 100644
index 000000000..52acacfe0
--- /dev/null
+++ b/src/commands.h
@@ -0,0 +1,40 @@
+#ifndef __REDIS_COMMANDS_H
+#define __REDIS_COMMANDS_H
+
+/* Must be synced with ARG_TYPE_STR and generate-command-code.py */
+typedef enum {
+ ARG_TYPE_STRING,
+ ARG_TYPE_INTEGER,
+ ARG_TYPE_DOUBLE,
+ ARG_TYPE_KEY, /* A string, but represents a keyname */
+ ARG_TYPE_PATTERN,
+ ARG_TYPE_UNIX_TIME,
+ ARG_TYPE_PURE_TOKEN,
+ ARG_TYPE_ONEOF, /* Has subargs */
+ ARG_TYPE_BLOCK /* Has subargs */
+} redisCommandArgType;
+
+#define CMD_ARG_NONE (0)
+#define CMD_ARG_OPTIONAL (1<<0)
+#define CMD_ARG_MULTIPLE (1<<1)
+#define CMD_ARG_MULTIPLE_TOKEN (1<<2)
+
+/* WARNING! This struct must match RedisModuleCommandArg */
+typedef struct redisCommandArg {
+ const char *name;
+ redisCommandArgType type;
+ int key_spec_index;
+ const char *token;
+ const char *summary;
+ const char *since;
+ int flags;
+ const char *deprecated_since;
+ int num_args;
+ struct redisCommandArg *subargs;
+ const char *display_text;
+} redisCommandArg;
+
+/* Returns the command group name by group number. */
+const char *commandGroupStr(int index);
+
+#endif
diff --git a/src/commands/acl-cat.json b/src/commands/acl-cat.json
index 635e2b88d..dfbe4c43e 100644
--- a/src/commands/acl-cat.json
+++ b/src/commands/acl-cat.json
@@ -1,6 +1,6 @@
{
"CAT": {
- "summary": "List the ACL categories or the commands inside a category",
+ "summary": "Lists the ACL categories, or the commands inside a category.",
"complexity": "O(1) since the categories and commands are a fixed set.",
"group": "server",
"since": "6.0.0",
@@ -17,14 +17,14 @@
"anyOf": [
{
"type": "array",
- "description": "In case `categoryname` was not given, a list of existing ACL categories",
+ "description": "In case `category` was not given, a list of existing ACL categories",
"items": {
"type": "string"
}
},
{
"type": "array",
- "description": "In case `categoryname` was given, list of commands that fall under the provided ACL category",
+ "description": "In case `category` was given, list of commands that fall under the provided ACL category",
"items": {
"type": "string"
}
@@ -33,7 +33,7 @@
},
"arguments": [
{
- "name": "categoryname",
+ "name": "category",
"type": "string",
"optional": true
}
diff --git a/src/commands/acl-deluser.json b/src/commands/acl-deluser.json
index 9568d6d4b..4fc106503 100644
--- a/src/commands/acl-deluser.json
+++ b/src/commands/acl-deluser.json
@@ -1,6 +1,6 @@
{
"DELUSER": {
- "summary": "Remove the specified ACL users and the associated rules",
+ "summary": "Deletes ACL users, and terminates their connections.",
"complexity": "O(1) amortized time considering the typical user.",
"group": "server",
"since": "6.0.0",
diff --git a/src/commands/acl-dryrun.json b/src/commands/acl-dryrun.json
index f8d009d4d..bee6a6aac 100644
--- a/src/commands/acl-dryrun.json
+++ b/src/commands/acl-dryrun.json
@@ -1,6 +1,6 @@
{
"DRYRUN": {
- "summary": "Returns whether the user can execute the given command without executing the command.",
+ "summary": "Simulates the execution of a command by a user, without executing the command.",
"complexity": "O(1).",
"group": "server",
"since": "7.0.0",
diff --git a/src/commands/acl-genpass.json b/src/commands/acl-genpass.json
index 8af04875a..86c1f8e29 100644
--- a/src/commands/acl-genpass.json
+++ b/src/commands/acl-genpass.json
@@ -1,6 +1,6 @@
{
"GENPASS": {
- "summary": "Generate a pseudorandom secure password to use for ACL users",
+ "summary": "Generates a pseudorandom, secure password that can be used to identify ACL users.",
"complexity": "O(1)",
"group": "server",
"since": "6.0.0",
diff --git a/src/commands/acl-getuser.json b/src/commands/acl-getuser.json
index b09b6abeb..535389bc5 100644
--- a/src/commands/acl-getuser.json
+++ b/src/commands/acl-getuser.json
@@ -1,6 +1,6 @@
{
"GETUSER": {
- "summary": "Get the rules for a specific ACL user",
+ "summary": "Lists the ACL rules of a user.",
"complexity": "O(N). Where N is the number of password, command and pattern rules that the user has.",
"group": "server",
"since": "6.0.0",
diff --git a/src/commands/acl-help.json b/src/commands/acl-help.json
index f6afea308..3c95914db 100644
--- a/src/commands/acl-help.json
+++ b/src/commands/acl-help.json
@@ -1,6 +1,6 @@
{
"HELP": {
- "summary": "Show helpful text about the different subcommands",
+ "summary": "Returns helpful text about the different subcommands.",
"complexity": "O(1)",
"group": "server",
"since": "6.0.0",
diff --git a/src/commands/acl-list.json b/src/commands/acl-list.json
index cb4de7e9f..0d75b137f 100644
--- a/src/commands/acl-list.json
+++ b/src/commands/acl-list.json
@@ -1,6 +1,6 @@
{
"LIST": {
- "summary": "List the current ACL rules in ACL config file format",
+ "summary": "Dumps the effective rules in ACL file format.",
"complexity": "O(N). Where N is the number of configured users.",
"group": "server",
"since": "6.0.0",
diff --git a/src/commands/acl-load.json b/src/commands/acl-load.json
index b9d377c2d..d7b91ba61 100644
--- a/src/commands/acl-load.json
+++ b/src/commands/acl-load.json
@@ -1,6 +1,6 @@
{
"LOAD": {
- "summary": "Reload the ACLs from the configured ACL file",
+ "summary": "Reloads the rules from the configured ACL file.",
"complexity": "O(N). Where N is the number of configured users.",
"group": "server",
"since": "6.0.0",
diff --git a/src/commands/acl-log.json b/src/commands/acl-log.json
index eb3a59b0e..de5f029e4 100644
--- a/src/commands/acl-log.json
+++ b/src/commands/acl-log.json
@@ -1,6 +1,6 @@
{
"LOG": {
- "summary": "List latest events denied because of ACLs in place",
+ "summary": "Lists recent security events generated due to ACL rules.",
"complexity": "O(N) with N being the number of entries shown.",
"group": "server",
"since": "6.0.0",
diff --git a/src/commands/acl-save.json b/src/commands/acl-save.json
index 4e2942f5d..0b2af21e6 100644
--- a/src/commands/acl-save.json
+++ b/src/commands/acl-save.json
@@ -1,6 +1,6 @@
{
"SAVE": {
- "summary": "Save the current ACL rules in the configured ACL file",
+ "summary": "Saves the effective ACL rules in the configured ACL file.",
"complexity": "O(N). Where N is the number of configured users.",
"group": "server",
"since": "6.0.0",
diff --git a/src/commands/acl-setuser.json b/src/commands/acl-setuser.json
index 875840d42..e26df464f 100644
--- a/src/commands/acl-setuser.json
+++ b/src/commands/acl-setuser.json
@@ -1,6 +1,6 @@
{
"SETUSER": {
- "summary": "Modify or create the rules for a specific ACL user",
+ "summary": "Creates and modifies an ACL user and its rules.",
"complexity": "O(N). Where N is the number of rules provided.",
"group": "server",
"since": "6.0.0",
diff --git a/src/commands/acl-users.json b/src/commands/acl-users.json
index 13e8eba95..1a6bc7557 100644
--- a/src/commands/acl-users.json
+++ b/src/commands/acl-users.json
@@ -1,6 +1,6 @@
{
"USERS": {
- "summary": "List the username of all the configured ACL rules",
+ "summary": "Lists all ACL users.",
"complexity": "O(N). Where N is the number of configured users.",
"group": "server",
"since": "6.0.0",
diff --git a/src/commands/acl-whoami.json b/src/commands/acl-whoami.json
index b0477b363..2efe98c52 100644
--- a/src/commands/acl-whoami.json
+++ b/src/commands/acl-whoami.json
@@ -1,6 +1,6 @@
{
"WHOAMI": {
- "summary": "Return the name of the user associated to the current connection",
+ "summary": "Returns the authenticated username of the current connection.",
"complexity": "O(1)",
"group": "server",
"since": "6.0.0",
diff --git a/src/commands/acl.json b/src/commands/acl.json
index 3c9141abc..1474b782f 100644
--- a/src/commands/acl.json
+++ b/src/commands/acl.json
@@ -1,6 +1,6 @@
{
"ACL": {
- "summary": "A container for Access List Control commands ",
+ "summary": "A container for Access List Control commands.",
"complexity": "Depends on subcommand.",
"group": "server",
"since": "6.0.0",
diff --git a/src/commands/append.json b/src/commands/append.json
index 0af5e7bb1..a8ec6bf82 100644
--- a/src/commands/append.json
+++ b/src/commands/append.json
@@ -1,6 +1,6 @@
{
"APPEND": {
- "summary": "Append a value to a key",
+ "summary": "Appends a string to the value of a key. Creates the key if it doesn't exist.",
"complexity": "O(1). The amortized time complexity is O(1) assuming the appended value is small and the already present value is of any size, since the dynamic string library used by Redis will double the free space available on every reallocation.",
"group": "string",
"since": "2.0.0",
@@ -36,7 +36,7 @@
],
"reply_schema": {
"type": "integer",
- "description": "The the length of the string after the append operation."
+ "description": "The length of the string after the append operation."
},
"arguments": [
{
diff --git a/src/commands/asking.json b/src/commands/asking.json
index 432c84709..388679509 100644
--- a/src/commands/asking.json
+++ b/src/commands/asking.json
@@ -1,6 +1,6 @@
{
"ASKING": {
- "summary": "Sent by cluster clients after an -ASK redirect",
+ "summary": "Signals that a cluster client is following an -ASK redirect.",
"complexity": "O(1)",
"group": "cluster",
"since": "3.0.0",
diff --git a/src/commands/auth.json b/src/commands/auth.json
index aaad29ad2..3b1ba3543 100644
--- a/src/commands/auth.json
+++ b/src/commands/auth.json
@@ -1,6 +1,6 @@
{
"AUTH": {
- "summary": "Authenticate to the server",
+ "summary": "Authenticates the connection.",
"complexity": "O(N) where N is the number of passwords defined for the user",
"group": "connection",
"since": "1.0.0",
diff --git a/src/commands/bgrewriteaof.json b/src/commands/bgrewriteaof.json
index e73351294..6267d3122 100644
--- a/src/commands/bgrewriteaof.json
+++ b/src/commands/bgrewriteaof.json
@@ -1,6 +1,6 @@
{
"BGREWRITEAOF": {
- "summary": "Asynchronously rewrite the append-only file",
+ "summary": "Asynchronously rewrites the append-only file to disk.",
"complexity": "O(1)",
"group": "server",
"since": "1.0.0",
diff --git a/src/commands/bgsave.json b/src/commands/bgsave.json
index 28aa14a02..f73d8a89b 100644
--- a/src/commands/bgsave.json
+++ b/src/commands/bgsave.json
@@ -1,6 +1,6 @@
{
"BGSAVE": {
- "summary": "Asynchronously save the dataset to disk",
+ "summary": "Asynchronously saves the database(s) to disk.",
"complexity": "O(1)",
"group": "server",
"since": "1.0.0",
diff --git a/src/commands/bitcount.json b/src/commands/bitcount.json
index 7906415b6..2d277a855 100644
--- a/src/commands/bitcount.json
+++ b/src/commands/bitcount.json
@@ -1,6 +1,6 @@
{
"BITCOUNT": {
- "summary": "Count set bits in a string",
+ "summary": "Counts the number of set bits (population counting) in a string.",
"complexity": "O(N)",
"group": "bitmap",
"since": "2.6.0",
diff --git a/src/commands/bitfield.json b/src/commands/bitfield.json
index 9f867ab55..843cea866 100644
--- a/src/commands/bitfield.json
+++ b/src/commands/bitfield.json
@@ -1,6 +1,6 @@
{
"BITFIELD": {
- "summary": "Perform arbitrary bitfield integer operations on strings",
+ "summary": "Performs arbitrary bitfield integer operations on strings.",
"complexity": "O(1) for each subcommand specified",
"group": "bitmap",
"since": "3.2.0",
diff --git a/src/commands/bitfield_ro.json b/src/commands/bitfield_ro.json
index ccf1aae23..0b5aa7147 100644
--- a/src/commands/bitfield_ro.json
+++ b/src/commands/bitfield_ro.json
@@ -1,6 +1,6 @@
{
"BITFIELD_RO": {
- "summary": "Perform arbitrary bitfield integer operations on strings. Read-only variant of BITFIELD",
+ "summary": "Performs arbitrary read-only bitfield integer operations on strings.",
"complexity": "O(1) for each subcommand specified",
"group": "bitmap",
"since": "6.0.0",
diff --git a/src/commands/bitop.json b/src/commands/bitop.json
index 7c959683e..d9e1ff9ef 100644
--- a/src/commands/bitop.json
+++ b/src/commands/bitop.json
@@ -1,6 +1,6 @@
{
"BITOP": {
- "summary": "Perform bitwise operations between strings",
+ "summary": "Performs bitwise operations on multiple strings, and stores the result.",
"complexity": "O(N)",
"group": "bitmap",
"since": "2.6.0",
diff --git a/src/commands/bitpos.json b/src/commands/bitpos.json
index 439b61246..11b0851e8 100644
--- a/src/commands/bitpos.json
+++ b/src/commands/bitpos.json
@@ -1,6 +1,6 @@
{
"BITPOS": {
- "summary": "Find first bit set or clear in a string",
+ "summary": "Finds the first set (1) or clear (0) bit in a string.",
"complexity": "O(N)",
"group": "bitmap",
"since": "2.8.7",
diff --git a/src/commands/blmove.json b/src/commands/blmove.json
index 2445ccf45..b316c5271 100644
--- a/src/commands/blmove.json
+++ b/src/commands/blmove.json
@@ -1,6 +1,6 @@
{
"BLMOVE": {
- "summary": "Pop an element from a list, push it to another list and return it; or block until one is available",
+ "summary": "Pops an element from a list, pushes it to another list and returns it. Blocks until an element is available otherwise. Deletes the list if the last element was moved.",
"complexity": "O(1)",
"group": "list",
"since": "6.2.0",
diff --git a/src/commands/blmpop.json b/src/commands/blmpop.json
index 419eb3e6f..48bfa3e89 100644
--- a/src/commands/blmpop.json
+++ b/src/commands/blmpop.json
@@ -1,6 +1,6 @@
{
"BLMPOP": {
- "summary": "Pop elements from a list, or block until one is available",
+ "summary": "Pops the first element from one of multiple lists. Blocks until an element is available otherwise. Deletes the list if the last element was popped.",
"complexity": "O(N+M) where N is the number of provided keys and M is the number of elements returned.",
"group": "list",
"since": "7.0.0",
diff --git a/src/commands/blpop.json b/src/commands/blpop.json
index 1c7f3b7b9..cf90f0aa4 100644
--- a/src/commands/blpop.json
+++ b/src/commands/blpop.json
@@ -1,6 +1,6 @@
{
"BLPOP": {
- "summary": "Remove and get the first element in a list, or block until one is available",
+ "summary": "Removes and returns the first element in a list. Blocks until an element is available otherwise. Deletes the list if the last element was popped.",
"complexity": "O(N) where N is the number of provided keys.",
"group": "list",
"since": "2.0.0",
diff --git a/src/commands/brpop.json b/src/commands/brpop.json
index 928f63967..129f74dd1 100644
--- a/src/commands/brpop.json
+++ b/src/commands/brpop.json
@@ -1,6 +1,6 @@
{
"BRPOP": {
- "summary": "Remove and get the last element in a list, or block until one is available",
+ "summary": "Removes and returns the last element in a list. Blocks until an element is available otherwise. Deletes the list if the last element was popped.",
"complexity": "O(N) where N is the number of provided keys.",
"group": "list",
"since": "2.0.0",
diff --git a/src/commands/brpoplpush.json b/src/commands/brpoplpush.json
index af8761666..dce1516e2 100644
--- a/src/commands/brpoplpush.json
+++ b/src/commands/brpoplpush.json
@@ -1,6 +1,6 @@
{
"BRPOPLPUSH": {
- "summary": "Pop an element from a list, push it to another list and return it; or block until one is available",
+ "summary": "Pops an element from a list, pushes it to another list and returns it. Block until an element is available otherwise. Deletes the list if the last element was popped.",
"complexity": "O(1)",
"group": "list",
"since": "2.2.0",
diff --git a/src/commands/bzmpop.json b/src/commands/bzmpop.json
index 30d502dc3..4ff5c110a 100644
--- a/src/commands/bzmpop.json
+++ b/src/commands/bzmpop.json
@@ -1,6 +1,6 @@
{
"BZMPOP": {
- "summary": "Remove and return members with scores in a sorted set or block until one is available",
+ "summary": "Removes and returns a member by score from one or more sorted sets. Blocks until a member is available otherwise. Deletes the sorted set if the last element was popped.",
"complexity": "O(K) + O(M*log(N)) where K is the number of provided keys, N being the number of elements in the sorted set, and M being the number of elements popped.",
"group": "sorted_set",
"since": "7.0.0",
diff --git a/src/commands/bzpopmax.json b/src/commands/bzpopmax.json
index ef0272320..70b0b70ab 100644
--- a/src/commands/bzpopmax.json
+++ b/src/commands/bzpopmax.json
@@ -1,6 +1,6 @@
{
"BZPOPMAX": {
- "summary": "Remove and return the member with the highest score from one or more sorted sets, or block until one is available",
+ "summary": "Removes and returns the member with the highest score from one or more sorted sets. Blocks until a member available otherwise. Deletes the sorted set if the last element was popped.",
"complexity": "O(log(N)) with N being the number of elements in the sorted set.",
"group": "sorted_set",
"since": "5.0.0",
diff --git a/src/commands/bzpopmin.json b/src/commands/bzpopmin.json
index c378a3da9..a2f305cd2 100644
--- a/src/commands/bzpopmin.json
+++ b/src/commands/bzpopmin.json
@@ -1,6 +1,6 @@
{
"BZPOPMIN": {
- "summary": "Remove and return the member with the lowest score from one or more sorted sets, or block until one is available",
+ "summary": "Removes and returns the member with the lowest score from one or more sorted sets. Blocks until a member is available otherwise. Deletes the sorted set if the last element was popped.",
"complexity": "O(log(N)) with N being the number of elements in the sorted set.",
"group": "sorted_set",
"since": "5.0.0",
diff --git a/src/commands/client-caching.json b/src/commands/client-caching.json
index 22172e094..2a4ae891d 100644
--- a/src/commands/client-caching.json
+++ b/src/commands/client-caching.json
@@ -1,6 +1,6 @@
{
"CACHING": {
- "summary": "Instruct the server about tracking or not keys in the next request",
+ "summary": "Instructs the server whether to track the keys in the next request.",
"complexity": "O(1)",
"group": "connection",
"since": "6.0.0",
diff --git a/src/commands/client-getname.json b/src/commands/client-getname.json
index c8cb21946..9e237af84 100644
--- a/src/commands/client-getname.json
+++ b/src/commands/client-getname.json
@@ -1,6 +1,6 @@
{
"GETNAME": {
- "summary": "Get the current connection name",
+ "summary": "Returns the name of the connection.",
"complexity": "O(1)",
"group": "connection",
"since": "2.6.9",
diff --git a/src/commands/client-getredir.json b/src/commands/client-getredir.json
index 5cbc27b87..6fdb002dc 100644
--- a/src/commands/client-getredir.json
+++ b/src/commands/client-getredir.json
@@ -1,6 +1,6 @@
{
"GETREDIR": {
- "summary": "Get tracking notifications redirection client ID if any",
+ "summary": "Returns the client ID to which the connection's tracking notifications are redirected.",
"complexity": "O(1)",
"group": "connection",
"since": "6.0.0",
diff --git a/src/commands/client-help.json b/src/commands/client-help.json
index ab18a6e6c..b49294c9e 100644
--- a/src/commands/client-help.json
+++ b/src/commands/client-help.json
@@ -1,6 +1,6 @@
{
"HELP": {
- "summary": "Show helpful text about the different subcommands",
+ "summary": "Returns helpful text about the different subcommands.",
"complexity": "O(1)",
"group": "connection",
"since": "5.0.0",
diff --git a/src/commands/client-id.json b/src/commands/client-id.json
index 771a3d057..7c2bf0820 100644
--- a/src/commands/client-id.json
+++ b/src/commands/client-id.json
@@ -1,6 +1,6 @@
{
"ID": {
- "summary": "Returns the client ID for the current connection",
+ "summary": "Returns the unique client ID of the connection.",
"complexity": "O(1)",
"group": "connection",
"since": "5.0.0",
diff --git a/src/commands/client-info.json b/src/commands/client-info.json
index 19c0a3cfd..93fa008f5 100644
--- a/src/commands/client-info.json
+++ b/src/commands/client-info.json
@@ -1,6 +1,6 @@
{
"INFO": {
- "summary": "Returns information about the current client connection.",
+ "summary": "Returns information about the connection.",
"complexity": "O(1)",
"group": "connection",
"since": "6.2.0",
diff --git a/src/commands/client-kill.json b/src/commands/client-kill.json
index 5791eea40..bd0262d4e 100644
--- a/src/commands/client-kill.json
+++ b/src/commands/client-kill.json
@@ -1,6 +1,6 @@
{
"KILL": {
- "summary": "Kill the connection of a client",
+ "summary": "Terminates open connections.",
"complexity": "O(N) where N is the number of client connections",
"group": "connection",
"since": "2.4.0",
diff --git a/src/commands/client-list.json b/src/commands/client-list.json
index 5c822e6c3..f72ffaf40 100644
--- a/src/commands/client-list.json
+++ b/src/commands/client-list.json
@@ -1,6 +1,6 @@
{
"LIST": {
- "summary": "Get the list of client connections",
+ "summary": "Lists open connections.",
"complexity": "O(N) where N is the number of client connections",
"group": "connection",
"since": "2.4.0",
diff --git a/src/commands/client-no-evict.json b/src/commands/client-no-evict.json
index 9cfb06628..9ed671840 100644
--- a/src/commands/client-no-evict.json
+++ b/src/commands/client-no-evict.json
@@ -1,6 +1,6 @@
{
"NO-EVICT": {
- "summary": "Set client eviction mode for the current connection",
+ "summary": "Sets the client eviction mode of the connection.",
"complexity": "O(1)",
"group": "connection",
"since": "7.0.0",
diff --git a/src/commands/client-no-touch.json b/src/commands/client-no-touch.json
index 03c2ad0dc..4cf7b7241 100644
--- a/src/commands/client-no-touch.json
+++ b/src/commands/client-no-touch.json
@@ -1,6 +1,6 @@
{
"NO-TOUCH": {
- "summary": "Controls whether commands sent by the client will alter the LRU/LFU of the keys they access.",
+ "summary": "Controls whether commands sent by the client affect the LRU/LFU of accessed keys.",
"complexity": "O(1)",
"group": "connection",
"since": "7.2.0",
diff --git a/src/commands/client-pause.json b/src/commands/client-pause.json
index 2be85424c..b1dd7bc47 100644
--- a/src/commands/client-pause.json
+++ b/src/commands/client-pause.json
@@ -1,6 +1,6 @@
{
"PAUSE": {
- "summary": "Stop processing commands from clients for some time",
+ "summary": "Suspends commands processing.",
"complexity": "O(1)",
"group": "connection",
"since": "3.0.0",
diff --git a/src/commands/client-reply.json b/src/commands/client-reply.json
index 51e93c81d..9406de85c 100644
--- a/src/commands/client-reply.json
+++ b/src/commands/client-reply.json
@@ -1,6 +1,6 @@
{
"REPLY": {
- "summary": "Instruct the server whether to reply to commands",
+ "summary": "Instructs the server whether to reply to commands.",
"complexity": "O(1)",
"group": "connection",
"since": "3.2.0",
diff --git a/src/commands/client-setinfo.json b/src/commands/client-setinfo.json
index 426ff4d28..e61ba5664 100644
--- a/src/commands/client-setinfo.json
+++ b/src/commands/client-setinfo.json
@@ -1,6 +1,6 @@
{
"SETINFO": {
- "summary": "Set client or connection specific info",
+ "summary": "Sets information specific to the client or connection.",
"complexity": "O(1)",
"group": "connection",
"since": "7.2.0",
diff --git a/src/commands/client-setname.json b/src/commands/client-setname.json
index c53bf1651..e8920b686 100644
--- a/src/commands/client-setname.json
+++ b/src/commands/client-setname.json
@@ -1,6 +1,6 @@
{
"SETNAME": {
- "summary": "Set the current connection name",
+ "summary": "Sets the connection name.",
"complexity": "O(1)",
"group": "connection",
"since": "2.6.9",
diff --git a/src/commands/client-tracking.json b/src/commands/client-tracking.json
index 48ed0f7ea..28e84eceb 100644
--- a/src/commands/client-tracking.json
+++ b/src/commands/client-tracking.json
@@ -1,6 +1,6 @@
{
"TRACKING": {
- "summary": "Enable or disable server assisted client side caching support",
+ "summary": "Controls server-assisted client-side caching for the connection.",
"complexity": "O(1). Some options may introduce additional complexity.",
"group": "connection",
"since": "6.0.0",
diff --git a/src/commands/client-trackinginfo.json b/src/commands/client-trackinginfo.json
index 6873c7512..270a3d5e6 100644
--- a/src/commands/client-trackinginfo.json
+++ b/src/commands/client-trackinginfo.json
@@ -1,6 +1,6 @@
{
"TRACKINGINFO": {
- "summary": "Return information about server assisted client side caching for the current connection",
+ "summary": "Returns information about server-assisted client-side caching for the connection.",
"complexity": "O(1)",
"group": "connection",
"since": "6.2.0",
diff --git a/src/commands/client-unblock.json b/src/commands/client-unblock.json
index c96b78d24..d028f4714 100644
--- a/src/commands/client-unblock.json
+++ b/src/commands/client-unblock.json
@@ -1,6 +1,6 @@
{
"UNBLOCK": {
- "summary": "Unblock a client blocked in a blocking command from a different connection",
+ "summary": "Unblocks a client blocked by a blocking command from a different connection.",
"complexity": "O(log N) where N is the number of client connections",
"group": "connection",
"since": "5.0.0",
diff --git a/src/commands/client-unpause.json b/src/commands/client-unpause.json
index 186b9cc4a..6c55210d2 100644
--- a/src/commands/client-unpause.json
+++ b/src/commands/client-unpause.json
@@ -1,6 +1,6 @@
{
"UNPAUSE": {
- "summary": "Resume processing of clients that were paused",
+ "summary": "Resumes processing commands from paused clients.",
"complexity": "O(N) Where N is the number of paused clients",
"group": "connection",
"since": "6.2.0",
diff --git a/src/commands/client.json b/src/commands/client.json
index dfe2a9bc7..b50996128 100644
--- a/src/commands/client.json
+++ b/src/commands/client.json
@@ -1,6 +1,6 @@
{
"CLIENT": {
- "summary": "A container for client connection commands",
+ "summary": "A container for client connection commands.",
"complexity": "Depends on subcommand.",
"group": "connection",
"since": "2.4.0",
diff --git a/src/commands/cluster-addslots.json b/src/commands/cluster-addslots.json
index db06e1ddb..4d2ea2593 100644
--- a/src/commands/cluster-addslots.json
+++ b/src/commands/cluster-addslots.json
@@ -1,6 +1,6 @@
{
"ADDSLOTS": {
- "summary": "Assign new hash slots to receiving node",
+ "summary": "Assigns new hash slots to a node.",
"complexity": "O(N) where N is the total number of hash slot arguments",
"group": "cluster",
"since": "3.0.0",
diff --git a/src/commands/cluster-addslotsrange.json b/src/commands/cluster-addslotsrange.json
index 769392bca..4ff8a4084 100644
--- a/src/commands/cluster-addslotsrange.json
+++ b/src/commands/cluster-addslotsrange.json
@@ -1,6 +1,6 @@
{
"ADDSLOTSRANGE": {
- "summary": "Assign new hash slots to receiving node",
+ "summary": "Assigns new hash slot ranges to a node.",
"complexity": "O(N) where N is the total number of the slots between the start slot and end slot arguments.",
"group": "cluster",
"since": "7.0.0",
diff --git a/src/commands/cluster-bumpepoch.json b/src/commands/cluster-bumpepoch.json
index ce4af47f3..786723c5c 100644
--- a/src/commands/cluster-bumpepoch.json
+++ b/src/commands/cluster-bumpepoch.json
@@ -1,6 +1,6 @@
{
"BUMPEPOCH": {
- "summary": "Advance the cluster config epoch",
+ "summary": "Advances the cluster config epoch.",
"complexity": "O(1)",
"group": "cluster",
"since": "3.0.0",
diff --git a/src/commands/cluster-count-failure-reports.json b/src/commands/cluster-count-failure-reports.json
index 7964d2be6..b80632d0f 100644
--- a/src/commands/cluster-count-failure-reports.json
+++ b/src/commands/cluster-count-failure-reports.json
@@ -1,6 +1,6 @@
{
"COUNT-FAILURE-REPORTS": {
- "summary": "Return the number of failure reports active for a given node",
+ "summary": "Returns the number of active failure reports active for a node.",
"complexity": "O(N) where N is the number of failure reports",
"group": "cluster",
"since": "3.0.0",
diff --git a/src/commands/cluster-countkeysinslot.json b/src/commands/cluster-countkeysinslot.json
index c4e7d7d4b..caeec5132 100644
--- a/src/commands/cluster-countkeysinslot.json
+++ b/src/commands/cluster-countkeysinslot.json
@@ -1,6 +1,6 @@
{
"COUNTKEYSINSLOT": {
- "summary": "Return the number of local keys in the specified hash slot",
+ "summary": "Returns the number of keys in a hash slot.",
"complexity": "O(1)",
"group": "cluster",
"since": "3.0.0",
diff --git a/src/commands/cluster-delslots.json b/src/commands/cluster-delslots.json
index cc96b214f..0732d2b9a 100644
--- a/src/commands/cluster-delslots.json
+++ b/src/commands/cluster-delslots.json
@@ -1,6 +1,6 @@
{
"DELSLOTS": {
- "summary": "Set hash slots as unbound in receiving node",
+ "summary": "Sets hash slots as unbound for a node.",
"complexity": "O(N) where N is the total number of hash slot arguments",
"group": "cluster",
"since": "3.0.0",
diff --git a/src/commands/cluster-delslotsrange.json b/src/commands/cluster-delslotsrange.json
index 2ecc81701..77f1aea82 100644
--- a/src/commands/cluster-delslotsrange.json
+++ b/src/commands/cluster-delslotsrange.json
@@ -1,6 +1,6 @@
{
"DELSLOTSRANGE": {
- "summary": "Set hash slots as unbound in receiving node",
+ "summary": "Sets hash slot ranges as unbound for a node.",
"complexity": "O(N) where N is the total number of the slots between the start slot and end slot arguments.",
"group": "cluster",
"since": "7.0.0",
diff --git a/src/commands/cluster-flushslots.json b/src/commands/cluster-flushslots.json
index 7834a4f35..09902ad7c 100644
--- a/src/commands/cluster-flushslots.json
+++ b/src/commands/cluster-flushslots.json
@@ -1,6 +1,6 @@
{
"FLUSHSLOTS": {
- "summary": "Delete a node's own slots information",
+ "summary": "Deletes all slots information from a node.",
"complexity": "O(1)",
"group": "cluster",
"since": "3.0.0",
diff --git a/src/commands/cluster-forget.json b/src/commands/cluster-forget.json
index 1c4e74aa3..6f9a25ce6 100644
--- a/src/commands/cluster-forget.json
+++ b/src/commands/cluster-forget.json
@@ -1,6 +1,6 @@
{
"FORGET": {
- "summary": "Remove a node from the nodes table",
+ "summary": "Removes a node from the nodes table.",
"complexity": "O(1)",
"group": "cluster",
"since": "3.0.0",
diff --git a/src/commands/cluster-getkeysinslot.json b/src/commands/cluster-getkeysinslot.json
index 6aa90234e..039dfeabf 100644
--- a/src/commands/cluster-getkeysinslot.json
+++ b/src/commands/cluster-getkeysinslot.json
@@ -1,6 +1,6 @@
{
"GETKEYSINSLOT": {
- "summary": "Return local key names in the specified hash slot",
+ "summary": "Returns the key names in a hash slot.",
"complexity": "O(N) where N is the number of requested keys",
"group": "cluster",
"since": "3.0.0",
diff --git a/src/commands/cluster-help.json b/src/commands/cluster-help.json
index 59a8362d7..27f7d0833 100644
--- a/src/commands/cluster-help.json
+++ b/src/commands/cluster-help.json
@@ -1,6 +1,6 @@
{
"HELP": {
- "summary": "Show helpful text about the different subcommands",
+ "summary": "Returns helpful text about the different subcommands.",
"complexity": "O(1)",
"group": "cluster",
"since": "5.0.0",
diff --git a/src/commands/cluster-info.json b/src/commands/cluster-info.json
index cf8b611b4..2c88760eb 100644
--- a/src/commands/cluster-info.json
+++ b/src/commands/cluster-info.json
@@ -1,6 +1,6 @@
{
"INFO": {
- "summary": "Provides info about Redis Cluster node state",
+ "summary": "Returns information about the state of a node.",
"complexity": "O(1)",
"group": "cluster",
"since": "3.0.0",
diff --git a/src/commands/cluster-keyslot.json b/src/commands/cluster-keyslot.json
index 19f1ad65e..e51b643b9 100644
--- a/src/commands/cluster-keyslot.json
+++ b/src/commands/cluster-keyslot.json
@@ -1,6 +1,6 @@
{
"KEYSLOT": {
- "summary": "Returns the hash slot of the specified key",
+ "summary": "Returns the hash slot for a key.",
"complexity": "O(N) where N is the number of bytes in the key",
"group": "cluster",
"since": "3.0.0",
diff --git a/src/commands/cluster-links.json b/src/commands/cluster-links.json
index 3681c7c55..07b2e833b 100644
--- a/src/commands/cluster-links.json
+++ b/src/commands/cluster-links.json
@@ -1,6 +1,6 @@
{
"LINKS": {
- "summary": "Returns a list of all TCP links to and from peer nodes in cluster",
+ "summary": "Returns a list of all TCP links to and from peer nodes.",
"complexity": "O(N) where N is the total number of Cluster nodes",
"group": "cluster",
"since": "7.0.0",
diff --git a/src/commands/cluster-meet.json b/src/commands/cluster-meet.json
index 04d374ae2..7ca23a74b 100644
--- a/src/commands/cluster-meet.json
+++ b/src/commands/cluster-meet.json
@@ -1,6 +1,6 @@
{
"MEET": {
- "summary": "Force a node cluster to handshake with another node",
+ "summary": "Forces a node to handshake with another node.",
"complexity": "O(1)",
"group": "cluster",
"since": "3.0.0",
diff --git a/src/commands/cluster-myid.json b/src/commands/cluster-myid.json
index f2c13b581..340d425fd 100644
--- a/src/commands/cluster-myid.json
+++ b/src/commands/cluster-myid.json
@@ -1,6 +1,6 @@
{
"MYID": {
- "summary": "Return the node id",
+ "summary": "Returns the ID of a node.",
"complexity": "O(1)",
"group": "cluster",
"since": "3.0.0",
diff --git a/src/commands/cluster-myshardid.json b/src/commands/cluster-myshardid.json
index 70ac1ddc3..7db63fd19 100644
--- a/src/commands/cluster-myshardid.json
+++ b/src/commands/cluster-myshardid.json
@@ -1,6 +1,6 @@
{
"MYSHARDID": {
- "summary": "Return the node shard id",
+ "summary": "Returns the shard ID of a node.",
"complexity": "O(1)",
"group": "cluster",
"since": "7.2.0",
diff --git a/src/commands/cluster-nodes.json b/src/commands/cluster-nodes.json
index 6eb7f5385..ce34944e1 100644
--- a/src/commands/cluster-nodes.json
+++ b/src/commands/cluster-nodes.json
@@ -1,6 +1,6 @@
{
"NODES": {
- "summary": "Get Cluster config for the node",
+ "summary": "Returns the cluster configuration for a node.",
"complexity": "O(N) where N is the total number of Cluster nodes",
"group": "cluster",
"since": "3.0.0",
diff --git a/src/commands/cluster-replicas.json b/src/commands/cluster-replicas.json
index a7c5a4604..49a922770 100644
--- a/src/commands/cluster-replicas.json
+++ b/src/commands/cluster-replicas.json
@@ -1,6 +1,6 @@
{
"REPLICAS": {
- "summary": "List replica nodes of the specified master node",
+ "summary": "Lists the replica nodes of a master node.",
"complexity": "O(1)",
"group": "cluster",
"since": "5.0.0",
diff --git a/src/commands/cluster-replicate.json b/src/commands/cluster-replicate.json
index d49be4fb2..060d4af19 100644
--- a/src/commands/cluster-replicate.json
+++ b/src/commands/cluster-replicate.json
@@ -1,6 +1,6 @@
{
"REPLICATE": {
- "summary": "Reconfigure a node as a replica of the specified master node",
+ "summary": "Configure a node as replica of a master node.",
"complexity": "O(1)",
"group": "cluster",
"since": "3.0.0",
diff --git a/src/commands/cluster-reset.json b/src/commands/cluster-reset.json
index cd49900c5..6bd4fe659 100644
--- a/src/commands/cluster-reset.json
+++ b/src/commands/cluster-reset.json
@@ -1,6 +1,6 @@
{
"RESET": {
- "summary": "Reset a Redis Cluster node",
+ "summary": "Resets a node.",
"complexity": "O(N) where N is the number of known nodes. The command may execute a FLUSHALL as a side effect.",
"group": "cluster",
"since": "3.0.0",
diff --git a/src/commands/cluster-saveconfig.json b/src/commands/cluster-saveconfig.json
index 09b73db5f..d004509ab 100644
--- a/src/commands/cluster-saveconfig.json
+++ b/src/commands/cluster-saveconfig.json
@@ -1,6 +1,6 @@
{
"SAVECONFIG": {
- "summary": "Forces the node to save cluster state on disk",
+ "summary": "Forces a node to save the cluster configuration to disk.",
"complexity": "O(1)",
"group": "cluster",
"since": "3.0.0",
diff --git a/src/commands/cluster-set-config-epoch.json b/src/commands/cluster-set-config-epoch.json
index c92cc62a7..70c7de454 100644
--- a/src/commands/cluster-set-config-epoch.json
+++ b/src/commands/cluster-set-config-epoch.json
@@ -1,6 +1,6 @@
{
"SET-CONFIG-EPOCH": {
- "summary": "Set the configuration epoch in a new node",
+ "summary": "Sets the configuration epoch for a new node.",
"complexity": "O(1)",
"group": "cluster",
"since": "3.0.0",
diff --git a/src/commands/cluster-setslot.json b/src/commands/cluster-setslot.json
index 9ddff8136..d0d48193d 100644
--- a/src/commands/cluster-setslot.json
+++ b/src/commands/cluster-setslot.json
@@ -1,6 +1,6 @@
{
"SETSLOT": {
- "summary": "Bind a hash slot to a specific node",
+ "summary": "Binds a hash slot to a node.",
"complexity": "O(1)",
"group": "cluster",
"since": "3.0.0",
diff --git a/src/commands/cluster-shards.json b/src/commands/cluster-shards.json
index ac45d70eb..cfc7250b0 100644
--- a/src/commands/cluster-shards.json
+++ b/src/commands/cluster-shards.json
@@ -1,6 +1,6 @@
{
"SHARDS": {
- "summary": "Get array of cluster slots to node mappings",
+ "summary": "Returns the mapping of cluster slots to shards.",
"complexity": "O(N) where N is the total number of cluster nodes",
"group": "cluster",
"since": "7.0.0",
diff --git a/src/commands/cluster-slaves.json b/src/commands/cluster-slaves.json
index e7a0eb3be..a2e6755a0 100644
--- a/src/commands/cluster-slaves.json
+++ b/src/commands/cluster-slaves.json
@@ -1,6 +1,6 @@
{
"SLAVES": {
- "summary": "List replica nodes of the specified master node",
+ "summary": "Lists the replica nodes of a master node.",
"complexity": "O(1)",
"group": "cluster",
"since": "3.0.0",
diff --git a/src/commands/cluster-slots.json b/src/commands/cluster-slots.json
index 3f76af518..fc98ba79a 100644
--- a/src/commands/cluster-slots.json
+++ b/src/commands/cluster-slots.json
@@ -1,6 +1,6 @@
{
"SLOTS": {
- "summary": "Get array of Cluster slot to node mappings",
+ "summary": "Returns the mapping of cluster slots to nodes.",
"complexity": "O(N) where N is the total number of Cluster nodes",
"group": "cluster",
"since": "3.0.0",
diff --git a/src/commands/cluster.json b/src/commands/cluster.json
index 732a82954..e74404d8e 100644
--- a/src/commands/cluster.json
+++ b/src/commands/cluster.json
@@ -1,6 +1,6 @@
{
"CLUSTER": {
- "summary": "A container for cluster commands",
+ "summary": "A container for Redis Cluster commands.",
"complexity": "Depends on subcommand.",
"group": "cluster",
"since": "3.0.0",
diff --git a/src/commands/command-count.json b/src/commands/command-count.json
index ecc29e4af..7306767b6 100644
--- a/src/commands/command-count.json
+++ b/src/commands/command-count.json
@@ -1,6 +1,6 @@
{
"COUNT": {
- "summary": "Get total number of Redis commands",
+ "summary": "Returns a count of commands.",
"complexity": "O(1)",
"group": "server",
"since": "2.8.13",
diff --git a/src/commands/command-docs.json b/src/commands/command-docs.json
index 73ac9fd3e..75df5b4c6 100644
--- a/src/commands/command-docs.json
+++ b/src/commands/command-docs.json
@@ -1,6 +1,6 @@
{
"DOCS": {
- "summary": "Get array of specific Redis command documentation",
+ "summary": "Returns documentary information about one, multiple or all commands.",
"complexity": "O(N) where N is the number of commands to look up",
"group": "server",
"since": "7.0.0",
diff --git a/src/commands/command-getkeys.json b/src/commands/command-getkeys.json
index 92c95252d..88103c5e6 100644
--- a/src/commands/command-getkeys.json
+++ b/src/commands/command-getkeys.json
@@ -1,6 +1,6 @@
{
"GETKEYS": {
- "summary": "Extract keys given a full Redis command",
+ "summary": "Extracts the key names from an arbitrary command.",
"complexity": "O(N) where N is the number of arguments to the command",
"group": "server",
"since": "2.8.13",
diff --git a/src/commands/command-getkeysandflags.json b/src/commands/command-getkeysandflags.json
index 05668a241..a069f5e60 100644
--- a/src/commands/command-getkeysandflags.json
+++ b/src/commands/command-getkeysandflags.json
@@ -1,6 +1,6 @@
{
"GETKEYSANDFLAGS": {
- "summary": "Extract keys and access flags given a full Redis command",
+ "summary": "Extracts the key names and access flags for an arbitrary command.",
"complexity": "O(N) where N is the number of arguments to the command",
"group": "server",
"since": "7.0.0",
diff --git a/src/commands/command-help.json b/src/commands/command-help.json
index 22d31563a..5735f01d4 100644
--- a/src/commands/command-help.json
+++ b/src/commands/command-help.json
@@ -1,6 +1,6 @@
{
"HELP": {
- "summary": "Show helpful text about the different subcommands",
+ "summary": "Returns helpful text about the different subcommands.",
"complexity": "O(1)",
"group": "server",
"since": "5.0.0",
diff --git a/src/commands/command-info.json b/src/commands/command-info.json
index 84e36dfaf..8c6906095 100644
--- a/src/commands/command-info.json
+++ b/src/commands/command-info.json
@@ -1,6 +1,6 @@
{
"INFO": {
- "summary": "Get array of specific Redis command details, or all when no argument is given.",
+ "summary": "Returns information about one, multiple or all commands.",
"complexity": "O(N) where N is the number of commands to look up",
"group": "server",
"since": "2.8.13",
diff --git a/src/commands/command-list.json b/src/commands/command-list.json
index 5d6be8071..b85a9a5d7 100644
--- a/src/commands/command-list.json
+++ b/src/commands/command-list.json
@@ -1,6 +1,6 @@
{
"LIST": {
- "summary": "Get an array of Redis command names",
+ "summary": "Returns a list of command names.",
"complexity": "O(N) where N is the total number of Redis commands",
"group": "server",
"since": "7.0.0",
diff --git a/src/commands/command.json b/src/commands/command.json
index 0bed04040..9d15be244 100644
--- a/src/commands/command.json
+++ b/src/commands/command.json
@@ -1,6 +1,6 @@
{
"COMMAND": {
- "summary": "Get array of Redis command details",
+ "summary": "Returns detailed information about all commands.",
"complexity": "O(N) where N is the total number of Redis commands",
"group": "server",
"since": "2.8.13",
diff --git a/src/commands/config-get.json b/src/commands/config-get.json
index e21c64b78..53f2d8545 100644
--- a/src/commands/config-get.json
+++ b/src/commands/config-get.json
@@ -1,6 +1,6 @@
{
"GET": {
- "summary": "Get the values of configuration parameters",
+ "summary": "Returns the effective values of configuration parameters.",
"complexity": "O(N) when N is the number of configuration parameters provided",
"group": "server",
"since": "2.0.0",
diff --git a/src/commands/config-help.json b/src/commands/config-help.json
index cd90bbb59..da3e9c418 100644
--- a/src/commands/config-help.json
+++ b/src/commands/config-help.json
@@ -1,6 +1,6 @@
{
"HELP": {
- "summary": "Show helpful text about the different subcommands",
+ "summary": "Returns helpful text about the different subcommands.",
"complexity": "O(1)",
"group": "server",
"since": "5.0.0",
diff --git a/src/commands/config-resetstat.json b/src/commands/config-resetstat.json
index 353d46bd2..87a08972a 100644
--- a/src/commands/config-resetstat.json
+++ b/src/commands/config-resetstat.json
@@ -1,6 +1,6 @@
{
"RESETSTAT": {
- "summary": "Reset the stats returned by INFO",
+ "summary": "Resets the server's statistics.",
"complexity": "O(1)",
"group": "server",
"since": "2.0.0",
diff --git a/src/commands/config-rewrite.json b/src/commands/config-rewrite.json
index 3e37bf49a..490e2f8e8 100644
--- a/src/commands/config-rewrite.json
+++ b/src/commands/config-rewrite.json
@@ -1,6 +1,6 @@
{
"REWRITE": {
- "summary": "Rewrite the configuration file with the in memory configuration",
+ "summary": "Persists the effective configuration to file.",
"complexity": "O(1)",
"group": "server",
"since": "2.8.0",
diff --git a/src/commands/config-set.json b/src/commands/config-set.json
index 6e95fe0ef..83f246111 100644
--- a/src/commands/config-set.json
+++ b/src/commands/config-set.json
@@ -1,6 +1,6 @@
{
"SET": {
- "summary": "Set configuration parameters to the given values",
+ "summary": "Sets configuration parameters in-flight.",
"complexity": "O(N) when N is the number of configuration parameters provided",
"group": "server",
"since": "2.0.0",
diff --git a/src/commands/config.json b/src/commands/config.json
index a12271dcc..1c4457a54 100644
--- a/src/commands/config.json
+++ b/src/commands/config.json
@@ -1,6 +1,6 @@
{
"CONFIG": {
- "summary": "A container for server configuration commands",
+ "summary": "A container for server configuration commands.",
"complexity": "Depends on subcommand.",
"group": "server",
"since": "2.0.0",
diff --git a/src/commands/copy.json b/src/commands/copy.json
index 450b7397a..b30ddaeae 100644
--- a/src/commands/copy.json
+++ b/src/commands/copy.json
@@ -1,6 +1,6 @@
{
"COPY": {
- "summary": "Copy a key",
+ "summary": "Copies the value of a key to a new key.",
"complexity": "O(N) worst case for collections, where N is the number of nested items. O(1) for string values.",
"group": "generic",
"since": "6.2.0",
diff --git a/src/commands/dbsize.json b/src/commands/dbsize.json
index 989e37d9f..249df419a 100644
--- a/src/commands/dbsize.json
+++ b/src/commands/dbsize.json
@@ -1,6 +1,6 @@
{
"DBSIZE": {
- "summary": "Return the number of keys in the selected database",
+ "summary": "Returns the number of keys in the database.",
"complexity": "O(1)",
"group": "server",
"since": "1.0.0",
diff --git a/src/commands/debug.json b/src/commands/debug.json
index 092d2e9ab..1646d7b92 100644
--- a/src/commands/debug.json
+++ b/src/commands/debug.json
@@ -1,6 +1,6 @@
{
"DEBUG": {
- "summary": "A container for debugging commands",
+ "summary": "A container for debugging commands.",
"complexity": "Depends on subcommand.",
"group": "server",
"since": "1.0.0",
diff --git a/src/commands/decr.json b/src/commands/decr.json
index 1bae4e098..0841928a5 100644
--- a/src/commands/decr.json
+++ b/src/commands/decr.json
@@ -1,6 +1,6 @@
{
"DECR": {
- "summary": "Decrement the integer value of a key by one",
+ "summary": "Decrements the integer value of a key by one. Uses 0 as initial value if the key doesn't exist.",
"complexity": "O(1)",
"group": "string",
"since": "1.0.0",
diff --git a/src/commands/decrby.json b/src/commands/decrby.json
index de5724c3a..3db98791a 100644
--- a/src/commands/decrby.json
+++ b/src/commands/decrby.json
@@ -1,6 +1,6 @@
{
"DECRBY": {
- "summary": "Decrement the integer value of a key by the given number",
+ "summary": "Decrements a number from the integer value of a key. Uses 0 as initial value if the key doesn't exist.",
"complexity": "O(1)",
"group": "string",
"since": "1.0.0",
diff --git a/src/commands/del.json b/src/commands/del.json
index 6d2ff7209..2727b536c 100644
--- a/src/commands/del.json
+++ b/src/commands/del.json
@@ -1,6 +1,6 @@
{
"DEL": {
- "summary": "Delete a key",
+ "summary": "Deletes one or more keys.",
"complexity": "O(N) where N is the number of keys that will be removed. When a key to remove holds a value other than a string, the individual complexity for this key is O(M) where M is the number of elements in the list, set, sorted set or hash. Removing a single key that holds a string value is O(1).",
"group": "generic",
"since": "1.0.0",
diff --git a/src/commands/discard.json b/src/commands/discard.json
index 6ef0dd809..ffd37f05e 100644
--- a/src/commands/discard.json
+++ b/src/commands/discard.json
@@ -1,6 +1,6 @@
{
"DISCARD": {
- "summary": "Discard all commands issued after MULTI",
+ "summary": "Discards a transaction.",
"complexity": "O(N), when N is the number of queued commands",
"group": "transactions",
"since": "2.0.0",
diff --git a/src/commands/dump.json b/src/commands/dump.json
index c0f0aa0ea..9e772edd1 100644
--- a/src/commands/dump.json
+++ b/src/commands/dump.json
@@ -1,6 +1,6 @@
{
"DUMP": {
- "summary": "Return a serialized version of the value stored at the specified key.",
+ "summary": "Returns a serialized representation of the value stored at a key.",
"complexity": "O(1) to access the key and additional O(N*M) to serialize it, where N is the number of Redis objects composing the value and M their average size. For small string values the time complexity is thus O(1)+O(1*M) where M is small, so simply O(1).",
"group": "generic",
"since": "2.6.0",
diff --git a/src/commands/echo.json b/src/commands/echo.json
index abb3d26f3..df8719825 100644
--- a/src/commands/echo.json
+++ b/src/commands/echo.json
@@ -1,6 +1,6 @@
{
"ECHO": {
- "summary": "Echo the given string",
+ "summary": "Returns the given string.",
"complexity": "O(1)",
"group": "connection",
"since": "1.0.0",
diff --git a/src/commands/eval.json b/src/commands/eval.json
index 4f25b2fe3..05e1cb5bf 100644
--- a/src/commands/eval.json
+++ b/src/commands/eval.json
@@ -1,6 +1,6 @@
{
"EVAL": {
- "summary": "Execute a Lua script server side",
+ "summary": "Executes a server-side Lua script.",
"complexity": "Depends on the script that is executed.",
"group": "scripting",
"since": "2.6.0",
diff --git a/src/commands/eval_ro.json b/src/commands/eval_ro.json
index f9aa54942..1440998ae 100644
--- a/src/commands/eval_ro.json
+++ b/src/commands/eval_ro.json
@@ -1,6 +1,6 @@
{
"EVAL_RO": {
- "summary": "Execute a read-only Lua script server side",
+ "summary": "Executes a read-only server-side Lua script.",
"complexity": "Depends on the script that is executed.",
"group": "scripting",
"since": "7.0.0",
diff --git a/src/commands/evalsha.json b/src/commands/evalsha.json
index 66e4379c6..c7d0aa79b 100644
--- a/src/commands/evalsha.json
+++ b/src/commands/evalsha.json
@@ -1,6 +1,6 @@
{
"EVALSHA": {
- "summary": "Execute a Lua script server side",
+ "summary": "Executes a server-side Lua script by SHA1 digest.",
"complexity": "Depends on the script that is executed.",
"group": "scripting",
"since": "2.6.0",
diff --git a/src/commands/evalsha_ro.json b/src/commands/evalsha_ro.json
index 8dca0964b..51e4dabf9 100644
--- a/src/commands/evalsha_ro.json
+++ b/src/commands/evalsha_ro.json
@@ -1,6 +1,6 @@
{
"EVALSHA_RO": {
- "summary": "Execute a read-only Lua script server side",
+ "summary": "Executes a read-only server-side Lua script by SHA1 digest.",
"complexity": "Depends on the script that is executed.",
"group": "scripting",
"since": "7.0.0",
diff --git a/src/commands/exec.json b/src/commands/exec.json
index b5ec6f0ab..5f03d76e0 100644
--- a/src/commands/exec.json
+++ b/src/commands/exec.json
@@ -1,6 +1,6 @@
{
"EXEC": {
- "summary": "Execute all commands issued after MULTI",
+ "summary": "Executes all commands in a transaction.",
"complexity": "Depends on commands in the transaction",
"group": "transactions",
"since": "1.2.0",
diff --git a/src/commands/exists.json b/src/commands/exists.json
index e8793cf2b..8b108bca2 100644
--- a/src/commands/exists.json
+++ b/src/commands/exists.json
@@ -1,6 +1,6 @@
{
"EXISTS": {
- "summary": "Determine if a key exists",
+ "summary": "Determines whether one or more keys exist.",
"complexity": "O(N) where N is the number of keys to check.",
"group": "generic",
"since": "1.0.0",
diff --git a/src/commands/expire.json b/src/commands/expire.json
index f0236f1d5..bf80939e9 100644
--- a/src/commands/expire.json
+++ b/src/commands/expire.json
@@ -1,6 +1,6 @@
{
"EXPIRE": {
- "summary": "Set a key's time to live in seconds",
+ "summary": "Sets the expiration time of a key in seconds.",
"complexity": "O(1)",
"group": "generic",
"since": "1.0.0",
diff --git a/src/commands/expireat.json b/src/commands/expireat.json
index a6e22754b..c2ba5d8c3 100644
--- a/src/commands/expireat.json
+++ b/src/commands/expireat.json
@@ -1,6 +1,6 @@
{
"EXPIREAT": {
- "summary": "Set the expiration for a key as a UNIX timestamp",
+ "summary": "Sets the expiration time of a key to a Unix timestamp.",
"complexity": "O(1)",
"group": "generic",
"since": "1.2.0",
diff --git a/src/commands/expiretime.json b/src/commands/expiretime.json
index 90d8525ad..99da9d26c 100644
--- a/src/commands/expiretime.json
+++ b/src/commands/expiretime.json
@@ -1,6 +1,6 @@
{
"EXPIRETIME": {
- "summary": "Get the expiration Unix timestamp for a key",
+ "summary": "Returns the expiration time of a key as a Unix timestamp.",
"complexity": "O(1)",
"group": "generic",
"since": "7.0.0",
diff --git a/src/commands/failover.json b/src/commands/failover.json
index d208bfcf1..c6948629a 100644
--- a/src/commands/failover.json
+++ b/src/commands/failover.json
@@ -1,6 +1,6 @@
{
"FAILOVER": {
- "summary": "Start a coordinated failover between this server and one of its replicas.",
+ "summary": "Starts a coordinated failover from a server to one of its replicas.",
"complexity": "O(1)",
"group": "server",
"since": "6.2.0",
diff --git a/src/commands/fcall.json b/src/commands/fcall.json
index 5817ed7c9..7d6be691a 100644
--- a/src/commands/fcall.json
+++ b/src/commands/fcall.json
@@ -1,6 +1,6 @@
{
"FCALL": {
- "summary": "Invoke a function",
+ "summary": "Invokes a function.",
"complexity": "Depends on the function that is executed.",
"group": "scripting",
"since": "7.0.0",
diff --git a/src/commands/fcall_ro.json b/src/commands/fcall_ro.json
index def485ffa..d0e4c7619 100644
--- a/src/commands/fcall_ro.json
+++ b/src/commands/fcall_ro.json
@@ -1,6 +1,6 @@
{
"FCALL_RO": {
- "summary": "Invoke a read-only function",
+ "summary": "Invokes a read-only function.",
"complexity": "Depends on the function that is executed.",
"group": "scripting",
"since": "7.0.0",
diff --git a/src/commands/flushall.json b/src/commands/flushall.json
index dc5c7ca39..304bd03f9 100644
--- a/src/commands/flushall.json
+++ b/src/commands/flushall.json
@@ -1,6 +1,6 @@
{
"FLUSHALL": {
- "summary": "Remove all keys from all databases",
+ "summary": "Removes all keys from all databases.",
"complexity": "O(N) where N is the total number of keys in all databases",
"group": "server",
"since": "1.0.0",
diff --git a/src/commands/flushdb.json b/src/commands/flushdb.json
index 1590221eb..f1d10b8ad 100644
--- a/src/commands/flushdb.json
+++ b/src/commands/flushdb.json
@@ -1,6 +1,6 @@
{
"FLUSHDB": {
- "summary": "Remove all keys from the current database",
+ "summary": "Remove all keys from the current database.",
"complexity": "O(N) where N is the number of keys in the selected database",
"group": "server",
"since": "1.0.0",
diff --git a/src/commands/function-delete.json b/src/commands/function-delete.json
index c56a0fbe6..0895457c0 100644
--- a/src/commands/function-delete.json
+++ b/src/commands/function-delete.json
@@ -1,6 +1,6 @@
{
"DELETE": {
- "summary": "Delete a function by name",
+ "summary": "Deletes a library and its functions.",
"complexity": "O(1)",
"group": "scripting",
"since": "7.0.0",
diff --git a/src/commands/function-dump.json b/src/commands/function-dump.json
index b535b28ad..d117241de 100644
--- a/src/commands/function-dump.json
+++ b/src/commands/function-dump.json
@@ -1,6 +1,6 @@
{
"DUMP": {
- "summary": "Dump all functions into a serialized binary payload",
+ "summary": "Dumps all libraries into a serialized binary payload.",
"complexity": "O(N) where N is the number of functions",
"group": "scripting",
"since": "7.0.0",
diff --git a/src/commands/function-flush.json b/src/commands/function-flush.json
index 538074878..58742a0af 100644
--- a/src/commands/function-flush.json
+++ b/src/commands/function-flush.json
@@ -1,6 +1,6 @@
{
"FLUSH": {
- "summary": "Deleting all functions",
+ "summary": "Deletes all libraries and functions.",
"complexity": "O(N) where N is the number of functions deleted",
"group": "scripting",
"since": "7.0.0",
diff --git a/src/commands/function-help.json b/src/commands/function-help.json
index 37a3826df..662e7e6f3 100644
--- a/src/commands/function-help.json
+++ b/src/commands/function-help.json
@@ -1,6 +1,6 @@
{
"HELP": {
- "summary": "Show helpful text about the different subcommands",
+ "summary": "Returns helpful text about the different subcommands.",
"complexity": "O(1)",
"group": "scripting",
"since": "7.0.0",
diff --git a/src/commands/function-kill.json b/src/commands/function-kill.json
index 2f9ae833e..396370c38 100644
--- a/src/commands/function-kill.json
+++ b/src/commands/function-kill.json
@@ -1,6 +1,6 @@
{
"KILL": {
- "summary": "Kill the function currently in execution.",
+ "summary": "Terminates a function during execution.",
"complexity": "O(1)",
"group": "scripting",
"since": "7.0.0",
diff --git a/src/commands/function-list.json b/src/commands/function-list.json
index 89c5162e7..2ab1cf510 100644
--- a/src/commands/function-list.json
+++ b/src/commands/function-list.json
@@ -1,6 +1,6 @@
{
"LIST": {
- "summary": "List information about all the functions",
+ "summary": "Returns information about all libraries.",
"complexity": "O(N) where N is the number of functions",
"group": "scripting",
"since": "7.0.0",
diff --git a/src/commands/function-load.json b/src/commands/function-load.json
index 28f5b2084..f918b6599 100644
--- a/src/commands/function-load.json
+++ b/src/commands/function-load.json
@@ -1,6 +1,6 @@
{
"LOAD": {
- "summary": "Create a function with the given arguments (name, code, description)",
+ "summary": "Creates a library.",
"complexity": "O(1) (considering compilation time is redundant)",
"group": "scripting",
"since": "7.0.0",
diff --git a/src/commands/function-restore.json b/src/commands/function-restore.json
index 900602577..0c37004b8 100644
--- a/src/commands/function-restore.json
+++ b/src/commands/function-restore.json
@@ -1,6 +1,6 @@
{
"RESTORE": {
- "summary": "Restore all the functions on the given payload",
+ "summary": "Restores all libraries from a payload.",
"complexity": "O(N) where N is the number of functions on the payload",
"group": "scripting",
"since": "7.0.0",
diff --git a/src/commands/function-stats.json b/src/commands/function-stats.json
index 8a64b9246..65519db1c 100644
--- a/src/commands/function-stats.json
+++ b/src/commands/function-stats.json
@@ -1,6 +1,6 @@
{
"STATS": {
- "summary": "Return information about the function currently running (name, description, duration)",
+ "summary": "Returns information about a function during execution.",
"complexity": "O(1)",
"group": "scripting",
"since": "7.0.0",
diff --git a/src/commands/function.json b/src/commands/function.json
index 3d33345cf..c474137a6 100644
--- a/src/commands/function.json
+++ b/src/commands/function.json
@@ -1,6 +1,6 @@
{
"FUNCTION": {
- "summary": "A container for function commands",
+ "summary": "A container for function commands.",
"complexity": "Depends on subcommand.",
"group": "scripting",
"since": "7.0.0",
diff --git a/src/commands/geoadd.json b/src/commands/geoadd.json
index bd9c40ebc..5409bfcdb 100644
--- a/src/commands/geoadd.json
+++ b/src/commands/geoadd.json
@@ -1,6 +1,6 @@
{
"GEOADD": {
- "summary": "Add one or more geospatial items in the geospatial index represented using a sorted set",
+ "summary": "Adds one or more members to a geospatial index. The key is created if it doesn't exist.",
"complexity": "O(log(N)) for each item added, where N is the number of elements in the sorted set.",
"group": "geo",
"since": "3.2.0",
diff --git a/src/commands/geodist.json b/src/commands/geodist.json
index 61f281a1b..97969d332 100644
--- a/src/commands/geodist.json
+++ b/src/commands/geodist.json
@@ -1,6 +1,6 @@
{
"GEODIST": {
- "summary": "Returns the distance between two members of a geospatial index",
+ "summary": "Returns the distance between two members of a geospatial index.",
"complexity": "O(log(N))",
"group": "geo",
"since": "3.2.0",
diff --git a/src/commands/geohash.json b/src/commands/geohash.json
index 0db62f315..8f4d55a62 100644
--- a/src/commands/geohash.json
+++ b/src/commands/geohash.json
@@ -1,6 +1,6 @@
{
"GEOHASH": {
- "summary": "Returns members of a geospatial index as standard geohash strings",
+ "summary": "Returns members from a geospatial index as geohash strings.",
"complexity": "O(log(N)) for each member requested, where N is the number of elements in the sorted set.",
"group": "geo",
"since": "3.2.0",
diff --git a/src/commands/geopos.json b/src/commands/geopos.json
index 64635fa7f..5473c1b76 100644
--- a/src/commands/geopos.json
+++ b/src/commands/geopos.json
@@ -1,6 +1,6 @@
{
"GEOPOS": {
- "summary": "Returns longitude and latitude of members of a geospatial index",
+ "summary": "Returns the longitude and latitude of members from a geospatial index.",
"complexity": "O(N) where N is the number of members requested.",
"group": "geo",
"since": "3.2.0",
diff --git a/src/commands/georadius.json b/src/commands/georadius.json
index 3c475784b..6ced9049c 100644
--- a/src/commands/georadius.json
+++ b/src/commands/georadius.json
@@ -1,6 +1,6 @@
{
"GEORADIUS": {
- "summary": "Query a sorted set representing a geospatial index to fetch members matching a given maximum distance from a point",
+ "summary": "Queries a geospatial index for members within a distance from a coordinate, optionally stores the result.",
"complexity": "O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.",
"group": "geo",
"since": "3.2.0",
@@ -186,20 +186,25 @@
]
},
{
- "token": "STORE",
- "name": "storekey",
- "display": "key",
- "type": "key",
- "key_spec_index": 1,
- "optional": true
- },
- {
- "token": "STOREDIST",
- "name": "storedistkey",
- "display": "key",
- "type": "key",
- "key_spec_index": 2,
- "optional": true
+ "name": "store",
+ "type": "oneof",
+ "optional": true,
+ "arguments": [
+ {
+ "token": "STORE",
+ "name": "storekey",
+ "display": "key",
+ "type": "key",
+ "key_spec_index": 1
+ },
+ {
+ "token": "STOREDIST",
+ "name": "storedistkey",
+ "display": "key",
+ "type": "key",
+ "key_spec_index": 2
+ }
+ ]
}
],
"reply_schema": {
diff --git a/src/commands/georadius_ro.json b/src/commands/georadius_ro.json
index 5b0b2a5e0..964246a20 100644
--- a/src/commands/georadius_ro.json
+++ b/src/commands/georadius_ro.json
@@ -1,6 +1,6 @@
{
"GEORADIUS_RO": {
- "summary": "A read-only variant for GEORADIUS",
+ "summary": "Returns members from a geospatial index that are within a distance from a coordinate.",
"complexity": "O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.",
"group": "geo",
"since": "3.2.10",
diff --git a/src/commands/georadiusbymember.json b/src/commands/georadiusbymember.json
index f2db041d1..4b627419b 100644
--- a/src/commands/georadiusbymember.json
+++ b/src/commands/georadiusbymember.json
@@ -1,6 +1,6 @@
{
"GEORADIUSBYMEMBER": {
- "summary": "Query a sorted set representing a geospatial index to fetch members matching a given maximum distance from a member",
+ "summary": "Queries a geospatial index for members within a distance from a member, optionally stores the result.",
"complexity": "O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.",
"group": "geo",
"since": "3.2.0",
@@ -177,20 +177,25 @@
]
},
{
- "token": "STORE",
- "name": "storekey",
- "display": "key",
- "type": "key",
- "key_spec_index": 1,
- "optional": true
- },
- {
- "token": "STOREDIST",
- "name": "storedistkey",
- "display": "key",
- "type": "key",
- "key_spec_index": 2,
- "optional": true
+ "name": "store",
+ "type": "oneof",
+ "optional": true,
+ "arguments": [
+ {
+ "token": "STORE",
+ "name": "storekey",
+ "display": "key",
+ "type": "key",
+ "key_spec_index": 1
+ },
+ {
+ "token": "STOREDIST",
+ "name": "storedistkey",
+ "display": "key",
+ "type": "key",
+ "key_spec_index": 2
+ }
+ ]
}
],
"reply_schema": {
diff --git a/src/commands/georadiusbymember_ro.json b/src/commands/georadiusbymember_ro.json
index fa35d529b..592588191 100644
--- a/src/commands/georadiusbymember_ro.json
+++ b/src/commands/georadiusbymember_ro.json
@@ -1,6 +1,6 @@
{
"GEORADIUSBYMEMBER_RO": {
- "summary": "A read-only variant for GEORADIUSBYMEMBER",
+ "summary": "Returns members from a geospatial index that are within a distance from a member.",
"complexity": "O(N+log(M)) where N is the number of elements inside the bounding box of the circular area delimited by center and radius and M is the number of items inside the index.",
"group": "geo",
"since": "3.2.10",
diff --git a/src/commands/geosearch.json b/src/commands/geosearch.json
index 88c6bf287..b2e2825d9 100644
--- a/src/commands/geosearch.json
+++ b/src/commands/geosearch.json
@@ -1,6 +1,6 @@
{
"GEOSEARCH": {
- "summary": "Query a sorted set representing a geospatial index to fetch members inside an area of a box or a circle.",
+ "summary": "Queries a geospatial index for members inside an area of a box or a circle.",
"complexity": "O(N+log(M)) where N is the number of elements in the grid-aligned bounding box area around the shape provided as the filter and M is the number of items inside the shape",
"group": "geo",
"since": "6.2.0",
diff --git a/src/commands/geosearchstore.json b/src/commands/geosearchstore.json
index abb7ba152..dfbdaaaec 100644
--- a/src/commands/geosearchstore.json
+++ b/src/commands/geosearchstore.json
@@ -1,6 +1,6 @@
{
"GEOSEARCHSTORE": {
- "summary": "Query a sorted set representing a geospatial index to fetch members inside an area of a box or a circle, and store the result in another key.",
+ "summary": "Queries a geospatial index for members inside an area of a box or a circle, optionally stores the result.",
"complexity": "O(N+log(M)) where N is the number of elements in the grid-aligned bounding box area around the shape provided as the filter and M is the number of items inside the shape",
"group": "geo",
"since": "6.2.0",
diff --git a/src/commands/get.json b/src/commands/get.json
index 0de9b164c..693c1ac82 100644
--- a/src/commands/get.json
+++ b/src/commands/get.json
@@ -1,6 +1,6 @@
{
"GET": {
- "summary": "Get the value of a key",
+ "summary": "Returns the string value of a key.",
"complexity": "O(1)",
"group": "string",
"since": "1.0.0",
diff --git a/src/commands/getbit.json b/src/commands/getbit.json
index 773445f63..629f5dbdd 100644
--- a/src/commands/getbit.json
+++ b/src/commands/getbit.json
@@ -1,6 +1,6 @@
{
"GETBIT": {
- "summary": "Returns the bit value at offset in the string value stored at key",
+ "summary": "Returns a bit value by offset.",
"complexity": "O(1)",
"group": "bitmap",
"since": "2.2.0",
diff --git a/src/commands/getdel.json b/src/commands/getdel.json
index 17e1664bb..f3d86b0ec 100644
--- a/src/commands/getdel.json
+++ b/src/commands/getdel.json
@@ -1,6 +1,6 @@
{
"GETDEL": {
- "summary": "Get the value of a key and delete the key",
+ "summary": "Returns the string value of a key after deleting the key.",
"complexity": "O(1)",
"group": "string",
"since": "6.2.0",
diff --git a/src/commands/getex.json b/src/commands/getex.json
index cb70eac8f..978b9d10a 100644
--- a/src/commands/getex.json
+++ b/src/commands/getex.json
@@ -1,6 +1,6 @@
{
"GETEX": {
- "summary": "Get the value of a key and optionally set its expiration",
+ "summary": "Returns the string value of a key after setting its expiration time.",
"complexity": "O(1)",
"group": "string",
"since": "6.2.0",
diff --git a/src/commands/getrange.json b/src/commands/getrange.json
index f54836535..82bb72326 100644
--- a/src/commands/getrange.json
+++ b/src/commands/getrange.json
@@ -1,6 +1,6 @@
{
"GETRANGE": {
- "summary": "Get a substring of the string stored at a key",
+ "summary": "Returns a substring of the string stored at a key.",
"complexity": "O(N) where N is the length of the returned string. The complexity is ultimately determined by the returned length, but because creating a substring from an existing string is very cheap, it can be considered O(1) for small strings.",
"group": "string",
"since": "2.4.0",
diff --git a/src/commands/getset.json b/src/commands/getset.json
index 78629ff91..42823d5ab 100644
--- a/src/commands/getset.json
+++ b/src/commands/getset.json
@@ -1,6 +1,6 @@
{
"GETSET": {
- "summary": "Set the string value of a key and return its old value",
+ "summary": "Returns the previous string value of a key after setting it to a new value.",
"complexity": "O(1)",
"group": "string",
"since": "1.0.0",
diff --git a/src/commands/hdel.json b/src/commands/hdel.json
index b5d63476b..11da02d3b 100644
--- a/src/commands/hdel.json
+++ b/src/commands/hdel.json
@@ -1,6 +1,6 @@
{
"HDEL": {
- "summary": "Delete one or more hash fields",
+ "summary": "Deletes one or more fields and their values from a hash. Deletes the hash if no fields remain.",
"complexity": "O(N) where N is the number of fields to be removed.",
"group": "hash",
"since": "2.0.0",
diff --git a/src/commands/hello.json b/src/commands/hello.json
index 7f5d6f202..e916e7255 100644
--- a/src/commands/hello.json
+++ b/src/commands/hello.json
@@ -1,6 +1,6 @@
{
"HELLO": {
- "summary": "Handshake with Redis",
+ "summary": "Handshakes with the Redis server.",
"complexity": "O(1)",
"group": "connection",
"since": "6.0.0",
diff --git a/src/commands/hexists.json b/src/commands/hexists.json
index 1fc1b6565..f5ea40571 100644
--- a/src/commands/hexists.json
+++ b/src/commands/hexists.json
@@ -1,6 +1,6 @@
{
"HEXISTS": {
- "summary": "Determine if a hash field exists",
+ "summary": "Determines whether a field exists in a hash.",
"complexity": "O(1)",
"group": "hash",
"since": "2.0.0",
diff --git a/src/commands/hget.json b/src/commands/hget.json
index 101a38ab4..a041143ec 100644
--- a/src/commands/hget.json
+++ b/src/commands/hget.json
@@ -1,6 +1,6 @@
{
"HGET": {
- "summary": "Get the value of a hash field",
+ "summary": "Returns the value of a field in a hash.",
"complexity": "O(1)",
"group": "hash",
"since": "2.0.0",
diff --git a/src/commands/hgetall.json b/src/commands/hgetall.json
index 03373ae77..9bbf835a3 100644
--- a/src/commands/hgetall.json
+++ b/src/commands/hgetall.json
@@ -1,6 +1,6 @@
{
"HGETALL": {
- "summary": "Get all the fields and values in a hash",
+ "summary": "Returns all fields and values in a hash.",
"complexity": "O(N) where N is the size of the hash.",
"group": "hash",
"since": "2.0.0",
diff --git a/src/commands/hincrby.json b/src/commands/hincrby.json
index bd9b0ea4c..a90f5baf0 100644
--- a/src/commands/hincrby.json
+++ b/src/commands/hincrby.json
@@ -1,6 +1,6 @@
{
"HINCRBY": {
- "summary": "Increment the integer value of a hash field by the given number",
+ "summary": "Increments the integer value of a field in a hash by a number. Uses 0 as initial value if the field doesn't exist.",
"complexity": "O(1)",
"group": "hash",
"since": "2.0.0",
diff --git a/src/commands/hincrbyfloat.json b/src/commands/hincrbyfloat.json
index bd34db5e1..6a7d1fdcb 100644
--- a/src/commands/hincrbyfloat.json
+++ b/src/commands/hincrbyfloat.json
@@ -1,6 +1,6 @@
{
"HINCRBYFLOAT": {
- "summary": "Increment the float value of a hash field by the given amount",
+ "summary": "Increments the floating point value of a field by a number. Uses 0 as initial value if the field doesn't exist.",
"complexity": "O(1)",
"group": "hash",
"since": "2.6.0",
diff --git a/src/commands/hkeys.json b/src/commands/hkeys.json
index c6a4d1fcf..917df1c9e 100644
--- a/src/commands/hkeys.json
+++ b/src/commands/hkeys.json
@@ -1,6 +1,6 @@
{
"HKEYS": {
- "summary": "Get all the fields in a hash",
+ "summary": "Returns all fields in a hash.",
"complexity": "O(N) where N is the size of the hash.",
"group": "hash",
"since": "2.0.0",
diff --git a/src/commands/hlen.json b/src/commands/hlen.json
index 1b476ab74..d4c13ac11 100644
--- a/src/commands/hlen.json
+++ b/src/commands/hlen.json
@@ -1,6 +1,6 @@
{
"HLEN": {
- "summary": "Get the number of fields in a hash",
+ "summary": "Returns the number of fields in a hash.",
"complexity": "O(1)",
"group": "hash",
"since": "2.0.0",
diff --git a/src/commands/hmget.json b/src/commands/hmget.json
index 7a31598a7..73fa9c311 100644
--- a/src/commands/hmget.json
+++ b/src/commands/hmget.json
@@ -1,6 +1,6 @@
{
"HMGET": {
- "summary": "Get the values of all the given hash fields",
+ "summary": "Returns the values of all fields in a hash.",
"complexity": "O(N) where N is the number of fields being requested.",
"group": "hash",
"since": "2.0.0",
diff --git a/src/commands/hmset.json b/src/commands/hmset.json
index c498b9086..e92f411e2 100644
--- a/src/commands/hmset.json
+++ b/src/commands/hmset.json
@@ -1,6 +1,6 @@
{
"HMSET": {
- "summary": "Set multiple hash fields to multiple values",
+ "summary": "Sets the values of multiple fields.",
"complexity": "O(N) where N is the number of fields being set.",
"group": "hash",
"since": "2.0.0",
diff --git a/src/commands/hrandfield.json b/src/commands/hrandfield.json
index c821d4508..83abc74a9 100644
--- a/src/commands/hrandfield.json
+++ b/src/commands/hrandfield.json
@@ -1,6 +1,6 @@
{
"HRANDFIELD": {
- "summary": "Get one or multiple random fields from a hash",
+ "summary": "Returns one or more random fields from a hash.",
"complexity": "O(N) where N is the number of fields returned",
"group": "hash",
"since": "6.2.0",
diff --git a/src/commands/hscan.json b/src/commands/hscan.json
index 478bda447..0888eec9f 100644
--- a/src/commands/hscan.json
+++ b/src/commands/hscan.json
@@ -1,6 +1,6 @@
{
"HSCAN": {
- "summary": "Incrementally iterate hash fields and associated values",
+ "summary": "Iterates over fields and values of a hash.",
"complexity": "O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.",
"group": "hash",
"since": "2.8.0",
diff --git a/src/commands/hset.json b/src/commands/hset.json
index 27762afea..818097227 100644
--- a/src/commands/hset.json
+++ b/src/commands/hset.json
@@ -1,6 +1,6 @@
{
"HSET": {
- "summary": "Set the string value of a hash field",
+ "summary": "Creates or modifies the value of a field in a hash.",
"complexity": "O(1) for each field/value pair added, so O(N) to add N field/value pairs when the command is called with multiple field/value pairs.",
"group": "hash",
"since": "2.0.0",
@@ -41,6 +41,7 @@
}
],
"reply_schema": {
+ "description": "The number of fields that were added",
"type": "integer"
},
"arguments": [
diff --git a/src/commands/hsetnx.json b/src/commands/hsetnx.json
index 53cd9c9d0..e024c412a 100644
--- a/src/commands/hsetnx.json
+++ b/src/commands/hsetnx.json
@@ -1,6 +1,6 @@
{
"HSETNX": {
- "summary": "Set the value of a hash field, only if the field does not exist",
+ "summary": "Sets the value of a field in a hash only when the field doesn't exist.",
"complexity": "O(1)",
"group": "hash",
"since": "2.0.0",
diff --git a/src/commands/hstrlen.json b/src/commands/hstrlen.json
index 5d2c6b1a9..82ac6dbe4 100644
--- a/src/commands/hstrlen.json
+++ b/src/commands/hstrlen.json
@@ -1,6 +1,6 @@
{
"HSTRLEN": {
- "summary": "Get the length of the value of a hash field",
+ "summary": "Returns the length of the value of a field.",
"complexity": "O(1)",
"group": "hash",
"since": "3.2.0",
diff --git a/src/commands/hvals.json b/src/commands/hvals.json
index 6118bcff4..55aeaaff9 100644
--- a/src/commands/hvals.json
+++ b/src/commands/hvals.json
@@ -1,6 +1,6 @@
{
"HVALS": {
- "summary": "Get all the values in a hash",
+ "summary": "Returns all values in a hash.",
"complexity": "O(N) where N is the size of the hash.",
"group": "hash",
"since": "2.0.0",
diff --git a/src/commands/incr.json b/src/commands/incr.json
index d6c1bd2cd..f33ec02b4 100644
--- a/src/commands/incr.json
+++ b/src/commands/incr.json
@@ -1,6 +1,6 @@
{
"INCR": {
- "summary": "Increment the integer value of a key by one",
+ "summary": "Increments the integer value of a key by one. Uses 0 as initial value if the key doesn't exist.",
"complexity": "O(1)",
"group": "string",
"since": "1.0.0",
diff --git a/src/commands/incrby.json b/src/commands/incrby.json
index 0febdd56e..2668011ac 100644
--- a/src/commands/incrby.json
+++ b/src/commands/incrby.json
@@ -1,6 +1,6 @@
{
"INCRBY": {
- "summary": "Increment the integer value of a key by the given amount",
+ "summary": "Increments the integer value of a key by a number. Uses 0 as initial value if the key doesn't exist.",
"complexity": "O(1)",
"group": "string",
"since": "1.0.0",
diff --git a/src/commands/incrbyfloat.json b/src/commands/incrbyfloat.json
index 0f2440024..c594a1afe 100644
--- a/src/commands/incrbyfloat.json
+++ b/src/commands/incrbyfloat.json
@@ -1,6 +1,6 @@
{
"INCRBYFLOAT": {
- "summary": "Increment the float value of a key by the given amount",
+ "summary": "Increment the floating point value of a key by a number. Uses 0 as initial value if the key doesn't exist.",
"complexity": "O(1)",
"group": "string",
"since": "2.6.0",
diff --git a/src/commands/info.json b/src/commands/info.json
index b44e0c01f..04a02b1b7 100644
--- a/src/commands/info.json
+++ b/src/commands/info.json
@@ -1,6 +1,6 @@
{
"INFO": {
- "summary": "Get information and statistics about the server",
+ "summary": "Returns information and statistics about the server.",
"complexity": "O(1)",
"group": "server",
"since": "1.0.0",
@@ -26,6 +26,7 @@
"RESPONSE_POLICY:SPECIAL"
],
"reply_schema": {
+ "description": "A map of info fields, one field per line in the form of <field>:<value> where the value can be a comma separated map like <key>=<val>. Also contains section header lines starting with `#` and blank lines.",
"type": "string"
},
"arguments": [
diff --git a/src/commands/keys.json b/src/commands/keys.json
index cedf3ce7f..9dd4e1101 100644
--- a/src/commands/keys.json
+++ b/src/commands/keys.json
@@ -1,6 +1,6 @@
{
"KEYS": {
- "summary": "Find all keys matching the given pattern",
+ "summary": "Returns all key names that match a pattern.",
"complexity": "O(N) with N being the number of keys in the database, under the assumption that the key names in the database and the given pattern have limited length.",
"group": "generic",
"since": "1.0.0",
diff --git a/src/commands/lastsave.json b/src/commands/lastsave.json
index 8988e75d7..dc0615425 100644
--- a/src/commands/lastsave.json
+++ b/src/commands/lastsave.json
@@ -1,6 +1,6 @@
{
"LASTSAVE": {
- "summary": "Get the UNIX time stamp of the last successful save to disk",
+ "summary": "Returns the Unix timestamp of the last successful save to disk.",
"complexity": "O(1)",
"group": "server",
"since": "1.0.0",
diff --git a/src/commands/latency-doctor.json b/src/commands/latency-doctor.json
index 8d3a98b3c..8f1f8dd37 100644
--- a/src/commands/latency-doctor.json
+++ b/src/commands/latency-doctor.json
@@ -1,6 +1,6 @@
{
"DOCTOR": {
- "summary": "Return a human readable latency analysis report.",
+ "summary": "Returns a human-readable latency analysis report.",
"complexity": "O(1)",
"group": "server",
"since": "2.8.13",
diff --git a/src/commands/latency-graph.json b/src/commands/latency-graph.json
index d634da746..cb5d209ea 100644
--- a/src/commands/latency-graph.json
+++ b/src/commands/latency-graph.json
@@ -1,6 +1,6 @@
{
"GRAPH": {
- "summary": "Return a latency graph for the event.",
+ "summary": "Returns a latency graph for an event.",
"complexity": "O(1)",
"group": "server",
"since": "2.8.13",
diff --git a/src/commands/latency-help.json b/src/commands/latency-help.json
index e91679eb7..36ff52766 100644
--- a/src/commands/latency-help.json
+++ b/src/commands/latency-help.json
@@ -1,6 +1,6 @@
{
"HELP": {
- "summary": "Show helpful text about the different subcommands.",
+ "summary": "Returns helpful text about the different subcommands.",
"complexity": "O(1)",
"group": "server",
"since": "2.8.13",
diff --git a/src/commands/latency-histogram.json b/src/commands/latency-histogram.json
index d9821ea85..5e33eb60d 100644
--- a/src/commands/latency-histogram.json
+++ b/src/commands/latency-histogram.json
@@ -1,6 +1,6 @@
{
"HISTOGRAM": {
- "summary": "Return the cumulative distribution of latencies of a subset of commands or all.",
+ "summary": "Returns the cumulative distribution of latencies of a subset or all commands.",
"complexity": "O(N) where N is the number of commands with latency information being retrieved.",
"group": "server",
"since": "7.0.0",
diff --git a/src/commands/latency-history.json b/src/commands/latency-history.json
index 11fa8857b..7c3591aa2 100644
--- a/src/commands/latency-history.json
+++ b/src/commands/latency-history.json
@@ -1,6 +1,6 @@
{
"HISTORY": {
- "summary": "Return timestamp-latency samples for the event.",
+ "summary": "Returns timestamp-latency samples for an event.",
"complexity": "O(1)",
"group": "server",
"since": "2.8.13",
diff --git a/src/commands/latency-latest.json b/src/commands/latency-latest.json
index 7e4d42317..88c9e7a8c 100644
--- a/src/commands/latency-latest.json
+++ b/src/commands/latency-latest.json
@@ -1,6 +1,6 @@
{
"LATEST": {
- "summary": "Return the latest latency samples for all events.",
+ "summary": "Returns the latest latency samples for all events.",
"complexity": "O(1)",
"group": "server",
"since": "2.8.13",
diff --git a/src/commands/latency-reset.json b/src/commands/latency-reset.json
index 354e3fe74..d4891da5e 100644
--- a/src/commands/latency-reset.json
+++ b/src/commands/latency-reset.json
@@ -1,6 +1,6 @@
{
"RESET": {
- "summary": "Reset latency data for one or more events.",
+ "summary": "Resets the latency data for one or more events.",
"complexity": "O(1)",
"group": "server",
"since": "2.8.13",
diff --git a/src/commands/latency.json b/src/commands/latency.json
index 1845fd4ff..e4844d4d7 100644
--- a/src/commands/latency.json
+++ b/src/commands/latency.json
@@ -1,6 +1,6 @@
{
"LATENCY": {
- "summary": "A container for latency diagnostics commands",
+ "summary": "A container for latency diagnostics commands.",
"complexity": "Depends on subcommand.",
"group": "server",
"since": "2.8.13",
diff --git a/src/commands/lcs.json b/src/commands/lcs.json
index fe7840b34..a26b089de 100644
--- a/src/commands/lcs.json
+++ b/src/commands/lcs.json
@@ -1,6 +1,6 @@
{
"LCS": {
- "summary": "Find longest common substring",
+ "summary": "Finds the longest common substring.",
"complexity": "O(N*M) where N and M are the lengths of s1 and s2, respectively",
"group": "string",
"since": "7.0.0",
diff --git a/src/commands/lindex.json b/src/commands/lindex.json
index 89b14ea9f..a589d52fc 100644
--- a/src/commands/lindex.json
+++ b/src/commands/lindex.json
@@ -1,6 +1,6 @@
{
"LINDEX": {
- "summary": "Get an element from a list by its index",
+ "summary": "Returns an element from a list by its index.",
"complexity": "O(N) where N is the number of elements to traverse to get to the element at index. This makes asking for the first or the last element of the list O(1).",
"group": "list",
"since": "1.0.0",
diff --git a/src/commands/linsert.json b/src/commands/linsert.json
index d31a4de8d..8059dc50d 100644
--- a/src/commands/linsert.json
+++ b/src/commands/linsert.json
@@ -1,6 +1,6 @@
{
"LINSERT": {
- "summary": "Insert an element before or after another element in a list",
+ "summary": "Inserts an element before or after another element in a list.",
"complexity": "O(N) where N is the number of elements to traverse before seeing the value pivot. This means that inserting somewhere on the left end on the list (head) can be considered O(1) and inserting somewhere on the right end (tail) is O(N).",
"group": "list",
"since": "2.2.0",
diff --git a/src/commands/llen.json b/src/commands/llen.json
index 1452e22d7..846aa4086 100644
--- a/src/commands/llen.json
+++ b/src/commands/llen.json
@@ -1,6 +1,6 @@
{
"LLEN": {
- "summary": "Get the length of a list",
+ "summary": "Returns the length of a list.",
"complexity": "O(1)",
"group": "list",
"since": "1.0.0",
diff --git a/src/commands/lmove.json b/src/commands/lmove.json
index 82c305c12..ab0c6adeb 100644
--- a/src/commands/lmove.json
+++ b/src/commands/lmove.json
@@ -1,6 +1,6 @@
{
"LMOVE": {
- "summary": "Pop an element from a list, push it to another list and return it",
+ "summary": "Returns an element after popping it from one list and pushing it to another. Deletes the list if the last element was moved.",
"complexity": "O(1)",
"group": "list",
"since": "6.2.0",
diff --git a/src/commands/lmpop.json b/src/commands/lmpop.json
index e788b5136..7cc347689 100644
--- a/src/commands/lmpop.json
+++ b/src/commands/lmpop.json
@@ -1,6 +1,6 @@
{
"LMPOP": {
- "summary": "Pop elements from a list",
+ "summary": "Returns multiple elements from a list after removing them. Deletes the list if the last element was popped.",
"complexity": "O(N+M) where N is the number of provided keys and M is the number of elements returned.",
"group": "list",
"since": "7.0.0",
diff --git a/src/commands/lolwut.json b/src/commands/lolwut.json
index b093404b5..546c14cfc 100644
--- a/src/commands/lolwut.json
+++ b/src/commands/lolwut.json
@@ -1,6 +1,6 @@
{
"LOLWUT": {
- "summary": "Display some computer art and the Redis version",
+ "summary": "Displays computer art and the Redis version",
"group": "server",
"since": "5.0.0",
"arity": -1,
diff --git a/src/commands/lpop.json b/src/commands/lpop.json
index a7b93c276..b1d6cd177 100644
--- a/src/commands/lpop.json
+++ b/src/commands/lpop.json
@@ -1,6 +1,6 @@
{
"LPOP": {
- "summary": "Remove and get the first elements in a list",
+ "summary": "Returns the first elements in a list after removing it. Deletes the list if the last element was popped.",
"complexity": "O(N) where N is the number of elements returned",
"group": "list",
"since": "1.0.0",
diff --git a/src/commands/lpos.json b/src/commands/lpos.json
index 8e3a9fa02..7b63b72b5 100644
--- a/src/commands/lpos.json
+++ b/src/commands/lpos.json
@@ -1,6 +1,6 @@
{
"LPOS": {
- "summary": "Return the index of matching elements on a list",
+ "summary": "Returns the index of matching elements in a list.",
"complexity": "O(N) where N is the number of elements in the list, for the average case. When searching for elements near the head or the tail of the list, or when the MAXLEN option is provided, the command may run in constant time.",
"group": "list",
"since": "6.0.6",
diff --git a/src/commands/lpush.json b/src/commands/lpush.json
index 6fc815298..34cd8e246 100644
--- a/src/commands/lpush.json
+++ b/src/commands/lpush.json
@@ -1,6 +1,6 @@
{
"LPUSH": {
- "summary": "Prepend one or multiple elements to a list",
+ "summary": "Prepends one or more elements to a list. Creates the key if it doesn't exist.",
"complexity": "O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.",
"group": "list",
"since": "1.0.0",
diff --git a/src/commands/lpushx.json b/src/commands/lpushx.json
index d41f50b77..5f6d17cbc 100644
--- a/src/commands/lpushx.json
+++ b/src/commands/lpushx.json
@@ -1,6 +1,6 @@
{
"LPUSHX": {
- "summary": "Prepend an element to a list, only if the list exists",
+ "summary": "Prepends one or more elements to a list only when the list exists.",
"complexity": "O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.",
"group": "list",
"since": "2.2.0",
diff --git a/src/commands/lrange.json b/src/commands/lrange.json
index 1c0868caa..303d2f60b 100644
--- a/src/commands/lrange.json
+++ b/src/commands/lrange.json
@@ -1,6 +1,6 @@
{
"LRANGE": {
- "summary": "Get a range of elements from a list",
+ "summary": "Returns a range of elements from a list.",
"complexity": "O(S+N) where S is the distance of start offset from HEAD for small lists, from nearest end (HEAD or TAIL) for large lists; and N is the number of elements in the specified range.",
"group": "list",
"since": "1.0.0",
diff --git a/src/commands/lrem.json b/src/commands/lrem.json
index 84baeea0b..c267d3ebc 100644
--- a/src/commands/lrem.json
+++ b/src/commands/lrem.json
@@ -1,6 +1,6 @@
{
"LREM": {
- "summary": "Remove elements from a list",
+ "summary": "Removes elements from a list. Deletes the list if the last element was removed.",
"complexity": "O(N+M) where N is the length of the list and M is the number of elements removed.",
"group": "list",
"since": "1.0.0",
diff --git a/src/commands/lset.json b/src/commands/lset.json
index 441db073c..473b02c34 100644
--- a/src/commands/lset.json
+++ b/src/commands/lset.json
@@ -1,6 +1,6 @@
{
"LSET": {
- "summary": "Set the value of an element in a list by its index",
+ "summary": "Sets the value of an element in a list by its index.",
"complexity": "O(N) where N is the length of the list. Setting either the first or the last element of the list is O(1).",
"group": "list",
"since": "1.0.0",
diff --git a/src/commands/ltrim.json b/src/commands/ltrim.json
index c041f49ca..3bba299f8 100644
--- a/src/commands/ltrim.json
+++ b/src/commands/ltrim.json
@@ -1,6 +1,6 @@
{
"LTRIM": {
- "summary": "Trim a list to the specified range",
+ "summary": "Removes elements from both ends a list. Deletes the list if all elements were trimmed.",
"complexity": "O(N) where N is the number of elements to be removed by the operation.",
"group": "list",
"since": "1.0.0",
diff --git a/src/commands/memory-doctor.json b/src/commands/memory-doctor.json
index 5df7456f5..c0c8c22a8 100644
--- a/src/commands/memory-doctor.json
+++ b/src/commands/memory-doctor.json
@@ -1,6 +1,6 @@
{
"DOCTOR": {
- "summary": "Outputs memory problems report",
+ "summary": "Outputs a memory problems report.",
"complexity": "O(1)",
"group": "server",
"since": "4.0.0",
diff --git a/src/commands/memory-help.json b/src/commands/memory-help.json
index 34ee382c5..e72934c56 100644
--- a/src/commands/memory-help.json
+++ b/src/commands/memory-help.json
@@ -1,6 +1,6 @@
{
"HELP": {
- "summary": "Show helpful text about the different subcommands",
+ "summary": "Returns helpful text about the different subcommands.",
"complexity": "O(1)",
"group": "server",
"since": "4.0.0",
diff --git a/src/commands/memory-malloc-stats.json b/src/commands/memory-malloc-stats.json
index a44959c88..5ef6a31c4 100644
--- a/src/commands/memory-malloc-stats.json
+++ b/src/commands/memory-malloc-stats.json
@@ -1,6 +1,6 @@
{
"MALLOC-STATS": {
- "summary": "Show allocator internal stats",
+ "summary": "Returns the allocator statistics.",
"complexity": "Depends on how much memory is allocated, could be slow",
"group": "server",
"since": "4.0.0",
diff --git a/src/commands/memory-purge.json b/src/commands/memory-purge.json
index 09c7d124c..77ed61dc5 100644
--- a/src/commands/memory-purge.json
+++ b/src/commands/memory-purge.json
@@ -1,6 +1,6 @@
{
"PURGE": {
- "summary": "Ask the allocator to release memory",
+ "summary": "Asks the allocator to release memory.",
"complexity": "Depends on how much memory is allocated, could be slow",
"group": "server",
"since": "4.0.0",
diff --git a/src/commands/memory-stats.json b/src/commands/memory-stats.json
index b50a9eb31..de82dc8cc 100644
--- a/src/commands/memory-stats.json
+++ b/src/commands/memory-stats.json
@@ -1,6 +1,6 @@
{
"STATS": {
- "summary": "Show memory usage details",
+ "summary": "Returns details about memory usage.",
"complexity": "O(1)",
"group": "server",
"since": "4.0.0",
diff --git a/src/commands/memory-usage.json b/src/commands/memory-usage.json
index ff977d9be..78678ad1e 100644
--- a/src/commands/memory-usage.json
+++ b/src/commands/memory-usage.json
@@ -1,6 +1,6 @@
{
"USAGE": {
- "summary": "Estimate the memory usage of a key",
+ "summary": "Estimates the memory usage of a key.",
"complexity": "O(N) where N is the number of samples.",
"group": "server",
"since": "4.0.0",
diff --git a/src/commands/memory.json b/src/commands/memory.json
index d3fa02f5a..aab0841d1 100644
--- a/src/commands/memory.json
+++ b/src/commands/memory.json
@@ -1,6 +1,6 @@
{
"MEMORY": {
- "summary": "A container for memory diagnostics commands",
+ "summary": "A container for memory diagnostics commands.",
"complexity": "Depends on subcommand.",
"group": "server",
"since": "4.0.0",
diff --git a/src/commands/mget.json b/src/commands/mget.json
index a5bd8c30e..a177853ca 100644
--- a/src/commands/mget.json
+++ b/src/commands/mget.json
@@ -1,6 +1,6 @@
{
"MGET": {
- "summary": "Get the values of all the given keys",
+ "summary": "Atomically returns the string values of one or more keys.",
"complexity": "O(N) where N is the number of keys to retrieve.",
"group": "string",
"since": "1.0.0",
diff --git a/src/commands/migrate.json b/src/commands/migrate.json
index 83a1dd149..f1dfae4e0 100644
--- a/src/commands/migrate.json
+++ b/src/commands/migrate.json
@@ -1,6 +1,6 @@
{
"MIGRATE": {
- "summary": "Atomically transfer a key from a Redis instance to another one.",
+ "summary": "Atomically transfers a key from one Redis instance to another.",
"complexity": "This command actually executes a DUMP+DEL in the source instance, and a RESTORE in the target instance. See the pages of these commands for time complexity. Also an O(N) data transfer between the two instances is performed.",
"group": "generic",
"since": "2.6.0",
diff --git a/src/commands/module-help.json b/src/commands/module-help.json
index 10b59dc2d..5f3db0f51 100644
--- a/src/commands/module-help.json
+++ b/src/commands/module-help.json
@@ -1,6 +1,6 @@
{
"HELP": {
- "summary": "Show helpful text about the different subcommands",
+ "summary": "Returns helpful text about the different subcommands.",
"complexity": "O(1)",
"group": "server",
"since": "5.0.0",
diff --git a/src/commands/module-list.json b/src/commands/module-list.json
index 65a8c9f3b..92a022bc7 100644
--- a/src/commands/module-list.json
+++ b/src/commands/module-list.json
@@ -1,6 +1,6 @@
{
"LIST": {
- "summary": "List all modules loaded by the server",
+ "summary": "Returns all loaded modules.",
"complexity": "O(N) where N is the number of loaded modules.",
"group": "server",
"since": "4.0.0",
diff --git a/src/commands/module-load.json b/src/commands/module-load.json
index 1ce5faf10..dd5d654ed 100644
--- a/src/commands/module-load.json
+++ b/src/commands/module-load.json
@@ -1,6 +1,6 @@
{
"LOAD": {
- "summary": "Load a module",
+ "summary": "Loads a module.",
"complexity": "O(1)",
"group": "server",
"since": "4.0.0",
diff --git a/src/commands/module-loadex.json b/src/commands/module-loadex.json
index 7aa278831..6c750ea7f 100644
--- a/src/commands/module-loadex.json
+++ b/src/commands/module-loadex.json
@@ -1,6 +1,6 @@
{
"LOADEX": {
- "summary": "Load a module with extended parameters",
+ "summary": "Loads a module using extended parameters.",
"complexity": "O(1)",
"group": "server",
"since": "7.0.0",
diff --git a/src/commands/module-unload.json b/src/commands/module-unload.json
index 3cd85b378..f2fbf80fa 100644
--- a/src/commands/module-unload.json
+++ b/src/commands/module-unload.json
@@ -1,6 +1,6 @@
{
"UNLOAD": {
- "summary": "Unload a module",
+ "summary": "Unloads a module.",
"complexity": "O(1)",
"group": "server",
"since": "4.0.0",
diff --git a/src/commands/module.json b/src/commands/module.json
index f04d5daf6..148f18223 100644
--- a/src/commands/module.json
+++ b/src/commands/module.json
@@ -1,6 +1,6 @@
{
"MODULE": {
- "summary": "A container for module commands",
+ "summary": "A container for module commands.",
"complexity": "Depends on subcommand.",
"group": "server",
"since": "4.0.0",
diff --git a/src/commands/monitor.json b/src/commands/monitor.json
index a305c4fce..23c659e4e 100644
--- a/src/commands/monitor.json
+++ b/src/commands/monitor.json
@@ -1,6 +1,6 @@
{
"MONITOR": {
- "summary": "Listen for all requests received by the server in real time",
+ "summary": "Listens for all requests received by the server in real-time.",
"group": "server",
"since": "1.0.0",
"arity": 1,
diff --git a/src/commands/move.json b/src/commands/move.json
index cd588ff59..203068067 100644
--- a/src/commands/move.json
+++ b/src/commands/move.json
@@ -1,6 +1,6 @@
{
"MOVE": {
- "summary": "Move a key to another database",
+ "summary": "Moves a key to another database.",
"complexity": "O(1)",
"group": "generic",
"since": "1.0.0",
diff --git a/src/commands/mset.json b/src/commands/mset.json
index deff39ec8..9a57446b5 100644
--- a/src/commands/mset.json
+++ b/src/commands/mset.json
@@ -1,6 +1,6 @@
{
"MSET": {
- "summary": "Set multiple keys to multiple values",
+ "summary": "Atomically creates or modifies the string values of one or more keys.",
"complexity": "O(N) where N is the number of keys to set.",
"group": "string",
"since": "1.0.1",
diff --git a/src/commands/msetnx.json b/src/commands/msetnx.json
index 90a6449d1..fa71d2b45 100644
--- a/src/commands/msetnx.json
+++ b/src/commands/msetnx.json
@@ -1,6 +1,6 @@
{
"MSETNX": {
- "summary": "Set multiple keys to multiple values, only if none of the keys exist",
+ "summary": "Atomically modifies the string values of one or more keys only when all keys don't exist.",
"complexity": "O(N) where N is the number of keys to set.",
"group": "string",
"since": "1.0.1",
diff --git a/src/commands/multi.json b/src/commands/multi.json
index dd94bce26..5f17a1da2 100644
--- a/src/commands/multi.json
+++ b/src/commands/multi.json
@@ -1,6 +1,6 @@
{
"MULTI": {
- "summary": "Mark the start of a transaction block",
+ "summary": "Starts a transaction.",
"complexity": "O(1)",
"group": "transactions",
"since": "1.2.0",
diff --git a/src/commands/object-encoding.json b/src/commands/object-encoding.json
index 902a2f39e..f255b57d7 100644
--- a/src/commands/object-encoding.json
+++ b/src/commands/object-encoding.json
@@ -1,6 +1,6 @@
{
"ENCODING": {
- "summary": "Inspect the internal encoding of a Redis object",
+ "summary": "Returns the internal encoding of a Redis object.",
"complexity": "O(1)",
"group": "generic",
"since": "2.2.3",
diff --git a/src/commands/object-freq.json b/src/commands/object-freq.json
index 79c58fb63..03b5b2b20 100644
--- a/src/commands/object-freq.json
+++ b/src/commands/object-freq.json
@@ -1,6 +1,6 @@
{
"FREQ": {
- "summary": "Get the logarithmic access frequency counter of a Redis object",
+ "summary": "Returns the logarithmic access frequency counter of a Redis object.",
"complexity": "O(1)",
"group": "generic",
"since": "4.0.0",
diff --git a/src/commands/object-help.json b/src/commands/object-help.json
index bf1fac643..52616508c 100644
--- a/src/commands/object-help.json
+++ b/src/commands/object-help.json
@@ -1,6 +1,6 @@
{
"HELP": {
- "summary": "Show helpful text about the different subcommands",
+ "summary": "Returns helpful text about the different subcommands.",
"complexity": "O(1)",
"group": "generic",
"since": "6.2.0",
diff --git a/src/commands/object-idletime.json b/src/commands/object-idletime.json
index 8e124df6f..03c202a2b 100644
--- a/src/commands/object-idletime.json
+++ b/src/commands/object-idletime.json
@@ -1,6 +1,6 @@
{
"IDLETIME": {
- "summary": "Get the time since a Redis object was last accessed",
+ "summary": "Returns the time since the last access to a Redis object.",
"complexity": "O(1)",
"group": "generic",
"since": "2.2.3",
diff --git a/src/commands/object-refcount.json b/src/commands/object-refcount.json
index 82e2a54ea..48009bbf7 100644
--- a/src/commands/object-refcount.json
+++ b/src/commands/object-refcount.json
@@ -1,6 +1,6 @@
{
"REFCOUNT": {
- "summary": "Get the number of references to the value of the key",
+ "summary": "Returns the reference count of a value of a key.",
"complexity": "O(1)",
"group": "generic",
"since": "2.2.3",
diff --git a/src/commands/object.json b/src/commands/object.json
index f51988cd7..14be26b28 100644
--- a/src/commands/object.json
+++ b/src/commands/object.json
@@ -1,6 +1,6 @@
{
"OBJECT": {
- "summary": "A container for object introspection commands",
+ "summary": "A container for object introspection commands.",
"complexity": "Depends on subcommand.",
"group": "generic",
"since": "2.2.3",
diff --git a/src/commands/persist.json b/src/commands/persist.json
index 2ca99642b..11e6e01ca 100644
--- a/src/commands/persist.json
+++ b/src/commands/persist.json
@@ -1,6 +1,6 @@
{
"PERSIST": {
- "summary": "Remove the expiration from a key",
+ "summary": "Removes the expiration time of a key.",
"complexity": "O(1)",
"group": "generic",
"since": "2.2.0",
diff --git a/src/commands/pexpire.json b/src/commands/pexpire.json
index 03416a1eb..a133f4fa2 100644
--- a/src/commands/pexpire.json
+++ b/src/commands/pexpire.json
@@ -1,6 +1,6 @@
{
"PEXPIRE": {
- "summary": "Set a key's time to live in milliseconds",
+ "summary": "Sets the expiration time of a key in milliseconds.",
"complexity": "O(1)",
"group": "generic",
"since": "2.6.0",
diff --git a/src/commands/pexpireat.json b/src/commands/pexpireat.json
index ac09ba2e0..dd08ed0a5 100644
--- a/src/commands/pexpireat.json
+++ b/src/commands/pexpireat.json
@@ -1,6 +1,6 @@
{
"PEXPIREAT": {
- "summary": "Set the expiration for a key as a UNIX timestamp specified in milliseconds",
+ "summary": "Sets the expiration time of a key to a Unix milliseconds timestamp.",
"complexity": "O(1)",
"group": "generic",
"since": "2.6.0",
diff --git a/src/commands/pexpiretime.json b/src/commands/pexpiretime.json
index 9295a6a17..3fa055cb0 100644
--- a/src/commands/pexpiretime.json
+++ b/src/commands/pexpiretime.json
@@ -1,6 +1,6 @@
{
"PEXPIRETIME": {
- "summary": "Get the expiration Unix timestamp for a key in milliseconds",
+ "summary": "Returns the expiration time of a key as a Unix milliseconds timestamp.",
"complexity": "O(1)",
"group": "generic",
"since": "7.0.0",
diff --git a/src/commands/pfadd.json b/src/commands/pfadd.json
index f457d0f55..7d8448ad8 100644
--- a/src/commands/pfadd.json
+++ b/src/commands/pfadd.json
@@ -1,6 +1,6 @@
{
"PFADD": {
- "summary": "Adds the specified elements to the specified HyperLogLog.",
+ "summary": "Adds elements to a HyperLogLog key. Creates the key if it doesn't exist.",
"complexity": "O(1) to add every element.",
"group": "hyperloglog",
"since": "2.8.9",
diff --git a/src/commands/pfcount.json b/src/commands/pfcount.json
index 92f84d895..02a64c3d5 100644
--- a/src/commands/pfcount.json
+++ b/src/commands/pfcount.json
@@ -1,6 +1,6 @@
{
"PFCOUNT": {
- "summary": "Return the approximated cardinality of the set(s) observed by the HyperLogLog at key(s).",
+ "summary": "Returns the approximated cardinality of the set(s) observed by the HyperLogLog key(s).",
"complexity": "O(1) with a very small average constant time when called with a single key. O(N) with N being the number of keys, and much bigger constant times, when called with multiple keys.",
"group": "hyperloglog",
"since": "2.8.9",
diff --git a/src/commands/pfdebug.json b/src/commands/pfdebug.json
index b07e725bc..4cd2853bc 100644
--- a/src/commands/pfdebug.json
+++ b/src/commands/pfdebug.json
@@ -1,6 +1,6 @@
{
"PFDEBUG": {
- "summary": "Internal commands for debugging HyperLogLog values",
+ "summary": "Internal commands for debugging HyperLogLog values.",
"complexity": "N/A",
"group": "hyperloglog",
"since": "2.8.9",
diff --git a/src/commands/pfmerge.json b/src/commands/pfmerge.json
index 9f1f417ff..c93070f11 100644
--- a/src/commands/pfmerge.json
+++ b/src/commands/pfmerge.json
@@ -1,6 +1,6 @@
{
"PFMERGE": {
- "summary": "Merge N different HyperLogLogs into a single one.",
+ "summary": "Merges one or more HyperLogLog values into a single key.",
"complexity": "O(N) to merge N HyperLogLogs, but with high constant times.",
"group": "hyperloglog",
"since": "2.8.9",
diff --git a/src/commands/pfselftest.json b/src/commands/pfselftest.json
index f9c6a1002..ed29280b4 100644
--- a/src/commands/pfselftest.json
+++ b/src/commands/pfselftest.json
@@ -1,6 +1,6 @@
{
"PFSELFTEST": {
- "summary": "An internal command for testing HyperLogLog values",
+ "summary": "An internal command for testing HyperLogLog values.",
"complexity": "N/A",
"group": "hyperloglog",
"since": "2.8.9",
diff --git a/src/commands/ping.json b/src/commands/ping.json
index 13db35d43..b634c0be0 100644
--- a/src/commands/ping.json
+++ b/src/commands/ping.json
@@ -1,6 +1,6 @@
{
"PING": {
- "summary": "Ping the server",
+ "summary": "Returns the server's liveliness response.",
"complexity": "O(1)",
"group": "connection",
"since": "1.0.0",
diff --git a/src/commands/psetex.json b/src/commands/psetex.json
index 427f23279..8d8876602 100644
--- a/src/commands/psetex.json
+++ b/src/commands/psetex.json
@@ -1,6 +1,6 @@
{
"PSETEX": {
- "summary": "Set the value and expiration in milliseconds of a key",
+ "summary": "Sets both string value and expiration time in milliseconds of a key. The key is created if it doesn't exist.",
"complexity": "O(1)",
"group": "string",
"since": "2.6.0",
diff --git a/src/commands/psubscribe.json b/src/commands/psubscribe.json
index 8a1e66e08..db9592139 100644
--- a/src/commands/psubscribe.json
+++ b/src/commands/psubscribe.json
@@ -1,6 +1,6 @@
{
"PSUBSCRIBE": {
- "summary": "Listen for messages published to channels matching the given patterns",
+ "summary": "Listens for messages published to channels that match one or more patterns.",
"complexity": "O(N) where N is the number of patterns the client is already subscribed to.",
"group": "pubsub",
"since": "2.0.0",
diff --git a/src/commands/psync.json b/src/commands/psync.json
index 91175a198..60da8ed21 100644
--- a/src/commands/psync.json
+++ b/src/commands/psync.json
@@ -1,6 +1,6 @@
{
"PSYNC": {
- "summary": "Internal command used for replication",
+ "summary": "An internal command used in replication.",
"group": "server",
"since": "2.8.0",
"arity": -3,
diff --git a/src/commands/pttl.json b/src/commands/pttl.json
index 863248901..304270b9e 100644
--- a/src/commands/pttl.json
+++ b/src/commands/pttl.json
@@ -1,6 +1,6 @@
{
"PTTL": {
- "summary": "Get the time to live for a key in milliseconds",
+ "summary": "Returns the expiration time in milliseconds of a key.",
"complexity": "O(1)",
"group": "generic",
"since": "2.6.0",
diff --git a/src/commands/publish.json b/src/commands/publish.json
index 05313766b..1dd757d69 100644
--- a/src/commands/publish.json
+++ b/src/commands/publish.json
@@ -1,6 +1,6 @@
{
"PUBLISH": {
- "summary": "Post a message to a channel",
+ "summary": "Posts a message to a channel.",
"complexity": "O(N+M) where N is the number of clients subscribed to the receiving channel and M is the total number of subscribed patterns (by any client).",
"group": "pubsub",
"since": "2.0.0",
diff --git a/src/commands/pubsub-channels.json b/src/commands/pubsub-channels.json
index 173271271..08505b3eb 100644
--- a/src/commands/pubsub-channels.json
+++ b/src/commands/pubsub-channels.json
@@ -1,6 +1,6 @@
{
"CHANNELS": {
- "summary": "List active channels",
+ "summary": "Returns the active channels.",
"complexity": "O(N) where N is the number of active channels, and assuming constant time pattern matching (relatively short channels and patterns)",
"group": "pubsub",
"since": "2.8.0",
diff --git a/src/commands/pubsub-help.json b/src/commands/pubsub-help.json
index 09c04f3a4..32faedcbe 100644
--- a/src/commands/pubsub-help.json
+++ b/src/commands/pubsub-help.json
@@ -1,6 +1,6 @@
{
"HELP": {
- "summary": "Show helpful text about the different subcommands",
+ "summary": "Returns helpful text about the different subcommands.",
"complexity": "O(1)",
"group": "pubsub",
"since": "6.2.0",
diff --git a/src/commands/pubsub-numpat.json b/src/commands/pubsub-numpat.json
index b593a4e6d..ae653b74f 100644
--- a/src/commands/pubsub-numpat.json
+++ b/src/commands/pubsub-numpat.json
@@ -1,6 +1,6 @@
{
"NUMPAT": {
- "summary": "Get the count of unique patterns pattern subscriptions",
+ "summary": "Returns a count of unique pattern subscriptions.",
"complexity": "O(1)",
"group": "pubsub",
"since": "2.8.0",
diff --git a/src/commands/pubsub-numsub.json b/src/commands/pubsub-numsub.json
index 1df663be2..1cfe8e2ef 100644
--- a/src/commands/pubsub-numsub.json
+++ b/src/commands/pubsub-numsub.json
@@ -1,6 +1,6 @@
{
"NUMSUB": {
- "summary": "Get the count of subscribers for channels",
+ "summary": "Returns a count of subscribers to channels.",
"complexity": "O(N) for the NUMSUB subcommand, where N is the number of requested channels",
"group": "pubsub",
"since": "2.8.0",
diff --git a/src/commands/pubsub-shardchannels.json b/src/commands/pubsub-shardchannels.json
index cd196105b..7aa0a7a87 100644
--- a/src/commands/pubsub-shardchannels.json
+++ b/src/commands/pubsub-shardchannels.json
@@ -1,6 +1,6 @@
{
"SHARDCHANNELS": {
- "summary": "List active shard channels",
+ "summary": "Returns the active shard channels.",
"complexity": "O(N) where N is the number of active shard channels, and assuming constant time pattern matching (relatively short shard channels).",
"group": "pubsub",
"since": "7.0.0",
diff --git a/src/commands/pubsub-shardnumsub.json b/src/commands/pubsub-shardnumsub.json
index 536568a2a..43675340f 100644
--- a/src/commands/pubsub-shardnumsub.json
+++ b/src/commands/pubsub-shardnumsub.json
@@ -1,6 +1,6 @@
{
"SHARDNUMSUB": {
- "summary": "Get the count of subscribers for shard channels",
+ "summary": "Returns the count of subscribers of shard channels.",
"complexity": "O(N) for the SHARDNUMSUB subcommand, where N is the number of requested shard channels",
"group": "pubsub",
"since": "7.0.0",
diff --git a/src/commands/pubsub.json b/src/commands/pubsub.json
index e31bd4043..2f0bb5e25 100644
--- a/src/commands/pubsub.json
+++ b/src/commands/pubsub.json
@@ -1,6 +1,6 @@
{
"PUBSUB": {
- "summary": "A container for Pub/Sub commands",
+ "summary": "A container for Pub/Sub commands.",
"complexity": "Depends on subcommand.",
"group": "pubsub",
"since": "2.8.0",
diff --git a/src/commands/punsubscribe.json b/src/commands/punsubscribe.json
index 30136b316..3a074517b 100644
--- a/src/commands/punsubscribe.json
+++ b/src/commands/punsubscribe.json
@@ -1,6 +1,6 @@
{
"PUNSUBSCRIBE": {
- "summary": "Stop listening for messages posted to channels matching the given patterns",
+ "summary": "Stops listening to messages published to channels that match one or more patterns.",
"complexity": "O(N+M) where N is the number of patterns the client is already subscribed and M is the number of total patterns subscribed in the system (by any client).",
"group": "pubsub",
"since": "2.0.0",
diff --git a/src/commands/quit.json b/src/commands/quit.json
index feb371955..e8dd6e9de 100644
--- a/src/commands/quit.json
+++ b/src/commands/quit.json
@@ -1,6 +1,6 @@
{
"QUIT": {
- "summary": "Close the connection",
+ "summary": "Closes the connection.",
"complexity": "O(1)",
"group": "connection",
"since": "1.0.0",
diff --git a/src/commands/randomkey.json b/src/commands/randomkey.json
index 18f838b05..e8773ee6b 100644
--- a/src/commands/randomkey.json
+++ b/src/commands/randomkey.json
@@ -1,6 +1,6 @@
{
"RANDOMKEY": {
- "summary": "Return a random key from the keyspace",
+ "summary": "Returns a random key name from the database.",
"complexity": "O(1)",
"group": "generic",
"since": "1.0.0",
diff --git a/src/commands/readonly.json b/src/commands/readonly.json
index 4dcd50af8..253573c97 100644
--- a/src/commands/readonly.json
+++ b/src/commands/readonly.json
@@ -1,6 +1,6 @@
{
"READONLY": {
- "summary": "Enables read queries for a connection to a cluster replica node",
+ "summary": "Enables read-only queries for a connection to a Redis Cluster replica node.",
"complexity": "O(1)",
"group": "cluster",
"since": "3.0.0",
diff --git a/src/commands/readwrite.json b/src/commands/readwrite.json
index e72d5cac5..440dd596b 100644
--- a/src/commands/readwrite.json
+++ b/src/commands/readwrite.json
@@ -1,6 +1,6 @@
{
"READWRITE": {
- "summary": "Disables read queries for a connection to a cluster replica node",
+ "summary": "Enables read-write queries for a connection to a Reids Cluster replica node.",
"complexity": "O(1)",
"group": "cluster",
"since": "3.0.0",
diff --git a/src/commands/rename.json b/src/commands/rename.json
index 046d40ae3..a8b65aee7 100644
--- a/src/commands/rename.json
+++ b/src/commands/rename.json
@@ -1,6 +1,6 @@
{
"RENAME": {
- "summary": "Rename a key",
+ "summary": "Renames a key and overwrites the destination.",
"complexity": "O(1)",
"group": "generic",
"since": "1.0.0",
diff --git a/src/commands/renamenx.json b/src/commands/renamenx.json
index a6c039182..72f0569d8 100644
--- a/src/commands/renamenx.json
+++ b/src/commands/renamenx.json
@@ -1,6 +1,6 @@
{
"RENAMENX": {
- "summary": "Rename a key, only if the new key does not exist",
+ "summary": "Renames a key only when the target key name doesn't exist.",
"complexity": "O(1)",
"group": "generic",
"since": "1.0.0",
diff --git a/src/commands/replconf.json b/src/commands/replconf.json
index d5c43d525..e8efc7b42 100644
--- a/src/commands/replconf.json
+++ b/src/commands/replconf.json
@@ -1,6 +1,6 @@
{
"REPLCONF": {
- "summary": "An internal command for configuring the replication stream",
+ "summary": "An internal command for configuring the replication stream.",
"complexity": "O(1)",
"group": "server",
"since": "3.0.0",
diff --git a/src/commands/replicaof.json b/src/commands/replicaof.json
index 7f1d08754..aa4939019 100644
--- a/src/commands/replicaof.json
+++ b/src/commands/replicaof.json
@@ -1,6 +1,6 @@
{
"REPLICAOF": {
- "summary": "Make the server a replica of another instance, or promote it as master.",
+ "summary": "Configures a server as replica of another, or promotes it to a master.",
"complexity": "O(1)",
"group": "server",
"since": "5.0.0",
diff --git a/src/commands/reset.json b/src/commands/reset.json
index 81a20a3a8..3fb1a449d 100644
--- a/src/commands/reset.json
+++ b/src/commands/reset.json
@@ -1,6 +1,6 @@
{
"RESET": {
- "summary": "Reset the connection",
+ "summary": "Resets the connection.",
"complexity": "O(1)",
"group": "connection",
"since": "6.2.0",
diff --git a/src/commands/restore-asking.json b/src/commands/restore-asking.json
index b260478f2..2694a873c 100644
--- a/src/commands/restore-asking.json
+++ b/src/commands/restore-asking.json
@@ -1,6 +1,6 @@
{
"RESTORE-ASKING": {
- "summary": "An internal command for migrating keys in a cluster",
+ "summary": "An internal command for migrating keys in a cluster.",
"complexity": "O(1) to create the new key and additional O(N*M) to reconstruct the serialized value, where N is the number of Redis objects composing the value and M their average size. For small string values the time complexity is thus O(1)+O(1*M) where M is small, so simply O(1). However for sorted set values the complexity is O(N*M*log(N)) because inserting values into sorted sets is O(log(N)).",
"group": "server",
"since": "3.0.0",
diff --git a/src/commands/restore.json b/src/commands/restore.json
index 7e40d1c4f..383dd451e 100644
--- a/src/commands/restore.json
+++ b/src/commands/restore.json
@@ -1,6 +1,6 @@
{
"RESTORE": {
- "summary": "Create a key using the provided serialized value, previously obtained using DUMP.",
+ "summary": "Creates a key from the serialized representation of a value.",
"complexity": "O(1) to create the new key and additional O(N*M) to reconstruct the serialized value, where N is the number of Redis objects composing the value and M their average size. For small string values the time complexity is thus O(1)+O(1*M) where M is small, so simply O(1). However for sorted set values the complexity is O(N*M*log(N)) because inserting values into sorted sets is O(log(N)).",
"group": "generic",
"since": "2.6.0",
diff --git a/src/commands/role.json b/src/commands/role.json
index 498a5de52..a0299fa73 100644
--- a/src/commands/role.json
+++ b/src/commands/role.json
@@ -1,6 +1,6 @@
{
"ROLE": {
- "summary": "Return the role of the instance in the context of replication",
+ "summary": "Returns the replication role.",
"complexity": "O(1)",
"group": "server",
"since": "2.8.12",
diff --git a/src/commands/rpop.json b/src/commands/rpop.json
index c9b55b125..79b5a9254 100644
--- a/src/commands/rpop.json
+++ b/src/commands/rpop.json
@@ -1,6 +1,6 @@
{
"RPOP": {
- "summary": "Remove and get the last elements in a list",
+ "summary": "Returns and removes the last elements of a list. Deletes the list if the last element was popped.",
"complexity": "O(N) where N is the number of elements returned",
"group": "list",
"since": "1.0.0",
diff --git a/src/commands/rpoplpush.json b/src/commands/rpoplpush.json
index ddb0537e7..195149956 100644
--- a/src/commands/rpoplpush.json
+++ b/src/commands/rpoplpush.json
@@ -1,6 +1,6 @@
{
"RPOPLPUSH": {
- "summary": "Remove the last element in a list, prepend it to another list and return it",
+ "summary": "Returns the last element of a list after removing and pushing it to another list. Deletes the list if the last element was popped.",
"complexity": "O(1)",
"group": "list",
"since": "1.2.0",
diff --git a/src/commands/rpush.json b/src/commands/rpush.json
index 7b6a4a634..e5d6908c8 100644
--- a/src/commands/rpush.json
+++ b/src/commands/rpush.json
@@ -1,6 +1,6 @@
{
"RPUSH": {
- "summary": "Append one or multiple elements to a list",
+ "summary": "Appends one or more elements to a list. Creates the key if it doesn't exist.",
"complexity": "O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.",
"group": "list",
"since": "1.0.0",
diff --git a/src/commands/rpushx.json b/src/commands/rpushx.json
index 19294dd02..b41f9c506 100644
--- a/src/commands/rpushx.json
+++ b/src/commands/rpushx.json
@@ -1,6 +1,6 @@
{
"RPUSHX": {
- "summary": "Append an element to a list, only if the list exists",
+ "summary": "Appends an element to a list only when the list exists.",
"complexity": "O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.",
"group": "list",
"since": "2.2.0",
diff --git a/src/commands/sadd.json b/src/commands/sadd.json
index 89c824625..00b3c26df 100644
--- a/src/commands/sadd.json
+++ b/src/commands/sadd.json
@@ -1,6 +1,6 @@
{
"SADD": {
- "summary": "Add one or more members to a set",
+ "summary": "Adds one or more members to a set. Creates the key if it doesn't exist.",
"complexity": "O(1) for each element added, so O(N) to add N elements when the command is called with multiple arguments.",
"group": "set",
"since": "1.0.0",
diff --git a/src/commands/save.json b/src/commands/save.json
index 1885128bf..0645e2778 100644
--- a/src/commands/save.json
+++ b/src/commands/save.json
@@ -1,6 +1,6 @@
{
"SAVE": {
- "summary": "Synchronously save the dataset to disk",
+ "summary": "Synchronously saves the database(s) to disk.",
"complexity": "O(N) where N is the total number of keys in all databases",
"group": "server",
"since": "1.0.0",
diff --git a/src/commands/scan.json b/src/commands/scan.json
index bdf27a575..ca9adf5b4 100644
--- a/src/commands/scan.json
+++ b/src/commands/scan.json
@@ -1,6 +1,6 @@
{
"SCAN": {
- "summary": "Incrementally iterate the keys space",
+ "summary": "Iterates over the key names in the database.",
"complexity": "O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.",
"group": "generic",
"since": "2.8.0",
diff --git a/src/commands/scard.json b/src/commands/scard.json
index 0b7a832de..8df0a4f1c 100644
--- a/src/commands/scard.json
+++ b/src/commands/scard.json
@@ -1,6 +1,6 @@
{
"SCARD": {
- "summary": "Get the number of members in a set",
+ "summary": "Returns the number of members in a set.",
"complexity": "O(1)",
"group": "set",
"since": "1.0.0",
diff --git a/src/commands/script-debug.json b/src/commands/script-debug.json
index 25899b94a..ebba38a6c 100644
--- a/src/commands/script-debug.json
+++ b/src/commands/script-debug.json
@@ -1,6 +1,6 @@
{
"DEBUG": {
- "summary": "Set the debug mode for executed scripts.",
+ "summary": "Sets the debug mode of server-side Lua scripts.",
"complexity": "O(1)",
"group": "scripting",
"since": "3.2.0",
diff --git a/src/commands/script-exists.json b/src/commands/script-exists.json
index 629615907..d8c47e482 100644
--- a/src/commands/script-exists.json
+++ b/src/commands/script-exists.json
@@ -1,6 +1,6 @@
{
"EXISTS": {
- "summary": "Check existence of scripts in the script cache.",
+ "summary": "Determines whether server-side Lua scripts exist in the script cache.",
"complexity": "O(N) with N being the number of scripts to check (so checking a single script is an O(1) operation).",
"group": "scripting",
"since": "2.6.0",
diff --git a/src/commands/script-flush.json b/src/commands/script-flush.json
index 63cfb1e28..7487dc51f 100644
--- a/src/commands/script-flush.json
+++ b/src/commands/script-flush.json
@@ -1,6 +1,6 @@
{
"FLUSH": {
- "summary": "Remove all the scripts from the script cache.",
+ "summary": "Removes all server-side Lua scripts from the script cache.",
"complexity": "O(N) with N being the number of scripts in cache",
"group": "scripting",
"since": "2.6.0",
diff --git a/src/commands/script-help.json b/src/commands/script-help.json
index d6c6853fd..c5ea5df6b 100644
--- a/src/commands/script-help.json
+++ b/src/commands/script-help.json
@@ -1,6 +1,6 @@
{
"HELP": {
- "summary": "Show helpful text about the different subcommands",
+ "summary": "Returns helpful text about the different subcommands.",
"complexity": "O(1)",
"group": "scripting",
"since": "5.0.0",
diff --git a/src/commands/script-kill.json b/src/commands/script-kill.json
index c10ff0a0d..fe72d31eb 100644
--- a/src/commands/script-kill.json
+++ b/src/commands/script-kill.json
@@ -1,6 +1,6 @@
{
"KILL": {
- "summary": "Kill the script currently in execution.",
+ "summary": "Terminates a server-side Lua script during execution.",
"complexity": "O(1)",
"group": "scripting",
"since": "2.6.0",
diff --git a/src/commands/script-load.json b/src/commands/script-load.json
index a369ee459..37f80fdc7 100644
--- a/src/commands/script-load.json
+++ b/src/commands/script-load.json
@@ -1,6 +1,6 @@
{
"LOAD": {
- "summary": "Load the specified Lua script into the script cache.",
+ "summary": "Loads a server-side Lua script to the script cache.",
"complexity": "O(N) with N being the length in bytes of the script body.",
"group": "scripting",
"since": "2.6.0",
diff --git a/src/commands/script.json b/src/commands/script.json
index 16e307a3c..1d420c138 100644
--- a/src/commands/script.json
+++ b/src/commands/script.json
@@ -1,6 +1,6 @@
{
"SCRIPT": {
- "summary": "A container for Lua scripts management commands",
+ "summary": "A container for Lua scripts management commands.",
"complexity": "Depends on subcommand.",
"group": "scripting",
"since": "2.6.0",
diff --git a/src/commands/sdiff.json b/src/commands/sdiff.json
index ce7846c46..ac04d0349 100644
--- a/src/commands/sdiff.json
+++ b/src/commands/sdiff.json
@@ -1,6 +1,6 @@
{
"SDIFF": {
- "summary": "Subtract multiple sets",
+ "summary": "Returns the difference of multiple sets.",
"complexity": "O(N) where N is the total number of elements in all given sets.",
"group": "set",
"since": "1.0.0",
diff --git a/src/commands/sdiffstore.json b/src/commands/sdiffstore.json
index 8ba88e627..94b2d242d 100644
--- a/src/commands/sdiffstore.json
+++ b/src/commands/sdiffstore.json
@@ -1,6 +1,6 @@
{
"SDIFFSTORE": {
- "summary": "Subtract multiple sets and store the resulting set in a key",
+ "summary": "Stores the difference of multiple sets in a key.",
"complexity": "O(N) where N is the total number of elements in all given sets.",
"group": "set",
"since": "1.0.0",
diff --git a/src/commands/select.json b/src/commands/select.json
index 0f68cde80..5cf8634a9 100644
--- a/src/commands/select.json
+++ b/src/commands/select.json
@@ -1,6 +1,6 @@
{
"SELECT": {
- "summary": "Change the selected database for the current connection",
+ "summary": "Changes the selected database.",
"complexity": "O(1)",
"group": "connection",
"since": "1.0.0",
diff --git a/src/commands/sentinel-ckquorum.json b/src/commands/sentinel-ckquorum.json
index df0a0032e..cdd6cd197 100644
--- a/src/commands/sentinel-ckquorum.json
+++ b/src/commands/sentinel-ckquorum.json
@@ -1,6 +1,6 @@
{
"CKQUORUM": {
- "summary": "Check for a Sentinel quorum",
+ "summary": "Checks for a Redis Sentinel quorum.",
"group": "sentinel",
"since": "2.8.4",
"arity": 3,
diff --git a/src/commands/sentinel-config.json b/src/commands/sentinel-config.json
index 2369ec1fe..ecc317603 100644
--- a/src/commands/sentinel-config.json
+++ b/src/commands/sentinel-config.json
@@ -1,6 +1,6 @@
{
"CONFIG": {
- "summary": "Configure Sentinel",
+ "summary": "Configures Redis Sentinel.",
"complexity": "O(1)",
"group": "sentinel",
"since": "6.2.0",
diff --git a/src/commands/sentinel-debug.json b/src/commands/sentinel-debug.json
index dac2f6b60..c671ec5e1 100644
--- a/src/commands/sentinel-debug.json
+++ b/src/commands/sentinel-debug.json
@@ -1,6 +1,6 @@
{
"DEBUG": {
- "summary": "List or update the current configurable parameters",
+ "summary": "Lists or updates the current configurable parameters of Redis Sentinel.",
"complexity": "O(N) where N is the number of configurable parameters",
"group": "sentinel",
"since": "7.0.0",
@@ -12,6 +12,21 @@
"SENTINEL",
"ONLY_SENTINEL"
],
+ "reply_schema": {
+ "oneOf": [
+ {
+ "description": "The configuration update was successful.",
+ "const": "OK"
+ },
+ {
+ "description": "List of configurable time parameters and their values (milliseconds).",
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ }
+ }
+ ]
+ },
"arguments": [
{
"name": "data",
diff --git a/src/commands/sentinel-failover.json b/src/commands/sentinel-failover.json
index 87f9c4aca..8f5037589 100644
--- a/src/commands/sentinel-failover.json
+++ b/src/commands/sentinel-failover.json
@@ -1,6 +1,6 @@
{
"FAILOVER": {
- "summary": "Force a failover",
+ "summary": "Forces a Redis Sentinel failover.",
"group": "sentinel",
"since": "2.8.4",
"arity": 3,
diff --git a/src/commands/sentinel-flushconfig.json b/src/commands/sentinel-flushconfig.json
index 117109f06..b2fa5decf 100644
--- a/src/commands/sentinel-flushconfig.json
+++ b/src/commands/sentinel-flushconfig.json
@@ -1,6 +1,6 @@
{
"FLUSHCONFIG": {
- "summary": "Rewrite configuration file",
+ "summary": "Rewrites the Redis Sentinel configuration file.",
"complexity": "O(1)",
"group": "sentinel",
"since": "2.8.4",
diff --git a/src/commands/sentinel-get-master-addr-by-name.json b/src/commands/sentinel-get-master-addr-by-name.json
index 3dc307867..998f95ec9 100644
--- a/src/commands/sentinel-get-master-addr-by-name.json
+++ b/src/commands/sentinel-get-master-addr-by-name.json
@@ -1,6 +1,6 @@
{
"GET-MASTER-ADDR-BY-NAME": {
- "summary": "Get port and address of a master",
+ "summary": "Returns the port and address of a master Redis instance.",
"complexity": "O(1)",
"group": "sentinel",
"since": "2.8.4",
diff --git a/src/commands/sentinel-help.json b/src/commands/sentinel-help.json
index 5e3e9a712..d60145016 100644
--- a/src/commands/sentinel-help.json
+++ b/src/commands/sentinel-help.json
@@ -1,6 +1,6 @@
{
"HELP": {
- "summary": "Show helpful text about the different subcommands",
+ "summary": "Returns helpful text about the different subcommands.",
"complexity": "O(1)",
"group": "sentinel",
"since": "6.2.0",
diff --git a/src/commands/sentinel-info-cache.json b/src/commands/sentinel-info-cache.json
index 5c7855663..af89f182e 100644
--- a/src/commands/sentinel-info-cache.json
+++ b/src/commands/sentinel-info-cache.json
@@ -1,6 +1,6 @@
{
"INFO-CACHE": {
- "summary": "Get cached INFO from the instances in the deployment",
+ "summary": "Returns the cached `INFO` replies from the deployment's instances.",
"complexity": "O(N) where N is the number of instances",
"group": "sentinel",
"since": "3.2.0",
@@ -12,6 +12,47 @@
"SENTINEL",
"ONLY_SENTINEL"
],
+ "reply_schema": {
+ "type": "array",
+ "description": "This is actually a map, the odd entries are a master name, and the even entries are the last cached INFO output from that master and all its replicas.",
+ "minItems": 0,
+ "maxItems": 4294967295,
+ "items": [
+ {
+ "oneOf": [
+ {
+ "type": "string",
+ "description": "The master name."
+ },
+ {
+ "type": "array",
+ "description": "This is an array of pairs, the odd entries are the INFO age, and the even entries are the cached INFO string. The first pair belong to the master and the rest are its replicas.",
+ "minItems": 2,
+ "maxItems": 2,
+ "items": [
+ {
+ "description": "The number of milliseconds since when the INFO was cached.",
+ "type": "integer"
+ },
+ {
+ "description": "The cached INFO string or null.",
+ "oneOf": [
+ {
+ "description": "The cached INFO string.",
+ "type": "string"
+ },
+ {
+ "description": "No cached INFO string.",
+ "type": "null"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ },
"arguments": [
{
"name": "nodename",
diff --git a/src/commands/sentinel-is-master-down-by-addr.json b/src/commands/sentinel-is-master-down-by-addr.json
index b0ca319f2..a13e96b79 100644
--- a/src/commands/sentinel-is-master-down-by-addr.json
+++ b/src/commands/sentinel-is-master-down-by-addr.json
@@ -1,6 +1,6 @@
{
"IS-MASTER-DOWN-BY-ADDR": {
- "summary": "Check if a master is down",
+ "summary": "Determines whether a master Redis instance is down.",
"complexity": "O(1)",
"group": "sentinel",
"since": "2.8.4",
diff --git a/src/commands/sentinel-master.json b/src/commands/sentinel-master.json
index 46d6d950a..8ca446d49 100644
--- a/src/commands/sentinel-master.json
+++ b/src/commands/sentinel-master.json
@@ -1,6 +1,6 @@
{
"MASTER": {
- "summary": "Shows the state of a master",
+ "summary": "Returns the state of a master Redis instance.",
"complexity": "O(1)",
"group": "sentinel",
"since": "2.8.4",
diff --git a/src/commands/sentinel-masters.json b/src/commands/sentinel-masters.json
index abfb0b5bf..1e96b7105 100644
--- a/src/commands/sentinel-masters.json
+++ b/src/commands/sentinel-masters.json
@@ -1,6 +1,6 @@
{
"MASTERS": {
- "summary": "List the monitored masters",
+ "summary": "Returns a list of monitored Redis masters.",
"complexity": "O(N) where N is the number of masters",
"group": "sentinel",
"since": "2.8.4",
@@ -11,6 +11,16 @@
"ADMIN",
"SENTINEL",
"ONLY_SENTINEL"
- ]
+ ],
+ "reply_schema": {
+ "type": "array",
+ "description": "List of monitored Redis masters, and their state.",
+ "items": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ }
+ }
+ }
}
}
diff --git a/src/commands/sentinel-monitor.json b/src/commands/sentinel-monitor.json
index 2ea9aff58..cf6b4eebf 100644
--- a/src/commands/sentinel-monitor.json
+++ b/src/commands/sentinel-monitor.json
@@ -1,6 +1,6 @@
{
"MONITOR": {
- "summary": "Start monitoring",
+ "summary": "Starts monitoring.",
"complexity": "O(1)",
"group": "sentinel",
"since": "2.8.4",
diff --git a/src/commands/sentinel-myid.json b/src/commands/sentinel-myid.json
index 1f8585942..4d366ebf3 100644
--- a/src/commands/sentinel-myid.json
+++ b/src/commands/sentinel-myid.json
@@ -1,6 +1,6 @@
{
"MYID": {
- "summary": "Get the Sentinel instance ID",
+ "summary": "Returns the Redis Sentinel instance ID.",
"complexity": "O(1)",
"group": "sentinel",
"since": "6.2.0",
@@ -11,6 +11,10 @@
"ADMIN",
"SENTINEL",
"ONLY_SENTINEL"
- ]
+ ],
+ "reply_schema": {
+ "description": "Node ID of the sentinel instance.",
+ "type": "string"
+ }
}
}
diff --git a/src/commands/sentinel-pending-scripts.json b/src/commands/sentinel-pending-scripts.json
index 6ef33ec69..22dae4774 100644
--- a/src/commands/sentinel-pending-scripts.json
+++ b/src/commands/sentinel-pending-scripts.json
@@ -1,6 +1,6 @@
{
"PENDING-SCRIPTS": {
- "summary": "Get information about pending scripts",
+ "summary": "Returns information about pending scripts for Redis Sentinel.",
"group": "sentinel",
"since": "2.8.4",
"arity": 2,
@@ -10,6 +10,43 @@
"ADMIN",
"SENTINEL",
"ONLY_SENTINEL"
- ]
+ ],
+ "reply_schema": {
+ "type": "array",
+ "description": "List of pending scripts.",
+ "items": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "argv": {
+ "type": "array",
+ "description": "Script arguments.",
+ "items": {
+ "type": "string"
+ }
+ },
+ "flags": {
+ "type": "string",
+ "description": "Script flags."
+ },
+ "pid": {
+ "type": "string",
+ "description": "Script pid."
+ },
+ "run-time": {
+ "type": "string",
+ "description": "Script run-time."
+ },
+ "run-delay": {
+ "type": "string",
+ "description": "Script run-delay."
+ },
+ "retry-num": {
+ "type": "string",
+ "description": "Number of times we tried to execute the script."
+ }
+ }
+ }
+ }
}
}
diff --git a/src/commands/sentinel-remove.json b/src/commands/sentinel-remove.json
index d79f60e6c..1fe084f42 100644
--- a/src/commands/sentinel-remove.json
+++ b/src/commands/sentinel-remove.json
@@ -1,6 +1,6 @@
{
"REMOVE": {
- "summary": "Stop monitoring",
+ "summary": "Stops monitoring.",
"complexity": "O(1)",
"group": "sentinel",
"since": "2.8.4",
diff --git a/src/commands/sentinel-replicas.json b/src/commands/sentinel-replicas.json
index 454fcfb91..09d88f2d0 100644
--- a/src/commands/sentinel-replicas.json
+++ b/src/commands/sentinel-replicas.json
@@ -1,6 +1,6 @@
{
"REPLICAS": {
- "summary": "List the monitored replicas",
+ "summary": "Returns a list of the monitored Redis replicas.",
"complexity": "O(N) where N is the number of replicas",
"group": "sentinel",
"since": "5.0.0",
diff --git a/src/commands/sentinel-reset.json b/src/commands/sentinel-reset.json
index 9c60c6be7..17b53a481 100644
--- a/src/commands/sentinel-reset.json
+++ b/src/commands/sentinel-reset.json
@@ -1,6 +1,6 @@
{
"RESET": {
- "summary": "Reset masters by name pattern",
+ "summary": "Resets Redis masters by name matching a pattern.",
"complexity": "O(N) where N is the number of monitored masters",
"group": "sentinel",
"since": "2.8.4",
@@ -12,6 +12,10 @@
"SENTINEL",
"ONLY_SENTINEL"
],
+ "reply_schema": {
+ "type": "integer",
+ "description": "The number of masters that were reset."
+ },
"arguments": [
{
"name": "pattern",
diff --git a/src/commands/sentinel-sentinels.json b/src/commands/sentinel-sentinels.json
index 01319ce83..fdaa5cb99 100644
--- a/src/commands/sentinel-sentinels.json
+++ b/src/commands/sentinel-sentinels.json
@@ -1,6 +1,6 @@
{
"SENTINELS": {
- "summary": "List the Sentinel instances",
+ "summary": "Returns a list of Sentinel instances.",
"complexity": "O(N) where N is the number of Sentinels",
"group": "sentinel",
"since": "2.8.4",
@@ -12,6 +12,16 @@
"SENTINEL",
"ONLY_SENTINEL"
],
+ "reply_schema": {
+ "type": "array",
+ "description": "List of sentinel instances, and their state.",
+ "items": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ }
+ }
+ },
"arguments": [
{
"name": "master-name",
diff --git a/src/commands/sentinel-set.json b/src/commands/sentinel-set.json
index 10dcc5735..3e8619611 100644
--- a/src/commands/sentinel-set.json
+++ b/src/commands/sentinel-set.json
@@ -1,6 +1,6 @@
{
"SET": {
- "summary": "Change the configuration of a monitored master",
+ "summary": "Changes the configuration of a monitored Redis master.",
"complexity": "O(1)",
"group": "sentinel",
"since": "2.8.4",
diff --git a/src/commands/sentinel-simulate-failure.json b/src/commands/sentinel-simulate-failure.json
index 4912a8b70..5031d44ae 100644
--- a/src/commands/sentinel-simulate-failure.json
+++ b/src/commands/sentinel-simulate-failure.json
@@ -1,6 +1,6 @@
{
"SIMULATE-FAILURE": {
- "summary": "Simulate failover scenarios",
+ "summary": "Simulates failover scenarios.",
"group": "sentinel",
"since": "3.2.0",
"arity": -3,
@@ -11,6 +11,21 @@
"SENTINEL",
"ONLY_SENTINEL"
],
+ "reply_schema": {
+ "oneOf": [
+ {
+ "description": "The simulated flag was set.",
+ "const": "OK"
+ },
+ {
+ "description": "Supported simulates flags. Returned in case `HELP` was used.",
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ ]
+ },
"arguments": [
{
"name": "mode",
diff --git a/src/commands/sentinel-slaves.json b/src/commands/sentinel-slaves.json
index bce5c7692..c1fec41bb 100644
--- a/src/commands/sentinel-slaves.json
+++ b/src/commands/sentinel-slaves.json
@@ -1,7 +1,7 @@
{
"SLAVES": {
- "summary": "List the monitored slaves",
- "complexity": "O(N) where N is the number of slaves",
+ "summary": "Returns a list of the monitored replicas.",
+ "complexity": "O(N) where N is the number of replicas.",
"group": "sentinel",
"since": "2.8.0",
"arity": 3,
@@ -17,6 +17,16 @@
"SENTINEL",
"ONLY_SENTINEL"
],
+ "reply_schema": {
+ "type": "array",
+ "description": "List of monitored replicas, and their state.",
+ "items": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ }
+ }
+ },
"arguments": [
{
"name": "master-name",
diff --git a/src/commands/sentinel.json b/src/commands/sentinel.json
index 81466e2dd..c14d5a30c 100644
--- a/src/commands/sentinel.json
+++ b/src/commands/sentinel.json
@@ -1,6 +1,6 @@
{
"SENTINEL": {
- "summary": "A container for Sentinel commands",
+ "summary": "A container for Redis Sentinel commands.",
"complexity": "Depends on subcommand.",
"group": "sentinel",
"since": "2.8.4",
diff --git a/src/commands/set.json b/src/commands/set.json
index 3c6c06ff4..8236bc7bb 100644
--- a/src/commands/set.json
+++ b/src/commands/set.json
@@ -1,6 +1,6 @@
{
"SET": {
- "summary": "Set the string value of a key",
+ "summary": "Sets the string value of a key, ignoring its type. The key is created if it doesn't exist.",
"complexity": "O(1)",
"group": "string",
"since": "1.0.0",
diff --git a/src/commands/setbit.json b/src/commands/setbit.json
index bf1843078..a1f6726aa 100644
--- a/src/commands/setbit.json
+++ b/src/commands/setbit.json
@@ -1,6 +1,6 @@
{
"SETBIT": {
- "summary": "Sets or clears the bit at offset in the string value stored at key",
+ "summary": "Sets or clears the bit at offset of the string value. Creates the key if it doesn't exist.",
"complexity": "O(1)",
"group": "bitmap",
"since": "2.2.0",
diff --git a/src/commands/setex.json b/src/commands/setex.json
index a45561a21..1543a41d7 100644
--- a/src/commands/setex.json
+++ b/src/commands/setex.json
@@ -1,6 +1,6 @@
{
"SETEX": {
- "summary": "Set the value and expiration of a key",
+ "summary": "Sets the string value and expiration time of a key. Creates the key if it doesn't exist.",
"complexity": "O(1)",
"group": "string",
"since": "2.0.0",
diff --git a/src/commands/setnx.json b/src/commands/setnx.json
index d026272bb..7459724e3 100644
--- a/src/commands/setnx.json
+++ b/src/commands/setnx.json
@@ -1,6 +1,6 @@
{
"SETNX": {
- "summary": "Set the value of a key, only if the key does not exist",
+ "summary": "Set the string value of a key only when the key doesn't exist.",
"complexity": "O(1)",
"group": "string",
"since": "1.0.0",
diff --git a/src/commands/setrange.json b/src/commands/setrange.json
index f9d61dfa3..32a8c521a 100644
--- a/src/commands/setrange.json
+++ b/src/commands/setrange.json
@@ -1,6 +1,6 @@
{
"SETRANGE": {
- "summary": "Overwrite part of a string at key starting at the specified offset",
+ "summary": "Overwrites a part of a string value with another by an offset. Creates the key if it doesn't exist.",
"complexity": "O(1), not counting the time taken to copy the new string in place. Usually, this string is very small so the amortized complexity is O(1). Otherwise, complexity is O(M) with M being the length of the value argument.",
"group": "string",
"since": "2.2.0",
diff --git a/src/commands/shutdown.json b/src/commands/shutdown.json
index 033a7012e..a9e45d42d 100644
--- a/src/commands/shutdown.json
+++ b/src/commands/shutdown.json
@@ -1,6 +1,6 @@
{
"SHUTDOWN": {
- "summary": "Synchronously save the dataset to disk and then shut down the server",
+ "summary": "Synchronously saves the database(s) to disk and shuts down the Redis server.",
"complexity": "O(N) when saving, where N is the total number of keys in all databases when saving data, otherwise O(1)",
"group": "server",
"since": "1.0.0",
diff --git a/src/commands/sinter.json b/src/commands/sinter.json
index f0cd95de0..ad5ac918b 100644
--- a/src/commands/sinter.json
+++ b/src/commands/sinter.json
@@ -1,6 +1,6 @@
{
"SINTER": {
- "summary": "Intersect multiple sets",
+ "summary": "Returns the intersect of multiple sets.",
"complexity": "O(N*M) worst case where N is the cardinality of the smallest set and M is the number of sets.",
"group": "set",
"since": "1.0.0",
diff --git a/src/commands/sintercard.json b/src/commands/sintercard.json
index 16769850d..9a79183fb 100644
--- a/src/commands/sintercard.json
+++ b/src/commands/sintercard.json
@@ -1,6 +1,6 @@
{
"SINTERCARD": {
- "summary": "Intersect multiple sets and return the cardinality of the result",
+ "summary": "Returns the number of members of the intersect of multiple sets.",
"complexity": "O(N*M) worst case where N is the cardinality of the smallest set and M is the number of sets.",
"group": "set",
"since": "7.0.0",
diff --git a/src/commands/sinterstore.json b/src/commands/sinterstore.json
index 60a8db52a..28ccfff69 100644
--- a/src/commands/sinterstore.json
+++ b/src/commands/sinterstore.json
@@ -1,6 +1,6 @@
{
"SINTERSTORE": {
- "summary": "Intersect multiple sets and store the resulting set in a key",
+ "summary": "Stores the intersect of multiple sets in a key.",
"complexity": "O(N*M) worst case where N is the cardinality of the smallest set and M is the number of sets.",
"group": "set",
"since": "1.0.0",
diff --git a/src/commands/sismember.json b/src/commands/sismember.json
index cb81682cc..51ef920be 100644
--- a/src/commands/sismember.json
+++ b/src/commands/sismember.json
@@ -1,6 +1,6 @@
{
"SISMEMBER": {
- "summary": "Determine if a given value is a member of a set",
+ "summary": "Determines whether a member belongs to a set.",
"complexity": "O(1)",
"group": "set",
"since": "1.0.0",
diff --git a/src/commands/slaveof.json b/src/commands/slaveof.json
index f9266fabe..9790730b2 100644
--- a/src/commands/slaveof.json
+++ b/src/commands/slaveof.json
@@ -1,6 +1,6 @@
{
"SLAVEOF": {
- "summary": "Make the server a replica of another instance, or promote it as master.",
+ "summary": "Sets a Redis server as a replica of another, or promotes it to being a master.",
"complexity": "O(1)",
"group": "server",
"since": "1.0.0",
diff --git a/src/commands/slowlog-get.json b/src/commands/slowlog-get.json
index e4652e895..ffc54b545 100644
--- a/src/commands/slowlog-get.json
+++ b/src/commands/slowlog-get.json
@@ -1,6 +1,6 @@
{
"GET": {
- "summary": "Get the slow log's entries",
+ "summary": "Returns the slow log's entries.",
"complexity": "O(N) where N is the number of entries returned",
"group": "server",
"since": "2.2.12",
diff --git a/src/commands/slowlog-len.json b/src/commands/slowlog-len.json
index f8c7798cf..717a8ad41 100644
--- a/src/commands/slowlog-len.json
+++ b/src/commands/slowlog-len.json
@@ -1,6 +1,6 @@
{
"LEN": {
- "summary": "Get the slow log's length",
+ "summary": "Returns the number of entries in the slow log.",
"complexity": "O(1)",
"group": "server",
"since": "2.2.12",
diff --git a/src/commands/slowlog-reset.json b/src/commands/slowlog-reset.json
index c4006e371..cfc1e4da7 100644
--- a/src/commands/slowlog-reset.json
+++ b/src/commands/slowlog-reset.json
@@ -1,6 +1,6 @@
{
"RESET": {
- "summary": "Clear all entries from the slow log",
+ "summary": "Clears all entries from the slow log.",
"complexity": "O(N) where N is the number of entries in the slowlog",
"group": "server",
"since": "2.2.12",
diff --git a/src/commands/slowlog.json b/src/commands/slowlog.json
index fab266c1d..1b9526b19 100644
--- a/src/commands/slowlog.json
+++ b/src/commands/slowlog.json
@@ -1,6 +1,6 @@
{
"SLOWLOG": {
- "summary": "A container for slow log commands",
+ "summary": "A container for slow log commands.",
"complexity": "Depends on subcommand.",
"group": "server",
"since": "2.2.12",
diff --git a/src/commands/smembers.json b/src/commands/smembers.json
index ff5969f51..c5114089b 100644
--- a/src/commands/smembers.json
+++ b/src/commands/smembers.json
@@ -1,6 +1,6 @@
{
"SMEMBERS": {
- "summary": "Get all the members in a set",
+ "summary": "Returns all members of a set.",
"complexity": "O(N) where N is the set cardinality.",
"group": "set",
"since": "1.0.0",
diff --git a/src/commands/smismember.json b/src/commands/smismember.json
index d78787900..dbc1ddc96 100644
--- a/src/commands/smismember.json
+++ b/src/commands/smismember.json
@@ -1,6 +1,6 @@
{
"SMISMEMBER": {
- "summary": "Returns the membership associated with the given elements for a set",
+ "summary": "Determines whether multiple members belong to a set.",
"complexity": "O(N) where N is the number of elements being checked for membership",
"group": "set",
"since": "6.2.0",
diff --git a/src/commands/smove.json b/src/commands/smove.json
index df282b2bb..9521bb364 100644
--- a/src/commands/smove.json
+++ b/src/commands/smove.json
@@ -1,6 +1,6 @@
{
"SMOVE": {
- "summary": "Move a member from one set to another",
+ "summary": "Moves a member from one set to another.",
"complexity": "O(1)",
"group": "set",
"since": "1.0.0",
diff --git a/src/commands/sort.json b/src/commands/sort.json
index 6e1b2626e..5e117c950 100644
--- a/src/commands/sort.json
+++ b/src/commands/sort.json
@@ -1,6 +1,6 @@
{
"SORT": {
- "summary": "Sort the elements in a list, set or sorted set",
+ "summary": "Sorts the elements in a list, a set, or a sorted set, optionally storing the result.",
"complexity": "O(N+M*log(M)) where N is the number of elements in the list or set to sort, and M the number of returned elements. When the elements are not sorted, complexity is O(N).",
"group": "generic",
"since": "1.0.0",
diff --git a/src/commands/sort_ro.json b/src/commands/sort_ro.json
index 27af54c99..8b32b17fa 100644
--- a/src/commands/sort_ro.json
+++ b/src/commands/sort_ro.json
@@ -1,6 +1,6 @@
{
"SORT_RO": {
- "summary": "Sort the elements in a list, set or sorted set. Read-only variant of SORT.",
+ "summary": "Returns the sorted elements of a list, a set, or a sorted set.",
"complexity": "O(N+M*log(M)) where N is the number of elements in the list or set to sort, and M the number of returned elements. When the elements are not sorted, complexity is O(N).",
"group": "generic",
"since": "7.0.0",
diff --git a/src/commands/spop.json b/src/commands/spop.json
index a116c8473..c3954bef5 100644
--- a/src/commands/spop.json
+++ b/src/commands/spop.json
@@ -1,6 +1,6 @@
{
"SPOP": {
- "summary": "Remove and return one or multiple random members from a set",
+ "summary": "Returns one or more random members from a set after removing them. Deletes the set if the last member was popped.",
"complexity": "Without the count argument O(1), otherwise O(N) where N is the value of the passed count.",
"group": "set",
"since": "1.0.0",
diff --git a/src/commands/srem.json b/src/commands/srem.json
index ec9ab41db..d7797cf0f 100644
--- a/src/commands/srem.json
+++ b/src/commands/srem.json
@@ -1,6 +1,6 @@
{
"SREM": {
- "summary": "Remove one or more members from a set",
+ "summary": "Removes one or more members from a set. Deletes the set if the last member was removed.",
"complexity": "O(N) where N is the number of members to be removed.",
"group": "set",
"since": "1.0.0",
diff --git a/src/commands/sscan.json b/src/commands/sscan.json
index f0d89e224..b221c94cf 100644
--- a/src/commands/sscan.json
+++ b/src/commands/sscan.json
@@ -1,6 +1,6 @@
{
"SSCAN": {
- "summary": "Incrementally iterate Set elements",
+ "summary": "Iterates over members of a set.",
"complexity": "O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.",
"group": "set",
"since": "2.8.0",
diff --git a/src/commands/ssubscribe.json b/src/commands/ssubscribe.json
index a63d520f1..46373d541 100644
--- a/src/commands/ssubscribe.json
+++ b/src/commands/ssubscribe.json
@@ -1,6 +1,6 @@
{
"SSUBSCRIBE": {
- "summary": "Listen for messages published to the given shard channels",
+ "summary": "Listens for messages published to shard channels.",
"complexity": "O(N) where N is the number of shard channels to subscribe to.",
"group": "pubsub",
"since": "7.0.0",
diff --git a/src/commands/strlen.json b/src/commands/strlen.json
index 3cdce4807..395a02dfa 100644
--- a/src/commands/strlen.json
+++ b/src/commands/strlen.json
@@ -1,6 +1,6 @@
{
"STRLEN": {
- "summary": "Get the length of the value stored in a key",
+ "summary": "Returns the length of a string value.",
"complexity": "O(1)",
"group": "string",
"since": "2.2.0",
diff --git a/src/commands/subscribe.json b/src/commands/subscribe.json
index fa6ac076a..bdf12b726 100644
--- a/src/commands/subscribe.json
+++ b/src/commands/subscribe.json
@@ -1,6 +1,6 @@
{
"SUBSCRIBE": {
- "summary": "Listen for messages published to the given channels",
+ "summary": "Listens for messages published to channels.",
"complexity": "O(N) where N is the number of channels to subscribe to.",
"group": "pubsub",
"since": "2.0.0",
diff --git a/src/commands/substr.json b/src/commands/substr.json
index c1134ce66..124418320 100644
--- a/src/commands/substr.json
+++ b/src/commands/substr.json
@@ -1,6 +1,6 @@
{
"SUBSTR": {
- "summary": "Get a substring of the string stored at a key",
+ "summary": "Returns a substring from a string value.",
"complexity": "O(N) where N is the length of the returned string. The complexity is ultimately determined by the returned length, but because creating a substring from an existing string is very cheap, it can be considered O(1) for small strings.",
"group": "string",
"since": "1.0.0",
diff --git a/src/commands/sunion.json b/src/commands/sunion.json
index 3873a6a39..56f2b9e9b 100644
--- a/src/commands/sunion.json
+++ b/src/commands/sunion.json
@@ -1,6 +1,6 @@
{
"SUNION": {
- "summary": "Add multiple sets",
+ "summary": "Returns the union of multiple sets.",
"complexity": "O(N) where N is the total number of elements in all given sets.",
"group": "set",
"since": "1.0.0",
diff --git a/src/commands/sunionstore.json b/src/commands/sunionstore.json
index b703904ac..94d4e16e9 100644
--- a/src/commands/sunionstore.json
+++ b/src/commands/sunionstore.json
@@ -1,6 +1,6 @@
{
"SUNIONSTORE": {
- "summary": "Add multiple sets and store the resulting set in a key",
+ "summary": "Stores the union of multiple sets in a key.",
"complexity": "O(N) where N is the total number of elements in all given sets.",
"group": "set",
"since": "1.0.0",
diff --git a/src/commands/sunsubscribe.json b/src/commands/sunsubscribe.json
index df9ae9cac..7965a05ea 100644
--- a/src/commands/sunsubscribe.json
+++ b/src/commands/sunsubscribe.json
@@ -1,6 +1,6 @@
{
"SUNSUBSCRIBE": {
- "summary": "Stop listening for messages posted to the given shard channels",
+ "summary": "Stops listening to messages posted to shard channels.",
"complexity": "O(N) where N is the number of clients already subscribed to a shard channel.",
"group": "pubsub",
"since": "7.0.0",
diff --git a/src/commands/swapdb.json b/src/commands/swapdb.json
index 7ed001871..e98bc768b 100644
--- a/src/commands/swapdb.json
+++ b/src/commands/swapdb.json
@@ -1,6 +1,6 @@
{
"SWAPDB": {
- "summary": "Swaps two Redis databases",
+ "summary": "Swaps two Redis databases.",
"complexity": "O(N) where N is the count of clients watching or blocking on keys from both databases.",
"group": "server",
"since": "4.0.0",
diff --git a/src/commands/sync.json b/src/commands/sync.json
index 85672d6ed..e18c337d8 100644
--- a/src/commands/sync.json
+++ b/src/commands/sync.json
@@ -1,6 +1,6 @@
{
"SYNC": {
- "summary": "Internal command used for replication",
+ "summary": "An internal command used in replication.",
"group": "server",
"since": "1.0.0",
"arity": 1,
diff --git a/src/commands/time.json b/src/commands/time.json
index 540190f55..3161d3fcd 100644
--- a/src/commands/time.json
+++ b/src/commands/time.json
@@ -1,6 +1,6 @@
{
"TIME": {
- "summary": "Return the current server time",
+ "summary": "Returns the server time.",
"complexity": "O(1)",
"group": "server",
"since": "2.6.0",
diff --git a/src/commands/touch.json b/src/commands/touch.json
index b2a2894c9..fd1dc61c3 100644
--- a/src/commands/touch.json
+++ b/src/commands/touch.json
@@ -1,6 +1,6 @@
{
"TOUCH": {
- "summary": "Alters the last access time of a key(s). Returns the number of existing keys specified.",
+ "summary": "Returns the number of existing keys out of those specified after updating the time they were last accessed.",
"complexity": "O(N) where N is the number of keys that will be touched.",
"group": "generic",
"since": "3.2.1",
diff --git a/src/commands/ttl.json b/src/commands/ttl.json
index f4d957825..9f5ab89e1 100644
--- a/src/commands/ttl.json
+++ b/src/commands/ttl.json
@@ -1,6 +1,6 @@
{
"TTL": {
- "summary": "Get the time to live for a key in seconds",
+ "summary": "Returns the expiration time in seconds of a key.",
"complexity": "O(1)",
"group": "generic",
"since": "1.0.0",
diff --git a/src/commands/type.json b/src/commands/type.json
index b4a4e766f..e8353b9f4 100644
--- a/src/commands/type.json
+++ b/src/commands/type.json
@@ -1,6 +1,6 @@
{
"TYPE": {
- "summary": "Determine the type stored at key",
+ "summary": "Determines the type of value stored at a key.",
"complexity": "O(1)",
"group": "generic",
"since": "1.0.0",
diff --git a/src/commands/unlink.json b/src/commands/unlink.json
index 559f1e96b..a05704a1b 100644
--- a/src/commands/unlink.json
+++ b/src/commands/unlink.json
@@ -1,6 +1,6 @@
{
"UNLINK": {
- "summary": "Delete a key asynchronously in another thread. Otherwise it is just as DEL, but non blocking.",
+ "summary": "Asynchronously deletes one or more keys.",
"complexity": "O(1) for each key removed regardless of its size. Then the command does O(N) work in a different thread in order to reclaim memory, where N is the number of allocations the deleted objects where composed of.",
"group": "generic",
"since": "4.0.0",
diff --git a/src/commands/unsubscribe.json b/src/commands/unsubscribe.json
index d98aaa278..530facde4 100644
--- a/src/commands/unsubscribe.json
+++ b/src/commands/unsubscribe.json
@@ -1,6 +1,6 @@
{
"UNSUBSCRIBE": {
- "summary": "Stop listening for messages posted to the given channels",
+ "summary": "Stops listening to messages posted to channels.",
"complexity": "O(N) where N is the number of clients already subscribed to a channel.",
"group": "pubsub",
"since": "2.0.0",
diff --git a/src/commands/unwatch.json b/src/commands/unwatch.json
index 256411f1d..28cc5f0db 100644
--- a/src/commands/unwatch.json
+++ b/src/commands/unwatch.json
@@ -1,6 +1,6 @@
{
"UNWATCH": {
- "summary": "Forget about all watched keys",
+ "summary": "Forgets about watched keys of a transaction.",
"complexity": "O(1)",
"group": "transactions",
"since": "2.2.0",
diff --git a/src/commands/wait.json b/src/commands/wait.json
index 92ec4af3e..f936b9242 100644
--- a/src/commands/wait.json
+++ b/src/commands/wait.json
@@ -1,6 +1,6 @@
{
"WAIT": {
- "summary": "Wait for the synchronous replication of all the write commands sent in the context of the current connection",
+ "summary": "Blocks until the asynchronous replication of all preceding write commands sent by the connection is completed.",
"complexity": "O(1)",
"group": "generic",
"since": "3.0.0",
diff --git a/src/commands/waitaof.json b/src/commands/waitaof.json
index bea18cae9..735a8f261 100644
--- a/src/commands/waitaof.json
+++ b/src/commands/waitaof.json
@@ -1,6 +1,6 @@
{
"WAITAOF": {
- "summary": "Wait for all write commands sent in the context of the current connection to be synced to AOF of local host and/or replicas",
+ "summary": "Blocks until all of the preceding write commands sent by the connection are written to the append-only file of the master and/or replicas.",
"complexity": "O(1)",
"group": "generic",
"since": "7.2.0",
diff --git a/src/commands/watch.json b/src/commands/watch.json
index 3f16f7360..9faab2b91 100644
--- a/src/commands/watch.json
+++ b/src/commands/watch.json
@@ -1,6 +1,6 @@
{
"WATCH": {
- "summary": "Watch the given keys to determine execution of the MULTI/EXEC block",
+ "summary": "Monitors changes to keys to determine the execution of a transaction.",
"complexity": "O(1) for every key.",
"group": "transactions",
"since": "2.2.0",
diff --git a/src/commands/xack.json b/src/commands/xack.json
index f7791f270..4a1e92b25 100644
--- a/src/commands/xack.json
+++ b/src/commands/xack.json
@@ -1,6 +1,6 @@
{
"XACK": {
- "summary": "Marks a pending message as correctly processed, effectively removing it from the pending entries list of the consumer group. Return value of the command is the number of messages successfully acknowledged, that is, the IDs we were actually able to resolve in the PEL.",
+ "summary": "Returns the number of messages that were successfully acknowledged by the consumer group member of a stream.",
"complexity": "O(1) for each message ID processed.",
"group": "stream",
"since": "5.0.0",
diff --git a/src/commands/xadd.json b/src/commands/xadd.json
index 72f864d92..21880524e 100644
--- a/src/commands/xadd.json
+++ b/src/commands/xadd.json
@@ -1,6 +1,6 @@
{
"XADD": {
- "summary": "Appends a new entry to a stream",
+ "summary": "Appends a new message to a stream. Creates the key if it doesn't exist.",
"complexity": "O(1) when adding a new entry, O(N) when trimming where N being the number of entries evicted.",
"group": "stream",
"since": "5.0.0",
diff --git a/src/commands/xautoclaim.json b/src/commands/xautoclaim.json
index f23386e4c..2e8e9c183 100644
--- a/src/commands/xautoclaim.json
+++ b/src/commands/xautoclaim.json
@@ -1,6 +1,6 @@
{
"XAUTOCLAIM": {
- "summary": "Changes (or acquires) ownership of messages in a consumer group, as if the messages were delivered to the specified consumer.",
+ "summary": "Changes, or acquires, ownership of messages in a consumer group, as if the messages were delivered to as consumer group member.",
"complexity": "O(1) if COUNT is small.",
"group": "stream",
"since": "6.2.0",
diff --git a/src/commands/xclaim.json b/src/commands/xclaim.json
index 950c215b9..5c4474606 100644
--- a/src/commands/xclaim.json
+++ b/src/commands/xclaim.json
@@ -1,6 +1,6 @@
{
"XCLAIM": {
- "summary": "Changes (or acquires) ownership of a message in a consumer group, as if the message was delivered to the specified consumer.",
+ "summary": "Changes, or acquires, ownership of a message in a consumer group, as if the message was delivered a consumer group member.",
"complexity": "O(log N) with N being the number of messages in the PEL of the consumer group.",
"group": "stream",
"since": "5.0.0",
diff --git a/src/commands/xdel.json b/src/commands/xdel.json
index 5854f5084..5cf4a700a 100644
--- a/src/commands/xdel.json
+++ b/src/commands/xdel.json
@@ -1,6 +1,6 @@
{
"XDEL": {
- "summary": "Removes the specified entries from the stream. Returns the number of items actually deleted, that may be different from the number of IDs passed in case certain IDs do not exist.",
+ "summary": "Returns the number of messages after removing them from a stream.",
"complexity": "O(1) for each single item to delete in the stream, regardless of the stream size.",
"group": "stream",
"since": "5.0.0",
diff --git a/src/commands/xgroup-create.json b/src/commands/xgroup-create.json
index 16a8b99eb..6b11a1f00 100644
--- a/src/commands/xgroup-create.json
+++ b/src/commands/xgroup-create.json
@@ -1,6 +1,6 @@
{
"CREATE": {
- "summary": "Create a consumer group.",
+ "summary": "Creates a consumer group.",
"complexity": "O(1)",
"group": "stream",
"since": "5.0.0",
diff --git a/src/commands/xgroup-createconsumer.json b/src/commands/xgroup-createconsumer.json
index 764b26447..2f3d6a9bb 100644
--- a/src/commands/xgroup-createconsumer.json
+++ b/src/commands/xgroup-createconsumer.json
@@ -1,6 +1,6 @@
{
"CREATECONSUMER": {
- "summary": "Create a consumer in a consumer group.",
+ "summary": "Creates a consumer in a consumer group.",
"complexity": "O(1)",
"group": "stream",
"since": "6.2.0",
diff --git a/src/commands/xgroup-delconsumer.json b/src/commands/xgroup-delconsumer.json
index c68f0b567..12244f8d5 100644
--- a/src/commands/xgroup-delconsumer.json
+++ b/src/commands/xgroup-delconsumer.json
@@ -1,6 +1,6 @@
{
"DELCONSUMER": {
- "summary": "Delete a consumer from a consumer group.",
+ "summary": "Deletes a consumer from a consumer group.",
"complexity": "O(1)",
"group": "stream",
"since": "5.0.0",
diff --git a/src/commands/xgroup-destroy.json b/src/commands/xgroup-destroy.json
index 60a481a80..c9affbdb4 100644
--- a/src/commands/xgroup-destroy.json
+++ b/src/commands/xgroup-destroy.json
@@ -1,6 +1,6 @@
{
"DESTROY": {
- "summary": "Destroy a consumer group.",
+ "summary": "Destroys a consumer group.",
"complexity": "O(N) where N is the number of entries in the group's pending entries list (PEL).",
"group": "stream",
"since": "5.0.0",
diff --git a/src/commands/xgroup-help.json b/src/commands/xgroup-help.json
index d4e9d4ad3..3d2a73862 100644
--- a/src/commands/xgroup-help.json
+++ b/src/commands/xgroup-help.json
@@ -1,6 +1,6 @@
{
"HELP": {
- "summary": "Show helpful text about the different subcommands",
+ "summary": "Returns helpful text about the different subcommands.",
"complexity": "O(1)",
"group": "stream",
"since": "5.0.0",
diff --git a/src/commands/xgroup-setid.json b/src/commands/xgroup-setid.json
index 89bbc2a6b..76a3c79ef 100644
--- a/src/commands/xgroup-setid.json
+++ b/src/commands/xgroup-setid.json
@@ -1,6 +1,6 @@
{
"SETID": {
- "summary": "Set a consumer group to an arbitrary last delivered ID value.",
+ "summary": "Sets the last-delivered ID of a consumer group.",
"complexity": "O(1)",
"group": "stream",
"since": "5.0.0",
diff --git a/src/commands/xgroup.json b/src/commands/xgroup.json
index 2af53cfcc..4910b7c05 100644
--- a/src/commands/xgroup.json
+++ b/src/commands/xgroup.json
@@ -1,6 +1,6 @@
{
"XGROUP": {
- "summary": "A container for consumer groups commands",
+ "summary": "A container for consumer groups commands.",
"complexity": "Depends on subcommand.",
"group": "stream",
"since": "5.0.0",
diff --git a/src/commands/xinfo-consumers.json b/src/commands/xinfo-consumers.json
index 98274bdf6..b507e8e59 100644
--- a/src/commands/xinfo-consumers.json
+++ b/src/commands/xinfo-consumers.json
@@ -1,6 +1,6 @@
{
"CONSUMERS": {
- "summary": "List the consumers in a consumer group",
+ "summary": "Returns a list of the consumers in a consumer group.",
"complexity": "O(1)",
"group": "stream",
"since": "5.0.0",
diff --git a/src/commands/xinfo-groups.json b/src/commands/xinfo-groups.json
index 4196773f3..a9cbe8eba 100644
--- a/src/commands/xinfo-groups.json
+++ b/src/commands/xinfo-groups.json
@@ -1,6 +1,6 @@
{
"GROUPS": {
- "summary": "List the consumer groups of a stream",
+ "summary": "Returns a list of the consumer groups of a stream.",
"complexity": "O(1)",
"group": "stream",
"since": "5.0.0",
diff --git a/src/commands/xinfo-help.json b/src/commands/xinfo-help.json
index 3441ace14..d4cbe3d34 100644
--- a/src/commands/xinfo-help.json
+++ b/src/commands/xinfo-help.json
@@ -1,6 +1,6 @@
{
"HELP": {
- "summary": "Show helpful text about the different subcommands",
+ "summary": "Returns helpful text about the different subcommands.",
"complexity": "O(1)",
"group": "stream",
"since": "5.0.0",
diff --git a/src/commands/xinfo-stream.json b/src/commands/xinfo-stream.json
index 7db07fb9b..018826f91 100644
--- a/src/commands/xinfo-stream.json
+++ b/src/commands/xinfo-stream.json
@@ -1,6 +1,6 @@
{
"STREAM": {
- "summary": "Get information about a stream",
+ "summary": "Returns information about a stream.",
"complexity": "O(1)",
"group": "stream",
"since": "5.0.0",
diff --git a/src/commands/xinfo.json b/src/commands/xinfo.json
index c969e59a7..cc85bf114 100644
--- a/src/commands/xinfo.json
+++ b/src/commands/xinfo.json
@@ -1,6 +1,6 @@
{
"XINFO": {
- "summary": "A container for stream introspection commands",
+ "summary": "A container for stream introspection commands.",
"complexity": "Depends on subcommand.",
"group": "stream",
"since": "5.0.0",
diff --git a/src/commands/xlen.json b/src/commands/xlen.json
index b3a82f774..16ce72cb6 100644
--- a/src/commands/xlen.json
+++ b/src/commands/xlen.json
@@ -1,6 +1,6 @@
{
"XLEN": {
- "summary": "Return the number of entries in a stream",
+ "summary": "Return the number of messages in a stream.",
"complexity": "O(1)",
"group": "stream",
"since": "5.0.0",
diff --git a/src/commands/xpending.json b/src/commands/xpending.json
index 04725e1e4..a6df80192 100644
--- a/src/commands/xpending.json
+++ b/src/commands/xpending.json
@@ -1,6 +1,6 @@
{
"XPENDING": {
- "summary": "Return information and entries from a stream consumer group pending entries list, that are messages fetched but never acknowledged.",
+ "summary": "Returns the information and entries from a stream consumer group's pending entries list.",
"complexity": "O(N) with N being the number of elements returned, so asking for a small fixed number of entries per call is O(1). O(M), where M is the total number of entries scanned when used with the IDLE filter. When the command returns just the summary and the list of consumers is small, it runs in O(1) time; otherwise, an additional O(N) time for iterating every consumer.",
"group": "stream",
"since": "5.0.0",
diff --git a/src/commands/xrange.json b/src/commands/xrange.json
index 325a4564a..edfe2ccfd 100644
--- a/src/commands/xrange.json
+++ b/src/commands/xrange.json
@@ -1,6 +1,6 @@
{
"XRANGE": {
- "summary": "Return a range of elements in a stream, with IDs matching the specified IDs interval",
+ "summary": "Returns the messages from a stream within a range of IDs.",
"complexity": "O(N) with N being the number of elements being returned. If N is constant (e.g. always asking for the first 10 elements with COUNT), you can consider it O(1).",
"group": "stream",
"since": "5.0.0",
diff --git a/src/commands/xread.json b/src/commands/xread.json
index e6978794a..3a78ffb22 100644
--- a/src/commands/xread.json
+++ b/src/commands/xread.json
@@ -1,7 +1,6 @@
{
"XREAD": {
- "summary": "Return never seen elements in multiple streams, with IDs greater than the ones reported by the caller for each stream. Can block.",
- "complexity": "For each stream mentioned: O(N) with N being the number of elements being returned, it means that XREAD-ing with a fixed COUNT is O(1). Note that when the BLOCK option is used, XADD will pay O(M) time in order to serve the M clients blocked on the stream getting new data.",
+ "summary": "Returns messages from multiple streams with IDs greater than the ones requested. Blocks until a message is available otherwise.",
"group": "stream",
"since": "5.0.0",
"arity": -4,
diff --git a/src/commands/xreadgroup.json b/src/commands/xreadgroup.json
index 96d7a89ba..93e45a877 100644
--- a/src/commands/xreadgroup.json
+++ b/src/commands/xreadgroup.json
@@ -1,6 +1,6 @@
{
"XREADGROUP": {
- "summary": "Return new entries from a stream using a consumer group, or access the history of the pending entries for a given consumer. Can block.",
+ "summary": "Returns new or historical messages from a stream for a consumer in a group. Blocks until a message is available otherwise.",
"complexity": "For each stream mentioned: O(M) with M being the number of elements returned. If M is constant (e.g. always asking for the first 10 elements with COUNT), you can consider it O(1). On the other side when XREADGROUP blocks, XADD will pay the O(N) time in order to serve the N clients blocked on the stream getting new data.",
"group": "stream",
"since": "5.0.0",
diff --git a/src/commands/xrevrange.json b/src/commands/xrevrange.json
index 41798a16c..a0c3e4f3e 100644
--- a/src/commands/xrevrange.json
+++ b/src/commands/xrevrange.json
@@ -1,6 +1,6 @@
{
"XREVRANGE": {
- "summary": "Return a range of elements in a stream, with IDs matching the specified IDs interval, in reverse order (from greater to smaller IDs) compared to XRANGE",
+ "summary": "Returns the messages from a stream within a range of IDs in reverse order.",
"complexity": "O(N) with N being the number of elements returned. If N is constant (e.g. always asking for the first 10 elements with COUNT), you can consider it O(1).",
"group": "stream",
"since": "5.0.0",
diff --git a/src/commands/xsetid.json b/src/commands/xsetid.json
index b69d80c88..460703717 100644
--- a/src/commands/xsetid.json
+++ b/src/commands/xsetid.json
@@ -1,6 +1,6 @@
{
"XSETID": {
- "summary": "An internal command for replicating stream values",
+ "summary": "An internal command for replicating stream values.",
"complexity": "O(1)",
"group": "stream",
"since": "5.0.0",
diff --git a/src/commands/xtrim.json b/src/commands/xtrim.json
index 6abd4903b..0b79cd468 100644
--- a/src/commands/xtrim.json
+++ b/src/commands/xtrim.json
@@ -1,6 +1,6 @@
{
"XTRIM": {
- "summary": "Trims the stream to (approximately if '~' is passed) a certain size",
+ "summary": "Deletes messages from the beginning of a stream.",
"complexity": "O(N), with N being the number of evicted entries. Constant times are very small however, since entries are organized in macro nodes containing multiple entries that can be released with a single deallocation.",
"group": "stream",
"since": "5.0.0",
diff --git a/src/commands/zadd.json b/src/commands/zadd.json
index e2cbd98c8..d489ee4fa 100644
--- a/src/commands/zadd.json
+++ b/src/commands/zadd.json
@@ -1,6 +1,6 @@
{
"ZADD": {
- "summary": "Add one or more members to a sorted set, or update its score if it already exists",
+ "summary": "Adds one or more members to a sorted set, or updates their scores. Creates the key if it doesn't exist.",
"complexity": "O(log(N)) for each item added, where N is the number of elements in the sorted set.",
"group": "sorted_set",
"since": "1.2.0",
diff --git a/src/commands/zcard.json b/src/commands/zcard.json
index f80a01007..58683a487 100644
--- a/src/commands/zcard.json
+++ b/src/commands/zcard.json
@@ -1,6 +1,6 @@
{
"ZCARD": {
- "summary": "Get the number of members in a sorted set",
+ "summary": "Returns the number of members in a sorted set.",
"complexity": "O(1)",
"group": "sorted_set",
"since": "1.2.0",
diff --git a/src/commands/zcount.json b/src/commands/zcount.json
index 9ad8fdb24..0fdebd7df 100644
--- a/src/commands/zcount.json
+++ b/src/commands/zcount.json
@@ -1,6 +1,6 @@
{
"ZCOUNT": {
- "summary": "Count the members in a sorted set with scores within the given values",
+ "summary": "Returns the count of members in a sorted set that have scores within a range.",
"complexity": "O(log(N)) with N being the number of elements in the sorted set.",
"group": "sorted_set",
"since": "2.0.0",
diff --git a/src/commands/zdiff.json b/src/commands/zdiff.json
index a361e249d..912d5c6d0 100644
--- a/src/commands/zdiff.json
+++ b/src/commands/zdiff.json
@@ -1,6 +1,6 @@
{
"ZDIFF": {
- "summary": "Subtract multiple sorted sets",
+ "summary": "Returns the difference between multiple sorted sets.",
"complexity": "O(L + (N-K)log(N)) worst case where L is the total number of elements in all the sets, N is the size of the first set, and K is the size of the result set.",
"group": "sorted_set",
"since": "6.2.0",
diff --git a/src/commands/zdiffstore.json b/src/commands/zdiffstore.json
index 26f205edc..35b720347 100644
--- a/src/commands/zdiffstore.json
+++ b/src/commands/zdiffstore.json
@@ -1,6 +1,6 @@
{
"ZDIFFSTORE": {
- "summary": "Subtract multiple sorted sets and store the resulting sorted set in a new key",
+ "summary": "Stores the difference of multiple sorted sets in a key.",
"complexity": "O(L + (N-K)log(N)) worst case where L is the total number of elements in all the sets, N is the size of the first set, and K is the size of the result set.",
"group": "sorted_set",
"since": "6.2.0",
diff --git a/src/commands/zincrby.json b/src/commands/zincrby.json
index a3283a3b6..683088327 100644
--- a/src/commands/zincrby.json
+++ b/src/commands/zincrby.json
@@ -1,6 +1,6 @@
{
"ZINCRBY": {
- "summary": "Increment the score of a member in a sorted set",
+ "summary": "Increments the score of a member in a sorted set.",
"complexity": "O(log(N)) where N is the number of elements in the sorted set.",
"group": "sorted_set",
"since": "1.2.0",
diff --git a/src/commands/zinter.json b/src/commands/zinter.json
index d0dd046ed..4828e21d6 100644
--- a/src/commands/zinter.json
+++ b/src/commands/zinter.json
@@ -1,6 +1,6 @@
{
"ZINTER": {
- "summary": "Intersect multiple sorted sets",
+ "summary": "Returns the intersect of multiple sorted sets.",
"complexity": "O(N*K)+O(M*log(M)) worst case with N being the smallest input sorted set, K being the number of input sorted sets and M being the number of elements in the resulting sorted set.",
"group": "sorted_set",
"since": "6.2.0",
diff --git a/src/commands/zintercard.json b/src/commands/zintercard.json
index 732bab830..7fdab3ed6 100644
--- a/src/commands/zintercard.json
+++ b/src/commands/zintercard.json
@@ -1,6 +1,6 @@
{
"ZINTERCARD": {
- "summary": "Intersect multiple sorted sets and return the cardinality of the result",
+ "summary": "Returns the number of members of the intersect of multiple sorted sets.",
"complexity": "O(N*K) worst case with N being the smallest input sorted set, K being the number of input sorted sets.",
"group": "sorted_set",
"since": "7.0.0",
diff --git a/src/commands/zinterstore.json b/src/commands/zinterstore.json
index 32661c564..5bd940c65 100644
--- a/src/commands/zinterstore.json
+++ b/src/commands/zinterstore.json
@@ -1,6 +1,6 @@
{
"ZINTERSTORE": {
- "summary": "Intersect multiple sorted sets and store the resulting sorted set in a new key",
+ "summary": "Stores the intersect of multiple sorted sets in a key.",
"complexity": "O(N*K)+O(M*log(M)) worst case with N being the smallest input sorted set, K being the number of input sorted sets and M being the number of elements in the resulting sorted set.",
"group": "sorted_set",
"since": "2.0.0",
diff --git a/src/commands/zlexcount.json b/src/commands/zlexcount.json
index 5366441a6..8bf2884c9 100644
--- a/src/commands/zlexcount.json
+++ b/src/commands/zlexcount.json
@@ -1,6 +1,6 @@
{
"ZLEXCOUNT": {
- "summary": "Count the number of members in a sorted set between a given lexicographical range",
+ "summary": "Returns the number of members in a sorted set within a lexicographical range.",
"complexity": "O(log(N)) with N being the number of elements in the sorted set.",
"group": "sorted_set",
"since": "2.8.9",
diff --git a/src/commands/zmpop.json b/src/commands/zmpop.json
index 58e710170..86dc3bab2 100644
--- a/src/commands/zmpop.json
+++ b/src/commands/zmpop.json
@@ -1,6 +1,6 @@
{
"ZMPOP": {
- "summary": "Remove and return members with scores in a sorted set",
+ "summary": "Returns the highest- or lowest-scoring members from one or more sorted sets after removing them. Deletes the sorted set if the last member was popped.",
"complexity": "O(K) + O(M*log(N)) where K is the number of provided keys, N being the number of elements in the sorted set, and M being the number of elements popped.",
"group": "sorted_set",
"since": "7.0.0",
diff --git a/src/commands/zmscore.json b/src/commands/zmscore.json
index fa2fba141..6a036fe0b 100644
--- a/src/commands/zmscore.json
+++ b/src/commands/zmscore.json
@@ -1,6 +1,6 @@
{
"ZMSCORE": {
- "summary": "Get the score associated with the given members in a sorted set",
+ "summary": "Returns the score of one or more members in a sorted set.",
"complexity": "O(N) where N is the number of members being requested.",
"group": "sorted_set",
"since": "6.2.0",
diff --git a/src/commands/zpopmax.json b/src/commands/zpopmax.json
index 33bb85c51..56d86bf85 100644
--- a/src/commands/zpopmax.json
+++ b/src/commands/zpopmax.json
@@ -1,6 +1,6 @@
{
"ZPOPMAX": {
- "summary": "Remove and return members with the highest scores in a sorted set",
+ "summary": "Returns the highest-scoring members from a sorted set after removing them. Deletes the sorted set if the last member was popped.",
"complexity": "O(log(N)*M) with N being the number of elements in the sorted set, and M being the number of elements popped.",
"group": "sorted_set",
"since": "5.0.0",
diff --git a/src/commands/zpopmin.json b/src/commands/zpopmin.json
index e583eeea0..3fe36f343 100644
--- a/src/commands/zpopmin.json
+++ b/src/commands/zpopmin.json
@@ -1,6 +1,6 @@
{
"ZPOPMIN": {
- "summary": "Remove and return members with the lowest scores in a sorted set",
+ "summary": "Returns the lowest-scoring members from a sorted set after removing them. Deletes the sorted set if the last member was popped.",
"complexity": "O(log(N)*M) with N being the number of elements in the sorted set, and M being the number of elements popped.",
"group": "sorted_set",
"since": "5.0.0",
diff --git a/src/commands/zrandmember.json b/src/commands/zrandmember.json
index 1da1ce68b..13abc9aa3 100644
--- a/src/commands/zrandmember.json
+++ b/src/commands/zrandmember.json
@@ -1,7 +1,7 @@
{
"ZRANDMEMBER": {
- "summary": "Get one or multiple random elements from a sorted set",
- "complexity": "O(N) where N is the number of elements returned",
+ "summary": "Returns one or more random members from a sorted set.",
+ "complexity": "O(N) where N is the number of members returned",
"group": "sorted_set",
"since": "6.2.0",
"arity": -2,
diff --git a/src/commands/zrange.json b/src/commands/zrange.json
index 24a387160..dc7af8dc1 100644
--- a/src/commands/zrange.json
+++ b/src/commands/zrange.json
@@ -1,6 +1,6 @@
{
"ZRANGE": {
- "summary": "Return a range of members in a sorted set",
+ "summary": "Returns members in a sorted set within a range of indexes.",
"complexity": "O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements returned.",
"group": "sorted_set",
"since": "1.2.0",
diff --git a/src/commands/zrangebylex.json b/src/commands/zrangebylex.json
index d9d8a3a19..5949b8716 100644
--- a/src/commands/zrangebylex.json
+++ b/src/commands/zrangebylex.json
@@ -1,6 +1,6 @@
{
"ZRANGEBYLEX": {
- "summary": "Return a range of members in a sorted set, by lexicographical range",
+ "summary": "Returns members in a sorted set within a lexicographical range.",
"complexity": "O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).",
"group": "sorted_set",
"since": "2.8.9",
diff --git a/src/commands/zrangebyscore.json b/src/commands/zrangebyscore.json
index b603db4da..557ef1dc6 100644
--- a/src/commands/zrangebyscore.json
+++ b/src/commands/zrangebyscore.json
@@ -1,6 +1,6 @@
{
"ZRANGEBYSCORE": {
- "summary": "Return a range of members in a sorted set, by score",
+ "summary": "Returns members in a sorted set within a range of scores.",
"complexity": "O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).",
"group": "sorted_set",
"since": "1.0.5",
diff --git a/src/commands/zrangestore.json b/src/commands/zrangestore.json
index f072553b7..8eeaf74db 100644
--- a/src/commands/zrangestore.json
+++ b/src/commands/zrangestore.json
@@ -1,6 +1,6 @@
{
"ZRANGESTORE": {
- "summary": "Store a range of members from sorted set into another key",
+ "summary": "Stores a range of members from sorted set in a key.",
"complexity": "O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements stored into the destination key.",
"group": "sorted_set",
"since": "6.2.0",
diff --git a/src/commands/zrank.json b/src/commands/zrank.json
index d08897e27..f8ebda94a 100644
--- a/src/commands/zrank.json
+++ b/src/commands/zrank.json
@@ -1,6 +1,6 @@
{
"ZRANK": {
- "summary": "Determine the index of a member in a sorted set",
+ "summary": "Returns the index of a member in a sorted set ordered by ascending scores.",
"complexity": "O(log(N))",
"group": "sorted_set",
"since": "2.0.0",
diff --git a/src/commands/zrem.json b/src/commands/zrem.json
index f8fceeadb..8766124ea 100644
--- a/src/commands/zrem.json
+++ b/src/commands/zrem.json
@@ -1,6 +1,6 @@
{
"ZREM": {
- "summary": "Remove one or more members from a sorted set",
+ "summary": "Removes one or more members from a sorted set. Deletes the sorted set if all members were removed.",
"complexity": "O(M*log(N)) with N being the number of elements in the sorted set and M the number of elements to be removed.",
"group": "sorted_set",
"since": "1.2.0",
diff --git a/src/commands/zremrangebylex.json b/src/commands/zremrangebylex.json
index 34eb99980..169472c0b 100644
--- a/src/commands/zremrangebylex.json
+++ b/src/commands/zremrangebylex.json
@@ -1,6 +1,6 @@
{
"ZREMRANGEBYLEX": {
- "summary": "Remove all members in a sorted set between the given lexicographical range",
+ "summary": "Removes members in a sorted set within a lexicographical range. Deletes the sorted set if all members were removed.",
"complexity": "O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements removed by the operation.",
"group": "sorted_set",
"since": "2.8.9",
diff --git a/src/commands/zremrangebyrank.json b/src/commands/zremrangebyrank.json
index 4e7744dff..7e668e8f6 100644
--- a/src/commands/zremrangebyrank.json
+++ b/src/commands/zremrangebyrank.json
@@ -1,6 +1,6 @@
{
"ZREMRANGEBYRANK": {
- "summary": "Remove all members in a sorted set within the given indexes",
+ "summary": "Removes members in a sorted set within a range of indexes. Deletes the sorted set if all members were removed.",
"complexity": "O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements removed by the operation.",
"group": "sorted_set",
"since": "2.0.0",
diff --git a/src/commands/zremrangebyscore.json b/src/commands/zremrangebyscore.json
index d5ca40c42..aed5d1bba 100644
--- a/src/commands/zremrangebyscore.json
+++ b/src/commands/zremrangebyscore.json
@@ -1,6 +1,6 @@
{
"ZREMRANGEBYSCORE": {
- "summary": "Remove all members in a sorted set within the given scores",
+ "summary": "Removes members in a sorted set within a range of scores. Deletes the sorted set if all members were removed.",
"complexity": "O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements removed by the operation.",
"group": "sorted_set",
"since": "1.2.0",
diff --git a/src/commands/zrevrange.json b/src/commands/zrevrange.json
index 725abf8d7..116fe82b2 100644
--- a/src/commands/zrevrange.json
+++ b/src/commands/zrevrange.json
@@ -1,6 +1,6 @@
{
"ZREVRANGE": {
- "summary": "Return a range of members in a sorted set, by index, with scores ordered from high to low",
+ "summary": "Returns members in a sorted set within a range of indexes in reverse order.",
"complexity": "O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements returned.",
"group": "sorted_set",
"since": "1.2.0",
diff --git a/src/commands/zrevrangebylex.json b/src/commands/zrevrangebylex.json
index 252c96f8c..d1d8100d1 100644
--- a/src/commands/zrevrangebylex.json
+++ b/src/commands/zrevrangebylex.json
@@ -1,6 +1,6 @@
{
"ZREVRANGEBYLEX": {
- "summary": "Return a range of members in a sorted set, by lexicographical range, ordered from higher to lower strings.",
+ "summary": "Returns members in a sorted set within a lexicographical range in reverse order.",
"complexity": "O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).",
"group": "sorted_set",
"since": "2.8.9",
diff --git a/src/commands/zrevrangebyscore.json b/src/commands/zrevrangebyscore.json
index 163faec57..ab040527e 100644
--- a/src/commands/zrevrangebyscore.json
+++ b/src/commands/zrevrangebyscore.json
@@ -1,6 +1,6 @@
{
"ZREVRANGEBYSCORE": {
- "summary": "Return a range of members in a sorted set, by score, with scores ordered from high to low",
+ "summary": "Returns members in a sorted set within a range of scores in reverse order.",
"complexity": "O(log(N)+M) with N being the number of elements in the sorted set and M the number of elements being returned. If M is constant (e.g. always asking for the first 10 elements with LIMIT), you can consider it O(log(N)).",
"group": "sorted_set",
"since": "2.2.0",
diff --git a/src/commands/zrevrank.json b/src/commands/zrevrank.json
index 0a025fe0d..43ac13bd8 100644
--- a/src/commands/zrevrank.json
+++ b/src/commands/zrevrank.json
@@ -1,6 +1,6 @@
{
"ZREVRANK": {
- "summary": "Determine the index of a member in a sorted set, with scores ordered from high to low",
+ "summary": "Returns the index of a member in a sorted set ordered by descending scores.",
"complexity": "O(log(N))",
"group": "sorted_set",
"since": "2.0.0",
diff --git a/src/commands/zscan.json b/src/commands/zscan.json
index ca4a7dd2a..7c69ccf65 100644
--- a/src/commands/zscan.json
+++ b/src/commands/zscan.json
@@ -1,6 +1,6 @@
{
"ZSCAN": {
- "summary": "Incrementally iterate sorted sets elements and associated scores",
+ "summary": "Iterates over members and scores of a sorted set.",
"complexity": "O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.",
"group": "sorted_set",
"since": "2.8.0",
diff --git a/src/commands/zscore.json b/src/commands/zscore.json
index 7e07c8248..502247051 100644
--- a/src/commands/zscore.json
+++ b/src/commands/zscore.json
@@ -1,6 +1,6 @@
{
"ZSCORE": {
- "summary": "Get the score associated with the given member in a sorted set",
+ "summary": "Returns the score of a member in a sorted set.",
"complexity": "O(1)",
"group": "sorted_set",
"since": "1.2.0",
diff --git a/src/commands/zunion.json b/src/commands/zunion.json
index 395a12741..1ce3dc5ee 100644
--- a/src/commands/zunion.json
+++ b/src/commands/zunion.json
@@ -1,6 +1,6 @@
{
"ZUNION": {
- "summary": "Add multiple sorted sets",
+ "summary": "Returns the union of multiple sorted sets.",
"complexity": "O(N)+O(M*log(M)) with N being the sum of the sizes of the input sorted sets, and M being the number of elements in the resulting sorted set.",
"group": "sorted_set",
"since": "6.2.0",
diff --git a/src/commands/zunionstore.json b/src/commands/zunionstore.json
index 25273af8c..65e7b5469 100644
--- a/src/commands/zunionstore.json
+++ b/src/commands/zunionstore.json
@@ -1,6 +1,6 @@
{
"ZUNIONSTORE": {
- "summary": "Add multiple sorted sets and store the resulting sorted set in a new key",
+ "summary": "Stores the union of multiple sorted sets in a key.",
"complexity": "O(N)+O(M log(M)) with N being the sum of the sizes of the input sorted sets, and M being the number of elements in the resulting sorted set.",
"group": "sorted_set",
"since": "2.0.0",
diff --git a/src/config.c b/src/config.c
index 94fc039e2..6978cc5e7 100644
--- a/src/config.c
+++ b/src/config.c
@@ -1065,7 +1065,7 @@ void rewriteConfigReleaseState(struct rewriteConfigState *state) {
}
/* Create the configuration rewrite state */
-struct rewriteConfigState *rewriteConfigCreateState() {
+struct rewriteConfigState *rewriteConfigCreateState(void) {
struct rewriteConfigState *state = zmalloc(sizeof(*state));
state->option_to_line = dictCreate(&optionToLineDictType);
state->rewritten = dictCreate(&optionSetDictType);
@@ -1221,7 +1221,7 @@ struct rewriteConfigState *rewriteConfigReadOldFile(char *path) {
*
* "line" is either used, or freed, so the caller does not need to free it
* in any way. */
-void rewriteConfigRewriteLine(struct rewriteConfigState *state, const char *option, sds line, int force) {
+int rewriteConfigRewriteLine(struct rewriteConfigState *state, const char *option, sds line, int force) {
sds o = sdsnew(option);
list *l = dictFetchValue(state->option_to_line,o);
@@ -1231,7 +1231,7 @@ void rewriteConfigRewriteLine(struct rewriteConfigState *state, const char *opti
/* Option not used previously, and we are not forced to use it. */
sdsfree(line);
sdsfree(o);
- return;
+ return 0;
}
if (l) {
@@ -1254,6 +1254,7 @@ void rewriteConfigRewriteLine(struct rewriteConfigState *state, const char *opti
rewriteConfigAppendLine(state,line);
}
sdsfree(o);
+ return 1;
}
/* Write the long long 'bytes' value as a string in a way that is parsable
@@ -1642,7 +1643,7 @@ void rewriteConfigRemoveOrphaned(struct rewriteConfigState *state) {
/* This function returns a string representation of all the config options
* marked with DEBUG_CONFIG, which can be used to help with debugging. */
-sds getConfigDebugInfo() {
+sds getConfigDebugInfo(void) {
struct rewriteConfigState *state = rewriteConfigCreateState();
state->force_write = 1; /* Force the output */
state->needs_signature = 0; /* Omit the rewrite signature */
@@ -3259,7 +3260,7 @@ int registerConfigValue(const char *name, const standardConfig *config, int alia
/* Initialize configs to their default values and create and populate the
* runtime configuration dictionary. */
-void initConfigValues() {
+void initConfigValues(void) {
configs = dictCreate(&sdsHashDictType);
dictExpand(configs, sizeof(static_configs) / sizeof(standardConfig));
for (standardConfig *config = static_configs; config->name != NULL; config++) {
diff --git a/src/config.h b/src/config.h
index c3d80c831..3c9a27013 100644
--- a/src/config.h
+++ b/src/config.h
@@ -309,16 +309,6 @@ int pthread_setname_np(const char *name);
void setcpuaffinity(const char *cpulist);
#endif
-/* deprecate unsafe functions
- *
- * NOTE: We do not use the poison pragma since it
- * will error on stdlib definitions in files as well*/
-#if (__GNUC__ && __GNUC__ >= 4) && !defined __APPLE__
-int sprintf(char *str, const char *format, ...) __attribute__((deprecated("please avoid use of unsafe C functions. prefer use of snprintf instead")));
-char *strcpy(char *restrict dest, const char *src) __attribute__((deprecated("please avoid use of unsafe C functions. prefer use of redis_strlcpy instead")));
-char *strcat(char *restrict dest, const char *restrict src) __attribute__((deprecated("please avoid use of unsafe C functions. prefer use of redis_strlcat instead")));
-#endif
-
/* Test for posix_fadvise() */
#if defined(__linux__) || __FreeBSD__ >= 10
#define HAVE_FADVISE
diff --git a/src/connection.c b/src/connection.c
index 6bb0c9ec1..fd9d5d17a 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -57,7 +57,7 @@ int connTypeRegister(ConnectionType *ct) {
return C_OK;
}
-int connTypeInitialize() {
+int connTypeInitialize(void) {
/* currently socket connection type is necessary */
serverAssert(RedisRegisterConnectionTypeSocket() == C_OK);
@@ -88,7 +88,7 @@ ConnectionType *connectionByType(const char *typename) {
}
/* Cache TCP connection type, query it by string once */
-ConnectionType *connectionTypeTcp() {
+ConnectionType *connectionTypeTcp(void) {
static ConnectionType *ct_tcp = NULL;
if (ct_tcp != NULL)
@@ -101,7 +101,7 @@ ConnectionType *connectionTypeTcp() {
}
/* Cache TLS connection type, query it by string once */
-ConnectionType *connectionTypeTls() {
+ConnectionType *connectionTypeTls(void) {
static ConnectionType *ct_tls = NULL;
static int cached = 0;
@@ -116,7 +116,7 @@ ConnectionType *connectionTypeTls() {
}
/* Cache Unix connection type, query it by string once */
-ConnectionType *connectionTypeUnix() {
+ConnectionType *connectionTypeUnix(void) {
static ConnectionType *ct_unix = NULL;
if (ct_unix != NULL)
@@ -141,7 +141,7 @@ int connectionIndexByType(const char *typename) {
return -1;
}
-void connTypeCleanupAll() {
+void connTypeCleanupAll(void) {
ConnectionType *ct;
int type;
diff --git a/src/connection.h b/src/connection.h
index da8b1b7c7..7c26ac635 100644
--- a/src/connection.h
+++ b/src/connection.h
@@ -379,7 +379,7 @@ static inline sds connGetPeerCert(connection *conn) {
}
/* Initialize the redis connection framework */
-int connTypeInitialize();
+int connTypeInitialize(void);
/* Register a connection type into redis connection framework */
int connTypeRegister(ConnectionType *ct);
@@ -388,13 +388,13 @@ int connTypeRegister(ConnectionType *ct);
ConnectionType *connectionByType(const char *typename);
/* Fast path to get TCP connection type */
-ConnectionType *connectionTypeTcp();
+ConnectionType *connectionTypeTcp(void);
/* Fast path to get TLS connection type */
-ConnectionType *connectionTypeTls();
+ConnectionType *connectionTypeTls(void);
/* Fast path to get Unix connection type */
-ConnectionType *connectionTypeUnix();
+ConnectionType *connectionTypeUnix(void);
/* Lookup the index of a connection type by type name, return -1 if not found */
int connectionIndexByType(const char *typename);
@@ -418,7 +418,7 @@ static inline int connTypeConfigure(ConnectionType *ct, void *priv, int reconfig
}
/* Walk all the connection types and cleanup them all if possible */
-void connTypeCleanupAll();
+void connTypeCleanupAll(void);
/* Test all the connection type has pending data or not. */
int connTypeHasPendingData(void);
@@ -441,8 +441,8 @@ static inline aeFileProc *connAcceptHandler(ConnectionType *ct) {
/* Get Listeners information, note that caller should free the non-empty string */
sds getListensInfoString(sds info);
-int RedisRegisterConnectionTypeSocket();
-int RedisRegisterConnectionTypeUnix();
-int RedisRegisterConnectionTypeTLS();
+int RedisRegisterConnectionTypeSocket(void);
+int RedisRegisterConnectionTypeUnix(void);
+int RedisRegisterConnectionTypeTLS(void);
#endif /* __REDIS_CONNECTION_H */
diff --git a/src/db.c b/src/db.c
index ddfb04ebb..38881a7f8 100644
--- a/src/db.c
+++ b/src/db.c
@@ -562,7 +562,7 @@ int selectDb(client *c, int id) {
return C_OK;
}
-long long dbTotalServerKeyCount() {
+long long dbTotalServerKeyCount(void) {
long long total = 0;
int j;
for (j = 0; j < server.dbnum; j++) {
@@ -774,17 +774,16 @@ void keysCommand(client *c) {
di = dictGetSafeIterator(c->db->dict);
allkeys = (pattern[0] == '*' && plen == 1);
+ robj keyobj;
while((de = dictNext(di)) != NULL) {
sds key = dictGetKey(de);
- robj *keyobj;
if (allkeys || stringmatchlen(pattern,plen,key,sdslen(key),0)) {
- keyobj = createStringObject(key,sdslen(key));
- if (!keyIsExpired(c->db,keyobj)) {
- addReplyBulk(c,keyobj);
+ initStaticStringObject(keyobj, key);
+ if (!keyIsExpired(c->db, &keyobj)) {
+ addReplyBulkCBuffer(c, key, sdslen(key));
numkeys++;
}
- decrRefCount(keyobj);
}
if (c->flags & CLIENT_CLOSE_ASAP)
break;
@@ -2316,7 +2315,7 @@ int migrateGetKeys(struct redisCommand *cmd, robj **argv, int argc, getKeysResul
/* Helper function to extract keys from following commands:
* GEORADIUS key x y radius unit [WITHDIST] [WITHHASH] [WITHCOORD] [ASC|DESC]
- * [COUNT count] [STORE key] [STOREDIST key]
+ * [COUNT count] [STORE key|STOREDIST key]
* GEORADIUSBYMEMBER key member radius unit ... options ...
*
* This command has a fully defined keyspec, so returning flags isn't needed. */
diff --git a/src/debug.c b/src/debug.c
index 83f37cd3d..b2c3de69a 100644
--- a/src/debug.c
+++ b/src/debug.c
@@ -329,7 +329,8 @@ void mallctl_int(client *c, robj **argv, int argc) {
}
size_t sz = sizeof(old);
while (sz > 0) {
- if ((ret=je_mallctl(argv[0]->ptr, &old, &sz, argc > 1? &val: NULL, argc > 1?sz: 0))) {
+ size_t zz = sz;
+ if ((ret=je_mallctl(argv[0]->ptr, &old, &zz, argc > 1? &val: NULL, argc > 1?sz: 0))) {
if (ret == EPERM && argc > 1) {
/* if this option is write only, try just writing to it. */
if (!(ret=je_mallctl(argv[0]->ptr, NULL, 0, &val, sz))) {
@@ -520,7 +521,7 @@ NULL
restartServer(flags,delay);
addReplyError(c,"failed to restart the server. Check server logs.");
} else if (!strcasecmp(c->argv[1]->ptr,"oom")) {
- void *ptr = zmalloc(ULONG_MAX); /* Should trigger an out of memory. */
+ void *ptr = zmalloc(SIZE_MAX/2); /* Should trigger an out of memory. */
zfree(ptr);
addReply(c,shared.ok);
} else if (!strcasecmp(c->argv[1]->ptr,"assert")) {
@@ -2056,9 +2057,9 @@ void dumpCodeAroundEIP(void *eip) {
}
}
-void invalidFunctionWasCalled() {}
+void invalidFunctionWasCalled(void) {}
-typedef void (*invalidFunctionWasCalledType)();
+typedef void (*invalidFunctionWasCalledType)(void);
void sigsegvHandler(int sig, siginfo_t *info, void *secret) {
UNUSED(secret);
@@ -2227,7 +2228,7 @@ void watchdogScheduleSignal(int period) {
it.it_interval.tv_usec = 0;
setitimer(ITIMER_REAL, &it, NULL);
}
-void applyWatchdogPeriod() {
+void applyWatchdogPeriod(void) {
struct sigaction act;
/* Disable watchdog when period is 0 */
diff --git a/src/defrag.c b/src/defrag.c
index 5b7fdb775..ff63cf8fd 100644
--- a/src/defrag.c
+++ b/src/defrag.c
@@ -781,7 +781,7 @@ float getAllocatorFragmentation(size_t *out_frag_bytes) {
/* We may need to defrag other globals, one small allocation can hold a full allocator run.
* so although small, it is still important to defrag these */
-void defragOtherGlobals() {
+void defragOtherGlobals(void) {
/* there are many more pointers to defrag (e.g. client argv, output / aof buffers, etc.
* but we assume most of these are short lived, we only need to defrag allocations
@@ -887,7 +887,7 @@ int defragLaterStep(redisDb *db, long long endtime) {
#define LIMIT(y, min, max) ((y)<(min)? min: ((y)>(max)? max: (y)))
/* decide if defrag is needed, and at what CPU effort to invest in it */
-void computeDefragCycles() {
+void computeDefragCycles(void) {
size_t frag_bytes;
float frag_pct = getAllocatorFragmentation(&frag_bytes);
/* If we're not already running, and below the threshold, exit. */
diff --git a/src/dict.c b/src/dict.c
index d46e30588..b3d7fa406 100644
--- a/src/dict.c
+++ b/src/dict.c
@@ -52,9 +52,9 @@
* for Redis, as we use copy-on-write and don't want to move too much memory
* around when there is a child performing saving operations.
*
- * Note that even when dict_can_resize is set to 0, not all resizes are
- * prevented: a hash table is still allowed to grow if the ratio between
- * the number of elements and the buckets > dict_force_resize_ratio. */
+ * Note that even when dict_can_resize is set to DICT_RESIZE_AVOID, not all
+ * resizes are prevented: a hash table is still allowed to grow if the ratio
+ * between the number of elements and the buckets > dict_force_resize_ratio. */
static dictResizeEnable dict_can_resize = DICT_RESIZE_ENABLE;
static unsigned int dict_force_resize_ratio = 5;
diff --git a/src/eval.c b/src/eval.c
index 8ae3061ca..eb4b52936 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -650,15 +650,15 @@ NULL
}
}
-unsigned long evalMemory() {
+unsigned long evalMemory(void) {
return luaMemory(lctx.lua);
}
-dict* evalScriptsDict() {
+dict* evalScriptsDict(void) {
return lctx.lua_scripts;
}
-unsigned long evalScriptsMemory() {
+unsigned long evalScriptsMemory(void) {
return lctx.lua_scripts_mem +
dictMemUsage(lctx.lua_scripts) +
dictSize(lctx.lua_scripts) * sizeof(luaScript);
@@ -688,7 +688,7 @@ void ldbFlushLog(list *log) {
listDelNode(log,ln);
}
-int ldbIsEnabled(){
+int ldbIsEnabled(void){
return ldb.active && ldb.step;
}
diff --git a/src/evict.c b/src/evict.c
index 41712b926..4ca9f62ed 100644
--- a/src/evict.c
+++ b/src/evict.c
@@ -494,7 +494,7 @@ static int isSafeToPerformEvictions(void) {
}
/* Algorithm for converting tenacity (0-100) to a time limit. */
-static unsigned long evictionTimeLimitUs() {
+static unsigned long evictionTimeLimitUs(void) {
serverAssert(server.maxmemory_eviction_tenacity >= 0);
serverAssert(server.maxmemory_eviction_tenacity <= 100);
diff --git a/src/fmacros.h b/src/fmacros.h
index a97d21a47..c5da4b734 100644
--- a/src/fmacros.h
+++ b/src/fmacros.h
@@ -58,6 +58,16 @@
#define _LARGEFILE_SOURCE
#define _FILE_OFFSET_BITS 64
+/* deprecate unsafe functions
+ *
+ * NOTE: We do not use the poison pragma since it
+ * will error on stdlib definitions in files as well*/
+#if (__GNUC__ && __GNUC__ >= 4) && !defined __APPLE__
+int sprintf(char *str, const char *format, ...) __attribute__((deprecated("please avoid use of unsafe C functions. prefer use of snprintf instead")));
+char *strcpy(char *restrict dest, const char *src) __attribute__((deprecated("please avoid use of unsafe C functions. prefer use of redis_strlcpy instead")));
+char *strcat(char *restrict dest, const char *restrict src) __attribute__((deprecated("please avoid use of unsafe C functions. prefer use of redis_strlcat instead")));
+#endif
+
#ifdef __linux__
/* features.h uses the defines above to set feature specific defines. */
#include <features.h>
diff --git a/src/function_lua.c b/src/function_lua.c
index ca89818d8..91bb5cd67 100644
--- a/src/function_lua.c
+++ b/src/function_lua.c
@@ -420,7 +420,7 @@ static int luaRegisterFunction(lua_State *lua) {
}
/* Initialize Lua engine, should be called once on start. */
-int luaEngineInitEngine() {
+int luaEngineInitEngine(void) {
luaEngineCtx *lua_engine_ctx = zmalloc(sizeof(*lua_engine_ctx));
lua_engine_ctx->lua = lua_open();
diff --git a/src/functions.c b/src/functions.c
index c60d40d3c..f5738ba79 100644
--- a/src/functions.c
+++ b/src/functions.c
@@ -212,12 +212,12 @@ void functionsLibCtxSwapWithCurrent(functionsLibCtx *new_lib_ctx) {
}
/* return the current functions ctx */
-functionsLibCtx* functionsLibCtxGetCurrent() {
+functionsLibCtx* functionsLibCtxGetCurrent(void) {
return curr_functions_lib_ctx;
}
/* Create a new functions ctx */
-functionsLibCtx* functionsLibCtxCreate() {
+functionsLibCtx* functionsLibCtxCreate(void) {
functionsLibCtx *ret = zmalloc(sizeof(functionsLibCtx));
ret->libraries = dictCreate(&librariesDictType);
ret->functions = dictCreate(&functionDictType);
@@ -1075,7 +1075,7 @@ void functionLoadCommand(client *c) {
}
/* Return memory usage of all the engines combine */
-unsigned long functionsMemory() {
+unsigned long functionsMemory(void) {
dictIterator *iter = dictGetIterator(engines);
dictEntry *entry = NULL;
size_t engines_nemory = 0;
@@ -1090,7 +1090,7 @@ unsigned long functionsMemory() {
}
/* Return memory overhead of all the engines combine */
-unsigned long functionsMemoryOverhead() {
+unsigned long functionsMemoryOverhead(void) {
size_t memory_overhead = dictMemUsage(engines);
memory_overhead += dictMemUsage(curr_functions_lib_ctx->functions);
memory_overhead += sizeof(functionsLibCtx);
@@ -1101,15 +1101,15 @@ unsigned long functionsMemoryOverhead() {
}
/* Returns the number of functions */
-unsigned long functionsNum() {
+unsigned long functionsNum(void) {
return dictSize(curr_functions_lib_ctx->functions);
}
-unsigned long functionsLibNum() {
+unsigned long functionsLibNum(void) {
return dictSize(curr_functions_lib_ctx->libraries);
}
-dict* functionsLibGet() {
+dict* functionsLibGet(void) {
return curr_functions_lib_ctx->libraries;
}
@@ -1119,7 +1119,7 @@ size_t functionsLibCtxfunctionsLen(functionsLibCtx *functions_ctx) {
/* Initialize engine data structures.
* Should be called once on server initialization */
-int functionsInit() {
+int functionsInit(void) {
engines = dictCreate(&engineDictType);
if (luaEngineInitEngine() != C_OK) {
diff --git a/src/functions.h b/src/functions.h
index 40716dbc7..26e45babc 100644
--- a/src/functions.h
+++ b/src/functions.h
@@ -110,14 +110,14 @@ struct functionLibInfo {
int functionsRegisterEngine(const char *engine_name, engine *engine_ctx);
sds functionsCreateWithLibraryCtx(sds code, int replace, sds* err, functionsLibCtx *lib_ctx);
-unsigned long functionsMemory();
-unsigned long functionsMemoryOverhead();
-unsigned long functionsNum();
-unsigned long functionsLibNum();
-dict* functionsLibGet();
+unsigned long functionsMemory(void);
+unsigned long functionsMemoryOverhead(void);
+unsigned long functionsNum(void);
+unsigned long functionsLibNum(void);
+dict* functionsLibGet(void);
size_t functionsLibCtxfunctionsLen(functionsLibCtx *functions_ctx);
-functionsLibCtx* functionsLibCtxGetCurrent();
-functionsLibCtx* functionsLibCtxCreate();
+functionsLibCtx* functionsLibCtxGetCurrent(void);
+functionsLibCtx* functionsLibCtxCreate(void);
void functionsLibCtxClearCurrent(int async);
void functionsLibCtxFree(functionsLibCtx *lib_ctx);
void functionsLibCtxClear(functionsLibCtx *lib_ctx);
@@ -125,7 +125,7 @@ void functionsLibCtxSwapWithCurrent(functionsLibCtx *lib_ctx);
int functionLibCreateFunction(sds name, void *function, functionLibInfo *li, sds desc, uint64_t f_flags, sds *err);
-int luaEngineInitEngine();
-int functionsInit();
+int luaEngineInitEngine(void);
+int functionsInit(void);
#endif /* __FUNCTIONS_H_ */
diff --git a/src/geo.c b/src/geo.c
index a1781a5b0..4ebd678b5 100644
--- a/src/geo.c
+++ b/src/geo.c
@@ -513,7 +513,7 @@ void geoaddCommand(client *c) {
#define GEOSEARCHSTORE (1<<4) /* GEOSEARCHSTORE just accept STOREDIST option */
/* GEORADIUS key x y radius unit [WITHDIST] [WITHHASH] [WITHCOORD] [ASC|DESC]
- * [COUNT count [ANY]] [STORE key] [STOREDIST key]
+ * [COUNT count [ANY]] [STORE key|STOREDIST key]
* GEORADIUSBYMEMBER key member radius unit ... options ...
* GEOSEARCH key [FROMMEMBER member] [FROMLONLAT long lat] [BYRADIUS radius unit]
* [BYBOX width height unit] [WITHCOORD] [WITHDIST] [WITHASH] [COUNT count [ANY]] [ASC|DESC]
diff --git a/src/help.h b/src/help.h
deleted file mode 100644
index 4d54636ce..000000000
--- a/src/help.h
+++ /dev/null
@@ -1,1884 +0,0 @@
-/* Automatically generated by utils/generate-command-help.rb, do not edit. */
-
-#ifndef __REDIS_HELP_H
-#define __REDIS_HELP_H
-
-static char *commandGroups[] = {
- "generic",
- "string",
- "list",
- "set",
- "sorted-set",
- "hash",
- "pubsub",
- "transactions",
- "connection",
- "server",
- "scripting",
- "hyperloglog",
- "cluster",
- "geo",
- "stream",
- "bitmap"
-};
-
-struct commandHelp {
- char *name;
- char *params;
- char *summary;
- int group;
- char *since;
-} commandHelp[] = {
- { "ACL",
- "",
- "A container for Access List Control commands ",
- 9,
- "6.0.0" },
- { "ACL CAT",
- "[categoryname]",
- "List the ACL categories or the commands inside a category",
- 9,
- "6.0.0" },
- { "ACL DELUSER",
- "username [username ...]",
- "Remove the specified ACL users and the associated rules",
- 9,
- "6.0.0" },
- { "ACL DRYRUN",
- "username command [arg [arg ...]]",
- "Returns whether the user can execute the given command without executing the command.",
- 9,
- "7.0.0" },
- { "ACL GENPASS",
- "[bits]",
- "Generate a pseudorandom secure password to use for ACL users",
- 9,
- "6.0.0" },
- { "ACL GETUSER",
- "username",
- "Get the rules for a specific ACL user",
- 9,
- "6.0.0" },
- { "ACL HELP",
- "",
- "Show helpful text about the different subcommands",
- 9,
- "6.0.0" },
- { "ACL LIST",
- "",
- "List the current ACL rules in ACL config file format",
- 9,
- "6.0.0" },
- { "ACL LOAD",
- "",
- "Reload the ACLs from the configured ACL file",
- 9,
- "6.0.0" },
- { "ACL LOG",
- "[count|RESET]",
- "List latest events denied because of ACLs in place",
- 9,
- "6.0.0" },
- { "ACL SAVE",
- "",
- "Save the current ACL rules in the configured ACL file",
- 9,
- "6.0.0" },
- { "ACL SETUSER",
- "username [rule [rule ...]]",
- "Modify or create the rules for a specific ACL user",
- 9,
- "6.0.0" },
- { "ACL USERS",
- "",
- "List the username of all the configured ACL rules",
- 9,
- "6.0.0" },
- { "ACL WHOAMI",
- "",
- "Return the name of the user associated to the current connection",
- 9,
- "6.0.0" },
- { "APPEND",
- "key value",
- "Append a value to a key",
- 1,
- "2.0.0" },
- { "ASKING",
- "",
- "Sent by cluster clients after an -ASK redirect",
- 12,
- "3.0.0" },
- { "AUTH",
- "[username] password",
- "Authenticate to the server",
- 8,
- "1.0.0" },
- { "BGREWRITEAOF",
- "",
- "Asynchronously rewrite the append-only file",
- 9,
- "1.0.0" },
- { "BGSAVE",
- "[SCHEDULE]",
- "Asynchronously save the dataset to disk",
- 9,
- "1.0.0" },
- { "BITCOUNT",
- "key [start end [BYTE|BIT]]",
- "Count set bits in a string",
- 15,
- "2.6.0" },
- { "BITFIELD",
- "key [GET encoding offset|[OVERFLOW WRAP|SAT|FAIL] SET encoding offset value|INCRBY encoding offset increment [GET encoding offset|[OVERFLOW WRAP|SAT|FAIL] SET encoding offset value|INCRBY encoding offset increment ...]]",
- "Perform arbitrary bitfield integer operations on strings",
- 15,
- "3.2.0" },
- { "BITFIELD_RO",
- "key [GET encoding offset [GET encoding offset ...]]",
- "Perform arbitrary bitfield integer operations on strings. Read-only variant of BITFIELD",
- 15,
- "6.0.0" },
- { "BITOP",
- "AND|OR|XOR|NOT destkey key [key ...]",
- "Perform bitwise operations between strings",
- 15,
- "2.6.0" },
- { "BITPOS",
- "key bit [start [end [BYTE|BIT]]]",
- "Find first bit set or clear in a string",
- 15,
- "2.8.7" },
- { "BLMOVE",
- "source destination LEFT|RIGHT LEFT|RIGHT timeout",
- "Pop an element from a list, push it to another list and return it; or block until one is available",
- 2,
- "6.2.0" },
- { "BLMPOP",
- "timeout numkeys key [key ...] LEFT|RIGHT [COUNT count]",
- "Pop elements from a list, or block until one is available",
- 2,
- "7.0.0" },
- { "BLPOP",
- "key [key ...] timeout",
- "Remove and get the first element in a list, or block until one is available",
- 2,
- "2.0.0" },
- { "BRPOP",
- "key [key ...] timeout",
- "Remove and get the last element in a list, or block until one is available",
- 2,
- "2.0.0" },
- { "BRPOPLPUSH",
- "source destination timeout",
- "Pop an element from a list, push it to another list and return it; or block until one is available",
- 2,
- "2.2.0" },
- { "BZMPOP",
- "timeout numkeys key [key ...] MIN|MAX [COUNT count]",
- "Remove and return members with scores in a sorted set or block until one is available",
- 4,
- "7.0.0" },
- { "BZPOPMAX",
- "key [key ...] timeout",
- "Remove and return the member with the highest score from one or more sorted sets, or block until one is available",
- 4,
- "5.0.0" },
- { "BZPOPMIN",
- "key [key ...] timeout",
- "Remove and return the member with the lowest score from one or more sorted sets, or block until one is available",
- 4,
- "5.0.0" },
- { "CLIENT",
- "",
- "A container for client connection commands",
- 8,
- "2.4.0" },
- { "CLIENT CACHING",
- "YES|NO",
- "Instruct the server about tracking or not keys in the next request",
- 8,
- "6.0.0" },
- { "CLIENT GETNAME",
- "",
- "Get the current connection name",
- 8,
- "2.6.9" },
- { "CLIENT GETREDIR",
- "",
- "Get tracking notifications redirection client ID if any",
- 8,
- "6.0.0" },
- { "CLIENT HELP",
- "",
- "Show helpful text about the different subcommands",
- 8,
- "5.0.0" },
- { "CLIENT ID",
- "",
- "Returns the client ID for the current connection",
- 8,
- "5.0.0" },
- { "CLIENT INFO",
- "",
- "Returns information about the current client connection.",
- 8,
- "6.2.0" },
- { "CLIENT KILL",
- "old-format|[ID client-id]|[TYPE NORMAL|MASTER|SLAVE|REPLICA|PUBSUB]|[USER username]|[ADDR addr]|[LADDR laddr]|[SKIPME YES|NO] [[ID client-id]|[TYPE NORMAL|MASTER|SLAVE|REPLICA|PUBSUB]|[USER username]|[ADDR addr]|[LADDR laddr]|[SKIPME YES|NO] ...]",
- "Kill the connection of a client",
- 8,
- "2.4.0" },
- { "CLIENT LIST",
- "[TYPE NORMAL|MASTER|REPLICA|PUBSUB] [ID client-id [client-id ...]]",
- "Get the list of client connections",
- 8,
- "2.4.0" },
- { "CLIENT NO-EVICT",
- "ON|OFF",
- "Set client eviction mode for the current connection",
- 8,
- "7.0.0" },
- { "CLIENT NO-TOUCH",
- "ON|OFF",
- "Controls whether commands sent by the client will alter the LRU/LFU of the keys they access.",
- 8,
- "7.2.0" },
- { "CLIENT PAUSE",
- "timeout [WRITE|ALL]",
- "Stop processing commands from clients for some time",
- 8,
- "3.0.0" },
- { "CLIENT REPLY",
- "ON|OFF|SKIP",
- "Instruct the server whether to reply to commands",
- 8,
- "3.2.0" },
- { "CLIENT SETINFO",
- "LIB-NAME libname|LIB-VER libver",
- "Set client or connection specific info",
- 8,
- "7.2.0" },
- { "CLIENT SETNAME",
- "connection-name",
- "Set the current connection name",
- 8,
- "2.6.9" },
- { "CLIENT TRACKING",
- "ON|OFF [REDIRECT client-id] [PREFIX prefix [PREFIX prefix ...]] [BCAST] [OPTIN] [OPTOUT] [NOLOOP]",
- "Enable or disable server assisted client side caching support",
- 8,
- "6.0.0" },
- { "CLIENT TRACKINGINFO",
- "",
- "Return information about server assisted client side caching for the current connection",
- 8,
- "6.2.0" },
- { "CLIENT UNBLOCK",
- "client-id [TIMEOUT|ERROR]",
- "Unblock a client blocked in a blocking command from a different connection",
- 8,
- "5.0.0" },
- { "CLIENT UNPAUSE",
- "",
- "Resume processing of clients that were paused",
- 8,
- "6.2.0" },
- { "CLUSTER",
- "",
- "A container for cluster commands",
- 12,
- "3.0.0" },
- { "CLUSTER ADDSLOTS",
- "slot [slot ...]",
- "Assign new hash slots to receiving node",
- 12,
- "3.0.0" },
- { "CLUSTER ADDSLOTSRANGE",
- "start-slot end-slot [start-slot end-slot ...]",
- "Assign new hash slots to receiving node",
- 12,
- "7.0.0" },
- { "CLUSTER BUMPEPOCH",
- "",
- "Advance the cluster config epoch",
- 12,
- "3.0.0" },
- { "CLUSTER COUNT-FAILURE-REPORTS",
- "node-id",
- "Return the number of failure reports active for a given node",
- 12,
- "3.0.0" },
- { "CLUSTER COUNTKEYSINSLOT",
- "slot",
- "Return the number of local keys in the specified hash slot",
- 12,
- "3.0.0" },
- { "CLUSTER DELSLOTS",
- "slot [slot ...]",
- "Set hash slots as unbound in receiving node",
- 12,
- "3.0.0" },
- { "CLUSTER DELSLOTSRANGE",
- "start-slot end-slot [start-slot end-slot ...]",
- "Set hash slots as unbound in receiving node",
- 12,
- "7.0.0" },
- { "CLUSTER FAILOVER",
- "[FORCE|TAKEOVER]",
- "Forces a replica to perform a manual failover of its master.",
- 12,
- "3.0.0" },
- { "CLUSTER FLUSHSLOTS",
- "",
- "Delete a node's own slots information",
- 12,
- "3.0.0" },
- { "CLUSTER FORGET",
- "node-id",
- "Remove a node from the nodes table",
- 12,
- "3.0.0" },
- { "CLUSTER GETKEYSINSLOT",
- "slot count",
- "Return local key names in the specified hash slot",
- 12,
- "3.0.0" },
- { "CLUSTER HELP",
- "",
- "Show helpful text about the different subcommands",
- 12,
- "5.0.0" },
- { "CLUSTER INFO",
- "",
- "Provides info about Redis Cluster node state",
- 12,
- "3.0.0" },
- { "CLUSTER KEYSLOT",
- "key",
- "Returns the hash slot of the specified key",
- 12,
- "3.0.0" },
- { "CLUSTER LINKS",
- "",
- "Returns a list of all TCP links to and from peer nodes in cluster",
- 12,
- "7.0.0" },
- { "CLUSTER MEET",
- "ip port [cluster-bus-port]",
- "Force a node cluster to handshake with another node",
- 12,
- "3.0.0" },
- { "CLUSTER MYID",
- "",
- "Return the node id",
- 12,
- "3.0.0" },
- { "CLUSTER MYSHARDID",
- "",
- "Return the node shard id",
- 12,
- "7.2.0" },
- { "CLUSTER NODES",
- "",
- "Get Cluster config for the node",
- 12,
- "3.0.0" },
- { "CLUSTER REPLICAS",
- "node-id",
- "List replica nodes of the specified master node",
- 12,
- "5.0.0" },
- { "CLUSTER REPLICATE",
- "node-id",
- "Reconfigure a node as a replica of the specified master node",
- 12,
- "3.0.0" },
- { "CLUSTER RESET",
- "[HARD|SOFT]",
- "Reset a Redis Cluster node",
- 12,
- "3.0.0" },
- { "CLUSTER SAVECONFIG",
- "",
- "Forces the node to save cluster state on disk",
- 12,
- "3.0.0" },
- { "CLUSTER SET-CONFIG-EPOCH",
- "config-epoch",
- "Set the configuration epoch in a new node",
- 12,
- "3.0.0" },
- { "CLUSTER SETSLOT",
- "slot IMPORTING importing|MIGRATING migrating|NODE node|STABLE",
- "Bind a hash slot to a specific node",
- 12,
- "3.0.0" },
- { "CLUSTER SHARDS",
- "",
- "Get array of cluster slots to node mappings",
- 12,
- "7.0.0" },
- { "CLUSTER SLAVES",
- "node-id",
- "List replica nodes of the specified master node",
- 12,
- "3.0.0" },
- { "CLUSTER SLOTS",
- "",
- "Get array of Cluster slot to node mappings",
- 12,
- "3.0.0" },
- { "COMMAND",
- "",
- "Get array of Redis command details",
- 9,
- "2.8.13" },
- { "COMMAND COUNT",
- "",
- "Get total number of Redis commands",
- 9,
- "2.8.13" },
- { "COMMAND DOCS",
- "[command-name [command-name ...]]",
- "Get array of specific Redis command documentation",
- 9,
- "7.0.0" },
- { "COMMAND GETKEYS",
- "command [arg [arg ...]]",
- "Extract keys given a full Redis command",
- 9,
- "2.8.13" },
- { "COMMAND GETKEYSANDFLAGS",
- "command [arg [arg ...]]",
- "Extract keys and access flags given a full Redis command",
- 9,
- "7.0.0" },
- { "COMMAND HELP",
- "",
- "Show helpful text about the different subcommands",
- 9,
- "5.0.0" },
- { "COMMAND INFO",
- "[command-name [command-name ...]]",
- "Get array of specific Redis command details, or all when no argument is given.",
- 9,
- "2.8.13" },
- { "COMMAND LIST",
- "[FILTERBY MODULE module-name|ACLCAT category|PATTERN pattern]",
- "Get an array of Redis command names",
- 9,
- "7.0.0" },
- { "CONFIG",
- "",
- "A container for server configuration commands",
- 9,
- "2.0.0" },
- { "CONFIG GET",
- "parameter [parameter ...]",
- "Get the values of configuration parameters",
- 9,
- "2.0.0" },
- { "CONFIG HELP",
- "",
- "Show helpful text about the different subcommands",
- 9,
- "5.0.0" },
- { "CONFIG RESETSTAT",
- "",
- "Reset the stats returned by INFO",
- 9,
- "2.0.0" },
- { "CONFIG REWRITE",
- "",
- "Rewrite the configuration file with the in memory configuration",
- 9,
- "2.8.0" },
- { "CONFIG SET",
- "parameter value [parameter value ...]",
- "Set configuration parameters to the given values",
- 9,
- "2.0.0" },
- { "COPY",
- "source destination [DB destination-db] [REPLACE]",
- "Copy a key",
- 0,
- "6.2.0" },
- { "DBSIZE",
- "",
- "Return the number of keys in the selected database",
- 9,
- "1.0.0" },
- { "DEBUG",
- "",
- "A container for debugging commands",
- 9,
- "1.0.0" },
- { "DECR",
- "key",
- "Decrement the integer value of a key by one",
- 1,
- "1.0.0" },
- { "DECRBY",
- "key decrement",
- "Decrement the integer value of a key by the given number",
- 1,
- "1.0.0" },
- { "DEL",
- "key [key ...]",
- "Delete a key",
- 0,
- "1.0.0" },
- { "DISCARD",
- "",
- "Discard all commands issued after MULTI",
- 7,
- "2.0.0" },
- { "DUMP",
- "key",
- "Return a serialized version of the value stored at the specified key.",
- 0,
- "2.6.0" },
- { "ECHO",
- "message",
- "Echo the given string",
- 8,
- "1.0.0" },
- { "EVAL",
- "script numkeys [key [key ...]] [arg [arg ...]]",
- "Execute a Lua script server side",
- 10,
- "2.6.0" },
- { "EVALSHA",
- "sha1 numkeys [key [key ...]] [arg [arg ...]]",
- "Execute a Lua script server side",
- 10,
- "2.6.0" },
- { "EVALSHA_RO",
- "sha1 numkeys [key [key ...]] [arg [arg ...]]",
- "Execute a read-only Lua script server side",
- 10,
- "7.0.0" },
- { "EVAL_RO",
- "script numkeys [key [key ...]] [arg [arg ...]]",
- "Execute a read-only Lua script server side",
- 10,
- "7.0.0" },
- { "EXEC",
- "",
- "Execute all commands issued after MULTI",
- 7,
- "1.2.0" },
- { "EXISTS",
- "key [key ...]",
- "Determine if a key exists",
- 0,
- "1.0.0" },
- { "EXPIRE",
- "key seconds [NX|XX|GT|LT]",
- "Set a key's time to live in seconds",
- 0,
- "1.0.0" },
- { "EXPIREAT",
- "key unix-time-seconds [NX|XX|GT|LT]",
- "Set the expiration for a key as a UNIX timestamp",
- 0,
- "1.2.0" },
- { "EXPIRETIME",
- "key",
- "Get the expiration Unix timestamp for a key",
- 0,
- "7.0.0" },
- { "FAILOVER",
- "[TO host port [FORCE]] [ABORT] [TIMEOUT milliseconds]",
- "Start a coordinated failover between this server and one of its replicas.",
- 9,
- "6.2.0" },
- { "FCALL",
- "function numkeys [key [key ...]] [arg [arg ...]]",
- "Invoke a function",
- 10,
- "7.0.0" },
- { "FCALL_RO",
- "function numkeys [key [key ...]] [arg [arg ...]]",
- "Invoke a read-only function",
- 10,
- "7.0.0" },
- { "FLUSHALL",
- "[ASYNC|SYNC]",
- "Remove all keys from all databases",
- 9,
- "1.0.0" },
- { "FLUSHDB",
- "[ASYNC|SYNC]",
- "Remove all keys from the current database",
- 9,
- "1.0.0" },
- { "FUNCTION",
- "",
- "A container for function commands",
- 10,
- "7.0.0" },
- { "FUNCTION DELETE",
- "library-name",
- "Delete a function by name",
- 10,
- "7.0.0" },
- { "FUNCTION DUMP",
- "",
- "Dump all functions into a serialized binary payload",
- 10,
- "7.0.0" },
- { "FUNCTION FLUSH",
- "[ASYNC|SYNC]",
- "Deleting all functions",
- 10,
- "7.0.0" },
- { "FUNCTION HELP",
- "",
- "Show helpful text about the different subcommands",
- 10,
- "7.0.0" },
- { "FUNCTION KILL",
- "",
- "Kill the function currently in execution.",
- 10,
- "7.0.0" },
- { "FUNCTION LIST",
- "[LIBRARYNAME library-name-pattern] [WITHCODE]",
- "List information about all the functions",
- 10,
- "7.0.0" },
- { "FUNCTION LOAD",
- "[REPLACE] function-code",
- "Create a function with the given arguments (name, code, description)",
- 10,
- "7.0.0" },
- { "FUNCTION RESTORE",
- "serialized-value [FLUSH|APPEND|REPLACE]",
- "Restore all the functions on the given payload",
- 10,
- "7.0.0" },
- { "FUNCTION STATS",
- "",
- "Return information about the function currently running (name, description, duration)",
- 10,
- "7.0.0" },
- { "GEOADD",
- "key [NX|XX] [CH] longitude latitude member [longitude latitude member ...]",
- "Add one or more geospatial items in the geospatial index represented using a sorted set",
- 13,
- "3.2.0" },
- { "GEODIST",
- "key member1 member2 [M|KM|FT|MI]",
- "Returns the distance between two members of a geospatial index",
- 13,
- "3.2.0" },
- { "GEOHASH",
- "key [member [member ...]]",
- "Returns members of a geospatial index as standard geohash strings",
- 13,
- "3.2.0" },
- { "GEOPOS",
- "key [member [member ...]]",
- "Returns longitude and latitude of members of a geospatial index",
- 13,
- "3.2.0" },
- { "GEORADIUS",
- "key longitude latitude radius M|KM|FT|MI [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC|DESC] [STORE storekey] [STOREDIST storedistkey]",
- "Query a sorted set representing a geospatial index to fetch members matching a given maximum distance from a point",
- 13,
- "3.2.0" },
- { "GEORADIUSBYMEMBER",
- "key member radius M|KM|FT|MI [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC|DESC] [STORE storekey] [STOREDIST storedistkey]",
- "Query a sorted set representing a geospatial index to fetch members matching a given maximum distance from a member",
- 13,
- "3.2.0" },
- { "GEORADIUSBYMEMBER_RO",
- "key member radius M|KM|FT|MI [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC|DESC]",
- "A read-only variant for GEORADIUSBYMEMBER",
- 13,
- "3.2.10" },
- { "GEORADIUS_RO",
- "key longitude latitude radius M|KM|FT|MI [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC|DESC]",
- "A read-only variant for GEORADIUS",
- 13,
- "3.2.10" },
- { "GEOSEARCH",
- "key FROMMEMBER member|FROMLONLAT longitude latitude BYRADIUS radius M|KM|FT|MI|BYBOX width height M|KM|FT|MI [ASC|DESC] [COUNT count [ANY]] [WITHCOORD] [WITHDIST] [WITHHASH]",
- "Query a sorted set representing a geospatial index to fetch members inside an area of a box or a circle.",
- 13,
- "6.2.0" },
- { "GEOSEARCHSTORE",
- "destination source FROMMEMBER member|FROMLONLAT longitude latitude BYRADIUS radius M|KM|FT|MI|BYBOX width height M|KM|FT|MI [ASC|DESC] [COUNT count [ANY]] [STOREDIST]",
- "Query a sorted set representing a geospatial index to fetch members inside an area of a box or a circle, and store the result in another key.",
- 13,
- "6.2.0" },
- { "GET",
- "key",
- "Get the value of a key",
- 1,
- "1.0.0" },
- { "GETBIT",
- "key offset",
- "Returns the bit value at offset in the string value stored at key",
- 15,
- "2.2.0" },
- { "GETDEL",
- "key",
- "Get the value of a key and delete the key",
- 1,
- "6.2.0" },
- { "GETEX",
- "key [EX seconds|PX milliseconds|EXAT unix-time-seconds|PXAT unix-time-milliseconds|PERSIST]",
- "Get the value of a key and optionally set its expiration",
- 1,
- "6.2.0" },
- { "GETRANGE",
- "key start end",
- "Get a substring of the string stored at a key",
- 1,
- "2.4.0" },
- { "GETSET",
- "key value",
- "Set the string value of a key and return its old value",
- 1,
- "1.0.0" },
- { "HDEL",
- "key field [field ...]",
- "Delete one or more hash fields",
- 5,
- "2.0.0" },
- { "HELLO",
- "[protover [AUTH username password] [SETNAME clientname]]",
- "Handshake with Redis",
- 8,
- "6.0.0" },
- { "HEXISTS",
- "key field",
- "Determine if a hash field exists",
- 5,
- "2.0.0" },
- { "HGET",
- "key field",
- "Get the value of a hash field",
- 5,
- "2.0.0" },
- { "HGETALL",
- "key",
- "Get all the fields and values in a hash",
- 5,
- "2.0.0" },
- { "HINCRBY",
- "key field increment",
- "Increment the integer value of a hash field by the given number",
- 5,
- "2.0.0" },
- { "HINCRBYFLOAT",
- "key field increment",
- "Increment the float value of a hash field by the given amount",
- 5,
- "2.6.0" },
- { "HKEYS",
- "key",
- "Get all the fields in a hash",
- 5,
- "2.0.0" },
- { "HLEN",
- "key",
- "Get the number of fields in a hash",
- 5,
- "2.0.0" },
- { "HMGET",
- "key field [field ...]",
- "Get the values of all the given hash fields",
- 5,
- "2.0.0" },
- { "HMSET",
- "key field value [field value ...]",
- "Set multiple hash fields to multiple values",
- 5,
- "2.0.0" },
- { "HRANDFIELD",
- "key [count [WITHVALUES]]",
- "Get one or multiple random fields from a hash",
- 5,
- "6.2.0" },
- { "HSCAN",
- "key cursor [MATCH pattern] [COUNT count]",
- "Incrementally iterate hash fields and associated values",
- 5,
- "2.8.0" },
- { "HSET",
- "key field value [field value ...]",
- "Set the string value of a hash field",
- 5,
- "2.0.0" },
- { "HSETNX",
- "key field value",
- "Set the value of a hash field, only if the field does not exist",
- 5,
- "2.0.0" },
- { "HSTRLEN",
- "key field",
- "Get the length of the value of a hash field",
- 5,
- "3.2.0" },
- { "HVALS",
- "key",
- "Get all the values in a hash",
- 5,
- "2.0.0" },
- { "INCR",
- "key",
- "Increment the integer value of a key by one",
- 1,
- "1.0.0" },
- { "INCRBY",
- "key increment",
- "Increment the integer value of a key by the given amount",
- 1,
- "1.0.0" },
- { "INCRBYFLOAT",
- "key increment",
- "Increment the float value of a key by the given amount",
- 1,
- "2.6.0" },
- { "INFO",
- "[section [section ...]]",
- "Get information and statistics about the server",
- 9,
- "1.0.0" },
- { "KEYS",
- "pattern",
- "Find all keys matching the given pattern",
- 0,
- "1.0.0" },
- { "LASTSAVE",
- "",
- "Get the UNIX time stamp of the last successful save to disk",
- 9,
- "1.0.0" },
- { "LATENCY",
- "",
- "A container for latency diagnostics commands",
- 9,
- "2.8.13" },
- { "LATENCY DOCTOR",
- "",
- "Return a human readable latency analysis report.",
- 9,
- "2.8.13" },
- { "LATENCY GRAPH",
- "event",
- "Return a latency graph for the event.",
- 9,
- "2.8.13" },
- { "LATENCY HELP",
- "",
- "Show helpful text about the different subcommands.",
- 9,
- "2.8.13" },
- { "LATENCY HISTOGRAM",
- "[command [command ...]]",
- "Return the cumulative distribution of latencies of a subset of commands or all.",
- 9,
- "7.0.0" },
- { "LATENCY HISTORY",
- "event",
- "Return timestamp-latency samples for the event.",
- 9,
- "2.8.13" },
- { "LATENCY LATEST",
- "",
- "Return the latest latency samples for all events.",
- 9,
- "2.8.13" },
- { "LATENCY RESET",
- "[event [event ...]]",
- "Reset latency data for one or more events.",
- 9,
- "2.8.13" },
- { "LCS",
- "key1 key2 [LEN] [IDX] [MINMATCHLEN min-match-len] [WITHMATCHLEN]",
- "Find longest common substring",
- 1,
- "7.0.0" },
- { "LINDEX",
- "key index",
- "Get an element from a list by its index",
- 2,
- "1.0.0" },
- { "LINSERT",
- "key BEFORE|AFTER pivot element",
- "Insert an element before or after another element in a list",
- 2,
- "2.2.0" },
- { "LLEN",
- "key",
- "Get the length of a list",
- 2,
- "1.0.0" },
- { "LMOVE",
- "source destination LEFT|RIGHT LEFT|RIGHT",
- "Pop an element from a list, push it to another list and return it",
- 2,
- "6.2.0" },
- { "LMPOP",
- "numkeys key [key ...] LEFT|RIGHT [COUNT count]",
- "Pop elements from a list",
- 2,
- "7.0.0" },
- { "LOLWUT",
- "[VERSION version]",
- "Display some computer art and the Redis version",
- 9,
- "5.0.0" },
- { "LPOP",
- "key [count]",
- "Remove and get the first elements in a list",
- 2,
- "1.0.0" },
- { "LPOS",
- "key element [RANK rank] [COUNT num-matches] [MAXLEN len]",
- "Return the index of matching elements on a list",
- 2,
- "6.0.6" },
- { "LPUSH",
- "key element [element ...]",
- "Prepend one or multiple elements to a list",
- 2,
- "1.0.0" },
- { "LPUSHX",
- "key element [element ...]",
- "Prepend an element to a list, only if the list exists",
- 2,
- "2.2.0" },
- { "LRANGE",
- "key start stop",
- "Get a range of elements from a list",
- 2,
- "1.0.0" },
- { "LREM",
- "key count element",
- "Remove elements from a list",
- 2,
- "1.0.0" },
- { "LSET",
- "key index element",
- "Set the value of an element in a list by its index",
- 2,
- "1.0.0" },
- { "LTRIM",
- "key start stop",
- "Trim a list to the specified range",
- 2,
- "1.0.0" },
- { "MEMORY",
- "",
- "A container for memory diagnostics commands",
- 9,
- "4.0.0" },
- { "MEMORY DOCTOR",
- "",
- "Outputs memory problems report",
- 9,
- "4.0.0" },
- { "MEMORY HELP",
- "",
- "Show helpful text about the different subcommands",
- 9,
- "4.0.0" },
- { "MEMORY MALLOC-STATS",
- "",
- "Show allocator internal stats",
- 9,
- "4.0.0" },
- { "MEMORY PURGE",
- "",
- "Ask the allocator to release memory",
- 9,
- "4.0.0" },
- { "MEMORY STATS",
- "",
- "Show memory usage details",
- 9,
- "4.0.0" },
- { "MEMORY USAGE",
- "key [SAMPLES count]",
- "Estimate the memory usage of a key",
- 9,
- "4.0.0" },
- { "MGET",
- "key [key ...]",
- "Get the values of all the given keys",
- 1,
- "1.0.0" },
- { "MIGRATE",
- "host port key| destination-db timeout [COPY] [REPLACE] [AUTH auth|AUTH2 username password] [KEYS keys [keys ...]]",
- "Atomically transfer a key from a Redis instance to another one.",
- 0,
- "2.6.0" },
- { "MODULE",
- "",
- "A container for module commands",
- 9,
- "4.0.0" },
- { "MODULE HELP",
- "",
- "Show helpful text about the different subcommands",
- 9,
- "5.0.0" },
- { "MODULE LIST",
- "",
- "List all modules loaded by the server",
- 9,
- "4.0.0" },
- { "MODULE LOAD",
- "path [arg [arg ...]]",
- "Load a module",
- 9,
- "4.0.0" },
- { "MODULE LOADEX",
- "path [CONFIG name value [CONFIG name value ...]] [ARGS args [args ...]]",
- "Load a module with extended parameters",
- 9,
- "7.0.0" },
- { "MODULE UNLOAD",
- "name",
- "Unload a module",
- 9,
- "4.0.0" },
- { "MONITOR",
- "",
- "Listen for all requests received by the server in real time",
- 9,
- "1.0.0" },
- { "MOVE",
- "key db",
- "Move a key to another database",
- 0,
- "1.0.0" },
- { "MSET",
- "key value [key value ...]",
- "Set multiple keys to multiple values",
- 1,
- "1.0.1" },
- { "MSETNX",
- "key value [key value ...]",
- "Set multiple keys to multiple values, only if none of the keys exist",
- 1,
- "1.0.1" },
- { "MULTI",
- "",
- "Mark the start of a transaction block",
- 7,
- "1.2.0" },
- { "OBJECT",
- "",
- "A container for object introspection commands",
- 0,
- "2.2.3" },
- { "OBJECT ENCODING",
- "key",
- "Inspect the internal encoding of a Redis object",
- 0,
- "2.2.3" },
- { "OBJECT FREQ",
- "key",
- "Get the logarithmic access frequency counter of a Redis object",
- 0,
- "4.0.0" },
- { "OBJECT HELP",
- "",
- "Show helpful text about the different subcommands",
- 0,
- "6.2.0" },
- { "OBJECT IDLETIME",
- "key",
- "Get the time since a Redis object was last accessed",
- 0,
- "2.2.3" },
- { "OBJECT REFCOUNT",
- "key",
- "Get the number of references to the value of the key",
- 0,
- "2.2.3" },
- { "PERSIST",
- "key",
- "Remove the expiration from a key",
- 0,
- "2.2.0" },
- { "PEXPIRE",
- "key milliseconds [NX|XX|GT|LT]",
- "Set a key's time to live in milliseconds",
- 0,
- "2.6.0" },
- { "PEXPIREAT",
- "key unix-time-milliseconds [NX|XX|GT|LT]",
- "Set the expiration for a key as a UNIX timestamp specified in milliseconds",
- 0,
- "2.6.0" },
- { "PEXPIRETIME",
- "key",
- "Get the expiration Unix timestamp for a key in milliseconds",
- 0,
- "7.0.0" },
- { "PFADD",
- "key [element [element ...]]",
- "Adds the specified elements to the specified HyperLogLog.",
- 11,
- "2.8.9" },
- { "PFCOUNT",
- "key [key ...]",
- "Return the approximated cardinality of the set(s) observed by the HyperLogLog at key(s).",
- 11,
- "2.8.9" },
- { "PFDEBUG",
- "subcommand key",
- "Internal commands for debugging HyperLogLog values",
- 11,
- "2.8.9" },
- { "PFMERGE",
- "destkey [sourcekey [sourcekey ...]]",
- "Merge N different HyperLogLogs into a single one.",
- 11,
- "2.8.9" },
- { "PFSELFTEST",
- "",
- "An internal command for testing HyperLogLog values",
- 11,
- "2.8.9" },
- { "PING",
- "[message]",
- "Ping the server",
- 8,
- "1.0.0" },
- { "PSETEX",
- "key milliseconds value",
- "Set the value and expiration in milliseconds of a key",
- 1,
- "2.6.0" },
- { "PSUBSCRIBE",
- "pattern [pattern ...]",
- "Listen for messages published to channels matching the given patterns",
- 6,
- "2.0.0" },
- { "PSYNC",
- "replicationid offset",
- "Internal command used for replication",
- 9,
- "2.8.0" },
- { "PTTL",
- "key",
- "Get the time to live for a key in milliseconds",
- 0,
- "2.6.0" },
- { "PUBLISH",
- "channel message",
- "Post a message to a channel",
- 6,
- "2.0.0" },
- { "PUBSUB",
- "",
- "A container for Pub/Sub commands",
- 6,
- "2.8.0" },
- { "PUBSUB CHANNELS",
- "[pattern]",
- "List active channels",
- 6,
- "2.8.0" },
- { "PUBSUB HELP",
- "",
- "Show helpful text about the different subcommands",
- 6,
- "6.2.0" },
- { "PUBSUB NUMPAT",
- "",
- "Get the count of unique patterns pattern subscriptions",
- 6,
- "2.8.0" },
- { "PUBSUB NUMSUB",
- "[channel [channel ...]]",
- "Get the count of subscribers for channels",
- 6,
- "2.8.0" },
- { "PUBSUB SHARDCHANNELS",
- "[pattern]",
- "List active shard channels",
- 6,
- "7.0.0" },
- { "PUBSUB SHARDNUMSUB",
- "[shardchannel [shardchannel ...]]",
- "Get the count of subscribers for shard channels",
- 6,
- "7.0.0" },
- { "PUNSUBSCRIBE",
- "[pattern [pattern ...]]",
- "Stop listening for messages posted to channels matching the given patterns",
- 6,
- "2.0.0" },
- { "QUIT",
- "",
- "Close the connection",
- 8,
- "1.0.0" },
- { "RANDOMKEY",
- "",
- "Return a random key from the keyspace",
- 0,
- "1.0.0" },
- { "READONLY",
- "",
- "Enables read queries for a connection to a cluster replica node",
- 12,
- "3.0.0" },
- { "READWRITE",
- "",
- "Disables read queries for a connection to a cluster replica node",
- 12,
- "3.0.0" },
- { "RENAME",
- "key newkey",
- "Rename a key",
- 0,
- "1.0.0" },
- { "RENAMENX",
- "key newkey",
- "Rename a key, only if the new key does not exist",
- 0,
- "1.0.0" },
- { "REPLCONF",
- "",
- "An internal command for configuring the replication stream",
- 9,
- "3.0.0" },
- { "REPLICAOF",
- "host port",
- "Make the server a replica of another instance, or promote it as master.",
- 9,
- "5.0.0" },
- { "RESET",
- "",
- "Reset the connection",
- 8,
- "6.2.0" },
- { "RESTORE",
- "key ttl serialized-value [REPLACE] [ABSTTL] [IDLETIME seconds] [FREQ frequency]",
- "Create a key using the provided serialized value, previously obtained using DUMP.",
- 0,
- "2.6.0" },
- { "RESTORE-ASKING",
- "key ttl serialized-value [REPLACE] [ABSTTL] [IDLETIME seconds] [FREQ frequency]",
- "An internal command for migrating keys in a cluster",
- 9,
- "3.0.0" },
- { "ROLE",
- "",
- "Return the role of the instance in the context of replication",
- 9,
- "2.8.12" },
- { "RPOP",
- "key [count]",
- "Remove and get the last elements in a list",
- 2,
- "1.0.0" },
- { "RPOPLPUSH",
- "source destination",
- "Remove the last element in a list, prepend it to another list and return it",
- 2,
- "1.2.0" },
- { "RPUSH",
- "key element [element ...]",
- "Append one or multiple elements to a list",
- 2,
- "1.0.0" },
- { "RPUSHX",
- "key element [element ...]",
- "Append an element to a list, only if the list exists",
- 2,
- "2.2.0" },
- { "SADD",
- "key member [member ...]",
- "Add one or more members to a set",
- 3,
- "1.0.0" },
- { "SAVE",
- "",
- "Synchronously save the dataset to disk",
- 9,
- "1.0.0" },
- { "SCAN",
- "cursor [MATCH pattern] [COUNT count] [TYPE type]",
- "Incrementally iterate the keys space",
- 0,
- "2.8.0" },
- { "SCARD",
- "key",
- "Get the number of members in a set",
- 3,
- "1.0.0" },
- { "SCRIPT",
- "",
- "A container for Lua scripts management commands",
- 10,
- "2.6.0" },
- { "SCRIPT DEBUG",
- "YES|SYNC|NO",
- "Set the debug mode for executed scripts.",
- 10,
- "3.2.0" },
- { "SCRIPT EXISTS",
- "sha1 [sha1 ...]",
- "Check existence of scripts in the script cache.",
- 10,
- "2.6.0" },
- { "SCRIPT FLUSH",
- "[ASYNC|SYNC]",
- "Remove all the scripts from the script cache.",
- 10,
- "2.6.0" },
- { "SCRIPT HELP",
- "",
- "Show helpful text about the different subcommands",
- 10,
- "5.0.0" },
- { "SCRIPT KILL",
- "",
- "Kill the script currently in execution.",
- 10,
- "2.6.0" },
- { "SCRIPT LOAD",
- "script",
- "Load the specified Lua script into the script cache.",
- 10,
- "2.6.0" },
- { "SDIFF",
- "key [key ...]",
- "Subtract multiple sets",
- 3,
- "1.0.0" },
- { "SDIFFSTORE",
- "destination key [key ...]",
- "Subtract multiple sets and store the resulting set in a key",
- 3,
- "1.0.0" },
- { "SELECT",
- "index",
- "Change the selected database for the current connection",
- 8,
- "1.0.0" },
- { "SET",
- "key value [NX|XX] [GET] [EX seconds|PX milliseconds|EXAT unix-time-seconds|PXAT unix-time-milliseconds|KEEPTTL]",
- "Set the string value of a key",
- 1,
- "1.0.0" },
- { "SETBIT",
- "key offset value",
- "Sets or clears the bit at offset in the string value stored at key",
- 15,
- "2.2.0" },
- { "SETEX",
- "key seconds value",
- "Set the value and expiration of a key",
- 1,
- "2.0.0" },
- { "SETNX",
- "key value",
- "Set the value of a key, only if the key does not exist",
- 1,
- "1.0.0" },
- { "SETRANGE",
- "key offset value",
- "Overwrite part of a string at key starting at the specified offset",
- 1,
- "2.2.0" },
- { "SHUTDOWN",
- "[NOSAVE|SAVE] [NOW] [FORCE] [ABORT]",
- "Synchronously save the dataset to disk and then shut down the server",
- 9,
- "1.0.0" },
- { "SINTER",
- "key [key ...]",
- "Intersect multiple sets",
- 3,
- "1.0.0" },
- { "SINTERCARD",
- "numkeys key [key ...] [LIMIT limit]",
- "Intersect multiple sets and return the cardinality of the result",
- 3,
- "7.0.0" },
- { "SINTERSTORE",
- "destination key [key ...]",
- "Intersect multiple sets and store the resulting set in a key",
- 3,
- "1.0.0" },
- { "SISMEMBER",
- "key member",
- "Determine if a given value is a member of a set",
- 3,
- "1.0.0" },
- { "SLAVEOF",
- "host port",
- "Make the server a replica of another instance, or promote it as master.",
- 9,
- "1.0.0" },
- { "SLOWLOG",
- "",
- "A container for slow log commands",
- 9,
- "2.2.12" },
- { "SLOWLOG GET",
- "[count]",
- "Get the slow log's entries",
- 9,
- "2.2.12" },
- { "SLOWLOG HELP",
- "",
- "Show helpful text about the different subcommands",
- 9,
- "6.2.0" },
- { "SLOWLOG LEN",
- "",
- "Get the slow log's length",
- 9,
- "2.2.12" },
- { "SLOWLOG RESET",
- "",
- "Clear all entries from the slow log",
- 9,
- "2.2.12" },
- { "SMEMBERS",
- "key",
- "Get all the members in a set",
- 3,
- "1.0.0" },
- { "SMISMEMBER",
- "key member [member ...]",
- "Returns the membership associated with the given elements for a set",
- 3,
- "6.2.0" },
- { "SMOVE",
- "source destination member",
- "Move a member from one set to another",
- 3,
- "1.0.0" },
- { "SORT",
- "key [BY by-pattern] [LIMIT offset count] [GET get-pattern [GET get-pattern ...]] [ASC|DESC] [ALPHA] [STORE destination]",
- "Sort the elements in a list, set or sorted set",
- 0,
- "1.0.0" },
- { "SORT_RO",
- "key [BY by-pattern] [LIMIT offset count] [GET get-pattern [GET get-pattern ...]] [ASC|DESC] [ALPHA]",
- "Sort the elements in a list, set or sorted set. Read-only variant of SORT.",
- 0,
- "7.0.0" },
- { "SPOP",
- "key [count]",
- "Remove and return one or multiple random members from a set",
- 3,
- "1.0.0" },
- { "SPUBLISH",
- "shardchannel message",
- "Post a message to a shard channel",
- 6,
- "7.0.0" },
- { "SRANDMEMBER",
- "key [count]",
- "Get one or multiple random members from a set",
- 3,
- "1.0.0" },
- { "SREM",
- "key member [member ...]",
- "Remove one or more members from a set",
- 3,
- "1.0.0" },
- { "SSCAN",
- "key cursor [MATCH pattern] [COUNT count]",
- "Incrementally iterate Set elements",
- 3,
- "2.8.0" },
- { "SSUBSCRIBE",
- "shardchannel [shardchannel ...]",
- "Listen for messages published to the given shard channels",
- 6,
- "7.0.0" },
- { "STRLEN",
- "key",
- "Get the length of the value stored in a key",
- 1,
- "2.2.0" },
- { "SUBSCRIBE",
- "channel [channel ...]",
- "Listen for messages published to the given channels",
- 6,
- "2.0.0" },
- { "SUBSTR",
- "key start end",
- "Get a substring of the string stored at a key",
- 1,
- "1.0.0" },
- { "SUNION",
- "key [key ...]",
- "Add multiple sets",
- 3,
- "1.0.0" },
- { "SUNIONSTORE",
- "destination key [key ...]",
- "Add multiple sets and store the resulting set in a key",
- 3,
- "1.0.0" },
- { "SUNSUBSCRIBE",
- "[shardchannel [shardchannel ...]]",
- "Stop listening for messages posted to the given shard channels",
- 6,
- "7.0.0" },
- { "SWAPDB",
- "index1 index2",
- "Swaps two Redis databases",
- 9,
- "4.0.0" },
- { "SYNC",
- "",
- "Internal command used for replication",
- 9,
- "1.0.0" },
- { "TIME",
- "",
- "Return the current server time",
- 9,
- "2.6.0" },
- { "TOUCH",
- "key [key ...]",
- "Alters the last access time of a key(s). Returns the number of existing keys specified.",
- 0,
- "3.2.1" },
- { "TTL",
- "key",
- "Get the time to live for a key in seconds",
- 0,
- "1.0.0" },
- { "TYPE",
- "key",
- "Determine the type stored at key",
- 0,
- "1.0.0" },
- { "UNLINK",
- "key [key ...]",
- "Delete a key asynchronously in another thread. Otherwise it is just as DEL, but non blocking.",
- 0,
- "4.0.0" },
- { "UNSUBSCRIBE",
- "[channel [channel ...]]",
- "Stop listening for messages posted to the given channels",
- 6,
- "2.0.0" },
- { "UNWATCH",
- "",
- "Forget about all watched keys",
- 7,
- "2.2.0" },
- { "WAIT",
- "numreplicas timeout",
- "Wait for the synchronous replication of all the write commands sent in the context of the current connection",
- 0,
- "3.0.0" },
- { "WAITAOF",
- "numlocal numreplicas timeout",
- "Wait for all write commands sent in the context of the current connection to be synced to AOF of local host and/or replicas",
- 0,
- "7.2.0" },
- { "WATCH",
- "key [key ...]",
- "Watch the given keys to determine execution of the MULTI/EXEC block",
- 7,
- "2.2.0" },
- { "XACK",
- "key group id [id ...]",
- "Marks a pending message as correctly processed, effectively removing it from the pending entries list of the consumer group. Return value of the command is the number of messages successfully acknowledged, that is, the IDs we were actually able to resolve in the PEL.",
- 14,
- "5.0.0" },
- { "XADD",
- "key [NOMKSTREAM] [MAXLEN|MINID [=|~] threshold [LIMIT count]] *|id field value [field value ...]",
- "Appends a new entry to a stream",
- 14,
- "5.0.0" },
- { "XAUTOCLAIM",
- "key group consumer min-idle-time start [COUNT count] [JUSTID]",
- "Changes (or acquires) ownership of messages in a consumer group, as if the messages were delivered to the specified consumer.",
- 14,
- "6.2.0" },
- { "XCLAIM",
- "key group consumer min-idle-time id [id ...] [IDLE ms] [TIME unix-time-milliseconds] [RETRYCOUNT count] [FORCE] [JUSTID] [LASTID lastid]",
- "Changes (or acquires) ownership of a message in a consumer group, as if the message was delivered to the specified consumer.",
- 14,
- "5.0.0" },
- { "XDEL",
- "key id [id ...]",
- "Removes the specified entries from the stream. Returns the number of items actually deleted, that may be different from the number of IDs passed in case certain IDs do not exist.",
- 14,
- "5.0.0" },
- { "XGROUP",
- "",
- "A container for consumer groups commands",
- 14,
- "5.0.0" },
- { "XGROUP CREATE",
- "key group id|$ [MKSTREAM] [ENTRIESREAD entries-read]",
- "Create a consumer group.",
- 14,
- "5.0.0" },
- { "XGROUP CREATECONSUMER",
- "key group consumer",
- "Create a consumer in a consumer group.",
- 14,
- "6.2.0" },
- { "XGROUP DELCONSUMER",
- "key group consumer",
- "Delete a consumer from a consumer group.",
- 14,
- "5.0.0" },
- { "XGROUP DESTROY",
- "key group",
- "Destroy a consumer group.",
- 14,
- "5.0.0" },
- { "XGROUP HELP",
- "",
- "Show helpful text about the different subcommands",
- 14,
- "5.0.0" },
- { "XGROUP SETID",
- "key group id|$ [ENTRIESREAD entriesread]",
- "Set a consumer group to an arbitrary last delivered ID value.",
- 14,
- "5.0.0" },
- { "XINFO",
- "",
- "A container for stream introspection commands",
- 14,
- "5.0.0" },
- { "XINFO CONSUMERS",
- "key group",
- "List the consumers in a consumer group",
- 14,
- "5.0.0" },
- { "XINFO GROUPS",
- "key",
- "List the consumer groups of a stream",
- 14,
- "5.0.0" },
- { "XINFO HELP",
- "",
- "Show helpful text about the different subcommands",
- 14,
- "5.0.0" },
- { "XINFO STREAM",
- "key [FULL [COUNT count]]",
- "Get information about a stream",
- 14,
- "5.0.0" },
- { "XLEN",
- "key",
- "Return the number of entries in a stream",
- 14,
- "5.0.0" },
- { "XPENDING",
- "key group [[IDLE min-idle-time] start end count [consumer]]",
- "Return information and entries from a stream consumer group pending entries list, that are messages fetched but never acknowledged.",
- 14,
- "5.0.0" },
- { "XRANGE",
- "key start end [COUNT count]",
- "Return a range of elements in a stream, with IDs matching the specified IDs interval",
- 14,
- "5.0.0" },
- { "XREAD",
- "[COUNT count] [BLOCK milliseconds] STREAMS key [key ...] id [id ...]",
- "Return never seen elements in multiple streams, with IDs greater than the ones reported by the caller for each stream. Can block.",
- 14,
- "5.0.0" },
- { "XREADGROUP",
- "GROUP group consumer [COUNT count] [BLOCK milliseconds] [NOACK] STREAMS key [key ...] id [id ...]",
- "Return new entries from a stream using a consumer group, or access the history of the pending entries for a given consumer. Can block.",
- 14,
- "5.0.0" },
- { "XREVRANGE",
- "key end start [COUNT count]",
- "Return a range of elements in a stream, with IDs matching the specified IDs interval, in reverse order (from greater to smaller IDs) compared to XRANGE",
- 14,
- "5.0.0" },
- { "XSETID",
- "key last-id [ENTRIESADDED entries-added] [MAXDELETEDID max-deleted-id]",
- "An internal command for replicating stream values",
- 14,
- "5.0.0" },
- { "XTRIM",
- "key MAXLEN|MINID [=|~] threshold [LIMIT count]",
- "Trims the stream to (approximately if '~' is passed) a certain size",
- 14,
- "5.0.0" },
- { "ZADD",
- "key [NX|XX] [GT|LT] [CH] [INCR] score member [score member ...]",
- "Add one or more members to a sorted set, or update its score if it already exists",
- 4,
- "1.2.0" },
- { "ZCARD",
- "key",
- "Get the number of members in a sorted set",
- 4,
- "1.2.0" },
- { "ZCOUNT",
- "key min max",
- "Count the members in a sorted set with scores within the given values",
- 4,
- "2.0.0" },
- { "ZDIFF",
- "numkeys key [key ...] [WITHSCORES]",
- "Subtract multiple sorted sets",
- 4,
- "6.2.0" },
- { "ZDIFFSTORE",
- "destination numkeys key [key ...]",
- "Subtract multiple sorted sets and store the resulting sorted set in a new key",
- 4,
- "6.2.0" },
- { "ZINCRBY",
- "key increment member",
- "Increment the score of a member in a sorted set",
- 4,
- "1.2.0" },
- { "ZINTER",
- "numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX] [WITHSCORES]",
- "Intersect multiple sorted sets",
- 4,
- "6.2.0" },
- { "ZINTERCARD",
- "numkeys key [key ...] [LIMIT limit]",
- "Intersect multiple sorted sets and return the cardinality of the result",
- 4,
- "7.0.0" },
- { "ZINTERSTORE",
- "destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]",
- "Intersect multiple sorted sets and store the resulting sorted set in a new key",
- 4,
- "2.0.0" },
- { "ZLEXCOUNT",
- "key min max",
- "Count the number of members in a sorted set between a given lexicographical range",
- 4,
- "2.8.9" },
- { "ZMPOP",
- "numkeys key [key ...] MIN|MAX [COUNT count]",
- "Remove and return members with scores in a sorted set",
- 4,
- "7.0.0" },
- { "ZMSCORE",
- "key member [member ...]",
- "Get the score associated with the given members in a sorted set",
- 4,
- "6.2.0" },
- { "ZPOPMAX",
- "key [count]",
- "Remove and return members with the highest scores in a sorted set",
- 4,
- "5.0.0" },
- { "ZPOPMIN",
- "key [count]",
- "Remove and return members with the lowest scores in a sorted set",
- 4,
- "5.0.0" },
- { "ZRANDMEMBER",
- "key [count [WITHSCORES]]",
- "Get one or multiple random elements from a sorted set",
- 4,
- "6.2.0" },
- { "ZRANGE",
- "key start stop [BYSCORE|BYLEX] [REV] [LIMIT offset count] [WITHSCORES]",
- "Return a range of members in a sorted set",
- 4,
- "1.2.0" },
- { "ZRANGEBYLEX",
- "key min max [LIMIT offset count]",
- "Return a range of members in a sorted set, by lexicographical range",
- 4,
- "2.8.9" },
- { "ZRANGEBYSCORE",
- "key min max [WITHSCORES] [LIMIT offset count]",
- "Return a range of members in a sorted set, by score",
- 4,
- "1.0.5" },
- { "ZRANGESTORE",
- "dst src min max [BYSCORE|BYLEX] [REV] [LIMIT offset count]",
- "Store a range of members from sorted set into another key",
- 4,
- "6.2.0" },
- { "ZRANK",
- "key member [WITHSCORE]",
- "Determine the index of a member in a sorted set",
- 4,
- "2.0.0" },
- { "ZREM",
- "key member [member ...]",
- "Remove one or more members from a sorted set",
- 4,
- "1.2.0" },
- { "ZREMRANGEBYLEX",
- "key min max",
- "Remove all members in a sorted set between the given lexicographical range",
- 4,
- "2.8.9" },
- { "ZREMRANGEBYRANK",
- "key start stop",
- "Remove all members in a sorted set within the given indexes",
- 4,
- "2.0.0" },
- { "ZREMRANGEBYSCORE",
- "key min max",
- "Remove all members in a sorted set within the given scores",
- 4,
- "1.2.0" },
- { "ZREVRANGE",
- "key start stop [WITHSCORES]",
- "Return a range of members in a sorted set, by index, with scores ordered from high to low",
- 4,
- "1.2.0" },
- { "ZREVRANGEBYLEX",
- "key max min [LIMIT offset count]",
- "Return a range of members in a sorted set, by lexicographical range, ordered from higher to lower strings.",
- 4,
- "2.8.9" },
- { "ZREVRANGEBYSCORE",
- "key max min [WITHSCORES] [LIMIT offset count]",
- "Return a range of members in a sorted set, by score, with scores ordered from high to low",
- 4,
- "2.2.0" },
- { "ZREVRANK",
- "key member [WITHSCORE]",
- "Determine the index of a member in a sorted set, with scores ordered from high to low",
- 4,
- "2.0.0" },
- { "ZSCAN",
- "key cursor [MATCH pattern] [COUNT count]",
- "Incrementally iterate sorted sets elements and associated scores",
- 4,
- "2.8.0" },
- { "ZSCORE",
- "key member",
- "Get the score associated with the given member in a sorted set",
- 4,
- "1.2.0" },
- { "ZUNION",
- "numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX] [WITHSCORES]",
- "Add multiple sorted sets",
- 4,
- "6.2.0" },
- { "ZUNIONSTORE",
- "destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]",
- "Add multiple sorted sets and store the resulting sorted set in a new key",
- 4,
- "2.0.0" }
-};
-
-#endif
diff --git a/src/latency.c b/src/latency.c
index a784be88e..d46890e82 100644
--- a/src/latency.c
+++ b/src/latency.c
@@ -34,6 +34,7 @@
*/
#include "server.h"
+#include "hdr_histogram.h"
/* Dictionary type for latency events. */
int dictStringKeyCompare(dict *d, const void *key1, const void *key2) {
@@ -725,3 +726,14 @@ nodataerr:
"No samples available for event '%s'", (char*) c->argv[2]->ptr);
}
+void durationAddSample(int type, monotime duration) {
+ if (type >= EL_DURATION_TYPE_NUM) {
+ return;
+ }
+ durationStats* ds = &server.duration_stats[type];
+ ds->cnt++;
+ ds->sum += duration;
+ if (duration > ds->max) {
+ ds->max = duration;
+ }
+}
diff --git a/src/latency.h b/src/latency.h
index 6f2a854dc..13503d5c0 100644
--- a/src/latency.h
+++ b/src/latency.h
@@ -89,4 +89,20 @@ void latencyAddSample(const char *event, mstime_t latency);
#define latencyRemoveNestedEvent(event_var,nested_var) \
event_var += nested_var;
+typedef struct durationStats {
+ unsigned long long cnt;
+ unsigned long long sum;
+ unsigned long long max;
+} durationStats;
+
+typedef enum {
+ EL_DURATION_TYPE_EL = 0, // cumulative time duration metric of the whole eventloop
+ EL_DURATION_TYPE_CMD, // cumulative time duration metric of executing commands
+ EL_DURATION_TYPE_AOF, // cumulative time duration metric of flushing AOF in eventloop
+ EL_DURATION_TYPE_CRON, // cumulative time duration metric of cron (serverCron and beforeSleep, but excluding IO and AOF)
+ EL_DURATION_TYPE_NUM
+} DurationType;
+
+void durationAddSample(int type, monotime duration);
+
#endif /* __LATENCY_H */
diff --git a/src/lazyfree.c b/src/lazyfree.c
index a44ad2df4..8ac55f777 100644
--- a/src/lazyfree.c
+++ b/src/lazyfree.c
@@ -82,7 +82,7 @@ size_t lazyfreeGetFreedObjectsCount(void) {
return aux;
}
-void lazyfreeResetStats() {
+void lazyfreeResetStats(void) {
atomicSet(lazyfreed_objects,0);
}
diff --git a/src/listpack.c b/src/listpack.c
index f7c867f2e..ecc7e9f6f 100644
--- a/src/listpack.c
+++ b/src/listpack.c
@@ -1693,7 +1693,7 @@ char *mixlist[] = {"hello", "foo", "quux", "1024"};
char *intlist[] = {"4294967296", "-100", "100", "128000",
"non integer", "much much longer non integer"};
-static unsigned char *createList() {
+static unsigned char *createList(void) {
unsigned char *lp = lpNew(0);
lp = lpAppend(lp, (unsigned char*)mixlist[1], strlen(mixlist[1]));
lp = lpAppend(lp, (unsigned char*)mixlist[2], strlen(mixlist[2]));
@@ -1702,7 +1702,7 @@ static unsigned char *createList() {
return lp;
}
-static unsigned char *createIntList() {
+static unsigned char *createIntList(void) {
unsigned char *lp = lpNew(0);
lp = lpAppend(lp, (unsigned char*)intlist[2], strlen(intlist[2]));
lp = lpAppend(lp, (unsigned char*)intlist[3], strlen(intlist[3]));
diff --git a/src/listpack_malloc.h b/src/listpack_malloc.h
index 3a9050052..a8a81c35e 100644
--- a/src/listpack_malloc.h
+++ b/src/listpack_malloc.h
@@ -39,8 +39,11 @@
#ifndef LISTPACK_ALLOC_H
#define LISTPACK_ALLOC_H
#include "zmalloc.h"
-#define lp_malloc zmalloc
-#define lp_realloc zrealloc
+/* We use zmalloc_usable/zrealloc_usable instead of zmalloc/zrealloc
+ * to ensure the safe invocation of 'zmalloc_usable_size().
+ * See comment in zmalloc_usable_size(). */
+#define lp_malloc(sz) zmalloc_usable(sz,NULL)
+#define lp_realloc(ptr,sz) zrealloc_usable(ptr,sz,NULL)
#define lp_free zfree
#define lp_malloc_size zmalloc_usable_size
#endif
diff --git a/src/logreqres.c b/src/logreqres.c
index aa54b721d..6e7621d35 100644
--- a/src/logreqres.c
+++ b/src/logreqres.c
@@ -186,7 +186,8 @@ size_t reqresAppendRequest(client *c) {
/* Ignore commands that have streaming non-standard response */
sds cmd = argv[0]->ptr;
- if (!strcasecmp(cmd,"sync") ||
+ if (!strcasecmp(cmd,"debug") || /* because of DEBUG SEGFAULT */
+ !strcasecmp(cmd,"sync") ||
!strcasecmp(cmd,"psync") ||
!strcasecmp(cmd,"monitor") ||
!strcasecmp(cmd,"subscribe") ||
@@ -194,11 +195,7 @@ size_t reqresAppendRequest(client *c) {
!strcasecmp(cmd,"ssubscribe") ||
!strcasecmp(cmd,"sunsubscribe") ||
!strcasecmp(cmd,"psubscribe") ||
- !strcasecmp(cmd,"punsubscribe") ||
- !strcasecmp(cmd,"debug") ||
- !strcasecmp(cmd,"pfdebug") ||
- !strcasecmp(cmd,"lolwut") ||
- (!strcasecmp(cmd,"sentinel") && argc > 1 && !strcasecmp(argv[1]->ptr,"debug")))
+ !strcasecmp(cmd,"punsubscribe"))
{
return 0;
}
diff --git a/src/module.c b/src/module.c
index a8df08bbb..7277e3e72 100644
--- a/src/module.c
+++ b/src/module.c
@@ -58,6 +58,7 @@
#include "monotonic.h"
#include "script.h"
#include "call_reply.h"
+#include "hdr_histogram.h"
#include <dlfcn.h>
#include <sys/stat.h>
#include <sys/wait.h>
@@ -516,13 +517,20 @@ void moduleCreateContext(RedisModuleCtx *out_ctx, RedisModule *module, int ctx_f
* You should avoid using malloc().
* This function panics if unable to allocate enough memory. */
void *RM_Alloc(size_t bytes) {
- return zmalloc(bytes);
+ /* Use 'zmalloc_usable()' instead of 'zmalloc()' to allow the compiler
+ * to recognize the additional memory size, which means that modules can
+ * use the memory reported by 'RM_MallocUsableSize()' safely. In theory this
+ * isn't really needed since this API can't be inlined (not even for embedded
+ * modules like TLS (we use function pointers for module APIs), and the API doesn't
+ * have the malloc_size attribute, but it's hard to predict how smart future compilers
+ * will be, so better safe than sorry. */
+ return zmalloc_usable(bytes,NULL);
}
/* Similar to RM_Alloc, but returns NULL in case of allocation failure, instead
* of panicking. */
void *RM_TryAlloc(size_t bytes) {
- return ztrymalloc(bytes);
+ return ztrymalloc_usable(bytes,NULL);
}
/* Use like calloc(). Memory allocated with this function is reported in
@@ -530,12 +538,12 @@ void *RM_TryAlloc(size_t bytes) {
* and in general is taken into account as memory allocated by Redis.
* You should avoid using calloc() directly. */
void *RM_Calloc(size_t nmemb, size_t size) {
- return zcalloc(nmemb*size);
+ return zcalloc_usable(nmemb*size,NULL);
}
/* Use like realloc() for memory obtained with RedisModule_Alloc(). */
void* RM_Realloc(void *ptr, size_t bytes) {
- return zrealloc(ptr,bytes);
+ return zrealloc_usable(ptr,bytes,NULL);
}
/* Use like free() for memory obtained by RedisModule_Alloc() and
@@ -647,6 +655,7 @@ void moduleReleaseTempClient(client *c) {
clearClientConnectionState(c);
listEmpty(c->reply);
c->reply_bytes = 0;
+ c->duration = 0;
resetClient(c);
c->bufpos = 0;
c->flags = CLIENT_MODULE;
@@ -780,7 +789,7 @@ int RM_GetApi(const char *funcname, void **targetPtrPtr) {
return REDISMODULE_OK;
}
-void modulePostExecutionUnitOperations() {
+void modulePostExecutionUnitOperations(void) {
if (server.execution_nesting)
return;
@@ -1294,10 +1303,9 @@ RedisModuleCommand *moduleCreateCommandProxy(struct RedisModule *module, sds dec
cp->rediscmd->proc = RedisModuleCommandDispatcher;
cp->rediscmd->flags = flags | CMD_MODULE;
cp->rediscmd->module_cmd = cp;
- cp->rediscmd->key_specs_max = STATIC_KEY_SPECS_NUM;
- cp->rediscmd->key_specs = cp->rediscmd->key_specs_static;
if (firstkey != 0) {
cp->rediscmd->key_specs_num = 1;
+ cp->rediscmd->key_specs = zcalloc(sizeof(keySpec));
cp->rediscmd->key_specs[0].flags = CMD_KEY_FULL_ACCESS;
if (flags & CMD_MODULE_GETKEYS)
cp->rediscmd->key_specs[0].flags |= CMD_KEY_VARIABLE_FLAGS;
@@ -1309,6 +1317,7 @@ RedisModuleCommand *moduleCreateCommandProxy(struct RedisModule *module, sds dec
cp->rediscmd->key_specs[0].fk.range.limit = 0;
} else {
cp->rediscmd->key_specs_num = 0;
+ cp->rediscmd->key_specs = NULL;
}
populateCommandLegacyRangeSpec(cp->rediscmd);
cp->rediscmd->microseconds = 0;
@@ -1424,6 +1433,21 @@ moduleCmdArgAt(const RedisModuleCommandInfoVersion *version,
return (RedisModuleCommandArg *)((char *)(args) + offset);
}
+/* Recursively populate the args structure (setting num_args to the number of
+ * subargs) and return the number of args. */
+int populateArgsStructure(struct redisCommandArg *args) {
+ if (!args)
+ return 0;
+ int count = 0;
+ while (args->name) {
+ serverAssert(count < INT_MAX);
+ args->num_args = populateArgsStructure(args->subargs);
+ count++;
+ args++;
+ }
+ return count;
+}
+
/* Helper for categoryFlagsFromString(). Attempts to find an acl flag representing the provided flag string
* and adds that flag to acl_categories_flags if a match is found.
*
@@ -1796,7 +1820,7 @@ int RM_SetCommandInfo(RedisModuleCommand *command, const RedisModuleCommandInfo
cmd->tips || cmd->args ||
!(cmd->key_specs_num == 0 ||
/* Allow key spec populated from legacy (first,last,step) to exist. */
- (cmd->key_specs_num == 1 && cmd->key_specs == cmd->key_specs_static &&
+ (cmd->key_specs_num == 1 &&
cmd->key_specs[0].begin_search_type == KSPEC_BS_INDEX &&
cmd->key_specs[0].find_keys_type == KSPEC_FK_RANGE))) {
errno = EEXIST;
@@ -1847,13 +1871,8 @@ int RM_SetCommandInfo(RedisModuleCommand *command, const RedisModuleCommandInfo
while (moduleCmdKeySpecAt(version, info->key_specs, count)->begin_search_type)
count++;
serverAssert(count < INT_MAX);
- if (count <= STATIC_KEY_SPECS_NUM) {
- cmd->key_specs_max = STATIC_KEY_SPECS_NUM;
- cmd->key_specs = cmd->key_specs_static;
- } else {
- cmd->key_specs_max = count;
- cmd->key_specs = zmalloc(sizeof(keySpec) * count);
- }
+ zfree(cmd->key_specs);
+ cmd->key_specs = zmalloc(sizeof(keySpec) * count);
/* Copy the contents of the RedisModuleCommandKeySpec array. */
cmd->key_specs_num = count;
@@ -2257,7 +2276,7 @@ uint64_t RM_MonotonicMicroseconds(void) {
}
/* Return the current UNIX time in microseconds */
-ustime_t RM_Microseconds() {
+ustime_t RM_Microseconds(void) {
return ustime();
}
@@ -2267,7 +2286,7 @@ ustime_t RM_Microseconds() {
* key space notification, causing a module to execute a RedisModule_Call,
* causing another notification, etc.
* It makes sense that all this callbacks would use the same clock. */
-ustime_t RM_CachedMicroseconds() {
+ustime_t RM_CachedMicroseconds(void) {
return server.ustime;
}
@@ -2978,6 +2997,32 @@ int RM_ReplyWithError(RedisModuleCtx *ctx, const char *err) {
return REDISMODULE_OK;
}
+/* Reply with the error create from a printf format and arguments.
+ *
+ * If the error code is already passed in the string 'fmt', the error
+ * code provided is used, otherwise the string "-ERR " for the generic
+ * error code is automatically added.
+ *
+ * The usage is, for example:
+ *
+ * RedisModule_ReplyWithErrorFormat(ctx, "An error: %s", "foo");
+ *
+ * RedisModule_ReplyWithErrorFormat(ctx, "-WRONGTYPE Wrong Type: %s", "foo");
+ *
+ * The function always returns REDISMODULE_OK.
+ */
+int RM_ReplyWithErrorFormat(RedisModuleCtx *ctx, const char *fmt, ...) {
+ client *c = moduleGetReplyClient(ctx);
+ if (c == NULL) return REDISMODULE_OK;
+
+ va_list ap;
+ va_start(ap, fmt);
+ addReplyErrorFormatInternal(c, 0, fmt, ap);
+ va_end(ap);
+
+ return REDISMODULE_OK;
+}
+
/* Reply with a simple string (`+... \r\n` in RESP protocol). This replies
* are suitable only when sending a small non-binary string with small
* overhead, like "OK" or similar replies.
@@ -3868,7 +3913,7 @@ int RM_GetContextFlags(RedisModuleCtx *ctx) {
* garbage collection tasks, or that do writes and replicate such writes
* periodically in timer callbacks or other periodic callbacks.
*/
-int RM_AvoidReplicaTraffic() {
+int RM_AvoidReplicaTraffic(void) {
return !!(isPausedActionsWithUpdate(PAUSE_ACTION_REPLICA));
}
@@ -3978,7 +4023,7 @@ RedisModuleKey *RM_OpenKey(RedisModuleCtx *ctx, robj *keyname, int mode) {
* // REDISMODULE_OPEN_KEY_NOTOUCH is not supported
* }
*/
-int RM_GetOpenKeyModesAll() {
+int RM_GetOpenKeyModesAll(void) {
return _REDISMODULE_OPEN_KEY_ALL;
}
@@ -4650,6 +4695,7 @@ int RM_ZsetAdd(RedisModuleKey *key, double score, RedisModuleString *ele, int *f
if (flagsptr) in_flags = moduleZsetAddFlagsToCoreFlags(*flagsptr);
if (zsetAdd(key->value,score,ele->ptr,in_flags,&out_flags,NULL) == 0) {
if (flagsptr) *flagsptr = 0;
+ moduleDelKeyIfEmpty(key);
return REDISMODULE_ERR;
}
if (flagsptr) *flagsptr = moduleZsetAddFlagsFromCoreFlags(out_flags);
@@ -4678,6 +4724,7 @@ int RM_ZsetIncrby(RedisModuleKey *key, double score, RedisModuleString *ele, int
in_flags |= ZADD_IN_INCR;
if (zsetAdd(key->value,score,ele->ptr,in_flags,&out_flags,newscore) == 0) {
if (flagsptr) *flagsptr = 0;
+ moduleDelKeyIfEmpty(key);
return REDISMODULE_ERR;
}
if (flagsptr) *flagsptr = moduleZsetAddFlagsFromCoreFlags(out_flags);
@@ -5360,6 +5407,7 @@ int RM_StreamAdd(RedisModuleKey *key, int flags, RedisModuleStreamID *id, RedisM
/* Either the ID not greater than all existing IDs in the stream, or
* the elements are too large to be stored. either way, errno is already
* set by streamAppendItem. */
+ if (created) moduleDelKeyIfEmpty(key);
return REDISMODULE_ERR;
}
/* Postponed signalKeyAsReady(). Done implicitly by moduleCreateEmptyKey()
@@ -6905,7 +6953,7 @@ void moduleRDBLoadError(RedisModuleIO *io) {
/* Returns 0 if there's at least one registered data type that did not declare
* REDISMODULE_OPTIONS_HANDLE_IO_ERRORS, in which case diskless loading should
* be avoided since it could cause data loss. */
-int moduleAllDatatypesHandleErrors() {
+int moduleAllDatatypesHandleErrors(void) {
dictIterator *di = dictGetIterator(modules);
dictEntry *de;
@@ -6925,7 +6973,7 @@ int moduleAllDatatypesHandleErrors() {
/* Returns 0 if module did not declare REDISMODULE_OPTIONS_HANDLE_REPL_ASYNC_LOAD, in which case
* diskless async loading should be avoided because module doesn't know there can be traffic during
* database full resynchronization. */
-int moduleAllModulesHandleReplAsyncLoad() {
+int moduleAllModulesHandleReplAsyncLoad(void) {
dictIterator *di = dictGetIterator(modules);
dictEntry *de;
@@ -8373,7 +8421,7 @@ void RM_FreeThreadSafeContext(RedisModuleCtx *ctx) {
zfree(ctx);
}
-void moduleGILAfterLock() {
+void moduleGILAfterLock(void) {
/* We should never get here if we already inside a module
* code block which already opened a context. */
serverAssert(server.execution_nesting == 0);
@@ -8409,7 +8457,7 @@ int RM_ThreadSafeContextTryLock(RedisModuleCtx *ctx) {
return REDISMODULE_OK;
}
-void moduleGILBeforeUnlock() {
+void moduleGILBeforeUnlock(void) {
/* We should never get here if we already inside a module
* code block which already opened a context, except
* the bump-up from moduleGILAcquired. */
@@ -8509,7 +8557,7 @@ void moduleReleaseGIL(void) {
* that the notification code will be executed in the middle on Redis logic
* (commands logic, eviction, expire). Changing the key space while the logic
* runs is dangerous and discouraged. In order to react to key space events with
- * write actions, please refer to `RM_AddPostExecutionUnitJob`.
+ * write actions, please refer to `RM_AddPostNotificationJob`.
*
* See https://redis.io/topics/notifications for more information.
*/
@@ -8524,7 +8572,7 @@ int RM_SubscribeToKeyspaceEvents(RedisModuleCtx *ctx, int types, RedisModuleNoti
return REDISMODULE_OK;
}
-void firePostExecutionUnitJobs() {
+void firePostExecutionUnitJobs(void) {
/* Avoid propagation of commands.
* In that way, postExecutionUnitOperations will prevent
* recursive calls to firePostExecutionUnitJobs.
@@ -8577,7 +8625,7 @@ int RM_AddPostNotificationJob(RedisModuleCtx *ctx, RedisModulePostNotificationJo
/* Get the configured bitmap of notify-keyspace-events (Could be used
* for additional filtering in RedisModuleNotificationFunc) */
-int RM_GetNotifyKeyspaceEvents() {
+int RM_GetNotifyKeyspaceEvents(void) {
return server.notify_keyspace_events;
}
@@ -9292,7 +9340,7 @@ int RM_EventLoopAddOneShot(RedisModuleEventLoopOneShotFunc func, void *user_data
/* This function will check the moduleEventLoopOneShots queue in order to
* call the callback for the registered oneshot events. */
-static void eventLoopHandleOneShotEvents() {
+static void eventLoopHandleOneShotEvents(void) {
pthread_mutex_lock(&moduleEventLoopMutex);
if (moduleEventLoopOneShots) {
while (listLength(moduleEventLoopOneShots)) {
@@ -10393,7 +10441,7 @@ int RM_ExportSharedAPI(RedisModuleCtx *ctx, const char *apiname, void *func) {
*
* Here is an example:
*
- * int ... myCommandImplementation() {
+ * int ... myCommandImplementation(void) {
* if (getExternalAPIs() == 0) {
* reply with an error here if we cannot have the APIs
* }
@@ -10693,6 +10741,9 @@ size_t RM_MallocSize(void* ptr) {
/* Similar to RM_MallocSize, the difference is that RM_MallocUsableSize
* returns the usable size of memory by the module. */
size_t RM_MallocUsableSize(void *ptr) {
+ /* It is safe to use 'zmalloc_usable_size()' to manipulate additional
+ * memory space, as we guarantee that the compiler can recognize this
+ * after 'RM_Alloc', 'RM_TryAlloc', 'RM_Realloc', or 'RM_Calloc'. */
return zmalloc_usable_size(ptr);
}
@@ -10723,7 +10774,7 @@ size_t RM_MallocSizeDict(RedisModuleDict* dict) {
* * Exactly 1 - Memory limit reached.
* * Greater 1 - More memory used than the configured limit.
*/
-float RM_GetUsedMemoryRatio(){
+float RM_GetUsedMemoryRatio(void){
float level;
getMaxmemoryState(NULL, NULL, NULL, &level);
return level;
@@ -10762,7 +10813,7 @@ static void moduleScanCallback(void *privdata, const dictEntry *de) {
}
/* Create a new cursor to be used with RedisModule_Scan */
-RedisModuleScanCursor *RM_ScanCursorCreate() {
+RedisModuleScanCursor *RM_ScanCursorCreate(void) {
RedisModuleScanCursor* cursor = zmalloc(sizeof(*cursor));
cursor->cursor = 0;
cursor->done = 0;
@@ -11645,7 +11696,7 @@ void moduleNotifyKeyUnlink(robj *key, robj *val, int dbid, int flags) {
} else if (flags & DB_FLAG_KEY_OVERWRITE) {
subevent = REDISMODULE_SUBEVENT_KEY_OVERWRITTEN;
}
- KeyInfo info = {dbid, key, val, REDISMODULE_WRITE};
+ KeyInfo info = {dbid, key, val, REDISMODULE_READ};
moduleFireServerEvent(REDISMODULE_EVENT_KEY, subevent, &info);
if (val->type == OBJ_MODULE) {
@@ -11925,8 +11976,7 @@ int moduleFreeCommand(struct RedisModule *module, struct redisCommand *cmd) {
if (cmd->key_specs[j].begin_search_type == KSPEC_BS_KEYWORD)
zfree((char *)cmd->key_specs[j].bs.keyword.keyword);
}
- if (cmd->key_specs != cmd->key_specs_static)
- zfree(cmd->key_specs);
+ zfree(cmd->key_specs);
for (int j = 0; cmd->tips && cmd->tips[j]; j++)
zfree((char *)cmd->tips[j]);
zfree(cmd->tips);
@@ -12741,6 +12791,137 @@ int RM_LoadConfigs(RedisModuleCtx *ctx) {
return REDISMODULE_OK;
}
+/* --------------------------------------------------------------------------
+ * ## RDB load/save API
+ * -------------------------------------------------------------------------- */
+
+#define REDISMODULE_RDB_STREAM_FILE 1
+
+typedef struct RedisModuleRdbStream {
+ int type;
+
+ union {
+ char *filename;
+ } data;
+} RedisModuleRdbStream;
+
+/* Create a stream object to save/load RDB to/from a file.
+ *
+ * This function returns a pointer to RedisModuleRdbStream which is owned
+ * by the caller. It requires a call to RM_RdbStreamFree() to free
+ * the object. */
+RedisModuleRdbStream *RM_RdbStreamCreateFromFile(const char *filename) {
+ RedisModuleRdbStream *stream = zmalloc(sizeof(*stream));
+ stream->type = REDISMODULE_RDB_STREAM_FILE;
+ stream->data.filename = zstrdup(filename);
+ return stream;
+}
+
+/* Release an RDB stream object. */
+void RM_RdbStreamFree(RedisModuleRdbStream *stream) {
+ switch (stream->type) {
+ case REDISMODULE_RDB_STREAM_FILE:
+ zfree(stream->data.filename);
+ break;
+ default:
+ serverAssert(0);
+ break;
+ }
+ zfree(stream);
+}
+
+/* Load RDB file from the `stream`. Dataset will be cleared first and then RDB
+ * file will be loaded.
+ *
+ * `flags` must be zero. This parameter is for future use.
+ *
+ * On success REDISMODULE_OK is returned, otherwise REDISMODULE_ERR is returned
+ * and errno is set accordingly.
+ *
+ * Example:
+ *
+ * RedisModuleRdbStream *s = RedisModule_RdbStreamCreateFromFile("exp.rdb");
+ * RedisModule_RdbLoad(ctx, s, 0);
+ * RedisModule_RdbStreamFree(s);
+ */
+int RM_RdbLoad(RedisModuleCtx *ctx, RedisModuleRdbStream *stream, int flags) {
+ UNUSED(ctx);
+
+ if (!stream || flags != 0) {
+ errno = EINVAL;
+ return REDISMODULE_ERR;
+ }
+
+ /* Not allowed on replicas. */
+ if (server.masterhost != NULL) {
+ errno = ENOTSUP;
+ return REDISMODULE_ERR;
+ }
+
+ /* Drop replicas if exist. */
+ disconnectSlaves();
+ freeReplicationBacklog();
+
+ if (server.aof_state != AOF_OFF) stopAppendOnly();
+
+ /* Kill existing RDB fork as it is saving outdated data. Also killing it
+ * will prevent COW memory issue. */
+ if (server.child_type == CHILD_TYPE_RDB) killRDBChild();
+
+ emptyData(-1,EMPTYDB_NO_FLAGS,NULL);
+
+ /* rdbLoad() can go back to the networking and process network events. If
+ * RM_RdbLoad() is called inside a command callback, we don't want to
+ * process the current client. Otherwise, we may free the client or try to
+ * process next message while we are already in the command callback. */
+ if (server.current_client) protectClient(server.current_client);
+
+ serverAssert(stream->type == REDISMODULE_RDB_STREAM_FILE);
+ int ret = rdbLoad(stream->data.filename,NULL,RDBFLAGS_NONE);
+
+ if (server.current_client) unprotectClient(server.current_client);
+ if (server.aof_state != AOF_OFF) startAppendOnly();
+
+ if (ret != RDB_OK) {
+ errno = (ret == RDB_NOT_EXIST) ? ENOENT : EIO;
+ return REDISMODULE_ERR;
+ }
+
+ errno = 0;
+ return REDISMODULE_OK;
+}
+
+/* Save dataset to the RDB stream.
+ *
+ * `flags` must be zero. This parameter is for future use.
+ *
+ * On success REDISMODULE_OK is returned, otherwise REDISMODULE_ERR is returned
+ * and errno is set accordingly.
+ *
+ * Example:
+ *
+ * RedisModuleRdbStream *s = RedisModule_RdbStreamCreateFromFile("exp.rdb");
+ * RedisModule_RdbSave(ctx, s, 0);
+ * RedisModule_RdbStreamFree(s);
+ */
+int RM_RdbSave(RedisModuleCtx *ctx, RedisModuleRdbStream *stream, int flags) {
+ UNUSED(ctx);
+
+ if (!stream || flags != 0) {
+ errno = EINVAL;
+ return REDISMODULE_ERR;
+ }
+
+ serverAssert(stream->type == REDISMODULE_RDB_STREAM_FILE);
+
+ if (rdbSaveToFile(stream->data.filename) != C_OK) {
+ return REDISMODULE_ERR;
+ }
+
+ errno = 0;
+ return REDISMODULE_OK;
+}
+
/* Redis MODULE command.
*
* MODULE LIST
@@ -12890,7 +13071,7 @@ int RM_GetLFU(RedisModuleKey *key, long long *lfu_freq) {
* // REDISMODULE_OPTIONS_ALLOW_NESTED_KEYSPACE_NOTIFICATIONS is not supported
* }
*/
-int RM_GetModuleOptionsAll() {
+int RM_GetModuleOptionsAll(void) {
return _REDISMODULE_OPTIONS_FLAGS_NEXT - 1;
}
@@ -12907,7 +13088,7 @@ int RM_GetModuleOptionsAll() {
* // REDISMODULE_CTX_FLAGS_MULTI is not supported
* }
*/
-int RM_GetContextFlagsAll() {
+int RM_GetContextFlagsAll(void) {
return _REDISMODULE_CTX_FLAGS_NEXT - 1;
}
@@ -12924,7 +13105,7 @@ int RM_GetContextFlagsAll() {
* // REDISMODULE_NOTIFY_LOADED is not supported
* }
*/
-int RM_GetKeyspaceNotificationFlagsAll() {
+int RM_GetKeyspaceNotificationFlagsAll(void) {
return _REDISMODULE_NOTIFY_NEXT - 1;
}
@@ -12932,7 +13113,7 @@ int RM_GetKeyspaceNotificationFlagsAll() {
* Return the redis version in format of 0x00MMmmpp.
* Example for 6.0.7 the return value will be 0x00060007.
*/
-int RM_GetServerVersion() {
+int RM_GetServerVersion(void) {
return REDIS_VERSION_NUM;
}
@@ -12941,7 +13122,7 @@ int RM_GetServerVersion() {
* You can use that when calling RM_CreateDataType to know which fields of
* RedisModuleTypeMethods are gonna be supported and which will be ignored.
*/
-int RM_GetTypeMethodVersion() {
+int RM_GetTypeMethodVersion(void) {
return REDISMODULE_TYPE_METHOD_VERSION;
}
@@ -13287,6 +13468,7 @@ void moduleRegisterCoreAPI(void) {
REGISTER_API(WrongArity);
REGISTER_API(ReplyWithLongLong);
REGISTER_API(ReplyWithError);
+ REGISTER_API(ReplyWithErrorFormat);
REGISTER_API(ReplyWithSimpleString);
REGISTER_API(ReplyWithArray);
REGISTER_API(ReplyWithMap);
@@ -13617,4 +13799,8 @@ void moduleRegisterCoreAPI(void) {
REGISTER_API(RegisterEnumConfig);
REGISTER_API(LoadConfigs);
REGISTER_API(RegisterAuthCallback);
+ REGISTER_API(RdbStreamCreateFromFile);
+ REGISTER_API(RdbStreamFree);
+ REGISTER_API(RdbLoad);
+ REGISTER_API(RdbSave);
}
diff --git a/src/monotonic.c b/src/monotonic.c
index 608fa351c..1d71962f3 100644
--- a/src/monotonic.c
+++ b/src/monotonic.c
@@ -32,11 +32,11 @@ static char monotonic_info_string[32];
static long mono_ticksPerMicrosecond = 0;
-static monotime getMonotonicUs_x86() {
+static monotime getMonotonicUs_x86(void) {
return __rdtsc() / mono_ticksPerMicrosecond;
}
-static void monotonicInit_x86linux() {
+static void monotonicInit_x86linux(void) {
const int bufflen = 256;
char buf[bufflen];
regex_t cpuGhzRegex, constTscRegex;
@@ -99,24 +99,24 @@ static void monotonicInit_x86linux() {
static long mono_ticksPerMicrosecond = 0;
/* Read the clock value. */
-static inline uint64_t __cntvct() {
+static inline uint64_t __cntvct(void) {
uint64_t virtual_timer_value;
__asm__ volatile("mrs %0, cntvct_el0" : "=r"(virtual_timer_value));
return virtual_timer_value;
}
/* Read the Count-timer Frequency. */
-static inline uint32_t cntfrq_hz() {
+static inline uint32_t cntfrq_hz(void) {
uint64_t virtual_freq_value;
__asm__ volatile("mrs %0, cntfrq_el0" : "=r"(virtual_freq_value));
return (uint32_t)virtual_freq_value; /* top 32 bits are reserved */
}
-static monotime getMonotonicUs_aarch64() {
+static monotime getMonotonicUs_aarch64(void) {
return __cntvct() / mono_ticksPerMicrosecond;
}
-static void monotonicInit_aarch64() {
+static void monotonicInit_aarch64(void) {
mono_ticksPerMicrosecond = (long)cntfrq_hz() / 1000L / 1000L;
if (mono_ticksPerMicrosecond == 0) {
fprintf(stderr, "monotonic: aarch64, unable to determine clock rate");
@@ -130,7 +130,7 @@ static void monotonicInit_aarch64() {
#endif
-static monotime getMonotonicUs_posix() {
+static monotime getMonotonicUs_posix(void) {
/* clock_gettime() is specified in POSIX.1b (1993). Even so, some systems
* did not support this until much later. CLOCK_MONOTONIC is technically
* optional and may not be supported - but it appears to be universal.
@@ -140,7 +140,7 @@ static monotime getMonotonicUs_posix() {
return ((uint64_t)ts.tv_sec) * 1000000 + ts.tv_nsec / 1000;
}
-static void monotonicInit_posix() {
+static void monotonicInit_posix(void) {
/* Ensure that CLOCK_MONOTONIC is supported. This should be supported
* on any reasonably current OS. If the assertion below fails, provide
* an appropriate alternate implementation. */
@@ -155,7 +155,7 @@ static void monotonicInit_posix() {
-const char * monotonicInit() {
+const char * monotonicInit(void) {
#if defined(USE_PROCESSOR_CLOCK) && defined(__x86_64__) && defined(__linux__)
if (getMonotonicUs == NULL) monotonicInit_x86linux();
#endif
@@ -169,11 +169,11 @@ const char * monotonicInit() {
return monotonic_info_string;
}
-const char *monotonicInfoString() {
+const char *monotonicInfoString(void) {
return monotonic_info_string;
}
-monotonic_clock_type monotonicGetType() {
+monotonic_clock_type monotonicGetType(void) {
if (getMonotonicUs == getMonotonicUs_posix)
return MONOTONIC_CLOCK_POSIX;
return MONOTONIC_CLOCK_HW;
diff --git a/src/monotonic.h b/src/monotonic.h
index 32cf70638..b465f90b1 100644
--- a/src/monotonic.h
+++ b/src/monotonic.h
@@ -33,13 +33,13 @@ typedef enum monotonic_clock_type {
* needs to be called once, it may be called additional times without impact.
* Returns a printable string indicating the type of clock initialized.
* (The returned string is static and doesn't need to be freed.) */
-const char *monotonicInit();
+const char *monotonicInit(void);
/* Return a string indicating the type of monotonic clock being used. */
-const char *monotonicInfoString();
+const char *monotonicInfoString(void);
/* Return the type of monotonic clock being used. */
-monotonic_clock_type monotonicGetType();
+monotonic_clock_type monotonicGetType(void);
/* Functions to measure elapsed time. Example:
* monotime myTimer;
diff --git a/src/networking.c b/src/networking.c
index ea54697df..25d2b4c34 100644
--- a/src/networking.c
+++ b/src/networking.c
@@ -134,7 +134,7 @@ client *createClient(connection *conn) {
connSetReadHandler(conn, readQueryFromClient);
connSetPrivateData(conn, c);
}
- c->buf = zmalloc(PROTO_REPLY_CHUNK_BYTES);
+ c->buf = zmalloc_usable(PROTO_REPLY_CHUNK_BYTES, &c->buf_usable_size);
selectDb(c,0);
uint64_t client_id;
atomicGetIncr(server.next_client_id, client_id, 1);
@@ -150,7 +150,6 @@ client *createClient(connection *conn) {
c->lib_name = NULL;
c->lib_ver = NULL;
c->bufpos = 0;
- c->buf_usable_size = zmalloc_usable_size(c->buf);
c->buf_peak = c->buf_usable_size;
c->buf_peak_last_reset_time = server.unixtime;
c->ref_repl_buf_node = NULL;
@@ -621,9 +620,9 @@ void addReplyErrorSdsSafe(client *c, sds err) {
addReplyErrorSdsEx(c, err, 0);
}
-/* Internal function used by addReplyErrorFormat and addReplyErrorFormatEx.
+/* Internal function used by addReplyErrorFormat, addReplyErrorFormatEx and RM_ReplyWithErrorFormat.
* Refer to afterErrorReply for more information about the flags. */
-static void addReplyErrorFormatInternal(client *c, int flags, const char *fmt, va_list ap) {
+void addReplyErrorFormatInternal(client *c, int flags, const char *fmt, va_list ap) {
va_list cpy;
va_copy(cpy,ap);
sds s = sdscatvprintf(sdsempty(),fmt,cpy);
@@ -701,11 +700,12 @@ void trimReplyUnusedTailSpace(client *c) {
if (tail->size - tail->used > tail->size / 4 &&
tail->used < PROTO_REPLY_CHUNK_BYTES)
{
+ size_t usable_size;
size_t old_size = tail->size;
- tail = zrealloc(tail, tail->used + sizeof(clientReplyBlock));
+ tail = zrealloc_usable(tail, tail->used + sizeof(clientReplyBlock), &usable_size);
/* take over the allocation's internal fragmentation (at least for
* memory usage tracking) */
- tail->size = zmalloc_usable_size(tail) - sizeof(clientReplyBlock);
+ tail->size = usable_size - sizeof(clientReplyBlock);
c->reply_bytes = c->reply_bytes + tail->size - old_size;
listNodeValue(ln) = tail;
}
@@ -785,9 +785,10 @@ void setDeferredReply(client *c, void *node, const char *s, size_t length) {
listDelNode(c->reply,ln);
} else {
/* Create a new node */
- clientReplyBlock *buf = zmalloc(length + sizeof(clientReplyBlock));
+ size_t usable_size;
+ clientReplyBlock *buf = zmalloc_usable(length + sizeof(clientReplyBlock), &usable_size);
/* Take over the allocation's internal fragmentation */
- buf->size = zmalloc_usable_size(buf) - sizeof(clientReplyBlock);
+ buf->size = usable_size - sizeof(clientReplyBlock);
buf->used = length;
memcpy(buf->buf, s, length);
listNodeValue(ln) = buf;
@@ -872,6 +873,7 @@ void addReplyDouble(client *c, double d) {
const int dlen = d2string(dbuf+7,sizeof(dbuf)-7,d);
int digits = digits10(dlen);
int start = 4 - digits;
+ serverAssert(start >= 0);
dbuf[start] = '$';
/* Convert `dlen` to string, putting it's digits after '$' and before the
@@ -1583,6 +1585,8 @@ void freeClient(client *c) {
c->querybuf = NULL;
/* Deallocate structures used to block on blocking ops. */
+ /* If there is any in-flight command, we don't record their duration. */
+ c->duration = 0;
if (c->flags & CLIENT_BLOCKED) unblockClient(c, 1);
dictRelease(c->bstate.keys);
@@ -2039,8 +2043,10 @@ void resetClient(client *c) {
c->multibulklen = 0;
c->bulklen = -1;
c->slot = -1;
- c->duration = 0;
c->flags &= ~CLIENT_EXECUTING_COMMAND;
+
+ /* Make sure the duration has been recorded to some command. */
+ serverAssert(c->duration == 0);
#ifdef LOG_REQ_RES
reqresReset(c, 1);
#endif
@@ -2337,6 +2343,9 @@ int processMultibulkBuffer(client *c) {
/* Hint the sds library about the amount of bytes this string is
* going to contain. */
c->querybuf = sdsMakeRoomForNonGreedy(c->querybuf,ll+2-sdslen(c->querybuf));
+ /* We later set the peak to the used portion of the buffer, but here we over
+ * allocated because we know what we need, make sure it'll not be shrunk before used. */
+ if (c->querybuf_peak < (size_t)ll + 2) c->querybuf_peak = ll + 2;
}
}
c->bulklen = ll;
@@ -2631,6 +2640,9 @@ void readQueryFromClient(connection *conn) {
* the query buffer, we also don't wanna use the greedy growth, in order
* to avoid collision with the RESIZE_THRESHOLD mechanism. */
c->querybuf = sdsMakeRoomForNonGreedy(c->querybuf, readlen);
+ /* We later set the peak to the used portion of the buffer, but here we over
+ * allocated because we know what we need, make sure it'll not be shrunk before used. */
+ if (c->querybuf_peak < qblen + readlen) c->querybuf_peak = qblen + readlen;
} else {
c->querybuf = sdsMakeRoomFor(c->querybuf, readlen);
@@ -2997,6 +3009,10 @@ void clientCommand(client *c) {
" Control the replies sent to the current connection.",
"SETNAME <name>",
" Assign the name <name> to the current connection.",
+"SETINFO <option> <value>",
+" Set client meta attr. Options are:",
+" * LIB-NAME: the client lib name.",
+" * LIB-VER: the client lib version.",
"UNBLOCK <clientid> [TIMEOUT|ERROR]",
" Unblock the specified blocked client.",
"TRACKING (ON|OFF) [REDIRECT <id>] [BCAST] [PREFIX <prefix> [...]]",
@@ -3606,7 +3622,13 @@ void securityWarningCommand(client *c) {
time_t now = time(NULL);
if (llabs(now-logged_time) > 60) {
- serverLog(LL_WARNING,"Possible SECURITY ATTACK detected. It looks like somebody is sending POST or Host: commands to Redis. This is likely due to an attacker attempting to use Cross Protocol Scripting to compromise your Redis instance. Connection aborted.");
+ char ip[NET_IP_STR_LEN];
+ int port;
+ if (connAddrPeerName(c->conn, ip, sizeof(ip), &port) == -1) {
+ serverLog(LL_WARNING,"Possible SECURITY ATTACK detected. It looks like somebody is sending POST or Host: commands to Redis. This is likely due to an attacker attempting to use Cross Protocol Scripting to compromise your Redis instance. Connection aborted.");
+ } else {
+ serverLog(LL_WARNING,"Possible SECURITY ATTACK detected. It looks like somebody is sending POST or Host: commands to Redis. This is likely due to an attacker attempting to use Cross Protocol Scripting to compromise your Redis instance. Connection from %s:%d aborted.", ip, port);
+ }
logged_time = now;
}
freeClientAsync(c);
@@ -3959,7 +3981,7 @@ void updatePausedActions(void) {
/* Unblock all paused clients (ones that where blocked by BLOCKED_POSTPONE (possibly in processCommand).
* This means they'll get re-processed in beforeSleep, and may get paused again if needed. */
-void unblockPostponedClients() {
+void unblockPostponedClients(void) {
listNode *ln;
listIter li;
listRewind(server.postponed_clients, &li);
diff --git a/src/pubsub.c b/src/pubsub.c
index 2bbe40380..ed7350843 100644
--- a/src/pubsub.c
+++ b/src/pubsub.c
@@ -207,12 +207,12 @@ void addReplyPubsubPatUnsubscribed(client *c, robj *pattern) {
*----------------------------------------------------------------------------*/
/* Return the number of pubsub channels + patterns is handled. */
-int serverPubsubSubscriptionCount() {
+int serverPubsubSubscriptionCount(void) {
return dictSize(server.pubsub_channels) + dictSize(server.pubsub_patterns);
}
/* Return the number of pubsub shard level channels is handled. */
-int serverPubsubShardSubscriptionCount() {
+int serverPubsubShardSubscriptionCount(void) {
return dictSize(server.pubsubshard_channels);
}
diff --git a/src/rand.c b/src/rand.c
index 09b0508f1..e1e98e63b 100644
--- a/src/rand.c
+++ b/src/rand.c
@@ -68,7 +68,7 @@
static uint32_t x[3] = { X0, X1, X2 }, a[3] = { A0, A1, A2 }, c = C;
static void next(void);
-int32_t redisLrand48() {
+int32_t redisLrand48(void) {
next();
return (((int32_t)x[2] << (N - 1)) + (x[1] >> 1));
}
diff --git a/src/rand.h b/src/rand.h
index 1dce3e8b0..9884915a9 100644
--- a/src/rand.h
+++ b/src/rand.h
@@ -30,7 +30,7 @@
#ifndef REDIS_RANDOM_H
#define REDIS_RANDOM_H
-int32_t redisLrand48();
+int32_t redisLrand48(void);
void redisSrand48(int32_t seedval);
#define REDIS_LRAND48_MAX INT32_MAX
diff --git a/src/rdb.c b/src/rdb.c
index a2be21ccb..c52d8faf7 100644
--- a/src/rdb.c
+++ b/src/rdb.c
@@ -88,6 +88,11 @@ void rdbReportError(int corruption_error, int linenum, char *reason, ...) {
/* If we're loading an rdb file form disk, run rdb check (and exit) */
serverLog(LL_WARNING, "%s", msg);
char *argv[2] = {"",rdbFileBeingLoaded};
+ if (anetIsFifo(argv[1])) {
+ /* Cannot check RDB FIFO because we cannot reopen the FIFO and check already streamed data. */
+ rdbCheckError("Cannot check RDB that is a FIFO: %s", argv[1]);
+ return;
+ }
redis_check_rdb_main(2,argv,NULL);
} else if (corruption_error) {
/* In diskless loading, in case of corrupt file, log and exit. */
@@ -1437,31 +1442,29 @@ werr: /* Write error. */
return C_ERR;
}
-/* Save the DB on disk. Return C_ERR on error, C_OK on success. */
-int rdbSave(int req, char *filename, rdbSaveInfo *rsi, int rdbflags) {
- char tmpfile[256];
+static int rdbSaveInternal(int req, const char *filename, rdbSaveInfo *rsi, int rdbflags) {
char cwd[MAXPATHLEN]; /* Current working dir path for error messages. */
- FILE *fp = NULL;
rio rdb;
int error = 0;
+ int saved_errno;
char *err_op; /* For a detailed log */
- snprintf(tmpfile,256,"temp-%d.rdb", (int) getpid());
- fp = fopen(tmpfile,"w");
+ FILE *fp = fopen(filename,"w");
if (!fp) {
+ saved_errno = errno;
char *str_err = strerror(errno);
char *cwdp = getcwd(cwd,MAXPATHLEN);
serverLog(LL_WARNING,
"Failed opening the temp RDB file %s (in server root dir %s) "
"for saving: %s",
- tmpfile,
+ filename,
cwdp ? cwdp : "unknown",
str_err);
+ errno = saved_errno;
return C_ERR;
}
rioInitWithFile(&rdb,fp);
- startSaving(RDBFLAGS_NONE);
if (server.rdb_save_incremental_fsync) {
rioSetAutoSync(&rdb,REDIS_AUTOSYNC_BYTES);
@@ -1481,7 +1484,46 @@ int rdbSave(int req, char *filename, rdbSaveInfo *rsi, int rdbflags) {
serverLog(LL_NOTICE,"Unable to reclaim cache after saving RDB: %s", strerror(errno));
}
if (fclose(fp)) { fp = NULL; err_op = "fclose"; goto werr; }
- fp = NULL;
+
+ return C_OK;
+
+werr:
+ saved_errno = errno;
+ serverLog(LL_WARNING,"Write error while saving DB to the disk(%s): %s", err_op, strerror(errno));
+ if (fp) fclose(fp);
+ unlink(filename);
+ errno = saved_errno;
+ return C_ERR;
+}
+
+/* Save DB to the file. Similar to rdbSave() but this function won't use a
+ * temporary file and won't update the metrics. */
+int rdbSaveToFile(const char *filename) {
+ startSaving(RDBFLAGS_NONE);
+
+ if (rdbSaveInternal(SLAVE_REQ_NONE,filename,NULL,RDBFLAGS_NONE) != C_OK) {
+ int saved_errno = errno;
+ stopSaving(0);
+ errno = saved_errno;
+ return C_ERR;
+ }
+
+ stopSaving(1);
+ return C_OK;
+}
+
+/* Save the DB on disk. Return C_ERR on error, C_OK on success. */
+int rdbSave(int req, char *filename, rdbSaveInfo *rsi, int rdbflags) {
+ char tmpfile[256];
+ char cwd[MAXPATHLEN]; /* Current working dir path for error messages. */
+
+ startSaving(RDBFLAGS_NONE);
+ snprintf(tmpfile,256,"temp-%d.rdb", (int) getpid());
+
+ if (rdbSaveInternal(req,tmpfile,rsi,rdbflags) != C_OK) {
+ stopSaving(0);
+ return C_ERR;
+ }
/* Use RENAME to make sure the DB file is changed atomically only
* if the generate DB file is ok. */
@@ -1499,7 +1541,12 @@ int rdbSave(int req, char *filename, rdbSaveInfo *rsi, int rdbflags) {
stopSaving(0);
return C_ERR;
}
- if (fsyncFileDir(filename) == -1) { err_op = "fsyncFileDir"; goto werr; }
+ if (fsyncFileDir(filename) != 0) {
+ serverLog(LL_WARNING,
+ "Failed to fsync directory while saving DB: %s", strerror(errno));
+ stopSaving(0);
+ return C_ERR;
+ }
serverLog(LL_NOTICE,"DB saved on disk");
server.dirty = 0;
@@ -1507,13 +1554,6 @@ int rdbSave(int req, char *filename, rdbSaveInfo *rsi, int rdbflags) {
server.lastbgsave_status = C_OK;
stopSaving(1);
return C_OK;
-
-werr:
- serverLog(LL_WARNING,"Write error saving DB on disk(%s): %s", err_op, strerror(errno));
- if (fp) fclose(fp);
- unlink(tmpfile);
- stopSaving(0);
- return C_ERR;
}
int rdbSaveBackground(int req, char *filename, rdbSaveInfo *rsi, int rdbflags) {
@@ -3361,7 +3401,7 @@ int rdbLoad(char *filename, rdbSaveInfo *rsi, int rdbflags) {
/* Reclaim the cache backed by rdb */
if (retval == C_OK && !(rdbflags & RDBFLAGS_KEEP_CACHE)) {
/* TODO: maybe we could combine the fopen and open into one in the future */
- rdb_fd = open(server.rdb_filename, O_RDONLY);
+ rdb_fd = open(filename, O_RDONLY);
if (rdb_fd > 0) bioCreateCloseJob(rdb_fd, 0, 1);
}
return (retval==C_OK) ? RDB_OK : RDB_FAILED;
diff --git a/src/rdb.h b/src/rdb.h
index dc096fd8b..234bde221 100644
--- a/src/rdb.h
+++ b/src/rdb.h
@@ -157,6 +157,7 @@ int rdbLoad(char *filename, rdbSaveInfo *rsi, int rdbflags);
int rdbSaveBackground(int req, char *filename, rdbSaveInfo *rsi, int rdbflags);
int rdbSaveToSlavesSockets(int req, rdbSaveInfo *rsi);
void rdbRemoveTempFile(pid_t childpid, int from_signal);
+int rdbSaveToFile(const char *filename);
int rdbSave(int req, char *filename, rdbSaveInfo *rsi, int rdbflags);
ssize_t rdbSaveObject(rio *rdb, robj *o, robj *key, int dbid);
size_t rdbSavedObjectLen(robj *o, robj *key, int dbid);
diff --git a/src/redis-benchmark.c b/src/redis-benchmark.c
index 0d10e8d3f..2eb689e9f 100644
--- a/src/redis-benchmark.c
+++ b/src/redis-benchmark.c
@@ -192,7 +192,7 @@ static void writeHandler(aeEventLoop *el, int fd, void *privdata, int mask);
static void createMissingClients(client c);
static benchmarkThread *createBenchmarkThread(int index);
static void freeBenchmarkThread(benchmarkThread *thread);
-static void freeBenchmarkThreads();
+static void freeBenchmarkThreads(void);
static void *execBenchmarkThread(void *ptr);
static clusterNode *createClusterNode(char *ip, int port);
static redisConfig *getRedisConfig(const char *ip, int port,
@@ -201,7 +201,7 @@ static redisContext *getRedisContext(const char *ip, int port,
const char *hostsocket);
static void freeRedisConfig(redisConfig *cfg);
static int fetchClusterSlotsConfiguration(client c);
-static void updateClusterSlotsConfiguration();
+static void updateClusterSlotsConfiguration(void);
int showThroughput(struct aeEventLoop *eventLoop, long long id,
void *clientData);
@@ -958,7 +958,7 @@ static void showLatencyReport(void) {
}
}
-static void initBenchmarkThreads() {
+static void initBenchmarkThreads(void) {
int i;
if (config.threads) freeBenchmarkThreads();
config.threads = zmalloc(config.num_threads * sizeof(benchmarkThread*));
@@ -968,7 +968,7 @@ static void initBenchmarkThreads() {
}
}
-static void startBenchmarkThreads() {
+static void startBenchmarkThreads(void) {
int i;
for (i = 0; i < config.num_threads; i++) {
benchmarkThread *t = config.threads[i];
@@ -1035,7 +1035,7 @@ static void freeBenchmarkThread(benchmarkThread *thread) {
zfree(thread);
}
-static void freeBenchmarkThreads() {
+static void freeBenchmarkThreads(void) {
int i = 0;
for (; i < config.num_threads; i++) {
benchmarkThread *thread = config.threads[i];
@@ -1096,7 +1096,7 @@ static void freeClusterNode(clusterNode *node) {
zfree(node);
}
-static void freeClusterNodes() {
+static void freeClusterNodes(void) {
int i = 0;
for (; i < config.cluster_node_count; i++) {
clusterNode *n = config.cluster_nodes[i];
@@ -1118,7 +1118,7 @@ static clusterNode **addClusterNode(clusterNode *node) {
/* TODO: This should be refactored to use CLUSTER SLOTS, the migrating/importing
* information is anyway not used.
*/
-static int fetchClusterConfiguration() {
+static int fetchClusterConfiguration(void) {
int success = 1;
redisContext *ctx = NULL;
redisReply *reply = NULL;
@@ -1377,7 +1377,7 @@ cleanup:
}
/* Atomically update the new slots configuration. */
-static void updateClusterSlotsConfiguration() {
+static void updateClusterSlotsConfiguration(void) {
pthread_mutex_lock(&config.is_updating_slots_mutex);
atomicSet(config.is_updating_slots, 1);
int i;
@@ -1414,6 +1414,7 @@ int parseOptions(int argc, char **argv) {
int i;
int lastarg;
int exit_status = 1;
+ char *tls_usage;
for (i = 1; i < argc; i++) {
lastarg = (i == (argc-1));
@@ -1495,6 +1496,11 @@ int parseOptions(int argc, char **argv) {
fprintf(stderr,
"WARNING: -e option has no effect. "
"We now immediately exit on error to avoid false results.\n");
+ } else if (!strcmp(argv[i],"--seed")) {
+ if (lastarg) goto invalid;
+ int rand_seed = atoi(argv[++i]);
+ srandom(rand_seed);
+ init_genrand64(rand_seed);
} else if (!strcmp(argv[i],"-t")) {
if (lastarg) goto invalid;
/* We get the list of tests to run as a string in the form
@@ -1575,8 +1581,31 @@ invalid:
printf("Invalid option \"%s\" or option argument missing\n\n",argv[i]);
usage:
+ tls_usage =
+#ifdef USE_OPENSSL
+" --tls Establish a secure TLS connection.\n"
+" --sni <host> Server name indication for TLS.\n"
+" --cacert <file> CA Certificate file to verify with.\n"
+" --cacertdir <dir> Directory where trusted CA certificates are stored.\n"
+" If neither cacert nor cacertdir are specified, the default\n"
+" system-wide trusted root certs configuration will apply.\n"
+" --insecure Allow insecure TLS connection by skipping cert validation.\n"
+" --cert <file> Client certificate to authenticate with.\n"
+" --key <file> Private key file to authenticate with.\n"
+" --tls-ciphers <list> Sets the list of preferred ciphers (TLSv1.2 and below)\n"
+" in order of preference from highest to lowest separated by colon (\":\").\n"
+" See the ciphers(1ssl) manpage for more information about the syntax of this string.\n"
+#ifdef TLS1_3_VERSION
+" --tls-ciphersuites <list> Sets the list of preferred ciphersuites (TLSv1.3)\n"
+" in order of preference from highest to lowest separated by colon (\":\").\n"
+" See the ciphers(1ssl) manpage for more information about the syntax of this string,\n"
+" and specifically for TLSv1.3 ciphersuites.\n"
+#endif
+#endif
+"";
+
printf(
-"%s%s", /* Split to avoid strings longer than 4095 (-Woverlength-strings). */
+"%s%s%s", /* Split to avoid strings longer than 4095 (-Woverlength-strings). */
"Usage: redis-benchmark [OPTIONS] [COMMAND ARGS...]\n\n"
"Options:\n"
" -h <hostname> Server hostname (default 127.0.0.1)\n"
@@ -1618,28 +1647,10 @@ usage:
" on the command line.\n"
" -I Idle mode. Just open N idle connections and wait.\n"
" -x Read last argument from STDIN.\n"
-#ifdef USE_OPENSSL
-" --tls Establish a secure TLS connection.\n"
-" --sni <host> Server name indication for TLS.\n"
-" --cacert <file> CA Certificate file to verify with.\n"
-" --cacertdir <dir> Directory where trusted CA certificates are stored.\n"
-" If neither cacert nor cacertdir are specified, the default\n"
-" system-wide trusted root certs configuration will apply.\n"
-" --insecure Allow insecure TLS connection by skipping cert validation.\n"
-" --cert <file> Client certificate to authenticate with.\n"
-" --key <file> Private key file to authenticate with.\n"
-" --tls-ciphers <list> Sets the list of preferred ciphers (TLSv1.2 and below)\n"
-" in order of preference from highest to lowest separated by colon (\":\").\n"
-" See the ciphers(1ssl) manpage for more information about the syntax of this string.\n"
-#ifdef TLS1_3_VERSION
-" --tls-ciphersuites <list> Sets the list of preferred ciphersuites (TLSv1.3)\n"
-" in order of preference from highest to lowest separated by colon (\":\").\n"
-" See the ciphers(1ssl) manpage for more information about the syntax of this string,\n"
-" and specifically for TLSv1.3 ciphersuites.\n"
-#endif
-#endif
+" --seed <num> Set the seed for random number generator. Default seed is based on time.\n",
+tls_usage,
" --help Output this help and exit.\n"
-" --version Output version and exit.\n\n",
+" --version Output version and exit.\n\n"
"Examples:\n\n"
" Run the benchmark with the default configuration against 127.0.0.1:6379:\n"
" $ redis-benchmark\n\n"
diff --git a/src/redis-check-aof.c b/src/redis-check-aof.c
index 54d70ae50..616177a8b 100644
--- a/src/redis-check-aof.c
+++ b/src/redis-check-aof.c
@@ -242,7 +242,7 @@ int checkSingleAof(char *aof_filename, char *aof_filepath, int last_file, int fi
}
if (preamble) {
- char *argv[2] = {NULL, aof_filename};
+ char *argv[2] = {NULL, aof_filepath};
if (redis_check_rdb_main(2, argv, fp) == C_ERR) {
printf("RDB preamble of AOF file is not sane, aborting.\n");
exit(1);
diff --git a/src/redis-check-rdb.c b/src/redis-check-rdb.c
index a537c6dae..682135e55 100644
--- a/src/redis-check-rdb.c
+++ b/src/redis-check-rdb.c
@@ -188,7 +188,7 @@ void rdbCheckSetupSignals(void) {
/* Check the specified RDB file. Return 0 if the RDB looks sane, otherwise
* 1 is returned.
- * The file is specified as a filename in 'rdbfilename' if 'fp' is not NULL,
+ * The file is specified as a filename in 'rdbfilename' if 'fp' is NULL,
* otherwise the already open file 'fp' is checked. */
int redis_check_rdb(char *rdbfilename, FILE *fp) {
uint64_t dbid;
diff --git a/src/redis-cli.c b/src/redis-cli.c
index d8e6b966a..3f5827fbe 100644
--- a/src/redis-cli.c
+++ b/src/redis-cli.c
@@ -59,13 +59,14 @@
#include "adlist.h"
#include "zmalloc.h"
#include "linenoise.h"
-#include "help.h" /* Used for backwards-compatibility with pre-7.0 servers that don't support COMMAND DOCS. */
#include "anet.h"
#include "ae.h"
#include "connection.h"
#include "cli_common.h"
#include "mt19937-64.h"
+#include "cli_commands.h"
+
#define UNUSED(V) ((void) V)
#define OUTPUT_STANDARD 0
@@ -183,15 +184,6 @@ static int dictSdsKeyCompare(dict *d, const void *key1,
static void dictSdsDestructor(dict *d, void *val);
static void dictListDestructor(dict *d, void *val);
-/* Command documentation info used for help output */
-struct commandDocs {
- char *name;
- char *params; /* A string describing the syntax of the command arguments. */
- char *summary;
- char *group;
- char *since;
-};
-
/* Cluster Manager Command Info */
typedef struct clusterManagerCommand {
char *name;
@@ -247,6 +239,7 @@ static struct config {
int get_functions_rdb_mode;
int stat_mode;
int scan_mode;
+ int count;
int intrinsic_latency_mode;
int intrinsic_latency_duration;
sds pattern;
@@ -281,6 +274,9 @@ static struct config {
int current_resp3; /* 1 if we have RESP3 right now in the current connection. */
int in_multi;
int pre_multi_dbnum;
+ char *server_version;
+ char *test_hint;
+ char *test_hint_file;
} config;
/* User preferences. */
@@ -422,7 +418,7 @@ typedef struct {
sds full;
/* Only used for help on commands */
- struct commandDocs org;
+ struct commandDocs docs;
} helpEntry;
static helpEntry *helpEntries = NULL;
@@ -442,50 +438,13 @@ static sds cliVersion(void) {
return version;
}
-/* For backwards compatibility with pre-7.0 servers. Initializes command help. */
-static void cliOldInitHelp(void) {
- int commandslen = sizeof(commandHelp)/sizeof(struct commandHelp);
- int groupslen = sizeof(commandGroups)/sizeof(char*);
- int i, len, pos = 0;
- helpEntry tmp;
-
- helpEntriesLen = len = commandslen+groupslen;
- helpEntries = zmalloc(sizeof(helpEntry)*len);
-
- for (i = 0; i < groupslen; i++) {
- tmp.argc = 1;
- tmp.argv = zmalloc(sizeof(sds));
- tmp.argv[0] = sdscatprintf(sdsempty(),"@%s",commandGroups[i]);
- tmp.full = tmp.argv[0];
- tmp.type = CLI_HELP_GROUP;
- tmp.org.name = NULL;
- tmp.org.params = NULL;
- tmp.org.summary = NULL;
- tmp.org.since = NULL;
- tmp.org.group = NULL;
- helpEntries[pos++] = tmp;
- }
-
- for (i = 0; i < commandslen; i++) {
- tmp.argv = sdssplitargs(commandHelp[i].name,&tmp.argc);
- tmp.full = sdsnew(commandHelp[i].name);
- tmp.type = CLI_HELP_COMMAND;
- tmp.org.name = commandHelp[i].name;
- tmp.org.params = commandHelp[i].params;
- tmp.org.summary = commandHelp[i].summary;
- tmp.org.since = commandHelp[i].since;
- tmp.org.group = commandGroups[commandHelp[i].group];
- helpEntries[pos++] = tmp;
- }
-}
-
/* For backwards compatibility with pre-7.0 servers.
- * cliOldInitHelp() setups the helpEntries array with the command and group
- * names from the help.h file. However the Redis instance we are connecting
+ * cliLegacyInitHelp() sets up the helpEntries array with the command and group
+ * names from the commands.c file. However the Redis instance we are connecting
* to may support more commands, so this function integrates the previous
* entries with additional entries obtained using the COMMAND command
* available in recent versions of Redis. */
-static void cliOldIntegrateHelp(void) {
+static void cliLegacyIntegrateHelp(void) {
if (cliConnect(CC_QUIET) == REDIS_ERR) return;
redisReply *reply = redisCommand(context, "COMMAND");
@@ -520,75 +479,88 @@ static void cliOldIntegrateHelp(void) {
new->type = CLI_HELP_COMMAND;
sdstoupper(new->argv[0]);
- new->org.name = new->argv[0];
- new->org.params = sdsempty();
+ new->docs.name = new->argv[0];
+ new->docs.args = NULL;
+ new->docs.numargs = 0;
+ new->docs.params = sdsempty();
int args = llabs(entry->element[1]->integer);
args--; /* Remove the command name itself. */
if (entry->element[3]->integer == 1) {
- new->org.params = sdscat(new->org.params,"key ");
+ new->docs.params = sdscat(new->docs.params,"key ");
args--;
}
- while(args-- > 0) new->org.params = sdscat(new->org.params,"arg ");
+ while(args-- > 0) new->docs.params = sdscat(new->docs.params,"arg ");
if (entry->element[1]->integer < 0)
- new->org.params = sdscat(new->org.params,"...options...");
- new->org.summary = "Help not available";
- new->org.since = "Not known";
- new->org.group = commandGroups[0];
+ new->docs.params = sdscat(new->docs.params,"...options...");
+ new->docs.summary = "Help not available";
+ new->docs.since = "Not known";
+ new->docs.group = "generic";
}
freeReplyObject(reply);
}
/* Concatenate a string to an sds string, but if it's empty substitute double quote marks. */
-static sds sdscat_orempty(sds params, char *value) {
+static sds sdscat_orempty(sds params, const char *value) {
if (value[0] == '\0') {
return sdscat(params, "\"\"");
}
return sdscat(params, value);
}
-static sds cliAddArgument(sds params, redisReply *argMap);
+static sds makeHint(char **inputargv, int inputargc, int cmdlen, struct commandDocs docs);
-/* Concatenate a list of arguments to the parameter string, separated by a separator string. */
-static sds cliConcatArguments(sds params, redisReply *arguments, char *separator) {
+static void cliAddCommandDocArg(cliCommandArg *cmdArg, redisReply *argMap);
+
+static void cliMakeCommandDocArgs(redisReply *arguments, cliCommandArg *result) {
for (size_t j = 0; j < arguments->elements; j++) {
- params = cliAddArgument(params, arguments->element[j]);
- if (j != arguments->elements - 1) {
- params = sdscat(params, separator);
- }
+ cliAddCommandDocArg(&result[j], arguments->element[j]);
}
- return params;
}
-/* Add an argument to the parameter string. */
-static sds cliAddArgument(sds params, redisReply *argMap) {
- char *name = NULL;
- char *type = NULL;
- int optional = 0;
- int multiple = 0;
- int multipleToken = 0;
- redisReply *arguments = NULL;
- sds tokenPart = sdsempty();
- sds repeatPart = sdsempty();
-
- /* First read the fields describing the argument. */
+static void cliAddCommandDocArg(cliCommandArg *cmdArg, redisReply *argMap) {
if (argMap->type != REDIS_REPLY_MAP && argMap->type != REDIS_REPLY_ARRAY) {
- return params;
+ return;
}
+
for (size_t i = 0; i < argMap->elements; i += 2) {
assert(argMap->element[i]->type == REDIS_REPLY_STRING);
char *key = argMap->element[i]->str;
if (!strcmp(key, "name")) {
assert(argMap->element[i + 1]->type == REDIS_REPLY_STRING);
- name = argMap->element[i + 1]->str;
+ cmdArg->name = sdsnew(argMap->element[i + 1]->str);
+ } else if (!strcmp(key, "display_text")) {
+ assert(argMap->element[i + 1]->type == REDIS_REPLY_STRING);
+ cmdArg->display_text = sdsnew(argMap->element[i + 1]->str);
} else if (!strcmp(key, "token")) {
assert(argMap->element[i + 1]->type == REDIS_REPLY_STRING);
- char *token = argMap->element[i + 1]->str;
- tokenPart = sdscat_orempty(tokenPart, token);
+ cmdArg->token = sdsnew(argMap->element[i + 1]->str);
} else if (!strcmp(key, "type")) {
assert(argMap->element[i + 1]->type == REDIS_REPLY_STRING);
- type = argMap->element[i + 1]->str;
+ char *type = argMap->element[i + 1]->str;
+ if (!strcmp(type, "string")) {
+ cmdArg->type = ARG_TYPE_STRING;
+ } else if (!strcmp(type, "integer")) {
+ cmdArg->type = ARG_TYPE_INTEGER;
+ } else if (!strcmp(type, "double")) {
+ cmdArg->type = ARG_TYPE_DOUBLE;
+ } else if (!strcmp(type, "key")) {
+ cmdArg->type = ARG_TYPE_KEY;
+ } else if (!strcmp(type, "pattern")) {
+ cmdArg->type = ARG_TYPE_PATTERN;
+ } else if (!strcmp(type, "unix-time")) {
+ cmdArg->type = ARG_TYPE_UNIX_TIME;
+ } else if (!strcmp(type, "pure-token")) {
+ cmdArg->type = ARG_TYPE_PURE_TOKEN;
+ } else if (!strcmp(type, "oneof")) {
+ cmdArg->type = ARG_TYPE_ONEOF;
+ } else if (!strcmp(type, "block")) {
+ cmdArg->type = ARG_TYPE_BLOCK;
+ }
} else if (!strcmp(key, "arguments")) {
- arguments = argMap->element[i + 1];
+ redisReply *arguments = argMap->element[i + 1];
+ cmdArg->subargs = zcalloc(arguments->elements * sizeof(cliCommandArg));
+ cmdArg->numsubargs = arguments->elements;
+ cliMakeCommandDocArgs(arguments, cmdArg->subargs);
} else if (!strcmp(key, "flags")) {
redisReply *flags = argMap->element[i + 1];
assert(flags->type == REDIS_REPLY_SET || flags->type == REDIS_REPLY_ARRAY);
@@ -596,57 +568,15 @@ static sds cliAddArgument(sds params, redisReply *argMap) {
assert(flags->element[j]->type == REDIS_REPLY_STATUS);
char *flag = flags->element[j]->str;
if (!strcmp(flag, "optional")) {
- optional = 1;
+ cmdArg->flags |= CMD_ARG_OPTIONAL;
} else if (!strcmp(flag, "multiple")) {
- multiple = 1;
+ cmdArg->flags |= CMD_ARG_MULTIPLE;
} else if (!strcmp(flag, "multiple_token")) {
- multipleToken = 1;
+ cmdArg->flags |= CMD_ARG_MULTIPLE_TOKEN;
}
}
}
}
-
- /* Then build the "repeating part" of the argument string. */
- if (!strcmp(type, "key") ||
- !strcmp(type, "string") ||
- !strcmp(type, "integer") ||
- !strcmp(type, "double") ||
- !strcmp(type, "pattern") ||
- !strcmp(type, "unix-time") ||
- !strcmp(type, "token"))
- {
- repeatPart = sdscat_orempty(repeatPart, name);
- } else if (!strcmp(type, "oneof")) {
- repeatPart = cliConcatArguments(repeatPart, arguments, "|");
- } else if (!strcmp(type, "block")) {
- repeatPart = cliConcatArguments(repeatPart, arguments, " ");
- } else if (strcmp(type, "pure-token") != 0) {
- fprintf(stderr, "Unknown type '%s' set for argument '%s'\n", type, name);
- }
-
- /* Finally, build the parameter string. */
- if (tokenPart[0] != '\0' && strcmp(type, "pure-token") != 0) {
- tokenPart = sdscat(tokenPart, " ");
- }
- if (optional) {
- params = sdscat(params, "[");
- }
- params = sdscat(params, tokenPart);
- params = sdscat(params, repeatPart);
- if (multiple) {
- params = sdscat(params, " [");
- if (multipleToken) {
- params = sdscat(params, tokenPart);
- }
- params = sdscat(params, repeatPart);
- params = sdscat(params, " ...]");
- }
- if (optional) {
- params = sdscat(params, "]");
- }
- sdsfree(tokenPart);
- sdsfree(repeatPart);
- return params;
}
/* Fill in the fields of a help entry for the command/subcommand name. */
@@ -656,8 +586,13 @@ static void cliFillInCommandHelpEntry(helpEntry *help, char *cmdname, char *subc
help->argv[0] = sdsnew(cmdname);
sdstoupper(help->argv[0]);
if (subcommandname) {
- /* Subcommand name is two words separated by a pipe character. */
- help->argv[1] = sdsnew(strchr(subcommandname, '|') + 1);
+ /* Subcommand name may be two words separated by a pipe character. */
+ char *pipe = strchr(subcommandname, '|');
+ if (pipe != NULL) {
+ help->argv[1] = sdsnew(pipe + 1);
+ } else {
+ help->argv[1] = sdsnew(subcommandname);
+ }
sdstoupper(help->argv[1]);
}
sds fullname = sdsnew(help->argv[0]);
@@ -668,9 +603,11 @@ static void cliFillInCommandHelpEntry(helpEntry *help, char *cmdname, char *subc
help->full = fullname;
help->type = CLI_HELP_COMMAND;
- help->org.name = help->full;
- help->org.params = sdsempty();
- help->org.since = NULL;
+ help->docs.name = help->full;
+ help->docs.params = NULL;
+ help->docs.args = NULL;
+ help->docs.numargs = 0;
+ help->docs.since = NULL;
}
/* Initialize a command help entry for the command/subcommand described in 'specs'.
@@ -692,23 +629,26 @@ static helpEntry *cliInitCommandHelpEntry(char *cmdname, char *subcommandname,
if (!strcmp(key, "summary")) {
redisReply *reply = specs->element[j + 1];
assert(reply->type == REDIS_REPLY_STRING);
- help->org.summary = sdsnew(reply->str);
+ help->docs.summary = sdsnew(reply->str);
} else if (!strcmp(key, "since")) {
redisReply *reply = specs->element[j + 1];
assert(reply->type == REDIS_REPLY_STRING);
- help->org.since = sdsnew(reply->str);
+ help->docs.since = sdsnew(reply->str);
} else if (!strcmp(key, "group")) {
redisReply *reply = specs->element[j + 1];
assert(reply->type == REDIS_REPLY_STRING);
- help->org.group = sdsnew(reply->str);
- sds group = sdsdup(help->org.group);
+ help->docs.group = sdsnew(reply->str);
+ sds group = sdsdup(help->docs.group);
if (dictAdd(groups, group, NULL) != DICT_OK) {
sdsfree(group);
}
} else if (!strcmp(key, "arguments")) {
- redisReply *args = specs->element[j + 1];
- assert(args->type == REDIS_REPLY_ARRAY);
- help->org.params = cliConcatArguments(help->org.params, args, " ");
+ redisReply *arguments = specs->element[j + 1];
+ assert(arguments->type == REDIS_REPLY_ARRAY);
+ help->docs.args = zcalloc(arguments->elements * sizeof(cliCommandArg));
+ help->docs.numargs = arguments->elements;
+ cliMakeCommandDocArgs(arguments, help->docs.args);
+ help->docs.params = makeHint(NULL, 0, 0, help->docs);
} else if (!strcmp(key, "subcommands")) {
redisReply *subcommands = specs->element[j + 1];
assert(subcommands->type == REDIS_REPLY_MAP || subcommands->type == REDIS_REPLY_ARRAY);
@@ -774,11 +714,13 @@ void cliInitGroupHelpEntries(dict *groups) {
tmp.argv[0] = sdscatprintf(sdsempty(),"@%s",(char *)dictGetKey(entry));
tmp.full = tmp.argv[0];
tmp.type = CLI_HELP_GROUP;
- tmp.org.name = NULL;
- tmp.org.params = NULL;
- tmp.org.summary = NULL;
- tmp.org.since = NULL;
- tmp.org.group = NULL;
+ tmp.docs.name = NULL;
+ tmp.docs.params = NULL;
+ tmp.docs.args = NULL;
+ tmp.docs.numargs = 0;
+ tmp.docs.summary = NULL;
+ tmp.docs.since = NULL;
+ tmp.docs.group = NULL;
helpEntries[pos++] = tmp;
}
dictReleaseIterator(iter);
@@ -798,6 +740,164 @@ void cliInitCommandHelpEntries(redisReply *commandTable, dict *groups) {
}
}
+/* Does the server version support a command/argument only available "since" some version?
+ * Returns 1 when supported, or 0 when the "since" version is newer than "version". */
+static int versionIsSupported(sds version, sds since) {
+ int i;
+ char *versionPos = version;
+ char *sincePos = since;
+ if (!since) {
+ return 1;
+ }
+
+ for (i = 0; i != 3; i++) {
+ int versionPart = atoi(versionPos);
+ int sincePart = atoi(sincePos);
+ if (versionPart > sincePart) {
+ return 1;
+ } else if (sincePart > versionPart) {
+ return 0;
+ }
+ versionPos = strchr(versionPos, '.');
+ sincePos = strchr(sincePos, '.');
+ if (!versionPos || !sincePos)
+ return 0;
+ versionPos++;
+ sincePos++;
+ }
+ return 0;
+}
+
+static void removeUnsupportedArgs(struct cliCommandArg *args, int *numargs, sds version) {
+ int i = 0, j;
+ while (i != *numargs) {
+ if (versionIsSupported(version, args[i].since)) {
+ if (args[i].subargs) {
+ removeUnsupportedArgs(args[i].subargs, &args[i].numsubargs, version);
+ }
+ i++;
+ continue;
+ }
+ for (j = i; j != *numargs; j++) {
+ args[j] = args[j + 1];
+ }
+ (*numargs)--;
+ }
+}
+
+static helpEntry *cliLegacyInitCommandHelpEntry(char *cmdname, char *subcommandname,
+ helpEntry *next, struct commandDocs *command,
+ dict *groups, sds version) {
+ helpEntry *help = next++;
+ cliFillInCommandHelpEntry(help, cmdname, subcommandname);
+
+ help->docs.summary = sdsnew(command->summary);
+ help->docs.since = sdsnew(command->since);
+ help->docs.group = sdsnew(command->group);
+ sds group = sdsdup(help->docs.group);
+ if (dictAdd(groups, group, NULL) != DICT_OK) {
+ sdsfree(group);
+ }
+
+ if (command->args != NULL) {
+ help->docs.args = command->args;
+ help->docs.numargs = command->numargs;
+ if (version)
+ removeUnsupportedArgs(help->docs.args, &help->docs.numargs, version);
+ help->docs.params = makeHint(NULL, 0, 0, help->docs);
+ }
+
+ if (command->subcommands != NULL) {
+ for (size_t i = 0; command->subcommands[i].name != NULL; i++) {
+ if (!version || versionIsSupported(version, command->subcommands[i].since)) {
+ char *subcommandname = command->subcommands[i].name;
+ next = cliLegacyInitCommandHelpEntry(
+ cmdname, subcommandname, next, &command->subcommands[i], groups, version);
+ }
+ }
+ }
+ return next;
+}
+
+int cliLegacyInitCommandHelpEntries(struct commandDocs *commands, dict *groups, sds version) {
+ helpEntry *next = helpEntries;
+ for (size_t i = 0; commands[i].name != NULL; i++) {
+ if (!version || versionIsSupported(version, commands[i].since)) {
+ next = cliLegacyInitCommandHelpEntry(commands[i].name, NULL, next, &commands[i], groups, version);
+ }
+ }
+ return next - helpEntries;
+}
+
+/* Returns the total number of commands and subcommands in the command docs table,
+ * filtered by server version (if provided).
+ */
+static size_t cliLegacyCountCommands(struct commandDocs *commands, sds version) {
+ int numCommands = 0;
+ for (size_t i = 0; commands[i].name != NULL; i++) {
+ if (version && !versionIsSupported(version, commands[i].since)) {
+ continue;
+ }
+ numCommands++;
+ if (commands[i].subcommands != NULL) {
+ numCommands += cliLegacyCountCommands(commands[i].subcommands, version);
+ }
+ }
+ return numCommands;
+}
+
+/* Gets the server version string by calling INFO SERVER.
+ * Stores the result in config.server_version.
+ * When not connected, or not possible, returns NULL. */
+static sds cliGetServerVersion(void) {
+ static const char *key = "\nredis_version:";
+ redisReply *serverInfo = NULL;
+ char *pos;
+
+ if (config.server_version != NULL) {
+ return config.server_version;
+ }
+
+ if (!context) return NULL;
+ serverInfo = redisCommand(context, "INFO SERVER");
+ if (serverInfo == NULL || serverInfo->type == REDIS_REPLY_ERROR) {
+ freeReplyObject(serverInfo);
+ return sdsempty();
+ }
+
+ assert(serverInfo->type == REDIS_REPLY_STRING || serverInfo->type == REDIS_REPLY_VERB);
+ sds info = serverInfo->str;
+
+ /* Finds the first appearance of "redis_version" in the INFO SERVER reply. */
+ pos = strstr(info, key);
+ if (pos) {
+ pos += strlen(key);
+ char *end = strchr(pos, '\r');
+ if (end) {
+ sds version = sdsnewlen(pos, end - pos);
+ freeReplyObject(serverInfo);
+ config.server_version = version;
+ return version;
+ }
+ }
+ freeReplyObject(serverInfo);
+ return NULL;
+}
+
+static void cliLegacyInitHelp(dict *groups) {
+ sds serverVersion = cliGetServerVersion();
+
+ /* Scan the commandDocs array and fill in the entries */
+ helpEntriesLen = cliLegacyCountCommands(redisCommandTable, serverVersion);
+ helpEntries = zmalloc(sizeof(helpEntry)*helpEntriesLen);
+
+ helpEntriesLen = cliLegacyInitCommandHelpEntries(redisCommandTable, groups, serverVersion);
+ cliInitGroupHelpEntries(groups);
+
+ qsort(helpEntries, helpEntriesLen, sizeof(helpEntry), helpEntryCompare);
+ dictRelease(groups);
+}
+
/* cliInitHelp() sets up the helpEntries array with the command and group
* names and command descriptions obtained using the COMMAND DOCS command.
*/
@@ -817,16 +917,20 @@ static void cliInitHelp(void) {
if (cliConnect(CC_QUIET) == REDIS_ERR) {
/* Can not connect to the server, but we still want to provide
- * help, generate it only from the old help.h data instead. */
- cliOldInitHelp();
+ * help, generate it only from the static cli_commands.c data instead. */
+ groups = dictCreate(&groupsdt);
+ cliLegacyInitHelp(groups);
return;
}
commandTable = redisCommand(context, "COMMAND DOCS");
if (commandTable == NULL || commandTable->type == REDIS_REPLY_ERROR) {
- /* New COMMAND DOCS subcommand not supported - generate help from old help.h data instead. */
+ /* New COMMAND DOCS subcommand not supported - generate help from
+ * static cli_commands.c data instead. */
freeReplyObject(commandTable);
- cliOldInitHelp();
- cliOldIntegrateHelp();
+
+ groups = dictCreate(&groupsdt);
+ cliLegacyInitHelp(groups);
+ cliLegacyIntegrateHelp();
return;
};
if (commandTable->type != REDIS_REPLY_MAP && commandTable->type != REDIS_REPLY_ARRAY) return;
@@ -901,7 +1005,7 @@ static void cliOutputHelp(int argc, char **argv) {
entry = &helpEntries[i];
if (entry->type != CLI_HELP_COMMAND) continue;
- help = &entry->org;
+ help = &entry->docs;
if (group == NULL) {
/* Compare all arguments */
if (argc <= entry->argc) {
@@ -948,36 +1052,429 @@ static void completionCallback(const char *buf, linenoiseCompletions *lc) {
}
}
-/* Linenoise hints callback. */
-static char *hintsCallback(const char *buf, int *color, int *bold) {
- if (!pref.hints) return NULL;
+static sds addHintForArgument(sds hint, cliCommandArg *arg);
- int i, rawargc, argc, buflen = strlen(buf), matchlen = 0, shift = 0;
- sds *rawargv, *argv = sdssplitargs(buf,&argc);
- int endspace = buflen && isspace(buf[buflen-1]);
- helpEntry *entry = NULL;
+/* Adds a separator character between words of a string under construction.
+ * A separator is added if the string length is greater than its previously-recorded
+ * length (*len), which is then updated, and it's not the last word to be added.
+ */
+static sds addSeparator(sds str, size_t *len, char *separator, int is_last) {
+ if (sdslen(str) > *len && !is_last) {
+ str = sdscat(str, separator);
+ *len = sdslen(str);
+ }
+ return str;
+}
- /* Check if the argument list is empty and return ASAP. */
- if (argc == 0) {
- sdsfreesplitres(argv,argc);
- return NULL;
+/* Recursively zeros the matched* fields of all arguments. */
+static void clearMatchedArgs(cliCommandArg *args, int numargs) {
+ for (int i = 0; i != numargs; ++i) {
+ args[i].matched = 0;
+ args[i].matched_token = 0;
+ args[i].matched_name = 0;
+ args[i].matched_all = 0;
+ if (args[i].subargs) {
+ clearMatchedArgs(args[i].subargs, args[i].numsubargs);
+ }
}
+}
- if (argc > 3 && (!strcasecmp(argv[0], "acl") && !strcasecmp(argv[1], "dryrun"))) {
- shift = 3;
- } else if (argc > 2 && (!strcasecmp(argv[0], "command") &&
- (!strcasecmp(argv[1], "getkeys") || !strcasecmp(argv[1], "getkeysandflags"))))
- {
- shift = 2;
+/* Builds a completion hint string describing the arguments, skipping parts already matched.
+ * Hints for all arguments are added to the input 'hint' parameter, separated by 'separator'.
+ */
+static sds addHintForArguments(sds hint, cliCommandArg *args, int numargs, char *separator) {
+ int i, j, incomplete;
+ size_t len=sdslen(hint);
+ for (i = 0; i < numargs; i++) {
+ if (!(args[i].flags & CMD_ARG_OPTIONAL)) {
+ hint = addHintForArgument(hint, &args[i]);
+ hint = addSeparator(hint, &len, separator, i == numargs-1);
+ continue;
+ }
+
+ /* The rule is that successive "optional" arguments can appear in any order.
+ * But if they are followed by a required argument, no more of those optional arguments
+ * can appear after that.
+ *
+ * This code handles all successive optional args together. This lets us show the
+ * completion of the currently-incomplete optional arg first, if there is one.
+ */
+ for (j = i, incomplete = -1; j < numargs; j++) {
+ if (!(args[j].flags & CMD_ARG_OPTIONAL)) break;
+ if (args[j].matched != 0 && args[j].matched_all == 0) {
+ /* User has started typing this arg; show its completion first. */
+ hint = addHintForArgument(hint, &args[j]);
+ hint = addSeparator(hint, &len, separator, i == numargs-1);
+ incomplete = j;
+ }
+ }
+
+ /* If the following non-optional arg has not been matched, add hints for
+ * any remaining optional args in this group.
+ */
+ if (j == numargs || args[j].matched == 0) {
+ for (; i < j; i++) {
+ if (incomplete != i) {
+ hint = addHintForArgument(hint, &args[i]);
+ hint = addSeparator(hint, &len, separator, i == numargs-1);
+ }
+ }
+ }
+
+ i = j - 1;
+ }
+ return hint;
+}
+
+/* Adds the "repeating" section of the hint string for a multiple-typed argument: [ABC def ...]
+ * The repeating part is a fixed unit; we don't filter matched elements from it.
+ */
+static sds addHintForRepeatedArgument(sds hint, cliCommandArg *arg) {
+ if (!(arg->flags & CMD_ARG_MULTIPLE)) {
+ return hint;
+ }
+
+ /* The repeating part is always shown at the end of the argument's hint,
+ * so we can safely clear its matched flags before printing it.
+ */
+ clearMatchedArgs(arg, 1);
+
+ if (hint[0] != '\0') {
+ hint = sdscat(hint, " ");
+ }
+ hint = sdscat(hint, "[");
+
+ if (arg->flags & CMD_ARG_MULTIPLE_TOKEN) {
+ hint = sdscat_orempty(hint, arg->token);
+ if (arg->type != ARG_TYPE_PURE_TOKEN) {
+ hint = sdscat(hint, " ");
+ }
+ }
+
+ switch (arg->type) {
+ case ARG_TYPE_ONEOF:
+ hint = addHintForArguments(hint, arg->subargs, arg->numsubargs, "|");
+ break;
+
+ case ARG_TYPE_BLOCK:
+ hint = addHintForArguments(hint, arg->subargs, arg->numsubargs, " ");
+ break;
+
+ case ARG_TYPE_PURE_TOKEN:
+ break;
+
+ default:
+ hint = sdscat_orempty(hint, arg->display_text ? arg->display_text : arg->name);
+ break;
+ }
+
+ hint = sdscat(hint, " ...]");
+ return hint;
+}
+
+/* Adds hint string for one argument, if not already matched. */
+static sds addHintForArgument(sds hint, cliCommandArg *arg) {
+ if (arg->matched_all) {
+ return hint;
+ }
+
+ /* Surround an optional arg with brackets, unless it's partially matched. */
+ if ((arg->flags & CMD_ARG_OPTIONAL) && !arg->matched) {
+ hint = sdscat(hint, "[");
+ }
+
+ /* Start with the token, if present and not matched. */
+ if (arg->token != NULL && !arg->matched_token) {
+ hint = sdscat_orempty(hint, arg->token);
+ if (arg->type != ARG_TYPE_PURE_TOKEN) {
+ hint = sdscat(hint, " ");
+ }
+ }
+
+ /* Add the body of the syntax string. */
+ switch (arg->type) {
+ case ARG_TYPE_ONEOF:
+ if (arg->matched == 0) {
+ hint = addHintForArguments(hint, arg->subargs, arg->numsubargs, "|");
+ } else {
+ int i;
+ for (i = 0; i < arg->numsubargs; i++) {
+ if (arg->subargs[i].matched != 0) {
+ hint = addHintForArgument(hint, &arg->subargs[i]);
+ }
+ }
+ }
+ break;
+
+ case ARG_TYPE_BLOCK:
+ hint = addHintForArguments(hint, arg->subargs, arg->numsubargs, " ");
+ break;
+
+ case ARG_TYPE_PURE_TOKEN:
+ break;
+
+ default:
+ if (!arg->matched_name) {
+ hint = sdscat_orempty(hint, arg->display_text ? arg->display_text : arg->name);
+ }
+ break;
+ }
+
+ hint = addHintForRepeatedArgument(hint, arg);
+
+ if ((arg->flags & CMD_ARG_OPTIONAL) && !arg->matched) {
+ hint = sdscat(hint, "]");
+ }
+
+ return hint;
+}
+
+static int matchArg(char **nextword, int numwords, cliCommandArg *arg);
+static int matchArgs(char **words, int numwords, cliCommandArg *args, int numargs);
+
+/* Tries to match the next words of the input against an argument. */
+static int matchNoTokenArg(char **nextword, int numwords, cliCommandArg *arg) {
+ int i;
+ switch (arg->type) {
+ case ARG_TYPE_BLOCK: {
+ arg->matched += matchArgs(nextword, numwords, arg->subargs, arg->numsubargs);
+
+ /* All the subargs must be matched for the block to match. */
+ arg->matched_all = 1;
+ for (i = 0; i < arg->numsubargs; i++) {
+ if (arg->subargs[i].matched_all == 0) {
+ arg->matched_all = 0;
+ }
+ }
+ break;
+ }
+ case ARG_TYPE_ONEOF: {
+ for (i = 0; i < arg->numsubargs; i++) {
+ if (matchArg(nextword, numwords, &arg->subargs[i])) {
+ arg->matched += arg->subargs[i].matched;
+ arg->matched_all = arg->subargs[i].matched_all;
+ break;
+ }
+ }
+ break;
+ }
+
+ case ARG_TYPE_INTEGER:
+ case ARG_TYPE_UNIX_TIME: {
+ long long value;
+ if (sscanf(*nextword, "%lld", &value)) {
+ arg->matched += 1;
+ arg->matched_name = 1;
+ arg->matched_all = 1;
+ } else {
+ /* Matching failed due to incorrect arg type. */
+ arg->matched = 0;
+ arg->matched_name = 0;
+ }
+ break;
+ }
+
+ case ARG_TYPE_DOUBLE: {
+ double value;
+ if (sscanf(*nextword, "%lf", &value)) {
+ arg->matched += 1;
+ arg->matched_name = 1;
+ arg->matched_all = 1;
+ } else {
+ /* Matching failed due to incorrect arg type. */
+ arg->matched = 0;
+ arg->matched_name = 0;
+ }
+ break;
+ }
+
+ default:
+ arg->matched += 1;
+ arg->matched_name = 1;
+ arg->matched_all = 1;
+ break;
}
- argc -= shift;
- argv += shift;
+ return arg->matched;
+}
+
+/* Tries to match the next word of the input against a token literal. */
+static int matchToken(char **nextword, cliCommandArg *arg) {
+ if (strcasecmp(arg->token, nextword[0]) != 0) {
+ return 0;
+ }
+ arg->matched_token = 1;
+ arg->matched = 1;
+ return 1;
+}
+
+/* Tries to match the next words of the input against the next argument.
+ * If the arg is repeated ("multiple"), it will be matched only once.
+ * If the next input word(s) can't be matched, returns 0 for failure.
+ */
+static int matchArgOnce(char **nextword, int numwords, cliCommandArg *arg) {
+ /* First match the token, if present. */
+ if (arg->token != NULL) {
+ if (!matchToken(nextword, arg)) {
+ return 0;
+ }
+ if (arg->type == ARG_TYPE_PURE_TOKEN) {
+ arg->matched_all = 1;
+ return 1;
+ }
+ if (numwords == 1) {
+ return 1;
+ }
+ nextword++;
+ numwords--;
+ }
+
+ /* Then match the rest of the argument. */
+ if (!matchNoTokenArg(nextword, numwords, arg)) {
+ return 0;
+ }
+ return arg->matched;
+}
+
+/* Tries to match the next words of the input against the next argument.
+ * If the arg is repeated ("multiple"), it will be matched as many times as possible.
+ */
+static int matchArg(char **nextword, int numwords, cliCommandArg *arg) {
+ int matchedWords = 0;
+ int matchedOnce = matchArgOnce(nextword, numwords, arg);
+ if (!(arg->flags & CMD_ARG_MULTIPLE)) {
+ return matchedOnce;
+ }
+
+ /* Found one match; now match a "multiple" argument as many times as possible. */
+ matchedWords += matchedOnce;
+ while (arg->matched_all && matchedWords < numwords) {
+ clearMatchedArgs(arg, 1);
+ if (arg->token != NULL && !(arg->flags & CMD_ARG_MULTIPLE_TOKEN)) {
+ /* The token only appears the first time; the rest of the times,
+ * pretend we saw it so we don't hint it.
+ */
+ matchedOnce = matchNoTokenArg(nextword + matchedWords, numwords - matchedWords, arg);
+ if (arg->matched) {
+ arg->matched_token = 1;
+ }
+ } else {
+ matchedOnce = matchArgOnce(nextword + matchedWords, numwords - matchedWords, arg);
+ }
+ matchedWords += matchedOnce;
+ }
+ arg->matched_all = 0; /* Because more repetitions are still possible. */
+ return matchedWords;
+}
+
+/* Tries to match the next words of the input against
+ * any one of a consecutive set of optional arguments.
+ */
+static int matchOneOptionalArg(char **words, int numwords, cliCommandArg *args, int numargs, int *matchedarg) {
+ for (int nextword = 0, nextarg = 0; nextword != numwords && nextarg != numargs; ++nextarg) {
+ if (args[nextarg].matched) {
+ /* Already matched this arg. */
+ continue;
+ }
+
+ int matchedWords = matchArg(&words[nextword], numwords - nextword, &args[nextarg]);
+ if (matchedWords != 0) {
+ *matchedarg = nextarg;
+ return matchedWords;
+ }
+ }
+ return 0;
+}
+
+/* Matches as many input words as possible against a set of consecutive optional arguments. */
+static int matchOptionalArgs(char **words, int numwords, cliCommandArg *args, int numargs) {
+ int nextword = 0;
+ int matchedarg = -1, lastmatchedarg = -1;
+ while (nextword != numwords) {
+ int matchedWords = matchOneOptionalArg(&words[nextword], numwords - nextword, args, numargs, &matchedarg);
+ if (matchedWords == 0) {
+ break;
+ }
+ /* Successfully matched an optional arg; mark any previous match as completed
+ * so it won't be partially hinted.
+ */
+ if (lastmatchedarg != -1) {
+ args[lastmatchedarg].matched_all = 1;
+ }
+ lastmatchedarg = matchedarg;
+ nextword += matchedWords;
+ }
+ return nextword;
+}
+
+/* Matches as many input words as possible against command arguments. */
+static int matchArgs(char **words, int numwords, cliCommandArg *args, int numargs) {
+ int nextword, nextarg, matchedWords;
+ for (nextword = 0, nextarg = 0; nextword != numwords && nextarg != numargs; ++nextarg) {
+ /* Optional args can occur in any order. Collect a range of consecutive optional args
+ * and try to match them as a group against the next input words.
+ */
+ if (args[nextarg].flags & CMD_ARG_OPTIONAL) {
+ int lastoptional;
+ for (lastoptional = nextarg; lastoptional < numargs; lastoptional++) {
+ if (!(args[lastoptional].flags & CMD_ARG_OPTIONAL)) break;
+ }
+ matchedWords = matchOptionalArgs(&words[nextword], numwords - nextword, &args[nextarg], lastoptional - nextarg);
+ nextarg = lastoptional - 1;
+ } else {
+ matchedWords = matchArg(&words[nextword], numwords - nextword, &args[nextarg]);
+ if (matchedWords == 0) {
+ /* Couldn't match a required word - matching fails! */
+ return 0;
+ }
+ }
+
+ nextword += matchedWords;
+ }
+ return nextword;
+}
+
+/* Compute the linenoise hint for the input prefix in inputargv/inputargc.
+ * cmdlen is the number of words from the start of the input that make up the command.
+ * If docs.args exists, dynamically creates a hint string by matching the arg specs
+ * against the input words.
+ */
+static sds makeHint(char **inputargv, int inputargc, int cmdlen, struct commandDocs docs) {
+ sds hint;
+
+ if (docs.args) {
+ /* Remove arguments from the returned hint to show only the
+ * ones the user did not yet type. */
+ clearMatchedArgs(docs.args, docs.numargs);
+ hint = sdsempty();
+ int matchedWords = 0;
+ if (inputargv && inputargc)
+ matchedWords = matchArgs(inputargv + cmdlen, inputargc - cmdlen, docs.args, docs.numargs);
+ if (matchedWords == inputargc - cmdlen) {
+ hint = addHintForArguments(hint, docs.args, docs.numargs, " ");
+ }
+ return hint;
+ }
+
+ /* If arg specs are not available, show the hint string until the user types something. */
+ if (inputargc <= cmdlen) {
+ hint = sdsnew(docs.params);
+ } else {
+ hint = sdsempty();
+ }
+ return hint;
+}
+
+/* Search for a command matching the longest possible prefix of input words. */
+static helpEntry* findHelpEntry(int argc, char **argv) {
+ helpEntry *entry = NULL;
+ int i, rawargc, matchlen = 0;
+ sds *rawargv;
- /* Search longest matching prefix command */
for (i = 0; i < helpEntriesLen; i++) {
if (!(helpEntries[i].type & CLI_HELP_COMMAND)) continue;
- rawargv = sdssplitargs(helpEntries[i].full,&rawargc);
+ rawargv = helpEntries[i].argv;
+ rawargc = helpEntries[i].argc;
if (rawargc <= argc) {
int j;
for (j = 0; j < rawargc; j++) {
@@ -990,35 +1487,51 @@ static char *hintsCallback(const char *buf, int *color, int *bold) {
entry = &helpEntries[i];
}
}
- sdsfreesplitres(rawargv,rawargc);
}
- sdsfreesplitres(argv - shift,argc + shift);
+ return entry;
+}
+
+/* Returns the command-line hint string for a given partial input. */
+static sds getHintForInput(const char *charinput) {
+ sds hint = NULL;
+ int inputargc, inputlen = strlen(charinput);
+ sds *inputargv = sdssplitargs(charinput, &inputargc);
+ int endspace = inputlen && isspace(charinput[inputlen-1]);
+ /* Don't match the last word until the user has typed a space after it. */
+ int matchargc = endspace ? inputargc : inputargc - 1;
+
+ helpEntry *entry = findHelpEntry(matchargc, inputargv);
if (entry) {
- *color = 90;
- *bold = 0;
- sds hint = sdsnew(entry->org.params);
+ hint = makeHint(inputargv, matchargc, entry->argc, entry->docs);
+ }
+ sdsfreesplitres(inputargv, inputargc);
+ return hint;
+}
- /* Remove arguments from the returned hint to show only the
- * ones the user did not yet type. */
- int toremove = argc-matchlen;
- while(toremove > 0 && sdslen(hint)) {
- if (hint[0] == '[') break;
- if (hint[0] == ' ') toremove--;
- sdsrange(hint,1,-1);
- }
+/* Linenoise hints callback. */
+static char *hintsCallback(const char *buf, int *color, int *bold) {
+ if (!pref.hints) return NULL;
- /* Add an initial space if needed. */
- if (!endspace) {
- sds newhint = sdsnewlen(" ",1);
- newhint = sdscatsds(newhint,hint);
- sdsfree(hint);
- hint = newhint;
- }
+ sds hint = getHintForInput(buf);
+ if (hint == NULL) {
+ return NULL;
+ }
- return hint;
+ *color = 90;
+ *bold = 0;
+
+ /* Add an initial space if needed. */
+ int len = strlen(buf);
+ int endspace = len && isspace(buf[len-1]);
+ if (!endspace) {
+ sds newhint = sdsnewlen(" ",1);
+ newhint = sdscatsds(newhint,hint);
+ sdsfree(hint);
+ hint = newhint;
}
- return NULL;
+
+ return hint;
}
static void freeHintsCallback(void *ptr) {
@@ -1119,6 +1632,16 @@ static int cliSwitchProto(void) {
result = REDIS_OK;
}
}
+
+ /* Retrieve server version string for later use. */
+ for (size_t i = 0; i < reply->elements; i += 2) {
+ assert(reply->element[i]->type == REDIS_REPLY_STRING);
+ char *key = reply->element[i]->str;
+ if (!strcmp(key, "version")) {
+ assert(reply->element[i + 1]->type == REDIS_REPLY_STRING);
+ config.server_version = sdsnew(reply->element[i + 1]->str);
+ }
+ }
freeReplyObject(reply);
config.current_resp3 = 1;
return result;
@@ -1202,7 +1725,7 @@ static int cliConnect(int flags) {
/* In cluster, if server replies ASK, we will redirect to a different node.
* Before sending the real command, we need to send ASKING command first. */
-static int cliSendAsking() {
+static int cliSendAsking(void) {
redisReply *reply;
config.cluster_send_asking = 0;
@@ -1797,7 +2320,7 @@ static int cliReadReply(int output_raw_strings) {
}
/* Simultaneously wait for pubsub messages from redis and input on stdin. */
-static void cliWaitForMessagesOrStdin() {
+static void cliWaitForMessagesOrStdin(void) {
int show_info = config.output != OUTPUT_RAW && (isatty(STDOUT_FILENO) ||
getenv("FAKETTY"));
int use_color = show_info && isColorTerm();
@@ -2205,6 +2728,8 @@ static int parseOptions(int argc, char **argv) {
} else if (!strcmp(argv[i],"--pattern") && !lastarg) {
sdsfree(config.pattern);
config.pattern = sdsnew(argv[++i]);
+ } else if (!strcmp(argv[i],"--count") && !lastarg) {
+ config.count = atoi(argv[++i]);
} else if (!strcmp(argv[i],"--quoted-pattern") && !lastarg) {
sdsfree(config.pattern);
config.pattern = unquoteCString(argv[++i]);
@@ -2341,6 +2866,10 @@ static int parseOptions(int argc, char **argv) {
} else if (!strcmp(argv[i],"--cluster-fix-with-unreachable-masters")) {
config.cluster_manager_command.flags |=
CLUSTER_MANAGER_CMD_FLAG_FIX_WITH_UNREACHABLE_MASTERS;
+ } else if (!strcmp(argv[i],"--test_hint") && !lastarg) {
+ config.test_hint = argv[++i];
+ } else if (!strcmp(argv[i],"--test_hint_file") && !lastarg) {
+ config.test_hint_file = argv[++i];
#ifdef USE_OPENSSL
} else if (!strcmp(argv[i],"--tls")) {
config.tls = 1;
@@ -2439,7 +2968,7 @@ static int parseOptions(int argc, char **argv) {
return i;
}
-static void parseEnv() {
+static void parseEnv(void) {
/* Set auth from env, but do not overwrite CLI arguments if passed */
char *auth = getenv(REDIS_CLI_AUTH_ENV);
if (auth != NULL && config.conn_info.auth == NULL) {
@@ -2455,6 +2984,29 @@ static void parseEnv() {
static void usage(int err) {
sds version = cliVersion();
FILE *target = err ? stderr: stdout;
+ const char *tls_usage =
+#ifdef USE_OPENSSL
+" --tls Establish a secure TLS connection.\n"
+" --sni <host> Server name indication for TLS.\n"
+" --cacert <file> CA Certificate file to verify with.\n"
+" --cacertdir <dir> Directory where trusted CA certificates are stored.\n"
+" If neither cacert nor cacertdir are specified, the default\n"
+" system-wide trusted root certs configuration will apply.\n"
+" --insecure Allow insecure TLS connection by skipping cert validation.\n"
+" --cert <file> Client certificate to authenticate with.\n"
+" --key <file> Private key file to authenticate with.\n"
+" --tls-ciphers <list> Sets the list of preferred ciphers (TLSv1.2 and below)\n"
+" in order of preference from highest to lowest separated by colon (\":\").\n"
+" See the ciphers(1ssl) manpage for more information about the syntax of this string.\n"
+#ifdef TLS1_3_VERSION
+" --tls-ciphersuites <list> Sets the list of preferred ciphersuites (TLSv1.3)\n"
+" in order of preference from highest to lowest separated by colon (\":\").\n"
+" See the ciphers(1ssl) manpage for more information about the syntax of this string,\n"
+" and specifically for TLSv1.3 ciphersuites.\n"
+#endif
+#endif
+"";
+
fprintf(target,
"redis-cli %s\n"
"\n"
@@ -2486,26 +3038,7 @@ static void usage(int err) {
" -D <delimiter> Delimiter between responses for raw formatting (default: \\n).\n"
" -c Enable cluster mode (follow -ASK and -MOVED redirections).\n"
" -e Return exit error code when command execution fails.\n"
-#ifdef USE_OPENSSL
-" --tls Establish a secure TLS connection.\n"
-" --sni <host> Server name indication for TLS.\n"
-" --cacert <file> CA Certificate file to verify with.\n"
-" --cacertdir <dir> Directory where trusted CA certificates are stored.\n"
-" If neither cacert nor cacertdir are specified, the default\n"
-" system-wide trusted root certs configuration will apply.\n"
-" --insecure Allow insecure TLS connection by skipping cert validation.\n"
-" --cert <file> Client certificate to authenticate with.\n"
-" --key <file> Private key file to authenticate with.\n"
-" --tls-ciphers <list> Sets the list of preferred ciphers (TLSv1.2 and below)\n"
-" in order of preference from highest to lowest separated by colon (\":\").\n"
-" See the ciphers(1ssl) manpage for more information about the syntax of this string.\n"
-#ifdef TLS1_3_VERSION
-" --tls-ciphersuites <list> Sets the list of preferred ciphersuites (TLSv1.3)\n"
-" in order of preference from highest to lowest separated by colon (\":\").\n"
-" See the ciphers(1ssl) manpage for more information about the syntax of this string,\n"
-" and specifically for TLSv1.3 ciphersuites.\n"
-#endif
-#endif
+"%s"
" --raw Use raw formatting for replies (default when STDOUT is\n"
" not a tty).\n"
" --no-raw Force formatted output even when STDOUT is not a tty.\n"
@@ -2515,7 +3048,8 @@ static void usage(int err) {
" --quoted-json Same as --json, but produce ASCII-safe quoted strings, not Unicode.\n"
" --show-pushes <yn> Whether to print RESP3 PUSH messages. Enabled by default when\n"
" STDOUT is a tty but can be overridden with --show-pushes no.\n"
-" --stat Print rolling stats about server: mem, clients, ...\n",version);
+" --stat Print rolling stats about server: mem, clients, ...\n",
+version,tls_usage);
fprintf(target,
" --latency Enter a special mode continuously sampling latency.\n"
@@ -2550,6 +3084,7 @@ static void usage(int err) {
" --scan List all keys using the SCAN command.\n"
" --pattern <pat> Keys pattern when using the --scan, --bigkeys or --hotkeys\n"
" options (default: *).\n"
+" --count <count> Count option when using the --scan, --bigkeys or --hotkeys (default: 10).\n"
" --quoted-pattern <pat> Same as --pattern, but the specified string can be\n"
" quoted, in order to pass an otherwise non binary-safe string.\n"
" --intrinsic-latency <sec> Run a test to measure intrinsic system latency.\n"
@@ -2580,6 +3115,7 @@ static void usage(int err) {
" redis-cli --quoted-input set '\"null-\\x00-separated\"' value\n"
" redis-cli --eval myscript.lua key1 key2 , arg1 arg2 arg3\n"
" redis-cli --scan --pattern '*:12345*'\n"
+" redis-cli --scan --pattern '*:12345*' --count 100\n"
"\n"
" (Note: when using --eval the comma separates KEYS[] from ARGV[] items)\n"
"\n"
@@ -2735,12 +3271,15 @@ static int isSensitiveCommand(int argc, char **argv) {
return 1;
} else if (argc > 2 &&
!strcasecmp(argv[0],"config") &&
- !strcasecmp(argv[1],"set") && (
- !strcasecmp(argv[2],"masterauth") ||
- !strcasecmp(argv[2],"masteruser") ||
- !strcasecmp(argv[2],"requirepass")))
- {
- return 1;
+ !strcasecmp(argv[1],"set")) {
+ for (int j = 2; j < argc; j = j+2) {
+ if (!strcasecmp(argv[j],"masterauth") ||
+ !strcasecmp(argv[j],"masteruser") ||
+ !strcasecmp(argv[j],"requirepass")) {
+ return 1;
+ }
+ }
+ return 0;
/* HELLO [protover [AUTH username password] [SETNAME clientname]] */
} else if (argc > 4 && !strcasecmp(argv[0],"hello")) {
for (int j = 2; j < argc; j++) {
@@ -5336,7 +5875,7 @@ static clusterManagerNode * clusterManagerGetNodeWithMostKeysInSlot(list *nodes,
* in the cluster. If there are multiple masters with the same smaller
* number of replicas, one at random is returned. */
-static clusterManagerNode *clusterManagerNodeWithLeastReplicas() {
+static clusterManagerNode *clusterManagerNodeWithLeastReplicas(void) {
clusterManagerNode *node = NULL;
int lowest_count = 0;
listIter li;
@@ -5355,7 +5894,7 @@ static clusterManagerNode *clusterManagerNodeWithLeastReplicas() {
/* This function returns a random master node, return NULL if none */
-static clusterManagerNode *clusterManagerNodeMasterRandom() {
+static clusterManagerNode *clusterManagerNodeMasterRandom(void) {
int master_count = 0;
int idx;
listIter li;
@@ -7857,7 +8396,7 @@ int sendReplconf(const char* arg1, const char* arg2) {
return res;
}
-void sendCapa() {
+void sendCapa(void) {
sendReplconf("capa", "eof");
}
@@ -8292,8 +8831,8 @@ static redisReply *sendScan(unsigned long long *it) {
redisReply *reply;
if (config.pattern)
- reply = redisCommand(context, "SCAN %llu MATCH %b",
- *it, config.pattern, sdslen(config.pattern));
+ reply = redisCommand(context, "SCAN %llu MATCH %b COUNT %d",
+ *it, config.pattern, sdslen(config.pattern), config.count);
else
reply = redisCommand(context,"SCAN %llu",*it);
@@ -9119,6 +9658,90 @@ static sds askPassword(const char *msg) {
return auth;
}
+/* Prints out the hint completion string for a given input prefix string. */
+void testHint(const char *input) {
+ cliInitHelp();
+
+ sds hint = getHintForInput(input);
+ printf("%s\n", hint);
+ exit(0);
+}
+
+sds readHintSuiteLine(char buf[], size_t size, FILE *fp) {
+ while (fgets(buf, size, fp) != NULL) {
+ if (buf[0] != '#') {
+ sds input = sdsnew(buf);
+
+ /* Strip newline. */
+ input = sdstrim(input, "\n");
+ return input;
+ }
+ }
+ return NULL;
+}
+
+/* Runs a suite of hint completion tests contained in a file. */
+void testHintSuite(char *filename) {
+ FILE *fp;
+ char buf[256];
+ sds line, input, expected, hint;
+ int pass=0, fail=0;
+ int argc;
+ char **argv;
+
+ fp = fopen(filename, "r");
+ if (!fp) {
+ fprintf(stderr,
+ "Can't open file '%s': %s\n", filename, strerror(errno));
+ exit(-1);
+ }
+
+ cliInitHelp();
+
+ while (1) {
+ line = readHintSuiteLine(buf, sizeof(buf), fp);
+ if (line == NULL) break;
+ argv = sdssplitargs(line, &argc);
+ sdsfree(line);
+ if (argc == 0) {
+ sdsfreesplitres(argv, argc);
+ continue;
+ }
+
+ if (argc == 1) {
+ fprintf(stderr,
+ "Missing expected hint for input '%s'\n", argv[0]);
+ exit(-1);
+ }
+ input = argv[0];
+ expected = argv[1];
+ hint = getHintForInput(input);
+ if (config.verbose) {
+ printf("Input: '%s', Expected: '%s', Hint: '%s'\n", input, expected, hint);
+ }
+
+ /* Strip trailing spaces from hint - they don't matter. */
+ while (hint != NULL && sdslen(hint) > 0 && hint[sdslen(hint) - 1] == ' ') {
+ sdssetlen(hint, sdslen(hint) - 1);
+ hint[sdslen(hint)] = '\0';
+ }
+
+ if (hint == NULL || strcmp(hint, expected) != 0) {
+ fprintf(stderr, "Test case '%s' FAILED: expected '%s', got '%s'\n", input, expected, hint);
+ ++fail;
+ }
+ else {
+ ++pass;
+ }
+ sdsfreesplitres(argv, argc);
+ sdsfree(hint);
+ }
+ fclose(fp);
+
+ printf("%s: %d/%d passed\n", fail == 0 ? "SUCCESS" : "FAILURE", pass, pass + fail);
+ exit(fail);
+}
+
/*------------------------------------------------------------------------------
* Program main()
*--------------------------------------------------------------------------- */
@@ -9152,6 +9775,7 @@ int main(int argc, char **argv) {
config.get_functions_rdb_mode = 0;
config.stat_mode = 0;
config.scan_mode = 0;
+ config.count = 10;
config.intrinsic_latency_mode = 0;
config.pattern = NULL;
config.rdb_filename = NULL;
@@ -9176,6 +9800,7 @@ int main(int argc, char **argv) {
config.set_errcode = 0;
config.no_auth_warning = 0;
config.in_multi = 0;
+ config.server_version = NULL;
config.cluster_manager_command.name = NULL;
config.cluster_manager_command.argc = 0;
config.cluster_manager_command.argv = NULL;
@@ -9321,6 +9946,15 @@ int main(int argc, char **argv) {
/* Intrinsic latency mode */
if (config.intrinsic_latency_mode) intrinsicLatencyMode();
+ /* Print command-line hint for an input prefix string */
+ if (config.test_hint) {
+ testHint(config.test_hint);
+ }
+ /* Run test suite for command-line hints */
+ if (config.test_hint_file) {
+ testHintSuite(config.test_hint_file);
+ }
+
/* Start interactive mode when no command is provided */
if (argc == 0 && !config.eval) {
/* Ignore SIGPIPE in interactive mode to force a reconnect */
diff --git a/src/redismodule.h b/src/redismodule.h
index 13ebc3829..049b0f340 100644
--- a/src/redismodule.h
+++ b/src/redismodule.h
@@ -881,6 +881,7 @@ typedef struct RedisModuleServerInfoData RedisModuleServerInfoData;
typedef struct RedisModuleScanCursor RedisModuleScanCursor;
typedef struct RedisModuleUser RedisModuleUser;
typedef struct RedisModuleKeyOptCtx RedisModuleKeyOptCtx;
+typedef struct RedisModuleRdbStream RedisModuleRdbStream;
typedef int (*RedisModuleCmdFunc)(RedisModuleCtx *ctx, RedisModuleString **argv, int argc);
typedef void (*RedisModuleDisconnectFunc)(RedisModuleCtx *ctx, RedisModuleBlockedClient *bc);
@@ -975,7 +976,7 @@ REDISMODULE_API int (*RedisModule_GetSelectedDb)(RedisModuleCtx *ctx) REDISMODUL
REDISMODULE_API int (*RedisModule_SelectDb)(RedisModuleCtx *ctx, int newid) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_KeyExists)(RedisModuleCtx *ctx, RedisModuleString *keyname) REDISMODULE_ATTR;
REDISMODULE_API RedisModuleKey * (*RedisModule_OpenKey)(RedisModuleCtx *ctx, RedisModuleString *keyname, int mode) REDISMODULE_ATTR;
-REDISMODULE_API int (*RedisModule_GetOpenKeyModesAll)() REDISMODULE_ATTR;
+REDISMODULE_API int (*RedisModule_GetOpenKeyModesAll)(void) REDISMODULE_ATTR;
REDISMODULE_API void (*RedisModule_CloseKey)(RedisModuleKey *kp) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_KeyType)(RedisModuleKey *kp) REDISMODULE_ATTR;
REDISMODULE_API size_t (*RedisModule_ValueLength)(RedisModuleKey *kp) REDISMODULE_ATTR;
@@ -1013,6 +1014,7 @@ REDISMODULE_API RedisModuleString * (*RedisModule_CreateStringPrintf)(RedisModul
REDISMODULE_API void (*RedisModule_FreeString)(RedisModuleCtx *ctx, RedisModuleString *str) REDISMODULE_ATTR;
REDISMODULE_API const char * (*RedisModule_StringPtrLen)(const RedisModuleString *str, size_t *len) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_ReplyWithError)(RedisModuleCtx *ctx, const char *err) REDISMODULE_ATTR;
+REDISMODULE_API int (*RedisModule_ReplyWithErrorFormat)(RedisModuleCtx *ctx, const char *fmt, ...) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_ReplyWithSimpleString)(RedisModuleCtx *ctx, const char *msg) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_ReplyWithArray)(RedisModuleCtx *ctx, long len) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_ReplyWithMap)(RedisModuleCtx *ctx, long len) REDISMODULE_ATTR;
@@ -1096,7 +1098,7 @@ REDISMODULE_API int (*RedisModule_SetClientNameById)(uint64_t id, RedisModuleStr
REDISMODULE_API int (*RedisModule_PublishMessage)(RedisModuleCtx *ctx, RedisModuleString *channel, RedisModuleString *message) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_PublishMessageShard)(RedisModuleCtx *ctx, RedisModuleString *channel, RedisModuleString *message) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_GetContextFlags)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
-REDISMODULE_API int (*RedisModule_AvoidReplicaTraffic)() REDISMODULE_ATTR;
+REDISMODULE_API int (*RedisModule_AvoidReplicaTraffic)(void) REDISMODULE_ATTR;
REDISMODULE_API void * (*RedisModule_PoolAlloc)(RedisModuleCtx *ctx, size_t bytes) REDISMODULE_ATTR;
REDISMODULE_API RedisModuleType * (*RedisModule_CreateDataType)(RedisModuleCtx *ctx, const char *name, int encver, RedisModuleTypeMethods *typemethods) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_ModuleTypeSetValue)(RedisModuleKey *key, RedisModuleType *mt, void *value) REDISMODULE_ATTR;
@@ -1199,17 +1201,17 @@ REDISMODULE_API RedisModuleBlockedClient * (*RedisModule_BlockClientOnKeys)(Redi
REDISMODULE_API RedisModuleBlockedClient * (*RedisModule_BlockClientOnKeysWithFlags)(RedisModuleCtx *ctx, RedisModuleCmdFunc reply_callback, RedisModuleCmdFunc timeout_callback, void (*free_privdata)(RedisModuleCtx*,void*), long long timeout_ms, RedisModuleString **keys, int numkeys, void *privdata, int flags) REDISMODULE_ATTR;
REDISMODULE_API void (*RedisModule_SignalKeyAsReady)(RedisModuleCtx *ctx, RedisModuleString *key) REDISMODULE_ATTR;
REDISMODULE_API RedisModuleString * (*RedisModule_GetBlockedClientReadyKey)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
-REDISMODULE_API RedisModuleScanCursor * (*RedisModule_ScanCursorCreate)() REDISMODULE_ATTR;
+REDISMODULE_API RedisModuleScanCursor * (*RedisModule_ScanCursorCreate)(void) REDISMODULE_ATTR;
REDISMODULE_API void (*RedisModule_ScanCursorRestart)(RedisModuleScanCursor *cursor) REDISMODULE_ATTR;
REDISMODULE_API void (*RedisModule_ScanCursorDestroy)(RedisModuleScanCursor *cursor) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_Scan)(RedisModuleCtx *ctx, RedisModuleScanCursor *cursor, RedisModuleScanCB fn, void *privdata) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_ScanKey)(RedisModuleKey *key, RedisModuleScanCursor *cursor, RedisModuleScanKeyCB fn, void *privdata) REDISMODULE_ATTR;
-REDISMODULE_API int (*RedisModule_GetContextFlagsAll)() REDISMODULE_ATTR;
-REDISMODULE_API int (*RedisModule_GetModuleOptionsAll)() REDISMODULE_ATTR;
-REDISMODULE_API int (*RedisModule_GetKeyspaceNotificationFlagsAll)() REDISMODULE_ATTR;
+REDISMODULE_API int (*RedisModule_GetContextFlagsAll)(void) REDISMODULE_ATTR;
+REDISMODULE_API int (*RedisModule_GetModuleOptionsAll)(void) REDISMODULE_ATTR;
+REDISMODULE_API int (*RedisModule_GetKeyspaceNotificationFlagsAll)(void) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_IsSubEventSupported)(RedisModuleEvent event, uint64_t subevent) REDISMODULE_ATTR;
-REDISMODULE_API int (*RedisModule_GetServerVersion)() REDISMODULE_ATTR;
-REDISMODULE_API int (*RedisModule_GetTypeMethodVersion)() REDISMODULE_ATTR;
+REDISMODULE_API int (*RedisModule_GetServerVersion)(void) REDISMODULE_ATTR;
+REDISMODULE_API int (*RedisModule_GetTypeMethodVersion)(void) REDISMODULE_ATTR;
REDISMODULE_API void (*RedisModule_Yield)(RedisModuleCtx *ctx, int flags, const char *busy_reply) REDISMODULE_ATTR;
REDISMODULE_API RedisModuleBlockedClient * (*RedisModule_BlockClient)(RedisModuleCtx *ctx, RedisModuleCmdFunc reply_callback, RedisModuleCmdFunc timeout_callback, void (*free_privdata)(RedisModuleCtx*,void*), long long timeout_ms) REDISMODULE_ATTR;
REDISMODULE_API void * (*RedisModule_BlockClientGetPrivateData)(RedisModuleBlockedClient *blocked_client) REDISMODULE_ATTR;
@@ -1232,7 +1234,7 @@ REDISMODULE_API void (*RedisModule_ThreadSafeContextUnlock)(RedisModuleCtx *ctx)
REDISMODULE_API int (*RedisModule_SubscribeToKeyspaceEvents)(RedisModuleCtx *ctx, int types, RedisModuleNotificationFunc cb) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_AddPostNotificationJob)(RedisModuleCtx *ctx, RedisModulePostNotificationJobFunc callback, void *pd, void (*free_pd)(void*)) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_NotifyKeyspaceEvent)(RedisModuleCtx *ctx, int type, const char *event, RedisModuleString *key) REDISMODULE_ATTR;
-REDISMODULE_API int (*RedisModule_GetNotifyKeyspaceEvents)() REDISMODULE_ATTR;
+REDISMODULE_API int (*RedisModule_GetNotifyKeyspaceEvents)(void) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_BlockedClientDisconnected)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
REDISMODULE_API void (*RedisModule_RegisterClusterMessageReceiver)(RedisModuleCtx *ctx, uint8_t type, RedisModuleClusterMessageReceiver callback) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_SendClusterMessage)(RedisModuleCtx *ctx, const char *target_id, uint8_t type, const char *msg, uint32_t len) REDISMODULE_ATTR;
@@ -1261,7 +1263,7 @@ REDISMODULE_API int (*RedisModule_Fork)(RedisModuleForkDoneHandler cb, void *use
REDISMODULE_API void (*RedisModule_SendChildHeartbeat)(double progress) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_ExitFromChild)(int retcode) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_KillForkChild)(int child_pid) REDISMODULE_ATTR;
-REDISMODULE_API float (*RedisModule_GetUsedMemoryRatio)() REDISMODULE_ATTR;
+REDISMODULE_API float (*RedisModule_GetUsedMemoryRatio)(void) REDISMODULE_ATTR;
REDISMODULE_API size_t (*RedisModule_MallocSize)(void* ptr) REDISMODULE_ATTR;
REDISMODULE_API size_t (*RedisModule_MallocUsableSize)(void *ptr) REDISMODULE_ATTR;
REDISMODULE_API size_t (*RedisModule_MallocSizeString)(RedisModuleString* str) REDISMODULE_ATTR;
@@ -1303,6 +1305,10 @@ REDISMODULE_API int (*RedisModule_RegisterNumericConfig)(RedisModuleCtx *ctx, co
REDISMODULE_API int (*RedisModule_RegisterStringConfig)(RedisModuleCtx *ctx, const char *name, const char *default_val, unsigned int flags, RedisModuleConfigGetStringFunc getfn, RedisModuleConfigSetStringFunc setfn, RedisModuleConfigApplyFunc applyfn, void *privdata) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_RegisterEnumConfig)(RedisModuleCtx *ctx, const char *name, int default_val, unsigned int flags, const char **enum_values, const int *int_values, int num_enum_vals, RedisModuleConfigGetEnumFunc getfn, RedisModuleConfigSetEnumFunc setfn, RedisModuleConfigApplyFunc applyfn, void *privdata) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_LoadConfigs)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
+REDISMODULE_API RedisModuleRdbStream *(*RedisModule_RdbStreamCreateFromFile)(const char *filename) REDISMODULE_ATTR;
+REDISMODULE_API void (*RedisModule_RdbStreamFree)(RedisModuleRdbStream *stream) REDISMODULE_ATTR;
+REDISMODULE_API int (*RedisModule_RdbLoad)(RedisModuleCtx *ctx, RedisModuleRdbStream *stream, int flags) REDISMODULE_ATTR;
+REDISMODULE_API int (*RedisModule_RdbSave)(RedisModuleCtx *ctx, RedisModuleRdbStream *stream, int flags) REDISMODULE_ATTR;
#define RedisModule_IsAOFClient(id) ((id) == UINT64_MAX)
@@ -1327,6 +1333,7 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int
REDISMODULE_GET_API(WrongArity);
REDISMODULE_GET_API(ReplyWithLongLong);
REDISMODULE_GET_API(ReplyWithError);
+ REDISMODULE_GET_API(ReplyWithErrorFormat);
REDISMODULE_GET_API(ReplyWithSimpleString);
REDISMODULE_GET_API(ReplyWithArray);
REDISMODULE_GET_API(ReplyWithMap);
@@ -1658,6 +1665,10 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int
REDISMODULE_GET_API(RegisterStringConfig);
REDISMODULE_GET_API(RegisterEnumConfig);
REDISMODULE_GET_API(LoadConfigs);
+ REDISMODULE_GET_API(RdbStreamCreateFromFile);
+ REDISMODULE_GET_API(RdbStreamFree);
+ REDISMODULE_GET_API(RdbLoad);
+ REDISMODULE_GET_API(RdbSave);
if (RedisModule_IsModuleNameBusy && RedisModule_IsModuleNameBusy(name)) return REDISMODULE_ERR;
RedisModule_SetModuleAttribs(ctx,name,ver,apiver);
diff --git a/src/replication.c b/src/replication.c
index c3cf41dee..ca3287491 100644
--- a/src/replication.c
+++ b/src/replication.c
@@ -55,7 +55,7 @@ int cancelReplicationHandshake(int reconnect);
int RDBGeneratedByReplication = 0;
/* --------------------------- Utility functions ---------------------------- */
-static ConnectionType *connTypeOfReplication() {
+static ConnectionType *connTypeOfReplication(void) {
if (server.tls_replication) {
return connectionTypeTls();
}
@@ -1793,7 +1793,7 @@ void replicationCreateMasterClient(connection *conn, int dbid) {
* master-replica synchronization: if it fails after multiple attempts
* the replica cannot be considered reliable and exists with an
* error. */
-void restartAOFAfterSYNC() {
+void restartAOFAfterSYNC(void) {
unsigned int tries, max_tries = 10;
for (tries = 0; tries < max_tries; ++tries) {
if (startAppendOnly() == C_OK) break;
@@ -1810,7 +1810,7 @@ void restartAOFAfterSYNC() {
}
}
-static int useDisklessLoad() {
+static int useDisklessLoad(void) {
/* compute boolean decision to use diskless load */
int enabled = server.repl_diskless_load == REPL_DISKLESS_LOAD_SWAPDB ||
(server.repl_diskless_load == REPL_DISKLESS_LOAD_WHEN_DB_EMPTY && dbTotalServerKeyCount()==0);
@@ -1849,7 +1849,7 @@ void disklessLoadDiscardTempDb(redisDb *tempDb) {
* we have no way to incrementally feed our replicas after that.
* We want our replicas to resync with us as well, if we have any sub-replicas.
* This is useful on readSyncBulkPayload in places where we just finished transferring db. */
-void replicationAttachToNewMaster() {
+void replicationAttachToNewMaster(void) {
/* Replica starts to apply data from new master, we must discard the cached
* master structure. */
serverAssert(server.master == NULL);
@@ -3606,6 +3606,7 @@ void unblockClientWaitingReplicas(client *c) {
listNode *ln = listSearchKey(server.clients_waiting_acks,c);
serverAssert(ln != NULL);
listDelNode(server.clients_waiting_acks,ln);
+ updateStatsOnUnblock(c, 0, 0, 0);
}
/* Check if there are clients blocked in WAIT or WAITAOF that can be unblocked
@@ -3992,7 +3993,7 @@ static client *findReplica(char *host, int port) {
return NULL;
}
-const char *getFailoverStateString() {
+const char *getFailoverStateString(void) {
switch(server.failover_state) {
case NO_FAILOVER: return "no-failover";
case FAILOVER_IN_PROGRESS: return "failover-in-progress";
@@ -4004,7 +4005,7 @@ const char *getFailoverStateString() {
/* Resets the internal failover configuration, this needs
* to be called after a failover either succeeds or fails
* as it includes the client unpause. */
-void clearFailoverState() {
+void clearFailoverState(void) {
server.failover_end_time = 0;
server.force_failover = 0;
zfree(server.target_replica_host);
diff --git a/src/script.c b/src/script.c
index 2a7721065..70680e3ea 100644
--- a/src/script.c
+++ b/src/script.c
@@ -60,16 +60,16 @@ static void enterScriptTimedoutMode(scriptRunCtx *run_ctx) {
blockingOperationStarts();
}
-int scriptIsTimedout() {
+int scriptIsTimedout(void) {
return scriptIsRunning() && (curr_run_ctx->flags & SCRIPT_TIMEDOUT);
}
-client* scriptGetClient() {
+client* scriptGetClient(void) {
serverAssert(scriptIsRunning());
return curr_run_ctx->c;
}
-client* scriptGetCaller() {
+client* scriptGetCaller(void) {
serverAssert(scriptIsRunning());
return curr_run_ctx->original_client;
}
@@ -130,7 +130,7 @@ uint64_t scriptFlagsToCmdFlags(uint64_t cmd_flags, uint64_t script_flags) {
/* Prepare the given run ctx for execution */
int scriptPrepareForRun(scriptRunCtx *run_ctx, client *engine_client, client *caller, const char *funcname, uint64_t script_flags, int ro) {
serverAssert(!curr_run_ctx);
- bool client_allow_oom = caller->flags & CLIENT_ALLOW_OOM;
+ int client_allow_oom = !!(caller->flags & CLIENT_ALLOW_OOM);
int running_stale = server.masterhost &&
server.repl_state != REPL_STATE_CONNECTED &&
@@ -269,16 +269,16 @@ void scriptResetRun(scriptRunCtx *run_ctx) {
}
/* return true if a script is currently running */
-int scriptIsRunning() {
+int scriptIsRunning(void) {
return curr_run_ctx != NULL;
}
-const char* scriptCurrFunction() {
+const char* scriptCurrFunction(void) {
serverAssert(scriptIsRunning());
return curr_run_ctx->funcname;
}
-int scriptIsEval() {
+int scriptIsEval(void) {
serverAssert(scriptIsRunning());
return curr_run_ctx->flags & SCRIPT_EVAL_MODE;
}
@@ -571,7 +571,7 @@ error:
incrCommandStatsOnError(cmd, ERROR_COMMAND_REJECTED);
}
-long long scriptRunDuration() {
+long long scriptRunDuration(void) {
serverAssert(scriptIsRunning());
return elapsedMs(curr_run_ctx->start_time);
}
diff --git a/src/script.h b/src/script.h
index edcacd578..c487165d6 100644
--- a/src/script.h
+++ b/src/script.h
@@ -100,12 +100,12 @@ int scriptSetRepl(scriptRunCtx *r_ctx, int repl);
void scriptCall(scriptRunCtx *r_ctx, sds *err);
int scriptInterrupt(scriptRunCtx *r_ctx);
void scriptKill(client *c, int is_eval);
-int scriptIsRunning();
-const char* scriptCurrFunction();
-int scriptIsEval();
-int scriptIsTimedout();
-client* scriptGetClient();
-client* scriptGetCaller();
-long long scriptRunDuration();
+int scriptIsRunning(void);
+const char* scriptCurrFunction(void);
+int scriptIsEval(void);
+int scriptIsTimedout(void);
+client* scriptGetClient(void);
+client* scriptGetCaller(void);
+long long scriptRunDuration(void);
#endif /* __SCRIPT_H_ */
diff --git a/src/sentinel.c b/src/sentinel.c
index b4dead7de..326def135 100644
--- a/src/sentinel.c
+++ b/src/sentinel.c
@@ -1740,7 +1740,7 @@ const char *sentinelCheckCreateInstanceErrors(int role) {
}
/* init function for server.sentinel_config */
-void initializeSentinelConfig() {
+void initializeSentinelConfig(void) {
server.sentinel_config = zmalloc(sizeof(struct sentinelConfig));
server.sentinel_config->monitor_cfg = listCreate();
server.sentinel_config->pre_monitor_cfg = listCreate();
@@ -1751,7 +1751,7 @@ void initializeSentinelConfig() {
}
/* destroy function for server.sentinel_config */
-void freeSentinelConfig() {
+void freeSentinelConfig(void) {
/* release these three config queues since we will not use it anymore */
listRelease(server.sentinel_config->pre_monitor_cfg);
listRelease(server.sentinel_config->monitor_cfg);
@@ -2176,7 +2176,12 @@ void rewriteConfigSentinelOption(struct rewriteConfigState *state) {
line = sdscatprintf(sdsempty(),
"sentinel known-replica %s %s %d",
master->name, announceSentinelAddr(slave_addr), slave_addr->port);
- rewriteConfigRewriteLine(state,"sentinel known-replica",line,1);
+ /* try to replace any known-slave option first if found */
+ if (rewriteConfigRewriteLine(state, "sentinel known-slave", sdsdup(line), 0) == 0) {
+ rewriteConfigRewriteLine(state, "sentinel known-replica", line, 1);
+ } else {
+ sdsfree(line);
+ }
/* rewriteConfigMarkAsProcessed is handled after the loop */
}
dictReleaseIterator(di2);
@@ -3174,7 +3179,7 @@ void sentinelSendPeriodicCommands(sentinelRedisInstance *ri) {
/* =========================== SENTINEL command ============================= */
-const char* getLogLevel() {
+const char* getLogLevel(void) {
switch (server.verbosity) {
case LL_DEBUG: return "debug";
case LL_VERBOSE: return "verbose";
@@ -4066,11 +4071,14 @@ NULL
}
/* Reply format:
- * 1.) master name
- * 2.) 1.) info from master
- * 2.) info from replica
- * ...
- * 3.) other master name
+ * 1) master name
+ * 2) 1) 1) info cached ms
+ * 2) info from master
+ * 2) 1) info cached ms
+ * 2) info from replica1
+ * ...
+ * 3) other master name
+ * ...
* ...
*/
addReplyArrayLen(c,dictSize(masters_local) * 2);
diff --git a/src/server.c b/src/server.c
index 4e77d2f6d..9389e707f 100644
--- a/src/server.c
+++ b/src/server.c
@@ -36,6 +36,7 @@
#include "atomicvar.h"
#include "mt19937-64.h"
#include "functions.h"
+#include "hdr_histogram.h"
#include "syscheck.h"
#include <time.h>
@@ -655,11 +656,11 @@ const char *strChildType(int type) {
/* Return true if there are active children processes doing RDB saving,
* AOF rewriting, or some side process spawned by a loaded module. */
-int hasActiveChildProcess() {
+int hasActiveChildProcess(void) {
return server.child_pid != -1;
}
-void resetChildState() {
+void resetChildState(void) {
server.child_type = CHILD_TYPE_NONE;
server.child_pid = -1;
server.stat_current_cow_peak = 0;
@@ -681,7 +682,7 @@ int isMutuallyExclusiveChildType(int type) {
}
/* Returns true when we're inside a long command that yielded to the event loop. */
-int isInsideYieldingLongCommand() {
+int isInsideYieldingLongCommand(void) {
return scriptIsTimedout() || server.busy_module_yield_flags;
}
@@ -693,22 +694,24 @@ int allPersistenceDisabled(void) {
/* ======================= Cron: called every 100 ms ======================== */
-/* Add a sample to the operations per second array of samples. */
-void trackInstantaneousMetric(int metric, long long current_reading) {
- long long now = mstime();
- long long t = now - server.inst_metric[metric].last_sample_time;
- long long ops = current_reading -
- server.inst_metric[metric].last_sample_count;
- long long ops_sec;
-
- ops_sec = t > 0 ? (ops*1000/t) : 0;
-
- server.inst_metric[metric].samples[server.inst_metric[metric].idx] =
- ops_sec;
- server.inst_metric[metric].idx++;
- server.inst_metric[metric].idx %= STATS_METRIC_SAMPLES;
- server.inst_metric[metric].last_sample_time = now;
- server.inst_metric[metric].last_sample_count = current_reading;
+/* Add a sample to the instantaneous metric. This function computes the quotient
+ * of the increment of value and base, which is useful to record operation count
+ * per second, or the average time consumption of an operation.
+ *
+ * current_value - The dividend
+ * current_base - The divisor
+ * */
+void trackInstantaneousMetric(int metric, long long current_value, long long current_base, long long factor) {
+ if (server.inst_metric[metric].last_sample_base > 0) {
+ long long base = current_base - server.inst_metric[metric].last_sample_base;
+ long long value = current_value - server.inst_metric[metric].last_sample_value;
+ long long avg = base > 0 ? (value * factor / base) : 0;
+ server.inst_metric[metric].samples[server.inst_metric[metric].idx] = avg;
+ server.inst_metric[metric].idx++;
+ server.inst_metric[metric].idx %= STATS_METRIC_SAMPLES;
+ }
+ server.inst_metric[metric].last_sample_base = current_base;
+ server.inst_metric[metric].last_sample_value = current_value;
}
/* Return the mean of all the samples. */
@@ -744,7 +747,7 @@ int clientsCronResizeQueryBuffer(client *c) {
* sure not to resize to less than the bulk length. */
size_t resize = sdslen(c->querybuf);
if (resize < c->querybuf_peak) resize = c->querybuf_peak;
- if (c->bulklen != -1 && resize < (size_t)c->bulklen) resize = c->bulklen;
+ if (c->bulklen != -1 && resize < (size_t)c->bulklen + 2) resize = c->bulklen + 2;
c->querybuf = sdsResize(c->querybuf, resize, 1);
}
}
@@ -754,8 +757,7 @@ int clientsCronResizeQueryBuffer(client *c) {
c->querybuf_peak = sdslen(c->querybuf);
/* We reset to either the current used, or currently processed bulk size,
* which ever is bigger. */
- if (c->bulklen != -1 && (size_t)c->bulklen > c->querybuf_peak)
- c->querybuf_peak = c->bulklen;
+ if (c->bulklen != -1 && (size_t)c->bulklen + 2 > c->querybuf_peak) c->querybuf_peak = c->bulklen + 2;
return 0;
}
@@ -1015,12 +1017,11 @@ void clientsCron(void) {
client *c;
listNode *head;
- /* Rotate the list, take the current head, process.
- * This way if the client must be removed from the list it's the
- * first element and we don't incur into O(N) computation. */
- listRotateTailToHead(server.clients);
+ /* Take the current head, process, and then rotate the head to tail.
+ * This way we can fairly iterate all clients step by step. */
head = listFirst(server.clients);
c = listNodeValue(head);
+ listRotateHeadToTail(server.clients);
/* The following functions do different service checks on the client.
* The protocol is that they return non-zero if the client was
* terminated. */
@@ -1149,7 +1150,7 @@ void enterExecutionUnit(int update_cached_time, long long us) {
}
}
-void exitExecutionUnit() {
+void exitExecutionUnit(void) {
--server.execution_nesting;
}
@@ -1205,7 +1206,7 @@ void checkChildrenDone(void) {
}
/* Called from serverCron and cronUpdateMemoryStats to update cached memory metrics. */
-void cronUpdateMemoryStats() {
+void cronUpdateMemoryStats(void) {
/* Record the max memory used since the server was started. */
if (zmalloc_used_memory() > server.stat_peak_memory)
server.stat_peak_memory = zmalloc_used_memory();
@@ -1286,6 +1287,8 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
/* for debug purposes: skip actual cron work if pause_cron is on */
if (server.pause_cron) return 1000/server.hz;
+ monotime cron_start = getMonotonicUs();
+
run_with_period(100) {
long long stat_net_input_bytes, stat_net_output_bytes;
long long stat_net_repl_input_bytes, stat_net_repl_output_bytes;
@@ -1293,16 +1296,21 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
atomicGet(server.stat_net_output_bytes, stat_net_output_bytes);
atomicGet(server.stat_net_repl_input_bytes, stat_net_repl_input_bytes);
atomicGet(server.stat_net_repl_output_bytes, stat_net_repl_output_bytes);
-
- trackInstantaneousMetric(STATS_METRIC_COMMAND,server.stat_numcommands);
- trackInstantaneousMetric(STATS_METRIC_NET_INPUT,
- stat_net_input_bytes + stat_net_repl_input_bytes);
- trackInstantaneousMetric(STATS_METRIC_NET_OUTPUT,
- stat_net_output_bytes + stat_net_repl_output_bytes);
- trackInstantaneousMetric(STATS_METRIC_NET_INPUT_REPLICATION,
- stat_net_repl_input_bytes);
- trackInstantaneousMetric(STATS_METRIC_NET_OUTPUT_REPLICATION,
- stat_net_repl_output_bytes);
+ monotime current_time = getMonotonicUs();
+ long long factor = 1000000; // us
+ trackInstantaneousMetric(STATS_METRIC_COMMAND, server.stat_numcommands, current_time, factor);
+ trackInstantaneousMetric(STATS_METRIC_NET_INPUT, stat_net_input_bytes + stat_net_repl_input_bytes,
+ current_time, factor);
+ trackInstantaneousMetric(STATS_METRIC_NET_OUTPUT, stat_net_output_bytes + stat_net_repl_output_bytes,
+ current_time, factor);
+ trackInstantaneousMetric(STATS_METRIC_NET_INPUT_REPLICATION, stat_net_repl_input_bytes, current_time,
+ factor);
+ trackInstantaneousMetric(STATS_METRIC_NET_OUTPUT_REPLICATION, stat_net_repl_output_bytes,
+ current_time, factor);
+ trackInstantaneousMetric(STATS_METRIC_EL_CYCLE, server.duration_stats[EL_DURATION_TYPE_EL].cnt,
+ current_time, factor);
+ trackInstantaneousMetric(STATS_METRIC_EL_DURATION, server.duration_stats[EL_DURATION_TYPE_EL].sum,
+ server.duration_stats[EL_DURATION_TYPE_EL].cnt, 1);
}
/* We have just LRU_BITS bits per object for LRU information.
@@ -1515,18 +1523,21 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
&ei);
server.cronloops++;
+
+ server.el_cron_duration = getMonotonicUs() - cron_start;
+
return 1000/server.hz;
}
-void blockingOperationStarts() {
+void blockingOperationStarts(void) {
if(!server.blocking_op_nesting++){
updateCachedTime(0);
server.blocked_last_cron = server.mstime;
}
}
-void blockingOperationEnds() {
+void blockingOperationEnds(void) {
if(!(--server.blocking_op_nesting)){
server.blocked_last_cron = 0;
}
@@ -1537,7 +1548,7 @@ void blockingOperationEnds() {
* It attempts to do its duties at a similar rate as the configured server.hz,
* and updates cronloops variable so that similarly to serverCron, the
* run_with_period can be used. */
-void whileBlockedCron() {
+void whileBlockedCron(void) {
/* Here we may want to perform some cron jobs (normally done server.hz times
* per second). */
@@ -1650,6 +1661,11 @@ void beforeSleep(struct aeEventLoop *eventLoop) {
/* If any connection type(typical TLS) still has pending unread data don't sleep at all. */
aeSetDontWait(server.el, connTypeHasPendingData());
+ /* Record cron time in beforeSleep, which is the sum of active-expire, active-defrag and all other
+ * tasks done by cron and beforeSleep, but excluding read, write and AOF, that are counted by other
+ * sets of metrics. */
+ monotime cron_start_time_before_aof = getMonotonicUs();
+
/* Call the Redis Cluster before sleep function. Note that this function
* may change the state of Redis Cluster (from ok to fail or vice versa),
* so it's a good idea to call it before serving the unblocked clients
@@ -1658,7 +1674,7 @@ void beforeSleep(struct aeEventLoop *eventLoop) {
/* Run a fast expire cycle (the called function will return
* ASAP if a fast cycle is not needed). */
- if (server.active_expire_enabled && server.masterhost == NULL)
+ if (server.active_expire_enabled && iAmMaster())
activeExpireCycle(ACTIVE_EXPIRE_CYCLE_FAST);
/* Unblock all the clients blocked for synchronous replication
@@ -1716,12 +1732,20 @@ void beforeSleep(struct aeEventLoop *eventLoop) {
* since the unblocked clients may write data. */
handleClientsBlockedOnKeys();
+ /* Record time consumption of AOF writing. */
+ monotime aof_start_time = getMonotonicUs();
+ /* Record cron time in beforeSleep. This does not include the time consumed by AOF writing and IO writing below. */
+ monotime duration_before_aof = aof_start_time - cron_start_time_before_aof;
+
/* Write the AOF buffer on disk,
* must be done before handleClientsWithPendingWritesUsingThreads,
* in case of appendfsync=always. */
if (server.aof_state == AOF_ON || server.aof_state == AOF_WAIT_REWRITE)
flushAppendOnlyFile(0);
+ /* Record time consumption of AOF writing. */
+ durationAddSample(EL_DURATION_TYPE_AOF, getMonotonicUs() - aof_start_time);
+
/* Update the fsynced replica offset.
* If an initial rewrite is in progress then not all data is guaranteed to have actually been
* persisted to disk yet, so we cannot update the field. We will wait for the rewrite to complete. */
@@ -1734,6 +1758,9 @@ void beforeSleep(struct aeEventLoop *eventLoop) {
/* Handle writes with pending output buffers. */
handleClientsWithPendingWritesUsingThreads();
+ /* Record cron time in beforeSleep. This does not include the time consumed by AOF writing and IO writing above. */
+ monotime cron_start_time_after_write = getMonotonicUs();
+
/* Close clients that need to be closed asynchronous */
freeClientsInAsyncFreeQueue();
@@ -1745,6 +1772,25 @@ void beforeSleep(struct aeEventLoop *eventLoop) {
/* Disconnect some clients if they are consuming too much memory. */
evictClients();
+ /* Record cron time in beforeSleep. */
+ monotime duration_after_write = getMonotonicUs() - cron_start_time_after_write;
+
+ /* Record eventloop latency. */
+ if (server.el_start > 0) {
+ monotime el_duration = getMonotonicUs() - server.el_start;
+ durationAddSample(EL_DURATION_TYPE_EL, el_duration);
+ }
+ server.el_cron_duration += duration_before_aof + duration_after_write;
+ durationAddSample(EL_DURATION_TYPE_CRON, server.el_cron_duration);
+ server.el_cron_duration = 0;
+ /* Record max command count per cycle. */
+ if (server.stat_numcommands > server.el_cmd_cnt_start) {
+ long long el_command_cnt = server.stat_numcommands - server.el_cmd_cnt_start;
+ if (el_command_cnt > server.el_cmd_cnt_max) {
+ server.el_cmd_cnt_max = el_command_cnt;
+ }
+ }
+
/* Before we are going to sleep, let the threads access the dataset by
* releasing the GIL. Redis main thread will not touch anything at this
* time. */
@@ -1775,6 +1821,10 @@ void afterSleep(struct aeEventLoop *eventLoop) {
latencyEndMonitor(latency);
latencyAddSampleIfNeeded("module-acquire-GIL",latency);
}
+ /* Set the eventloop start time. */
+ server.el_start = getMonotonicUs();
+ /* Set the eventloop command count at start. */
+ server.el_cmd_cnt_start = server.stat_numcommands;
}
/* Update the time cache. */
@@ -1955,7 +2005,7 @@ void createSharedObjects(void) {
shared.maxstring = sdsnew("maxstring");
}
-void initServerClientMemUsageBuckets() {
+void initServerClientMemUsageBuckets(void) {
if (server.client_mem_usage_buckets)
return;
server.client_mem_usage_buckets = zmalloc(sizeof(clientMemUsageBucket)*CLIENT_MEM_USAGE_BUCKETS);
@@ -1965,7 +2015,7 @@ void initServerClientMemUsageBuckets() {
}
}
-void freeServerClientMemUsageBuckets() {
+void freeServerClientMemUsageBuckets(void) {
if (!server.client_mem_usage_buckets)
return;
for (int j = 0; j < CLIENT_MEM_USAGE_BUCKETS; j++)
@@ -2018,6 +2068,7 @@ void initServerConfig(void) {
server.aof_selected_db = -1; /* Make sure the first time will not match */
server.aof_flush_postponed_start = 0;
server.aof_last_incr_size = 0;
+ server.aof_last_incr_fsync_offset = 0;
server.active_defrag_running = 0;
server.notify_keyspace_events = 0;
server.blocked_clients = 0;
@@ -2494,8 +2545,8 @@ void resetServerStats(void) {
atomicSet(server.stat_total_writes_processed, 0);
for (j = 0; j < STATS_METRIC_COUNT; j++) {
server.inst_metric[j].idx = 0;
- server.inst_metric[j].last_sample_time = mstime();
- server.inst_metric[j].last_sample_count = 0;
+ server.inst_metric[j].last_sample_base = 0;
+ server.inst_metric[j].last_sample_value = 0;
memset(server.inst_metric[j].samples,0,
sizeof(server.inst_metric[j].samples));
}
@@ -2512,6 +2563,8 @@ void resetServerStats(void) {
server.aof_delayed_fsync = 0;
server.stat_reply_buffer_shrinks = 0;
server.stat_reply_buffer_expands = 0;
+ memset(server.duration_stats, 0, sizeof(durationStats) * EL_DURATION_TYPE_NUM);
+ server.el_cmd_cnt_max = 0;
lazyfreeResetStats();
}
@@ -2717,7 +2770,7 @@ void initServer(void) {
initServerClientMemUsageBuckets();
}
-void initListeners() {
+void initListeners(void) {
/* Setup listeners from server config for TCP/TLS/Unix */
int conn_index;
connListener *listener;
@@ -2794,7 +2847,7 @@ void initListeners() {
* Specifically, creation of threads due to a race bug in ld.so, in which
* Thread Local Storage initialization collides with dlopen call.
* see: https://sourceware.org/bugzilla/show_bug.cgi?id=19329 */
-void InitServerLast() {
+void InitServerLast(void) {
bioInit();
initThreadedIO();
set_jemalloc_bg_thread(server.jemalloc_bg_thread);
@@ -2942,21 +2995,6 @@ void setImplicitACLCategories(struct redisCommand *c) {
c->acl_categories |= ACL_CATEGORY_SLOW;
}
-/* Recursively populate the args structure (setting num_args to the number of
- * subargs) and return the number of args. */
-int populateArgsStructure(struct redisCommandArg *args) {
- if (!args)
- return 0;
- int count = 0;
- while (args->name) {
- serverAssert(count < INT_MAX);
- args->num_args = populateArgsStructure(args->subargs);
- count++;
- args++;
- }
- return count;
-}
-
/* Recursively populate the command structure.
*
* On success, the function return C_OK. Otherwise C_ERR is returned and we won't
@@ -2974,28 +3012,10 @@ int populateCommandStructure(struct redisCommand *c) {
* set of flags. */
setImplicitACLCategories(c);
- /* Redis commands don't need more args than STATIC_KEY_SPECS_NUM (Number of keys
- * specs can be greater than STATIC_KEY_SPECS_NUM only for module commands) */
- c->key_specs = c->key_specs_static;
- c->key_specs_max = STATIC_KEY_SPECS_NUM;
-
/* We start with an unallocated histogram and only allocate memory when a command
* has been issued for the first time */
c->latency_histogram = NULL;
- for (int i = 0; i < STATIC_KEY_SPECS_NUM; i++) {
- if (c->key_specs[i].begin_search_type == KSPEC_BS_INVALID)
- break;
- c->key_specs_num++;
- }
-
- /* Count things so we don't have to use deferred reply in COMMAND reply. */
- while (c->history && c->history[c->num_history].since)
- c->num_history++;
- while (c->tips && c->tips[c->num_tips])
- c->num_tips++;
- c->num_args = populateArgsStructure(c->args);
-
/* Handle the legacy range spec and the "movablekeys" flag (must be done after populating all key specs). */
populateCommandLegacyRangeSpec(c);
@@ -3155,12 +3175,13 @@ struct redisCommand *lookupCommandBySdsLogic(dict *commands, sds s) {
sds *strings = sdssplitlen(s,sdslen(s),"|",1,&argc);
if (strings == NULL)
return NULL;
- if (argc > 2) {
+ if (argc < 1 || argc > 2) {
/* Currently we support just one level of subcommands */
sdsfreesplitres(strings,argc);
return NULL;
}
+ serverAssert(argc > 0); /* Avoid warning `-Wmaybe-uninitialized` in lookupCommandLogic() */
robj objects[argc];
robj *argv[argc];
for (j = 0; j < argc; j++) {
@@ -3336,7 +3357,7 @@ void updateCommandLatencyHistogram(struct hdr_histogram **latency_histogram, int
/* Handle the alsoPropagate() API to handle commands that want to propagate
* multiple separated commands. Note that alsoPropagate() is not affected
* by CLIENT_PREVENT_PROP flag. */
-static void propagatePendingCommands() {
+static void propagatePendingCommands(void) {
if (server.also_propagate.numops == 0)
return;
@@ -3392,7 +3413,7 @@ static void propagatePendingCommands() {
* currently with respect to replication and post jobs, but in the future there might
* be other considerations. So we basically want the `postUnitOperations` to trigger
* after the entire chain finished. */
-void postExecutionUnitOperations() {
+void postExecutionUnitOperations(void) {
if (server.execution_nesting)
return;
@@ -3563,6 +3584,8 @@ void call(client *c, int flags) {
char *latency_event = (real_cmd->flags & CMD_FAST) ?
"fast-command" : "command";
latencyAddSampleIfNeeded(latency_event,duration/1000);
+ if (server.execution_nesting == 0)
+ durationAddSample(EL_DURATION_TYPE_CMD, duration);
}
/* Log the command into the Slow log if needed.
@@ -3595,6 +3618,12 @@ void call(client *c, int flags) {
updateCommandLatencyHistogram(&(real_cmd->latency_histogram), c->duration*1000);
}
+ /* The duration needs to be reset after each call except for a blocked command,
+ * which is expected to record and reset the duration after unblocking. */
+ if (!(c->flags & CLIENT_BLOCKED)) {
+ c->duration = 0;
+ }
+
/* Propagate the command into the AOF and replication link.
* We never propagate EXEC explicitly, it will be implicitly
* propagated if needed (see propagatePendingCommands).
@@ -4356,6 +4385,8 @@ int finishShutdown(void) {
serverLog(LL_WARNING, "Writing initial AOF. Exit anyway.");
} else {
serverLog(LL_WARNING, "Writing initial AOF, can't exit.");
+ if (server.supervised_mode == SUPERVISED_SYSTEMD)
+ redisCommunicateSystemd("STATUS=Writing initial AOF, can't exit.\n");
goto error;
}
}
@@ -4853,28 +4884,6 @@ void addReplyCommandSubCommands(client *c, struct redisCommand *cmd, void (*repl
dictReleaseIterator(di);
}
-/* Must match redisCommandGroup */
-const char *COMMAND_GROUP_STR[] = {
- "generic",
- "string",
- "list",
- "set",
- "sorted-set",
- "hash",
- "pubsub",
- "transactions",
- "connection",
- "server",
- "scripting",
- "hyperloglog",
- "cluster",
- "sentinel",
- "geo",
- "stream",
- "bitmap",
- "module"
-};
-
/* Output the representation of a Redis command. Used by the COMMAND command and COMMAND INFO. */
void addReplyCommandInfo(client *c, struct redisCommand *cmd) {
if (!cmd) {
@@ -4933,7 +4942,7 @@ void addReplyCommandDocs(client *c, struct redisCommand *cmd) {
/* Always have the group, for module commands the group is always "module". */
addReplyBulkCString(c, "group");
- addReplyBulkCString(c, COMMAND_GROUP_STR[cmd->group]);
+ addReplyBulkCString(c, commandGroupStr(cmd->group));
if (cmd->complexity) {
addReplyBulkCString(c, "complexity");
@@ -5933,7 +5942,12 @@ sds genRedisInfoString(dict *section_dict, int all_sections, int everything) {
"io_threaded_reads_processed:%lld\r\n"
"io_threaded_writes_processed:%lld\r\n"
"reply_buffer_shrinks:%lld\r\n"
- "reply_buffer_expands:%lld\r\n",
+ "reply_buffer_expands:%lld\r\n"
+ "eventloop_cycles:%llu\r\n"
+ "eventloop_duration_sum:%llu\r\n"
+ "eventloop_duration_cmd_sum:%llu\r\n"
+ "instantaneous_eventloop_cycles_per_sec:%llu\r\n"
+ "instantaneous_eventloop_duration_usec:%llu\r\n",
server.stat_numconnections,
server.stat_numcommands,
getInstantaneousMetric(STATS_METRIC_COMMAND),
@@ -5983,7 +5997,12 @@ sds genRedisInfoString(dict *section_dict, int all_sections, int everything) {
server.stat_io_reads_processed,
server.stat_io_writes_processed,
server.stat_reply_buffer_shrinks,
- server.stat_reply_buffer_expands);
+ server.stat_reply_buffer_expands,
+ server.duration_stats[EL_DURATION_TYPE_EL].cnt,
+ server.duration_stats[EL_DURATION_TYPE_EL].sum,
+ server.duration_stats[EL_DURATION_TYPE_CMD].sum,
+ getInstantaneousMetric(STATS_METRIC_EL_CYCLE),
+ getInstantaneousMetric(STATS_METRIC_EL_DURATION));
info = genRedisInfoStringACLStats(info);
}
@@ -6233,6 +6252,21 @@ sds genRedisInfoString(dict *section_dict, int all_sections, int everything) {
0, /* not a crash report */
sections);
}
+
+ if (dictFind(section_dict, "debug") != NULL) {
+ if (sections++) info = sdscat(info,"\r\n");
+ info = sdscatprintf(info,
+ "# Debug\r\n"
+ "eventloop_duration_aof_sum:%llu\r\n"
+ "eventloop_duration_cron_sum:%llu\r\n"
+ "eventloop_duration_max:%llu\r\n"
+ "eventloop_cmd_per_cycle_max:%lld\r\n",
+ server.duration_stats[EL_DURATION_TYPE_AOF].sum,
+ server.duration_stats[EL_DURATION_TYPE_CRON].sum,
+ server.duration_stats[EL_DURATION_TYPE_EL].max,
+ server.el_cmd_cnt_max);
+ }
+
return info;
}
@@ -6551,7 +6585,7 @@ void setupChildSignalHandlers(void) {
* of the parent process, e.g. fd(socket or flock) etc.
* should close the resources not used by the child process, so that if the
* parent restarts it can bind/lock despite the child possibly still running. */
-void closeChildUnusedResourceAfterFork() {
+void closeChildUnusedResourceAfterFork(void) {
closeListeningSockets(0);
if (server.cluster_enabled && server.cluster_config_file_lock_fd != -1)
close(server.cluster_config_file_lock_fd); /* don't care if this fails */
@@ -6746,6 +6780,7 @@ void loadDataFromDisk(void) {
serverLog(LL_NOTICE, "DB loaded from append only file: %.3f seconds", (float)(ustime()-start)/1000000);
} else {
rdbSaveInfo rsi = RDB_SAVE_INFO_INIT;
+ int rsi_is_valid = 0;
errno = 0; /* Prevent a stale value from affecting error checking */
int rdb_flags = RDBFLAGS_NONE;
if (iAmMaster()) {
@@ -6767,6 +6802,7 @@ void loadDataFromDisk(void) {
* information in function rdbPopulateSaveInfo. */
rsi.repl_stream_db != -1)
{
+ rsi_is_valid = 1;
if (!iAmMaster()) {
memcpy(server.replid,rsi.repl_id,sizeof(server.replid));
server.master_repl_offset = rsi.repl_offset;
@@ -6800,7 +6836,7 @@ void loadDataFromDisk(void) {
* if RDB doesn't have replication info or there is no rdb, it is not
* possible to support partial resynchronization, to avoid extra memory
* of replication backlog, we drop it. */
- if (server.master_repl_offset == 0 && server.repl_backlog)
+ if (!rsi_is_valid && server.repl_backlog)
freeReplicationBacklog();
}
}
@@ -7224,6 +7260,34 @@ int main(int argc, char **argv) {
sdsfree(options);
}
if (server.sentinel_mode) sentinelCheckConfigFile();
+
+ /* Do system checks */
+#ifdef __linux__
+ linuxMemoryWarnings();
+ sds err_msg = NULL;
+ if (checkXenClocksource(&err_msg) < 0) {
+ serverLog(LL_WARNING, "WARNING %s", err_msg);
+ sdsfree(err_msg);
+ }
+#if defined (__arm64__)
+ int ret;
+ if ((ret = checkLinuxMadvFreeForkBug(&err_msg)) <= 0) {
+ if (ret < 0) {
+ serverLog(LL_WARNING, "WARNING %s", err_msg);
+ sdsfree(err_msg);
+ } else
+ serverLog(LL_WARNING, "Failed to test the kernel for a bug that could lead to data corruption during background save. "
+ "Your system could be affected, please report this error.");
+ if (!checkIgnoreWarning("ARM64-COW-BUG")) {
+ serverLog(LL_WARNING,"Redis will now exit to prevent data corruption. "
+ "Note that it is possible to suppress this warning by setting the following config: ignore-warnings ARM64-COW-BUG");
+ exit(1);
+ }
+ }
+#endif /* __arm64__ */
+#endif /* __linux__ */
+
+ /* Daemonize if needed */
server.supervised = redisIsSupervised(server.supervised_mode);
int background = server.daemonize && !server.supervised;
if (background) daemonize();
@@ -7265,30 +7329,6 @@ int main(int argc, char **argv) {
if (!server.sentinel_mode) {
/* Things not needed when running in Sentinel mode. */
serverLog(LL_NOTICE,"Server initialized");
- #ifdef __linux__
- linuxMemoryWarnings();
- sds err_msg = NULL;
- if (checkXenClocksource(&err_msg) < 0) {
- serverLog(LL_WARNING, "WARNING %s", err_msg);
- sdsfree(err_msg);
- }
- #if defined (__arm64__)
- int ret;
- if ((ret = checkLinuxMadvFreeForkBug(&err_msg)) <= 0) {
- if (ret < 0) {
- serverLog(LL_WARNING, "WARNING %s", err_msg);
- sdsfree(err_msg);
- } else
- serverLog(LL_WARNING, "Failed to test the kernel for a bug that could lead to data corruption during background save. "
- "Your system could be affected, please report this error.");
- if (!checkIgnoreWarning("ARM64-COW-BUG")) {
- serverLog(LL_WARNING,"Redis will now exit to prevent data corruption. "
- "Note that it is possible to suppress this warning by setting the following config: ignore-warnings ARM64-COW-BUG");
- exit(1);
- }
- }
- #endif /* __arm64__ */
- #endif /* __linux__ */
aofLoadManifestFromDisk();
loadDataFromDisk();
aofOpenIfNeededOnServerStart();
diff --git a/src/server.h b/src/server.h
index 1a68071f1..5d37cd823 100644
--- a/src/server.h
+++ b/src/server.h
@@ -35,6 +35,7 @@
#include "solarisfixes.h"
#include "rio.h"
#include "atomicvar.h"
+#include "commands.h"
#include <assert.h>
#include <stdio.h>
@@ -52,7 +53,6 @@
#include <sys/socket.h>
#include <lua.h>
#include <signal.h>
-#include "hdr_histogram.h"
#ifdef HAVE_LIBSYSTEMD
#include <systemd/sd-daemon.h>
@@ -91,6 +91,8 @@ typedef struct redisObject robj;
#include "endianconv.h"
#include "crc64.h"
+struct hdr_histogram;
+
/* helpers */
#define numElements(x) (sizeof(x)/sizeof((x)[0]))
@@ -166,7 +168,9 @@ typedef struct redisObject robj;
#define STATS_METRIC_NET_OUTPUT 2 /* Bytes written to network. */
#define STATS_METRIC_NET_INPUT_REPLICATION 3 /* Bytes read to network during replication. */
#define STATS_METRIC_NET_OUTPUT_REPLICATION 4 /* Bytes written to network during replication. */
-#define STATS_METRIC_COUNT 5
+#define STATS_METRIC_EL_CYCLE 5 /* Number of eventloop cycled. */
+#define STATS_METRIC_EL_DURATION 6 /* Eventloop duration. */
+#define STATS_METRIC_COUNT 7
/* Protocol and I/O related defines */
#define PROTO_IOBUF_LEN (1024*16) /* Generic I/O buffer size */
@@ -1370,9 +1374,8 @@ typedef struct redisOp {
/* Defines an array of Redis operations. There is an API to add to this
* structure in an easy way.
*
- * redisOpArrayInit();
- * redisOpArrayAppend();
- * redisOpArrayFree();
+ * int redisOpArrayAppend(redisOpArray *oa, int dbid, robj **argv, int argc, int target);
+ * void redisOpArrayFree(redisOpArray *oa);
*/
typedef struct redisOpArray {
redisOp *ops;
@@ -1693,13 +1696,22 @@ struct redisServer {
/* The following two are used to track instantaneous metrics, like
* number of operations per second, network traffic. */
struct {
- long long last_sample_time; /* Timestamp of last sample in ms */
- long long last_sample_count;/* Count in last sample */
+ long long last_sample_base; /* The divisor of last sample window */
+ long long last_sample_value; /* The dividend of last sample window */
long long samples[STATS_METRIC_SAMPLES];
int idx;
} inst_metric[STATS_METRIC_COUNT];
long long stat_reply_buffer_shrinks; /* Total number of output buffer shrinks */
long long stat_reply_buffer_expands; /* Total number of output buffer expands */
+ monotime el_start;
+ /* The following two are used to record the max number of commands executed in one eventloop.
+ * Note that commands in transactions are also counted. */
+ long long el_cmd_cnt_start;
+ long long el_cmd_cnt_max;
+ /* The sum of active-expire, active-defrag and all other tasks done by cron and beforeSleep,
+ but excluding read, write and AOF, which are counted by other sets of metrics. */
+ monotime el_cron_duration;
+ durationStats duration_stats[EL_DURATION_TYPE_NUM];
/* Configuration */
int verbosity; /* Loglevel in redis.conf */
@@ -1742,7 +1754,8 @@ struct redisServer {
off_t aof_rewrite_base_size; /* AOF size on latest startup or rewrite. */
off_t aof_current_size; /* AOF current size (Including BASE + INCRs). */
off_t aof_last_incr_size; /* The size of the latest incr AOF. */
- off_t aof_fsync_offset; /* AOF offset which is already synced to disk. */
+ off_t aof_last_incr_fsync_offset; /* AOF offset which is already requested to be synced to disk.
+ * Compare with the aof_last_incr_size. */
int aof_flush_sleep; /* Micros to sleep before flush. (used by tests) */
int aof_rewrite_scheduled; /* Rewrite once BGSAVE terminates. */
sds aof_buf; /* AOF buffer, written before entering the event loop */
@@ -2134,43 +2147,6 @@ typedef struct {
} fk;
} keySpec;
-/* Number of static key specs */
-#define STATIC_KEY_SPECS_NUM 4
-
-/* Must be synced with ARG_TYPE_STR and generate-command-code.py */
-typedef enum {
- ARG_TYPE_STRING,
- ARG_TYPE_INTEGER,
- ARG_TYPE_DOUBLE,
- ARG_TYPE_KEY, /* A string, but represents a keyname */
- ARG_TYPE_PATTERN,
- ARG_TYPE_UNIX_TIME,
- ARG_TYPE_PURE_TOKEN,
- ARG_TYPE_ONEOF, /* Has subargs */
- ARG_TYPE_BLOCK /* Has subargs */
-} redisCommandArgType;
-
-#define CMD_ARG_NONE (0)
-#define CMD_ARG_OPTIONAL (1<<0)
-#define CMD_ARG_MULTIPLE (1<<1)
-#define CMD_ARG_MULTIPLE_TOKEN (1<<2)
-
-/* WARNING! This struct must match RedisModuleCommandArg */
-typedef struct redisCommandArg {
- const char *name;
- redisCommandArgType type;
- int key_spec_index;
- const char *token;
- const char *summary;
- const char *since;
- int flags;
- const char *deprecated_since;
- struct redisCommandArg *subargs;
- const char *display_text;
- /* runtime populated data */
- int num_args;
-} redisCommandArg;
-
#ifdef LOG_REQ_RES
/* Must be synced with generate-command-code.py */
@@ -2340,15 +2316,19 @@ struct redisCommand {
const char *deprecated_since; /* In case the command is deprecated, when did it happen? */
redisCommandGroup group; /* Command group */
commandHistory *history; /* History of the command */
+ int num_history;
const char **tips; /* An array of strings that are meant to be tips for clients/proxies regarding this command */
+ int num_tips;
redisCommandProc *proc; /* Command implementation */
int arity; /* Number of arguments, it is possible to use -N to say >= N */
uint64_t flags; /* Command flags, see CMD_*. */
uint64_t acl_categories; /* ACl categories, see ACL_CATEGORY_*. */
- keySpec key_specs_static[STATIC_KEY_SPECS_NUM]; /* Key specs. See keySpec */
+ keySpec *key_specs;
+ int key_specs_num;
/* Use a function to determine keys arguments in a command line.
* Used for Redis Cluster redirect (may be NULL) */
redisGetKeysProc *getkeys_proc;
+ int num_args; /* Length of args array. */
/* Array of subcommands (may be NULL) */
struct redisCommand *subcommands;
/* Array of arguments (may be NULL) */
@@ -2367,16 +2347,10 @@ struct redisCommand {
bit set in the bitmap of allowed commands. */
sds fullname; /* A SDS string representing the command fullname. */
struct hdr_histogram* latency_histogram; /*points to the command latency command histogram (unit of time nanosecond) */
- keySpec *key_specs;
keySpec legacy_range_key_spec; /* The legacy (first,last,step) key spec is
* still maintained (if applicable) so that
* we can still support the reply format of
* COMMAND INFO and COMMAND GETKEYS */
- int num_args;
- int num_history;
- int num_tips;
- int key_specs_num;
- int key_specs_max;
dict *subcommands_dict; /* A dictionary that holds the subcommands, the key is the subcommand sds name
* (not the fullname), and the value is the redisCommand structure pointer. */
struct redisCommand *parent;
@@ -2483,7 +2457,6 @@ extern dict *modules;
/* Command metadata */
void populateCommandLegacyRangeSpec(struct redisCommand *c);
-int populateArgsStructure(struct redisCommandArg *args);
/* Modules */
void moduleInitModulesSystem(void);
@@ -2509,14 +2482,14 @@ void moduleAcquireGIL(void);
int moduleTryAcquireGIL(void);
void moduleReleaseGIL(void);
void moduleNotifyKeyspaceEvent(int type, const char *event, robj *key, int dbid);
-void firePostExecutionUnitJobs();
+void firePostExecutionUnitJobs(void);
void moduleCallCommandFilters(client *c);
-void modulePostExecutionUnitOperations();
+void modulePostExecutionUnitOperations(void);
void ModuleForkDoneHandler(int exitcode, int bysignal);
int TerminateModuleForkChild(int child_pid, int wait);
ssize_t rdbSaveModulesAux(rio *rdb, int when);
-int moduleAllDatatypesHandleErrors();
-int moduleAllModulesHandleReplAsyncLoad();
+int moduleAllDatatypesHandleErrors(void);
+int moduleAllModulesHandleReplAsyncLoad(void);
sds modulesCollectInfo(sds info, dict *sections_dict, int for_crash_report, int sections);
void moduleFireServerEvent(uint64_t eid, int subid, void *data);
void processModuleLoadingProgressEvent(int is_aof);
@@ -2591,6 +2564,7 @@ void setDeferredReplyBulkSds(client *c, void *node, sds s);
void addReplyErrorObject(client *c, robj *err);
void addReplyOrErrorObject(client *c, robj *reply);
void afterErrorReply(client *c, const char *s, size_t len, int flags);
+void addReplyErrorFormatInternal(client *c, int flags, const char *fmt, va_list ap);
void addReplyErrorSdsEx(client *c, sds err, int flags);
void addReplyErrorSds(client *c, sds err);
void addReplyErrorSdsSafe(client *c, sds err);
@@ -2643,11 +2617,11 @@ void unpauseActions(pause_purpose purpose);
uint32_t isPausedActions(uint32_t action_bitmask);
uint32_t isPausedActionsWithUpdate(uint32_t action_bitmask);
void updatePausedActions(void);
-void unblockPostponedClients();
+void unblockPostponedClients(void);
void processEventsWhileBlocked(void);
-void whileBlockedCron();
-void blockingOperationStarts();
-void blockingOperationEnds();
+void whileBlockedCron(void);
+void blockingOperationStarts(void);
+void blockingOperationEnds(void);
int handleClientsWithPendingWrites(void);
int handleClientsWithPendingWritesUsingThreads(void);
int handleClientsWithPendingReadsUsingThreads(void);
@@ -2813,7 +2787,7 @@ void replicationCron(void);
void replicationStartPendingFork(void);
void replicationHandleMasterDisconnection(void);
void replicationCacheMaster(client *c);
-void resizeReplicationBacklog();
+void resizeReplicationBacklog(void);
void replicationSetMaster(char *ip, int port);
void replicationUnsetMaster(void);
void refreshGoodSlavesCount(void);
@@ -2842,7 +2816,7 @@ void rdbPipeWriteHandlerConnRemoved(struct connection *conn);
void clearFailoverState(void);
void updateFailoverStatus(void);
void abortFailover(const char *err);
-const char *getFailoverStateString();
+const char *getFailoverStateString(void);
/* Generic persistence functions */
void startLoadingFile(size_t size, char* filename, int rdbflags);
@@ -2876,7 +2850,7 @@ void stopAppendOnly(void);
int startAppendOnly(void);
void backgroundRewriteDoneHandler(int exitcode, int bysignal);
void killAppendOnlyChild(void);
-void restartAOFAfterSYNC();
+void restartAOFAfterSYNC(void);
void aofLoadManifestFromDisk(void);
void aofOpenIfNeededOnServerStart(void);
void aofManifestFree(aofManifest *am);
@@ -2893,8 +2867,8 @@ void receiveChildInfo(void);
/* Fork helpers */
int redisFork(int purpose);
-int hasActiveChildProcess();
-void resetChildState();
+int hasActiveChildProcess(void);
+void resetChildState(void);
int isMutuallyExclusiveChildType(int type);
/* acl.c -- Authentication related prototypes. */
@@ -2948,13 +2922,13 @@ int ACLLoadConfiguredUsers(void);
robj *ACLDescribeUser(user *u);
void ACLLoadUsersAtStartup(void);
void addReplyCommandCategories(client *c, struct redisCommand *cmd);
-user *ACLCreateUnlinkedUser();
+user *ACLCreateUnlinkedUser(void);
void ACLFreeUserAndKillClients(user *u);
void addACLLogEntry(client *c, int reason, int context, int argpos, sds username, sds object);
sds getAclErrorMessage(int acl_res, user *user, struct redisCommand *cmd, sds errored_val, int verbose);
void ACLUpdateDefaultUserPassword(sds password);
sds genRedisInfoStringACLStats(sds info);
-void ACLRecomputeCommandBitsFromCommandRulesAllUsers();
+void ACLRecomputeCommandBitsFromCommandRulesAllUsers(void);
/* Sorted sets data type */
@@ -3026,7 +3000,7 @@ int zslLexValueLteMax(sds value, zlexrangespec *spec);
/* Core functions */
int getMaxmemoryState(size_t *total, size_t *logical, size_t *tofree, float *level);
-size_t freeMemoryGetNotCountedMemory();
+size_t freeMemoryGetNotCountedMemory(void);
int overMaxmemoryAfterAlloc(size_t moremem);
uint64_t getCommandFlags(client *c);
int processCommand(client *c);
@@ -3047,11 +3021,11 @@ struct redisCommand *lookupCommandByCString(const char *s);
struct redisCommand *lookupCommandOrOriginal(robj **argv, int argc);
int commandCheckExistence(client *c, sds *err);
int commandCheckArity(client *c, sds *err);
-void startCommandExecution();
+void startCommandExecution(void);
int incrCommandStatsOnError(struct redisCommand *cmd, int flags);
void call(client *c, int flags);
void alsoPropagate(int dbid, robj **argv, int argc, int target);
-void postExecutionUnitOperations();
+void postExecutionUnitOperations(void);
void redisOpArrayFree(redisOpArray *oa);
void forceCommandPropagation(client *c, int flags);
void preventCommandPropagation(client *c);
@@ -3083,7 +3057,7 @@ void incrementErrorCount(const char *fullerr, size_t namelen);
void closeListeningSockets(int unlink_unix_socket);
void updateCachedTime(int update_daylight_info);
void enterExecutionUnit(int update_cached_time, long long us);
-void exitExecutionUnit();
+void exitExecutionUnit(void);
void resetServerStats(void);
void activeDefragCycle(void);
unsigned int getLRUClock(void);
@@ -3106,7 +3080,7 @@ void dismissMemoryInChild(void);
int restartServer(int flags, mstime_t delay);
/* Set data type */
-robj *setTypeCreate(sds value);
+robj *setTypeCreate(sds value, size_t size_hint);
int setTypeAdd(robj *subject, sds value);
int setTypeAddAux(robj *set, char *str, size_t len, int64_t llval, int str_is_sds);
int setTypeRemove(robj *subject, sds value);
@@ -3157,8 +3131,8 @@ int pubsubUnsubscribeAllPatterns(client *c, int notify);
int pubsubPublishMessage(robj *channel, robj *message, int sharded);
int pubsubPublishMessageAndPropagateToCluster(robj *channel, robj *message, int sharded);
void addReplyPubsubMessage(client *c, robj *channel, robj *msg, robj *message_bulk);
-int serverPubsubSubscriptionCount();
-int serverPubsubShardSubscriptionCount();
+int serverPubsubSubscriptionCount(void);
+int serverPubsubShardSubscriptionCount(void);
size_t pubsubMemOverhead(client *c);
/* Keyspace events notification */
@@ -3209,15 +3183,15 @@ void loadServerConfig(char *filename, char config_from_stdin, char *options);
void appendServerSaveParams(time_t seconds, int changes);
void resetServerSaveParams(void);
struct rewriteConfigState; /* Forward declaration to export API. */
-void rewriteConfigRewriteLine(struct rewriteConfigState *state, const char *option, sds line, int force);
+int rewriteConfigRewriteLine(struct rewriteConfigState *state, const char *option, sds line, int force);
void rewriteConfigMarkAsProcessed(struct rewriteConfigState *state, const char *option);
int rewriteConfig(char *path, int force_write);
-void initConfigValues();
+void initConfigValues(void);
void removeConfig(sds name);
-sds getConfigDebugInfo();
+sds getConfigDebugInfo(void);
int allowProtectedAction(int config, client *c);
-void initServerClientMemUsageBuckets();
-void freeServerClientMemUsageBuckets();
+void initServerClientMemUsageBuckets(void);
+void freeServerClientMemUsageBuckets(void);
/* Module Configuration */
typedef struct ModuleConfig ModuleConfig;
@@ -3285,7 +3259,7 @@ robj *dbUnshareStringValue(redisDb *db, robj *key, robj *o);
long long emptyData(int dbnum, int flags, void(callback)(dict*));
long long emptyDbStructure(redisDb *dbarray, int dbnum, int async, void(callback)(dict*));
void flushAllDataAndResetRDB(int flags);
-long long dbTotalServerKeyCount();
+long long dbTotalServerKeyCount(void);
redisDb *initTempDb(void);
void discardTempDb(redisDb *tempDb, void(callback)(dict*));
@@ -3362,16 +3336,16 @@ sds luaCreateFunction(client *c, robj *body);
void luaLdbLineHook(lua_State *lua, lua_Debug *ar);
void freeLuaScriptsAsync(dict *lua_scripts);
void freeFunctionsAsync(functionsLibCtx *lib_ctx);
-int ldbIsEnabled();
+int ldbIsEnabled(void);
void ldbLog(sds entry);
void ldbLogRedisReply(char *reply);
void sha1hex(char *digest, char *script, size_t len);
-unsigned long evalMemory();
-dict* evalScriptsDict();
-unsigned long evalScriptsMemory();
+unsigned long evalMemory(void);
+dict* evalScriptsDict(void);
+unsigned long evalScriptsMemory(void);
uint64_t evalGetCommandFlags(client *c, uint64_t orig_flags);
uint64_t fcallGetCommandFlags(client *c, uint64_t orig_flags);
-int isInsideYieldingLongCommand();
+int isInsideYieldingLongCommand(void);
typedef struct luaScript {
uint64_t flags;
@@ -3727,7 +3701,7 @@ dict *genInfoSectionDict(robj **argv, int argc, char **defaults, int *out_all, i
void releaseInfoSectionDict(dict *sec);
sds genRedisInfoString(dict *section_dict, int all_sections, int everything);
sds genModulesInfoString(sds info);
-void applyWatchdogPeriod();
+void applyWatchdogPeriod(void);
void watchdogScheduleSignal(int period);
void serverLogHexDump(int level, char *descr, void *value, size_t len);
int memtest_preserving_test(unsigned long *m, size_t bytes, int passes);
diff --git a/src/sha1.h b/src/sha1.h
index 167d0390c..a6cb6e885 100644
--- a/src/sha1.h
+++ b/src/sha1.h
@@ -15,7 +15,10 @@ typedef struct {
void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]);
void SHA1Init(SHA1_CTX* context);
-void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len);
+/* 'noinline' attribute is intended to prevent the `-Wstringop-overread` warning
+ * when using gcc-12 later with LTO enabled. It may be removed once the
+ * bug[https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80922] is fixed. */
+__attribute__((noinline)) void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len);
void SHA1Final(unsigned char digest[20], SHA1_CTX* context);
#ifdef REDIS_TEST
diff --git a/src/slowlog.c b/src/slowlog.c
index 320f334a8..4c31917bb 100644
--- a/src/slowlog.c
+++ b/src/slowlog.c
@@ -162,9 +162,8 @@ NULL
} else if ((c->argc == 2 || c->argc == 3) &&
!strcasecmp(c->argv[1]->ptr,"get"))
{
- long count = 10, sent = 0;
+ long count = 10;
listIter li;
- void *totentries;
listNode *ln;
slowlogEntry *se;
@@ -181,11 +180,15 @@ NULL
}
}
- listRewind(server.slowlog,&li);
- totentries = addReplyDeferredLen(c);
- while(count-- && (ln = listNext(&li))) {
+ if (count > (long)listLength(server.slowlog)) {
+ count = listLength(server.slowlog);
+ }
+ addReplyArrayLen(c, count);
+ listRewind(server.slowlog, &li);
+ while (count--) {
int j;
+ ln = listNext(&li);
se = ln->value;
addReplyArrayLen(c,6);
addReplyLongLong(c,se->id);
@@ -196,9 +199,7 @@ NULL
addReplyBulk(c,se->argv[j]);
addReplyBulkCBuffer(c,se->peerid,sdslen(se->peerid));
addReplyBulkCBuffer(c,se->cname,sdslen(se->cname));
- sent++;
}
- setDeferredArrayLen(c,totentries,sent);
} else {
addReplySubcommandSyntaxError(c);
}
diff --git a/src/socket.c b/src/socket.c
index e7f6a5125..0cfb2e4ec 100644
--- a/src/socket.c
+++ b/src/socket.c
@@ -341,7 +341,7 @@ static int connSocketIsLocal(connection *conn) {
if (connSocketAddr(conn, cip, sizeof(cip) - 1, NULL, 1) == C_ERR)
return -1;
- return !strcmp(cip,"127.0.0.1") || !strcmp(cip,"::1");
+ return !strncmp(cip, "127.", 4) || !strcmp(cip, "::1");
}
static int connSocketListen(connListener *listener) {
@@ -464,7 +464,7 @@ int connRecvTimeout(connection *conn, long long ms) {
return anetRecvTimeout(NULL, conn->fd, ms);
}
-int RedisRegisterConnectionTypeSocket()
+int RedisRegisterConnectionTypeSocket(void)
{
return connTypeRegister(&CT_Socket);
}
diff --git a/src/t_hash.c b/src/t_hash.c
index 6005f4442..f7d5af649 100644
--- a/src/t_hash.c
+++ b/src/t_hash.c
@@ -43,6 +43,16 @@ void hashTypeTryConversion(robj *o, robj **argv, int start, int end) {
if (o->encoding != OBJ_ENCODING_LISTPACK) return;
+ /* We guess that most of the values in the input are unique, so
+ * if there are enough arguments we create a pre-sized hash, which
+ * might overallocate memory if their are duplicates. */
+ size_t new_fields = (end - start + 1) / 2;
+ if (new_fields > server.hash_max_listpack_entries) {
+ hashTypeConvert(o, OBJ_ENCODING_HT);
+ dictExpand(o->ptr, new_fields);
+ return;
+ }
+
for (i = start; i <= end; i++) {
if (!sdsEncodedObject(argv[i]))
continue;
diff --git a/src/t_list.c b/src/t_list.c
index 98c013966..6e4f629dc 100644
--- a/src/t_list.c
+++ b/src/t_list.c
@@ -971,7 +971,7 @@ void lposCommand(client *c) {
if (!strcasecmp(opt,"RANK") && moreargs) {
j++;
- if (getLongFromObjectOrReply(c, c->argv[j], &rank, NULL) != C_OK)
+ if (getRangeLongFromObjectOrReply(c, c->argv[j], -LONG_MAX, LONG_MAX, &rank, NULL) != C_OK)
return;
if (rank == 0) {
addReplyError(c,"RANK can't be zero: use 1 to start from "
diff --git a/src/t_set.c b/src/t_set.c
index ba32ae51c..56ad95a53 100644
--- a/src/t_set.c
+++ b/src/t_set.c
@@ -39,11 +39,31 @@ void sunionDiffGenericCommand(client *c, robj **setkeys, int setnum,
/* Factory method to return a set that *can* hold "value". When the object has
* an integer-encodable value, an intset will be returned. Otherwise a regular
- * hash table. */
-robj *setTypeCreate(sds value) {
- if (isSdsRepresentableAsLongLong(value,NULL) == C_OK)
+ * hash table.
+ *
+ * The size hint indicates approximately how many items will be added which is
+ * used to determine the initial representation. */
+robj *setTypeCreate(sds value, size_t size_hint) {
+ if (isSdsRepresentableAsLongLong(value,NULL) == C_OK && size_hint < server.set_max_intset_entries)
return createIntsetObject();
- return createSetListpackObject();
+ if (size_hint < server.set_max_listpack_entries)
+ return createSetListpackObject();
+
+ /* We may oversize the set by using the hint if the hint is not accurate,
+ * but we will assume this is accpetable to maximize performance. */
+ robj *o = createSetObject();
+ dictExpand(o->ptr, size_hint);
+ return o;
+}
+
+/* Check if the existing set should be converted to another encoding based off the
+ * the size hint. */
+void setTypeMaybeConvert(robj *set, size_t size_hint) {
+ if ((set->encoding == OBJ_ENCODING_LISTPACK && size_hint >= server.set_max_listpack_entries)
+ || (set->encoding == OBJ_ENCODING_INTSET && size_hint >= server.set_max_intset_entries))
+ {
+ setTypeConvertAndExpand(set, OBJ_ENCODING_HT, size_hint, 1);
+ }
}
/* Return the maximum number of entries to store in an intset. */
@@ -590,8 +610,10 @@ void saddCommand(client *c) {
if (checkType(c,set,OBJ_SET)) return;
if (set == NULL) {
- set = setTypeCreate(c->argv[2]->ptr);
+ set = setTypeCreate(c->argv[2]->ptr, c->argc - 2);
dbAdd(c->db,c->argv[1],set);
+ } else {
+ setTypeMaybeConvert(set, c->argc - 2);
}
for (j = 2; j < c->argc; j++) {
@@ -672,7 +694,7 @@ void smoveCommand(client *c) {
/* Create the destination set when it doesn't exist */
if (!dstset) {
- dstset = setTypeCreate(ele->ptr);
+ dstset = setTypeCreate(ele->ptr, 1);
dbAdd(c->db,c->argv[2],dstset);
}
diff --git a/src/t_zset.c b/src/t_zset.c
index c78969caf..86d241883 100644
--- a/src/t_zset.c
+++ b/src/t_zset.c
@@ -3563,7 +3563,7 @@ void zrevrangebylexCommand(client *c) {
/**
* This function handles ZRANGE and ZRANGESTORE, and also the deprecated
- * Z[REV]RANGE[BYPOS|BYLEX] commands.
+ * Z[REV]RANGE[BYSCORE|BYLEX] commands.
*
* The simple ZRANGE and ZRANGESTORE can take _AUTO in rangetype and direction,
* other command pass explicit value.
diff --git a/src/tls.c b/src/tls.c
index 6e8c8531d..94cba63f1 100644
--- a/src/tls.c
+++ b/src/tls.c
@@ -1062,13 +1062,13 @@ static const char *connTLSGetType(connection *conn_) {
return CONN_TYPE_TLS;
}
-static int tlsHasPendingData() {
+static int tlsHasPendingData(void) {
if (!pending_list)
return 0;
return listLength(pending_list) > 0;
}
-static int tlsProcessPendingData() {
+static int tlsProcessPendingData(void) {
listIter li;
listNode *ln;
@@ -1151,13 +1151,13 @@ static ConnectionType CT_TLS = {
.get_peer_cert = connTLSGetPeerCert,
};
-int RedisRegisterConnectionTypeTLS() {
+int RedisRegisterConnectionTypeTLS(void) {
return connTypeRegister(&CT_TLS);
}
#else /* USE_OPENSSL */
-int RedisRegisterConnectionTypeTLS() {
+int RedisRegisterConnectionTypeTLS(void) {
serverLog(LL_VERBOSE, "Connection type %s not builtin", CONN_TYPE_TLS);
return C_ERR;
}
diff --git a/src/unix.c b/src/unix.c
index d85e200d3..73e56d4a0 100644
--- a/src/unix.c
+++ b/src/unix.c
@@ -200,7 +200,7 @@ static ConnectionType CT_Unix = {
.process_pending_data = NULL,
};
-int RedisRegisterConnectionTypeUnix()
+int RedisRegisterConnectionTypeUnix(void)
{
return connTypeRegister(&CT_Unix);
}
diff --git a/src/version.h b/src/version.h
index 4e7bd469d..ac47de2b1 100644
--- a/src/version.h
+++ b/src/version.h
@@ -1,2 +1,2 @@
-#define REDIS_VERSION "7.1.240"
-#define REDIS_VERSION_NUM 0x000701f0
+#define REDIS_VERSION "7.1.241"
+#define REDIS_VERSION_NUM 0x000701f1
diff --git a/src/ziplist.c b/src/ziplist.c
index c3aa65633..c891625a9 100644
--- a/src/ziplist.c
+++ b/src/ziplist.c
@@ -1694,7 +1694,7 @@ unsigned int ziplistRandomPairsUnique(unsigned char *zl, unsigned int count, zip
#define debug(f, ...) { if (DEBUG) printf(f, __VA_ARGS__); }
-static unsigned char *createList() {
+static unsigned char *createList(void) {
unsigned char *zl = ziplistNew();
zl = ziplistPush(zl, (unsigned char*)"foo", 3, ZIPLIST_TAIL);
zl = ziplistPush(zl, (unsigned char*)"quux", 4, ZIPLIST_TAIL);
@@ -1703,7 +1703,7 @@ static unsigned char *createList() {
return zl;
}
-static unsigned char *createIntList() {
+static unsigned char *createIntList(void) {
unsigned char *zl = ziplistNew();
char buf[32];
diff --git a/src/zmalloc.c b/src/zmalloc.c
index e7b426277..bbfa38651 100644
--- a/src/zmalloc.c
+++ b/src/zmalloc.c
@@ -102,9 +102,16 @@ static void zmalloc_default_oom(size_t size) {
static void (*zmalloc_oom_handler)(size_t) = zmalloc_default_oom;
+#ifdef HAVE_MALLOC_SIZE
+void *extend_to_usable(void *ptr, size_t size) {
+ UNUSED(size);
+ return ptr;
+}
+#endif
+
/* Try allocating memory, and return NULL if failed.
* '*usable' is set to the usable size if non NULL. */
-void *ztrymalloc_usable(size_t size, size_t *usable) {
+static inline void *ztrymalloc_usable_internal(size_t size, size_t *usable) {
/* Possible overflow, return NULL, so that the caller can panic or handle a failed allocation. */
if (size >= SIZE_MAX/2) return NULL;
void *ptr = malloc(MALLOC_MIN_SIZE(size)+PREFIX_SIZE);
@@ -123,24 +130,39 @@ void *ztrymalloc_usable(size_t size, size_t *usable) {
#endif
}
+void *ztrymalloc_usable(size_t size, size_t *usable) {
+ size_t usable_size = 0;
+ void *ptr = ztrymalloc_usable_internal(size, &usable_size);
+#ifdef HAVE_MALLOC_SIZE
+ ptr = extend_to_usable(ptr, usable_size);
+#endif
+ if (usable) *usable = usable_size;
+ return ptr;
+}
+
/* Allocate memory or panic */
void *zmalloc(size_t size) {
- void *ptr = ztrymalloc_usable(size, NULL);
+ void *ptr = ztrymalloc_usable_internal(size, NULL);
if (!ptr) zmalloc_oom_handler(size);
return ptr;
}
/* Try allocating memory, and return NULL if failed. */
void *ztrymalloc(size_t size) {
- void *ptr = ztrymalloc_usable(size, NULL);
+ void *ptr = ztrymalloc_usable_internal(size, NULL);
return ptr;
}
/* Allocate memory or panic.
* '*usable' is set to the usable size if non NULL. */
void *zmalloc_usable(size_t size, size_t *usable) {
- void *ptr = ztrymalloc_usable(size, usable);
+ size_t usable_size = 0;
+ void *ptr = ztrymalloc_usable_internal(size, &usable_size);
if (!ptr) zmalloc_oom_handler(size);
+#ifdef HAVE_MALLOC_SIZE
+ ptr = extend_to_usable(ptr, usable_size);
+#endif
+ if (usable) *usable = usable_size;
return ptr;
}
@@ -165,7 +187,7 @@ void zfree_no_tcache(void *ptr) {
/* Try allocating memory and zero it, and return NULL if failed.
* '*usable' is set to the usable size if non NULL. */
-void *ztrycalloc_usable(size_t size, size_t *usable) {
+static inline void *ztrycalloc_usable_internal(size_t size, size_t *usable) {
/* Possible overflow, return NULL, so that the caller can panic or handle a failed allocation. */
if (size >= SIZE_MAX/2) return NULL;
void *ptr = calloc(1, MALLOC_MIN_SIZE(size)+PREFIX_SIZE);
@@ -184,6 +206,16 @@ void *ztrycalloc_usable(size_t size, size_t *usable) {
#endif
}
+void *ztrycalloc_usable(size_t size, size_t *usable) {
+ size_t usable_size = 0;
+ void *ptr = ztrycalloc_usable_internal(size, &usable_size);
+#ifdef HAVE_MALLOC_SIZE
+ ptr = extend_to_usable(ptr, usable_size);
+#endif
+ if (usable) *usable = usable_size;
+ return ptr;
+}
+
/* Allocate memory and zero it or panic.
* We need this wrapper to have a calloc compatible signature */
void *zcalloc_num(size_t num, size_t size) {
@@ -193,35 +225,40 @@ void *zcalloc_num(size_t num, size_t size) {
zmalloc_oom_handler(SIZE_MAX);
return NULL;
}
- void *ptr = ztrycalloc_usable(num*size, NULL);
+ void *ptr = ztrycalloc_usable_internal(num*size, NULL);
if (!ptr) zmalloc_oom_handler(num*size);
return ptr;
}
/* Allocate memory and zero it or panic */
void *zcalloc(size_t size) {
- void *ptr = ztrycalloc_usable(size, NULL);
+ void *ptr = ztrycalloc_usable_internal(size, NULL);
if (!ptr) zmalloc_oom_handler(size);
return ptr;
}
/* Try allocating memory, and return NULL if failed. */
void *ztrycalloc(size_t size) {
- void *ptr = ztrycalloc_usable(size, NULL);
+ void *ptr = ztrycalloc_usable_internal(size, NULL);
return ptr;
}
/* Allocate memory or panic.
* '*usable' is set to the usable size if non NULL. */
void *zcalloc_usable(size_t size, size_t *usable) {
- void *ptr = ztrycalloc_usable(size, usable);
+ size_t usable_size = 0;
+ void *ptr = ztrycalloc_usable_internal(size, &usable_size);
if (!ptr) zmalloc_oom_handler(size);
+#ifdef HAVE_MALLOC_SIZE
+ ptr = extend_to_usable(ptr, usable_size);
+#endif
+ if (usable) *usable = usable_size;
return ptr;
}
/* Try reallocating memory, and return NULL if failed.
* '*usable' is set to the usable size if non NULL. */
-void *ztryrealloc_usable(void *ptr, size_t size, size_t *usable) {
+static inline void *ztryrealloc_usable_internal(void *ptr, size_t size, size_t *usable) {
#ifndef HAVE_MALLOC_SIZE
void *realptr;
#endif
@@ -275,24 +312,39 @@ void *ztryrealloc_usable(void *ptr, size_t size, size_t *usable) {
#endif
}
+void *ztryrealloc_usable(void *ptr, size_t size, size_t *usable) {
+ size_t usable_size = 0;
+ ptr = ztryrealloc_usable_internal(ptr, size, &usable_size);
+#ifdef HAVE_MALLOC_SIZE
+ ptr = extend_to_usable(ptr, usable_size);
+#endif
+ if (usable) *usable = usable_size;
+ return ptr;
+}
+
/* Reallocate memory and zero it or panic */
void *zrealloc(void *ptr, size_t size) {
- ptr = ztryrealloc_usable(ptr, size, NULL);
+ ptr = ztryrealloc_usable_internal(ptr, size, NULL);
if (!ptr && size != 0) zmalloc_oom_handler(size);
return ptr;
}
/* Try Reallocating memory, and return NULL if failed. */
void *ztryrealloc(void *ptr, size_t size) {
- ptr = ztryrealloc_usable(ptr, size, NULL);
+ ptr = ztryrealloc_usable_internal(ptr, size, NULL);
return ptr;
}
/* Reallocate memory or panic.
* '*usable' is set to the usable size if non NULL. */
void *zrealloc_usable(void *ptr, size_t size, size_t *usable) {
- ptr = ztryrealloc_usable(ptr, size, usable);
+ size_t usable_size = 0;
+ ptr = ztryrealloc_usable(ptr, size, &usable_size);
if (!ptr && size != 0) zmalloc_oom_handler(size);
+#ifdef HAVE_MALLOC_SIZE
+ ptr = extend_to_usable(ptr, usable_size);
+#endif
+ if (usable) *usable = usable_size;
return ptr;
}
@@ -604,7 +656,7 @@ void set_jemalloc_bg_thread(int enable) {
je_mallctl("background_thread", NULL, 0, &val, 1);
}
-int jemalloc_purge() {
+int jemalloc_purge(void) {
/* return all unused (reserved) pages to the OS */
char tmp[32];
unsigned narenas = 0;
@@ -630,7 +682,7 @@ void set_jemalloc_bg_thread(int enable) {
((void)(enable));
}
-int jemalloc_purge() {
+int jemalloc_purge(void) {
return 0;
}
diff --git a/src/zmalloc.h b/src/zmalloc.h
index 8d4c980cc..1ff66a635 100644
--- a/src/zmalloc.h
+++ b/src/zmalloc.h
@@ -96,20 +96,23 @@
#define HAVE_DEFRAG
#endif
-__attribute__((malloc)) void *zmalloc(size_t size);
-__attribute__((malloc)) void *zcalloc(size_t size);
-__attribute__((malloc)) __attribute((alloc_size(1,2))) void *zcalloc_num(size_t num, size_t size);
-__attribute__((alloc_size(2))) void *zrealloc(void *ptr, size_t size);
-__attribute__((malloc)) void *ztrymalloc(size_t size);
-__attribute__((malloc)) void *ztrycalloc(size_t size);
-__attribute__((alloc_size(2))) void *ztryrealloc(void *ptr, size_t size);
+/* 'noinline' attribute is intended to prevent the `-Wstringop-overread` warning
+ * when using gcc-12 later with LTO enabled. It may be removed once the
+ * bug[https://gcc.gnu.org/bugzilla/show_bug.cgi?id=96503] is fixed. */
+__attribute__((malloc,alloc_size(1),noinline)) void *zmalloc(size_t size);
+__attribute__((malloc,alloc_size(1),noinline)) void *zcalloc(size_t size);
+__attribute__((malloc,alloc_size(1,2),noinline)) void *zcalloc_num(size_t num, size_t size);
+__attribute__((alloc_size(2),noinline)) void *zrealloc(void *ptr, size_t size);
+__attribute__((malloc,alloc_size(1),noinline)) void *ztrymalloc(size_t size);
+__attribute__((malloc,alloc_size(1),noinline)) void *ztrycalloc(size_t size);
+__attribute__((alloc_size(2),noinline)) void *ztryrealloc(void *ptr, size_t size);
void zfree(void *ptr);
-__attribute__((malloc)) __attribute__((alloc_size(1))) void *zmalloc_usable(size_t size, size_t *usable);
-__attribute__((malloc)) __attribute__((alloc_size(1))) void *zcalloc_usable(size_t size, size_t *usable);
-__attribute__((alloc_size(2))) void *zrealloc_usable(void *ptr, size_t size, size_t *usable);
-__attribute__((malloc)) __attribute__((alloc_size(1))) void *ztrymalloc_usable(size_t size, size_t *usable);
-__attribute__((malloc)) __attribute__((alloc_size(1))) void *ztrycalloc_usable(size_t size, size_t *usable);
-__attribute__((alloc_size(2))) void *ztryrealloc_usable(void *ptr, size_t size, size_t *usable);
+void *zmalloc_usable(size_t size, size_t *usable);
+void *zcalloc_usable(size_t size, size_t *usable);
+void *zrealloc_usable(void *ptr, size_t size, size_t *usable);
+void *ztrymalloc_usable(size_t size, size_t *usable);
+void *ztrycalloc_usable(size_t size, size_t *usable);
+void *ztryrealloc_usable(void *ptr, size_t size, size_t *usable);
void zfree_usable(void *ptr, size_t *usable);
__attribute__((malloc)) char *zstrdup(const char *s);
size_t zmalloc_used_memory(void);
@@ -117,7 +120,7 @@ void zmalloc_set_oom_handler(void (*oom_handler)(size_t));
size_t zmalloc_get_rss(void);
int zmalloc_get_allocator_info(size_t *allocated, size_t *active, size_t *resident);
void set_jemalloc_bg_thread(int enable);
-int jemalloc_purge();
+int jemalloc_purge(void);
size_t zmalloc_get_private_dirty(long pid);
size_t zmalloc_get_smap_bytes_by_field(char *field, long pid);
size_t zmalloc_get_memory_size(void);
@@ -133,7 +136,22 @@ __attribute__((malloc)) void *zmalloc_no_tcache(size_t size);
size_t zmalloc_size(void *ptr);
size_t zmalloc_usable_size(void *ptr);
#else
+/* If we use 'zmalloc_usable_size()' to obtain additional available memory size
+ * and manipulate it, we need to call 'extend_to_usable()' afterwards to ensure
+ * the compiler recognizes this extra memory. However, if we use the pointer
+ * obtained from z[*]_usable() family functions, there is no need for this step. */
#define zmalloc_usable_size(p) zmalloc_size(p)
+
+/* derived from https://github.com/systemd/systemd/pull/25688
+ * We use zmalloc_usable_size() everywhere to use memory blocks, but that is an abuse since the
+ * malloc_usable_size() isn't meant for this kind of use, it is for diagnostics only. That is also why the
+ * behavior is flaky when built with _FORTIFY_SOURCE, the compiler can sense that we reach outside
+ * the allocated block and SIGABRT.
+ * We use a dummy allocator function to tell the compiler that the new size of ptr is newsize.
+ * The implementation returns the pointer as is; the only reason for its existence is as a conduit for the
+ * alloc_size attribute. This cannot be a static inline because gcc then loses the attributes on the function.
+ * See: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=96503 */
+__attribute__((alloc_size(2),noinline)) void *extend_to_usable(void *ptr, size_t size);
#endif
int get_proc_stat_ll(int i, long long *res);
diff --git a/tests/assets/default.conf b/tests/assets/default.conf
index 4ae420790..de460cc08 100644
--- a/tests/assets/default.conf
+++ b/tests/assets/default.conf
@@ -13,8 +13,9 @@ databases 16
latency-monitor-threshold 1
repl-diskless-sync-delay 0
+# Turn off RDB by default (to speedup tests)
# Note the infrastructure in server.tcl uses a dict, we can't provide several save directives
-save 900 1
+save ''
rdbcompression yes
dbfilename dump.rdb
@@ -30,4 +31,7 @@ enable-protected-configs yes
enable-debug-command yes
enable-module-command yes
-propagation-error-behavior panic \ No newline at end of file
+propagation-error-behavior panic
+
+# Make sure shutdown doesn't fail if there's an initial AOFRW
+shutdown-on-sigterm force
diff --git a/tests/assets/test_cli_hint_suite.txt b/tests/assets/test_cli_hint_suite.txt
new file mode 100644
index 000000000..18c1fe07a
--- /dev/null
+++ b/tests/assets/test_cli_hint_suite.txt
@@ -0,0 +1,111 @@
+# Test suite for redis-cli command-line hinting mechanism.
+# Each test case consists of two strings: a (partial) input command line, and the expected hint string.
+
+# Command with one arg: GET key
+"GET " "key"
+"GET abc " ""
+
+# Command with two args: DECRBY key decrement
+"DECRBY xyz 2 " ""
+"DECRBY xyz " "decrement"
+"DECRBY " "key decrement"
+
+# Command with optional arg: LPOP key [count]
+"LPOP key " "[count]"
+"LPOP key 3 " ""
+
+# Command with optional token arg: XRANGE key start end [COUNT count]
+"XRANGE " "key start end [COUNT count]"
+"XRANGE k 4 2 " "[COUNT count]"
+"XRANGE k 4 2 COU" "[COUNT count]"
+"XRANGE k 4 2 COUNT" "[COUNT count]"
+"XRANGE k 4 2 COUNT " "count"
+
+# Command with optional token block arg: BITFIELD_RO key [GET encoding offset [GET encoding offset ...]]
+"BITFIELD_RO k " "[GET encoding offset [GET encoding offset ...]]"
+"BITFIELD_RO k GE" "[GET encoding offset [GET encoding offset ...]]"
+"BITFIELD_RO k GET" "[GET encoding offset [GET encoding offset ...]]"
+# TODO: The following hints end with an unbalanced "]" which shouldn't be there.
+"BITFIELD_RO k GET " "encoding offset [GET encoding offset ...]]"
+"BITFIELD_RO k GET xyz " "offset [GET encoding offset ...]]"
+"BITFIELD_RO k GET xyz 12 " "[GET encoding offset ...]]"
+"BITFIELD_RO k GET xyz 12 GET " "encoding offset [GET encoding offset ...]]"
+"BITFIELD_RO k GET enc1 12 GET enc2 " "offset [GET encoding offset ...]]"
+"BITFIELD_RO k GET enc1 12 GET enc2 34 " "[GET encoding offset ...]]"
+
+# Two-word command with multiple non-token block args: CONFIG SET parameter value [parameter value ...]
+"CONFIG SET param " "value [parameter value ...]"
+"CONFIG SET param val " "[parameter value ...]"
+"CONFIG SET param val parm2 val2 " "[parameter value ...]"
+
+# Command with nested optional args: ZRANDMEMBER key [count [WITHSCORES]]
+"ZRANDMEMBER k " "[count [WITHSCORES]]"
+"ZRANDMEMBER k 3 " "[WITHSCORES]"
+"ZRANDMEMBER k 3 WI" "[WITHSCORES]"
+"ZRANDMEMBER k 3 WITHSCORES " ""
+# Wrong data type: count must be an integer. Hinting fails.
+"ZRANDMEMBER k cnt " ""
+
+# Command ends with repeated arg: MGET key [key ...]
+"MGET " "key [key ...]"
+"MGET k " "[key ...]"
+"MGET k k " "[key ...]"
+
+# Optional args can be in any order: SCAN cursor [MATCH pattern] [COUNT count] [TYPE type]
+"SCAN 2 MATCH " "pattern [COUNT count] [TYPE type]"
+"SCAN 2 COUNT " "count [MATCH pattern] [TYPE type]"
+
+# One-of choices: BLMOVE source destination LEFT|RIGHT LEFT|RIGHT timeout
+"BLMOVE src dst LEFT " "LEFT|RIGHT timeout"
+
+# Optional args can be in any order: ZRANGE key min max [BYSCORE|BYLEX] [REV] [LIMIT offset count] [WITHSCORES]
+"ZRANGE k 1 2 " "[BYSCORE|BYLEX] [REV] [LIMIT offset count] [WITHSCORES]"
+"ZRANGE k 1 2 bylex " "[REV] [LIMIT offset count] [WITHSCORES]"
+"ZRANGE k 1 2 bylex rev " "[LIMIT offset count] [WITHSCORES]"
+"ZRANGE k 1 2 limit 2 4 " "[BYSCORE|BYLEX] [REV] [WITHSCORES]"
+"ZRANGE k 1 2 bylex rev limit 2 4 WITHSCORES " ""
+"ZRANGE k 1 2 rev " "[BYSCORE|BYLEX] [LIMIT offset count] [WITHSCORES]"
+"ZRANGE k 1 2 WITHSCORES " "[BYSCORE|BYLEX] [REV] [LIMIT offset count]"
+
+# Optional one-of args with parameters: SET key value [NX|XX] [GET] [EX seconds|PX milliseconds|EXAT unix-time-seconds|PXAT unix-time-milliseconds|KEEPTTL]
+"SET key value " "[NX|XX] [GET] [EX seconds|PX milliseconds|EXAT unix-time-seconds|PXAT unix-time-milliseconds|KEEPTTL]"
+"SET key value EX" "[NX|XX] [GET] [EX seconds|PX milliseconds|EXAT unix-time-seconds|PXAT unix-time-milliseconds|KEEPTTL]"
+"SET key value EX " "seconds [NX|XX] [GET]"
+"SET key value EX 23 " "[NX|XX] [GET]"
+"SET key value EXAT" "[NX|XX] [GET] [EX seconds|PX milliseconds|EXAT unix-time-seconds|PXAT unix-time-milliseconds|KEEPTTL]"
+"SET key value EXAT " "unix-time-seconds [NX|XX] [GET]"
+"SET key value PX" "[NX|XX] [GET] [EX seconds|PX milliseconds|EXAT unix-time-seconds|PXAT unix-time-milliseconds|KEEPTTL]"
+"SET key value PX " "milliseconds [NX|XX] [GET]"
+"SET key value PXAT" "[NX|XX] [GET] [EX seconds|PX milliseconds|EXAT unix-time-seconds|PXAT unix-time-milliseconds|KEEPTTL]"
+"SET key value PXAT " "unix-time-milliseconds [NX|XX] [GET]"
+"SET key value KEEPTTL " "[NX|XX] [GET]"
+"SET key value XX " "[GET] [EX seconds|PX milliseconds|EXAT unix-time-seconds|PXAT unix-time-milliseconds|KEEPTTL]"
+
+# If an input word can't be matched, stop hinting.
+"SET key value FOOBAR " ""
+# Incorrect type for EX 'seconds' parameter - stop hinting.
+"SET key value EX sec " ""
+
+# Reordering partially-matched optional argument: GEORADIUS key longitude latitude radius M|KM|FT|MI [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC|DESC] [STORE key|STOREDIST key]
+"GEORADIUS key " "longitude latitude radius M|KM|FT|MI [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC|DESC] [STORE key|STOREDIST key]"
+"GEORADIUS key 1 2 3 M " "[WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count [ANY]] [ASC|DESC] [STORE key|STOREDIST key]"
+"GEORADIUS key 1 2 3 M COUNT " "count [ANY] [WITHCOORD] [WITHDIST] [WITHHASH] [ASC|DESC] [STORE key|STOREDIST key]"
+"GEORADIUS key 1 2 3 M COUNT 12 " "[ANY] [WITHCOORD] [WITHDIST] [WITHHASH] [ASC|DESC] [STORE key|STOREDIST key]"
+"GEORADIUS key 1 2 3 M COUNT 12 " "[ANY] [WITHCOORD] [WITHDIST] [WITHHASH] [ASC|DESC] [STORE key|STOREDIST key]"
+"GEORADIUS key 1 -2.345 3 M COUNT 12 " "[ANY] [WITHCOORD] [WITHDIST] [WITHHASH] [ASC|DESC] [STORE key|STOREDIST key]"" ""
+# Wrong data type: latitude must be a double. Hinting fails.
+"GEORADIUS key 1 X " ""
+# Once the next optional argument is started, the [ANY] hint completing the COUNT argument disappears.
+"GEORADIUS key 1 2 3 M COUNT 12 ASC " "[WITHCOORD] [WITHDIST] [WITHHASH] [STORE key|STOREDIST key]"
+
+# Incorrect argument type for double-valued token parameter.
+"GEOSEARCH k FROMLONLAT " "longitude latitude BYRADIUS radius M|KM|FT|MI|BYBOX width height M|KM|FT|MI [ASC|DESC] [COUNT count [ANY]] [WITHCOORD] [WITHDIST] [WITHHASH]"
+"GEOSEARCH k FROMLONLAT 2.34 4.45 BYRADIUS badvalue " ""
+
+# Optional parameters followed by mandatory params: ZADD key [NX|XX] [GT|LT] [CH] [INCR] score member [score member ...]
+"ZADD key " "[NX|XX] [GT|LT] [CH] [INCR] score member [score member ...]"
+"ZADD key CH LT " "[NX|XX] [INCR] score member [score member ...]"
+"ZADD key 0 " "member [score member ...]"
+
+# Empty-valued token argument represented as a pair of double-quotes.
+"MIGRATE " "host port key|\"\" destination-db timeout [COPY] [REPLACE] [AUTH password|AUTH2 username password] [KEYS key [key ...]]"
diff --git a/tests/cluster/run.tcl b/tests/cluster/run.tcl
index c81d8f39d..86c5f589b 100644
--- a/tests/cluster/run.tcl
+++ b/tests/cluster/run.tcl
@@ -17,6 +17,7 @@ proc main {} {
"appendonly yes"
"enable-protected-configs yes"
"enable-debug-command yes"
+ "save ''"
}
run_tests
cleanup
diff --git a/tests/integration/aof-multi-part.tcl b/tests/integration/aof-multi-part.tcl
index 74f6b4949..1d41a8a83 100644
--- a/tests/integration/aof-multi-part.tcl
+++ b/tests/integration/aof-multi-part.tcl
@@ -755,7 +755,7 @@ tags {"external:skip"} {
# writing pressure, etc.
- start_server {tags {"Multi Part AOF"} overrides {aof-use-rdb-preamble {yes} appendonly {no}}} {
+ start_server {tags {"Multi Part AOF"} overrides {aof-use-rdb-preamble {yes} appendonly {no} save {}}} {
set dir [get_redis_dir]
set aof_basename "appendonly.aof"
set aof_dirname "appendonlydir"
@@ -1173,7 +1173,7 @@ tags {"external:skip"} {
assert {$d1 eq $d2}
}
- start_server {overrides {aof-use-rdb-preamble {yes} appendonly {no}}} {
+ start_server {overrides {aof-use-rdb-preamble {yes} appendonly {no} save {}}} {
set dir [get_redis_dir]
set aof_basename "appendonly.aof"
set aof_dirname "appendonlydir"
diff --git a/tests/integration/block-repl.tcl b/tests/integration/block-repl.tcl
index 3f3a86ed8..52b4a53ea 100644
--- a/tests/integration/block-repl.tcl
+++ b/tests/integration/block-repl.tcl
@@ -12,7 +12,7 @@ proc stop_bg_block_op {handle} {
}
start_server {tags {"repl" "external:skip"}} {
- start_server {} {
+ start_server {overrides {save {}}} {
set master [srv -1 client]
set master_host [srv -1 host]
set master_port [srv -1 port]
diff --git a/tests/integration/failover.tcl b/tests/integration/failover.tcl
index 2cd944851..21fa3d281 100644
--- a/tests/integration/failover.tcl
+++ b/tests/integration/failover.tcl
@@ -1,6 +1,6 @@
-start_server {tags {"failover external:skip"}} {
-start_server {} {
-start_server {} {
+start_server {tags {"failover external:skip"} overrides {save {}}} {
+start_server {overrides {save {}}} {
+start_server {overrides {save {}}} {
set node_0 [srv 0 client]
set node_0_host [srv 0 host]
set node_0_port [srv 0 port]
@@ -66,13 +66,13 @@ start_server {} {
# Generate a delta between primary and replica
set load_handler [start_write_load $node_0_host $node_0_port 5]
- exec kill -SIGSTOP [srv -1 pid]
+ pause_process [srv -1 pid]
wait_for_condition 50 100 {
[s 0 total_commands_processed] > 100
} else {
fail "Node 0 did not accept writes"
}
- exec kill -SIGCONT [srv -1 pid]
+ resume_process [srv -1 pid]
# Execute the failover
$node_0 failover to $node_1_host $node_1_port
@@ -108,7 +108,7 @@ start_server {} {
wait_for_ofs_sync $node_1 $node_2
# We stop node 0 to and make sure node 2 is selected
- exec kill -SIGSTOP $node_0_pid
+ pause_process $node_0_pid
$node_1 set CASE 1
$node_1 FAILOVER
@@ -118,7 +118,7 @@ start_server {} {
} else {
fail "Failover from node 1 to node 2 did not finish"
}
- exec kill -SIGCONT $node_0_pid
+ resume_process $node_0_pid
$node_0 replicaof $node_2_host $node_2_port
wait_for_sync $node_0
@@ -138,7 +138,7 @@ start_server {} {
set initial_psyncs [s 0 sync_partial_ok]
set initial_syncs [s 0 sync_full]
- exec kill -SIGSTOP $node_0_pid
+ pause_process $node_0_pid
# node 0 will never acknowledge this write
$node_2 set case 2
$node_2 failover to $node_0_host $node_0_port TIMEOUT 100 FORCE
@@ -155,7 +155,7 @@ start_server {} {
assert_match *slave* [$node_1 role]
assert_match *slave* [$node_2 role]
- exec kill -SIGCONT $node_0_pid
+ resume_process $node_0_pid
# Wait for failover to end
wait_for_condition 50 100 {
@@ -186,7 +186,7 @@ start_server {} {
set initial_syncs [s 0 sync_full]
# Stop replica so it never catches up
- exec kill -SIGSTOP [srv -1 pid]
+ pause_process [srv -1 pid]
$node_0 SET CASE 1
$node_0 failover to [srv -1 host] [srv -1 port] TIMEOUT 500
@@ -197,7 +197,7 @@ start_server {} {
fail "Failover from node_0 to replica did not finish"
}
- exec kill -SIGCONT [srv -1 pid]
+ resume_process [srv -1 pid]
# We need to make sure the nodes actually sync back up
wait_for_ofs_sync $node_0 $node_1
@@ -218,7 +218,7 @@ start_server {} {
set initial_syncs [s 0 sync_full]
# Stop replica so it never catches up
- exec kill -SIGSTOP [srv -1 pid]
+ pause_process [srv -1 pid]
$node_0 SET CASE 2
$node_0 failover to [srv -1 host] [srv -1 port] TIMEOUT 60000
@@ -230,7 +230,7 @@ start_server {} {
$node_0 failover abort
assert_match [s 0 master_failover_state] "no-failover"
- exec kill -SIGCONT [srv -1 pid]
+ resume_process [srv -1 pid]
# Just make sure everything is still synced
wait_for_ofs_sync $node_0 $node_1
@@ -255,11 +255,11 @@ start_server {} {
# We pause the target long enough to send a write command
# during the pause. This write will not be interrupted.
- exec kill -SIGSTOP [srv -1 pid]
+ pause_process [srv -1 pid]
set rd [redis_deferring_client]
$rd SET FOO BAR
$node_0 failover to $node_1_host $node_1_port
- exec kill -SIGCONT [srv -1 pid]
+ resume_process [srv -1 pid]
# Wait for failover to end
wait_for_condition 50 100 {
diff --git a/tests/integration/psync2-master-restart.tcl b/tests/integration/psync2-master-restart.tcl
index 03470bf81..a9e21d12d 100644
--- a/tests/integration/psync2-master-restart.tcl
+++ b/tests/integration/psync2-master-restart.tcl
@@ -11,6 +11,9 @@ start_server {} {
set sub_replica [srv -2 client]
+ # Make sure the server saves an RDB on shutdown
+ $master config set save "3600 1"
+
# Because we will test partial resync later, we don’t want a timeout to cause
# the master-replica disconnect, then the extra reconnections will break the
# sync_partial_ok stat test
@@ -18,6 +21,10 @@ start_server {} {
$replica config set repl-timeout 3600
$sub_replica config set repl-timeout 3600
+ # Avoid PINGs
+ $master config set repl-ping-replica-period 3600
+ $master config rewrite
+
# Build replication chain
$replica replicaof $master_host $master_port
$sub_replica replicaof $replica_host $replica_port
@@ -29,14 +36,43 @@ start_server {} {
fail "Replication not started."
}
- # Avoid PINGs
- $master config set repl-ping-replica-period 3600
- $master config rewrite
+ test "PSYNC2: Partial resync after Master restart using RDB aux fields when offset is 0" {
+ assert {[status $master master_repl_offset] == 0}
+
+ set replid [status $master master_replid]
+ $replica config resetstat
+
+ catch {
+ restart_server 0 true false true now
+ set master [srv 0 client]
+ }
+ wait_for_condition 50 1000 {
+ [status $replica master_link_status] eq {up} &&
+ [status $sub_replica master_link_status] eq {up}
+ } else {
+ fail "Replicas didn't sync after master restart"
+ }
+
+ # Make sure master restore replication info correctly
+ assert {[status $master master_replid] != $replid}
+ assert {[status $master master_repl_offset] == 0}
+ assert {[status $master master_replid2] eq $replid}
+ assert {[status $master second_repl_offset] == 1}
+
+ # Make sure master set replication backlog correctly
+ assert {[status $master repl_backlog_active] == 1}
+ assert {[status $master repl_backlog_first_byte_offset] == 1}
+ assert {[status $master repl_backlog_histlen] == 0}
+
+ # Partial resync after Master restart
+ assert {[status $master sync_partial_ok] == 1}
+ assert {[status $replica sync_partial_ok] == 1}
+ }
# Generate some data
createComplexDataset $master 1000
- test "PSYNC2: Partial resync after Master restart using RDB aux fields" {
+ test "PSYNC2: Partial resync after Master restart using RDB aux fields with data" {
wait_for_condition 500 100 {
[status $master master_repl_offset] == [status $replica master_repl_offset] &&
[status $master master_repl_offset] == [status $sub_replica master_repl_offset]
diff --git a/tests/integration/psync2.tcl b/tests/integration/psync2.tcl
index a258f1b83..4abe059b1 100644
--- a/tests/integration/psync2.tcl
+++ b/tests/integration/psync2.tcl
@@ -355,6 +355,8 @@ start_server {} {
set sync_partial [status $R($master_id) sync_partial_ok]
set sync_partial_err [status $R($master_id) sync_partial_err]
catch {
+ # Make sure the server saves an RDB on shutdown
+ $R($slave_id) config set save "900 1"
$R($slave_id) config rewrite
restart_server [expr {0-$slave_id}] true false
set R($slave_id) [srv [expr {0-$slave_id}] client]
diff --git a/tests/integration/rdb.tcl b/tests/integration/rdb.tcl
index 2362ef079..cce21671f 100644
--- a/tests/integration/rdb.tcl
+++ b/tests/integration/rdb.tcl
@@ -173,7 +173,7 @@ start_server {} {
}
test {client freed during loading} {
- start_server [list overrides [list key-load-delay 50 loading-process-events-interval-bytes 1024 rdbcompression no]] {
+ start_server [list overrides [list key-load-delay 50 loading-process-events-interval-bytes 1024 rdbcompression no save "900 1"]] {
# create a big rdb that will take long to load. it is important
# for keys to be big since the server processes events only once in 2mb.
# 100mb of rdb, 100k keys will load in more than 5 seconds
@@ -370,6 +370,9 @@ start_server [list overrides [list "dir" $server_path "dbfilename" "scriptbackup
start_server {} {
test "failed bgsave prevents writes" {
+ # Make sure the server saves an RDB on shutdown
+ r config set save "900 1"
+
r config set rdb-key-save-delay 10000000
populate 1000
r set x x
diff --git a/tests/integration/redis-cli.tcl b/tests/integration/redis-cli.tcl
index 2c39d720a..da82dda65 100644
--- a/tests/integration/redis-cli.tcl
+++ b/tests/integration/redis-cli.tcl
@@ -423,6 +423,27 @@ if {!$::tls} { ;# fake_redis_node doesn't support TLS
file delete $tmpfile
}
+ test_nontty_cli "Test command-line hinting - latest server" {
+ # cli will connect to the running server and will use COMMAND DOCS
+ catch {run_cli --test_hint_file tests/assets/test_cli_hint_suite.txt} output
+ assert_match "*SUCCESS*" $output
+ }
+
+ test_nontty_cli "Test command-line hinting - no server" {
+ # cli will fail to connect to the server and will use the cached commands.c
+ catch {run_cli -p 123 --test_hint_file tests/assets/test_cli_hint_suite.txt} output
+ assert_match "*SUCCESS*" $output
+ }
+
+ test_nontty_cli "Test command-line hinting - old server" {
+ # cli will connect to the server but will not use COMMAND DOCS,
+ # and complete the missing info from the cached commands.c
+ r ACL setuser clitest on nopass +@all -command|docs
+ catch {run_cli --user clitest -a nopass --no-auth-warning --test_hint_file tests/assets/test_cli_hint_suite.txt} output
+ assert_match "*SUCCESS*" $output
+ r acl deluser clitest
+ }
+
proc test_redis_cli_rdb_dump {functions_only} {
r flushdb
r function flush
diff --git a/tests/integration/replication-2.tcl b/tests/integration/replication-2.tcl
index f9f259211..c18ff24fc 100644
--- a/tests/integration/replication-2.tcl
+++ b/tests/integration/replication-2.tcl
@@ -42,7 +42,7 @@ start_server {tags {"repl external:skip"}} {
test {No write if min-slaves-max-lag is > of the slave lag} {
r config set min-slaves-to-write 1
r config set min-slaves-max-lag 2
- exec kill -SIGSTOP [srv -1 pid]
+ pause_process [srv -1 pid]
assert {[r set foo 12345] eq {OK}}
wait_for_condition 100 100 {
[catch {r set foo 12345}] != 0
@@ -52,7 +52,7 @@ start_server {tags {"repl external:skip"}} {
catch {r set foo 12345} err
assert_match {NOREPLICAS*} $err
}
- exec kill -SIGCONT [srv -1 pid]
+ resume_process [srv -1 pid]
test {min-slaves-to-write is ignored by slaves} {
r config set min-slaves-to-write 1
diff --git a/tests/integration/replication-4.tcl b/tests/integration/replication-4.tcl
index f772eccb2..867ef364e 100644
--- a/tests/integration/replication-4.tcl
+++ b/tests/integration/replication-4.tcl
@@ -1,5 +1,5 @@
-start_server {tags {"repl network external:skip singledb:skip"}} {
- start_server {} {
+start_server {tags {"repl network external:skip singledb:skip"} overrides {save {}}} {
+ start_server { overrides {save {}}} {
set master [srv -1 client]
set master_host [srv -1 host]
@@ -104,7 +104,7 @@ start_server {tags {"repl external:skip"}} {
assert_equal OK [$master set foo 123]
assert_equal OK [$master eval "return redis.call('set','foo',12345)" 0]
# Killing a slave to make it become a lagged slave.
- exec kill -SIGSTOP [srv 0 pid]
+ pause_process [srv 0 pid]
# Waiting for slave kill.
wait_for_condition 100 100 {
[catch {$master set foo 123}] != 0
@@ -113,7 +113,7 @@ start_server {tags {"repl external:skip"}} {
}
assert_error "*NOREPLICAS*" {$master set foo 123}
assert_error "*NOREPLICAS*" {$master eval "return redis.call('set','foo',12345)" 0}
- exec kill -SIGCONT [srv 0 pid]
+ resume_process [srv 0 pid]
}
}
}
@@ -146,12 +146,12 @@ start_server {tags {"repl external:skip"}} {
$master debug set-active-expire 0
$master set k 1 px $px_ms
wait_for_ofs_sync $master $slave
- exec kill -SIGSTOP [srv 0 pid]
+ pause_process [srv 0 pid]
$master incr k
after [expr $px_ms + 1]
# Stopping the replica for one second to makes sure the INCR arrives
# to the replica after the key is logically expired.
- exec kill -SIGCONT [srv 0 pid]
+ resume_process [srv 0 pid]
wait_for_ofs_sync $master $slave
# Check that k is logically expired but is present in the replica.
set res [$slave exists k]
diff --git a/tests/integration/replication-buffer.tcl b/tests/integration/replication-buffer.tcl
index 2e402480d..143dc74aa 100644
--- a/tests/integration/replication-buffer.tcl
+++ b/tests/integration/replication-buffer.tcl
@@ -159,7 +159,7 @@ start_server {} {
assert {[s repl_backlog_histlen] > [expr 2*10000*10000]}
assert_equal [s connected_slaves] {2}
- exec kill -SIGSTOP $replica2_pid
+ pause_process $replica2_pid
r config set client-output-buffer-limit "replica 128k 0 0"
# trigger output buffer limit check
r set key [string repeat A [expr 64*1024]]
@@ -178,7 +178,7 @@ start_server {} {
} else {
fail "Replication backlog memory is not smaller"
}
- exec kill -SIGCONT $replica2_pid
+ resume_process $replica2_pid
}
# speed up termination
$master config set shutdown-timeout 0
diff --git a/tests/integration/replication-psync.tcl b/tests/integration/replication-psync.tcl
index 16f3b8889..dc1df0fa6 100644
--- a/tests/integration/replication-psync.tcl
+++ b/tests/integration/replication-psync.tcl
@@ -9,8 +9,8 @@
# reconnect with the master, otherwise just the initial synchronization is
# checked for consistency.
proc test_psync {descr duration backlog_size backlog_ttl delay cond mdl sdl reconnect} {
- start_server {tags {"repl"}} {
- start_server {} {
+ start_server {tags {"repl"} overrides {save {}}} {
+ start_server {overrides {save {}}} {
set master [srv -1 client]
set master_host [srv -1 host]
diff --git a/tests/integration/replication.tcl b/tests/integration/replication.tcl
index b4e9ee673..de4d527f4 100644
--- a/tests/integration/replication.tcl
+++ b/tests/integration/replication.tcl
@@ -302,7 +302,7 @@ start_server {tags {"repl external:skip"}} {
foreach mdl {no yes} {
foreach sdl {disabled swapdb} {
- start_server {tags {"repl external:skip"}} {
+ start_server {tags {"repl external:skip"} overrides {save {}}} {
set master [srv 0 client]
$master config set repl-diskless-sync $mdl
$master config set repl-diskless-sync-delay 5
@@ -310,11 +310,11 @@ foreach mdl {no yes} {
set master_host [srv 0 host]
set master_port [srv 0 port]
set slaves {}
- start_server {} {
+ start_server {overrides {save {}}} {
lappend slaves [srv 0 client]
- start_server {} {
+ start_server {overrides {save {}}} {
lappend slaves [srv 0 client]
- start_server {} {
+ start_server {overrides {save {}}} {
lappend slaves [srv 0 client]
test "Connect multiple replicas at the same time (issue #141), master diskless=$mdl, replica diskless=$sdl" {
# start load handles only inside the test, so that the test can be skipped
@@ -391,11 +391,11 @@ foreach mdl {no yes} {
}
}
-start_server {tags {"repl external:skip"}} {
+start_server {tags {"repl external:skip"} overrides {save {}}} {
set master [srv 0 client]
set master_host [srv 0 host]
set master_port [srv 0 port]
- start_server {} {
+ start_server {overrides {save {}}} {
test "Master stream is correctly processed while the replica has a script in -BUSY state" {
set load_handle0 [start_write_load $master_host $master_port 3]
set slave [srv 0 client]
@@ -705,11 +705,11 @@ foreach testType {Successful Aborted} {
}
test {diskless loading short read} {
- start_server {tags {"repl"}} {
+ start_server {tags {"repl"} overrides {save ""}} {
set replica [srv 0 client]
set replica_host [srv 0 host]
set replica_port [srv 0 port]
- start_server {} {
+ start_server {overrides {save ""}} {
set master [srv 0 client]
set master_host [srv 0 host]
set master_port [srv 0 port]
@@ -847,7 +847,7 @@ proc compute_cpu_usage {start end} {
# test diskless rdb pipe with multiple replicas, which may drop half way
-start_server {tags {"repl external:skip"}} {
+start_server {tags {"repl external:skip"} overrides {save ""}} {
set master [srv 0 client]
$master config set repl-diskless-sync yes
$master config set repl-diskless-sync-delay 5
@@ -868,10 +868,10 @@ start_server {tags {"repl external:skip"}} {
set replicas {}
set replicas_alive {}
# start one replica that will read the rdb fast, and one that will be slow
- start_server {} {
+ start_server {overrides {save ""}} {
lappend replicas [srv 0 client]
lappend replicas_alive [srv 0 client]
- start_server {} {
+ start_server {overrides {save ""}} {
lappend replicas [srv 0 client]
lappend replicas_alive [srv 0 client]
@@ -913,7 +913,7 @@ start_server {tags {"repl external:skip"}} {
if {$all_drop == "timeout"} {
$master config set repl-timeout 2
# we want the slow replica to hang on a key for very long so it'll reach repl-timeout
- exec kill -SIGSTOP [srv -1 pid]
+ pause_process [srv -1 pid]
after 2000
}
@@ -940,7 +940,7 @@ start_server {tags {"repl external:skip"}} {
# master disconnected the slow replica, remove from array
set replicas_alive [lreplace $replicas_alive 0 0]
# release it
- exec kill -SIGCONT [srv -1 pid]
+ resume_process [srv -1 pid]
}
# make sure we don't have a busy loop going thought epoll_wait
@@ -1000,7 +1000,7 @@ test "diskless replication child being killed is collected" {
# when diskless master is waiting for the replica to become writable
# it removes the read event from the rdb pipe so if the child gets killed
# the replica will hung. and the master may not collect the pid with waitpid
- start_server {tags {"repl"}} {
+ start_server {tags {"repl"} overrides {save ""}} {
set master [srv 0 client]
set master_host [srv 0 host]
set master_port [srv 0 port]
@@ -1010,7 +1010,7 @@ test "diskless replication child being killed is collected" {
# put enough data in the db that the rdb file will be bigger than the socket buffers
$master debug populate 20000 test 10000
$master config set rdbcompression no
- start_server {} {
+ start_server {overrides {save ""}} {
set replica [srv 0 client]
set loglines [count_log_lines 0]
$replica config set repl-diskless-load swapdb
@@ -1044,7 +1044,7 @@ test "diskless replication child being killed is collected" {
foreach mdl {yes no} {
test "replication child dies when parent is killed - diskless: $mdl" {
# when master is killed, make sure the fork child can detect that and exit
- start_server {tags {"repl"}} {
+ start_server {tags {"repl"} overrides {save ""}} {
set master [srv 0 client]
set master_host [srv 0 host]
set master_port [srv 0 port]
@@ -1054,7 +1054,7 @@ foreach mdl {yes no} {
# create keys that will take 10 seconds to save
$master config set rdb-key-save-delay 1000
$master debug populate 10000
- start_server {} {
+ start_server {overrides {save ""}} {
set replica [srv 0 client]
$replica replicaof $master_host $master_port
@@ -1085,7 +1085,7 @@ test "diskless replication read pipe cleanup" {
# When we close this pipe (fd), the read handler also needs to be removed from the event loop (if it still registered).
# Otherwise, next time we will use the same fd, the registration will be fail (panic), because
# we will use EPOLL_CTL_MOD (the fd still register in the event loop), on fd that already removed from epoll_ctl
- start_server {tags {"repl"}} {
+ start_server {tags {"repl"} overrides {save ""}} {
set master [srv 0 client]
set master_host [srv 0 host]
set master_port [srv 0 port]
@@ -1097,7 +1097,7 @@ test "diskless replication read pipe cleanup" {
$master config set rdb-key-save-delay 100000
$master debug populate 20000 test 10000
$master config set rdbcompression no
- start_server {} {
+ start_server {overrides {save ""}} {
set replica [srv 0 client]
set loglines [count_log_lines 0]
$replica config set repl-diskless-load swapdb
@@ -1122,17 +1122,17 @@ test "diskless replication read pipe cleanup" {
test {replicaof right after disconnection} {
# this is a rare race condition that was reproduced sporadically by the psync2 unit.
# see details in #7205
- start_server {tags {"repl"}} {
+ start_server {tags {"repl"} overrides {save ""}} {
set replica1 [srv 0 client]
set replica1_host [srv 0 host]
set replica1_port [srv 0 port]
set replica1_log [srv 0 stdout]
- start_server {} {
+ start_server {overrides {save ""}} {
set replica2 [srv 0 client]
set replica2_host [srv 0 host]
set replica2_port [srv 0 port]
set replica2_log [srv 0 stdout]
- start_server {} {
+ start_server {overrides {save ""}} {
set master [srv 0 client]
set master_host [srv 0 host]
set master_port [srv 0 port]
diff --git a/tests/integration/shutdown.tcl b/tests/integration/shutdown.tcl
index 60afc5c7f..b2ec32cbd 100644
--- a/tests/integration/shutdown.tcl
+++ b/tests/integration/shutdown.tcl
@@ -19,8 +19,8 @@ proc fill_up_os_socket_send_buffer_for_repl {idx} {
foreach how {sigterm shutdown} {
test "Shutting down master waits for replica to catch up ($how)" {
- start_server {} {
- start_server {} {
+ start_server {overrides {save ""}} {
+ start_server {overrides {save ""}} {
set master [srv -1 client]
set master_host [srv -1 host]
set master_port [srv -1 port]
@@ -42,8 +42,7 @@ foreach how {sigterm shutdown} {
wait_for_ofs_sync $master $replica
# Pause the replica.
- exec kill -SIGSTOP $replica_pid
- after 10
+ pause_process $replica_pid
# Fill up the OS socket send buffer for the replica connection
# to prevent the following INCR from reaching the replica via
@@ -69,7 +68,7 @@ foreach how {sigterm shutdown} {
# Wake up replica and check if master has waited for it.
after 20; # 2 cron intervals
- exec kill -SIGCONT $replica_pid
+ resume_process $replica_pid
wait_for_condition 300 1000 {
[$replica get k] eq 2
} else {
@@ -86,8 +85,8 @@ foreach how {sigterm shutdown} {
}
test {Shutting down master waits for replica timeout} {
- start_server {} {
- start_server {} {
+ start_server {overrides {save ""}} {
+ start_server {overrides {save ""}} {
set master [srv -1 client]
set master_host [srv -1 host]
set master_port [srv -1 port]
@@ -107,8 +106,7 @@ test {Shutting down master waits for replica timeout} {
wait_for_ofs_sync $master $replica
# Pause the replica.
- exec kill -SIGSTOP $replica_pid
- after 10
+ pause_process $replica_pid
# Fill up the OS socket send buffer for the replica connection to
# prevent the following INCR k from reaching the replica via the OS.
@@ -129,15 +127,15 @@ test {Shutting down master waits for replica timeout} {
verify_log_message -1 "*0 of 1 replicas are in sync*" 0
# Wake up replica.
- exec kill -SIGCONT $replica_pid
+ resume_process $replica_pid
assert_equal 1 [$replica get k]
}
}
} {} {repl external:skip}
test "Shutting down master waits for replica then fails" {
- start_server {} {
- start_server {} {
+ start_server {overrides {save ""}} {
+ start_server {overrides {save ""}} {
set master [srv -1 client]
set master_host [srv -1 host]
set master_port [srv -1 port]
@@ -150,8 +148,7 @@ test "Shutting down master waits for replica then fails" {
wait_for_sync $replica
# Pause the replica and write a key on master.
- exec kill -SIGSTOP $replica_pid
- after 10
+ pause_process $replica_pid
$master incr k
# Two clients call blocking SHUTDOWN in parallel.
@@ -168,7 +165,7 @@ test "Shutting down master waits for replica then fails" {
$master config set appendonly yes
# Wake up replica, causing master to continue shutting down.
- exec kill -SIGCONT $replica_pid
+ resume_process $replica_pid
# SHUTDOWN returns an error to both clients blocking on SHUTDOWN.
catch { $rd1 read } e1
@@ -190,8 +187,8 @@ test "Shutting down master waits for replica then fails" {
} {} {repl external:skip}
test "Shutting down master waits for replica then aborted" {
- start_server {} {
- start_server {} {
+ start_server {overrides {save ""}} {
+ start_server {overrides {save ""}} {
set master [srv -1 client]
set master_host [srv -1 host]
set master_port [srv -1 port]
@@ -204,8 +201,7 @@ test "Shutting down master waits for replica then aborted" {
wait_for_sync $replica
# Pause the replica and write a key on master.
- exec kill -SIGSTOP $replica_pid
- after 10
+ pause_process $replica_pid
$master incr k
# Two clients call blocking SHUTDOWN in parallel.
@@ -221,7 +217,7 @@ test "Shutting down master waits for replica then aborted" {
$master shutdown abort
# Wake up replica, causing master to continue shutting down.
- exec kill -SIGCONT $replica_pid
+ resume_process $replica_pid
# SHUTDOWN returns an error to both clients blocking on SHUTDOWN.
catch { $rd1 read } e1
diff --git a/tests/modules/Makefile b/tests/modules/Makefile
index a1f5b074b..d63c8548d 100644
--- a/tests/modules/Makefile
+++ b/tests/modules/Makefile
@@ -61,7 +61,8 @@ TEST_MODULES = \
publish.so \
usercall.so \
postnotifications.so \
- moduleauthtwo.so
+ moduleauthtwo.so \
+ rdbloadsave.so
.PHONY: all
diff --git a/tests/modules/blockonkeys.c b/tests/modules/blockonkeys.c
index 8f4353a55..bc3b6b1a4 100644
--- a/tests/modules/blockonkeys.c
+++ b/tests/modules/blockonkeys.c
@@ -21,7 +21,7 @@ typedef struct {
static RedisModuleType *fsltype = NULL;
-fsl_t *fsl_type_create() {
+fsl_t *fsl_type_create(void) {
fsl_t *o;
o = RedisModule_Alloc(sizeof(*o));
o->length = 0;
diff --git a/tests/modules/cmdintrospection.c b/tests/modules/cmdintrospection.c
index dd9fb7f60..1a5e4863b 100644
--- a/tests/modules/cmdintrospection.c
+++ b/tests/modules/cmdintrospection.c
@@ -23,7 +23,7 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
RedisModuleCommandInfo info = {
.version = REDISMODULE_COMMAND_INFO_VERSION,
.arity = -5,
- .summary = "Appends a new entry to a stream",
+ .summary = "Appends a new message to a stream. Creates the key if it doesn't exist.",
.since = "5.0.0",
.complexity = "O(1) when adding a new entry, O(N) when trimming where N being the number of entries evicted.",
.tips = "nondeterministic_output",
diff --git a/tests/modules/rdbloadsave.c b/tests/modules/rdbloadsave.c
new file mode 100644
index 000000000..687269a5a
--- /dev/null
+++ b/tests/modules/rdbloadsave.c
@@ -0,0 +1,162 @@
+#include "redismodule.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <memory.h>
+#include <errno.h>
+
+/* Sanity tests to verify inputs and return values. */
+int sanity(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
+ REDISMODULE_NOT_USED(argv);
+ REDISMODULE_NOT_USED(argc);
+
+ RedisModuleRdbStream *s = RedisModule_RdbStreamCreateFromFile("dbnew.rdb");
+
+ /* NULL stream should fail. */
+ if (RedisModule_RdbLoad(ctx, NULL, 0) == REDISMODULE_OK || errno != EINVAL) {
+ RedisModule_ReplyWithError(ctx, strerror(errno));
+ goto out;
+ }
+
+ /* Invalid flags should fail. */
+ if (RedisModule_RdbLoad(ctx, s, 188) == REDISMODULE_OK || errno != EINVAL) {
+ RedisModule_ReplyWithError(ctx, strerror(errno));
+ goto out;
+ }
+
+ /* Missing file should fail. */
+ if (RedisModule_RdbLoad(ctx, s, 0) == REDISMODULE_OK || errno != ENOENT) {
+ RedisModule_ReplyWithError(ctx, strerror(errno));
+ goto out;
+ }
+
+ /* Save RDB file. */
+ if (RedisModule_RdbSave(ctx, s, 0) != REDISMODULE_OK || errno != 0) {
+ RedisModule_ReplyWithError(ctx, strerror(errno));
+ goto out;
+ }
+
+ /* Load the saved RDB file. */
+ if (RedisModule_RdbLoad(ctx, s, 0) != REDISMODULE_OK || errno != 0) {
+ RedisModule_ReplyWithError(ctx, strerror(errno));
+ goto out;
+ }
+
+ RedisModule_ReplyWithSimpleString(ctx, "OK");
+
+ out:
+ RedisModule_RdbStreamFree(s);
+ return REDISMODULE_OK;
+}
+
+int cmd_rdbsave(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
+ if (argc != 2) {
+ RedisModule_WrongArity(ctx);
+ return REDISMODULE_OK;
+ }
+
+ size_t len;
+ const char *filename = RedisModule_StringPtrLen(argv[1], &len);
+
+ char tmp[len + 1];
+ memcpy(tmp, filename, len);
+ tmp[len] = '\0';
+
+ RedisModuleRdbStream *stream = RedisModule_RdbStreamCreateFromFile(tmp);
+
+ if (RedisModule_RdbSave(ctx, stream, 0) != REDISMODULE_OK || errno != 0) {
+ RedisModule_ReplyWithError(ctx, strerror(errno));
+ goto out;
+ }
+
+ RedisModule_ReplyWithSimpleString(ctx, "OK");
+
+out:
+ RedisModule_RdbStreamFree(stream);
+ return REDISMODULE_OK;
+}
+
+/* Fork before calling RM_RdbSave(). */
+int cmd_rdbsave_fork(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
+ if (argc != 2) {
+ RedisModule_WrongArity(ctx);
+ return REDISMODULE_OK;
+ }
+
+ size_t len;
+ const char *filename = RedisModule_StringPtrLen(argv[1], &len);
+
+ char tmp[len + 1];
+ memcpy(tmp, filename, len);
+ tmp[len] = '\0';
+
+ int fork_child_pid = RedisModule_Fork(NULL, NULL);
+ if (fork_child_pid < 0) {
+ RedisModule_ReplyWithError(ctx, strerror(errno));
+ return REDISMODULE_OK;
+ } else if (fork_child_pid > 0) {
+ /* parent */
+ RedisModule_ReplyWithSimpleString(ctx, "OK");
+ return REDISMODULE_OK;
+ }
+
+ RedisModuleRdbStream *stream = RedisModule_RdbStreamCreateFromFile(tmp);
+
+ int ret = 0;
+ if (RedisModule_RdbSave(ctx, stream, 0) != REDISMODULE_OK) {
+ ret = errno;
+ }
+ RedisModule_RdbStreamFree(stream);
+
+ RedisModule_ExitFromChild(ret);
+ return REDISMODULE_OK;
+}
+
+int cmd_rdbload(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
+ if (argc != 2) {
+ RedisModule_WrongArity(ctx);
+ return REDISMODULE_OK;
+ }
+
+ size_t len;
+ const char *filename = RedisModule_StringPtrLen(argv[1], &len);
+
+ char tmp[len + 1];
+ memcpy(tmp, filename, len);
+ tmp[len] = '\0';
+
+ RedisModuleRdbStream *stream = RedisModule_RdbStreamCreateFromFile(tmp);
+
+ if (RedisModule_RdbLoad(ctx, stream, 0) != REDISMODULE_OK || errno != 0) {
+ RedisModule_RdbStreamFree(stream);
+ RedisModule_ReplyWithError(ctx, strerror(errno));
+ return REDISMODULE_OK;
+ }
+
+ RedisModule_RdbStreamFree(stream);
+ RedisModule_ReplyWithSimpleString(ctx, "OK");
+ return REDISMODULE_OK;
+}
+
+int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
+ REDISMODULE_NOT_USED(argv);
+ REDISMODULE_NOT_USED(argc);
+
+ if (RedisModule_Init(ctx, "rdbloadsave", 1, REDISMODULE_APIVER_1) == REDISMODULE_ERR)
+ return REDISMODULE_ERR;
+
+ if (RedisModule_CreateCommand(ctx, "test.sanity", sanity, "", 0, 0, 0) == REDISMODULE_ERR)
+ return REDISMODULE_ERR;
+
+ if (RedisModule_CreateCommand(ctx, "test.rdbsave", cmd_rdbsave, "", 0, 0, 0) == REDISMODULE_ERR)
+ return REDISMODULE_ERR;
+
+ if (RedisModule_CreateCommand(ctx, "test.rdbsave_fork", cmd_rdbsave_fork, "", 0, 0, 0) == REDISMODULE_ERR)
+ return REDISMODULE_ERR;
+
+ if (RedisModule_CreateCommand(ctx, "test.rdbload", cmd_rdbload, "", 0, 0, 0) == REDISMODULE_ERR)
+ return REDISMODULE_ERR;
+
+ return REDISMODULE_OK;
+}
diff --git a/tests/modules/reply.c b/tests/modules/reply.c
index f890560e0..c5baa6635 100644
--- a/tests/modules/reply.c
+++ b/tests/modules/reply.c
@@ -156,6 +156,14 @@ int rw_error(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
return RedisModule_ReplyWithError(ctx, "An error");
}
+int rw_error_format(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
+ if (argc != 3) return RedisModule_WrongArity(ctx);
+
+ return RedisModule_ReplyWithErrorFormat(ctx,
+ RedisModule_StringPtrLen(argv[1], NULL),
+ RedisModule_StringPtrLen(argv[2], NULL));
+}
+
int rw_verbatim(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
if (argc != 2) return RedisModule_WrongArity(ctx);
@@ -197,6 +205,8 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx,"rw.error",rw_error,"",0,0,0) != REDISMODULE_OK)
return REDISMODULE_ERR;
+ if (RedisModule_CreateCommand(ctx,"rw.error_format",rw_error_format,"",0,0,0) != REDISMODULE_OK)
+ return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx,"rw.verbatim",rw_verbatim,"",0,0,0) != REDISMODULE_OK)
return REDISMODULE_ERR;
diff --git a/tests/modules/zset.c b/tests/modules/zset.c
index 91791f907..13f2ab3b6 100644
--- a/tests/modules/zset.c
+++ b/tests/modules/zset.c
@@ -1,4 +1,6 @@
#include "redismodule.h"
+#include <math.h>
+#include <errno.h>
/* ZSET.REM key element
*
@@ -17,14 +19,73 @@ int zset_rem(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
return RedisModule_ReplyWithError(ctx, "ERR ZsetRem failed");
}
+/* ZSET.ADD key score member
+ *
+ * Adds a specified member with the specified score to the sorted
+ * set stored at key.
+ */
+int zset_add(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
+ if (argc != 4) return RedisModule_WrongArity(ctx);
+ RedisModule_AutoMemory(ctx);
+ int keymode = REDISMODULE_READ | REDISMODULE_WRITE;
+ RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], keymode);
+
+ size_t len;
+ double score;
+ char *endptr;
+ const char *str = RedisModule_StringPtrLen(argv[2], &len);
+ score = strtod(str, &endptr);
+ if (*endptr != '\0' || errno == ERANGE)
+ return RedisModule_ReplyWithError(ctx, "value is not a valid float");
+
+ if (RedisModule_ZsetAdd(key, score, argv[3], NULL) == REDISMODULE_OK)
+ return RedisModule_ReplyWithSimpleString(ctx, "OK");
+ else
+ return RedisModule_ReplyWithError(ctx, "ERR ZsetAdd failed");
+}
+
+/* ZSET.INCRBY key member increment
+ *
+ * Increments the score stored at member in the sorted set stored at key by increment.
+ * Replies with the new score of this element.
+ */
+int zset_incrby(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
+ if (argc != 4) return RedisModule_WrongArity(ctx);
+ RedisModule_AutoMemory(ctx);
+ int keymode = REDISMODULE_READ | REDISMODULE_WRITE;
+ RedisModuleKey *key = RedisModule_OpenKey(ctx, argv[1], keymode);
+
+ size_t len;
+ double score, newscore;
+ char *endptr;
+ const char *str = RedisModule_StringPtrLen(argv[3], &len);
+ score = strtod(str, &endptr);
+ if (*endptr != '\0' || errno == ERANGE)
+ return RedisModule_ReplyWithError(ctx, "value is not a valid float");
+
+ if (RedisModule_ZsetIncrby(key, score, argv[2], NULL, &newscore) == REDISMODULE_OK)
+ return RedisModule_ReplyWithDouble(ctx, newscore);
+ else
+ return RedisModule_ReplyWithError(ctx, "ERR ZsetIncrby failed");
+}
+
int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc) {
REDISMODULE_NOT_USED(argv);
REDISMODULE_NOT_USED(argc);
- if (RedisModule_Init(ctx, "zset", 1, REDISMODULE_APIVER_1) ==
- REDISMODULE_OK &&
- RedisModule_CreateCommand(ctx, "zset.rem", zset_rem, "write",
- 1, 1, 1) == REDISMODULE_OK)
- return REDISMODULE_OK;
- else
+ if (RedisModule_Init(ctx, "zset", 1, REDISMODULE_APIVER_1) == REDISMODULE_ERR)
return REDISMODULE_ERR;
+
+ if (RedisModule_CreateCommand(ctx, "zset.rem", zset_rem, "write",
+ 1, 1, 1) == REDISMODULE_ERR)
+ return REDISMODULE_ERR;
+
+ if (RedisModule_CreateCommand(ctx, "zset.add", zset_add, "write",
+ 1, 1, 1) == REDISMODULE_ERR)
+ return REDISMODULE_ERR;
+
+ if (RedisModule_CreateCommand(ctx, "zset.incrby", zset_incrby, "write",
+ 1, 1, 1) == REDISMODULE_ERR)
+ return REDISMODULE_ERR;
+
+ return REDISMODULE_OK;
}
diff --git a/tests/sentinel/run.tcl b/tests/sentinel/run.tcl
index 98c4c118b..6d3db324d 100644
--- a/tests/sentinel/run.tcl
+++ b/tests/sentinel/run.tcl
@@ -22,6 +22,7 @@ proc main {} {
spawn_instance redis $::redis_base_port $::instances_count {
"enable-protected-configs yes"
"enable-debug-command yes"
+ "save ''"
}
run_tests
cleanup
diff --git a/tests/sentinel/tests/00-base.tcl b/tests/sentinel/tests/00-base.tcl
index 1b33ca7a3..b4d65751b 100644
--- a/tests/sentinel/tests/00-base.tcl
+++ b/tests/sentinel/tests/00-base.tcl
@@ -31,6 +31,50 @@ test "Sentinel command flag infrastructure works correctly" {
}
}
+test "SENTINEL HELP output the sentinel subcommand help" {
+ assert_match "*SENTINEL <subcommand> *" [S 0 SENTINEL HELP]
+}
+
+test "SENTINEL MYID return the sentinel instance ID" {
+ assert_equal 40 [string length [S 0 SENTINEL MYID]]
+ assert_equal [S 0 SENTINEL MYID] [S 0 SENTINEL MYID]
+}
+
+test "SENTINEL INFO CACHE returns the cached info" {
+ set res [S 0 SENTINEL INFO-CACHE mymaster]
+ assert_morethan_equal [llength $res] 2
+ assert_equal "mymaster" [lindex $res 0]
+
+ set res [lindex $res 1]
+ assert_morethan_equal [llength $res] 2
+ assert_morethan [lindex $res 0] 0
+ assert_match "*# Server*" [lindex $res 1]
+}
+
+test "SENTINEL PENDING-SCRIPTS returns the information about pending scripts" {
+ # may or may not have a value, so assert greater than or equal to 0.
+ assert_morethan_equal [llength [S 0 SENTINEL PENDING-SCRIPTS]] 0
+}
+
+test "SENTINEL MASTERS returns a list of monitored masters" {
+ assert_match "*mymaster*" [S 0 SENTINEL MASTERS]
+ assert_morethan_equal [llength [S 0 SENTINEL MASTERS]] 1
+}
+
+test "SENTINEL SENTINELS returns a list of sentinel instances" {
+ assert_morethan_equal [llength [S 0 SENTINEL SENTINELS mymaster]] 1
+}
+
+test "SENTINEL SLAVES returns a list of the monitored replicas" {
+ assert_morethan_equal [llength [S 0 SENTINEL SLAVES mymaster]] 1
+}
+
+test "SENTINEL SIMULATE-FAILURE HELP list supported flags" {
+ set res [S 0 SENTINEL SIMULATE-FAILURE HELP]
+ assert_morethan_equal [llength $res] 2
+ assert_equal {crash-after-election crash-after-promotion} $res
+}
+
test "Basic failover works if the master is down" {
set old_port [RPort $master_id]
set addr [S 0 SENTINEL GET-MASTER-ADDR-BY-NAME mymaster]
@@ -149,3 +193,10 @@ test "Failover works if we configure for absolute agreement" {
test "New master [join $addr {:}] role matches" {
assert {[RI $master_id role] eq {master}}
}
+
+test "SENTINEL RESET can resets the master" {
+ assert_equal 1 [S 0 SENTINEL RESET mymaster]
+ assert_equal 0 [llength [S 0 SENTINEL SENTINELS mymaster]]
+ assert_equal 0 [llength [S 0 SENTINEL SLAVES mymaster]]
+ assert_equal 0 [llength [S 0 SENTINEL REPLICAS mymaster]]
+}
diff --git a/tests/sentinel/tests/05-manual.tcl b/tests/sentinel/tests/05-manual.tcl
index 72d80fdf8..7e050b0dc 100644
--- a/tests/sentinel/tests/05-manual.tcl
+++ b/tests/sentinel/tests/05-manual.tcl
@@ -61,3 +61,28 @@ test "The old master eventually gets reconfigured as a slave" {
fail "Old master not reconfigured as slave of new master"
}
}
+
+foreach flag {crash-after-election crash-after-promotion} {
+ test "SENTINEL SIMULATE-FAILURE $flag works" {
+ assert_equal {OK} [S 0 SENTINEL SIMULATE-FAILURE $flag]
+
+ # Trigger a failover, failover will trigger leader election, replica promotion
+ wait_for_condition 300 50 {
+ [catch {S 0 SENTINEL FAILOVER mymaster}] == 0
+ } else {
+ catch {S 0 SENTINEL FAILOVER mymaster} reply
+ puts [S 0 SENTINEL REPLICAS mymaster]
+ fail "Sentinel manual failover did not work, got: $reply"
+ }
+
+ # Wait for sentinel to exit (due to simulate-failure flags)
+ wait_for_condition 1000 50 {
+ [catch {S 0 PING}] == 1
+ } else {
+ fail "Sentinel set $flag but did not exit"
+ }
+ assert_error {*couldn't open socket: connection refused*} {S 0 PING}
+
+ restart_instance sentinel 0
+ }
+}
diff --git a/tests/sentinel/tests/07-down-conditions.tcl b/tests/sentinel/tests/07-down-conditions.tcl
index 403f81e73..dabbc14c5 100644
--- a/tests/sentinel/tests/07-down-conditions.tcl
+++ b/tests/sentinel/tests/07-down-conditions.tcl
@@ -49,9 +49,9 @@ test "SDOWN is triggered by non-responding but not crashed instance" {
set master_id [get_instance_id_by_port redis [lindex $master_addr 1]]
set pid [get_instance_attrib redis $master_id pid]
- exec kill -SIGSTOP $pid
+ pause_process $pid
ensure_master_down
- exec kill -SIGCONT $pid
+ resume_process $pid
ensure_master_up
}
diff --git a/tests/support/cluster_util.tcl b/tests/support/cluster_util.tcl
index 8a58a4fa9..d80dcf062 100644
--- a/tests/support/cluster_util.tcl
+++ b/tests/support/cluster_util.tcl
@@ -36,7 +36,7 @@ proc wait_for_cluster_propagation {} {
# Wait for cluster size to be consistent across nodes.
proc wait_for_cluster_size {cluster_size} {
- wait_for_condition 50 100 {
+ wait_for_condition 1000 50 {
[cluster_size_consistent $cluster_size] eq 1
} else {
fail "cluster size did not reach a consistent size $cluster_size"
diff --git a/tests/support/server.tcl b/tests/support/server.tcl
index 4c596290d..9a3733b61 100644
--- a/tests/support/server.tcl
+++ b/tests/support/server.tcl
@@ -537,6 +537,9 @@ proc start_server {options {code undefined}} {
set fd [open $stdout "a+"]
puts $fd "### Starting server for test $::cur_test"
close $fd
+ if {$::verbose > 1} {
+ puts "### Starting server $stdout for test - $::cur_test"
+ }
}
# We may have a stdout left over from the previous tests, so we need
diff --git a/tests/support/test.tcl b/tests/support/test.tcl
index 68180bea4..b7cd38b38 100644
--- a/tests/support/test.tcl
+++ b/tests/support/test.tcl
@@ -168,7 +168,9 @@ proc test {name code {okpattern undefined} {tags {}}} {
send_data_packet $::test_server_fd skip $name
return
}
-
+ if {$::verbose > 1} {
+ puts "starting test $name"
+ }
# abort if only_tests was set but test name is not included
if {[llength $::only_tests] > 0 && ![search_pattern_list $name $::only_tests]} {
incr ::num_skipped
@@ -200,11 +202,16 @@ proc test {name code {okpattern undefined} {tags {}}} {
$r close
}
} else {
+ set servers {}
foreach srv $::servers {
set stdout [dict get $srv stdout]
set fd [open $stdout "a+"]
puts $fd "### Starting test $::cur_test"
close $fd
+ lappend servers $stdout
+ }
+ if {$::verbose > 1} {
+ puts "### Starting test $::cur_test - with servers: $servers"
}
}
diff --git a/tests/support/util.tcl b/tests/support/util.tcl
index 236fad314..a36003029 100644
--- a/tests/support/util.tcl
+++ b/tests/support/util.tcl
@@ -602,15 +602,24 @@ proc stop_bg_complex_data {handle} {
# Write num keys with the given key prefix and value size (in bytes). If idx is
# given, it's the index (AKA level) used with the srv procedure and it specifies
# to which Redis instance to write the keys.
-proc populate {num {prefix key:} {size 3} {idx 0}} {
- set rd [redis_deferring_client $idx]
- for {set j 0} {$j < $num} {incr j} {
- $rd set $prefix$j [string repeat A $size]
+proc populate {num {prefix key:} {size 3} {idx 0} {prints false}} {
+ r $idx deferred 1
+ if {$num > 16} {set pipeline 16} else {set pipeline $num}
+ set val [string repeat A $size]
+ for {set j 0} {$j < $pipeline} {incr j} {
+ r $idx set $prefix$j $val
+ if {$prints} {puts $j}
}
- for {set j 0} {$j < $num} {incr j} {
- $rd read
+ for {} {$j < $num} {incr j} {
+ r $idx set $prefix$j $val
+ r $idx read
+ if {$prints} {puts $j}
}
- $rd close
+ for {set j 0} {$j < $pipeline} {incr j} {
+ r $idx read
+ if {$prints} {puts $j}
+ }
+ r $idx deferred 0
}
proc get_child_pid {idx} {
@@ -628,7 +637,7 @@ proc get_child_pid {idx} {
}
proc process_is_alive pid {
- if {[catch {exec ps -p $pid} err]} {
+ if {[catch {exec ps -p $pid -f} err]} {
return 0
} else {
if {[string match "*<defunct>*" $err]} { return 0 }
@@ -636,6 +645,20 @@ proc process_is_alive pid {
}
}
+proc pause_process pid {
+ exec kill -SIGSTOP $pid
+ wait_for_condition 50 100 {
+ [string match {*T*} [lindex [exec ps j $pid] 16]]
+ } else {
+ puts [exec ps j $pid]
+ fail "process didn't stop"
+ }
+}
+
+proc resume_process pid {
+ exec kill -SIGCONT $pid
+}
+
proc cmdrstat {cmd r} {
if {[regexp "\r\ncmdstat_$cmd:(.*?)\r\n" [$r info commandstats] _ value]} {
set _ $value
diff --git a/tests/test_helper.tcl b/tests/test_helper.tcl
index 922cb438d..6ec2ae1fc 100644
--- a/tests/test_helper.tcl
+++ b/tests/test_helper.tcl
@@ -526,6 +526,7 @@ proc signal_idle_client fd {
incr ::next_test
if {$::loop && $::next_test == [llength $::all_tests]} {
set ::next_test 0
+ incr ::loop -1
}
} elseif {[llength $::run_solo_tests] != 0 && [llength $::active_clients] == 0} {
if {!$::quiet} {
@@ -620,6 +621,7 @@ proc print_help_screen {} {
"--no-latency Skip latency measurements and validation by some tests."
"--stop Blocks once the first test fails."
"--loop Execute the specified set of tests forever."
+ "--loops <count> Execute the specified set of tests several times."
"--wait-server Wait after server is started (so that you can attach a debugger)."
"--dump-logs Dump server log on test failure."
"--tls Run tests in TLS mode."
@@ -721,7 +723,7 @@ for {set j 0} {$j < [llength $argv]} {incr j} {
}
exit 0
} elseif {$opt eq {--verbose}} {
- set ::verbose 1
+ incr ::verbose
} elseif {$opt eq {--client}} {
set ::client 1
set ::test_server_port $arg
@@ -744,7 +746,10 @@ for {set j 0} {$j < [llength $argv]} {incr j} {
} elseif {$opt eq {--stop}} {
set ::stop_on_failure 1
} elseif {$opt eq {--loop}} {
- set ::loop 1
+ set ::loop 2147483647
+ } elseif {$opt eq {--loops}} {
+ set ::loop $arg
+ incr j
} elseif {$opt eq {--timeout}} {
set ::timeout $arg
incr j
diff --git a/tests/unit/acl.tcl b/tests/unit/acl.tcl
index 555fb5a34..4d8c77b9f 100644
--- a/tests/unit/acl.tcl
+++ b/tests/unit/acl.tcl
@@ -289,6 +289,20 @@ start_server {tags {"acl external:skip"}} {
$rd close
} {0}
+ test {Subscribers are killed when revoked of allchannels permission} {
+ set rd [redis_deferring_client]
+ r ACL setuser psuser allchannels
+ $rd AUTH psuser pspass
+ $rd read
+ $rd CLIENT SETNAME deathrow
+ $rd read
+ $rd PSUBSCRIBE foo
+ $rd read
+ r ACL setuser psuser resetchannels
+ assert_no_match {*deathrow*} [r CLIENT LIST]
+ $rd close
+ } {0}
+
test {Subscribers are pardoned if literal permissions are retained and/or gaining allchannels} {
set rd [redis_deferring_client]
r ACL setuser psuser resetchannels &foo:1 &bar:* &orders
diff --git a/tests/unit/aofrw.tcl b/tests/unit/aofrw.tcl
index fe07351a3..cc7545265 100644
--- a/tests/unit/aofrw.tcl
+++ b/tests/unit/aofrw.tcl
@@ -1,6 +1,6 @@
# This unit has the potential to create huge .reqres files, causing log-req-res-validator.py to run for a very long time...
# Since this unit doesn't do anything worth validating, reply_schema-wise, we decided to skip it
-start_server {tags {"aofrw external:skip logreqres:skip"}} {
+start_server {tags {"aofrw external:skip logreqres:skip"} overrides {save {}}} {
# Enable the AOF
r config set appendonly yes
r config set auto-aof-rewrite-percentage 0 ; # Disable auto-rewrite.
diff --git a/tests/unit/client-eviction.tcl b/tests/unit/client-eviction.tcl
index 76f7bf0f2..1fc7c02ca 100644
--- a/tests/unit/client-eviction.tcl
+++ b/tests/unit/client-eviction.tcl
@@ -347,12 +347,12 @@ start_server {} {
# We use two obuf-clients to make sure that even if client eviction is attempted
# between two command processing (with no sleep) we don't perform any client eviction
# because the obuf limit is enforced with precedence.
- exec kill -SIGSTOP $server_pid
+ pause_process $server_pid
$rr2 get k
$rr2 flush
$rr3 get k
$rr3 flush
- exec kill -SIGCONT $server_pid
+ resume_process $server_pid
r ping ;# make sure a full event loop cycle is processed before issuing CLIENT LIST
# Validate obuf-clients were disconnected (because of obuf limit)
diff --git a/tests/unit/cluster/cli.tcl b/tests/unit/cluster/cli.tcl
index 7131ee20f..5b7f24927 100644
--- a/tests/unit/cluster/cli.tcl
+++ b/tests/unit/cluster/cli.tcl
@@ -64,7 +64,7 @@ start_multiple_servers 3 [list overrides $base_conf] {
}
test "Wait for cluster to be stable" {
- # Cluster check just verifies the the config state is self-consistent,
+ # Cluster check just verifies the config state is self-consistent,
# waiting for cluster_state to be okay is an independent check that all the
# nodes actually believe each other are healthy, prevent cluster down error.
wait_for_condition 1000 50 {
@@ -116,7 +116,7 @@ start_multiple_servers 3 [list overrides $base_conf] {
test "Kill a cluster node and wait for fail state" {
# kill node3 in cluster
- exec kill -SIGSTOP $node3_pid
+ pause_process $node3_pid
wait_for_condition 1000 50 {
[CI 0 cluster_state] eq {fail} &&
@@ -134,7 +134,7 @@ start_multiple_servers 3 [list overrides $base_conf] {
assert_equal [s -1 blocked_clients] {0}
}
- exec kill -SIGCONT $node3_pid
+ resume_process $node3_pid
$node1_rd close
} ;# stop servers
diff --git a/tests/unit/cluster/links.tcl b/tests/unit/cluster/links.tcl
index 63c2b143c..a202c378b 100644
--- a/tests/unit/cluster/links.tcl
+++ b/tests/unit/cluster/links.tcl
@@ -200,7 +200,7 @@ start_cluster 3 0 {tags {external:skip cluster}} {
# To manufacture an ever-growing send buffer from primary1 to primary2,
# make primary2 unresponsive.
set primary2_pid [srv [expr -1*$primary2_id] pid]
- exec kill -SIGSTOP $primary2_pid
+ pause_process $primary2_pid
# On primary1, send 128KB Pubsub messages in a loop until the send buffer of the link from
# primary1 to primary2 exceeds buffer limit therefore be dropped.
@@ -226,7 +226,7 @@ start_cluster 3 0 {tags {external:skip cluster}} {
assert {[dict get $same_link_p1_from_p2 create-time] eq [dict get $orig_link_p1_from_p2 create-time]}
# Revive primary2
- exec kill -SIGCONT $primary2_pid
+ resume_process $primary2_pid
# Reset configs on primary1 so config changes don't leak out to other tests
$primary1 CONFIG set cluster-node-timeout $oldtimeout
diff --git a/tests/unit/expire.tcl b/tests/unit/expire.tcl
index 5ee4488b3..ec9d73cc2 100644
--- a/tests/unit/expire.tcl
+++ b/tests/unit/expire.tcl
@@ -76,20 +76,22 @@ start_server {tags {"expire"}} {
# This test is very likely to do a false positive if the
# server is under pressure, so if it does not work give it a few more
# chances.
- for {set j 0} {$j < 10} {incr j} {
+ for {set j 0} {$j < 30} {incr j} {
r del x
r setex x 1 somevalue
- after 900
+ after 800
set a [r get x]
- after 1100
+ if {$a ne {somevalue}} continue
+ after 300
set b [r get x]
- if {$a eq {somevalue} && $b eq {}} break
+ if {$b eq {}} break
}
if {$::verbose} {
puts "millisecond expire test attempts: $j"
}
- list $a $b
- } {somevalue {}}
+ assert_equal $a {somevalue}
+ assert_equal $b {}
+ }
test "PSETEX can set sub-second expires" {
# This test is very likely to do a false positive if the server is
diff --git a/tests/unit/info.tcl b/tests/unit/info.tcl
index 759e5bc0b..2f3ad8d6f 100644
--- a/tests/unit/info.tcl
+++ b/tests/unit/info.tcl
@@ -274,5 +274,68 @@ start_server {tags {"info" "external:skip"}} {
$rd close
}
+ test {stats: eventloop metrics} {
+ set info1 [r info stats]
+ set cycle1 [getInfoProperty $info1 eventloop_cycles]
+ set el_sum1 [getInfoProperty $info1 eventloop_duration_sum]
+ set cmd_sum1 [getInfoProperty $info1 eventloop_duration_cmd_sum]
+ assert_morethan $cycle1 0
+ assert_morethan $el_sum1 0
+ assert_morethan $cmd_sum1 0
+ after 110 ;# default hz is 10, wait for a cron tick.
+ set info2 [r info stats]
+ set cycle2 [getInfoProperty $info2 eventloop_cycles]
+ set el_sum2 [getInfoProperty $info2 eventloop_duration_sum]
+ set cmd_sum2 [getInfoProperty $info2 eventloop_duration_cmd_sum]
+ assert_morethan $cycle2 $cycle1
+ assert_lessthan $cycle2 [expr $cycle1+10] ;# we expect 2 or 3 cycles here, but allow some tolerance
+ assert_morethan $el_sum2 $el_sum1
+ assert_lessthan $el_sum2 [expr $el_sum1+5000] ;# we expect roughly 100ms here, but allow some tolerance
+ assert_morethan $cmd_sum2 $cmd_sum1
+ assert_lessthan $cmd_sum2 [expr $cmd_sum1+3000] ;# we expect about tens of ms here, but allow some tolerance
+ }
+
+ test {stats: instantaneous metrics} {
+ r config resetstat
+ after 1600 ;# hz is 10, wait for 16 cron tick so that sample array is fulfilled
+ set value [s instantaneous_eventloop_cycles_per_sec]
+ assert_morethan $value 0
+ assert_lessthan $value 15 ;# default hz is 10
+ set value [s instantaneous_eventloop_duration_usec]
+ assert_morethan $value 0
+ assert_lessthan $value 22000 ;# default hz is 10, so duration < 1000 / 10, allow some tolerance
+ }
+
+ test {stats: debug metrics} {
+ # make sure debug info is hidden
+ set info [r info]
+ assert_equal [getInfoProperty $info eventloop_duration_aof_sum] {}
+ set info_all [r info all]
+ assert_equal [getInfoProperty $info_all eventloop_duration_aof_sum] {}
+
+ set info1 [r info debug]
+
+ set aof1 [getInfoProperty $info1 eventloop_duration_aof_sum]
+ assert {$aof1 >= 0}
+ set cron1 [getInfoProperty $info1 eventloop_duration_cron_sum]
+ assert {$cron1 > 0}
+ set cycle_max1 [getInfoProperty $info1 eventloop_cmd_per_cycle_max]
+ assert {$cycle_max1 > 0}
+ set duration_max1 [getInfoProperty $info1 eventloop_duration_max]
+ assert {$duration_max1 > 0}
+
+ after 110 ;# hz is 10, wait for a cron tick.
+ set info2 [r info debug]
+
+ set aof2 [getInfoProperty $info2 eventloop_duration_aof_sum]
+ assert {$aof2 >= $aof1} ;# AOF is disabled, we expect $aof2 == $aof1, but allow some tolerance.
+ set cron2 [getInfoProperty $info2 eventloop_duration_cron_sum]
+ assert_morethan $cron2 $cron1
+ set cycle_max2 [getInfoProperty $info2 eventloop_cmd_per_cycle_max]
+ assert {$cycle_max2 >= $cycle_max1}
+ set duration_max2 [getInfoProperty $info2 eventloop_duration_max]
+ assert {$duration_max2 >= $duration_max1}
+ }
+
}
}
diff --git a/tests/unit/introspection-2.tcl b/tests/unit/introspection-2.tcl
index a0cf0c30f..52a13edf7 100644
--- a/tests/unit/introspection-2.tcl
+++ b/tests/unit/introspection-2.tcl
@@ -118,6 +118,10 @@ start_server {tags {"introspection"}} {
assert_match {*calls=1,*} [cmdstat geoadd]
} {} {needs:config-resetstat}
+ test {COMMAND COUNT get total number of Redis commands} {
+ assert_morethan [r command count] 0
+ }
+
test {COMMAND GETKEYS GET} {
assert_equal {key} [r command getkeys get key]
}
diff --git a/tests/unit/introspection.tcl b/tests/unit/introspection.tcl
index 4452de6b1..76d56ee65 100644
--- a/tests/unit/introspection.tcl
+++ b/tests/unit/introspection.tcl
@@ -340,10 +340,10 @@ start_server {tags {"introspection"}} {
r client info
} {*lib-name=redis.py lib-ver=1.2.3*}
- test {RESET doesn NOT clean library name} {
+ test {RESET does NOT clean library name} {
r reset
r client info
- } {*lib-name=redis.py*}
+ } {*lib-name=redis.py*} {needs:reset}
test {CLIENT SETINFO can clear library name} {
r CLIENT SETINFO lib-name ""
@@ -362,18 +362,13 @@ start_server {tags {"introspection"}} {
assert_match [r config get save] {save {100 100}}
}
- # First "save" keyword in default config file
- start_server {config "default.conf"} {
- assert_match [r config get save] {save {900 1}}
- }
-
# First "save" keyword appends default from config file
- start_server {config "default.conf" args {--save 100 100}} {
+ start_server {config "default.conf" overrides {save {900 1}} args {--save 100 100}} {
assert_match [r config get save] {save {900 1 100 100}}
}
# Empty "save" keyword resets all
- start_server {config "default.conf" args {--save {}}} {
+ start_server {config "default.conf" overrides {save {900 1}} args {--save {}}} {
assert_match [r config get save] {save {}}
}
} {} {external:skip}
@@ -789,7 +784,7 @@ start_server {config "minimal.conf" tags {"introspection external:skip"} overrid
}
test {config during loading} {
- start_server [list overrides [list key-load-delay 50 loading-process-events-interval-bytes 1024 rdbcompression no]] {
+ start_server [list overrides [list key-load-delay 50 loading-process-events-interval-bytes 1024 rdbcompression no save "900 1"]] {
# create a big rdb that will take long to load. it is important
# for keys to be big since the server processes events only once in 2mb.
# 100mb of rdb, 100k keys will load in more than 5 seconds
diff --git a/tests/unit/maxmemory.tcl b/tests/unit/maxmemory.tcl
index 564250f2e..54aba6715 100644
--- a/tests/unit/maxmemory.tcl
+++ b/tests/unit/maxmemory.tcl
@@ -350,7 +350,7 @@ proc test_slave_buffers {test_name cmd_count payload_len limit_memory pipeline}
# put the slave to sleep
set rd_slave [redis_deferring_client]
- exec kill -SIGSTOP $slave_pid
+ pause_process $slave_pid
# send some 10mb worth of commands that don't increase the memory usage
if {$pipeline == 1} {
@@ -399,7 +399,7 @@ proc test_slave_buffers {test_name cmd_count payload_len limit_memory pipeline}
}
# unfreeze slave process (after the 'test' succeeded or failed, but before we attempt to terminate the server
- exec kill -SIGCONT $slave_pid
+ resume_process $slave_pid
}
}
}
diff --git a/tests/unit/moduleapi/cluster.tcl b/tests/unit/moduleapi/cluster.tcl
index 43356f77d..807508387 100644
--- a/tests/unit/moduleapi/cluster.tcl
+++ b/tests/unit/moduleapi/cluster.tcl
@@ -132,7 +132,7 @@ start_cluster 3 0 [list config_lines $modules] {
test "Kill a cluster node and wait for fail state" {
# kill node3 in cluster
- exec kill -SIGSTOP $node3_pid
+ pause_process $node3_pid
wait_for_condition 1000 50 {
[CI 0 cluster_state] eq {fail} &&
@@ -158,7 +158,7 @@ start_cluster 3 0 [list config_lines $modules] {
assert_error "ERR Can not execute a command 'set' while the cluster is down" {$node1 do_rm_call set x 1}
}
- exec kill -SIGCONT $node3_pid
+ resume_process $node3_pid
$node1_rd close
$node2_rd close
}
diff --git a/tests/unit/moduleapi/misc.tcl b/tests/unit/moduleapi/misc.tcl
index 6bf7b8c2a..9b0989149 100644
--- a/tests/unit/moduleapi/misc.tcl
+++ b/tests/unit/moduleapi/misc.tcl
@@ -1,6 +1,6 @@
set testmodule [file normalize tests/modules/misc.so]
-start_server {tags {"modules"}} {
+start_server {overrides {save {900 1}} tags {"modules"}} {
r module load $testmodule
test {test RM_Call} {
diff --git a/tests/unit/moduleapi/rdbloadsave.tcl b/tests/unit/moduleapi/rdbloadsave.tcl
new file mode 100644
index 000000000..9319c9385
--- /dev/null
+++ b/tests/unit/moduleapi/rdbloadsave.tcl
@@ -0,0 +1,200 @@
+set testmodule [file normalize tests/modules/rdbloadsave.so]
+
+start_server {tags {"modules"}} {
+ r module load $testmodule
+
+ test "Module rdbloadsave sanity" {
+ r test.sanity
+
+ # Try to load non-existing file
+ assert_error {*No such file or directory*} {r test.rdbload sanity.rdb}
+
+ r set x 1
+ assert_equal OK [r test.rdbsave sanity.rdb]
+
+ r flushdb
+ assert_equal OK [r test.rdbload sanity.rdb]
+ assert_equal 1 [r get x]
+ }
+
+ test "Module rdbloadsave test with pipelining" {
+ r config set save ""
+ r config set loading-process-events-interval-bytes 1024
+ r config set key-load-delay 50
+ r flushdb
+
+ populate 3000 a 1024
+ r set x 111
+ assert_equal [r dbsize] 3001
+
+ assert_equal OK [r test.rdbsave blabla.rdb]
+ r flushdb
+ assert_equal [r dbsize] 0
+
+ # Send commands with pipeline. First command will call RM_RdbLoad() in
+ # the command callback. While loading RDB, Redis can go to networking to
+ # reply -LOADING. By sending commands in pipeline, we verify it doesn't
+ # cause a problem.
+ # e.g. Redis won't try to process next message of the current client
+ # while it is in the command callback for that client .
+ set rd1 [redis_deferring_client]
+ $rd1 test.rdbload blabla.rdb
+
+ wait_for_condition 50 100 {
+ [s loading] eq 1
+ } else {
+ fail "Redis did not start loading or loaded RDB too fast"
+ }
+
+ $rd1 get x
+ $rd1 dbsize
+
+ assert_equal OK [$rd1 read]
+ assert_equal 111 [$rd1 read]
+ assert_equal 3001 [$rd1 read]
+ r flushdb
+ r config set key-load-delay 0
+ }
+
+ test "Module rdbloadsave with aof" {
+ r config set save ""
+
+ # Enable the AOF
+ r config set appendonly yes
+ r config set auto-aof-rewrite-percentage 0 ; # Disable auto-rewrite.
+ waitForBgrewriteaof r
+
+ r set k v1
+ assert_equal OK [r test.rdbsave aoftest.rdb]
+
+ r set k v2
+ r config set rdb-key-save-delay 10000000
+ r bgrewriteaof
+
+ # RM_RdbLoad() should kill aof fork
+ assert_equal OK [r test.rdbload aoftest.rdb]
+
+ wait_for_condition 50 100 {
+ [string match {*Killing*AOF*child*} [exec tail -20 < [srv 0 stdout]]]
+ } else {
+ fail "Can't find 'Killing AOF child' in recent log lines"
+ }
+
+ # Verify the value in the loaded rdb
+ assert_equal v1 [r get k]
+
+ r flushdb
+ r config set rdb-key-save-delay 0
+ r config set appendonly no
+ }
+
+ test "Module rdbloadsave with bgsave" {
+ r flushdb
+ r config set save ""
+
+ r set k v1
+ assert_equal OK [r test.rdbsave bgsave.rdb]
+
+ r set k v2
+ r config set rdb-key-save-delay 500000
+ r bgsave
+
+ # RM_RdbLoad() should kill RDB fork
+ assert_equal OK [r test.rdbload bgsave.rdb]
+
+ wait_for_condition 10 1000 {
+ [string match {*Background*saving*terminated*} [exec tail -20 < [srv 0 stdout]]]
+ } else {
+ fail "Can't find 'Background saving terminated' in recent log lines"
+ }
+
+ assert_equal v1 [r get k]
+ r flushall
+ waitForBgsave r
+ r config set rdb-key-save-delay 0
+ }
+
+ test "Module rdbloadsave calls rdbsave in a module fork" {
+ r flushdb
+ r config set save ""
+ r config set rdb-key-save-delay 500000
+
+ r set k v1
+
+ # Module will call RM_Fork() before calling RM_RdbSave()
+ assert_equal OK [r test.rdbsave_fork rdbfork.rdb]
+ assert_equal [s module_fork_in_progress] 1
+
+ wait_for_condition 10 1000 {
+ [status r module_fork_in_progress] == "0"
+ } else {
+ fail "Module fork didn't finish"
+ }
+
+ r set k v2
+ assert_equal OK [r test.rdbload rdbfork.rdb]
+ assert_equal v1 [r get k]
+
+ r config set rdb-key-save-delay 0
+ }
+
+ test "Unload the module - rdbloadsave" {
+ assert_equal {OK} [r module unload rdbloadsave]
+ }
+
+ tags {repl} {
+ test {Module rdbloadsave on master and replica} {
+ start_server [list overrides [list loadmodule "$testmodule"]] {
+ set replica [srv 0 client]
+ set replica_host [srv 0 host]
+ set replica_port [srv 0 port]
+ start_server [list overrides [list loadmodule "$testmodule"]] {
+ set master [srv 0 client]
+ set master_host [srv 0 host]
+ set master_port [srv 0 port]
+
+ $master set x 10000
+
+ # Start the replication process...
+ $replica replicaof $master_host $master_port
+
+ wait_for_condition 100 100 {
+ [status $master sync_full] == 1
+ } else {
+ fail "Master <-> Replica didn't start the full sync"
+ }
+
+ # RM_RdbSave() is allowed on replicas
+ assert_equal OK [$replica test.rdbsave rep.rdb]
+
+ # RM_RdbLoad() is not allowed on replicas
+ assert_error {*supported*} {$replica test.rdbload rep.rdb}
+
+ assert_equal OK [$master test.rdbsave master.rdb]
+ $master set x 20000
+
+ wait_for_condition 100 100 {
+ [$replica get x] == 20000
+ } else {
+ fail "Replica didn't get the update"
+ }
+
+ # Loading RDB on master will drop replicas
+ assert_equal OK [$master test.rdbload master.rdb]
+
+ wait_for_condition 100 100 {
+ [status $master sync_full] == 2
+ } else {
+ fail "Master <-> Replica didn't start the full sync"
+ }
+
+ wait_for_condition 100 100 {
+ [$replica get x] == 10000
+ } else {
+ fail "Replica didn't get the update"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/unit/moduleapi/reply.tcl b/tests/unit/moduleapi/reply.tcl
index 291253d3c..547be21c0 100644
--- a/tests/unit/moduleapi/reply.tcl
+++ b/tests/unit/moduleapi/reply.tcl
@@ -126,6 +126,17 @@ start_server {tags {"modules"}} {
assert_match "An error" $e
}
+ test "RESP$proto: RM_ReplyWithErrorFormat: error format reply" {
+ catch {r rw.error_format "An error: %s" foo} e
+ assert_match "ERR An error: foo" $e
+
+ catch {r rw.error_format "-ERR An error: %s" foo2} e
+ assert_match "ERR An error: foo2" $e
+
+ catch {r rw.error_format "-WRONGTYPE A type error: %s" foo3} e
+ assert_match "WRONGTYPE A type error: foo3" $e
+ }
+
r hello 2
}
diff --git a/tests/unit/moduleapi/stream.tcl b/tests/unit/moduleapi/stream.tcl
index 80c24ff6c..7ad1a3059 100644
--- a/tests/unit/moduleapi/stream.tcl
+++ b/tests/unit/moduleapi/stream.tcl
@@ -61,6 +61,23 @@ start_server {tags {"modules"}} {
assert_equal $result $n
}
+ test {Module stream XADD big fields doesn't create empty key} {
+ set original_proto [config_get_set proto-max-bulk-len 2147483647] ;#2gb
+ set original_query [config_get_set client-query-buffer-limit 2147483647] ;#2gb
+
+ r del mystream
+ r write "*4\r\n\$10\r\nstream.add\r\n\$8\r\nmystream\r\n\$5\r\nfield\r\n"
+ catch {
+ write_big_bulk 1073741824 ;#1gb
+ } err
+ assert {$err eq "ERR StreamAdd failed"}
+ assert_equal 0 [r exists mystream]
+
+ # restore defaults
+ r config set proto-max-bulk-len $original_proto
+ r config set client-query-buffer-limit $original_query
+ } {OK} {large-memory}
+
test {Module stream iterator} {
r del mystream
set streamid1 [r xadd mystream * item 1 value a]
diff --git a/tests/unit/moduleapi/testrdb.tcl b/tests/unit/moduleapi/testrdb.tcl
index 2545a8ad2..ae3036f70 100644
--- a/tests/unit/moduleapi/testrdb.tcl
+++ b/tests/unit/moduleapi/testrdb.tcl
@@ -61,13 +61,13 @@ tags "modules" {
# 7 == 0111 - use aux_save2 before and after key space with data
test {modules are able to persist globals before and after} {
set server_path [tmpdir "server.module-testrdb"]
- start_server [list overrides [list loadmodule "$testmodule $test_case" "dir" $server_path] keep_persistence true] {
+ start_server [list overrides [list loadmodule "$testmodule $test_case" "dir" $server_path "save" "900 1"] keep_persistence true] {
r testrdb.set.before global1
r testrdb.set.after global2
assert_equal "global1" [r testrdb.get.before]
assert_equal "global2" [r testrdb.get.after]
}
- start_server [list overrides [list loadmodule "$testmodule $test_case" "dir" $server_path]] {
+ start_server [list overrides [list loadmodule "$testmodule $test_case" "dir" $server_path "save" "900 1"]] {
assert_equal "global1" [r testrdb.get.before]
assert_equal "global2" [r testrdb.get.after]
}
@@ -80,11 +80,11 @@ tags "modules" {
# 5 == 0101 - use aux_save2 after key space with data
test {modules are able to persist globals just after} {
set server_path [tmpdir "server.module-testrdb"]
- start_server [list overrides [list loadmodule "$testmodule $test_case" "dir" $server_path] keep_persistence true] {
+ start_server [list overrides [list loadmodule "$testmodule $test_case" "dir" $server_path "save" "900 1"] keep_persistence true] {
r testrdb.set.after global2
assert_equal "global2" [r testrdb.get.after]
}
- start_server [list overrides [list loadmodule "$testmodule $test_case" "dir" $server_path]] {
+ start_server [list overrides [list loadmodule "$testmodule $test_case" "dir" $server_path "save" "900 1"]] {
assert_equal "global2" [r testrdb.get.after]
}
}
diff --git a/tests/unit/moduleapi/zset.tcl b/tests/unit/moduleapi/zset.tcl
index 1c146eaf4..b6ab41d5f 100644
--- a/tests/unit/moduleapi/zset.tcl
+++ b/tests/unit/moduleapi/zset.tcl
@@ -14,6 +14,26 @@ start_server {tags {"modules"}} {
assert_equal 0 [r exists k]
}
+ test {Module zset add} {
+ r del k
+ # Check that failure does not create empty key
+ assert_error "ERR ZsetAdd failed" {r zset.add k nan hello}
+ assert_equal 0 [r exists k]
+
+ r zset.add k 100 hello
+ assert_equal {hello 100} [r zrange k 0 -1 withscores]
+ }
+
+ test {Module zset incrby} {
+ r del k
+ # Check that failure does not create empty key
+ assert_error "ERR ZsetIncrby failed" {r zset.incrby k hello nan}
+ assert_equal 0 [r exists k]
+
+ r zset.incrby k hello 100
+ assert_equal {hello 100} [r zrange k 0 -1 withscores]
+ }
+
test "Unload the module - zset" {
assert_equal {OK} [r module unload zset]
}
diff --git a/tests/unit/multi.tcl b/tests/unit/multi.tcl
index bdf398150..851e02247 100644
--- a/tests/unit/multi.tcl
+++ b/tests/unit/multi.tcl
@@ -891,14 +891,14 @@ start_server {tags {"multi"}} {
set res [r read]
assert_equal $res "+OK"
set res [r read]
- r readraw 1
+ r readraw 0
set _ $res
} {*CONFIG SET failed*}
test "Flushall while watching several keys by one client" {
r flushall
- r mset a a b b
- r watch b a
+ r mset a{t} a b{t} b
+ r watch b{t} a{t}
r flushall
r ping
}
diff --git a/tests/unit/querybuf.tcl b/tests/unit/querybuf.tcl
index bbbea12f4..f4859dd27 100644
--- a/tests/unit/querybuf.tcl
+++ b/tests/unit/querybuf.tcl
@@ -5,7 +5,7 @@ proc client_idle_sec {name} {
return $idle
}
-# Calculate query buffer memory of slave
+# Calculate query buffer memory of client
proc client_query_buffer {name} {
set clients [split [r client list] "\r\n"]
set c [lsearch -inline $clients *name=$name*]
@@ -18,6 +18,9 @@ proc client_query_buffer {name} {
}
start_server {tags {"querybuf slow"}} {
+ # increase the execution frequency of clientsCron
+ r config set hz 100
+
# The test will run at least 2s to check if client query
# buffer will be resized when client idle 2s.
test "query buffer resized correctly" {
@@ -38,6 +41,9 @@ start_server {tags {"querybuf slow"}} {
}
test "query buffer resized correctly when not idle" {
+ # Pause cron to prevent premature shrinking (timing issue).
+ r debug pause-cron 1
+
# Memory will increase by more than 32k due to client query buffer.
set rd [redis_client]
$rd client setname test_client
@@ -49,6 +55,8 @@ start_server {tags {"querybuf slow"}} {
set orig_test_client_qbuf [client_query_buffer test_client]
assert {$orig_test_client_qbuf > 32768}
+ r debug pause-cron 0
+
# Wait for qbuf to shrink due to lower peak
set t [clock milliseconds]
while true {
@@ -62,5 +70,27 @@ start_server {tags {"querybuf slow"}} {
# Validate qbuf shrunk but isn't 0 since we maintain room based on latest peak
assert {[client_query_buffer test_client] > 0 && [client_query_buffer test_client] < $orig_test_client_qbuf}
$rd close
+ } {0} {needs:debug}
+
+ test "query buffer resized correctly with fat argv" {
+ set rd [redis_client]
+ $rd client setname test_client
+ $rd write "*3\r\n\$3\r\nset\r\n\$1\r\na\r\n\$1000000\r\n"
+ $rd flush
+
+ after 20
+ if {[client_query_buffer test_client] < 1000000} {
+ fail "query buffer should not be resized when client idle time smaller than 2s"
+ }
+
+ # Check that the query buffer is resized after 2 sec
+ wait_for_condition 1000 10 {
+ [client_idle_sec test_client] >= 3 && [client_query_buffer test_client] < 1000000
+ } else {
+ fail "query buffer should be resized when client idle time bigger than 2s"
+ }
+
+ $rd close
}
+
}
diff --git a/tests/unit/shutdown.tcl b/tests/unit/shutdown.tcl
index b419c83a1..7504851a1 100644
--- a/tests/unit/shutdown.tcl
+++ b/tests/unit/shutdown.tcl
@@ -30,7 +30,7 @@ start_server {tags {"shutdown external:skip"}} {
}
}
-start_server {tags {"shutdown external:skip"}} {
+start_server {tags {"shutdown external:skip"} overrides {save {900 1}}} {
test {SHUTDOWN ABORT can cancel SIGTERM} {
r debug pause-cron 1
set pid [s process_id]
@@ -48,7 +48,7 @@ start_server {tags {"shutdown external:skip"}} {
}
# It will cost 2s (20 * 100ms) to dump rdb
r config set rdb-key-save-delay 100000
-
+
set pid [s process_id]
set temp_rdb [file join [lindex [r config get dir] 1] temp-${pid}.rdb]
@@ -72,7 +72,7 @@ start_server {tags {"shutdown external:skip"}} {
}
}
-start_server {tags {"shutdown external:skip"}} {
+start_server {tags {"shutdown external:skip"} overrides {save {900 1}}} {
set pid [s process_id]
set dump_rdb [file join [lindex [r config get dir] 1] dump.rdb]
@@ -107,3 +107,27 @@ start_server {tags {"shutdown external:skip"}} {
exec rm -r $dump_rdb
}
}
+
+
+start_server {tags {"shutdown external:skip"} overrides {appendonly no}} {
+ test {SHUTDOWN SIGTERM will abort if there's an initial AOFRW - default} {
+ r config set shutdown-on-sigterm default
+ r config set rdb-key-save-delay 10000000
+ for {set i 0} {$i < 10} {incr i} {
+ r set $i $i
+ }
+
+ r config set appendonly yes
+ wait_for_condition 1000 10 {
+ [s aof_rewrite_in_progress] eq 1
+ } else {
+ fail "aof rewrite did not start in time"
+ }
+
+ set pid [s process_id]
+ exec kill -SIGTERM $pid
+ wait_for_log_messages 0 {"*Writing initial AOF, can't exit*"} 0 1000 10
+
+ r config set shutdown-on-sigterm force
+ }
+}
diff --git a/tests/unit/slowlog.tcl b/tests/unit/slowlog.tcl
index bc15c6411..3c547b924 100644
--- a/tests/unit/slowlog.tcl
+++ b/tests/unit/slowlog.tcl
@@ -24,8 +24,11 @@ start_server {tags {"slowlog"} overrides {slowlog-log-slower-than 1000000}} {
} {10}
test {SLOWLOG - GET optional argument to limit output len works} {
- llength [r slowlog get 5]
- } {5}
+
+ assert_equal 5 [llength [r slowlog get 5]]
+ assert_equal 10 [llength [r slowlog get -1]]
+ assert_equal 10 [llength [r slowlog get 20]]
+ }
test {SLOWLOG - RESET subcommand works} {
r config set slowlog-log-slower-than 100000
@@ -39,7 +42,7 @@ start_server {tags {"slowlog"} overrides {slowlog-log-slower-than 1000000}} {
set e [lindex [r slowlog get] 0]
assert_equal [llength $e] 6
if {!$::external} {
- assert_equal [lindex $e 0] 105
+ assert_equal [lindex $e 0] 107
}
assert_equal [expr {[lindex $e 2] > 100000}] 1
assert_equal [lindex $e 3] {debug sleep 0.2}
diff --git a/tests/unit/type/hash.tcl b/tests/unit/type/hash.tcl
index 17e3ba40b..4cff0f244 100644
--- a/tests/unit/type/hash.tcl
+++ b/tests/unit/type/hash.tcl
@@ -350,9 +350,16 @@ start_server {tags {"hash"}} {
set _ $rv
} {{{} {}} {{} {}} {{} {}}}
- test {HMGET against wrong type} {
+ test {HMGET HRANDFIELD HGET HGETALL HDEL HINCRBY HINCRBYFLOAT HSTRLEN against wrong type} {
r set wrongtype somevalue
- assert_error "*wrong*" {r hmget wrongtype field1 field2}
+ assert_error "WRONGTYPE Operation against a key*" {r hmget wrongtype field1 field2}
+ assert_error "WRONGTYPE Operation against a key*" {r hrandfield wrongtype}
+ assert_error "WRONGTYPE Operation against a key*" {r hget wrongtype field1}
+ assert_error "WRONGTYPE Operation against a key*" {r hgetall wrongtype}
+ assert_error "WRONGTYPE Operation against a key*" {r hdel wrongtype field1}
+ assert_error "WRONGTYPE Operation against a key*" {r hincrby wrongtype field1 2}
+ assert_error "WRONGTYPE Operation against a key*" {r hincrbyfloat wrongtype field1 2.5}
+ assert_error "WRONGTYPE Operation against a key*" {r hstrlen wrongtype field1}
}
test {HMGET - small hash} {
diff --git a/tests/unit/type/incr.tcl b/tests/unit/type/incr.tcl
index a64f357ae..c09f2e8b2 100644
--- a/tests/unit/type/incr.tcl
+++ b/tests/unit/type/incr.tcl
@@ -13,6 +13,12 @@ start_server {tags {"incr"}} {
r decr novar
} {1}
+ test {DECR against key is not exist and incr} {
+ r del novar_not_exist
+ assert_equal {-1} [r decr novar_not_exist]
+ assert_equal {0} [r incr novar_not_exist]
+ }
+
test {INCR against key originally set with SET} {
r set novar 100
r incr novar
@@ -64,6 +70,11 @@ start_server {tags {"incr"}} {
r decrby novar 17179869185
} {-1}
+ test {DECRBY against key is not exist} {
+ r del key_not_exist
+ assert_equal {-1} [r decrby key_not_exist 1]
+ }
+
test {INCR uses shared objects in the 0-9999 range} {
r set foo -1
r incr foo
diff --git a/tests/unit/type/list.tcl b/tests/unit/type/list.tcl
index d970b0278..a57e5df3e 100644
--- a/tests/unit/type/list.tcl
+++ b/tests/unit/type/list.tcl
@@ -464,6 +464,7 @@ foreach {type large} [array get largevalue] {
assert {[r LPOS mylist c RANK -1] == 7}
assert {[r LPOS mylist c RANK -2] == 6}
assert_error "*RANK can't be zero: use 1 to start from the first match, 2 from the second ... or use negative to start*" {r LPOS mylist c RANK 0}
+ assert_error "*value is out of range*" {r LPOS mylist c RANK -9223372036854775808}
}
test {LPOS COUNT option} {
@@ -1416,6 +1417,15 @@ foreach {pop} {BLPOP BLMPOP_LEFT} {
set e
} {*ERR*syntax*error*}
+ test {LINSERT against non-list value error} {
+ r set k1 v1
+ assert_error {WRONGTYPE Operation against a key holding the wrong kind of value*} {r linsert k1 after 0 0}
+ }
+
+ test {LINSERT against non existing key} {
+ assert_equal 0 [r linsert not-a-key before 0 0]
+ }
+
foreach type {listpack quicklist} {
foreach {num} {250 500} {
if {$type == "quicklist"} {
diff --git a/tests/unit/type/string.tcl b/tests/unit/type/string.tcl
index b25a14f4c..68c360b97 100644
--- a/tests/unit/type/string.tcl
+++ b/tests/unit/type/string.tcl
@@ -151,6 +151,14 @@ start_server {tags {"string"}} {
set ex
} {*syntax*}
+ test "GETEX and GET expired key or not exist" {
+ r del foo
+ r set foo bar px 1
+ after 2
+ assert_equal {} [r getex foo]
+ assert_equal {} [r get foo]
+ }
+
test "GETEX no arguments" {
set ex {}
catch {r getex} ex
@@ -438,6 +446,11 @@ start_server {tags {"string"}} {
assert_equal "" [r getrange mykey 0 -1]
}
+ test "GETRANGE against wrong key type" {
+ r lpush lkey1 "list"
+ assert_error {WRONGTYPE Operation against a key holding the wrong kind of value*} {r getrange lkey1 0 -1}
+ }
+
test "GETRANGE against string value" {
r set mykey "Hello World"
assert_equal "Hell" [r getrange mykey 0 3]
@@ -474,6 +487,9 @@ start_server {tags {"string"}} {
assert_equal "a" [r substr key 0 0]
assert_equal "abcd" [r substr key 0 3]
assert_equal "bcde" [r substr key -4 -1]
+ assert_equal "" [r substr key -1 -3]
+ assert_equal "" [r substr key 7 8]
+ assert_equal "" [r substr nokey 0 1]
}
if {[string match {*jemalloc*} [s mem_allocator]]} {
diff --git a/tests/unit/type/zset.tcl b/tests/unit/type/zset.tcl
index a52a77f24..d1da02778 100644
--- a/tests/unit/type/zset.tcl
+++ b/tests/unit/type/zset.tcl
@@ -755,6 +755,46 @@ start_server {tags {"zset"}} {
assert_equal 0 [r exists zset]
}
+ test "ZREMRANGEBYLEX basics - $encoding" {
+ proc remrangebylex {min max} {
+ create_default_lex_zset
+ assert_equal 1 [r exists zset]
+ r zremrangebylex zset $min $max
+ }
+
+ # inclusive range
+ assert_equal 3 [remrangebylex - \[cool]
+ assert_equal {down elephant foo great hill omega} [r zrange zset 0 -1]
+ assert_equal 3 [remrangebylex \[bar \[down]
+ assert_equal {alpha elephant foo great hill omega} [r zrange zset 0 -1]
+ assert_equal 3 [remrangebylex \[g +]
+ assert_equal {alpha bar cool down elephant foo} [r zrange zset 0 -1]
+ assert_equal 6 [r zcard zset]
+
+ # exclusive range
+ assert_equal 2 [remrangebylex - (cool]
+ assert_equal {cool down elephant foo great hill omega} [r zrange zset 0 -1]
+ assert_equal 1 [remrangebylex (bar (down]
+ assert_equal {alpha bar down elephant foo great hill omega} [r zrange zset 0 -1]
+ assert_equal 2 [remrangebylex (great +]
+ assert_equal {alpha bar cool down elephant foo great} [r zrange zset 0 -1]
+ assert_equal 7 [r zcard zset]
+
+ # inclusive and exclusive
+ assert_equal 0 [remrangebylex (az (b]
+ assert_equal {alpha bar cool down elephant foo great hill omega} [r zrange zset 0 -1]
+ assert_equal 0 [remrangebylex (z +]
+ assert_equal {alpha bar cool down elephant foo great hill omega} [r zrange zset 0 -1]
+ assert_equal 0 [remrangebylex - \[aaaa]
+ assert_equal {alpha bar cool down elephant foo great hill omega} [r zrange zset 0 -1]
+ assert_equal 9 [r zcard zset]
+
+ # destroy when empty
+ assert_equal 9 [remrangebylex - +]
+ assert_equal 0 [r zcard zset]
+ assert_equal 0 [r exists zset]
+ }
+
test "ZUNIONSTORE against non-existing key doesn't set destination - $encoding" {
r del zseta{t}
assert_equal 0 [r zunionstore dst_key{t} 1 zseta{t}]
diff --git a/tests/unit/wait.tcl b/tests/unit/wait.tcl
index 08a7a71f6..8c6010afb 100644
--- a/tests/unit/wait.tcl
+++ b/tests/unit/wait.tcl
@@ -47,25 +47,25 @@ start_server {} {
}
test {WAIT should not acknowledge 1 additional copy if slave is blocked} {
- exec kill -SIGSTOP $slave_pid
+ pause_process $slave_pid
$master set foo 0
$master incr foo
$master incr foo
$master incr foo
assert {[$master wait 1 1000] == 0}
- exec kill -SIGCONT $slave_pid
+ resume_process $slave_pid
assert {[$master wait 1 1000] == 1}
}
test {WAIT implicitly blocks on client pause since ACKs aren't sent} {
- exec kill -SIGSTOP $slave_pid
+ pause_process $slave_pid
$master multi
$master incr foo
$master client pause 10000 write
$master exec
assert {[$master wait 1 1000] == 0}
$master client unpause
- exec kill -SIGCONT $slave_pid
+ resume_process $slave_pid
assert {[$master wait 1 1000] == 1}
}
@@ -73,7 +73,7 @@ start_server {} {
set rd [redis_deferring_client -1]
set rd2 [redis_deferring_client -1]
- exec kill -SIGSTOP $slave_pid
+ pause_process $slave_pid
$rd incr foo
$rd read
@@ -85,7 +85,7 @@ start_server {} {
$rd2 wait 1 0
wait_for_blocked_clients_count 2 100 10 -1
- exec kill -SIGCONT $slave_pid
+ resume_process $slave_pid
assert_equal [$rd read] {1}
assert_equal [$rd2 read] {1}
@@ -175,7 +175,49 @@ tags {"wait aof network external:skip"} {
$replica config set appendfsync everysec
test {WAITAOF replica copy everysec} {
+ $replica config set appendfsync everysec
+ waitForBgrewriteaof $replica ;# Make sure there is no AOFRW
+
+ $master incr foo
+ assert_equal [$master waitaof 0 1 0] {1 1}
+ }
+
+ test {WAITAOF replica copy everysec with AOFRW} {
+ $replica config set appendfsync everysec
+
+ # When we trigger an AOFRW, a fsync is triggered when closing the old INCR file,
+ # so with the everysec, we will skip that second of fsync, and in the next second
+ # after that, we will eventually do the fsync.
+ $replica bgrewriteaof
+ waitForBgrewriteaof $replica
+
+ $master incr foo
+ assert_equal [$master waitaof 0 1 0] {1 1}
+ }
+
+ test {WAITAOF replica copy everysec with slow AOFRW} {
+ $replica config set appendfsync everysec
+ $replica config set rdb-key-save-delay 1000000 ;# 1 sec
+
+ $replica bgrewriteaof
+
+ $master incr foo
+ assert_equal [$master waitaof 0 1 0] {1 1}
+
+ $replica config set rdb-key-save-delay 0
+ waitForBgrewriteaof $replica
+ }
+
+ test {WAITAOF replica copy everysec->always with AOFRW} {
+ $replica config set appendfsync everysec
+
+ # Try to fit all of them in the same round second, although there's no way to guarantee
+ # that, it can be done on fast machine. In any case, the test shouldn't fail either.
+ $replica bgrewriteaof
$master incr foo
+ waitForBgrewriteaof $replica
+ $replica config set appendfsync always
+
assert_equal [$master waitaof 0 1 0] {1 1}
}
@@ -187,10 +229,10 @@ tags {"wait aof network external:skip"} {
}
test {WAITAOF replica copy if replica is blocked} {
- exec kill -SIGSTOP $replica_pid
+ pause_process $replica_pid
$master incr foo
assert_equal [$master waitaof 0 1 50] {1 0} ;# exits on timeout
- exec kill -SIGCONT $replica_pid
+ resume_process $replica_pid
assert_equal [$master waitaof 0 1 0] {1 1}
}
@@ -198,7 +240,7 @@ tags {"wait aof network external:skip"} {
set rd [redis_deferring_client -1]
set rd2 [redis_deferring_client -1]
- exec kill -SIGSTOP $replica_pid
+ pause_process $replica_pid
$rd incr foo
$rd read
@@ -210,7 +252,7 @@ tags {"wait aof network external:skip"} {
$rd2 waitaof 0 1 0
wait_for_blocked_clients_count 2 100 10 -1
- exec kill -SIGCONT $replica_pid
+ resume_process $replica_pid
assert_equal [$rd read] {1 1}
assert_equal [$rd2 read] {1 1}
@@ -396,7 +438,7 @@ start_server {} {
waitForBgrewriteaof $replica1
waitForBgrewriteaof $replica2
- exec kill -SIGSTOP $replica1_pid
+ pause_process $replica1_pid
$rd incr foo
$rd read
@@ -409,7 +451,7 @@ start_server {} {
wait_for_blocked_clients_count 2
- exec kill -SIGCONT $replica1_pid
+ resume_process $replica1_pid
# WAIT will unblock the client first.
assert_equal [$rd2 read] {2}
diff --git a/utils/generate-command-code.py b/utils/generate-command-code.py
index 81d8c19f1..dc66ce81f 100755
--- a/utils/generate-command-code.py
+++ b/utils/generate-command-code.py
@@ -196,7 +196,7 @@ class Argument(object):
def struct_code(self):
"""
Output example:
- "expiration",ARG_TYPE_ONEOF,NULL,NULL,NULL,CMD_ARG_OPTIONAL,.value.subargs=SET_expiration_Subargs
+ MAKE_ARG("expiration",ARG_TYPE_ONEOF,-1,NULL,NULL,NULL,CMD_ARG_OPTIONAL,5,NULL),.subargs=GETEX_expiration_Subargs
"""
def _flags_code():
@@ -210,7 +210,7 @@ class Argument(object):
s += "CMD_ARG_MULTIPLE_TOKEN|"
return s[:-1] if s else "CMD_ARG_NONE"
- s = "\"%s\",%s,%d,%s,%s,%s,%s" % (
+ s = "MAKE_ARG(\"%s\",%s,%d,%s,%s,%s,%s,%d,%s)" % (
self.name,
ARG_TYPES[self.type],
self.desc.get("key_spec_index", -1),
@@ -218,9 +218,9 @@ class Argument(object):
get_optional_desc_string(self.desc, "summary"),
get_optional_desc_string(self.desc, "since"),
_flags_code(),
+ len(self.subargs),
+ get_optional_desc_string(self.desc, "deprecated_since"),
)
- if "deprecated_since" in self.desc:
- s += ",.deprecated_since=\"%s\"" % self.desc["deprecated_since"]
if "display" in self.desc:
s += ",.display_text=\"%s\"" % self.desc["display"].lower()
if self.subargs:
@@ -234,10 +234,9 @@ class Argument(object):
subarg.write_internal_structs(f)
f.write("/* %s argument table */\n" % self.fullname())
- f.write("struct redisCommandArg %s[] = {\n" % self.subarg_table_name())
+ f.write("struct COMMAND_ARG %s[] = {\n" % self.subarg_table_name())
for subarg in self.subargs:
f.write("{%s},\n" % subarg.struct_code())
- f.write("{0}\n")
f.write("};\n\n")
@@ -339,11 +338,14 @@ class Command(object):
return "%s_History" % (self.fullname().replace(" ", "_"))
def tips_table_name(self):
- return "%s_tips" % (self.fullname().replace(" ", "_"))
+ return "%s_Tips" % (self.fullname().replace(" ", "_"))
def arg_table_name(self):
return "%s_Args" % (self.fullname().replace(" ", "_"))
+ def key_specs_table_name(self):
+ return "%s_Keyspecs" % (self.fullname().replace(" ", "_"))
+
def reply_schema_name(self):
return "%s_ReplySchema" % (self.fullname().replace(" ", "_"))
@@ -356,22 +358,37 @@ class Command(object):
s = ""
for tupl in self.desc["history"]:
s += "{\"%s\",\"%s\"},\n" % (tupl[0], tupl[1])
- s += "{0}"
return s
+ def num_history(self):
+ if not self.desc.get("history"):
+ return 0
+ return len(self.desc["history"])
+
def tips_code(self):
if not self.desc.get("command_tips"):
return ""
s = ""
for hint in self.desc["command_tips"]:
s += "\"%s\",\n" % hint.lower()
- s += "NULL"
return s
+ def num_tips(self):
+ if not self.desc.get("command_tips"):
+ return 0
+ return len(self.desc["command_tips"])
+
+ def key_specs_code(self):
+ s = ""
+ for spec in self.key_specs:
+ s += "{%s}," % KeySpec(spec).struct_code()
+ return s[:-1]
+
+
def struct_code(self):
"""
Output example:
- "set","Set the string value of a key","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,COMMAND_GROUP_STRING,SET_History,SET_tips,setCommand,-3,"write denyoom @string",{{"write read",KSPEC_BS_INDEX,.bs.index={1},KSPEC_FK_RANGE,.fk.range={0,1,0}}},.args=SET_Args
+ MAKE_CMD("set","Set the string value of a key","O(1)","1.0.0",CMD_DOC_NONE,NULL,NULL,"string",COMMAND_GROUP_STRING,SET_History,4,SET_Tips,0,setCommand,-3,CMD_WRITE|CMD_DENYOOM,ACL_CATEGORY_STRING,SET_Keyspecs,1,setGetKeys,5),.args=SET_Args
"""
def _flags_code():
@@ -392,13 +409,7 @@ class Command(object):
s += "CMD_DOC_%s|" % flag
return s[:-1] if s else "CMD_DOC_NONE"
- def _key_specs_code():
- s = ""
- for spec in self.key_specs:
- s += "{%s}," % KeySpec(spec).struct_code()
- return s[:-1]
-
- s = "\"%s\",%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%d,%s,%s," % (
+ s = "MAKE_CMD(\"%s\",%s,%s,%s,%s,%s,%s,%s,%s,%s,%d,%s,%d,%s,%d,%s,%s,%s,%d,%s,%d)," % (
self.name.lower(),
get_optional_desc_string(self.desc, "summary"),
get_optional_desc_string(self.desc, "complexity"),
@@ -406,22 +417,22 @@ class Command(object):
_doc_flags_code(),
get_optional_desc_string(self.desc, "replaced_by"),
get_optional_desc_string(self.desc, "deprecated_since"),
+ "\"%s\"" % self.group,
GROUPS[self.group],
self.history_table_name(),
+ self.num_history(),
self.tips_table_name(),
+ self.num_tips(),
self.desc.get("function", "NULL"),
self.desc["arity"],
_flags_code(),
- _acl_categories_code()
+ _acl_categories_code(),
+ self.key_specs_table_name(),
+ len(self.key_specs),
+ self.desc.get("get_keys_function", "NULL"),
+ len(self.args),
)
- specs = _key_specs_code()
- if specs:
- s += "{%s}," % specs
-
- if self.desc.get("get_keys_function"):
- s += "%s," % self.desc["get_keys_function"]
-
if self.subcommands:
s += ".subcommands=%s," % self.subcommand_table_name()
@@ -440,7 +451,7 @@ class Command(object):
subcommand.write_internal_structs(f)
f.write("/* %s command table */\n" % self.fullname())
- f.write("struct redisCommand %s[] = {\n" % self.subcommand_table_name())
+ f.write("struct COMMAND_STRUCT %s[] = {\n" % self.subcommand_table_name())
for subcommand in subcommand_list:
f.write("{%s},\n" % subcommand.struct_code())
f.write("{0}\n")
@@ -448,33 +459,47 @@ class Command(object):
f.write("/********** %s ********************/\n\n" % self.fullname())
+ f.write("#ifndef SKIP_CMD_HISTORY_TABLE\n")
f.write("/* %s history */\n" % self.fullname())
code = self.history_code()
if code:
f.write("commandHistory %s[] = {\n" % self.history_table_name())
- f.write("%s\n" % code)
- f.write("};\n\n")
+ f.write("%s" % code)
+ f.write("};\n")
else:
- f.write("#define %s NULL\n\n" % self.history_table_name())
+ f.write("#define %s NULL\n" % self.history_table_name())
+ f.write("#endif\n\n")
+ f.write("#ifndef SKIP_CMD_TIPS_TABLE\n")
f.write("/* %s tips */\n" % self.fullname())
code = self.tips_code()
if code:
f.write("const char *%s[] = {\n" % self.tips_table_name())
+ f.write("%s" % code)
+ f.write("};\n")
+ else:
+ f.write("#define %s NULL\n" % self.tips_table_name())
+ f.write("#endif\n\n")
+
+ f.write("#ifndef SKIP_CMD_KEY_SPECS_TABLE\n")
+ f.write("/* %s key specs */\n" % self.fullname())
+ code = self.key_specs_code()
+ if code:
+ f.write("keySpec %s[%d] = {\n" % (self.key_specs_table_name(), len(self.key_specs)))
f.write("%s\n" % code)
- f.write("};\n\n")
+ f.write("};\n")
else:
- f.write("#define %s NULL\n\n" % self.tips_table_name())
+ f.write("#define %s NULL\n" % self.key_specs_table_name())
+ f.write("#endif\n\n")
if self.args:
for arg in self.args:
arg.write_internal_structs(f)
f.write("/* %s argument table */\n" % self.fullname())
- f.write("struct redisCommandArg %s[] = {\n" % self.arg_table_name())
+ f.write("struct COMMAND_ARG %s[] = {\n" % self.arg_table_name())
for arg in self.args:
f.write("{%s},\n" % arg.struct_code())
- f.write("{0}\n")
f.write("};\n\n")
if self.reply_schema and args.with_reply_schema:
@@ -543,15 +568,40 @@ if check_command_error_counter != 0:
exit(1)
commands_filename = "commands_with_reply_schema" if args.with_reply_schema else "commands"
-print("Generating %s.c..." % commands_filename)
-with open("%s/%s.c" % (srcdir, commands_filename), "w") as f:
+print("Generating %s.def..." % commands_filename)
+with open("%s/%s.def" % (srcdir, commands_filename), "w") as f:
f.write("/* Automatically generated by %s, do not edit. */\n\n" % os.path.basename(__file__))
- f.write("#include \"server.h\"\n")
f.write(
"""
/* We have fabulous commands from
* the fantastic
- * Redis Command Table! */\n
+ * Redis Command Table! */
+
+/* Must match redisCommandGroup */
+const char *COMMAND_GROUP_STR[] = {
+ "generic",
+ "string",
+ "list",
+ "set",
+ "sorted-set",
+ "hash",
+ "pubsub",
+ "transactions",
+ "connection",
+ "server",
+ "scripting",
+ "hyperloglog",
+ "cluster",
+ "sentinel",
+ "geo",
+ "stream",
+ "bitmap",
+ "module"
+};
+
+const char *commandGroupStr(int index) {
+ return COMMAND_GROUP_STR[index];
+}
"""
)
@@ -560,7 +610,7 @@ with open("%s/%s.c" % (srcdir, commands_filename), "w") as f:
command.write_internal_structs(f)
f.write("/* Main command table */\n")
- f.write("struct redisCommand redisCommandTable[] = {\n")
+ f.write("struct COMMAND_STRUCT redisCommandTable[] = {\n")
curr_group = None
for command in command_list:
if curr_group != command.group:
diff --git a/utils/generate-command-help.rb b/utils/generate-command-help.rb
deleted file mode 100755
index 1042ce6d2..000000000
--- a/utils/generate-command-help.rb
+++ /dev/null
@@ -1,151 +0,0 @@
-#!/usr/bin/env ruby -w
-# Usage: generate-command-help.r [path/to/commands.json]
-# or: generate-commands-json.py | generate-command-help.rb -
-#
-# Defaults to downloading commands.json from the redis-doc repo if not provided
-# or STDINed.
-
-GROUPS = [
- "generic",
- "string",
- "list",
- "set",
- "sorted-set",
- "hash",
- "pubsub",
- "transactions",
- "connection",
- "server",
- "scripting",
- "hyperloglog",
- "cluster",
- "geo",
- "stream",
- "bitmap"
-].freeze
-
-GROUPS_BY_NAME = Hash[*
- GROUPS.each_with_index.map do |n,i|
- [n,i]
- end.flatten
-].freeze
-
-def argument arg
- if "block" == arg["type"]
- name = arg["arguments"].map do |entry|
- argument entry
- end.join " "
- elsif "oneof" == arg["type"]
- name = arg["arguments"].map do |entry|
- argument entry
- end.join "|"
- elsif "pure-token" == arg["type"]
- name = nil # prepended later
- else
- name = arg["name"].is_a?(Array) ? arg["name"].join(" ") : arg["name"]
- end
- if arg["multiple"]
- if arg["multiple_token"]
- name = "#{name} [#{arg["token"]} #{name} ...]"
- else
- name = "#{name} [#{name} ...]"
- end
- end
- if arg["token"]
- name = [arg["token"], name].compact.join " "
- end
- if arg["optional"]
- name = "[#{name}]"
- end
- name
-end
-
-def arguments command
- return "" unless command["arguments"]
- command["arguments"].map do |arg|
- argument arg
- end.join " "
-end
-
-def commands
- return @commands if @commands
-
- require "rubygems"
- require "net/http"
- require "net/https"
- require "json"
- require "uri"
- if ARGV.length > 0
- if ARGV[0] == '-'
- data = STDIN.read
- elsif FileTest.exist? ARGV[0]
- data = File.read(ARGV[0])
- else
- raise Exception.new "File not found: #{ARGV[0]}"
- end
- else
- url = URI.parse "https://raw.githubusercontent.com/redis/redis-doc/master/commands.json"
- client = Net::HTTP.new url.host, url.port
- client.use_ssl = true
- response = client.get url.path
- if !response.is_a?(Net::HTTPSuccess)
- response.error!
- return
- else
- data = response.body
- end
- end
- @commands = JSON.parse(data)
-end
-
-def generate_groups
- GROUPS.map do |n|
- "\"#{n}\""
- end.join(",\n ");
-end
-
-def generate_commands
- commands.to_a.sort do |x,y|
- x[0] <=> y[0]
- end.map do |key, command|
- group = GROUPS_BY_NAME[command["group"]]
- if group.nil?
- STDERR.puts "Please update groups array in #{__FILE__}"
- raise "Unknown group #{command["group"]}"
- end
-
- ret = <<-SPEC
-{ "#{key}",
- "#{arguments(command)}",
- "#{command["summary"]}",
- #{group},
- "#{command["since"]}" }
- SPEC
- ret.strip
- end.join(",\n ")
-end
-
-# Write to stdout
-puts <<-HELP_H
-/* Automatically generated by #{__FILE__}, do not edit. */
-
-#ifndef __REDIS_HELP_H
-#define __REDIS_HELP_H
-
-static char *commandGroups[] = {
- #{generate_groups}
-};
-
-struct commandHelp {
- char *name;
- char *params;
- char *summary;
- int group;
- char *since;
-} commandHelp[] = {
- #{generate_commands}
-};
-
-#endif
-HELP_H
-
diff --git a/utils/req-res-log-validator.py b/utils/req-res-log-validator.py
index e2b9d4f8d..46c110019 100755
--- a/utils/req-res-log-validator.py
+++ b/utils/req-res-log-validator.py
@@ -12,7 +12,6 @@ import argparse
import multiprocessing
import collections
import io
-import signal
import traceback
from datetime import timedelta
from functools import partial
@@ -44,7 +43,9 @@ Future validations:
1. Fail the script if one or more of the branches of the reply schema (e.g. oneOf, anyOf) was not hit.
"""
-IGNORED_COMMANDS = [
+IGNORED_COMMANDS = {
+ # Commands that don't work in a req-res manner (see logreqres.c)
+ "debug", # because of DEBUG SEGFAULT
"sync",
"psync",
"monitor",
@@ -54,11 +55,10 @@ IGNORED_COMMANDS = [
"sunsubscribe",
"psubscribe",
"punsubscribe",
- "debug",
+ # Commands to which we decided not write a reply schema
"pfdebug",
"lolwut",
-]
-
+}
class Request(object):
"""
@@ -169,7 +169,7 @@ class Response(object):
value = Response(f, line_counter)
self.json[field] = value.json
if line[0] == '|':
- # We don't care abou the attributes, read the real response
+ # We don't care about the attributes, read the real response
real_res = Response(f, line_counter)
self.__dict__.update(real_res.__dict__)
@@ -180,7 +180,7 @@ class Response(object):
def process_file(docs, path):
"""
- This function processes a single filegenerated by logreqres.c
+ This function processes a single file generated by logreqres.c
"""
line_counter = [0] # A list with one integer: to force python to pass it by reference
command_counter = dict()
@@ -190,7 +190,7 @@ def process_file(docs, path):
# Convert file to StringIO in order to minimize IO operations
with open(path, "r", newline="\r\n", encoding="latin-1") as f:
content = f.read()
-
+
with io.StringIO(content) as fakefile:
while True:
try:
@@ -217,6 +217,9 @@ def process_file(docs, path):
if res.error or res.queued:
continue
+ if req.command in IGNORED_COMMANDS:
+ continue
+
try:
jsonschema.validate(instance=res.json, schema=req.schema, cls=schema_validator)
except (jsonschema.ValidationError, jsonschema.exceptions.SchemaError) as err:
@@ -244,7 +247,7 @@ def fetch_schemas(cli, port, args, docs):
break
except Exception as e:
time.sleep(0.1)
- pass
+
print('Connected')
cli_proc = subprocess.Popen([cli, '-p', str(port), '--json', 'command', 'docs'], stdout=subprocess.PIPE)
@@ -287,16 +290,6 @@ if __name__ == '__main__':
fetch_schemas(args.cli, args.port, redis_args, docs)
- missing_schema = [k for k, v in docs.items()
- if "reply_schema" not in v and k not in IGNORED_COMMANDS]
- if missing_schema:
- print("WARNING! The following commands are missing a reply_schema:")
- for k in sorted(missing_schema):
- print(f" {k}")
- if args.fail_missing_reply_schemas:
- print("ERROR! at least one command does not have a reply_schema")
- sys.exit(1)
-
# Fetch schemas from a sentinel
print('Starting Redis sentinel')
@@ -308,9 +301,19 @@ if __name__ == '__main__':
fetch_schemas(args.cli, args.port, sentinel_args, docs)
os.unlink(config_file)
+ missing_schema = [k for k, v in docs.items()
+ if "reply_schema" not in v and k not in IGNORED_COMMANDS]
+ if missing_schema:
+ print("WARNING! The following commands are missing a reply_schema:")
+ for k in sorted(missing_schema):
+ print(f" {k}")
+ if args.fail_missing_reply_schemas:
+ print("ERROR! at least one command does not have a reply_schema")
+ sys.exit(1)
+
start = time.time()
- # Obtain all the files toprocesses
+ # Obtain all the files to processes
paths = []
for path in glob.glob('%s/tmp/*/*.reqres' % testdir):
paths.append(path)
@@ -335,9 +338,7 @@ if __name__ == '__main__':
print("Hits per command:")
for k, v in sorted(command_counter.items()):
print(f" {k}: {v}")
- # We don't care about SENTINEL commands
- not_hit = set(filter(lambda x: not x.startswith("sentinel"),
- set(docs.keys()) - set(command_counter.keys()) - set(IGNORED_COMMANDS)))
+ not_hit = set(set(docs.keys()) - set(command_counter.keys()) - set(IGNORED_COMMANDS))
if not_hit:
if args.verbose:
print("WARNING! The following commands were not hit at all:")