Improve CI (#98)

* New action to publish on release the python package on Pypi
* Migrate tests to unittest and pytest
* Rework the CI pipeline to build and test on 3 python versions (3.7, 3.8 and 3.9)
* Rename Dockerfile into avatar2.Dockerfile

Co-authored-by: mariusmue <[email protected]>
This commit is contained in:
rawsample
2022-01-25 19:45:37 +01:00
committed by GitHub
co-authored by mariusmue
parent a321ba22b3
commit c996a4a8a8
10 changed files with 678 additions and 625 deletions
-70
View File
@@ -1,70 +0,0 @@
name: Build and test
on:
push:
branches:
- 'main'
- 'dev/*'
pull_request:
branches:
- 'main'
- 'dev/*'
jobs:
build_and_test:
if: github.repository == 'avatartwo/avatar2'
runs-on: ubuntu-20.04
steps:
- name: Check out repository code
uses: actions/checkout@v2
- name: Generate Dockerfile
run: python3 ${{ github.workspace }}/generate_dockerfile.py --endpoint_list avatar-qemu panda --qemu_targets arm-softmmu mips-softmmu
- name: Build docker container
run: docker build --tag avatar2 .
- name: Test import avatar2
run: docker run --rm avatar2 bash -c "python3 -c 'import avatar2'"
- name: Run avatar2 container
run: |
docker run -v ${{ github.workspace }}:/avatar2 --name avatar2 --cap-add=SYS_PTRACE --security-opt seccomp=unconfined -dit avatar2
docker exec avatar2 pip3 install nose
# Test cases
- name: hello_world
run: docker exec avatar2 bash -c 'cd /avatar2/ && python3 ./tests/hello_world.py'
- name: test_remote_memoryprotocol
run: docker exec avatar2 bash -c 'cd /avatar2/ && nosetests ./tests/test_remote_memoryprotocol.py'
- name: test_gdbprotocol
run: docker exec avatar2 bash -c 'cd /avatar2/ && nosetests ./tests/test_gdbprotocol.py'
- name: test_gdbplugin
run: docker exec avatar2 bash -c 'cd /avatar2/ && nosetests ./tests/test_gdbplugin.py'
- name: test_inceptionprotocol
run: docker exec avatar2 bash -c 'cd /avatar2/ && nosetests ./tests/test_inceptionprotocol.py'
- name: gdb_memory_map_loader
run: docker exec avatar2 bash -c 'cd /avatar2/ && python3 ./tests/gdb_memory_map_loader.py'
- name: test_qemutarget ARM
run: docker exec avatar2 bash -c 'cd /avatar2/ && AVATAR2_GDB_EXECUTABLE=gdb-multiarch AVATAR2_ARCH=ARM AVATAR2_QEMU_EXECUTABLE=qemu-system-arm nosetests ./tests/test_qemutarget.py'
- name: test_qemutarget MIPS
run: docker exec avatar2 bash -c 'cd /avatar2/ && AVATAR2_GDB_EXECUTABLE=gdb-multiarch AVATAR2_ARCH=MIPS AVATAR2_QEMU_EXECUTABLE=qemu-system-mips nosetests ./tests/test_qemutarget.py'
- name: test_pyperipheral
run: docker exec avatar2 bash -c 'cd /avatar2/ && AVATAR2_GDB_EXECUTABLE=gdb-multiarch AVATAR2_QEMU_EXECUTABLE=qemu-system-arm nosetests ./tests/pyperipheral/test_pyperipheral.py'
- name: test_qemutarget ARM PANDA
run: docker exec avatar2 bash -c 'cd /avatar2/ && AVATAR2_GDB_EXECUTABLE=gdb-multiarch AVATAR2_ARCH=ARM AVATAR2_QEMU_EXECUTABLE=panda-system-arm nosetests ./tests/test_qemutarget.py'
- name: test_qemutarget MIPS PANDA
run: docker exec avatar2 bash -c 'cd /avatar2/ && AVATAR2_GDB_EXECUTABLE=gdb-multiarch AVATAR2_ARCH=MIPS AVATAR2_QEMU_EXECUTABLE=panda-system-mips nosetests ./tests/test_qemutarget.py'
- name: test_pyperipheral
run: docker exec avatar2 bash -c 'cd /avatar2/ && AVATAR2_GDB_EXECUTABLE=gdb-multiarch AVATAR2_QEMU_EXECUTABLE=panda-system-arm nosetests ./tests/pyperipheral/test_pyperipheral.py'
- name: smoke panda_thumb
run: docker exec avatar2 bash -c 'cd /avatar2/ && AVATAR2_GDB_EXECUTABLE=gdb-multiarch AVATAR2_QEMU_EXECUTABLE=panda-system-arm AVATAR2_PANDA_EXECUTABLE=panda-system-arm nosetests ./tests/smoke/panda_thumb.py'
- name: test_pypandatarget
run: docker exec avatar2 bash -c 'cd /avatar2/ && AVATAR2_GDB_EXECUTABLE=gdb-multiarch AVATAR2_QEMU_EXECUTABLE=panda-system-arm AVATAR2_PANDA_EXECUTABLE=panda-system-arm nosetests ./tests/test_pypandatarget.py'
- name: smoke target_wait
run: docker exec avatar2 bash -c 'cd /avatar2/ && nosetests --processes=-1 --process-timeout=20 ./tests/smoke/target_wait.py'
- name: smoke 54_sync_hooks
run: docker exec avatar2 bash -c 'cd /avatar2/ && AVATAR2_GDB_EXECUTABLE=gdb-multiarch AVATAR2_QEMU_EXECUTABLE=panda-system-arm nosetests --processes=-1 --process-timeout=20 ./tests/smoke/54_sync_hooks.py'
@@ -0,0 +1,130 @@
name: CI
on:
push:
branches:
- 'main'
- 'dev**'
pull_request:
branches:
- 'main'
- 'dev**'
jobs:
build:
if: github.repository == 'avatartwo/avatar2'
runs-on: ubuntu-20.04
strategy:
fail-fast: false
matrix:
python-version: ["3.7", "3.8", "3.9"]
steps:
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get upgrade -y
sudo apt-get install -y -m -f --install-suggests cmake build-essential git libcapstone3 libcapstone-dev
python -m pip install --upgrade pip
pip install pytest
pip install "cffi>1.14.3" # for pypanda
- name: Check out repository code
uses: actions/checkout@v2
- name: Build avatar2
run: |
cd ${{ github.workspace }}
python setup.py install
- name: Install debuggers targets
run: |
sudo apt-get install -y -m -f --install-suggests gdb gdb-multiarch openocd
- name: Build avatar-qemu target
run: |
cd ${{ github.workspace }}/targets
echo yes | ./build_qemu.sh arm-softmmu,mips-softmmu
cd build/qemu
sudo make install
- name: Run unit tests
env:
AVATAR2_ARCH: ARM
AVATAR2_GDB_EXECUTABLE: gdb-multiarch
run: |
cd ${{ github.workspace }}
python ./tests/hello_world.py
python ./tests/gdb_memory_map_loader.py
- name: Run unit tests for ARM
env:
AVATAR2_ARCH: ARM
AVATAR2_GDB_EXECUTABLE: gdb-multiarch
AVATAR2_QEMU_EXECUTABLE: qemu-system-arm
run: |
cd ${{ github.workspace }}/tests
pytest test_gdbplugin.py \
test_gdbprotocol.py \
test_inceptionprotocol.py \
test_remote_memoryprotocol.py \
test_qemutarget.py
- name: Run unit tests for MIPS
env:
AVATAR2_ARCH: MIPS
AVATAR2_GDB_EXECUTABLE: gdb-multiarch
AVATAR2_QEMU_EXECUTABLE: qemu-system-mips
run: |
cd ${{ github.workspace }}/tests
pytest test_gdbplugin.py \
test_gdbprotocol.py \
test_inceptionprotocol.py \
test_remote_memoryprotocol.py \
test_qemutarget.py
- name: Build panda target
run: |
git clone https://github.com/panda-re/panda.git
cd panda
git checkout 5fb8a5098d0647a13e5e7d39d20ff9f04242572d
sudo prefix=/usr/local ./panda/scripts/install_ubuntu.sh i386-softmmu,arm-softmmu,mips-softmmu
pushd build
sudo make install
# Install pandare in the matrix python environment
sudo chown -R $(id -u):$(id -u) ../panda/python/core
pip install ../panda/python/core
- name: Run unit tests on panda target for ARM
env:
AVATAR2_ARCH: ARM
AVATAR2_GDB_EXECUTABLE: gdb-multiarch
AVATAR2_QEMU_EXECUTABLE: panda-system-arm
AVATAR2_PANDA_EXECUTABLE: panda-system-arm
run: |
cd ${{ github.workspace }}/tests
pytest test_qemutarget.py \
test_pypandatarget.py
- name: Run unit tests on panda target for MIPS
env:
AVATAR2_ARCH: MIPS
AVATAR2_GDB_EXECUTABLE: gdb-multiarch
AVATAR2_QEMU_EXECUTABLE: panda-system-mips
AVATAR2_PANDA_EXECUTABLE: panda-system-mips
run: |
cd ${{ github.workspace }}/tests
pytest test_qemutarget.py \
test_pypandatarget.py
+1 -1
View File
@@ -1,5 +1,5 @@
[![PyPI version](https://badge.fury.io/py/avatar2.svg)](https://badge.fury.io/py/avatar2)
[![Build Status](https://travis-ci.org/avatartwo/avatar2.svg?branch=master)](https://travis-ci.org/avatartwo/avatar2)
[![CI](https://github.com/avatartwo/avatar2/actions/workflows/continuous_integration.yml/badge.svg)](https://github.com/avatartwo/avatar2/actions/workflows/continuous_integration.yml)
<img src="./avatar_logo.png" width="40%" height="40%"><br/>
+16 -1
View File
@@ -1,9 +1,14 @@
#!/bin/bash
# Usage ./build_qemu.sh [architectures]
# example: ./build_qemu.sh arm-softmmu,mips-softmmu
#
source /etc/os-release
repo="deb-src http://archive.ubuntu.com/ubuntu/ $UBUNTU_CODENAME-security main restricted"
apt_src="/etc/apt/sources.list"
QEMU_NPROC=$(QEMU_NPROC:-$(nproc))
TARGET_LIST="arm-softmmu,mips-softmmu"
if [[ "$ID" == "ubuntu" ]]
then
@@ -28,6 +33,14 @@ else
echo "You may have to install dependencies manually"
fi
if [[ $# -ge 1 ]]
then
TARGET_LIST="$1"
fi
echo "Building for targets: $TARGET_LIST"
cd `dirname "$BASH_SOURCE"`/src/
git submodule update --init avatar-qemu
@@ -36,7 +49,9 @@ git submodule update --init dtc
mkdir -p ../../build/qemu/
cd ../../build/qemu
../../src/avatar-qemu/configure --disable-sdl --target-list=arm-softmmu
../../src/avatar-qemu/configure \
--disable-sdl \
--target-list=$TARGET_LIST
make -j $QEMU_NPROC
+109 -103
View File
@@ -1,3 +1,5 @@
import unittest
from avatar2.protocols.gdb import GDBProtocol
import avatar2
@@ -7,138 +9,142 @@ import time
import sys
from os.path import dirname, abspath
from os.path import dirname, abspath, realpath
from nose.tools import *
SLEEP_TIME = 2
MEM_ADDR = 0x555555554000
MAIN_ADDR = 0x5555555546b4
SLEEP_TIME = .1
# TODO: Resolve those dynamically.
# They can change based on the environment. Unfortunately, for now
# The GDB stub cannot resolve them
MEM_ADDR = 0x555555400000
MAIN_ADDR = 0x5555554006b4
XML_PATH = dirname(abspath((__file__))) \
+ '/../avatar2/plugins/gdb/x86_64-target.xml'
port = 4444
avatar_gdb_port = 7777
a = None
s = None
p = None
g = None
x86_regs = [u'rax', u'rbx', u'rcx', u'rdx', u'rsi', u'rdi', u'rbp', u'rsp',
PORT = 4444
AV_GDB_PORT = 7777
X86_REGS = [u'rax', u'rbx', u'rcx', u'rdx', u'rsi', u'rdi', u'rbp', u'rsp',
u'r8', u'r9', u'r10', u'r11', u'r12', u'r13', u'r14', u'r15',
u'rip', u'eflags', u'cs', u'ss', u'ds', u'es', u'fs', u'gs']
def setup_avatar_gdb_server():
global a,s
a = avatar2.Avatar(arch=avatar2.archs.X86_64)
gdb_target = a.add_target(avatar2.GDBTarget, gdb_port=avatar_gdb_port)
a.init_targets()
a.load_plugin('gdbserver')
s = a.spawn_gdb_server(gdb_target, port, True, XML_PATH)
class GdbPluginTestCase(unittest.TestCase):
def setUp(self):
pass
def setup_avatar_gdb_server(self):
self.avatar = avatar2.Avatar(arch=avatar2.archs.X86_64)
self.gdb_target = self.avatar.add_target(avatar2.GDBTarget, gdb_port=AV_GDB_PORT)
self.avatar.init_targets()
self.avatar.load_plugin('gdbserver')
self.sk = self.avatar.spawn_gdb_server(self.gdb_target, PORT, True, XML_PATH)
def setup_env(self, binary):
self.process = subprocess.Popen(['gdbserver', '--once', '127.0.0.1:%d' %
AV_GDB_PORT, binary],
stderr=subprocess.PIPE)
out = str(self.process.stderr.readline())
self.assertEqual(binary in out, True, out)
out = str(self.process.stderr.readline())
self.assertEqual(str(AV_GDB_PORT) in out, True, out)
# create avatar instance offering the gdbserver
self.setup_avatar_gdb_server()
self.gdb = GDBProtocol(arch=avatar2.archs.X86_64)
self.gdb.remote_connect(port=PORT)
def wait_stopped(self):
# As we do not have access to avatar synchronizing target states
# on this level, we apply this little hack to synchronize the target
while True:
ret, out = self.gdb.console_command('info program')
if 'Program stopped' in out:
break
time.sleep(SLEEP_TIME)
def tearDown(self):
self.sk.shutdown()
self.avatar.shutdown()
self.gdb.shutdown()
self.process.terminate()
def setup_helloworld():
global p, g, port
class TestCaseOnHelloWorld(GdbPluginTestCase):
def setUp(self):
dir_path = dirname(realpath(__file__))
binary = '%s/binaries/hello_world' % dir_path
self.setup_env(binary)
def test_register_names(self):
regs = self.gdb.get_register_names()
self.assertListEqual(regs[:len(X86_REGS)], X86_REGS, regs)
binary = '%s/tests/binaries/hello_world' % os.getcwd()
p = subprocess.Popen(['gdbserver', '--once', '127.0.0.1:%d' %
avatar_gdb_port, binary],
stderr=subprocess.PIPE)
out = str(p.stderr.readline())
assert_equal(binary in out, True)
out = str(p.stderr.readline())
assert_equal(str(avatar_gdb_port) in out, True)
# create avatar instance offering the gdbserver
setup_avatar_gdb_server()
def test_register_read_and_write(self):
g = GDBProtocol(arch=avatar2.archs.X86_64)
g.remote_connect(port=port)
ret = self.gdb.write_register('rbx', 1678)
ret = self.gdb.read_register('rbx')
self.assertEqual(ret, 1678, ret)
def setup_inf_loop():
global p, g, port
def test_break_run_and_read_write_mem(self):
binary = '%s/tests/binaries/infinite_loop' % os.getcwd()
p = subprocess.Popen(['gdbserver', '--once', '127.0.0.1:%d' %
avatar_gdb_port, binary],
stderr=subprocess.PIPE)
ret = self.gdb.set_breakpoint(MAIN_ADDR) # we don't support execfile transfer yet
self.assertEqual(ret, True, ret)
out = str(p.stderr.readline())
assert_equal(binary in out, True)
out = str(p.stderr.readline())
assert_equal(str(avatar_gdb_port) in out, True)
ret = self.gdb.cont()
self.assertEqual(ret, True, ret)
setup_avatar_gdb_server()
g = GDBProtocol(arch=avatar2.archs.X86_64)
g.remote_connect(port=port)
self.wait_stopped()
ret = self.gdb.read_memory(MEM_ADDR, 4)
self.assertEqual(ret, 0x464c457f, ret)
ret = self.gdb.write_memory(MEM_ADDR, 4, 0x41414141)
self.assertEqual(ret, True, ret)
ret = self.gdb.read_memory(MEM_ADDR, 4)
self.assertEqual(ret, 0x41414141, ret)
def teardown_func():
s.shutdown()
a.shutdown()
g.shutdown()
p.terminate()
class TestCaseOnInfiniteLoop(GdbPluginTestCase):
@with_setup(setup_helloworld, teardown_func)
def test_register_names():
regs = g.get_register_names()
assert_list_equal(regs[:len(x86_regs)], x86_regs)
def setUp(self):
dir_path = dirname(realpath(__file__))
binary = '%s/binaries/infinite_loop' % dir_path
self.setup_env(binary)
@with_setup(setup_helloworld, teardown_func)
def test_register_read_and_write():
def test_continue_stopping_stepping(self):
ret = g.write_register('rbx', 1678)
ret = g.read_register('rbx')
assert_equal(ret, 1678)
ret = self.gdb.cont()
self.assertEqual(ret, True, ret)
ret = self.gdb.stop()
self.assertEqual(ret, True, ret)
self.wait_stopped()
ret = self.gdb.step()
self.assertEqual(ret, True, ret)
self.wait_stopped()
@with_setup(setup_helloworld, teardown_func)
def test_break_run_and_read_write_mem():
ret = g.set_breakpoint(MAIN_ADDR) # we don't support execfile transfer yet
assert_equal(ret, True)
ret = g.cont()
assert_equal(ret, True)
# todo: enable waiting
time.sleep(SLEEP_TIME)
ret = g.read_memory(MEM_ADDR, 4)
assert_equal(ret, 0x464c457f)
ret = g.write_memory(MEM_ADDR, 4, 0x41414141)
assert_equal(ret, True)
ret = g.read_memory(MEM_ADDR, 4)
assert_equal(ret, 0x41414141)
@with_setup(setup_inf_loop, teardown_func)
def test_continue_stopping_stepping():
ret = g.cont()
assert_equal(ret, True)
time.sleep(SLEEP_TIME)
ret = g.stop()
assert_equal(ret, True)
time.sleep(SLEEP_TIME)
ret = g.step()
assert_equal(ret, True)
if __name__ == '__main__':
setup_inf_loop()
test_continue_stopping_stepping()
teardown_func()
unittest.main()
+107 -94
View File
@@ -1,131 +1,144 @@
import unittest
from avatar2.protocols.gdb import GDBProtocol
import avatar2
import subprocess
import os
import time
import re
from nose.tools import *
from os.path import dirname, realpath
SLEEP_TIME = 1
MEM_ADDR = 0x555555554000
port = 4444
p = None
g = None
SLEEP_TIME = .1
x86_regs = [u'rax', u'rbx', u'rcx', u'rdx', u'rsi', u'rdi', u'rbp', u'rsp',
PORT = 4444
X86_REGS = [u'rax', u'rbx', u'rcx', u'rdx', u'rsi', u'rdi', u'rbp', u'rsp',
u'r8', u'r9', u'r10', u'r11', u'r12', u'r13', u'r14', u'r15',
u'rip', u'eflags', u'cs', u'ss', u'ds', u'es', u'fs', u'gs']
def setup_helloworld():
global p, g, port
class GdbProtocolTestCase(unittest.TestCase):
def setUp(self):
pass
def setup_env(self, binary):
self.process = subprocess.Popen(['gdbserver', '--once', '127.0.0.1:%d' % PORT, binary],
stderr=subprocess.PIPE)
out = str(self.process.stderr.readline())
self.assertEqual(binary in out, True, out)
out = str(self.process.stderr.readline())
self.assertEqual(str(PORT) in out, True, out)
self.gdb = GDBProtocol(arch=avatar2.archs.X86_64)
self.gdb.remote_connect(port=PORT)
# let's resolve the base address of the binary
ret, out = self.gdb.console_command("p &main")
main_addr = int(re.search("0x[0-9a-f]+", out).group(0), 16)
self.base_address = main_addr - main_addr % 0x1000
def wait_stopped(self):
# As we do not have access to avatar synchronizing target states
# on this level, we apply this little hack to synchronize the target
while True:
ret, out = self.gdb.console_command('info program')
if 'Program stopped' in out:
break
time.sleep(SLEEP_TIME)
binary = '%s/tests/binaries/hello_world' % os.getcwd()
p = subprocess.Popen(['gdbserver', '--once', '127.0.0.1:%d' % port, binary],
stderr=subprocess.PIPE)
out = str(p.stderr.readline())
assert_equal(binary in out, True)
out = str(p.stderr.readline())
assert_equal(str(port) in out, True)
g = GDBProtocol(arch=avatar2.archs.X86_64)
g.remote_connect(port=port)
def tearDown(self):
self.gdb.shutdown()
self.process.terminate()
def setup_inf_loop():
global p, g, port
class GDBProtocolTestCaseOnHelloWorld(GdbProtocolTestCase):
binary = '%s/tests/binaries/infinite_loop' % os.getcwd()
p = subprocess.Popen(['gdbserver', '--once', '127.0.0.1:%d' % port, binary],
stderr=subprocess.PIPE)
out = str(p.stderr.readline())
assert_equal(binary in out, True)
out = str(p.stderr.readline())
assert_equal(str(port) in out, True)
g = GDBProtocol(arch=avatar2.archs.X86_64)
g.remote_connect(port=port)
def setUp(self):
dir_path = dirname(realpath(__file__))
binary = '%s/binaries/hello_world' % dir_path
self.setup_env(binary)
def teardown_func():
g.shutdown()
p.terminate()
def test_register_names(self):
regs = self.gdb.get_register_names()
self.assertListEqual(regs[:len(X86_REGS)], X86_REGS)
@with_setup(setup_helloworld, teardown_func)
def test_register_names():
regs = g.get_register_names()
assert_list_equal(regs[:len(x86_regs)], x86_regs)
def test_register_read_and_write(self):
ret = self.gdb.write_register('rax', 1678)
self.assertEqual(ret, True, ret)
ret = self.gdb.read_register('rax')
self.assertEqual(ret, 1678, ret)
def test_break_run_and_read_write_mem(self):
ret = self.gdb.set_breakpoint('main')
self.assertEqual(ret, True, ret)
ret = self.gdb.cont()
self.assertEqual(ret, True, ret)
self.wait_stopped()
ret = self.gdb.read_memory(self.base_address, 4)
self.assertEqual(ret, 0x464c457f, ret)
ret = self.gdb.write_memory(self.base_address, 4, 0x41414141)
self.assertEqual(ret, True, ret)
ret = self.gdb.read_memory(self.base_address, 4)
self.assertEqual(ret, 0x41414141, ret)
def test_watchpoint(self):
ret = self.gdb.set_watchpoint(self.base_address+0x754, read=True,
write=False)
self.assertEqual(ret, True, ret)
ret = self.gdb.cont()
self.assertEqual(ret, True, ret)
self.wait_stopped()
ret = self.gdb.read_memory(self.base_address, 4)
self.assertEqual(ret, 0x464c457f, ret)
@with_setup(setup_helloworld, teardown_func)
def test_register_read_and_write():
ret = g.write_register('rax', 1678)
assert_equal(ret, True)
ret = g.read_register('rax')
assert_equal(ret, 1678)
class GDBProtocolTestCaseOnInfiniteLoop(GdbProtocolTestCase):
@with_setup(setup_helloworld, teardown_func)
def test_break_run_and_read_write_mem():
ret = g.set_breakpoint('main')
assert_equal(ret, True)
ret = g.cont()
assert_equal(ret, True)
# todo: enable waiting
time.sleep(SLEEP_TIME)
ret = g.read_memory(MEM_ADDR, 4)
assert_equal(ret, 0x464c457f)
ret = g.write_memory(MEM_ADDR, 4, 0x41414141)
assert_equal(ret, True)
ret = g.read_memory(MEM_ADDR, 4)
assert_equal(ret, 0x41414141)
@with_setup(setup_inf_loop, teardown_func)
def test_continue_stopping_stepping():
ret = g.cont()
assert_equal(ret, True)
def setUp(self):
dir_path = dirname(realpath(__file__))
binary = '%s/binaries/infinite_loop' % dir_path
self.setup_env(binary)
ret = g.stop()
assert_equal(ret, True)
def test_continue_stopping_stepping(self):
time.sleep(SLEEP_TIME)
ret = self.gdb.cont()
self.assertEqual(ret, True, ret)
ret = g.step()
assert_equal(ret, True)
time.sleep(SLEEP_TIME)
ret = self.gdb.stop()
self.assertEqual(ret, True, ret)
@with_setup(setup_helloworld, teardown_func)
def test_watchpoint():
ret = g.set_watchpoint(0x555555554754, read=True,
write=False)
assert_equal(ret, True)
self.wait_stopped()
ret = g.cont()
assert_equal(ret, True)
ret = self.gdb.step()
self.assertEqual(ret, True, ret)
self.wait_stopped()
time.sleep(SLEEP_TIME)
ret = g.read_memory(MEM_ADDR, 4)
assert_equal(ret, 0x464c457f)
if __name__ == '__main__':
setup_helloworld()
test_break_run_and_read_write_mem()
teardown_func()
unittest.main()
+38 -42
View File
@@ -1,14 +1,14 @@
from avatar2.protocols.inception import IPCortexM3
#import avatar2
import unittest
import struct
import ctypes
from nose.tools import *
from avatar2.protocols.inception import IPCortexM3
SLEEP_TIME = 1
MEM_ADDR = 0x12345678
i = None
@@ -110,49 +110,45 @@ class FakeIPCortexM3(IPCortexM3):
# ****************************************************************************
def setup():
global i
class InceptionProtocolTestCase(unittest.TestCase):
i = FakeIPCortexM3()
i.connect()
i.reset()
def setUp(self):
self.ip = FakeIPCortexM3()
self.ip.connect()
self.ip.reset()
def tearDown(self):
self.ip.shutdown()
def teardown():
i.shutdown()
def test_register_read_and_write(self):
ret = self.ip.write_register('R0', 2020)
self.assertEqual(ret, True, ret)
ret = self.ip.read_register('r0')
self.assertEqual(ret, 0xdeadbeef, ret)
def test_break_run_and_read_write_mem(self):
ret = self.ip.set_breakpoint(0x8000000)
self.assertEqual(ret, True, ret)
ret = self.ip.cont()
self.assertEqual(ret, True, ret)
#time.sleep(SLEEP_TIME)
ret = self.ip.read_memory(MEM_ADDR, 4)
self.assertEqual(ret, 0xdeadbeef, ret)
ret = self.ip.write_memory(MEM_ADDR, 4, 0x8badf00d)
self.assertEqual(ret, True, ret)
self.assertEqual(self.ip._fakecm3.fake_write_addr, MEM_ADDR, self.ip._fakecm3.fake_write_addr)
self.assertEqual(self.ip._fakecm3.fake_write_val, 0x8badf00d, self.ip._fakecm3.fake_write_val)
@with_setup(setup, teardown)
def test_register_read_and_write():
ret = i.write_register('R0', 2020)
assert_equal(ret, True)
ret = i.read_register('r0')
assert_equal(ret, 0xdeadbeef)
@with_setup(setup, teardown)
def test_break_run_and_read_write_mem():
ret = i.set_breakpoint(0x8000000)
assert_equal(ret, True)
ret = i.cont()
assert_equal(ret, True)
#time.sleep(SLEEP_TIME)
ret = i.read_memory(MEM_ADDR, 4)
assert_equal(ret, 0xdeadbeef)
ret = i.write_memory(MEM_ADDR, 4, 0x8badf00d)
assert_equal(ret, True)
assert_equal(i._fakecm3.fake_write_addr, MEM_ADDR)
assert_equal(i._fakecm3.fake_write_val, 0x8badf00d)
if __name__ == '__main__':
setup()
test_break_run_and_read_write_mem()
teardown()
unittest.main()
+133 -146
View File
@@ -1,27 +1,21 @@
import os
from os.path import abspath
from time import sleep
from nose.tools import *
import unittest
import socket
from os.path import dirname, realpath
from time import sleep
from avatar2 import *
from avatar2.peripherals.nucleo_usart import *
from pandare import ffi
PORT = 9997
TEST_STRING = bytearray(b'Hello World !\n')
panda = None
avatar = None
s = None
test_string = bytearray(b'Hello World !\n')
sram_dump = './tests/pyperipheral/sram_dump.bin'
rcc_dump = './tests/pyperipheral/rcc_dump.bin'
firmware = './tests/pyperipheral/firmware.bin'
dir_path = dirname(realpath(__file__))
sram_dump = '%s/pyperipheral/sram_dump.bin' % dir_path
rcc_dump = '%s/pyperipheral/rcc_dump.bin' % dir_path
firmware = '%s/pyperipheral/firmware.bin' % dir_path
with open(sram_dump, 'rb') as f:
sram_data = f.read()
@@ -30,156 +24,149 @@ with open(rcc_dump, 'rb') as f:
rcc_data = f.read()
@timed(7)
def setup_func():
# There is only one pypanda instance in the process space allowed.
# Unfortunately, we need a lot of hacks to make this working for CI tests.
# So, beware the globals!
global panda
global avatar
global s
if panda is None:
class PypandaTargetTestCase(unittest.TestCase):
panda = None
avatar = None
sk = None
@classmethod
def setUpClass(cls):
# Initiate the avatar-object
avatar = Avatar(output_directory='/tmp/avatar', arch=ARM_CORTEX_M3)
cls.avatar = Avatar(output_directory='/tmp/avatar', arch=ARM_CORTEX_M3)
panda = avatar.add_target(PyPandaTarget, gdb_port=1236,
entry_address=0x08005105)
cls.panda = cls.avatar.add_target(PyPandaTarget, gdb_port=1236,
entry_address=0x08005105)
# Define the various memory ranges and store references to them
avatar.add_memory_range(0x08000000, 0x1000000, file=firmware)
avatar.add_memory_range(0x20000000, 0x14000)
avatar.add_memory_range(0x40023000, 0x1000)
cls.avatar.add_memory_range(0x08000000, 0x1000000, file=firmware)
cls.avatar.add_memory_range(0x20000000, 0x14000)
cls.avatar.add_memory_range(0x40023000, 0x1000)
avatar.init_targets()
cls.avatar.init_targets()
panda.pypanda.register_pyperipheral(NucleoUSART('USART', 0x40004400, 0x100,
nucleo_usart_port=PORT))
cls.panda.pypanda.register_pyperipheral(NucleoUSART('USART', 0x40004400, 0x100,
nucleo_usart_port=PORT))
time.sleep(1)
cls.sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
cls.sk.connect(('127.0.0.1', PORT))
@classmethod
def tearDownClass(cls):
cls.panda.shutdown()
cls.avatar.shutdown()
cls.sk.close()
time.sleep(10)
#@timed(7)
def setUp(self):
self.recv_data = []
self.panda.write_memory(0x20000000, len(sram_data), sram_data, raw=True)
self.panda.write_memory(0x40023000, len(rcc_data), rcc_data, raw=True)
self.panda.regs.pc = 0x08005105
self.panda.regs.sp = 0x20014000
self.panda.bp(0x0800419c) # Termination breakpoint to avoid qemu dying
#@timed(8)
def tearDown(self):
pass
def test_nucleo_usart_read(self):
self.panda.cont()
data = self.sk.recv(len(TEST_STRING), socket.MSG_WAITALL)
self.assertEqual(data, TEST_STRING, data)
self.panda.wait()
def test_panda_callback(self):
recv_data = []
def cb(env, pc, addr, size, buf):
if addr == 0x40004404:
recv_data.append(buf[0])
cb_handle = self.panda.register_callback('mmio_before_write', cb)
self.panda.cont()
self.panda.wait()
self.assertEqual(bytearray(recv_data), TEST_STRING, bytearray(recv_data))
self.panda.disable_callback(cb_handle)
#@timed(11)
def test_panda_hook(self):
def hook(env, tb, hook):
#global recv_data
self.recv_data += [self.panda.pypanda.arch.get_reg(env, 'r0')]
self.panda.add_hook(0x8004622, hook) # return address of serial_putc
self.panda.cont()
self.panda.wait()
self.assertEqual(bytearray(self.recv_data), TEST_STRING, bytearray(self.recv_data))
self.panda.pypanda.plugins['hooks'].disable_hooking()
#@timed(10)
def test_nucleo_usart_debug_read(self):
# pyperipherals in debug mode need to be read from/written to directly
pyperiph = self.panda.pypanda.pyperipherals[0]
self.sk.send(b'Hello World')
reply = bytearray()
time.sleep(.1)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(('127.0.0.1', PORT))
while pyperiph.read_memory(0x40004400,4) & (1<<5) != 0:
reply.append(pyperiph.read_memory(0x40004404,4))
panda.write_memory(0x20000000, len(sram_data), sram_data, raw=True)
panda.write_memory(0x40023000, len(rcc_data), rcc_data, raw=True)
self.assertEqual(reply, b'Hello World', reply)
panda.regs.pc = 0x08005105
panda.regs.sp = 0x20014000
self.sk.send(b'Hello World')
reply = bytearray()
time.sleep(.1)
while pyperiph.read_memory(0x40004400,1) & (1<<5) != 0:
reply.append(pyperiph.read_memory(0x40004404,1))
panda.bp(0x0800419c) # Termination breakpoint to avoid qemu dying
@timed(8)
def teardown_func():
#panda.shutdown()
#avatar.shutdown()
#time.sleep(1)
pass
self.assertEqual(reply, b'Hello World', reply)
#@timed(11)
def test_nucleo_usart_debug_write(self):
pyperiph = self.panda.pypanda.pyperipherals[0]
@with_setup(setup_func, teardown_func)
def test_nucleo_usart_read():
panda.cont()
data = s.recv(len(test_string), socket.MSG_WAITALL)
assert_equal(data, test_string)
@with_setup(setup_func, teardown_func)
def test_panda_callback():
recv_data = []
def cb(env, pc, addr, size, buf):
if addr == 0x40004404:
recv_data.append(buf[0])
cb_handle = panda.register_callback('mmio_before_write', cb)
panda.cont()
panda.wait()
assert_equal(bytearray(recv_data), test_string)
panda.disable_callback(cb_handle)
recv_data = []
@timed(11)
@with_setup(setup_func, teardown_func)
def test_panda_hook():
def hook(env, tb, hook):
global recv_data
recv_data += [panda.pypanda.arch.get_reg(env, 'r0')]
panda.add_hook(0x8004622, hook) # return address of serial_putc
panda.cont()
panda.wait()
assert_equal(bytearray(recv_data), test_string)
panda.pypanda.plugins['hooks'].disable_hooking()
@timed(10)
@with_setup(setup_func, teardown_func)
def test_nucleo_usart_debug_read():
# pyperipherals in debug mode need to be read from/written to directly
pyperiph = panda.pypanda.pyperipherals[0]
s.send(b'Hello World')
reply = bytearray()
time.sleep(.1)
while pyperiph.read_memory(0x40004400,4) & (1<<5) != 0:
reply.append(pyperiph.read_memory(0x40004404,4))
assert_equal(reply, b'Hello World')
s.send(b'Hello World')
reply = bytearray()
time.sleep(.1)
while pyperiph.read_memory(0x40004400,1) & (1<<5) != 0:
reply.append(pyperiph.read_memory(0x40004404,1))
assert_equal(reply, b'Hello World')
@timed(11)
@with_setup(setup_func, teardown_func)
def test_nucleo_usart_debug_write():
pyperiph = panda.pypanda.pyperipherals[0]
time.sleep(1)
for c in test_string:
pyperiph.write_memory(0x40004404, 1, c)
reply = s.recv(len(test_string), socket.MSG_WAITALL)
assert_equal(reply, test_string)
time.sleep(.1)
for c in test_string:
pyperiph.write_memory(0x40004404, 4, c)
reply = s.recv(len(test_string), socket.MSG_WAITALL)
assert_equal(reply, test_string)
time.sleep(1)
for c in TEST_STRING:
pyperiph.write_memory(0x40004404, 1, c)
reply = self.sk.recv(len(TEST_STRING), socket.MSG_WAITALL)
self.assertEqual(reply, TEST_STRING, reply)
time.sleep(.1)
for c in TEST_STRING:
pyperiph.write_memory(0x40004404, 4, c)
reply = self.sk.recv(len(TEST_STRING), socket.MSG_WAITALL)
self.assertEqual(reply, TEST_STRING, reply)
if __name__ == '__main__':
#setup_func()
#test_panda_callback()
#teardown_func()
#setup_func()
#test_panda_hook()
#teardown_func()
pass
unittest.main()
+107 -136
View File
@@ -1,3 +1,11 @@
import unittest
import tempfile
import os
import time
import intervaltree
import logging
from avatar2 import QemuTarget
from avatar2 import MemoryRange
from avatar2 import Avatar
@@ -6,23 +14,12 @@ from avatar2.targets import Target, TargetStates
from avatar2.message import *
import tempfile
import os
import time
import intervaltree
import logging
from nose.tools import *
TEST_DIR = '/tmp/testava'
ARM_BASE_ADDR = 0x08000000
MIPS_BASE_ADDR = 0x1fc00000
qemu = None
fake_target = None
ARCH = None
rom_addr = None
test_dir = '/tmp/testava'
arm_bin = (b'\x1e\x10\xa0\xe3' # mov r1, #0x1e
ARM_BIN = (b'\x1e\x10\xa0\xe3' # mov r1, #0x1e
b'\x00\x00\x20\xe0' # eor r0, r0, r0
b'\x01\x00\x80\xe2' # add r0, r0, #1
b'\x01\x00\x50\xe1' # cmp r0, r1
@@ -30,7 +27,7 @@ arm_bin = (b'\x1e\x10\xa0\xe3' # mov r1, #0x1e
b'\x00\x00\x00\xe0' # and r0, r0, r0
b'\x00\x00\x00\xe0') # and r0, r0, r0
mips_bin = (b'\x24\x09\x00\x1e' # addiu $t1, $zero, 0x1e
MIPS_BIN = (b'\x24\x09\x00\x1e' # addiu $t1, $zero, 0x1e
b'\x01\x08\x40\x26' # xor $t0, $t0, $t0
b'\x21\x08\x00\x01' # addi $t0, $t0, 1
b'\x15\x09\xff\xfe' # bne $t0, $t1, 8
@@ -38,7 +35,7 @@ mips_bin = (b'\x24\x09\x00\x1e' # addiu $t1, $zero, 0x1e
b'\x01\x08\x40\x24' # and $t0, $t0, $t0
b'\x01\x08\x40\x24') # and $t0, $t0, $t0
x86_bin = (b'\xbb\x1e\x00\x00\x00' # mov ebx, 0x1e
X86_BIN = (b'\xbb\x1e\x00\x00\x00' # mov ebx, 0x1e
b'\x31\xc0' # xor eax, eax
b'\x83\xc0\x01' # add eax, 1
b'\x39\xd8' # cmp eax, ebx
@@ -46,7 +43,7 @@ x86_bin = (b'\xbb\x1e\x00\x00\x00' # mov ebx, 0x1e
b'\x21\xc0' # and eax, eax
b'\x21\xc0') # and eax, eax
x86_64_bin = (b'\x48\xc7\xc3\x1e\x00\x00\x00' # mov rbx, 0x1e
X86_64_BIN = (b'\x48\xc7\xc3\x1e\x00\x00\x00' # mov rbx, 0x1e
b'\x48\x31\xc0' # xor rax, rax
b'\x48\x83\xc0\x01' # add rax, 1
b'\x48\x39\xd8' # cmp rax, rbx
@@ -73,144 +70,118 @@ class FakeTarget(object):
return True
def setup():
global qemu
global avatar
global fake_target
arch = setup_ARCH()
avatar = Avatar(arch=arch, output_directory=test_dir)
qemu = QemuTarget(avatar, name='qemu_test',
#firmware="./tests/binaries/qemu_arm_test",
firmware='%s/firmware' % test_dir,
)
fake_target = FakeTarget()
dev1 = avatar.add_memory_range(0x101f2000, 0x1000, 'dev1', forwarded=True,
forwarded_to=fake_target,
qemu_name='avatar-rmemory')
mem1 = avatar.add_memory_range(rom_addr, 0x1000, 'mem1',
#file='%s/tests/binaries/qemu_arm_test' %
# os.getcwd())
file='%s/firmware' % test_dir)
def setup_ARCH():
global ARCH
global rom_addr
ARCH = os.getenv('AVATAR2_ARCH')
if ARCH == 'ARM':
arch = ARM
firmware = arm_bin
rom_addr = 0x08000000
elif ARCH == 'MIPS':
arch = MIPS_24KF
firmware = mips_bin
rom_addr = 0x1fc00000
else:
assert False, 'Invalid Achitecture'
if not os.path.exists(test_dir): os.makedirs(test_dir)
with open('%s/firmware' % test_dir, 'wb') as f:
f.write(firmware)
return arch
def teardown():
global qemu
qemu.shutdown()
class QemuTargetTestCase(unittest.TestCase):
@with_setup(setup, teardown)
def test_initilization():
global qemu
def setUp(self):
qemu.init()
qemu.wait()
assert_equal(qemu.state, TargetStates.STOPPED)
self.rom_addr = None
self.arch = None
self.setup_arch()
self.avatar = Avatar(arch=self.arch, output_directory=TEST_DIR)
self.qemu = QemuTarget(self.avatar, name='qemu_test',
#firmware="./tests/binaries/qemu_arm_test",
firmware='%s/firmware' % TEST_DIR,
)
self.fake_target = FakeTarget()
dev1 = self.avatar.add_memory_range(0x101f2000, 0x1000, 'dev1', forwarded=True,
forwarded_to=self.fake_target,
qemu_name='avatar-rmemory')
mem1 = self.avatar.add_memory_range(self.rom_addr, 0x1000, 'mem1',
#file='%s/tests/binaries/qemu_arm_test' %
# os.getcwd())
file='%s/firmware' % TEST_DIR
)
def setup_arch(self):
ARCH = os.getenv('AVATAR2_ARCH')
if ARCH == 'ARM':
self.arch = ARM
self.rom_addr = ARM_BASE_ADDR
firmware = ARM_BIN
elif ARCH == 'MIPS':
self.arch = MIPS_24KF
self.rom_addr = MIPS_BASE_ADDR
firmware = MIPS_BIN
else:
self.assertTrue(False, 'Invalid Achitecture')
if not os.path.exists(TEST_DIR): os.makedirs(TEST_DIR)
with open('%s/firmware' % TEST_DIR, 'wb') as f:
f.write(firmware)
def tearDown(self):
self.qemu.shutdown()
@with_setup(setup, teardown)
def test_step():
global qemu
global ARCH
def test_initilization(self):
self.qemu.init()
self.qemu.wait()
self.assertEqual(self.qemu.state, TargetStates.STOPPED, self.qemu.state)
qemu.init()
qemu.wait()
def test_step(self):
self.qemu.init()
self.qemu.wait()
qemu.regs.pc=rom_addr
qemu.step()
assert_equal(qemu.regs.pc, rom_addr + 4)
self.qemu.regs.pc=self.rom_addr
self.qemu.step()
pc = self.qemu.regs.pc
self.assertEqual(pc, self.rom_addr + 4, pc)
@with_setup(setup, teardown)
def test_memory_read():
global qemu
global ARCH
def test_memory_read(self):
self.qemu.init()
self.qemu.wait()
qemu.init()
qemu.wait()
mem = self.qemu.read_memory(self.rom_addr, 4)
mem = qemu.read_memory(rom_addr,4)
if self.arch == ARM:
self.assertEqual(mem, 0xe3a0101e, mem)
if ARCH == 'ARM':
assert_equal(mem, 0xe3a0101e)
elif self.arch == MIPS_24KF:
#self.assertEqual(mem, 0x2409001e, mem)
self.assertEqual(mem, 0x1e000924, mem)
else:
self.assertTrue(False, "Architecture not supported")
elif ARCH == 'MIPS':
#assert_equal(mem, 0x2409001e)
assert_equal(mem, 0x1e000924)
def test_memory_write(self):
self.qemu.init()
self.qemu.wait()
else:
assert False, "Architecture not supported"
self.qemu.write_memory(self.rom_addr, 4, 0x41414141)
mem = self.qemu.read_memory(self.rom_addr, 4)
self.assertEqual(mem, 0x41414141, mem)
def test_remote_memory_write(self):
self.qemu.init()
self.qemu.wait()
remote_memory_write = self.qemu.write_memory(0x101f2000,4,0x41414141)
self.assertEqual(remote_memory_write, True)
@with_setup(setup, teardown)
def test_memory_write():
global qemu
addr = self.fake_target.fake_write_addr
size = self.fake_target.fake_write_size
val = self.fake_target.fake_write_val
self.assertEqual(addr, 0x101f2000, addr)
self.assertEqual(size, 4, size)
self.assertEqual(val, 0x41414141, val)
qemu.init()
qemu.wait()
def test_remote_memory_read(self):
self.qemu.init()
self.qemu.wait()
self.assertEqual(self.qemu.state, TargetStates.STOPPED, self.qemu.state)
qemu.write_memory(rom_addr,4, 0x41414141)
mem = qemu.read_memory(rom_addr,4)
assert_equal(mem, 0x41414141)
remote_memory_read = self.qemu.read_memory(0x101f2000,4)
self.assertEqual(remote_memory_read, 0xdeadbeef, remote_memory_read)
@with_setup(setup, teardown)
def test_remote_memory_write():
global qemu
global avatar
qemu.init()
qemu.wait()
remote_memory_write = qemu.write_memory(0x101f2000,4,0x41414141)
assert_equal(remote_memory_write, True)
assert_equal(fake_target.fake_write_addr, 0x101f2000)
assert_equal(fake_target.fake_write_size, 4)
assert_equal(fake_target.fake_write_val, 0x41414141)
@with_setup(setup, teardown)
def test_remote_memory_read():
global qemu
global avatar
qemu.init()
qemu.wait()
assert_equal(qemu.state, TargetStates.STOPPED)
remote_memory_read = qemu.read_memory(0x101f2000,4)
assert_equal(remote_memory_read, 0xdeadbeef)
if __name__ == '__main__':
setup()
#test_remote_memory()
test_initilization()
teardown()
unittest.main()
+37 -32
View File
@@ -1,3 +1,5 @@
import unittest
import sys
if sys.version_info < (3, 0):
import Queue as queue
@@ -9,45 +11,48 @@ import avatar2.protocols.remote_memory
from os import O_CREAT, O_RDONLY, O_WRONLY, O_RDWR
from posix_ipc import MessageQueue
from nose.tools import *
def setup_func():
pass
class RemoteMemoryTestCase(unittest.TestCase):
def setUp(self):
pass
def tearDown(self):
pass
def teardown_func():
pass
def test_remote_memory_request(self):
magic1 = 0x4141414141414141
magic2 = 0x4242424242424242
magic3 = 0x4343434343434343
magic4 = 0x4444444444444444
magic5 = 4
q = queue.Queue()
a = MessageQueue('/a', flags=O_CREAT|O_RDWR)
b = MessageQueue('/b', flags=O_CREAT|O_RDWR)
mprot1 = avatar2.protocols.remote_memory.RemoteMemoryProtocol('/a','/b', q)
ret = mprot1.connect()
self.assertEqual(ret, True)
self.assertNotEqual(mprot1._rx_queue, None)
self.assertNotEqual(mprot1._tx_queue, None)
@with_setup(setup_func, teardown_func)
def test_remote_memory_request():
magic1 = 0x4141414141414141
magic2 = 0x4242424242424242
magic3 = 0x4343434343434343
magic4 = 0x4444444444444444
magic5 = 4
request = avatar2.protocols.remote_memory.RemoteMemoryReq(magic1, magic2,
magic3, magic4,
magic5, 1)
a.send(request)
msg = q.get()
q = queue.Queue()
a = MessageQueue('/a', flags=O_CREAT|O_RDWR)
b = MessageQueue('/b', flags=O_CREAT|O_RDWR)
mprot1 = avatar2.protocols.remote_memory.RemoteMemoryProtocol('/a','/b', q)
ret = mprot1.connect()
assert_equal(ret, True)
assert_not_equal(mprot1._rx_queue, None)
assert_not_equal(mprot1._tx_queue, None)
self.assertEqual(msg.id, magic1)
self.assertEqual(msg.pc, magic2)
self.assertEqual(msg.address, magic3)
self.assertEqual(msg.value, magic4)
self.assertEqual(msg.size, magic5)
request = avatar2.protocols.remote_memory.RemoteMemoryReq(magic1, magic2,
magic3, magic4,
magic5, 1)
a.send(request)
msg = q.get()
assert_equal(msg.id, magic1)
assert_equal(msg.pc, magic2)
assert_equal(msg.address, magic3)
assert_equal(msg.value, magic4)
assert_equal(msg.size, magic5)
if __name__ == '__main__':
unittest.main()