Re-activate unit testing and starting work on proper memory segmentation support

Use Qt testing framework.

Reorganize source file references in CMakeLists.txt

Add simplistic Address header and type ( typedef for now )
This commit is contained in:
nemerle 2016-05-20 12:08:39 +02:00
parent 171abc0415
commit 3905c4e281
22 changed files with 521 additions and 183 deletions

View File

@ -25,12 +25,14 @@ find_package(Boost)
OPTION(dcc_build_tests "Enable unit tests." OFF)
IF(dcc_build_tests)
enable_testing()
FIND_PACKAGE(GMock)
enable_testing()
find_package(Qt5Test)
#FIND_PACKAGE(GMock)
ENDIF()
INCLUDE_DIRECTORIES(
${PROJECT_SOURCE_DIR}
3rd_party/libdisasm
include
include/idioms
@ -38,135 +40,7 @@ INCLUDE_DIRECTORIES(
${Boost_INCLUDE_DIRS}
)
ADD_SUBDIRECTORY(3rd_party)
ADD_SUBDIRECTORY(common)
ADD_SUBDIRECTORY(tools)
set(dcc_LIB_SOURCES
src/CallConvention.cpp
src/ast.cpp
src/backend.cpp
src/bundle.cpp
src/chklib.cpp
src/comwrite.cpp
src/control.cpp
src/dataflow.cpp
src/disassem.cpp
src/DccFrontend.cpp
src/error.cpp
src/fixwild.cpp
src/graph.cpp
src/hlicode.cpp
src/hltype.cpp
src/machine_x86.cpp
src/icode.cpp
src/RegisterNode
src/idioms.cpp
src/idioms/idiom1.cpp
src/idioms/arith_idioms.cpp
src/idioms/call_idioms.cpp
src/idioms/epilogue_idioms.cpp
src/idioms/mov_idioms.cpp
src/idioms/neg_idioms.cpp
src/idioms/shift_idioms.cpp
src/idioms/xor_idioms.cpp
src/locident.cpp
src/liveness_set.cpp
src/parser.h
src/parser.cpp
src/procs.cpp
src/project.cpp
src/Procedure.cpp
src/proplong.cpp
src/reducible.cpp
src/scanner.cpp
src/symtab.cpp
src/udm.cpp
src/BasicBlock.cpp
src/dcc_interface.cpp
src/Command.cpp
src/Command.h
src/Loaders.cpp
src/Loaders.h
src/FollowControlFlow.cpp
src/FollowControlFlow.h
src/AutomatedPlanner
)
set(dcc_UI_SOURCES
src/ui/DccMainWindow.ui
src/ui/DccMainWindow.h
src/ui/DccMainWindow.cpp
src/ui/FunctionViewWidget.ui
src/ui/FunctionViewWidget.h
src/ui/FunctionViewWidget.cpp
src/ui/FunctionListDockWidget.ui
src/ui/FunctionListDockWidget.cpp
src/ui/FunctionListDockWidget.h
src/ui/RenderTags.cpp
src/ui/RenderTags.h
src/ui/CommandQueueView.cpp
src/ui/CommandQueueView.h
src/ui/CommandQueueView.ui
)
set(dcc_SOURCES
src/dcc.cpp
)
set(dcc_HEADERS
include/ast.h
include/bundle.h
include/BinaryImage.h
include/DccFrontend.h
include/Enums.h
include/dcc.h
include/disassem.h
include/dosdcc.h
include/error.h
include/graph.h
include/hlicode.h
include/machine_x86.h
include/icode.h
include/idioms/idiom.h
include/idioms/idiom1.h
include/idioms/arith_idioms.h
include/idioms/call_idioms.h
include/idioms/epilogue_idioms.h
include/idioms/mov_idioms.h
include/idioms/neg_idioms.h
include/idioms/shift_idioms.h
include/idioms/xor_idioms.h
include/locident.h
include/CallConvention.h
include/project.h
include/scanner.h
include/state.h
include/symtab.h
include/types.h
include/Procedure.h
include/StackFrame.h
include/BasicBlock.h
include/dcc_interface.h
)
SOURCE_GROUP(Source FILES ${dcc_SOURCES})
SOURCE_GROUP(Headers FILES ${dcc_HEADERS})
ADD_LIBRARY(dcc_lib STATIC ${dcc_LIB_SOURCES} ${dcc_HEADERS})
qt5_use_modules(dcc_lib Core)
#cotire(dcc_lib)
ADD_EXECUTABLE(dcc_original ${dcc_SOURCES} ${dcc_UI_SOURCES})
ADD_DEPENDENCIES(dcc_original dcc_lib)
TARGET_LINK_LIBRARIES(dcc_original dcc_lib dcc_hash disasm_s)
qt5_use_modules(dcc_original Core Widgets)
SET_PROPERTY(TARGET dcc_original PROPERTY CXX_STANDARD 11)
SET_PROPERTY(TARGET dcc_original PROPERTY CXX_STANDARD_REQUIRED ON)
#ADD_SUBDIRECTORY(gui)
if(dcc_build_tests)
ADD_SUBDIRECTORY(src)
endif()

View File

@ -0,0 +1,21 @@
MACRO(ADD_UNIT_TEST name)
IF(NOT ${name}_TEST_VISITED)
# add the loader as a dll
ADD_EXECUTABLE(${name} ${ARGN})
qt5_use_modules(${name} Core)
MESSAGE(WARNING "Adding test " ${name} " " ${ARGN})
TARGET_LINK_LIBRARIES(${name} ${UNIT_TEST_LIBS})
ADD_TEST(NAME ${name} COMMAND ${name})
set_property(TEST ${name} APPEND PROPERTY ENVIRONMENT DCC_TEST_BASE=${PROJECT_SOURCE_DIR})
SET(${name}_TEST_VISITED true)
ENDIF()
ENDMACRO()
function(ADD_QTEST NAME)
add_executable(${NAME} ${NAME}.cpp ${NAME}.h) #${PROTO_SRCS} ${PROTO_HDRS}
target_link_libraries(${NAME} ${test_LIBRARIES})
qt5_use_modules(${NAME} Core Test)
add_test( NAME ${NAME} COMMAND $<TARGET_FILE:${NAME}>)
set_property(TEST ${NAME} APPEND PROPERTY ENVIRONMENT DCC_TEST_BASE=${PROJECT_SOURCE_DIR})
endfunction()

6
src/Address.h Normal file
View File

@ -0,0 +1,6 @@
#pragma once
#include <stdint.h>
typedef uint32_t LinearAddress;
#define INVALID_ADDR Address(~0U)

View File

@ -1,13 +1,131 @@
SET(dcc_test_SOURCES
tests/comwrite.cpp
tests/project.cpp
tests/loader.cpp
set(dcc_LIB_SOURCES
CallConvention.cpp
ast.cpp
backend.cpp
bundle.cpp
chklib.cpp
comwrite.cpp
control.cpp
dataflow.cpp
disassem.cpp
DccFrontend.cpp
error.cpp
fixwild.cpp
graph.cpp
hlicode.cpp
hltype.cpp
machine_x86.cpp
icode.cpp
RegisterNode
idioms.cpp
idioms/idiom1.cpp
idioms/arith_idioms.cpp
idioms/call_idioms.cpp
idioms/epilogue_idioms.cpp
idioms/mov_idioms.cpp
idioms/neg_idioms.cpp
idioms/shift_idioms.cpp
idioms/xor_idioms.cpp
locident.cpp
liveness_set.cpp
parser.h
parser.cpp
procs.cpp
project.cpp
Procedure.cpp
proplong.cpp
reducible.cpp
scanner.cpp
symtab.cpp
udm.cpp
BasicBlock.cpp
dcc_interface.cpp
MemoryChunk
MemorySegment
MemorySegmentCoordinator
Command.cpp
Command.h
Loaders.cpp
Loaders.h
FollowControlFlow.cpp
FollowControlFlow.h
AutomatedPlanner
)
set(dcc_UI_SOURCES
ui/DccMainWindow.ui
ui/DccMainWindow.h
ui/DccMainWindow.cpp
ui/FunctionViewWidget.ui
ui/FunctionViewWidget.h
ui/FunctionViewWidget.cpp
ui/FunctionListDockWidget.ui
ui/FunctionListDockWidget.cpp
ui/FunctionListDockWidget.h
ui/RenderTags.cpp
ui/RenderTags.h
ui/CommandQueueView.cpp
ui/CommandQueueView.h
ui/CommandQueueView.ui
)
set(dcc_HEADERS
../include/ast.h
../include/bundle.h
../include/BinaryImage.h
../include/DccFrontend.h
../include/Enums.h
../include/dcc.h
../include/disassem.h
../include/dosdcc.h
../include/error.h
../include/graph.h
../include/hlicode.h
../include/machine_x86.h
../include/icode.h
../include/idioms/idiom.h
../include/idioms/idiom1.h
../include/idioms/arith_idioms.h
../include/idioms/call_idioms.h
../include/idioms/epilogue_idioms.h
../include/idioms/mov_idioms.h
../include/idioms/neg_idioms.h
../include/idioms/shift_idioms.h
../include/idioms/xor_idioms.h
../include/locident.h
../include/CallConvention.h
../include/project.h
../include/scanner.h
../include/state.h
../include/symtab.h
../include/types.h
../include/Procedure.h
../include/StackFrame.h
../include/BasicBlock.h
../include/dcc_interface.h
)
include_directories(${GMOCK_INCLUDE_DIRS} ${GMOCK_ROOT}/gtest/include)
add_executable(tester ${dcc_test_SOURCES})
ADD_DEPENDENCIES(tester dcc_lib)
target_link_libraries(tester dcc_lib disasm_s
${GMOCK_BOTH_LIBRARIES} ${REQ_LLVM_LIBRARIES})
add_test(dcc-tests tester)
SOURCE_GROUP(Headers FILES ${dcc_HEADERS})
set(dcc_SOURCES
dcc.cpp
)
SOURCE_GROUP(Source FILES ${dcc_SOURCES} ${dcc_LIB_SOURCES})
ADD_LIBRARY(dcc_lib STATIC ${dcc_LIB_SOURCES} ${dcc_HEADERS})
qt5_use_modules(dcc_lib Core)
ADD_EXECUTABLE(dcc_original ${dcc_SOURCES} ${dcc_UI_SOURCES})
ADD_DEPENDENCIES(dcc_original dcc_lib)
TARGET_LINK_LIBRARIES(dcc_original dcc_lib dcc_hash disasm_s)
qt5_use_modules(dcc_original Core Widgets)
SET_PROPERTY(TARGET dcc_original PROPERTY CXX_STANDARD 11)
SET_PROPERTY(TARGET dcc_original PROPERTY CXX_STANDARD_REQUIRED ON)
if(dcc_build_tests)
ADD_SUBDIRECTORY(tests)
endif()

22
src/MemoryChunk.cpp Normal file
View File

@ -0,0 +1,22 @@
#include "MemoryChunk.h"
#include <boost/icl/interval.hpp>
#include <boost/icl/right_open_interval.hpp>
#include <boost/icl/left_open_interval.hpp>
#include <boost/icl/closed_interval.hpp>
#include <boost/icl/open_interval.hpp>
using namespace boost::icl;
MemoryChunk::MemoryChunk(LinearAddress start, LinearAddress fin) : m_start(start),m_fin(fin)
{
}
bool MemoryChunk::contains(LinearAddress addr) const
{
return addr>=m_start && addr<m_fin;
}
uint64_t MemoryChunk::size() const
{
return m_fin-m_start;
}

24
src/MemoryChunk.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef BYTECHUNK_H
#define BYTECHUNK_H
#include "Address.h"
#include <utility>
#include <inttypes.h>
/**
* @brief The MemoryChunk class represents a continuous range of Addresses
*/
class MemoryChunk
{
private:
LinearAddress m_start;
LinearAddress m_fin;
public:
MemoryChunk(LinearAddress start,LinearAddress fin);
bool contains(LinearAddress addr) const;
uint64_t size() const;
std::pair<LinearAddress,LinearAddress> bounds() const { return std::make_pair(m_start,m_fin); }
};
#endif // BYTECHUNK_H

5
src/MemorySegment.cpp Normal file
View File

@ -0,0 +1,5 @@
#include "MemorySegment.h"
MemorySegment::MemorySegment(LinearAddress base, LinearAddress start, LinearAddress fin) : MemoryChunk(start,fin) {
m_base = base;
}

19
src/MemorySegment.h Normal file
View File

@ -0,0 +1,19 @@
#pragma once
#include "MemoryChunk.h"
#include <QtCore/QString>
/**
* @brief The MemorySegment represents a single chunk of memory with additional properties.
*/
class MemorySegment : public MemoryChunk
{
uint16_t m_base;
int m_flags;
QString m_name;
public:
MemorySegment(LinearAddress base,LinearAddress start,LinearAddress fin);
const QString &getName() const { return m_name; }
void setName(const QString &v) { m_name = v; }
};

View File

@ -0,0 +1,54 @@
#include "MemorySegmentCoordinator.h"
#include <boost/icl/interval_map.hpp>
#include <boost/icl/split_interval_map.hpp>
#include <utility>
using namespace boost::icl;
class MemorySegmentCoordinatorImpl {
boost::icl::interval_map<LinearAddress,SegmentHolder> m_segmentation_map;
public:
bool addSegment(LinearAddress base, LinearAddress start, LinearAddress fin, const char * name, int flags) {
if(start>fin)
return false;
if(start<base)
return false;
MemorySegment *seg = new MemorySegment(base,start,fin);
seg->setName(name);
//
auto segment_bounds(seg->bounds());
m_segmentation_map.add(std::make_pair(
interval<LinearAddress>::right_open(segment_bounds.first,segment_bounds.second),
seg)
);
return true;
}
uint32_t numberOfSegments() const { return interval_count(m_segmentation_map); }
const MemorySegment *get(LinearAddress addr) {
auto iter = m_segmentation_map.find(addr);
if(iter==m_segmentation_map.end()) {
return nullptr;
}
return iter->second;
}
};
MemorySegmentCoordinator::MemorySegmentCoordinator()
{
m_impl = new MemorySegmentCoordinatorImpl;
}
bool MemorySegmentCoordinator::addSegment(LinearAddress base, LinearAddress start, LinearAddress fin, const char * name, int flags)
{
return m_impl->addSegment(base,start,fin,name,flags);
}
uint32_t MemorySegmentCoordinator::size()
{
return m_impl->numberOfSegments();
}
MemorySegment *MemorySegmentCoordinator::getSegment(LinearAddress addr)
{
return const_cast<MemorySegment *>(m_impl->get(addr));
}

View File

@ -0,0 +1,34 @@
#pragma once
#include "MemorySegment.h"
struct SegmentHolder {
SegmentHolder() : val(nullptr) {}
SegmentHolder(MemorySegment *inf) : val(inf) {}
MemorySegment *operator->() { return val;}
MemorySegment &operator*() const { return *val;}
operator MemorySegment *() { return val;}
operator const MemorySegment *() const { return val;}
SegmentHolder operator+=(const SegmentHolder &/*s*/) {
throw std::runtime_error("Cannot aggregate MemorySegments !");
}
MemorySegment *val;
};
/**
* @brief The MemorySegmentCoordinator class is responsible for:
* - Managing the lifetime of MemorySegments
* - Providing convenience functions for querying the segment-related data
*/
class MemorySegmentCoordinator
{
class MemorySegmentCoordinatorImpl *m_impl;
public:
MemorySegmentCoordinator();
bool addSegment(LinearAddress base,LinearAddress start,LinearAddress fin,const char *name,int flags);
uint32_t size();
MemorySegment *getSegment(LinearAddress addr);
};

24
src/tests/CMakeLists.txt Normal file
View File

@ -0,0 +1,24 @@
set(TESTS
ProjectTests
LoaderTests
MemoryChunkTests
MemorySegmentCoordinatorTests
)
include(DCC_Macros)
set(target_INCLUDE_DIR
..
)
include_directories(${target_INCLUDE_DIR}
../../frontend/sparc
../../frontend/pentium
)
set(test_LIBRARIES
dcc_lib dcc_hash disasm_s
)
foreach(t ${TESTS})
ADD_QTEST(${t})
endforeach()

15
src/tests/LoaderTests.cpp Normal file
View File

@ -0,0 +1,15 @@
#include "LoaderTests.h"
#include "project.h"
#include "loader.h"
#include <QTextStream>
#include <QStringList>
#include <QDir>
#include <QProcessEnvironment>
#include <QDebug>
void LoaderTest::testDummy() {
QVERIFY2(false,"No tests written for loader");
}
QTEST_MAIN(LoaderTest)

7
src/tests/LoaderTests.h Normal file
View File

@ -0,0 +1,7 @@
#include <QtTest/QTest>
class LoaderTest : public QObject {
Q_OBJECT
private slots:
void testDummy();
};

View File

@ -0,0 +1,22 @@
#include "MemoryChunkTests.h"
#include "MemoryChunk.h"
#include "project.h"
#include "loader.h"
#include <QTextStream>
#include <QStringList>
#include <QDir>
#include <QProcessEnvironment>
#include <QDebug>
void MemoryChunkTest::testIfConstructionWorksProperly() {
MemoryChunk mc(0,10);
QCOMPARE(mc.size(),size_t(10));
QVERIFY(mc.contains(1));
QVERIFY(not mc.contains(10));
QVERIFY(not mc.contains(-1));
QVERIFY(not mc.contains(100));
}
QTEST_MAIN(MemoryChunkTest)

View File

@ -0,0 +1,7 @@
#include <QtTest/QTest>
class MemoryChunkTest : public QObject {
Q_OBJECT
private slots:
void testIfConstructionWorksProperly();
};

View File

@ -0,0 +1,55 @@
#include "MemorySegmentCoordinatorTests.h"
#include "MemorySegmentCoordinator.h"
#include "project.h"
#include "loader.h"
#include <QTextStream>
#include <QStringList>
#include <QDir>
#include <QProcessEnvironment>
#include <QDebug>
void MemorySegmentCoordinatorTest::testSimpleQueries()
{
MemorySegmentCoordinator segmenter;
segmenter.addSegment(
LinearAddress(0x10),
LinearAddress(0x13),LinearAddress(0x20),
".text",4);
MemorySegment * seg;
QCOMPARE(segmenter.getSegment(LinearAddress(0x9)),(MemorySegment *)nullptr);
seg = segmenter.getSegment(0x14);
QVERIFY(seg!=nullptr);
if(seg) {
QCOMPARE(seg->getName(),QString(".text"));
}
}
void MemorySegmentCoordinatorTest::testAddingSegments()
{
MemorySegmentCoordinator segmenter;
QVERIFY(segmenter.addSegment(
LinearAddress(0x10),
LinearAddress(0x13),LinearAddress(0x20),
".text",4));
QCOMPARE(segmenter.size(),uint32_t(1));
QVERIFY(segmenter.addSegment(
LinearAddress(0x20),
LinearAddress(0x22),LinearAddress(0x33),
".text",4));
QVERIFY2(not segmenter.addSegment(
LinearAddress(0x20),
LinearAddress(0x40),LinearAddress(0x20),
".text",4),"Adding segment with start>fin should fail");
QVERIFY2(not segmenter.addSegment(
LinearAddress(0x20),
LinearAddress(0x10),LinearAddress(0x20),
".text",4),"Segment start should be >= base");
QCOMPARE(segmenter.size(),uint32_t(2));
}
void MemorySegmentCoordinatorTest::testAddingIntersectingSegments()
{
}
QTEST_MAIN(MemorySegmentCoordinatorTest)

View File

@ -0,0 +1,9 @@
#include <QtTest/QTest>
class MemorySegmentCoordinatorTest : public QObject {
Q_OBJECT
private slots:
void testAddingSegments();
void testAddingIntersectingSegments();
void testSimpleQueries();
};

View File

@ -0,0 +1,53 @@
#include "ProjectTests.h"
#include "project.h"
#include <QTextStream>
#include <QStringList>
#include <QDir>
#include <QProcessEnvironment>
#include <QDebug>
static bool logset = false;
static QString TEST_BASE;
static QDir baseDir;
void ProjectTest::initTestCase() {
if (!logset) {
TEST_BASE = QProcessEnvironment::systemEnvironment().value("DCC_TEST_BASE", "");
baseDir = QDir(TEST_BASE);
if (TEST_BASE.isEmpty()) {
qWarning() << "DCC_TEST_BASE environment variable not set, will assume '..', many test may fail";
TEST_BASE = "..";
baseDir = QDir("..");
}
logset = true;
// Boomerang::get()->setProgPath(TEST_BASE);
// Boomerang::get()->setPluginPath(TEST_BASE + "/out");
// Boomerang::get()->setLogger(new NullLogger());
}
}
void ProjectTest::testNewProjectIsInitalized() {
Project p;
QCOMPARE((CALL_GRAPH *)nullptr,p.callGraph);
QVERIFY(p.pProcList.empty());
QVERIFY(p.binary_path().isEmpty());
QVERIFY(p.project_name().isEmpty());
QVERIFY(p.symtab.empty());
}
void ProjectTest::testCreatedProjectHasValidNames() {
Project p;
QStringList strs = {"./Project1.EXE","/home/Project2.EXE","/home/Pro\\ ject3"};
QStringList expected = {"Project1","Project2","Pro\\ ject3"};
for(size_t i=0; i<strs.size(); i++)
{
p.create(strs[i]);
QCOMPARE((CALL_GRAPH *)nullptr,p.callGraph);
QVERIFY(p.pProcList.empty());
QCOMPARE(expected[i],p.project_name());
QCOMPARE(strs[i],p.binary_path());
QVERIFY(p.symtab.empty());
}
}
QTEST_MAIN(ProjectTest)

10
src/tests/ProjectTests.h Normal file
View File

@ -0,0 +1,10 @@
#include <QtTest/QTest>
class ProjectTest : public QObject {
Q_OBJECT
private slots:
void testNewProjectIsInitalized();
void testCreatedProjectHasValidNames();
void initTestCase();
};

View File

@ -1,14 +0,0 @@
#include "project.h"
#include "loader.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
TEST(Loader, NewProjectIsInitalized) {
Project p;
EXPECT_EQ(nullptr,p.callGraph);
ASSERT_TRUE(p.pProcList.empty());
ASSERT_TRUE(p.binary_path().empty());
ASSERT_TRUE(p.project_name().empty());
ASSERT_TRUE(p.symtab.empty());
}

View File

@ -1,27 +0,0 @@
#include "project.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
TEST(Project, NewProjectIsInitalized) {
Project p;
EXPECT_EQ(nullptr,p.callGraph);
ASSERT_TRUE(p.pProcList.empty());
ASSERT_TRUE(p.binary_path().empty());
ASSERT_TRUE(p.project_name().empty());
ASSERT_TRUE(p.symtab.empty());
}
TEST(Project, CreatedProjectHasValidNames) {
Project p;
std::vector<std::string> strs = {"./Project1.EXE","/home/Project2.EXE","/home/Pro\\ ject3"};
std::vector<std::string> expected = {"Project1","Project2","Pro\\ ject3"};
for(size_t i=0; i<strs.size(); i++)
{
p.create(strs[i]);
EXPECT_EQ(nullptr,p.callGraph);
ASSERT_TRUE(p.pProcList.empty());
EXPECT_EQ(expected[i],p.project_name());
EXPECT_EQ(strs[i],p.binary_path());
ASSERT_TRUE(p.symtab.empty());
}
}

View File

@ -406,8 +406,9 @@ void phChar(char ch) {
/* Take a lump of data from a header file, and churn the state machine
through each char */
boolT phData(char *buff, int ndata) {
int i, j;
int i;
#ifdef DEBUG
int j=0;
char cLine[81];
char cfLine[90];
#endif
@ -415,7 +416,6 @@ boolT phData(char *buff, int ndata) {
if (ndata < 1) {
ndata = strlen(buff);
}
j = 0;
for (i = 0; i < ndata; i++) {
phChar(buff[i]);