def read_memory(addr, num_elements, options = {}, &block)
      check_alive! # this would set @pid
      options[:as] = block if block_given?
      MemoryIO.attach(@pid).read(addr, num_elements, **options)
    end
def read_memory(addr, num_elements, options = {}, &block)
      check_alive! # this would set @pid
      options[:as] = block if block_given?
      MemoryIO.attach(@pid).read(addr, num_elements, **options)
    end
Read current process's memory.

 @param [Integer, String] addr
   Address to start to read.
   +addr+ can be a string like 'heap+0x10'.
   Supported variables are names in /proc/$pid/maps such as +heap/libc/stack/ld+.

 @param [Integer] num_elements
   Number of elements to read.
   If +num_elements+ equals to 1, an object read will be returned.
   Otherwise, an array with size +num_elements+ will be returned.

 @option [Symbol, Class] as
   Types that supported by [MemoryIO](https://github.com/david942j/memory_io).

 @return [Object, Array<Object>]
   Return types are decided by value of +num_elements+ and option +as+.

 @yieldparam [IO] io
   The +IO+ object that points to +addr+,
   read from it.

 @yieldreturn [Object]
   Whatever you read from +io+.

 @example
   gdb = GDB::GDB.new('spec/binaries/amd64.elf')
   gdb.break('main')
   gdb.run
   gdb.read_memory('amd64.elf', 4)
   #=> "\x7fELF"

 @example
   # example of fetching argv
   gdb = GDB::GDB.new('spec/binaries/amd64.elf')
   gdb.break('main')
   gdb.run('pusheen the cat')
   gdb.read_memory(0x400000, 4)
   #=> "\x7fELF"
   argc = gdb.register(:rdi)
   #=> 4
   args = gdb.read_memory(gdb.register(:rsi), argc, as: :u64)
   Array.new(3) do |i|
     gdb.read_memory(args[i + 1], 1) do |m|
       str = ''
       loop do
         c = m.read(1)
         break if c == "\x00"
         str << c
       end
       str
     end
   end
   #=> ["pusheen", "the", "cat"]

   # or, use our build-in types of gem +memory_io+.
   gdb.read_memory(args[1], 3, as: :c_str)
   #=> ["pusheen", "the", "cat"]