Import 'tikv-jemallocator' crate

Request Document: go/android-rust-importing-crates
For CL Reviewers: go/android3p#cl-review
For Build Team: go/ab-third-party-imports
Bug: http://b/340345793
Test: m tikv_jemallocator

Change-Id: I5cbb3556434f954c1eb327f3cbdf76e77ed2c990
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..4910c12
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,26 @@
+// This file is generated by cargo_embargo.
+// Do not modify this file after the first "rust_*" or "genrule" module
+// because the changes will be overridden on upgrade.
+// Content before the first "rust_*" or "genrule" module is preserved.
+
+package {
+    default_visibility: ["//visibility:private"],
+}
+
+rust_library_host {
+    name: "libtikv_jemallocator",
+    crate_name: "tikv_jemallocator",
+    cargo_env_compat: true,
+    cargo_pkg_version: "0.5.4",
+    srcs: ["src/lib.rs"],
+    edition: "2018",
+    features: [
+        "background_threads_runtime_support",
+        "default",
+    ],
+    rustlibs: [
+        "liblibc",
+        "libtikv_jemalloc_sys",
+    ],
+    visibility: ["//external/n2:__subpackages__"],
+}
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..9f259df
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,89 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies.
+#
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
+
+[package]
+edition = "2018"
+name = "tikv-jemallocator"
+version = "0.5.4"
+authors = [
+    "Alex Crichton <[email protected]>",
+    "Gonzalo Brito Gadeschi <[email protected]>",
+    "Simon Sapin <[email protected]>",
+    "Steven Fackler <[email protected]>",
+    "The TiKV Project Developers",
+]
+description = """
+A Rust allocator backed by jemalloc
+"""
+homepage = "https://github.com/tikv/jemallocator"
+documentation = "https://docs.rs/jemallocator"
+readme = "README.md"
+keywords = [
+    "allocator",
+    "jemalloc",
+]
+categories = [
+    "memory-management",
+    "api-bindings",
+]
+license = "MIT/Apache-2.0"
+repository = "https://github.com/tikv/jemallocator"
+
+[package.metadata.docs.rs]
+features = []
+rustdoc-args = [
+    "--cfg",
+    "jemallocator_docs",
+]
+
+[lib]
+test = false
+bench = false
+
+[dependencies.libc]
+version = "^0.2.8"
+default-features = false
+
+[dependencies.tikv-jemalloc-sys]
+version = "0.5.0"
+default-features = false
+
+[dev-dependencies.paste]
+version = "1"
+
+[dev-dependencies.tikv-jemalloc-ctl]
+version = "0.5.0"
+
+[features]
+alloc_trait = []
+background_threads = ["tikv-jemalloc-sys/background_threads"]
+background_threads_runtime_support = ["tikv-jemalloc-sys/background_threads_runtime_support"]
+debug = ["tikv-jemalloc-sys/debug"]
+default = ["background_threads_runtime_support"]
+disable_initial_exec_tls = ["tikv-jemalloc-sys/disable_initial_exec_tls"]
+profiling = ["tikv-jemalloc-sys/profiling"]
+stats = ["tikv-jemalloc-sys/stats"]
+unprefixed_malloc_on_supported_platforms = ["tikv-jemalloc-sys/unprefixed_malloc_on_supported_platforms"]
+
+[badges.codecov]
+repository = "tikv/jemallocator"
+
+[badges.is-it-maintained-issue-resolution]
+repository = "tikv/jemallocator"
+
+[badges.is-it-maintained-open-issues]
+repository = "tikv/jemallocator"
+
+[badges.maintenance]
+status = "actively-developed"
+
+[badges.travis-ci]
+repository = "tikv/jemallocator"
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
new file mode 100644
index 0000000..010b99c
--- /dev/null
+++ b/Cargo.toml.orig
@@ -0,0 +1,56 @@
+[package]
+name = "tikv-jemallocator"
+# Make sure to update the version in the README as well:
+version = "0.5.4"
+authors = [
+    "Alex Crichton <[email protected]>",
+    "Gonzalo Brito Gadeschi <[email protected]>",
+    "Simon Sapin <[email protected]>",
+    "Steven Fackler <[email protected]>",
+    "The TiKV Project Developers",
+]
+license = "MIT/Apache-2.0"
+readme = "README.md"
+keywords = ["allocator", "jemalloc"]
+categories = ["memory-management", "api-bindings"]
+repository = "https://github.com/tikv/jemallocator"
+homepage = "https://github.com/tikv/jemallocator"
+documentation = "https://docs.rs/jemallocator"
+description = """
+A Rust allocator backed by jemalloc
+"""
+edition = "2018"
+
+[badges]
+travis-ci = { repository = "tikv/jemallocator" }
+codecov = { repository = "tikv/jemallocator" }
+is-it-maintained-issue-resolution = { repository = "tikv/jemallocator" }
+is-it-maintained-open-issues = { repository = "tikv/jemallocator" }
+maintenance = { status = "actively-developed" }
+
+[lib]
+test = false
+bench = false
+
+[dependencies]
+tikv-jemalloc-sys = { path = "../jemalloc-sys", version = "0.5.0", default-features = false }
+libc = { version = "^0.2.8", default-features = false }
+
+[dev-dependencies]
+paste = "1"
+tikv-jemalloc-ctl = { path = "../jemalloc-ctl", version = "0.5.0" }
+
+[features]
+default = ["background_threads_runtime_support"]
+alloc_trait = []
+profiling = ["tikv-jemalloc-sys/profiling"]
+debug = ["tikv-jemalloc-sys/debug"]
+stats = ["tikv-jemalloc-sys/stats"]
+background_threads_runtime_support = ["tikv-jemalloc-sys/background_threads_runtime_support"]
+background_threads = ["tikv-jemalloc-sys/background_threads"]
+unprefixed_malloc_on_supported_platforms = ["tikv-jemalloc-sys/unprefixed_malloc_on_supported_platforms"]
+disable_initial_exec_tls = ["tikv-jemalloc-sys/disable_initial_exec_tls"]
+
+[package.metadata.docs.rs]
+features = []
+rustdoc-args = [ "--cfg", "jemallocator_docs" ]
diff --git a/LICENSE b/LICENSE
new file mode 120000
index 0000000..6b579aa
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1 @@
+LICENSE-APACHE
\ No newline at end of file
diff --git a/LICENSE-APACHE b/LICENSE-APACHE
new file mode 100644
index 0000000..16fe87b
--- /dev/null
+++ b/LICENSE-APACHE
@@ -0,0 +1,201 @@
+                              Apache License
+                        Version 2.0, January 2004
+                     http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+   "License" shall mean the terms and conditions for use, reproduction,
+   and distribution as defined by Sections 1 through 9 of this document.
+
+   "Licensor" shall mean the copyright owner or entity authorized by
+   the copyright owner that is granting the License.
+
+   "Legal Entity" shall mean the union of the acting entity and all
+   other entities that control, are controlled by, or are under common
+   control with that entity. For the purposes of this definition,
+   "control" means (i) the power, direct or indirect, to cause the
+   direction or management of such entity, whether by contract or
+   otherwise, or (ii) ownership of fifty percent (50%) or more of the
+   outstanding shares, or (iii) beneficial ownership of such entity.
+
+   "You" (or "Your") shall mean an individual or Legal Entity
+   exercising permissions granted by this License.
+
+   "Source" form shall mean the preferred form for making modifications,
+   including but not limited to software source code, documentation
+   source, and configuration files.
+
+   "Object" form shall mean any form resulting from mechanical
+   transformation or translation of a Source form, including but
+   not limited to compiled object code, generated documentation,
+   and conversions to other media types.
+
+   "Work" shall mean the work of authorship, whether in Source or
+   Object form, made available under the License, as indicated by a
+   copyright notice that is included in or attached to the work
+   (an example is provided in the Appendix below).
+
+   "Derivative Works" shall mean any work, whether in Source or Object
+   form, that is based on (or derived from) the Work and for which the
+   editorial revisions, annotations, elaborations, or other modifications
+   represent, as a whole, an original work of authorship. For the purposes
+   of this License, Derivative Works shall not include works that remain
+   separable from, or merely link (or bind by name) to the interfaces of,
+   the Work and Derivative Works thereof.
+
+   "Contribution" shall mean any work of authorship, including
+   the original version of the Work and any modifications or additions
+   to that Work or Derivative Works thereof, that is intentionally
+   submitted to Licensor for inclusion in the Work by the copyright owner
+   or by an individual or Legal Entity authorized to submit on behalf of
+   the copyright owner. For the purposes of this definition, "submitted"
+   means any form of electronic, verbal, or written communication sent
+   to the Licensor or its representatives, including but not limited to
+   communication on electronic mailing lists, source code control systems,
+   and issue tracking systems that are managed by, or on behalf of, the
+   Licensor for the purpose of discussing and improving the Work, but
+   excluding communication that is conspicuously marked or otherwise
+   designated in writing by the copyright owner as "Not a Contribution."
+
+   "Contributor" shall mean Licensor and any individual or Legal Entity
+   on behalf of whom a Contribution has been received by Licensor and
+   subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+   this License, each Contributor hereby grants to You a perpetual,
+   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+   copyright license to reproduce, prepare Derivative Works of,
+   publicly display, publicly perform, sublicense, and distribute the
+   Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+   this License, each Contributor hereby grants to You a perpetual,
+   worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+   (except as stated in this section) patent license to make, have made,
+   use, offer to sell, sell, import, and otherwise transfer the Work,
+   where such license applies only to those patent claims licensable
+   by such Contributor that are necessarily infringed by their
+   Contribution(s) alone or by combination of their Contribution(s)
+   with the Work to which such Contribution(s) was submitted. If You
+   institute patent litigation against any entity (including a
+   cross-claim or counterclaim in a lawsuit) alleging that the Work
+   or a Contribution incorporated within the Work constitutes direct
+   or contributory patent infringement, then any patent licenses
+   granted to You under this License for that Work shall terminate
+   as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+   Work or Derivative Works thereof in any medium, with or without
+   modifications, and in Source or Object form, provided that You
+   meet the following conditions:
+
+   (a) You must give any other recipients of the Work or
+       Derivative Works a copy of this License; and
+
+   (b) You must cause any modified files to carry prominent notices
+       stating that You changed the files; and
+
+   (c) You must retain, in the Source form of any Derivative Works
+       that You distribute, all copyright, patent, trademark, and
+       attribution notices from the Source form of the Work,
+       excluding those notices that do not pertain to any part of
+       the Derivative Works; and
+
+   (d) If the Work includes a "NOTICE" text file as part of its
+       distribution, then any Derivative Works that You distribute must
+       include a readable copy of the attribution notices contained
+       within such NOTICE file, excluding those notices that do not
+       pertain to any part of the Derivative Works, in at least one
+       of the following places: within a NOTICE text file distributed
+       as part of the Derivative Works; within the Source form or
+       documentation, if provided along with the Derivative Works; or,
+       within a display generated by the Derivative Works, if and
+       wherever such third-party notices normally appear. The contents
+       of the NOTICE file are for informational purposes only and
+       do not modify the License. You may add Your own attribution
+       notices within Derivative Works that You distribute, alongside
+       or as an addendum to the NOTICE text from the Work, provided
+       that such additional attribution notices cannot be construed
+       as modifying the License.
+
+   You may add Your own copyright statement to Your modifications and
+   may provide additional or different license terms and conditions
+   for use, reproduction, or distribution of Your modifications, or
+   for any such Derivative Works as a whole, provided Your use,
+   reproduction, and distribution of the Work otherwise complies with
+   the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+   any Contribution intentionally submitted for inclusion in the Work
+   by You to the Licensor shall be under the terms and conditions of
+   this License, without any additional terms or conditions.
+   Notwithstanding the above, nothing herein shall supersede or modify
+   the terms of any separate license agreement you may have executed
+   with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+   names, trademarks, service marks, or product names of the Licensor,
+   except as required for reasonable and customary use in describing the
+   origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+   agreed to in writing, Licensor provides the Work (and each
+   Contributor provides its Contributions) on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+   implied, including, without limitation, any warranties or conditions
+   of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+   PARTICULAR PURPOSE. You are solely responsible for determining the
+   appropriateness of using or redistributing the Work and assume any
+   risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+   whether in tort (including negligence), contract, or otherwise,
+   unless required by applicable law (such as deliberate and grossly
+   negligent acts) or agreed to in writing, shall any Contributor be
+   liable to You for damages, including any direct, indirect, special,
+   incidental, or consequential damages of any character arising as a
+   result of this License or out of the use or inability to use the
+   Work (including but not limited to damages for loss of goodwill,
+   work stoppage, computer failure or malfunction, or any and all
+   other commercial damages or losses), even if such Contributor
+   has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+   the Work or Derivative Works thereof, You may choose to offer,
+   and charge a fee for, acceptance of support, warranty, indemnity,
+   or other liability obligations and/or rights consistent with this
+   License. However, in accepting such obligations, You may act only
+   on Your own behalf and on Your sole responsibility, not on behalf
+   of any other Contributor, and only if You agree to indemnify,
+   defend, and hold each Contributor harmless for any liability
+   incurred by, or claims asserted against, such Contributor by reason
+   of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+   To apply the Apache License to your work, attach the following
+   boilerplate notice, with the fields enclosed by brackets "[]"
+   replaced with your own identifying information. (Don't include
+   the brackets!)  The text should be enclosed in the appropriate
+   comment syntax for the file format. We also recommend that a
+   file or class name and description of purpose be included on the
+   same "printed page" as the copyright notice for easier
+   identification within third-party archives.
+
+Copyright [yyyy] [name of copyright owner]
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+	http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/LICENSE-MIT b/LICENSE-MIT
new file mode 100644
index 0000000..39e0ed6
--- /dev/null
+++ b/LICENSE-MIT
@@ -0,0 +1,25 @@
+Copyright (c) 2014 Alex Crichton
+
+Permission is hereby granted, free of charge, to any
+person obtaining a copy of this software and associated
+documentation files (the "Software"), to deal in the
+Software without restriction, including without
+limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software
+is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
+IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..3d85c01
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,21 @@
+name: "tikv-jemallocator"
+description: "()"
+third_party {
+  identifier {
+    type: "crates.io"
+    value: "tikv-jemallocator"
+  }
+  identifier {
+    type: "Archive"
+    value: "https://static.crates.io/crates/tikv-jemallocator/tikv-jemallocator-0.5.4.crate"
+    primary_source: true
+  }
+  version: "0.5.4"
+  # Dual-licensed, using the least restrictive per go/thirdpartylicenses#same.
+  license_type: NOTICE
+  last_upgrade_date {
+    year: 2024
+    month: 5
+    day: 16
+  }
+}
diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_APACHE2
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..083b8a3
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1,2 @@
+include platform/prebuilts/rust:main:/OWNERS
+include platform/build/soong:main:/OWNERS
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..e9e13e6
--- /dev/null
+++ b/README.md
@@ -0,0 +1,97 @@
+# tikv-jemallocator
+
+[![ci]][github actions] [![Latest Version]][crates.io] [![docs]][docs.rs]
+
+This project is the successor of [jemallocator](https://github.com/gnzlbg/jemallocator).
+
+The project is also published as `jemallocator` for historical reasons. The two crates are the same except names. For new projects, it's recommended to use `tikv-xxx` versions instead.
+
+> Links against `jemalloc` and provides a `Jemalloc` unit type that implements
+> the allocator APIs and can be set as the `#[global_allocator]`
+
+## Overview
+
+The `jemalloc` support ecosystem consists of the following crates:
+
+* `tikv-jemalloc-sys`: builds and links against `jemalloc` exposing raw C bindings to it.
+* `tikv-jemallocator`: provides the `Jemalloc` type which implements the
+  `GlobalAlloc` and `Alloc` traits. 
+* `tikv-jemalloc-ctl`: high-level wrapper over `jemalloc`'s control and introspection
+  APIs (the `mallctl*()` family of functions and the _MALLCTL NAMESPACE_)'
+
+## Documentation
+
+* [Latest release (docs.rs)][docs.rs]
+
+To use `tikv-jemallocator` add it as a dependency:
+
+```toml
+# Cargo.toml
+[dependencies]
+
+[target.'cfg(not(target_env = "msvc"))'.dependencies]
+tikv-jemallocator = "0.5"
+```
+
+To set `tikv_jemallocator::Jemalloc` as the global allocator add this to your project:
+
+```rust
+# main.rs
+#[cfg(not(target_env = "msvc"))]
+use tikv_jemallocator::Jemalloc;
+
+#[cfg(not(target_env = "msvc"))]
+#[global_allocator]
+static GLOBAL: Jemalloc = Jemalloc;
+```
+
+And that's it! Once you've defined this `static` then jemalloc will be used for
+all allocations requested by Rust code in the same program.
+
+## Platform support
+
+The following table describes the supported platforms: 
+
+* `build`: does the library compile for the target?
+* `run`: do `tikv-jemallocator` and `tikv-jemalloc-sys` tests pass on the target?
+* `jemalloc`: do `tikv-jemalloc`'s tests pass on the target?
+
+Tier 1 targets are tested on all Rust channels (stable, beta, and nightly). All
+other targets are only tested on Rust nightly.
+
+| Linux targets:                      | build     | run     | jemalloc     |
+|-------------------------------------|-----------|---------|--------------|
+| `aarch64-unknown-linux-gnu`         | ✓         | ✓       | ✗            |
+| `powerpc64le-unknown-linux-gnu`     | ✓         | ✓       | ✗            |
+| `x86_64-unknown-linux-gnu` (tier 1) | ✓         | ✓       | ✓            |
+| **MacOSX targets:**                 | **build** | **run** | **jemalloc** |
+| `x86_64-apple-darwin` (tier 1)      | ✓         | ✓       | ✗            |
+
+## Features
+
+The `tikv-jemallocator` crate re-exports the [features of the `tikv-jemalloc-sys`
+dependency](https://github.com/tikv/jemallocator/blob/master/jemalloc-sys/README.md).
+
+## License
+
+This project is licensed under either of
+
+ * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or
+   http://www.apache.org/licenses/LICENSE-2.0)
+ * MIT license ([LICENSE-MIT](LICENSE-MIT) or
+   http://opensource.org/licenses/MIT)
+
+at your option.
+
+## Contribution
+
+Unless you explicitly state otherwise, any contribution intentionally submitted
+for inclusion in `tikv-jemallocator` by you, as defined in the Apache-2.0 license,
+shall be dual licensed as above, without any additional terms or conditions.
+
+[Latest Version]: https://img.shields.io/crates/v/tikv-jemallocator.svg
+[crates.io]: https://crates.io/crates/tikv-jemallocator
+[docs]: https://docs.rs/tikv-jemallocator/badge.svg
+[docs.rs]: https://docs.rs/tikv-jemallocator/
+[ci]: https://github.com/tikv/jemallocator/actions/workflows/main.yml/badge.svg
+[github actions]: https://github.com/tikv/jemallocator/actions
diff --git a/benches/roundtrip.rs b/benches/roundtrip.rs
new file mode 100644
index 0000000..30f7bf4
--- /dev/null
+++ b/benches/roundtrip.rs
@@ -0,0 +1,253 @@
+//! Benchmarks the cost of the different allocation functions by doing a
+//! roundtrip (allocate, deallocate).
+#![feature(test, allocator_api)]
+#![cfg(feature = "alloc_trait")]
+
+extern crate test;
+
+use jemallocator::Jemalloc;
+use libc::c_int;
+use std::{
+    alloc::{Alloc, Excess, Layout},
+    ptr,
+};
+use test::Bencher;
+use tikv_jemalloc_sys::MALLOCX_ALIGN;
+
+#[global_allocator]
+static A: Jemalloc = Jemalloc;
+
+// FIXME: replace with jemallocator::layout_to_flags
+#[cfg(all(any(
+    target_arch = "arm",
+    target_arch = "mips",
+    target_arch = "mipsel",
+    target_arch = "powerpc"
+)))]
+const MIN_ALIGN: usize = 8;
+#[cfg(all(any(
+    target_arch = "x86",
+    target_arch = "x86_64",
+    target_arch = "aarch64",
+    target_arch = "powerpc64",
+    target_arch = "powerpc64le",
+    target_arch = "loongarch64",
+    target_arch = "mips64",
+    target_arch = "riscv64",
+    target_arch = "s390x",
+    target_arch = "sparc64"
+)))]
+const MIN_ALIGN: usize = 16;
+
+fn layout_to_flags(layout: &Layout) -> c_int {
+    if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() {
+        0
+    } else {
+        MALLOCX_ALIGN(layout.align())
+    }
+}
+
+macro_rules! rt {
+    ($size:expr, $align:expr) => {
+        paste::paste! {
+            #[bench]
+            fn [<rt_mallocx_size_ $size _align_ $align>](b: &mut Bencher) {
+                b.iter(|| unsafe {
+                    use jemalloc_sys as jemalloc;
+                    let flags = layout_to_flags(&Layout::from_size_align($size, $align).unwrap());
+                    let ptr = jemalloc::mallocx($size, flags);
+                    test::black_box(ptr);
+                    jemalloc::sdallocx(ptr, $size, flags);
+                });
+            }
+
+            #[bench]
+            fn [<rt_mallocx_nallocx_size_ $size _align_ $align>](b: &mut Bencher) {
+                b.iter(|| unsafe {
+                    use jemalloc_sys as jemalloc;
+                    let flags = layout_to_flags(&Layout::from_size_align($size, $align).unwrap());
+                    let ptr = jemalloc::mallocx($size, flags);
+                    test::black_box(ptr);
+                    let rsz = jemalloc::nallocx($size, flags);
+                    test::black_box(rsz);
+                    jemalloc::sdallocx(ptr, rsz, flags);
+                });
+            }
+
+            #[bench]
+            fn [<rt_alloc_layout_checked_size_ $size _align_ $align>](b: &mut Bencher) {
+                b.iter(|| unsafe {
+                    let layout = Layout::from_size_align($size, $align).unwrap();
+                    let ptr = Jemalloc.alloc(layout.clone()).unwrap();
+                    test::black_box(ptr);
+                    Jemalloc.dealloc(ptr, layout);
+                });
+            }
+
+            #[bench]
+            fn [<rt_alloc_layout_unchecked_size_ $size _align_ $align>](b: &mut Bencher) {
+                b.iter(|| unsafe {
+                    let layout = Layout::from_size_align_unchecked($size, $align);
+                    let ptr = Jemalloc.alloc(layout.clone()).unwrap();
+                    test::black_box(ptr);
+                    Jemalloc.dealloc(ptr, layout);
+                });
+            }
+
+            #[bench]
+            fn [<rt_alloc_excess_unused_size_ $size _align_ $align>](b: &mut Bencher) {
+                b.iter(|| unsafe {
+                    let layout = Layout::from_size_align($size, $align).unwrap();
+                    let Excess(ptr, _) = Jemalloc.alloc_excess(layout.clone()).unwrap();
+                    test::black_box(ptr);
+                    Jemalloc.dealloc(ptr, layout);
+                });
+            }
+
+            #[bench]
+            fn [<rt_alloc_excess_used_size_ $size _align_ $align>](b: &mut Bencher) {
+                b.iter(|| unsafe {
+                    let layout = Layout::from_size_align($size, $align).unwrap();
+                    let Excess(ptr, excess) = Jemalloc.alloc_excess(layout.clone()).unwrap();
+                    test::black_box(ptr);
+                    test::black_box(excess);
+                    Jemalloc.dealloc(ptr, layout);
+                });
+            }
+
+            #[bench]
+            fn [<rt_mallocx_zeroed_size_ $size _align_ $align>](b: &mut Bencher) {
+                b.iter(|| unsafe {
+                    use jemalloc_sys as jemalloc;
+                    let flags = layout_to_flags(&Layout::from_size_align($size, $align).unwrap());
+                    let ptr = jemalloc::mallocx($size, flags | jemalloc::MALLOCX_ZERO);
+                    test::black_box(ptr);
+                    jemalloc::sdallocx(ptr, $size, flags);
+                });
+            }
+
+            #[bench]
+            fn [<rt_calloc_size_ $size _align_ $align>](b: &mut Bencher) {
+                b.iter(|| unsafe {
+                    use jemalloc_sys as jemalloc;
+                    let flags = layout_to_flags(&Layout::from_size_align($size, $align).unwrap());
+                    test::black_box(flags);
+                    let ptr = jemalloc::calloc(1, $size);
+                    test::black_box(ptr);
+                    jemalloc::sdallocx(ptr, $size, 0);
+                });
+            }
+
+            #[bench]
+            fn [<rt_realloc_naive_size_ $size _align_ $align>](b: &mut Bencher) {
+                b.iter(|| unsafe {
+                    let layout = Layout::from_size_align($size, $align).unwrap();
+                    let ptr = Jemalloc.alloc(layout.clone()).unwrap();
+                    test::black_box(ptr);
+
+                    // navie realloc:
+                    let new_layout = Layout::from_size_align(2 * $size, $align).unwrap();
+                    let ptr = {
+                        let new_ptr = Jemalloc.alloc(new_layout.clone()).unwrap();
+                        ptr::copy_nonoverlapping(ptr.as_ptr() as *const u8, new_ptr.as_ptr(), layout.size());
+                        Jemalloc.dealloc(ptr, layout);
+                        new_ptr
+                    };
+                    test::black_box(ptr);
+
+                    Jemalloc.dealloc(ptr, new_layout);
+                });
+            }
+
+            #[bench]
+            fn [<rt_realloc_size_ $size _align_ $align>](b: &mut Bencher) {
+                b.iter(|| unsafe {
+                    let layout = Layout::from_size_align($size, $align).unwrap();
+                    let ptr = Jemalloc.alloc(layout.clone()).unwrap();
+                    test::black_box(ptr);
+
+                    let new_layout = Layout::from_size_align(2 * $size, $align).unwrap();
+                    let ptr = Jemalloc.realloc(ptr, layout, new_layout.size()).unwrap();
+                    test::black_box(ptr);
+
+                    Jemalloc.dealloc(ptr, new_layout);
+                });
+            }
+
+            #[bench]
+            fn [<rt_realloc_excess_unused_size_ $size _align_ $align>](b: &mut Bencher) {
+                b.iter(|| unsafe {
+                    let layout = Layout::from_size_align($size, $align).unwrap();
+                    let ptr = Jemalloc.alloc(layout.clone()).unwrap();
+                    test::black_box(ptr);
+
+                    let new_layout = Layout::from_size_align(2 * $size, $align).unwrap();
+                    let Excess(ptr, _) = Jemalloc
+                        .realloc_excess(ptr, layout, new_layout.size())
+                        .unwrap();
+                    test::black_box(ptr);
+
+                    Jemalloc.dealloc(ptr, new_layout);
+                });
+            }
+
+            #[bench]
+            fn [<rt_realloc_excess_used_size_ $size _align_ $align>](b: &mut Bencher) {
+                b.iter(|| unsafe {
+                    let layout = Layout::from_size_align($size, $align).unwrap();
+                    let ptr = Jemalloc.alloc(layout.clone()).unwrap();
+                    test::black_box(ptr);
+
+                    let new_layout = Layout::from_size_align(2 * $size, $align).unwrap();
+                    let Excess(ptr, excess) = Jemalloc
+                        .realloc_excess(ptr, layout, new_layout.size())
+                        .unwrap();
+                    test::black_box(ptr);
+                    test::black_box(excess);
+
+                    Jemalloc.dealloc(ptr, new_layout);
+                });
+            }
+
+        }
+    };
+    ([$($size:expr),*]) => {
+        $(
+            rt!($size, 1);
+            rt!($size, 2);
+            rt!($size, 4);
+            rt!($size, 8);
+            rt!($size, 16);
+            rt!($size, 32);
+        )*
+    }
+}
+
+// Powers of two
+mod pow2 {
+    use super::*;
+
+    rt!([
+        1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072,
+        4194304
+    ]);
+}
+
+mod even {
+    use super::*;
+
+    rt!([10, 100, 1000, 10000, 100000, 1000000]);
+}
+
+mod odd {
+    use super::*;
+    rt!([9, 99, 999, 9999, 99999, 999999]);
+}
+
+mod primes {
+    use super::*;
+    rt!([
+        3, 7, 13, 17, 31, 61, 96, 127, 257, 509, 1021, 2039, 4093, 8191, 16381, 32749, 65537,
+        131071, 4194301
+    ]);
+}
diff --git a/cargo_embargo.json b/cargo_embargo.json
new file mode 100644
index 0000000..bd465d2
--- /dev/null
+++ b/cargo_embargo.json
@@ -0,0 +1,14 @@
+{
+  "package": {
+    "tikv-jemallocator": {
+      "device_supported": false
+    }
+  },
+  "module_visibility": {
+    "libtikv_jemallocator": [
+      "//external/n2:__subpackages__"
+    ]
+  },
+  "run_cargo": false,
+  "tests": false
+}
\ No newline at end of file
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..165660e
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,292 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Bindings for jemalloc as an allocator
+//!
+//! This crate provides bindings to jemalloc as a memory allocator for Rust.
+//! This crate mainly exports, one type, `Jemalloc`, which implements the
+//! `GlobalAlloc` trait and optionally the `Alloc` trait,
+//! and is suitable both as a memory allocator and as a global allocator.
+
+#![cfg_attr(feature = "alloc_trait", feature(allocator_api))]
+// TODO: rename the following lint on next minor bump
+#![allow(renamed_and_removed_lints)]
+#![deny(missing_docs, broken_intra_doc_links)]
+#![no_std]
+
+#[cfg(feature = "alloc_trait")]
+use core::alloc::{Alloc, AllocErr, CannotReallocInPlace, Excess};
+use core::alloc::{GlobalAlloc, Layout};
+#[cfg(feature = "alloc_trait")]
+use core::ptr::NonNull;
+
+use libc::{c_int, c_void};
+
+// This constant equals _Alignof(max_align_t) and is platform-specific. It
+// contains the _maximum_ alignment that the memory allocations returned by the
+// C standard library memory allocation APIs (e.g. `malloc`) are guaranteed to
+// have.
+//
+// The memory allocation APIs are required to return memory that can fit any
+// object whose fundamental aligment is <= _Alignof(max_align_t).
+//
+// In C, there are no ZSTs, and the size of all types is a multiple of their
+// alignment (size >= align). So for allocations with size <=
+// _Alignof(max_align_t), the malloc-APIs return memory whose alignment is
+// either the requested size if its a power-of-two, or the next smaller
+// power-of-two.
+#[cfg(any(
+    target_arch = "arm",
+    target_arch = "mips",
+    target_arch = "mipsel",
+    target_arch = "powerpc"
+))]
+const ALIGNOF_MAX_ALIGN_T: usize = 8;
+#[cfg(any(
+    target_arch = "x86",
+    target_arch = "x86_64",
+    target_arch = "aarch64",
+    target_arch = "powerpc64",
+    target_arch = "powerpc64le",
+    target_arch = "loongarch64",
+    target_arch = "mips64",
+    target_arch = "riscv64",
+    target_arch = "s390x",
+    target_arch = "sparc64"
+))]
+const ALIGNOF_MAX_ALIGN_T: usize = 16;
+
+/// If `align` is less than `_Alignof(max_align_t)`, and if the requested
+/// allocation `size` is larger than the alignment, we are guaranteed to get a
+/// suitably aligned allocation by default, without passing extra flags, and
+/// this function returns `0`.
+///
+/// Otherwise, it returns the alignment flag to pass to the jemalloc APIs.
+fn layout_to_flags(align: usize, size: usize) -> c_int {
+    if align <= ALIGNOF_MAX_ALIGN_T && align <= size {
+        0
+    } else {
+        ffi::MALLOCX_ALIGN(align)
+    }
+}
+
+// Assumes a condition that always must hold.
+macro_rules! assume {
+    ($e:expr) => {
+        debug_assert!($e);
+        if !($e) {
+            core::hint::unreachable_unchecked();
+        }
+    };
+}
+
+/// Handle to the jemalloc allocator
+///
+/// This type implements the `GlobalAllocAlloc` trait, allowing usage a global allocator.
+///
+/// When the `alloc_trait` feature of this crate is enabled, it also implements the `Alloc` trait,
+/// allowing usage in collections.
+#[derive(Copy, Clone, Default, Debug)]
+pub struct Jemalloc;
+
+unsafe impl GlobalAlloc for Jemalloc {
+    #[inline]
+    unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
+        assume!(layout.size() != 0);
+        let flags = layout_to_flags(layout.align(), layout.size());
+        let ptr = if flags == 0 {
+            ffi::malloc(layout.size())
+        } else {
+            ffi::mallocx(layout.size(), flags)
+        };
+        ptr as *mut u8
+    }
+
+    #[inline]
+    unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
+        assume!(layout.size() != 0);
+        let flags = layout_to_flags(layout.align(), layout.size());
+        let ptr = if flags == 0 {
+            ffi::calloc(1, layout.size())
+        } else {
+            ffi::mallocx(layout.size(), flags | ffi::MALLOCX_ZERO)
+        };
+        ptr as *mut u8
+    }
+
+    #[inline]
+    unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
+        assume!(!ptr.is_null());
+        assume!(layout.size() != 0);
+        let flags = layout_to_flags(layout.align(), layout.size());
+        ffi::sdallocx(ptr as *mut c_void, layout.size(), flags)
+    }
+
+    #[inline]
+    unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
+        assume!(layout.size() != 0);
+        assume!(new_size != 0);
+        let flags = layout_to_flags(layout.align(), new_size);
+        let ptr = if flags == 0 {
+            ffi::realloc(ptr as *mut c_void, new_size)
+        } else {
+            ffi::rallocx(ptr as *mut c_void, new_size, flags)
+        };
+        ptr as *mut u8
+    }
+}
+
+#[cfg(feature = "alloc_trait")]
+unsafe impl Alloc for Jemalloc {
+    #[inline]
+    unsafe fn alloc(&mut self, layout: Layout) -> Result<NonNull<u8>, AllocErr> {
+        NonNull::new(GlobalAlloc::alloc(self, layout)).ok_or(AllocErr)
+    }
+
+    #[inline]
+    unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result<NonNull<u8>, AllocErr> {
+        NonNull::new(GlobalAlloc::alloc_zeroed(self, layout)).ok_or(AllocErr)
+    }
+
+    #[inline]
+    unsafe fn dealloc(&mut self, ptr: NonNull<u8>, layout: Layout) {
+        GlobalAlloc::dealloc(self, ptr.as_ptr(), layout)
+    }
+
+    #[inline]
+    unsafe fn realloc(
+        &mut self,
+        ptr: NonNull<u8>,
+        layout: Layout,
+        new_size: usize,
+    ) -> Result<NonNull<u8>, AllocErr> {
+        NonNull::new(GlobalAlloc::realloc(self, ptr.as_ptr(), layout, new_size)).ok_or(AllocErr)
+    }
+
+    #[inline]
+    unsafe fn alloc_excess(&mut self, layout: Layout) -> Result<Excess, AllocErr> {
+        let flags = layout_to_flags(layout.align(), layout.size());
+        let ptr = ffi::mallocx(layout.size(), flags);
+        if let Some(nonnull) = NonNull::new(ptr as *mut u8) {
+            let excess = ffi::nallocx(layout.size(), flags);
+            Ok(Excess(nonnull, excess))
+        } else {
+            Err(AllocErr)
+        }
+    }
+
+    #[inline]
+    unsafe fn realloc_excess(
+        &mut self,
+        ptr: NonNull<u8>,
+        layout: Layout,
+        new_size: usize,
+    ) -> Result<Excess, AllocErr> {
+        let flags = layout_to_flags(layout.align(), new_size);
+        let ptr = ffi::rallocx(ptr.cast().as_ptr(), new_size, flags);
+        if let Some(nonnull) = NonNull::new(ptr as *mut u8) {
+            let excess = ffi::nallocx(new_size, flags);
+            Ok(Excess(nonnull, excess))
+        } else {
+            Err(AllocErr)
+        }
+    }
+
+    #[inline]
+    fn usable_size(&self, layout: &Layout) -> (usize, usize) {
+        let flags = layout_to_flags(layout.align(), layout.size());
+        unsafe {
+            let max = ffi::nallocx(layout.size(), flags);
+            (layout.size(), max)
+        }
+    }
+
+    #[inline]
+    unsafe fn grow_in_place(
+        &mut self,
+        ptr: NonNull<u8>,
+        layout: Layout,
+        new_size: usize,
+    ) -> Result<(), CannotReallocInPlace> {
+        let flags = layout_to_flags(layout.align(), new_size);
+        let usable_size = ffi::xallocx(ptr.cast().as_ptr(), new_size, 0, flags);
+        if usable_size >= new_size {
+            Ok(())
+        } else {
+            // `xallocx` returns a size smaller than the requested one to
+            // indicate that the allocation could not be grown in place
+            //
+            // the old allocation remains unaltered
+            Err(CannotReallocInPlace)
+        }
+    }
+
+    #[inline]
+    unsafe fn shrink_in_place(
+        &mut self,
+        ptr: NonNull<u8>,
+        layout: Layout,
+        new_size: usize,
+    ) -> Result<(), CannotReallocInPlace> {
+        if new_size == layout.size() {
+            return Ok(());
+        }
+        let flags = layout_to_flags(layout.align(), new_size);
+        let usable_size = ffi::xallocx(ptr.cast().as_ptr(), new_size, 0, flags);
+
+        if usable_size < layout.size() {
+            // If `usable_size` is smaller than the original size, the
+            // size-class of the allocation was shrunk to the size-class of
+            // `new_size`, and it is safe to deallocate the allocation with
+            // `new_size`:
+            Ok(())
+        } else if usable_size == ffi::nallocx(new_size, flags) {
+            // If the allocation was not shrunk and the size class of `new_size`
+            // is the same as the size-class of `layout.size()`, then the
+            // allocation can be properly deallocated using `new_size` (and also
+            // using `layout.size()` because the allocation did not change)
+
+            // note: when the allocation is not shrunk, `xallocx` returns the
+            // usable size of the original allocation, which in this case matches
+            // that of the requested allocation:
+            debug_assert_eq!(
+                ffi::nallocx(new_size, flags),
+                ffi::nallocx(layout.size(), flags)
+            );
+            Ok(())
+        } else {
+            // If the allocation was not shrunk, but the size-class of
+            // `new_size` is not the same as that of the original allocation,
+            // then shrinking the allocation failed:
+            Err(CannotReallocInPlace)
+        }
+    }
+}
+
+/// Return the usable size of the allocation pointed to by ptr.
+///
+/// The return value may be larger than the size that was requested during allocation.
+/// This function is not a mechanism for in-place `realloc()`;
+/// rather it is provided solely as a tool for introspection purposes.
+/// Any discrepancy between the requested allocation size
+/// and the size reported by this function should not be depended on,
+/// since such behavior is entirely implementation-dependent.
+///
+/// # Safety
+///
+/// `ptr` must have been allocated by `Jemalloc` and must not have been freed yet.
+pub unsafe fn usable_size<T>(ptr: *const T) -> usize {
+    ffi::malloc_usable_size(ptr as *const c_void)
+}
+
+/// Raw bindings to jemalloc
+mod ffi {
+    pub use tikv_jemalloc_sys::*;
+}
diff --git a/tests/background_thread_defaults.rs b/tests/background_thread_defaults.rs
new file mode 100644
index 0000000..0fa7798
--- /dev/null
+++ b/tests/background_thread_defaults.rs
@@ -0,0 +1,23 @@
+//! Test background threads run-time default settings.
+
+use tikv_jemallocator::Jemalloc;
+
+#[global_allocator]
+static A: Jemalloc = Jemalloc;
+
+// Returns true if background threads are enabled.
+fn background_threads() -> bool {
+    tikv_jemalloc_ctl::opt::background_thread::read().unwrap()
+}
+
+#[test]
+fn background_threads_runtime_defaults() {
+    if !cfg!(feature = "background_threads_runtime_support") {
+        // If the crate was compiled without background thread support,
+        // then background threads are always disabled at run-time by default:
+        assert!(!background_threads());
+        return;
+    }
+
+    assert_eq!(background_threads(), cfg!(feature = "background_threads"));
+}
diff --git a/tests/background_thread_enabled.rs b/tests/background_thread_enabled.rs
new file mode 100644
index 0000000..76d286d
--- /dev/null
+++ b/tests/background_thread_enabled.rs
@@ -0,0 +1,32 @@
+//! Test enabling / disabling background threads at run-time if the
+//! library was compiled with background thread run-time support.
+#![cfg(feature = "background_threads_runtime_support")]
+#![cfg(not(feature = "unprefixed_malloc_on_supported_platforms"))]
+#![cfg(not(target_env = "musl"))]
+
+use tikv_jemallocator::Jemalloc;
+
+#[global_allocator]
+static A: Jemalloc = Jemalloc;
+
+union U {
+    x: &'static u8,
+    y: &'static libc::c_char,
+}
+
+// Even if background threads are not enabled at run-time by default
+// at configuration time, this enables them.
+#[allow(non_upper_case_globals)]
+#[export_name = "_rjem_malloc_conf"]
+pub static malloc_conf: Option<&'static libc::c_char> = Some(unsafe {
+    U {
+        x: &b"background_thread:true\0"[0],
+    }
+    .y
+});
+
+#[test]
+fn background_threads_enabled() {
+    // Background threads are unconditionally enabled at run-time by default.
+    assert!(tikv_jemalloc_ctl::opt::background_thread::read().unwrap(),);
+}
diff --git a/tests/ffi.rs b/tests/ffi.rs
new file mode 100644
index 0000000..8269370
--- /dev/null
+++ b/tests/ffi.rs
@@ -0,0 +1,93 @@
+extern crate tikv_jemalloc_sys as ffi;
+
+use std::mem;
+use std::ptr;
+
+use libc::{c_char, c_void};
+use tikv_jemallocator::Jemalloc;
+
+#[global_allocator]
+static A: Jemalloc = Jemalloc;
+
+#[test]
+fn test_basic_alloc() {
+    unsafe {
+        let exp_size = ffi::nallocx(100, 0);
+        assert!(exp_size >= 100);
+
+        let mut ptr = ffi::mallocx(100, 0);
+        assert!(!ptr.is_null());
+        assert_eq!(exp_size, ffi::malloc_usable_size(ptr));
+        ptr = ffi::rallocx(ptr, 50, 0);
+        let size = ffi::xallocx(ptr, 30, 20, 0);
+        assert!(size >= 50);
+        ffi::sdallocx(ptr, 50, 0);
+    }
+}
+
+#[test]
+fn test_mallctl() {
+    let ptr = unsafe { ffi::mallocx(100, 0) };
+    let mut allocated: usize = 0;
+    let mut val_len = mem::size_of_val(&allocated);
+    let field = "stats.allocated\0";
+    let mut code;
+    code = unsafe {
+        ffi::mallctl(
+            field.as_ptr() as *const _,
+            &mut allocated as *mut _ as *mut c_void,
+            &mut val_len,
+            ptr::null_mut(),
+            0,
+        )
+    };
+    assert_eq!(code, 0);
+    assert!(allocated > 0);
+
+    let mut mib = [0, 0];
+    let mut mib_len = 2;
+    code = unsafe {
+        ffi::mallctlnametomib(field.as_ptr() as *const _, mib.as_mut_ptr(), &mut mib_len)
+    };
+    assert_eq!(code, 0);
+    let mut allocated_by_mib = 0;
+    let code = unsafe {
+        ffi::mallctlbymib(
+            mib.as_ptr(),
+            mib_len,
+            &mut allocated_by_mib as *mut _ as *mut c_void,
+            &mut val_len,
+            ptr::null_mut(),
+            0,
+        )
+    };
+    assert_eq!(code, 0);
+    assert_eq!(allocated_by_mib, allocated);
+
+    unsafe { ffi::sdallocx(ptr, 100, 0) };
+}
+
+#[test]
+fn test_stats() {
+    struct PrintCtx {
+        called_times: usize,
+    }
+
+    extern "C" fn write_cb(ctx: *mut c_void, _: *const c_char) {
+        let print_ctx = unsafe { &mut *(ctx as *mut PrintCtx) };
+        print_ctx.called_times += 1;
+    }
+
+    let mut ctx = PrintCtx { called_times: 0 };
+    unsafe {
+        ffi::malloc_stats_print(
+            Some(write_cb),
+            &mut ctx as *mut _ as *mut c_void,
+            ptr::null(),
+        );
+    }
+    assert_ne!(
+        ctx.called_times, 0,
+        "print should be triggered at lease once."
+    );
+}
diff --git a/tests/grow_in_place.rs b/tests/grow_in_place.rs
new file mode 100644
index 0000000..ca245e2
--- /dev/null
+++ b/tests/grow_in_place.rs
@@ -0,0 +1,33 @@
+#![cfg_attr(feature = "alloc_trait", feature(allocator_api))]
+
+use tikv_jemallocator::Jemalloc;
+
+#[global_allocator]
+static A: Jemalloc = Jemalloc;
+
+#[test]
+#[cfg(feature = "alloc_trait")]
+fn shrink_in_place() {
+    unsafe {
+        use std::alloc::{Alloc, Layout};
+
+        // allocate 7 bytes which end up in the 8 byte size-class as long as
+        // jemalloc's default size classes are used:
+        let orig_sz = 7;
+        let orig_l = Layout::from_size_align(orig_sz, 1).unwrap();
+        let ptr = Jemalloc.alloc(orig_l).unwrap();
+
+        // try to grow it in place by 1 byte - it should grow without problems:
+        let new_sz = orig_sz + 1;
+        assert!(Jemalloc.grow_in_place(ptr, orig_l, new_sz).is_ok());
+        let new_l = Layout::from_size_align(orig_sz + 1, 1).unwrap();
+
+        // trying to do it again fails because it would require moving the
+        // allocation to a different size class which jemalloc's xallocx does not
+        // do:
+        let new_sz = new_sz + 1;
+        assert!(Jemalloc.grow_in_place(ptr, new_l, new_sz).is_err());
+
+        Jemalloc.dealloc(ptr, new_l)
+    }
+}
diff --git a/tests/malloctl.rs b/tests/malloctl.rs
new file mode 100644
index 0000000..155608b
--- /dev/null
+++ b/tests/malloctl.rs
@@ -0,0 +1,49 @@
+use std::alloc::{GlobalAlloc, Layout};
+use tikv_jemalloc_ctl::{Access, AsName};
+use tikv_jemallocator::Jemalloc;
+
+#[global_allocator]
+static A: Jemalloc = Jemalloc;
+
+#[test]
+fn smoke() {
+    let layout = Layout::from_size_align(100, 8).unwrap();
+    unsafe {
+        let ptr = Jemalloc.alloc(layout);
+        assert!(!ptr.is_null());
+        Jemalloc.dealloc(ptr, layout);
+    }
+}
+
+#[test]
+fn ctl_get_set() {
+    let epoch: u64 = "epoch\0".name().read().unwrap();
+    assert!(epoch > 0);
+    "epoch\0".name().write(epoch).unwrap();
+}
+
+#[test]
+#[should_panic]
+fn ctl_panic_empty_get() {
+    let _: u64 = "".name().read().unwrap();
+}
+
+#[test]
+#[should_panic]
+fn ctl_panic_empty_set() {
+    let epoch: u64 = "epoch\0".name().read().unwrap();
+    "".name().write(epoch).unwrap();
+}
+
+#[test]
+#[should_panic]
+fn ctl_panic_non_null_terminated_get() {
+    let _: u64 = "epoch".name().read().unwrap();
+}
+
+#[test]
+#[should_panic]
+fn ctl_panic_non_null_terminated_set() {
+    let epoch: u64 = "epoch\0".name().read().unwrap();
+    "epoch".name().write(epoch).unwrap();
+}
diff --git a/tests/shrink_in_place.rs b/tests/shrink_in_place.rs
new file mode 100644
index 0000000..ae43268
--- /dev/null
+++ b/tests/shrink_in_place.rs
@@ -0,0 +1,32 @@
+#![cfg_attr(feature = "alloc_trait", feature(allocator_api))]
+
+use tikv_jemallocator::Jemalloc;
+
+#[global_allocator]
+static A: Jemalloc = Jemalloc;
+
+#[test]
+#[cfg(feature = "alloc_trait")]
+fn shrink_in_place() {
+    unsafe {
+        use std::alloc::{Alloc, Layout};
+
+        // allocate a "large" block of memory:
+        let orig_sz = 10 * 4096;
+        let orig_l = Layout::from_size_align(orig_sz, 1).unwrap();
+        let ptr = Jemalloc.alloc(orig_l).unwrap();
+
+        // try to shrink it in place to 1 byte - if this succeeds,
+        // the size-class of the new allocation should be different
+        // than that of the original allocation:
+        let new_sz = 1;
+        if let Ok(()) = Jemalloc.shrink_in_place(ptr, orig_l, new_sz) {
+            // test that deallocating with the new layout succeeds:
+            let new_l = Layout::from_size_align(new_sz, 1).unwrap();
+            Jemalloc.dealloc(ptr, new_l);
+        } else {
+            // if shrink in place failed - deallocate with the old layout
+            Jemalloc.dealloc(ptr, orig_l);
+        }
+    }
+}
diff --git a/tests/smoke.rs b/tests/smoke.rs
new file mode 100644
index 0000000..69b8c42
--- /dev/null
+++ b/tests/smoke.rs
@@ -0,0 +1,41 @@
+use std::alloc::{GlobalAlloc, Layout};
+use tikv_jemallocator::Jemalloc;
+
+#[global_allocator]
+static A: Jemalloc = Jemalloc;
+
+#[test]
+fn smoke() {
+    let mut a = Vec::new();
+    a.reserve(1);
+    a.push(3);
+}
+
+/// https://github.com/rust-lang/rust/issues/45955
+#[test]
+fn overaligned() {
+    let size = 8;
+    let align = 16; // greater than size
+    let iterations = 100;
+    unsafe {
+        let pointers: Vec<_> = (0..iterations)
+            .map(|_| {
+                let ptr = Jemalloc.alloc(Layout::from_size_align(size, align).unwrap());
+                assert!(!ptr.is_null());
+                ptr
+            })
+            .collect();
+        for &ptr in &pointers {
+            assert_eq!(
+                (ptr as usize) % align,
+                0,
+                "Got a pointer less aligned than requested"
+            )
+        }
+
+        // Clean up
+        for &ptr in &pointers {
+            Jemalloc.dealloc(ptr, Layout::from_size_align(size, align).unwrap())
+        }
+    }
+}
diff --git a/tests/smoke_ffi.rs b/tests/smoke_ffi.rs
new file mode 100644
index 0000000..311ce34
--- /dev/null
+++ b/tests/smoke_ffi.rs
@@ -0,0 +1,13 @@
+// Work around https://github.com/gnzlbg/jemallocator/issues/19
+#[global_allocator]
+static A: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc;
+
+#[test]
+fn smoke() {
+    unsafe {
+        let ptr = tikv_jemalloc_sys::malloc(4);
+        *(ptr as *mut u32) = 0xDECADE;
+        assert_eq!(*(ptr as *mut u32), 0xDECADE);
+        tikv_jemalloc_sys::free(ptr);
+    }
+}
diff --git a/tests/usable_size.rs b/tests/usable_size.rs
new file mode 100644
index 0000000..2aa15de
--- /dev/null
+++ b/tests/usable_size.rs
@@ -0,0 +1,10 @@
+use tikv_jemallocator::Jemalloc;
+
+#[global_allocator]
+static A: Jemalloc = Jemalloc;
+
+#[test]
+fn smoke() {
+    let a = Box::new(3_u32);
+    assert!(unsafe { tikv_jemallocator::usable_size(&*a) } >= 4);
+}