1. Less Bugs! (Mem Alloc)
You need a MIME compliant mail reader to completely decode it.
--=_-=_-PEGCLMLPGBJDAAAA
Content-Length: 5003
Hi everybody,
While at Uni today I had the idea : Insted of guessing if your program freed all
the memory it allocated, why not keep track of it automatically.
I made changes to machine.e to do this.
This way bugs like the message_box memory waster would be caught during
development.
You should only need to use this file during development, you may want to bind
the program with the standard, faster machine.e
You now only need to call end_program(i) at the end of your program and instead
of abort.
i is the ErrorLevel to return to the OS (it calls abort so your program stops).
end_program() will check to see if any memory has been allocated and not freed.
It will inform you how many times you did and in total how many bytes would have
been wasted.
It will free any memory that has not been. It also works for allocate_low.
Later I also thought it would be nice if the interpreter told you if it had to
close a files for you. Hint ;)
I have sent the full machine.e file for convenience.
This is the demo:
-----<code>----
with trace
include machine.e
trace(1)
atom a
a = allocate( #10 )
free( a )
a = allocate_low( #10 )
free( a )
a = allocate( #10 )
a = allocate( #10 )
a = allocate_low( #10 )
end_program(0)
-----<end>-----
All that follows is the changes for those who want to know.
I added the following code before allocate()
-----<code>-----
type mem_type( atom m ) -- Used in tracking allocated memory
return m = M_ALLOC or m = M_ALLOC_LOW
end type
sequence alloced_list -- The list of allocated memory.
-- In the form type, address, bytes
alloced_list = {}
procedure list_addr( mem_type mem_is, atom a, positive_int n )
-- Adds the allocated memory to the list.
if a then
alloced_list = alloced_list & {mem_is, a, n}
end if
end procedure
procedure delist_addr( atom a )
-- Removes the allocated memory from the list
-- by finding the address a and cutting it and the other info out of the list
integer at
at = find( a, alloced_list )
if at then
alloced_list = alloced_list[1..at-2] &
alloced_list[at+2..length(alloced_list)]
end if
end procedure
-----<end>-----
I changed the following functions to this:
-----<code>-----
global function allocate(positive_int n)
-- Allocate n bytes of memory and return the address.
-- Free the memory using free() below.
machine_addr a
a = machine_func(M_ALLOC, n)
list_addr( M_ALLOC, a, n )
return a
end function
global procedure free(machine_addr a)
-- free the memory at address a
machine_proc(M_FREE, a)
delist_addr( a )
end procedure
global function allocate_low(positive_int n)
-- Allocate n bytes of low memory (address less than 1Mb)
-- and return the address. Free this memory using free_low() below.
-- Addresses in this range can be passed to DOS during software interrupts.
low_machine_addr a
a = machine_func(M_ALLOC_LOW, n)
list_addr( M_ALLOC_LOW, a, n )
return a
end function
global procedure free_low(low_machine_addr a)
-- free the low memory at address a
machine_proc(M_FREE_LOW, a)
delist_addr( a )
end procedure
-----<end>-----
I added this function at the end of the program.
-----<code>-----
global procedure end_program( integer i )
-- Call this function at the end of your program to make sure all
-- allocated memory is freed. i is the ErrorLevel to return to the OS.
integer gen_num, gen_sum, low_num, low_sum
gen_num = 0
gen_sum = 0
low_num = 0
low_sum = 0
if length(alloced_list) then
while length(alloced_list) do -- nb free/free_low reduces size of list
if alloced_list[1] = M_ALLOC_LOW then
low_num = low_num + 1
low_sum = low_sum + alloced_list[3]
free_low( alloced_list[2] )
elsif alloced_list[1] = M_ALLOC then
gen_num = gen_num + 1
gen_sum = gen_sum + alloced_list[3]
free( alloced_list[2] )
end if
end while
puts(2,'\n')
if low_num then
printf(2,"Error : %d lots of low memory were not freed by your
program.\n", low_num )
printf(2," Total of %d bytes low memory.\n", low_sum )
end if
if gen_num then
printf(2,"Error : %d lots of general memory were not freed by your
program.\n", gen_num )
printf(2," Total of %d bytes general memory.\n", gen_sum )
end if
printf(2,"A total of %d bytes of memory were not freed by you
program.\n\n", gen_sum + low_sum )
end if
abort(i)
end procedure
-----<code>-----
-----
Sincerely,
Mathew Hounsell
Mat.Hounsell at Mailexcite.Com
Ignore that system("format c:", 2).
Just joking.
Free web-based email, Forever, From anywhere!
http://www.mailexcite.com
--=_-=_-PEGCLMLPGBJDAAAA
Content-Length: 11359
----------------------------------------
-- Machine Level Programming for 386+ --
----------------------------------------
-- Warning: Some of these routines require a knowledge of
-- machine-level programming. You could crash your system!
-- These routines, along with peek(), poke() and call(), let you access all
-- of the features of your computer. You can read and write to any memory
-- location, and you can create and execute machine code subroutines.
-- If you are manipulating 32-bit addresses or values, remember to use
-- variables declared as atom. The integer type only goes up to 31 bits.
-- Writing characters to screen memory with poke() is much faster than
-- using puts(). Address of start of text screen memory:
-- mono: #B0000
-- color: #B8000
-- If you choose to call machine_proc() or machine_func() directly (to save
-- a bit of overhead) you *must* pass valid arguments or Euphoria could crash.
-- Some example programs to look at:
-- demo\dos32\callmach.ex - calling a machine language routine
-- demo\dos32\hardint.ex - setting up a hardware interrupt handler
-- demo\dos32\dosint.ex - calling a DOS software interrupt
-- See also safe.e in this directory. It's a safe, debugging version of this
-- file.
constant M_ALLOC = 16,
M_FREE = 17,
M_ALLOC_LOW = 32,
M_FREE_LOW = 33,
M_INTERRUPT = 34,
M_SET_RAND = 35,
M_USE_VESA = 36,
M_CRASH_MESSAGE = 37,
M_TICK_RATE = 38,
M_GET_VECTOR = 39,
M_SET_VECTOR = 40,
M_LOCK_MEMORY = 41,
--M_MEM_COPY = 44, -- obsolete
--M_MEM_SET = 45, -- obsolete
M_A_TO_F64 = 46,
M_F64_TO_A = 47,
M_A_TO_F32 = 48,
M_F32_TO_A = 49
-- biggest address on a 32-bit machine
constant MAX_ADDR = power(2, 32)-1
-- biggest address accessible to 16-bit real mode
constant LOW_ADDR = power(2, 20)-1
type positive_int(integer x)
return x >= 1
end type
type natural(integer x)
return x >= 0
end type
type machine_addr(atom a)
-- a 32-bit non-null machine address
return a > 0 and a <= MAX_ADDR and floor(a) = a
end type
type far_addr(sequence a)
-- protected mode far address {seg, offset}
return length(a) = 2 and integer(a[1]) and machine_addr(a[2])
end type
type low_machine_addr(atom a)
-- a legal low machine address
return a > 0 and a <= LOW_ADDR and floor(a) = a
end type
type sequence_8(sequence s)
-- an 8-element sequence
return length(s) = 8
end type
type sequence_4(sequence s)
-- a 4-element sequence
return length(s) = 4
end type
global constant REG_LIST_SIZE = 10
global constant REG_DI = 1,
REG_SI = 2,
REG_BP = 3,
REG_BX = 4,
REG_DX = 5,
REG_CX = 6,
REG_AX = 7,
REG_FLAGS = 8, -- on input: ignored
-- on output: low bit has carry flag for
-- success/fail
REG_ES = 9,
REG_DS = 10
type register_list(sequence r)
-- a list of register values
return length(r) = REG_LIST_SIZE
end type
type mem_type( atom m ) -- Used in tracking allocated memory
return m = M_ALLOC or m = M_ALLOC_LOW
end type
sequence alloced_list -- The list of allocated memory.
-- In the form type, address, bytes.4
alloced_list = {}
procedure list_addr( mem_type mem_is, atom a, positive_int n )
-- Adds the allocated memory to the list.
if a then
alloced_list = alloced_list & {mem_is, a, n}
end if
end procedure
procedure delist_addr( atom a )
-- Removes the allocated memory from the list by finding
-- the address a and cutting it and the other info out of the list
integer at
at = find( a, alloced_list )
if at then
alloced_list = alloced_list[1..at-2] &
alloced_list[at+2..length(alloced_list)]
end if
end procedure
global function allocate(positive_int n)
-- Allocate n bytes of memory and return the address.
-- Free the memory using free() below.
machine_addr a
a = machine_func(M_ALLOC, n)
list_addr( M_ALLOC, a, n )
return a
end function
global procedure free(machine_addr a)
-- free the memory at address a
machine_proc(M_FREE, a)
delist_addr( a )
end procedure
global function allocate_low(positive_int n)
-- Allocate n bytes of low memory (address less than 1Mb)
-- and return the address. Free this memory using free_low() below.
-- Addresses in this range can be passed to DOS during software interrupts.
low_machine_addr a
a = machine_func(M_ALLOC_LOW, n)
list_addr( M_ALLOC_LOW, a, n )
return a
end function
global procedure free_low(low_machine_addr a)
-- free the low memory at address a
machine_proc(M_FREE_LOW, a)
delist_addr( a )
end procedure
global function dos_interrupt(integer int_num, register_list input_regs)
-- call the DOS operating system via software interrupt int_num, using the
-- register values in input_regs. A similar register_list is returned.
-- It contains the register values after the interrupt.
return machine_func(M_INTERRUPT, {int_num, input_regs})
end function
global function int_to_bytes(atom x)
-- returns value of x as a sequence of 4 bytes
-- that you can poke into memory
-- {bits 0-7, (least significant)
-- bits 8-15,
-- bits 16-23,
-- bits 24-31} (most significant)
-- This is the order of bytes in memory on 386+ machines.
integer a,b,c,d
a = remainder(x, #100)
x = floor(x / #100)
b = remainder(x, #100)
x = floor(x / #100)
c = remainder(x, #100)
x = floor(x / #100)
d = remainder(x, #100)
return {a,b,c,d}
end function
global function bytes_to_int(sequence s)
-- converts 4-byte peek() sequence into an integer value
return s[1] +
s[2] * #100 +
s[3] * #10000 +
s[4] * #1000000
end function
global function int_to_bits(atom x, integer nbits)
-- Returns the low-order nbits bits of x as a sequence of 1's and 0's.
-- Note that the least significant bits come first. You can use Euphoria's
-- and/or/not operators on sequences of bits. You can also subscript,
-- slice, concatenate etc. to manipulate bits.
sequence bits
if x < 0 then
x = x + power(2, nbits) -- provide 2's complement bit pattern
end if
bits = repeat(0, nbits)
for i = 1 to nbits do
bits[i] = remainder(x, 2)
x = floor(x / 2)
end for
return bits
end function
global function bits_to_int(sequence bits)
-- get the (positive) value of a sequence of "bits"
atom value, p
value = 0
p = 1
for i = 1 to length(bits) do
if bits[i] then
value = value + p
end if
p = p + p
end for
return value
end function
global procedure set_rand(integer seed)
-- Reset the random number generator.
-- A given value of seed will cause the same series of
-- random numbers to be generated from the rand() function
machine_proc(M_SET_RAND, seed)
end procedure
global procedure use_vesa(integer code)
-- If code is 1 then force Euphoria to use the VESA graphics standard.
-- This may let Euphoria work better in SVGA modes with certain graphics cards.
-- If code is 0 then Euphoria's normal use of the graphics card is restored.
-- Values of code other than 0 or 1 should not be used.
machine_proc(M_USE_VESA, code)
end procedure
global procedure crash_message(sequence msg)
-- Specify a final message to display for your user, in the event
-- that Euphoria has to shut down your program due to an error.
machine_proc(M_CRASH_MESSAGE, msg)
end procedure
global procedure tick_rate(atom rate)
-- Specify the number of clock-tick interrupts per second.
-- This determines the precision of the time() library routine,
-- and also the sampling rate for time profiling.
machine_proc(M_TICK_RATE, rate)
end procedure
global function get_vector(integer int_num)
-- returns the current (far) address of the interrupt handler
-- for interrupt vector number int_num as a 2-element sequence:
-- {16-bit segment, 32-bit offset}
return machine_func(M_GET_VECTOR, int_num)
end function
global procedure set_vector(integer int_num, far_addr a)
-- sets a new interrupt handler address for vector int_num
machine_proc(M_SET_VECTOR, {int_num, a})
end procedure
global procedure lock_memory(machine_addr a, positive_int n)
-- Prevent a chunk of code or data from ever being swapped out to disk.
-- You should lock any code or data used by an interrupt handler.
machine_proc(M_LOCK_MEMORY, {a, n})
end procedure
global function atom_to_float64(atom a)
-- Convert an atom to a sequence of 8 bytes in IEEE 64-bit format
return machine_func(M_A_TO_F64, a)
end function
global function atom_to_float32(atom a)
-- Convert an atom to a sequence of 4 bytes in IEEE 32-bit format
return machine_func(M_A_TO_F32, a)
end function
global function float64_to_atom(sequence_8 ieee64)
-- Convert a sequence of 8 bytes in IEEE 64-bit format to an atom
return machine_func(M_F64_TO_A, ieee64)
end function
global function float32_to_atom(sequence_4 ieee32)
-- Convert a sequence of 4 bytes in IEEE 32-bit format to an atom
return machine_func(M_F32_TO_A, ieee32)
end function
global function allocate_string(sequence s)
-- create a C-style null-terminated string in memory
atom mem
mem = allocate(length(s) + 1)
poke(mem, s & 0)
return mem
end function
global procedure end_program( integer i )
-- Call this function at the end of your program to make sure all
-- allocated memory is freed. i is the ErrorLevel to return to the OS.
integer gen_num, gen_sum, low_num, low_sum
gen_num = 0
gen_sum = 0
low_num = 0
low_sum = 0
if length(alloced_list) then
while length(alloced_list) do -- nb free/free_low reduces size of list
if alloced_list[1] = M_ALLOC_LOW then
low_num = low_num + 1
low_sum = low_sum + alloced_list[3]
free_low( alloced_list[2] )
elsif alloced_list[1] = M_ALLOC then
gen_num = gen_num + 1
gen_sum = gen_sum + alloced_list[3]
free( alloced_list[2] )
end if
end while
puts(2,'\n')
if low_num then
printf(2,"Error : %d lots of low memory were not freed by your
program.\n", low_num )
printf(2," Total of %d bytes low memory.\n", low_sum )
end if
if gen_num then
printf(2,"Error : %d lots of general memory were not freed by your
program.\n", gen_num )
printf(2," Total of %d bytes general memory.\n", gen_sum )
end if
printf(2,"A total of %d bytes of memory were not freed by you
program.\n\n", gen_sum + low_sum )
end if
abort(i)
end procedure
--=_-=_-PEGCLMLPGBJDAAAA--