Re: Adding to or Updating the standard library
- Posted by Shawn Pringle <shawn.pringle at gma?l.?om> May 11, 2008
- 652 views
Jeremy Cowgar wrote: > > CChris wrote: > > > Two questions: > > 1/ How to unit-test that something should crash? > > I have not solved that yet. Let me work on that now. > Testing if something crashes requires re-thinking the way we think of program control. For my executable memory allocator, allocate_code() I wrote a test that first tries to run code allocated by allocate_code() and then tries the same with allocate(). For allocate() in particular, I made a global flag variable and a crash_routine handler. The global flag is set to true, when we want it to intercept a crash and say something like: "code executed from memory using allocate caused an exception and as it should." When the flag is set to false the the handler returns 0 and no success message is printed and EUPHORIA calls the other handlers if applicable. I set the global flag before executing the code that is supposed to cause the exception and then I clear the flag afterwards. After clearing the flag an error message is printed, for it means that the crash routine wasn't executed. At this point though, the mainline of the program is in a crash handler! In the case of allocate_code(), we do not want to see an exception. So, in this cause the crash handler prints a message saying the test has failed and then terminates the program. After calling code on allocate_code that could cause an exception (if there is a bug) it prints a success message. The program flow reminds me of goto from BASIC.
-- Test the system for : include xalloc.e as dep include joy.e as joy function isspace( integer x ) return find( x, "\t \n" ) end function integer log log = open( "wordwrap.txt", "w" ) function visual_slice( joy:string_of_atoms s, integer i1, integer i2, integer line_length ) integer c, m1, m2 c = 0 m1 = -1 m2 = -1 for i3 = 1 to length(s) do if s[i3] = '\n' then c += line_length elsif s[i3] = '\t' then c += 8 else c += 1 end if if m1 = -1 then if c > i1 then m1 = c-1 elsif c = i1 then m1 = c end if end if if m2 = -1 then if c > i2 then m2 = c-1 else m2 = c end if end if if m2 != -1 then exit end if end for return s[m1..m2] end function -- word_wrap function: Please do not pass strings with TAB characters function word_wrap( joy:string_of_integers s, integer line_length ) sequence lines sequence haystack integer whitelocation lines = {} while length(s) do haystack = s[1..joy:min({length(s),line_length})] puts( log, "haystack = "&haystack&10 ) if length(s) <= line_length then lines = append( lines, s ) s = "" else -- if no spaces Grab line_length of it -- if '\n' grab the first one, or the last ' ' -- whichever comes first. whitelocation = joy:min( {max({length(s),line_length}) + 1} & joy:remove_0s( { find( '\n', haystack ), joy:rfind( ' ', haystack ) } ) ) puts( log, "s[1..whitelocation-1]=" & s[1..whitelocation-1] & 10 ) lines = append( lines, s[1..whitelocation-1] ) flush(log) if whitelocation+1 > length(s)+1 then s = "" else s = s[whitelocation+1..length(s)] end if end if end while return joy:join( "\n", lines ) end function object line procedure PETC() puts(1,"Press Enter to Continue") line = gets(0) puts(1,10) end procedure integer calling_memfunction calling_memfunction = 0 function print_failure(object x) if calling_memfunction then puts( 1, word_wrap( "We have found that the xalloc.e routines do not really make memory executable.\n", 80 ) ) puts( 1, word_wrap( "This conclusively tells us that there is a bug in the allocate_code() function. Please let me know about this: Email me at shawn.pringle at gmail.com", 80 ) ) puts( 1, "Test result for allocate_code: FAILURE\n" ) PETC() abort(0) end if return 0 end function integer calling_dmemfunction calling_dmemfunction = 0 function dep_is_enabled(object x) if calling_dmemfunction then puts(1, word_wrap( "The code allocated with allocate(), however raised an exception and it SHOULD. Therefore D.E.P. is enabled for the interpreter you used for this test on your system. This means, your system is configured correctly to test allocate_code and that allocate_code works properly.\n", 80 ) ) puts(1, "Test result for allocate_code: SUCCESS!\n" ) PETC() abort(0) end if return 0 end function atom code_space, data_space sequence multiply_code atom rexec, rdata atom x,y object void -- machine code taken from callmach.ex multiply_code = { -- int argument is at stack offset +4, double is at +8 #DB, #44, #24, #04, -- fild dword ptr +4[esp] #DC, #4C, #24, #08, -- fmul qword ptr +8[esp] #C2, #0C - 4 * (platform()=LINUX), #00 -- ret C -- pop 12 (or 8) bytes -- off the stack } crash_routine(routine_id("print_failure")) crash_routine(routine_id("dep_is_enabled")) code_space = dep:allocate_code(multiply_code) data_space = allocate(length(multiply_code)) poke( data_space, multiply_code ) rexec = define_c_func("", code_space, {C_INT, C_DOUBLE}, C_DOUBLE) rdata = define_c_func("", data_space, {C_INT, C_DOUBLE}, C_DOUBLE ) x = 7 y = 8.5 puts( 1, word_wrap( "We will now call a machine code function using memory marked as executable.\n", 80 ) ) PETC() calling_memfunction = 1 void = c_func(rexec, {x, y}) calling_memfunction = 0 printf(1, word_wrap( "The code allocated with allocate_code() was called without an exception being raised.\nThe function allocate_code() works as it should.\n", 80 ), {} ) puts(1, "\n\n" ) puts(1, word_wrap( "We will now call a machine code function without using memory marked as executable. This should cause an exception.\n", 80 ) ) PETC() function ifthenelse( integer condition, sequence s1, object x2 ) if condition then return s1 else return x2 end if end function calling_dmemfunction = 1 void = c_func(rdata, {x,y}) calling_dmemfunction = 0 printf(1, word_wrap( "The code allocated with allocate() was also called " & "without an exception being "& "raised. This means your system has no DEP for this interpreter " & " and these tests are " & "inconclusive. " & "You need to enable DEP in both your pre-OS hardware configuration screen" & " at " & "boot up and your Operating system. "& "You probably can get into the hardware by pressing the delete key at boot up. "& ifthenelse( platform() = WIN32, "In Windows XP, you can find the configuration for "& "hardware DEP by first, opening Control_Panel, next from there opening "& "System, after that clicking the Advanced_Tab, and next clicking the Performance button and "& "finally clicking the Data execution Prevention tab. " & "There you can choose the option for enabling DEP for all processes.", "")& ifthenelse( platform() = LINUX, "In Linux, you will need to download some kernel patches to allow, "& "this to work.", "" ) & ifthenelse( platform() = DOS32, "In DOS, there is no way to enable hardware DEP.", "" )&"\n", 80) , {} ) close(log) puts(1, "Test result for allocate_code: INCONCLUSIVE!\n" ) PETC()
Shawn Pringle