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) ]
86use 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