Skip to content

Commit a120fcc

Browse files
authored
examples/check_real_memory: use a hand written buddy allocator instead of jemalloc (#500)
* examples/check_real_memory: use a hand written buddy allocator instead of jemalloc * Use pastey instead of paste * Use buddy-alloc
1 parent 8f14f77 commit a120fcc

File tree

4 files changed

+67
-206
lines changed

4 files changed

+67
-206
lines changed

.github/workflows/develop.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ jobs:
1111
steps:
1212
- uses: actions/checkout@v3
1313
# https://github.com/EmbarkStudios/cargo-deny-action
14-
- uses: EmbarkStudios/cargo-deny-action@v1
14+
- uses: EmbarkStudios/cargo-deny-action@v2
1515
with:
1616
arguments: --all-features
1717
command: check advisories licenses sources bans

Cargo.toml

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,7 @@ cc = "1.0"
4040
argparse = "0.2"
4141
criterion = "0.5.1"
4242
proptest = "1.5.0"
43-
44-
[target.'cfg(not(target_os = "windows"))'.dev-dependencies]
45-
jemallocator = "0.5.0"
46-
jemalloc-ctl = "0.5.0"
43+
buddy-alloc = "0.6.0"
4744

4845
[[bench]]
4946
name = "bits_benchmark"

definitions/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,4 @@ name = "generate_asm_constants"
1818
path = "src/generate_asm_constants.rs"
1919

2020
[dependencies]
21-
paste = "1.0.12"
21+
paste = { package = "pastey", version = "0.2.1" }

examples/check_real_memory.rs

Lines changed: 64 additions & 200 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
// This example is mainly to test whether there is memory overflow.
2-
// Under linux, we choose to use smem, which can monitor memory changes more accurately
32

4-
use ckb_vm::{Bytes, FlatMemory, SparseMemory, run_with_memory};
5-
use std::process::{Command, id};
3+
use ckb_vm::{Bytes, DEFAULT_MEMORY_SIZE, SparseMemory, run_with_memory};
64

75
#[cfg(has_asm)]
86
use ckb_vm::{
@@ -13,162 +11,97 @@ use ckb_vm::{
1311
},
1412
};
1513

16-
#[cfg(has_asm)]
17-
use std::thread;
14+
use buddy_alloc::{BuddyAllocParam, buddy_alloc::BuddyAlloc};
15+
use std::alloc::GlobalAlloc;
16+
use std::alloc::Layout;
17+
use std::cell::RefCell;
1818

19-
static BIN_PATH_BUFFER: &'static [u8] = include_bytes!("../tests/programs/alloc_many");
20-
static BIN_NAME: &str = "alloc_many";
21-
22-
#[cfg(not(target_os = "windows"))]
23-
#[global_allocator]
24-
static GLOBAL: jemallocator::Jemalloc = jemallocator::Jemalloc;
19+
const HEAP_SIZE: usize = 16 * 1024 * 1024;
20+
const LEAF_SIZE: usize = 64;
21+
#[repr(align(64))]
22+
struct Heap<const S: usize>([u8; S]);
23+
static mut HEAP: Heap<HEAP_SIZE> = Heap([0u8; HEAP_SIZE]);
2524

26-
static G_CHECK_LOOP: usize = 10;
27-
28-
fn get_current_memory_linux() -> usize {
29-
let output = String::from_utf8(
30-
Command::new("smem")
31-
.arg("-P")
32-
.arg("check_real_memory") // current process name
33-
.arg("-c")
34-
.arg("pid uss")
35-
.output()
36-
.expect("run ps failed")
37-
.stdout,
38-
)
39-
.unwrap();
25+
pub struct NonThreadsafeAlloc {
26+
buddy_alloc_param: BuddyAllocParam,
27+
inner_buddy_alloc: RefCell<Option<BuddyAlloc>>,
28+
}
4029

41-
let outputs = output.split("\n").collect::<Vec<&str>>();
42-
for i in 1..outputs.len() {
43-
let mut has_pid = false;
44-
let mut memory_size: u32 = 0;
45-
for d in outputs[i].split(" ").collect::<Vec<&str>>() {
46-
if d == " " || d == "" {
47-
continue;
48-
}
49-
let val: u32 = d.parse().unwrap();
50-
if !has_pid {
51-
if val != id() {
52-
continue;
53-
}
54-
has_pid = true;
55-
} else {
56-
memory_size = val;
57-
}
58-
}
59-
if memory_size != 0 {
60-
return memory_size as usize;
30+
impl NonThreadsafeAlloc {
31+
/// see BuddyAlloc::new
32+
pub const fn new(buddy_alloc_param: BuddyAllocParam) -> Self {
33+
NonThreadsafeAlloc {
34+
inner_buddy_alloc: RefCell::new(None),
35+
buddy_alloc_param,
6136
}
6237
}
6338

64-
0
65-
}
66-
67-
fn get_current_memory() -> usize {
68-
if !cfg!(target_os = "linux") {
69-
get_current_memory_linux()
70-
} else {
71-
let pid = format!("{}", id());
72-
let output = String::from_utf8(
73-
Command::new("ps")
74-
.arg("-p")
75-
.arg(pid)
76-
.arg("-o")
77-
.arg("rss")
78-
.output()
79-
.expect("run ps failed")
80-
.stdout,
81-
)
82-
.unwrap();
83-
84-
let output = output.split("\n").collect::<Vec<&str>>();
39+
unsafe fn with_buddy_alloc<R, F: FnOnce(&mut BuddyAlloc) -> R>(&self, f: F) -> R {
40+
let mut inner = self.inner_buddy_alloc.borrow_mut();
41+
let alloc = inner.get_or_insert_with(|| unsafe { BuddyAlloc::new(self.buddy_alloc_param) });
42+
f(alloc)
43+
}
8544

86-
let memory_size = output[1].replace(" ", "");
87-
memory_size.parse().unwrap()
45+
fn used(&self) -> usize {
46+
HEAP_SIZE
47+
- self
48+
.inner_buddy_alloc
49+
.borrow()
50+
.as_ref()
51+
.unwrap()
52+
.available_bytes()
8853
}
8954
}
9055

91-
struct MemoryOverflow {
92-
#[cfg(not(target_os = "windows"))]
93-
base_allocated: usize,
56+
unsafe impl GlobalAlloc for NonThreadsafeAlloc {
57+
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
58+
let bytes = layout.size();
59+
unsafe { self.with_buddy_alloc(|alloc| alloc.malloc(bytes)) }
60+
}
9461

95-
#[cfg(not(target_os = "windows"))]
96-
base_resident: usize,
62+
unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
63+
unsafe { self.with_buddy_alloc(|alloc| alloc.free(ptr)) };
64+
}
9765
}
9866

99-
impl MemoryOverflow {
100-
pub fn new() -> Self {
101-
Self {
102-
#[cfg(not(target_os = "windows"))]
103-
base_allocated: jemalloc_ctl::stats::allocated::read().unwrap(),
104-
105-
#[cfg(not(target_os = "windows"))]
106-
base_resident: jemalloc_ctl::stats::resident::read().unwrap(),
107-
}
108-
}
67+
unsafe impl Sync for NonThreadsafeAlloc {}
10968

110-
pub fn check(&self) {
111-
#[cfg(not(target_os = "windows"))]
112-
assert!(jemalloc_ctl::stats::allocated::read().unwrap() <= self.base_allocated);
69+
#[allow(static_mut_refs)]
70+
#[global_allocator]
71+
static ALLOC: NonThreadsafeAlloc =
72+
unsafe { NonThreadsafeAlloc::new(BuddyAllocParam::new(HEAP.0.as_ptr(), HEAP_SIZE, LEAF_SIZE)) };
11373

114-
#[cfg(not(target_os = "windows"))]
115-
assert!(jemalloc_ctl::stats::resident::read().unwrap() <= self.base_resident);
116-
}
117-
}
74+
static BIN_PATH_BUFFER: &'static [u8] = include_bytes!("../tests/programs/alloc_many");
75+
static BIN_NAME: &str = "alloc_many";
76+
static G_CHECK_LOOP: usize = 10;
11877

119-
fn check_interpreter(memory_size: usize) -> Result<(), ()> {
120-
println!(
121-
"Check interpreter memory overflow, ckb-vm memory size: {}",
122-
memory_size
123-
);
124-
println!("Base memory: {}", get_current_memory());
78+
fn check_interpreter() -> Result<(), Box<dyn std::error::Error>> {
79+
println!("Check interpreter: init");
80+
println!("Check interpreter: base memory used {}", ALLOC.used());
12581
for _ in 0..G_CHECK_LOOP {
12682
let result = run_with_memory::<u64, SparseMemory<u64>>(
12783
&Bytes::from(BIN_PATH_BUFFER),
12884
&vec![Bytes::from(BIN_NAME)],
129-
memory_size,
85+
DEFAULT_MEMORY_SIZE,
13086
);
13187
assert!(result.is_ok());
13288
assert_eq!(result.unwrap(), 0);
133-
println!("Current memory: {}", get_current_memory());
89+
println!("Check interpreter: step memory used {}", ALLOC.used());
13490
}
135-
println!("End of check");
136-
Ok(())
137-
}
138-
139-
fn check_falt(memory_size: usize) -> Result<(), ()> {
140-
println!(
141-
"Check falt memory overflow, ckb-vm memory size: {}",
142-
memory_size
143-
);
144-
println!("Base memory: {}", get_current_memory());
145-
for _ in 0..G_CHECK_LOOP {
146-
let result = run_with_memory::<u64, FlatMemory<u64>>(
147-
&Bytes::from(BIN_PATH_BUFFER),
148-
&vec![Bytes::from(BIN_NAME)],
149-
memory_size,
150-
);
151-
assert!(result.is_ok());
152-
assert_eq!(result.unwrap(), 0);
153-
println!("Current memory: {}", get_current_memory());
154-
}
155-
println!("End of check");
91+
println!("Check interpreter: done memory used {}", ALLOC.used());
15692
Ok(())
15793
}
15894

15995
#[cfg(has_asm)]
160-
fn check_asm(memory_size: usize) -> Result<(), ()> {
161-
println!(
162-
"Check asm memory overflow, ckb-vm memory size: {}",
163-
memory_size
164-
);
165-
println!("Base memory: {}", get_current_memory());
96+
fn check_asm() -> Result<(), Box<dyn std::error::Error>> {
97+
println!("Check asm: init",);
98+
println!("Check asm: base memory used {}", ALLOC.used());
16699
for _ in 0..G_CHECK_LOOP {
167100
let asm_core = <AsmCoreMachine as SupportMachine>::new_with_memory(
168101
ISA_IMC,
169102
VERSION0,
170103
u64::MAX,
171-
memory_size,
104+
DEFAULT_MEMORY_SIZE,
172105
);
173106
let core = AsmDefaultMachineBuilder::new(asm_core).build();
174107
let mut machine = AsmMachine::new(core);
@@ -181,84 +114,15 @@ fn check_asm(memory_size: usize) -> Result<(), ()> {
181114
let result = machine.run();
182115
assert!(result.is_ok());
183116
assert_eq!(result.unwrap(), 0);
184-
185-
println!("Current memory: {}", get_current_memory());
117+
println!("Check asm: step memory used {}", ALLOC.used());
186118
}
187-
println!("End of check");
119+
println!("Check asm: done memory used {}", ALLOC.used());
188120
Ok(())
189121
}
190122

191-
#[cfg(has_asm)]
192-
fn check_asm_in_thread(memory_size: usize) -> Result<(), ()> {
193-
println!(
194-
"Check asm in thread memory overflow, ckb-vm memory size: {}",
195-
memory_size
196-
);
197-
println!("Base memory: {}", get_current_memory());
198-
for _ in 0..G_CHECK_LOOP {
199-
let asm_core = <AsmCoreMachine as SupportMachine>::new_with_memory(
200-
ISA_IMC,
201-
VERSION0,
202-
u64::MAX,
203-
memory_size,
204-
);
205-
let core = AsmDefaultMachineBuilder::new(asm_core).build();
206-
let mut machine = AsmMachine::new(core);
207-
machine
208-
.load_program(
209-
&Bytes::from(BIN_PATH_BUFFER),
210-
[Ok(Bytes::from(BIN_NAME))].into_iter(),
211-
)
212-
.unwrap();
213-
let thread_join_handle = thread::spawn(move || {
214-
let result = machine.run();
215-
assert!(result.is_ok());
216-
assert_eq!(result.unwrap(), 0);
217-
});
218-
thread_join_handle.join().unwrap();
219-
println!("Current memory: {}", get_current_memory());
220-
}
221-
println!("End of check");
222-
Ok(())
223-
}
224-
225-
fn test_memory(memory_size: usize) -> Result<(), ()> {
226-
if check_interpreter(memory_size).is_err() {
227-
return Err(());
228-
}
229-
if check_falt(memory_size).is_err() {
230-
return Err(());
231-
}
232-
123+
fn main() -> Result<(), Box<dyn std::error::Error>> {
124+
check_interpreter()?;
233125
#[cfg(has_asm)]
234-
if check_asm(memory_size).is_err() {
235-
return Err(());
236-
}
237-
238-
#[cfg(has_asm)]
239-
if check_asm_in_thread(memory_size).is_err() {
240-
return Err(());
241-
}
126+
check_asm()?;
242127
Ok(())
243128
}
244-
245-
fn main() {
246-
#[cfg(not(target_os = "windows"))]
247-
jemalloc_ctl::epoch::advance().unwrap();
248-
249-
let memory_overflow = MemoryOverflow::new();
250-
251-
let memory_size = 1024 * 1024 * 4;
252-
if test_memory(memory_size).is_err() {
253-
panic!("run testcase failed");
254-
}
255-
256-
memory_overflow.check();
257-
258-
let memory_size = 1024 * 1024 * 2;
259-
if test_memory(memory_size).is_err() {
260-
panic!("run testcase failed");
261-
}
262-
263-
memory_overflow.check();
264-
}

0 commit comments

Comments
 (0)